From 974335e4144eab90a8c5eb91da55d2f8385c68c6 Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Mon, 3 Jun 2024 10:35:59 +0200
Subject: [PATCH 001/133] Add experimental server-side notification grouping
 (#29889)

---
 .../api/v2_alpha/notifications_controller.rb  |  91 ++++++++++
 app/models/notification.rb                    |  62 +++++++
 app/models/notification_group.rb              |  29 ++++
 .../rest/notification_group_serializer.rb     |  45 +++++
 app/services/notify_service.rb                |  22 +++
 config/application.rb                         |   2 +
 config/routes/api.rb                          |  12 ++
 ...13095755_add_group_key_to_notifications.rb |   7 +
 ...tifications_on_account_id_and_group_key.rb |   9 +
 db/schema.rb                                  |   2 +
 lib/active_record/with_recursive.rb           |  65 +++++++
 lib/arel/union_parenthesizing.rb              |  51 ++++++
 spec/models/notification_spec.rb              |  60 +++++++
 .../api/v2_alpha/notifications_spec.rb        | 161 ++++++++++++++++++
 14 files changed, 618 insertions(+)
 create mode 100644 app/controllers/api/v2_alpha/notifications_controller.rb
 create mode 100644 app/models/notification_group.rb
 create mode 100644 app/serializers/rest/notification_group_serializer.rb
 create mode 100644 db/migrate/20240513095755_add_group_key_to_notifications.rb
 create mode 100644 db/migrate/20240513123807_add_index_notifications_on_account_id_and_group_key.rb
 create mode 100644 lib/active_record/with_recursive.rb
 create mode 100644 lib/arel/union_parenthesizing.rb
 create mode 100644 spec/requests/api/v2_alpha/notifications_spec.rb

diff --git a/app/controllers/api/v2_alpha/notifications_controller.rb b/app/controllers/api/v2_alpha/notifications_controller.rb
new file mode 100644
index 000000000..19d3ac901
--- /dev/null
+++ b/app/controllers/api/v2_alpha/notifications_controller.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+class Api::V2Alpha::NotificationsController < Api::BaseController
+  before_action -> { doorkeeper_authorize! :read, :'read:notifications' }, except: [:clear, :dismiss]
+  before_action -> { doorkeeper_authorize! :write, :'write:notifications' }, only: [:clear, :dismiss]
+  before_action :require_user!
+  after_action :insert_pagination_headers, only: :index
+
+  DEFAULT_NOTIFICATIONS_LIMIT = 40
+
+  def index
+    with_read_replica do
+      @notifications = load_notifications
+      @group_metadata = load_group_metadata
+      @relationships = StatusRelationshipsPresenter.new(target_statuses_from_notifications, current_user&.account_id)
+    end
+
+    render json: @notifications.map { |notification| NotificationGroup.from_notification(notification) }, each_serializer: REST::NotificationGroupSerializer, relationships: @relationships, group_metadata: @group_metadata
+  end
+
+  def show
+    @notification = current_account.notifications.without_suspended.find_by!(group_key: params[:id])
+    render json: NotificationGroup.from_notification(@notification), serializer: REST::NotificationGroupSerializer
+  end
+
+  def clear
+    current_account.notifications.delete_all
+    render_empty
+  end
+
+  def dismiss
+    current_account.notifications.where(group_key: params[:id]).destroy_all
+    render_empty
+  end
+
+  private
+
+  def load_notifications
+    notifications = browserable_account_notifications.includes(from_account: [:account_stat, :user]).to_a_grouped_paginated_by_id(
+      limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
+      params_slice(:max_id, :since_id, :min_id)
+    )
+
+    Notification.preload_cache_collection_target_statuses(notifications) do |target_statuses|
+      preload_collection(target_statuses, Status)
+    end
+  end
+
+  def load_group_metadata
+    return {} if @notifications.empty?
+
+    browserable_account_notifications
+      .where(group_key: @notifications.filter_map(&:group_key))
+      .where(id: (@notifications.last.id)..(@notifications.first.id))
+      .group(:group_key)
+      .pluck(:group_key, 'min(notifications.id) as min_id', 'max(notifications.id) as max_id', 'max(notifications.created_at) as latest_notification_at')
+      .to_h { |group_key, min_id, max_id, latest_notification_at| [group_key, { min_id: min_id, max_id: max_id, latest_notification_at: latest_notification_at }] }
+  end
+
+  def browserable_account_notifications
+    current_account.notifications.without_suspended.browserable(
+      types: Array(browserable_params[:types]),
+      exclude_types: Array(browserable_params[:exclude_types]),
+      include_filtered: truthy_param?(:include_filtered)
+    )
+  end
+
+  def target_statuses_from_notifications
+    @notifications.filter_map(&:target_status)
+  end
+
+  def next_path
+    api_v2_alpha_notifications_url pagination_params(max_id: pagination_max_id) unless @notifications.empty?
+  end
+
+  def prev_path
+    api_v2_alpha_notifications_url pagination_params(min_id: pagination_since_id) unless @notifications.empty?
+  end
+
+  def pagination_collection
+    @notifications
+  end
+
+  def browserable_params
+    params.permit(:include_filtered, types: [], exclude_types: [])
+  end
+
+  def pagination_params(core_params)
+    params.slice(:limit, :types, :exclude_types, :include_filtered).permit(:limit, :include_filtered, types: [], exclude_types: []).merge(core_params)
+  end
+end
diff --git a/app/models/notification.rb b/app/models/notification.rb
index 7cbab4dc8..e3deaa534 100644
--- a/app/models/notification.rb
+++ b/app/models/notification.rb
@@ -13,6 +13,7 @@
 #  from_account_id :bigint(8)        not null
 #  type            :string
 #  filtered        :boolean          default(FALSE), not null
+#  group_key       :string
 #
 
 class Notification < ApplicationRecord
@@ -136,6 +137,67 @@ class Notification < ApplicationRecord
       end
     end
 
+    # This returns notifications from the request page, but with at most one notification per group.
+    # Notifications that have no `group_key` each count as a separate group.
+    def paginate_groups_by_max_id(limit, max_id: nil, since_id: nil)
+      query = reorder(id: :desc)
+      query = query.where(id: ...max_id) if max_id.present?
+      query = query.where(id: (since_id + 1)...) if since_id.present?
+
+      unscoped
+        .with_recursive(
+          grouped_notifications: [
+            query
+              .select('notifications.*', "ARRAY[COALESCE(notifications.group_key, 'ungrouped-' || notifications.id)] groups")
+              .limit(1),
+            query
+              .joins('CROSS JOIN grouped_notifications')
+              .where('notifications.id < grouped_notifications.id')
+              .where.not("COALESCE(notifications.group_key, 'ungrouped-' || notifications.id) = ANY(grouped_notifications.groups)")
+              .select('notifications.*', "array_append(grouped_notifications.groups, COALESCE(notifications.group_key, 'ungrouped-' || notifications.id))")
+              .limit(1),
+          ]
+        )
+        .from('grouped_notifications AS notifications')
+        .order(id: :desc)
+        .limit(limit)
+    end
+
+    # Differs from :paginate_groups_by_max_id in that it gives the results immediately following min_id,
+    # whereas since_id gives the items with largest id, but with since_id as a cutoff.
+    # Results will be in ascending order by id.
+    def paginate_groups_by_min_id(limit, max_id: nil, min_id: nil)
+      query = reorder(id: :asc)
+      query = query.where(id: (min_id + 1)...) if min_id.present?
+      query = query.where(id: ...max_id) if max_id.present?
+
+      unscoped
+        .with_recursive(
+          grouped_notifications: [
+            query
+              .select('notifications.*', "ARRAY[COALESCE(notifications.group_key, 'ungrouped-' || notifications.id)] groups")
+              .limit(1),
+            query
+              .joins('CROSS JOIN grouped_notifications')
+              .where('notifications.id > grouped_notifications.id')
+              .where.not("COALESCE(notifications.group_key, 'ungrouped-' || notifications.id) = ANY(grouped_notifications.groups)")
+              .select('notifications.*', "array_append(grouped_notifications.groups, COALESCE(notifications.group_key, 'ungrouped-' || notifications.id))")
+              .limit(1),
+          ]
+        )
+        .from('grouped_notifications AS notifications')
+        .order(id: :asc)
+        .limit(limit)
+    end
+
+    def to_a_grouped_paginated_by_id(limit, options = {})
+      if options[:min_id].present?
+        paginate_groups_by_min_id(limit, min_id: options[:min_id], max_id: options[:max_id]).reverse
+      else
+        paginate_groups_by_max_id(limit, max_id: options[:max_id], since_id: options[:since_id]).to_a
+      end
+    end
+
     def preload_cache_collection_target_statuses(notifications, &_block)
       notifications.group_by(&:type).each do |type, grouped_notifications|
         associations = TARGET_STATUS_INCLUDES_BY_TYPE[type]
diff --git a/app/models/notification_group.rb b/app/models/notification_group.rb
new file mode 100644
index 000000000..07967f9dc
--- /dev/null
+++ b/app/models/notification_group.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class NotificationGroup < ActiveModelSerializers::Model
+  attributes :group_key, :sample_accounts, :notifications_count, :notification
+
+  def self.from_notification(notification)
+    if notification.group_key.present?
+      # TODO: caching and preloading
+      sample_accounts = notification.account.notifications.where(group_key: notification.group_key).order(id: :desc).limit(3).map(&:from_account)
+      notifications_count = notification.account.notifications.where(group_key: notification.group_key).count
+    else
+      sample_accounts = [notification.from_account]
+      notifications_count = 1
+    end
+
+    NotificationGroup.new(
+      notification: notification,
+      group_key: notification.group_key || "ungrouped-#{notification.id}",
+      sample_accounts: sample_accounts,
+      notifications_count: notifications_count
+    )
+  end
+
+  delegate :type,
+           :target_status,
+           :report,
+           :account_relationship_severance_event,
+           to: :notification, prefix: false
+end
diff --git a/app/serializers/rest/notification_group_serializer.rb b/app/serializers/rest/notification_group_serializer.rb
new file mode 100644
index 000000000..6c1d2465d
--- /dev/null
+++ b/app/serializers/rest/notification_group_serializer.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+class REST::NotificationGroupSerializer < ActiveModel::Serializer
+  attributes :group_key, :notifications_count, :type
+
+  attribute :page_min_id, if: :paginated?
+  attribute :page_max_id, if: :paginated?
+  attribute :latest_page_notification_at, if: :paginated?
+
+  has_many :sample_accounts, serializer: REST::AccountSerializer
+  belongs_to :target_status, key: :status, if: :status_type?, serializer: REST::StatusSerializer
+  belongs_to :report, if: :report_type?, serializer: REST::ReportSerializer
+  belongs_to :account_relationship_severance_event, key: :event, if: :relationship_severance_event?, serializer: REST::AccountRelationshipSeveranceEventSerializer
+
+  def status_type?
+    [:favourite, :reblog, :status, :mention, :poll, :update].include?(object.type)
+  end
+
+  def report_type?
+    object.type == :'admin.report'
+  end
+
+  def relationship_severance_event?
+    object.type == :severed_relationships
+  end
+
+  def page_min_id
+    range = instance_options[:group_metadata][object.group_key]
+    range.present? ? range[:min_id].to_s : object.notification.id.to_s
+  end
+
+  def page_max_id
+    range = instance_options[:group_metadata][object.group_key]
+    range.present? ? range[:max_id].to_s : object.notification.id.to_s
+  end
+
+  def latest_page_notification_at
+    range = instance_options[:group_metadata][object.group_key]
+    range.present? ? range[:latest_notification_at] : object.notification.created_at
+  end
+
+  def paginated?
+    instance_options[:group_metadata].present?
+  end
+end
diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb
index 1f01c2d48..d69b5af14 100644
--- a/app/services/notify_service.rb
+++ b/app/services/notify_service.rb
@@ -3,6 +3,9 @@
 class NotifyService < BaseService
   include Redisable
 
+  MAXIMUM_GROUP_SPAN_HOURS = 12
+  MAXIMUM_GROUP_GAP_TIME = 4.hours.to_i
+
   NON_EMAIL_TYPES = %i(
     admin.report
     admin.sign_up
@@ -183,6 +186,7 @@ class NotifyService < BaseService
     return if dismiss?
 
     @notification.filtered = filter?
+    @notification.group_key = notification_group_key
     @notification.save!
 
     # It's possible the underlying activity has been deleted
@@ -202,6 +206,24 @@ class NotifyService < BaseService
 
   private
 
+  def notification_group_key
+    return nil if @notification.filtered || %i(favourite reblog).exclude?(@notification.type)
+
+    type_prefix = "#{@notification.type}-#{@notification.target_status.id}"
+    redis_key   = "notif-group/#{@recipient.id}/#{type_prefix}"
+    hour_bucket = @notification.activity.created_at.utc.to_i / 1.hour.to_i
+
+    # Reuse previous group if it does not span too large an amount of time
+    previous_bucket = redis.get(redis_key).to_i
+    hour_bucket = previous_bucket if hour_bucket < previous_bucket + MAXIMUM_GROUP_SPAN_HOURS
+
+    # Do not track groups past a given inactivity time
+    # We do not concern ourselves with race conditions since we use hour buckets
+    redis.set(redis_key, hour_bucket, ex: MAXIMUM_GROUP_GAP_TIME)
+
+    "#{type_prefix}-#{hour_bucket}"
+  end
+
   def dismiss?
     DismissCondition.new(@notification).dismiss?
   end
diff --git a/config/application.rb b/config/application.rb
index 6d6e91a5c..a8e313069 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -51,6 +51,8 @@ require_relative '../lib/rails/engine_extensions'
 require_relative '../lib/action_dispatch/remote_ip_extensions'
 require_relative '../lib/active_record/database_tasks_extensions'
 require_relative '../lib/active_record/batches'
+require_relative '../lib/active_record/with_recursive'
+require_relative '../lib/arel/union_parenthesizing'
 require_relative '../lib/simple_navigation/item_extensions'
 
 Bundler.require(:pam_authentication) if ENV['PAM_ENABLED'] == 'true'
diff --git a/config/routes/api.rb b/config/routes/api.rb
index bf3cee0c1..135a19a0a 100644
--- a/config/routes/api.rb
+++ b/config/routes/api.rb
@@ -327,6 +327,18 @@ namespace :api, format: false do
     end
   end
 
+  namespace :v2_alpha do
+    resources :notifications, only: [:index, :show] do
+      collection do
+        post :clear
+      end
+
+      member do
+        post :dismiss
+      end
+    end
+  end
+
   namespace :web do
     resource :settings, only: [:update]
     resources :embeds, only: [:show]
diff --git a/db/migrate/20240513095755_add_group_key_to_notifications.rb b/db/migrate/20240513095755_add_group_key_to_notifications.rb
new file mode 100644
index 000000000..2e2a302ff
--- /dev/null
+++ b/db/migrate/20240513095755_add_group_key_to_notifications.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddGroupKeyToNotifications < ActiveRecord::Migration[7.1]
+  def change
+    add_column :notifications, :group_key, :string
+  end
+end
diff --git a/db/migrate/20240513123807_add_index_notifications_on_account_id_and_group_key.rb b/db/migrate/20240513123807_add_index_notifications_on_account_id_and_group_key.rb
new file mode 100644
index 000000000..66874418b
--- /dev/null
+++ b/db/migrate/20240513123807_add_index_notifications_on_account_id_and_group_key.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddIndexNotificationsOnAccountIdAndGroupKey < ActiveRecord::Migration[7.1]
+  disable_ddl_transaction!
+
+  def change
+    add_index :notifications, [:account_id, :group_key], algorithm: :concurrently, where: 'group_key IS NOT NULL'
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 3a47522d2..73f6b464e 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -724,6 +724,8 @@ ActiveRecord::Schema[7.1].define(version: 2024_05_22_041528) do
     t.bigint "from_account_id", null: false
     t.string "type"
     t.boolean "filtered", default: false, null: false
+    t.string "group_key"
+    t.index ["account_id", "group_key"], name: "index_notifications_on_account_id_and_group_key", where: "(group_key IS NOT NULL)"
     t.index ["account_id", "id", "type"], name: "index_notifications_on_account_id_and_id_and_type", order: { id: :desc }
     t.index ["account_id", "id", "type"], name: "index_notifications_on_filtered", order: { id: :desc }, where: "(filtered = false)"
     t.index ["activity_id", "activity_type"], name: "index_notifications_on_activity_id_and_activity_type"
diff --git a/lib/active_record/with_recursive.rb b/lib/active_record/with_recursive.rb
new file mode 100644
index 000000000..4bd3e81ee
--- /dev/null
+++ b/lib/active_record/with_recursive.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+# Add support for writing recursive CTEs in ActiveRecord
+
+# Initially from Lorin Thwaits (https://github.com/lorint) as per comment:
+# https://github.com/vlado/activerecord-cte/issues/16#issuecomment-1433043310
+
+# Modified from the above code to change the signature to
+# `with_recursive(hash)` and extending CTE hash values to also includes arrays
+# of values that get turned into UNION ALL expressions.
+
+# This implementation has been merged in Rails: https://github.com/rails/rails/pull/51601
+
+module ActiveRecord
+  module QueryMethodsExtensions
+    def with_recursive(*args)
+      @with_is_recursive = true
+      check_if_method_has_arguments!(__callee__, args)
+      spawn.with_recursive!(*args)
+    end
+
+    # Like #with_recursive but modifies the relation in place.
+    def with_recursive!(*args) # :nodoc:
+      self.with_values += args
+      @with_is_recursive = true
+      self
+    end
+
+    private
+
+    def build_with(arel)
+      return if with_values.empty?
+
+      with_statements = with_values.map do |with_value|
+        raise ArgumentError, "Unsupported argument type: #{with_value} #{with_value.class}" unless with_value.is_a?(Hash)
+
+        build_with_value_from_hash(with_value)
+      end
+
+      # Was:  arel.with(with_statements)
+      @with_is_recursive ? arel.with(:recursive, with_statements) : arel.with(with_statements)
+    end
+
+    def build_with_value_from_hash(hash)
+      hash.map do |name, value|
+        Arel::Nodes::TableAlias.new(build_with_expression_from_value(value), name)
+      end
+    end
+
+    def build_with_expression_from_value(value)
+      case value
+      when Arel::Nodes::SqlLiteral then Arel::Nodes::Grouping.new(value)
+      when ActiveRecord::Relation then value.arel
+      when Arel::SelectManager then value
+      when Array then value.map { |e| build_with_expression_from_value(e) }.reduce { |result, value| Arel::Nodes::UnionAll.new(result, value) }
+      else
+        raise ArgumentError, "Unsupported argument type: `#{value}` #{value.class}"
+      end
+    end
+  end
+end
+
+ActiveSupport.on_load(:active_record) do
+  ActiveRecord::QueryMethods.prepend(ActiveRecord::QueryMethodsExtensions)
+end
diff --git a/lib/arel/union_parenthesizing.rb b/lib/arel/union_parenthesizing.rb
new file mode 100644
index 000000000..852d8e92d
--- /dev/null
+++ b/lib/arel/union_parenthesizing.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+# Fix an issue with `LIMIT` ocurring on the left side of a `UNION` causing syntax errors.
+# See https://github.com/rails/rails/issues/40181
+
+# The fix has been merged in ActiveRecord: https://github.com/rails/rails/pull/51549
+# TODO: drop this when available in ActiveRecord
+
+# rubocop:disable all -- This is a mostly vendored file
+
+module Arel
+  module Visitors
+    class ToSql
+      private
+
+        def infix_value_with_paren(o, collector, value, suppress_parens = false)
+          collector << "( " unless suppress_parens
+          collector = if o.left.class == o.class
+            infix_value_with_paren(o.left, collector, value, true)
+          else
+            select_parentheses o.left, collector, false # Changed from `visit o.left, collector`
+          end
+          collector << value
+          collector = if o.right.class == o.class
+            infix_value_with_paren(o.right, collector, value, true)
+          else
+            select_parentheses o.right, collector, false # Changed from `visit o.right, collector`
+          end
+          collector << " )" unless suppress_parens
+          collector
+        end
+
+        def select_parentheses(o, collector, always_wrap_selects = true)
+          if o.is_a?(Nodes::SelectStatement) && (always_wrap_selects || require_parentheses?(o))
+            collector << "("
+            visit o, collector
+            collector << ")"
+            collector
+          else
+            visit o, collector
+          end
+        end
+
+        def require_parentheses?(o)
+          !o.orders.empty? || o.limit || o.offset
+        end
+    end
+  end
+end
+
+# rubocop:enable all
diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb
index 3c7d51ae1..d498ee02a 100644
--- a/spec/models/notification_spec.rb
+++ b/spec/models/notification_spec.rb
@@ -151,6 +151,66 @@ RSpec.describe Notification do
     end
   end
 
+  describe '.paginate_groups_by_max_id' do
+    let(:account) { Fabricate(:account) }
+
+    let!(:notifications) do
+      ['group-1', 'group-1', nil, 'group-2', nil, 'group-1', 'group-2', 'group-1']
+        .map { |group_key| Fabricate(:notification, account: account, group_key: group_key) }
+    end
+
+    context 'without since_id or max_id' do
+      it 'returns the most recent notifications, only keeping one notification per group' do
+        expect(described_class.without_suspended.paginate_groups_by_max_id(4).pluck(:id))
+          .to eq [notifications[7], notifications[6], notifications[4], notifications[2]].pluck(:id)
+      end
+    end
+
+    context 'with since_id' do
+      it 'returns the most recent notifications, only keeping one notification per group' do
+        expect(described_class.without_suspended.paginate_groups_by_max_id(4, since_id: notifications[4].id).pluck(:id))
+          .to eq [notifications[7], notifications[6]].pluck(:id)
+      end
+    end
+
+    context 'with max_id' do
+      it 'returns the most recent notifications after max_id, only keeping one notification per group' do
+        expect(described_class.without_suspended.paginate_groups_by_max_id(4, max_id: notifications[7].id).pluck(:id))
+          .to eq [notifications[6], notifications[5], notifications[4], notifications[2]].pluck(:id)
+      end
+    end
+  end
+
+  describe '.paginate_groups_by_min_id' do
+    let(:account) { Fabricate(:account) }
+
+    let!(:notifications) do
+      ['group-1', 'group-1', nil, 'group-2', nil, 'group-1', 'group-2', 'group-1']
+        .map { |group_key| Fabricate(:notification, account: account, group_key: group_key) }
+    end
+
+    context 'without min_id or max_id' do
+      it 'returns the oldest notifications, only keeping one notification per group' do
+        expect(described_class.without_suspended.paginate_groups_by_min_id(4).pluck(:id))
+          .to eq [notifications[0], notifications[2], notifications[3], notifications[4]].pluck(:id)
+      end
+    end
+
+    context 'with max_id' do
+      it 'returns the oldest notifications, stopping at max_id, only keeping one notification per group' do
+        expect(described_class.without_suspended.paginate_groups_by_min_id(4, max_id: notifications[4].id).pluck(:id))
+          .to eq [notifications[0], notifications[2], notifications[3]].pluck(:id)
+      end
+    end
+
+    context 'with min_id' do
+      it 'returns the most oldest notifications after min_id, only keeping one notification per group' do
+        expect(described_class.without_suspended.paginate_groups_by_min_id(4, min_id: notifications[0].id).pluck(:id))
+          .to eq [notifications[1], notifications[2], notifications[3], notifications[4]].pluck(:id)
+      end
+    end
+  end
+
   describe '.preload_cache_collection_target_statuses' do
     subject do
       described_class.preload_cache_collection_target_statuses(notifications) do |target_statuses|
diff --git a/spec/requests/api/v2_alpha/notifications_spec.rb b/spec/requests/api/v2_alpha/notifications_spec.rb
new file mode 100644
index 000000000..9bd1a32e9
--- /dev/null
+++ b/spec/requests/api/v2_alpha/notifications_spec.rb
@@ -0,0 +1,161 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Notifications' do
+  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_alpha/notifications', :sidekiq_inline do
+    subject do
+      get '/api/v2_alpha/notifications', headers: headers, params: params
+    end
+
+    let(:bob)    { Fabricate(:user) }
+    let(:tom)    { Fabricate(:user) }
+    let(:params) { {} }
+
+    before do
+      first_status = PostStatusService.new.call(user.account, text: 'Test')
+      ReblogService.new.call(bob.account, first_status)
+      mentioning_status = PostStatusService.new.call(bob.account, text: 'Hello @alice')
+      mentioning_status.mentions.first
+      FavouriteService.new.call(bob.account, first_status)
+      FavouriteService.new.call(tom.account, first_status)
+      FollowService.new.call(bob.account, user.account)
+    end
+
+    it_behaves_like 'forbidden for wrong scope', 'write write:notifications'
+
+    context 'with no options' do
+      it 'returns expected notification types', :aggregate_failures do
+        subject
+
+        expect(response).to have_http_status(200)
+        expect(body_json_types).to include('reblog', 'mention', 'favourite', 'follow')
+      end
+    end
+
+    context 'with exclude_types param' do
+      let(:params) { { exclude_types: %w(mention) } }
+
+      it 'returns everything but excluded type', :aggregate_failures do
+        subject
+
+        expect(response).to have_http_status(200)
+        expect(body_as_json.size).to_not eq 0
+        expect(body_json_types.uniq).to_not include 'mention'
+      end
+    end
+
+    context 'with types param' do
+      let(:params) { { types: %w(mention) } }
+
+      it 'returns only requested type', :aggregate_failures do
+        subject
+
+        expect(response).to have_http_status(200)
+        expect(body_json_types.uniq).to eq ['mention']
+      end
+    end
+
+    context 'with limit param' do
+      let(:params) { { limit: 3 } }
+
+      it 'returns the requested number of notifications paginated', :aggregate_failures do
+        subject
+
+        notifications = user.account.notifications
+
+        expect(body_as_json.size)
+          .to eq(params[:limit])
+
+        expect(response)
+          .to include_pagination_headers(
+            prev: api_v2_alpha_notifications_url(limit: params[:limit], min_id: notifications.last.id),
+            # TODO: one downside of the current approach is that we return the first ID matching the group,
+            # not the last that has been skipped, so pagination is very likely to give overlap
+            next: api_v2_alpha_notifications_url(limit: params[:limit], max_id: notifications[1].id)
+          )
+      end
+    end
+
+    def body_json_types
+      body_as_json.pluck(:type)
+    end
+  end
+
+  describe 'GET /api/v2_alpha/notifications/:id' do
+    subject do
+      get "/api/v2_alpha/notifications/#{notification.group_key}", headers: headers
+    end
+
+    let(:notification) { Fabricate(:notification, account: user.account, group_key: 'foobar') }
+
+    it_behaves_like 'forbidden for wrong scope', 'write write:notifications'
+
+    it 'returns http success' do
+      subject
+
+      expect(response).to have_http_status(200)
+    end
+
+    context 'when notification belongs to someone else' do
+      let(:notification) { Fabricate(:notification, group_key: 'foobar') }
+
+      it 'returns http not found' do
+        subject
+
+        expect(response).to have_http_status(404)
+      end
+    end
+  end
+
+  describe 'POST /api/v2_alpha/notifications/:id/dismiss' do
+    subject do
+      post "/api/v2_alpha/notifications/#{notification.group_key}/dismiss", headers: headers
+    end
+
+    let!(:notification) { Fabricate(:notification, account: user.account, group_key: 'foobar') }
+
+    it_behaves_like 'forbidden for wrong scope', 'read read:notifications'
+
+    it 'destroys the notification' do
+      subject
+
+      expect(response).to have_http_status(200)
+      expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
+    end
+
+    context 'when notification belongs to someone else' do
+      let(:notification) { Fabricate(:notification) }
+
+      it 'returns http not found' do
+        subject
+
+        expect(response).to have_http_status(404)
+      end
+    end
+  end
+
+  describe 'POST /api/v2_alpha/notifications/clear' do
+    subject do
+      post '/api/v2_alpha/notifications/clear', headers: headers
+    end
+
+    before do
+      Fabricate(:notification, account: user.account)
+    end
+
+    it_behaves_like 'forbidden for wrong scope', 'read read:notifications'
+
+    it 'clears notifications for the account' do
+      subject
+
+      expect(user.account.reload.notifications).to be_empty
+      expect(response).to have_http_status(200)
+    end
+  end
+end

From 249cbc449c301aef2a21117138d140efb17b0d96 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Mon, 3 Jun 2024 05:15:58 -0400
Subject: [PATCH 002/133] Use existing config access to `local_domain` value
 (#30509)

---
 app/views/auth/registrations/edit.html.haml     | 2 +-
 app/views/errors/self_destruct.html.haml        | 2 +-
 spec/requests/well_known/oauth_metadata_spec.rb | 2 +-
 spec/support/signed_request_helpers.rb          | 2 +-
 spec/system/profile_spec.rb                     | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/app/views/auth/registrations/edit.html.haml b/app/views/auth/registrations/edit.html.haml
index 48350f478..07d6c1af5 100644
--- a/app/views/auth/registrations/edit.html.haml
+++ b/app/views/auth/registrations/edit.html.haml
@@ -3,7 +3,7 @@
 
 - if self_destruct?
   .flash-message.warning
-    = t('auth.status.self_destruct', domain: ENV.fetch('LOCAL_DOMAIN'))
+    = t('auth.status.self_destruct', domain: Rails.configuration.x.local_domain)
 - else
   = render partial: 'status', locals: { user: @user, strikes: @strikes }
 
diff --git a/app/views/errors/self_destruct.html.haml b/app/views/errors/self_destruct.html.haml
index 09b17a5a9..b9ff48f68 100644
--- a/app/views/errors/self_destruct.html.haml
+++ b/app/views/errors/self_destruct.html.haml
@@ -3,7 +3,7 @@
 
 .simple_form
   %h1.title= t('self_destruct.title')
-  %p.lead= t('self_destruct.lead_html', domain: ENV.fetch('LOCAL_DOMAIN'))
+  %p.lead= t('self_destruct.lead_html', domain: Rails.configuration.x.local_domain)
 
 .form-footer
   %ul.no-list
diff --git a/spec/requests/well_known/oauth_metadata_spec.rb b/spec/requests/well_known/oauth_metadata_spec.rb
index deef189ac..3350d5931 100644
--- a/spec/requests/well_known/oauth_metadata_spec.rb
+++ b/spec/requests/well_known/oauth_metadata_spec.rb
@@ -6,7 +6,7 @@ describe 'The /.well-known/oauth-authorization-server request' do
   let(:protocol) { ENV.fetch('LOCAL_HTTPS', true) ? :https : :http }
 
   before do
-    host! ENV.fetch('LOCAL_DOMAIN')
+    host! Rails.configuration.x.local_domain
   end
 
   it 'returns http success with valid JSON response' do
diff --git a/spec/support/signed_request_helpers.rb b/spec/support/signed_request_helpers.rb
index eba4095e4..8a52179ca 100644
--- a/spec/support/signed_request_helpers.rb
+++ b/spec/support/signed_request_helpers.rb
@@ -6,7 +6,7 @@ module SignedRequestHelpers
 
     headers ||= {}
     headers['Date'] = Time.now.utc.httpdate
-    headers['Host'] = ENV.fetch('LOCAL_DOMAIN')
+    headers['Host'] = Rails.configuration.x.local_domain
     signed_headers = headers.merge('(request-target)' => "get #{path}").slice('(request-target)', 'Host', 'Date')
 
     key_id = ActivityPub::TagManager.instance.key_uri_for(sign_with)
diff --git a/spec/system/profile_spec.rb b/spec/system/profile_spec.rb
index 421b68a16..2517e823b 100644
--- a/spec/system/profile_spec.rb
+++ b/spec/system/profile_spec.rb
@@ -7,7 +7,7 @@ describe 'Profile' do
 
   subject { page }
 
-  let(:local_domain) { ENV['LOCAL_DOMAIN'] }
+  let(:local_domain) { Rails.configuration.x.local_domain }
 
   before do
     as_a_logged_in_user

From 91b6502d827e1eea28e18d30930c2b31be89cdd7 Mon Sep 17 00:00:00 2001
From: Shlee <github@shl.ee>
Date: Mon, 3 Jun 2024 19:16:11 +1000
Subject: [PATCH 003/133] Revert "Revert "Change default ruby version to 3.3.2
 (#30478)"" (#30516)

---
 Dockerfile | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index 4278242bc..c90d5dc98 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,22 +7,22 @@
 ARG TARGETPLATFORM=${TARGETPLATFORM}
 ARG BUILDPLATFORM=${BUILDPLATFORM}
 
-# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.1"]
-ARG RUBY_VERSION="3.3.1"
+# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"]
+ARG RUBY_VERSION="3.3.2"
 # # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
 ARG NODE_MAJOR_VERSION="20"
 # Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
 ARG DEBIAN_VERSION="bookworm"
 # Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
 FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim as node
-# Ruby image to use for base image based on combined variables (ex: 3.3.1-slim-bookworm)
+# Ruby image to use for base image based on combined variables (ex: 3.3.x-slim-bookworm)
 FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} as ruby
 
 # Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA
 # Example: v4.2.0-nightly.2023.11.09+something
 # Overwrite existence of 'alpha.0' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"]
 ARG MASTODON_VERSION_PRERELEASE=""
-# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="something"]
+# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="pr-12345"]
 ARG MASTODON_VERSION_METADATA=""
 
 # Allow Ruby on Rails to serve static files

From d4e094987e530fb0e0fe35e57b8dd00bb919a770 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Mon, 3 Jun 2024 11:16:46 -0400
Subject: [PATCH 004/133] Use bundler version 2.5.11 (#30508)

---
 Gemfile.lock | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index c62c57dcc..8bf6f68ad 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1050,7 +1050,7 @@ DEPENDENCIES
   xorcist (~> 1.1)
 
 RUBY VERSION
-   ruby 3.3.1p55
+   ruby 3.3.2p78
 
 BUNDLED WITH
-   2.5.9
+   2.5.11

From 9ab7e66caeb68df565b47f024ad69a7d29a49e8c Mon Sep 17 00:00:00 2001
From: Nick Schonning <nschonni@gmail.com>
Date: Tue, 4 Jun 2024 13:29:08 -0400
Subject: [PATCH 005/133] Cleanup leftover Nanobox references (#30550)

---
 .nanoignore | 19 -------------------
 README.md   |  2 +-
 2 files changed, 1 insertion(+), 20 deletions(-)
 delete mode 100644 .nanoignore

diff --git a/.nanoignore b/.nanoignore
deleted file mode 100644
index 80e939703..000000000
--- a/.nanoignore
+++ /dev/null
@@ -1,19 +0,0 @@
-.DS_Store
-.git/
-.gitignore
-
-.bundle/
-.cache/
-config/deploy/*
-coverage
-docs/
-.env
-log/*.log
-neo4j/
-node_modules/
-public/assets/
-public/system/
-spec/
-tmp/
-.vagrant/
-vendor/bundle/
diff --git a/README.md b/README.md
index 0353a4c67..b8ee3f5db 100644
--- a/README.md
+++ b/README.md
@@ -72,7 +72,7 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre
 - **Ruby** 3.1+
 - **Node.js** 18+
 
-The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
+The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, and **Scalingo**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation.
 
 ## Development
 

From 7d9a8c959692fb7121d154412312a7f8e3005186 Mon Sep 17 00:00:00 2001
From: Michael Stanclift <mx@vmstan.com>
Date: Tue, 4 Jun 2024 12:30:22 -0500
Subject: [PATCH 006/133] Set Devcontainer to Ruby 3.3 Bookworm (#30548)

---
 .devcontainer/Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index b5e72a097..994a41d05 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,5 +1,5 @@
 # For details, see https://github.com/devcontainers/images/tree/main/src/ruby
-FROM mcr.microsoft.com/devcontainers/ruby:1-3.2-bullseye
+FROM mcr.microsoft.com/devcontainers/ruby:1-3.3-bookworm
 
 # Install Rails
 # RUN gem install rails webdrivers

From e5984c95ebdae71b8323dd0c984cb7096d7f3d75 Mon Sep 17 00:00:00 2001
From: Filippo Giunchedi <filippog@users.noreply.github.com>
Date: Tue, 4 Jun 2024 22:28:05 +0200
Subject: [PATCH 007/133] Add libvirt provider parameters to Vagrant (#28102)

Co-authored-by: Filippo Giunchedi <filippo@debian.org>
---
 Vagrantfile | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Vagrantfile b/Vagrantfile
index 8a95e91f3..89f5536ed 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -151,6 +151,12 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
     vb.customize ["modifyvm", :id, "--nictype2", "virtio"]
   end
 
+  config.vm.provider :libvirt do |libvirt|
+    libvirt.cpus = 3
+    libvirt.memory = 8192
+  end
+
+
   # This uses the vagrant-hostsupdater plugin, and lets you
   # access the development site at http://mastodon.local.
   # If you change it, also change it in .env.vagrant before provisioning

From f40f3cb82e2bf18670e25a1d1675a6f8a202257d Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 5 Jun 2024 09:22:02 +0200
Subject: [PATCH 008/133] chore(deps): update dependency rails to v7.1.3.4
 (#30551)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 Gemfile.lock | 114 +++++++++++++++++++++++++--------------------------
 1 file changed, 57 insertions(+), 57 deletions(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index 8bf6f68ad..fadc2bc82 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -10,35 +10,35 @@ GIT
 GEM
   remote: https://rubygems.org/
   specs:
-    actioncable (7.1.3.3)
-      actionpack (= 7.1.3.3)
-      activesupport (= 7.1.3.3)
+    actioncable (7.1.3.4)
+      actionpack (= 7.1.3.4)
+      activesupport (= 7.1.3.4)
       nio4r (~> 2.0)
       websocket-driver (>= 0.6.1)
       zeitwerk (~> 2.6)
-    actionmailbox (7.1.3.3)
-      actionpack (= 7.1.3.3)
-      activejob (= 7.1.3.3)
-      activerecord (= 7.1.3.3)
-      activestorage (= 7.1.3.3)
-      activesupport (= 7.1.3.3)
+    actionmailbox (7.1.3.4)
+      actionpack (= 7.1.3.4)
+      activejob (= 7.1.3.4)
+      activerecord (= 7.1.3.4)
+      activestorage (= 7.1.3.4)
+      activesupport (= 7.1.3.4)
       mail (>= 2.7.1)
       net-imap
       net-pop
       net-smtp
-    actionmailer (7.1.3.3)
-      actionpack (= 7.1.3.3)
-      actionview (= 7.1.3.3)
-      activejob (= 7.1.3.3)
-      activesupport (= 7.1.3.3)
+    actionmailer (7.1.3.4)
+      actionpack (= 7.1.3.4)
+      actionview (= 7.1.3.4)
+      activejob (= 7.1.3.4)
+      activesupport (= 7.1.3.4)
       mail (~> 2.5, >= 2.5.4)
       net-imap
       net-pop
       net-smtp
       rails-dom-testing (~> 2.2)
-    actionpack (7.1.3.3)
-      actionview (= 7.1.3.3)
-      activesupport (= 7.1.3.3)
+    actionpack (7.1.3.4)
+      actionview (= 7.1.3.4)
+      activesupport (= 7.1.3.4)
       nokogiri (>= 1.8.5)
       racc
       rack (>= 2.2.4)
@@ -46,15 +46,15 @@ GEM
       rack-test (>= 0.6.3)
       rails-dom-testing (~> 2.2)
       rails-html-sanitizer (~> 1.6)
-    actiontext (7.1.3.3)
-      actionpack (= 7.1.3.3)
-      activerecord (= 7.1.3.3)
-      activestorage (= 7.1.3.3)
-      activesupport (= 7.1.3.3)
+    actiontext (7.1.3.4)
+      actionpack (= 7.1.3.4)
+      activerecord (= 7.1.3.4)
+      activestorage (= 7.1.3.4)
+      activesupport (= 7.1.3.4)
       globalid (>= 0.6.0)
       nokogiri (>= 1.8.5)
-    actionview (7.1.3.3)
-      activesupport (= 7.1.3.3)
+    actionview (7.1.3.4)
+      activesupport (= 7.1.3.4)
       builder (~> 3.1)
       erubi (~> 1.11)
       rails-dom-testing (~> 2.2)
@@ -64,22 +64,22 @@ GEM
       activemodel (>= 4.1)
       case_transform (>= 0.2)
       jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
-    activejob (7.1.3.3)
-      activesupport (= 7.1.3.3)
+    activejob (7.1.3.4)
+      activesupport (= 7.1.3.4)
       globalid (>= 0.3.6)
-    activemodel (7.1.3.3)
-      activesupport (= 7.1.3.3)
-    activerecord (7.1.3.3)
-      activemodel (= 7.1.3.3)
-      activesupport (= 7.1.3.3)
+    activemodel (7.1.3.4)
+      activesupport (= 7.1.3.4)
+    activerecord (7.1.3.4)
+      activemodel (= 7.1.3.4)
+      activesupport (= 7.1.3.4)
       timeout (>= 0.4.0)
-    activestorage (7.1.3.3)
-      actionpack (= 7.1.3.3)
-      activejob (= 7.1.3.3)
-      activerecord (= 7.1.3.3)
-      activesupport (= 7.1.3.3)
+    activestorage (7.1.3.4)
+      actionpack (= 7.1.3.4)
+      activejob (= 7.1.3.4)
+      activerecord (= 7.1.3.4)
+      activesupport (= 7.1.3.4)
       marcel (~> 1.0)
-    activesupport (7.1.3.3)
+    activesupport (7.1.3.4)
       base64
       bigdecimal
       concurrent-ruby (~> 1.0, >= 1.0.2)
@@ -424,7 +424,7 @@ GEM
       mime-types-data (~> 3.2015)
     mime-types-data (3.2024.0507)
     mini_mime (1.1.5)
-    mini_portile2 (2.8.6)
+    mini_portile2 (2.8.7)
     minitest (5.23.1)
     msgpack (1.7.2)
     multi_json (1.15.0)
@@ -434,7 +434,7 @@ GEM
       uri
     net-http-persistent (4.0.2)
       connection_pool (~> 2.2)
-    net-imap (0.4.11)
+    net-imap (0.4.12)
       date
       net-protocol
     net-ldap (0.19.0)
@@ -634,20 +634,20 @@ GEM
     rackup (1.0.0)
       rack (< 3)
       webrick
-    rails (7.1.3.3)
-      actioncable (= 7.1.3.3)
-      actionmailbox (= 7.1.3.3)
-      actionmailer (= 7.1.3.3)
-      actionpack (= 7.1.3.3)
-      actiontext (= 7.1.3.3)
-      actionview (= 7.1.3.3)
-      activejob (= 7.1.3.3)
-      activemodel (= 7.1.3.3)
-      activerecord (= 7.1.3.3)
-      activestorage (= 7.1.3.3)
-      activesupport (= 7.1.3.3)
+    rails (7.1.3.4)
+      actioncable (= 7.1.3.4)
+      actionmailbox (= 7.1.3.4)
+      actionmailer (= 7.1.3.4)
+      actionpack (= 7.1.3.4)
+      actiontext (= 7.1.3.4)
+      actionview (= 7.1.3.4)
+      activejob (= 7.1.3.4)
+      activemodel (= 7.1.3.4)
+      activerecord (= 7.1.3.4)
+      activestorage (= 7.1.3.4)
+      activesupport (= 7.1.3.4)
       bundler (>= 1.15.0)
-      railties (= 7.1.3.3)
+      railties (= 7.1.3.4)
     rails-controller-testing (1.0.5)
       actionpack (>= 5.0.1.rc1)
       actionview (>= 5.0.1.rc1)
@@ -662,9 +662,9 @@ GEM
     rails-i18n (7.0.9)
       i18n (>= 0.7, < 2)
       railties (>= 6.0.0, < 8)
-    railties (7.1.3.3)
-      actionpack (= 7.1.3.3)
-      activesupport (= 7.1.3.3)
+    railties (7.1.3.4)
+      actionpack (= 7.1.3.4)
+      activesupport (= 7.1.3.4)
       irb
       rackup (>= 1.0.0)
       rake (>= 12.2)
@@ -686,7 +686,7 @@ GEM
     redlock (1.3.2)
       redis (>= 3.0.0, < 6.0)
     regexp_parser (2.9.2)
-    reline (0.5.7)
+    reline (0.5.8)
       io-console (~> 0.5)
     request_store (1.6.0)
       rack (>= 1.4)
@@ -895,7 +895,7 @@ GEM
     xorcist (1.1.3)
     xpath (3.2.0)
       nokogiri (~> 1.8)
-    zeitwerk (2.6.14)
+    zeitwerk (2.6.15)
 
 PLATFORMS
   ruby

From 1d3b75d124ddcf7bf1148a2a0cacc0b8f82d6340 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 5 Jun 2024 07:22:58 +0000
Subject: [PATCH 009/133] fix(deps): update dependency pg to v8.12.0 (#30549)

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 162f0cb35..bad4fa01e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12941,8 +12941,8 @@ __metadata:
   linkType: hard
 
 "pg@npm:^8.5.0":
-  version: 8.11.5
-  resolution: "pg@npm:8.11.5"
+  version: 8.12.0
+  resolution: "pg@npm:8.12.0"
   dependencies:
     pg-cloudflare: "npm:^1.1.1"
     pg-connection-string: "npm:^2.6.4"
@@ -12958,7 +12958,7 @@ __metadata:
   peerDependenciesMeta:
     pg-native:
       optional: true
-  checksum: 10c0/20f29a41a99bad5931faf4d4a01e7be7fb27e5b5338fdfb06d2368e295c3d3d4ef49958ad57d2b17bad108e5c84574db6244ed8221e6b77a767f64ef12564119
+  checksum: 10c0/973e49b5e7327c42fc62806efa8c824159ab7a0b676cefe6eeb51a59b6e226587911ec27697f36c18d69e58a7f4f0b76d0829364087194d13ed431ab7c9c417a
   languageName: node
   linkType: hard
 

From f3893ae65de540fac9a6aed5bd6bc50affaf5d26 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 5 Jun 2024 07:23:19 +0000
Subject: [PATCH 010/133] chore(deps): update dependency rubocop-rspec to
 v2.30.0 (#30529)

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 fadc2bc82..597005b6b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -751,7 +751,7 @@ GEM
       rack (>= 1.1)
       rubocop (>= 1.33.0, < 2.0)
       rubocop-ast (>= 1.31.1, < 2.0)
-    rubocop-rspec (2.29.2)
+    rubocop-rspec (2.30.0)
       rubocop (~> 1.40)
       rubocop-capybara (~> 2.17)
       rubocop-factory_bot (~> 2.22)

From e4e3875452c13dd76d9aee5e33e7d1e59629a1ff Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 5 Jun 2024 09:52:25 +0200
Subject: [PATCH 011/133] New Crowdin Translations (automated) (#30543)

Co-authored-by: GitHub Actions <noreply@github.com>
---
 app/javascript/mastodon/locales/ia.json |  16 +-
 app/javascript/mastodon/locales/ko.json |   2 +-
 app/javascript/mastodon/locales/lt.json |  11 +-
 config/locales/ca.yml                   |   2 +-
 config/locales/doorkeeper.ia.yml        |   4 +-
 config/locales/ia.yml                   | 876 ++++++++++++------------
 config/locales/lt.yml                   |   5 +-
 config/locales/simple_form.ia.yml       |  52 +-
 config/locales/simple_form.lv.yml       |   1 +
 9 files changed, 491 insertions(+), 478 deletions(-)

diff --git a/app/javascript/mastodon/locales/ia.json b/app/javascript/mastodon/locales/ia.json
index 4bcf4c88b..ce4e89e99 100644
--- a/app/javascript/mastodon/locales/ia.json
+++ b/app/javascript/mastodon/locales/ia.json
@@ -58,7 +58,7 @@
   "account.open_original_page": "Aperir le pagina original",
   "account.posts": "Messages",
   "account.posts_with_replies": "Messages e responsas",
-  "account.report": "Signalar @{name}",
+  "account.report": "Reportar @{name}",
   "account.requested": "Attendente le approbation. Clicca pro cancellar le requesta de sequer",
   "account.requested_follow": "{name} ha requestate de sequer te",
   "account.share": "Compartir profilo de @{name}",
@@ -274,7 +274,7 @@
   "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.",
   "error.unexpected_crash.next_steps_addons": "Tenta disactivar istes e 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.",
   "errors.unexpected_crash.copy_stacktrace": "Copiar le traciamento del pila al area de transferentia",
-  "errors.unexpected_crash.report_issue": "Signalar un defecto",
+  "errors.unexpected_crash.report_issue": "Reportar problema",
   "explore.search_results": "Resultatos de recerca",
   "explore.suggested_follows": "Personas",
   "explore.title": "Explorar",
@@ -468,7 +468,7 @@
   "navigation_bar.search": "Cercar",
   "navigation_bar.security": "Securitate",
   "not_signed_in_indicator.not_signed_in": "Es necessari aperir session pro acceder a iste ressource.",
-  "notification.admin.report": "{name} ha signalate {target}",
+  "notification.admin.report": "{name} ha reportate {target}",
   "notification.admin.sign_up": "{name} se ha inscribite",
   "notification.favourite": "{name} ha marcate tu message como favorite",
   "notification.follow": "{name} te ha sequite",
@@ -499,7 +499,7 @@
   "notification_requests.title": "Notificationes filtrate",
   "notifications.clear": "Rader notificationes",
   "notifications.clear_confirmation": "Es tu secur que tu vole rader permanentemente tote tu notificationes?",
-  "notifications.column_settings.admin.report": "Nove signalationes:",
+  "notifications.column_settings.admin.report": "Nove reportos:",
   "notifications.column_settings.admin.sign_up": "Nove inscriptiones:",
   "notifications.column_settings.alert": "Notificationes de scriptorio",
   "notifications.column_settings.favourite": "Favorites:",
@@ -636,7 +636,7 @@
   "report.close": "Facite",
   "report.comment.title": "Ha il altere cosas que nos deberea saper?",
   "report.forward": "Reinviar a {target}",
-  "report.forward_hint": "Le conto es de un altere servitor. Inviar un copia anonymisate del signalation a illo tamben?",
+  "report.forward_hint": "Le conto es de un altere servitor. Inviar un copia anonymisate del reporto a illo tamben?",
   "report.mute": "Silentiar",
   "report.mute_explanation": "Tu non videra le messages de iste persona. Ille pote totevia sequer te e vider tu messages e non sapera de esser silentiate.",
   "report.next": "Sequente",
@@ -656,11 +656,11 @@
   "report.statuses.subtitle": "Selige tote le responsas appropriate",
   "report.statuses.title": "Existe alcun messages que appoia iste reporto?",
   "report.submit": "Submitter",
-  "report.target": "Signalamento de {target}",
+  "report.target": "Reportage de {target}",
   "report.thanks.take_action": "Ecce tu optiones pro controlar lo que tu vide sur Mastodon:",
   "report.thanks.take_action_actionable": "Durante que nos revide isto, tu pote prender mesuras contra @{name}:",
   "report.thanks.title": "Non vole vider isto?",
-  "report.thanks.title_actionable": "Gratias pro signalar, nos investigara isto.",
+  "report.thanks.title_actionable": "Gratias pro reportar, nos investigara isto.",
   "report.unfollow": "Cessar de sequer @{name}",
   "report.unfollow_explanation": "Tu seque iste conto. Pro non plus vider su messages in tu fluxo de initio, cessa de sequer lo.",
   "report_notification.attached_statuses": "{count, plural, one {{count} message} other {{count} messages}} annexate",
@@ -747,7 +747,7 @@
   "status.replied_to": "Respondite a {name}",
   "status.reply": "Responder",
   "status.replyAll": "Responder al discussion",
-  "status.report": "Signalar @{name}",
+  "status.report": "Reportar @{name}",
   "status.sensitive_warning": "Contento sensibile",
   "status.share": "Compartir",
   "status.show_filter_reason": "Monstrar in omne caso",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index 7cd74fa50..277a87fe3 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -414,7 +414,7 @@
   "limited_account_hint.action": "그래도 프로필 보기",
   "limited_account_hint.title": "이 프로필은 {domain}의 중재자에 의해 숨겨진 상태입니다.",
   "link_preview.author": "{name}",
-  "link_preview.more_from_author": "{name} 더 둘러보기",
+  "link_preview.more_from_author": "{name} 프로필 보기",
   "lists.account.add": "리스트에 추가",
   "lists.account.remove": "리스트에서 제거",
   "lists.delete": "리스트 삭제",
diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json
index 40541b375..307230036 100644
--- a/app/javascript/mastodon/locales/lt.json
+++ b/app/javascript/mastodon/locales/lt.json
@@ -217,7 +217,7 @@
   "domain_block_modal.title": "Blokuoti domeną?",
   "domain_block_modal.you_will_lose_followers": "Visi tavo sekėjai iš šio serverio bus pašalinti.",
   "domain_block_modal.you_wont_see_posts": "Nematysi naudotojų įrašų ar pranešimų šiame serveryje.",
-  "domain_pill.activitypub_lets_connect": "Tai leidžia tau sąveikauti su žmonėmis ne tik Mastodon, bet ir įvairiose socialinėse programėlėse.",
+  "domain_pill.activitypub_lets_connect": "Tai leidžia tau prisijungti ir bendrauti su žmonėmis ne tik Mastodon, bet ir įvairiose socialinėse programėlėse.",
   "domain_pill.activitypub_like_language": "ActivityPub – tai tarsi kalba, kuria Mastodon kalba su kitais socialiniais tinklais.",
   "domain_pill.server": "Serveris",
   "domain_pill.their_handle": "Jų socialinis medijos vardas:",
@@ -433,7 +433,15 @@
   "loading_indicator.label": "Kraunama…",
   "media_gallery.toggle_visible": "{number, plural, one {Slėpti vaizdą} few {Slėpti vaizdus} many {Slėpti vaizdo} other {Slėpti vaizdų}}",
   "moved_to_account_banner.text": "Tavo paskyra {disabledAccount} šiuo metu išjungta, nes persikėlei į {movedToAccount}.",
+  "mute_modal.hide_from_notifications": "Slėpti nuo pranešimų",
+  "mute_modal.hide_options": "Slėpti parinktis",
+  "mute_modal.indefinite": "Kol atšauksiu jų nutildymą",
   "mute_modal.show_options": "Rodyti parinktis",
+  "mute_modal.they_can_mention_and_follow": "Jie gali tave paminėti ir sekti, bet tu jų nematysi.",
+  "mute_modal.they_wont_know": "Jie nežinos, kad buvo nutildyti.",
+  "mute_modal.title": "Nutildyti naudotoją?",
+  "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.advanced_interface": "Atidaryti išplėstinę žiniatinklio sąsają",
   "navigation_bar.blocks": "Užblokuoti naudotojai",
@@ -478,6 +486,7 @@
   "notification.own_poll": "Tavo apklausa baigėsi",
   "notification.poll": "Apklausa, kurioje balsavai, pasibaigė",
   "notification.reblog": "{name} pakėlė 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.",
   "notification.status": "{name} ką tik paskelbė",
diff --git a/config/locales/ca.yml b/config/locales/ca.yml
index ec32f771e..c91ae64a7 100644
--- a/config/locales/ca.yml
+++ b/config/locales/ca.yml
@@ -466,7 +466,7 @@ ca:
       status: Estat
       suppress: Suprimeix les recomanacions de seguiment
       suppressed: Suprimit
-      title: Seguir les recomanacions
+      title: Recomanacions de comptes a seguir
       unsuppress: Restaurar les recomanacions de seguiment
     instances:
       availability:
diff --git a/config/locales/doorkeeper.ia.yml b/config/locales/doorkeeper.ia.yml
index 9c493e3d7..82a1b4b14 100644
--- a/config/locales/doorkeeper.ia.yml
+++ b/config/locales/doorkeeper.ia.yml
@@ -154,7 +154,7 @@ ia:
       admin:read:domain_blocks: leger informationes sensibile de tote le blocadas de dominio
       admin:read:email_domain_blocks: leger informationes sensibile de tote le blocadas de dominio email
       admin:read:ip_blocks: leger informationes sensibile de tote le blocadas de IP
-      admin:read:reports: leger information sensibile de tote le reportos e contos signalate
+      admin:read:reports: leger information sensibile de tote le reportos e contos reportate
       admin:write: modificar tote le datos in le servitor
       admin:write:accounts: exequer action de moderation sur contos
       admin:write:canonical_email_blocks: exequer actiones de moderation sur blocadas de email canonic
@@ -192,5 +192,5 @@ ia:
       write:media: incargar files de medios
       write:mutes: silentiar personas e conversationes
       write:notifications: rader tu notificationes
-      write:reports: signalar altere personas
+      write:reports: reportar altere personas
       write:statuses: publicar messages
diff --git a/config/locales/ia.yml b/config/locales/ia.yml
index ab1f674fd..cadc465c5 100644
--- a/config/locales/ia.yml
+++ b/config/locales/ia.yml
@@ -59,7 +59,7 @@ ia:
       destroyed_msg: Le datos de %{username} es ora in cauda pro lor imminente deletion
       disable: Gelar
       disable_sign_in_token_auth: Disactivar le authentication per token in e-mail
-      disable_two_factor_authentication: Disactivar authentication bifactorial
+      disable_two_factor_authentication: Disactivar A2F
       disabled: Gelate
       display_name: Nomine a monstrar
       domain: Dominio
@@ -105,10 +105,10 @@ ia:
       not_subscribed: Non subscribite
       pending: Attende revision
       perform_full_suspension: Suspender
-      previous_strikes: Previe admonitiones
+      previous_strikes: Sanctiones precedente
       previous_strikes_description_html:
-        one: Iste conto ha <strong>un</strong> admonition.
-        other: Iste conto ha <strong>%{count}</strong> admonitiones.
+        one: Iste conto ha <strong>un</strong> sanction.
+        other: Iste conto ha <strong>%{count}</strong> sanctiones.
       promote: Promover
       protocol: Protocollo
       public: Public
@@ -143,11 +143,11 @@ ia:
       shared_inbox_url: URL del cassa de entrata condividite
       show:
         created_reports: Reportos facite
-        targeted_reports: Signalate per alteres
+        targeted_reports: Reportate per alteres
       silence: Limitar
       silenced: Limitate
       statuses: Messages
-      strikes: Previe admonitiones
+      strikes: Sanctiones precedente
       subscribe: Subscriber
       suspend: Suspender
       suspended: Suspendite
@@ -178,21 +178,21 @@ ia:
         confirm_user: Confirmar le usator
         create_account_warning: Crear un advertimento
         create_announcement: Crear annuncio
-        create_canonical_email_block: Crear blocada de email
-        create_custom_emoji: Crear emoticone personalisate
+        create_canonical_email_block: Crear blocada de e-mail
+        create_custom_emoji: Crear emoji personalisate
         create_domain_allow: Crear permisso de dominio
         create_domain_block: Crear blocada de dominio
-        create_email_domain_block: Crear blocada de dominio email
+        create_email_domain_block: Crear blocada de dominio de e-mail
         create_ip_block: Crear un regula IP
         create_unavailable_domain: Crear dominio indisponibile
         create_user_role: Crear un rolo
         demote_user: Degradar usator
         destroy_announcement: Deler annuncio
-        destroy_canonical_email_block: Deler blocada de email
-        destroy_custom_emoji: Deler emoticone personalisate
+        destroy_canonical_email_block: Deler blocada de e-mail
+        destroy_custom_emoji: Deler emoji personalisate
         destroy_domain_allow: Deler permisso de dominio
         destroy_domain_block: Deler blocada de dominio
-        destroy_email_domain_block: Crear blocada de dominio email
+        destroy_email_domain_block: Crear blocada de dominio de e-mail
         destroy_instance: Purgar dominio
         destroy_ip_block: Deler le regula IP
         destroy_status: Deler message
@@ -234,62 +234,62 @@ ia:
         assigned_to_self_report_html: "%{name} assignava reporto %{target} a se mesme"
         change_email_user_html: "%{name} cambiava le adresse de e-mail address del usator %{target}"
         change_role_user_html: "%{name} cambiava rolo de %{target}"
-        confirm_user_html: "%{name} confirmava le adresse email del usator %{target}"
+        confirm_user_html: "%{name} confirmava le adresse de e-mail del usator %{target}"
         create_account_warning_html: "%{name} inviava un advertimento a %{target}"
         create_announcement_html: "%{name} creava un nove annuncio %{target}"
-        create_canonical_email_block_html: "%{name} blocava email con le hash %{target}"
-        create_custom_emoji_html: "%{name} cargava nove emoticone %{target}"
+        create_canonical_email_block_html: "%{name} blocava e-mail con le hash %{target}"
+        create_custom_emoji_html: "%{name} incargava le nove emoji %{target}"
         create_domain_allow_html: "%{name} permitteva federation con dominio %{target}"
         create_domain_block_html: "%{name} blocava dominio %{target}"
-        create_email_domain_block_html: "%{name} blocava dominio email %{target}"
+        create_email_domain_block_html: "%{name} blocava dominio de e-mail %{target}"
         create_ip_block_html: "%{name} creava regula pro IP %{target}"
-        create_unavailable_domain_html: "%{name} stoppava consignation a dominio %{target}"
+        create_unavailable_domain_html: "%{name} stoppava livration al dominio %{target}"
         create_user_role_html: "%{name} creava rolo de %{target}"
         demote_user_html: "%{name} degradava usator %{target}"
         destroy_announcement_html: "%{name} deleva annuncio %{target}"
-        destroy_canonical_email_block_html: "%{name} disblocava email con le hash %{target}"
+        destroy_canonical_email_block_html: "%{name} disblocava e-mail con le hash %{target}"
         destroy_custom_emoji_html: "%{name} deleva emoji %{target}"
         destroy_domain_allow_html: "%{name} impediva le federation con dominio %{target}"
         destroy_domain_block_html: "%{name} disblocava dominio %{target}"
-        destroy_email_domain_block_html: "%{name} disblocava le dominio email %{target}"
+        destroy_email_domain_block_html: "%{name} disblocava dominio de e-mail %{target}"
         destroy_instance_html: "%{name} purgava le dominio %{target}"
         destroy_ip_block_html: "%{name} deleva le regula pro IP %{target}"
-        destroy_status_html: "%{name} removeva le message de %{target}"
-        destroy_unavailable_domain_html: "%{name} resumeva le consignation al dominio %{target}"
-        destroy_user_role_html: "%{name} deleva le rolo de %{target}"
+        destroy_status_html: "%{name} removeva un message de %{target}"
+        destroy_unavailable_domain_html: "%{name} reprendeva le livration al dominio %{target}"
+        destroy_user_role_html: "%{name} deleva le rolo %{target}"
         disable_2fa_user_html: "%{name} disactivava le authentication a duo factores pro le usator %{target}"
-        disable_custom_emoji_html: "%{name} disactivava le emoticone %{target}"
-        disable_sign_in_token_auth_user_html: "%{name} disactivava authentication per testimonio via email pro %{target}"
-        disable_user_html: "%{name} disactivava le accesso pro le usator %{target}"
-        enable_custom_emoji_html: "%{name} activava le emoticone %{target}"
-        enable_sign_in_token_auth_user_html: "%{name} activava le authentication per testimonio via email pro %{target}"
-        enable_user_html: "%{name} activava le accesso pro le usator %{target}"
-        memorialize_account_html: "%{name} mutava le conto de %{target} in un pagina commemorative"
+        disable_custom_emoji_html: "%{name} disactivava le emoji %{target}"
+        disable_sign_in_token_auth_user_html: "%{name} disactivava le authentication per token de e-mail pro %{target}"
+        disable_user_html: "%{name} disactivava le apertura de session pro le usator %{target}"
+        enable_custom_emoji_html: "%{name} activava le emoji %{target}"
+        enable_sign_in_token_auth_user_html: "%{name} activava le authentication per token de e-mail pro %{target}"
+        enable_user_html: "%{name} activava le apertura de session pro le usator %{target}"
+        memorialize_account_html: "%{name} converteva le conto de %{target} in un pagina commemorative"
         promote_user_html: "%{name} promoveva le usator %{target}"
         reject_appeal_html: "%{name} refusava le appello del decision de moderation de %{target}"
         reject_user_html: "%{name} refusava le inscription de %{target}"
         remove_avatar_user_html: "%{name} removeva le avatar de %{target}"
         reopen_report_html: "%{name} reaperiva le reporto %{target}"
-        resend_user_html: "%{name} reinviava le email de confirmation pro %{target}"
+        resend_user_html: "%{name} reinviava le e-mail de confirmation pro %{target}"
         reset_password_user_html: "%{name} reinitialisava le contrasigno del usator %{target}"
         resolve_report_html: "%{name} resolveva le reporto %{target}"
-        sensitive_account_html: "%{name} marcava como sensibile le medios de %{target}"
+        sensitive_account_html: "%{name} marcava le multimedia de %{target} como sensibile"
         silence_account_html: "%{name} limitava le conto de %{target}"
         suspend_account_html: "%{name} suspendeva le conto de %{target}"
-        unassigned_report_html: "%{name} de-assignava le reporto %{target}"
-        unblock_email_account_html: "%{name} disblocava le adresse email de %{target}"
-        unsensitive_account_html: "%{name} dismarcava como sensibile le medios de %{target}"
+        unassigned_report_html: "%{name} disassignava le reporto %{target}"
+        unblock_email_account_html: "%{name} disblocava le adresse de e-mail de %{target}"
+        unsensitive_account_html: "%{name} dismarcava le multimedia de %{target} como sensibile"
         unsilence_account_html: "%{name} removeva le limite del conto de %{target}"
         unsuspend_account_html: "%{name} removeva le suspension del conto de %{target}"
         update_announcement_html: "%{name} actualisava le annuncio %{target}"
-        update_custom_emoji_html: "%{name} actualisava le emoticone %{target}"
+        update_custom_emoji_html: "%{name} actualisava le emoji %{target}"
         update_domain_block_html: "%{name} actualisava le blocada de dominio pro %{target}"
-        update_ip_block_html: "%{name} cambiava le regula pro IP %{target}"
-        update_report_html: "%{name} actualisava le reporto %{target}"
-        update_status_html: "%{name} actualisava le message per %{target}"
-        update_user_role_html: "%{name} cambiava le rolo de %{target}"
+        update_ip_block_html: "%{name} cambiava regula pro IP %{target}"
+        update_report_html: "%{name} actualisava reporto %{target}"
+        update_status_html: "%{name} actualisava message de %{target}"
+        update_user_role_html: "%{name} cambiava rolo de %{target}"
       deleted_account: conto delite
-      empty: Nulle registrationes trovate.
+      empty: Nulle registros trovate.
       filter_by_action: Filtrar per action
       filter_by_user: Filtrar per usator
       title: Registro de inspection
@@ -298,7 +298,7 @@ ia:
       edit:
         title: Modificar annuncio
       empty: Necun annuncios trovate.
-      live: Al vivo
+      live: In directo
       new:
         create: Crear annuncio
         title: Nove annuncio
@@ -316,15 +316,15 @@ ia:
       by_domain: Dominio
       copied_msg: Copia local del emoji create con successo
       copy: Copiar
-      copy_failed_msg: Impossibile crear un copia local de ille emoticone
+      copy_failed_msg: Non poteva crear un copia local de ille emoji
       create_new_category: Crear nove categoria
       created_msg: Emoji create con successo!
       delete: Deler
-      destroyed_msg: Emoticone destruite con successo destroyed!
+      destroyed_msg: Emoji destruite con successo!
       disable: Disactivar
       disabled: Disactivate
       disabled_msg: Emoji disactivate con successo
-      emoji: Emoticone
+      emoji: Emoji
       enable: Activar
       enabled: Activate
       enabled_msg: Emoji activate con successo
@@ -333,24 +333,24 @@ ia:
       listed: Listate
       new:
         title: Adder nove emoji personalisate
-      no_emoji_selected: Nulle emoticones ha essite cambiate perque nulle ha essite seligite
+      no_emoji_selected: Necun emoji ha essite cambiate perque necun ha essite seligite
       not_permitted: Tu non es autorisate a exequer iste action
       overwrite: Superscriber
-      shortcode: Via breve
+      shortcode: Codice curte
       shortcode_hint: Al minus 2 characteres, solo characteres alphanumeric e lineettas basse
       title: Emojis personalisate
       uncategorized: Sin categoria
       unlist: Non listar
       unlisted: Non listate
-      update_failed_msg: Impossibile actualisar ille emoticone
-      updated_msg: Emoticone actualisate con successo!
+      update_failed_msg: Non poteva actualisar le emoji
+      updated_msg: Emoji actualisate con successo!
       upload: Incargar
     dashboard:
       active_users: usatores active
       interactions: interactiones
-      media_storage: Immagazinage de medios
+      media_storage: Immagazinage multimedial
       new_users: nove usatores
-      opened_reports: reportos aperte
+      opened_reports: reportos aperite
       pending_appeals_html:
         one: "<strong>%{count}</strong> appello pendente"
         other: "<strong>%{count}</strong> appellos pendente"
@@ -377,7 +377,7 @@ ia:
         title: Appellos
     domain_allows:
       add_new: Permitter federation con dominio
-      created_msg: Le dominio ha essite permittite con successo pro federation
+      created_msg: Le dominio ha essite correctemente autorisate pro federation
       destroyed_msg: Le dominio ha essite prohibite pro federation
       export: Exportar
       import: Importar
@@ -390,7 +390,7 @@ ia:
         permanent_action: Disfacer le suspension non restaurara alcun datos o relation.
         preamble_html: Tu es sur le puncto de suspender <strong>%{domain}</strong> e su subdominios.
         remove_all_data: Isto removera de tu servitor tote le contento, multimedia e datos de profilo del contos de iste dominio.
-        stop_communication: Tu servitor stoppara le communication con iste servitores.
+        stop_communication: Tu servitor cessara de communicar con iste servitores.
         title: Confirmar le blocada del dominio %{domain}
         undo_relationships: Isto disfacera omne relation de sequimento inter le contos de iste servitores e illos del tue.
       created_msg: Le blocada del dominio es ora in tractamento
@@ -406,7 +406,7 @@ ia:
         hint: Le blocada del dominio non impedira le creation de entratas de conto in le base de datos, ma applicara retroactive- e automaticamente le methodos specific de moderation a iste contos.
         severity:
           desc_html: "<strong>Limitar</strong> rendera le messages del contos de iste dominio invisibile pro tote persona que non los seque. <strong>Suspender</strong> removera de tu servitor tote le contento, multimedia e datos de profilo del contos de iste dominio. Usa <strong>Necun</strong> si tu solmente vole rejectar le files multimedial."
-          noop: Nemo
+          noop: Necun
           silence: Limitar
           suspend: Suspender
         title: Nove blocada de dominio
@@ -462,11 +462,11 @@ ia:
       no_file: Necun file seligite
     follow_recommendations:
       description_html: "<strong>Le recommendationes de sequimento adjuta le nove usatores a trovar rapidemente contento interessante.</strong> Quando un usator non ha un historia sufficiente de interactiones con alteres pro formar recommendationes personalisate de sequimento, iste contos es recommendate. Illos se recalcula cata die a partir de un mixtura de contos con le plus grande numero de ingagiamentos recente e le numero de sequitores local le plus alte pro un lingua date."
-      language: Per lingua
+      language: Pro le lingua
       status: Stato
       suppress: Supprimer recommendation de sequimento
       suppressed: Supprimite
-      title: Sequer le recommendationes
+      title: Recommendationes de contos a sequer
       unsuppress: Restaurar recommendation de sequimento
     instances:
       availability:
@@ -504,7 +504,7 @@ ia:
         instance_follows_measure: lor sequitores hic
         instance_languages_dimension: Linguas principal
         instance_media_attachments_measure: annexos multimedial immagazinate
-        instance_reports_measure: signalationes sur illos
+        instance_reports_measure: reportos sur illes
         instance_statuses_measure: messages immagazinate
       delivery:
         all: Totes
@@ -517,7 +517,7 @@ ia:
       delivery_error_days: Dies de errores de livration
       delivery_error_hint: Si le livration non es possibile durante %{count} dies, illo essera automaticamente marcate como non livrabile.
       destroyed_msg: Le datos de %{domain} es ora in cauda pro deletion imminente.
-      empty: Necun dominios trovate.
+      empty: Necun dominio trovate.
       known_accounts:
         one: "%{count} conto cognoscite"
         other: "%{count} contos cognoscite"
@@ -533,7 +533,7 @@ ia:
       total_blocked_by_us: Blocate per nos
       total_followed_by_them: Sequite per illes
       total_followed_by_us: Sequite per nos
-      total_reported: Signalationes sur illes
+      total_reported: Reportos sur illes
       total_storage: Annexos multimedial
       totals_time_period_hint_html: Le totales monstrate hic infra include le datos de tote le tempore.
       unknown_instance: Iste dominio non es actualmente cognoscite sur iste servitor.
@@ -579,24 +579,24 @@ ia:
       status: Stato
       title: Repetitores
     report_notes:
-      created_msg: Nota de signalation create con successo!
-      destroyed_msg: Nota de signalation delite con successo!
+      created_msg: Nota de reporto create con successo!
+      destroyed_msg: Nota de reporto delite con successo!
     reports:
       account:
         notes:
           one: "%{count} nota"
           other: "%{count} notas"
       action_log: Registro de inspection
-      action_taken_by: Action prendite per
+      action_taken_by: Mesura prendite per
       actions:
-        delete_description_html: Le messages signalate essera delite e un admonition essera registrate pro adjutar te a prender mesuras in caso de futur infractiones proveniente del mesme conto.
-        mark_as_sensitive_description_html: Le files multimedial in le messages reportate essera marcate como sensibile e un admonition essera registrate pro adjutar te a prender mesuras in caso de futur infractiones proveniente del mesme conto.
+        delete_description_html: Le messages reportate essera delite e un sanction essera registrate pro adjutar te a prender mesuras adequate in caso de futur infractiones committite desde le mesme conto.
+        mark_as_sensitive_description_html: Le files multimedial in le messages reportate essera marcate como sensibile e un sanction essera registrate pro adjutar te a prender mesuras adequate in caso de futur infractiones committite desde le mesme conto.
         other_description_html: Vider plus optiones pro controlar le comportamento del conto e personalisar le communication al conto signalate.
-        resolve_description_html: Necun action essera prendite contra le conto signalate, necun admonition registrate, e le signalation essera claudite.
-        silence_description_html: Iste conto essera visibile solmente a qui ja lo seque o manualmente lo cerca, limitante gravemente su portata. Pote sempre esser revertite. Claude tote le signalationes contra iste conto.
-        suspend_description_html: Le conto e tote su contento essera inaccessible e finalmente delite, e interager con illo essera impossibile. Reversibile intra 30 dies. Claude tote le signalationes contra iste conto.
-      actions_description_html: Decide qual action prender pro resolver iste signalation. Si tu prende un action punitive contra le conto signalate, le persona recipera un notification in e-mail, excepte si le categoria <strong>Spam</strong> es seligite.
-      actions_description_remote_html: Decide qual action prender pro resolver iste signalation. Isto affectara solmente le maniera in que <strong>tu</strong> servitor communica con iste conto remote e gere su contento.
+        resolve_description_html: Necun mesura essera prendite contra le conto denunciate, necun sanction registrate, e le reporto essera claudite.
+        silence_description_html: Iste conto essera visibile solmente a qui ja lo seque o manualmente lo cerca, limitante gravemente su portata. Pote sempre esser revertite. Claude tote le reportos contra iste conto.
+        suspend_description_html: Le conto e tote su contento essera inaccessibile e finalmente delite, e interager con illo essera impossibile. Reversibile intra 30 dies. Claude tote le reportos contra iste conto.
+      actions_description_html: Decide qual mesura prender pro resolver iste reporto. Si tu prende un mesura punitive contra le conto reportate, le persona recipera un notification in e-mail, excepte si le categoria <strong>Spam</strong> es seligite.
+      actions_description_remote_html: Decide qual mesura prender pro resolver iste reporto. Isto affectara solmente le maniera in que <strong>tu</strong> servitor communica con iste conto remote e gere su contento.
       add_to_report: Adder plus al reporto
       already_suspended_badges:
         local: Ja suspendite sur iste servitor
@@ -604,19 +604,19 @@ ia:
       are_you_sure: Es tu secur?
       assign_to_self: Assignar a me
       assigned: Moderator assignate
-      by_target_domain: Dominio del conto signalate
+      by_target_domain: Dominio del conto reportate
       cancel: Cancellar
       category: Categoria
-      category_description_html: Le motivo pro le qual iste conto e/o contento ha essite signalate essera citate in le communication con le conto signalate
+      category_description_html: Le motivo pro le qual iste conto e/o contento ha essite reportate essera citate in le communication con le conto reportate
       comment:
         none: Necun
       comment_description_html: 'Pro fornir plus information, %{name} ha scribite:'
       confirm: Confirmar
-      confirm_action: Confirmar le action de moderation contra %{acct}
-      created_at: Signalate
+      confirm_action: Confirmar le mesura de moderation contra %{acct}
+      created_at: Reportate
       delete_and_resolve: Deler le messages
       forwarded: Reexpedite
-      forwarded_replies_explanation: Iste signalation proveni de un usator remote e concerne contento remote. Illo te ha essite reexpedite perque le contento signalate es in responsa a un usator tue.
+      forwarded_replies_explanation: Iste reporto proveni de un usator remote e concerne contento remote. Illo te ha essite reexpedite perque le contento reportate es in responsa a un usator tue.
       forwarded_to: Reexpedite a %{domain}
       mark_as_resolved: Marcar como resolvite
       mark_as_sensitive: Marcar como sensibile
@@ -627,41 +627,41 @@ ia:
         create_and_resolve: Resolver con nota
         create_and_unresolve: Reaperir con nota
         delete: Deler
-        placeholder: Describe le actiones prendite, o insere altere information pertinente...
+        placeholder: Describe le mesuras prendite, o insere altere information pertinente...
         title: Notas
-      notes_description_html: Vider e lassar notas pro altere moderatores e pro tu proprie futuro
+      notes_description_html: Vider e lassar notas a altere moderatores e a tu ego futur
       processed_msg: 'Reporto #%{id} elaborate con successo'
       quick_actions_description_html: 'Face un rapide action o rola a basso pro vider le contento reportate:'
       remote_user_placeholder: le usator remote ab %{instance}
       reopen: Reaperir reporto
       report: 'Reporto #%{id}'
-      reported_account: Conto signalate
-      reported_by: Signalate per
+      reported_account: Conto reportate
+      reported_by: Reportate per
       resolved: Resolvite
       resolved_msg: Reporto resolvite con successo!
       skip_to_actions: Saltar al actiones
       status: Stato
-      statuses: Contento signalate
-      statuses_description_html: Le contento offensive sera citate in communication con le conto reportate
+      statuses: Contento reportate
+      statuses_description_html: Le contento offensive essera citate in communication con le conto reportate
       summary:
         action_preambles:
-          delete_html: 'Tu va <strong>remover</strong> parte de messages de <strong>@%{acct}</strong>. Isto ira:'
-          mark_as_sensitive_html: 'Tu va <strong>marcar</strong> parte de messages de <strong>@%{acct}</strong> como <strong>sensibile</strong>. Isto ira:'
-          silence_html: 'Tu va <strong>limitar</strong> le conto de <strong>@%{acct}</strong>. Isto ira:'
-          suspend_html: 'Tu va <strong>limitar</strong> le conto de <strong>@%{acct}</strong>. Isto ira:'
+          delete_html: 'Tu es sur le puncto de <strong>remover</strong> alcunes del messages de <strong>@%{acct}</strong>. Isto va:'
+          mark_as_sensitive_html: 'Tu es sur le puncto de <strong>marcar</strong> alcunes del messages de <strong>@%{acct}</strong> como <strong>sensibile</strong>. Isto va:'
+          silence_html: 'Tu es sur le puncto de <strong>limitar</strong> le conto de <strong>@%{acct}</strong>. Isto va:'
+          suspend_html: 'Tu es sur le puncto de <strong>suspender</strong> le conto de <strong>@%{acct}</strong>. Isto va:'
         actions:
           delete_html: Remover le messages offensive
-          mark_as_sensitive_html: Marcar le medios de messages offensive como sensibile
+          mark_as_sensitive_html: Marcar le multimedia de messages offensive como sensibile
           silence_html: Limitar gravemente le portata de <strong>@%{acct}</strong> rendente le profilo e contento visibile solmente a qui ja lo seque o lo cerca manualmente
           suspend_html: Suspender <strong>@%{acct}</strong>, rendente le profilo e contento inaccessibile e le interaction con illo impossibile
-        close_report: Marcar le signalation №%{id} como resolvite
-        close_reports_html: Marcar <strong>tote</strong> le signalationes contra <strong>@%{acct}</strong> como resolvite
+        close_report: 'Marcar le reporto #%{id} como resolvite'
+        close_reports_html: Marcar <strong>tote</strong> le reportos contra <strong>@%{acct}</strong> como resolvite
         delete_data_html: Deler le profilo e contento de <strong>@%{acct}</strong> in 30 dies excepte si le suspension es disfacite intertanto
         preview_preamble_html: "<strong>@%{acct}</strong> recipera un advertimento con le sequente contento:"
-        record_strike_html: Registrar un admonition contra <strong>@%{acct}</strong> pro adjutar te a imponer sanctiones in caso de futur violationes de iste conto
+        record_strike_html: Registra un sanction contra <strong>@%{acct}</strong> pro adjutar te a prender mesuras adequate in caso de futur violationes committite desde iste conto
         send_email_html: Inviar un e-mail de advertimento a <strong>@%{acct}</strong>
         warning_placeholder: Motivation supplementari facultative pro le action de moderation.
-      target_origin: Origine del conto signalate
+      target_origin: Origine del conto reportate
       title: Reportos
       unassign: Disassignar
       unknown_action_msg: 'Action incognite: %{action}'
@@ -707,7 +707,7 @@ ia:
         manage_invites: Gerer le invitationes
         manage_invites_description: Permitte que usatores examina e deactiva ligamines de invitation
         manage_reports: Gerer le reportos
-        manage_reports_description: Permitte que usatores revide signalationes e exeque actiones de moderation a base de illos
+        manage_reports_description: Permitte que usatores revide reportos e prende mesuras de moderation a base de illos
         manage_roles: Gerer le rolos
         manage_roles_description: Permitte que usatores gere e assigna rolos inferior a lor privilegios actual
         manage_rules: Gerer le regulas
@@ -716,7 +716,7 @@ ia:
         manage_settings_description: Permitte que usatores cambia le parametros del sito
         manage_taxonomies: Gerer taxonomias
         manage_taxonomies_description: Permitte que usatores revide contento in tendentias e actualisa le parametros de hashtag
-        manage_user_access: Gerer le accessos de usator
+        manage_user_access: Gerer le accesso de usatores
         manage_user_access_description: Permitte que usatores disactiva le authentication bifactorial de altere usatores, cambia lor adresses de e-mail, e reinitialisa lor contrasigno
         manage_users: Gerer usatores
         manage_users_description: Permitte que usatores vide le detalios de altere usatores e exeque actiones de moderation contra illes
@@ -741,7 +741,7 @@ ia:
         manage_rules: Gerer le regulas del servitor
         preamble: Fornir information detaliate sur le functionamento, moderation e financiamento del servitor.
         rules_hint: Il ha un area dedicate al regulas que tu usatores debe acceptar.
-        title: A proposito de
+        title: A proposito
       appearance:
         preamble: Personalisar le interfacie web de Mastodon.
         title: Apparentia
@@ -756,43 +756,43 @@ ia:
         preamble: Controlar como contento generate per le usator es immagazinate in Mastodon.
         title: Retention de contento
       default_noindex:
-        desc_html: Affice tote le usatores qui non ha cambiate iste parametro per se mesme
-        title: Refusar de ordinario le indexation del usatores per le motores de recerca
+        desc_html: Affecta tote le usatores qui non ha personalmente cambiate iste parametro
+        title: Excluder le usatores del indexation del motores de recerca per predefinition
       discovery:
-        follow_recommendations: Sequer le recommendationes
-        preamble: Presentar contento interessante es instrumental in introducer nove usatores qui pote non cognoscer alcuno de Mastodon.
+        follow_recommendations: Recommendationes de contos a sequer
+        preamble: Presentar contento interessante es essential pro attraher e retener nove usatores qui pote non cognoscer alcun persona sur Mastodon. Controla como varie optiones de discoperta functiona sur tu servitor.
         profile_directory: Directorio de profilos
         public_timelines: Chronologias public
         publish_discovered_servers: Publicar servitores discoperite
-        publish_statistics: Publicar statistica
-        title: Discoperi
+        publish_statistics: Publicar statisticas
+        title: Discoperta
         trends: Tendentias
       domain_blocks:
         all: A omnes
         disabled: A necuno
         users: A usators local in session
       registrations:
-        moderation_recommandation: Per favor verifica que tu ha un adequate e reactive equipa de moderation ante que tu aperi registrationes a quicunque!
+        moderation_recommandation: Per favor assecura te de haber un equipa de moderation adequate e reactive ante de aperir le inscription a omnes!
         preamble: Controla qui pote crear un conto sur tu servitor.
-        title: Registrationes
+        title: Inscriptiones
       registrations_mode:
         modes:
           approved: Approbation necessari pro le inscription
           none: Nemo pote inscriber se
           open: Quicunque pote inscriber se
-        warning_hint: Nos consilia usar “Approbation necessari pro le inscription” si tu non crede que tu equipa de moderation pote tractar spam e registrationes maligne in un modo opportun.
+        warning_hint: Nos recommenda usar “Approbation necessari pro le inscription” si tu non es secur que tu equipa de moderation pote tractar spam e inscriptiones malevolente in tempore utile.
       security:
         authorized_fetch: Require authentication ab servitores federate
         authorized_fetch_hint: Requirer authentication de servitores federate permitte un application plus stricte de blocadas a nivello de usator e de servitor. Nonobstante, isto diminue le prestationes del servitor, reduce le portata de tu responsas e pote introducer problemas de compatibilitate con certe servicios federate. In plus, isto non impedira le actores dedicate a recuperar tu messages public e tu contos.
-        authorized_fetch_overridden_hint: Tu actualmente non pote cambiar iste parametros perque il es superate per un variabile de ambiente.
+        authorized_fetch_overridden_hint: Tu actualmente non pote cambiar iste parametro perque illo es supplantate per un variabile de ambiente.
         federation_authentication: Application del authentication de federation
       title: Parametros de servitor
     site_uploads:
       delete: Deler file incargate
-      destroyed_msg: Incarga de sito delite con successo!
+      destroyed_msg: Le file incargate al sito ha essite delite!
     software_updates:
-      critical_update: Critic! Actualisa tosto
-      description: Il es recommendate de mantener actualisate tu installation de Mastodon pro beneficiar del ultime reparationes e functiones. In ultra, il es aliquando critic actualisar Mastodon in maniera opportun pro evitar problemas de securitate. Pro iste rationes, Mastodon controla pro actualisationes cata 30 minutas, e te notificara secundo tu preferentias de notificationes per email.
+      critical_update: Critic – per favor, actualisa rapidemente
+      description: Il es recommendate mantener tu installation de Mastodon actualisate pro beneficiar del ultime reparationes e functiones. In ultra, de tempore a tempore, il es de importantia critic actualisar Mastodon in tempore utile pro evitar problemas de securitate. Pro iste rationes, Mastodon verifica le presentia de actualisationes cata 30 minutas, e te notificara secundo tu preferentias de notification in e-mail.
       documentation_link: Pro saper plus
       release_notes: Notas de version
       title: Actualisationes disponibile
@@ -800,33 +800,33 @@ ia:
       types:
         major: Version major
         minor: Version minor
-        patch: 'Version de pecias: remedios de bugs e cambiamentos facile a applicar'
+        patch: 'Version corrective: remedios de bugs e cambiamentos facile a applicar'
       version: Version
     statuses:
       account: Autor
       application: Application
-      back_to_account: Retro al pagina de conto
+      back_to_account: Retornar al pagina del conto
       back_to_report: Retro al pagina de reporto
       batch:
-        remove_from_report: Remover ab reporto
+        remove_from_report: Remover del reporto
         report: Reporto
       deleted: Delite
-      favourites: Favoritos
-      history: Chronologia del versiones
-      in_reply_to: Replicante a
+      favourites: Favorites
+      history: Historia de versiones
+      in_reply_to: In responsa a
       language: Lingua
       media:
-        title: Medios
+        title: Multimedia
       metadata: Metadatos
-      no_status_selected: Nulle messages era cambiate perque necun era seligite
+      no_status_selected: Necun message ha essite cambiate perque necun ha essite seligite
       open: Aperir message
       original_status: Message original
-      reblogs: Promotiones
-      status_changed: Messages cambiate
+      reblogs: Republicationes
+      status_changed: Message cambiate
       title: Messages del conto
       trending: Tendentias
       visibility: Visibilitate
-      with_media: Con medios
+      with_media: Con multimedia
     strikes:
       actions:
         delete_statuses: "%{name} ha delite le messages de %{target}"
@@ -853,31 +853,31 @@ ia:
         message_html: Tu aggregation Elasticsearch ha plus que un nodo, ma Mastodon non es configurate a usar los.
       elasticsearch_preset_single_node:
         action: Vide documentation
-        message_html: Tu aggregation Elasticsearch ha un sol nodo, <code>ES_PRESET</code> deberea esser predefinite a <code>single_node_cluster</code>.
+        message_html: Tu aggregation Elasticsearch ha un sol nodo, <code>ES_PRESET</code> deberea esser mittite a <code>single_node_cluster</code>.
       elasticsearch_reset_chewy:
-        message_html: Le indexation de tu systema Elasticsearch es obsolete per un cambio de configuration. Per cfavor exeque <code>tootctl search deploy --reset-chewy</code> pro actualisar lo.
+        message_html: Le indexation de tu systema Elasticsearch es obsolete a causa de un cambio de parametro. Per favor exeque <code>tootctl search deploy --reset-chewy</code> pro actualisar lo.
       elasticsearch_running_check:
-        message_html: Impossibile connecter se a Elasticsearch. Verifica que illo flue, o disactiva le recerca a plen texto
+        message_html: Impossibile connecter se a Elasticsearch. Verifica que illo es active, o disactiva le recerca a plen texto
       elasticsearch_version_check:
         message_html: 'Version de Elasticsearch incompatibile: %{value}'
-        version_comparison: Elasticsearch %{running_version} es currente dum %{required_version} es necesse
+        version_comparison: Elasticsearch %{running_version} es active, ma %{required_version} es requirite
       rules_check:
         action: Gerer le regulas del servitor
-        message_html: Tu non ha definite ulle regulas de servitor.
+        message_html: Tu non ha definite alcun regula de servitor.
       sidekiq_process_check:
-        message_html: Nulle processo Sidekiq currente pro le %{value} cauda(s). Controla tu configuration de Sidekiq
+        message_html: Necun processo Sidekiq es active pro le cauda(s) %{value}. Per favor verifica tu configuration de Sidekiq
       software_version_critical_check:
         action: Vider le actualisationes disponibile
-        message_html: Un actualisation critic de Mastodon es disponibile, actualisa lo le plus rapide possibile.
+        message_html: Un actualisation critic de Mastodon es disponibile. Per favor actualisa lo le plus tosto possibile.
       software_version_patch_check:
         action: Vider le actualisationes disponibile
         message_html: Un actualisation de remedio de bug pro Mastodon es disponibile.
       upload_check_privacy_error:
-        action: Verifica hic pro plus de information
-        message_html: "<strong>Tu servitor de web es mal-configurate. Le confidentialitate de tu usatores es a risco.</strong>"
+        action: Consulta hic pro plus information
+        message_html: "<strong>Tu servitor web es mal configurate. Le confidentialitate de tu usatores es in risco.</strong>"
       upload_check_privacy_error_object_storage:
-        action: Verifica hic pro plus de information
-        message_html: "<strong>Tu immagazinage de objectos es mal-configurate. Le confidentialitate de tu usatores es a risco.</strong>"
+        action: Consulta hic pro plus information
+        message_html: "<strong>Tu immagazinage de objectos es mal configurate. Le confidentialitate de tu usatores es in risco.</strong>"
     tags:
       review: Revide le stato
       updated_msg: Parametros de hashtag actualisate con successo
@@ -885,67 +885,67 @@ ia:
     trends:
       allow: Permitter
       approved: Approbate
-      disallow: Impedir
+      disallow: Refusar
       links:
         allow: Permitter ligamine
-        allow_provider: Permitter editor
-        description_html: Istos es ligamines que es actualmente multo compartite per contos de que tu servitor vide messages. Illo pote adjutar tu usatores a discoperir lo que eveni in le mundo. Nulle ligamines es monstrate publicamente usque tu non approba le editor. Tu alsi pote permitter o rejectar ligamines singule.
-        disallow: Impedir ligamine
-        disallow_provider: Impedir editor
-        no_link_selected: Nulle ligamine era cambiate perque nulle era seligite
+        allow_provider: Autorisar le publicator
+        description_html: Istes es ligamines que es actualmente compartite multo per contos del quales tu servitor recipe messages. Illos pote adjutar tu usatores a discoperir lo que eveni in le mundo. Necun ligamine es monstrate publicamente usque tu autorisa le publicator. Tu pote tamben permitter o rejectar ligamines singule.
+        disallow: Prohibir le ligamine
+        disallow_provider: Prohibir le publicator
+        no_link_selected: Necun ligamine ha essite cambiate perque necun ha essite seligite
         publishers:
-          no_publisher_selected: Nulle editores era cambiate perque nemo era seligite
+          no_publisher_selected: Necun publicator ha essite cambiate perque necun ha essite seligite
         shared_by_over_week:
           one: Compartite per un persona le septimana passate
           other: Compartite per %{count} personas le septimana passate
-        title: Ligamines de tendentia
-        usage_comparison: Compartite %{today} vices hodie, comparate al %{yesterday} de heri
-      not_allowed_to_trend: Non permittite haber tendentia
-      only_allowed: Solo permittite
+        title: Ligamines in tendentia
+        usage_comparison: Compartite %{today} vices hodie, in comparation con le %{yesterday} de heri
+      not_allowed_to_trend: Non autorisate a apparer in tendentias
+      only_allowed: Solo permittites
       pending_review: Attende revision
       preview_card_providers:
-        allowed: Ligamines ab iste editor pote haber tendentia
-        description_html: Il ha dominios ab que ligamines es sovente compartite sur tu servitor. Ligamines non habera publicamente tendentia salvo que le dominio del ligamine es approbate. Tu approbation (o rejection) se extende al sub-dominios.
-        rejected: Ligamines ab iste editor non habera tendentia
-        title: Editores
+        allowed: Ligamines de iste publicator pote apparer in tendentias
+        description_html: Istes es le dominios del quales le ligamines es frequentemente compartite sur tu servitor. Le ligamines solmente apparera in le tendentias public si le dominio del ligamine es approbate. Tu approbation (o rejection) se extende al subdominios.
+        rejected: Ligamines de iste publicator non apparera in tendentias
+        title: Publicatores
       rejected: Rejectate
       statuses:
         allow: Permitter message
         allow_account: Permitter autor
-        description_html: Istos es messages que tu servitor cognosce perque illos es al momento multo compartite e favorite. Isto pote adjutar tu nove e renovate usatores a trovar altere personas a sequer. Nulle messages es monstrate publicamente usque tu approba le autor, e le autor permitte que su conto es suggerite a alteres. Tu alsi pote permitter o rejectar messages singule.
-        disallow: Impedir message
-        disallow_account: Impedir autor
-        no_status_selected: Nulle messages era cambiate perque nulle era seligite
-        not_discoverable: Le autor non ha optate pro esser detectabile
+        description_html: Istes es le messages cognoscite sur tu servitor que al momento es multo compartite e marcate como favorite. Illos pote adjutar tu usatores nove e reveniente a trovar plus personas a sequer. Necun message es monstrate publicamente usque tu approba le autor, a condition que le autor permitte que su conto es suggerite a alteres. Tu pote tamben permitter o rejectar messages singule.
+        disallow: Non permitter message
+        disallow_account: Non permitter autor
+        no_status_selected: Necun message in tendentia ha essite cambiate perque necun ha essite seligite
+        not_discoverable: Le autor non ha optate pro esser discoperibile
         shared_by:
-          one: Compartite e favorite un tempore
-          other: Compartite e favorite %{friendly_count} tempores
-        title: Messages de tendentia
+          one: Compartite o marcate como favorite un vice
+          other: Compartite o marcate como favorite %{friendly_count} vices
+        title: Messages in tendentia
       tags:
-        current_score: Punctuage actual %{score}
+        current_score: Score actual %{score}
         dashboard:
           tag_accounts_measure: usos unic
           tag_languages_dimension: Linguas principal
           tag_servers_dimension: Servitores principal
           tag_servers_measure: servitores differente
           tag_uses_measure: usos total
-        description_html: Istos es hashtags que actualmente appare in tante messages que tu servitor vide. Illo pote adjutar tu usatores a discoperir re que le personas parla plus al momento. Nulle hashtags es monstrate publicamente usque tu los approba.
+        description_html: Istes es hashtags que actualmente appare in multe messages que tu servitor vide. Illos pote adjutar tu usatores a discoperir le cosas sur le quales le gente parla le plus al momento. Nulle hashtags es monstrate publicamente usque tu los approba.
         listable: Pote esser suggerite
-        no_tag_selected: Nulle placas era cambiate perque nulle era seligite
-        not_listable: Non sera suggerite
+        no_tag_selected: Necun etiquetta ha essite cambiate perque necun ha essite seligite
+        not_listable: Non essera suggerite
         not_trendable: Non apparera sub tendentias
         not_usable: Non pote esser usate
-        peaked_on_and_decaying: Habeva un picco %{date}, ora decade
-        title: Hashtags de tendentia
+        peaked_on_and_decaying: Ha attingite su maximo le %{date}, ora in declino
+        title: Hashtags in tendentia
         trendable: Pote apparer sub tendentias
-        trending_rank: 'De tendentia #%{rank}'
+        trending_rank: 'Tendentia #%{rank}'
         usable: Pote esser usate
-        usage_comparison: Usate %{today} vices hodie, al contrario del %{yesterday} de heri
+        usage_comparison: Usate %{today} vices hodie, in comparation con le %{yesterday} de heri
         used_by_over_week:
-          one: Usate per un persona le ultime septimana
-          other: Usate per %{count} personas le ultime septimana
+          one: Usate per un persona in le ultime septimana
+          other: Usate per %{count} personas in le ultime septimana
       title: Tendentias
-      trending: Tendentias
+      trending: In tendentia
     warning_presets:
       add_new: Adder nove
       delete: Deler
@@ -953,46 +953,46 @@ ia:
       empty: Tu non ha ancora definite alcun avisos predefinite.
       title: Predefinitiones de avisos
     webhooks:
-      add_new: Adder terminal
+      add_new: Adder puncto final
       delete: Deler
-      description_html: Un <strong>croc web</strong> habilita Mastodon a transmitter <strong>notificationes in tempore real</strong> re eventos seligite pro tu pro activar application, assi tu application pote <strong>automaticamente discatenar reactiones</strong>.
+      description_html: Un <strong>webhook</strong> o puncto de ancorage web permitte a Mastodon transmitter <strong>notificationes in tempore real</strong> sur eventos seligite a tu proprie application, de maniera que tu application pote <strong>automaticamente activar reactiones</strong>.
       disable: Disactivar
       disabled: Disactivate
-      edit: Rediger terminal
-      empty: Tu ancora non ha configurate alcun punctos final de web croc.
+      edit: Rediger puncto final
+      empty: Tu ancora non ha configurate alcun punctos final de webhook.
       enable: Activar
       enabled: Active
       enabled_events:
         one: 1 evento activate
         other: "%{count} eventos activate"
       events: Eventos
-      new: Nove croc web
-      rotate_secret: Rotar secrete
-      secret: Firmante secrete
+      new: Nove webhook
+      rotate_secret: Rotar le secreto
+      secret: Secreto de signatura
       status: Stato
-      title: Crocs web
-      webhook: Crocs web
+      title: Webhooks
+      webhook: Webhook
   admin_mailer:
     auto_close_registrations:
-      body: Per un carentia recente de activate de moderator, le registrationes sur %{instance} ha essite automaticamente mutate a besoniante revision manual, pro impedir %{instance} de esser usate como un platteforma pro potential mal actores. Tu pote mutar lo retro pro sempre aperir le registrationes.
-      subject: Le registrationes pro %{instance} ha essite automaticamente mutate a besoniante de approbation
+      body: A causa de un manco de activate recente de moderatores, le parametros de inscription sur %{instance} ha essite automaticamente cambiate pro exiger un verification manual, a fin de impedir le possibile uso de %{instance} per actores malevolente. Tu pote reaperir le inscription a omne momento.
+      subject: Le inscriptiones a %{instance} ha essite automaticamente cambiate pro exiger approbation
     new_appeal:
       actions:
-        delete_statuses: pro deler lor messages
-        disable: pro gelar lor conto
-        mark_statuses_as_sensitive: pro marcar lor messages como sensibile
-        none: pro advertir
-        sensitive: a marcar lor conto como sensibile
-        silence: pro limitar lor conto
-        suspend: pro suspender lor conto
-      body: "%{target} appella un decision de moderation per %{action_taken_by} ab le %{date}, que era %{type}. Ille scribeva:"
-      next_steps: Tu pote approbar le appello a disfacer le decision de moderation, o ignorar lo.
-      subject: "%{username} appella un decision de moderation sur %{instance}"
+        delete_statuses: deler su messages
+        disable: gelar su conto
+        mark_statuses_as_sensitive: marcar su messages como sensibile
+        none: emitter un advertimento
+        sensitive: marcar su conto como sensibile
+        silence: limitar su conto
+        suspend: suspender su conto
+      body: "%{target} appella contra un decision de moderation, prendite per %{action_taken_by} le %{date}, de %{type}. Le appellante ha scribite:"
+      next_steps: Tu pote approbar le appello pro disfacer le decision de moderation, o ignorar lo.
+      subject: "%{username} appella contra un decision de moderation sur %{instance}"
     new_critical_software_updates:
-      body: Nove versiones critic de Mastodon ha essite publicate, tu poterea voler actualisar al plus tosto possibile!
+      body: Nove versiones critic de Mastodon ha essite publicate, per favor considera actualisar le plus tosto possibile!
       subject: Actualisationes critic de Mastodon es disponibile pro %{instance}!
     new_pending_account:
-      body: Le detalios del nove conto es infra.
+      body: Le detalios del nove conto es infra. Tu pote approbar o refusar iste demanda.
       subject: Nove conto preste a revider sur %{instance} (%{username})
     new_report:
       body: "%{reporter} ha reportate %{target}"
@@ -1002,30 +1002,30 @@ ia:
       body: Nove versiones de Mastodon ha essite publicate, tu poterea voler actualisar!
       subject: Nove versiones de Mastodon es disponibile pro %{instance}!
     new_trends:
-      body: 'Le sequente elementos besoniar de un recension ante que illos pote esser monstrate publicamente:'
+      body: 'Le sequente entratas require un revision ante que illos pote esser monstrate publicamente:'
       new_trending_links:
-        title: Ligamines de tendentia
+        title: Ligamines in tendentia
       new_trending_statuses:
-        title: Messages de tendentia
+        title: Messages in tendentia
       new_trending_tags:
-        title: Hashtags de tendentia
-      subject: Nove tendentias pro recenser sur %{instance}
+        title: Hashtags in tendentia
+      subject: Nove tendentias a revider sur %{instance}
   aliases:
     add_new: Crear alias
-    created_msg: Create con successo un nove alias. Ora tu pote initiar le motion ab le vetere conto.
-    deleted_msg: Removite con successo le alias. Mover de ille conto a isto non sera plus possibile.
+    created_msg: Le nove alias ha essite create. Ora tu pote initiar le migration desde le conto ancian.
+    deleted_msg: Le alias ha essite removite. Non essera plus possibile migrar de ille conto a iste.
     empty: Tu non ha aliases.
-    hint_html: Si tu desira mover ab un altere conto a isto, ci tu pote crear un alias, que es requirite ante que tu pote continuar con mover sequaces ab le vetere conto a isto. Iste action per se mesme es <strong>innocue e reversibile</strong>. <strong>Le migration de conto es initiate ab le vetere conto</strong>.
+    hint_html: Si tu vole migrar de un altere conto a iste, tu pote crear un alias ci, que es necessari pro poter transferer le sequitores del conto ancian a iste. Iste action per se es <strong>innocue e reversibile</strong>. <strong>Le migration del conto es initiate desde le conto ancian</strong>.
     remove: Disligar alias
   appearance:
     advanced_web_interface: Interfacie web avantiate
-    advanced_web_interface_hint: 'Si tu desira facer uso de tu integre largessa de schermo, le interfacie web avantiate te permitte de configurar plure columnas differente pro vider al mesme tempore tante informationes como tu vole: pagina principal, notificationes, chronogramma federate, ulle numero de listas e hashtags.'
+    advanced_web_interface_hint: 'Si tu vole utilisar tote le largessa de tu schermo, le interfacie web avantiate te permitte configurar multe columnas differente pro vider al mesme tempore tante informationes como tu vole: pagina principal, notificationes, chronologia federate, un numero illimitate de listas e hashtags.'
     animations_and_accessibility: Animationes e accessibilitate
     confirmation_dialogs: Dialogos de confirmation
     discovery: Discoperta
     localization:
       body: Mastodon es traducite per voluntarios.
-      guide_link: https://crowdin.com/project/mastodon
+      guide_link: https://crowdin.com/project/mastodon/ia
       guide_link_text: Totes pote contribuer.
     sensitive_content: Contento sensibile
   application_mailer:
@@ -1033,110 +1033,110 @@ ia:
     salutation: "%{name},"
     settings: 'Cambiar preferentias de e-mail: %{link}'
     unsubscribe: Desubscriber
-    view: 'Vider:'
+    view: 'Visita:'
     view_profile: Vider profilo
     view_status: Vider message
   applications:
     created: Application create con successo
     destroyed: Application delite con successo
-    logout: Clauder le session
-    regenerate_token: Regenerar testimonio de accesso
-    token_regenerated: Testimonio de accesso regenerate con successo
-    warning: Sia multo attente con iste datos. Jammais compartir los con quicunque!
-    your_token: Tu testimonio de accesso
+    logout: Clauder session
+    regenerate_token: Regenerar token de accesso
+    token_regenerated: Le token de accesso ha essite regenerate
+    warning: Sia multo prudente con iste datos. Non comparti los jammais con alcuno!
+    your_token: Tu token de accesso
   auth:
-    apply_for_account: Peter un conto
+    apply_for_account: Requestar un conto
     captcha_confirmation:
       help_html: Si tu ha problemas a solver le CAPTCHA, tu pote contactar nos per %{email} e nos pote assister te.
-      hint_html: Justo un altere cosa! Nos debe confirmar que tu es un human (isto es assi proque nos pote mantener foras le spam!). Solve le CAPTCHA infra e clicca "Continuar".
+      hint_html: Solo un altere cosa! Nos debe confirmar que tu es un humano (de sorta que nos pote mantener le spam foras!). Solve le CAPTCHA infra e clicca sur "Continuar".
       title: Controlo de securitate
     confirmations:
-      awaiting_review: Tu adresse email es confirmate! Le personal de %{domain} ora revide tu registration. Tu recipera un email si illes approba tu conto!
-      awaiting_review_title: Tu registration es revidite
-      clicking_this_link: cliccante iste ligamine
-      login_link: acceder
-      proceed_to_login_html: Ora tu pote continuar a %{login_link}.
-      redirect_to_app_html: Tu deberea haber essite re-dirigite al app <strong>%{app_name}</strong>. Si isto non eveni, tenta %{clicking_this_link} o manualmente retorna al app.
-      registration_complete: Tu registration sur %{domain} es ora complete!
+      awaiting_review: Tu adresse de e-mail es confirmate! Le personal de %{domain} ora revide tu registration. Tu recipera un e-mail si illes approba tu conto!
+      awaiting_review_title: Tu inscription es in curso de revision
+      clicking_this_link: cliccar sur iste ligamine
+      login_link: aperir session
+      proceed_to_login_html: Ora tu pote %{login_link}.
+      redirect_to_app_html: Tu deberea haber essite redirigite al app <strong>%{app_name}</strong>. Si isto non ha evenite, tenta %{clicking_this_link} o retornar manualmente al app.
+      registration_complete: Tu inscription sur %{domain} es ora concludite!
       welcome_title: Benvenite, %{name}!
-      wrong_email_hint: Si ille adresse email non es correcte, tu pote cambiar lo in parametros de conto.
+      wrong_email_hint: Si ille adresse de e-mail non es correcte, tu pote cambiar lo in le parametros del conto.
     delete_account: Deler le conto
-    delete_account_html: Si tu vole a dele tu conto, tu pote <a href="%{path}">continuar ci</a>. Te sera demandate confirmation.
+    delete_account_html: Si tu vole deler tu conto, tu pote <a href="%{path}">facer lo hic</a>. Te essera demandate un confirmation.
     description:
       prefix_invited_by_user: "@%{name} te invita a junger te a iste servitor de Mastodon!"
       prefix_sign_up: Inscribe te sur Mastodon hodie!
-      suffix: Con un conto, tu potera sequer personas, messages de actualisation e excambios de messages con usatores de ulle servitor de Mastodon e plus!
+      suffix: Con un conto, tu potera sequer personas, publicar tu pensatas e excambiar messages con usatores de qualcunque servitor de Mastodon e multo plus!
     didnt_get_confirmation: Non recipeva tu un ligamine de confirmation?
     dont_have_your_security_key: Non ha tu le clave de securitate?
     forgot_password: Contrasigno oblidate?
-    invalid_reset_password_token: Pete un nove.
-    link_to_otp: Insere un codice a duo factores o un codice de recuperation ab tu telephono
+    invalid_reset_password_token: Le token pro reinitialisar le contrasigno non es valide o ha expirate. Per favor requesta un nove.
+    link_to_otp: Insere un codice a duo factores de tu telephono o un codice de recuperation
     link_to_webauth: Usa tu apparato clave de securitate
-    log_in_with: Accede con
-    login: Accede
-    logout: Clauder le session
-    migrate_account: Move a un conto differente
-    migrate_account_html: Si tu vole re-adressar iste conto a un altere, tu pote <a href="%{path}">configurar lo ci</a>.
-    or_log_in_with: O accede con
-    privacy_policy_agreement_html: Io ha legite e acceptar le <a href="<a href="%{privacy_policy_path}" target="_blank">politica de confidentialitate</a>
+    log_in_with: Aperir session con
+    login: Aperir session
+    logout: Clauder session
+    migrate_account: Migrar a un altere conto
+    migrate_account_html: Si tu vole rediriger iste conto a un altere, tu pote <a href="%{path}">configurar lo hic</a>.
+    or_log_in_with: O aperi session con
+    privacy_policy_agreement_html: Io ha legite e accepta le <a href="<a href="%{privacy_policy_path}" target="_blank">politica de confidentialitate</a>
     progress:
-      confirm: Confirma le email
+      confirm: Confirmar e-mail
       details: Tu detalios
       review: Nostre revision
       rules: Accepta le regulas
     providers:
       cas: CAS
       saml: SAML
-    register: Inscribe te
+    register: Inscriber se
     registration_closed: "%{instance} non accepta nove membros"
     resend_confirmation: Reinviar ligamine de confirmation
-    reset_password: Remontar le contrasigno
+    reset_password: Reinitialisar contrasigno
     rules:
       accept: Acceptar
       back: Retro
       invited_by: 'Tu pote junger te a %{domain} gratias al invitation que tu ha recipite de:'
-      preamble: Illos es predefinite e fortiarte per le moderatores de %{domain}.
-      preamble_invited: Ante que tu continua, considera le regulas base definite per le moderatores de %{domain}.
-      title: Alcun regulas base.
+      preamble: Istes es definite e applicate per le moderatores de %{domain}.
+      preamble_invited: Ante que tu continua, considera le regulas de base definite per le moderatores de %{domain}.
+      title: Alcun regulas de base.
       title_invited: Tu ha essite invitate.
     security: Securitate
     set_new_password: Definir un nove contrasigno
     setup:
-      email_below_hint_html: Verifica tu plica de spam, o pete un altero. Tu pote corriger tu adresse email si illo es errate.
-      email_settings_hint_html: Clicca le ligamine que nos te inviava pro verificar %{email}.
-      link_not_received: Non obteneva tu un ligamine?
-      new_confirmation_instructions_sent: Tu recipera un nove email con le ligamine de confirmation in alcun minutas!
-      title: Verifica tu cassa de ingresso
+      email_below_hint_html: Consulta tu dossier de spam, o requesta un altere. Tu pote corriger tu adresse de e-mail si illo es errate.
+      email_settings_hint_html: Clicca sur le ligamine que nos te ha inviate pro verificar %{email}. Nos te attendera hic.
+      link_not_received: Necun ligamine recipite?
+      new_confirmation_instructions_sent: Tu recipera un nove e-mail con le ligamine de confirmation in poc minutas!
+      title: Consulta tu cassa de entrata
     sign_in:
-      preamble_html: Accede con tu <strong>%{domain}</strong> credentiales. Si tu conto es hospite sur un differente servitor, tu non potera authenticar te ci.
-      title: Acceder a %{domain}
+      preamble_html: Aperi session con tu credentiales de <strong>%{domain}</strong>. Si tu conto es albergate sur un altere servitor, tu non potera aperir session hic.
+      title: Aperir session sur %{domain}
     sign_up:
-      manual_review: Le inscriptiones sur %{domain} passa per revision manual de nostre moderatores. Pro adjutar nos a processar tu registration, scribe un poco re te mesme e perque tu vole un conto sur %{domain}.
-      preamble: Con un conto sur iste servitor de Mastodon, tu potera sequer ulle altere persona in rete, sin reguardo de ubi lor conto es hospite.
-      title: Lassa que nos te configura sur %{domain}.
+      manual_review: Le inscriptiones sur %{domain} passa per un revision manual de nostre moderatores. Pro adjutar nos a processar tu inscription, per favor scribe un poco sur te e explica proque tu vole un conto sur %{domain}.
+      preamble: Con un conto sur iste servitor de Mastodon, tu potera sequer qualcunque altere persona sur le rete, independentemente de ubi su conto es albergate.
+      title: Lassa nos installar tu conto sur %{domain}.
     status:
       account_status: Stato del conto
-      confirming: Attendente esser completate email de confirmation.
-      functional: Tu conto es plenmente operative.
-      pending: Tu application es pendente de revision per nostre personal. Isto pote prender alcun tempore. Tu recipera un email si tu application es approbate.
-      redirecting_to: Tu conto es inactive perque illo es actualmente re-adressa a %{acct}.
-      self_destruct: Dum %{domain} va clauder, tu solo habera accesso limitate a tu conto.
-      view_strikes: Examinar le admonitiones passate contra tu conto
-    too_fast: Formulario inviate troppo velocemente, retenta.
+      confirming: Attendente le termination del confirmation del adresse de e-mail.
+      functional: Tu conto es completemente operative.
+      pending: Tu demanda attende le revision per nostre personal. Isto pote prender alcun tempore. Tu recipera un e-mail si tu demanda es approbate.
+      redirecting_to: Tu conto es inactive perque illo actualmente redirige a %{acct}.
+      self_destruct: Perque %{domain} va clauder, tu solo habera accesso limitate a tu conto.
+      view_strikes: Examinar le sanctiones passate contra tu conto
+    too_fast: Formulario inviate troppo rapidemente. Tenta lo de novo.
     use_security_key: Usar clave de securitate
   challenge:
     confirm: Continuar
-    hint_html: "<strong>Consilio:</strong> Nos non te demandara tu contrasigno ancora pro le proxime hora."
+    hint_html: "<strong>Consilio:</strong> Nos non te demandara tu contrasigno de novo in le proxime hora."
     invalid_password: Contrasigno non valide
     prompt: Confirma le contrasigno pro continuar
   crypto:
     errors:
       invalid_key: non es un clave Ed25519 o Curve25519 valide
-      invalid_signature: non es un valide firma Ed25519
+      invalid_signature: non es un signatura Ed25519 valide
   date:
     formats:
-      default: "%b %d, %Y"
-      with_month_name: "%B %d, %Y"
+      default: "%d %b %Y"
+      with_month_name: "%d de %B %Y"
   datetime:
     distance_in_words:
       about_x_hours: "%{count}h"
@@ -1144,44 +1144,44 @@ ia:
       about_x_years: "%{count}a"
       almost_x_years: "%{count}a"
       half_a_minute: Justo ora
-      less_than_x_minutes: "%{count} m"
+      less_than_x_minutes: "%{count}m"
       less_than_x_seconds: Justo ora
       over_x_years: "%{count}a"
       x_days: "%{count}d"
-      x_minutes: "%{count} m"
+      x_minutes: "%{count}m"
       x_months: "%{count}me"
       x_seconds: "%{count}s"
   deletes:
-    challenge_not_passed: Le informationes que tu ha inserite non era correcte
+    challenge_not_passed: Le informationes que tu ha inserite non es correcte
     confirm_password: Insere tu contrasigno actual pro verificar tu identitate
-    confirm_username: Insere tu actual contrasigno pro verificar tu identitate
+    confirm_username: Insere tu nomine de usator pro confirmar le procedura
     proceed: Deler le conto
-    success_msg: Tu conto esseva delite con successo
+    success_msg: Tu conto ha essite delite
     warning:
-      before: 'Insere tu nomine de usator pro confirmar le procedura:'
-      caches: Contente que ha essite in cache per altere servitores pote persister
+      before: 'Ante de continuar, per favor lege attentemente iste notas:'
+      caches: Le contento que altere servitores ha immagazinate in cache pote persister
       data_removal: Tu messages e altere datos essera removite permanentemente
       email_change_html: Tu pote <a href="%{path}">cambiar tu adresse de e-mail</a> sin deler tu conto
-      email_contact_html: Si illo ancora non arriva, tu pote inviar email a <a href="mailto:%{email}">%{email}</a> pro peter adjuta
-      email_reconfirmation_html: Si tu non recipe le email de confirmation, tu pote <a href="%{path}>requirer lo ancora</a>
+      email_contact_html: Si illo ancora non arriva, tu pote inviar e-mail a <a href="mailto:%{email}">%{email}</a> pro peter adjuta
+      email_reconfirmation_html: Si tu non recipe le e-mail de confirmation, tu pote <a href="%{path}>requestar lo de novo</a>
       irreversible: Tu non potera restaurar o reactivar tu conto
-      more_details_html: Pro altere detalios, vide le <a href="%{terms_path}">politica de confidentialitate</a>.
+      more_details_html: Pro plus detalios, vide le <a href="%{terms_path}">politica de confidentialitate</a>.
       username_available: Tu nomine de usator essera disponibile novemente
       username_unavailable: Tu nomine de usator remanera indisponibile
   disputes:
     strikes:
-      action_taken: Action prendite
-      appeal: Facer appello
-      appeal_approved: Iste admonition ha essite annullate in appello e non es plus valide
+      action_taken: Mesura prendite
+      appeal: Appellar
+      appeal_approved: Iste sanction ha essite annullate in appello e non es plus valide
       appeal_rejected: Le appello ha essite rejectate
       appeal_submitted_at: Appello submittite
       appealed_msg: Tu appello ha essite submittite. Si es approbate, tu recipera notification.
       appeals:
         submit: Submitter appello
       approve_appeal: Approbar apello
-      associated_report: Signalation associate
+      associated_report: Le reporto associate
       created_at: Del data
-      description_html: Istes es le actiones prendite contra tu conto e le advertimentos que te ha essite inviate per le personal de %{instance}.
+      description_html: Istes es le mesuras prendite contra tu conto e le advertimentos que te ha essite inviate per le personal de %{instance}.
       recipient: Adressate a
       reject_appeal: Rejectar appello
       status: Message №%{id}
@@ -1202,23 +1202,23 @@ ia:
     invalid_domain: non es un nomine de dominio valide
   edit_profile:
     basic_information: Information basic
-    hint_html: "<strong>Personalisa lo que le personas vide sur tu profilo public e presso tu messages.</strong> Il es plus probabile que altere personas te seque e interage con te quando tu ha un profilo compilate e un photo de profilo."
+    hint_html: "<strong>Personalisa lo que le personas vide sur tu profilo public e presso tu messages.</strong> Il es plus probabile que altere personas te seque e interage con te quando tu ha un profilo complete e un photo."
     other: Alteres
   errors:
-    '400': Le requesta que tu inviava era non valide o mal formate.
-    '403': Tu non ha le permisso pro acceder a iste pagina.
+    '400': Le requesta que tu ha inviate non es valide o es mal formate.
+    '403': Tu non ha le permission de acceder a iste pagina.
     '404': Le pagina que tu cerca non es ci.
-    '406': Iste pagina non es disponibile in le formato requirite.
+    '406': Iste pagina non es disponibile in le formato demandate.
     '410': Le pagina que tu cercava non plus existe ci.
     '422':
       content: Le verification de securitate ha fallite. Bloca tu le cookies?
       title: Falleva le verification de securitate
-    '429': Troppe requestas
+    '429': Troppo de requestas
     '500':
-      content: Nos lo regretta, ma alco errate eveniva sur nostre extremo.
+      content: Nos lo regretta, ma qualcosa non ha functionate de nostre latere.
       title: Iste pagina non es correcte
-    '503': Le pagina non poteva esser servite per un panna de servitor temporari.
-    noscript_html: A usar le application web Mastodon, activa JavaScript. In alternativa, tenta un del <a href="%{apps_path}">apps native</a> de Mastodon pro tu platteforma.
+    '503': Le pagina non poteva esser servite a causa de un panna temporari del servitor.
+    noscript_html: Pro usar le application web Mastodon, es necessari activar JavaScript. Tu pote tamben probar un del <a href="%{apps_path}">apps native</a> de Mastodon pro tu platteforma.
   existing_username_validator:
     not_found: impossibile trovar un usator local con ille nomine de usator
     not_found_multiple: non poteva trovar %{usernames}
@@ -1226,9 +1226,9 @@ ia:
     archive_takeout:
       date: Data
       download: Discargar tu archivo
-      hint_html: Tu pote requirer un archivo de tu <strong>messages e medios cargate</strong>. Le datos exportate sera in le formato ActivityPub, legibile per ulle software conforme.
+      hint_html: Tu pote requestar un archivo de tu <strong>messages e files multimedial incargate</strong>. Le datos exportate essera in le formato ActivityPub, legibile per qualcunque software conforme. Tu pote requestar un archivo cata 7 dies.
       in_progress: Compilante tu archivo...
-      request: Pete tu archivo
+      request: Requestar tu archivo
       size: Dimension
     blocks: Tu ha blocate
     bookmarks: Marcapaginas
@@ -1236,16 +1236,16 @@ ia:
     domain_blocks: Blocadas de dominio
     lists: Listas
     mutes: Tu ha silentiate
-    storage: Immagazinage de medios
+    storage: Immagazinage multimedial
   featured_tags:
     add_new: Adder nove
     errors:
-      limit: Tu ha jam consiliate le maxime numero de hashtags
-    hint_html: "<strong>Consilia tu plus importante hashtags sur tu profilo.</strong> Un grande instrumento pro tener tracia de tu labores creative e projectos de longe-tempore, le hashtags consiliate es monstrate prominentemente sur tu profilo e permitte accesso rapide a tu proprie messages."
+      limit: Tu ha jam mittite in evidentia le maxime numero de hashtags
+    hint_html: "<strong>Monstra tu plus importante hashtags sur tu profilo.</strong> Un excellente instrumento pro tener tracia de tu labores creative e projectos de longe termino, le hashtags que tu mitte in evidentia appare prominentemente sur tu profilo e permitte le accesso rapide a tu proprie messages."
   filters:
     contexts:
       account: Profilos
-      home: Pagina de initio e listas
+      home: Initio e listas
       notifications: Notificationes
       public: Chronologias public
       thread: Conversationes
@@ -1253,11 +1253,11 @@ ia:
       add_keyword: Adder parola clave
       keywords: Parolas clave
       statuses: Messages individual
-      statuses_hint_html: Iste filtro se applica a seliger messages singule sin reguardo si illes concorda le parolas clave infra. <a href="%{path}">Revide o remove le messages ab le filtro</a>.
+      statuses_hint_html: Iste filtro se applica a un selection de messages individual, independentemente de si illos corresponde al parolas clave infra. <a href="%{path}">Revide o remove messages del filtro</a>.
       title: Modificar filtro
     errors:
-      deprecated_api_multiple_keywords: Iste parametros non pote esser cambiate ab iste application perque illos se applica a plus que un sol parola clave del filtro. Usa un application plus recente o le interfacie web.
-      invalid_context: Nulle o non valide contexto supplite
+      deprecated_api_multiple_keywords: Iste parametros non pote esser cambiate desde iste application perque illos se applica a plus de un parola clave del filtro. Usa un application plus recente o le interfacie web.
+      invalid_context: Contexto mancante o non valide
     index:
       contexts: Filtros in %{contexts}
       delete: Deler
@@ -1271,8 +1271,8 @@ ia:
         one: "%{count} message"
         other: "%{count} messages"
       statuses_long:
-        one: "%{count} singule message celate"
-        other: "%{count} singule messages celate"
+        one: "%{count} message individual celate"
+        other: "%{count} messages individual celate"
       title: Filtros
     new:
       save: Salveguardar nove filtro
@@ -1280,9 +1280,9 @@ ia:
     statuses:
       back_to_filter: Retro al filtro
       batch:
-        remove: Remover ab filtro
+        remove: Remover del filtro
       index:
-        hint: Iste filtro se applica pro seliger messages singule sin reguardo de altere criterios. Tu pote adder altere messages a iste filtro ab le interfacie web.
+        hint: Iste filtro se applica a un selection de messages individual, independentemente de altere criterios. Tu pote adder plus messages a iste filtro desde le interfacie web.
         title: Messages filtrate
   generic:
     all: Toto
@@ -1290,53 +1290,53 @@ ia:
       one: "<strong>%{count}</strong> elemento sur iste pagina es seligite."
       other: Tote le <strong>%{count}</strong> elementos sur iste pagina es seligite.
     all_matching_items_selected_html:
-      one: "<strong>%{count}</strong> elemento concordante que tu cerca es seligite."
-      other: Tote le <strong>%{count}</strong> elementos concordante que tu cerca es seligite.
+      one: "<strong>%{count}</strong> elemento correspondente al recerca es seligite."
+      other: Tote le <strong>%{count}</strong> elementos correspondente al recerca es seligite.
     cancel: Cancellar
     changes_saved_msg: Cambios salveguardate con successo!
     confirm: Confirmar
     copy: Copiar
     delete: Deler
     deselect: Deseliger toto
-    none: Nemo
+    none: Necun
     order_by: Ordinar per
     save_changes: Salvar le cambios
     select_all_matching_items:
-      one: Selige %{count} elemento concordante tu recerca.
-      other: Selige %{count} elementos concordante tu recerca.
+      one: Selige %{count} elemento correspondente a tu recerca.
+      other: Selige %{count} elementos correspondente a tu recerca.
     today: hodie
     validation_errors:
-      one: Alco non es multo bon ancora! Controla le error infra
-      other: Alco non es multo bon ancora! Controla %{count} errores infra
+      one: Qualcosa ancora non es multo bon! Per favor controla le error infra
+      other: Qualcosa ancora non es multo bon! Per favor controla le %{count} errores infra
   imports:
     errors:
       empty: File CSV vacue
       incompatible_type: Incompatibile con le typo de importation seligite
       invalid_csv_file: 'File CSV non valide. Error: %{error}'
-      over_rows_processing_limit: contine plus que %{count} rangos
-      too_large: Le file es troppo longe
+      over_rows_processing_limit: contine plus de %{count} lineas
+      too_large: Le file es troppo grande
     failures: Fallimentos
     imported: Importate
-    mismatched_types_warning: Il appare que tu pote haber seligite le typo errate pro iste importation, controla duo vices.
+    mismatched_types_warning: Il pare que tu pote haber seligite le typo errate pro iste importation. Per favor reverifica lo.
     modes:
-      merge: Funder
-      merge_long: Mantene le registrationes existente e adde illos nove
+      merge: Fusionar
+      merge_long: Conservar le registros existente e adder noves
       overwrite: Superscriber
       overwrite_long: Reimplaciar registros actual con le noves
     overwrite_preambles:
       blocking_html: Tu es sur le puncto de <strong>reimplaciar tu lista de blocadas</strong> per usque a <strong>%{total_items} contos</strong> proveniente de <strong>%{filename}</strong>.
-      bookmarks_html: Tu va <strong>reimplaciar tu lista de blocadas</strong> per usque a <strong>%{total_items} contos</strong> proveniente de <strong>%{filename}</strong>.
+      bookmarks_html: Tu es sur le puncto de <strong>reimplaciar tu marcapaginas</strong> per usque a <strong>%{total_items} messages</strong> desde <strong>%{filename}</strong>.
       domain_blocking_html: Tu es sur le puncto de <strong>reimplaciar tu lista de blocadas de dominio</strong> per usque a <strong>%{total_items} dominios</strong> proveniente de <strong>%{filename}</strong>.
-      following_html: Tu va <strong>sequer</strong> usque <strong>%{total_items} contos</strong> de <strong>%{filename}</strong> e <strong>cessar de sequer ulle altere</strong>.
-      lists_html: Tu va <strong>reimplaciar tu lista</strong> con contentos de <strong>%{filename}</strong>. Usque <strong>%{total_items} contos</strong> sera addite a nove listas.
-      muting_html: Tu va <strong>reimplaciar tu lista de contos silentiate</strong> con usque <strong>%{total_items} contos</strong> ab <strong>%{filename}</strong>.
+      following_html: Tu es sur le puncto de <strong>sequer</strong> usque a <strong>%{total_items} contos</strong> de <strong>%{filename}</strong> e <strong>cessar de sequer tote le alteres</strong>.
+      lists_html: Tu es sur le puncto de <strong>reimplaciar tu listas</strong> per le contento de <strong>%{filename}</strong>. Usque a <strong>%{total_items} contos</strong> essera addite a nove listas.
+      muting_html: Tu es sur le puncto de <strong>reimplaciar tu lista de contos silentiate</strong> con usque a <strong>%{total_items} contos</strong> desde <strong>%{filename}</strong>.
     preambles:
       blocking_html: Tu es sur le puncto de <strong>blocar</strong> usque a <strong>%{total_items} contos</strong> a partir de <strong>%{filename}</strong>.
-      bookmarks_html: Tu va adder usque <strong>%{total_items} messages</strong> de <strong>%{filename}</strong> a tu <strong>marcapaginas</strong>.
+      bookmarks_html: Tu es sur le puncto de adder usque a <strong>%{total_items} messages</strong> desde <strong>%{filename}</strong> a tu <strong>marcapaginas</strong>.
       domain_blocking_html: Tu es sur le puncto de <strong>blocar</strong> usque a <strong>%{total_items} dominios</strong> a partir de <strong>%{filename}</strong>.
-      following_html: Tu va <strong>blocar</strong> usque a <strong>%{total_items} dominios</strong> ab <strong>%{filename}</strong>.
-      lists_html: Tu va adder usque <strong>%{total_items} contos</strong> ab <strong>%{filename}</strong> a tu <strong>lista</strong>. Nove listas sera create si il non ha lista a adder.
-      muting_html: Tu va <strong>silentiar</strong> usque <strong>%{total_items} contos</strong> ab <strong>%{filename}</strong>.
+      following_html: Tu es sur le puncto de <strong>sequer</strong> usque a <strong>%{total_items} contos</strong> desde <strong>%{filename}</strong>.
+      lists_html: Tu es sur le puncto de adder usque <strong>%{total_items} contos</strong> desde <strong>%{filename}</strong> a tu <strong>listas</strong>. Nove listas essera create si il non ha un lista al qual adder los.
+      muting_html: Tu es sur le puncto de <strong>silentiar</strong> usque a <strong>%{total_items} contos</strong> desde <strong>%{filename}</strong>.
     preface: Tu pote importar datos que tu ha exportate de un altere servitor, como un lista de personas que tu seque o bloca.
     recent_imports: Importationes recente
     states:
@@ -1345,7 +1345,7 @@ ia:
       scheduled: Planificate
       unconfirmed: Non confirmate
     status: Stato
-    success: Tu datos era cargate con successo e sera processate in tempore debite
+    success: Tu datos ha essite correctemente incargate e essera tractate in tempore debite
     time_started: Initiate le
     titles:
       blocking: Importation de contos blocate
@@ -1362,9 +1362,9 @@ ia:
       blocking: Lista de blocadas
       bookmarks: Marcapaginas
       domain_blocking: Lista de dominios blocate
-      following: Sequente lista
+      following: Lista de contos sequite
       lists: Listas
-      muting: Lista del silentiates
+      muting: Lista de contos silentiate
     upload: Incargar
   invites:
     delete: Disactivar
@@ -1399,10 +1399,10 @@ ia:
       sign_in_token: codice de securitate de e-mail
       webauthn: claves de securitate
     description_html: Si tu vide activitate que tu non recognosce, considera de cambiar tu contrasigno e activar le authentication a duo factores.
-    empty: Nulle chronologia de authentication disponibile
+    empty: Nulle historia de authentication disponibile
     failed_sign_in_html: Tentativa de authentication fallite con %{method} ab %{ip} (%{browser})
-    successful_sign_in_html: Apertura de session con successo con %{method} ab %{ip} (%{browser})
-    title: Chronologia de authentication
+    successful_sign_in_html: Apertura de session succedite con %{method} desde %{ip} (%{browser})
+    title: Historia de authentication
   mail_subscriptions:
     unsubscribe:
       action: Si, desubscriber
@@ -1421,127 +1421,127 @@ ia:
   media_attachments:
     validations:
       images_and_video: Impossibile annexar un video a un message que jam contine imagines
-      not_ready: Impossibile annexar un video a un message que jam contine imagines. Retenta post un momento!
-      too_many: Impossibile annexar plus que 4 files
+      not_ready: Impossibile annexar files que non ha ancora essite processate. Retenta post un momento!
+      too_many: Impossibile annexar plus de 4 files
   migrations:
-    acct: Movite a
+    acct: Ha migrate a
     cancel: Cancellar redirection
-    cancel_explanation: Cancellar le redirection reactivara tu conto actual, ma non reportara sequaces que ha essite movite in ille conto.
+    cancel_explanation: Cancellar le redirection reactivara tu conto actual, ma non te retornara le sequitores que ha essite transferite al altere conto.
     cancelled_msg: Redirection cancellate con successo.
     errors:
-      already_moved: is the same account you have already moved to
+      already_moved: es le mesme conto al qual tu ha ja migrate
       missing_also_known_as: non es un alias de iste conto
       move_to_self: non pote esser le conto actual
-      not_found: non poterea esser trovate
+      not_found: non poteva esser trovate
       on_cooldown: Tu es in pausa
-    followers_count: Sequaces a tempore de mover
-    incoming_migrations: Movente ab un conto differente
-    incoming_migrations_html: Pro mover ab un altere conto a isto, primo tu debe <a href="%{path}">crear un alias de conto</a>.
-    moved_msg: Tu conto ora es redirigite a %{acct} e tu sequaces es movite super.
-    not_redirecting: Tu conto actualmente non es redirigite a ulle altere conto.
-    on_cooldown: Tu recentemente ha migrate tu conto. Iste function de novo sera disponibile in %{count} dies.
+    followers_count: Sequitores al momento de migration
+    incoming_migrations: Migrar de un altere conto
+    incoming_migrations_html: Pro migrar de un altere conto a iste, primo tu debe <a href="%{path}">crear un alias de conto</a>.
+    moved_msg: Tu conto es ora redirigite a %{acct} e le transferentia de tu sequitores es in curso.
+    not_redirecting: Tu conto actualmente non es redirigite a un altere conto.
+    on_cooldown: Tu ha recentemente migrate tu conto. Iste function essera disponibile de novo in %{count} dies.
     past_migrations: Migrationes passate
-    proceed_with_move: Mover sequaces
+    proceed_with_move: Transferer sequitores
     redirected_msg: Tu conto es ora redirigite a %{acct}.
     redirecting_to: Tu conto es redirigite a %{acct}.
-    set_redirect: Predefinir redirection
+    set_redirect: Definir redirection
     warning:
-      backreference_required: Le nove conto debe primo esser configurate pro referer se a isto
-      before: 'Ante de continuar, lege iste notas accuratemente:'
-      cooldown: Post le movimento il ha un periodo de pausa durante le qual tu non potera mover te ancora
-      disabled_account: Tu conto actual non sera plenmente usabile postea. Comocunque, tu habera accesso a exportation de datos e re-activation.
-      followers: Iste action movera tote le sequaces ab le conto actual al nove conto
-      only_redirect_html: In alternativa, tu pote <a href="%{path}">solo superponer un redirection sur tu profilo</a>.
-      other_data: Nulle altere datos sera movite automaticamente
-      redirect: Le profilo de tu conto actual sera actualisate con un aviso de redirection e sera excludite de recercas
+      backreference_required: Le nove conto debe primo esser configurate pro referer se a iste
+      before: 'Ante de continuar, lege attentemente iste notas:'
+      cooldown: Post le migration il ha un periodo de pausa durante le qual tu non potera migrar de novo
+      disabled_account: Tu conto actual non essera plenmente usabile postea. Nonobstante, tu habera accesso al exportation de datos e al reactivation.
+      followers: Iste action transferera tote le sequitores del conto actual al conto nove
+      only_redirect_html: Como alternativa, tu pote <a href="%{path}">poner solmente un redirection sur tu profilo</a>.
+      other_data: Nulle altere datos essera migrate automaticamente
+      redirect: Le profilo de tu conto actual essera actualisate con un aviso de redirection e excludite de recercas
   moderation:
     title: Moderation
   move_handler:
     carry_blocks_over_text: Iste usator ha cambiate de conto desde %{acct}, que tu habeva blocate.
-    carry_mutes_over_text: Iste usator moveva ab %{acct}, que tu habeva silentiate.
-    copy_account_note_text: 'Iste usator moveva ab %{acct}, ci era tu previe notas re ille:'
+    carry_mutes_over_text: Iste usator ha migrate de %{acct}, que tu habeva silentiate.
+    copy_account_note_text: 'Iste usator ha migrate de %{acct}, ecce tu previe notas sur iste persona:'
   navigation:
-    toggle_menu: Mutar menu
+    toggle_menu: Commutar menu
   notification_mailer:
     admin:
       report:
-        subject: "%{name} inviava un reporto"
+        subject: "%{name} ha inviate un reporto"
       sign_up:
         subject: "%{name} se ha inscribite"
     favourite:
-      body: 'Tu message era favorite per %{name}:'
-      subject: "%{name} favoriva tu message"
-      title: Nove preferito
+      body: 'Tu message ha essite marcate como favorite per %{name}:'
+      subject: "%{name} ha marcate tu message como favorite"
+      title: Nove favorite
     follow:
       body: "%{name} ora te seque!"
       subject: "%{name} ora te seque"
       title: Nove sequitor
     follow_request:
-      action: Gere requestas de sequer
+      action: Gerer requestas de sequimento
       body: "%{name} ha demandate de sequer te"
-      subject: 'Sequace pendente: %{name}'
+      subject: 'Sequitor pendente: %{name}'
       title: Nove requesta de sequimento
     mention:
       action: Responder
-      body: 'Tu era mentionate per %{name} in:'
-      subject: Tu ha essite mentionate per %{name}
+      body: "%{name} te ha mentionate in:"
+      subject: "%{name} te ha mentionate"
       title: Nove mention
     poll:
-      subject: Un inquesta de %{name} ha finite
+      subject: Un sondage de %{name} ha finite
     reblog:
-      body: 'Tu message ha essite impulsate per %{name}:'
+      body: "%{name} ha impulsate tu message:"
       subject: "%{name} ha impulsate tu message"
       title: Nove impulso
     status:
-      subject: "%{name} justo ha publicate"
+      subject: "%{name} ha publicate un message"
     update:
       subject: "%{name} ha modificate un message"
   notifications:
-    administration_emails: Avisos de email per administrator
-    email_events: Eventos pro avisos de email
-    email_events_hint: 'Selige eventos pro que tu vole reciper avisos:'
+    administration_emails: Notificationes per e-mail pro administratores
+    email_events: Eventos pro notificationes per e-mail
+    email_events_hint: 'Selige eventos pro le quales tu vole reciper notificationes:'
   number:
     human:
       decimal_units:
-        format: "%n%u"
+        format: "%n %u"
         units:
-          billion: B
-          million: M
-          quadrillion: Q
-          thousand: K
-          trillion: T
+          billion: mld
+          million: mln
+          quadrillion: bld
+          thousand: mil
+          trillion: bln
   otp_authentication:
     code_hint: Insere le codice generate per tu app de authentication pro confirmar
-    description_html: Si tu activa <strong>le authentication a duo factores</strong> per un app de authentication, le authentication requirera que tu es in possession de tu telephono, que generara testimonios pro facer te entrar.
+    description_html: Si tu activa <strong>le authentication a duo factores</strong> usante un app de authentication, le authentication requirera que tu es in possession de tu telephono, que generara tokens que tu debera inserer.
     enable: Activar
-    instructions_html: "<strong>Scande iste codice QR in Google Authenticator o un simile app TOTP sur tu telephono</strong>. Desde ora in avante, ille app generara testimonios que tu debera inserer quando tu te authenticara."
-    manual_instructions: 'Si tu non pote scander le codice QR e besonia de inserer lo manualmente, ecce le texto-simple secrete:'
+    instructions_html: "<strong>Scanna iste codice QR in Google Authenticator o un app TOTP simile sur tu telephono</strong>. Desde ora in avante, ille app generara tokens que tu debera inserer quando tu aperi session."
+    manual_instructions: 'Si tu non pote scannar le codice QR e debe inserer lo manualmente, ecce le secreto in texto simple:'
     setup: Configurar
-    wrong_code: Le codice inserite non era valide! Es tempore de servitor e tempore de apparato correcte?
+    wrong_code: Le codice inserite non es valide! Es le hora del servitor e del apparato correcte?
   pagination:
-    newer: Plus recente
+    newer: Plus nove
     next: Sequente
-    older: Plus vetere
+    older: Plus ancian
     prev: Previe
     truncate: "&hellip;"
   polls:
     errors:
       already_voted: Tu jam ha votate in iste sondage
       duplicate_options: contine elementos duplicate
-      duration_too_long: il es troppo lontan in le futuro
-      duration_too_short: il es troppo tosto
+      duration_too_long: es troppo lontan in le futuro
+      duration_too_short: es troppo tosto
       expired: Le sondage ha jam finite
       invalid_choice: Le option de voto eligite non existe
       over_character_limit: non pote esser plus longe que %{max} characteres cata un
-      self_vote: Tu non pote vota in tu proprie sondages
-      too_few_options: debe haber plus que un elemento
-      too_many_options: non pote continer plus que %{max} elementos
+      self_vote: Tu non pote votar in tu proprie sondages
+      too_few_options: debe haber plus de un elemento
+      too_many_options: non pote continer plus de %{max} elementos
   preferences:
-    other: Altere
-    posting_defaults: Publicationes predefinite
+    other: Alteres
+    posting_defaults: Parametros de publication predefinite
     public_timelines: Chronologias public
   privacy:
-    hint_html: "<strong>Personalisa como tu vole que tu profilo e tu messages a es trovate.</strong> Un varietate de functiones in Mastodon pote adjutar te attinger un plus large auditorio si activate. Prende un momento pro revider iste parametros pro assecurar te que illos se adapta a tu caso de uso."
+    hint_html: "<strong>Personalisa como tu vole que tu profilo e tu messages es trovate.</strong> Un varietate de functiones in Mastodon pote adjutar te a attinger un plus grande publico quando activate. Prende un momento pro revider iste parametros pro assecurar te que illos se adapta a tu besonios."
     privacy: Confidentialitate
     privacy_hint_html: Controla quanto tu vole divulgar pro le beneficio de alteres. Le gente discoperi profilos e applicationes interessante percurrente le profilos sequite per altere personas e vidente a partir de qual applicationes illos publica lor messages, ma tu pote preferer de mantener tal information private.
     reach: Portata
@@ -1554,47 +1554,47 @@ ia:
   reactions:
     errors:
       limit_reached: Limite de reactiones differente attingite
-      unrecognized_emoji: non es un emoticone recognoscite
+      unrecognized_emoji: non es un emoji recognoscite
   redirects:
-    prompt: Si tu te fide de iste ligamine, clicca lo pro continuar.
+    prompt: Si tu te fide a iste ligamine, clicca sur illo pro continuar.
     title: Tu va lassar %{instance}.
   relationships:
     activity: Activitate del conto
-    confirm_follow_selected_followers: Desira tu vermente remover le sequaces seligite?
-    confirm_remove_selected_followers: Desira tu vermente remover le sequaces seligite?
-    confirm_remove_selected_follows: Desira tu vermente remover le sequaces seligite?
+    confirm_follow_selected_followers: Es tu secur que tu vole sequer le sequitores seligite?
+    confirm_remove_selected_followers: Es tu secur que tu vole remover le sequitores seligite?
+    confirm_remove_selected_follows: Es tu secur que tu vole cessar de sequer le contos seligite?
     dormant: Dormiente
-    follow_failure: Impossibile sequer alcun del contos seligite.
-    follow_selected_followers: Sequer le sequaces seligite
-    followers: Sequaces
+    follow_failure: Impossibile sequer alcunes del contos seligite.
+    follow_selected_followers: Sequer le sequitores seligite
+    followers: Sequitores
     following: Sequente
     invited: Invitate
-    last_active: Ultimo active
+    last_active: Ultime activitate
     most_recent: Plus recente
-    moved: Movite
+    moved: Migrate
     mutual: Mutue
     primary: Primari
     relationship: Relation
-    remove_selected_domains: Remover tote le sequaces ab le dominios seligite
-    remove_selected_followers: Remover le sequaces seligite
+    remove_selected_domains: Remover tote le sequitores del dominios seligite
+    remove_selected_followers: Remover le sequitores seligite
     remove_selected_follows: Non plus sequer le usatores seligite
     status: Stato del conto
   remote_follow:
-    missing_resource: Impossibile trovar le requirite re-adresse URL pro tu conto
+    missing_resource: Impossibile trovar le URL de redirection requirite pro tu conto
   reports:
     errors:
-      invalid_rules: non referentia regulas valide
+      invalid_rules: non face referentia a regulas valide
   rss:
     content_warning: 'Advertimento de contento:'
     descriptions:
       account: Messages public de @%{acct}
-      tag: 'Messages public plachettate #%{hashtag}'
+      tag: 'Messages public con hashtag #%{hashtag}'
   scheduled_statuses:
     over_daily_limit: Tu ha excedite le limite de %{limit} messages programmate pro hodie
     over_total_limit: Tu ha excedite le limite de %{limit} messages programmate
     too_soon: Le data programmate debe esser in le futuro
   self_destruct:
-    lead_html: Infortunatemente, <strong>%{domain}</strong> va clauder permanentemente. Si tu habeva un conto illac, tu non potera continuar a usar lo, ma tu pote ancora peter un salveguarda de tu datos.
+    lead_html: Infortunatemente, <strong>%{domain}</strong> tosto claudera permanentemente. Si tu habeva un conto illac, tu non potera continuar a usar lo, ma tu pote ancora requestar un copia de tu datos.
     title: Iste servitor va clauder
   sessions:
     activity: Ultime activitate
@@ -1617,12 +1617,12 @@ ia:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
-      unknown_browser: Navigator Incognite
+      unknown_browser: Navigator incognite
       weibo: Weibo
     current_session: Session actual
     date: Data
     description: "%{browser} sur %{platform}"
-    explanation: Il ha navigatores del web actualmente connexe a tu conto Mastodon.
+    explanation: Istes es le navigatores web actualmente in session sur tu conto Mastodon.
     ip: IP
     platforms:
       adobe_air: Adobe Air
@@ -1641,29 +1641,29 @@ ia:
     revoke: Revocar
     revoke_success: Session revocate con successo
     title: Sessiones
-    view_authentication_history: Vider chronologia de authentication de tu conto
+    view_authentication_history: Vider le historia de authentication de tu conto
   settings:
     account: Conto
     account_settings: Parametros de conto
     aliases: Aliases de conto
     appearance: Apparentia
     authorized_apps: Apps autorisate
-    back: Tornar a Mastodon
+    back: Retornar a Mastodon
     delete: Deletion de conto
     development: Disveloppamento
     edit_profile: Modificar profilo
     export: Exportation de datos
-    featured_tags: Hashtags eminente
+    featured_tags: Hashtags in evidentia
     import: Importar
     import_and_export: Importar e exportar
     migrate: Migration de conto
-    notifications: Notificationes de e-mail
+    notifications: Notificationes per e-mail
     preferences: Preferentias
     profile: Profilo public
     relationships: Sequites e sequitores
     severed_relationships: Relationes rupte
     statuses_cleanup: Deletion de message automatic
-    strikes: Admonitiones de moderation
+    strikes: Sanctiones de moderation
     two_factor_authentication: Authentication a duo factores
     webauthn_authentication: Claves de securitate
   severed_relationships:
@@ -1675,7 +1675,7 @@ ia:
     lost_followers: Sequitores perdite
     lost_follows: Sequites perdite
     preamble: Tu pote perder sequites e sequitores quando tu bloca un dominio o quando tu moderatores decide suspender un servitor remote. Quando isto occurre, tu potera discargar listas de relationes rumpite, a inspectar e eventualmente importar in un altere servitor.
-    purged: Le information re iste servitor ha essite purgate per le administratores de tu servitor.
+    purged: Le information sur iste servitor ha essite purgate per le administratores de tu servitor.
     type: Evento
   statuses:
     attached:
@@ -1698,7 +1698,7 @@ ia:
     edited_at_html: Modificate le %{date}
     errors:
       in_reply_not_found: Le message a que tu tenta responder non pare exister.
-    open_in_web: Aperir in le web
+    open_in_web: Aperir sur le web
     over_character_limit: limite de characteres de %{max} excedite
     pin_errors:
       direct: Messages que es solo visibile a usatores mentionate non pote esser appunctate
@@ -1714,11 +1714,11 @@ ia:
         other: "%{count} votos"
       vote: Votar
     show_more: Monstrar plus
-    show_thread: Monstrar argumento
-    title: '%{name}: "%{quote}"'
+    show_thread: Monstrar discussion
+    title: "%{name}: “%{quote}”"
     visibilities:
       direct: Directe
-      private: Solo-sequaces
+      private: Solmente sequitores
       private_long: Solmente monstrar a sequitores
       public: Public
       public_long: Omnes pote vider
@@ -1729,22 +1729,22 @@ ia:
     enabled_hint: Dele automaticamente tu messages un vice que illos attinge un limine de etate specificate, salvo que illes concorda un del exceptiones infra
     exceptions: Exceptiones
     explanation: Pois que deler messages es un operation costose, isto es facite lentemente in le tempore quando le servitor non es alteremente occupate. Pro iste ration, tu messages pote esser delite un poco post que illos attinge le limine de etate.
-    ignore_favs: Ignorar favoritos
+    ignore_favs: Ignorar favorites
     ignore_reblogs: Ignorar impulsos
-    interaction_exceptions: Exceptiones basate super interactiones
+    interaction_exceptions: Exceptiones basate sur interactiones
     interaction_exceptions_explanation: Nota que il non ha garantia que le messages essera delite si illos va sub le limine de favorites o impulsos post haber lo superate un vice.
     keep_direct: Mantener le messages directe
-    keep_direct_hint: Non dele alcuno de tu messages directe
-    keep_media: Mantener messages con annexos de medios
-    keep_media_hint: Non dele alcuno de tu messages que ha annexos de medios
-    keep_pinned: Mantener messages appunctate
-    keep_pinned_hint: Non dele alcuno de tu messages appunctate
+    keep_direct_hint: Non dele alcun de tu messages directe
+    keep_media: Conservar messages con annexos multimedial
+    keep_media_hint: Non dele alcun de tu messages que ha annexos multimedial
+    keep_pinned: Conservar le messages fixate
+    keep_pinned_hint: Non dele alcun de tu messages fixate
     keep_polls: Mantener sondages
-    keep_polls_hint: Non dele ulle de tu sondages
-    keep_self_bookmark: Mantener messages que tu marcava con marcapaginas
-    keep_self_bookmark_hint: Non dele tu proprie messages si tu los ha marcate con marcapaginas
-    keep_self_fav: Mantene messages que tu favoriva
-    keep_self_fav_hint: Non dele tu proprie messages si tu los ha favorite
+    keep_polls_hint: Non dele alcun de tu sondages
+    keep_self_bookmark: Conservar le messages que tu ha in marcapaginas
+    keep_self_bookmark_hint: Non dele tu proprie messages si tu los ha addite al marcapaginas
+    keep_self_fav: Conservar tu messages favorite
+    keep_self_fav_hint: Non dele tu proprie messages si tu los ha marcate como favorite
     min_age:
       '1209600': 2 septimanas
       '15778476': 6 menses
@@ -1755,17 +1755,17 @@ ia:
       '63113904': 2 annos
       '7889238': 3 menses
     min_age_label: Limine de etate
-    min_favs: Mantener messages favorite al minus
-    min_favs_hint: Non deler alcuno de tu messages que ha recipite al minus iste numero de favoritos. Lassar blanc pro deler messages sin reguardo de lor numero de favoritos
-    min_reblogs: Mantener messages impulsate al minus
+    min_favs: Conservar messages marcate como favorite al minus
+    min_favs_hint: Non dele alcun de tu messages que ha essite marcate como favorite al minus iste numero de vices. Lassa vacue pro deler messages independentemente de lor numero de marcas como favorite
+    min_reblogs: Conservar messages impulsate al minus
     min_reblogs_hint: Non dele alcun de tu messages que ha essite impulsate al minus iste numero de vices. Lassar vacue pro deler messages independentemente de lor numero de impulsos
   stream_entries:
     sensitive_content: Contento sensibile
   strikes:
     errors:
-      too_late: Es troppo tarde pro facer appello contra iste admonition
+      too_late: Es troppo tarde pro appellar contra iste sanction
   tags:
-    does_not_match_previous_name: non concorda le nomine previe
+    does_not_match_previous_name: non corresponde al nomine precedente
   themes:
     contrast: Mastodon (Alte contrasto)
     default: Mastodon (Obscur)
@@ -1776,22 +1776,22 @@ ia:
       default: "%d %b %Y, %H:%M"
       month: "%b %Y"
       time: "%H:%M"
-      with_time_zone: "%b %d, %Y, %H:%M %Z"
+      with_time_zone: "%d %b %Y, %H:%M %Z"
   translation:
     errors:
       quota_exceeded: Le quota de utilisation del servitor pro le servicio de traduction ha essite excedite.
-      too_many_requests: Il ha habite troppe requestas al servicio de traduction recentemente.
+      too_many_requests: Il ha habite troppo de requestas al servicio de traduction recentemente.
   two_factor_authentication:
     add: Adder
-    disable: Disactivar 2FA
+    disable: Disactivar A2F
     disabled_success: Authentication a duo factores disactivate con successo
     edit: Modificar
     enabled: Le authentication a duo factores es activate
     enabled_success: Authentication a duo factores activate con successo
     generate_recovery_codes: Generar codices de recuperation
-    lost_recovery_codes: Le codices de recuperation te permitte de reganiar accesso a tu conto si tu perde tu telephono. Si tu ha perdite tu codices de recuperation, tu pote regenerar los ci. Tu vetere codices de recuperation sera invalidate.
+    lost_recovery_codes: Le codices de recuperation te permitte reganiar le accesso a tu conto si tu perde tu telephono. Si tu ha perdite tu codices de recuperation, tu pote regenerar los hic. Tu ancian codices de recuperation essera invalidate.
     methods: Methodos a duo factores
-    otp: App de authenticator
+    otp: App authenticator
     recovery_codes: Salveguardar codices de recuperation
     recovery_codes_regenerated: Codices de recuperation regenerate con successo
     recovery_instructions_html: Si tu perde le accesso a tu telephono, tu pote usar un del codices de recuperation hic infra pro reganiar le accesso a tu conto. <strong>Mantene le codices de recuperation secur.</strong> Per exemplo, tu pote imprimer los e guardar los con altere documentos importante.
@@ -1799,33 +1799,33 @@ ia:
   user_mailer:
     appeal_approved:
       action: Parametros de conto
-      explanation: Le appello contra le admonition contra tu conto del %{strike_date}, que tu ha submittite le %{appeal_date}, ha essite approbate. Tu conto ha de novo un bon reputation.
-      subject: Tu appello ab %{date} ha essite approbate
-      subtitle: Tu conto es ancora un vice in regula.
+      explanation: Le appello contra le sanction del %{strike_date} contra tu conto, que tu ha submittite le %{appeal_date}, ha essite approbate. Tu conto es de bon reputation de novo.
+      subject: Tu appello del %{date} ha essite approbate
+      subtitle: Tu conto es de bon reputation de novo.
       title: Appello approbate
     appeal_rejected:
-      explanation: Le appello contra le admonition contra tu conto del %{strike_date}, que tu ha submittite le %{appeal_date}, ha essite rejectate.
-      subject: Tu appello ab %{date} ha essite rejectate
+      explanation: Le appello contra le sanction del %{strike_date} contra tu conto, que tu ha submittite le %{appeal_date}, ha essite rejectate.
+      subject: Tu appello del %{date} ha essite rejectate
       subtitle: Tu appello ha essite rejectate.
       title: Appello rejectate
     backup_ready:
       explanation: Tu ha requestate un copia de securitate complete de tu conto de Mastodon.
-      extra: Isto es preste pro discargar!
+      extra: Illo es ora preste pro discargar!
       subject: Tu archivo es preste pro discargar
       title: Discargar archivo
     failed_2fa:
-      details: 'Hic es le detalios del tentativa de initio de session:'
+      details: 'Ecce le detalios del tentativa de apertura de session:'
       explanation: Alcuno ha tentate aperir session a tu conto ma ha fornite un secunde factor de authentication non valide.
       further_actions_html: Si non se tractava de te, nos recommenda %{action} immediatemente perque illo pote esser compromittite.
-      subject: Fallimento del authentication de duo factores
-      title: Falleva le authentication de duo factores
+      subject: Fallimento de authentication del secunde factor
+      title: Ha fallite le authentication del secunde factor
     suspicious_sign_in:
       change_password: cambiar tu contrasigno
-      details: 'Hic es le detalios del initio de session:'
-      explanation: Nos ha detegite un initio de session a tu conto ab un nove adresse IP.
+      details: 'Ecce le detalios del apertura de session:'
+      explanation: Nos ha detegite un apertura de session sur tu conto desde un nove adresse IP.
       further_actions_html: Si non se tractava de te, nos recommenda %{action} immediatemente e activar le authentication bifactorial pro mantener tu conto secur.
       subject: Alcuno ha accedite a tu conto desde un nove adresse IP
-      title: Un nove initio de session
+      title: Un nove apertura de session
     warning:
       appeal: Submitter un appello
       appeal_description: Si tu crede que se tracta de un error, tu pote presentar un appello al personal de %{instance}.
@@ -1840,9 +1840,9 @@ ia:
         silence: Tu pote ancora usar tu conto ma solmente le personas qui ja te seque videra tu messages sur iste servitor, e tu pote esser excludite de varie functiones de discoperta. Nonobstante, altere personas pote ancora sequer te manualmente.
         suspend: Tu non pote plus usar tu conto, e tu profilo e altere datos non es plus accessibile. Tu pote ancora aperir session pro requestar un copia de reserva de tu datos usque lor elimination in circa 30 dies. Nos retenera certe datos de base pro impedir que tu evade le suspension.
       reason: 'Ration:'
-      statuses: 'Message citate:'
+      statuses: 'Messages citate:'
       subject:
-        delete_statuses: Tu messages sur %{acct} esseva removite
+        delete_statuses: Tu messages sur %{acct} ha essite removite
         disable: Tu conto %{acct} ha essite gelate
         mark_statuses_as_sensitive: Tu messages sur %{acct} ha essite marcate como sensibile
         none: Advertimento pro %{acct}
@@ -1858,7 +1858,7 @@ ia:
         silence: Conto limitate
         suspend: Conto suspendite
     welcome:
-      apps_android_action: Obtene lo sur Google Play
+      apps_android_action: Obtener lo sur Google Play
       apps_ios_action: Discargar sur le App Store
       apps_step: Discarga nostre applicationes official.
       apps_title: Applicationes de Mastodon
@@ -1870,7 +1870,7 @@ ia:
       explanation: Ecce alcun consilios pro initiar
       feature_action: Apprender plus
       feature_audience: Mastodon te presenta le possibilitate unic de gerer tu audientia sin intermediarios. Mastodon, installate sur tu proprie infrastructura, te permitte sequer, e esser sequite per, personas sur qualcunque altere servitor Mastodon in linea, e necuno lo controla salvo tu.
-      feature_audience_title: Crea tu auditorio in fiducia
+      feature_audience_title: Crea tu audientia in confidentia
       feature_control: Tu sape melio lo que tu vole vider sur tu fluxo de initio. Nulle algorithmos o annuncios dissipa tu tempore. Seque quicinque sur qualcunque servitor Mastodon desde un sol conto, recipe lor messages in ordine chronologic, e face te un angulo del internet ubi tu te senti a casa.
       feature_control_title: Mantene le controlo de tu proprie chronologia
       feature_creativity: Mastodon supporta messages con audio, video e imagines, descriptiones de accessibilitate, sondages, advertimentos de contento, avatares con animation, emojis personalisate, controlo de retalio de miniaturas, e plus, pro adjutar te a exprimer te in linea. Que tu publica tu arte, tu musica o tu podcast, Mastodon existe pro te.
@@ -1879,13 +1879,13 @@ ia:
       feature_moderation_title: Moderation como deberea esser
       follow_action: Sequer
       follow_step: Sequer personas interessante es le ration de esser de Mastodon.
-      follow_title: Personalisa tu fluxo de initio
+      follow_title: Personalisa tu fluxo principal
       follows_subtitle: Seque contos popular
       follows_title: Qui sequer
-      follows_view_more: Vider plus de personas a sequer
+      follows_view_more: Vider plus personas a sequer
       hashtags_recent_count:
         one: "%{people} persona in le passate duo dies"
-        other: "%{people} personas in le passate duo diea"
+        other: "%{people} personas in le passate duo dies"
       hashtags_subtitle: Explora le tendentias del passate 2 dies
       hashtags_title: Hashtags in tendentia
       hashtags_view_more: Vider plus de hashtags in tendentia
@@ -1929,4 +1929,4 @@ ia:
     not_enabled: Tu ancora non ha activate WebAuthn
     not_supported: Iste navigator non supporta claves de securitate
     otp_required: Pro usar le claves de securitate activa prime le authentication de duo factores.
-    registered_on: Registrate le %{date}
+    registered_on: Inscribite le %{date}
diff --git a/config/locales/lt.yml b/config/locales/lt.yml
index c29218c1f..9e60ddfe5 100644
--- a/config/locales/lt.yml
+++ b/config/locales/lt.yml
@@ -446,6 +446,7 @@ lt:
     instances:
       availability:
         title: Prieinamumas
+        warning: Paskutinis bandymas prisijungti prie šio serverio buvo nesėkmingas
       back_to_all: Visi
       back_to_limited: Apribotas
       back_to_warning: Įspėjimas
@@ -488,7 +489,7 @@ lt:
       inbox_url: Perdavimo URL
       pending: Laukiama perdavimo patvirtinimo
       save_and_enable: Išsaugoti ir įjungti
-      setup: Sukurti perdavimo ryšį
+      setup: Nustatyti perdavimo ryšį
       status: Statusas
       title: Perdavimai
     report_notes:
@@ -600,6 +601,8 @@ lt:
       elasticsearch_preset_single_node:
         action: Žiūrėti dokumentaciją
         message_html: Tavo Elasticsearch klasteris turi tik vieną mazgą, <code>ES_PRESET</code> turėtų būti nustatyta į <code>single_node_cluster</code>.
+      elasticsearch_running_check:
+        message_html: Nepavyko prijungti prie Elasticsearch. Patikrink, ar ji veikia, arba išjunk viso teksto paiešką.
     title: Administracija
     trends:
       allow: Leisti
diff --git a/config/locales/simple_form.ia.yml b/config/locales/simple_form.ia.yml
index d3237762e..7e364889d 100644
--- a/config/locales/simple_form.ia.yml
+++ b/config/locales/simple_form.ia.yml
@@ -3,40 +3,40 @@ ia:
   simple_form:
     hints:
       account:
-        discoverable: Tu messages public e tu profilo pote esser consiliate o recommendate in varie areas de Mastodon e tu profilo pote esser suggerite a altere usatores.
+        discoverable: Tu messages public e tu profilo pote esser mittite in evidentia o recommendate in varie areas de Mastodon e tu profilo pote esser suggerite a altere usatores.
         display_name: Tu prenomine e nomine de familia o tu pseudonymo.
-        fields: Tu pagina principal, pronomines, etate, toto lo que tu vole.
-        indexable: Tu messages public pote apparer in resultatos del recerca sur Mastodon. Illes qui ha interagite con tu messages totevia pote cercar les.
+        fields: Tu pagina principal, pronomines, etate, tote lo que tu vole.
+        indexable: Tu messages public pote apparer in le resultatos de recerca sur Mastodon. Le personas qui ha interagite con tu messages pote cercar los in omne caso.
         note: 'Tu pote @mentionar altere personas o #hashtags.'
-        show_collections: Le personas potera navigar per tu sequites e sequaces. Le personas potera navigar per tu sequites e sequaces.
-        unlocked: Le personas potera sequer te sin requestar approbation. Dismarca si tu desira revider le requestas de sequer e selige si acceptar o rejectar nove sequaces.
+        show_collections: Le personas potera percurrer tu sequites e sequitores. Le personas que tu seque videra que tu les seque in omne caso.
+        unlocked: Le personas potera sequer te sin requestar approbation. Dismarca si tu vole revider le requestas de sequimento e seliger si acceptar o rejectar nove sequitores.
       account_alias:
-        acct: Specifica le nomine_de_usator@dominio del conto ab que tu vole mover
+        acct: Specifica le nomine_de_usator@dominio del conto desde le qual tu vole migrar
       account_migration:
-        acct: Specifica le nomine_de_usator@dominio del conto a que tu vole mover
+        acct: Specifica le nomine_de_usator@dominio del conto al qual tu vole migrar
       account_warning_preset:
-        text: Tu pote usar le syntaxe de message, tal como URLs, hashtags e mentiones
+        text: Tu pote usar le syntaxe de messages, como URLs, hashtags e mentiones
         title: Optional. Non visibile al destinatario
       admin_account_action:
-        include_statuses: Le usator videra que messages ha causate le action o aviso de moderation
-        send_email_notification: Le usator recipera un explication de cosa eveniva con lor conto
-        text_html: Optional. Tu pote usar le syntaxe de message. Tu pote <a href="%{path}">adder avisos preconfigurate</a> pro sparniar tempore
-        type_html: Selige lo que tu vole facer con <strong>%{acct}</strong>
+        include_statuses: Le usator videra qual messages ha causate le action o advertimento de moderation
+        send_email_notification: Le usator recipera un explication de lo que ha evenite con su conto
+        text_html: Optional. Tu pote usar le syntaxe de messages. Tu pote <a href="%{path}">adder advertimentos preconfigurate</a> pro sparniar tempore
+        type_html: Selige lo que facer con <strong>%{acct}</strong>
         types:
-          disable: Impedir al usator de usar lor conto, sin deler o celar lor contentos.
-          none: Usar lo pro inviar un aviso al usator, sin discatenar ulle altere action.
-          sensitive: Fortiar tote le annexos multimedial de iste usator a esser signalate como sensibile.
-          silence: Impedir al usator de poter publicar messages con public visibilitate, celar lor messages e notificationes ab gente non sequente illes. Clauder tote le reportos contra iste conto.
-          suspend: Impedir ulle interaction de o a iste conto e deler su contentos. Reversibile intra 30 dies. Clauder tote le reportos contra iste conto.
-        warning_preset_id: Optional. Tu pote ancora adder personal texto a fin del preconfigurate
+          disable: Impedir al usator de usar su conto, sin deler o celar su contento.
+          none: Usa isto pro inviar un advertimento al usator, sin prender alcun altere mesura.
+          sensitive: Fortiar tote le annexos multimedial de iste usator a esser marcate como sensibile.
+          silence: Impedir al usator de publicar messages con visibilitate public, celante su messages e notificationes a qui non le seque. Claude tote le reportos contra iste conto.
+          suspend: Impedir tote interaction desde o verso iste conto e deler su contento. Reversibile intra 30 dies. Claude tote le reportos contra iste conto.
+        warning_preset_id: Optional. Tu pote ancora adder texto personalisate al fin del preconfigurate
       announcement:
-        all_day: Si marcate, solo le datas del campo tempore sera monstrate
-        ends_at: Le annuncio sera automaticamente obscurate a iste tempore
-        scheduled_at: Lassar blanc pro publicar le annuncio immediatemente
-        starts_at: Optional. In caso tu annuncio es ligate con un specific campo tempore
-        text: Tu pote usar le syntaxe de message. Presta attention al spatio que le annuncio occupara sur le schermo de usator
+        all_day: Si marcate, solmente le datas del intervallo de tempore essera monstrate
+        ends_at: Optional. Le annuncio essera automaticamente retirate del publication a iste tempore
+        scheduled_at: Lassa vacue pro publicar le annuncio immediatemente
+        starts_at: Optional. In caso que tu annuncio es ligate a un intervallo specific de tempore
+        text: Tu pote usar le syntaxe de messages. Presta attention al spatio que le annuncio occupara sur le schermo del usator
       appeal:
-        text: Tu pote solo appellar te un vice
+        text: Tu pote solo appellar contra un sanction un vice
       defaults:
         autofollow: Illes qui se inscribe per le invitation automaticamente devenira tu sequaces
         avatar: WEBP, PNG, GIF or JPG. Al maximo %{size}. Sera diminuite a %{dimensions}px
@@ -64,7 +64,7 @@ ia:
         username: Tu pote usar litteras, numeros e tractos de sublineamento
         whole_word: Quando le parola o expression clave es solo alphanumeric, illo sera solo applicate si illo concorda con tote le parola
       domain_allow:
-        domain: Iste dominio potera reportar datos ab iste servitor e le datos in ingresso ab illo sera processate e immagazinate
+        domain: Iste dominio potera extraher datos de iste servitor e le datos entrante de illo essera processate e immagazinate
       email_domain_block:
         domain: Isto pote esser le nomine de dominio que apparera in le adresse email o le registration MX que illo usa. Illos sera verificate durante le inscription.
         with_dns_records: Un tentativa sera facite pro resolver le registrationes de DNS del dominio date e le resultatos sera alsi blocate
@@ -158,7 +158,7 @@ ia:
         text: Texto predefinite
         title: Titulo
       admin_account_action:
-        include_statuses: Includer messages reportate in le email
+        include_statuses: Includer le messages reportate in le e-mail
         send_email_notification: Notificar le usator per e-mail
         text: Advertimento personalisate
         type: Action
diff --git a/config/locales/simple_form.lv.yml b/config/locales/simple_form.lv.yml
index 888e08b65..017acd0a5 100644
--- a/config/locales/simple_form.lv.yml
+++ b/config/locales/simple_form.lv.yml
@@ -78,6 +78,7 @@ lv:
       form_admin_settings:
         activity_api_enabled: Vietēji publicēto ziņu, aktīvo lietotāju un jauno reģistrāciju skaits nedēļas kopās
         app_icon: WEBP, PNG, GIF vai JPG. Mobilajās ierīcēs aizstāj noklusējuma lietotnes ikonu ar pielāgotu.
+        backups_retention_period: Lietotājiem ir iespēja izveidot savu ierakstu arhīvu lejupielādēšanai vēlāk. Kad iestatīta pozitīva vērtība, šie arhīvi tiks automātiski izdzēsti no krātuves pēc norādītā dienu skaita.
         bootstrap_timeline_accounts: Šie konti tiks piesprausti jauno lietotāju ieteikumu augšdaļā.
         closed_registrations_message: Tiek rādīts, kad reģistrēšanās ir slēgta
         custom_css: Vari lietot pielāgotus stilus Mastodon tīmekļa versijā.

From 048f9b9d45b1d313de1d427956a2d354273a5d85 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 5 Jun 2024 09:56:52 +0200
Subject: [PATCH 012/133] chore(deps): update dependency pghero to v3.5.0
 (#30393)

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 597005b6b..b5192c925 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -586,7 +586,7 @@ GEM
     pastel (0.8.0)
       tty-color (~> 0.5)
     pg (1.5.6)
-    pghero (3.4.1)
+    pghero (3.5.0)
       activerecord (>= 6)
     premailer (1.23.0)
       addressable

From eef2cc054faaa808cb014d644317fbc398f6c75f Mon Sep 17 00:00:00 2001
From: Emelia Smith <ThisIsMissEm@users.noreply.github.com>
Date: Wed, 5 Jun 2024 10:06:06 +0200
Subject: [PATCH 013/133] Add url validation to Web::PushSubscription endpoints
 (#30540)

---
 app/models/web/push_subscription.rb             |  2 +-
 spec/requests/api/v1/push/subscriptions_spec.rb | 15 ++++++++++++++-
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/app/models/web/push_subscription.rb b/app/models/web/push_subscription.rb
index a3a2ec3f0..1860f0aa3 100644
--- a/app/models/web/push_subscription.rb
+++ b/app/models/web/push_subscription.rb
@@ -21,7 +21,7 @@ class Web::PushSubscription < ApplicationRecord
 
   has_one :session_activation, foreign_key: 'web_push_subscription_id', inverse_of: :web_push_subscription, dependent: nil
 
-  validates :endpoint, presence: true
+  validates :endpoint, presence: true, url: true
   validates :key_p256dh, presence: true
   validates :key_auth, presence: true
 
diff --git a/spec/requests/api/v1/push/subscriptions_spec.rb b/spec/requests/api/v1/push/subscriptions_spec.rb
index 700250ee2..82ea308cd 100644
--- a/spec/requests/api/v1/push/subscriptions_spec.rb
+++ b/spec/requests/api/v1/push/subscriptions_spec.rb
@@ -4,10 +4,11 @@ require 'rails_helper'
 
 describe 'API V1 Push Subscriptions' do
   let(:user) { Fabricate(:user) }
+  let(:endpoint) { 'https://fcm.googleapis.com/fcm/send/fiuH06a27qE:APA91bHnSiGcLwdaxdyqVXNDR9w1NlztsHb6lyt5WDKOC_Z_Q8BlFxQoR8tWFSXUIDdkyw0EdvxTu63iqamSaqVSevW5LfoFwojws8XYDXv_NRRLH6vo2CdgiN4jgHv5VLt2A8ah6lUX' }
   let(:create_payload) do
     {
       subscription: {
-        endpoint: 'https://fcm.googleapis.com/fcm/send/fiuH06a27qE:APA91bHnSiGcLwdaxdyqVXNDR9w1NlztsHb6lyt5WDKOC_Z_Q8BlFxQoR8tWFSXUIDdkyw0EdvxTu63iqamSaqVSevW5LfoFwojws8XYDXv_NRRLH6vo2CdgiN4jgHv5VLt2A8ah6lUX',
+        endpoint: endpoint,
         keys: {
           p256dh: 'BEm_a0bdPDhf0SOsrnB2-ategf1hHoCnpXgQsFj5JCkcoMrMt2WHoPfEYOYPzOIs9mZE8ZUaD7VA5vouy0kEkr8=',
           auth: 'eH_C8rq2raXqlcBVDa1gLg==',
@@ -63,6 +64,18 @@ describe 'API V1 Push Subscriptions' do
       expect(endpoint_push_subscriptions.count)
         .to eq(1)
     end
+
+    context 'with invalid endpoint URL' do
+      let(:endpoint) { 'app://example.foo' }
+
+      it 'returns a validation error' do
+        subject
+
+        expect(response).to have_http_status(422)
+        expect(endpoint_push_subscriptions.count).to eq(0)
+        expect(endpoint_push_subscription).to be_nil
+      end
+    end
   end
 
   describe 'PUT /api/v1/push/subscription' do

From 20e490ba7e996033c549e6d1de7826b86ac2988d Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 5 Jun 2024 15:06:03 +0200
Subject: [PATCH 014/133] fix(deps): update dependency cssnano to v7.0.2
 (#30560)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 yarn.lock | 94 +++++++++++++++++++++++++++----------------------------
 1 file changed, 47 insertions(+), 47 deletions(-)

diff --git a/yarn.lock b/yarn.lock
index bad4fa01e..7c36984cd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6715,9 +6715,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"cssnano-preset-default@npm:^7.0.1":
-  version: 7.0.1
-  resolution: "cssnano-preset-default@npm:7.0.1"
+"cssnano-preset-default@npm:^7.0.2":
+  version: 7.0.2
+  resolution: "cssnano-preset-default@npm:7.0.2"
   dependencies:
     browserslist: "npm:^4.23.0"
     css-declaration-sorter: "npm:^7.2.0"
@@ -6729,12 +6729,12 @@ __metadata:
     postcss-discard-duplicates: "npm:^7.0.0"
     postcss-discard-empty: "npm:^7.0.0"
     postcss-discard-overridden: "npm:^7.0.0"
-    postcss-merge-longhand: "npm:^7.0.0"
-    postcss-merge-rules: "npm:^7.0.0"
+    postcss-merge-longhand: "npm:^7.0.1"
+    postcss-merge-rules: "npm:^7.0.1"
     postcss-minify-font-values: "npm:^7.0.0"
     postcss-minify-gradients: "npm:^7.0.0"
     postcss-minify-params: "npm:^7.0.0"
-    postcss-minify-selectors: "npm:^7.0.0"
+    postcss-minify-selectors: "npm:^7.0.1"
     postcss-normalize-charset: "npm:^7.0.0"
     postcss-normalize-display-values: "npm:^7.0.0"
     postcss-normalize-positions: "npm:^7.0.0"
@@ -6747,11 +6747,11 @@ __metadata:
     postcss-ordered-values: "npm:^7.0.0"
     postcss-reduce-initial: "npm:^7.0.0"
     postcss-reduce-transforms: "npm:^7.0.0"
-    postcss-svgo: "npm:^7.0.0"
-    postcss-unique-selectors: "npm:^7.0.0"
+    postcss-svgo: "npm:^7.0.1"
+    postcss-unique-selectors: "npm:^7.0.1"
   peerDependencies:
     postcss: ^8.4.31
-  checksum: 10c0/bee65239d25de2ba87e85b4091cbc1cac9ba1b57c9f803dff5a71ea8a55a885045805840dd732be284c28cca6343dece37fc76d7096aba37cfa02eff2ee7714c
+  checksum: 10c0/7c66240594c1d7a0cc761e755236228b17251455aa57abc45be0631f7de0fde070c23b0e41ffa200d39cd8351718514217d8c7a8cc4f06b54289dc1d555dfeb2
   languageName: node
   linkType: hard
 
@@ -6765,14 +6765,14 @@ __metadata:
   linkType: hard
 
 "cssnano@npm:^7.0.0":
-  version: 7.0.1
-  resolution: "cssnano@npm:7.0.1"
+  version: 7.0.2
+  resolution: "cssnano@npm:7.0.2"
   dependencies:
-    cssnano-preset-default: "npm:^7.0.1"
+    cssnano-preset-default: "npm:^7.0.2"
     lilconfig: "npm:^3.1.1"
   peerDependencies:
     postcss: ^8.4.31
-  checksum: 10c0/8b17d13efe98ec2db2fbde9ca24e91842b9afe2f80becc5e4271ee1170d77cf73eed3cdc2f35ed51bacdeac763ff85db45ae8e9627a8862bf01d457a819a640e
+  checksum: 10c0/ad43d8c2e96fa1022fc5103064e4f08da3fdc5501a946d455edf0b81981b58cd06ad2d3f0c68d666e2b687c10c02ffbb383252aa34da0ddc3bd4d075f4a922c7
   languageName: node
   linkType: hard
 
@@ -13458,29 +13458,29 @@ __metadata:
   languageName: node
   linkType: hard
 
-"postcss-merge-longhand@npm:^7.0.0":
-  version: 7.0.0
-  resolution: "postcss-merge-longhand@npm:7.0.0"
+"postcss-merge-longhand@npm:^7.0.1":
+  version: 7.0.1
+  resolution: "postcss-merge-longhand@npm:7.0.1"
   dependencies:
     postcss-value-parser: "npm:^4.2.0"
-    stylehacks: "npm:^7.0.0"
+    stylehacks: "npm:^7.0.1"
   peerDependencies:
     postcss: ^8.4.31
-  checksum: 10c0/5f814f396a5107dcb5e74c2d4e55ebcd03b9bc2b3619ed7aea63a441854023ce349bc371d30aec1ac33a375139afac02709e7721e055b5e624701ac6576e8a10
+  checksum: 10c0/e3d20502e65c82c9c4ba2e400bd093ee6b9c1b0019618ccd50eb40ef0e496206dd518c7e655a6986d780d5a52576e32e8f310d00484b15f67c77664a148df6eb
   languageName: node
   linkType: hard
 
-"postcss-merge-rules@npm:^7.0.0":
-  version: 7.0.0
-  resolution: "postcss-merge-rules@npm:7.0.0"
+"postcss-merge-rules@npm:^7.0.1":
+  version: 7.0.1
+  resolution: "postcss-merge-rules@npm:7.0.1"
   dependencies:
     browserslist: "npm:^4.23.0"
     caniuse-api: "npm:^3.0.0"
     cssnano-utils: "npm:^5.0.0"
-    postcss-selector-parser: "npm:^6.0.16"
+    postcss-selector-parser: "npm:^6.1.0"
   peerDependencies:
     postcss: ^8.4.31
-  checksum: 10c0/d9cb3a4e55db57aa7ba0bb1caefb82db93c8493d2b3db66091dae9d5794ca04729e660115765ff254d0eb960e4db037f6c5b92562b396b05216888d12acc08e0
+  checksum: 10c0/d380c162327e7aad59efb55cfddc5ec4e3bf51d18b07b832fdd876505279bac3cb44511ada8e1e1992428dcec4f64c7ec457b6ff9109063c5a61abf4b59b7176
   languageName: node
   linkType: hard
 
@@ -13521,14 +13521,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"postcss-minify-selectors@npm:^7.0.0":
-  version: 7.0.0
-  resolution: "postcss-minify-selectors@npm:7.0.0"
+"postcss-minify-selectors@npm:^7.0.1":
+  version: 7.0.1
+  resolution: "postcss-minify-selectors@npm:7.0.1"
   dependencies:
-    postcss-selector-parser: "npm:^6.0.16"
+    postcss-selector-parser: "npm:^6.1.0"
   peerDependencies:
     postcss: ^8.4.31
-  checksum: 10c0/6baf0ea71b8dfd01bdb5b516d01aa00244c55cad8d9c674358d735cef2a6aca6586dd480d419cc8d3f470e6d2d7d19354592044f19766993caf9800d3d7e0d36
+  checksum: 10c0/a8ff69657fb1808d8f0f105b13a416426902d6f498a4b7ebb3b96b4b9149e97ee2e2ad6cd98108e2f0b8f781701724e6c51e120e215cee3e40c2d7a2afac755a
   languageName: node
   linkType: hard
 
@@ -13898,26 +13898,26 @@ __metadata:
   languageName: node
   linkType: hard
 
-"postcss-svgo@npm:^7.0.0":
-  version: 7.0.0
-  resolution: "postcss-svgo@npm:7.0.0"
+"postcss-svgo@npm:^7.0.1":
+  version: 7.0.1
+  resolution: "postcss-svgo@npm:7.0.1"
   dependencies:
     postcss-value-parser: "npm:^4.2.0"
-    svgo: "npm:^3.2.0"
+    svgo: "npm:^3.3.2"
   peerDependencies:
     postcss: ^8.4.31
-  checksum: 10c0/0e724069b5de83aa2b8f8a4746cb60cb663e0a8bbab0e4ba995649cb0562205af57d1f54b89fb90d8ae04a4b7ac3ac6e3751afffc3cff697cb19f7a36b71b195
+  checksum: 10c0/7c7b177e6f4e2a3e9ada76d53afa02e08d900c8ac15600ba9daa80480269d538405e544bd8091bc5eb7529173a476896fad885a72a247258265424b29a9195ed
   languageName: node
   linkType: hard
 
-"postcss-unique-selectors@npm:^7.0.0":
-  version: 7.0.0
-  resolution: "postcss-unique-selectors@npm:7.0.0"
+"postcss-unique-selectors@npm:^7.0.1":
+  version: 7.0.1
+  resolution: "postcss-unique-selectors@npm:7.0.1"
   dependencies:
-    postcss-selector-parser: "npm:^6.0.16"
+    postcss-selector-parser: "npm:^6.1.0"
   peerDependencies:
     postcss: ^8.4.31
-  checksum: 10c0/33b532ad0e9271c5a379859e18adfdc72986bb538672cc0fbc06295d824f82dba3f7b57264e18a3214901bc5244ff5408d28b530374d24a088507287c7f520ce
+  checksum: 10c0/6352d71ce2f65265f545831c2ce3686bd71961d08a2247c545d717d93d23b1eb08bb986efc11194d31970eea4cb42207b9aa9a3f4666d75492a6cbf1493cf466
   languageName: node
   linkType: hard
 
@@ -16399,15 +16399,15 @@ __metadata:
   languageName: node
   linkType: hard
 
-"stylehacks@npm:^7.0.0":
-  version: 7.0.0
-  resolution: "stylehacks@npm:7.0.0"
+"stylehacks@npm:^7.0.1":
+  version: 7.0.1
+  resolution: "stylehacks@npm:7.0.1"
   dependencies:
     browserslist: "npm:^4.23.0"
-    postcss-selector-parser: "npm:^6.0.16"
+    postcss-selector-parser: "npm:^6.1.0"
   peerDependencies:
     postcss: ^8.4.31
-  checksum: 10c0/c1c0231974ab7922af3a535a9cb78bfe84997767da7defe111cc76d7f10c9e139fe8cb0f9d5bea87b0c0cc0166c82a6ec98a3d6242d7e29ef90adceecfd330ae
+  checksum: 10c0/538d5d9c6d84906efad3706f0873b85b67fa224f17759b122bad3d60f2928c31204fd658dd16ec952bf54858a3aeaef4643e040c04030459285ce1b13c4cae91
   languageName: node
   linkType: hard
 
@@ -16632,9 +16632,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"svgo@npm:^3.2.0":
-  version: 3.2.0
-  resolution: "svgo@npm:3.2.0"
+"svgo@npm:^3.3.2":
+  version: 3.3.2
+  resolution: "svgo@npm:3.3.2"
   dependencies:
     "@trysound/sax": "npm:0.2.0"
     commander: "npm:^7.2.0"
@@ -16645,7 +16645,7 @@ __metadata:
     picocolors: "npm:^1.0.0"
   bin:
     svgo: ./bin/svgo
-  checksum: 10c0/28fa9061ccbcf2e3616d48d1feb613aaa05f8f290a329beb0e585914f1864385152934a7d4d683a4609fafbae3d51666633437c359c5c5ef74fb58ad09092a7c
+  checksum: 10c0/a6badbd3d1d6dbb177f872787699ab34320b990d12e20798ecae915f0008796a0f3c69164f1485c9def399e0ce0a5683eb4a8045e51a5e1c364bb13a0d9f79e1
   languageName: node
   linkType: hard
 

From 5f15a892fa4f01ef9bcf223ec6798b8a9d9945ed Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Wed, 5 Jun 2024 21:15:39 +0200
Subject: [PATCH 015/133] Add support for libvips in addition to ImageMagick
 (#30090)

Co-authored-by: Claire <claire.github-309c@sitedethib.com>
---
 .devcontainer/Dockerfile                      |   2 +-
 .github/actions/setup-ruby/action.yml         |   2 +-
 .github/workflows/test-ruby.yml               |  93 +++++++++++-
 Dockerfile                                    |   4 +-
 Gemfile                                       |   1 +
 Gemfile.lock                                  |   3 +
 .../dimension/software_versions_dimension.rb  |  13 +-
 app/models/concerns/attachmentable.rb         |   2 +-
 app/models/preview_card.rb                    |   6 +-
 config/application.rb                         |  10 +-
 config/initializers/vips.rb                   |  27 ++++
 lib/paperclip/blurhash_transcoder.rb          |  20 ++-
 lib/paperclip/color_extractor.rb              |  81 ++++++++--
 lib/paperclip/vips_lazy_thumbnail.rb          | 141 ++++++++++++++++++
 spec/fixtures/files/monochrome.png            | Bin 0 -> 9216 bytes
 spec/models/media_attachment_spec.rb          |  10 +-
 16 files changed, 392 insertions(+), 23 deletions(-)
 create mode 100644 config/initializers/vips.rb
 create mode 100644 lib/paperclip/vips_lazy_thumbnail.rb
 create mode 100644 spec/fixtures/files/monochrome.png

diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 994a41d05..9d8fa2702 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -9,7 +9,7 @@ RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSI
 
 # [Optional] Uncomment this section to install additional OS packages.
 RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
-    && apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libpam-dev
+    && apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libvips42 libpam-dev
 
 # [Optional] Uncomment this line to install additional gems.
 RUN gem install foreman
diff --git a/.github/actions/setup-ruby/action.yml b/.github/actions/setup-ruby/action.yml
index 3a6fba940..3e232f134 100644
--- a/.github/actions/setup-ruby/action.yml
+++ b/.github/actions/setup-ruby/action.yml
@@ -14,7 +14,7 @@ runs:
       shell: bash
       run: |
         sudo apt-get update
-        sudo apt-get install -y libicu-dev libidn11-dev ${{ inputs.additional-system-dependencies }}
+        sudo apt-get install -y libicu-dev libidn11-dev libvips42 ${{ inputs.additional-system-dependencies }}
 
     - name: Set up Ruby
       uses: ruby/setup-ruby@v1
diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml
index 2bfa59e6b..5f2297381 100644
--- a/.github/workflows/test-ruby.yml
+++ b/.github/workflows/test-ruby.yml
@@ -133,7 +133,7 @@ jobs:
         uses: ./.github/actions/setup-ruby
         with:
           ruby-version: ${{ matrix.ruby-version}}
-          additional-system-dependencies: ffmpeg imagemagick libpam-dev
+          additional-system-dependencies: ffmpeg libpam-dev
 
       - name: Load database schema
         run: './bin/rails db:create db:schema:load db:seed'
@@ -148,6 +148,93 @@ jobs:
         env:
           CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
 
+  test-libvips:
+    name: Libvips tests
+    runs-on: ubuntu-24.04
+
+    needs:
+      - build
+
+    services:
+      postgres:
+        image: postgres:14-alpine
+        env:
+          POSTGRES_PASSWORD: postgres
+          POSTGRES_USER: postgres
+        options: >-
+          --health-cmd pg_isready
+          --health-interval 10s
+          --health-timeout 5s
+          --health-retries 5
+        ports:
+          - 5432:5432
+
+      redis:
+        image: redis:7-alpine
+        options: >-
+          --health-cmd "redis-cli ping"
+          --health-interval 10s
+          --health-timeout 5s
+          --health-retries 5
+        ports:
+          - 6379:6379
+
+    env:
+      DB_HOST: localhost
+      DB_USER: postgres
+      DB_PASS: postgres
+      DISABLE_SIMPLECOV: ${{ matrix.ruby-version != '.ruby-version' }}
+      RAILS_ENV: test
+      ALLOW_NOPAM: true
+      PAM_ENABLED: true
+      PAM_DEFAULT_SERVICE: pam_test
+      PAM_CONTROLLED_SERVICE: pam_test_controlled
+      OIDC_ENABLED: true
+      OIDC_SCOPE: read
+      SAML_ENABLED: true
+      CAS_ENABLED: true
+      BUNDLE_WITH: 'pam_authentication test'
+      GITHUB_RSPEC: ${{ matrix.ruby-version == '.ruby-version' && github.event.pull_request && 'true' }}
+      MASTODON_USE_LIBVIPS: true
+
+    strategy:
+      fail-fast: false
+      matrix:
+        ruby-version:
+          - '3.1'
+          - '3.2'
+          - '.ruby-version'
+    steps:
+      - uses: actions/checkout@v4
+
+      - uses: actions/download-artifact@v4
+        with:
+          path: './'
+          name: ${{ github.sha }}
+
+      - name: Expand archived asset artifacts
+        run: |
+          tar xvzf artifacts.tar.gz
+
+      - name: Set up Ruby environment
+        uses: ./.github/actions/setup-ruby
+        with:
+          ruby-version: ${{ matrix.ruby-version}}
+          additional-system-dependencies: ffmpeg libpam-dev libyaml-dev
+
+      - name: Load database schema
+        run: './bin/rails db:create db:schema:load db:seed'
+
+      - run: bin/rspec --tag paperclip_processing
+
+      - name: Upload coverage reports to Codecov
+        if: matrix.ruby-version == '.ruby-version'
+        uses: codecov/codecov-action@v4
+        with:
+          files: coverage/lcov/mastodon.lcov
+        env:
+          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+
   test-e2e:
     name: End to End testing
     runs-on: ubuntu-latest
@@ -209,7 +296,7 @@ jobs:
         uses: ./.github/actions/setup-ruby
         with:
           ruby-version: ${{ matrix.ruby-version}}
-          additional-system-dependencies: ffmpeg imagemagick
+          additional-system-dependencies: ffmpeg
 
       - name: Set up Javascript environment
         uses: ./.github/actions/setup-javascript
@@ -329,7 +416,7 @@ jobs:
         uses: ./.github/actions/setup-ruby
         with:
           ruby-version: ${{ matrix.ruby-version}}
-          additional-system-dependencies: ffmpeg imagemagick
+          additional-system-dependencies: ffmpeg
 
       - name: Set up Javascript environment
         uses: ./.github/actions/setup-javascript
diff --git a/Dockerfile b/Dockerfile
index c90d5dc98..6d342db43 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -43,6 +43,8 @@ ENV \
 # Apply Mastodon version information
   MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \
   MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \
+# Enable libvips
+  MASTODON_USE_LIBVIPS=true \
 # Apply Mastodon static files and YJIT options
   RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \
   RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \
@@ -97,7 +99,7 @@ RUN \
     curl \
     ffmpeg \
     file \
-    imagemagick \
+    libvips42 \
     libjemalloc2 \
     patchelf \
     procps \
diff --git a/Gemfile b/Gemfile
index d9de33182..ca32d0cca 100644
--- a/Gemfile
+++ b/Gemfile
@@ -23,6 +23,7 @@ gem 'fog-core', '<= 2.4.0'
 gem 'fog-openstack', '~> 1.0', require: false
 gem 'kt-paperclip', '~> 7.2'
 gem 'md-paperclip-azure', '~> 2.2', require: false
+gem 'ruby-vips', '~> 2.2', require: false
 
 gem 'active_model_serializers', '~> 0.10'
 gem 'addressable', '~> 2.8'
diff --git a/Gemfile.lock b/Gemfile.lock
index b5192c925..bf5340a5b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -763,6 +763,8 @@ GEM
     ruby-saml (1.16.0)
       nokogiri (>= 1.13.10)
       rexml
+    ruby-vips (2.2.1)
+      ffi (~> 1.12)
     ruby2_keywords (0.0.5)
     rubyzip (2.3.2)
     rufus-scheduler (3.9.1)
@@ -1023,6 +1025,7 @@ DEPENDENCIES
   rubocop-rspec
   ruby-prof
   ruby-progressbar (~> 1.13)
+  ruby-vips (~> 2.2)
   rubyzip (~> 2.3)
   sanitize (~> 6.0)
   scenic (~> 1.7)
diff --git a/app/lib/admin/metrics/dimension/software_versions_dimension.rb b/app/lib/admin/metrics/dimension/software_versions_dimension.rb
index 97cdaf589..9dd0d393f 100644
--- a/app/lib/admin/metrics/dimension/software_versions_dimension.rb
+++ b/app/lib/admin/metrics/dimension/software_versions_dimension.rb
@@ -10,7 +10,7 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim
   protected
 
   def perform_query
-    [mastodon_version, ruby_version, postgresql_version, redis_version, elasticsearch_version].compact
+    [mastodon_version, ruby_version, postgresql_version, redis_version, elasticsearch_version, libvips_version].compact
   end
 
   def mastodon_version
@@ -71,6 +71,17 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim
     nil
   end
 
+  def libvips_version
+    return unless Rails.configuration.x.use_vips
+
+    {
+      key: 'libvips',
+      human_key: 'libvips',
+      value: Vips.version_string,
+      human_value: Vips.version_string,
+    }
+  end
+
   def redis_info
     @redis_info ||= if redis.is_a?(Redis::Namespace)
                       redis.redis.info
diff --git a/app/models/concerns/attachmentable.rb b/app/models/concerns/attachmentable.rb
index f457f5822..a83e178fc 100644
--- a/app/models/concerns/attachmentable.rb
+++ b/app/models/concerns/attachmentable.rb
@@ -69,7 +69,7 @@ module Attachmentable
     original_extension       = Paperclip::Interpolations.extension(attachment, :original)
     proper_extension         = extensions_for_mime_type.first.to_s
     extension                = extensions_for_mime_type.include?(original_extension) ? original_extension : proper_extension
-    extension                = 'jpeg' if extension == 'jpe'
+    extension                = 'jpeg' if ['jpe', 'jfif'].include?(extension)
 
     extension
   end
diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb
index 11fdd9d88..cbfc39378 100644
--- a/app/models/preview_card.rb
+++ b/app/models/preview_card.rb
@@ -57,7 +57,11 @@ class PreviewCard < ApplicationRecord
   has_one :trend, class_name: 'PreviewCardTrend', inverse_of: :preview_card, dependent: :destroy
   belongs_to :author_account, class_name: 'Account', optional: true
 
-  has_attached_file :image, processors: [:thumbnail, :blurhash_transcoder], styles: ->(f) { image_styles(f) }, convert_options: { all: '-quality 90 +profile "!icc,*" +set date:modify +set date:create +set date:timestamp' }, validate_media_type: false
+  has_attached_file :image,
+                    processors: [Rails.configuration.x.use_vips ? :lazy_thumbnail : :thumbnail, :blurhash_transcoder],
+                    styles: ->(f) { image_styles(f) },
+                    convert_options: { all: '-quality 90 +profile "!icc,*" +set date:modify +set date:create +set date:timestamp' },
+                    validate_media_type: false
 
   validates :url, presence: true, uniqueness: true, url: true
   validates_attachment_content_type :image, content_type: IMAGE_MIME_TYPES
diff --git a/config/application.rb b/config/application.rb
index a8e313069..069eb3774 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -27,7 +27,7 @@ require_relative '../lib/sanitize_ext/sanitize_config'
 require_relative '../lib/redis/namespace_extensions'
 require_relative '../lib/paperclip/url_generator_extensions'
 require_relative '../lib/paperclip/attachment_extensions'
-require_relative '../lib/paperclip/lazy_thumbnail'
+
 require_relative '../lib/paperclip/gif_transcoder'
 require_relative '../lib/paperclip/media_type_spoof_detector_extensions'
 require_relative '../lib/paperclip/transcoder'
@@ -100,6 +100,14 @@ module Mastodon
 
     config.before_configuration do
       require 'mastodon/redis_config'
+
+      config.x.use_vips = ENV['MASTODON_USE_LIBVIPS'] == 'true'
+
+      if config.x.use_vips
+        require_relative '../lib/paperclip/vips_lazy_thumbnail'
+      else
+        require_relative '../lib/paperclip/lazy_thumbnail'
+      end
     end
 
     config.to_prepare do
diff --git a/config/initializers/vips.rb b/config/initializers/vips.rb
new file mode 100644
index 000000000..25a17b2a1
--- /dev/null
+++ b/config/initializers/vips.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+if Rails.configuration.x.use_vips
+  ENV['VIPS_BLOCK_UNTRUSTED'] = 'true'
+
+  require 'vips'
+
+  abort('Incompatible libvips version, please install libvips >= 8.13') unless Vips.at_least_libvips?(8, 13)
+
+  Vips.block('VipsForeign', true)
+
+  %w(
+    VipsForeignLoadNsgif
+    VipsForeignLoadJpeg
+    VipsForeignLoadPng
+    VipsForeignLoadWebp
+    VipsForeignLoadHeif
+    VipsForeignSavePng
+    VipsForeignSaveSpng
+    VipsForeignSaveJpeg
+    VipsForeignSaveWebp
+  ).each do |operation|
+    Vips.block(operation, false)
+  end
+
+  Vips.block_untrusted(true)
+end
diff --git a/lib/paperclip/blurhash_transcoder.rb b/lib/paperclip/blurhash_transcoder.rb
index c22c20c57..e9cecef50 100644
--- a/lib/paperclip/blurhash_transcoder.rb
+++ b/lib/paperclip/blurhash_transcoder.rb
@@ -5,12 +5,26 @@ module Paperclip
     def make
       return @file unless options[:style] == :small || options[:blurhash]
 
-      pixels   = convert(':source -depth 8 RGB:-', source: File.expand_path(@file.path)).unpack('C*')
-      geometry = options.fetch(:file_geometry_parser).from_file(@file)
+      width, height, data = blurhash_params
+      # Guard against segfaults if data has unexpected size
+      raise RangeError("Invalid image data size (expected #{width * height * 3}, got #{data.size})") if data.size != width * height * 3 # TODO: should probably be another exception type
 
-      attachment.instance.blurhash = Blurhash.encode(geometry.width, geometry.height, pixels, **(options[:blurhash] || {}))
+      attachment.instance.blurhash = Blurhash.encode(width, height, data, **(options[:blurhash] || {}))
 
       @file
     end
+
+    private
+
+    def blurhash_params
+      if Rails.configuration.x.use_vips
+        image = Vips::Image.thumbnail(@file.path, 100)
+        [image.width, image.height, image.colourspace(:srgb).extract_band(0, n: 3).to_a.flatten]
+      else
+        pixels   = convert(':source -depth 8 RGB:-', source: File.expand_path(@file.path)).unpack('C*')
+        geometry = options.fetch(:file_geometry_parser).from_file(@file)
+        [geometry.width, geometry.height, pixels]
+      end
+    end
   end
 end
diff --git a/lib/paperclip/color_extractor.rb b/lib/paperclip/color_extractor.rb
index d2f7e7c60..b5992f90b 100644
--- a/lib/paperclip/color_extractor.rb
+++ b/lib/paperclip/color_extractor.rb
@@ -7,15 +7,10 @@ module Paperclip
     MIN_CONTRAST        = 3.0
     ACCENT_MIN_CONTRAST = 2.0
     FREQUENCY_THRESHOLD = 0.01
+    BINS = 10
 
     def make
-      depth = 8
-
-      # Determine background palette by getting colors close to the image's edge only
-      background_palette = palette_from_histogram(convert(':source -alpha set -gravity Center -region 75%x75% -fill None -colorize 100% -alpha transparent +region -format %c -colors :quantity -depth :depth histogram:info:', source: File.expand_path(@file.path), quantity: 10, depth: depth), 10)
-
-      # Determine foreground palette from the whole image
-      foreground_palette = palette_from_histogram(convert(':source -format %c -colors :quantity -depth :depth histogram:info:', source: File.expand_path(@file.path), quantity: 10, depth: depth), 10)
+      background_palette, foreground_palette = Rails.configuration.x.use_vips ? palettes_from_libvips : palettes_from_imagemagick
 
       background_color   = background_palette.first || foreground_palette.first
       foreground_colors  = []
@@ -78,6 +73,75 @@ module Paperclip
 
     private
 
+    def palettes_from_libvips
+      image = downscaled_image
+      block_edge_dim = (image.height * 0.25).floor
+      line_edge_dim = (image.width * 0.25).floor
+
+      edge_image = begin
+        top = image.crop(0, 0, image.width, block_edge_dim)
+        bottom = image.crop(0, image.height - block_edge_dim, image.width, block_edge_dim)
+        left = image.crop(0, block_edge_dim, line_edge_dim, image.height - (block_edge_dim * 2))
+        right = image.crop(image.width - line_edge_dim, block_edge_dim, line_edge_dim, image.height - (block_edge_dim * 2))
+        top.join(bottom, :vertical).join(left, :horizontal).join(right, :horizontal)
+      end
+
+      background_palette = palette_from_image(edge_image)
+      foreground_palette = palette_from_image(image)
+      [background_palette, foreground_palette]
+    end
+
+    def palettes_from_imagemagick
+      depth = 8
+
+      # Determine background palette by getting colors close to the image's edge only
+      background_palette = palette_from_im_histogram(convert(':source -alpha set -gravity Center -region 75%x75% -fill None -colorize 100% -alpha transparent +region -format %c -colors :quantity -depth :depth histogram:info:', source: File.expand_path(@file.path), quantity: 10, depth: depth), 10)
+
+      # Determine foreground palette from the whole image
+      foreground_palette = palette_from_im_histogram(convert(':source -format %c -colors :quantity -depth :depth histogram:info:', source: File.expand_path(@file.path), quantity: 10, depth: depth), 10)
+      [background_palette, foreground_palette]
+    end
+
+    def downscaled_image
+      image = Vips::Image.new_from_file(@file.path, access: :random).thumbnail_image(100)
+
+      image.colourspace(:srgb).extract_band(0, n: 3)
+    end
+
+    def palette_from_image(image)
+      # `hist_find_ndim` will create a BINS×BINS×BINS 3D histogram of the image
+      # represented as an image of size BINS×BINS with `BINS` bands.
+      # The number of occurrences of a color (r, g, b) is thus encoded in band `b` at pixel position `(r, g)`
+      histogram = image.hist_find_ndim(bins: BINS)
+
+      # `histogram.max` returns an array of maxima with their pixel positions, but we don't know in which
+      # band they are
+      _, colors = histogram.max(size: 10, out_array: true, x_array: true, y_array: true)
+
+      colors['out_array'].zip(colors['x_array'], colors['y_array']).map do |v, x, y|
+        rgb_from_xyv(histogram, x, y, v)
+      end.reverse
+    end
+
+    # rubocop:disable Naming/MethodParameterName
+    def rgb_from_xyv(image, x, y, v)
+      pixel = image.getpoint(x, y)
+
+      # Unfortunately, we only have the first 2 dimensions, so try to
+      # guess the third one by looking up the value
+
+      # NOTE: this means that if multiple bins with the same `r` and `g`
+      # components have the same number of occurrences, we will always return
+      # the one with the lowest `b` value. This means that in case of a tie,
+      # we will return the same color twice and skip the ones it tied with.
+      z = pixel.find_index(v)
+
+      r = (x + 0.5) * 256 / BINS
+      g = (y + 0.5) * 256 / BINS
+      b = (z + 0.5) * 256 / BINS
+      ColorDiff::Color::RGB.new(r, g, b)
+    end
+
     def w3c_contrast(color1, color2)
       luminance1 = (color1.to_xyz.y * 0.01) + 0.05
       luminance2 = (color2.to_xyz.y * 0.01) + 0.05
@@ -89,7 +153,6 @@ module Paperclip
       end
     end
 
-    # rubocop:disable Naming/MethodParameterName
     def rgb_to_hsl(r, g, b)
       r /= 255.0
       g /= 255.0
@@ -170,7 +233,7 @@ module Paperclip
       ColorDiff::Color::RGB.new(*hsl_to_rgb(hue, saturation, light))
     end
 
-    def palette_from_histogram(result, quantity)
+    def palette_from_im_histogram(result, quantity)
       frequencies       = result.scan(/([0-9]+):/).flatten.map(&:to_f)
       hex_values        = result.scan(/\#([0-9A-Fa-f]{6,8})/).flatten
       total_frequencies = frequencies.sum.to_f
diff --git a/lib/paperclip/vips_lazy_thumbnail.rb b/lib/paperclip/vips_lazy_thumbnail.rb
new file mode 100644
index 000000000..06d99bf79
--- /dev/null
+++ b/lib/paperclip/vips_lazy_thumbnail.rb
@@ -0,0 +1,141 @@
+# frozen_string_literal: true
+
+module Paperclip
+  class LazyThumbnail < Paperclip::Processor
+    GIF_MAX_FPS = 60
+    GIF_MAX_FRAMES = 3000
+    GIF_PALETTE_COLORS = 32
+
+    ALLOWED_FIELDS = %w(
+      icc-profile-data
+    ).freeze
+
+    class PixelGeometryParser
+      def self.parse(current_geometry, pixels)
+        width  = Math.sqrt(pixels * (current_geometry.width.to_f / current_geometry.height)).round.to_i
+        height = Math.sqrt(pixels * (current_geometry.height.to_f / current_geometry.width)).round.to_i
+
+        Paperclip::Geometry.new(width, height)
+      end
+    end
+
+    def initialize(file, options = {}, attachment = nil)
+      super
+
+      @crop = options[:geometry].to_s[-1, 1] == '#'
+      @current_geometry = options.fetch(:file_geometry_parser, Geometry).from_file(@file)
+      @target_geometry = options[:pixels] ? PixelGeometryParser.parse(@current_geometry, options[:pixels]) : options.fetch(:string_geometry_parser, Geometry).parse(options[:geometry].to_s)
+      @format = options[:format]
+      @current_format = File.extname(@file.path)
+      @basename = File.basename(@file.path, @current_format)
+
+      correct_current_format!
+    end
+
+    def make
+      return File.open(@file.path) unless needs_convert?
+
+      dst = TempfileFactory.new.generate([@basename, @format ? ".#{@format}" : @current_format].join)
+
+      if preserve_animation?
+        if @target_geometry.nil? || (@current_geometry.width <= @target_geometry.width && @current_geometry.height <= @target_geometry.height)
+          target_width = 'iw'
+          target_height = 'ih'
+        else
+          scale = [@target_geometry.width.to_f / @current_geometry.width, @target_geometry.height.to_f / @current_geometry.height].min
+          target_width = (@current_geometry.width * scale).round
+          target_height = (@current_geometry.height * scale).round
+        end
+
+        # The only situation where we use crop on GIFs is cropping them to a square
+        # aspect ratio, such as for avatars, so this is the only special case we
+        # implement. If cropping ever becomes necessary for other situations, this will
+        # need to be expanded.
+        crop_width = crop_height = [target_width, target_height].min if @target_geometry&.square?
+
+        filter = begin
+          if @crop
+            "scale=#{target_width}:#{target_height}:force_original_aspect_ratio=increase,crop=#{crop_width}:#{crop_height}"
+          else
+            "scale=#{target_width}:#{target_height}:force_original_aspect_ratio=decrease"
+          end
+        end
+
+        command = Terrapin::CommandLine.new(Rails.configuration.x.ffmpeg_binary, '-nostdin -i :source -map_metadata -1 -fpsmax :max_fps -frames:v :max_frames -filter_complex :filter -y :destination', logger: Paperclip.logger)
+        command.run({ source: @file.path, filter: "#{filter},split[a][b];[a]palettegen=max_colors=#{GIF_PALETTE_COLORS}[p];[b][p]paletteuse=dither=bayer", max_fps: GIF_MAX_FPS, max_frames: GIF_MAX_FRAMES, destination: dst.path })
+      else
+        transformed_image.write_to_file(dst.path, **save_options)
+      end
+
+      dst
+    rescue Terrapin::ExitStatusError => e
+      raise Paperclip::Error, "Error while optimizing #{@basename}: #{e}"
+    rescue Terrapin::CommandNotFoundError
+      raise Paperclip::Errors::CommandNotFoundError, 'Could not run the `ffmpeg` command. Please install ffmpeg.'
+    end
+
+    private
+
+    def correct_current_format!
+      # If the attachment was uploaded through a base64 payload, the tempfile
+      # will not have a file extension. It could also have the wrong file extension,
+      # depending on what the uploaded file was named. We correct for this in the final
+      # file name, which is however not yet physically in place on the temp file, so we
+      # need to use it here. Mind that this only reliably works if this processor is
+      # the first in line and we're working with the original, unmodified file.
+      @current_format = File.extname(attachment.instance_read(:file_name))
+    end
+
+    def transformed_image
+      # libvips has some optimizations for resizing an image on load. If we don't need to
+      # resize the image, we have to load it a different way.
+      if @target_geometry.nil?
+        Vips::Image.new_from_file(preserve_animation? ? "#{@file.path}[n=-1]" : @file.path, access: :sequential).copy.mutate do |mutable|
+          (mutable.get_fields - ALLOWED_FIELDS).each do |field|
+            mutable.remove!(field)
+          end
+        end
+      else
+        Vips::Image.thumbnail(@file.path, @target_geometry.width, height: @target_geometry.height, **thumbnail_options).mutate do |mutable|
+          (mutable.get_fields - ALLOWED_FIELDS).each do |field|
+            mutable.remove!(field)
+          end
+        end
+      end
+    end
+
+    def thumbnail_options
+      @crop ? { crop: :centre } : { size: :down }
+    end
+
+    def save_options
+      case @format
+      when 'jpg'
+        { Q: 90, interlace: true }
+      else
+        {}
+      end
+    end
+
+    def preserve_animation?
+      @format == 'gif' || (@format.blank? && @current_format == '.gif')
+    end
+
+    def needs_convert?
+      needs_different_geometry? || needs_different_format? || needs_metadata_stripping?
+    end
+
+    def needs_different_geometry?
+      (options[:geometry] && @current_geometry.width != @target_geometry.width && @current_geometry.height != @target_geometry.height) ||
+        (options[:pixels] && @current_geometry.width * @current_geometry.height > options[:pixels])
+    end
+
+    def needs_different_format?
+      @format.present? && @current_format != ".#{@format}"
+    end
+
+    def needs_metadata_stripping?
+      @attachment.instance.respond_to?(:local?) && @attachment.instance.local?
+    end
+  end
+end
diff --git a/spec/fixtures/files/monochrome.png b/spec/fixtures/files/monochrome.png
new file mode 100644
index 0000000000000000000000000000000000000000..fa36101cad383620a94c627add680bff8d38c3c2
GIT binary patch
literal 9216
zcmYjX1zb~I*avAwcaKJ-F@O<+jZOtbLQ;_uiy=5b6c}C7-K8L)fJ!P1Ma363T9g<e
zL!?KI`tIiYe&4s>Z@-=AJkR+*xqF{`&W*clX~fJZz(_$s!E9ozZ%sjQ#-4(LGL@bh
zn3>QT;{<*f?io7-P*8w?F^H3rBCmiCnB+$o*dyR~{rx@fASmDuJlzqVH${Vd5Z<Cj
zCKi{kJ!Rnqgql6=O>UW(oTH!z2wDmbN-7FUU_|-~P|}cp?SQf1zhf6*eD+_NGr*Yh
z%>T*z#88vvZvpe9k1_Bi;r#bYOF@xJM?tb$Sye@u^4Z@pC4fLt^6$KgvNC1KU&PZ>
zz|oSwi1vUgs(;6+BuL=XHx!{pK_N<EqOWHgT(DaBD2ID7w~H#4Bgh~TqWH|5qntfX
zKdGO4{@q9RZ@p&q>H>RFD$?aA9_IPxpQLlto5QBx{>I2Z)V-|C_M=9W?ZP|JGI4Qn
zN`r@NJ`U73{YvudT>~Y~;0o57LS408M<1;nf&+XO4qwbHZH_EEc5TqWw#o)imL-Oa
zv=AAHJG<tqRZ~Zx#Wak_kPQ+!{;2ce3+Nr<4D#ym%y2^9iN@-i?bF2|Ut&-&UCADd
zK2D0;KVPn<wqV_rXYAWcB1mDl%dryPVpudTOH5WA8Etc*Lf^q&Y`c8xw;wU+CDwfs
zdStUcthw{%9(VQ%&4VgLy;fT&l&~olMG=fYdun?M>%3GKH4_tf!Mb?Zr%iX9UrBtp
ze%yd)XJ1&3ctXIocdQV^HC}Fc6E-EI_<9c05Tk3z5PJ(WqGJHF*e2|vRrPY58@JTN
zK+HMOTgTKkI}vjxFX1Ju=zUOI$4BXlGd=08bRB&=qZ`|w?SgYlepZ>G(<h!yM-pv1
zZBJSTPw>UeVhf;Y$_IISaD`zp;%h|~s9#A!&($EjYN62MuSYMhmpgYTm4)Al%3#Vg
zBJ%zg9<nMPMom&QM5s@mHS36jd^o(@Ik6sf@`xi%y+g@Yt<~Y&;-h1Fk-dsmYuC$G
zkJBKUVamiMbK}J4zDm{0NoixUQb+!_&Gi_8PBWz%b^7Bb8Ocld@kP7i^a119yM#H9
z$blhuWP8D<88wcyXL%1F<c*s}uc!uJU%sOj%vt&Ny7d&LHaJRM&tGu)#oq6kKu)cK
z3+;ibCgIgfBhBzZkeFzPl8M#xvBeGLp`TNDixJgRTf!eU4zUMS1Z)rvSB^S-YWumB
z+aH^*A8X({xlA*84f>=nDery@Q?^LxFKQD}WU}4zx!~TKN8>AOr?AJ9;tXPe5n0RE
zgX?OXp&89Vw8$}f*@zdrM2Xyrqzz?kP3UktDqA;|&-xK!ztvY(OZNhD+zhhgNjPFp
zJ3n;QXyajC+8e#%F%Txe*tmAT8fmp9nR@B4pa@lD1bbZ;W0q%CSr#%;bMZMRCcxyg
z&d3|=55z?+(GhKErt4Og$Q;*+2~dm5HSLy}cT2o7Qg8YG2<}8sX>pABuT%tG4I$K}
zrB%-<ubTw(d21DX?H8u!(l+DmzM(rm?|U3X_?E^|`6g+)^2aQD$>!@TLs}OlpV{0`
zO^jxRCRM6_S&uDpwYJ{xTSgEjhWXk*Kxwqm6*n=89cHOK%Z{D&+I<z0sd5CH{ddFr
zP7QC%y>cZ*I-P?)7!^u?Jj8CBb0i%ls$H^4H>E*VnRsABu=5t2c#5w2Oncq_*>a2Y
zWpt>xmP+D@h|buBR+d|y?2&jdPQoI6)vbt+10H<J4ED+kmP#rLNTcr%+|Sn&h<k51
z2#M2vKm$gaOT5OmQ);hEj(zrN6Vdt9jQFW|8eQ~=!^ygDt4L4aRWW`X8(`&O5=a-u
zsngX=D5#Dp_bTpX6FviWc1-)a9oo<Gh4VlIdE00CTqkzh$^%u<9|N{2(OfV^&>G*y
zpaWGxZ~FMOk7=?OQgLu1xI(YXhM{I|J`=*6Mx>mpv^ZfYq?ywqUY%kB+nVOrjW=N)
ziCWrV?PSn>8AHxpu2YnS40FoU4YLxa%SF2eUJ>dE;qiB#vYxFBnVb!$D+J9+w)hjI
z_Jgg7g9E>6S50UMhWp;ud3x*D@VD8PHdscv@!b^{%JYxYF$bc_rBt&wOni7cWv~aL
zmZi?kl-d~?Z)QK@6WL-?mvB@jKA^AWUG#~e(T~B}$r*2cC*cd&WNLh>n`Y6*K7NW|
zgeXL=m$JiYw%pi9F{`u*Hf!55j@r<xxWGG3(55crJ!5zWl_FNz`gPF0(!xTskPZwp
z4ah8hWh&UpuM?5+OTyL5wavD9v~`2B;sW^E7K4LSf(s28ie_OFVKt;vqP2<4?f&U@
zrBS`c|Dn(RI4JUaw^R4R?7{4p*=M56u6`<2k)=4rB32jXpbMHmzn9&M(F|%+KU!(U
ztyM=Gxg(Ut`6yg#@85+lML)r0<IrBcV%-yN$VSNtjs<j^@<aoz)yK0#R6V0z0`*8|
zm&2bzBDu>rb^#{W_hMe-Z}enhC@_kX^x&>CA-+Tq)b;jPxvs+@zB(powbt^6S3Sbs
zb{Kt?r@v~$JBc|(015s0+ckQCg;ErajlA}b3O!s}={}ls(@$9@Sb75&^1}wI^)f{1
zdaj88+UojXU;7*Bk|wx#-#XV|kY<RU#m3`Cw0G3r%>d<#!PH&-(lNBdJ);w4V)8wC
z8{TC>4yj3*uJEN8eRCmKHC#;fliHUC!B)TMBuX`f`Rhd)$7nf)b+`xWZA-S{;)Y8l
zF^bS*Wk*2?hE2sR+7$Rh;*y&~#o<r3BGI-$@q$S3w#K>?%7p^l+7Wu`NdC#}v72$l
zp%BMO9Q_z=ISx91lu?W27iTX+W2my*Q>YU}#iqX`&nCs{gztA(l!&|=uIyIcAB3>>
zm7muaFJBjHW6-&$u}o(p|G2wIPA(ER76j?};0gLT#Acp6_{BXkTnl0(DNg9yxZnS!
zR8IkdF2=H%H@6M*qM_Z)cIsQ2Lt~k)WdZu)u)eYw1>QbAtJGc3JD8cTI)`-I7^fcz
ztKIhdgE}%Z`b)9;5hC?YIP_Tj@~kl8&iTg4o>hfNiMGoU(COrl&r46a5n0C6?@o;u
zPP(lT9!-t1eC~*9iTJKppLxvW!-_7P120`;#0PBX`C+@e1rUx-ZXwEFCoGHa{iz7L
zJN7QToxp_1iVf`H>{$h5x{V^3@fB}iVV7^*tiEVDew8!7;60`r%3mns!M_|Q@;WQ@
z+uT&ZuNu}9Yl7ueL#^~Bnv@=|GJ|#9r8xUfi)Mx~iwF90LQZI3g*(-Oc!h<}x86cM
zli&asxLl6)#K<Q^N_1UoRKLWt$vo)h_1<?-Uq!QJji>ASMQlYEGx(J}3&oUzy}BGI
zbU+VESMM{J(!v7Gyjrj86>qQKkEP0TfZ9h`dQyIq^=@dw^eaB`Tyx33iW=a072CxK
zZcJ<o&h=JY2uBa-6VgBn{`-kHH>nU+u@_%xdJBhsP*tZb{1owga9eKK{nh@I+pX)J
z?lOV$kZZjhTiS$so)2PiURg%{FSNS8nD(vxsMY@d3-6B|;w+T&VBme<y%ougm*n{(
zsIk7oOt4N^kdP^+R654Aif3VXG3b_tbd0Qgp0Gv{4@=<e7nhG);-N0PS8tUC7}Q-J
zo$l=0Wlq`*g0wrN4ZHA`XZt)s@>+p{Y(?S*cwXuLzP<0gDzSy@lja}(Z8?9Kfa-Lh
zF3t_hl*4`(=SeLtrCTW$PO>akxkPhSVeNu&osklj&#HUr7I@vrE5Tm(lp0*uX>J`G
z>E@ddl5{w}8uEIeGr_o6kHoA-)A=d6>dOqCWmX7nKQGBXGqU<=ZF?1TPZr|TtA2HL
zs?EFkoShoQ5^VQw5Kl?=t(>HRPKC+!r~zJ;M1%qB==7)42hm}Vq~|S*^_2S0)H_d$
zW|wapFwKvp{~UgW%rLJH6(ICC4hP~r9o!=QW^PQykd{`>!dqfEF>y`3eusYu7YS+d
zDl9tikaKA4fp|353kz?lR_vPmO1;ya+PRzC-0&l)i38w&3F4AwE(C#J3<{$x^9^=S
zeuwEGHF#7Gdz!?ZQvhJogU5=Ua$c7pes<r(+!t-|mG+*}p~<_=Ti%FIv3lp;M~l=<
z?OOfu8L}J;+ci7A`FN@;fA!_{Dex=alJka4Wm7n+oz{7*Nz*{auP{qJB{alRduaU<
zY4Jk###7=?6u!WrT8B^QNETNtk)Oj_TsdV*1G&*Fnr6s4qIhU@Vp|i~{m0E(S(=Z1
z%=q-zaL!5hhm8g!>OtCc^n0&)f9-A9KTVhRjo!_9<p-%M|1=mrK64C=q<`WHK3nMp
zU1;cLH!3CB5{_aW@k&mTw@m7ubAu_Ljk%~Tl=Y#bMC^6uq8VIS?K%#vvAS?@Z#?PQ
z%RdhLT48H9Np=#L_jSX56&Ob}UkpB9GZ5fO6%p56{j5$AJQh>?buYj?P+4Ju=gaR~
z=%wWV-ZGM={#mNgI&in#q+ldJP4SBgH1gh3)t@BXQYGx}Q+~EDwZm`TZD;JY0bOqf
zJKVN8aPeFn`<O)YB|e7*fkVv)g%f|u-irxvcfWN+<IcDA$0Wm+68Xf#n2<RSeGd8U
z6PP<~!w`*ATc%F8oU&w~3oB3Do-by$ZU3ZQXF?3biZu(8B=_tx_n36xZqyqi_tLw6
zxE#%f)%q3k9x!?C%wRPnqEqE&(S+Q51w*ecoKX=It@ar&_`Xd3&zQSAU*B3h`^VDn
z>p%DccKGM;vKXIgRwt$bQ;=16DA$JN@0S`~i^|~P?sExp1B*S+&Stce0Pih^X<QtG
zHEHwt`h3JX50B{I*q51{=u=J@pZei&|HSkz$+!hIy!+(y&OY%UAqueO_4Y5vEqe#P
z>*K;o4o(3@NRmcx*L~%Sek*0zQ6BI_K8w|*a*xW7Vf$m2pu*N*1Run!myM_#nv>VU
z&EGB~wc8;412ttd_Z<nzK0he3_=6!m;)F)WTyNNmTl1}U`0cR8he}KnA4A6W+?Sf>
zSgyGccT~w3YWvD4mKKievo0|k#?)3h6WE>vdkdeHQSk1yqV)3^z$Z-hESihRE9d?^
zVd4Gyx`V@E(YH7^ULUh;pTu~#RSk8z!t);VVca_ArSo-Bi&Ms9cTQXQu~E|l&S}Y7
zj}RgxMG%1f^ksrT(K)1ZtIzgOf7MKYVG~HxwWIyMd!)P`kX_h}A*)ULu!4T2&TFO6
zRcH8^zU?zBr`9Kgm$7y;@!-+`L#9%oRpNq-!)&t*-XNXct4^38T%DBOHX;w-&wTrG
zl@vMr;f6co@n@K=OUk`tKl!?F89(u#A8kP^t|B<1Q>v1U1!Ef<VCy9jY2Hyady6g~
z_hZdm0if%7(|FRtUjLp(tsKxKW(H{Toj<U?;zD^-;<skp>xcdYlXnvD9V@&OCOLde
z0{PWI>Dw5*KGnr?q3gt1|KJJFOpifhAPL36-b61O9V0t(v|e4-#^;v3lC)kD7s$Te
za};!chkac%LB6h{1l%1XyOG!@NzY@@L8rOLWq7Rl<|EH8+bYZJcg!R;&XnLPQGYS%
zE6|CBKu`#M#JKo0Dk<hj{r=&$9l6qZfINqTRC0MXMLs!Az`4}!uCF@r?9TSet&nVh
zMzf}8F15br*c35zoPe_(Y0NpBziEt#k(h=Xu8<OZue6cYDb{3Uy|-zbBdNx9&TZcP
z%dMGhn-V8-{xdh+Ut8Pn7A+Jx4ED&$3P^;1Kh&QOadT%c$Di9*c95-Oc((45u&I4J
zIh6I1$o!JN`=;q$MM*6VeY3LhlTjCFdtBMz5g2FbfAsr9T+d|G0KW<~s6PWNFat*n
z=1+KffczxL<;W14!(nZokht#dz7S%3+x?{hsW8TSVr0co7iacGei?Md^zw|Ek{m|}
zlZAiX9e4!Y(w2PFxZjJdC|SXwGg5n0!=20s>lT)`-4)0PP!y{TjL|P^Q~O(dC~KTT
z9SyluX!n(+DjKIRcG2-<sVtR|3QQ1(?(O+{2GpN(s!P(X)*Tav%A^7KGvp#wL;put
zlM1rCM^y?Wcixg~Y=w--v#<QB@%4uu)n9TXy0OvxOV8ItPSnmBl7DT$zd*iDzOgY;
zgNKydDO#{Jj~$P_{iixI_}l-$Wj)nN@{TDY0RNJ`eN~1pIoFih!1~(&s;mz=1a!z8
zd;9Nh?zz_I$f+_UkkkW4RT2yV>(6MLTD)#lMFaE^ElDBa>_xyHCLs0?)JO{OSA5e@
znf!ovca2E$I;NB$IYD{(8`I<*y+^41>s<;7e%6z@>ZZHrr??&>C24iXy^#$0x^M3;
z+n;#;KW{-OsU^~1s5^wgg5*62Rq0G#J}f|v1_+f&Bwu~x<!u1#=3(v!!laAmEY;D!
z{86uPl6(8HH^~CJ?xUqPhKZ$t8}~ijJ)fdHKVJoI8#n$9E)}YUqEOX?sq0ri$r|;`
z_UAFO5}#)Ci^<xlP#?!@r0J%p!7}7fM=v-rq*DPA_$w=c1!OVI{~;T8Ey(HNAq4fm
zVJx>TBPp)M&I3h33+hkZD<{!%vm_cQlEhFNg9wZ#o@7@fVAl_lW{koAqO}T;EGvu=
z0dxatj6PC?c=qx^=yWW}W$+DO9B;|565W;XufVN3E%J&mL2W<+NOa<aeHWO#hFi<>
zZ*C+WM-1?(#9C6O*nA)x6$>VH8sh06=D8Zl$)EGXCjt-ZpxOJ3<m%wR&qQh{Ae8?>
za3WL4&B={d^COZ#`O3?4{_Q!4Ob-k4fD}igJbPWs0n|Bd29fF~s7z*_EQUopQa6B*
z{6CRe48H{JGo$YRRs4vo82z8(+{P`kDX}TS)zS|3x$f@3j4`>VIBlFCxv9!n>x!9a
zJQkmUp8?gDmMM_TL_!e-c@~E5Qr*-%b_x*p-m{L;EDWH2VNQGdVJw4$AyyMB=I-8l
z;|wSvh(j9GzzdO-mk%;`asMKU=!w13A&4Qfw;0V%c?*?2xLRq2Ie4-(r5hT9Z&>zV
z*6^u~jXzZ$p(<yLWD@xdlni5{P>ab7@^y;%DE9Kt2mkRGgY-%K0evz*POgsBt^gAI
z)n)LWo(Fxs++)`fU9s**>3{8+V_eZ7(dSbBBAUp_KQ~-T;Hzq*`fH)1`|@3&AN_=V
ziO*?lysqMdk{T}`Hb^Y5#i#%dc>n;Rh_=}K2ug^wy!<tDNB4t1GXMNveh;Hxh=nFe
zg5ZfTx3{m)Y3#{M0h;<J$$EW&5Ah6gb({Fx>>xb-Uv^y&2e?qdgK{+`83-UZr=2~N
z)Wd%WVH4)$$&!<<Z6sJD-o(-nbpDgyvVxKN_;p<}Ufj95m!T+MaV)s}2H^2WeeN+R
z=8L#ps>ojluAp|fAP&8ZMCZTjYkG(hBzoT~`8D9*6;z-CiF_>cmtE1bWo00fU|(AH
z@{$`FkeYjJ?8PqrCc7El^fuX}f}XToye+#Q{`P<L5Ic!30V|^c3xEX;*yrM7sWOe^
zkUd~DtJkfLx2HPppe1=!-MD{mfx3Kgf*fEK)Q%X$rdNFH{9mw~9^!ct{ebxrIl!jw
zU#u{>;ym3m7RJx@f+|w<NDqHqlwcLpe-0QSS9;GqHrkK9;%g4n7(lf6H;ZWB%n*1`
z9&{F<Rol+!+{(z?w8CFtU%_A4&$;5;BOF?1&QM~7rc1KsJmD^s;-DO2)$z{YS!X$Q
z!plFS$EfbtTybnx6)KeEV0fU79675n30c=iq!~-@^Y+9f4S95e3ne(%Pp-|P4Sntp
z?6Bwp&*u8Y$ANUAvu7248Xky13?yL$`2OcBj_=|v+2XhgnZO#Rpuprk14M*z1^&iH
zX?e4<TD7GSu(3hfxO<qvQ}Hv=k~L1KlR+CG7SQ-VY!n?F37>|tgH_-rX%I>sWX7+R
z#IBf<bhs$6f%(tIX88p$@Amgqsl+Z2Sl2WlC@Gu~a-KSNPcoER_McIO2RaZH>i83O
zov4gozixGMk+-IzNn2Czt|-B(Q6I3|vysL=EA`v&BL^7oQ*;%ke)^C(NmIx*M~_o3
z>3cSmRX2LK%m!AV=lX<IvhsV_IFzA~eU5DoZIm`7kF>HAin64?&v-10JS+Q;clqv1
z?}P!m`%D_VIyo6@eudXl259f6cEv=UO2JuWDnr^cl#HYtQl&OFD-c%2yP9P-dIbi3
zALxKxzB{|fQck@B!+spxblK8t+hnzZyIC{TT-G1i@0Mr9A@y<&Ob5Pdn*DnZYa*(m
zckM%8X4dPSv|l$;Oa}1k`=5t~0;7vimK1T^p-?fJR+E0;$<n3bUZ!PA@O8&{okeg;
zQ5XkY1QLI(eK@vIdImnI9j8E~41MDB<*XUu6L8JQl~FN_DUHd=iY@l$Z+IhaFt-!I
zs%p`d-$5tG0w08!P&^;Tq0Q4*tJ5rn6M(%`wsVue9rulgE847)D>yvAOdL$+LtmaW
zmc~Wtu@`W`@Z7*wnQRAkW@V!|<SGWDQ`9Vy1uGoe%DnO<bMhXxvFQA|z%YPqAEGN?
z0(N|L9%b+>OAF^g1cCiveatBxI9TA?3ZicM^WarUSjYL<8&>m6V^uG<SPZO`WHo$$
z;7#}UdDmtT7ekpV7kPvm)HaFd&cLJ5@XL)BR&|7w^djImppCgRFRK0Wi%OYdcGC~|
zl-Vn1?cWctg%Q5HS`G{5*ZRpJo#P&qC+{F1$;?S-q-p~@7a}6@XDkX+eT*VmG6Hs3
zo{y;^sE{a?hednurk{IlieH}NgYb(a!;XFZLO@o0A>IzblbS|?v$aW)D`=~lu*$xo
zVrPc?@%z&y>4PA3I^mir;IxJE5$u+)Fmln-p;jrT*l7KkTxC<ItOW3kWD$ImvE_qW
zgKhJURaXDv9sUZX%-NNJH@{(FQb!{PPp&t3ZO&11Xd>9P^l4<X^C!YS>Jk6h=-ss3
zA>?ihk5}WJ`QPYeulQ|6#ZNCDUmi3S?x*8U&ymR7u;;G$d3)a5R^<8Af<WgrI(%?p
zCAd5&H+zTptV^m~O1S>iQ3DzXJ9vC4nO#hmAQX&Y+<tpSsPM(^$tI4y&Gd2A`|)mw
z-3*JBg!4XQc;vwNxF^ECZg0i^LFMVokyqUQ<?7;5y3hT~&2XD?y(cvicEgH+b*Bog
zKie(COBd62MaCJ3RRbVsioZ%5Itj-xbvw-Bv`WG=^+aziYi04lavqUaM>Hiv`NhVT
zY;QHTPk|FB^E!Fx`)O8qX?QLnqi9;eMd_e5%O`iQWcMHZv7<C}4~dewuf1d4sjelp
zDsUnQ*%CMlQ!QKS?yqL6E=v&<qt*?!e#hhjJ!t0$`$;dxc7dj$_zXdl%z9D~tymnP
z#Y$!%XgeNn?Oh^Vk%-d8w9oFOzrTtvH>7_$8<dEHZ&a102#8VZGtHDXO&y98YuI6;
zc-C2(jJFST$izr<x)lD**B_?_;EOPN`U7w<<>Cq#K}90!$%f{~HEtH>OrtUqMAQkt
zi{7Jir*S&BqS)9r+nF5=%TxHps15(r2hU{boC*6z4;(D5%yMTmKn=j>Vf3+i4<TR|
zy@LZ5Vh%gZ4KKXG`1P7j%hOcr$~O47X>LyZS(2I$sWTts&O!x`Y%qO^Y-h^ml$}|X
zN{@uddiapgj)<)Qqu&U!MaE#E_VriN+fsj`GUOkq0jvzQWj5>A!F0WAVHmk!i3?&<
z2|*xWe)LO_9w$t^q^husuAxLLGaqY|#C8UzsK<MG`i@DMVAu#?Ex{+4W^u)O-HaTs
zRO0J9Pp@6>>UyTbM-FVWkc;lYp+_Jc;l4x^Ma^0X2uG9get!QG3}(vF%HaY<sXMS5
zfi+<C6py$_lpJuL^-Lm4-}0k7QLN)J<Nc&?e&Pf>Ox&m)-CwON*xUgeaAE-gb8{<D
zerOZWwUryu$7K-M+k{-SQI6G%9hj%7X+7_iwYp)~tZ<5{wFkZOFjG=iR=Jk)k+MWS
z4w!gZ*-ITdr9zKF>2cd;1MnB`8h;7r9i1?T0sV$3Hu}faeBtRWKMWk#iW7CRE^Ve{
zuk5J2Jih&&w{#p%Pk&rLh6eRTC8?U+HJ$VyI=R;2w15ro$~8rnj2qB19n*B&{?>ZM
zZ^7<VouHz?vVOVKIQ(V!Xo~83nia4Il4RGN@TpPN<{UwMor3V}AG&o4ZJDE#z8l1%
zHE+)#s9r-Ro9M+=f&=2C{VSKH&-Xgb+DDt=;Q4PE>oSFvF<GeEg8IPLHM2FthJ3cO
zIFrxN*&UDJB#1!_zLQ9GjJz81=ha$BEF#md`MMwC%^f9gwfE??Cv55`Ca*$9eM`qy
zwC<K^y<o))55Wh_!8hLa99d5DPdeWnhvJP3U(_=FnLWK-pPg#Y_USh#aHxA^%e#D`
za8`Sbd8CV|+1a<U=)81%SrHr1oPc<YuZfRzggoSqsXX}zmD=%d)vq7RCx|@9l;V7L
zSK5^KI6JlK;(ev*@AGNPF)6k|em+dE_uu7oKKbq{W~aiXEgPl&w5VDTSE=iCrAT*>
zK`gn1*GVI;R9ZNthy%Fowl}>N+T$i9vdyubpxQ)4O!KCe@eE?UvV{EacR-~q>b#_*
z+Gxbs!G_z=x5u$XEU<!xk@Bm++XCIBcLC5)MclRbedB@n@1nhWM3FKpG}>gZMMRrF
zO5Ma?P{D8YJLLuB{EiSKOkBTxJ1Dw-IY{4%Mr@$Wb$k!YUK#ScFt&&RCiF?u_61g~
zvVF(Oz=~QdSz;V7fVjMDY2#I1{giFV1Y8~tH_E)!zxRu62*T~ZnB7JrGM<*b{*RcX
zQAJZSobJs+CyzEwR4@H<X#%cP!Tn%b2)5;cc{=3uwk*==#a5?A)Iw5tyiR2jej}?Q
z^e&4SDiz}FH@m5z(MgAe2EjZM3Tl<gB(6J}LJ5NfsrK`(RZ1l{uER(xxe^|3ir{!R
zpQ&>kH%Uvf2fGe!r9Fzd@M&?n0l2k>4@61Qu2ZzS6c5t|H*;2hBiyO+O*uK>NYl0M
z`>jn>gv`agvNXa6VWBoKj^u|}u7-G}AqS)vk)B|cmYOCO*fw2l6P%XwR<5G>RG46u
zlA5+qTHYbJz~BsOc&h&HtKiw0TJMCDTz2a%W3Ps6rg6^=>wCj3s@IVlwSh1pIAkTs
z`eni_H!dC|rI1bd4n7VLi)wxn{(2g(ZQXM>&!u`0FMRR^UrZ$?$vV{M9i%Y+s_l%6
z81|im>&VP+jf<-oqarqI>XZ9JaAeHrRge<5U_DciT@~*}U`>=H(`oPlYnUk0e#C?;
z*DF2OE1UJh4P_~^V!e=Q%4vS0#Il0b=Wl#d9-X!_c=$B)7PCv4u5q08@W5$20TU7!
z+f}kHRNLSWxz_i*8k!l<3C9}6v&{)Tzp*2<{Cv;Yu@A3FG^wCzxyrj-3o$*{;evb>
z&H*)HC#Uc^=Yo0b_4@JliWIfWr{~fUZywjJB%G}xZk(RF*jwB{)Ko2~A@i?4cRsSj
zcXGfu%mZTx?9*{u4U-lF*=7sA%efAxs-5abUTvmJBMi$Dw8F72Y9`XB;NG&ss+(=s
Rr%5lkm>5{<*TdW*{s*~%YhM5W

literal 0
HcmV?d00001

diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb
index 1b9a13c38..221645ac5 100644
--- a/spec/models/media_attachment_spec.rb
+++ b/spec/models/media_attachment_spec.rb
@@ -139,6 +139,12 @@ RSpec.describe MediaAttachment, :paperclip_processing do
     it_behaves_like 'static 600x400 image', 'image/png', '.png'
   end
 
+  describe 'monochrome jpg' do
+    let(:media) { Fabricate(:media_attachment, file: attachment_fixture('monochrome.png')) }
+
+    it_behaves_like 'static 600x400 image', 'image/png', '.png'
+  end
+
   describe 'webp' do
     let(:media) { Fabricate(:media_attachment, file: attachment_fixture('600x400.webp')) }
 
@@ -203,7 +209,9 @@ RSpec.describe MediaAttachment, :paperclip_processing do
       expect(media.type).to eq 'audio'
       expect(media.file.meta['original']['duration']).to be_within(0.05).of(0.235102)
       expect(media.thumbnail.present?).to be true
-      expect(media.file.meta['colors']['background']).to eq '#3088d4'
+
+      # NOTE: Our libvips and ImageMagick implementations currently have different results
+      expect(media.file.meta['colors']['background']).to eq(ENV['MASTODON_USE_LIBVIPS'] ? '#268cd9' : '#3088d4')
       expect(media.file_file_name).to_not eq 'boop.ogg'
     end
   end

From 4655be0da6c0f9a58f4d09a32189cbe5619c42d1 Mon Sep 17 00:00:00 2001
From: Emelia Smith <ThisIsMissEm@users.noreply.github.com>
Date: Wed, 5 Jun 2024 21:16:47 +0200
Subject: [PATCH 016/133] Fix add validation to webpush subscription keys
 (#30542)

---
 app/models/web/push_subscription.rb           |  2 +
 app/validators/web_push_key_validator.rb      | 11 +++++
 .../web_push_subscription_fabricator.rb       |  8 +++-
 .../api/v1/push/subscriptions_spec.rb         | 47 +++++++++++++++----
 4 files changed, 57 insertions(+), 11 deletions(-)
 create mode 100644 app/validators/web_push_key_validator.rb

diff --git a/app/models/web/push_subscription.rb b/app/models/web/push_subscription.rb
index 1860f0aa3..b482ad3af 100644
--- a/app/models/web/push_subscription.rb
+++ b/app/models/web/push_subscription.rb
@@ -25,6 +25,8 @@ class Web::PushSubscription < ApplicationRecord
   validates :key_p256dh, presence: true
   validates :key_auth, presence: true
 
+  validates_with WebPushKeyValidator
+
   delegate :locale, to: :associated_user
 
   def encrypt(payload)
diff --git a/app/validators/web_push_key_validator.rb b/app/validators/web_push_key_validator.rb
new file mode 100644
index 000000000..a8ad5c9c6
--- /dev/null
+++ b/app/validators/web_push_key_validator.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class WebPushKeyValidator < ActiveModel::Validator
+  def validate(subscription)
+    begin
+      Webpush::Encryption.encrypt('validation_test', subscription.key_p256dh, subscription.key_auth)
+    rescue ArgumentError, OpenSSL::PKey::EC::Point::Error
+      subscription.errors.add(:base, I18n.t('crypto.errors.invalid_key'))
+    end
+  end
+end
diff --git a/spec/fabricators/web_push_subscription_fabricator.rb b/spec/fabricators/web_push_subscription_fabricator.rb
index baffdbf83..6b4028342 100644
--- a/spec/fabricators/web_push_subscription_fabricator.rb
+++ b/spec/fabricators/web_push_subscription_fabricator.rb
@@ -2,6 +2,10 @@
 
 Fabricator(:web_push_subscription, from: Web::PushSubscription) do
   endpoint   Faker::Internet.url
-  key_p256dh Faker::Internet.password
-  key_auth   Faker::Internet.password
+  key_p256dh do
+    curve = OpenSSL::PKey::EC.generate('prime256v1')
+    ecdh_key = curve.public_key.to_bn.to_s(2)
+    Base64.urlsafe_encode64(ecdh_key)
+  end
+  key_auth { Base64.urlsafe_encode64(Random.new.bytes(16)) }
 end
diff --git a/spec/requests/api/v1/push/subscriptions_spec.rb b/spec/requests/api/v1/push/subscriptions_spec.rb
index 82ea308cd..54ef5a13a 100644
--- a/spec/requests/api/v1/push/subscriptions_spec.rb
+++ b/spec/requests/api/v1/push/subscriptions_spec.rb
@@ -5,14 +5,17 @@ require 'rails_helper'
 describe 'API V1 Push Subscriptions' do
   let(:user) { Fabricate(:user) }
   let(:endpoint) { 'https://fcm.googleapis.com/fcm/send/fiuH06a27qE:APA91bHnSiGcLwdaxdyqVXNDR9w1NlztsHb6lyt5WDKOC_Z_Q8BlFxQoR8tWFSXUIDdkyw0EdvxTu63iqamSaqVSevW5LfoFwojws8XYDXv_NRRLH6vo2CdgiN4jgHv5VLt2A8ah6lUX' }
+  let(:keys) do
+    {
+      p256dh: 'BEm_a0bdPDhf0SOsrnB2-ategf1hHoCnpXgQsFj5JCkcoMrMt2WHoPfEYOYPzOIs9mZE8ZUaD7VA5vouy0kEkr8=',
+      auth: 'eH_C8rq2raXqlcBVDa1gLg==',
+    }
+  end
   let(:create_payload) do
     {
       subscription: {
         endpoint: endpoint,
-        keys: {
-          p256dh: 'BEm_a0bdPDhf0SOsrnB2-ategf1hHoCnpXgQsFj5JCkcoMrMt2WHoPfEYOYPzOIs9mZE8ZUaD7VA5vouy0kEkr8=',
-          auth: 'eH_C8rq2raXqlcBVDa1gLg==',
-        },
+        keys: keys,
       },
     }.with_indifferent_access
   end
@@ -37,6 +40,16 @@ describe 'API V1 Push Subscriptions' do
   let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
   let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
 
+  shared_examples 'validation error' do
+    it 'returns a validation error' do
+      subject
+
+      expect(response).to have_http_status(422)
+      expect(endpoint_push_subscriptions.count).to eq(0)
+      expect(endpoint_push_subscription).to be_nil
+    end
+  end
+
   describe 'POST /api/v1/push/subscription' do
     subject { post '/api/v1/push/subscription', params: create_payload, headers: headers }
 
@@ -68,13 +81,29 @@ describe 'API V1 Push Subscriptions' do
     context 'with invalid endpoint URL' do
       let(:endpoint) { 'app://example.foo' }
 
-      it 'returns a validation error' do
-        subject
+      it_behaves_like 'validation error'
+    end
 
-        expect(response).to have_http_status(422)
-        expect(endpoint_push_subscriptions.count).to eq(0)
-        expect(endpoint_push_subscription).to be_nil
+    context 'with invalid p256dh key' do
+      let(:keys) do
+        {
+          p256dh: 'BEm_invalidf0SOsrnB2-ategf1hHoCnpXgQsFj5JCkcoMrMt2WHoPfEYOYPzOIs9mZE8ZUaD7VA5vouy0kEkr8=',
+          auth: 'eH_C8rq2raXqlcBVDa1gLg==',
+        }
       end
+
+      it_behaves_like 'validation error'
+    end
+
+    context 'with invalid base64 p256dh key' do
+      let(:keys) do
+        {
+          p256dh: 'not base64',
+          auth: 'eH_C8rq2raXqlcBVDa1gLg==',
+        }
+      end
+
+      it_behaves_like 'validation error'
     end
   end
 

From 3c435f9ba0d3a1a0a07722718eee26cf10ff55b6 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Thu, 6 Jun 2024 01:52:46 +0200
Subject: [PATCH 017/133] Change counters to be displayed on profile timelines
 in web UI (#30525)

---
 app/javascript/mastodon/features/account_timeline/index.jsx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/app/javascript/mastodon/features/account_timeline/index.jsx b/app/javascript/mastodon/features/account_timeline/index.jsx
index 5ec029593..0478f7a1a 100644
--- a/app/javascript/mastodon/features/account_timeline/index.jsx
+++ b/app/javascript/mastodon/features/account_timeline/index.jsx
@@ -199,6 +199,7 @@ class AccountTimeline extends ImmutablePureComponent {
           emptyMessage={emptyMessage}
           bindToDocument={!multiColumn}
           timelineId='account'
+          withCounters
         />
       </Column>
     );

From 569b7d2f25da120473937ab5719eba5ab0e314e1 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Wed, 5 Jun 2024 19:54:59 -0400
Subject: [PATCH 018/133] Clarify the purpose of separate Docker resources
 (#30568)

---
 Dockerfile         | 3 +++
 README.md          | 4 +++-
 docker-compose.yml | 3 +++
 3 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index 6d342db43..09aa8f2dd 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,8 @@
 # syntax=docker/dockerfile:1.7
 
+# This file is designed for production server deployment, not local development work
+# For a containerized local dev environment, see: https://github.com/mastodon/mastodon/blob/main/README.md#docker
+
 # Please see https://docs.docker.com/engine/reference/builder for information about
 # the extended buildx capabilities used in this file.
 # Make sure multiarch TARGETPLATFORM is available for interpolation
diff --git a/README.md b/README.md
index b8ee3f5db..45291d637 100644
--- a/README.md
+++ b/README.md
@@ -101,7 +101,9 @@ To set up **MacOS** for native development, complete the following steps:
 
 ### Docker
 
-For development with **Docker**, complete the following steps:
+For production hosting and deployment with **Docker**, use the `Dockerfile` and
+`docker-compose.yml` in the project root directory. To create a local
+development environment with **Docker**, complete the following steps:
 
 - Install Docker Desktop
 - Run `docker compose -f .devcontainer/docker-compose.yml up -d`
diff --git a/docker-compose.yml b/docker-compose.yml
index e7ae95ea7..7089b0d14 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,3 +1,6 @@
+# This file is designed for production server deployment, not local development work
+# For a containerized local dev environment, see: https://github.com/mastodon/mastodon/blob/main/README.md#docker
+
 services:
   db:
     restart: always

From e02d23b5499318432981b16d8968e109ebeca18c Mon Sep 17 00:00:00 2001
From: Emelia Smith <ThisIsMissEm@users.noreply.github.com>
Date: Thu, 6 Jun 2024 09:30:10 +0200
Subject: [PATCH 019/133] Change `read:me` scope to `profile` scope (#30357)

Co-authored-by: Claire <claire.github-309c@sitedethib.com>
---
 .../api/v1/accounts/credentials_controller.rb |  2 +-
 .../settings/applications_controller.rb       |  2 +-
 app/lib/scope_transformer.rb                  |  3 ++
 config/initializers/doorkeeper.rb             |  4 +--
 config/locales/doorkeeper.en.yml              |  3 +-
 ...3195202_change_read_me_scope_to_profile.rb | 23 +++++++++++++++
 db/schema.rb                                  |  2 +-
 lib/tasks/tests.rake                          | 28 ++++++++++++++++++-
 spec/lib/scope_transformer_spec.rb            |  6 ++++
 .../api/v1/accounts/credentials_spec.rb       |  4 +--
 10 files changed, 68 insertions(+), 9 deletions(-)
 create mode 100644 db/post_migrate/20240603195202_change_read_me_scope_to_profile.rb

diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb
index e8f712457..a37842518 100644
--- a/app/controllers/api/v1/accounts/credentials_controller.rb
+++ b/app/controllers/api/v1/accounts/credentials_controller.rb
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::Accounts::CredentialsController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read, :'read:accounts', :'read:me' }, except: [:update]
+  before_action -> { doorkeeper_authorize! :profile, :read, :'read:accounts' }, except: [:update]
   before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:update]
   before_action :require_user!
 
diff --git a/app/controllers/settings/applications_controller.rb b/app/controllers/settings/applications_controller.rb
index 6849979b1..d6573f9b4 100644
--- a/app/controllers/settings/applications_controller.rb
+++ b/app/controllers/settings/applications_controller.rb
@@ -13,7 +13,7 @@ class Settings::ApplicationsController < Settings::BaseController
   def new
     @application = Doorkeeper::Application.new(
       redirect_uri: Doorkeeper.configuration.native_redirect_uri,
-      scopes: 'read:me'
+      scopes: 'profile'
     )
   end
 
diff --git a/app/lib/scope_transformer.rb b/app/lib/scope_transformer.rb
index adcb711f8..7dda70922 100644
--- a/app/lib/scope_transformer.rb
+++ b/app/lib/scope_transformer.rb
@@ -11,6 +11,9 @@ class ScopeTransformer < Parslet::Transform
       @namespace = scope[:namespace]&.to_s
       @access    = scope[:access] ? [scope[:access].to_s] : DEFAULT_ACCESS.dup
       @term      = scope[:term]&.to_s || DEFAULT_TERM
+
+      # # override for profile scope which is read only
+      @access = %w(read) if @term == 'profile'
     end
 
     def key
diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb
index 1e8f9ad50..83100b1cf 100644
--- a/config/initializers/doorkeeper.rb
+++ b/config/initializers/doorkeeper.rb
@@ -74,7 +74,8 @@ Doorkeeper.configure do
   # For more information go to
   # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
   default_scopes  :read
-  optional_scopes :write,
+  optional_scopes :profile,
+                  :write,
                   :'write:accounts',
                   :'write:blocks',
                   :'write:bookmarks',
@@ -89,7 +90,6 @@ Doorkeeper.configure do
                   :'write:reports',
                   :'write:statuses',
                   :read,
-                  :'read:me',
                   :'read:accounts',
                   :'read:blocks',
                   :'read:bookmarks',
diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml
index 98776f219..b623cc713 100644
--- a/config/locales/doorkeeper.en.yml
+++ b/config/locales/doorkeeper.en.yml
@@ -135,6 +135,7 @@ en:
         media: Media attachments
         mutes: Mutes
         notifications: Notifications
+        profile: Your Mastodon profile
         push: Push notifications
         reports: Reports
         search: Search
@@ -165,6 +166,7 @@ en:
       admin:write:reports: perform moderation actions on reports
       crypto: use end-to-end encryption
       follow: modify account relationships
+      profile: read only your account's profile information
       push: receive your push notifications
       read: read all your account's data
       read:accounts: see accounts information
@@ -174,7 +176,6 @@ en:
       read:filters: see your filters
       read:follows: see your follows
       read:lists: see your lists
-      read:me: read only your account's basic information
       read:mutes: see your mutes
       read:notifications: see your notifications
       read:reports: see your reports
diff --git a/db/post_migrate/20240603195202_change_read_me_scope_to_profile.rb b/db/post_migrate/20240603195202_change_read_me_scope_to_profile.rb
new file mode 100644
index 000000000..05e5984c4
--- /dev/null
+++ b/db/post_migrate/20240603195202_change_read_me_scope_to_profile.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class ChangeReadMeScopeToProfile < ActiveRecord::Migration[7.1]
+  def up
+    replace_scopes('read:me', 'profile')
+  end
+
+  def down
+    replace_scopes('profile', 'read:me')
+  end
+
+  private
+
+  def replace_scopes(old_scope, new_scope)
+    Doorkeeper::Application.where("scopes LIKE '%#{old_scope}%'").in_batches do |applications|
+      applications.update_all("scopes = replace(scopes, '#{old_scope}', '#{new_scope}')")
+    end
+
+    Doorkeeper::AccessToken.where("scopes LIKE '%#{old_scope}%'").in_batches do |access_tokens|
+      access_tokens.update_all("scopes = replace(scopes, '#{old_scope}', '#{new_scope}')")
+    end
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 73f6b464e..ce2951608 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[7.1].define(version: 2024_05_22_041528) do
+ActiveRecord::Schema[7.1].define(version: 2024_06_03_195202) do
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
 
diff --git a/lib/tasks/tests.rake b/lib/tasks/tests.rake
index c8e0312bb..c8e4dc31c 100644
--- a/lib/tasks/tests.rake
+++ b/lib/tasks/tests.rake
@@ -130,11 +130,20 @@ namespace :tests do
       # This is checking the attribute rather than the method, to avoid the legacy fallback
       # and ensure the data has been migrated
       unless Account.find_local('qcuser').user[:otp_secret] == 'anotpsecretthatshouldbeencrypted'
-        puts "DEBUG: #{Account.find_local('qcuser').user.inspect}"
         puts 'OTP secret for user not preserved as expected'
         exit(1)
       end
 
+      unless Doorkeeper::Application.find(2)[:scopes] == 'write:accounts profile'
+        puts 'Application OAuth scopes not rewritten as expected'
+        exit(1)
+      end
+
+      unless Doorkeeper::Application.find(2).access_tokens.first[:scopes] == 'write:accounts profile'
+        puts 'OAuth access token scopes not rewritten as expected'
+        exit(1)
+      end
+
       puts 'No errors found. Database state is consistent with a successful migration process.'
     end
 
@@ -152,6 +161,23 @@ namespace :tests do
         VALUES
           (1, 'https://example.com/users/foobar', 'foobar@example.com', now(), now()),
           (1, 'https://example.com/users/foobar', 'foobar@example.com', now(), now());
+
+        /* Doorkeeper records
+           While the `read:me` scope was technically not valid in 3.3.0,
+           it is still useful for the purposes of testing the `ChangeReadMeScopeToProfile`
+           migration.
+        */
+
+        INSERT INTO "oauth_applications"
+          (id, name, uid, secret, redirect_uri, scopes, created_at, updated_at)
+        VALUES
+          (2, 'foo', 'foo', 'foo', 'https://example.com/#foo', 'write:accounts read:me', now(), now()),
+          (3, 'bar', 'bar', 'bar', 'https://example.com/#bar', 'read:me', now(), now());
+
+        INSERT INTO "oauth_access_tokens"
+          (token, application_id, scopes, resource_owner_id, created_at)
+        VALUES
+          ('secret', 2, 'write:accounts read:me', 4, now());
       SQL
     end
 
diff --git a/spec/lib/scope_transformer_spec.rb b/spec/lib/scope_transformer_spec.rb
index 8a9c7cf96..7bc226e94 100644
--- a/spec/lib/scope_transformer_spec.rb
+++ b/spec/lib/scope_transformer_spec.rb
@@ -20,6 +20,12 @@ describe ScopeTransformer do
       end
     end
 
+    context 'with scope "profile"' do
+      let(:input) { 'profile' }
+
+      it_behaves_like 'a scope', nil, 'profile', 'read'
+    end
+
     context 'with scope "read"' do
       let(:input) { 'read' }
 
diff --git a/spec/requests/api/v1/accounts/credentials_spec.rb b/spec/requests/api/v1/accounts/credentials_spec.rb
index 8ae9c78a0..a3f552cad 100644
--- a/spec/requests/api/v1/accounts/credentials_spec.rb
+++ b/spec/requests/api/v1/accounts/credentials_spec.rb
@@ -29,8 +29,8 @@ RSpec.describe 'credentials API' do
       })
     end
 
-    describe 'allows the read:me scope' do
-      let(:scopes) { 'read:me' }
+    describe 'allows the profile scope' do
+      let(:scopes) { 'profile' }
 
       it 'returns the response successfully' do
         subject

From 2fdd782f21a0d07b5c658c0e0f467c5b5622988a Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 6 Jun 2024 03:50:15 -0400
Subject: [PATCH 020/133] Fix empty `aria-hidden` attribute value in logo
 resources area (#30570)

---
 app/views/layouts/application.html.haml | 2 +-
 app/views/layouts/embedded.html.haml    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 5957d1dbf..ec6caa33a 100755
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -43,6 +43,6 @@
   %body{ class: body_classes }
     = content_for?(:content) ? yield(:content) : yield
 
-    .logo-resources{ 'tabindex' => '-1', 'inert' => true, 'aria-hidden' => true }
+    .logo-resources{ 'tabindex' => '-1', 'inert' => true, 'aria-hidden' => 'true' }
       = inline_svg_tag 'logo-symbol-icon.svg'
       = inline_svg_tag 'logo-symbol-wordmark.svg'
diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml
index c633fa9e0..f912b3faf 100644
--- a/app/views/layouts/embedded.html.haml
+++ b/app/views/layouts/embedded.html.haml
@@ -20,5 +20,5 @@
   %body.embed
     = yield
 
-    .logo-resources{ 'tabindex' => '-1', 'inert' => true, 'aria-hidden' => true }
+    .logo-resources{ 'tabindex' => '-1', 'inert' => true, 'aria-hidden' => 'true' }
       = inline_svg_tag 'logo-symbol-icon.svg'

From d72714e5ce5a7146665ad833f760dba82ad7a0cd Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 6 Jun 2024 03:50:31 -0400
Subject: [PATCH 021/133] Remove deprecated version value from
 `.devcontainer/docker-compose.yml` (#30567)

---
 .devcontainer/docker-compose.yml | 2 --
 1 file changed, 2 deletions(-)

diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
index 5d9917b39..85f9eb22c 100644
--- a/.devcontainer/docker-compose.yml
+++ b/.devcontainer/docker-compose.yml
@@ -1,5 +1,3 @@
-version: '3'
-
 services:
   app:
     working_dir: /workspaces/mastodon/

From a2505e861150abda0e692ffe86178393d701bc93 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Thu, 6 Jun 2024 10:43:04 +0200
Subject: [PATCH 022/133] Add timeline of public posts about a trending link to
 REST API (#30381)

---
 .../api/v1/timelines/link_controller.rb       |  52 +++++++
 app/models/link_feed.rb                       |  35 +++++
 config/routes/api.rb                          |   1 +
 spec/requests/api/v1/timelines/link_spec.rb   | 131 ++++++++++++++++++
 4 files changed, 219 insertions(+)
 create mode 100644 app/controllers/api/v1/timelines/link_controller.rb
 create mode 100644 app/models/link_feed.rb
 create mode 100644 spec/requests/api/v1/timelines/link_spec.rb

diff --git a/app/controllers/api/v1/timelines/link_controller.rb b/app/controllers/api/v1/timelines/link_controller.rb
new file mode 100644
index 000000000..af962c430
--- /dev/null
+++ b/app/controllers/api/v1/timelines/link_controller.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+class Api::V1::Timelines::LinkController < Api::V1::Timelines::BaseController
+  before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth?
+  before_action :set_preview_card
+  before_action :set_statuses
+
+  PERMITTED_PARAMS = %i(
+    url
+    limit
+  ).freeze
+
+  def show
+    cache_if_unauthenticated!
+    render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
+  end
+
+  private
+
+  def require_auth?
+    !Setting.timeline_preview
+  end
+
+  def set_preview_card
+    @preview_card = PreviewCard.joins(:trend).merge(PreviewCardTrend.allowed).find_by!(url: params[:url])
+  end
+
+  def set_statuses
+    @statuses = @preview_card.nil? ? [] : preload_collection(link_timeline_statuses, Status)
+  end
+
+  def link_timeline_statuses
+    link_feed.get(
+      limit_param(DEFAULT_STATUSES_LIMIT),
+      params[:max_id],
+      params[:since_id],
+      params[:min_id]
+    )
+  end
+
+  def link_feed
+    LinkFeed.new(@preview_card, current_account)
+  end
+
+  def next_path
+    api_v1_timelines_link_url next_path_params
+  end
+
+  def prev_path
+    api_v1_timelines_link_url prev_path_params
+  end
+end
diff --git a/app/models/link_feed.rb b/app/models/link_feed.rb
new file mode 100644
index 000000000..32efb331b
--- /dev/null
+++ b/app/models/link_feed.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+class LinkFeed < PublicFeed
+  # @param [PreviewCard] preview_card
+  # @param [Account] account
+  # @param [Hash] options
+  def initialize(preview_card, account, options = {})
+    @preview_card = preview_card
+    super(account, options)
+  end
+
+  # @param [Integer] limit
+  # @param [Integer] max_id
+  # @param [Integer] since_id
+  # @param [Integer] min_id
+  # @return [Array<Status>]
+  def get(limit, max_id = nil, since_id = nil, min_id = nil)
+    scope = public_scope
+
+    scope.merge!(discoverable)
+    scope.merge!(attached_to_preview_card)
+
+    scope.to_a_paginated_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
+  end
+
+  private
+
+  def attached_to_preview_card
+    Status.joins(:preview_cards_status).where(preview_cards_status: { preview_card_id: @preview_card.id })
+  end
+
+  def discoverable
+    Account.discoverable
+  end
+end
diff --git a/config/routes/api.rb b/config/routes/api.rb
index 135a19a0a..3eb4bb4b4 100644
--- a/config/routes/api.rb
+++ b/config/routes/api.rb
@@ -39,6 +39,7 @@ namespace :api, format: false do
     namespace :timelines do
       resource :home, only: :show, controller: :home
       resource :public, only: :show, controller: :public
+      resource :link, only: :show, controller: :link
       resources :tag, only: :show
       resources :list, only: :show
     end
diff --git a/spec/requests/api/v1/timelines/link_spec.rb b/spec/requests/api/v1/timelines/link_spec.rb
new file mode 100644
index 000000000..a219c9bcd
--- /dev/null
+++ b/spec/requests/api/v1/timelines/link_spec.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe 'Link' 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}" } }
+
+  shared_examples 'a successful request to the link timeline' do
+    it 'returns the expected statuses successfully', :aggregate_failures do
+      subject
+
+      expect(response).to have_http_status(200)
+      expect(body_as_json.pluck(:id)).to match_array(expected_statuses.map { |status| status.id.to_s })
+    end
+  end
+
+  describe 'GET /api/v1/timelines/link' do
+    subject do
+      get '/api/v1/timelines/link', headers: headers, params: params
+    end
+
+    let(:url) { 'https://example.com/' }
+    let(:private_status) { Fabricate(:status, visibility: :private) }
+    let(:undiscoverable_status) { Fabricate(:status, account: Fabricate.build(:account, domain: nil, discoverable: false)) }
+    let(:local_status) { Fabricate(:status, account: Fabricate.build(:account, domain: nil, discoverable: true)) }
+    let(:remote_status) { Fabricate(:status, account: Fabricate.build(:account, domain: 'example.com', discoverable: true)) }
+    let(:params) { { url: url } }
+    let(:expected_statuses) { [local_status, remote_status] }
+    let(:preview_card) { Fabricate(:preview_card, url: url) }
+
+    before do
+      if preview_card.present?
+        preview_card.create_trend!(allowed: true)
+
+        [private_status, undiscoverable_status, remote_status, local_status].each do |status|
+          PreviewCardsStatus.create(status: status, preview_card: preview_card, url: url)
+        end
+      end
+    end
+
+    context 'when there is no preview card' do
+      let(:preview_card) { nil }
+
+      it 'returns http not found' do
+        subject
+
+        expect(response).to have_http_status(404)
+      end
+    end
+
+    context 'when preview card is not trending' do
+      before do
+        preview_card.trend.destroy!
+      end
+
+      it 'returns http not found' do
+        subject
+
+        expect(response).to have_http_status(404)
+      end
+    end
+
+    context 'when preview card is trending but not approved' do
+      before do
+        preview_card.trend.update(allowed: false)
+      end
+
+      it 'returns http not found' do
+        subject
+
+        expect(response).to have_http_status(404)
+      end
+    end
+
+    context 'when the instance does not allow public preview' do
+      before do
+        Form::AdminSettings.new(timeline_preview: false).save
+      end
+
+      context 'when the user is not authenticated' do
+        let(:headers) { {} }
+
+        it 'returns http unauthorized' do
+          subject
+
+          expect(response).to have_http_status(401)
+        end
+      end
+
+      context 'when the user is authenticated' do
+        it_behaves_like 'a successful request to the link timeline'
+      end
+    end
+
+    context 'when the instance allows public preview' do
+      context 'with an authorized user' do
+        it_behaves_like 'a successful request to the link timeline'
+      end
+
+      context 'with an anonymous user' do
+        let(:headers) { {} }
+
+        it_behaves_like 'a successful request to the link timeline'
+      end
+
+      context 'with limit param' do
+        let(:params) { { limit: 1, url: url } }
+
+        it 'returns only the requested number of statuses', :aggregate_failures do
+          subject
+
+          expect(response).to have_http_status(200)
+          expect(body_as_json.size).to eq(params[:limit])
+        end
+
+        it 'sets the correct pagination headers', :aggregate_failures do
+          subject
+
+          expect(response)
+            .to include_pagination_headers(
+              prev: api_v1_timelines_link_url(limit: params[:limit], url: url, min_id: local_status.id),
+              next: api_v1_timelines_link_url(limit: params[:limit], url: url, max_id: local_status.id)
+            )
+        end
+      end
+    end
+  end
+end

From 2f0a34ac00f2d3cc561cd4fe3edbcf2bd62c35a8 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 6 Jun 2024 04:49:31 -0400
Subject: [PATCH 023/133] Run as root in devcontainer from vscode (#30574)

---
 .devcontainer/devcontainer.json | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index fa8d6542c..2c53be9c7 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -23,6 +23,8 @@
     }
   },
 
+  "remoteUser": "root",
+
   "otherPortsAttributes": {
     "onAutoForward": "silent"
   },

From a729104a4159106f959bcc9b3ab438d2876ce9a5 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 6 Jun 2024 10:53:28 +0200
Subject: [PATCH 024/133] New Crowdin Translations (automated) (#30575)

Co-authored-by: GitHub Actions <noreply@github.com>
---
 app/javascript/mastodon/locales/sk.json |  2 ++
 config/locales/activerecord.ia.yml      |  2 +-
 config/locales/devise.ia.yml            | 38 ++++++++++++-------------
 config/locales/devise.sv.yml            |  6 ++--
 config/locales/doorkeeper.ia.yml        | 36 +++++++++++------------
 config/locales/ia.yml                   |  2 +-
 config/locales/simple_form.ia.yml       |  6 ++--
 7 files changed, 47 insertions(+), 45 deletions(-)

diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index c583b5822..9b5be21f9 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -391,6 +391,7 @@
   "limited_account_hint.action": "Aj tak zobraziť profil",
   "limited_account_hint.title": "Tento profil bol skrytý správcami servera {domain}.",
   "link_preview.author": "Autor: {name}",
+  "link_preview.more_from_author": "Viac od {name}",
   "lists.account.add": "Pridať do zoznamu",
   "lists.account.remove": "Odstrániť zo zoznamu",
   "lists.delete": "Vymazať zoznam",
@@ -411,6 +412,7 @@
   "moved_to_account_banner.text": "Váš účet {disabledAccount} je momentálne deaktivovaný, pretože ste sa presunuli na {movedToAccount}.",
   "mute_modal.hide_from_notifications": "Ukryť z upozornení",
   "mute_modal.hide_options": "Skryť možnosti",
+  "mute_modal.indefinite": "Pokiaľ ich neodtíšim",
   "mute_modal.show_options": "Zobraziť možnosti",
   "mute_modal.title": "Stíšiť užívateľa?",
   "navigation_bar.about": "O tomto serveri",
diff --git a/config/locales/activerecord.ia.yml b/config/locales/activerecord.ia.yml
index b1dd90bc3..bf1fbc67e 100644
--- a/config/locales/activerecord.ia.yml
+++ b/config/locales/activerecord.ia.yml
@@ -19,7 +19,7 @@ ia:
         account:
           attributes:
             username:
-              invalid: debe continer solmente litteras, numeros e tractos de sublineamento
+              invalid: debe continer solmente litteras, numeros e lineettas basse
               reserved: es reservate
         admin/webhook:
           attributes:
diff --git a/config/locales/devise.ia.yml b/config/locales/devise.ia.yml
index e6ae6d4af..8e073c9ef 100644
--- a/config/locales/devise.ia.yml
+++ b/config/locales/devise.ia.yml
@@ -3,8 +3,8 @@ ia:
   devise:
     confirmations:
       confirmed: Tu conto de e-mail ha essite confirmate con successo.
-      send_instructions: Tu recipera un e-mail con instructiones pro confirmar tu adresse de e-mail in poc minutas. Per favor verifica tu dossier de spam si tu non lo recipe.
-      send_paranoid_instructions: Si tu adresse de e-mail existe in nostre base de datos, tu recipera un e-mail con instructiones pro confirmar tu adresse de e-mail in poc minutas. Per favor verifica tu dossier de spam si tu non lo recipe.
+      send_instructions: Tu recipera un e-mail con instructiones pro confirmar tu adresse de e-mail in poc minutas. Per favor consulta tu dossier de spam si tu non lo recipe.
+      send_paranoid_instructions: Si tu adresse de e-mail existe in nostre base de datos, tu recipera un e-mail con instructiones pro confirmar tu adresse de e-mail in poc minutas. Per favor consulta tu dossier de spam si tu non lo recipe.
     failure:
       already_authenticated: Tu ha jam aperite session.
       inactive: Tu conto non es ancora activate.
@@ -27,7 +27,7 @@ ia:
         subject: 'Mastodon: Instructiones de confirmation pro %{instance}'
         title: Verificar adresse de e-mail
       email_changed:
-        explanation: 'Le adresse de e-mail pro tu conto essera cambiate a:'
+        explanation: 'Le adresse de e-mail pro tu conto se cambia in:'
         extra: Si tu non ha cambiate de adresse de e-mail, es probabile que alcuno ha ganiate le accesso a tu conto. Per favor cambia immediatemente tu contrasigno o contacta le administrator del servitor si tu non pote acceder a tu conto.
         subject: 'Mastodon: E-mail cambiate'
         title: Nove adresse de e-mail
@@ -37,8 +37,8 @@ ia:
         subject: 'Mastodon: Contrasigno cambiate'
         title: Contrasigno cambiate
       reconfirmation_instructions:
-        explanation: Confirma le nove adresse pro cambiar tu email.
-        extra: Si non es tu qui ha initiate iste cambiamento, per favor ignora iste e-mail. Le adresse de e-mail pro le conto de Mastodon non cambiara usque tu accede al ligamine hic supra.
+        explanation: Confirma le nove adresse pro cambiar tu adresse de e-mail.
+        extra: Si non es tu qui ha initiate iste cambiamento, per favor ignora iste e-mail. Le adresse de e-mail pro le conto de Mastodon non cambiara usque tu accede al ligamine supra.
         subject: 'Mastodon: Confirmar e-mail pro %{instance}'
         title: Verificar adresse de e-mail
       reset_password_instructions:
@@ -49,19 +49,19 @@ ia:
         title: Reinitialisar contrasigno
       two_factor_disabled:
         explanation: Ora es possibile aperir session con solmente le adresse de e-mail e contrasigno.
-        subject: 'Mastodon: Authentication bifactorial disactivate'
-        subtitle: Le authentication bifactorial ha essite disactivate pro tu conto.
+        subject: 'Mastodon: Authentication a duo factores disactivate'
+        subtitle: Le authentication a duo factores ha essite disactivate pro tu conto.
         title: A2F disactivate
       two_factor_enabled:
         explanation: Pro le apertura de session essera necessari un token generate per le application TOTP accopulate.
-        subject: 'Mastodon: Authentication bifactorial activate'
-        subtitle: Le authentication bifactorial ha essite activate pro tu conto.
+        subject: 'Mastodon: Authentication a duo factores activate'
+        subtitle: Le authentication a duo factores ha essite activate pro tu conto.
         title: A2F activate
       two_factor_recovery_codes_changed:
         explanation: Le ancian codices de recuperation ha essite invalidate e nove codices ha essite generate.
-        subject: 'Mastodon: Codices de recuperation regenerate'
+        subject: 'Mastodon: Codices de recuperation A2F regenerate'
         subtitle: Le ancian codices de recuperation ha essite invalidate e nove codices ha essite generate.
-        title: Codices de recuperation cambiate
+        title: Codices de recuperation A2F cambiate
       unlock_instructions:
         subject: 'Mastodon: Instructiones pro disblocar'
       webauthn_credential:
@@ -84,11 +84,11 @@ ia:
         subject: 'Mastodon: authentication de clave de securitate activate'
         title: Claves de securitate activate
     omniauth_callbacks:
-      failure: Impossibile authenticar te ab %{kind} perque “%{reason}”.
-      success: Authenticate con successo ab conto %{kind}.
+      failure: Non poteva authenticar te desde %{kind} perque “%{reason}”.
+      success: Authenticate correctemente desde le conto %{kind}.
     passwords:
-      no_token: Tu non pote acceder iste pagina sin venir ab un email de redefinition de contrasigno. Si tu veni ab un email de redefinition de contrasigno, verifica que tu usava le integre URL fornite.
-      send_instructions: Si tu adresse de e-mail existe in nostre base de datos, tu recipera un ligamine de recuperation de contrasigno in tu adresse de e-mail in poc minutas. Per favor verifica tu dossier de spam si tu non lo recipe.
+      no_token: Non es possibile acceder a iste pagina sin venir de un e-mail de redefinition de contrasigno. Si tu veni de un e-mail de redefinition de contrasigno, per favor assecura te de haber usate le URL complete fornite.
+      send_instructions: Si tu adresse de e-mail existe in nostre base de datos, tu recipera un ligamine de recuperation de contrasigno a tu adresse de e-mail in poc minutas. Per favor consulta tu dossier de spam si tu non lo recipe.
       send_paranoid_instructions: Si tu adresse de e-mail existe in nostre base de datos, tu recipera un ligamine de recuperation de contrasigno in tu adresse de e-mail in poc minutas. Per favor verifica tu dossier de spam si tu non lo recipe.
       updated: Tu contrasigno ha essite cambiate. Tu ha ora aperite session.
       updated_not_active: Tu contrasigno ha essite cambiate.
@@ -98,17 +98,17 @@ ia:
       signed_up_but_inactive: Tu te ha inscribite con successo. Nonobstante, nos non poteva aperir tu session perque tu conto non es ancora activate.
       signed_up_but_locked: Tu te ha inscribite con successo. Nonobstante, nos non poteva aperir tu session perque tu conto es serrate.
       signed_up_but_pending: Un message con un ligamine de confirmation ha essite inviate a tu adresse de email. Post que tu clicca sur le ligamine, nos revidera tu demanda. Tu essera notificate si illo es approbate.
-      signed_up_but_unconfirmed: Un message con un ligamine de confirmation ha essite inviate a tu adresse de e-mail. Per favor seque le ligamine pro activar tu conto. Verifica tu dossier de spam si tu non recipe iste e-mail.
-      update_needs_confirmation: Tu ha actualisate tu conto con successo, ma nos debe verificar tu nove adresse de e-mail. Accede a tu e-mail e seque le ligamine de confirmation pro confirmar tu nove adresse de e-mail. Verifica tu dossier de spam si tu non recipe iste e-mail.
+      signed_up_but_unconfirmed: Un message con un ligamine de confirmation ha essite inviate a tu adresse de e-mail. Per favor seque le ligamine pro activar tu conto. Consulta tu dossier de spam si tu non recipe iste e-mail.
+      update_needs_confirmation: Tu ha actualisate tu conto con successo, ma nos debe verificar tu nove adresse de e-mail. Accede a tu e-mail e seque le ligamine de confirmation pro confirmar tu nove adresse de e-mail. Consulta tu dossier de spam si tu non recipe iste e-mail.
       updated: Tu conto ha essite actualisate con successo.
     sessions:
       already_signed_out: Session claudite con successo.
       signed_in: Session aperite con successo.
       signed_out: Session claudite con successo.
     unlocks:
-      send_instructions: Tu recipera un e-mail con instructiones explicante como disserrar tu conto in alcun minutas. Verifica tu dossier de spam si tu non recipe iste e-mail.
+      send_instructions: Tu recipera un e-mail con instructiones explicante como disserrar tu conto in alcun minutas. Consulta tu dossier de spam si tu non recipe iste e-mail.
       send_paranoid_instructions: Si tu conto existe, tu recipera un email con instructiones explicante como disserrar lo in alcun minutas. Verifica tu dossier de spam si tu non recipe iste e-mail.
-      unlocked: Tu conto ha essite disserrate con successo. Aperi session pro continuar.
+      unlocked: Tu conto ha essite disserrate con successo. Per favor aperi session pro continuar.
   errors:
     messages:
       already_confirmed: jam esseva confirmate, tenta aperir session
diff --git a/config/locales/devise.sv.yml b/config/locales/devise.sv.yml
index 27d424f61..1e14a7de5 100644
--- a/config/locales/devise.sv.yml
+++ b/config/locales/devise.sv.yml
@@ -30,14 +30,14 @@ sv:
         explanation: 'E-postadressen för ditt konto ändras till:'
         extra: Om du inte ändrade din e-post är det troligt att någon har fått tillgång till ditt konto. Vänligen ändra ditt lösenord omedelbart eller kontakta serveradministratören om du är utelåst från ditt konto.
         subject: 'Mastodon: e-post ändrad'
-        title: Ny e-post adress
+        title: Ny e-postadress
       password_change:
         explanation: Lösenordet för ditt konto har ändrats.
         extra: Om du inte ändrade ditt lösenord är det troligt att någon har fått tillgång till ditt konto. Vänligen ändra ditt lösenord omedelbart eller kontakta serveradministratören om du är utelåst från ditt konto.
         subject: 'Mastodon: Lösenordet har ändrats'
         title: Lösenordet har ändrats
       reconfirmation_instructions:
-        explanation: Bekräfta den nya adressen för att ändra din e-post adress.
+        explanation: Bekräfta den nya adressen för att ändra din e-postadress.
         extra: Om den här ändringen inte initierades av dig kan du ignorera det här e-postmeddelandet. E-postadressen för Mastodon-kontot ändras inte förrän du klickar på länken ovan.
         subject: 'Mastodon: Bekräfta e-post för %{instance}'
         title: Verifiera e-postadress
@@ -63,7 +63,7 @@ sv:
         subtitle: De tidigare återhämtningskoderna har ogiltigförklarats och nya har skapats.
         title: 2FA-återställningskoder ändrades
       unlock_instructions:
-        subject: 'Mastodon: Lås upp instruktioner'
+        subject: 'Mastodon: Instruktioner för upplåsning'
       webauthn_credential:
         added:
           explanation: Följande säkerhetsnyckel har lagts till i ditt konto
diff --git a/config/locales/doorkeeper.ia.yml b/config/locales/doorkeeper.ia.yml
index 82a1b4b14..40109a311 100644
--- a/config/locales/doorkeeper.ia.yml
+++ b/config/locales/doorkeeper.ia.yml
@@ -61,7 +61,7 @@ ia:
         title: Un error ha occurrite
       new:
         prompt_html: "%{client_name} vole haber le permission de acceder a tu conto. Illo es un application tertie. <strong>Si tu non confide in illo, alora tu non deberea autorisar lo.</strong>"
-        review_permissions: Revisionar le permissos
+        review_permissions: Revider permissiones
         title: Autorisation necessari
       show:
         title: Copia iste codice de autorisation e colla lo in le application.
@@ -148,34 +148,34 @@ ia:
         title: Autorisation OAuth necessari
     scopes:
       admin:read: leger tote le datos in le servitor
-      admin:read:accounts: leger information sensibile de tote le contos
-      admin:read:canonical_email_blocks: leger datos sensibile de tote le blocadas de email canonic
+      admin:read:accounts: leger informationes sensibile de tote le contos
+      admin:read:canonical_email_blocks: leger informationes sensibile de tote le blocadas de e-mail canonic
       admin:read:domain_allows: leger informationes sensibile de tote le dominios permittite
       admin:read:domain_blocks: leger informationes sensibile de tote le blocadas de dominio
-      admin:read:email_domain_blocks: leger informationes sensibile de tote le blocadas de dominio email
-      admin:read:ip_blocks: leger informationes sensibile de tote le blocadas de IP
-      admin:read:reports: leger information sensibile de tote le reportos e contos reportate
+      admin:read:email_domain_blocks: leger informationes sensibile de tote le blocadas de dominio de e-mail
+      admin:read:ip_blocks: leger informationes sensibile de tote le blocadas de adresses IP
+      admin:read:reports: leger informationes sensibile de tote le reportos e contos reportate
       admin:write: modificar tote le datos in le servitor
-      admin:write:accounts: exequer action de moderation sur contos
-      admin:write:canonical_email_blocks: exequer actiones de moderation sur blocadas de email canonic
+      admin:write:accounts: exequer actiones de moderation sur contos
+      admin:write:canonical_email_blocks: exequer actiones de moderation sur blocadas de e-mail canonic
       admin:write:domain_allows: exequer actiones de moderation sur dominios permittite
       admin:write:domain_blocks: exequer actiones de moderation sur blocadas de dominio
-      admin:write:email_domain_blocks: exequer actiones de moderation sur blocadas de dominio email
-      admin:write:ip_blocks: exequer actiones de moderation sur blocadas de IP
-      admin:write:reports: exequer action de moderation sur reportos
-      crypto: usar cryptation de extremo-a-extremo
-      follow: modificar relationes del contos
+      admin:write:email_domain_blocks: exequer actiones de moderation sur blocadas de dominio de e-mail
+      admin:write:ip_blocks: exequer actiones de moderation sur blocadas de adresses IP
+      admin:write:reports: exequer actiones de moderation sur reportos
+      crypto: usar cryptation de puncta a puncta
+      follow: modificar relationes inter contos
       push: reciper tu notificationes push
       read: leger tote le datos de tu conto
-      read:accounts: vider informationes de conto
+      read:accounts: vider informationes de contos
       read:blocks: vider tu blocadas
       read:bookmarks: vider tu marcapaginas
-      read:favourites: vider tu favoritos
+      read:favourites: vider tu favorites
       read:filters: vider tu filtros
-      read:follows: vider tu sequites
+      read:follows: vider qui tu seque
       read:lists: vider tu listas
       read:me: leger solmente le information basic de tu conto
-      read:mutes: vider tu silentiates
+      read:mutes: vider qui tu silentia
       read:notifications: vider tu notificationes
       read:reports: vider tu reportos
       read:search: cercar in tu nomine
@@ -189,7 +189,7 @@ ia:
       write:filters: crear filtros
       write:follows: sequer personas
       write:lists: crear listas
-      write:media: incargar files de medios
+      write:media: incargar files multimedial
       write:mutes: silentiar personas e conversationes
       write:notifications: rader tu notificationes
       write:reports: reportar altere personas
diff --git a/config/locales/ia.yml b/config/locales/ia.yml
index cadc465c5..b7f4ecf85 100644
--- a/config/locales/ia.yml
+++ b/config/locales/ia.yml
@@ -1102,7 +1102,7 @@ ia:
     security: Securitate
     set_new_password: Definir un nove contrasigno
     setup:
-      email_below_hint_html: Consulta tu dossier de spam, o requesta un altere. Tu pote corriger tu adresse de e-mail si illo es errate.
+      email_below_hint_html: Consulta tu dossier de spam, o requesta un altere ligamine de confirmation. Tu pote corriger tu adresse de e-mail si illo es errate.
       email_settings_hint_html: Clicca sur le ligamine que nos te ha inviate pro verificar %{email}. Nos te attendera hic.
       link_not_received: Necun ligamine recipite?
       new_confirmation_instructions_sent: Tu recipera un nove e-mail con le ligamine de confirmation in poc minutas!
diff --git a/config/locales/simple_form.ia.yml b/config/locales/simple_form.ia.yml
index 7e364889d..2d0af3001 100644
--- a/config/locales/simple_form.ia.yml
+++ b/config/locales/simple_form.ia.yml
@@ -8,7 +8,7 @@ ia:
         fields: Tu pagina principal, pronomines, etate, tote lo que tu vole.
         indexable: Tu messages public pote apparer in le resultatos de recerca sur Mastodon. Le personas qui ha interagite con tu messages pote cercar los in omne caso.
         note: 'Tu pote @mentionar altere personas o #hashtags.'
-        show_collections: Le personas potera percurrer tu sequites e sequitores. Le personas que tu seque videra que tu les seque in omne caso.
+        show_collections: Le gente potera percurrer le listas de personas que tu seque e qui te seque. Le personas que tu seque videra que tu les seque in omne caso.
         unlocked: Le personas potera sequer te sin requestar approbation. Dismarca si tu vole revider le requestas de sequimento e seliger si acceptar o rejectar nove sequitores.
       account_alias:
         acct: Specifica le nomine_de_usator@dominio del conto desde le qual tu vole migrar
@@ -38,7 +38,7 @@ ia:
       appeal:
         text: Tu pote solo appellar contra un sanction un vice
       defaults:
-        autofollow: Illes qui se inscribe per le invitation automaticamente devenira tu sequaces
+        autofollow: Le personas qui se inscribe per medio del invitation te sequera automaticamente
         avatar: WEBP, PNG, GIF or JPG. Al maximo %{size}. Sera diminuite a %{dimensions}px
         bot: Signala a alteres que le conto principalmente exeque actiones automatisate e poterea non esser surveliate
         context: Un o plure contextos ubi le filtro deberea applicar se
@@ -269,7 +269,7 @@ ia:
         trends: Activar tendentias
         trends_as_landing_page: Usar tendentias como pagina de destination
       interactions:
-        must_be_follower: Blocar notificationes de non-sequaces
+        must_be_follower: Blocar notificationes de personas qui non te seque
         must_be_following: Blocar notificationes de gente que tu non sequer
         must_be_following_dm: Blocar messages directe de gente que tu non seque
       invite:

From cd4b00810d49bb95b6421c84d48a6e075a18486c Mon Sep 17 00:00:00 2001
From: Fabio Leandro Janiszevski <fabiosammy@gmail.com>
Date: Thu, 6 Jun 2024 06:00:09 -0300
Subject: [PATCH 025/133] Clear the docker setup - Deprecate post-create.sh and
 use bin/setup (#30502)

---
 .devcontainer/codespaces/devcontainer.json |  2 +-
 .devcontainer/devcontainer.json            |  2 +-
 .devcontainer/post-create.sh               | 27 ----------------------
 README.md                                  |  2 +-
 4 files changed, 3 insertions(+), 30 deletions(-)
 delete mode 100755 .devcontainer/post-create.sh

diff --git a/.devcontainer/codespaces/devcontainer.json b/.devcontainer/codespaces/devcontainer.json
index ca9156fda..6736734e6 100644
--- a/.devcontainer/codespaces/devcontainer.json
+++ b/.devcontainer/codespaces/devcontainer.json
@@ -37,7 +37,7 @@
   },
 
   "onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
-  "postCreateCommand": ".devcontainer/post-create.sh",
+  "postCreateCommand": "bin/setup",
   "waitFor": "postCreateCommand",
 
   "customizations": {
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 2c53be9c7..4a9cf11cc 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -30,7 +30,7 @@
   },
 
   "onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
-  "postCreateCommand": ".devcontainer/post-create.sh",
+  "postCreateCommand": "bin/setup",
   "waitFor": "postCreateCommand",
 
   "customizations": {
diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh
deleted file mode 100755
index 82a2ccbb6..000000000
--- a/.devcontainer/post-create.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-
-set -e # Fail the whole script on first error
-
-# Fetch Ruby gem dependencies
-bundle config path 'vendor/bundle'
-bundle config with 'development test'
-bundle install
-
-# Make Gemfile.lock pristine again
-git checkout -- Gemfile.lock
-
-# Fetch Javascript dependencies
-corepack prepare
-yarn install --immutable
-
-# [re]create, migrate, and seed the test database
-RAILS_ENV=test ./bin/rails db:setup
-
-# [re]create, migrate, and seed the development database
-RAILS_ENV=development ./bin/rails db:setup
-
-# Precompile assets for development
-RAILS_ENV=development ./bin/rails assets:precompile
-
-# Precompile assets for test
-RAILS_ENV=test ./bin/rails assets:precompile
diff --git a/README.md b/README.md
index 45291d637..3773b647f 100644
--- a/README.md
+++ b/README.md
@@ -107,7 +107,7 @@ development environment with **Docker**, complete the following steps:
 
 - Install Docker Desktop
 - Run `docker compose -f .devcontainer/docker-compose.yml up -d`
-- Run `docker compose -f .devcontainer/docker-compose.yml exec app .devcontainer/post-create.sh`
+- Run `docker compose -f .devcontainer/docker-compose.yml exec app bin/setup`
 - Finally, run `docker compose -f .devcontainer/docker-compose.yml exec app bin/dev`
 
 If you are using an IDE with [support for the Development Container specification](https://containers.dev/supporting), it will run the above `docker compose` commands automatically. For **Visual Studio Code** this requires the [Dev Container extension](https://containers.dev/supporting#dev-containers).

From 5652ca613582df03e5b838626078981414f3b897 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 6 Jun 2024 11:27:54 +0200
Subject: [PATCH 026/133] fix(deps): update babel monorepo to v7.24.7 (#30561)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 yarn.lock | 1290 +++++++++++++++++++++++++++--------------------------
 1 file changed, 649 insertions(+), 641 deletions(-)

diff --git a/yarn.lock b/yarn.lock
index 7c36984cd..dcc17d659 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -42,128 +42,129 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/code-frame@npm:7.24.6"
+"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/code-frame@npm:7.24.7"
   dependencies:
-    "@babel/highlight": "npm:^7.24.6"
+    "@babel/highlight": "npm:^7.24.7"
     picocolors: "npm:^1.0.0"
-  checksum: 10c0/c93c6d1763530f415218c31d07359364397f19b70026abdff766164c21ed352a931cf07f3102c5fb9e04792de319e332d68bcb1f7debef601a02197f90f9ba24
+  checksum: 10c0/ab0af539473a9f5aeaac7047e377cb4f4edd255a81d84a76058595f8540784cc3fbe8acf73f1e073981104562490aabfb23008cd66dc677a456a4ed5390fdde6
   languageName: node
   linkType: hard
 
-"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/compat-data@npm:7.24.6"
-  checksum: 10c0/f50abbd4008eb2a5d12139c578809cebbeaeb8e660fb12d546eb2e7c2108ae1836ab8339184a5f5ce0e95bf81bb91e18edce86b387c59db937b01693ec0bc774
+"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/compat-data@npm:7.24.7"
+  checksum: 10c0/dcd93a5632b04536498fbe2be5af1057f635fd7f7090483d8e797878559037e5130b26862ceb359acbae93ed27e076d395ddb4663db6b28a665756ffd02d324f
   languageName: node
   linkType: hard
 
 "@babel/core@npm:^7.10.4, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.22.1, @babel/core@npm:^7.24.4":
-  version: 7.24.6
-  resolution: "@babel/core@npm:7.24.6"
+  version: 7.24.7
+  resolution: "@babel/core@npm:7.24.7"
   dependencies:
     "@ampproject/remapping": "npm:^2.2.0"
-    "@babel/code-frame": "npm:^7.24.6"
-    "@babel/generator": "npm:^7.24.6"
-    "@babel/helper-compilation-targets": "npm:^7.24.6"
-    "@babel/helper-module-transforms": "npm:^7.24.6"
-    "@babel/helpers": "npm:^7.24.6"
-    "@babel/parser": "npm:^7.24.6"
-    "@babel/template": "npm:^7.24.6"
-    "@babel/traverse": "npm:^7.24.6"
-    "@babel/types": "npm:^7.24.6"
+    "@babel/code-frame": "npm:^7.24.7"
+    "@babel/generator": "npm:^7.24.7"
+    "@babel/helper-compilation-targets": "npm:^7.24.7"
+    "@babel/helper-module-transforms": "npm:^7.24.7"
+    "@babel/helpers": "npm:^7.24.7"
+    "@babel/parser": "npm:^7.24.7"
+    "@babel/template": "npm:^7.24.7"
+    "@babel/traverse": "npm:^7.24.7"
+    "@babel/types": "npm:^7.24.7"
     convert-source-map: "npm:^2.0.0"
     debug: "npm:^4.1.0"
     gensync: "npm:^1.0.0-beta.2"
     json5: "npm:^2.2.3"
     semver: "npm:^6.3.1"
-  checksum: 10c0/e0762a8daef7f417494d555929418cfacd6848c7fc3310ec00e6dd8cecac20b7f590e760bfc9365d2af07874a3f5599832f9c9ff7f1a9d126a168f77ba67945a
+  checksum: 10c0/4004ba454d3c20a46ea66264e06c15b82e9f6bdc35f88819907d24620da70dbf896abac1cb4cc4b6bb8642969e45f4d808497c9054a1388a386cf8c12e9b9e0d
   languageName: node
   linkType: hard
 
-"@babel/generator@npm:^7.24.6, @babel/generator@npm:^7.7.2":
-  version: 7.24.6
-  resolution: "@babel/generator@npm:7.24.6"
+"@babel/generator@npm:^7.24.7, @babel/generator@npm:^7.7.2":
+  version: 7.24.7
+  resolution: "@babel/generator@npm:7.24.7"
   dependencies:
-    "@babel/types": "npm:^7.24.6"
+    "@babel/types": "npm:^7.24.7"
     "@jridgewell/gen-mapping": "npm:^0.3.5"
     "@jridgewell/trace-mapping": "npm:^0.3.25"
     jsesc: "npm:^2.5.1"
-  checksum: 10c0/8d71a17b386536582354afba53cc784396458a88cc9f05f0c6de0ec99475f6f539943b3566b2e733820c4928236952473831765e483c25d68cc007a6e604d782
+  checksum: 10c0/06b1f3350baf527a3309e50ffd7065f7aee04dd06e1e7db794ddfde7fe9d81f28df64edd587173f8f9295496a7ddb74b9a185d4bf4de7bb619e6d4ec45c8fd35
   languageName: node
   linkType: hard
 
-"@babel/helper-annotate-as-pure@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-annotate-as-pure@npm:7.24.6"
+"@babel/helper-annotate-as-pure@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-annotate-as-pure@npm:7.24.7"
   dependencies:
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/3fe446e3bd37e5e32152279c84ace4e83815e5b88b9e09a82a83974a0bb22e941d89db26b23aaab4c9eb0f9713772c2f6163feffc1bcb055c4cdb6b67e5dc82f
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/4679f7df4dffd5b3e26083ae65228116c3da34c3fff2c11ae11b259a61baec440f51e30fd236f7a0435b9d471acd93d0bc5a95df8213cbf02b1e083503d81b9a
   languageName: node
   linkType: hard
 
-"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.24.6"
+"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.24.7"
   dependencies:
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/d468ba492163bdcf5b6c53248edcf0aaed6194c0f7bdebef4f29ef626e5b03e9fcc7ed737445eb80a961ec6e687c330e1c5242d8a724efb0af002141f3b3e66c
+    "@babel/traverse": "npm:^7.24.7"
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/0ed84abf848c79fb1cd4c1ddac12c771d32c1904d87fc3087f33cfdeb0c2e0db4e7892b74b407d9d8d0c000044f3645a7391a781f788da8410c290bb123a1f13
   languageName: node
   linkType: hard
 
-"@babel/helper-builder-react-jsx@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-builder-react-jsx@npm:7.24.6"
+"@babel/helper-builder-react-jsx@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-builder-react-jsx@npm:7.24.7"
   dependencies:
-    "@babel/helper-annotate-as-pure": "npm:^7.24.6"
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/93b0500d00f214bc2f7f142ebfa0a634872cadd446bd767f7d58b26ae1b46e1f262b0fe80a9151691463611a3148a69ad28f930295d976bf8ced32c79449a3ce
+    "@babel/helper-annotate-as-pure": "npm:^7.24.7"
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/c95c8856c67c57060461f39b669707b2dca3501149bcc54b7c3e49c95069afce52179fc7c2bd809981acfbfdf0860ef1035dd7512cdba46c83bdb1ad6f6dc53f
   languageName: node
   linkType: hard
 
-"@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-compilation-targets@npm:7.24.6"
+"@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-compilation-targets@npm:7.24.7"
   dependencies:
-    "@babel/compat-data": "npm:^7.24.6"
-    "@babel/helper-validator-option": "npm:^7.24.6"
+    "@babel/compat-data": "npm:^7.24.7"
+    "@babel/helper-validator-option": "npm:^7.24.7"
     browserslist: "npm:^4.22.2"
     lru-cache: "npm:^5.1.1"
     semver: "npm:^6.3.1"
-  checksum: 10c0/4d41150086959f5f4d72d27bae29204192e943537ecb71df1711d1f5d8791358a44f3a5882ed3c8238ba0c874b0b55213af43767e14771765f13b8d15b262432
+  checksum: 10c0/1d580a9bcacefe65e6bf02ba1dafd7ab278269fef45b5e281d8354d95c53031e019890464e7f9351898c01502dd2e633184eb0bcda49ed2ecd538675ce310f51
   languageName: node
   linkType: hard
 
-"@babel/helper-create-class-features-plugin@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-create-class-features-plugin@npm:7.24.6"
+"@babel/helper-create-class-features-plugin@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-create-class-features-plugin@npm:7.24.7"
   dependencies:
-    "@babel/helper-annotate-as-pure": "npm:^7.24.6"
-    "@babel/helper-environment-visitor": "npm:^7.24.6"
-    "@babel/helper-function-name": "npm:^7.24.6"
-    "@babel/helper-member-expression-to-functions": "npm:^7.24.6"
-    "@babel/helper-optimise-call-expression": "npm:^7.24.6"
-    "@babel/helper-replace-supers": "npm:^7.24.6"
-    "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.6"
-    "@babel/helper-split-export-declaration": "npm:^7.24.6"
+    "@babel/helper-annotate-as-pure": "npm:^7.24.7"
+    "@babel/helper-environment-visitor": "npm:^7.24.7"
+    "@babel/helper-function-name": "npm:^7.24.7"
+    "@babel/helper-member-expression-to-functions": "npm:^7.24.7"
+    "@babel/helper-optimise-call-expression": "npm:^7.24.7"
+    "@babel/helper-replace-supers": "npm:^7.24.7"
+    "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7"
+    "@babel/helper-split-export-declaration": "npm:^7.24.7"
     semver: "npm:^6.3.1"
   peerDependencies:
     "@babel/core": ^7.0.0
-  checksum: 10c0/e6734671bc6a5f3cca4ec46e4cc70238e5a2fa063e51225c2be572f157119002af419b33ea0f846dbb1307370fe9f3aa92d199449abbea5e88e0262513c8a821
+  checksum: 10c0/6b7b47d70b41c00f39f86790cff67acf2bce0289d52a7c182b28e797f4e0e6d69027e3d06eccf1d54dddc2e5dde1df663bb1932437e5f447aeb8635d8d64a6ab
   languageName: node
   linkType: hard
 
-"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-create-regexp-features-plugin@npm:7.24.6"
+"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-create-regexp-features-plugin@npm:7.24.7"
   dependencies:
-    "@babel/helper-annotate-as-pure": "npm:^7.24.6"
+    "@babel/helper-annotate-as-pure": "npm:^7.24.7"
     regexpu-core: "npm:^5.3.1"
     semver: "npm:^6.3.1"
   peerDependencies:
     "@babel/core": ^7.0.0
-  checksum: 10c0/c6e1b07c94b3b93a3f534039da88bc67ec3156080f1959aa07d5d534e9a640de3533e7ded0516dfcbccde955e91687044e6a950852b1d3f402ac5d5001be56cf
+  checksum: 10c0/ed611a7eb0c71843f9cdc471eeb38767972229f9225f7aaa90d124d7ee0062cf6908fd53ee9c34f731394c429594f06049a7738a71d342e0191d4047b2fc0ac2
   languageName: node
   linkType: hard
 
@@ -182,242 +183,249 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@babel/helper-environment-visitor@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-environment-visitor@npm:7.24.6"
-  checksum: 10c0/fdcd18ac505ed71f40c05cc992b648a4495b0aa5310a774492a0f74d8dcf3579691102f516561a651d3de6c3a44fe64bfb3049d11c14c5857634ef1823ea409a
+"@babel/helper-environment-visitor@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-environment-visitor@npm:7.24.7"
+  dependencies:
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/36ece78882b5960e2d26abf13cf15ff5689bf7c325b10a2895a74a499e712de0d305f8d78bb382dd3c05cfba7e47ec98fe28aab5674243e0625cd38438dd0b2d
   languageName: node
   linkType: hard
 
-"@babel/helper-function-name@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-function-name@npm:7.24.6"
+"@babel/helper-function-name@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-function-name@npm:7.24.7"
   dependencies:
-    "@babel/template": "npm:^7.24.6"
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/5ba2f8db789b3f5a2b2239300a217aa212e303cd7bfad9c8b90563807f49215e8c679e8f8f177b6aaca2038038e29bc702b83839e1f7b4896d79c44a75cac97a
+    "@babel/template": "npm:^7.24.7"
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/e5e41e6cf86bd0f8bf272cbb6e7c5ee0f3e9660414174435a46653efba4f2479ce03ce04abff2aa2ef9359cf057c79c06cb7b134a565ad9c0e8a50dcdc3b43c4
   languageName: node
   linkType: hard
 
-"@babel/helper-hoist-variables@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-hoist-variables@npm:7.24.6"
+"@babel/helper-hoist-variables@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-hoist-variables@npm:7.24.7"
   dependencies:
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/e10ec6b864aaa419ec4934f5fcb5d0cfcc9d0657584a1b6c3c42ada949d44ca6bffcdab433a90ada4396c747e551cca31ba0e565ea005ab3f50964e3817bf6cf
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/19ee37563bbd1219f9d98991ad0e9abef77803ee5945fd85aa7aa62a67c69efca9a801696a1b58dda27f211e878b3327789e6fd2a6f6c725ccefe36774b5ce95
   languageName: node
   linkType: hard
 
-"@babel/helper-member-expression-to-functions@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-member-expression-to-functions@npm:7.24.6"
+"@babel/helper-member-expression-to-functions@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-member-expression-to-functions@npm:7.24.7"
   dependencies:
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/7595f62978f55921b24de6ed5252fcedbffacfb8271f71e092f38724179ba554cb3a24a4764a1a3890b8a53504c2bee9c99eab81f1f365582739f566c8e28eaa
+    "@babel/traverse": "npm:^7.24.7"
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/9638c1d33cf6aba028461ccd3db6061c76ff863ca0d5013dd9a088bf841f2f77c46956493f9da18355c16759449d23b74cc1de4da357ade5c5c34c858f840f0a
   languageName: node
   linkType: hard
 
-"@babel/helper-module-imports@npm:^7.0.0-beta.49, @babel/helper-module-imports@npm:^7.10.4, @babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-module-imports@npm:7.24.6"
+"@babel/helper-module-imports@npm:^7.0.0-beta.49, @babel/helper-module-imports@npm:^7.10.4, @babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-module-imports@npm:7.24.7"
   dependencies:
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/e0db3fbfcd963d138f0792ff626f940a576fcf212d02b8fe6478dccf3421bd1c2a76f8e69c7450c049985e7b63b30be309a24eeeb6ad7c2137a31b676a095a84
+    "@babel/traverse": "npm:^7.24.7"
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/97c57db6c3eeaea31564286e328a9fb52b0313c5cfcc7eee4bc226aebcf0418ea5b6fe78673c0e4a774512ec6c86e309d0f326e99d2b37bfc16a25a032498af0
   languageName: node
   linkType: hard
 
-"@babel/helper-module-transforms@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-module-transforms@npm:7.24.6"
+"@babel/helper-module-transforms@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-module-transforms@npm:7.24.7"
   dependencies:
-    "@babel/helper-environment-visitor": "npm:^7.24.6"
-    "@babel/helper-module-imports": "npm:^7.24.6"
-    "@babel/helper-simple-access": "npm:^7.24.6"
-    "@babel/helper-split-export-declaration": "npm:^7.24.6"
-    "@babel/helper-validator-identifier": "npm:^7.24.6"
+    "@babel/helper-environment-visitor": "npm:^7.24.7"
+    "@babel/helper-module-imports": "npm:^7.24.7"
+    "@babel/helper-simple-access": "npm:^7.24.7"
+    "@babel/helper-split-export-declaration": "npm:^7.24.7"
+    "@babel/helper-validator-identifier": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0
-  checksum: 10c0/9e2e3d0ddb397b36b9e8c7d94e175a36be8cb888ef370cefef2cdfd53ae1f87d567b268bd90ed9a6c706485a8de3da19cac577657613e9cd17210b91cbdfb00b
+  checksum: 10c0/4f311755fcc3b4cbdb689386309cdb349cf0575a938f0b9ab5d678e1a81bbb265aa34ad93174838245f2ac7ff6d5ddbd0104638a75e4e961958ed514355687b6
   languageName: node
   linkType: hard
 
-"@babel/helper-optimise-call-expression@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-optimise-call-expression@npm:7.24.6"
+"@babel/helper-optimise-call-expression@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-optimise-call-expression@npm:7.24.7"
   dependencies:
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/7fce2c4ce22c4ba3c2178d1ce85f34fc9bbe286af5ec153b4b6ea9bf2212390359c4a1e8a54551c4daa4688022d619668bdb8c8060cb185c0c9ad02c5247efc9
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/ca6a9884705dea5c95a8b3ce132d1e3f2ae951ff74987d400d1d9c215dae9c0f9e29924d8f8e131e116533d182675bc261927be72f6a9a2968eaeeaa51eb1d0f
   languageName: node
   linkType: hard
 
-"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.6, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3":
-  version: 7.24.6
-  resolution: "@babel/helper-plugin-utils@npm:7.24.6"
-  checksum: 10c0/636d3ce8cabc0621c1f78187e1d95f1087209921fa452f76aad06224ef5dffb3d934946f5183109920f32a4b94dd75ac91c63bc52813fee639d10cd54d49ba1f
+"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.7, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3":
+  version: 7.24.7
+  resolution: "@babel/helper-plugin-utils@npm:7.24.7"
+  checksum: 10c0/c3d38cd9b3520757bb4a279255cc3f956fc0ac1c193964bd0816ebd5c86e30710be8e35252227e0c9d9e0f4f56d9b5f916537f2bc588084b0988b4787a967d31
   languageName: node
   linkType: hard
 
-"@babel/helper-remap-async-to-generator@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-remap-async-to-generator@npm:7.24.6"
+"@babel/helper-remap-async-to-generator@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-remap-async-to-generator@npm:7.24.7"
   dependencies:
-    "@babel/helper-annotate-as-pure": "npm:^7.24.6"
-    "@babel/helper-environment-visitor": "npm:^7.24.6"
-    "@babel/helper-wrap-function": "npm:^7.24.6"
+    "@babel/helper-annotate-as-pure": "npm:^7.24.7"
+    "@babel/helper-environment-visitor": "npm:^7.24.7"
+    "@babel/helper-wrap-function": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0
-  checksum: 10c0/b379b844eba352ac9487d31867e7bb2b8a264057f1739d9161b614145ea6e60969a7a82e75e5e83089e50cf1b6559f53aa085a787942bf40706fee15a2faa33c
+  checksum: 10c0/4e7fa2cdcbc488e41c27066c16e562857ef3c5c2bfe70d2f1e32e9ee7546b17c3fc1c20d05bf2a7f1c291bd9e7a0a219f6a9fa387209013294be79a26fcfe64d
   languageName: node
   linkType: hard
 
-"@babel/helper-replace-supers@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-replace-supers@npm:7.24.6"
+"@babel/helper-replace-supers@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-replace-supers@npm:7.24.7"
   dependencies:
-    "@babel/helper-environment-visitor": "npm:^7.24.6"
-    "@babel/helper-member-expression-to-functions": "npm:^7.24.6"
-    "@babel/helper-optimise-call-expression": "npm:^7.24.6"
+    "@babel/helper-environment-visitor": "npm:^7.24.7"
+    "@babel/helper-member-expression-to-functions": "npm:^7.24.7"
+    "@babel/helper-optimise-call-expression": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0
-  checksum: 10c0/aaf2dfaf25360da1525ecea5979d5afed201b96f0feeed2e15f90883a97776132a720b25039e67fee10a5c537363aea5cc2a46c0f1d13fdb86d0e920244f2da7
+  checksum: 10c0/0e133bb03371dee78e519c334a09c08e1493103a239d9628db0132dfaac3fc16380479ca3c590d278a9b71b624030a338c18ebbfe6d430ebb2e4653775c4b3e3
   languageName: node
   linkType: hard
 
-"@babel/helper-simple-access@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-simple-access@npm:7.24.6"
+"@babel/helper-simple-access@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-simple-access@npm:7.24.7"
   dependencies:
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/b17e404dd6c9787fc7d558aea5222471a77e29596705f0d10b4c2a58b9d71ff7eae915094204848cc1af99b771553caa69337a768b9abdd82b54a0050ba83eb9
+    "@babel/traverse": "npm:^7.24.7"
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/7230e419d59a85f93153415100a5faff23c133d7442c19e0cd070da1784d13cd29096ee6c5a5761065c44e8164f9f80e3a518c41a0256df39e38f7ad6744fed7
   languageName: node
   linkType: hard
 
-"@babel/helper-skip-transparent-expression-wrappers@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.24.6"
+"@babel/helper-skip-transparent-expression-wrappers@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.24.7"
   dependencies:
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/6928f698362d6082a67ee2bc73991ef6b0cc6b5f2854177389bc8f3c09296580f0ee20134dd1a29dfcb1906ad9e346fa0f7c6fcd7589ab3ff176d4f09504577f
+    "@babel/traverse": "npm:^7.24.7"
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/e3a9b8ac9c262ac976a1bcb5fe59694db5e6f0b4f9e7bdba5c7693b8b5e28113c23bdaa60fe8d3ec32a337091b67720b2053bcb3d5655f5406536c3d0584242b
   languageName: node
   linkType: hard
 
-"@babel/helper-split-export-declaration@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-split-export-declaration@npm:7.24.6"
+"@babel/helper-split-export-declaration@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-split-export-declaration@npm:7.24.7"
   dependencies:
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/53a5dd8691fdffc89cc7fcf5aed0ad1d8bc39796a5782a3d170dcbf249eb5c15cc8a290e8d09615711d18798ad04a7d0694ab5195d35fa651abbc1b9c885d6a8
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/0254577d7086bf09b01bbde98f731d4fcf4b7c3fa9634fdb87929801307c1f6202a1352e3faa5492450fa8da4420542d44de604daf540704ff349594a78184f6
   languageName: node
   linkType: hard
 
-"@babel/helper-string-parser@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-string-parser@npm:7.24.6"
-  checksum: 10c0/95115bf676e92c4e99166395649108d97447e6cabef1fabaec8cdbc53a43f27b5df2268ff6534439d405bc1bd06685b163eb3b470455bd49f69159dada414145
+"@babel/helper-string-parser@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-string-parser@npm:7.24.7"
+  checksum: 10c0/47840c7004e735f3dc93939c77b099bb41a64bf3dda0cae62f60e6f74a5ff80b63e9b7cf77b5ec25a324516381fc994e1f62f922533236a8e3a6af57decb5e1e
   languageName: node
   linkType: hard
 
-"@babel/helper-validator-identifier@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-validator-identifier@npm:7.24.6"
-  checksum: 10c0/d29d2e3fca66c31867a009014169b93f7bc21c8fc1dd7d0b9d85d7a4000670526ff2222d966febb75a6e12f9859a31d1e75b558984e28ecb69651314dd0a6fd1
+"@babel/helper-validator-identifier@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-validator-identifier@npm:7.24.7"
+  checksum: 10c0/87ad608694c9477814093ed5b5c080c2e06d44cb1924ae8320474a74415241223cc2a725eea2640dd783ff1e3390e5f95eede978bc540e870053152e58f1d651
   languageName: node
   linkType: hard
 
-"@babel/helper-validator-option@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-validator-option@npm:7.24.6"
-  checksum: 10c0/787268dff5cf77f3b704454b96ab7b58aa4f43b2808247e51859a103a1c28a9c252100f830433f4b37a73f4a61ba745bbeef4cdccbab48c1e9adf037f4ca3491
+"@babel/helper-validator-option@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-validator-option@npm:7.24.7"
+  checksum: 10c0/21aea2b7bc5cc8ddfb828741d5c8116a84cbc35b4a3184ec53124f08e09746f1f67a6f9217850188995ca86059a7942e36d8965a6730784901def777b7e8a436
   languageName: node
   linkType: hard
 
-"@babel/helper-wrap-function@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helper-wrap-function@npm:7.24.6"
+"@babel/helper-wrap-function@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helper-wrap-function@npm:7.24.7"
   dependencies:
-    "@babel/helper-function-name": "npm:^7.24.6"
-    "@babel/template": "npm:^7.24.6"
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/d32844275a544a8e7c71c13e9832d34d80656aafce659dc6c23b02e14d1c1179d8045125ded5096da1a99de83299ffb48211183d0403da2c8584ed55dc0ab646
+    "@babel/helper-function-name": "npm:^7.24.7"
+    "@babel/template": "npm:^7.24.7"
+    "@babel/traverse": "npm:^7.24.7"
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/d5689f031bf0eb38c0d7fad6b7e320ddef4bfbdf08d12d7d76ef41b7ca365a32721e74cb5ed5a9a9ec634bc20f9b7a27314fa6fb08f1576b8f6d8330fcea6f47
   languageName: node
   linkType: hard
 
-"@babel/helpers@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/helpers@npm:7.24.6"
+"@babel/helpers@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/helpers@npm:7.24.7"
   dependencies:
-    "@babel/template": "npm:^7.24.6"
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/e5b5c0919fd6fa56ae11c15a72962d8de0ac19db524849554af28cf08ac32f9ae5aee49a43146eb150f54418cefb8e890fa2b2f33d029434dc7777dbcdfd5bac
+    "@babel/template": "npm:^7.24.7"
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/aa8e230f6668773e17e141dbcab63e935c514b4b0bf1fed04d2eaefda17df68e16b61a56573f7f1d4d1e605ce6cc162b5f7e9fdf159fde1fd9b77c920ae47d27
   languageName: node
   linkType: hard
 
-"@babel/highlight@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/highlight@npm:7.24.6"
+"@babel/highlight@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/highlight@npm:7.24.7"
   dependencies:
-    "@babel/helper-validator-identifier": "npm:^7.24.6"
+    "@babel/helper-validator-identifier": "npm:^7.24.7"
     chalk: "npm:^2.4.2"
     js-tokens: "npm:^4.0.0"
     picocolors: "npm:^1.0.0"
-  checksum: 10c0/5bbc31695e5d44e97feb267f7aaf4c52908560d184ffeb2e2e57aae058d40125592931883889413e19def3326895ddb41ff45e090fa90b459d8c294b4ffc238c
+  checksum: 10c0/674334c571d2bb9d1c89bdd87566383f59231e16bcdcf5bb7835babdf03c9ae585ca0887a7b25bdf78f303984af028df52831c7989fecebb5101cc132da9393a
   languageName: node
   linkType: hard
 
-"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/parser@npm:7.24.6"
+"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/parser@npm:7.24.7"
   bin:
     parser: ./bin/babel-parser.js
-  checksum: 10c0/cbef70923078a20fe163b03f4a6482be65ed99d409a57f3091a23ce3a575ee75716c30e7ea9f40b692ac5660f34055f4cbeb66a354fad15a6cf1fca35c3496c5
+  checksum: 10c0/8b244756872185a1c6f14b979b3535e682ff08cb5a2a5fd97cc36c017c7ef431ba76439e95e419d43000c5b07720495b00cf29a7f0d9a483643d08802b58819b
   languageName: node
   linkType: hard
 
-"@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.24.6"
+"@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.24.7"
   dependencies:
-    "@babel/helper-environment-visitor": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-environment-visitor": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0
-  checksum: 10c0/0dbf12de5a7e5d092271124f0d9bff1ceb94871d5563041940512671cd40ab2a93d613715ee37076cd8263cf49579afb805faa3189996c11639bb10d3e9837f1
+  checksum: 10c0/394c30e2b708ad385fa1219528e039066a1f1cb40f47986f283878848fd354c745e6397f588b4e5a046ee8d64bfdf4c208e4c3dfbdcfb2fd34315ec67c64e7af
   languageName: node
   linkType: hard
 
-"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.24.6"
+"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0
-  checksum: 10c0/b0a03d4f587e1fa92312c912864a0af3f68bfc87367b7c93770e94f171767d563d7adfca7ad571d20cd755e89e1373e7414973ce30e694e7b6eb8f57d2b1b889
+  checksum: 10c0/a36307428ecc1a01b00cf90812335eed1575d13f211ab24fe4d0c55c28a2fcbd4135f142efabc3b277b2a8e09ee05df594a1272353f061b63829495b5dcfdb96
   languageName: node
   linkType: hard
 
-"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.24.6"
+"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.6"
-    "@babel/plugin-transform-optional-chaining": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7"
+    "@babel/plugin-transform-optional-chaining": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.13.0
-  checksum: 10c0/fdd40fdf7e87f3dbc5396c9a8f92005798865f6f20d2c24c33246ac43aab8df93742b63dfcfcda67c0a5cf1f7b8a987fdbccaceb9ccbb9a67bef10012b522390
+  checksum: 10c0/aeb6e7aa363a47f815cf956ea1053c5dd8b786a17799f065c9688ba4b0051fe7565d258bbe9400bfcbfb3114cb9fda66983e10afe4d750bc70ff75403e15dd36
   languageName: node
   linkType: hard
 
-"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.24.6"
+"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.24.7"
   dependencies:
-    "@babel/helper-environment-visitor": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-environment-visitor": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0
-  checksum: 10c0/cc1e8ee138c71e78ec262a5198d2cf75c305f2fb4ea9771ebd4ded47f51bc1bacbf917db3cb28c681e7499a07f9803ab0bbe5ad50b9576cbe03902189e3871ed
+  checksum: 10c0/2b52a73e444f6adc73f927b623e53a4cf64397170dd1071268536df1b3db1e02131418c8dc91351af48837a6298212118f4a72d5407f8005cf9a732370a315b0
   languageName: node
   linkType: hard
 
@@ -496,25 +504,25 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@babel/plugin-syntax-import-assertions@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-syntax-import-assertions@npm:7.24.6"
+"@babel/plugin-syntax-import-assertions@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-syntax-import-assertions@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/8e81c7cd3d5812a3dda32f06f84492a1b5640f42c594619ed57bf4017529889f87bfb4e8e95c50ba1527d89501dae71a0c73770502676545c2cd9ce58ce3258d
+  checksum: 10c0/b82c53e095274ee71c248551352d73441cf65b3b3fc0107258ba4e9aef7090772a425442b3ed1c396fa207d0efafde8929c87a17d3c885b3ca2021316e87e246
   languageName: node
   linkType: hard
 
-"@babel/plugin-syntax-import-attributes@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-syntax-import-attributes@npm:7.24.6"
+"@babel/plugin-syntax-import-attributes@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-syntax-import-attributes@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/c4d8554b89c0daa6d3c430582b98c10a3af2de8eab484082e97cb73f2712780ab6dd8d11d783c4b266efef76f4479abf4944ef8f416a4459b05eecaf438f8774
+  checksum: 10c0/eccc54d0f03c96d0eec7a6e2fa124dadbc7298345b62ffc4238f173308c4325b5598f139695ff05a95cf78412ef6903599e4b814496612bf39aad4715a16375b
   languageName: node
   linkType: hard
 
@@ -540,14 +548,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@babel/plugin-syntax-jsx@npm:7, @babel/plugin-syntax-jsx@npm:^7.24.6, @babel/plugin-syntax-jsx@npm:^7.7.2":
-  version: 7.24.6
-  resolution: "@babel/plugin-syntax-jsx@npm:7.24.6"
+"@babel/plugin-syntax-jsx@npm:7, @babel/plugin-syntax-jsx@npm:^7.24.7, @babel/plugin-syntax-jsx@npm:^7.7.2":
+  version: 7.24.7
+  resolution: "@babel/plugin-syntax-jsx@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/f00d783a9e2d52f0a8797823a3cbdbe2d0dc09c7235fe8c88e6dce3a02f234f52fb5e976a001cc30b0e2b330590b5680f54436e56d67f9ab05d1e4bdeb3992cd
+  checksum: 10c0/f44d927a9ae8d5ef016ff5b450e1671e56629ddc12e56b938e41fd46e141170d9dfc9a53d6cb2b9a20a7dd266a938885e6a3981c60c052a2e1daed602ac80e51
   languageName: node
   linkType: hard
 
@@ -639,14 +647,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@babel/plugin-syntax-typescript@npm:^7.24.6, @babel/plugin-syntax-typescript@npm:^7.7.2":
-  version: 7.24.6
-  resolution: "@babel/plugin-syntax-typescript@npm:7.24.6"
+"@babel/plugin-syntax-typescript@npm:^7.24.7, @babel/plugin-syntax-typescript@npm:^7.7.2":
+  version: 7.24.7
+  resolution: "@babel/plugin-syntax-typescript@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/b1eeabf8bebfa78cea559c0a0d55e480fe2ebd799472d1f6bd5afbd2759d02b362d29ad30009c81d5b112797beb987e58a3000d2331adaa4bf03862e1ed18cef
+  checksum: 10c0/cdabd2e8010fb0ad15b49c2c270efc97c4bfe109ead36c7bbcf22da7a74bc3e49702fc4f22f12d2d6049e8e22a5769258df1fd05f0420ae45e11bdd5bc07805a
   languageName: node
   linkType: hard
 
@@ -662,456 +670,456 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-arrow-functions@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-arrow-functions@npm:7.24.6"
+"@babel/plugin-transform-arrow-functions@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-arrow-functions@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/46250eb3f535327825db323740a301b76b882b70979f1fb5f89cbb1a820378ab68ee880b912981dd5276dd116deaaee0f4a2a95f1c9cf537a67749fd4209a2d3
+  checksum: 10c0/6ac05a54e5582f34ac6d5dc26499e227227ec1c7fa6fc8de1f3d40c275f140d3907f79bbbd49304da2d7008a5ecafb219d0b71d78ee3290ca22020d878041245
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-async-generator-functions@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-async-generator-functions@npm:7.24.6"
+"@babel/plugin-transform-async-generator-functions@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-async-generator-functions@npm:7.24.7"
   dependencies:
-    "@babel/helper-environment-visitor": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/helper-remap-async-to-generator": "npm:^7.24.6"
+    "@babel/helper-environment-visitor": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/helper-remap-async-to-generator": "npm:^7.24.7"
     "@babel/plugin-syntax-async-generators": "npm:^7.8.4"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/8876431855220ccfbf1ae510a4a7c4e0377b21189d3f73ea6dde5ffd31eee57f03ea2b2d1da59b6a36b6e107e41b38d0c1d1bb015e0d1c2c2fb627962260edb7
+  checksum: 10c0/6b5e33ae66dce0afce9b06d8dace6fa052528e60f7622aa6cfd3e71bd372ca5079d426e78336ca564bc0d5f37acbcda1b21f4fe656fcb642f1a93a697ab39742
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-async-to-generator@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-async-to-generator@npm:7.24.6"
+"@babel/plugin-transform-async-to-generator@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-async-to-generator@npm:7.24.7"
   dependencies:
-    "@babel/helper-module-imports": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/helper-remap-async-to-generator": "npm:^7.24.6"
+    "@babel/helper-module-imports": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/helper-remap-async-to-generator": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/52c137668e7a35356c3b1caf25ab3bf90ff61199885bfd9f0232bfe168a53a5cf0ca4c1e283c27e44ad76cc366b73e4ff7042241469d1944c7042fb78c57bfd8
+  checksum: 10c0/83c82e243898875af8457972a26ab29baf8a2078768ee9f35141eb3edff0f84b165582a2ff73e90a9e08f5922bf813dbf15a85c1213654385198f4591c0dc45d
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-block-scoped-functions@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.24.6"
+"@babel/plugin-transform-block-scoped-functions@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/0c761b5e3a2959b63edf47d67f6752e01f24777ad1accd82457a2dca059877f8a8297fbc7a062db6b48836309932f2ac645c507070ef6ad4e765b3600822c048
+  checksum: 10c0/113e86de4612ae91773ff5cb6b980f01e1da7e26ae6f6012127415d7ae144e74987bc23feb97f63ba4bc699331490ddea36eac004d76a20d5369e4cc6a7f61cd
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-block-scoping@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-block-scoping@npm:7.24.6"
+"@babel/plugin-transform-block-scoping@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-block-scoping@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/95c25e501c4553515f92d4e86032a8859a8855cea8aafb6df30f956979caa70af1e126e6dfaf9e51328d1306232ff1e081bda7d84a9aaf23f418d9da120c7018
+  checksum: 10c0/dcbc5e385c0ca5fb5736b1c720c90755cffe9f91d8c854f82e61e59217dd3f6c91b3633eeee4b55a89d3f59e5275d0f5b0b1b1363d4fa70c49c468b55aa87700
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-class-properties@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-class-properties@npm:7.24.6"
+"@babel/plugin-transform-class-properties@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-class-properties@npm:7.24.7"
   dependencies:
-    "@babel/helper-create-class-features-plugin": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-create-class-features-plugin": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/ae01e00dd528112d542a77f0f1cf6b43726553d2011bbdec9e4fac441dfa161d44bf14449dc4121b45cc971686a8c652652032594e83c5d6cab8e9fd794eecb2
+  checksum: 10c0/75018a466c7ede3d2397e158891c224ba7fca72864506ce067ddbc02fc65191d44da4d6379c996d0c7f09019e26b5c3f5f1d3a639cd98366519723886f0689d0
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-class-static-block@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-class-static-block@npm:7.24.6"
+"@babel/plugin-transform-class-static-block@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-class-static-block@npm:7.24.7"
   dependencies:
-    "@babel/helper-create-class-features-plugin": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-create-class-features-plugin": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
     "@babel/plugin-syntax-class-static-block": "npm:^7.14.5"
   peerDependencies:
     "@babel/core": ^7.12.0
-  checksum: 10c0/425f237faf62b531d973f23ac3eefe3f29c4f6c988c33c2dd660b6dfb61d4ed1e865a5088574742d87ed02437d26aa6ec6b107468b7df35ca9d3082bad742d8f
+  checksum: 10c0/b0ade39a3d09dce886f79dbd5907c3d99b48167eddb6b9bbde24a0598129654d7017e611c20494cdbea48b07ac14397cd97ea34e3754bbb2abae4e698128eccb
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-classes@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-classes@npm:7.24.6"
+"@babel/plugin-transform-classes@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-classes@npm:7.24.7"
   dependencies:
-    "@babel/helper-annotate-as-pure": "npm:^7.24.6"
-    "@babel/helper-compilation-targets": "npm:^7.24.6"
-    "@babel/helper-environment-visitor": "npm:^7.24.6"
-    "@babel/helper-function-name": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/helper-replace-supers": "npm:^7.24.6"
-    "@babel/helper-split-export-declaration": "npm:^7.24.6"
+    "@babel/helper-annotate-as-pure": "npm:^7.24.7"
+    "@babel/helper-compilation-targets": "npm:^7.24.7"
+    "@babel/helper-environment-visitor": "npm:^7.24.7"
+    "@babel/helper-function-name": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/helper-replace-supers": "npm:^7.24.7"
+    "@babel/helper-split-export-declaration": "npm:^7.24.7"
     globals: "npm:^11.1.0"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/d29c26feea9ad5a64d790aeab1833b7a50d6af2be24140dad7e06510b754b8fe0ffb292d43d96fedaf7765fcb90c0034ac7c42635f814d9235697431076a1cf0
+  checksum: 10c0/e51dba7ce8b770d1eee929e098d5a3be3efc3e8b941e22dda7d0097dc4e7be5feabd2da7b707ac06fcac5661b31223c541941dec08ce76c1faa55544d87d06ec
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-computed-properties@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-computed-properties@npm:7.24.6"
+"@babel/plugin-transform-computed-properties@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-computed-properties@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/template": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/template": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/c464144c2eda8d526d70c8d8e3bf30820f591424991452f816617347ef3ccc5d04133c6e903b90c1d832d95d9c8550e5693ea40ea14856ede54fb8e1cd36c5de
+  checksum: 10c0/25636dbc1f605c0b8bc60aa58628a916b689473d11551c9864a855142e36742fe62d4a70400ba3b74902338e77fb3d940376c0a0ba154b6b7ec5367175233b49
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-destructuring@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-destructuring@npm:7.24.6"
+"@babel/plugin-transform-destructuring@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-destructuring@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/1fcc064e2b0c45a4340418bd70d2cf2b3644d1215eb975ec14f83e4f7615fdc3948e355db5091f81602f6c3d933f9308caa66232091aad4edd6c16b00240fcc7
+  checksum: 10c0/929f07a807fb62230bfbf881cfcedf187ac5daf2f1b01da94a75c7a0f6f72400268cf4bcfee534479e43260af8193e42c31ee03c8b0278ba77d0036ed6709c27
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-dotall-regex@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-dotall-regex@npm:7.24.6"
+"@babel/plugin-transform-dotall-regex@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-dotall-regex@npm:7.24.7"
   dependencies:
-    "@babel/helper-create-regexp-features-plugin": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/4a2c98f1c22a18754c6ada1486563865690008df2536066d8a146fa58eed8515b607e162c7efb0b8fa062d755e77afea145495046cffdb4ea56194d38f489254
+  checksum: 10c0/793f14c9494972d294b7e7b97b747f47874b6d57d7804d3443c701becf5db192c9311be6a1835c07664486df1f5c60d33196c36fb7e11a53015e476b4c145b33
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-duplicate-keys@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-duplicate-keys@npm:7.24.6"
+"@babel/plugin-transform-duplicate-keys@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-duplicate-keys@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/44ddba252f0b9f1f0b1ff8d903bbcf8871246670fb2883f65d09d371d403ce9c3e2e582b94b36506c1d042110b464eb3492e53cd1e87c1d479b145bcc01c04fd
+  checksum: 10c0/75ff7ec1117ac500e77bf20a144411d39c0fdd038f108eec061724123ce6d1bb8d5bd27968e466573ee70014f8be0043361cdb0ef388f8a182d1d97ad67e51b9
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-dynamic-import@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-dynamic-import@npm:7.24.6"
+"@babel/plugin-transform-dynamic-import@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-dynamic-import@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
     "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/b4411f21112127a02aef15103765e207e4c03e7321d7f4de3522fc181cb377c5abc8484cf0169e6c30f2e51e6c602c09894fa6b15643d24f66273833ef34e4a6
+  checksum: 10c0/eeda48372efd0a5103cb22dadb13563c975bce18ae85daafbb47d57bb9665d187da9d4fe8d07ac0a6e1288afcfcb73e4e5618bf75ff63fddf9736bfbf225203b
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-exponentiation-operator@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.24.6"
+"@babel/plugin-transform-exponentiation-operator@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.24.7"
   dependencies:
-    "@babel/helper-builder-binary-assignment-operator-visitor": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-builder-binary-assignment-operator-visitor": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/c4f15518a5d1614dfac0dbadfb99b0f36a98c1c1ff1c39794a105c3c87cfce00689e0943fcb13368b43b00b2eebaa01136ea12fb8600a574720853b5a8a11de7
+  checksum: 10c0/ace3e11c94041b88848552ba8feb39ae4d6cad3696d439ff51445bd2882d8b8775d85a26c2c0edb9b5e38c9e6013cc11b0dea89ec8f93c7d9d7ee95e3645078c
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-export-namespace-from@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-export-namespace-from@npm:7.24.6"
+"@babel/plugin-transform-export-namespace-from@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-export-namespace-from@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
     "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/bff16d1800d7e5b38d3a3c8d404cc14442a37383dff7769dcc599a0723b2507647cafe9ba7d9b52d2e2f02a78bb78d149676d8d8ddf7357b160f4096b89ae9c5
+  checksum: 10c0/4e144d7f1c57bc63b4899dbbbdfed0880f2daa75ea9c7251c7997f106e4b390dc362175ab7830f11358cb21f6b972ca10a43a2e56cd789065f7606b082674c0c
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-for-of@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-for-of@npm:7.24.6"
+"@babel/plugin-transform-for-of@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-for-of@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/c8def2a160783c5c4a1c136c721fc88aca9cd3757a60f1c885a804b5320edb5f143d3f989f698bdd9aae359fdabab0830dba3d35138cea42988a77d2c72c8443
+  checksum: 10c0/77629b1173e55d07416f05ba7353caa09d2c2149da2ca26721ab812209b63689d1be45116b68eadc011c49ced59daf5320835b15245eb7ae93ae0c5e8277cfc0
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-function-name@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-function-name@npm:7.24.6"
+"@babel/plugin-transform-function-name@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-function-name@npm:7.24.7"
   dependencies:
-    "@babel/helper-compilation-targets": "npm:^7.24.6"
-    "@babel/helper-function-name": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-compilation-targets": "npm:^7.24.7"
+    "@babel/helper-function-name": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/efa6527438ad94df0b7a4c92c33110ec40b086a0aceda567176b150ed291f8eb44b2ce697d8e3e1d4841496c10693add1e88f296418e72a171ead5c76b890a47
+  checksum: 10c0/3e9642428d6952851850d89ea9307d55946528d18973784d0e2f04a651b23bd9924dd8a2641c824b483bd4ab1223bab1d2f6a1106a939998f7ced512cb60ac5b
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-json-strings@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-json-strings@npm:7.24.6"
+"@babel/plugin-transform-json-strings@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-json-strings@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
     "@babel/plugin-syntax-json-strings": "npm:^7.8.3"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/46af52dcc16f494c6c11dc22c944f2533623b9d9dfce5097bc0bdb99535ad4c4cfe5bca0d8ce8c39a94202e69d99ee60f546ce0be0ad782b681c7b5b4c9ddd6f
+  checksum: 10c0/17c72cd5bf3e90e722aabd333559275f3309e3fa0b9cea8c2944ab83ae01502c71a2be05da5101edc02b3fc8df15a8dbb9b861cbfcc8a52bf5e797cf01d3a40a
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-literals@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-literals@npm:7.24.6"
+"@babel/plugin-transform-literals@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-literals@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/961b64df79a673706d74cf473d1f4646f250b4f8813f9d7ef5d897e30acdacd1ca104584de2e88546289fce055d71bd7559cdb8ad4a2d5e7eea17f3c829faa97
+  checksum: 10c0/9f3f6f3831929cd2a977748c07addf9944d5cccb50bd3a24a58beb54f91f00d6cacd3d7831d13ffe1ad6f8aba0aefd7bca5aec65d63b77f39c62ad1f2d484a3e
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-logical-assignment-operators@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.24.6"
+"@babel/plugin-transform-logical-assignment-operators@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
     "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/0ae7f4098c63f442fd038de6034155bcf20214e7e490e92189decb2980932247b97cb069b11ac8bc471b53f71d6859e607969440d63ff400b8932ee3e05b4958
+  checksum: 10c0/dbe882eb9053931f2ab332c50fc7c2a10ef507d6421bd9831adbb4cb7c9f8e1e5fbac4fbd2e007f6a1bf1df1843547559434012f118084dc0bf42cda3b106272
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-member-expression-literals@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-member-expression-literals@npm:7.24.6"
+"@babel/plugin-transform-member-expression-literals@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-member-expression-literals@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/ec8908a409bd39d20f0428e35425c9e4c540bad252a0e33e08b84e3bea5088c785531197bdcf049afbdba841325962a93030b7be6da3586cb13d0ca0ebab89c9
+  checksum: 10c0/e789ae359bdf2d20e90bedef18dfdbd965c9ebae1cee398474a0c349590fda7c8b874e1a2ceee62e47e5e6ec1730e76b0f24e502164357571854271fc12cc684
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-modules-amd@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-modules-amd@npm:7.24.6"
+"@babel/plugin-transform-modules-amd@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-modules-amd@npm:7.24.7"
   dependencies:
-    "@babel/helper-module-transforms": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-module-transforms": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/074d26c79f517b27a07fef00319aff9705df1e6b41a805db855fe719e0f246b9815d6525cf1c5f0890c7f830dd0b9776e9b2493bbc929a3c23c0dee15f10a514
+  checksum: 10c0/6df7de7fce34117ca4b2fa07949b12274c03668cbfe21481c4037b6300796d50ae40f4f170527b61b70a67f26db906747797e30dbd0d9809a441b6e220b5728f
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-modules-commonjs@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-modules-commonjs@npm:7.24.6"
+"@babel/plugin-transform-modules-commonjs@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-modules-commonjs@npm:7.24.7"
   dependencies:
-    "@babel/helper-module-transforms": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/helper-simple-access": "npm:^7.24.6"
+    "@babel/helper-module-transforms": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/helper-simple-access": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/4fc790136d066105fa773ffc7e249d88c6f0d0126984ede36fedd51ac2b622b46c08565bcdd1ab62ac10195eeedeaba0d26e7e4c676ed50906cbed16540a4e22
+  checksum: 10c0/9442292b3daf6a5076cdc3c4c32bf423bda824ccaeb0dd0dc8b3effaa1fecfcb0130ae6e647fef12a5d5ff25bcc99a0d6bfc6d24a7525345e1bcf46fcdf81752
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-modules-systemjs@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-modules-systemjs@npm:7.24.6"
+"@babel/plugin-transform-modules-systemjs@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-modules-systemjs@npm:7.24.7"
   dependencies:
-    "@babel/helper-hoist-variables": "npm:^7.24.6"
-    "@babel/helper-module-transforms": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/helper-validator-identifier": "npm:^7.24.6"
+    "@babel/helper-hoist-variables": "npm:^7.24.7"
+    "@babel/helper-module-transforms": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/helper-validator-identifier": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/500962e3ac1bb1a9890e94f1967ec9e3aa3d41e22d4a9d1c739918707e4a8936710fd8d0ed4f3a8aad87260f7566b54566bead77977eb21e90124835cb6bcdca
+  checksum: 10c0/e2a795e0a6baafe26f4a74010622212ddd873170742d673f450e0097f8d984f6e6a95eb8ce41b05071ee9790c4be088b33801aaab3f78ee202c567634e52a331
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-modules-umd@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-modules-umd@npm:7.24.6"
+"@babel/plugin-transform-modules-umd@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-modules-umd@npm:7.24.7"
   dependencies:
-    "@babel/helper-module-transforms": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-module-transforms": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/73c6cecb4f45ca3f665e2c57b6d04d65358518522dfaffb9b6913c026aeb704281d015324d02bf07f2cb026de6bac9308c62e82979364bd39f3687f752652b0d
+  checksum: 10c0/7791d290121db210e4338b94b4a069a1a79e4c7a8d7638d8159a97b281851bbed3048dac87a4ae718ad963005e6c14a5d28e6db2eeb2b04e031cee92fb312f85
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.24.6"
+"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.24.7"
   dependencies:
-    "@babel/helper-create-regexp-features-plugin": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0
-  checksum: 10c0/92547309d81938488753f87b05a679a7557a1cec253756966044367c268b27311e51efad91724aa3e433cf61626e10bf1008e112998350c2013a87824c4cfe0b
+  checksum: 10c0/41a0b0f2d0886318237440aa3b489f6d0305361d8671121777d9ff89f9f6de9d0c02ce93625049061426c8994064ef64deae8b819d1b14c00374a6a2336fb5d9
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-new-target@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-new-target@npm:7.24.6"
+"@babel/plugin-transform-new-target@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-new-target@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/5e9b9edfbe46489f64013d2bbd422f29acdb8057ccc85e7c759f7cf1415fde6a82ac13a13f0f246defaba6e2f7f4d424178ba78fc02237bdbf7df6692fc1dca8
+  checksum: 10c0/2540808a35e1a978e537334c43dab439cf24c93e7beb213a2e71902f6710e60e0184316643790c0a6644e7a8021e52f7ab8165e6b3e2d6651be07bdf517b67df
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.3, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.24.6"
+"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.3, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
     "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/53ab5b16bbcf47e842a48f1f0774d238dae0222c3e1f31653307808048e249ed140cba12dfc280cbc9a577cb3bb5b2f50ca0e3e4ffe5260fcf8c3ca0b83fb21e
+  checksum: 10c0/7243c8ff734ed5ef759dd8768773c4b443c12e792727e759a1aec2c7fa2bfdd24f1ecb42e292a7b3d8bd3d7f7b861cf256a8eb4ba144fc9cc463892c303083d9
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-numeric-separator@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-numeric-separator@npm:7.24.6"
+"@babel/plugin-transform-numeric-separator@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-numeric-separator@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
     "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/14863e735fc407e065e1574914864a956b8250a84cfb4704592656763c9455d67034c7745e53066725195d9ed042121f424c4aaee00027791640e2639386b701
+  checksum: 10c0/e18e09ca5a6342645d00ede477731aa6e8714ff357efc9d7cda5934f1703b3b6fb7d3298dce3ce3ba53e9ff1158eab8f1aadc68874cc21a6099d33a1ca457789
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-object-rest-spread@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-object-rest-spread@npm:7.24.6"
+"@babel/plugin-transform-object-rest-spread@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-object-rest-spread@npm:7.24.7"
   dependencies:
-    "@babel/helper-compilation-targets": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-compilation-targets": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
     "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3"
-    "@babel/plugin-transform-parameters": "npm:^7.24.6"
+    "@babel/plugin-transform-parameters": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/1a192b9756ebfa0bc69ad5e285d7d0284963b4b95738ca7721354297329d5c1ab4eb05ff5b198cbfffa3ec00e97a15a712aa7a5011d9407478796966aab54527
+  checksum: 10c0/9ad64bc003f583030f9da50614b485852f8edac93f8faf5d1cd855201a4852f37c5255ae4daf70dd4375bdd4874e16e39b91f680d4668ec219ba05441ce286eb
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-object-super@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-object-super@npm:7.24.6"
+"@babel/plugin-transform-object-super@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-object-super@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/helper-replace-supers": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/helper-replace-supers": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/2e48b9e0a1f3b04b439ede2d0c83bcc5324a81c8bab73c70f0c466cf48061a4ff469f283e2feb17b4cc2e20372c1362253604477ecd77e622192d5d7906aa062
+  checksum: 10c0/770cebb4b4e1872c216b17069db9a13b87dfee747d359dc56d9fcdd66e7544f92dc6ab1861a4e7e0528196aaff2444e4f17dc84efd8eaf162d542b4ba0943869
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-optional-catch-binding@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.24.6"
+"@babel/plugin-transform-optional-catch-binding@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
     "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/411db3177b1bffd2f9e5b33a6b62e70158380e67d91ff4725755312e8a0a2f2c3fd340c60005295a672115fb593222ab2d7076266aebced6ef087a5505b6f371
+  checksum: 10c0/1e2f10a018f7d03b3bde6c0b70d063df8d5dd5209861d4467726cf834f5e3d354e2276079dc226aa8e6ece35f5c9b264d64b8229a8bb232829c01e561bcfb07a
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-optional-chaining@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-optional-chaining@npm:7.24.6"
+"@babel/plugin-transform-optional-chaining@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-optional-chaining@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7"
     "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/8ee5a500a2309444d4fb27979857598e9c91d804fe23217c51cc208b1bc6b9cd0650b355b1ebd625f180c5f1dc4cb89b5f313c982f7c89d90281a69b24a88ccb
+  checksum: 10c0/b9e3649b299e103b0d1767bbdba56574d065ff776e5350403b7bfd4e3982743c0cdb373d33bdbf94fa3c322d155e45d0aad946acf0aa741b870aed22dfec8b8e
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-parameters@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-parameters@npm:7.24.6"
+"@babel/plugin-transform-parameters@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-parameters@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/d9648924b9c0d35a243c0742c22838932a024205c61f4cc419857e5195edd893a33e6be4f2c8fbd89e925051c7cbe8968029ec2d3e7f2f098bfa682f4e2b9731
+  checksum: 10c0/53bf190d6926771545d5184f1f5f3f5144d0f04f170799ad46a43f683a01fab8d5fe4d2196cf246774530990c31fe1f2b9f0def39f0a5ddbb2340b924f5edf01
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-private-methods@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-private-methods@npm:7.24.6"
+"@babel/plugin-transform-private-methods@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-private-methods@npm:7.24.7"
   dependencies:
-    "@babel/helper-create-class-features-plugin": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-create-class-features-plugin": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/55f93959b2e8aeda818db7cdc7dfdcd5076f5bdc8a819566818004a68969fb7297d617f9d108bf76ac232d6056d9f9d20f73ce10380baa43ff1755c5591aa803
+  checksum: 10c0/5b7bf923b738fbe3ad6c33b260e0a7451be288edfe4ef516303fa787a1870cd87533bfbf61abb779c22ed003c2fc484dec2436fe75a48756f686c0241173d364
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-private-property-in-object@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-private-property-in-object@npm:7.24.6"
+"@babel/plugin-transform-private-property-in-object@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-private-property-in-object@npm:7.24.7"
   dependencies:
-    "@babel/helper-annotate-as-pure": "npm:^7.24.6"
-    "@babel/helper-create-class-features-plugin": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-annotate-as-pure": "npm:^7.24.7"
+    "@babel/helper-create-class-features-plugin": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
     "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/c9eb9597362b598a91536375a49ba80cdf13461e849680e040898b103f7998c4d33a7832da5afba9fa51e3473f79cf8605f9ace07a887e386b7801797021631b
+  checksum: 10c0/c6fa7defb90b1b0ed46f24ff94ff2e77f44c1f478d1090e81712f33cf992dda5ba347016f030082a2f770138bac6f4a9c2c1565e9f767a125901c77dd9c239ba
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-property-literals@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-property-literals@npm:7.24.6"
+"@babel/plugin-transform-property-literals@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-property-literals@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/d1195d93406b6c400cdbc9ac57a2b8b58c72cc6480cc03656abfc243be0e2a48133cbb96559c2db95b1c78803daeb538277821540fe19e2a9105905e727ef618
+  checksum: 10c0/52564b58f3d111dc02d241d5892a4b01512e98dfdf6ef11b0ed62f8b11b0acacccef0fc229b44114fe8d1a57a8b70780b11bdd18b807d3754a781a07d8f57433
   languageName: node
   linkType: hard
 
@@ -1126,243 +1134,243 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-react-display-name@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-react-display-name@npm:7.24.6"
+"@babel/plugin-transform-react-display-name@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-react-display-name@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/e929d054035fa3b7432bd2b3e5cf280ffd8cf60d1ce80c863c5e0b03ad01bf6ae2546575d2da31cca2ab83d9399ac01a351f20e21af5075d9c0d4c893e4a36bd
+  checksum: 10c0/c14a07a9e75723c96f1a0a306b8a8e899ff1c6a0cc3d62bcda79bb1b54e4319127b258651c513a1a47da152cdc22e16525525a30ae5933a2980c7036fd0b4d24
   languageName: node
   linkType: hard
 
 "@babel/plugin-transform-react-inline-elements@npm:^7.21.0":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-react-inline-elements@npm:7.24.6"
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-react-inline-elements@npm:7.24.7"
   dependencies:
-    "@babel/helper-builder-react-jsx": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-builder-react-jsx": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/b29f32a0c345db24f32569cf7a5626e37dd31c21bb764148757e91f609d41e2d09031ff1ad86e5672d578cf16f513b197ef3ebc8f0650d8314890a34ca68f02c
+  checksum: 10c0/affe44efc641e5dc4de077db74c80e3dee896a5dfea04491cbd8167ac40dcbb6eaa1ad0ba599e4a85071acdaa08732e7f5d9cf3236a2aa635406c232c8f08236
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-react-jsx-development@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-react-jsx-development@npm:7.24.6"
+"@babel/plugin-transform-react-jsx-development@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-react-jsx-development@npm:7.24.7"
   dependencies:
-    "@babel/plugin-transform-react-jsx": "npm:^7.24.6"
+    "@babel/plugin-transform-react-jsx": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/f899ffa65c7f459a682246a346af0e4132929ffe928cb0d02ae08aac1cf3fb01b2f6e944ef1eaca78f14e94eff935e2bf96aad878030c25ff6de2070a8b72448
+  checksum: 10c0/fce647db50f90a5291681f0f97865d9dc76981262dff71d6d0332e724b85343de5860c26f9e9a79e448d61e1d70916b07ce91e8c7f2b80dceb4b16aee41794d8
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-react-jsx@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-react-jsx@npm:7.24.6"
+"@babel/plugin-transform-react-jsx@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-react-jsx@npm:7.24.7"
   dependencies:
-    "@babel/helper-annotate-as-pure": "npm:^7.24.6"
-    "@babel/helper-module-imports": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/plugin-syntax-jsx": "npm:^7.24.6"
-    "@babel/types": "npm:^7.24.6"
+    "@babel/helper-annotate-as-pure": "npm:^7.24.7"
+    "@babel/helper-module-imports": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/plugin-syntax-jsx": "npm:^7.24.7"
+    "@babel/types": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/6144f56a76529a82077475583a17be8f0b0b461c83673e650f3894e09dbe2bcdfdbfff089eca2e5e239e119f72cd9562749a9af7eb3f2e3266a730da31cd19f2
+  checksum: 10c0/5c46d2c1c06a30e6bde084839df9cc689bf9c9cb0292105d61c225ca731f64247990724caee7dfc7f817dc964c062e8319e7f05394209590c476b65d75373435
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-react-pure-annotations@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.24.6"
+"@babel/plugin-transform-react-pure-annotations@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.24.7"
   dependencies:
-    "@babel/helper-annotate-as-pure": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-annotate-as-pure": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/7f83c5a3a275dbb9a291dee4642a3a0f2249265346d8d3cc9324fc9ee063c3e35c3853b52752ece603f0ac92b405deb38c4b5307a99a74d3e1c9c32a2cefa465
+  checksum: 10c0/fae517d293d9c93b7b920458c3e4b91cb0400513889af41ba184a5f3acc8bfef27242cc262741bb8f87870df376f1733a0d0f52b966d342e2aaaf5607af8f73d
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-regenerator@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-regenerator@npm:7.24.6"
+"@babel/plugin-transform-regenerator@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-regenerator@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
     regenerator-transform: "npm:^0.15.2"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/d17eaa97514d583866182420024b8c22da2c6ca822bdbf16fe7564121564c1844935592dc3315c73d1f78f7c908a4338b1d783618811e694c9bb6d5f9233e58d
+  checksum: 10c0/d2dc2c788fdae9d97217e70d46ba8ca9db0035c398dc3e161552b0c437113719a75c04f201f9c91ddc8d28a1da60d0b0853f616dead98a396abb9c845c44892b
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-reserved-words@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-reserved-words@npm:7.24.6"
+"@babel/plugin-transform-reserved-words@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-reserved-words@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/5d2d4c579bd90c60fc6468a1285b3384e7b650b47d41a937a1590d4aecfc28bd945e82704c6e71cc91aa016b7e78c5594290c1c386edf11ec98e09e36235c5ae
+  checksum: 10c0/2229de2768615e7f5dc0bbc55bc121b5678fd6d2febd46c74a58e42bb894d74cd5955c805880f4e02d0e1cf94f6886270eda7fafc1be9305a1ec3b9fd1d063f5
   languageName: node
   linkType: hard
 
 "@babel/plugin-transform-runtime@npm:^7.22.4":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-runtime@npm:7.24.6"
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-runtime@npm:7.24.7"
   dependencies:
-    "@babel/helper-module-imports": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-module-imports": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
     babel-plugin-polyfill-corejs2: "npm:^0.4.10"
     babel-plugin-polyfill-corejs3: "npm:^0.10.1"
     babel-plugin-polyfill-regenerator: "npm:^0.6.1"
     semver: "npm:^6.3.1"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/89c43c1236506ecbfc547b12936283ca41e611430c2d2e6d12bf1cbdb0d80760cdae481951f486946733e1c9ae064cb05f4bc779c65b3288d40963b0c4a20c5c
+  checksum: 10c0/a33f5095872bbba00b8ee553dfe6941477e69a017a2e65e9dd86e80dab5c627635093b796eb1eb22aaaf2f874704f63ad1d99b952b83b59ef6b368ae04e5bb41
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-shorthand-properties@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-shorthand-properties@npm:7.24.6"
+"@babel/plugin-transform-shorthand-properties@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-shorthand-properties@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/4141b5da1d0d20d66ca0affaef8dfc45ed5e954bfa9003eb8aa779842599de443b37c2b265da27693f304c35ab68a682b44098e9eea0d39f8f94072ab616657f
+  checksum: 10c0/41b155bdbb3be66618358488bf7731b3b2e8fff2de3dbfd541847720a9debfcec14db06a117abedd03c9cd786db20a79e2a86509a4f19513f6e1b610520905cf
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-spread@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-spread@npm:7.24.6"
+"@babel/plugin-transform-spread@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-spread@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/6d12da05311690c4a73d775688ba6931b441e96e512377a166a60184292edeac0b17f5154a49e2f1d262a3f80b96e064bc9c88c63b2a6125f0a2132eff9ed585
+  checksum: 10c0/facba1553035f76b0d2930d4ada89a8cd0f45b79579afd35baefbfaf12e3b86096995f4b0c402cf9ee23b3f2ea0a4460c3b1ec0c192d340962c948bb223d4e66
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-sticky-regex@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-sticky-regex@npm:7.24.6"
+"@babel/plugin-transform-sticky-regex@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-sticky-regex@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/2a65f57554f51d3b9cd035513a610f47e46b26dba112b3b9fb42d1c1f2ae153fce8f76294b4721d099817814f57895c656f5b7dccd5df683277da6522c817ee9
+  checksum: 10c0/5a74ed2ed0a3ab51c3d15fcaf09d9e2fe915823535c7a4d7b019813177d559b69677090e189ec3d5d08b619483eb5ad371fbcfbbff5ace2a76ba33ee566a1109
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-template-literals@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-template-literals@npm:7.24.6"
+"@babel/plugin-transform-template-literals@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-template-literals@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/fcde48e9c3ecd7f5f37ceb6908f1edd537d3115fc2f27d187d58fd83b2a13637a1bb3d24589d841529ed081405b951bf1c5d194ea81eff6ad2d88204d153010d
+  checksum: 10c0/3630f966257bcace122f04d3157416a09d40768c44c3a800855da81146b009187daa21859d1c3b7d13f4e19e8888e60613964b175b2275d451200fb6d8d6cfe6
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-typeof-symbol@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-typeof-symbol@npm:7.24.6"
+"@babel/plugin-transform-typeof-symbol@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-typeof-symbol@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/a24b3a3c7b87c6496ee13d2438effd4645868f054397357ec3cbe92a2f0df4152ac7fd7228cb956576c1b772c0675b065d6ad5f5053c382e97dd022015e9a028
+  checksum: 10c0/5649e7260a138681e68b296ab5931e2b1f132f287d6b4131d49b24f9dc20d62902b7e9d63c4d2decd5683b41df35ef4b9b03f58c7f9f65e4c25a6d8bbf04e9e9
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-typescript@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-typescript@npm:7.24.6"
+"@babel/plugin-transform-typescript@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-typescript@npm:7.24.7"
   dependencies:
-    "@babel/helper-annotate-as-pure": "npm:^7.24.6"
-    "@babel/helper-create-class-features-plugin": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/plugin-syntax-typescript": "npm:^7.24.6"
+    "@babel/helper-annotate-as-pure": "npm:^7.24.7"
+    "@babel/helper-create-class-features-plugin": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/plugin-syntax-typescript": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/46b054e4d4253187403e392ef30f4dd624d8486a1992703f5ff1b415d4e8d00f474e35fb77bc7a3a16a17330873cadcd5af4a8493c61b16da2dde212b2788ccd
+  checksum: 10c0/e8dacdc153a4c4599014b66eb01b94e3dc933d58d4f0cc3039c1a8f432e77b9df14f34a61964e014b975bf466f3fefd8c4768b3e887d3da1be9dc942799bdfdf
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-unicode-escapes@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-unicode-escapes@npm:7.24.6"
+"@babel/plugin-transform-unicode-escapes@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-unicode-escapes@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/0e4038c589b7a63a2469466a25b78aad4ecb7267732e3c953c3055f9a77c7bee859a71983a08b025179f1b094964f2ebbfca1b6c33de4ead90a0b5ef06ddb47e
+  checksum: 10c0/8b18e2e66af33471a6971289492beff5c240e56727331db1d34c4338a6a368a82a7ed6d57ec911001b6d65643aed76531e1e7cac93265fb3fb2717f54d845e69
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-unicode-property-regex@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.24.6"
+"@babel/plugin-transform-unicode-property-regex@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.24.7"
   dependencies:
-    "@babel/helper-create-regexp-features-plugin": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/bca99e00de91d0460dfcb25f285f3606248acc905193c05587e2862c54ddb790c5d8cb45e80927290390cffbcba7620f8af3e74c5301ff0c1c59ce7d47c5629f
+  checksum: 10c0/bc57656eb94584d1b74a385d378818ac2b3fca642e3f649fead8da5fb3f9de22f8461185936915dfb33d5a9104e62e7a47828331248b09d28bb2d59e9276de3e
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-unicode-regex@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-unicode-regex@npm:7.24.6"
+"@babel/plugin-transform-unicode-regex@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-unicode-regex@npm:7.24.7"
   dependencies:
-    "@babel/helper-create-regexp-features-plugin": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/ab6e253cfc38c7e8a2844d7ad46f85fdcbe33610b7f92f71045cf0b040438a08f1f1717ab4b84c480537f54e5478db8b404a4ccc2ff846b4e3ed33d373e3b47a
+  checksum: 10c0/83f72a345b751566b601dc4d07e9f2c8f1bc0e0c6f7abb56ceb3095b3c9d304de73f85f2f477a09f8cc7edd5e65afd0ff9e376cdbcbea33bc0c28f3705b38fd9
   languageName: node
   linkType: hard
 
-"@babel/plugin-transform-unicode-sets-regex@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.24.6"
+"@babel/plugin-transform-unicode-sets-regex@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.24.7"
   dependencies:
-    "@babel/helper-create-regexp-features-plugin": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
+    "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0
-  checksum: 10c0/a52e84f85519fed330e88f7a17611064d2b5f1d0fe2823f8113ed312828e69787888bd023f404e8d35d0bb96461e42e19cdc4f0a44d35959bc86c219a3062237
+  checksum: 10c0/7457c0ee8e80a80cb6fdc1fe54ab115b52815627616ce9151be8ef292fc99d04a910ec24f11382b4f124b89374264396892b086886bd2a9c2317904d87c9b21b
   languageName: node
   linkType: hard
 
 "@babel/preset-env@npm:^7.11.0, @babel/preset-env@npm:^7.12.1, @babel/preset-env@npm:^7.22.4":
-  version: 7.24.6
-  resolution: "@babel/preset-env@npm:7.24.6"
+  version: 7.24.7
+  resolution: "@babel/preset-env@npm:7.24.7"
   dependencies:
-    "@babel/compat-data": "npm:^7.24.6"
-    "@babel/helper-compilation-targets": "npm:^7.24.6"
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/helper-validator-option": "npm:^7.24.6"
-    "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "npm:^7.24.6"
-    "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.24.6"
-    "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.24.6"
-    "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "npm:^7.24.6"
+    "@babel/compat-data": "npm:^7.24.7"
+    "@babel/helper-compilation-targets": "npm:^7.24.7"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/helper-validator-option": "npm:^7.24.7"
+    "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "npm:^7.24.7"
+    "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.24.7"
+    "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.24.7"
+    "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "npm:^7.24.7"
     "@babel/plugin-proposal-private-property-in-object": "npm:7.21.0-placeholder-for-preset-env.2"
     "@babel/plugin-syntax-async-generators": "npm:^7.8.4"
     "@babel/plugin-syntax-class-properties": "npm:^7.12.13"
     "@babel/plugin-syntax-class-static-block": "npm:^7.14.5"
     "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3"
     "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3"
-    "@babel/plugin-syntax-import-assertions": "npm:^7.24.6"
-    "@babel/plugin-syntax-import-attributes": "npm:^7.24.6"
+    "@babel/plugin-syntax-import-assertions": "npm:^7.24.7"
+    "@babel/plugin-syntax-import-attributes": "npm:^7.24.7"
     "@babel/plugin-syntax-import-meta": "npm:^7.10.4"
     "@babel/plugin-syntax-json-strings": "npm:^7.8.3"
     "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4"
@@ -1374,54 +1382,54 @@ __metadata:
     "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5"
     "@babel/plugin-syntax-top-level-await": "npm:^7.14.5"
     "@babel/plugin-syntax-unicode-sets-regex": "npm:^7.18.6"
-    "@babel/plugin-transform-arrow-functions": "npm:^7.24.6"
-    "@babel/plugin-transform-async-generator-functions": "npm:^7.24.6"
-    "@babel/plugin-transform-async-to-generator": "npm:^7.24.6"
-    "@babel/plugin-transform-block-scoped-functions": "npm:^7.24.6"
-    "@babel/plugin-transform-block-scoping": "npm:^7.24.6"
-    "@babel/plugin-transform-class-properties": "npm:^7.24.6"
-    "@babel/plugin-transform-class-static-block": "npm:^7.24.6"
-    "@babel/plugin-transform-classes": "npm:^7.24.6"
-    "@babel/plugin-transform-computed-properties": "npm:^7.24.6"
-    "@babel/plugin-transform-destructuring": "npm:^7.24.6"
-    "@babel/plugin-transform-dotall-regex": "npm:^7.24.6"
-    "@babel/plugin-transform-duplicate-keys": "npm:^7.24.6"
-    "@babel/plugin-transform-dynamic-import": "npm:^7.24.6"
-    "@babel/plugin-transform-exponentiation-operator": "npm:^7.24.6"
-    "@babel/plugin-transform-export-namespace-from": "npm:^7.24.6"
-    "@babel/plugin-transform-for-of": "npm:^7.24.6"
-    "@babel/plugin-transform-function-name": "npm:^7.24.6"
-    "@babel/plugin-transform-json-strings": "npm:^7.24.6"
-    "@babel/plugin-transform-literals": "npm:^7.24.6"
-    "@babel/plugin-transform-logical-assignment-operators": "npm:^7.24.6"
-    "@babel/plugin-transform-member-expression-literals": "npm:^7.24.6"
-    "@babel/plugin-transform-modules-amd": "npm:^7.24.6"
-    "@babel/plugin-transform-modules-commonjs": "npm:^7.24.6"
-    "@babel/plugin-transform-modules-systemjs": "npm:^7.24.6"
-    "@babel/plugin-transform-modules-umd": "npm:^7.24.6"
-    "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.24.6"
-    "@babel/plugin-transform-new-target": "npm:^7.24.6"
-    "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.24.6"
-    "@babel/plugin-transform-numeric-separator": "npm:^7.24.6"
-    "@babel/plugin-transform-object-rest-spread": "npm:^7.24.6"
-    "@babel/plugin-transform-object-super": "npm:^7.24.6"
-    "@babel/plugin-transform-optional-catch-binding": "npm:^7.24.6"
-    "@babel/plugin-transform-optional-chaining": "npm:^7.24.6"
-    "@babel/plugin-transform-parameters": "npm:^7.24.6"
-    "@babel/plugin-transform-private-methods": "npm:^7.24.6"
-    "@babel/plugin-transform-private-property-in-object": "npm:^7.24.6"
-    "@babel/plugin-transform-property-literals": "npm:^7.24.6"
-    "@babel/plugin-transform-regenerator": "npm:^7.24.6"
-    "@babel/plugin-transform-reserved-words": "npm:^7.24.6"
-    "@babel/plugin-transform-shorthand-properties": "npm:^7.24.6"
-    "@babel/plugin-transform-spread": "npm:^7.24.6"
-    "@babel/plugin-transform-sticky-regex": "npm:^7.24.6"
-    "@babel/plugin-transform-template-literals": "npm:^7.24.6"
-    "@babel/plugin-transform-typeof-symbol": "npm:^7.24.6"
-    "@babel/plugin-transform-unicode-escapes": "npm:^7.24.6"
-    "@babel/plugin-transform-unicode-property-regex": "npm:^7.24.6"
-    "@babel/plugin-transform-unicode-regex": "npm:^7.24.6"
-    "@babel/plugin-transform-unicode-sets-regex": "npm:^7.24.6"
+    "@babel/plugin-transform-arrow-functions": "npm:^7.24.7"
+    "@babel/plugin-transform-async-generator-functions": "npm:^7.24.7"
+    "@babel/plugin-transform-async-to-generator": "npm:^7.24.7"
+    "@babel/plugin-transform-block-scoped-functions": "npm:^7.24.7"
+    "@babel/plugin-transform-block-scoping": "npm:^7.24.7"
+    "@babel/plugin-transform-class-properties": "npm:^7.24.7"
+    "@babel/plugin-transform-class-static-block": "npm:^7.24.7"
+    "@babel/plugin-transform-classes": "npm:^7.24.7"
+    "@babel/plugin-transform-computed-properties": "npm:^7.24.7"
+    "@babel/plugin-transform-destructuring": "npm:^7.24.7"
+    "@babel/plugin-transform-dotall-regex": "npm:^7.24.7"
+    "@babel/plugin-transform-duplicate-keys": "npm:^7.24.7"
+    "@babel/plugin-transform-dynamic-import": "npm:^7.24.7"
+    "@babel/plugin-transform-exponentiation-operator": "npm:^7.24.7"
+    "@babel/plugin-transform-export-namespace-from": "npm:^7.24.7"
+    "@babel/plugin-transform-for-of": "npm:^7.24.7"
+    "@babel/plugin-transform-function-name": "npm:^7.24.7"
+    "@babel/plugin-transform-json-strings": "npm:^7.24.7"
+    "@babel/plugin-transform-literals": "npm:^7.24.7"
+    "@babel/plugin-transform-logical-assignment-operators": "npm:^7.24.7"
+    "@babel/plugin-transform-member-expression-literals": "npm:^7.24.7"
+    "@babel/plugin-transform-modules-amd": "npm:^7.24.7"
+    "@babel/plugin-transform-modules-commonjs": "npm:^7.24.7"
+    "@babel/plugin-transform-modules-systemjs": "npm:^7.24.7"
+    "@babel/plugin-transform-modules-umd": "npm:^7.24.7"
+    "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.24.7"
+    "@babel/plugin-transform-new-target": "npm:^7.24.7"
+    "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.24.7"
+    "@babel/plugin-transform-numeric-separator": "npm:^7.24.7"
+    "@babel/plugin-transform-object-rest-spread": "npm:^7.24.7"
+    "@babel/plugin-transform-object-super": "npm:^7.24.7"
+    "@babel/plugin-transform-optional-catch-binding": "npm:^7.24.7"
+    "@babel/plugin-transform-optional-chaining": "npm:^7.24.7"
+    "@babel/plugin-transform-parameters": "npm:^7.24.7"
+    "@babel/plugin-transform-private-methods": "npm:^7.24.7"
+    "@babel/plugin-transform-private-property-in-object": "npm:^7.24.7"
+    "@babel/plugin-transform-property-literals": "npm:^7.24.7"
+    "@babel/plugin-transform-regenerator": "npm:^7.24.7"
+    "@babel/plugin-transform-reserved-words": "npm:^7.24.7"
+    "@babel/plugin-transform-shorthand-properties": "npm:^7.24.7"
+    "@babel/plugin-transform-spread": "npm:^7.24.7"
+    "@babel/plugin-transform-sticky-regex": "npm:^7.24.7"
+    "@babel/plugin-transform-template-literals": "npm:^7.24.7"
+    "@babel/plugin-transform-typeof-symbol": "npm:^7.24.7"
+    "@babel/plugin-transform-unicode-escapes": "npm:^7.24.7"
+    "@babel/plugin-transform-unicode-property-regex": "npm:^7.24.7"
+    "@babel/plugin-transform-unicode-regex": "npm:^7.24.7"
+    "@babel/plugin-transform-unicode-sets-regex": "npm:^7.24.7"
     "@babel/preset-modules": "npm:0.1.6-no-external-plugins"
     babel-plugin-polyfill-corejs2: "npm:^0.4.10"
     babel-plugin-polyfill-corejs3: "npm:^0.10.4"
@@ -1430,7 +1438,7 @@ __metadata:
     semver: "npm:^6.3.1"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/d837d294197803d550e48d9458a356853a54a0528e7cdc51c2b8a5d8dfe41c6fbc597b4fc67464615a7385198a3db2e839da15cca7b9502fedf27170fc6ef673
+  checksum: 10c0/c6714346f3ccc1271eaa90051c75b8bb57b20ef57408ab68740e2f3552693ae0ee5a4bcce3a00211d40e4947af1f7b8ab422066b953f0095461937fb72d11274
   languageName: node
   linkType: hard
 
@@ -1448,33 +1456,33 @@ __metadata:
   linkType: hard
 
 "@babel/preset-react@npm:^7.12.5, @babel/preset-react@npm:^7.22.3":
-  version: 7.24.6
-  resolution: "@babel/preset-react@npm:7.24.6"
+  version: 7.24.7
+  resolution: "@babel/preset-react@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/helper-validator-option": "npm:^7.24.6"
-    "@babel/plugin-transform-react-display-name": "npm:^7.24.6"
-    "@babel/plugin-transform-react-jsx": "npm:^7.24.6"
-    "@babel/plugin-transform-react-jsx-development": "npm:^7.24.6"
-    "@babel/plugin-transform-react-pure-annotations": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/helper-validator-option": "npm:^7.24.7"
+    "@babel/plugin-transform-react-display-name": "npm:^7.24.7"
+    "@babel/plugin-transform-react-jsx": "npm:^7.24.7"
+    "@babel/plugin-transform-react-jsx-development": "npm:^7.24.7"
+    "@babel/plugin-transform-react-pure-annotations": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/edc470b86dfcfdedf53feca3f2266bd7f836a300806938a422f4120d39bbdea6a780b9b0a9ac0333e0bb1b8e554699a74cafd135b2a75b02b77ef1b21f7c7f62
+  checksum: 10c0/9658b685b25cedaadd0b65c4e663fbc7f57394b5036ddb4c99b1a75b0711fb83292c1c625d605c05b73413fc7a6dc20e532627f6a39b6dc8d4e00415479b054c
   languageName: node
   linkType: hard
 
 "@babel/preset-typescript@npm:^7.21.5":
-  version: 7.24.6
-  resolution: "@babel/preset-typescript@npm:7.24.6"
+  version: 7.24.7
+  resolution: "@babel/preset-typescript@npm:7.24.7"
   dependencies:
-    "@babel/helper-plugin-utils": "npm:^7.24.6"
-    "@babel/helper-validator-option": "npm:^7.24.6"
-    "@babel/plugin-syntax-jsx": "npm:^7.24.6"
-    "@babel/plugin-transform-modules-commonjs": "npm:^7.24.6"
-    "@babel/plugin-transform-typescript": "npm:^7.24.6"
+    "@babel/helper-plugin-utils": "npm:^7.24.7"
+    "@babel/helper-validator-option": "npm:^7.24.7"
+    "@babel/plugin-syntax-jsx": "npm:^7.24.7"
+    "@babel/plugin-transform-modules-commonjs": "npm:^7.24.7"
+    "@babel/plugin-transform-typescript": "npm:^7.24.7"
   peerDependencies:
     "@babel/core": ^7.0.0-0
-  checksum: 10c0/bfcef91ed80d67301301e17a799814457b57bfd0d85d9897dce6df6ed0b0af155c0f5b2af7a1a122a3f36faaaa1de87ccf9954ce06d2f440898ffdfaf18aab86
+  checksum: 10c0/986bc0978eedb4da33aba8e1e13a3426dd1829515313b7e8f4ba5d8c18aff1663b468939d471814e7acf4045d326ae6cff37239878d169ac3fe53a8fde71f8ee
   languageName: node
   linkType: hard
 
@@ -1495,51 +1503,51 @@ __metadata:
   linkType: hard
 
 "@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.3, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
-  version: 7.24.6
-  resolution: "@babel/runtime@npm:7.24.6"
+  version: 7.24.7
+  resolution: "@babel/runtime@npm:7.24.7"
   dependencies:
     regenerator-runtime: "npm:^0.14.0"
-  checksum: 10c0/224ad205de33ea28979baaec89eea4c4d4e9482000dd87d15b97859365511cdd4d06517712504024f5d33a5fb9412f9b91c96f1d923974adf9359e1575cde049
+  checksum: 10c0/b6fa3ec61a53402f3c1d75f4d808f48b35e0dfae0ec8e2bb5c6fc79fb95935da75766e0ca534d0f1c84871f6ae0d2ebdd950727cfadb745a2cdbef13faef5513
   languageName: node
   linkType: hard
 
-"@babel/template@npm:^7.24.6, @babel/template@npm:^7.3.3":
-  version: 7.24.6
-  resolution: "@babel/template@npm:7.24.6"
+"@babel/template@npm:^7.24.7, @babel/template@npm:^7.3.3":
+  version: 7.24.7
+  resolution: "@babel/template@npm:7.24.7"
   dependencies:
-    "@babel/code-frame": "npm:^7.24.6"
-    "@babel/parser": "npm:^7.24.6"
-    "@babel/types": "npm:^7.24.6"
-  checksum: 10c0/a4d5805770de908b445f7cdcebfcb6eaa07b1ec9c7b78fd3f375a911b1522c249bddae6b96bc4aac24247cc603e3e6cffcf2fe50b4c929dfeb22de289b517525
+    "@babel/code-frame": "npm:^7.24.7"
+    "@babel/parser": "npm:^7.24.7"
+    "@babel/types": "npm:^7.24.7"
+  checksum: 10c0/95b0b3ee80fcef685b7f4426f5713a855ea2cd5ac4da829b213f8fb5afe48a2a14683c2ea04d446dbc7f711c33c5cd4a965ef34dcbe5bc387c9e966b67877ae3
   languageName: node
   linkType: hard
 
-"@babel/traverse@npm:7, @babel/traverse@npm:^7.24.6":
-  version: 7.24.6
-  resolution: "@babel/traverse@npm:7.24.6"
+"@babel/traverse@npm:7, @babel/traverse@npm:^7.24.7":
+  version: 7.24.7
+  resolution: "@babel/traverse@npm:7.24.7"
   dependencies:
-    "@babel/code-frame": "npm:^7.24.6"
-    "@babel/generator": "npm:^7.24.6"
-    "@babel/helper-environment-visitor": "npm:^7.24.6"
-    "@babel/helper-function-name": "npm:^7.24.6"
-    "@babel/helper-hoist-variables": "npm:^7.24.6"
-    "@babel/helper-split-export-declaration": "npm:^7.24.6"
-    "@babel/parser": "npm:^7.24.6"
-    "@babel/types": "npm:^7.24.6"
+    "@babel/code-frame": "npm:^7.24.7"
+    "@babel/generator": "npm:^7.24.7"
+    "@babel/helper-environment-visitor": "npm:^7.24.7"
+    "@babel/helper-function-name": "npm:^7.24.7"
+    "@babel/helper-hoist-variables": "npm:^7.24.7"
+    "@babel/helper-split-export-declaration": "npm:^7.24.7"
+    "@babel/parser": "npm:^7.24.7"
+    "@babel/types": "npm:^7.24.7"
     debug: "npm:^4.3.1"
     globals: "npm:^11.1.0"
-  checksum: 10c0/39027d5fc7a241c6b71bb5872c2bdcec53743cd7ef3c151bbe6fd7cf874d15f4bc09e5d7e19e2f534b0eb2c115f5368553885fa4253aa1bc9441c6e5bf9efdaf
+  checksum: 10c0/a5135e589c3f1972b8877805f50a084a04865ccb1d68e5e1f3b94a8841b3485da4142e33413d8fd76bc0e6444531d3adf1f59f359c11ffac452b743d835068ab
   languageName: node
   linkType: hard
 
-"@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.24.6, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3":
-  version: 7.24.6
-  resolution: "@babel/types@npm:7.24.6"
+"@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.24.7, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3":
+  version: 7.24.7
+  resolution: "@babel/types@npm:7.24.7"
   dependencies:
-    "@babel/helper-string-parser": "npm:^7.24.6"
-    "@babel/helper-validator-identifier": "npm:^7.24.6"
+    "@babel/helper-string-parser": "npm:^7.24.7"
+    "@babel/helper-validator-identifier": "npm:^7.24.7"
     to-fast-properties: "npm:^2.0.0"
-  checksum: 10c0/1d94d92d97ef49030ad7f9e14cfccfeb70b1706dabcaa69037e659ec9d2c3178fb005d2088cce40d88dfc1306153d9157fe038a79ea2be92e5e6b99a59ef80cc
+  checksum: 10c0/d9ecbfc3eb2b05fb1e6eeea546836ac30d990f395ef3fe3f75ced777a222c3cfc4489492f72e0ce3d9a5a28860a1ce5f81e66b88cf5088909068b3ff4fab72c1
   languageName: node
   linkType: hard
 

From 1ffc293b86fa373beada17fbe789b0e729781e07 Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Thu, 6 Jun 2024 16:12:06 +0200
Subject: [PATCH 027/133] Add missing `moderation_warning` notification support
 to grouped notifications API (#30576)

---
 app/serializers/rest/notification_group_serializer.rb | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/app/serializers/rest/notification_group_serializer.rb b/app/serializers/rest/notification_group_serializer.rb
index 6c1d2465d..05b51b07a 100644
--- a/app/serializers/rest/notification_group_serializer.rb
+++ b/app/serializers/rest/notification_group_serializer.rb
@@ -11,6 +11,7 @@ class REST::NotificationGroupSerializer < ActiveModel::Serializer
   belongs_to :target_status, key: :status, if: :status_type?, serializer: REST::StatusSerializer
   belongs_to :report, if: :report_type?, serializer: REST::ReportSerializer
   belongs_to :account_relationship_severance_event, key: :event, if: :relationship_severance_event?, serializer: REST::AccountRelationshipSeveranceEventSerializer
+  belongs_to :account_warning, key: :moderation_warning, if: :moderation_warning_event?, serializer: REST::AccountWarningSerializer
 
   def status_type?
     [:favourite, :reblog, :status, :mention, :poll, :update].include?(object.type)
@@ -24,6 +25,10 @@ class REST::NotificationGroupSerializer < ActiveModel::Serializer
     object.type == :severed_relationships
   end
 
+  def moderation_warning_event?
+    object.type == :moderation_warning
+  end
+
   def page_min_id
     range = instance_options[:group_metadata][object.group_key]
     range.present? ? range[:min_id].to_s : object.notification.id.to_s

From a662c6d1d82fbc0bb27a2d0a55c1a1c028c16dea Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 6 Jun 2024 10:12:58 -0400
Subject: [PATCH 028/133] Use `sidekiq_inline` in admin/account_action model
 spec (#30565)

---
 spec/models/admin/account_action_spec.rb | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/spec/models/admin/account_action_spec.rb b/spec/models/admin/account_action_spec.rb
index a9dcf352d..e55db2f81 100644
--- a/spec/models/admin/account_action_spec.rb
+++ b/spec/models/admin/account_action_spec.rb
@@ -69,20 +69,22 @@ RSpec.describe Admin::AccountAction do
       end
     end
 
-    it 'sends notification, log the action, and closes other reports', :aggregate_failures do
-      other_report = Fabricate(:report, target_account: target_account)
-
-      emails = []
-      expect do
-        emails = capture_emails { subject }
-      end.to (change(Admin::ActionLog.where(action: type), :count).by 1)
-         .and(change { other_report.reload.action_taken? }.from(false).to(true))
+    it 'sends email to target account user', :sidekiq_inline do
+      emails = capture_emails { subject }
 
       expect(emails).to contain_exactly(
         have_attributes(
           to: contain_exactly(target_account.user.email)
         )
       )
+    end
+
+    it 'sends notification, log the action, and closes other reports', :aggregate_failures do
+      other_report = Fabricate(:report, target_account: target_account)
+
+      expect { subject }
+        .to (change(Admin::ActionLog.where(action: type), :count).by 1)
+        .and(change { other_report.reload.action_taken? }.from(false).to(true))
 
       expect(LocalNotificationWorker).to have_enqueued_sidekiq_job(target_account.id, anything, 'AccountWarning', 'moderation_warning')
     end

From 9b9b0e25b6839d162d96a19407611e9fc8192c81 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 6 Jun 2024 10:14:33 -0400
Subject: [PATCH 029/133] Use `sidekiq_inline` in requests/api/v1/reports spec
 (#30564)

---
 spec/requests/api/v1/reports_spec.rb | 38 +++++++++++++---------------
 1 file changed, 18 insertions(+), 20 deletions(-)

diff --git a/spec/requests/api/v1/reports_spec.rb b/spec/requests/api/v1/reports_spec.rb
index 94baf8cb9..9e8954a4c 100644
--- a/spec/requests/api/v1/reports_spec.rb
+++ b/spec/requests/api/v1/reports_spec.rb
@@ -33,30 +33,28 @@ RSpec.describe 'Reports' do
 
     it_behaves_like 'forbidden for wrong scope', 'read read:reports'
 
-    it 'creates a report', :aggregate_failures do
-      perform_enqueued_jobs do
-        emails = capture_emails { subject }
+    it 'creates a report', :aggregate_failures, :sidekiq_inline do
+      emails = capture_emails { subject }
 
-        expect(response).to have_http_status(200)
-        expect(body_as_json).to match(
-          a_hash_including(
-            status_ids: [status.id.to_s],
-            category: category,
-            comment: 'reasons'
-          )
+      expect(response).to have_http_status(200)
+      expect(body_as_json).to match(
+        a_hash_including(
+          status_ids: [status.id.to_s],
+          category: category,
+          comment: 'reasons'
         )
+      )
 
-        expect(target_account.targeted_reports).to_not be_empty
-        expect(target_account.targeted_reports.first.comment).to eq 'reasons'
+      expect(target_account.targeted_reports).to_not be_empty
+      expect(target_account.targeted_reports.first.comment).to eq 'reasons'
 
-        expect(emails.size)
-          .to eq(1)
-        expect(emails.first)
-          .to have_attributes(
-            to: contain_exactly(admin.email),
-            subject: eq(I18n.t('admin_mailer.new_report.subject', instance: Rails.configuration.x.local_domain, id: target_account.targeted_reports.first.id))
-          )
-      end
+      expect(emails.size)
+        .to eq(1)
+      expect(emails.first)
+        .to have_attributes(
+          to: contain_exactly(admin.email),
+          subject: eq(I18n.t('admin_mailer.new_report.subject', instance: Rails.configuration.x.local_domain, id: target_account.targeted_reports.first.id))
+        )
     end
 
     context 'when a status does not belong to the reported account' do

From 07cc94e05fa89794714fb0434529657dfa992bca Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 6 Jun 2024 10:19:22 -0400
Subject: [PATCH 030/133] Use `sidekiq_inline` in
 requests/api/v1/admin/account_actions spec (#30563)

---
 spec/requests/api/v1/admin/account_actions_spec.rb | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/spec/requests/api/v1/admin/account_actions_spec.rb b/spec/requests/api/v1/admin/account_actions_spec.rb
index 4167911a1..778658508 100644
--- a/spec/requests/api/v1/admin/account_actions_spec.rb
+++ b/spec/requests/api/v1/admin/account_actions_spec.rb
@@ -10,10 +10,16 @@ RSpec.describe 'Account actions' do
   let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
 
   shared_examples 'a successful notification delivery' do
-    it 'notifies the user about the action taken' do
-      expect { subject }
-        .to have_enqueued_job(ActionMailer::MailDeliveryJob)
-        .with('UserMailer', 'warning', 'deliver_now!', args: [User, AccountWarning])
+    it 'notifies the user about the action taken', :sidekiq_inline do
+      emails = capture_emails { subject }
+
+      expect(emails.size)
+        .to eq(1)
+
+      expect(emails.first)
+        .to have_attributes(
+          to: contain_exactly(target_account.user.email)
+        )
     end
   end
 

From 04ebbe3077e7f39025428bfa596ada2d21be6dfb Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 6 Jun 2024 10:19:37 -0400
Subject: [PATCH 031/133] Add `sidekiq_inline` to appeal service spec (#30562)

---
 spec/services/appeal_service_spec.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/spec/services/appeal_service_spec.rb b/spec/services/appeal_service_spec.rb
index 10c0f148d..3fad74db9 100644
--- a/spec/services/appeal_service_spec.rb
+++ b/spec/services/appeal_service_spec.rb
@@ -2,7 +2,7 @@
 
 require 'rails_helper'
 
-RSpec.describe AppealService do
+RSpec.describe AppealService, :sidekiq_inline do
   describe '#call' do
     let!(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
 

From 12823908bbc101dc3106ae0fd1b804ef164390ac Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 6 Jun 2024 10:19:55 -0400
Subject: [PATCH 032/133] Adjust devcontainers welcome message to be less
 codespaces-specific (#30566)

---
 .devcontainer/welcome-message.txt | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/.devcontainer/welcome-message.txt b/.devcontainer/welcome-message.txt
index 488cf9285..dbc19c910 100644
--- a/.devcontainer/welcome-message.txt
+++ b/.devcontainer/welcome-message.txt
@@ -1,8 +1,7 @@
-👋 Welcome to "Mastodon" in GitHub Codespaces!
+👋 Welcome to your Mastodon Dev Container!
 
-🛠️  Your environment is fully setup with all the required software.
+🛠️ Your environment is fully setup with all the required software.
 
-🔍 To explore VS Code to its fullest, search using the Command Palette (Cmd/Ctrl + Shift + P or F1).
-
-📝 Edit away, run your app as usual, and we'll automatically make it available for you to access.
+💥 Run `bin/dev` to start the application processes.
 
+🥼 Run `RAILS_ENV=test bin/rails assets:precompile && RAILS_ENV=test bin/rspec` to run the test suite.

From 94d8d1094f32fcdd2372533efde9ad0e129020fc Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 6 Jun 2024 16:29:16 -0400
Subject: [PATCH 033/133] Provide richer failure information in `bin/setup`
 (#30581)

---
 bin/setup | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/bin/setup b/bin/setup
index 90700ac4f..5d9622151 100755
--- a/bin/setup
+++ b/bin/setup
@@ -5,7 +5,7 @@ require "fileutils"
 APP_ROOT = File.expand_path('..', __dir__)
 
 def system!(*args)
-  system(*args) || abort("\n== Command #{args} failed ==")
+  system(*args, exception: true)
 end
 
 FileUtils.chdir APP_ROOT do

From 3495f0d9ba37ec10189ce1df60921f15554224d5 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 6 Jun 2024 16:33:28 -0400
Subject: [PATCH 034/133] Use corepack yarn in `bin/setup` (#30573)

---
 bin/setup | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/bin/setup b/bin/setup
index 5d9622151..93c6981d0 100755
--- a/bin/setup
+++ b/bin/setup
@@ -13,17 +13,13 @@ FileUtils.chdir APP_ROOT do
   # This script is idempotent, so that you can run it at any time and get an expectable outcome.
   # Add necessary setup steps to this file.
 
-  puts '== Installing dependencies =='
+  puts "\n== Installing Ruby dependencies =="
   system! 'gem install bundler --conservative'
   system('bundle check') || system!('bundle install')
 
-  # Install JavaScript dependencies
-  system! 'bin/yarn'
-
-  # puts "\n== Copying sample files =="
-  # unless File.exist?('config/database.yml')
-  #   FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
-  # end
+  puts "\n== Installing JS dependencies =="
+  system! 'corepack prepare'
+  system! 'bin/yarn install --immutable'
 
   puts "\n== Preparing database =="
   system! 'bin/rails db:prepare'

From 8041fa174b7c90c1c130342aede2d556e8f56e82 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 7 Jun 2024 09:39:01 +0200
Subject: [PATCH 035/133] chore(deps): update opentelemetry-ruby (non-major)
 (#30555)

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 ca32d0cca..136d074d3 100644
--- a/Gemfile
+++ b/Gemfile
@@ -107,7 +107,7 @@ gem 'private_address_check', '~> 0.5'
 gem 'opentelemetry-api', '~> 1.2.5'
 
 group :opentelemetry do
-  gem 'opentelemetry-exporter-otlp', '~> 0.26.3', require: false
+  gem 'opentelemetry-exporter-otlp', '~> 0.27.0', require: false
   gem 'opentelemetry-instrumentation-active_job', '~> 0.7.1', require: false
   gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.20.1', require: false
   gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.21.2', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index bf5340a5b..9ad9deacf 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -489,7 +489,7 @@ GEM
     opentelemetry-api (1.2.5)
     opentelemetry-common (0.20.1)
       opentelemetry-api (~> 1.0)
-    opentelemetry-exporter-otlp (0.26.3)
+    opentelemetry-exporter-otlp (0.27.0)
       google-protobuf (~> 3.14)
       googleapis-common-protos-types (~> 1.3)
       opentelemetry-api (~> 1.1)
@@ -978,7 +978,7 @@ DEPENDENCIES
   omniauth-saml (~> 2.0)
   omniauth_openid_connect (~> 0.6.1)
   opentelemetry-api (~> 1.2.5)
-  opentelemetry-exporter-otlp (~> 0.26.3)
+  opentelemetry-exporter-otlp (~> 0.27.0)
   opentelemetry-instrumentation-active_job (~> 0.7.1)
   opentelemetry-instrumentation-active_model_serializers (~> 0.20.1)
   opentelemetry-instrumentation-concurrent_ruby (~> 0.21.2)

From 1408733386219e1588c14d2fbf9f3c926513a5a3 Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Fri, 7 Jun 2024 11:27:59 +0200
Subject: [PATCH 036/133] Fix Mastodon relying on ImageMagick even with
 `MASTODON_USE_LIBVIPS` (#30590)

---
 app/models/custom_emoji.rb       | 2 +-
 spec/models/custom_emoji_spec.rb | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb
index 1c9b44395..31ba91ad0 100644
--- a/app/models/custom_emoji.rb
+++ b/app/models/custom_emoji.rb
@@ -39,7 +39,7 @@ class CustomEmoji < ApplicationRecord
 
   has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode, inverse_of: false, dependent: nil
 
-  has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce +profile "!icc,*" +set date:modify +set date:create +set date:timestamp' } }, validate_media_type: false
+  has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce +profile "!icc,*" +set date:modify +set date:create +set date:timestamp', file_geometry_parser: FastGeometryParser } }, validate_media_type: false, processors: [:lazy_thumbnail]
 
   normalizes :domain, with: ->(domain) { domain.downcase }
 
diff --git a/spec/models/custom_emoji_spec.rb b/spec/models/custom_emoji_spec.rb
index a0903e597..038d1d0c6 100644
--- a/spec/models/custom_emoji_spec.rb
+++ b/spec/models/custom_emoji_spec.rb
@@ -2,7 +2,7 @@
 
 require 'rails_helper'
 
-RSpec.describe CustomEmoji do
+RSpec.describe CustomEmoji, :paperclip_processing do
   describe '#search' do
     subject { described_class.search(search_term) }
 

From ce07394ed5e9e5728db7da59910fbd83bc4a38ea Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 7 Jun 2024 11:53:59 +0200
Subject: [PATCH 037/133] chore(deps): update dependency aws-sdk-s3 to v1.152.0
 (#30572)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 Gemfile.lock | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index 9ad9deacf..1003e1442 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -100,17 +100,17 @@ GEM
     attr_required (1.0.2)
     awrence (1.2.1)
     aws-eventstream (1.3.0)
-    aws-partitions (1.929.0)
-    aws-sdk-core (3.196.1)
+    aws-partitions (1.940.0)
+    aws-sdk-core (3.197.0)
       aws-eventstream (~> 1, >= 1.3.0)
       aws-partitions (~> 1, >= 1.651.0)
       aws-sigv4 (~> 1.8)
       jmespath (~> 1, >= 1.6.1)
-    aws-sdk-kms (1.81.0)
-      aws-sdk-core (~> 3, >= 3.193.0)
+    aws-sdk-kms (1.83.0)
+      aws-sdk-core (~> 3, >= 3.197.0)
       aws-sigv4 (~> 1.1)
-    aws-sdk-s3 (1.151.0)
-      aws-sdk-core (~> 3, >= 3.194.0)
+    aws-sdk-s3 (1.152.0)
+      aws-sdk-core (~> 3, >= 3.197.0)
       aws-sdk-kms (~> 1)
       aws-sigv4 (~> 1.8)
     aws-sigv4 (1.8.0)

From c1b0c1a5e40c277b4b1f036fa882d90f0882cd67 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 7 Jun 2024 11:59:30 +0200
Subject: [PATCH 038/133] New Crowdin Translations (automated) (#30586)

Co-authored-by: GitHub Actions <noreply@github.com>
---
 app/javascript/mastodon/locales/la.json | 4 ++++
 config/locales/doorkeeper.be.yml        | 1 -
 config/locales/doorkeeper.bg.yml        | 3 ++-
 config/locales/doorkeeper.br.yml        | 1 +
 config/locales/doorkeeper.ca.yml        | 3 ++-
 config/locales/doorkeeper.cs.yml        | 1 -
 config/locales/doorkeeper.cy.yml        | 1 -
 config/locales/doorkeeper.da.yml        | 3 ++-
 config/locales/doorkeeper.de.yml        | 3 ++-
 config/locales/doorkeeper.en-GB.yml     | 1 -
 config/locales/doorkeeper.es-AR.yml     | 3 ++-
 config/locales/doorkeeper.es-MX.yml     | 3 ++-
 config/locales/doorkeeper.es.yml        | 3 ++-
 config/locales/doorkeeper.eu.yml        | 1 -
 config/locales/doorkeeper.fi.yml        | 3 ++-
 config/locales/doorkeeper.fo.yml        | 3 ++-
 config/locales/doorkeeper.fy.yml        | 1 -
 config/locales/doorkeeper.gl.yml        | 3 ++-
 config/locales/doorkeeper.he.yml        | 3 ++-
 config/locales/doorkeeper.hu.yml        | 3 ++-
 config/locales/doorkeeper.ia.yml        | 1 -
 config/locales/doorkeeper.ie.yml        | 1 -
 config/locales/doorkeeper.is.yml        | 3 ++-
 config/locales/doorkeeper.it.yml        | 3 ++-
 config/locales/doorkeeper.ja.yml        | 1 -
 config/locales/doorkeeper.ko.yml        | 3 ++-
 config/locales/doorkeeper.lt.yml        | 1 -
 config/locales/doorkeeper.lv.yml        | 1 -
 config/locales/doorkeeper.nl.yml        | 3 ++-
 config/locales/doorkeeper.nn.yml        | 1 -
 config/locales/doorkeeper.pl.yml        | 3 ++-
 config/locales/doorkeeper.pt-BR.yml     | 1 -
 config/locales/doorkeeper.pt-PT.yml     | 3 ++-
 config/locales/doorkeeper.sl.yml        | 1 -
 config/locales/doorkeeper.sq.yml        | 3 ++-
 config/locales/doorkeeper.sr-Latn.yml   | 3 ++-
 config/locales/doorkeeper.sr.yml        | 3 ++-
 config/locales/doorkeeper.sv.yml        | 1 -
 config/locales/doorkeeper.th.yml        | 1 -
 config/locales/doorkeeper.tr.yml        | 3 ++-
 config/locales/doorkeeper.uk.yml        | 3 ++-
 config/locales/doorkeeper.vi.yml        | 1 -
 config/locales/doorkeeper.zh-CN.yml     | 3 ++-
 config/locales/doorkeeper.zh-HK.yml     | 1 -
 config/locales/doorkeeper.zh-TW.yml     | 3 ++-
 45 files changed, 55 insertions(+), 43 deletions(-)

diff --git a/app/javascript/mastodon/locales/la.json b/app/javascript/mastodon/locales/la.json
index 48b233400..a49ec94d7 100644
--- a/app/javascript/mastodon/locales/la.json
+++ b/app/javascript/mastodon/locales/la.json
@@ -32,6 +32,7 @@
   "compose_form.direct_message_warning_learn_more": "Discere plura",
   "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.",
   "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.lock_disclaimer": "Tua ratio non est {clausa}. Quisquis te sequi potest ut visum accipiat nuntios tuos tantum pro sectatoribus.",
   "compose_form.lock_disclaimer.lock": "clausum",
   "compose_form.placeholder": "What is on your mind?",
   "compose_form.publish_form": "Barrire",
@@ -91,6 +92,7 @@
   "lightbox.next": "Secundum",
   "navigation_bar.domain_blocks": "Hidden domains",
   "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.",
+  "notification.moderation_warning.action_none": "Tua ratiō monitum moderātiōnis accēpit.",
   "notification.reblog": "{name} boosted your status",
   "notifications.filter.all": "Omnia",
   "notifications.filter.polls": "Eventus electionis",
@@ -107,6 +109,8 @@
   "onboarding.steps.setup_profile.title": "Customize your profile",
   "onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
   "onboarding.steps.share_profile.title": "Share your profile",
+  "onboarding.tips.accounts_from_other_servers": "<strong>Scisne?</strong> Quoniam Mastodon dēcentālis est, nōnnulla profīlia quae invenīs in servīs aliīs quam tuōrum erunt hospitāta. Tamen cum eīs sine impedīmentō interāgere potes! Servus eōrum in alterā parte nōminis eōrum est!",
+  "onboarding.tips.migration": "<strong>Scisne?</strong> Sī sentīs {domain} tibi in futūrō nōn esse optimam servī ēlēctiōnem, ad alium servum Mastodon sine amittendō sectātōribus tuīs migrāre potes. Etiam tuum servum hospitārī potes!",
   "poll.closed": "Clausum",
   "poll.vote": "Eligere",
   "poll.voted": "Elegisti hoc responsum",
diff --git a/config/locales/doorkeeper.be.yml b/config/locales/doorkeeper.be.yml
index 5f0536c8d..748cbeafa 100644
--- a/config/locales/doorkeeper.be.yml
+++ b/config/locales/doorkeeper.be.yml
@@ -174,7 +174,6 @@ be:
       read:filters: бачыць свае фільтры
       read:follows: бачыць свае падпіскі
       read:lists: бачыць свае спісы
-      read:me: чытайце толькі базавую інфармацыю аб сваім уліковым запісе
       read:mutes: бачыць свае ігнараванні
       read:notifications: бачыць свае абвесткі
       read:reports: бачыць свае скаргі
diff --git a/config/locales/doorkeeper.bg.yml b/config/locales/doorkeeper.bg.yml
index 7633156d7..dd5366182 100644
--- a/config/locales/doorkeeper.bg.yml
+++ b/config/locales/doorkeeper.bg.yml
@@ -135,6 +135,7 @@ bg:
         media: Прикачена мултимедия
         mutes: Заглушения
         notifications: Известия
+        profile: Вашият профил в Mastodon
         push: Изскачащи известия
         reports: Доклади
         search: Търсене
@@ -165,6 +166,7 @@ bg:
       admin:write:reports: извършване на действия за модериране на докладвания
       crypto: употреба на цялостно шифроване
       follow: промяна на взаимоотношенията на акаунта
+      profile: само за четене на сведенията ви за профила на акаунта
       push: получаване на вашите изскачащи известия
       read: четене на всички данни от акаунта ви
       read:accounts: преглед на информация за акаунти
@@ -174,7 +176,6 @@ bg:
       read:filters: преглед на вашите филтри
       read:follows: преглед на вашите последвания
       read:lists: преглед на вашите списъци
-      read:me: четене само на основните сведения за акаунта ви
       read:mutes: преглед на вашите заглушавания
       read:notifications: преглед на вашите известия
       read:reports: преглед на вашите докладвания
diff --git a/config/locales/doorkeeper.br.yml b/config/locales/doorkeeper.br.yml
index 7b7f4155b..119d8681f 100644
--- a/config/locales/doorkeeper.br.yml
+++ b/config/locales/doorkeeper.br.yml
@@ -104,6 +104,7 @@ br:
         lists: Listennoù
         media: Restroù media stag
         mutes: Kuzhet
+        profile: Ho profil Mastodon
         search: Klask
         statuses: Toudoù
     layouts:
diff --git a/config/locales/doorkeeper.ca.yml b/config/locales/doorkeeper.ca.yml
index 80827a87d..0323656da 100644
--- a/config/locales/doorkeeper.ca.yml
+++ b/config/locales/doorkeeper.ca.yml
@@ -135,6 +135,7 @@ ca:
         media: Adjunts multimèdia
         mutes: Silenciats
         notifications: Notificacions
+        profile: El vostre perfil de Mastodon
         push: Notificacions push
         reports: Informes
         search: Cerca
@@ -165,6 +166,7 @@ ca:
       admin:write:reports: fer l'acció de moderació en els informes
       crypto: usa xifrat d'extrem a extrem
       follow: modifica les relacions del compte
+      profile: només llegir la informació del perfil del vostre compte
       push: rebre notificacions push del teu compte
       read: llegir les dades del teu compte
       read:accounts: mira informació dels comptes
@@ -174,7 +176,6 @@ ca:
       read:filters: mira els teus filtres
       read:follows: mira els teus seguiments
       read:lists: mira les teves llistes
-      read:me: llegir només la informació bàsica del vostre compte
       read:mutes: mira els teus silenciats
       read:notifications: mira les teves notificacions
       read:reports: mira els teus informes
diff --git a/config/locales/doorkeeper.cs.yml b/config/locales/doorkeeper.cs.yml
index 9719a9a24..be2a4d971 100644
--- a/config/locales/doorkeeper.cs.yml
+++ b/config/locales/doorkeeper.cs.yml
@@ -174,7 +174,6 @@ cs:
       read:filters: vidět vaše filtry
       read:follows: vidět vaše sledování
       read:lists: vidět vaše seznamy
-      read:me: číst pouze základní informace vašeho účtu
       read:mutes: vidět vaše skrytí
       read:notifications: vidět vaše oznámení
       read:reports: vidět vaše hlášení
diff --git a/config/locales/doorkeeper.cy.yml b/config/locales/doorkeeper.cy.yml
index 88cd2b9d5..e79aa0359 100644
--- a/config/locales/doorkeeper.cy.yml
+++ b/config/locales/doorkeeper.cy.yml
@@ -174,7 +174,6 @@ cy:
       read:filters: gweld eich hidlwyr
       read:follows: gweld eich dilynwyr
       read:lists: gweld eich rhestrau
-      read:me: darllen dim ond manylion elfennol eich cyfrif
       read:mutes: gweld eich anwybyddiadau
       read:notifications: gweld eich hysbysiadau
       read:reports: gweld eich adroddiadau
diff --git a/config/locales/doorkeeper.da.yml b/config/locales/doorkeeper.da.yml
index ed10e14e2..d462f43d3 100644
--- a/config/locales/doorkeeper.da.yml
+++ b/config/locales/doorkeeper.da.yml
@@ -135,6 +135,7 @@ da:
         media: Medievedhæftninger
         mutes: Tavsgørelser
         notifications: Notifikationer
+        profile: Din Mastodon-profil
         push: Push-notifikationer
         reports: Anmeldelser
         search: Søgning
@@ -165,6 +166,7 @@ da:
       admin:write:reports: udfør modereringshandlinger på anmeldelser
       crypto: benyt ende-til-ende kryptering
       follow: ændre kontorelationer
+      profile: læs kun kontoprofiloplysningerne
       push: modtag dine push-notifikationer
       read: læs alle dine kontodata
       read:accounts: se kontooplysninger
@@ -174,7 +176,6 @@ da:
       read:filters: se dine filtre
       read:follows: se dine følger
       read:lists: se dine lister
-      read:me: læs kun kontoens basisoplysninger
       read:mutes: se dine tavsgørelser
       read:notifications: se dine notifikationer
       read:reports: se dine anmeldelser
diff --git a/config/locales/doorkeeper.de.yml b/config/locales/doorkeeper.de.yml
index 80d612255..f303aa23a 100644
--- a/config/locales/doorkeeper.de.yml
+++ b/config/locales/doorkeeper.de.yml
@@ -135,6 +135,7 @@ de:
         media: Medienanhänge
         mutes: Stummschaltungen
         notifications: Benachrichtigungen
+        profile: Dein Mastodon-Profil
         push: Push-Benachrichtigungen
         reports: Meldungen
         search: Suche
@@ -165,6 +166,7 @@ de:
       admin:write:reports: Moderationsaktionen auf Meldungen ausführen
       crypto: Ende-zu-Ende-Verschlüsselung verwenden
       follow: Kontenbeziehungen verändern
+      profile: nur die Profilinformationen deines Kontos lesen
       push: deine Push-Benachrichtigungen erhalten
       read: all deine Daten lesen
       read:accounts: deine Kontoinformationen einsehen
@@ -174,7 +176,6 @@ de:
       read:filters: deine Filter einsehen
       read:follows: sehen, wem du folgst
       read:lists: deine Listen sehen
-      read:me: nur deine grundlegenden Kontoinformationen lesen
       read:mutes: deine Stummschaltungen einsehen
       read:notifications: deine Benachrichtigungen sehen
       read:reports: deine Meldungen sehen
diff --git a/config/locales/doorkeeper.en-GB.yml b/config/locales/doorkeeper.en-GB.yml
index 2e537c530..b3ceffb13 100644
--- a/config/locales/doorkeeper.en-GB.yml
+++ b/config/locales/doorkeeper.en-GB.yml
@@ -174,7 +174,6 @@ en-GB:
       read:filters: see your filters
       read:follows: see your follows
       read:lists: see your lists
-      read:me: read only your account's basic information
       read:mutes: see your mutes
       read:notifications: see your notifications
       read:reports: see your reports
diff --git a/config/locales/doorkeeper.es-AR.yml b/config/locales/doorkeeper.es-AR.yml
index 47cfc451a..0b04696b6 100644
--- a/config/locales/doorkeeper.es-AR.yml
+++ b/config/locales/doorkeeper.es-AR.yml
@@ -135,6 +135,7 @@ es-AR:
         media: Adjuntos de medios
         mutes: Silenciados
         notifications: Notificaciones
+        profile: Tu perfil de Mastodon
         push: Notificaciones push
         reports: Denuncias
         search: Buscar
@@ -165,6 +166,7 @@ es-AR:
       admin:write:reports: ejecutar acciones de moderación en denuncias
       crypto: usar cifrado de extremo a extremo
       follow: modificar relaciones de cuenta
+      profile: leer solo la información del perfil de tu cuenta
       push: recibir tus notificaciones push
       read: leer todos los datos de tu cuenta
       read:accounts: ver información de cuentas
@@ -174,7 +176,6 @@ es-AR:
       read:filters: ver tus filtros
       read:follows: ver qué cuentas seguís
       read:lists: ver tus listas
-      read:me: leer solo la información básica de tu cuenta
       read:mutes: ver qué cuentas silenciaste
       read:notifications: ver tus notificaciones
       read:reports: ver tus denuncias
diff --git a/config/locales/doorkeeper.es-MX.yml b/config/locales/doorkeeper.es-MX.yml
index e56e0df3b..54386c4c3 100644
--- a/config/locales/doorkeeper.es-MX.yml
+++ b/config/locales/doorkeeper.es-MX.yml
@@ -135,6 +135,7 @@ es-MX:
         media: Archivos adjuntos
         mutes: Silenciados
         notifications: Notificaciones
+        profile: Tu perfil de Mastodon
         push: Notificaciones push
         reports: Reportes
         search: Busqueda
@@ -165,6 +166,7 @@ es-MX:
       admin:write:reports: realizar acciones de moderación en informes
       crypto: usar cifrado de extremo a extremo
       follow: seguir, bloquear, desbloquear y dejar de seguir cuentas
+      profile: leer sólo la información del perfil de tu cuenta
       push: recibir tus notificaciones push
       read: leer los datos de tu cuenta
       read:accounts: ver información de cuentas
@@ -174,7 +176,6 @@ es-MX:
       read:filters: ver tus filtros
       read:follows: ver a quién sigues
       read:lists: ver tus listas
-      read:me: leer solo la información básica de tu cuenta
       read:mutes: ver a quién has silenciado
       read:notifications: ver tus notificaciones
       read:reports: ver tus informes
diff --git a/config/locales/doorkeeper.es.yml b/config/locales/doorkeeper.es.yml
index 44e165a21..9be036a1d 100644
--- a/config/locales/doorkeeper.es.yml
+++ b/config/locales/doorkeeper.es.yml
@@ -135,6 +135,7 @@ es:
         media: Adjuntos multimedia
         mutes: Silenciados
         notifications: Notificaciones
+        profile: Tu perfil de Mastodon
         push: Notificaciones push
         reports: Informes
         search: Buscar
@@ -165,6 +166,7 @@ es:
       admin:write:reports: realizar acciones de moderación en informes
       crypto: usar cifrado de extremo a extremo
       follow: seguir, bloquear, desbloquear y dejar de seguir cuentas
+      profile: leer sólo la información del perfil de tu cuenta
       push: recibir tus notificaciones push
       read: leer los datos de tu cuenta
       read:accounts: ver información de cuentas
@@ -174,7 +176,6 @@ es:
       read:filters: ver tus filtros
       read:follows: ver a quién sigues
       read:lists: ver tus listas
-      read:me: leer solo la información básica de tu cuenta
       read:mutes: ver a quién has silenciado
       read:notifications: ver tus notificaciones
       read:reports: ver tus informes
diff --git a/config/locales/doorkeeper.eu.yml b/config/locales/doorkeeper.eu.yml
index 88a63f698..e7963672f 100644
--- a/config/locales/doorkeeper.eu.yml
+++ b/config/locales/doorkeeper.eu.yml
@@ -174,7 +174,6 @@ eu:
       read:filters: ikusi zure iragazkiak
       read:follows: ikusi zuk jarraitutakoak
       read:lists: ikusi zure zerrendak
-      read:me: irakurri soilik zure kontuaren oinarrizko informazioa
       read:mutes: ikusi zuk mutututakoak
       read:notifications: ikusi zure jakinarazpenak
       read:reports: ikusi zure salaketak
diff --git a/config/locales/doorkeeper.fi.yml b/config/locales/doorkeeper.fi.yml
index ae8963c76..b028c10a8 100644
--- a/config/locales/doorkeeper.fi.yml
+++ b/config/locales/doorkeeper.fi.yml
@@ -135,6 +135,7 @@ fi:
         media: Medialiitteet
         mutes: Mykistykset
         notifications: Ilmoitukset
+        profile: Mastodon-profiilisi
         push: Puskuilmoitukset
         reports: Raportit
         search: Hae
@@ -165,6 +166,7 @@ fi:
       admin:write:reports: suorita valvontatoimia raporteille
       crypto: käytä päästä päähän -salausta
       follow: muokkaa tilin suhteita
+      profile: lue vain tilisi profiilitietoja
       push: vastaanota puskuilmoituksiasi
       read: lue kaikkia tilin tietoja
       read:accounts: katso tilien tietoja
@@ -174,7 +176,6 @@ fi:
       read:filters: katso suodattimiasi
       read:follows: katso seurattujasi
       read:lists: katso listojasi
-      read:me: lue tilisi perustietoja
       read:mutes: katso mykistyksiäsi
       read:notifications: katso ilmoituksiasi
       read:reports: katso raporttejasi
diff --git a/config/locales/doorkeeper.fo.yml b/config/locales/doorkeeper.fo.yml
index 4f5cc5a64..bd9457b62 100644
--- a/config/locales/doorkeeper.fo.yml
+++ b/config/locales/doorkeeper.fo.yml
@@ -135,6 +135,7 @@ fo:
         media: Viðfestir miðlar
         mutes: Doyvir
         notifications: Fráboðanir
+        profile: Tín Mastodon vangi
         push: Skumpifráboðanir
         reports: Meldingar
         search: Leita
@@ -165,6 +166,7 @@ fo:
       admin:write:reports: útinna kjakleiðsluatgerðir á meldingum
       crypto: brúka enda-til-enda bronglan
       follow: broyta viðurskifti millum kontur
+      profile: les bara vangaupplýsingar av tíni kontu
       push: móttaka tínar skumpifráboðanir
       read: lesa allar dátur í tíni kontu
       read:accounts: vís kontuupplýsingar
@@ -174,7 +176,6 @@ fo:
       read:filters: síggja tíni filtur
       read:follows: síggja hvørji tú fylgir
       read:lists: síggja tínar listar
-      read:me: les bara grundleggjandi upplýsingar av tínari kontu
       read:mutes: síggja tínar doyvingar
       read:notifications: síggja tínar fráboðanir
       read:reports: síggja tínar meldingar
diff --git a/config/locales/doorkeeper.fy.yml b/config/locales/doorkeeper.fy.yml
index 51f0055ff..a43defc42 100644
--- a/config/locales/doorkeeper.fy.yml
+++ b/config/locales/doorkeeper.fy.yml
@@ -174,7 +174,6 @@ fy:
       read:filters: jo filters besjen
       read:follows: de accounts dy’tsto folgest besjen
       read:lists: jo listen besjen
-      read:me: allinnich de basisgegevens fan jo account lêze
       read:mutes: jo negearre brûkers besjen
       read:notifications: jo meldingen besjen
       read:reports: jo rapportearre berjochten besjen
diff --git a/config/locales/doorkeeper.gl.yml b/config/locales/doorkeeper.gl.yml
index d34c58dec..e86babd64 100644
--- a/config/locales/doorkeeper.gl.yml
+++ b/config/locales/doorkeeper.gl.yml
@@ -135,6 +135,7 @@ gl:
         media: Anexos multimedia
         mutes: Acaladas
         notifications: Notificacións
+        profile: O teu perfil en Mastodon
         push: Notificacións Push
         reports: Denuncias
         search: Busca
@@ -165,6 +166,7 @@ gl:
       admin:write:reports: executar accións de moderación nas denuncias
       crypto: usar cifrado de extremo-a-extremo
       follow: modificar as relacións da conta
+      profile: ler só a información de perfil da túa conta
       push: recibir notificacións push
       read: ler todos os datos da tua conta
       read:accounts: ver información das contas
@@ -174,7 +176,6 @@ gl:
       read:filters: ver os filtros
       read:follows: ver a quen segues
       read:lists: ver as tuas listaxes
-      read:me: ler só a información básica da túa conta
       read:mutes: ver a quen tes acalado
       read:notifications: ver as notificacións
       read:reports: ver as túas denuncias
diff --git a/config/locales/doorkeeper.he.yml b/config/locales/doorkeeper.he.yml
index a6376fa4c..7a664c486 100644
--- a/config/locales/doorkeeper.he.yml
+++ b/config/locales/doorkeeper.he.yml
@@ -135,6 +135,7 @@ he:
         media: קבצי מדיה מצורפים
         mutes: השתקות
         notifications: התראות
+        profile: פרופיל המסטודון שלך
         push: התראות בדחיפה
         reports: דיווחים
         search: חיפוש
@@ -165,6 +166,7 @@ he:
       admin:write:reports: ביצוע פעולות הנהלה על חשבונות
       crypto: שימוש בהצפנה מקצה לקצה
       follow: לעקוב, לחסום, להסיר חסימה ולהפסיק לעקוב אחרי חשבונות
+      profile: קריאה של פרטי הפרופיל שלך בלבד
       push: קבלת התראות בדחיפה
       read: לקרוא את המידע שבחשבונך
       read:accounts: צפיה במידע על חשבונות
@@ -174,7 +176,6 @@ he:
       read:filters: צפייה במסננים
       read:follows: צפייה בנעקבים
       read:lists: צפיה ברשימותיך
-      read:me: לקריאה בלבד של פרטי חשבונך הבסיסיים
       read:mutes: צפיה במושתקיך
       read:notifications: צפיה בהתראותיך
       read:reports: צפיה בדוחותיך
diff --git a/config/locales/doorkeeper.hu.yml b/config/locales/doorkeeper.hu.yml
index 28ce283ff..b2e51a47c 100644
--- a/config/locales/doorkeeper.hu.yml
+++ b/config/locales/doorkeeper.hu.yml
@@ -135,6 +135,7 @@ hu:
         media: Médiamellékletek
         mutes: Némítások
         notifications: Értesítések
+        profile: Saját Mastodon-profil
         push: Push értesítések
         reports: Bejelentések
         search: Keresés
@@ -165,6 +166,7 @@ hu:
       admin:write:reports: moderációs műveletek végzése bejelentéseken
       crypto: végpontok közti titkosítás használata
       follow: fiókkapcsolatok módosítása
+      profile: csak a saját profil alapvető adatainak olvasása
       push: push értesítések fogadása
       read: saját fiók adatainak olvasása
       read:accounts: fiók adatainak megtekintése
@@ -174,7 +176,6 @@ hu:
       read:filters: szűrök megtekintése
       read:follows: követések megtekintése
       read:lists: listák megtekintése
-      read:me: csak a fiókod alapvető adatainak elolvasása
       read:mutes: némítások megtekintése
       read:notifications: értesítések megtekintése
       read:reports: bejelentések megtekintése
diff --git a/config/locales/doorkeeper.ia.yml b/config/locales/doorkeeper.ia.yml
index 40109a311..5b99abb7b 100644
--- a/config/locales/doorkeeper.ia.yml
+++ b/config/locales/doorkeeper.ia.yml
@@ -174,7 +174,6 @@ ia:
       read:filters: vider tu filtros
       read:follows: vider qui tu seque
       read:lists: vider tu listas
-      read:me: leger solmente le information basic de tu conto
       read:mutes: vider qui tu silentia
       read:notifications: vider tu notificationes
       read:reports: vider tu reportos
diff --git a/config/locales/doorkeeper.ie.yml b/config/locales/doorkeeper.ie.yml
index fc8132c92..0119f3573 100644
--- a/config/locales/doorkeeper.ie.yml
+++ b/config/locales/doorkeeper.ie.yml
@@ -174,7 +174,6 @@ ie:
       read:filters: vider tui filtres
       read:follows: vider tui sequitores
       read:lists: vider tui listes
-      read:me: leer solmen li basic information de tui conto
       read:mutes: vider tui silentias
       read:notifications: vider tui notificationes
       read:reports: vider tui raportes
diff --git a/config/locales/doorkeeper.is.yml b/config/locales/doorkeeper.is.yml
index 995d507f5..84a4d3895 100644
--- a/config/locales/doorkeeper.is.yml
+++ b/config/locales/doorkeeper.is.yml
@@ -135,6 +135,7 @@ is:
         media: Myndefnisviðhengi
         mutes: Þagganir
         notifications: Tilkynningar
+        profile: Mastodon notandasniðið þitt
         push: Ýti-tilkynningar
         reports: Kærur
         search: Leita
@@ -165,6 +166,7 @@ is:
       admin:write:reports: framkvæma umsjónaraðgerðir á kærur
       crypto: nota enda-í-enda dulritun
       follow: breyta venslum aðgangs
+      profile: lesa einungis upplýsingar úr notandasniðinu þínu
       push: taka á móti ýti-tilkynningum til þín
       read: lesa öll gögn á notandaaðgangnum þínum
       read:accounts: sjá upplýsingar í notendaaðgöngum
@@ -174,7 +176,6 @@ is:
       read:filters: skoða síurnar þínar
       read:follows: sjá hverjum þú fylgist með
       read:lists: skoða listana þína
-      read:me: lesa einungis grunnupplýsingar aðgangsins þíns
       read:mutes: skoða hverja þú þaggar
       read:notifications: sjá tilkynningarnar þínar
       read:reports: skoða skýrslurnar þína
diff --git a/config/locales/doorkeeper.it.yml b/config/locales/doorkeeper.it.yml
index f39f78466..f5df14dea 100644
--- a/config/locales/doorkeeper.it.yml
+++ b/config/locales/doorkeeper.it.yml
@@ -135,6 +135,7 @@ it:
         media: Allegati multimediali
         mutes: Silenziati
         notifications: Notifiche
+        profile: Il tuo profilo Mastodon
         push: Notifiche push
         reports: Segnalazioni
         search: Cerca
@@ -165,6 +166,7 @@ it:
       admin:write:reports: eseguire azioni di moderazione sulle segnalazioni
       crypto: utilizzare la crittografia end-to-end
       follow: modifica le relazioni tra profili
+      profile: leggi solo le informazioni sul profilo del tuo account
       push: ricevere le tue notifiche push
       read: leggere tutti i dati del tuo profilo
       read:accounts: visualizzare le informazioni sui profili
@@ -174,7 +176,6 @@ it:
       read:filters: visualizzare i tuoi filtri
       read:follows: visualizzare i tuoi seguiti
       read:lists: visualizzare i tuoi elenchi
-      read:me: leggi solo le informazioni di base del tuo account
       read:mutes: visualizzare i tuoi silenziamenti
       read:notifications: visualizzare le tue notifiche
       read:reports: visualizzare le tue segnalazioni
diff --git a/config/locales/doorkeeper.ja.yml b/config/locales/doorkeeper.ja.yml
index af61dbdcb..62f2a3eb0 100644
--- a/config/locales/doorkeeper.ja.yml
+++ b/config/locales/doorkeeper.ja.yml
@@ -174,7 +174,6 @@ ja:
       read:filters: フィルターの読み取り
       read:follows: フォローの読み取り
       read:lists: リストの読み取り
-      read:me: 自分のアカウントの基本的な情報の読み取りのみ
       read:mutes: ミュートの読み取り
       read:notifications: 通知の読み取り
       read:reports: 通報の読み取り
diff --git a/config/locales/doorkeeper.ko.yml b/config/locales/doorkeeper.ko.yml
index 12674cc12..3ab0698d5 100644
--- a/config/locales/doorkeeper.ko.yml
+++ b/config/locales/doorkeeper.ko.yml
@@ -135,6 +135,7 @@ ko:
         media: 첨부된 미디어
         mutes: 뮤트
         notifications: 알림
+        profile: 내 마스토돈 프로필
         push: 푸시 알림
         reports: 신고
         search: 검색
@@ -165,6 +166,7 @@ ko:
       admin:write:reports: 신고에 모더레이션 조치 취하기
       crypto: 종단간 암호화 사용
       follow: 계정 관계 수정
+      profile: 내 계정의 프로필 정보만을 읽습니다
       push: 푸시 알림 받기
       read: 계정의 모든 데이터 읽기
       read:accounts: 계정 정보 보기
@@ -174,7 +176,6 @@ ko:
       read:filters: 필터 보기
       read:follows: 팔로우 보기
       read:lists: 리스트 보기
-      read:me: 내 계정의 기본 정보만을 읽습니다
       read:mutes: 뮤트 보기
       read:notifications: 알림 보기
       read:reports: 신고 보기
diff --git a/config/locales/doorkeeper.lt.yml b/config/locales/doorkeeper.lt.yml
index 82695d8ba..5c2e4fd4e 100644
--- a/config/locales/doorkeeper.lt.yml
+++ b/config/locales/doorkeeper.lt.yml
@@ -174,7 +174,6 @@ lt:
       read:filters: matyti tavo filtrus
       read:follows: matyti tavo sekimus
       read:lists: matyti tavo sąrašus
-      read:me: skaityti tik pagrindinę paskyros informaciją
       read:mutes: matyti tavo nutildymus
       read:notifications: matyti tavo pranešimus
       read:reports: matyti tavo ataskaitas
diff --git a/config/locales/doorkeeper.lv.yml b/config/locales/doorkeeper.lv.yml
index 2005ce3c7..5aa5daef3 100644
--- a/config/locales/doorkeeper.lv.yml
+++ b/config/locales/doorkeeper.lv.yml
@@ -174,7 +174,6 @@ lv:
       read:filters: apskatīt savus filtrus
       read:follows: apskatīt savus sekotājus
       read:lists: apskatīt savus sarakstus
-      read:me: lasīt tikai Tava konta pamatinformāciju
       read:mutes: apskatīt savus apklusinātos
       read:notifications: apskatīt savus paziņojumus
       read:reports: apskatīt savus pārskatus
diff --git a/config/locales/doorkeeper.nl.yml b/config/locales/doorkeeper.nl.yml
index 9554c0ee6..4115e0a17 100644
--- a/config/locales/doorkeeper.nl.yml
+++ b/config/locales/doorkeeper.nl.yml
@@ -135,6 +135,7 @@ nl:
         media: Mediabijlagen
         mutes: Negeren
         notifications: Meldingen
+        profile: Jouw Mastodonprofiel
         push: Pushmeldingen
         reports: Rapportages
         search: Zoeken
@@ -165,6 +166,7 @@ nl:
       admin:write:reports: moderatieacties op rapportages uitvoeren
       crypto: end-to-end-encryptie gebruiken
       follow: volgrelaties tussen accounts bewerken
+      profile: alleen de profielgegevens van jouw account lezen
       push: jouw pushmeldingen ontvangen
       read: alle gegevens van jouw account lezen
       read:accounts: informatie accounts bekijken
@@ -174,7 +176,6 @@ nl:
       read:filters: jouw filters bekijken
       read:follows: de accounts die jij volgt bekijken
       read:lists: jouw lijsten bekijken
-      read:me: alleen de basisgegevens van jouw account lezen
       read:mutes: jouw genegeerde gebruikers bekijken
       read:notifications: jouw meldingen bekijken
       read:reports: jouw gerapporteerde berichten bekijken
diff --git a/config/locales/doorkeeper.nn.yml b/config/locales/doorkeeper.nn.yml
index ab0380c6f..0e5d1ca45 100644
--- a/config/locales/doorkeeper.nn.yml
+++ b/config/locales/doorkeeper.nn.yml
@@ -174,7 +174,6 @@ nn:
       read:filters: sjå filtera dine
       read:follows: sjå fylgjarane dine
       read:lists: sjå listene dine
-      read:me: les berre kontoen din sin grunnleggjande informasjon
       read:mutes: sjå kven du har målbunde
       read:notifications: sjå varsla dine
       read:reports: sjå rapportane dine
diff --git a/config/locales/doorkeeper.pl.yml b/config/locales/doorkeeper.pl.yml
index eefca2de6..a18a86e97 100644
--- a/config/locales/doorkeeper.pl.yml
+++ b/config/locales/doorkeeper.pl.yml
@@ -135,6 +135,7 @@ pl:
         media: Załączniki multimedialne
         mutes: Wyciszenia
         notifications: Powiadomienia
+        profile: Twój profil
         push: Powiadomienia push
         reports: Zgłoszenia
         search: Szukaj
@@ -165,6 +166,7 @@ pl:
       admin:write:reports: wykonaj działania moderacyjne na zgłoszeniach
       crypto: użyj szyfrowania end-to-end
       follow: możliwość zarządzania relacjami kont
+      profile: odczytaj tylko informacje o profilu
       push: otrzymywanie powiadomień push dla Twojego konta
       read: możliwość odczytu wszystkich danych konta
       read:accounts: dostęp do informacji o koncie
@@ -174,7 +176,6 @@ pl:
       read:filters: dostęp do filtrów
       read:follows: dostęp do listy obserwowanych
       read:lists: dostęp do Twoich list
-      read:me: odczytaj tylko podstawowe informacje o koncie
       read:mutes: dostęp do listy wyciszonych
       read:notifications: możliwość odczytu powiadomień
       read:reports: dostęp do Twoich zgłoszeń
diff --git a/config/locales/doorkeeper.pt-BR.yml b/config/locales/doorkeeper.pt-BR.yml
index 150b4339e..d7e9353b5 100644
--- a/config/locales/doorkeeper.pt-BR.yml
+++ b/config/locales/doorkeeper.pt-BR.yml
@@ -174,7 +174,6 @@ pt-BR:
       read:filters: ver seus filtros
       read:follows: ver quem você segue
       read:lists: ver suas listas
-      read:me: ler só as informações básicas da sua conta
       read:mutes: ver seus silenciados
       read:notifications: ver suas notificações
       read:reports: ver suas denúncias
diff --git a/config/locales/doorkeeper.pt-PT.yml b/config/locales/doorkeeper.pt-PT.yml
index 0457190cd..f03cee6b3 100644
--- a/config/locales/doorkeeper.pt-PT.yml
+++ b/config/locales/doorkeeper.pt-PT.yml
@@ -135,6 +135,7 @@ pt-PT:
         media: Anexos de media
         mutes: Silenciados
         notifications: Notificações
+        profile: O seu perfil Mastodon
         push: Notificações push
         reports: Denúncias
         search: Pesquisa
@@ -165,6 +166,7 @@ pt-PT:
       admin:write:reports: executar ações de moderação em denúncias
       crypto: usa encriptação ponta-a-ponta
       follow: siga, bloqueie, desbloqueie, e deixa de seguir contas
+      profile: apenas ler as informações do perfil da sua conta
       push: receber as suas notificações push
       read: tenha acesso aos dados da tua conta
       read:accounts: ver as informações da conta
@@ -174,7 +176,6 @@ pt-PT:
       read:filters: ver os seus filtros
       read:follows: ver quem você segue
       read:lists: ver as suas listas
-      read:me: ler apenas as informações básicas da sua conta
       read:mutes: ver os utilizadores que silenciou
       read:notifications: ver as suas notificações
       read:reports: ver as suas denúncias
diff --git a/config/locales/doorkeeper.sl.yml b/config/locales/doorkeeper.sl.yml
index 55e00ff96..a613308b2 100644
--- a/config/locales/doorkeeper.sl.yml
+++ b/config/locales/doorkeeper.sl.yml
@@ -174,7 +174,6 @@ sl:
       read:filters: oglejte si svoje filtre
       read:follows: oglejte si svoje sledilce
       read:lists: oglejte si svoje sezname
-      read:me: preberi le osnovne podatke računa
       read:mutes: oglejte si svoje utišane
       read:notifications: oglejte si svoja obvestila
       read:reports: oglejte si svoje prijave
diff --git a/config/locales/doorkeeper.sq.yml b/config/locales/doorkeeper.sq.yml
index 793819c59..de3415406 100644
--- a/config/locales/doorkeeper.sq.yml
+++ b/config/locales/doorkeeper.sq.yml
@@ -135,6 +135,7 @@ sq:
         media: Bashkëngjitje media
         mutes: Heshtime
         notifications: Njoftime
+        profile: Profili juaj Mastodon
         push: Njoftime Push
         reports: Raportime
         search: Kërkim
@@ -165,6 +166,7 @@ sq:
       admin:write:reports: të kryejë veprime moderimi në raportime
       crypto: përdor fshehtëzim skaj-më-skaj
       follow: të ndryshojë marrëdhënie llogarish
+      profile: të lexojë vetëm hollësi profili llogarie tuaj
       push: të marrë njoftime push për ju
       read: të lexojë krejt të dhënat e llogarisë tuaj
       read:accounts: të shohë hollësi llogarish
@@ -174,7 +176,6 @@ sq:
       read:filters: të shohë filtrat tuaj
       read:follows: të shohë ndjekësit tuaj
       read:lists: të shohë listat tuaja
-      read:me: të shohë vetëm hollësi elementare të llogarisë tuaj
       read:mutes: të shohë ç’keni heshtuar
       read:notifications: të shohë njoftimet tuaja
       read:reports: të shohë raportimet tuaja
diff --git a/config/locales/doorkeeper.sr-Latn.yml b/config/locales/doorkeeper.sr-Latn.yml
index 58ed5e8b6..6445353c1 100644
--- a/config/locales/doorkeeper.sr-Latn.yml
+++ b/config/locales/doorkeeper.sr-Latn.yml
@@ -135,6 +135,7 @@ sr-Latn:
         media: Multimedijalni prilozi
         mutes: Ignorisani
         notifications: Obaveštenja
+        profile: Vaš Mastodon profil
         push: Prosleđena obaveštenja
         reports: Prijave
         search: Pretraga
@@ -165,6 +166,7 @@ sr-Latn:
       admin:write:reports: vršenje moderatorskih aktivnosti nad izveštajima
       crypto: korišćenje end-to-end enkripcije
       follow: menja odnose naloga
+      profile: čita samo informacije o profilu vašeg naloga
       push: primanje prosleđenih obaveštenja
       read: čita podatke Vašeg naloga
       read:accounts: pogledaj informacije o nalozima
@@ -174,7 +176,6 @@ sr-Latn:
       read:filters: pogledaj svoje filtere
       read:follows: pogledaj koga pratiš
       read:lists: pogledaj svoje liste
-      read:me: čita samo osnovne informacije o vašem nalogu
       read:mutes: pogledaj ignorisanja
       read:notifications: pogledaj svoja obaveštenja
       read:reports: pogledaj svoje prijave
diff --git a/config/locales/doorkeeper.sr.yml b/config/locales/doorkeeper.sr.yml
index f40a05e90..feb0fec3e 100644
--- a/config/locales/doorkeeper.sr.yml
+++ b/config/locales/doorkeeper.sr.yml
@@ -135,6 +135,7 @@ sr:
         media: Мултимедијални прилози
         mutes: Игнорисани
         notifications: Обавештења
+        profile: Ваш Mastodon профил
         push: Прослеђена обавештења
         reports: Пријаве
         search: Претрага
@@ -165,6 +166,7 @@ sr:
       admin:write:reports: вршење модераторских активности над извештајима
       crypto: коришћење end-to-end енкрипције
       follow: мења односе налога
+      profile: чита само информације о профилу вашег налога
       push: примање прослеђених обавештења
       read: чита податке Вашег налога
       read:accounts: погледај информације о налозима
@@ -174,7 +176,6 @@ sr:
       read:filters: погледај своје филтере
       read:follows: погледај кога пратиш
       read:lists: погледај своје листе
-      read:me: чита само основне информације о вашем налогу
       read:mutes: погледај игнорисања
       read:notifications: погледај своја обавештења
       read:reports: погледај своје пријаве
diff --git a/config/locales/doorkeeper.sv.yml b/config/locales/doorkeeper.sv.yml
index d336f08c5..f2c8bd34b 100644
--- a/config/locales/doorkeeper.sv.yml
+++ b/config/locales/doorkeeper.sv.yml
@@ -174,7 +174,6 @@ sv:
       read:filters: se dina filter
       read:follows: se vem du följer
       read:lists: se dina listor
-      read:me: läs endast den grundläggande informationen för ditt konto
       read:mutes: se dina tystningar
       read:notifications: se dina notiser
       read:reports: se dina rapporter
diff --git a/config/locales/doorkeeper.th.yml b/config/locales/doorkeeper.th.yml
index 8a28566a0..067e06558 100644
--- a/config/locales/doorkeeper.th.yml
+++ b/config/locales/doorkeeper.th.yml
@@ -174,7 +174,6 @@ th:
       read:filters: ดูตัวกรองของคุณ
       read:follows: ดูการติดตามของคุณ
       read:lists: ดูรายการของคุณ
-      read:me: อ่านเฉพาะข้อมูลพื้นฐานของบัญชีของคุณเท่านั้น
       read:mutes: ดูการซ่อนของคุณ
       read:notifications: ดูการแจ้งเตือนของคุณ
       read:reports: ดูรายงานของคุณ
diff --git a/config/locales/doorkeeper.tr.yml b/config/locales/doorkeeper.tr.yml
index f5ebbc5fd..330449b1b 100644
--- a/config/locales/doorkeeper.tr.yml
+++ b/config/locales/doorkeeper.tr.yml
@@ -135,6 +135,7 @@ tr:
         media: Medya ekleri
         mutes: Sessize alınanlar
         notifications: Bildirimler
+        profile: Mastodon profiliniz
         push: Anlık bildirimler
         reports: Şikayetler
         search: Arama
@@ -165,6 +166,7 @@ tr:
       admin:write:reports: raporlarda denetleme eylemleri gerçekleştirin
       crypto: uçtan uca şifreleme kullan
       follow: hesap ilişkilerini değiştirin
+      profile: hesabınızın sadece profil bilgilerini okuma
       push: anlık bildirimlerizi alın
       read: hesabınızın tüm verilerini okuyun
       read:accounts: hesap bilgilerini görün
@@ -174,7 +176,6 @@ tr:
       read:filters: süzgeçlerinizi görün
       read:follows: takip ettiklerinizi görün
       read:lists: listelerinizi görün
-      read:me: hesabınızın sadece temel bilgilerini okuma
       read:mutes: sessize aldıklarınızı görün
       read:notifications: bildirimlerinizi görün
       read:reports: raporlarınızı görün
diff --git a/config/locales/doorkeeper.uk.yml b/config/locales/doorkeeper.uk.yml
index ac7fbbe15..ca54fcb65 100644
--- a/config/locales/doorkeeper.uk.yml
+++ b/config/locales/doorkeeper.uk.yml
@@ -135,6 +135,7 @@ uk:
         media: Мультимедійні вкладення
         mutes: Нехтувані
         notifications: Сповіщення
+        profile: Ваш профіль Mastodon
         push: Push-сповіщення
         reports: Скарги
         search: Пошук
@@ -171,6 +172,7 @@ uk:
       admin:write:reports: модерувати скарги
       crypto: використовувати наскрізне шифрування
       follow: змінювати стосунки облікового запису
+      profile: читати лише інформацію профілю вашого облікового запису
       push: отримувати Ваші Push-повідомлення
       read: читати усі дані вашого облікового запису
       read:accounts: бачити інформацію про облікові записи
@@ -180,7 +182,6 @@ uk:
       read:filters: бачити Ваші фільтри
       read:follows: бачити Ваші підписки
       read:lists: бачити Ваші списки
-      read:me: читайте лише основну інформацію вашого облікового запису
       read:mutes: бачити ваші нехтування
       read:notifications: бачити Ваші сповіщення
       read:reports: бачити Ваші скарги
diff --git a/config/locales/doorkeeper.vi.yml b/config/locales/doorkeeper.vi.yml
index 624db9aff..7f1c5430e 100644
--- a/config/locales/doorkeeper.vi.yml
+++ b/config/locales/doorkeeper.vi.yml
@@ -174,7 +174,6 @@ vi:
       read:filters: xem bộ lọc
       read:follows: xem những người theo dõi
       read:lists: xem danh sách
-      read:me: chỉ đọc thông tin cơ bản tài khoản
       read:mutes: xem những người đã ẩn
       read:notifications: xem thông báo
       read:reports: xem báo cáo của bạn
diff --git a/config/locales/doorkeeper.zh-CN.yml b/config/locales/doorkeeper.zh-CN.yml
index 73f1f9725..18477bc84 100644
--- a/config/locales/doorkeeper.zh-CN.yml
+++ b/config/locales/doorkeeper.zh-CN.yml
@@ -135,6 +135,7 @@ zh-CN:
         media: 媒体文件
         mutes: 已被隐藏的
         notifications: 通知
+        profile: 你的 Mastodon 个人资料
         push: 推送通知
         reports: 举报
         search: 搜索
@@ -165,6 +166,7 @@ zh-CN:
       admin:write:reports: 对举报执行管理操作
       crypto: 使用端到端加密
       follow: 关注或屏蔽用户
+      profile: 仅读取你账户中的个人资料信息
       push: 接收你的账户的推送通知
       read: 读取你的账户数据
       read:accounts: 查看账号信息
@@ -174,7 +176,6 @@ zh-CN:
       read:filters: 查看你的过滤器
       read:follows: 查看你的关注
       read:lists: 查看你的列表
-      read:me: 只读取你账户的基本信息
       read:mutes: 查看你的隐藏列表
       read:notifications: 查看你的通知
       read:reports: 查看你的举报
diff --git a/config/locales/doorkeeper.zh-HK.yml b/config/locales/doorkeeper.zh-HK.yml
index 76d13a74a..79629b12f 100644
--- a/config/locales/doorkeeper.zh-HK.yml
+++ b/config/locales/doorkeeper.zh-HK.yml
@@ -174,7 +174,6 @@ zh-HK:
       read:filters: 檢視你的過濾條件
       read:follows: 檢視你關注的人
       read:lists: 檢視你的清單
-      read:me: 僅讀取帳號的基本資訊
       read:mutes: 檢視被你靜音的人
       read:notifications: 檢視你的通知
       read:reports: 檢視你的檢舉
diff --git a/config/locales/doorkeeper.zh-TW.yml b/config/locales/doorkeeper.zh-TW.yml
index 86827a712..d12651a64 100644
--- a/config/locales/doorkeeper.zh-TW.yml
+++ b/config/locales/doorkeeper.zh-TW.yml
@@ -135,6 +135,7 @@ zh-TW:
         media: 多媒體附加檔案
         mutes: 靜音
         notifications: 通知
+        profile: 您 Mastodon 個人檔案
         push: 推播通知
         reports: 檢舉報告
         search: 搜尋
@@ -165,6 +166,7 @@ zh-TW:
       admin:write:reports: 對報告進行管理動作
       crypto: 使用端到端加密
       follow: 修改帳號關係
+      profile: 僅讀取您的帳號個人檔案資訊
       push: 接收帳號的推播通知
       read: 讀取您所有的帳號資料
       read:accounts: 檢視帳號資訊
@@ -174,7 +176,6 @@ zh-TW:
       read:filters: 檢視您的過濾條件
       read:follows: 檢視您跟隨之使用者
       read:lists: 檢視您的列表
-      read:me: 僅讀取您的帳號基本資訊
       read:mutes: 檢視您靜音的人
       read:notifications: 檢視您的通知
       read:reports: 檢視您的檢舉

From 3dfc7267e20aab8e5e72adffbf1ef4773811c068 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Fri, 7 Jun 2024 06:00:27 -0400
Subject: [PATCH 039/133] Rename deprecated config option to `enable_reloading`
 in dev env (#30577)

---
 config/environments/development.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config/environments/development.rb b/config/environments/development.rb
index a3254125c..cc601bde3 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -8,7 +8,7 @@ Rails.application.configure do
   # In the development environment your application's code is reloaded any time
   # it changes. This slows down response time but is perfect for development
   # since you don't have to restart the web server when you make code changes.
-  config.cache_classes = false
+  config.enable_reloading = true
 
   # Do not eager load code on boot.
   config.eager_load = false

From a5e3b814a207365edfcf1f625c1594774f936ab4 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Fri, 7 Jun 2024 06:00:51 -0400
Subject: [PATCH 040/133] Remove Status/ivar/shapes regression check from test
 env (#30580)

---
 config/environments/test.rb | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/config/environments/test.rb b/config/environments/test.rb
index 49b0c1f30..716bf8d31 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -61,12 +61,6 @@ Rails.application.configure do
   config.i18n.default_locale = :en
   config.i18n.fallbacks = true
 
-  config.to_prepare do
-    # Force Status to always be SHAPE_TOO_COMPLEX
-    # Ref: https://github.com/mastodon/mastodon/issues/23644
-    10.times { |i| Status.allocate.instance_variable_set(:"@ivar_#{i}", nil) }
-  end
-
   # Tell Active Support which deprecation messages to disallow.
   config.active_support.disallowed_deprecation_warnings = []
 

From 3d058f898a8e029b8e0d007f4528b2ae878b8fc4 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 7 Jun 2024 12:37:50 +0200
Subject: [PATCH 041/133] chore(deps): update dependency rubocop-rspec to
 v2.31.0 (#30588)

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 1003e1442..8ff990260 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -751,7 +751,7 @@ GEM
       rack (>= 1.1)
       rubocop (>= 1.33.0, < 2.0)
       rubocop-ast (>= 1.31.1, < 2.0)
-    rubocop-rspec (2.30.0)
+    rubocop-rspec (2.31.0)
       rubocop (~> 1.40)
       rubocop-capybara (~> 2.17)
       rubocop-factory_bot (~> 2.22)

From bc01b328a10cdf93e098fd016feb26b01cc58bdb Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Fri, 7 Jun 2024 08:21:38 -0400
Subject: [PATCH 042/133] Dont include peer dirs in devcontainer compose config
 (#30592)

---
 .devcontainer/docker-compose.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
index 85f9eb22c..4d6eb3c48 100644
--- a/.devcontainer/docker-compose.yml
+++ b/.devcontainer/docker-compose.yml
@@ -5,7 +5,7 @@ services:
       context: .
       dockerfile: Dockerfile
     volumes:
-      - ../..:/workspaces:cached
+      - ..:/workspaces/mastodon:cached
     environment:
       RAILS_ENV: development
       NODE_ENV: development

From 299ae9bf922401751dc2a4dd50739a0391e0863a Mon Sep 17 00:00:00 2001
From: Victor Dyotte <vdyotte@gmail.com>
Date: Fri, 7 Jun 2024 08:29:30 -0400
Subject: [PATCH 043/133] Add `S3_KEY_PREFIX` environment variable (#30181)

---
 config/initializers/paperclip.rb | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb
index 5b9365a53..0be78b99f 100644
--- a/config/initializers/paperclip.rb
+++ b/config/initializers/paperclip.rb
@@ -3,6 +3,8 @@
 Paperclip::DataUriAdapter.register
 Paperclip::ResponseWithLimitAdapter.register
 
+PATH = ':prefix_url:class/:attachment/:id_partition/:style/:filename'
+
 Paperclip.interpolates :filename do |attachment, style|
   if style == :original
     attachment.original_filename
@@ -29,7 +31,7 @@ end
 
 Paperclip::Attachment.default_options.merge!(
   use_timestamp: false,
-  path: ':prefix_url:class/:attachment/:id_partition/:style/:filename',
+  path: PATH,
   storage: :fog
 )
 
@@ -40,6 +42,8 @@ if ENV['S3_ENABLED'] == 'true'
   s3_protocol = ENV.fetch('S3_PROTOCOL') { 'https' }
   s3_hostname = ENV.fetch('S3_HOSTNAME') { "s3-#{s3_region}.amazonaws.com" }
 
+  Paperclip::Attachment.default_options[:path] = ENV.fetch('S3_KEY_PREFIX') + "/#{PATH}" if ENV.has_key?('S3_KEY_PREFIX')
+
   Paperclip::Attachment.default_options.merge!(
     storage: :s3,
     s3_protocol: s3_protocol,
@@ -159,7 +163,7 @@ else
   Paperclip::Attachment.default_options.merge!(
     storage: :filesystem,
     path: File.join(ENV.fetch('PAPERCLIP_ROOT_PATH', File.join(':rails_root', 'public', 'system')), ':prefix_path:class', ':attachment', ':id_partition', ':style', ':filename'),
-    url: "#{ENV.fetch('PAPERCLIP_ROOT_URL', '/system')}/:prefix_url:class/:attachment/:id_partition/:style/:filename"
+    url: ENV.fetch('PAPERCLIP_ROOT_URL', '/system') + "/#{PATH}"
   )
 end
 

From 37e4d96b70b46fb0994af23eefd6a77498895ba3 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Fri, 7 Jun 2024 08:39:53 -0400
Subject: [PATCH 044/133] Restore `verbose` option to media remove cli (#30536)

---
 lib/mastodon/cli/media.rb | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lib/mastodon/cli/media.rb b/lib/mastodon/cli/media.rb
index 509d11a81..123973d19 100644
--- a/lib/mastodon/cli/media.rb
+++ b/lib/mastodon/cli/media.rb
@@ -13,6 +13,7 @@ module Mastodon::CLI
     option :remove_headers, type: :boolean, default: false
     option :include_follows, type: :boolean, default: false
     option :concurrency, type: :numeric, default: 5, aliases: [:c]
+    option :verbose, type: :boolean, default: false, aliases: [:v]
     option :dry_run, type: :boolean, default: false
     desc 'remove', 'Remove remote media files, headers or avatars'
     long_desc <<-DESC

From 9e9613b2864062ce4174ae02db3f94629ebdca0e Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Fri, 7 Jun 2024 15:45:11 +0200
Subject: [PATCH 045/133] Fix `mentions.account_id` and `mentions.status_id`
 not having `NOT NULL` database constraints (#30591)

---
 app/models/mention.rb                              |  4 ++--
 ...093446_change_mention_status_id_non_nullable.rb |  7 +++++++
 ...lidate_change_mention_status_id_non_nullable.rb | 14 ++++++++++++++
 ...94603_change_mention_account_id_non_nullable.rb |  7 +++++++
 ...idate_change_mention_account_id_non_nullable.rb | 14 ++++++++++++++
 db/schema.rb                                       |  6 +++---
 6 files changed, 47 insertions(+), 5 deletions(-)
 create mode 100644 db/migrate/20240607093446_change_mention_status_id_non_nullable.rb
 create mode 100644 db/migrate/20240607093954_validate_change_mention_status_id_non_nullable.rb
 create mode 100644 db/migrate/20240607094603_change_mention_account_id_non_nullable.rb
 create mode 100644 db/migrate/20240607094856_validate_change_mention_account_id_non_nullable.rb

diff --git a/app/models/mention.rb b/app/models/mention.rb
index 2348b2905..af9bb7378 100644
--- a/app/models/mention.rb
+++ b/app/models/mention.rb
@@ -5,10 +5,10 @@
 # Table name: mentions
 #
 #  id         :bigint(8)        not null, primary key
-#  status_id  :bigint(8)
+#  status_id  :bigint(8)        not null
 #  created_at :datetime         not null
 #  updated_at :datetime         not null
-#  account_id :bigint(8)
+#  account_id :bigint(8)        not null
 #  silent     :boolean          default(FALSE), not null
 #
 
diff --git a/db/migrate/20240607093446_change_mention_status_id_non_nullable.rb b/db/migrate/20240607093446_change_mention_status_id_non_nullable.rb
new file mode 100644
index 000000000..b6ee4d318
--- /dev/null
+++ b/db/migrate/20240607093446_change_mention_status_id_non_nullable.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class ChangeMentionStatusIdNonNullable < ActiveRecord::Migration[7.1]
+  def change
+    add_check_constraint :mentions, 'status_id IS NOT NULL', name: 'mentions_status_id_null', validate: false
+  end
+end
diff --git a/db/migrate/20240607093954_validate_change_mention_status_id_non_nullable.rb b/db/migrate/20240607093954_validate_change_mention_status_id_non_nullable.rb
new file mode 100644
index 000000000..bd3d9a33a
--- /dev/null
+++ b/db/migrate/20240607093954_validate_change_mention_status_id_non_nullable.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class ValidateChangeMentionStatusIdNonNullable < ActiveRecord::Migration[7.1]
+  def up
+    validate_check_constraint :mentions, name: 'mentions_status_id_null'
+    change_column_null :mentions, :status_id, false
+    remove_check_constraint :mentions, name: 'mentions_status_id_null'
+  end
+
+  def down
+    add_check_constraint :mentions, 'status_id IS NOT NULL', name: 'mentions_status_id_null', validate: false
+    change_column_null :mentions, :status_id, true
+  end
+end
diff --git a/db/migrate/20240607094603_change_mention_account_id_non_nullable.rb b/db/migrate/20240607094603_change_mention_account_id_non_nullable.rb
new file mode 100644
index 000000000..72d7bf244
--- /dev/null
+++ b/db/migrate/20240607094603_change_mention_account_id_non_nullable.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class ChangeMentionAccountIdNonNullable < ActiveRecord::Migration[7.1]
+  def change
+    add_check_constraint :mentions, 'account_id IS NOT NULL', name: 'mentions_account_id_null', validate: false
+  end
+end
diff --git a/db/migrate/20240607094856_validate_change_mention_account_id_non_nullable.rb b/db/migrate/20240607094856_validate_change_mention_account_id_non_nullable.rb
new file mode 100644
index 000000000..1125dffb3
--- /dev/null
+++ b/db/migrate/20240607094856_validate_change_mention_account_id_non_nullable.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class ValidateChangeMentionAccountIdNonNullable < ActiveRecord::Migration[7.1]
+  def up
+    validate_check_constraint :mentions, name: 'mentions_account_id_null'
+    change_column_null :mentions, :account_id, false
+    remove_check_constraint :mentions, name: 'mentions_account_id_null'
+  end
+
+  def down
+    add_check_constraint :mentions, 'account_id IS NOT NULL', name: 'mentions_account_id_null', validate: false
+    change_column_null :mentions, :account_id, true
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index ce2951608..5f8c7e693 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[7.1].define(version: 2024_06_03_195202) do
+ActiveRecord::Schema[7.1].define(version: 2024_06_07_094856) do
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
 
@@ -661,10 +661,10 @@ ActiveRecord::Schema[7.1].define(version: 2024_06_03_195202) do
   end
 
   create_table "mentions", force: :cascade do |t|
-    t.bigint "status_id"
+    t.bigint "status_id", null: false
     t.datetime "created_at", precision: nil, null: false
     t.datetime "updated_at", precision: nil, null: false
-    t.bigint "account_id"
+    t.bigint "account_id", null: false
     t.boolean "silent", default: false, null: false
     t.index ["account_id", "status_id"], name: "index_mentions_on_account_id_and_status_id", unique: true
     t.index ["status_id"], name: "index_mentions_on_status_id"

From 773283ffb9d227d7768d3c32a1a4bf556f0fb0a3 Mon Sep 17 00:00:00 2001
From: Isa S <smiba@users.noreply.github.com>
Date: Fri, 7 Jun 2024 15:54:55 +0200
Subject: [PATCH 046/133] Make S3's retry limit a ENV variable (#23215)

---
 config/initializers/paperclip.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb
index 0be78b99f..e9e3c78cf 100644
--- a/config/initializers/paperclip.rb
+++ b/config/initializers/paperclip.rb
@@ -68,7 +68,7 @@ if ENV['S3_ENABLED'] == 'true'
       http_open_timeout: ENV.fetch('S3_OPEN_TIMEOUT') { '5' }.to_i,
       http_read_timeout: ENV.fetch('S3_READ_TIMEOUT') { '5' }.to_i,
       http_idle_timeout: 5,
-      retry_limit: 0,
+      retry_limit: ENV.fetch('S3_RETRY_LIMIT'){ '0' }.to_i,
     }
   )
 

From 4f6cc547d0a7828d036d22f320b9c078e492a9c4 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Fri, 7 Jun 2024 09:55:55 -0400
Subject: [PATCH 047/133] Align root/vscode user dynamic for codespaces with
 non-codespaces config (#30593)

---
 .devcontainer/Dockerfile                   | 4 ++--
 .devcontainer/codespaces/devcontainer.json | 2 ++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 9d8fa2702..a0dc24ee8 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -5,7 +5,7 @@ FROM mcr.microsoft.com/devcontainers/ruby:1-3.3-bookworm
 # RUN gem install rails webdrivers
 
 ARG NODE_VERSION="20"
-RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"
+RUN . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1
 
 # [Optional] Uncomment this section to install additional OS packages.
 RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
@@ -15,6 +15,6 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
 RUN gem install foreman
 
 # [Optional] Uncomment this line to install global node packages.
-RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && corepack enable" 2>&1
+RUN . /usr/local/share/nvm/nvm.sh && corepack enable 2>&1
 
 COPY welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt
diff --git a/.devcontainer/codespaces/devcontainer.json b/.devcontainer/codespaces/devcontainer.json
index 6736734e6..c14d2c529 100644
--- a/.devcontainer/codespaces/devcontainer.json
+++ b/.devcontainer/codespaces/devcontainer.json
@@ -23,6 +23,8 @@
     }
   },
 
+  "remoteUser": "root",
+
   "otherPortsAttributes": {
     "onAutoForward": "silent"
   },

From 80cd001e0aa876b690c6862e2f9cbb945074f2ff Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Fri, 7 Jun 2024 16:32:29 +0200
Subject: [PATCH 048/133] Fix linting issue (#30595)

---
 config/initializers/paperclip.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb
index e9e3c78cf..070d250bf 100644
--- a/config/initializers/paperclip.rb
+++ b/config/initializers/paperclip.rb
@@ -68,7 +68,7 @@ if ENV['S3_ENABLED'] == 'true'
       http_open_timeout: ENV.fetch('S3_OPEN_TIMEOUT') { '5' }.to_i,
       http_read_timeout: ENV.fetch('S3_READ_TIMEOUT') { '5' }.to_i,
       http_idle_timeout: 5,
-      retry_limit: ENV.fetch('S3_RETRY_LIMIT'){ '0' }.to_i,
+      retry_limit: ENV.fetch('S3_RETRY_LIMIT') { '0' }.to_i,
     }
   )
 

From 82be5d033f9a5e9335d4efee6008439c7770f102 Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Fri, 7 Jun 2024 17:39:41 +0200
Subject: [PATCH 049/133] Improve handling of libvips failures (#30597)

---
 lib/paperclip/blurhash_transcoder.rb | 2 ++
 lib/paperclip/color_extractor.rb     | 2 ++
 lib/paperclip/vips_lazy_thumbnail.rb | 2 +-
 3 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/lib/paperclip/blurhash_transcoder.rb b/lib/paperclip/blurhash_transcoder.rb
index e9cecef50..150275bc9 100644
--- a/lib/paperclip/blurhash_transcoder.rb
+++ b/lib/paperclip/blurhash_transcoder.rb
@@ -12,6 +12,8 @@ module Paperclip
       attachment.instance.blurhash = Blurhash.encode(width, height, data, **(options[:blurhash] || {}))
 
       @file
+    rescue Vips::Error => e
+      raise Paperclip::Error, "Error while generating blurhash for #{@basename}: #{e}"
     end
 
     private
diff --git a/lib/paperclip/color_extractor.rb b/lib/paperclip/color_extractor.rb
index b5992f90b..0f168d233 100644
--- a/lib/paperclip/color_extractor.rb
+++ b/lib/paperclip/color_extractor.rb
@@ -69,6 +69,8 @@ module Paperclip
       attachment.instance.file.instance_write(:meta, (attachment.instance.file.instance_read(:meta) || {}).merge(meta))
 
       @file
+    rescue Vips::Error => e
+      raise Paperclip::Error, "Error while extracting colors for #{@basename}: #{e}"
     end
 
     private
diff --git a/lib/paperclip/vips_lazy_thumbnail.rb b/lib/paperclip/vips_lazy_thumbnail.rb
index 06d99bf79..4764b04af 100644
--- a/lib/paperclip/vips_lazy_thumbnail.rb
+++ b/lib/paperclip/vips_lazy_thumbnail.rb
@@ -68,7 +68,7 @@ module Paperclip
       end
 
       dst
-    rescue Terrapin::ExitStatusError => e
+    rescue Vips::Error, Terrapin::ExitStatusError => e
       raise Paperclip::Error, "Error while optimizing #{@basename}: #{e}"
     rescue Terrapin::CommandNotFoundError
       raise Paperclip::Errors::CommandNotFoundError, 'Could not run the `ffmpeg` command. Please install ffmpeg.'

From 496c10542bd39ca86a85d4de81778c134ea4383c Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Fri, 7 Jun 2024 19:42:43 +0200
Subject: [PATCH 050/133] Fix division by zero on some video/GIF files (#30600)

---
 app/lib/video_metadata_extractor.rb | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/app/lib/video_metadata_extractor.rb b/app/lib/video_metadata_extractor.rb
index df5409375..215576625 100644
--- a/app/lib/video_metadata_extractor.rb
+++ b/app/lib/video_metadata_extractor.rb
@@ -41,8 +41,8 @@ class VideoMetadataExtractor
         @colorspace  = video_stream[:pix_fmt]
         @width       = video_stream[:width]
         @height      = video_stream[:height]
-        @frame_rate  = video_stream[:avg_frame_rate] == '0/0' ? nil : Rational(video_stream[:avg_frame_rate])
-        @r_frame_rate = video_stream[:r_frame_rate] == '0/0' ? nil : Rational(video_stream[:r_frame_rate])
+        @frame_rate  = parse_framerate(video_stream[:avg_frame_rate])
+        @r_frame_rate = parse_framerate(video_stream[:r_frame_rate])
         # For some video streams the frame_rate reported by `ffprobe` will be 0/0, but for these streams we
         # should use `r_frame_rate` instead. Video screencast generated by Gnome Screencast have this issue.
         @frame_rate ||= @r_frame_rate
@@ -55,4 +55,10 @@ class VideoMetadataExtractor
 
     @invalid = true if @metadata.key?(:error)
   end
+
+  def parse_framerate(raw)
+    Rational(raw)
+  rescue ZeroDivisionError
+    nil
+  end
 end

From 521b43393350be16fbd1aa003607480c2442f3eb Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Fri, 7 Jun 2024 16:18:02 -0400
Subject: [PATCH 051/133] Doc updates for Docker/devcontainers/codespace
 (#30582)

---
 .devcontainer/Dockerfile                      | 17 +++-----
 .devcontainer/codespaces/devcontainer.json    |  2 +-
 .../{docker-compose.yml => compose.yaml}      |  0
 .devcontainer/devcontainer.json               |  2 +-
 README.md                                     | 43 +++++++++++++------
 5 files changed, 38 insertions(+), 26 deletions(-)
 rename .devcontainer/{docker-compose.yml => compose.yaml} (100%)

diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index a0dc24ee8..113dd7188 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,20 +1,17 @@
 # For details, see https://github.com/devcontainers/images/tree/main/src/ruby
 FROM mcr.microsoft.com/devcontainers/ruby:1-3.3-bookworm
 
-# Install Rails
-# RUN gem install rails webdrivers
-
+# Update existing node version, keep in sync with .nvmrc
 ARG NODE_VERSION="20"
 RUN . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1
 
-# [Optional] Uncomment this section to install additional OS packages.
-RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
-    && apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libvips42 libpam-dev
+# Install additional OS packages
+RUN apt-get update && \
+    export DEBIAN_FRONTEND=noninteractive && \
+    apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libvips42 libpam-dev
 
-# [Optional] Uncomment this line to install additional gems.
-RUN gem install foreman
-
-# [Optional] Uncomment this line to install global node packages.
+# Install global node packages
 RUN . /usr/local/share/nvm/nvm.sh && corepack enable 2>&1
 
+# Move welcome message to where VS Code expects it
 COPY welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt
diff --git a/.devcontainer/codespaces/devcontainer.json b/.devcontainer/codespaces/devcontainer.json
index c14d2c529..d2358657f 100644
--- a/.devcontainer/codespaces/devcontainer.json
+++ b/.devcontainer/codespaces/devcontainer.json
@@ -1,6 +1,6 @@
 {
   "name": "Mastodon on GitHub Codespaces",
-  "dockerComposeFile": "../docker-compose.yml",
+  "dockerComposeFile": "../compose.yaml",
   "service": "app",
   "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
 
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/compose.yaml
similarity index 100%
rename from .devcontainer/docker-compose.yml
rename to .devcontainer/compose.yaml
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 4a9cf11cc..fb88f7801 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,6 +1,6 @@
 {
   "name": "Mastodon on local machine",
-  "dockerComposeFile": "docker-compose.yml",
+  "dockerComposeFile": "compose.yaml",
   "service": "app",
   "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
 
diff --git a/README.md b/README.md
index 3773b647f..f807120c1 100644
--- a/README.md
+++ b/README.md
@@ -102,26 +102,35 @@ To set up **MacOS** for native development, complete the following steps:
 ### Docker
 
 For production hosting and deployment with **Docker**, use the `Dockerfile` and
-`docker-compose.yml` in the project root directory. To create a local
-development environment with **Docker**, complete the following steps:
+`docker-compose.yml` in the project root directory.
 
-- Install Docker Desktop
-- Run `docker compose -f .devcontainer/docker-compose.yml up -d`
-- Run `docker compose -f .devcontainer/docker-compose.yml exec app bin/setup`
-- Finally, run `docker compose -f .devcontainer/docker-compose.yml exec app bin/dev`
+For local development, install and launch [Docker], and run:
 
-If you are using an IDE with [support for the Development Container specification](https://containers.dev/supporting), it will run the above `docker compose` commands automatically. For **Visual Studio Code** this requires the [Dev Container extension](https://containers.dev/supporting#dev-containers).
+```shell
+docker compose -f .devcontainer/compose.yaml up -d
+docker compose -f .devcontainer/compose.yaml exec app bin/setup
+docker compose -f .devcontainer/compose.yaml exec app bin/dev
+```
+
+### Dev Containers
+
+Within IDEs that support the [Development Containers] specification, start the
+"Mastodon on local machine" container from the editor. The necessary `docker
+compose` commands to build and setup the container should run automatically. For
+**Visual Studio Code** this requires installing the [Dev Container extension].
 
 ### GitHub Codespaces
 
-To get you coding in just a few minutes, GitHub Codespaces provides a web-based version of Visual Studio Code and a cloud-hosted development environment fully configured with the software needed for this project..
+[GitHub Codespaces] provides a web-based version of VS Code and a cloud hosted
+development environment configured with the software needed for this project.
 
-- Click this button to create a new codespace:<br>
-  [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=52281283&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json)
-- Wait for the environment to build. This will take a few minutes.
-- When the editor is ready, run `bin/dev` in the terminal.
-- After a few seconds, a popup will appear with a button labeled _Open in Browser_. This will open Mastodon.
-- On the _Ports_ tab, right click on the “stream” row and select _Port visibility_ → _Public_.
+[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)][codespace]
+
+- Click the button to create a new codespace, and confirm the options
+- Wait for the environment to build (takes a few minutes)
+- When the editor is ready, run `bin/dev` in the terminal
+- Wait for an _Open in Browser_ prompt. This will open Mastodon
+- On the _Ports_ tab "stream" setting change _Port visibility_ → _Public_
 
 ## Contributing
 
@@ -140,3 +149,9 @@ This program is free software: you can redistribute it and/or modify it under th
 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
 
 You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+[codespace]: https://codespaces.new/mastodon/mastodon?quickstart=1&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json
+[Dev Container extension]: https://containers.dev/supporting#dev-containers
+[Development Containers]: https://containers.dev/supporting
+[Docker]: https://docs.docker.com
+[GitHub Codespaces]: https://docs.github.com/en/codespaces

From c1a84f1b5b85e98c78847ddf03d1490300bcdc9d Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Sat, 8 Jun 2024 06:32:39 -0400
Subject: [PATCH 052/133] Case correction `Github` -> `GitHub` (#30446)

Co-authored-by: Igor <igordsmelo@gmail.com>
---
 .github/codecov.yml                         | 4 ++--
 .github/renovate.json5                      | 2 +-
 .github/workflows/build-container-image.yml | 2 +-
 .github/workflows/crowdin-download.yml      | 4 ++--
 SECURITY.md                                 | 2 +-
 crowdin.yml                                 | 2 +-
 6 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/.github/codecov.yml b/.github/codecov.yml
index 9d6413a10..701ba3af8 100644
--- a/.github/codecov.yml
+++ b/.github/codecov.yml
@@ -3,9 +3,9 @@ coverage:
   status:
     project:
       default:
-        # Github status check is not blocking
+        # GitHub status check is not blocking
         informational: true
     patch:
       default:
-        # Github status check is not blocking
+        # GitHub status check is not blocking
         informational: true
diff --git a/.github/renovate.json5 b/.github/renovate.json5
index 378d4fc83..52f7c63e5 100644
--- a/.github/renovate.json5
+++ b/.github/renovate.json5
@@ -59,7 +59,7 @@
       dependencyDashboardApproval: true,
     },
     {
-      // Update Github Actions and Docker images weekly
+      // Update GitHub Actions and Docker images weekly
       matchManagers: ['github-actions', 'dockerfile', 'docker-compose'],
       extends: ['schedule:weekly'],
     },
diff --git a/.github/workflows/build-container-image.yml b/.github/workflows/build-container-image.yml
index e100e1582..dbb32af9b 100644
--- a/.github/workflows/build-container-image.yml
+++ b/.github/workflows/build-container-image.yml
@@ -68,7 +68,7 @@ jobs:
           username: ${{ secrets.DOCKERHUB_USERNAME }}
           password: ${{ secrets.DOCKERHUB_TOKEN }}
 
-      - name: Log in to the Github Container registry
+      - name: Log in to the GitHub Container registry
         if: contains(inputs.push_to_images, 'ghcr.io')
         uses: docker/login-action@v3
         with:
diff --git a/.github/workflows/crowdin-download.yml b/.github/workflows/crowdin-download.yml
index 1df7672d6..e9da7cb26 100644
--- a/.github/workflows/crowdin-download.yml
+++ b/.github/workflows/crowdin-download.yml
@@ -58,13 +58,13 @@ jobs:
           title: 'New Crowdin Translations (automated)'
           author: 'GitHub Actions <noreply@github.com>'
           body: |
-            New Crowdin translations, automated with Github Actions
+            New Crowdin translations, automated with GitHub Actions
 
             See `.github/workflows/crowdin-download.yml`
 
             This PR will be updated every day with new translations.
 
-            Due to a limitation in Github Actions, checks are not running on this PR without manual action.
+            Due to a limitation in GitHub Actions, checks are not running on this PR without manual action.
             If you want to run the checks, then close and re-open it.
           branch: i18n/crowdin/translations
           base: main
diff --git a/SECURITY.md b/SECURITY.md
index 81472b01b..156954ce0 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,7 +2,7 @@
 
 If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you can either:
 
-- open a [Github security issue on the Mastodon project](https://github.com/mastodon/mastodon/security/advisories/new)
+- open a [GitHub security issue on the Mastodon project](https://github.com/mastodon/mastodon/security/advisories/new)
 - reach us at <security@joinmastodon.org>
 
 You should _not_ report such issues on public GitHub issues or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk.
diff --git a/crowdin.yml b/crowdin.yml
index d05b0e69f..991c5b825 100644
--- a/crowdin.yml
+++ b/crowdin.yml
@@ -1,4 +1,4 @@
-# This is needed for the Github Action
+# This is needed for the GitHub Action
 project_id_env: CROWDIN_PROJECT_ID
 api_token_env: CROWDIN_PERSONAL_TOKEN
 

From 6952af137d17f65e65963e4d9ba129060fad9d3d Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Sat, 8 Jun 2024 06:33:28 -0400
Subject: [PATCH 053/133] Install from nvmrc during devcontainer Dockerfile
 build (#30603)

---
 .devcontainer/Dockerfile   | 12 +++++-------
 .devcontainer/compose.yaml |  4 ++--
 bin/setup                  |  2 +-
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 113dd7188..c6dcc4d46 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,17 +1,15 @@
 # For details, see https://github.com/devcontainers/images/tree/main/src/ruby
 FROM mcr.microsoft.com/devcontainers/ruby:1-3.3-bookworm
 
-# Update existing node version, keep in sync with .nvmrc
-ARG NODE_VERSION="20"
-RUN . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1
+# Install node version from .nvmrc
+WORKDIR /app
+COPY .nvmrc .
+RUN /bin/bash --login -i -c "nvm install"
 
 # Install additional OS packages
 RUN apt-get update && \
     export DEBIAN_FRONTEND=noninteractive && \
     apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libvips42 libpam-dev
 
-# Install global node packages
-RUN . /usr/local/share/nvm/nvm.sh && corepack enable 2>&1
-
 # Move welcome message to where VS Code expects it
-COPY welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt
+COPY .devcontainer/welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt
diff --git a/.devcontainer/compose.yaml b/.devcontainer/compose.yaml
index 4d6eb3c48..1e2e1ba7d 100644
--- a/.devcontainer/compose.yaml
+++ b/.devcontainer/compose.yaml
@@ -2,8 +2,8 @@ services:
   app:
     working_dir: /workspaces/mastodon/
     build:
-      context: .
-      dockerfile: Dockerfile
+      context: ..
+      dockerfile: .devcontainer/Dockerfile
     volumes:
       - ..:/workspaces/mastodon:cached
     environment:
diff --git a/bin/setup b/bin/setup
index 93c6981d0..4ccf4594b 100755
--- a/bin/setup
+++ b/bin/setup
@@ -18,7 +18,7 @@ FileUtils.chdir APP_ROOT do
   system('bundle check') || system!('bundle install')
 
   puts "\n== Installing JS dependencies =="
-  system! 'corepack prepare'
+  system! 'corepack enable'
   system! 'bin/yarn install --immutable'
 
   puts "\n== Preparing database =="

From 827e36ff9ee22ec58c64322c0a9d78eaa2dd4875 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Sat, 8 Jun 2024 13:10:06 -0400
Subject: [PATCH 054/133] Fix `Capybara/NegationMatcher` cop in spec/system
 (#30616)

---
 spec/system/filters_spec.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/spec/system/filters_spec.rb b/spec/system/filters_spec.rb
index 9d18e9046..a0cb965a6 100644
--- a/spec/system/filters_spec.rb
+++ b/spec/system/filters_spec.rb
@@ -46,7 +46,7 @@ describe 'Filters' do
         click_on I18n.t('filters.index.delete')
       end.to change(CustomFilter, :count).by(-1)
 
-      expect(page).to_not have_content(filter_title)
+      expect(page).to have_no_content(filter_title)
     end
   end
 

From 0cf91213c93e87ce775453ae20a3d9a7f9b4e8d0 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Mon, 10 Jun 2024 02:32:20 -0400
Subject: [PATCH 055/133] Opt in to remaining Rails 7.1 defaults (#30332)

Co-authored-by: Claire <claire.github-309c@sitedethib.com>
---
 config/application.rb                         |   5 +-
 .../initializers/active_record_encryption.rb  |   5 +
 .../new_framework_defaults_7_1.rb             | 214 ------------------
 3 files changed, 8 insertions(+), 216 deletions(-)
 delete mode 100644 config/initializers/new_framework_defaults_7_1.rb

diff --git a/config/application.rb b/config/application.rb
index 069eb3774..b3a9b99ff 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -60,9 +60,10 @@ Bundler.require(:pam_authentication) if ENV['PAM_ENABLED'] == 'true'
 module Mastodon
   class Application < Rails::Application
     # Initialize configuration defaults for originally generated Rails version.
-    config.load_defaults 7.0
+    config.load_defaults 7.1
 
-    config.active_record.marshalling_format_version = 7.1
+    # Explicitly set the cache format version to align with Rails version
+    config.active_support.cache_format_version = 7.1
 
     # Please, add to the `ignore` list any other `lib` subdirectories that do
     # not contain `.rb` files, or that should not be reloaded or eager loaded.
diff --git a/config/initializers/active_record_encryption.rb b/config/initializers/active_record_encryption.rb
index 777bafc27..900f3c68f 100644
--- a/config/initializers/active_record_encryption.rb
+++ b/config/initializers/active_record_encryption.rb
@@ -32,4 +32,9 @@ Rails.application.configure do
   config.active_record.encryption.deterministic_key = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY')
   config.active_record.encryption.key_derivation_salt = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT')
   config.active_record.encryption.primary_key = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY')
+  config.active_record.encryption.support_sha1_for_non_deterministic_encryption = true
+
+  # TODO: https://github.com/rails/rails/issues/50604#issuecomment-1880990392
+  # Remove after updating to Rails 7.1.4
+  ActiveRecord::Encryption.configure(**config.active_record.encryption)
 end
diff --git a/config/initializers/new_framework_defaults_7_1.rb b/config/initializers/new_framework_defaults_7_1.rb
deleted file mode 100644
index bcc300c89..000000000
--- a/config/initializers/new_framework_defaults_7_1.rb
+++ /dev/null
@@ -1,214 +0,0 @@
-# frozen_string_literal: true
-
-# Be sure to restart your server when you modify this file.
-#
-# This file eases your Rails 7.1 framework defaults upgrade.
-#
-# Uncomment each configuration one by one to switch to the new default.
-# Once your application is ready to run with all new defaults, you can remove
-# this file and set the `config.load_defaults` to `7.1`.
-#
-# Read the Guide for Upgrading Ruby on Rails for more info on each option.
-# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html
-
-# No longer add autoloaded paths into `$LOAD_PATH`. This means that you won't be able
-# to manually require files that are managed by the autoloader, which you shouldn't do anyway.
-# This will reduce the size of the load path, making `require` faster if you don't use bootsnap, or reduce the size
-# of the bootsnap cache if you use it.
-Rails.application.config.add_autoload_paths_to_load_path = false
-
-# Remove the default X-Download-Options headers since it is used only by Internet Explorer.
-# If you need to support Internet Explorer, add back `"X-Download-Options" => "noopen"`.
-# Rails.application.config.action_dispatch.default_headers = {
-#   "X-Frame-Options" => "SAMEORIGIN",
-#   "X-XSS-Protection" => "0",
-#   "X-Content-Type-Options" => "nosniff",
-#   "X-Permitted-Cross-Domain-Policies" => "none",
-#   "Referrer-Policy" => "strict-origin-when-cross-origin"
-# }
-
-# Do not treat an `ActionController::Parameters` instance
-# as equal to an equivalent `Hash` by default.
-Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality = false
-
-# Active Record Encryption now uses SHA-256 as its hash digest algorithm. Important: If you have
-# data encrypted with previous Rails versions, there are two scenarios to consider:
-#
-# 1. If you have +config.active_support.key_generator_hash_digest_class+ configured as SHA1 (the default
-# before Rails 7.0), you need to configure SHA-1 for Active Record Encryption too:
-# Rails.application.config.active_record.encryption.hash_digest_class = OpenSSL::Digest::SHA1
-# 2. If you have +config.active_support.key_generator_hash_digest_class+ configured as SHA256 (the new default
-# in 7.0), then you need to configure SHA-256 for Active Record Encryption:
-# Rails.application.config.active_record.encryption.hash_digest_class = OpenSSL::Digest::SHA256
-#
-# If you don't currently have data encrypted with Active Record encryption, you can disable this setting to
-# configure the default behavior starting 7.1+:
-# Rails.application.config.active_record.encryption.support_sha1_for_non_deterministic_encryption = false
-
-# No longer run after_commit callbacks on the first of multiple Active Record
-# instances to save changes to the same database row within a transaction.
-# Instead, run these callbacks on the instance most likely to have internal
-# state which matches what was committed to the database, typically the last
-# instance to save.
-Rails.application.config.active_record.run_commit_callbacks_on_first_saved_instances_in_transaction = false
-
-# Configures SQLite with a strict strings mode, which disables double-quoted string literals.
-#
-# SQLite has some quirks around double-quoted string literals.
-# It first tries to consider double-quoted strings as identifier names, but if they don't exist
-# it then considers them as string literals. Because of this, typos can silently go unnoticed.
-# For example, it is possible to create an index for a non existing column.
-# See https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted for more details.
-Rails.application.config.active_record.sqlite3_adapter_strict_strings_by_default = true
-
-# Disable deprecated singular associations names
-Rails.application.config.active_record.allow_deprecated_singular_associations_name = false
-
-# Enable the Active Job `BigDecimal` argument serializer, which guarantees
-# roundtripping. Without this serializer, some queue adapters may serialize
-# `BigDecimal` arguments as simple (non-roundtrippable) strings.
-#
-# When deploying an application with multiple replicas, old (pre-Rails 7.1)
-# replicas will not be able to deserialize `BigDecimal` arguments from this
-# serializer. Therefore, this setting should only be enabled after all replicas
-# have been successfully upgraded to Rails 7.1.
-# Rails.application.config.active_job.use_big_decimal_serializer = true
-
-# Specify if an `ArgumentError` should be raised if `Rails.cache` `fetch` or
-# `write` are given an invalid `expires_at` or `expires_in` time.
-# Options are `true`, and `false`. If `false`, the exception will be reported
-# as `handled` and logged instead.
-Rails.application.config.active_support.raise_on_invalid_cache_expiration_time = true
-
-# Specify whether Query Logs will format tags using the SQLCommenter format
-# (https://open-telemetry.github.io/opentelemetry-sqlcommenter/), or using the legacy format.
-# Options are `:legacy` and `:sqlcommenter`.
-Rails.application.config.active_record.query_log_tags_format = :sqlcommenter
-
-# Specify the default serializer used by `MessageEncryptor` and `MessageVerifier`
-# instances.
-#
-# The legacy default is `:marshal`, which is a potential vector for
-# deserialization attacks in cases where a message signing secret has been
-# leaked.
-#
-# In Rails 7.1, the new default is `:json_allow_marshal` which serializes and
-# deserializes with `ActiveSupport::JSON`, but can fall back to deserializing
-# with `Marshal` so that legacy messages can still be read.
-#
-# In Rails 7.2, the default will become `:json` which serializes and
-# deserializes with `ActiveSupport::JSON` only.
-#
-# Alternatively, you can choose `:message_pack` or `:message_pack_allow_marshal`,
-# which serialize with `ActiveSupport::MessagePack`. `ActiveSupport::MessagePack`
-# can roundtrip some Ruby types that are not supported by JSON, and may provide
-# improved performance, but it requires the `msgpack` gem.
-#
-# For more information, see
-# https://guides.rubyonrails.org/v7.1/configuring.html#config-active-support-message-serializer
-#
-# If you are performing a rolling deploy of a Rails 7.1 upgrade, wherein servers
-# that have not yet been upgraded must be able to read messages from upgraded
-# servers, first deploy without changing the serializer, then set the serializer
-# in a subsequent deploy.
-# Rails.application.config.active_support.message_serializer = :json_allow_marshal
-
-# Enable a performance optimization that serializes message data and metadata
-# together. This changes the message format, so messages serialized this way
-# cannot be read by older versions of Rails. However, messages that use the old
-# format can still be read, regardless of whether this optimization is enabled.
-#
-# To perform a rolling deploy of a Rails 7.1 upgrade, wherein servers that have
-# not yet been upgraded must be able to read messages from upgraded servers,
-# leave this optimization off on the first deploy, then enable it on a
-# subsequent deploy.
-# Rails.application.config.active_support.use_message_serializer_for_metadata = true
-
-# Set the maximum size for Rails log files.
-#
-# `config.load_defaults 7.1` does not set this value for environments other than
-# development and test.
-#
-Rails.application.config.log_file_size = 100 * 1024 * 1024 if Rails.env.local?
-
-# Enable raising on assignment to attr_readonly attributes. The previous
-# behavior would allow assignment but silently not persist changes to the
-# database.
-Rails.application.config.active_record.raise_on_assign_to_attr_readonly = true
-
-# Enable validating only parent-related columns for presence when the parent is mandatory.
-# The previous behavior was to validate the presence of the parent record, which performed an extra query
-# to get the parent every time the child record was updated, even when parent has not changed.
-Rails.application.config.active_record.belongs_to_required_validates_foreign_key = false
-
-# Enable precompilation of `config.filter_parameters`. Precompilation can
-# improve filtering performance, depending on the quantity and types of filters.
-Rails.application.config.precompile_filter_parameters = true
-
-# Enable before_committed! callbacks on all enrolled records in a transaction.
-# The previous behavior was to only run the callbacks on the first copy of a record
-# if there were multiple copies of the same record enrolled in the transaction.
-Rails.application.config.active_record.before_committed_on_all_records = true
-
-# Disable automatic column serialization into YAML.
-# To keep the historic behavior, you can set it to `YAML`, however it is
-# recommended to explicitly define the serialization method for each column
-# rather than to rely on a global default.
-Rails.application.config.active_record.default_column_serializer = nil
-
-# Run `after_commit` and `after_*_commit` callbacks in the order they are defined in a model.
-# This matches the behaviour of all other callbacks.
-# In previous versions of Rails, they ran in the inverse order.
-Rails.application.config.active_record.run_after_transaction_callbacks_in_order_defined = true
-
-# Whether a `transaction` block is committed or rolled back when exited via `return`, `break` or `throw`.
-#
-# Rails.application.config.active_record.commit_transaction_on_non_local_return = true
-
-# Controls when to generate a value for <tt>has_secure_token</tt> declarations.
-#
-Rails.application.config.active_record.generate_secure_token_on = :initialize
-
-# ** Please read carefully, this must be configured in config/application.rb **
-# Change the format of the cache entry.
-# Changing this default means that all new cache entries added to the cache
-# will have a different format that is not supported by Rails 7.0
-# applications.
-# Only change this value after your application is fully deployed to Rails 7.1
-# and you have no plans to rollback.
-# When you're ready to change format, add this to `config/application.rb` (NOT
-# this file):
-#   config.active_support.cache_format_version = 7.1
-
-# Configure Action View to use HTML5 standards-compliant sanitizers when they are supported on your
-# platform.
-#
-# `Rails::HTML::Sanitizer.best_supported_vendor` will cause Action View to use HTML5-compliant
-# sanitizers if they are supported, else fall back to HTML4 sanitizers.
-#
-# In previous versions of Rails, Action View always used `Rails::HTML4::Sanitizer` as its vendor.
-#
-Rails.application.config.action_view.sanitizer_vendor = Rails::HTML::Sanitizer.best_supported_vendor
-
-# Configure Action Text to use an HTML5 standards-compliant sanitizer when it is supported on your
-# platform.
-#
-# `Rails::HTML::Sanitizer.best_supported_vendor` will cause Action Text to use HTML5-compliant
-# sanitizers if they are supported, else fall back to HTML4 sanitizers.
-#
-# In previous versions of Rails, Action Text always used `Rails::HTML4::Sanitizer` as its vendor.
-#
-# Rails.application.config.action_text.sanitizer_vendor = Rails::HTML::Sanitizer.best_supported_vendor
-
-# Configure the log level used by the DebugExceptions middleware when logging
-# uncaught exceptions during requests
-# Rails.application.config.action_dispatch.debug_exception_log_level = :error
-
-# Configure the test helpers in Action View, Action Dispatch, and rails-dom-testing to use HTML5
-# parsers.
-#
-# Nokogiri::HTML5 isn't supported on JRuby, so JRuby applications must set this to :html4.
-#
-# In previous versions of Rails, these test helpers always used an HTML4 parser.
-#
-Rails.application.config.dom_testing_default_html_version = :html5

From 77c2216e47c8ffba4dde91c1a300cf924a8e5c05 Mon Sep 17 00:00:00 2001
From: Daniel M Brasil <danielmbrasil@protonmail.com>
Date: Mon, 10 Jun 2024 10:33:48 -0300
Subject: [PATCH 056/133] fix: Return HTTP 422 when scheduled status time is
 less than 5 minutes (#30584)

---
 app/services/post_status_service.rb       |  2 +-
 spec/requests/api/v1/statuses_spec.rb     | 26 +++++++++++++++++++++++
 spec/services/post_status_service_spec.rb | 10 +++++++++
 3 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb
index 83a931817..8b18ce038 100644
--- a/app/services/post_status_service.rb
+++ b/app/services/post_status_service.rb
@@ -171,7 +171,7 @@ class PostStatusService < BaseService
   end
 
   def scheduled_in_the_past?
-    @scheduled_at.present? && @scheduled_at <= Time.now.utc + MIN_SCHEDULE_OFFSET
+    @scheduled_at.present? && @scheduled_at <= Time.now.utc
   end
 
   def bump_potential_friendship!
diff --git a/spec/requests/api/v1/statuses_spec.rb b/spec/requests/api/v1/statuses_spec.rb
index 694861fb1..2f99b35e7 100644
--- a/spec/requests/api/v1/statuses_spec.rb
+++ b/spec/requests/api/v1/statuses_spec.rb
@@ -193,6 +193,32 @@ describe '/api/v1/statuses' do
           expect(response).to have_http_status(404)
         end
       end
+
+      context 'when scheduling a status' do
+        let(:params) { { status: 'Hello world', scheduled_at: 10.minutes.from_now } }
+        let(:account) { user.account }
+
+        it 'returns HTTP 200' do
+          subject
+
+          expect(response).to have_http_status(200)
+        end
+
+        it 'creates a scheduled status' do
+          expect { subject }.to change { account.scheduled_statuses.count }.from(0).to(1)
+        end
+
+        context 'when the scheduling time is less than 5 minutes' do
+          let(:params) { { status: 'Hello world', scheduled_at: 4.minutes.from_now } }
+
+          it 'does not create a scheduled status', :aggregate_failures do
+            subject
+
+            expect(response).to have_http_status(422)
+            expect(account.scheduled_statuses).to be_empty
+          end
+        end
+      end
     end
 
     describe 'DELETE /api/v1/statuses/:id' do
diff --git a/spec/services/post_status_service_spec.rb b/spec/services/post_status_service_spec.rb
index 11bf4c30e..f21548b5f 100644
--- a/spec/services/post_status_service_spec.rb
+++ b/spec/services/post_status_service_spec.rb
@@ -61,6 +61,16 @@ RSpec.describe PostStatusService do
       status2 = subject.call(account, text: 'test', idempotency: 'meepmeep', scheduled_at: future)
       expect(status2.id).to eq status1.id
     end
+
+    context 'when scheduled_at is less than min offset' do
+      let(:invalid_scheduled_time) { 4.minutes.from_now }
+
+      it 'raises invalid record error' do
+        expect do
+          subject.call(account, text: 'Hi future!', scheduled_at: invalid_scheduled_time)
+        end.to raise_error(ActiveRecord::RecordInvalid)
+      end
+    end
   end
 
   it 'creates response to the original status of boost' do

From 801f9feec3d23be013751a3c468356a1c3c6ddf7 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 10 Jun 2024 15:52:57 +0200
Subject: [PATCH 057/133] chore(deps): update dependency concurrent-ruby to
 v1.3.3 (#30605)

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 8ff990260..93877e15d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -168,7 +168,7 @@ GEM
     climate_control (1.2.0)
     cocoon (1.2.15)
     color_diff (0.1)
-    concurrent-ruby (1.3.1)
+    concurrent-ruby (1.3.3)
     connection_pool (2.4.1)
     cose (1.3.0)
       cbor (~> 0.5.9)

From bfd674d819327b4af3053ec35aea96f0c9e36428 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 10 Jun 2024 16:04:44 +0200
Subject: [PATCH 058/133] chore(deps): update dependency httplog to '~> 1.7.0'
 (#30613)

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 136d074d3..be02a6562 100644
--- a/Gemfile
+++ b/Gemfile
@@ -57,7 +57,7 @@ gem 'hiredis', '~> 0.6'
 gem 'htmlentities', '~> 4.3'
 gem 'http', '~> 5.2.0'
 gem 'http_accept_language', '~> 2.1'
-gem 'httplog', '~> 1.6.2'
+gem 'httplog', '~> 1.7.0'
 gem 'i18n'
 gem 'idn-ruby', require: 'idn'
 gem 'inline_svg'
diff --git a/Gemfile.lock b/Gemfile.lock
index 93877e15d..812e23a85 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -321,7 +321,7 @@ GEM
     http-form_data (2.3.0)
     http_accept_language (2.1.1)
     httpclient (2.8.3)
-    httplog (1.6.3)
+    httplog (1.7.0)
       rack (>= 2.0)
       rainbow (>= 2.0.0)
     i18n (1.14.5)
@@ -947,7 +947,7 @@ DEPENDENCIES
   htmlentities (~> 4.3)
   http (~> 5.2.0)
   http_accept_language (~> 2.1)
-  httplog (~> 1.6.2)
+  httplog (~> 1.7.0)
   i18n
   i18n-tasks (~> 1.0)
   idn-ruby

From 2dca6ec77ad8be8674cde103578c416095a3522b Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 10 Jun 2024 16:05:57 +0200
Subject: [PATCH 059/133] chore(deps): update dependency oj to v3.16.4 (#30620)

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 812e23a85..095433981 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -453,7 +453,7 @@ GEM
       concurrent-ruby (~> 1.0, >= 1.0.2)
       sidekiq (>= 3.5)
       statsd-ruby (~> 1.4, >= 1.4.0)
-    oj (3.16.3)
+    oj (3.16.4)
       bigdecimal (>= 3.0)
     omniauth (2.1.2)
       hashie (>= 3.4.6)

From 452872412dd5a3af0ca9186726bf5a5fb8038856 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 10 Jun 2024 16:07:35 +0200
Subject: [PATCH 060/133] chore(deps): update dependency @types/lodash to
 v4.17.5 (#30627)

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 dcc17d659..e3d3bebeb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3712,9 +3712,9 @@ __metadata:
   linkType: hard
 
 "@types/lodash@npm:^4.14.195":
-  version: 4.17.4
-  resolution: "@types/lodash@npm:4.17.4"
-  checksum: 10c0/0124c64cb9fe7a0f78b6777955abd05ef0d97844d49118652eae45f8fa57bfb7f5a7a9bccc0b5a84c0a6dc09631042e4590cb665acb9d58dfd5e6543c75341ec
+  version: 4.17.5
+  resolution: "@types/lodash@npm:4.17.5"
+  checksum: 10c0/55924803ed853e72261512bd3eaf2c5b16558c3817feb0a3125ef757afe46e54b86f33d1960e40b7606c0ddab91a96f47966bf5e6006b7abfd8994c13b04b19b
   languageName: node
   linkType: hard
 

From dd4a976122ac1da57ac011f84d71e168ad911cfe Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 10 Jun 2024 16:08:02 +0200
Subject: [PATCH 061/133] chore(deps): update devdependencies (non-major)
 (#30628)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 yarn.lock | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/yarn.lock b/yarn.lock
index e3d3bebeb..8a1f96b69 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -13062,8 +13062,8 @@ __metadata:
   linkType: hard
 
 "pino-pretty@npm:^11.0.0":
-  version: 11.1.0
-  resolution: "pino-pretty@npm:11.1.0"
+  version: 11.2.0
+  resolution: "pino-pretty@npm:11.2.0"
   dependencies:
     colorette: "npm:^2.0.7"
     dateformat: "npm:^4.6.3"
@@ -13081,7 +13081,7 @@ __metadata:
     strip-json-comments: "npm:^3.1.1"
   bin:
     pino-pretty: bin.js
-  checksum: 10c0/418be6f854b0d62c83c65e75b0969d5311792bfadeefbfe77d8a7f8c5ba26b8bea40f549222b5f500439f440eb4d6c2fa99d712bdd02881ebae7be3a0193b581
+  checksum: 10c0/59421522c0e07877614ed8b51eb45fe79aad9865244b95dfaf5e28c83f9e95631941b5b9e37a277d1751ed90903d7593915e8a8857cc856b4af7e6bf20a5f97d
   languageName: node
   linkType: hard
 
@@ -14022,11 +14022,11 @@ __metadata:
   linkType: hard
 
 "prettier@npm:^3.0.0":
-  version: 3.3.0
-  resolution: "prettier@npm:3.3.0"
+  version: 3.3.1
+  resolution: "prettier@npm:3.3.1"
   bin:
     prettier: bin/prettier.cjs
-  checksum: 10c0/d033c356320aa2e468bf29c931b094ac730d2f4defd5eb2989d8589313dec901d2fc866e3788f3d161e420b142ea4ec3dda535dbe0169ef4d0026397a68ba9cf
+  checksum: 10c0/c25a709c9f0be670dc6bcb190b622347e1dbeb6c3e7df8b0711724cb64d8647c60b839937a4df4df18e9cfb556c2b08ca9d24d9645eb5488a7fc032a2c4d5cb3
   languageName: node
   linkType: hard
 

From a6e0e167a71025b27719666906cfb5511734134e Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 10 Jun 2024 16:29:11 +0200
Subject: [PATCH 062/133] fix(deps): update dependency uuid to v10 (#30624)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 streaming/package.json |  2 +-
 yarn.lock              | 20 ++++++++++----------
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/streaming/package.json b/streaming/package.json
index cf1fe4ba6..2e515167c 100644
--- a/streaming/package.json
+++ b/streaming/package.json
@@ -27,7 +27,7 @@
     "pino": "^9.0.0",
     "pino-http": "^10.0.0",
     "prom-client": "^15.0.0",
-    "uuid": "^9.0.0",
+    "uuid": "^10.0.0",
     "ws": "^8.12.1"
   },
   "devDependencies": {
diff --git a/yarn.lock b/yarn.lock
index 8a1f96b69..33cd7b481 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2942,7 +2942,7 @@ __metadata:
     prom-client: "npm:^15.0.0"
     typescript: "npm:^5.0.4"
     utf-8-validate: "npm:^6.0.3"
-    uuid: "npm:^9.0.0"
+    uuid: "npm:^10.0.0"
     ws: "npm:^8.12.1"
   dependenciesMeta:
     bufferutil:
@@ -17489,6 +17489,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"uuid@npm:^10.0.0":
+  version: 10.0.0
+  resolution: "uuid@npm:10.0.0"
+  bin:
+    uuid: dist/bin/uuid
+  checksum: 10c0/eab18c27fe4ab9fb9709a5d5f40119b45f2ec8314f8d4cf12ce27e4c6f4ffa4a6321dc7db6c515068fa373c075b49691ba969f0010bf37f44c37ca40cd6bf7fe
+  languageName: node
+  linkType: hard
+
 "uuid@npm:^3.3.2":
   version: 3.4.0
   resolution: "uuid@npm:3.4.0"
@@ -17507,15 +17516,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"uuid@npm:^9.0.0":
-  version: 9.0.1
-  resolution: "uuid@npm:9.0.1"
-  bin:
-    uuid: dist/bin/uuid
-  checksum: 10c0/1607dd32ac7fc22f2d8f77051e6a64845c9bce5cd3dd8aa0070c074ec73e666a1f63c7b4e0f4bf2bc8b9d59dc85a15e17807446d9d2b17c8485fbc2147b27f9b
-  languageName: node
-  linkType: hard
-
 "v8-compile-cache@npm:^2.1.1":
   version: 2.3.0
   resolution: "v8-compile-cache@npm:2.3.0"

From 589365ba52b5e982f31c7acbb0a264bc378e061b Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 10 Jun 2024 16:29:26 +0200
Subject: [PATCH 063/133] chore(deps): update dependency rubocop-capybara to
 v2.21.0 (#30611)

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 095433981..4d16fea47 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -578,7 +578,7 @@ GEM
       opentelemetry-api (~> 1.0)
     orm_adapter (0.5.0)
     ox (2.14.18)
-    parallel (1.24.0)
+    parallel (1.25.1)
     parser (3.3.2.0)
       ast (~> 2.4.1)
       racc
@@ -739,7 +739,7 @@ GEM
       unicode-display_width (>= 2.4.0, < 3.0)
     rubocop-ast (1.31.3)
       parser (>= 3.3.1.0)
-    rubocop-capybara (2.20.0)
+    rubocop-capybara (2.21.0)
       rubocop (~> 1.41)
     rubocop-factory_bot (2.25.1)
       rubocop (~> 1.41)

From bb27321781778d4d849dd0a4c2f16747f85f1a3d Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 10 Jun 2024 16:29:30 +0200
Subject: [PATCH 064/133] New Crowdin Translations (automated) (#30608)

Co-authored-by: GitHub Actions <noreply@github.com>
---
 app/javascript/mastodon/locales/eo.json    |   7 ++
 app/javascript/mastodon/locales/fr-CA.json |   6 +-
 app/javascript/mastodon/locales/fr.json    |   6 +-
 app/javascript/mastodon/locales/ia.json    |   4 +-
 app/javascript/mastodon/locales/id.json    |  10 ++
 app/javascript/mastodon/locales/la.json    | 125 +++++++++++++++++++--
 config/locales/devise.la.yml               |   7 ++
 config/locales/doorkeeper.ja.yml           |   2 +
 config/locales/doorkeeper.lad.yml          |   2 +
 config/locales/doorkeeper.lt.yml           |   2 +
 config/locales/doorkeeper.sv.yml           |   1 +
 config/locales/doorkeeper.th.yml           |   2 +
 config/locales/doorkeeper.vi.yml           |   2 +
 config/locales/id.yml                      |   6 +
 config/locales/la.yml                      |  33 ++++++
 config/locales/nl.yml                      |   4 +-
 config/locales/simple_form.id.yml          |   3 +
 config/locales/th.yml                      |   4 +-
 18 files changed, 204 insertions(+), 22 deletions(-)

diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index 6e7885f48..2dbbf7877 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -498,7 +498,14 @@
   "poll_button.add_poll": "Aldoni balotenketon",
   "poll_button.remove_poll": "Forigi balotenketon",
   "privacy.change": "Agordi mesaĝan privatecon",
+  "privacy.direct.long": "Ĉiuj menciitaj en la afiŝo",
+  "privacy.direct.short": "Specifaj homoj",
+  "privacy.private.long": "Nur viaj sekvantoj",
+  "privacy.private.short": "Sekvantoj",
+  "privacy.public.long": "Ĉiujn ajn ĉe kaj ekster Mastodon",
   "privacy.public.short": "Publika",
+  "privacy.unlisted.long": "Malpli algoritmaj fanfaroj",
+  "privacy.unlisted.short": "Diskrete publika",
   "privacy_policy.last_updated": "Laste ĝisdatigita en {date}",
   "privacy_policy.title": "Politiko de privateco",
   "recommended": "Rekomendita",
diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json
index 9e2985290..9c14d05d5 100644
--- a/app/javascript/mastodon/locales/fr-CA.json
+++ b/app/javascript/mastodon/locales/fr-CA.json
@@ -156,7 +156,7 @@
   "compose_form.poll.duration": "Durée du sondage",
   "compose_form.poll.multiple": "Choix multiple",
   "compose_form.poll.option_placeholder": "Option {number}",
-  "compose_form.poll.single": "Choisissez-en un",
+  "compose_form.poll.single": "Choix unique",
   "compose_form.poll.switch_to_multiple": "Changer le sondage pour autoriser plusieurs choix",
   "compose_form.poll.switch_to_single": "Changer le sondage pour n'autoriser qu'un seul choix",
   "compose_form.poll.type": "Style",
@@ -585,9 +585,9 @@
   "privacy.private.short": "Abonnés",
   "privacy.public.long": "Tout le monde sur et en dehors de Mastodon",
   "privacy.public.short": "Public",
-  "privacy.unlisted.additional": "Cette option se comporte exactement comme l'option publique, sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, l'exploration ou la recherche Mastodon, même si vous avez opté pour l'option publique pour l'ensemble de votre compte.",
+  "privacy.unlisted.additional": "Se comporte exactement comme « public », sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, explorer ou la recherche Mastodon, même si vous les avez activé au niveau de votre compte.",
   "privacy.unlisted.long": "Moins de fanfares algorithmiques",
-  "privacy.unlisted.short": "Public calme",
+  "privacy.unlisted.short": "Public discret",
   "privacy_policy.last_updated": "Dernière mise à jour {date}",
   "privacy_policy.title": "Politique de confidentialité",
   "recommended": "Recommandé",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index 1a5803623..36ec673a4 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -156,7 +156,7 @@
   "compose_form.poll.duration": "Durée du sondage",
   "compose_form.poll.multiple": "Choix multiple",
   "compose_form.poll.option_placeholder": "Option {number}",
-  "compose_form.poll.single": "Choisissez-en un",
+  "compose_form.poll.single": "Choix unique",
   "compose_form.poll.switch_to_multiple": "Changer le sondage pour autoriser plusieurs choix",
   "compose_form.poll.switch_to_single": "Modifier le sondage pour autoriser qu'un seul choix",
   "compose_form.poll.type": "Style",
@@ -585,9 +585,9 @@
   "privacy.private.short": "Abonnés",
   "privacy.public.long": "Tout le monde sur et en dehors de Mastodon",
   "privacy.public.short": "Public",
-  "privacy.unlisted.additional": "Cette option se comporte exactement comme l'option publique, sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, l'exploration ou la recherche Mastodon, même si vous avez opté pour l'option publique pour l'ensemble de votre compte.",
+  "privacy.unlisted.additional": "Se comporte exactement comme « public », sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, explorer ou la recherche Mastodon, même si vous les avez activé au niveau de votre compte.",
   "privacy.unlisted.long": "Moins de fanfares algorithmiques",
-  "privacy.unlisted.short": "Public calme",
+  "privacy.unlisted.short": "Public discret",
   "privacy_policy.last_updated": "Dernière mise à jour {date}",
   "privacy_policy.title": "Politique de confidentialité",
   "recommended": "Recommandé",
diff --git a/app/javascript/mastodon/locales/ia.json b/app/javascript/mastodon/locales/ia.json
index ce4e89e99..ed33a45d4 100644
--- a/app/javascript/mastodon/locales/ia.json
+++ b/app/javascript/mastodon/locales/ia.json
@@ -19,7 +19,7 @@
   "account.block_domain": "Blocar dominio {domain}",
   "account.block_short": "Blocar",
   "account.blocked": "Blocate",
-  "account.browse_more_on_origin_server": "Percurrer plus sur le profilo original",
+  "account.browse_more_on_origin_server": "Explorar plus sur le profilo original",
   "account.cancel_follow_request": "Cancellar sequimento",
   "account.copy": "Copiar ligamine a profilo",
   "account.direct": "Mentionar privatemente @{name}",
@@ -111,7 +111,7 @@
   "bundle_modal_error.message": "Un error ha occurrite durante le cargamento de iste componente.",
   "bundle_modal_error.retry": "Tentar novemente",
   "closed_registrations.other_server_instructions": "Perque Mastodon es decentralisate, tu pote crear un conto sur un altere servitor e totevia interager con iste servitor.",
-  "closed_registrations_modal.description": "Crear un conto in {domain} actualmente non es possibile, ma considera que non es necessari haber un conto specificamente sur {domain} pro usar Mastodon.",
+  "closed_registrations_modal.description": "Crear un conto sur {domain} non es actualmente possibile, ma considera que non es necessari haber un conto specificamente sur {domain} pro usar Mastodon.",
   "closed_registrations_modal.find_another_server": "Cercar un altere servitor",
   "closed_registrations_modal.preamble": "Mastodon es decentralisate, dunque, non importa ubi tu crea tu conto, tu pote sequer e communicar con omne persona sur iste servitor. Tu pote mesmo hospitar tu proprie servitor!",
   "closed_registrations_modal.title": "Crear un conto sur Mastodon",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 33161f888..79224c57d 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -299,6 +299,11 @@
   "follow_suggestions.dismiss": "Jangan tampilkan lagi",
   "follow_suggestions.hints.featured": "Profil ini telah dipilih sendiri oleh tim {domain}.",
   "follow_suggestions.hints.friends_of_friends": "Profil ini populer di kalangan orang yang anda ikuti.",
+  "follow_suggestions.personalized_suggestion": "Saran yang dipersonalisasi",
+  "follow_suggestions.popular_suggestion": "Saran populer",
+  "follow_suggestions.popular_suggestion_longer": "Populer di {domain}",
+  "follow_suggestions.similar_to_recently_followed_longer": "Serupa dengan profil yang baru Anda ikuti",
+  "follow_suggestions.view_all": "Lihat semua",
   "followed_tags": "Tagar yang diikuti",
   "footer.about": "Tentang",
   "footer.directory": "Direktori profil",
@@ -324,6 +329,7 @@
   "home.column_settings.show_reblogs": "Tampilkan boost",
   "home.column_settings.show_replies": "Tampilkan balasan",
   "home.hide_announcements": "Sembunyikan pengumuman",
+  "home.pending_critical_update.link": "Lihat pembaruan",
   "home.show_announcements": "Tampilkan pengumuman",
   "interaction_modal.description.follow": "Dengan sebuah akun di Mastodon, Anda bisa mengikuti {name} untuk menerima kirimannya di beranda Anda.",
   "interaction_modal.description.reblog": "Dengan sebuah akun di Mastodon, Anda bisa mem-boost kiriman ini untuk membagikannya ke pengikut Anda sendiri.",
@@ -375,6 +381,7 @@
   "lightbox.previous": "Sebelumnya",
   "limited_account_hint.action": "Tetap tampilkan profil",
   "limited_account_hint.title": "Profil ini telah disembunyikan oleh moderator {domain}.",
+  "link_preview.author": "Oleh {name}",
   "lists.account.add": "Tambah ke daftar",
   "lists.account.remove": "Hapus dari daftar",
   "lists.delete": "Hapus daftar",
@@ -389,8 +396,11 @@
   "lists.search": "Cari di antara orang yang Anda ikuti",
   "lists.subheading": "Daftar Anda",
   "load_pending": "{count, plural, other {# item baru}}",
+  "loading_indicator.label": "Memuat…",
   "media_gallery.toggle_visible": "Tampil/Sembunyikan",
   "moved_to_account_banner.text": "Akun {disabledAccount} Anda kini dinonaktifkan karena Anda pindah ke {movedToAccount}.",
+  "mute_modal.hide_options": "Sembunyikan opsi",
+  "mute_modal.title": "Bisukan pengguna?",
   "navigation_bar.about": "Tentang",
   "navigation_bar.blocks": "Pengguna diblokir",
   "navigation_bar.bookmarks": "Markah",
diff --git a/app/javascript/mastodon/locales/la.json b/app/javascript/mastodon/locales/la.json
index a49ec94d7..4bee0efed 100644
--- a/app/javascript/mastodon/locales/la.json
+++ b/app/javascript/mastodon/locales/la.json
@@ -1,7 +1,9 @@
 {
   "about.contact": "Ratio:",
   "about.domain_blocks.no_reason_available": "Ratio abdere est",
+  "about.domain_blocks.silenced.explanation": "Tua profilia atque tuum contentum ab hac serve praecipue non videbis, nisi explōrēs expresse aut subsequeris et optēs.",
   "account.account_note_header": "Annotatio",
+  "account.add_or_remove_from_list": "Adde aut ēripe ex tabellīs",
   "account.badges.bot": "Robotum",
   "account.badges.group": "Congregatio",
   "account.block": "Impedire @{name}",
@@ -11,11 +13,21 @@
   "account.domain_blocked": "Dominium impeditum",
   "account.edit_profile": "Recolere notionem",
   "account.featured_tags.last_status_never": "Nulla contributa",
+  "account.featured_tags.title": "Hashtag notātī {name}",
+  "account.followers_counter": "{count, plural, one {{counter} Sectator} other {{counter} Sectatores}}",
+  "account.following_counter": "{count, plural, one {{counter} Sequens} other {{counter} Sequentes}}",
+  "account.moved_to": "{name} significavit eum suam rationem novam nunc esse:",
   "account.muted": "Confutatus",
+  "account.requested_follow": "{name} postulavit ut te sequeretur",
+  "account.statuses_counter": "{count, plural, one {{counter} Nuntius} other {{counter} Nuntii}}",
   "account.unblock_short": "Solvere impedimentum",
   "account_note.placeholder": "Click to add a note",
   "admin.dashboard.retention.average": "Mediocritas",
+  "admin.impact_report.instance_accounts": "Rationes perfiles hoc deleret",
+  "alert.unexpected.message": "Error inopinatus occurrit.",
   "announcement.announcement": "Proclamatio",
+  "attachments_list.unprocessed": "(immūtātus)",
+  "block_modal.you_wont_see_mentions": "Nuntios quibus eos commemorant non videbis.",
   "bundle_column_error.error.title": "Eheu!",
   "bundle_column_error.retry": "Retemptare",
   "bundle_column_error.routing.title": "CCCCIIII",
@@ -37,26 +49,55 @@
   "compose_form.placeholder": "What is on your mind?",
   "compose_form.publish_form": "Barrire",
   "compose_form.spoiler.marked": "Text is hidden behind warning",
-  "compose_form.spoiler.unmarked": "Text is not hidden",
+  "compose_form.spoiler.unmarked": "Adde praeconium contentūs",
   "confirmations.block.confirm": "Impedire",
   "confirmations.delete.confirm": "Oblitterare",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
   "confirmations.delete_list.confirm": "Oblitterare",
+  "confirmations.discard_edit_media.message": "Habēs mutationēs in descriptionem vel prōspectum medii quae nōn sunt servātae; eas dēmittam?",
   "confirmations.mute.confirm": "Confutare",
   "confirmations.reply.confirm": "Respondere",
+  "disabled_account_banner.account_settings": "Praeferentiae ratiōnis",
+  "disabled_account_banner.text": "Ratio tua {disabledAccount} debilitata est.",
   "dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.",
   "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.",
+  "domain_block_modal.you_will_lose_followers": "Omnes sectatores tuī ex hoc servō removēbuntur.",
+  "domain_block_modal.you_wont_see_posts": "Nuntios aut notificātiōnēs ab usoribus in hōc servō nōn vidēbis.",
+  "domain_pill.activitypub_like_language": "ActivityPub est velut lingua quam Mastodon cum aliīs sociālibus rētibus loquitur.",
+  "domain_pill.your_handle": "Tuus nominulus:",
+  "domain_pill.your_server": "Tua domus digitalis, ubi omnia tua nuntia habitant. Hanc non amas? Servēs trānsferāre potes quōcumque tempore et sectātōrēs tuōs simul addūcere.",
+  "domain_pill.your_username": "Tuō singulāre id indicium in hōc servō est. Est possibile invenīre usōrēs cum eōdem nōmine in servīs aliīs.",
   "embed.instructions": "Embed this status on your website by copying the code below.",
+  "emoji_button.activity": "Actiō",
   "emoji_button.food": "Cibus et potus",
   "emoji_button.people": "Homines",
   "emoji_button.search": "Quaerere...",
+  "empty_column.account_suspended": "Rātiō suspēnsa",
   "empty_column.account_timeline": "Hic nulla contributa!",
   "empty_column.account_unavailable": "Notio non impetrabilis",
-  "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
+  "empty_column.blocks": "Nondum quemquam usorem obsēcāvisti.",
+  "empty_column.direct": "Nōn habēs adhūc ullo mentionēs prīvātās. Cum ūnam mīseris aut accipis, hīc apparēbit.",
+  "empty_column.followed_tags": "Nōn adhūc aliquem hastāginem secūtus es. Cum id fēceris, hic ostendētur.",
+  "empty_column.home": "Tua linea temporum domesticus vacua est! Sequere plures personas ut eam compleas.",
   "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.",
+  "empty_column.lists": "Nōn adhūc habēs ullo tabellās. Cum creās, hīc apparēbunt.",
+  "empty_column.mutes": "Nondum quemquam usorem tacuisti.",
+  "empty_column.notification_requests": "Omnia clara sunt! Nihil hic est. Cum novās notificātiōnēs accipīs, hic secundum tua praecepta apparebunt.",
+  "empty_column.notifications": "Nōn adhūc habēs ullo notificātiōnēs. Cum aliī tē interagunt, hīc videbis.",
   "explore.trending_statuses": "Contributa",
+  "filtered_notifications_banner.mentions": "{count, plural, one {mentiō} other {mentiōnēs}}",
+  "firehose.all": "Omnis",
+  "footer.about": "De",
   "generic.saved": "Servavit",
+  "hashtag.column_settings.tag_mode.all": "Haec omnia",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
+  "hashtag.counter_by_accounts": "{count, plural, one {{counter} particeps} other {{counter} participēs}}",
+  "hashtag.counter_by_uses": "{count, plural, one {{counter} nuntius} other {{counter} nuntii}}",
+  "hashtag.counter_by_uses_today": "{count, plural, one {{counter} nuntius} other {{counter} nuntii}} hodie",
+  "hashtags.and_other": "…et {count, plural, other {# plus}}",
+  "intervals.full.days": "{number, plural, one {# die} other {# dies}}",
+  "intervals.full.hours": "{number, plural, one {# hora} other {# horae}}",
+  "intervals.full.minutes": "{number, plural, one {# minutum} other {# minuta}}",
   "keyboard_shortcuts.back": "Re navigare",
   "keyboard_shortcuts.blocked": "Aperire listam usorum obstructorum",
   "keyboard_shortcuts.boost": "Inlustrare publicatio",
@@ -90,18 +131,47 @@
   "keyboard_shortcuts.up": "to move up in the list",
   "lightbox.close": "Claudere",
   "lightbox.next": "Secundum",
+  "lists.account.add": "Adde ad tabellās",
+  "lists.new.create": "Addere tabella",
+  "load_pending": "{count, plural, one {# novum item} other {# nova itema}}",
+  "media_gallery.toggle_visible": "{number, plural, one {Cēla imaginem} other {Cēla imagines}}",
+  "moved_to_account_banner.text": "Tua ratione {disabledAccount} interdum reposita est, quod ad {movedToAccount} migrāvisti.",
+  "mute_modal.you_wont_see_mentions": "Non videbis nuntios quī eōs commemorant.",
+  "navigation_bar.about": "De",
   "navigation_bar.domain_blocks": "Hidden domains",
-  "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.",
+  "not_signed_in_indicator.not_signed_in": "Ad hunc locum pervenire oportet ut inīre facias.",
+  "notification.admin.report": "{name} nuntiavit {target}",
+  "notification.admin.sign_up": "{name} subscripsit",
+  "notification.favourite": "{name} nuntium tuum favit",
+  "notification.follow": "{name} te secutus est",
+  "notification.follow_request": "{name} postulavit ut te sequeretur",
+  "notification.mention": "{name} memoravi",
+  "notification.moderation_warning": "Accepistī monitionem moderationis.",
+  "notification.moderation_warning.action_disable": "Ratio tua debilitata est.",
   "notification.moderation_warning.action_none": "Tua ratiō monitum moderātiōnis accēpit.",
-  "notification.reblog": "{name} boosted your status",
+  "notification.moderation_warning.action_sensitive": "Tua nuntia hinc sensibiliter notabuntur.",
+  "notification.moderation_warning.action_silence": "Ratio tua est limitata.",
+  "notification.moderation_warning.action_suspend": "Ratio tua suspensus est.",
+  "notification.own_poll": "Suffragium tuum terminatum est.",
+  "notification.poll": "Electione in quam suffragium dedisti finita est.",
+  "notification.reblog": "{name} tuum nuntium amplificavit.",
+  "notification.relationships_severance_event.account_suspension": "Admin ab {from} {target} suspendit, quod significat nōn iam posse tē novitātēs ab eīs accipere aut cum eīs interagere.",
+  "notification.relationships_severance_event.domain_block": "Admin ab {from} {target} obsēcāvit, includēns {followersCount} ex tuīs sectātōribus et {followingCount, plural, one {# ratione} other {# rationibus}} quās sequeris.",
+  "notification.relationships_severance_event.user_domain_block": "Bloqueāstī {target}, removēns {followersCount} ex sectātōribus tuīs et {followingCount, plural, one {# rationem} other {# rationēs}} quōs sequeris.",
+  "notification.status": "{name} nuper publicavit",
+  "notification.update": "{name} nuntium correxit",
+  "notification_requests.accept": "Accipe",
   "notifications.filter.all": "Omnia",
   "notifications.filter.polls": "Eventus electionis",
+  "notifications.group": "Notificātiōnēs",
   "onboarding.actions.go_to_explore": "See what's trending",
   "onboarding.actions.go_to_home": "Go to your home feed",
-  "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!",
+  "onboarding.follows.lead": "Tua domus feed est principalis via Mastodon experīrī. Quō plūrēs persōnas sequeris, eō actīvior et interessantior erit. Ad tē incipiendum, ecce quaedam suāsiones:",
   "onboarding.follows.title": "Popular on Mastodon",
-  "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:",
+  "onboarding.profile.display_name_hint": "Tuum nomen completum aut tuum nomen ludens...",
+  "onboarding.start.lead": "Nunc pars es Mastodonis, singularis, socialis medii platformae decentralis ubi—non algorismus—tuam ipsius experientiam curas. Incipiāmus in nova hac socialis regione:",
   "onboarding.start.skip": "Want to skip right ahead?",
+  "onboarding.start.title": "Perfecisti eam!",
   "onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.",
   "onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}",
   "onboarding.steps.publish_status.body": "Say hello to the world.",
@@ -109,31 +179,48 @@
   "onboarding.steps.setup_profile.title": "Customize your profile",
   "onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
   "onboarding.steps.share_profile.title": "Share your profile",
-  "onboarding.tips.accounts_from_other_servers": "<strong>Scisne?</strong> Quoniam Mastodon dēcentālis est, nōnnulla profīlia quae invenīs in servīs aliīs quam tuōrum erunt hospitāta. Tamen cum eīs sine impedīmentō interāgere potes! Servus eōrum in alterā parte nōminis eōrum est!",
+  "onboarding.tips.2fa": "<strong>Scisne?</strong>  Tūam ratiōnem sēcūrāre potes duōrum elementōrum authentīcātiōnem in ratiōnis tuī praeferentiīs statuendō. Cum ūllā app TOTP ex tuā ēlēctiōne operātur, numerus tēlephōnicus necessārius nōn est!",
+  "onboarding.tips.accounts_from_other_servers": "<strong>Scisne?</strong> Quoniam Mastodon dēcentrālis est, nōnnulla profīlia quae invenīs in servīs aliīs quam tuōrum erunt hospitāta. Tamen cum eīs sine impedīmentō interāgere potes! Servus eōrum in alterā parte nōminis eōrum est!",
   "onboarding.tips.migration": "<strong>Scisne?</strong> Sī sentīs {domain} tibi in futūrō nōn esse optimam servī ēlēctiōnem, ad alium servum Mastodon sine amittendō sectātōribus tuīs migrāre potes. Etiam tuum servum hospitārī potes!",
+  "onboarding.tips.verification": "<strong>Scisne?</strong>  Tūam ratiōnem verificāre potes iungendō nexum ad prōfīlium Mastodon tuum in propriā pāginā interrētiā et addendō pāginam ad prōfīlium tuum. Nullae pecūniae aut documenta necessāria sunt!",
   "poll.closed": "Clausum",
+  "poll.total_people": "{count, plural, one {# persona} other {# personae}}",
+  "poll.total_votes": "{count, plural, one {# suffragium} other {# suffragia}}",
   "poll.vote": "Eligere",
   "poll.voted": "Elegisti hoc responsum",
+  "poll.votes": "{votes, plural, one {# sufragium} other {# sufragia}}",
   "poll_button.add_poll": "Addere electionem",
   "poll_button.remove_poll": "Auferre electionem",
   "privacy.change": "Adjust status privacy",
   "privacy.public.short": "Coram publico",
+  "regeneration_indicator.sublabel": "Tua domus feed praeparātur!",
+  "relative_time.full.days": "{number, plural, one {# ante die} other {# ante dies}}",
+  "relative_time.full.hours": "{number, plural, one {# ante horam} other {# ante horas}}",
   "relative_time.full.just_now": "nunc",
+  "relative_time.full.minutes": "{number, plural, one {# ante minutum} other {# ante minuta}}",
+  "relative_time.full.seconds": "{number, plural, one {# ante secundum} other {# ante secunda}}",
   "relative_time.just_now": "nunc",
   "relative_time.today": "hodie",
+  "reply_indicator.attachments": "{count, plural, one {# annexus} other {# annexūs}}",
   "report.block": "Impedimentum",
+  "report.block_explanation": "Non videbis eorum nuntios. Non poterunt vidēre tuōs nuntios aut tē sequī. Intelligere poterunt sē obstrūctōs esse.",
   "report.categories.other": "Altera",
   "report.category.title_account": "notio",
   "report.category.title_status": "contributum",
   "report.close": "Confectum",
   "report.mute": "Confutare",
+  "report.mute_explanation": "Non videbis eōrum nuntiōs. Possunt adhuc tē sequī et tuōs nuntiōs vidēre, nec sciēbunt sē tacitōs esse.",
   "report.next": "Secundum",
-  "report.placeholder": "Type or paste additional comments",
+  "report.placeholder": "Commentāriī adiūnctī",
   "report.submit": "Mittere",
   "report.target": "Report {target}",
-  "report_notification.attached_statuses": "{count, plural, one {# post} other {# posts}} attached",
+  "report_notification.attached_statuses": "{count, plural, one {{count} nuntius} other {{count} nuntii}} attachiatus",
   "report_notification.categories.other": "Altera",
   "search.placeholder": "Quaerere",
+  "search_results.all": "Omnis",
+  "server_banner.active_users": "Usūrāriī āctīvī",
+  "server_banner.administered_by": "Administratur:",
+  "server_banner.introduction": "{domain} pars est de rete sociali decentralizato a {mastodon} propulsato.",
   "server_banner.learn_more": "Discere plura",
   "sign_in_banner.sign_in": "Sign in",
   "status.admin_status": "Open this status in the moderation interface",
@@ -143,13 +230,29 @@
   "status.delete": "Oblitterare",
   "status.edit": "Recolere",
   "status.edited_x_times": "Edited {count, plural, one {# time} other {# times}}",
+  "status.favourites": "{count, plural, one {favoritum} other {favorita}}",
+  "status.history.created": "{name} creatum {date}",
+  "status.history.edited": "{name} correxit {date}",
   "status.open": "Expand this status",
-  "status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}",
+  "status.reblogged_by": "{name} adiuvavit",
+  "status.reblogs": "{count, plural, one {auctus} other {auctūs}}",
+  "status.title.with_attachments": "{user} publicavit {attachmentCount, plural, one {unum annexum} other {{attachmentCount} annexa}}",
   "tabs_bar.home": "Domi",
+  "time_remaining.days": "{number, plural, one {# die} other {# dies}} restant",
+  "time_remaining.hours": "{number, plural, one {# hora} other {# horae}} restant",
+  "time_remaining.minutes": "{number, plural, one {# minutum} other {# minuta}} restant",
+  "time_remaining.seconds": "{number, plural, one {# secundum} other {# secunda}} restant",
+  "timeline_hint.remote_resource_not_displayed": "{resource} ab aliīs servīs nōn ostenduntur.",
   "timeline_hint.resources.statuses": "Contributa pristina",
-  "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {# days}}",
+  "trends.counter_by_accounts": "{count, plural, one {{counter} persōna} other {{counter} persōnae}} in {days, plural, one {diē prīdiē} other {diēbus praeteritīs {days}}}",
+  "ui.beforeunload": "Si Mastodon discesseris, tua epitome peribit.",
+  "units.short.billion": "{count} millia milionum",
+  "units.short.million": "{count} milionum",
+  "units.short.thousand": "{count} millia",
+  "upload_button.label": "Imaginēs, vīdeō aut fīle audītūs adde",
   "upload_form.audio_description": "Describe for people who are hard of hearing",
   "upload_form.edit": "Recolere",
+  "upload_modal.description_placeholder": "A velox brunneis vulpes salit super piger canis",
   "upload_progress.label": "Uploading…",
   "video.mute": "Confutare soni"
 }
diff --git a/config/locales/devise.la.yml b/config/locales/devise.la.yml
index 3a7ba0d44..a6fe5e1e4 100644
--- a/config/locales/devise.la.yml
+++ b/config/locales/devise.la.yml
@@ -1 +1,8 @@
+---
 la:
+  devise:
+    passwords:
+      send_instructions: Sī adresa tua epistularis in nostra basi datōrum exstat, vinculum ad recuperandam clavem adresa tua epistulari adferētur pauca momenta post. Sī autem hanc epistulam nōn recēpistī, rogāmus ut scrūtināriōnem spurcāriī tuī faciās.
+    registrations:
+      destroyed: Vale! Ratio tua succēssu cancellāta est. Spērāmus tē mox iterum vidēre.
+      signed_up_but_inactive: Te cōnscrīpsistī succēdāneē. At nōn potuimus tē introīre quod ratio* tua nōn adhūc est activāta.*
diff --git a/config/locales/doorkeeper.ja.yml b/config/locales/doorkeeper.ja.yml
index 62f2a3eb0..26f7ff563 100644
--- a/config/locales/doorkeeper.ja.yml
+++ b/config/locales/doorkeeper.ja.yml
@@ -135,6 +135,7 @@ ja:
         media: メディアの添付
         mutes: ミュート
         notifications: 通知
+        profile: Mastodonのプロフィール
         push: プッシュ通知
         reports: 通報
         search: 検索
@@ -165,6 +166,7 @@ ja:
       admin:write:reports: 通報に対するアクションの実行
       crypto: エンドツーエンド暗号化の使用
       follow: アカウントのつながりを変更
+      profile: アカウントのプロフィール情報の読み取りのみ
       push: プッシュ通知の受信
       read: アカウントのすべてのデータの読み取り
       read:accounts: アカウント情報の読み取り
diff --git a/config/locales/doorkeeper.lad.yml b/config/locales/doorkeeper.lad.yml
index b2c140b9c..c335d67fd 100644
--- a/config/locales/doorkeeper.lad.yml
+++ b/config/locales/doorkeeper.lad.yml
@@ -135,6 +135,7 @@ lad:
         media: Aneksos de multimedia
         mutes: Silensiasyones
         notifications: Avizos
+        profile: Tu profil de Mastodon
         push: Avizos arrepushados
         reports: Raportos
         search: Bushkeda
@@ -165,6 +166,7 @@ lad:
       admin:write:reports: fazer aksyones de moderasyon en raportos
       crypto: kulanear shifrasyon de lado a lado
       follow: modifikar relasyones de kuentos
+      profile: melda solo la informasyon del profil
       push: risivir tus avizos arrepushados
       read: meldar todos tus datos de kuento
       read:accounts: ver enformasyon de kuentos
diff --git a/config/locales/doorkeeper.lt.yml b/config/locales/doorkeeper.lt.yml
index 5c2e4fd4e..38bb17ad1 100644
--- a/config/locales/doorkeeper.lt.yml
+++ b/config/locales/doorkeeper.lt.yml
@@ -135,6 +135,7 @@ lt:
         media: Medijos priedai
         mutes: Nutildymai
         notifications: Pranešimai
+        profile: Tavo Mastodon profilis
         push: Tiesioginiai pranešimai
         reports: Ataskaitos
         search: Paieška
@@ -165,6 +166,7 @@ lt:
       admin:write:reports: atlikti ataskaitų prižiūrėjimo veiksmus
       crypto: naudoti visapusį šifravimą
       follow: modifikuoti paskyros sąryšius
+      profile: skaityti tik tavo paskyros profilio informaciją
       push: gauti tiesioginius pranešimus
       read: skaityti visus paskyros duomenis
       read:accounts: matyti paskyrų informaciją
diff --git a/config/locales/doorkeeper.sv.yml b/config/locales/doorkeeper.sv.yml
index f2c8bd34b..bc4ba6f53 100644
--- a/config/locales/doorkeeper.sv.yml
+++ b/config/locales/doorkeeper.sv.yml
@@ -135,6 +135,7 @@ sv:
         media: Mediabilagor
         mutes: Tystade användare
         notifications: Aviseringar
+        profile: Din Mastodon-profil
         push: Push-aviseringar
         reports: Rapporter
         search: Sök
diff --git a/config/locales/doorkeeper.th.yml b/config/locales/doorkeeper.th.yml
index 067e06558..b0d0549d1 100644
--- a/config/locales/doorkeeper.th.yml
+++ b/config/locales/doorkeeper.th.yml
@@ -135,6 +135,7 @@ th:
         media: ไฟล์แนบสื่อ
         mutes: การซ่อน
         notifications: การแจ้งเตือน
+        profile: โปรไฟล์ Mastodon ของคุณ
         push: การแจ้งเตือนแบบผลัก
         reports: การรายงาน
         search: ค้นหา
@@ -165,6 +166,7 @@ th:
       admin:write:reports: ทำการกระทำการกลั่นกรองต่อรายงาน
       crypto: ใช้การเข้ารหัสแบบต้นทางถึงปลายทาง
       follow: ปรับเปลี่ยนความสัมพันธ์ของบัญชี
+      profile: อ่านเฉพาะข้อมูลโปรไฟล์ของบัญชีของคุณเท่านั้น
       push: รับการแจ้งเตือนแบบผลักของคุณ
       read: อ่านข้อมูลบัญชีทั้งหมดของคุณ
       read:accounts: ดูข้อมูลบัญชี
diff --git a/config/locales/doorkeeper.vi.yml b/config/locales/doorkeeper.vi.yml
index 7f1c5430e..d0bdd2cc7 100644
--- a/config/locales/doorkeeper.vi.yml
+++ b/config/locales/doorkeeper.vi.yml
@@ -135,6 +135,7 @@ vi:
         media: Tập tin đính kèm
         mutes: Đã ẩn
         notifications: Thông báo
+        profile: Hồ sơ Mastodon của bạn
         push: Thông báo đẩy
         reports: Báo cáo
         search: Tìm kiếm
@@ -165,6 +166,7 @@ vi:
       admin:write:reports: áp đặt kiểm duyệt với các báo cáo
       crypto: dùng mã hóa đầu cuối
       follow: sửa đổi các mối quan hệ tài khoản
+      profile: chỉ đọc thông tin tài khoản cơ bản
       push: nhận thông báo đẩy
       read: đọc mọi dữ liệu tài khoản
       read:accounts: xem thông tin tài khoản
diff --git a/config/locales/id.yml b/config/locales/id.yml
index aae790f48..ac42afdb4 100644
--- a/config/locales/id.yml
+++ b/config/locales/id.yml
@@ -89,6 +89,7 @@ id:
       moderation:
         active: Aktif
         all: Semua
+        disabled: Nonaktif
         pending: Tertunda
         silenced: Terbatas
         suspended: Disuspen
@@ -121,6 +122,8 @@ id:
       removed_header_msg: Berhasil menghapus gambar header %{username}
       resend_confirmation:
         already_confirmed: Pengguna ini sudah dikonfirmasi
+        send: Kirim ulang email konfirmasi
+        success: Email konfirmasi berhasil dikirim!
       reset: Atur ulang
       reset_password: Reset kata sandi
       resubscribe: Langganan ulang
@@ -128,6 +131,7 @@ id:
       search: Cari
       search_same_email_domain: Pengguna lain dengan domain email yang sama
       search_same_ip: Pengguna lain dengan IP yang sama
+      security: Keamanan
       security_measures:
         only_password: Hanya kata sandi
         password_and_2fa: Kata sandi dan 2FA
@@ -278,6 +282,7 @@ id:
         update_custom_emoji_html: "%{name} memperbarui emoji %{target}"
         update_domain_block_html: "%{name} memperbarui blokir domain untuk %{target}"
         update_ip_block_html: "%{name} mengubah peraturan untuk IP %{target}"
+        update_report_html: "%{name} memperbarui laporan %{target}"
         update_status_html: "%{name} memperbarui status %{target}"
         update_user_role_html: "%{name} mengubah peran %{target}"
       deleted_account: akun yang dihapus
@@ -302,6 +307,7 @@ id:
       unpublish: Batal terbitkan
       unpublished_msg: Pengumuman berhasil ditarik!
       updated_msg: Pengumuman berhasil diperbarui!
+    critical_update_pending: Pembaruan penting tertunda
     custom_emojis:
       assign_category: Beri kategori
       by_domain: Domain
diff --git a/config/locales/la.yml b/config/locales/la.yml
index 3a7ba0d44..d3733df93 100644
--- a/config/locales/la.yml
+++ b/config/locales/la.yml
@@ -1 +1,34 @@
+---
 la:
+  about:
+    about_mastodon_html: '"Rete sociale futuri: Nullae praebendae, nulla observatio corporativa, ethica designatio, et decentralizatio! Tua data cum Mastodonte posside!"'
+    contact_missing: Non definitum
+    contact_unavailable: Nōn Applicābilis
+    hosted_on: Mastodon in %{domain} hospitātum
+    title: De
+  accounts:
+    follow: Sequere
+    followers:
+      one: Sectātor
+      other: Sectātōrēs
+    following: Sequendī
+    instance_actor_flash: Hic ratio est actōr virtuālis ad repraesentandam ipsum servatorem et non ullam individuam usorem. Ad scopōs foederātiōnis ūtor nec suspendendus est.
+    last_active: Ultimum Actum
+    link_verified_on: Dominium huius nexūs est comprobatum die %{date}.
+    nothing_here: Nihil est hic!
+    pin_errors:
+      following: Iam debes sequi personam quam vis probare.
+    posts:
+      one: Nuntius
+      other: Nuntii
+    posts_tab_heading: Nuntii
+  admin:
+    account_actions:
+      action: Agere actionem
+      title: Actionem moderationis in %{acct} gerere
+    account_moderation_notes:
+      create: Relinque nota
+      created_msg: Nota moderationis feliciter creata est!
+      destroyed_msg: Nota moderationis feliciter deleta est!
+    accounts:
+      are_you_sure: Esne certus?
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index a527fdb5a..328467f48 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -1814,14 +1814,14 @@ nl:
       subject: Jouw archief staat klaar om te worden gedownload
       title: Archief ophalen
     failed_2fa:
-      details: 'Hier zijn details van de inlogpoging:'
+      details: 'Hier zijn de details van de inlogpoging:'
       explanation: Iemand heeft geprobeerd om in te loggen op jouw account maar heeft een ongeldige tweede verificatiefactor opgegeven.
       further_actions_html: Als jij dit niet was, raden we je aan om onmiddellijk %{action} aangezien het in gevaar kan zijn.
       subject: Tweestapsverificatiefout
       title: Tweestapsverificatie mislukt
     suspicious_sign_in:
       change_password: je wachtwoord te wijzigen
-      details: 'Hier zijn de details van inlogpoging:'
+      details: 'Hier zijn de details van het inloggen:'
       explanation: We hebben vastgesteld dat iemand vanaf een nieuw IP-adres op jouw account is ingelogd.
       further_actions_html: Wanneer jij dit niet was, adviseren wij om onmiddellijk %{action} en om tweestapsverificatie in te schakelen, om zo je account veilig te houden.
       subject: Jouw account is vanaf een nieuw IP-adres benaderd
diff --git a/config/locales/simple_form.id.yml b/config/locales/simple_form.id.yml
index 856f312ed..1f493435e 100644
--- a/config/locales/simple_form.id.yml
+++ b/config/locales/simple_form.id.yml
@@ -2,6 +2,9 @@
 id:
   simple_form:
     hints:
+      account:
+        discoverable: Postingan dan profil publik Anda mungkin ditampilkan atau direkomendasikan di berbagai area Mastodon dan profil Anda mungkin disarankan ke pengguna lain.
+        display_name: Nama lengkap Anda atau nama lucu Anda.
       account_alias:
         acct: Tentukan namapengguna@domain akun yang ingin Anda pindah
       account_migration:
diff --git a/config/locales/th.yml b/config/locales/th.yml
index bafcd30de..d1359e017 100644
--- a/config/locales/th.yml
+++ b/config/locales/th.yml
@@ -1837,11 +1837,13 @@ th:
       edit_profile_title: ปรับแต่งโปรไฟล์ของคุณ
       explanation: นี่คือเคล็ดลับบางส่วนที่จะช่วยให้คุณเริ่มต้นใช้งาน
       feature_action: เรียนรู้เพิ่มเติม
-      feature_audience: Mastodon มีความพิเศษที่ให้คุณจัดการผู้รับสารของคุณได้โดยไม่มีตัวกลาง นอกจากนี้ การติดตั้ง Mastodon บนโครงสร้างพื้นฐานของคุณจะทำให้คุณสามารถติดตาม (และติดตามโดย) เซิร์ฟเวอร์ Mastodon แห่งไหนก็ได้ที่ทำงานอยู่ โดยไม่มีใครสามารถควบคุมได้นอกจากคุณ
+      feature_audience: Mastodon ให้ความเป็นไปได้ที่เป็นเอกลักษณ์แก่คุณในการจัดการผู้ชมของคุณโดยไม่มีตัวกลาง Mastodon ที่ปรับใช้ในโครงสร้างพื้นฐานของคุณเองอนุญาตให้คุณติดตามและได้รับการติดตามจากเซิร์ฟเวอร์ Mastodon อื่นใดทางออนไลน์และไม่อยู่ภายใต้การควบคุมของใครนอกจากคุณ
       feature_audience_title: สร้างผู้ชมของคุณด้วยความมั่นใจ
       feature_control: คุณทราบดีที่สุดถึงสิ่งที่คุณต้องการเห็นในฟีดหน้าแรกของคุณ ไม่มีอัลกอริทึมหรือโฆษณาให้เสียเวลาของคุณ ติดตามใครก็ตามทั่วทั้งเซิร์ฟเวอร์ Mastodon ใด ๆ จากบัญชีเดียวและรับโพสต์ของเขาตามลำดับเวลา และทำให้มุมอินเทอร์เน็ตของคุณเป็นเหมือนคุณมากขึ้นอีกนิด
       feature_control_title: การควบคุมเส้นเวลาของคุณเอง
+      feature_creativity: Mastodon รองรับโพสต์เสียง วิดีโอ และรูปภาพ, คำอธิบายการช่วยการเข้าถึง, การสำรวจความคิดเห็น, คำเตือนเนื้อหา, ภาพประจำตัวแบบเคลื่อนไหว, อีโมจิที่กำหนดเอง, การควบคุมการครอบตัดภาพขนาดย่อ และอื่น ๆ เพื่อช่วยให้คุณแสดงออกตัวคุณเองทางออนไลน์ ไม่ว่าคุณกำลังจะเผยแพร่ศิลปะของคุณ, เพลงของคุณ หรือพอดแคสต์ของคุณ Mastodon อยู่ที่นั่นเพื่อคุณ
       feature_creativity_title: ความคิดสร้างสรรค์ที่ไม่มีใครเทียบได้
+      feature_moderation: Mastodon นำการตัดสินใจกลับมาอยู่ในมือของคุณ แต่ละเซิร์ฟเวอร์สร้างกฎและระเบียบข้อบังคับของตนเอง ซึ่งบังคับใช้ในเซิร์ฟเวอร์และไม่ใช่จากบนลงล่างเหมือนสื่อสังคมขององค์กร ทำให้สื่อสังคมยืดหยุ่นมากที่สุดในการตอบสนองต่อความต้องการของกลุ่มคนที่แตกต่างกัน เข้าร่วมเซิร์ฟเวอร์ที่มีกฎที่คุณเห็นด้วย หรือโฮสต์ของคุณเอง
       feature_moderation_title: การกลั่นกรองในแบบที่ควรจะเป็น
       follow_action: ติดตาม
       follow_step: การติดตามผู้คนที่น่าสนใจคือสิ่งที่ Mastodon ให้ความสำคัญ

From f0f144e96da62718e849a8dd5cd55ddbc03763ab Mon Sep 17 00:00:00 2001
From: Yamagishi Kazutoshi <ykzts@desire.sh>
Date: Mon, 10 Jun 2024 23:47:59 +0900
Subject: [PATCH 065/133] Add `customManagers:dockerfileVersions` to
 renovate.json5 (#30607)

---
 .github/renovate.json5 | 1 +
 Dockerfile             | 2 ++
 streaming/Dockerfile   | 1 +
 3 files changed, 4 insertions(+)

diff --git a/.github/renovate.json5 b/.github/renovate.json5
index 52f7c63e5..2cf7bec8e 100644
--- a/.github/renovate.json5
+++ b/.github/renovate.json5
@@ -2,6 +2,7 @@
   $schema: 'https://docs.renovatebot.com/renovate-schema.json',
   extends: [
     'config:recommended',
+    'customManagers:dockerfileVersions',
     ':labels(dependencies)',
     ':prConcurrentLimitNone', // Remove limit for open PRs at any time.
     ':prHourlyLimit2', // Rate limit PR creation to a maximum of two per hour.
diff --git a/Dockerfile b/Dockerfile
index 09aa8f2dd..cb5b87205 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -11,8 +11,10 @@ ARG TARGETPLATFORM=${TARGETPLATFORM}
 ARG BUILDPLATFORM=${BUILDPLATFORM}
 
 # Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"]
+# renovate: datasource=docker depName=docker.io/ruby
 ARG RUBY_VERSION="3.3.2"
 # # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
+# renovate: datasource=node-version depName=node
 ARG NODE_MAJOR_VERSION="20"
 # Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
 ARG DEBIAN_VERSION="bookworm"
diff --git a/streaming/Dockerfile b/streaming/Dockerfile
index 7f373e9cd..564e717a4 100644
--- a/streaming/Dockerfile
+++ b/streaming/Dockerfile
@@ -8,6 +8,7 @@ ARG TARGETPLATFORM=${TARGETPLATFORM}
 ARG BUILDPLATFORM=${BUILDPLATFORM}
 
 # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
+# renovate: datasource=node-version depName=node
 ARG NODE_MAJOR_VERSION="20"
 # Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
 ARG DEBIAN_VERSION="bookworm"

From 28f9a8f2ecabb0c087e27522217b78da732611c2 Mon Sep 17 00:00:00 2001
From: Daniel M Brasil <danielmbrasil@protonmail.com>
Date: Mon, 10 Jun 2024 11:52:33 -0300
Subject: [PATCH 066/133] Add Specs for Scheduled Status Model Validations 
 (#30585)

---
 spec/models/scheduled_status_spec.rb | 50 ++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 spec/models/scheduled_status_spec.rb

diff --git a/spec/models/scheduled_status_spec.rb b/spec/models/scheduled_status_spec.rb
new file mode 100644
index 000000000..15031a589
--- /dev/null
+++ b/spec/models/scheduled_status_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe ScheduledStatus do
+  let(:account) { Fabricate(:account) }
+
+  describe 'validations' do
+    context 'when scheduled_at is less than minimum offset' do
+      subject { Fabricate.build(:scheduled_status, scheduled_at: 4.minutes.from_now, account: account) }
+
+      it 'is not valid', :aggregate_failures do
+        expect(subject).to_not be_valid
+        expect(subject.errors[:scheduled_at]).to include(I18n.t('scheduled_statuses.too_soon'))
+      end
+    end
+
+    context 'when account has reached total limit' do
+      subject { Fabricate.build(:scheduled_status, account: account) }
+
+      before do
+        allow(account.scheduled_statuses).to receive(:count).and_return(described_class::TOTAL_LIMIT)
+      end
+
+      it 'is not valid', :aggregate_failures do
+        expect(subject).to_not be_valid
+        expect(subject.errors[:base]).to include(I18n.t('scheduled_statuses.over_total_limit', limit: ScheduledStatus::TOTAL_LIMIT))
+      end
+    end
+
+    context 'when account has reached daily limit' do
+      subject { Fabricate.build(:scheduled_status, account: account, scheduled_at: base_time + 10.minutes) }
+
+      let(:base_time) { Time.current.change(hour: 12) }
+
+      before do
+        stub_const('ScheduledStatus::DAILY_LIMIT', 3)
+
+        travel_to base_time do
+          Fabricate.times(ScheduledStatus::DAILY_LIMIT, :scheduled_status, account: account, scheduled_at: base_time + 1.hour)
+        end
+      end
+
+      it 'is not valid', :aggregate_failures do
+        expect(subject).to_not be_valid
+        expect(subject.errors[:base]).to include(I18n.t('scheduled_statuses.over_daily_limit', limit: ScheduledStatus::DAILY_LIMIT))
+      end
+    end
+  end
+end

From 92b3004bf31d91c94efae4957234a36aef10da59 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Mon, 10 Jun 2024 11:03:41 -0400
Subject: [PATCH 067/133] Reference constants from account validation specs
 (#30634)

---
 spec/models/account_spec.rb | 32 ++++++++++++++++++++------------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 2c5df198d..225929ae3 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -768,20 +768,20 @@ RSpec.describe Account do
         expect(account).to model_have_error_on_field(:username)
       end
 
-      it 'is invalid if the username is longer than 30 characters' do
-        account = Fabricate.build(:account, username: Faker::Lorem.characters(number: 31))
+      it 'is invalid if the username is longer than the character limit' do
+        account = Fabricate.build(:account, username: username_over_limit)
         account.valid?
         expect(account).to model_have_error_on_field(:username)
       end
 
-      it 'is invalid if the display name is longer than 30 characters' do
-        account = Fabricate.build(:account, display_name: Faker::Lorem.characters(number: 31))
+      it 'is invalid if the display name is longer than the character limit' do
+        account = Fabricate.build(:account, display_name: username_over_limit)
         account.valid?
         expect(account).to model_have_error_on_field(:display_name)
       end
 
-      it 'is invalid if the note is longer than 500 characters' do
-        account = Fabricate.build(:account, note: Faker::Lorem.characters(number: 501))
+      it 'is invalid if the note is longer than the character limit' do
+        account = Fabricate.build(:account, note: account_note_over_limit)
         account.valid?
         expect(account).to model_have_error_on_field(:note)
       end
@@ -814,24 +814,32 @@ RSpec.describe Account do
         expect(account).to model_have_error_on_field(:username)
       end
 
-      it 'is valid even if the username is longer than 30 characters' do
-        account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(number: 31))
+      it 'is valid even if the username is longer than the character limit' do
+        account = Fabricate.build(:account, domain: 'domain', username: username_over_limit)
         account.valid?
         expect(account).to_not model_have_error_on_field(:username)
       end
 
-      it 'is valid even if the display name is longer than 30 characters' do
-        account = Fabricate.build(:account, domain: 'domain', display_name: Faker::Lorem.characters(number: 31))
+      it 'is valid even if the display name is longer than the character limit' do
+        account = Fabricate.build(:account, domain: 'domain', display_name: username_over_limit)
         account.valid?
         expect(account).to_not model_have_error_on_field(:display_name)
       end
 
-      it 'is valid even if the note is longer than 500 characters' do
-        account = Fabricate.build(:account, domain: 'domain', note: Faker::Lorem.characters(number: 501))
+      it 'is valid even if the note is longer than the character limit' do
+        account = Fabricate.build(:account, domain: 'domain', note: account_note_over_limit)
         account.valid?
         expect(account).to_not model_have_error_on_field(:note)
       end
     end
+
+    def username_over_limit
+      'a' * described_class::USERNAME_LENGTH_LIMIT * 2
+    end
+
+    def account_note_over_limit
+      'a' * described_class::NOTE_LENGTH_LIMIT * 2
+    end
   end
 
   describe 'scopes' do

From 3e3f3d75805ec4209e0b12984626a5be0a9ba2e5 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Mon, 10 Jun 2024 11:04:01 -0400
Subject: [PATCH 068/133] Match report validation spec to extracted constant
 (#30633)

---
 spec/models/report_spec.rb | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb
index 0168268bc..d01d37bd8 100644
--- a/spec/models/report_spec.rb
+++ b/spec/models/report_spec.rb
@@ -123,14 +123,14 @@ describe Report do
   describe 'validations' do
     let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') }
 
-    it 'is invalid if comment is longer than 1000 characters only if reporter is local' do
-      report = Fabricate.build(:report, comment: Faker::Lorem.characters(number: 1001))
+    it 'is invalid if comment is longer than character limit and reporter is local' do
+      report = Fabricate.build(:report, comment: comment_over_limit)
       expect(report.valid?).to be false
       expect(report).to model_have_error_on_field(:comment)
     end
 
-    it 'is valid if comment is longer than 1000 characters and reporter is not local' do
-      report = Fabricate.build(:report, account: remote_account, comment: Faker::Lorem.characters(number: 1001))
+    it 'is valid if comment is longer than character limit and reporter is not local' do
+      report = Fabricate.build(:report, account: remote_account, comment: comment_over_limit)
       expect(report.valid?).to be true
     end
 
@@ -146,5 +146,9 @@ describe Report do
       expect(report.valid?).to be false
       expect(report).to model_have_error_on_field(:rule_ids)
     end
+
+    def comment_over_limit
+      'a' * described_class::COMMENT_SIZE_LIMIT * 2
+    end
   end
 end

From 28921a12fe0033c7231f42e95a2be80628844bb4 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Mon, 10 Jun 2024 11:22:26 -0400
Subject: [PATCH 069/133] Update macOS local dev setup instructions (#30641)

---
 README.md | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/README.md b/README.md
index f807120c1..4aca37673 100644
--- a/README.md
+++ b/README.md
@@ -86,18 +86,18 @@ A **Vagrant** configuration is included for development purposes. To use it, com
 - Run `vagrant ssh -c "cd /vagrant && bin/dev"`
 - Open `http://mastodon.local` in your browser
 
-### MacOS
+### macOS
 
-To set up **MacOS** for native development, complete the following steps:
+To set up **macOS** for native development, complete the following steps:
 
-- Use a Ruby version manager to install the specified version from `.ruby-version`
-- Run `bundle` to install required gems
-- Run `brew install postgresql@14 redis imagemagick libidn` to install required dependencies
-- Navigate to Mastodon's root directory and run `brew install nvm` then `nvm use` to use the version from `.nvmrc`
-- Run `yarn` to install required packages
-- Run `corepack enable && corepack prepare`
-- Run `RAILS_ENV=development bundle exec rails db:setup`
-- Finally, run `bin/dev` which will launch the local services via `overmind` (if installed) or `foreman`
+- Install [Homebrew] and run `brew install postgresql@14 redis imagemagick
+libidn nvm` to install the required project dependencies
+- Use a Ruby version manager to activate the ruby in `.ruby-version` and run
+  `nvm use` to activate the node version from `.nvmrc`
+- Run the `bin/setup` script, which will install the required ruby gems and node
+  packages and prepare the database for local development
+- Finally, run the `bin/dev` script which will launch services via `overmind`
+  (if installed) or `foreman`
 
 ### Docker
 
@@ -155,3 +155,4 @@ You should have received a copy of the GNU Affero General Public License along w
 [Development Containers]: https://containers.dev/supporting
 [Docker]: https://docs.docker.com
 [GitHub Codespaces]: https://docs.github.com/en/codespaces
+[Homebrew]: https://brew.sh

From 9bf2e2eda0ffea6382e161f7ebb43d73aaf658ca Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Mon, 10 Jun 2024 11:23:17 -0400
Subject: [PATCH 070/133] Extract `TEXT_LENGTH_LIMIT` constant in `Appeal`
 class (#30638)

---
 app/models/appeal.rb       |  4 +++-
 spec/models/appeal_spec.rb | 13 +++++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/app/models/appeal.rb b/app/models/appeal.rb
index 395056b76..fafa75e69 100644
--- a/app/models/appeal.rb
+++ b/app/models/appeal.rb
@@ -18,6 +18,8 @@
 class Appeal < ApplicationRecord
   MAX_STRIKE_AGE = 20.days
 
+  TEXT_LENGTH_LIMIT = 2_000
+
   belongs_to :account
   belongs_to :strike, class_name: 'AccountWarning', foreign_key: 'account_warning_id', inverse_of: :appeal
 
@@ -26,7 +28,7 @@ class Appeal < ApplicationRecord
     belongs_to :rejected_by_account
   end
 
-  validates :text, presence: true, length: { maximum: 2_000 }
+  validates :text, presence: true, length: { maximum: TEXT_LENGTH_LIMIT }
   validates :account_warning_id, uniqueness: true
 
   validate :validate_time_frame, on: :create
diff --git a/spec/models/appeal_spec.rb b/spec/models/appeal_spec.rb
index 12373a949..13ca3a2d9 100644
--- a/spec/models/appeal_spec.rb
+++ b/spec/models/appeal_spec.rb
@@ -3,6 +3,19 @@
 require 'rails_helper'
 
 describe Appeal do
+  describe 'Validations' do
+    it 'validates text length is under limit' do
+      appeal = Fabricate.build(
+        :appeal,
+        strike: Fabricate(:account_warning),
+        text: 'a' * described_class::TEXT_LENGTH_LIMIT * 2
+      )
+
+      expect(appeal).to_not be_valid
+      expect(appeal).to model_have_error_on_field(:text)
+    end
+  end
+
   describe 'scopes' do
     describe 'approved' do
       let(:approved_appeal) { Fabricate(:appeal, approved_at: 10.days.ago) }

From 9cc4040308a758d4b77961f4da79cf63a044fffe Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Mon, 10 Jun 2024 11:23:55 -0400
Subject: [PATCH 071/133] Extract `COMMENT_SIZE_LIMIT` constant in
 `AP::Activity::Flag` class (#30637)

---
 app/lib/activitypub/activity/flag.rb       |  4 +++-
 spec/lib/activitypub/activity/flag_spec.rb | 12 +++++++-----
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/app/lib/activitypub/activity/flag.rb b/app/lib/activitypub/activity/flag.rb
index 68ee43d0e..b7a412485 100644
--- a/app/lib/activitypub/activity/flag.rb
+++ b/app/lib/activitypub/activity/flag.rb
@@ -1,6 +1,8 @@
 # frozen_string_literal: true
 
 class ActivityPub::Activity::Flag < ActivityPub::Activity
+  COMMENT_SIZE_LIMIT = 5000
+
   def perform
     return if skip_reports?
 
@@ -38,6 +40,6 @@ class ActivityPub::Activity::Flag < ActivityPub::Activity
   end
 
   def report_comment
-    (@json['content'] || '')[0...5000]
+    (@json['content'] || '')[0...COMMENT_SIZE_LIMIT]
   end
 end
diff --git a/spec/lib/activitypub/activity/flag_spec.rb b/spec/lib/activitypub/activity/flag_spec.rb
index 8593d567f..426cd97df 100644
--- a/spec/lib/activitypub/activity/flag_spec.rb
+++ b/spec/lib/activitypub/activity/flag_spec.rb
@@ -54,7 +54,7 @@ RSpec.describe ActivityPub::Activity::Flag do
         }.with_indifferent_access, sender)
       end
 
-      let(:long_comment) { Faker::Lorem.characters(number: 6000) }
+      let(:long_comment) { 'a' * described_class::COMMENT_SIZE_LIMIT * 2 }
 
       before do
         subject.perform
@@ -63,10 +63,12 @@ RSpec.describe ActivityPub::Activity::Flag do
       it 'creates a report but with a truncated comment' do
         report = Report.find_by(account: sender, target_account: flagged)
 
-        expect(report).to_not be_nil
-        expect(report.comment.length).to eq 5000
-        expect(report.comment).to eq long_comment[0...5000]
-        expect(report.status_ids).to eq [status.id]
+        expect(report)
+          .to be_present
+          .and have_attributes(status_ids: [status.id])
+        expect(report.comment)
+          .to have_attributes(length: described_class::COMMENT_SIZE_LIMIT)
+          .and eq(long_comment[0...described_class::COMMENT_SIZE_LIMIT])
       end
     end
 

From 0e1110c947caf31ae650c73ef35adedebc16b28a Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Mon, 10 Jun 2024 16:08:04 -0400
Subject: [PATCH 072/133] Use `SECRET_KEY_BASE_DUMMY` feature as placeholder
 during asset compilation (#30505)

---
 .github/workflows/test-ruby.yml                 | 6 +-----
 Dockerfile                                      | 6 +-----
 config/environments/production.rb               | 6 +++++-
 config/initializers/active_record_encryption.rb | 5 +++++
 4 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml
index 5f2297381..8f05dcab3 100644
--- a/.github/workflows/test-ruby.yml
+++ b/.github/workflows/test-ruby.yml
@@ -28,11 +28,7 @@ jobs:
     env:
       RAILS_ENV: ${{ matrix.mode }}
       BUNDLE_WITH: ${{ matrix.mode }}
-      ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: precompile_placeholder
-      ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: precompile_placeholder
-      ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: precompile_placeholder
-      OTP_SECRET: precompile_placeholder
-      SECRET_KEY_BASE: precompile_placeholder
+      SECRET_KEY_BASE_DUMMY: 1
 
     steps:
       - uses: actions/checkout@v4
diff --git a/Dockerfile b/Dockerfile
index cb5b87205..2dc7602b2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -212,11 +212,7 @@ ARG TARGETPLATFORM
 
 RUN \
 # Use Ruby on Rails to create Mastodon assets
-  ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=precompile_placeholder \
-  ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=precompile_placeholder \
-  ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=precompile_placeholder \
-  OTP_SECRET=precompile_placeholder \
-  SECRET_KEY_BASE=precompile_placeholder \
+  SECRET_KEY_BASE_DUMMY=1 \
   bundle exec rails assets:precompile; \
 # Cleanup temporary files
   rm -fr /opt/mastodon/tmp;
diff --git a/config/environments/production.rb b/config/environments/production.rb
index a39843e95..6686a23d6 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -156,7 +156,11 @@ Rails.application.configure do
   }
 
   # TODO: Remove once devise-two-factor data migration complete
-  config.x.otp_secret = ENV.fetch('OTP_SECRET')
+  config.x.otp_secret = if ENV['SECRET_KEY_BASE_DUMMY']
+                          SecureRandom.hex(64)
+                        else
+                          ENV.fetch('OTP_SECRET')
+                        end
 
   # Enable DNS rebinding protection and other `Host` header attacks.
   # config.hosts = [
diff --git a/config/initializers/active_record_encryption.rb b/config/initializers/active_record_encryption.rb
index 900f3c68f..a83ca8076 100644
--- a/config/initializers/active_record_encryption.rb
+++ b/config/initializers/active_record_encryption.rb
@@ -5,6 +5,11 @@
   ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT
   ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
 ).each do |key|
+  if ENV['SECRET_KEY_BASE_DUMMY']
+    # Use placeholder value during production env asset compilation
+    ENV[key] = SecureRandom.hex(64)
+  end
+
   value = ENV.fetch(key) do
     abort <<~MESSAGE
 

From ef23abcf617813901e34c5dd9587ef7c88fd754d Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 11 Jun 2024 08:55:30 +0200
Subject: [PATCH 073/133] chore(deps): update dependency aws-sdk-s3 to v1.152.1
 (#30643)

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 4d16fea47..984bc32d4 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -109,7 +109,7 @@ GEM
     aws-sdk-kms (1.83.0)
       aws-sdk-core (~> 3, >= 3.197.0)
       aws-sigv4 (~> 1.1)
-    aws-sdk-s3 (1.152.0)
+    aws-sdk-s3 (1.152.1)
       aws-sdk-core (~> 3, >= 3.197.0)
       aws-sdk-kms (~> 1)
       aws-sigv4 (~> 1.8)

From cfd4823b65cc91e758ac9d6d97e367b19ca35691 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 11 Jun 2024 02:57:09 -0400
Subject: [PATCH 074/133] Use fabricator in follow_spec (#30642)

---
 spec/models/follow_spec.rb | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/spec/models/follow_spec.rb b/spec/models/follow_spec.rb
index c7743183c..9aa172b2f 100644
--- a/spec/models/follow_spec.rb
+++ b/spec/models/follow_spec.rb
@@ -36,16 +36,15 @@ RSpec.describe Follow do
     end
   end
 
-  describe 'recent' do
-    it 'sorts so that more recent follows comes earlier' do
-      follow0 = described_class.create!(account: alice, target_account: bob)
-      follow1 = described_class.create!(account: bob, target_account: alice)
+  describe '.recent' do
+    let!(:follow_earlier) { Fabricate(:follow) }
+    let!(:follow_later) { Fabricate(:follow) }
 
-      a = described_class.recent.to_a
+    it 'sorts with most recent follows first' do
+      results = described_class.recent
 
-      expect(a.size).to eq 2
-      expect(a[0]).to eq follow1
-      expect(a[1]).to eq follow0
+      expect(results.size).to eq 2
+      expect(results).to eq [follow_later, follow_earlier]
     end
   end
 

From 0dabda9beec11706fbef8cbf8a141266cc69590c Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 11 Jun 2024 09:14:52 +0200
Subject: [PATCH 075/133] New Crowdin Translations (automated) (#30646)

Co-authored-by: GitHub Actions <noreply@github.com>
---
 app/javascript/mastodon/locales/ar.json | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 68e32dd2a..6d67d354b 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -225,7 +225,11 @@
   "domain_pill.their_username": "مُعرّفُهم الفريد على الخادم. من الممكن العثور على مستخدمين بنفس اسم المستخدم على خوادم مختلفة.",
   "domain_pill.username": "اسم المستخدم",
   "domain_pill.whats_in_a_handle": "ما المقصود بالمُعرِّف؟",
+  "domain_pill.who_they_are": "بما أن المعالجات تقول من هو الشخص ومكان وجوده، يمكنك التفاعل مع الناس عبر الشبكة الاجتماعية لـ <button>ActivityPub-Power منصات</button>.",
+  "domain_pill.who_you_are": "لأن معالجتك تقول من أنت ومكان وجودك، يمكن الناس التفاعل معك عبر الشبكة الاجتماعية لـ <button>ActivityPub-Power منصات</button>.",
   "domain_pill.your_handle": "عنوانك الكامل:",
+  "domain_pill.your_server": "منزلك الرقمي، حيث تعيش جميع مشاركاتك. لا تحب هذا؟ إنقل الخوادم في أي وقت واخضر متابعينك أيضًا.",
+  "domain_pill.your_username": "معرفك الفريد على هذا الخادم. من الممكن العثور على مستخدمين بنفس إسم المستخدم على خوادم مختلفة.",
   "embed.instructions": "يمكنكم إدماج هذا المنشور على موقعكم الإلكتروني عن طريق نسخ الشفرة أدناه.",
   "embed.preview": "إليك ما سيبدو عليه:",
   "emoji_button.activity": "الأنشطة",
@@ -262,6 +266,7 @@
   "empty_column.list": "هذه القائمة فارغة مؤقتا و لكن سوف تمتلئ تدريجيا عندما يبدأ الأعضاء المُنتَمين إليها بنشر منشورات.",
   "empty_column.lists": "ليس عندك أية قائمة بعد. سوف تظهر قوائمك هنا إن قمت بإنشاء واحدة.",
   "empty_column.mutes": "لم تقم بكتم أي مستخدم بعد.",
+  "empty_column.notification_requests": "لا يوجد شيء هنا. عندما تتلقى إشعارات جديدة، سوف تظهر هنا وفقًا لإعداداتك.",
   "empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.",
   "empty_column.public": "لا يوجد أي شيء هنا! قم بنشر شيء ما للعامة، أو اتبع المستخدمين الآخرين المتواجدين على الخوادم الأخرى لملء خيط المحادثات",
   "error.unexpected_crash.explanation": "نظرا لوجود خطأ في التعليمات البرمجية أو مشكلة توافق مع المتصفّح، تعذر عرض هذه الصفحة بشكل صحيح.",
@@ -292,6 +297,8 @@
   "filter_modal.select_filter.subtitle": "استخدم فئة موجودة أو قم بإنشاء فئة جديدة",
   "filter_modal.select_filter.title": "تصفية هذا المنشور",
   "filter_modal.title.status": "تصفية منشور",
+  "filtered_notifications_banner.mentions": "{count, plural, one {إشارة} two {إشارتين} few {# إشارات} other {# إشارة}}",
+  "filtered_notifications_banner.pending_requests": "إشعارات من {count, plural, zero {}=0 {لا أحد} one {شخص واحد قد تعرفه} two {شخصين قد تعرفهما} few {# أشخاص قد تعرفهم} many {# شخص قد تعرفهم} other {# شخص قد تعرفهم}}",
   "filtered_notifications_banner.title": "الإشعارات المصفاة",
   "firehose.all": "الكل",
   "firehose.local": "هذا الخادم",
@@ -301,6 +308,8 @@
   "follow_requests.unlocked_explanation": "حتى وإن كان حسابك غير مقفل، يعتقد فريق {domain} أنك قد ترغب في مراجعة طلبات المتابعة من هذه الحسابات يدوياً.",
   "follow_suggestions.curated_suggestion": "اختيار الموظفين",
   "follow_suggestions.dismiss": "لا تُظهرها مجدّدًا",
+  "follow_suggestions.featured_longer": "مختار يدوياً من قِبل فريق {domain}",
+  "follow_suggestions.friends_of_friends_longer": "مشهور بين الأشخاص الذين تتابعهم",
   "follow_suggestions.hints.featured": "تم اختيار هذا الملف الشخصي يدوياً من قبل فريق {domain}.",
   "follow_suggestions.hints.friends_of_friends": "هذا الملف الشخصي مشهور بين الأشخاص الذين تتابعهم.",
   "follow_suggestions.hints.most_followed": "هذا الملف الشخصي هو واحد من الأكثر متابعة على {domain}.",
@@ -405,6 +414,7 @@
   "limited_account_hint.action": "إظهار الملف التعريفي على أي حال",
   "limited_account_hint.title": "تم إخفاء هذا الملف الشخصي من قبل مشرفي {domain}.",
   "link_preview.author": "مِن {name}",
+  "link_preview.more_from_author": "المزيد من {name}",
   "lists.account.add": "أضف إلى القائمة",
   "lists.account.remove": "احذف من القائمة",
   "lists.delete": "احذف القائمة",
@@ -465,10 +475,13 @@
   "notification.follow_request": "لقد طلب {name} متابعتك",
   "notification.mention": "{name} ذكرك",
   "notification.moderation-warning.learn_more": "اعرف المزيد",
+  "notification.moderation_warning": "لقد تلقيت تحذيرًا بالإشراف",
+  "notification.moderation_warning.action_delete_statuses": "تم إزالة بعض مشاركاتك.",
   "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_silence": "لقد تم تقييد حسابك.",
   "notification.moderation_warning.action_suspend": "لقد تم تعليق حسابك.",
   "notification.own_poll": "انتهى استطلاعك للرأي",
   "notification.poll": "لقد انتهى استطلاع رأي شاركتَ فيه",

From b2496177e00af2b9ce73b378233b2ef3de14cbe4 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 11 Jun 2024 03:35:30 -0400
Subject: [PATCH 076/133] Use correct params in `v1/admin/domain_allows` spec
 (#30378)

---
 spec/requests/api/v1/admin/domain_allows_spec.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/spec/requests/api/v1/admin/domain_allows_spec.rb b/spec/requests/api/v1/admin/domain_allows_spec.rb
index 662a8f9a8..b8f0b0055 100644
--- a/spec/requests/api/v1/admin/domain_allows_spec.rb
+++ b/spec/requests/api/v1/admin/domain_allows_spec.rb
@@ -113,7 +113,7 @@ RSpec.describe 'Domain Allows' do
     end
 
     context 'with invalid domain name' do
-      let(:params) { 'foo bar' }
+      let(:params) { { domain: 'foo bar' } }
 
       it 'returns http unprocessable entity' do
         subject

From edf6d64eebdf4c1651f09c0e314d46b7436b46df Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 11 Jun 2024 03:36:46 -0400
Subject: [PATCH 077/133] Use correct params in
 `settings/preferences/appearance` spec (#30379)

---
 .../settings/preferences/appearance_controller_spec.rb       | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/spec/controllers/settings/preferences/appearance_controller_spec.rb b/spec/controllers/settings/preferences/appearance_controller_spec.rb
index 261c426ac..84b827725 100644
--- a/spec/controllers/settings/preferences/appearance_controller_spec.rb
+++ b/spec/controllers/settings/preferences/appearance_controller_spec.rb
@@ -23,8 +23,11 @@ describe Settings::Preferences::AppearanceController do
   end
 
   describe 'PUT #update' do
+    subject { put :update, params: { user: { settings_attributes: { theme: 'contrast' } } } }
+
     it 'redirects correctly' do
-      put :update, params: { user: { setting_theme: 'contrast' } }
+      expect { subject }
+        .to change { user.reload.settings.theme }.to('contrast')
 
       expect(response).to redirect_to(settings_preferences_appearance_path)
     end

From 88cfc4056de1f7ab86011eb80b904cd9cd6dc754 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 11 Jun 2024 03:42:15 -0400
Subject: [PATCH 078/133] Extract method to generate series of days in measure
 sql classes (#29928)

---
 .../admin/metrics/measure/instance_accounts_measure.rb    | 2 +-
 .../admin/metrics/measure/instance_followers_measure.rb   | 2 +-
 app/lib/admin/metrics/measure/instance_follows_measure.rb | 2 +-
 .../metrics/measure/instance_media_attachments_measure.rb | 2 +-
 app/lib/admin/metrics/measure/instance_reports_measure.rb | 2 +-
 .../admin/metrics/measure/instance_statuses_measure.rb    | 2 +-
 app/lib/admin/metrics/measure/new_users_measure.rb        | 2 +-
 app/lib/admin/metrics/measure/opened_reports_measure.rb   | 2 +-
 app/lib/admin/metrics/measure/query_helper.rb             | 8 ++++++++
 app/lib/admin/metrics/measure/resolved_reports_measure.rb | 2 +-
 app/lib/admin/metrics/measure/tag_servers_measure.rb      | 2 +-
 11 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/app/lib/admin/metrics/measure/instance_accounts_measure.rb b/app/lib/admin/metrics/measure/instance_accounts_measure.rb
index 3d081fdd9..746780ee7 100644
--- a/app/lib/admin/metrics/measure/instance_accounts_measure.rb
+++ b/app/lib/admin/metrics/measure/instance_accounts_measure.rb
@@ -43,7 +43,7 @@ class Admin::Metrics::Measure::InstanceAccountsMeasure < Admin::Metrics::Measure
         SELECT count(*) FROM new_accounts
       ) AS value
       FROM (
-        SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
+        #{generated_series_days}
       ) AS axis
     SQL
   end
diff --git a/app/lib/admin/metrics/measure/instance_followers_measure.rb b/app/lib/admin/metrics/measure/instance_followers_measure.rb
index 378c6754d..0693d5a64 100644
--- a/app/lib/admin/metrics/measure/instance_followers_measure.rb
+++ b/app/lib/admin/metrics/measure/instance_followers_measure.rb
@@ -44,7 +44,7 @@ class Admin::Metrics::Measure::InstanceFollowersMeasure < Admin::Metrics::Measur
         SELECT count(*) FROM new_followers
       ) AS value
       FROM (
-        SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
+        #{generated_series_days}
       ) AS axis
     SQL
   end
diff --git a/app/lib/admin/metrics/measure/instance_follows_measure.rb b/app/lib/admin/metrics/measure/instance_follows_measure.rb
index e213348fb..90d381935 100644
--- a/app/lib/admin/metrics/measure/instance_follows_measure.rb
+++ b/app/lib/admin/metrics/measure/instance_follows_measure.rb
@@ -44,7 +44,7 @@ class Admin::Metrics::Measure::InstanceFollowsMeasure < Admin::Metrics::Measure:
         SELECT count(*) FROM new_follows
       ) AS value
       FROM (
-        SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
+        #{generated_series_days}
       ) AS axis
     SQL
   end
diff --git a/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb b/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb
index 1d2dbbe41..89f8b4149 100644
--- a/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb
+++ b/app/lib/admin/metrics/measure/instance_media_attachments_measure.rb
@@ -53,7 +53,7 @@ class Admin::Metrics::Measure::InstanceMediaAttachmentsMeasure < Admin::Metrics:
         SELECT COALESCE(SUM(size), 0) FROM new_media_attachments
       ) AS value
       FROM (
-        SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
+        #{generated_series_days}
       ) AS axis
     SQL
   end
diff --git a/app/lib/admin/metrics/measure/instance_reports_measure.rb b/app/lib/admin/metrics/measure/instance_reports_measure.rb
index 9da3d53e3..5f58387a6 100644
--- a/app/lib/admin/metrics/measure/instance_reports_measure.rb
+++ b/app/lib/admin/metrics/measure/instance_reports_measure.rb
@@ -44,7 +44,7 @@ class Admin::Metrics::Measure::InstanceReportsMeasure < Admin::Metrics::Measure:
         SELECT count(*) FROM new_reports
       ) AS value
       FROM (
-        SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
+        #{generated_series_days}
       ) AS axis
     SQL
   end
diff --git a/app/lib/admin/metrics/measure/instance_statuses_measure.rb b/app/lib/admin/metrics/measure/instance_statuses_measure.rb
index b918a30a5..5873c6e71 100644
--- a/app/lib/admin/metrics/measure/instance_statuses_measure.rb
+++ b/app/lib/admin/metrics/measure/instance_statuses_measure.rb
@@ -45,7 +45,7 @@ class Admin::Metrics::Measure::InstanceStatusesMeasure < Admin::Metrics::Measure
         SELECT count(*) FROM new_statuses
       ) AS value
       FROM (
-        SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
+        #{generated_series_days}
       ) AS axis
     SQL
   end
diff --git a/app/lib/admin/metrics/measure/new_users_measure.rb b/app/lib/admin/metrics/measure/new_users_measure.rb
index 6837c14c8..32057154d 100644
--- a/app/lib/admin/metrics/measure/new_users_measure.rb
+++ b/app/lib/admin/metrics/measure/new_users_measure.rb
@@ -32,7 +32,7 @@ class Admin::Metrics::Measure::NewUsersMeasure < Admin::Metrics::Measure::BaseMe
         SELECT count(*) FROM new_users
       ) AS value
       FROM (
-        SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
+        #{generated_series_days}
       ) AS axis
     SQL
   end
diff --git a/app/lib/admin/metrics/measure/opened_reports_measure.rb b/app/lib/admin/metrics/measure/opened_reports_measure.rb
index c395c4634..47de38bbe 100644
--- a/app/lib/admin/metrics/measure/opened_reports_measure.rb
+++ b/app/lib/admin/metrics/measure/opened_reports_measure.rb
@@ -32,7 +32,7 @@ class Admin::Metrics::Measure::OpenedReportsMeasure < Admin::Metrics::Measure::B
         SELECT count(*) FROM new_reports
       ) AS value
       FROM (
-        SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
+        #{generated_series_days}
       ) AS axis
     SQL
   end
diff --git a/app/lib/admin/metrics/measure/query_helper.rb b/app/lib/admin/metrics/measure/query_helper.rb
index 969065f73..5969a96ba 100644
--- a/app/lib/admin/metrics/measure/query_helper.rb
+++ b/app/lib/admin/metrics/measure/query_helper.rb
@@ -15,6 +15,14 @@ module Admin::Metrics::Measure::QueryHelper
     ActiveRecord::Base.sanitize_sql_array(sql_array)
   end
 
+  def generated_series_days
+    Arel.sql(
+      <<~SQL.squish
+        SELECT generate_series(timestamp :start_at, :end_at, '1 day')::date AS period
+      SQL
+    )
+  end
+
   def account_domain_sql(include_subdomains)
     if include_subdomains
       "accounts.domain IN (SELECT domain FROM instances WHERE reverse('.' || domain) LIKE reverse('.' || :domain::text))"
diff --git a/app/lib/admin/metrics/measure/resolved_reports_measure.rb b/app/lib/admin/metrics/measure/resolved_reports_measure.rb
index 780db75a1..ecfd779c8 100644
--- a/app/lib/admin/metrics/measure/resolved_reports_measure.rb
+++ b/app/lib/admin/metrics/measure/resolved_reports_measure.rb
@@ -32,7 +32,7 @@ class Admin::Metrics::Measure::ResolvedReportsMeasure < Admin::Metrics::Measure:
         SELECT count(*) FROM resolved_reports
       ) AS value
       FROM (
-        SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
+        #{generated_series_days}
       ) AS axis
     SQL
   end
diff --git a/app/lib/admin/metrics/measure/tag_servers_measure.rb b/app/lib/admin/metrics/measure/tag_servers_measure.rb
index f273d739d..5db107606 100644
--- a/app/lib/admin/metrics/measure/tag_servers_measure.rb
+++ b/app/lib/admin/metrics/measure/tag_servers_measure.rb
@@ -40,7 +40,7 @@ class Admin::Metrics::Measure::TagServersMeasure < Admin::Metrics::Measure::Base
         SELECT COUNT(*) FROM tag_servers
       ) AS value
       FROM (
-        SELECT generate_series(date_trunc('day', :start_at::timestamp)::date, date_trunc('day', :end_at::timestamp)::date, interval '1 day') AS period
+        #{generated_series_days}
       ) as axis
     SQL
   end

From 1622f7aeb9e911d43296caef45e17181652c9c0e Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 11 Jun 2024 03:48:42 -0400
Subject: [PATCH 079/133] Remove duplicate fabricator validity checks (#29667)

---
 spec/models/import_spec.rb | 5 -----
 spec/models/poll_spec.rb   | 8 --------
 2 files changed, 13 deletions(-)

diff --git a/spec/models/import_spec.rb b/spec/models/import_spec.rb
index 3605f0b9b..10df5f8c0 100644
--- a/spec/models/import_spec.rb
+++ b/spec/models/import_spec.rb
@@ -8,11 +8,6 @@ RSpec.describe Import do
   let(:data) { attachment_fixture('imports.txt') }
 
   describe 'validations' do
-    it 'has a valid parameters' do
-      import = described_class.create(account: account, type: type, data: data)
-      expect(import).to be_valid
-    end
-
     it 'is invalid without an type' do
       import = described_class.create(account: account, data: data)
       expect(import).to model_have_error_on_field(:type)
diff --git a/spec/models/poll_spec.rb b/spec/models/poll_spec.rb
index 5aa5548cc..ebcc45907 100644
--- a/spec/models/poll_spec.rb
+++ b/spec/models/poll_spec.rb
@@ -31,14 +31,6 @@ describe Poll do
   end
 
   describe 'validations' do
-    context 'when valid' do
-      let(:poll) { Fabricate.build(:poll) }
-
-      it 'is valid with valid attributes' do
-        expect(poll).to be_valid
-      end
-    end
-
     context 'when not valid' do
       let(:poll) { Fabricate.build(:poll, expires_at: nil) }
 

From 665f6f09a07fa9d1d813e8343f1687b369e7f3dc Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 11 Jun 2024 04:50:51 -0400
Subject: [PATCH 080/133] Add expired/revoked scopes for doorkeeper models via
 extension modules (#29936)

---
 app/lib/access_grant_extension.rb      | 10 ++++++++++
 app/lib/access_token_extension.rb      |  4 ++++
 app/lib/vacuum/access_tokens_vacuum.rb |  8 ++++----
 app/models/web/push_subscription.rb    |  2 +-
 config/application.rb                  |  1 +
 5 files changed, 20 insertions(+), 5 deletions(-)
 create mode 100644 app/lib/access_grant_extension.rb

diff --git a/app/lib/access_grant_extension.rb b/app/lib/access_grant_extension.rb
new file mode 100644
index 000000000..bf8f5ae25
--- /dev/null
+++ b/app/lib/access_grant_extension.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module AccessGrantExtension
+  extend ActiveSupport::Concern
+
+  included do
+    scope :expired, -> { where.not(expires_in: nil).where('created_at + MAKE_INTERVAL(secs => expires_in) < NOW()') }
+    scope :revoked, -> { where.not(revoked_at: nil).where(revoked_at: ...Time.now.utc) }
+  end
+end
diff --git a/app/lib/access_token_extension.rb b/app/lib/access_token_extension.rb
index 4e9585dd1..6e06f988a 100644
--- a/app/lib/access_token_extension.rb
+++ b/app/lib/access_token_extension.rb
@@ -9,6 +9,10 @@ module AccessTokenExtension
     has_many :web_push_subscriptions, class_name: 'Web::PushSubscription', inverse_of: :access_token
 
     after_commit :push_to_streaming_api
+
+    scope :expired, -> { where.not(expires_in: nil).where('created_at + MAKE_INTERVAL(secs => expires_in) < NOW()') }
+    scope :not_revoked, -> { where(revoked_at: nil) }
+    scope :revoked, -> { where.not(revoked_at: nil).where(revoked_at: ...Time.now.utc) }
   end
 
   def revoke(clock = Time)
diff --git a/app/lib/vacuum/access_tokens_vacuum.rb b/app/lib/vacuum/access_tokens_vacuum.rb
index a224f6d63..281ae22bf 100644
--- a/app/lib/vacuum/access_tokens_vacuum.rb
+++ b/app/lib/vacuum/access_tokens_vacuum.rb
@@ -9,12 +9,12 @@ class Vacuum::AccessTokensVacuum
   private
 
   def vacuum_revoked_access_tokens!
-    Doorkeeper::AccessToken.where.not(expires_in: nil).where('created_at + make_interval(secs => expires_in) < NOW()').in_batches.delete_all
-    Doorkeeper::AccessToken.where.not(revoked_at: nil).where('revoked_at < NOW()').in_batches.delete_all
+    Doorkeeper::AccessToken.expired.in_batches.delete_all
+    Doorkeeper::AccessToken.revoked.in_batches.delete_all
   end
 
   def vacuum_revoked_access_grants!
-    Doorkeeper::AccessGrant.where.not(expires_in: nil).where('created_at + make_interval(secs => expires_in) < NOW()').in_batches.delete_all
-    Doorkeeper::AccessGrant.where.not(revoked_at: nil).where('revoked_at < NOW()').in_batches.delete_all
+    Doorkeeper::AccessGrant.expired.in_batches.delete_all
+    Doorkeeper::AccessGrant.revoked.in_batches.delete_all
   end
 end
diff --git a/app/models/web/push_subscription.rb b/app/models/web/push_subscription.rb
index b482ad3af..ddfd08146 100644
--- a/app/models/web/push_subscription.rb
+++ b/app/models/web/push_subscription.rb
@@ -75,7 +75,7 @@ class Web::PushSubscription < ApplicationRecord
 
   class << self
     def unsubscribe_for(application_id, resource_owner)
-      access_token_ids = Doorkeeper::AccessToken.where(application_id: application_id, resource_owner_id: resource_owner.id, revoked_at: nil).pluck(:id)
+      access_token_ids = Doorkeeper::AccessToken.where(application_id: application_id, resource_owner_id: resource_owner.id).not_revoked.pluck(:id)
       where(access_token_id: access_token_ids).delete_all
     end
   end
diff --git a/config/application.rb b/config/application.rb
index b3a9b99ff..65407da05 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -115,6 +115,7 @@ module Mastodon
       Doorkeeper::AuthorizationsController.layout 'modal'
       Doorkeeper::AuthorizedApplicationsController.layout 'admin'
       Doorkeeper::Application.include ApplicationExtension
+      Doorkeeper::AccessGrant.include AccessGrantExtension
       Doorkeeper::AccessToken.include AccessTokenExtension
       Devise::FailureApp.include AbstractController::Callbacks
       Devise::FailureApp.include Localized

From 410370eecdcc6ad7aeac30c48957d3b044e7cabe Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 11 Jun 2024 05:40:47 -0400
Subject: [PATCH 081/133] Extract `PERMITTED_PARAMS` constant from
 `admin/domain_blocks` controller (#30380)

---
 .../admin/domain_blocks_controller.rb         | 22 +++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb
index 325b33df8..16a8cb9ee 100644
--- a/app/controllers/admin/domain_blocks_controller.rb
+++ b/app/controllers/admin/domain_blocks_controller.rb
@@ -4,6 +4,18 @@ module Admin
   class DomainBlocksController < BaseController
     before_action :set_domain_block, only: [:destroy, :edit, :update]
 
+    PERMITTED_PARAMS = %i(
+      domain
+      obfuscate
+      private_comment
+      public_comment
+      reject_media
+      reject_reports
+      severity
+    ).freeze
+
+    PERMITTED_UPDATE_PARAMS = PERMITTED_PARAMS.without(:domain).freeze
+
     def batch
       authorize :domain_block, :create?
       @form = Form::DomainBlockBatch.new(form_domain_block_batch_params.merge(current_account: current_account, action: action_from_button))
@@ -88,11 +100,17 @@ module Admin
     end
 
     def update_params
-      params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
+      params
+        .require(:domain_block)
+        .slice(*PERMITTED_UPDATE_PARAMS)
+        .permit(*PERMITTED_UPDATE_PARAMS)
     end
 
     def resource_params
-      params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
+      params
+        .require(:domain_block)
+        .slice(*PERMITTED_PARAMS)
+        .permit(*PERMITTED_PARAMS)
     end
 
     def form_domain_block_batch_params

From f48f39a7679667da51171a77a0e1bcbc7512cd36 Mon Sep 17 00:00:00 2001
From: David Roetzel <david@roetzel.de>
Date: Tue, 11 Jun 2024 14:54:37 +0200
Subject: [PATCH 082/133] Fix cutoff of instance name (#30598)

---
 app/javascript/styles/mastodon/forms.scss | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index f6ec44fb5..26bb2bee1 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -613,9 +613,10 @@ code {
       font-family: inherit;
       pointer-events: none;
       cursor: default;
-      max-width: 140px;
+      max-width: 50%;
       white-space: nowrap;
       overflow: hidden;
+      text-overflow: ellipsis;
 
       &::after {
         content: '';

From 328d3a87f5749b5b9ea2c17289fc7a53dbaa6d7b Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Tue, 11 Jun 2024 15:58:10 +0200
Subject: [PATCH 083/133] Fix libvips color extraction when multiple maxima
 differ only on blue component (#30632)

---
 lib/paperclip/color_extractor.rb | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/lib/paperclip/color_extractor.rb b/lib/paperclip/color_extractor.rb
index 0f168d233..378af0961 100644
--- a/lib/paperclip/color_extractor.rb
+++ b/lib/paperclip/color_extractor.rb
@@ -122,26 +122,28 @@ module Paperclip
 
       colors['out_array'].zip(colors['x_array'], colors['y_array']).map do |v, x, y|
         rgb_from_xyv(histogram, x, y, v)
-      end.reverse
+      end.flatten.reverse.uniq
     end
 
     # rubocop:disable Naming/MethodParameterName
     def rgb_from_xyv(image, x, y, v)
       pixel = image.getpoint(x, y)
 
-      # Unfortunately, we only have the first 2 dimensions, so try to
-      # guess the third one by looking up the value
+      # As we only have the first 2 dimensions for this maximum, we
+      # can't distinguish with different maxima with the same `r` and `g`
+      # values but different `b` values.
+      #
+      # Therefore, we return an array of maxima, which is always non-empty,
+      # but may contain multiple colors with the same values.
 
-      # NOTE: this means that if multiple bins with the same `r` and `g`
-      # components have the same number of occurrences, we will always return
-      # the one with the lowest `b` value. This means that in case of a tie,
-      # we will return the same color twice and skip the ones it tied with.
-      z = pixel.find_index(v)
+      pixel.filter_map.with_index do |pv, z|
+        next if pv != v
 
-      r = (x + 0.5) * 256 / BINS
-      g = (y + 0.5) * 256 / BINS
-      b = (z + 0.5) * 256 / BINS
-      ColorDiff::Color::RGB.new(r, g, b)
+        r = (x + 0.5) * 256 / BINS
+        g = (y + 0.5) * 256 / BINS
+        b = (z + 0.5) * 256 / BINS
+        ColorDiff::Color::RGB.new(r, g, b)
+      end
     end
 
     def w3c_contrast(color1, color2)

From b124dff1748797e54256e1c8161d18513b921458 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 11 Jun 2024 15:58:40 +0200
Subject: [PATCH 084/133] chore(deps): update opentelemetry-ruby (non-major)
 (#30648)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 Gemfile.lock | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index 984bc32d4..c2253fe19 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -498,6 +498,10 @@ GEM
       opentelemetry-semantic_conventions
     opentelemetry-helpers-sql-obfuscation (0.1.0)
       opentelemetry-common (~> 0.20)
+    opentelemetry-instrumentation-action_mailer (0.1.0)
+      opentelemetry-api (~> 1.0)
+      opentelemetry-instrumentation-active_support (~> 0.1)
+      opentelemetry-instrumentation-base (~> 0.22.1)
     opentelemetry-instrumentation-action_pack (0.9.0)
       opentelemetry-api (~> 1.0)
       opentelemetry-instrumentation-base (~> 0.22.1)
@@ -551,8 +555,9 @@ GEM
       opentelemetry-api (~> 1.0)
       opentelemetry-common (~> 0.20.0)
       opentelemetry-instrumentation-base (~> 0.22.1)
-    opentelemetry-instrumentation-rails (0.30.1)
+    opentelemetry-instrumentation-rails (0.30.2)
       opentelemetry-api (~> 1.0)
+      opentelemetry-instrumentation-action_mailer (~> 0.1.0)
       opentelemetry-instrumentation-action_pack (~> 0.9.0)
       opentelemetry-instrumentation-action_view (~> 0.7.0)
       opentelemetry-instrumentation-active_job (~> 0.7.0)

From 62d070c438a2cccb6af486a02d8c6839d642c827 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 11 Jun 2024 09:59:56 -0400
Subject: [PATCH 085/133] Check both before/after state in `AccountDomainBlock`
 spec (#30640)

---
 spec/models/account_domain_block_spec.rb | 24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/spec/models/account_domain_block_spec.rb b/spec/models/account_domain_block_spec.rb
index 10bd57936..d994403b8 100644
--- a/spec/models/account_domain_block_spec.rb
+++ b/spec/models/account_domain_block_spec.rb
@@ -3,22 +3,30 @@
 require 'rails_helper'
 
 RSpec.describe AccountDomainBlock do
+  let(:account) { Fabricate(:account) }
+
   it 'removes blocking cache after creation' do
-    account = Fabricate(:account)
     Rails.cache.write("exclude_domains_for:#{account.id}", 'a.domain.already.blocked')
 
-    described_class.create!(account: account, domain: 'a.domain.blocked.later')
-
-    expect(Rails.cache.exist?("exclude_domains_for:#{account.id}")).to be false
+    expect { block_domain_for_account('a.domain.blocked.later') }
+      .to change { account_has_exclude_domains_cache? }.to(false)
   end
 
   it 'removes blocking cache after destruction' do
-    account = Fabricate(:account)
-    block = described_class.create!(account: account, domain: 'domain')
+    block = block_domain_for_account('domain')
     Rails.cache.write("exclude_domains_for:#{account.id}", 'domain')
 
-    block.destroy!
+    expect { block.destroy! }
+      .to change { account_has_exclude_domains_cache? }.to(false)
+  end
 
-    expect(Rails.cache.exist?("exclude_domains_for:#{account.id}")).to be false
+  private
+
+  def block_domain_for_account(domain)
+    Fabricate(:account_domain_block, account: account, domain: domain)
+  end
+
+  def account_has_exclude_domains_cache?
+    Rails.cache.exist?("exclude_domains_for:#{account.id}")
   end
 end

From 978601a0ae556c4e214df8f6d73181c2a6359531 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 11 Jun 2024 11:29:41 -0400
Subject: [PATCH 086/133] Extract permitted params constant in v1/admin/tags
 (#30652)

---
 app/controllers/api/v1/admin/tags_controller.rb | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/app/controllers/api/v1/admin/tags_controller.rb b/app/controllers/api/v1/admin/tags_controller.rb
index 67d987d0e..283383acb 100644
--- a/app/controllers/api/v1/admin/tags_controller.rb
+++ b/app/controllers/api/v1/admin/tags_controller.rb
@@ -13,6 +13,13 @@ class Api::V1::Admin::TagsController < Api::BaseController
 
   LIMIT = 100
 
+  PERMITTED_PARAMS = %i(
+    display_name
+    listable
+    trendable
+    usable
+  ).freeze
+
   def index
     authorize :tag, :index?
     render json: @tags, each_serializer: REST::Admin::TagSerializer
@@ -40,7 +47,9 @@ class Api::V1::Admin::TagsController < Api::BaseController
   end
 
   def tag_params
-    params.permit(:display_name, :trendable, :usable, :listable)
+    params
+      .slice(*PERMITTED_PARAMS)
+      .permit(*PERMITTED_PARAMS)
   end
 
   def next_path

From 921b0db5440cc6e0bc990d6f0186c43aaa887fe9 Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Tue, 11 Jun 2024 17:29:45 +0200
Subject: [PATCH 087/133] Add `noindex` meta tag and `rel=canonical` link to
 redirect interstitials (#30651)

---
 app/views/redirects/show.html.haml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/app/views/redirects/show.html.haml b/app/views/redirects/show.html.haml
index 0d09387a9..64436e05d 100644
--- a/app/views/redirects/show.html.haml
+++ b/app/views/redirects/show.html.haml
@@ -1,3 +1,7 @@
+- content_for :header_tags do
+  %meta{ name: 'robots', content: 'noindex, noarchive' }/
+  %link{ rel: 'canonical', href: @redirect_path }
+
 .redirect
   .redirect__logo
     = link_to render_logo, root_path

From d818ddd6870094e89e58ef61f37da4cb73935856 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 11 Jun 2024 11:36:21 -0400
Subject: [PATCH 088/133] Extract `SIGN_COUNT_LIMIT` constant in
 `WebauthnCredential` class (#30636)

---
 app/models/webauthn_credential.rb                             | 4 +++-
 ...bauthn_credentials_spec.rb => webauthn_credential_spec.rb} | 4 ++--
 2 files changed, 5 insertions(+), 3 deletions(-)
 rename spec/models/{webauthn_credentials_spec.rb => webauthn_credential_spec.rb} (95%)

diff --git a/app/models/webauthn_credential.rb b/app/models/webauthn_credential.rb
index 4fa31ece5..d7ed1b9d4 100644
--- a/app/models/webauthn_credential.rb
+++ b/app/models/webauthn_credential.rb
@@ -15,9 +15,11 @@
 #
 
 class WebauthnCredential < ApplicationRecord
+  SIGN_COUNT_LIMIT = (2**63)
+
   validates :external_id, :public_key, :nickname, :sign_count, presence: true
   validates :external_id, uniqueness: true
   validates :nickname, uniqueness: { scope: :user_id }
   validates :sign_count,
-            numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: (2**63) - 1 }
+            numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: SIGN_COUNT_LIMIT - 1 }
 end
diff --git a/spec/models/webauthn_credentials_spec.rb b/spec/models/webauthn_credential_spec.rb
similarity index 95%
rename from spec/models/webauthn_credentials_spec.rb
rename to spec/models/webauthn_credential_spec.rb
index 9631245e1..23f0229a6 100644
--- a/spec/models/webauthn_credentials_spec.rb
+++ b/spec/models/webauthn_credential_spec.rb
@@ -71,8 +71,8 @@ RSpec.describe WebauthnCredential do
       expect(webauthn_credential).to model_have_error_on_field(:sign_count)
     end
 
-    it 'is invalid if sign_count is greater 2**63 - 1' do
-      webauthn_credential = Fabricate.build(:webauthn_credential, sign_count: 2**63)
+    it 'is invalid if sign_count is greater than the limit' do
+      webauthn_credential = Fabricate.build(:webauthn_credential, sign_count: (described_class::SIGN_COUNT_LIMIT * 2))
 
       webauthn_credential.valid?
 

From cec8e34b250c368ec6c4a5acb75eed311aa901bf Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Tue, 11 Jun 2024 16:29:00 -0400
Subject: [PATCH 089/133] Remove unused CI env vars (#30660)

---
 .github/workflows/test-migrations-one-step.yml | 1 -
 .github/workflows/test-migrations-two-step.yml | 1 -
 2 files changed, 2 deletions(-)

diff --git a/.github/workflows/test-migrations-one-step.yml b/.github/workflows/test-migrations-one-step.yml
index 1ff5cc06b..104e1b067 100644
--- a/.github/workflows/test-migrations-one-step.yml
+++ b/.github/workflows/test-migrations-one-step.yml
@@ -57,7 +57,6 @@ jobs:
           - 6379:6379
 
     env:
-      CONTINUOUS_INTEGRATION: true
       DB_HOST: localhost
       DB_USER: postgres
       DB_PASS: postgres
diff --git a/.github/workflows/test-migrations-two-step.yml b/.github/workflows/test-migrations-two-step.yml
index 669884731..c4209d632 100644
--- a/.github/workflows/test-migrations-two-step.yml
+++ b/.github/workflows/test-migrations-two-step.yml
@@ -57,7 +57,6 @@ jobs:
           - 6379:6379
 
     env:
-      CONTINUOUS_INTEGRATION: true
       DB_HOST: localhost
       DB_USER: postgres
       DB_PASS: postgres

From 1dfd51628416598d2386621b6229a4f169cd360e Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Wed, 12 Jun 2024 09:28:28 +0200
Subject: [PATCH 090/133] Fix duplicate `@context` attribute in user export
 (#30653)

---
 app/services/backup_service.rb       |  4 ++--
 spec/services/backup_service_spec.rb | 10 ++++++++--
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/app/services/backup_service.rb b/app/services/backup_service.rb
index 886bab1eb..1e9018437 100644
--- a/app/services/backup_service.rb
+++ b/app/services/backup_service.rb
@@ -19,8 +19,8 @@ class BackupService < BaseService
 
   def build_outbox_json!(file)
     skeleton = serialize(collection_presenter, ActivityPub::CollectionSerializer)
-    skeleton[:@context] = full_context
-    skeleton[:orderedItems] = ['!PLACEHOLDER!']
+    skeleton['@context'] = full_context
+    skeleton['orderedItems'] = ['!PLACEHOLDER!']
     skeleton = Oj.dump(skeleton)
     prepend, append = skeleton.split('"!PLACEHOLDER!"')
     add_comma = false
diff --git a/spec/services/backup_service_spec.rb b/spec/services/backup_service_spec.rb
index b4cb60083..145b06e37 100644
--- a/spec/services/backup_service_spec.rb
+++ b/spec/services/backup_service_spec.rb
@@ -55,9 +55,11 @@ RSpec.describe BackupService do
   end
 
   def expect_outbox_export
-    json = export_json(:outbox)
+    body = export_json_raw(:outbox)
+    json = Oj.load(body)
 
     aggregate_failures do
+      expect(body.scan('@context').count).to eq 1
       expect(json['@context']).to_not be_nil
       expect(json['type']).to eq 'OrderedCollection'
       expect(json['totalItems']).to eq 2
@@ -85,8 +87,12 @@ RSpec.describe BackupService do
     end
   end
 
+  def export_json_raw(type)
+    read_zip_file(backup, "#{type}.json")
+  end
+
   def export_json(type)
-    Oj.load(read_zip_file(backup, "#{type}.json"))
+    Oj.load(export_json_raw(type))
   end
 
   def include_create_item(status)

From ced463360e39b92689144e366b6f3a9ceabf1bf7 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Wed, 12 Jun 2024 12:48:31 +0200
Subject: [PATCH 091/133] New Crowdin Translations (automated) (#30666)

Co-authored-by: GitHub Actions <noreply@github.com>
---
 app/javascript/mastodon/locales/be.json | 19 ++++++++++++++++++-
 app/javascript/mastodon/locales/ur.json |  5 +++++
 config/locales/be.yml                   |  3 +++
 config/locales/doorkeeper.be.yml        |  2 ++
 config/locales/doorkeeper.fy.yml        |  2 ++
 config/locales/fy.yml                   |  1 +
 config/locales/simple_form.be.yml       |  6 ++++++
 config/locales/simple_form.fy.yml       |  2 ++
 8 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json
index 61e96e4b5..041d90775 100644
--- a/app/javascript/mastodon/locales/be.json
+++ b/app/javascript/mastodon/locales/be.json
@@ -308,6 +308,8 @@
   "follow_requests.unlocked_explanation": "Ваш акаўнт не схаваны, аднак прадстаўнікі {domain} палічылі, што вы можаце захацець праглядзець запыты на падпіску з гэтых профіляў уручную.",
   "follow_suggestions.curated_suggestion": "Выбар адміністрацыі",
   "follow_suggestions.dismiss": "Не паказваць зноў",
+  "follow_suggestions.featured_longer": "Адабраныя камандай {domain} уручную",
+  "follow_suggestions.friends_of_friends_longer": "Папулярнае сярод людзей, на якіх Вы падпісаны",
   "follow_suggestions.hints.featured": "Гэты профіль быў выбраны ўручную камандай {domain}.",
   "follow_suggestions.hints.friends_of_friends": "Гэты профіль папулярны сярод людзей, на якіх вы падпісаліся.",
   "follow_suggestions.hints.most_followed": "Гэты профіль - адзін з профіляў з самай вялікай колькасцю падпісак на {domain}.",
@@ -315,6 +317,8 @@
   "follow_suggestions.hints.similar_to_recently_followed": "Гэты профіль падобны на профілі, на якія вы нядаўна падпісаліся.",
   "follow_suggestions.personalized_suggestion": "Персаналізаваная прапанова",
   "follow_suggestions.popular_suggestion": "Папулярная прапанова",
+  "follow_suggestions.popular_suggestion_longer": "Папулярнае на {domain}",
+  "follow_suggestions.similar_to_recently_followed_longer": "Падобныя профілі, за якімі вы нядаўна сачылі",
   "follow_suggestions.view_all": "Праглядзець усё",
   "follow_suggestions.who_to_follow": "На каго падпісацца",
   "followed_tags": "Падпіскі",
@@ -410,6 +414,7 @@
   "limited_account_hint.action": "Усе роўна паказваць профіль",
   "limited_account_hint.title": "Гэты профіль быў схаваны мадэратарамі",
   "link_preview.author": "Ад {name}",
+  "link_preview.more_from_author": "Больш ад {name}",
   "lists.account.add": "Дадаць да спісу",
   "lists.account.remove": "Выдаліць са спісу",
   "lists.delete": "Выдаліць спіс",
@@ -458,7 +463,7 @@
   "navigation_bar.opened_in_classic_interface": "Допісы, уліковыя запісы і іншыя спецыфічныя старонкі па змоўчанні адчыняюцца ў класічным вэб-інтэрфейсе.",
   "navigation_bar.personal": "Асабістае",
   "navigation_bar.pins": "Замацаваныя допісы",
-  "navigation_bar.preferences": "Параметры",
+  "navigation_bar.preferences": "Налады",
   "navigation_bar.public_timeline": "Глабальная стужка",
   "navigation_bar.search": "Пошук",
   "navigation_bar.security": "Бяспека",
@@ -470,10 +475,22 @@
   "notification.follow_request": "{name} адправіў запыт на падпіску",
   "notification.mention": "{name} згадаў вас",
   "notification.moderation-warning.learn_more": "Даведацца больш",
+  "notification.moderation_warning": "Вы атрымалі папярэджанне аб мадэрацыі",
+  "notification.moderation_warning.action_delete_statuses": "Некаторыя вашыя допісы былі выдаленыя.",
+  "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_silence": "Ваш уліковы запіс быў абмежаваны.",
+  "notification.moderation_warning.action_suspend": "Ваш уліковы запіс быў прыпынены.",
   "notification.own_poll": "Ваша апытанне скончылася",
   "notification.poll": "Апытанне, дзе вы прынялі ўдзел, скончылася",
   "notification.reblog": "{name} пашырыў ваш допіс",
+  "notification.relationships_severance_event": "Страціў сувязь з {name}",
+  "notification.relationships_severance_event.account_suspension": "Адміністратар з {from} прыпыніў працу {target}, што азначае, што вы больш не можаце атрымліваць ад іх абнаўлення ці ўзаемадзейнічаць з імі.",
+  "notification.relationships_severance_event.domain_block": "Адміністратар з {from} заблакіраваў {target}, у тым ліку {followersCount} вашых падпісчыка(-аў) і {followingCount, plural, one {# уліковы запіс} few {# уліковыя запісы} many {# уліковых запісаў} other {# уліковых запісаў}}.",
   "notification.relationships_severance_event.learn_more": "Даведацца больш",
+  "notification.relationships_severance_event.user_domain_block": "Вы заблакіравалі {target} выдаліўшы {followersCount} сваіх падпісчыкаў і {followingCount, plural, one {# уліковы запіс} few {# уліковыя запісы} many {# уліковых запісаў} other {# уліковых запісаў}}, за якімі вы сочыце.",
   "notification.status": "Новы допіс ад {name}",
   "notification.update": "Допіс {name} адрэдагаваны",
   "notification_requests.accept": "Прыняць",
diff --git a/app/javascript/mastodon/locales/ur.json b/app/javascript/mastodon/locales/ur.json
index 37f156c28..6f3debae2 100644
--- a/app/javascript/mastodon/locales/ur.json
+++ b/app/javascript/mastodon/locales/ur.json
@@ -26,6 +26,7 @@
   "account.featured_tags.last_status_never": "کوئی مراسلہ نہیں",
   "account.featured_tags.title": "{name} کے نمایاں ہیش ٹیگز",
   "account.follow": "پیروی کریں",
+  "account.follow_back": "اکاؤنٹ کو فالو بیک ",
   "account.followers": "پیروکار",
   "account.followers.empty": "\"ہنوز اس صارف کی کوئی پیروی نہیں کرتا\".",
   "account.followers_counter": "{count, plural,one {{counter} پیروکار} other {{counter} پیروکار}}",
@@ -46,6 +47,7 @@
   "account.mute_notifications_short": "نوٹیفیکیشنز کو خاموش کریں",
   "account.mute_short": "خاموش",
   "account.muted": "خاموش کردہ",
+  "account.mutual": "میوچول اکاؤنٹ",
   "account.no_bio": "کوئی تفصیل نہیں دی گئی۔",
   "account.open_original_page": "اصل صفحہ کھولیں",
   "account.posts": "ٹوٹ",
@@ -65,7 +67,10 @@
   "account.unmute_notifications_short": "نوٹیفیکیشنز کو خاموش نہ کریں",
   "account.unmute_short": "کو خاموش نہ کریں",
   "account_note.placeholder": "Click to add a note",
+  "admin.dashboard.daily_retention": "ایڈمن ڈیش بورڈ کو ڈیلی چیک ان کریں",
+  "admin.dashboard.monthly_retention": "ایڈمن کیش بورڈ کو منتھلی چیک ان کریں",
   "admin.dashboard.retention.average": "اوسط",
+  "admin.dashboard.retention.cohort": "Sign-up month",
   "admin.dashboard.retention.cohort_size": "نئے یسرز",
   "alert.rate_limited.message": "\"{retry_time, time, medium} کے بعد کوشش کریں\".",
   "alert.rate_limited.title": "محدود شرح",
diff --git a/config/locales/be.yml b/config/locales/be.yml
index 6f1f18952..a14ba3d22 100644
--- a/config/locales/be.yml
+++ b/config/locales/be.yml
@@ -291,6 +291,7 @@ be:
         update_custom_emoji_html: "%{name} абнавіў эмодзі %{target}"
         update_domain_block_html: "%{name} абнавіў блакіроўку дамена для %{target}"
         update_ip_block_html: "%{name} змяніў правіла для IP %{target}"
+        update_report_html: "%{name} абнавіў скаргу %{target}"
         update_status_html: "%{name} абнавіў допіс %{target}"
         update_user_role_html: "%{name} змяніў ролю %{target}"
       deleted_account: выдалены ўліковы запіс
@@ -779,6 +780,7 @@ be:
         desc_html: Гэта функцыянальнасць залежыць ад знешніх скрыптоў hCaptcha, што можа быць праблемай бяспекі і прыватнасці. Акрамя таго, <strong>гэта можа зрабіць працэс рэгістрацыі значна менш даступным для некаторых людзей, асабліва інвалідаў</strong>. Па гэтых прычынах, калі ласка, разгледзьце альтэрнатыўныя меры, такія як рэгістрацыя на аснове зацвярджэння або запрашэння.
         title: Патрабаваць ад новых карыстальнікаў рашэння CAPTCHA для пацверджання іх уліковага запісу
       content_retention:
+        danger_zone: Небяспечная зона
         preamble: Кантралюйце, як створаны карыстальнікамі кантэнт захоўваецца ў Mastodon.
         title: Утрыманне кантэнту
       default_noindex:
@@ -983,6 +985,7 @@ be:
       delete: Выдаліць
       edit_preset: Рэдагаваць шаблон папярэджання
       empty: Вы яшчэ не вызначылі ніякіх шаблонаў папярэджанняў.
+      title: Папярэджальныя прадусталёўкі
     webhooks:
       add_new: Дадаць канцавую кропку
       delete: Выдаліць
diff --git a/config/locales/doorkeeper.be.yml b/config/locales/doorkeeper.be.yml
index 748cbeafa..f4faed1f0 100644
--- a/config/locales/doorkeeper.be.yml
+++ b/config/locales/doorkeeper.be.yml
@@ -135,6 +135,7 @@ be:
         media: Мультымедыйныя далучэнні
         mutes: Ігнараваныя
         notifications: Апавяшчэнні
+        profile: Ваш профіль Mastodon
         push: Push-апавяшчэнні
         reports: Скаргі
         search: Пошук
@@ -165,6 +166,7 @@ be:
       admin:write:reports: мадэраваць скаргі
       crypto: выкарыстоўваць скразное шыфраванне (end-to-end)
       follow: змяняць зносіны ўліковага запісу
+      profile: чытаць толькі інфармацыю профілю вашага ўліковага запісу
       push: атрымліваць push-апавяшчэнні
       read: чытаць усе даныя вашага ўліковага запісу
       read:accounts: бачыць інфармацыю аб уліковых запісах
diff --git a/config/locales/doorkeeper.fy.yml b/config/locales/doorkeeper.fy.yml
index a43defc42..1cf2d3221 100644
--- a/config/locales/doorkeeper.fy.yml
+++ b/config/locales/doorkeeper.fy.yml
@@ -135,6 +135,7 @@ fy:
         media: Mediabylagen
         mutes: Negearre
         notifications: Meldingen
+        profile: Jo Mastodon-profyl
         push: Pushmeldingen
         reports: Rapportaazjes
         search: Sykje
@@ -165,6 +166,7 @@ fy:
       admin:write:reports: moderaasjemaatregelen nimme yn rapportaazjes
       crypto: ein-ta-ein-fersifering brûke
       follow: relaasjes tusken accounts bewurkje
+      profile: allinnich de profylgegevens fan jo account lêze
       push: jo pushmeldingen ûntfange
       read: alle gegevens fan jo account lêze
       read:accounts: accountynformaasje besjen
diff --git a/config/locales/fy.yml b/config/locales/fy.yml
index c8e287732..54e28608e 100644
--- a/config/locales/fy.yml
+++ b/config/locales/fy.yml
@@ -285,6 +285,7 @@ fy:
         update_custom_emoji_html: Emoji %{target} is troch %{name} bywurke
         update_domain_block_html: "%{name} hat de domeinblokkade bywurke foar %{target}"
         update_ip_block_html: "%{name} hat de rigel foar IP %{target} wizige"
+        update_report_html: Rapportaazje %{target} is troch %{name} bywurke
         update_status_html: "%{name} hat de berjochten %{target} bywurke"
         update_user_role_html: "%{name} hat de rol %{target} wizige"
       deleted_account: fuortsmiten account
diff --git a/config/locales/simple_form.be.yml b/config/locales/simple_form.be.yml
index f8000a1c8..101d40f11 100644
--- a/config/locales/simple_form.be.yml
+++ b/config/locales/simple_form.be.yml
@@ -77,10 +77,15 @@ be:
           warn: Схаваць адфільтраваны кантэнт за папярэджаннем з назвай фільтру
       form_admin_settings:
         activity_api_enabled: Падлік лакальна апублікаваных пастоў, актыўных карыстальнікаў і новых рэгістрацый у тыдзень
+        app_icon: WEBP, PNG, GIF ці JPG. Заменіце прадвызначаны значок праграмы на мабільных прыладах карыстальніцкім значком.
+        backups_retention_period: Карыстальнікі могуць ствараць архівы сваіх допісаў для наступнай запампоўкі. Пры станоўчай колькасці дзён гэтыя архівы будуць аўтаматычна выдаляцца са сховішча пасля заканчэння названай колькасці дзён.
         bootstrap_timeline_accounts: Гэтыя ўліковыя запісы будуць замацаваны ў топе рэкамендацый для новых карыстальнікаў.
         closed_registrations_message: Паказваецца, калі рэгістрацыя закрытая
+        content_cache_retention_period: Усе допісы з іншых сервераў (уключаючы пашырэнні і адказы) будуць выдаленыя праз паказаную колькасць дзён, незалежна ад таго, як лакальны карыстальнік узаемадзейнічаў з гэтымі допісамі. Гэта датычыцца і тых допісаў, якія лакальны карыстальнік пазначыў у закладкі або ўпадабанае. Прыватныя згадкі паміж карыстальнікамі з розных інстанс таксама будуць страчаныя і не змогуць быць адноўлены. Выкарыстанне гэтай налады прызначана для асобнікаў спецыяльнага прызначэння і парушае многія чаканні карыстальнікаў пры выкарыстанні ў агульных мэтах.
         custom_css: Вы можаце прымяняць карыстальніцкія стылі ў вэб-версіі Mastodon.
+        favicon: WEBP, PNG, GIF ці JPG. Замяняе прадвызначаны favicon Mastodon на ўласны значок.
         mascot: Замяняе ілюстрацыю ў пашыраным вэб-інтэрфейсе.
+        media_cache_retention_period: Медыяфайлы з допісаў, зробленых выдаленымі карыстальнікамі, кэшыруюцца на вашым серверы. Пры станоўчым значэнні медыяфайлы будуць выдалены праз пазначаную колькасць дзён. Калі медыядадзеныя будуць запытаны пасля выдалення, яны будуць загружаны паўторна, калі зыходны кантэнт усё яшчэ даступны. У сувязі з абмежаваннямі на частату абнаўлення відарысаў іншых сайтаў, рэкамендуецца ўсталяваць значэнне не менш за 14 дзён, інакш відарысы не будуць загружацца па запыце раней за гэты тэрмін.
         peers_api_enabled: Спіс даменных імён, з якімі сутыкнуўся гэты сервер у федэсвеце. Дадзеныя аб тым, ці знаходзіцеся вы з пэўным серверам у федэрацыі, не ўключаныя, ёсць толькі тое, што ваш сервер ведае пра гэта. Гэта выкарыстоўваецца сэрвісамі, якія збіраюць статыстыку па федэрацыі ў агульным сэнсе.
         profile_directory: Дырэкторыя профіляў змяшчае спіс усіх карыстальнікаў, якія вырашылі быць бачнымі.
         require_invite_text: Калі рэгістрацыя патрабуе ручнога пацвержання, зрабіце поле "Чаму вы хочаце далучыцца?" абавязковым
@@ -240,6 +245,7 @@ be:
         backups_retention_period: Працягласць захавання архіву карыстальніка
         bootstrap_timeline_accounts: Заўсёды раіць гэтыя ўліковыя запісы новым карыстальнікам
         closed_registrations_message: Уласнае паведамленне, калі рэгістрацыя немагчымая
+        content_cache_retention_period: Перыяд захоўвання выдаленага змесціва
         custom_css: CSS карыстальніка
         mascot: Уласны маскот(спадчына)
         media_cache_retention_period: Працягласць захавання кэшу для медыя
diff --git a/config/locales/simple_form.fy.yml b/config/locales/simple_form.fy.yml
index 8d599324b..9e0f67b70 100644
--- a/config/locales/simple_form.fy.yml
+++ b/config/locales/simple_form.fy.yml
@@ -77,11 +77,13 @@ fy:
           warn: Ferstopje de filtere ynhâld efter in warskôging, mei de titel fan it filter as warskôgingstekst
       form_admin_settings:
         activity_api_enabled: Tal lokaal publisearre artikelen, aktive brûkers en nije registraasjes yn wyklikse werjefte
+        app_icon: WEBP, PNG, GIF of JPG. Ferfangt op mobile apparaten it standert app-pictogram mei in oanpast piktogram.
         backups_retention_period: Brûkers hawwe de mooglikheid om argiven fan harren berjochten te generearjen om letter te downloaden. Wannear ynsteld op in positive wearde, wurde dizze argiven automatysk fuortsmiten út jo ûnthâld nei it opjûne oantal dagen.
         bootstrap_timeline_accounts: Dizze accounts wurde boppe oan de oanrekommandaasjes oan nije brûkers toand. Meardere brûkersnammen troch komma’s skiede.
         closed_registrations_message: Werjûn wannear’t registraasje fan nije accounts útskeakele is
         content_cache_retention_period: Alle berjochten fan oare servers (ynklusyf boosts en reaksjes) wurde fuortsmiten nei it opjûne oantal dagen, nettsjinsteande iennige lokale brûkersynteraksje mei dy berjochten. Dit oanbelanget ek berjochten dy’t in lokale brûker oan harren blêdwizers tafoege hat of as favoryt markearre hat. Priveeberjochten tusken brûkers fan ferskate servers gean ek ferlern en binne ûnmooglik te werstellen. It gebrûk fan dizze ynstelling is bedoeld foar servers dy’t in spesjaal doel tsjinje en oertrêdet in protte brûkersferwachtingen wannear’t dizze foar algemien gebrûk ymplemintearre wurdt.
         custom_css: Jo kinne oanpaste CSS tapasse op de webferzje fan dizze Mastodon-server.
+        favicon: WEBP, PNG, GIF of JPG. Ferfangt de standert Mastodon-favicon mei in oanpast piktogram.
         mascot: Oerskriuwt de yllustraasje yn de avansearre webomjouwing.
         media_cache_retention_period: Mediabestannen fan berjochten fan eksterne brûkers wurde op jo server yn de buffer bewarre. Wannear ynsteld op in positive wearde, wurde media fuortsmiten nei it opjûne oantal dagen. As de mediagegevens opfrege wurde neidat se fuortsmiten binne, wurde se opnij download wannear de orizjinele ynhâld noch hieltyd beskikber is. Fanwegen beheiningen op hoe faak keppelingsfoarbylden websites fan tredden rieplachtsje, wurdt oanrekommandearre om dizze wearde yn te stellen op op syn minste 14 dagen. Oars wurde keppelingsfoarbylden net op oanfraach bywurke.
         peers_api_enabled: In list mei domeinnammen, dêr’t dizze server yn fediverse kontakt hân mei hat. Hjir wurdt gjin data dield, oft jo mei in bepaalde server federearrest, mar alinnich, dat jo server dat wit. Dit wurdt foar tsjinsten brûkt, dy’t statistiken oer federaasje yn algemiene sin sammelet.

From dff48ff705b8193702cdef149f8cd8748365d48f Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 12 Jun 2024 13:04:54 +0200
Subject: [PATCH 092/133] fix(deps): update dependency sass to v1.77.5 (#30665)

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 33cd7b481..b88cfaab1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -15355,15 +15355,15 @@ __metadata:
   linkType: hard
 
 "sass@npm:^1.62.1":
-  version: 1.77.4
-  resolution: "sass@npm:1.77.4"
+  version: 1.77.5
+  resolution: "sass@npm:1.77.5"
   dependencies:
     chokidar: "npm:>=3.0.0 <4.0.0"
     immutable: "npm:^4.0.0"
     source-map-js: "npm:>=0.6.2 <2.0.0"
   bin:
     sass: sass.js
-  checksum: 10c0/b9cb4882bded282aabe38d011adfce375e1f282184fcf93dc3da5d5be834c6aa53c474c15634c351ef7bd85146cfd1cc81343654cc3bcf000d78e856da4225ef
+  checksum: 10c0/9da049b0a3fadab419084d6becdf471e107cf6e3c8ac87cabea2feb845afac75e86c99e06ee721a5aa4f6a2d833ec5380137c4e540ab2f760edf1e4eb6139e69
   languageName: node
   linkType: hard
 

From 9321a454de78dc57d4382f6f366f282a111d2b31 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Wed, 12 Jun 2024 07:38:20 -0400
Subject: [PATCH 093/133] Combine CI migration tests (#30661)

---
 .../workflows/test-migrations-two-step.yml    | 94 -------------------
 ...tions-one-step.yml => test-migrations.yml} | 32 ++++---
 2 files changed, 19 insertions(+), 107 deletions(-)
 delete mode 100644 .github/workflows/test-migrations-two-step.yml
 rename .github/workflows/{test-migrations-one-step.yml => test-migrations.yml} (66%)

diff --git a/.github/workflows/test-migrations-two-step.yml b/.github/workflows/test-migrations-two-step.yml
deleted file mode 100644
index c4209d632..000000000
--- a/.github/workflows/test-migrations-two-step.yml
+++ /dev/null
@@ -1,94 +0,0 @@
-name: Test two step migrations
-on:
-  push:
-    branches-ignore:
-      - 'dependabot/**'
-      - 'renovate/**'
-  pull_request:
-
-jobs:
-  pre_job:
-    runs-on: ubuntu-latest
-
-    outputs:
-      should_skip: ${{ steps.skip_check.outputs.should_skip }}
-
-    steps:
-      - id: skip_check
-        uses: fkirc/skip-duplicate-actions@v5
-        with:
-          paths: '["Gemfile*", ".ruby-version", "**/*.rb", ".github/workflows/test-migrations-two-step.yml", "lib/tasks/tests.rake"]'
-
-  test:
-    runs-on: ubuntu-latest
-    needs: pre_job
-    if: needs.pre_job.outputs.should_skip != 'true'
-
-    strategy:
-      fail-fast: false
-
-      matrix:
-        postgres:
-          - 14-alpine
-          - 15-alpine
-
-    services:
-      postgres:
-        image: postgres:${{ matrix.postgres}}
-        env:
-          POSTGRES_PASSWORD: postgres
-          POSTGRES_USER: postgres
-        options: >-
-          --health-cmd pg_isready
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
-        ports:
-          - 5432:5432
-
-      redis:
-        image: redis:7-alpine
-        options: >-
-          --health-cmd "redis-cli ping"
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
-        ports:
-          - 6379:6379
-
-    env:
-      DB_HOST: localhost
-      DB_USER: postgres
-      DB_PASS: postgres
-      DISABLE_SIMPLECOV: true
-      RAILS_ENV: test
-      BUNDLE_CLEAN: true
-      BUNDLE_FROZEN: true
-      BUNDLE_WITHOUT: 'development production'
-      BUNDLE_JOBS: 3
-      BUNDLE_RETRY: 3
-
-    steps:
-      - uses: actions/checkout@v4
-
-      - name: Set up Ruby environment
-        uses: ./.github/actions/setup-ruby
-
-      - name: Create database
-        run: './bin/rails db:create'
-
-      - name: Run historical migrations with data population
-        run: './bin/rails tests:migrations:prepare_database'
-        env:
-          SKIP_POST_DEPLOYMENT_MIGRATIONS: true
-
-      - name: Run all remaining pre-deployment migrations
-        run: './bin/rails db:migrate'
-        env:
-          SKIP_POST_DEPLOYMENT_MIGRATIONS: true
-
-      - name: Run all post-deployment migrations
-        run: './bin/rails db:migrate'
-
-      - name: Check migration result
-        run: './bin/rails tests:migrations:check_database'
diff --git a/.github/workflows/test-migrations-one-step.yml b/.github/workflows/test-migrations.yml
similarity index 66%
rename from .github/workflows/test-migrations-one-step.yml
rename to .github/workflows/test-migrations.yml
index 104e1b067..f057efc11 100644
--- a/.github/workflows/test-migrations-one-step.yml
+++ b/.github/workflows/test-migrations.yml
@@ -1,4 +1,5 @@
-name: Test one step migrations
+name: Historical data migration test
+
 on:
   push:
     branches-ignore:
@@ -17,7 +18,7 @@ jobs:
       - id: skip_check
         uses: fkirc/skip-duplicate-actions@v5
         with:
-          paths: '["Gemfile*", ".ruby-version", "**/*.rb", ".github/workflows/test-migrations-one-step.yml", "lib/tasks/tests.rake"]'
+          paths: '["Gemfile*", ".ruby-version", "**/*.rb", ".github/workflows/test-migrations.yml", "lib/tasks/tests.rake"]'
 
   test:
     runs-on: ubuntu-latest
@@ -64,7 +65,7 @@ jobs:
       RAILS_ENV: test
       BUNDLE_CLEAN: true
       BUNDLE_FROZEN: true
-      BUNDLE_WITHOUT: 'development production'
+      BUNDLE_WITHOUT: 'development:production'
       BUNDLE_JOBS: 3
       BUNDLE_RETRY: 3
 
@@ -74,14 +75,19 @@ jobs:
       - name: Set up Ruby environment
         uses: ./.github/actions/setup-ruby
 
-      - name: Create database
-        run: './bin/rails db:create'
+      - name: Test "one step migration" flow
+        run: |
+          bin/rails db:drop
+          bin/rails db:create
+          bin/rails tests:migrations:prepare_database
+          bin/rails db:migrate
+          bin/rails tests:migrations:check_database
 
-      - name: Run historical migrations with data population
-        run: './bin/rails tests:migrations:prepare_database'
-
-      - name: Run all remaining migrations
-        run: './bin/rails db:migrate'
-
-      - name: Check migration result
-        run: './bin/rails tests:migrations:check_database'
+      - name: Test "two step migration" flow
+        run: |
+          bin/rails db:drop
+          bin/rails db:create
+          SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails tests:migrations:prepare_database
+          SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails db:migrate
+          bin/rails db:migrate
+          bin/rails tests:migrations:check_database

From 47f97e113a1047c067948f385b5179fa0489180f Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Wed, 12 Jun 2024 07:39:16 -0400
Subject: [PATCH 094/133] Update the bundler-audit vulnerability DB when
 running (#30658)

---
 .github/workflows/bundler-audit.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/bundler-audit.yml b/.github/workflows/bundler-audit.yml
index bbc31598c..923abcd91 100644
--- a/.github/workflows/bundler-audit.yml
+++ b/.github/workflows/bundler-audit.yml
@@ -31,4 +31,4 @@ jobs:
         uses: ./.github/actions/setup-ruby
 
       - name: Run bundler-audit
-        run: bundle exec bundler-audit
+        run: bundle exec bundler-audit check --update

From 0a7249c7c687775c7ab6ef495b259cedff5f25ca Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 12 Jun 2024 14:41:14 +0200
Subject: [PATCH 095/133] fix(deps): update dependency pino to v9.2.0 (#30659)

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 b88cfaab1..b34f45aac 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -13093,8 +13093,8 @@ __metadata:
   linkType: hard
 
 "pino@npm:^9.0.0":
-  version: 9.1.0
-  resolution: "pino@npm:9.1.0"
+  version: 9.2.0
+  resolution: "pino@npm:9.2.0"
   dependencies:
     atomic-sleep: "npm:^1.0.0"
     fast-redact: "npm:^3.1.1"
@@ -13109,7 +13109,7 @@ __metadata:
     thread-stream: "npm:^3.0.0"
   bin:
     pino: bin.js
-  checksum: 10c0/d060530ae2e4e8f21d04bb0f44f009f94d207d7f4337f508f618416514214ddaf1b29f8c5c265153a19ce3b6480b451461f40020f916ace9d53a5aa07624b79c
+  checksum: 10c0/5fbd226ff7dab0961232b5aa5eca0530cdc5bb29f6bf17d929e42239293b1a587a26cc311db6abc1090c9dd57e8f7b031eae341b41d00d4a642b4f1736474c80
   languageName: node
   linkType: hard
 

From 54ab70dfcf39e13c055b99a4fc4aea721781af32 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 12 Jun 2024 14:42:04 +0200
Subject: [PATCH 096/133] chore(deps): update yarn to v4.3.0 (#30644)

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 f84d45c32..d52f0ea1c 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "@mastodon/mastodon",
   "license": "AGPL-3.0-or-later",
-  "packageManager": "yarn@4.2.2",
+  "packageManager": "yarn@4.3.0",
   "engines": {
     "node": ">=18"
   },
diff --git a/streaming/package.json b/streaming/package.json
index 2e515167c..ba024fe7a 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.2.2",
+  "packageManager": "yarn@4.3.0",
   "engines": {
     "node": ">=18"
   },

From 19f1c081e161bf8ca3325f48f7fe0194827de56a Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Wed, 12 Jun 2024 08:59:32 -0400
Subject: [PATCH 097/133] Update `rubocop-rspec` to version 3.0.1 (#30655)

---
 .rubocop.yml |  5 -----
 Gemfile      |  1 +
 Gemfile.lock | 19 ++++++++-----------
 3 files changed, 9 insertions(+), 16 deletions(-)

diff --git a/.rubocop.yml b/.rubocop.yml
index cbc0afd28..090b89b25 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -133,11 +133,6 @@ Rails/NegateInclude:
 RSpec/ExampleLength:
   CountAsOne: ['array', 'heredoc', 'method_call']
 
-# Reason: Deprecated cop, will be removed in 3.0, replaced by SpecFilePathFormat
-# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecfilepath
-RSpec/FilePath:
-  Enabled: false
-
 # Reason:
 # https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecnamedsubject
 RSpec/NamedSubject:
diff --git a/Gemfile b/Gemfile
index be02a6562..b00eaecbc 100644
--- a/Gemfile
+++ b/Gemfile
@@ -171,6 +171,7 @@ group :development do
   gem 'rubocop-performance', require: false
   gem 'rubocop-rails', require: false
   gem 'rubocop-rspec', require: false
+  gem 'rubocop-rspec_rails', require: false
 
   # Annotates modules with schema
   gem 'annotate', '~> 3.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index c2253fe19..902ddd5f5 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -698,8 +698,8 @@ GEM
     responders (3.1.1)
       actionpack (>= 5.2)
       railties (>= 5.2)
-    rexml (3.2.8)
-      strscan (>= 3.0.9)
+    rexml (3.3.0)
+      strscan
     rotp (6.3.0)
     rouge (4.2.1)
     rpam2 (4.0.2)
@@ -746,8 +746,6 @@ GEM
       parser (>= 3.3.1.0)
     rubocop-capybara (2.21.0)
       rubocop (~> 1.41)
-    rubocop-factory_bot (2.25.1)
-      rubocop (~> 1.41)
     rubocop-performance (1.21.0)
       rubocop (>= 1.48.1, < 2.0)
       rubocop-ast (>= 1.31.1, < 2.0)
@@ -756,13 +754,11 @@ GEM
       rack (>= 1.1)
       rubocop (>= 1.33.0, < 2.0)
       rubocop-ast (>= 1.31.1, < 2.0)
-    rubocop-rspec (2.31.0)
-      rubocop (~> 1.40)
-      rubocop-capybara (~> 2.17)
-      rubocop-factory_bot (~> 2.22)
-      rubocop-rspec_rails (~> 2.28)
-    rubocop-rspec_rails (2.28.3)
-      rubocop (~> 1.40)
+    rubocop-rspec (3.0.1)
+      rubocop (~> 1.61)
+    rubocop-rspec_rails (2.30.0)
+      rubocop (~> 1.61)
+      rubocop-rspec (~> 3, >= 3.0.1)
     ruby-prof (1.7.0)
     ruby-progressbar (1.13.0)
     ruby-saml (1.16.0)
@@ -1028,6 +1024,7 @@ DEPENDENCIES
   rubocop-performance
   rubocop-rails
   rubocop-rspec
+  rubocop-rspec_rails
   ruby-prof
   ruby-progressbar (~> 1.13)
   ruby-vips (~> 2.2)

From 99842434677ef3a01f5c7700d3b67ba5e72cd1a1 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Wed, 12 Jun 2024 15:10:51 +0200
Subject: [PATCH 098/133] Fix a few visual glitches with link previews in web
 UI (#30670)

---
 app/javascript/styles/mastodon/components.scss | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 4f36d85aa..2400f319d 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -1411,10 +1411,15 @@ body > [data-popper-placement] {
     .audio-player,
     .attachment-list,
     .picture-in-picture-placeholder,
+    .more-from-author,
     .status-card,
     .hashtag-bar {
       margin-inline-start: $thread-margin;
-      width: calc(100% - ($thread-margin));
+      width: calc(100% - $thread-margin);
+    }
+
+    .more-from-author {
+      width: calc(100% - $thread-margin + 2px);
     }
 
     .status__content__read-more-button {
@@ -4129,6 +4134,13 @@ a.status-card {
   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%;
 }
@@ -10229,6 +10241,7 @@ noscript {
 }
 
 .more-from-author {
+  box-sizing: border-box;
   font-size: 14px;
   color: $darker-text-color;
   background: var(--surface-background-color);

From a5a15846756dd62e01bf7eb64fdf3b8fafc3ba69 Mon Sep 17 00:00:00 2001
From: Sujay <sujay.mukherjeedev@gmail.com>
Date: Wed, 12 Jun 2024 19:18:30 +0530
Subject: [PATCH 099/133] Update README.md (#30626)

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 4aca37673..9c0b0d20e 100644
--- a/README.md
+++ b/README.md
@@ -62,7 +62,7 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre
 ### Tech stack
 
 - **Ruby on Rails** powers the REST API and other web pages
-- **React.js** and Redux are used for the dynamic parts of the interface
+- **React.js** and **Redux** are used for the dynamic parts of the interface
 - **Node.js** powers the streaming API
 
 ### Requirements

From bf56e982a9c211396efea16f2ee596102b36db3f Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Wed, 12 Jun 2024 15:50:38 +0200
Subject: [PATCH 100/133] Fix notifications from limited users being outright
 dropped (#30559)

---
 app/lib/feed_manager.rb              |  5 +----
 spec/lib/feed_manager_spec.rb        |  6 +++---
 spec/services/notify_service_spec.rb | 29 ++++++++++++++++++++++++++++
 3 files changed, 33 insertions(+), 7 deletions(-)

diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index 95a687fa4..1fb224a13 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -420,10 +420,7 @@ class FeedManager
     check_for_blocks = status.active_mentions.pluck(:account_id)
     check_for_blocks.push(status.in_reply_to_account) if status.reply? && !status.in_reply_to_account_id.nil?
 
-    should_filter   = blocks_or_mutes?(receiver_id, check_for_blocks, :mentions) # Filter if it's from someone I blocked, in reply to someone I blocked, or mentioning someone I blocked (or muted)
-    should_filter ||= status.account.silenced? && !Follow.exists?(account_id: receiver_id, target_account_id: status.account_id) # Filter if the account is silenced and I'm not following them
-
-    should_filter
+    blocks_or_mutes?(receiver_id, check_for_blocks, :mentions) # Filter if it's from someone I blocked, in reply to someone I blocked, or mentioning someone I blocked (or muted)
   end
 
   # Check if status should not be added to the list feed
diff --git a/spec/lib/feed_manager_spec.rb b/spec/lib/feed_manager_spec.rb
index 613bcb304..679309bd1 100644
--- a/spec/lib/feed_manager_spec.rb
+++ b/spec/lib/feed_manager_spec.rb
@@ -206,13 +206,13 @@ RSpec.describe FeedManager do
         expect(described_class.instance.filter?(:mentions, reply, bob)).to be true
       end
 
-      it 'returns true for status by silenced account who recipient is not following' do
+      it 'returns false for status by limited account who recipient is not following' do
         status = Fabricate(:status, text: 'Hello world', account: alice)
         alice.silence!
-        expect(described_class.instance.filter?(:mentions, status, bob)).to be true
+        expect(described_class.instance.filter?(:mentions, status, bob)).to be false
       end
 
-      it 'returns false for status by followed silenced account' do
+      it 'returns false for status by followed limited account' do
         status = Fabricate(:status, text: 'Hello world', account: alice)
         alice.silence!
         bob.follow!(alice)
diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb
index 6064d2b05..8c810f1c3 100644
--- a/spec/services/notify_service_spec.rb
+++ b/spec/services/notify_service_spec.rb
@@ -129,6 +129,35 @@ RSpec.describe NotifyService do
     end
   end
 
+  describe NotifyService::DismissCondition do
+    subject { described_class.new(notification) }
+
+    let(:activity) { Fabricate(:mention, status: Fabricate(:status)) }
+    let(:notification) { Fabricate(:notification, type: :mention, activity: activity, from_account: activity.status.account, account: activity.account) }
+
+    describe '#dismiss?' do
+      context 'when sender is silenced' do
+        before do
+          notification.from_account.silence!
+        end
+
+        it 'returns false' do
+          expect(subject.dismiss?).to be false
+        end
+      end
+
+      context 'when recipient has blocked sender' do
+        before do
+          notification.account.block!(notification.from_account)
+        end
+
+        it 'returns true' do
+          expect(subject.dismiss?).to be true
+        end
+      end
+    end
+  end
+
   describe NotifyService::FilterCondition do
     subject { described_class.new(notification) }
 

From 7ad5a3a2b728229704e29819b3b87bc0ccdc473f Mon Sep 17 00:00:00 2001
From: Renaud Chaput <renchap@gmail.com>
Date: Thu, 13 Jun 2024 10:21:50 +0200
Subject: [PATCH 101/133] Disable `consistent-return` eslint rule for
 Typescript files (#30675)

---
 .eslintrc.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.eslintrc.js b/.eslintrc.js
index 759003b55..e3afb1c9f 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -349,6 +349,9 @@ module.exports = defineConfig({
         // Disable formatting rules that have been enabled in the base config
         'indent': 'off',
 
+        // This is not needed as we use noImplicitReturns, which handles this in addition to understanding types
+        'consistent-return': 'off',
+
         'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
 
         '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],

From fc2f49cfbcbe996abeb267592bcf39d60d2995ae Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 13 Jun 2024 10:26:00 +0200
Subject: [PATCH 102/133] chore(deps): update docker.io/ruby docker tag to
 v3.3.3 (#30679)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index 2dc7602b2..e29377829 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -12,7 +12,7 @@ ARG BUILDPLATFORM=${BUILDPLATFORM}
 
 # Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"]
 # renovate: datasource=docker depName=docker.io/ruby
-ARG RUBY_VERSION="3.3.2"
+ARG RUBY_VERSION="3.3.3"
 # # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
 # renovate: datasource=node-version depName=node
 ARG NODE_MAJOR_VERSION="20"

From b379dc156e9a2292f2e21f80543c0b80d4087688 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 13 Jun 2024 04:26:09 -0400
Subject: [PATCH 103/133] Update parser to version 3.3.3.0 (#30676)

---
 Gemfile.lock | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index 902ddd5f5..222aaff50 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -584,7 +584,7 @@ GEM
     orm_adapter (0.5.0)
     ox (2.14.18)
     parallel (1.25.1)
-    parser (3.3.2.0)
+    parser (3.3.3.0)
       ast (~> 2.4.1)
       racc
     parslet (2.0.0)

From fe740455767a361798df3a21791f9db8e2056f7e Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 13 Jun 2024 10:26:17 +0200
Subject: [PATCH 104/133] chore(deps): update dependency ruby to v3.3.3
 (#30667)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
 .ruby-version | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.ruby-version b/.ruby-version
index 477254331..619b53766 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-3.3.2
+3.3.3

From 37f53542fe1c36c6126932cbb3840f6d4659104e Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Thu, 13 Jun 2024 14:42:40 +0200
Subject: [PATCH 105/133] Fix limit handling in grouped notifications CTE
 (#30685)

---
 app/models/notification.rb | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/app/models/notification.rb b/app/models/notification.rb
index e3deaa534..01abe74f5 100644
--- a/app/models/notification.rb
+++ b/app/models/notification.rb
@@ -152,6 +152,7 @@ class Notification < ApplicationRecord
               .limit(1),
             query
               .joins('CROSS JOIN grouped_notifications')
+              .where('array_length(grouped_notifications.groups, 1) < :limit', limit: limit)
               .where('notifications.id < grouped_notifications.id')
               .where.not("COALESCE(notifications.group_key, 'ungrouped-' || notifications.id) = ANY(grouped_notifications.groups)")
               .select('notifications.*', "array_append(grouped_notifications.groups, COALESCE(notifications.group_key, 'ungrouped-' || notifications.id))")
@@ -179,6 +180,7 @@ class Notification < ApplicationRecord
               .limit(1),
             query
               .joins('CROSS JOIN grouped_notifications')
+              .where('array_length(grouped_notifications.groups, 1) < :limit', limit: limit)
               .where('notifications.id > grouped_notifications.id')
               .where.not("COALESCE(notifications.group_key, 'ungrouped-' || notifications.id) = ANY(grouped_notifications.groups)")
               .select('notifications.*', "array_append(grouped_notifications.groups, COALESCE(notifications.group_key, 'ungrouped-' || notifications.id))")

From ed6d24330ba2f80b99428cb8ee19e5d8400ebd16 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Thu, 13 Jun 2024 15:04:16 +0200
Subject: [PATCH 106/133] Add author links on the explore page in web UI
 (#30521)

---
 .../mastodon/actions/importer/index.js        |   8 +-
 .../mastodon/actions/importer/normalizer.js   |   4 +
 app/javascript/mastodon/actions/trends.js     |   9 +-
 .../mastodon/components/more_from_author.jsx  |  19 +++
 .../explore/components/author_link.jsx        |  21 ++++
 .../features/explore/components/story.jsx     | 110 +++++++++++-------
 .../mastodon/features/explore/links.jsx       |   3 +-
 .../features/status/components/card.jsx       |  19 +--
 app/javascript/mastodon/locales/en.json       |   1 +
 .../styles/mastodon/components.scss           |  69 ++++++++---
 app/javascript/styles/mastodon/variables.scss |   2 +
 11 files changed, 185 insertions(+), 80 deletions(-)
 create mode 100644 app/javascript/mastodon/components/more_from_author.jsx
 create mode 100644 app/javascript/mastodon/features/explore/components/author_link.jsx

diff --git a/app/javascript/mastodon/actions/importer/index.js b/app/javascript/mastodon/actions/importer/index.js
index 16f191b58..d906bdfb1 100644
--- a/app/javascript/mastodon/actions/importer/index.js
+++ b/app/javascript/mastodon/actions/importer/index.js
@@ -68,13 +68,17 @@ export function importFetchedStatuses(statuses) {
         status.filtered.forEach(result => pushUnique(filters, result.filter));
       }
 
-      if (status.reblog && status.reblog.id) {
+      if (status.reblog?.id) {
         processStatus(status.reblog);
       }
 
-      if (status.poll && status.poll.id) {
+      if (status.poll?.id) {
         pushUnique(polls, normalizePoll(status.poll, getState().getIn(['polls', status.poll.id])));
       }
+
+      if (status.card?.author_account) {
+        pushUnique(accounts, status.card.author_account);
+      }
     }
 
     statuses.forEach(processStatus);
diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js
index b5a30343e..be76b0f39 100644
--- a/app/javascript/mastodon/actions/importer/normalizer.js
+++ b/app/javascript/mastodon/actions/importer/normalizer.js
@@ -36,6 +36,10 @@ export function normalizeStatus(status, normalOldStatus) {
     normalStatus.poll = status.poll.id;
   }
 
+  if (status.card?.author_account) {
+    normalStatus.card = { ...status.card, author_account: status.card.author_account.id };
+  }
+
   if (status.filtered) {
     normalStatus.filtered = status.filtered.map(normalizeFilterResult);
   }
diff --git a/app/javascript/mastodon/actions/trends.js b/app/javascript/mastodon/actions/trends.js
index 0b840b41c..01089fccb 100644
--- a/app/javascript/mastodon/actions/trends.js
+++ b/app/javascript/mastodon/actions/trends.js
@@ -1,6 +1,6 @@
 import api, { getLinks } from '../api';
 
-import { importFetchedStatuses } from './importer';
+import { importFetchedStatuses, importFetchedAccounts } from './importer';
 
 export const TRENDS_TAGS_FETCH_REQUEST = 'TRENDS_TAGS_FETCH_REQUEST';
 export const TRENDS_TAGS_FETCH_SUCCESS = 'TRENDS_TAGS_FETCH_SUCCESS';
@@ -49,8 +49,11 @@ export const fetchTrendingLinks = () => (dispatch) => {
   dispatch(fetchTrendingLinksRequest());
 
   api()
-    .get('/api/v1/trends/links')
-    .then(({ data }) => dispatch(fetchTrendingLinksSuccess(data)))
+    .get('/api/v1/trends/links', { params: { limit: 20 } })
+    .then(({ data }) => {
+      dispatch(importFetchedAccounts(data.map(link => link.author_account).filter(account => !!account)));
+      dispatch(fetchTrendingLinksSuccess(data));
+    })
     .catch(err => dispatch(fetchTrendingLinksFail(err)));
 };
 
diff --git a/app/javascript/mastodon/components/more_from_author.jsx b/app/javascript/mastodon/components/more_from_author.jsx
new file mode 100644
index 000000000..c20e76ac4
--- /dev/null
+++ b/app/javascript/mastodon/components/more_from_author.jsx
@@ -0,0 +1,19 @@
+import PropTypes from 'prop-types';
+
+import { FormattedMessage } from 'react-intl';
+
+import { AuthorLink } from 'mastodon/features/explore/components/author_link';
+
+export const MoreFromAuthor = ({ accountId }) => (
+  <div className='more-from-author'>
+    <svg viewBox='0 0 79 79' className='logo logo--icon' role='img'>
+      <use xlinkHref='#logo-symbol-icon' />
+    </svg>
+
+    <FormattedMessage id='link_preview.more_from_author' defaultMessage='More from {name}' values={{ name: <AuthorLink accountId={accountId} /> }} />
+  </div>
+);
+
+MoreFromAuthor.propTypes = {
+  accountId: PropTypes.string.isRequired,
+};
diff --git a/app/javascript/mastodon/features/explore/components/author_link.jsx b/app/javascript/mastodon/features/explore/components/author_link.jsx
new file mode 100644
index 000000000..b9dec3367
--- /dev/null
+++ b/app/javascript/mastodon/features/explore/components/author_link.jsx
@@ -0,0 +1,21 @@
+import PropTypes from 'prop-types';
+
+import { Link } from 'react-router-dom';
+
+import { Avatar } from 'mastodon/components/avatar';
+import { useAppSelector } from 'mastodon/store';
+
+export const AuthorLink = ({ accountId }) => {
+  const account = useAppSelector(state => state.getIn(['accounts', accountId]));
+
+  return (
+    <Link to={`/@${account.get('acct')}`} className='story__details__shared__author-link'>
+      <Avatar account={account} size={16} />
+      <bdi dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} />
+    </Link>
+  );
+};
+
+AuthorLink.propTypes = {
+  accountId: PropTypes.string.isRequired,
+};
diff --git a/app/javascript/mastodon/features/explore/components/story.jsx b/app/javascript/mastodon/features/explore/components/story.jsx
index 80dd5200f..a2cae942d 100644
--- a/app/javascript/mastodon/features/explore/components/story.jsx
+++ b/app/javascript/mastodon/features/explore/components/story.jsx
@@ -1,61 +1,89 @@
 import PropTypes from 'prop-types';
-import { PureComponent } from 'react';
+import { useState, useCallback } from 'react';
 
 import { FormattedMessage } from 'react-intl';
 
 import classNames from 'classnames';
 
+
 import { Blurhash } from 'mastodon/components/blurhash';
-import { accountsCountRenderer } from 'mastodon/components/hashtag';
 import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
 import { ShortNumber } from 'mastodon/components/short_number';
 import { Skeleton } from 'mastodon/components/skeleton';
 
-export default class Story extends PureComponent {
+import { AuthorLink } from './author_link';
 
-  static propTypes = {
-    url: PropTypes.string,
-    title: PropTypes.string,
-    lang: PropTypes.string,
-    publisher: PropTypes.string,
-    publishedAt: PropTypes.string,
-    author: PropTypes.string,
-    sharedTimes: PropTypes.number,
-    thumbnail: PropTypes.string,
-    thumbnailDescription: PropTypes.string,
-    blurhash: PropTypes.string,
-    expanded: PropTypes.bool,
-  };
+const sharesCountRenderer = (displayNumber, pluralReady) => (
+  <FormattedMessage
+    id='link_preview.shares'
+    defaultMessage='{count, plural, one {{counter} post} other {{counter} posts}}'
+    values={{
+      count: pluralReady,
+      counter: <strong>{displayNumber}</strong>,
+    }}
+  />
+);
 
-  state = {
-    thumbnailLoaded: false,
-  };
+export const Story = ({
+  url,
+  title,
+  lang,
+  publisher,
+  publishedAt,
+  author,
+  authorAccount,
+  sharedTimes,
+  thumbnail,
+  thumbnailDescription,
+  blurhash,
+  expanded
+}) => {
+  const [thumbnailLoaded, setThumbnailLoaded] = useState(false);
 
-  handleImageLoad = () => this.setState({ thumbnailLoaded: true });
+  const handleImageLoad = useCallback(() => {
+    setThumbnailLoaded(true);
+  }, [setThumbnailLoaded]);
 
-  render () {
-    const { expanded, url, title, lang, publisher, author, publishedAt, sharedTimes, thumbnail, thumbnailDescription, blurhash } = this.props;
-
-    const { thumbnailLoaded } = this.state;
-
-    return (
-      <a className={classNames('story', { expanded })} href={url} target='blank' rel='noopener'>
-        <div className='story__details'>
-          <div className='story__details__publisher'>{publisher ? <span lang={lang}>{publisher}</span> : <Skeleton width={50} />}{publishedAt && <> · <RelativeTimestamp timestamp={publishedAt} /></>}</div>
-          <div className='story__details__title' lang={lang}>{title ? title : <Skeleton />}</div>
-          <div className='story__details__shared'>{author && <><FormattedMessage id='link_preview.author' defaultMessage='By {name}' values={{ name: <strong>{author}</strong> }} /> · </>}{typeof sharedTimes === 'number' ? <ShortNumber value={sharedTimes} renderer={accountsCountRenderer} /> : <Skeleton width={100} />}</div>
+  return (
+    <div className={classNames('story', { expanded })}>
+      <div className='story__details'>
+        <div className='story__details__publisher'>
+          {publisher ? <span lang={lang}>{publisher}</span> : <Skeleton width={50} />}{publishedAt && <> · <RelativeTimestamp timestamp={publishedAt} /></>}
         </div>
 
-        <div className='story__thumbnail'>
-          {thumbnail ? (
-            <>
-              <div className={classNames('story__thumbnail__preview', { 'story__thumbnail__preview--hidden': thumbnailLoaded })}><Blurhash hash={blurhash} /></div>
-              <img src={thumbnail} onLoad={this.handleImageLoad} alt={thumbnailDescription} title={thumbnailDescription} lang={lang} />
-            </>
-          ) : <Skeleton />}
+        <a className='story__details__title' lang={lang} href={url} target='blank' rel='noopener'>
+          {title ? title : <Skeleton />}
+        </a>
+
+        <div className='story__details__shared'>
+          {author ? <FormattedMessage id='link_preview.author' className='story__details__shared__author' defaultMessage='By {name}' values={{ name: authorAccount ? <AuthorLink accountId={authorAccount} /> : <strong>{author}</strong> }} /> : <span />}
+          {typeof sharedTimes === 'number' ? <span className='story__details__shared__pill'><ShortNumber value={sharedTimes} renderer={sharesCountRenderer} /></span> : <Skeleton width='10ch' />}
         </div>
+      </div>
+
+      <a className='story__thumbnail' href={url} target='blank' rel='noopener'>
+        {thumbnail ? (
+          <>
+            <div className={classNames('story__thumbnail__preview', { 'story__thumbnail__preview--hidden': thumbnailLoaded })}><Blurhash hash={blurhash} /></div>
+            <img src={thumbnail} onLoad={handleImageLoad} alt={thumbnailDescription} title={thumbnailDescription} lang={lang} />
+          </>
+        ) : <Skeleton />}
       </a>
-    );
-  }
+    </div>
+  );
+};
 
-}
+Story.propTypes = {
+  url: PropTypes.string,
+  title: PropTypes.string,
+  lang: PropTypes.string,
+  publisher: PropTypes.string,
+  publishedAt: PropTypes.string,
+  author: PropTypes.string,
+  authorAccount: PropTypes.string,
+  sharedTimes: PropTypes.number,
+  thumbnail: PropTypes.string,
+  thumbnailDescription: PropTypes.string,
+  blurhash: PropTypes.string,
+  expanded: PropTypes.bool,
+};
diff --git a/app/javascript/mastodon/features/explore/links.jsx b/app/javascript/mastodon/features/explore/links.jsx
index 9e143b450..93fd1fb6d 100644
--- a/app/javascript/mastodon/features/explore/links.jsx
+++ b/app/javascript/mastodon/features/explore/links.jsx
@@ -13,7 +13,7 @@ import { DismissableBanner } from 'mastodon/components/dismissable_banner';
 import { LoadingIndicator } from 'mastodon/components/loading_indicator';
 import { WithRouterPropTypes } from 'mastodon/utils/react_router';
 
-import Story from './components/story';
+import { Story } from './components/story';
 
 const mapStateToProps = state => ({
   links: state.getIn(['trends', 'links', 'items']),
@@ -75,6 +75,7 @@ class Links extends PureComponent {
             publisher={link.get('provider_name')}
             publishedAt={link.get('published_at')}
             author={link.get('author_name')}
+            authorAccount={link.getIn(['author_account', 'id'])}
             sharedTimes={link.getIn(['history', 0, 'accounts']) * 1 + link.getIn(['history', 1, 'accounts']) * 1}
             thumbnail={link.get('image')}
             thumbnailDescription={link.get('image_description')}
diff --git a/app/javascript/mastodon/features/status/components/card.jsx b/app/javascript/mastodon/features/status/components/card.jsx
index c2f5703b3..f562e53f0 100644
--- a/app/javascript/mastodon/features/status/components/card.jsx
+++ b/app/javascript/mastodon/features/status/components/card.jsx
@@ -6,7 +6,6 @@ import { PureComponent } from 'react';
 import { FormattedMessage } from 'react-intl';
 
 import classNames from 'classnames';
-import { Link } from 'react-router-dom';
 
 
 import Immutable from 'immutable';
@@ -15,9 +14,9 @@ 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 { Avatar } from 'mastodon/components/avatar';
 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';
 
@@ -59,20 +58,6 @@ const addAutoPlay = html => {
   return html;
 };
 
-const MoreFromAuthor = ({ author }) => (
-  <div className='more-from-author'>
-    <svg viewBox='0 0 79 79' className='logo logo--icon' role='img'>
-      <use xlinkHref='#logo-symbol-icon' />
-    </svg>
-
-    <FormattedMessage id='link_preview.more_from_author' defaultMessage='More from {name}' values={{ name: <Link to={`/@${author.get('acct')}`}><Avatar account={author} size={16} /> {author.get('display_name')}</Link> }} />
-  </div>
-);
-
-MoreFromAuthor.propTypes = {
-  author: ImmutablePropTypes.map,
-};
-
 export default class Card extends PureComponent {
 
   static propTypes = {
@@ -259,7 +244,7 @@ export default class Card extends PureComponent {
           {description}
         </a>
 
-        {showAuthor && <MoreFromAuthor author={card.get('author_account')} />}
+        {showAuthor && <MoreFromAuthor accountId={card.get('author_account')} />}
       </>
     );
   }
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 63298d59e..4f5caeb6a 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
   "link_preview.author": "By {name}",
   "link_preview.more_from_author": "More from {name}",
+  "link_preview.shares": "{count, plural, one {{counter} post} other {{counter} posts}}",
   "lists.account.add": "Add to list",
   "lists.account.remove": "Remove from list",
   "lists.delete": "Delete list",
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 2400f319d..d5c3d2605 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -8796,43 +8796,80 @@ noscript {
   display: flex;
   align-items: center;
   color: $primary-text-color;
-  text-decoration: none;
-  padding: 15px;
+  padding: 16px;
   border-bottom: 1px solid var(--background-border-color);
-  gap: 15px;
+  gap: 16px;
 
   &:last-child {
     border-bottom: 0;
   }
 
-  &:hover,
-  &:active,
-  &:focus {
-    color: $highlight-text-color;
-
-    .story__details__publisher,
-    .story__details__shared {
-      color: $highlight-text-color;
-    }
-  }
-
   &__details {
     flex: 1 1 auto;
 
     &__publisher {
       color: $darker-text-color;
       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: $primary-text-color;
+
+      &:hover,
+      &:active,
+      &:focus {
+        color: $highlight-text-color;
+      }
     }
 
     &__shared {
+      display: flex;
+      align-items: center;
       color: $darker-text-color;
+      gap: 8px;
+      justify-content: space-between;
+      font-size: 14px;
+      line-height: 20px;
+
+      & > span {
+        display: flex;
+        align-items: center;
+        gap: 4px;
+      }
+
+      &__pill {
+        background: var(--surface-variant-background-color);
+        border-radius: 4px;
+        color: inherit;
+        text-decoration: none;
+        padding: 4px 12px;
+        font-size: 12px;
+        font-weight: 500;
+        line-height: 16px;
+      }
+
+      &__author-link {
+        display: inline-flex;
+        align-items: center;
+        gap: 4px;
+        color: $primary-text-color;
+        font-weight: 500;
+        text-decoration: none;
+
+        &:hover,
+        &:active,
+        &:focus {
+          color: $highlight-text-color;
+        }
+      }
     }
 
     strong {
@@ -9903,14 +9940,14 @@ noscript {
     color: inherit;
     text-decoration: none;
     padding: 4px 12px;
-    background: $ui-base-color;
+    background: var(--surface-variant-background-color);
     border-radius: 4px;
     font-weight: 500;
 
     &:hover,
     &:focus,
     &:active {
-      background: lighten($ui-base-color, 4%);
+      background: var(--surface-variant-active-background-color);
     }
   }
 
diff --git a/app/javascript/styles/mastodon/variables.scss b/app/javascript/styles/mastodon/variables.scss
index 58b9dd9b6..2848a42b3 100644
--- a/app/javascript/styles/mastodon/variables.scss
+++ b/app/javascript/styles/mastodon/variables.scss
@@ -106,4 +106,6 @@ $font-monospace: 'mastodon-font-monospace' !default;
   --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%)};
 }

From 64fc17352bec11684cf617b10ebc5a11eb7ec924 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 13 Jun 2024 15:13:06 +0200
Subject: [PATCH 107/133] chore(deps): update dependency sanitize to v6.1.1
 (#30683)

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 222aaff50..89425363d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -772,7 +772,7 @@ GEM
       fugit (~> 1.1, >= 1.1.6)
     safety_net_attestation (0.4.0)
       jwt (~> 2.0)
-    sanitize (6.1.0)
+    sanitize (6.1.1)
       crass (~> 1.0.2)
       nokogiri (>= 1.12.0)
     scenic (1.8.0)

From 474dda70272b9e406fb4a22b1f66caf797c2f8b2 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 13 Jun 2024 15:15:01 +0200
Subject: [PATCH 108/133] chore(deps): update dependency aws-sdk-s3 to v1.152.2
 (#30680)

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 89425363d..172b7cbdd 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -109,7 +109,7 @@ GEM
     aws-sdk-kms (1.83.0)
       aws-sdk-core (~> 3, >= 3.197.0)
       aws-sigv4 (~> 1.1)
-    aws-sdk-s3 (1.152.1)
+    aws-sdk-s3 (1.152.2)
       aws-sdk-core (~> 3, >= 3.197.0)
       aws-sdk-kms (~> 1)
       aws-sigv4 (~> 1.8)

From 3b7c50abca213353f6e210837fda0f21baf1be20 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 13 Jun 2024 09:15:32 -0400
Subject: [PATCH 109/133] Remove bundler-audit ignore config (#30672)

---
 .bundler-audit.yml                  | 6 ------
 .github/workflows/bundler-audit.yml | 2 --
 2 files changed, 8 deletions(-)
 delete mode 100644 .bundler-audit.yml

diff --git a/.bundler-audit.yml b/.bundler-audit.yml
deleted file mode 100644
index 0671df390..000000000
--- a/.bundler-audit.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-ignore:
-  # devise-two-factor advisory about brute-forcing TOTP
-  # We have rate-limits on authentication endpoints in place (including second
-  # factor verification) since Mastodon v3.2.0
-  - CVE-2024-0227
diff --git a/.github/workflows/bundler-audit.yml b/.github/workflows/bundler-audit.yml
index 923abcd91..48f9d8293 100644
--- a/.github/workflows/bundler-audit.yml
+++ b/.github/workflows/bundler-audit.yml
@@ -6,14 +6,12 @@ on:
     paths:
       - 'Gemfile*'
       - '.ruby-version'
-      - '.bundler-audit.yml'
       - '.github/workflows/bundler-audit.yml'
 
   pull_request:
     paths:
       - 'Gemfile*'
       - '.ruby-version'
-      - '.bundler-audit.yml'
       - '.github/workflows/bundler-audit.yml'
 
   schedule:

From 8889816a51df17e2e78cdcaa17bda0780393bd0d Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 13 Jun 2024 09:23:32 -0400
Subject: [PATCH 110/133] Use stock ruby environment on CI lint tasks (#30657)

---
 .github/workflows/bundler-audit.yml |  9 +++++++--
 .github/workflows/lint-haml.yml     | 10 ++++++++--
 .github/workflows/lint-ruby.yml     |  9 +++++++--
 3 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/bundler-audit.yml b/.github/workflows/bundler-audit.yml
index 48f9d8293..e3e2da0c7 100644
--- a/.github/workflows/bundler-audit.yml
+++ b/.github/workflows/bundler-audit.yml
@@ -21,12 +21,17 @@ jobs:
   security:
     runs-on: ubuntu-latest
 
+    env:
+      BUNDLE_ONLY: development
+
     steps:
       - name: Clone repository
         uses: actions/checkout@v4
 
-      - name: Set up Ruby environment
-        uses: ./.github/actions/setup-ruby
+      - name: Set up Ruby
+        uses: ruby/setup-ruby@v1
+        with:
+          bundler-cache: true
 
       - name: Run bundler-audit
         run: bundle exec bundler-audit check --update
diff --git a/.github/workflows/lint-haml.yml b/.github/workflows/lint-haml.yml
index 25615b720..ca4b0c80b 100644
--- a/.github/workflows/lint-haml.yml
+++ b/.github/workflows/lint-haml.yml
@@ -26,12 +26,18 @@ on:
 jobs:
   lint:
     runs-on: ubuntu-latest
+
+    env:
+      BUNDLE_ONLY: development
+
     steps:
       - name: Clone repository
         uses: actions/checkout@v4
 
-      - name: Set up Ruby environment
-        uses: ./.github/actions/setup-ruby
+      - name: Set up Ruby
+        uses: ruby/setup-ruby@v1
+        with:
+          bundler-cache: true
 
       - name: Run haml-lint
         run: |
diff --git a/.github/workflows/lint-ruby.yml b/.github/workflows/lint-ruby.yml
index 411b32348..2e4de5572 100644
--- a/.github/workflows/lint-ruby.yml
+++ b/.github/workflows/lint-ruby.yml
@@ -27,12 +27,17 @@ jobs:
   lint:
     runs-on: ubuntu-latest
 
+    env:
+      BUNDLE_ONLY: development
+
     steps:
       - name: Clone repository
         uses: actions/checkout@v4
 
-      - name: Set up Ruby environment
-        uses: ./.github/actions/setup-ruby
+      - name: Set up Ruby
+        uses: ruby/setup-ruby@v1
+        with:
+          bundler-cache: true
 
       - name: Set-up RuboCop Problem Matcher
         uses: r7kamura/rubocop-problem-matchers-action@v1

From dd587d29b68ee8548cd91697ba85b1ee274845d5 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 13 Jun 2024 15:30:07 +0200
Subject: [PATCH 111/133] New Crowdin Translations (automated) (#30684)

Co-authored-by: GitHub Actions <noreply@github.com>
---
 app/javascript/mastodon/locales/ur.json |  4 +---
 config/locales/activerecord.ur.yml      | 26 +++++++++++++++++++++++++
 config/locales/doorkeeper.cs.yml        |  1 +
 config/locales/doorkeeper.ia.yml        |  2 ++
 config/locales/doorkeeper.sl.yml        |  2 ++
 config/locales/doorkeeper.sv.yml        |  1 +
 config/locales/simple_form.sv.yml       |  2 ++
 config/locales/sv.yml                   |  1 +
 8 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/app/javascript/mastodon/locales/ur.json b/app/javascript/mastodon/locales/ur.json
index 6f3debae2..1b9f8d969 100644
--- a/app/javascript/mastodon/locales/ur.json
+++ b/app/javascript/mastodon/locales/ur.json
@@ -28,7 +28,7 @@
   "account.follow": "پیروی کریں",
   "account.follow_back": "اکاؤنٹ کو فالو بیک ",
   "account.followers": "پیروکار",
-  "account.followers.empty": "\"ہنوز اس صارف کی کوئی پیروی نہیں کرتا\".",
+  "account.followers.empty": "ہنوز اس صارف کی کوئی پیروی نہیں کرتا.",
   "account.followers_counter": "{count, plural,one {{counter} پیروکار} other {{counter} پیروکار}}",
   "account.following": "فالو کر رہے ہیں",
   "account.following_counter": "{count, plural, one {{counter} پیروی کر رہے ہیں} other {{counter} پیروی کر رہے ہیں}}",
@@ -66,11 +66,9 @@
   "account.unmute": "@{name} کو با آواز کریں",
   "account.unmute_notifications_short": "نوٹیفیکیشنز کو خاموش نہ کریں",
   "account.unmute_short": "کو خاموش نہ کریں",
-  "account_note.placeholder": "Click to add a note",
   "admin.dashboard.daily_retention": "ایڈمن ڈیش بورڈ کو ڈیلی چیک ان کریں",
   "admin.dashboard.monthly_retention": "ایڈمن کیش بورڈ کو منتھلی چیک ان کریں",
   "admin.dashboard.retention.average": "اوسط",
-  "admin.dashboard.retention.cohort": "Sign-up month",
   "admin.dashboard.retention.cohort_size": "نئے یسرز",
   "alert.rate_limited.message": "\"{retry_time, time, medium} کے بعد کوشش کریں\".",
   "alert.rate_limited.title": "محدود شرح",
diff --git a/config/locales/activerecord.ur.yml b/config/locales/activerecord.ur.yml
index 2cace5883..e8fbef9af 100644
--- a/config/locales/activerecord.ur.yml
+++ b/config/locales/activerecord.ur.yml
@@ -1 +1,27 @@
+---
 ur:
+  activerecord:
+    attributes:
+      poll:
+        expires_at: ڈیڈ لائن
+        options: اپنی مرضی
+      user:
+        agreement: سروس کا معاہدہ
+        email: ای میل ایڈریسز
+        locale: لوکل
+        password: پاس ورڈ
+      user/account:
+        username: یوزر نیم
+      user/invite_request:
+        text: وجہ
+    errors:
+      models:
+        account:
+          attributes:
+            username:
+              invalid: صرف حروف، نمبر اور انڈر سکور پر مشتمل ہونا چاہیے۔
+              reserved: محفوظ ہے
+        admin/webhook:
+          attributes:
+            url:
+              invalid: ایک درست URL نہیں ہے۔
diff --git a/config/locales/doorkeeper.cs.yml b/config/locales/doorkeeper.cs.yml
index be2a4d971..332383468 100644
--- a/config/locales/doorkeeper.cs.yml
+++ b/config/locales/doorkeeper.cs.yml
@@ -135,6 +135,7 @@ cs:
         media: Mediální přílohy
         mutes: Skrytí
         notifications: Oznámení
+        profile: Váš Mastodon profil
         push: Push oznámení
         reports: Hlášení
         search: Hledání
diff --git a/config/locales/doorkeeper.ia.yml b/config/locales/doorkeeper.ia.yml
index 5b99abb7b..985d073ab 100644
--- a/config/locales/doorkeeper.ia.yml
+++ b/config/locales/doorkeeper.ia.yml
@@ -135,6 +135,7 @@ ia:
         media: Annexos multimedial
         mutes: Silentiates
         notifications: Notificationes
+        profile: Tu profilo de Mastodon
         push: Notificationes push
         reports: Reportos
         search: Cercar
@@ -165,6 +166,7 @@ ia:
       admin:write:reports: exequer actiones de moderation sur reportos
       crypto: usar cryptation de puncta a puncta
       follow: modificar relationes inter contos
+      profile: leger solmente le information de profilo de tu conto
       push: reciper tu notificationes push
       read: leger tote le datos de tu conto
       read:accounts: vider informationes de contos
diff --git a/config/locales/doorkeeper.sl.yml b/config/locales/doorkeeper.sl.yml
index a613308b2..f6f64fc87 100644
--- a/config/locales/doorkeeper.sl.yml
+++ b/config/locales/doorkeeper.sl.yml
@@ -135,6 +135,7 @@ sl:
         media: Predstavnostne priloge
         mutes: Utišani
         notifications: Obvestila
+        profile: Vaš profil Mastodon
         push: Potisna obvestila
         reports: Prijave
         search: Iskanje
@@ -165,6 +166,7 @@ sl:
       admin:write:reports: izvedi moderirana dejanja na prijavah
       crypto: Uporabi šifriranje od konca do konca
       follow: spremeni razmerja med računi
+      profile: preberi le podatke profila računa
       push: prejmi potisna obvestila
       read: preberi vse podatke svojega računa
       read:accounts: oglejte si podrobnosti računov
diff --git a/config/locales/doorkeeper.sv.yml b/config/locales/doorkeeper.sv.yml
index bc4ba6f53..b46c16d4d 100644
--- a/config/locales/doorkeeper.sv.yml
+++ b/config/locales/doorkeeper.sv.yml
@@ -166,6 +166,7 @@ sv:
       admin:write:reports: utföra modereringsåtgärder på rapporter
       crypto: använd obruten kryptering
       follow: modifiera kontorelationer
+      profile: läs endast ditt kontos profilinformation
       push: ta emot dina push-notiser
       read: läsa dina kontodata
       read:accounts: se kontoinformation
diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml
index 5e5c6f954..11d142a2b 100644
--- a/config/locales/simple_form.sv.yml
+++ b/config/locales/simple_form.sv.yml
@@ -77,11 +77,13 @@ sv:
           warn: Dölj det filtrerade innehållet bakom en varning som visar filtrets rubrik
       form_admin_settings:
         activity_api_enabled: Antalet lokalt publicerade inlägg, aktiva användare och nya registrerade konton per vecka
+        app_icon: WEBP, PNG, GIF eller JPG. Använd istället för appens egna ikon på mobila enheter.
         backups_retention_period: Användare har möjlighet att generera arkiv av sina inlägg för att ladda ned senare. När det sätts till ett positivt värde raderas dessa arkiv automatiskt från din lagring efter det angivna antalet dagar.
         bootstrap_timeline_accounts: Dessa konton kommer fästas högst upp i nya användares följrekommendationer.
         closed_registrations_message: Visas när nyregistreringar är avstängda
         content_cache_retention_period: Alla inlägg från andra servrar (inklusive booster och svar) kommer att raderas efter det angivna antalet dagar, utan hänsyn till någon lokal användarinteraktion med dessa inlägg. Detta inkluderar inlägg där en lokal användare har markerat det som bokmärke eller favoriter. Privata omnämnanden mellan användare från olika instanser kommer också att gå förlorade och blir omöjliga att återställa. Användningen av denna inställning är avsedd för specialfall och bryter många användarförväntningar när de implementeras för allmänt bruk.
         custom_css: Du kan använda anpassade stilar på webbversionen av Mastodon.
+        favicon: WEBP, PNG, GIF eller JPG. Används på mobila enheter istället för appens egen ikon.
         mascot: Åsidosätter illustrationen i det avancerade webbgränssnittet.
         media_cache_retention_period: Mediafiler från inlägg som gjorts av fjärranvändare cachas på din server. När inställd på ett positivt värde kommer media att raderas efter det angivna antalet dagar. Om mediadatat begärs efter att det har raderats, kommer det att laddas ned igen om källinnehållet fortfarande är tillgängligt. På grund av begränsningar för hur ofta förhandsgranskningskort för länkar hämtas från tredjepartswebbplatser, rekommenderas det att ange detta värde till minst 14 dagar, annars kommer förhandsgranskningskorten inte att uppdateras på begäran före den tiden.
         peers_api_enabled: En lista över domänen den här servern har stött på i fediversum. Ingen data inkluderas om du har federerat med servern, bara att din server känner till den. Detta används av tjänster som samlar statistik om federering i allmänhet.
diff --git a/config/locales/sv.yml b/config/locales/sv.yml
index cf68cdd56..a3d31547c 100644
--- a/config/locales/sv.yml
+++ b/config/locales/sv.yml
@@ -951,6 +951,7 @@ sv:
       delete: Radera
       edit_preset: Redigera varningsförval
       empty: Du har inte definierat några varningsförval ännu.
+      title: Varning förinställningar
     webhooks:
       add_new: Lägg till slutpunkt
       delete: Ta bort

From 45abddb302a44adafdcc0479cc93dea4d8fb4b64 Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Thu, 13 Jun 2024 16:10:34 +0200
Subject: [PATCH 112/133] Fix pagination attributes not being returned in
 ungroupable-only pages (#30688)

---
 app/serializers/rest/notification_group_serializer.rb | 2 +-
 spec/requests/api/v2_alpha/notifications_spec.rb      | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/app/serializers/rest/notification_group_serializer.rb b/app/serializers/rest/notification_group_serializer.rb
index 05b51b07a..ce1950c5a 100644
--- a/app/serializers/rest/notification_group_serializer.rb
+++ b/app/serializers/rest/notification_group_serializer.rb
@@ -45,6 +45,6 @@ class REST::NotificationGroupSerializer < ActiveModel::Serializer
   end
 
   def paginated?
-    instance_options[:group_metadata].present?
+    !instance_options[:group_metadata].nil?
   end
 end
diff --git a/spec/requests/api/v2_alpha/notifications_spec.rb b/spec/requests/api/v2_alpha/notifications_spec.rb
index 9bd1a32e9..ac44605ac 100644
--- a/spec/requests/api/v2_alpha/notifications_spec.rb
+++ b/spec/requests/api/v2_alpha/notifications_spec.rb
@@ -58,6 +58,7 @@ RSpec.describe 'Notifications' do
 
         expect(response).to have_http_status(200)
         expect(body_json_types.uniq).to eq ['mention']
+        expect(body_as_json[0][:page_min_id]).to_not be_nil
       end
     end
 

From 3a191b3797dde1daf79cd748a14b87240532d543 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 13 Jun 2024 10:27:17 -0400
Subject: [PATCH 113/133] Add `rubocop` binstub, simplify configuration
 (#30407)

---
 .github/workflows/lint-ruby.yml |   2 +-
 .rubocop.yml                    | 254 ++++----------------------------
 .rubocop/custom.yml             |   6 +
 .rubocop/layout.yml             |   6 +
 .rubocop/metrics.yml            |  23 +++
 .rubocop/naming.yml             |   3 +
 .rubocop/rails.yml              |  27 ++++
 .rubocop/rspec.yml              |  17 +++
 .rubocop/rspec_rails.yml        |   3 +
 .rubocop/strict.yml             |  19 +++
 .rubocop/style.yml              |  47 ++++++
 bin/rubocop                     |  27 ++++
 lint-staged.config.js           |   3 +-
 13 files changed, 210 insertions(+), 227 deletions(-)
 create mode 100644 .rubocop/custom.yml
 create mode 100644 .rubocop/layout.yml
 create mode 100644 .rubocop/metrics.yml
 create mode 100644 .rubocop/naming.yml
 create mode 100644 .rubocop/rails.yml
 create mode 100644 .rubocop/rspec.yml
 create mode 100644 .rubocop/rspec_rails.yml
 create mode 100644 .rubocop/strict.yml
 create mode 100644 .rubocop/style.yml
 create mode 100755 bin/rubocop

diff --git a/.github/workflows/lint-ruby.yml b/.github/workflows/lint-ruby.yml
index 2e4de5572..f4e81d508 100644
--- a/.github/workflows/lint-ruby.yml
+++ b/.github/workflows/lint-ruby.yml
@@ -43,7 +43,7 @@ jobs:
         uses: r7kamura/rubocop-problem-matchers-action@v1
 
       - name: Run rubocop
-        run: bundle exec rubocop
+        run: bin/rubocop
 
       - name: Run brakeman
         if: always() # Run both checks, even if the first failed
diff --git a/.rubocop.yml b/.rubocop.yml
index 090b89b25..cf4ee565e 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,7 +1,34 @@
-# Can be removed once all rules are addressed or moved to this file as documented overrides
-inherit_from: .rubocop_todo.yml
+---
+AllCops:
+  CacheRootDirectory: tmp
+  DisplayCopNames: true
+  DisplayStyleGuide: true
+  Exclude:
+    - db/schema.rb
+    - bin/*
+    - node_modules/**/*
+    - Vagrantfile
+    - vendor/**/*
+    - config/initializers/json_ld*
+    - lib/mastodon/migration_helpers.rb
+    - lib/templates/**/*
+  ExtraDetails: true
+  NewCops: enable
+  TargetRubyVersion: 3.1 # Oldest supported ruby version
+  UseCache: true
+
+inherit_from:
+  - .rubocop/layout.yml
+  - .rubocop/metrics.yml
+  - .rubocop/naming.yml
+  - .rubocop/rails.yml
+  - .rubocop/rspec_rails.yml
+  - .rubocop/rspec.yml
+  - .rubocop/style.yml
+  - .rubocop/custom.yml
+  - .rubocop_todo.yml
+  - .rubocop/strict.yml
 
-# Used for merging with exclude lists with .rubocop_todo.yml
 inherit_mode:
   merge:
     - Exclude
@@ -12,224 +39,3 @@ require:
   - rubocop-rspec_rails
   - rubocop-performance
   - rubocop-capybara
-  - ./lib/linter/rubocop_middle_dot
-
-AllCops:
-  TargetRubyVersion: 3.1 # Set to minimum supported version of CI
-  DisplayCopNames: true
-  DisplayStyleGuide: true
-  ExtraDetails: true
-  UseCache: true
-  CacheRootDirectory: tmp
-  NewCops: enable # Opt-in to newly added rules
-  Exclude:
-    - db/schema.rb
-    - 'bin/*'
-    - 'node_modules/**/*'
-    - 'Vagrantfile'
-    - 'vendor/**/*'
-    - 'config/initializers/json_ld*' # Generated files
-    - 'lib/mastodon/migration_helpers.rb' # Vendored from GitLab
-    - 'lib/templates/**/*'
-
-# Reason: Prefer Hashes without extreme indentation
-# https://docs.rubocop.org/rubocop/cops_layout.html#layoutfirsthashelementindentation
-Layout/FirstHashElementIndentation:
-  EnforcedStyle: consistent
-
-# Reason: Currently disabled in .rubocop_todo.yml
-# https://docs.rubocop.org/rubocop/cops_layout.html#layoutlinelength
-Layout/LineLength:
-  Max: 300 # Default of 120 causes a duplicate entry in generated todo file
-
-## Disable most Metrics/*Length cops
-# Reason: those are often triggered and force significant refactors when this happend
-#         but the team feel they are not really improving the code quality.
-
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocklength
-Metrics/BlockLength:
-  Enabled: false
-
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsclasslength
-Metrics/ClassLength:
-  Enabled: false
-
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmethodlength
-Metrics/MethodLength:
-  Enabled: false
-
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmodulelength
-Metrics/ModuleLength:
-  Enabled: false
-
-## End Disable Metrics/*Length cops
-
-# Reason: Currently disabled in .rubocop_todo.yml
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsabcsize
-Metrics/AbcSize:
-  Exclude:
-    - 'lib/mastodon/cli/*.rb'
-
-# Reason: Currently disabled in .rubocop_todo.yml
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricscyclomaticcomplexity
-Metrics/CyclomaticComplexity:
-  Exclude:
-    - lib/mastodon/cli/*.rb
-
-# Reason:
-# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsparameterlists
-Metrics/ParameterLists:
-  CountKeywordArgs: false
-
-# Reason: Prefer seeing a variable name
-# https://docs.rubocop.org/rubocop/cops_naming.html#namingblockforwarding
-Naming/BlockForwarding:
-  EnforcedStyle: explicit
-
-# Reason: Prevailing style is argument file paths
-# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsfilepath
-Rails/FilePath:
-  EnforcedStyle: arguments
-
-# Reason: Prevailing style uses numeric status codes, matches RSpec/Rails/HttpStatus
-# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railshttpstatus
-Rails/HttpStatus:
-  EnforcedStyle: numeric
-
-# Reason: Conflicts with `Lint/UselessMethodDefinition` for inherited controller actions
-# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railslexicallyscopedactionfilter
-Rails/LexicallyScopedActionFilter:
-  Exclude:
-    - 'app/controllers/auth/*'
-
-# Reason: These tasks are doing local work which do not need full env loaded
-# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsrakeenvironment
-Rails/RakeEnvironment:
-  Exclude:
-    - 'lib/tasks/auto_annotate_models.rake'
-    - 'lib/tasks/emojis.rake'
-    - 'lib/tasks/mastodon.rake'
-    - 'lib/tasks/repo.rake'
-    - 'lib/tasks/statistics.rake'
-
-# Reason: There are appropriate times to use these features
-# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsskipsmodelvalidations
-Rails/SkipsModelValidations:
-  Enabled: false
-
-# Reason: We want to preserve the ability to migrate from arbitrary old versions,
-# and cannot guarantee that every installation has run every migration as they upgrade.
-# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsunusedignoredcolumns
-Rails/UnusedIgnoredColumns:
-  Enabled: false
-
-# Reason: Prevailing style choice
-# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsnegateinclude
-Rails/NegateInclude:
-  Enabled: false
-
-# Reason: Enforce default limit, but allow some elements to span lines
-# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecexamplelength
-RSpec/ExampleLength:
-  CountAsOne: ['array', 'heredoc', 'method_call']
-
-# Reason:
-# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecnamedsubject
-RSpec/NamedSubject:
-  EnforcedStyle: named_only
-
-# Reason: Prevailing style choice
-# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecnottonot
-RSpec/NotToNot:
-  EnforcedStyle: to_not
-
-# Reason: Match overrides from Rspec/FilePath rule above
-# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecspecfilepathformat
-RSpec/SpecFilePathFormat:
-  CustomTransform:
-    ActivityPub: activitypub
-    DeepL: deepl
-    FetchOEmbedService: fetch_oembed_service
-    OEmbedController: oembed_controller
-    OStatus: ostatus
-
-# Reason: Prevailing style uses numeric status codes, matches Rails/HttpStatus
-# https://docs.rubocop.org/rubocop-rspec/cops_rspec_rails.html#rspecrailshttpstatus
-RSpecRails/HttpStatus:
-  EnforcedStyle: numeric
-
-# Reason:
-# https://docs.rubocop.org/rubocop/cops_style.html#styleclassandmodulechildren
-Style/ClassAndModuleChildren:
-  Enabled: false
-
-# Reason: Classes mostly self-document with their names
-# https://docs.rubocop.org/rubocop/cops_style.html#styledocumentation
-Style/Documentation:
-  Enabled: false
-
-# Reason: Route redirects are not token-formatted and must be skipped
-# https://docs.rubocop.org/rubocop/cops_style.html#styleformatstringtoken
-Style/FormatStringToken:
-  inherit_mode:
-    merge:
-      - AllowedMethods # The rubocop-rails config adds `redirect`
-  AllowedMethods:
-    - redirect_with_vary
-
-# Reason: Prevailing style choice
-# https://docs.rubocop.org/rubocop/cops_style.html#stylehashaslastarrayitem
-Style/HashAsLastArrayItem:
-  Enabled: false
-
-# Reason: Enforce modern Ruby style
-# https://docs.rubocop.org/rubocop/cops_style.html#stylehashsyntax
-Style/HashSyntax:
-  EnforcedStyle: ruby19_no_mixed_keys
-  EnforcedShorthandSyntax: either
-
-# Reason:
-# https://docs.rubocop.org/rubocop/cops_style.html#stylenumericliterals
-Style/NumericLiterals:
-  AllowedPatterns:
-    - \d{4}_\d{2}_\d{2}_\d{6} # For DB migration date version number readability
-
-# Reason:
-# https://docs.rubocop.org/rubocop/cops_style.html#stylepercentliteraldelimiters
-Style/PercentLiteralDelimiters:
-  PreferredDelimiters:
-    '%i': '()'
-    '%w': '()'
-
-# Reason: Prefer less indentation in conditional assignments
-# https://docs.rubocop.org/rubocop/cops_style.html#styleredundantbegin
-Style/RedundantBegin:
-  Enabled: false
-
-# Reason: Prevailing style choice
-# https://docs.rubocop.org/rubocop/cops_style.html#styleredundantfetchblock
-Style/RedundantFetchBlock:
-  Enabled: false
-
-# Reason: Overridden to reduce implicit StandardError rescues
-# https://docs.rubocop.org/rubocop/cops_style.html#stylerescuestandarderror
-Style/RescueStandardError:
-  EnforcedStyle: implicit
-
-# Reason: Originally disabled for CodeClimate, and no config consensus has been found
-# https://docs.rubocop.org/rubocop/cops_style.html#stylesymbolarray
-Style/SymbolArray:
-  Enabled: false
-
-# Reason:
-# https://docs.rubocop.org/rubocop/cops_style.html#styletrailingcommainarrayliteral
-Style/TrailingCommaInArrayLiteral:
-  EnforcedStyleForMultiline: 'comma'
-
-# Reason:
-# https://docs.rubocop.org/rubocop/cops_style.html#styletrailingcommainhashliteral
-Style/TrailingCommaInHashLiteral:
-  EnforcedStyleForMultiline: 'comma'
-
-Style/MiddleDot:
-  Enabled: true
diff --git a/.rubocop/custom.yml b/.rubocop/custom.yml
new file mode 100644
index 000000000..63035837f
--- /dev/null
+++ b/.rubocop/custom.yml
@@ -0,0 +1,6 @@
+---
+require:
+  - ../lib/linter/rubocop_middle_dot
+
+Style/MiddleDot:
+  Enabled: true
diff --git a/.rubocop/layout.yml b/.rubocop/layout.yml
new file mode 100644
index 000000000..487879ca2
--- /dev/null
+++ b/.rubocop/layout.yml
@@ -0,0 +1,6 @@
+---
+Layout/FirstHashElementIndentation:
+  EnforcedStyle: consistent
+
+Layout/LineLength:
+  Max: 300 # Default of 120 causes a duplicate entry in generated todo file
diff --git a/.rubocop/metrics.yml b/.rubocop/metrics.yml
new file mode 100644
index 000000000..89532af42
--- /dev/null
+++ b/.rubocop/metrics.yml
@@ -0,0 +1,23 @@
+---
+Metrics/AbcSize:
+  Exclude:
+    - lib/mastodon/cli/*.rb
+
+Metrics/BlockLength:
+  Enabled: false
+
+Metrics/ClassLength:
+  Enabled: false
+
+Metrics/CyclomaticComplexity:
+  Exclude:
+    - lib/mastodon/cli/*.rb
+
+Metrics/MethodLength:
+  Enabled: false
+
+Metrics/ModuleLength:
+  Enabled: false
+
+Metrics/ParameterLists:
+  CountKeywordArgs: false
diff --git a/.rubocop/naming.yml b/.rubocop/naming.yml
new file mode 100644
index 000000000..da6ad4ac5
--- /dev/null
+++ b/.rubocop/naming.yml
@@ -0,0 +1,3 @@
+---
+Naming/BlockForwarding:
+  EnforcedStyle: explicit
diff --git a/.rubocop/rails.yml b/.rubocop/rails.yml
new file mode 100644
index 000000000..b83928dee
--- /dev/null
+++ b/.rubocop/rails.yml
@@ -0,0 +1,27 @@
+---
+Rails/FilePath:
+  EnforcedStyle: arguments
+
+Rails/HttpStatus:
+  EnforcedStyle: numeric
+
+Rails/LexicallyScopedActionFilter:
+  Exclude:
+    - app/controllers/auth/* # Conflicts with `Lint/UselessMethodDefinition` for inherited controller actions
+
+Rails/NegateInclude:
+  Enabled: false
+
+Rails/RakeEnvironment:
+  Exclude: # Tasks are doing local work which do not need full env loaded
+    - lib/tasks/auto_annotate_models.rake
+    - lib/tasks/emojis.rake
+    - lib/tasks/mastodon.rake
+    - lib/tasks/repo.rake
+    - lib/tasks/statistics.rake
+
+Rails/SkipsModelValidations:
+  Enabled: false
+
+Rails/UnusedIgnoredColumns:
+  Enabled: false # Preserve ability to migrate from arbitrary old versions
diff --git a/.rubocop/rspec.yml b/.rubocop/rspec.yml
new file mode 100644
index 000000000..a6f8a7aee
--- /dev/null
+++ b/.rubocop/rspec.yml
@@ -0,0 +1,17 @@
+---
+RSpec/ExampleLength:
+  CountAsOne: ['array', 'heredoc', 'method_call']
+
+RSpec/NamedSubject:
+  EnforcedStyle: named_only
+
+RSpec/NotToNot:
+  EnforcedStyle: to_not
+
+RSpec/SpecFilePathFormat:
+  CustomTransform:
+    ActivityPub: activitypub
+    DeepL: deepl
+    FetchOEmbedService: fetch_oembed_service
+    OEmbedController: oembed_controller
+    OStatus: ostatus
diff --git a/.rubocop/rspec_rails.yml b/.rubocop/rspec_rails.yml
new file mode 100644
index 000000000..993a5689a
--- /dev/null
+++ b/.rubocop/rspec_rails.yml
@@ -0,0 +1,3 @@
+---
+RSpecRails/HttpStatus:
+  EnforcedStyle: numeric
diff --git a/.rubocop/strict.yml b/.rubocop/strict.yml
new file mode 100644
index 000000000..2222c6d8b
--- /dev/null
+++ b/.rubocop/strict.yml
@@ -0,0 +1,19 @@
+Lint/Debugger: # Remove any `binding.pry`
+  Enabled: true
+  Exclude: []
+
+RSpec/Focus: # Require full spec run on CI
+  Enabled: true
+  Exclude: []
+
+Rails/Output: # Remove any `puts` debugging
+  Enabled: true
+  Exclude: []
+
+Rails/FindEach: # Using `each` could impact performance, use `find_each`
+  Enabled: true
+  Exclude: []
+
+Rails/UniqBeforePluck: # Require `uniq.pluck` and not `pluck.uniq`
+  Enabled: true
+  Exclude: []
diff --git a/.rubocop/style.yml b/.rubocop/style.yml
new file mode 100644
index 000000000..03e35a70a
--- /dev/null
+++ b/.rubocop/style.yml
@@ -0,0 +1,47 @@
+---
+Style/ClassAndModuleChildren:
+  Enabled: false
+
+Style/Documentation:
+  Enabled: false
+
+Style/FormatStringToken:
+  AllowedMethods:
+    - redirect_with_vary # Route redirects are not token-formatted
+  inherit_mode:
+    merge:
+      - AllowedMethods
+
+Style/HashAsLastArrayItem:
+  Enabled: false
+
+Style/HashSyntax:
+  EnforcedShorthandSyntax: either
+  EnforcedStyle: ruby19_no_mixed_keys
+
+Style/NumericLiterals:
+  AllowedPatterns:
+    - \d{4}_\d{2}_\d{2}_\d{6}
+
+Style/PercentLiteralDelimiters:
+  PreferredDelimiters:
+    '%i': ()
+    '%w': ()
+
+Style/RedundantBegin:
+  Enabled: false
+
+Style/RedundantFetchBlock:
+  Enabled: false
+
+Style/RescueStandardError:
+  EnforcedStyle: implicit
+
+Style/SymbolArray:
+  Enabled: false
+
+Style/TrailingCommaInArrayLiteral:
+  EnforcedStyleForMultiline: comma
+
+Style/TrailingCommaInHashLiteral:
+  EnforcedStyleForMultiline: comma
diff --git a/bin/rubocop b/bin/rubocop
new file mode 100755
index 000000000..369a05bed
--- /dev/null
+++ b/bin/rubocop
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+#
+# This file was generated by Bundler.
+#
+# The application 'rubocop' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
+
+bundle_binstub = File.expand_path("bundle", __dir__)
+
+if File.file?(bundle_binstub)
+  if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
+    load(bundle_binstub)
+  else
+    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
+Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
+  end
+end
+
+require "rubygems"
+require "bundler/setup"
+
+load Gem.bin_path("rubocop", "rubocop")
diff --git a/lint-staged.config.js b/lint-staged.config.js
index 06fe66d11..6740def51 100644
--- a/lint-staged.config.js
+++ b/lint-staged.config.js
@@ -1,7 +1,6 @@
 const config = {
   '*': 'prettier --ignore-unknown --write',
-  'Capfile|Gemfile|*.{rb,ruby,ru,rake}':
-    'bundle exec rubocop --force-exclusion -a',
+  'Capfile|Gemfile|*.{rb,ruby,ru,rake}': 'bin/rubocop --force-exclusion -a',
   '*.{js,jsx,ts,tsx}': 'eslint --fix',
   '*.{css,scss}': 'stylelint --fix',
   '*.haml': 'bundle exec haml-lint -a',

From f0ca874b09e29feda0db3eb50aece86e95192575 Mon Sep 17 00:00:00 2001
From: Louis Brauer <louis@openbooking.ch>
Date: Thu, 13 Jun 2024 16:37:43 +0200
Subject: [PATCH 114/133] Include crossorigin in inert css (#30687)

---
 app/views/layouts/application.html.haml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index ec6caa33a..a73287959 100755
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -29,7 +29,7 @@
     = stylesheet_pack_tag 'common', media: 'all', crossorigin: 'anonymous'
     = theme_style_tags current_theme
     -# Needed for the wicg-inert polyfill. It needs to be on it's own <style> tag, with this `id`
-    = stylesheet_pack_tag 'inert', media: 'all', id: 'inert-style'
+    = stylesheet_pack_tag 'inert', media: 'all', crossorigin: 'anonymous', id: 'inert-style'
 
     = javascript_pack_tag 'common', crossorigin: 'anonymous'
     = preload_pack_asset "locale/#{I18n.locale}-json.js"

From 271a8e99e2666dfb90a8a57c8ef6563279bc406d Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 13 Jun 2024 10:37:49 -0400
Subject: [PATCH 115/133] Update merge conflict rebase action version (silences
 node version warning) (#30690)

---
 .github/workflows/rebase-needed.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/rebase-needed.yml b/.github/workflows/rebase-needed.yml
index 06d835c09..8784397a8 100644
--- a/.github/workflows/rebase-needed.yml
+++ b/.github/workflows/rebase-needed.yml
@@ -17,7 +17,7 @@ jobs:
 
     steps:
       - name: Check for merge conflicts
-        uses: eps1lon/actions-label-merge-conflict@releases/2.x
+        uses: eps1lon/actions-label-merge-conflict@v3
         with:
           dirtyLabel: 'rebase needed :construction:'
           repoToken: '${{ secrets.GITHUB_TOKEN }}'

From f8ce61d6705699fc509a6677e8df847af77ebac1 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 13 Jun 2024 16:37:54 +0200
Subject: [PATCH 116/133] chore(deps): update dependency nokogiri to v1.16.6
 (#30689)

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 172b7cbdd..75fcf2417 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -445,7 +445,7 @@ GEM
     net-smtp (0.5.0)
       net-protocol
     nio4r (2.7.3)
-    nokogiri (1.16.5)
+    nokogiri (1.16.6)
       mini_portile2 (~> 2.8.2)
       racc (~> 1.4)
     nsa (0.3.0)

From a66f6e10056333a3ca3466895ef5f8967b1f4a27 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 13 Jun 2024 11:07:02 -0400
Subject: [PATCH 117/133] Add binstub for `brakeman` (#30493)

---
 .github/workflows/lint-ruby.yml |  2 +-
 bin/brakeman                    | 27 +++++++++++++++++++++++++++
 2 files changed, 28 insertions(+), 1 deletion(-)
 create mode 100755 bin/brakeman

diff --git a/.github/workflows/lint-ruby.yml b/.github/workflows/lint-ruby.yml
index f4e81d508..b3a89c3ca 100644
--- a/.github/workflows/lint-ruby.yml
+++ b/.github/workflows/lint-ruby.yml
@@ -47,4 +47,4 @@ jobs:
 
       - name: Run brakeman
         if: always() # Run both checks, even if the first failed
-        run: bundle exec brakeman
+        run: bin/brakeman
diff --git a/bin/brakeman b/bin/brakeman
new file mode 100755
index 000000000..b4fe8de26
--- /dev/null
+++ b/bin/brakeman
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+#
+# This file was generated by Bundler.
+#
+# The application 'brakeman' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
+
+bundle_binstub = File.expand_path("bundle", __dir__)
+
+if File.file?(bundle_binstub)
+  if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
+    load(bundle_binstub)
+  else
+    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
+Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
+  end
+end
+
+require "rubygems"
+require "bundler/setup"
+
+load Gem.bin_path("brakeman", "brakeman")

From 179f7b11bac81479a518504873c980b04d6ca6e5 Mon Sep 17 00:00:00 2001
From: Michael Stanclift <mx@vmstan.com>
Date: Thu, 13 Jun 2024 10:58:34 -0500
Subject: [PATCH 118/133] Build libvips from source in Dockerfile (#30571)

---
 Dockerfile | 81 ++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 70 insertions(+), 11 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index e29377829..0d7516b10 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -48,8 +48,6 @@ ENV \
 # Apply Mastodon version information
   MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \
   MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \
-# Enable libvips
-  MASTODON_USE_LIBVIPS=true \
 # Apply Mastodon static files and YJIT options
   RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \
   RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \
@@ -67,7 +65,9 @@ ENV \
   DEBIAN_FRONTEND="noninteractive" \
   PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \
 # Optimize jemalloc 5.x performance
-  MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0"
+  MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0" \
+# Enable libvips, should not be changed
+  MASTODON_USE_LIBVIPS=true
 
 # Set default shell used for running commands
 SHELL ["/bin/bash", "-o", "pipefail", "-o", "errexit", "-c"]
@@ -104,7 +104,6 @@ RUN \
     curl \
     ffmpeg \
     file \
-    libvips42 \
     libjemalloc2 \
     patchelf \
     procps \
@@ -138,18 +137,31 @@ RUN \
 --mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \
 # Install build tools and bundler dependencies from APT
   apt-get install -y --no-install-recommends \
-    g++ \
-    gcc \
+    build-essential \
     git \
     libgdbm-dev \
+    libglib2.0-dev \
     libgmp-dev \
     libicu-dev \
     libidn-dev \
     libpq-dev \
     libssl-dev \
-    make \
+    meson \
+    pkg-config \
     shared-mime-info \
-    zlib1g-dev \
+	# libvips components
+    libcgif-dev \
+    libexif-dev \
+    libexpat1-dev \
+    libgirepository1.0-dev \
+    libheif-dev \
+    libimagequant-dev \
+    libjpeg62-turbo-dev \
+    liblcms2-dev \
+    liborc-dev \
+    libspng-dev \
+    libtiff-dev \
+    libwebp-dev \
   ;
 
 RUN \
@@ -158,6 +170,26 @@ RUN \
   corepack enable; \
   corepack prepare --activate;
 
+# Create temporary libvips specific build layer from build layer
+FROM build as libvips
+
+# libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"]
+# renovate: datasource=github-releases depName=libvips packageName=libvips/libvips
+ARG VIPS_VERSION=8.15.2
+# libvips download URL, change with [--build-arg VIPS_URL="https://github.com/libvips/libvips/releases/download"]
+ARG VIPS_URL=https://github.com/libvips/libvips/releases/download
+
+WORKDIR /usr/local/libvips/src
+
+RUN \
+  curl -sSL -o vips-${VIPS_VERSION}.tar.xz ${VIPS_URL}/v${VIPS_VERSION}/vips-${VIPS_VERSION}.tar.xz; \
+  tar xf vips-${VIPS_VERSION}.tar.xz; \
+  cd vips-${VIPS_VERSION}; \
+  meson setup build --prefix /usr/local/libvips --libdir=lib -Ddeprecated=false -Dintrospection=disabled -Dmodules=disabled -Dexamples=false; \
+  cd build; \
+  ninja; \
+  ninja install;
+
 # Create temporary bundler specific build layer from build layer
 FROM build as bundler
 
@@ -207,10 +239,14 @@ COPY . /opt/mastodon/
 COPY --from=yarn /opt/mastodon /opt/mastodon/
 COPY --from=bundler /opt/mastodon /opt/mastodon/
 COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
+# Copy libvips components to layer for precompiler
+COPY --from=libvips /usr/local/libvips/bin /usr/local/bin
+COPY --from=libvips /usr/local/libvips/lib /usr/local/lib
 
 ARG TARGETPLATFORM
 
 RUN \
+  ldconfig; \
 # Use Ruby on Rails to create Mastodon assets
   SECRET_KEY_BASE_DUMMY=1 \
   bundle exec rails assets:precompile; \
@@ -232,12 +268,27 @@ RUN \
 --mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
 # Apt update install non-dev versions of necessary components
   apt-get install -y --no-install-recommends \
-    libssl3 \
-    libpq5 \
+    libexpat1 \
+    libglib2.0-0 \
     libicu72 \
     libidn12 \
+    libpq5 \
     libreadline8 \
+    libssl3 \
     libyaml-0-2 \
+  # libvips components
+    libcgif0 \
+    libexif12 \
+    libheif1 \
+    libimagequant0 \
+    libjpeg62-turbo \
+    liblcms2-2 \
+    liborc-0.4-0 \
+    libspng0 \
+    libtiff6 \
+    libwebp7 \
+    libwebpdemux2 \
+    libwebpmux3 \
   ;
 
 # Copy Mastodon sources into final layer
@@ -248,9 +299,17 @@ COPY --from=precompiler /opt/mastodon/public/packs /opt/mastodon/public/packs
 COPY --from=precompiler /opt/mastodon/public/assets /opt/mastodon/public/assets
 # Copy bundler components to layer
 COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
+# Copy libvips components to layer
+COPY --from=libvips /usr/local/libvips/bin /usr/local/bin
+COPY --from=libvips /usr/local/libvips/lib /usr/local/lib
 
 RUN \
-# Precompile bootsnap code for faster Rails startup
+  ldconfig; \
+# Smoketest media processors
+  vips -v;
+
+RUN \
+  # Precompile bootsnap code for faster Rails startup
   bundle exec bootsnap precompile --gemfile app/ lib/;
 
 RUN \

From a243963e934123e17519711ce0378eaa7b5f8204 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= <git@mkljczk.pl>
Date: Thu, 13 Jun 2024 21:46:45 +0200
Subject: [PATCH 119/133] LanguageDropdown: remove unused function (#30346)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
---
 .../compose/components/language_dropdown.jsx         | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/app/javascript/mastodon/features/compose/components/language_dropdown.jsx b/app/javascript/mastodon/features/compose/components/language_dropdown.jsx
index c3bd908a4..47e81cf13 100644
--- a/app/javascript/mastodon/features/compose/components/language_dropdown.jsx
+++ b/app/javascript/mastodon/features/compose/components/language_dropdown.jsx
@@ -110,18 +110,6 @@ class LanguageDropdownMenu extends PureComponent {
     }).map(result => result.obj);
   }
 
-  frequentlyUsed () {
-    const { languages, value } = this.props;
-    const current = languages.find(lang => lang[0] === value);
-    const results = [];
-
-    if (current) {
-      results.push(current);
-    }
-
-    return results;
-  }
-
   handleClick = e => {
     const value = e.currentTarget.getAttribute('data-index');
 

From 3d9f00ae167a40835a571a97ea5afcc85ec6e396 Mon Sep 17 00:00:00 2001
From: Emelia Smith <ThisIsMissEm@users.noreply.github.com>
Date: Fri, 14 Jun 2024 09:54:09 +0200
Subject: [PATCH 120/133] Fix unsafe URLs in audit log resulting from domain
 blocks (#27139)

Co-authored-by: Claire <claire.github-309c@sitedethib.com>
---
 app/helpers/admin/action_logs_helper.rb | 6 +++---
 config/locales/en.yml                   | 1 +
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/app/helpers/admin/action_logs_helper.rb b/app/helpers/admin/action_logs_helper.rb
index 4018ef6b1..e8d563412 100644
--- a/app/helpers/admin/action_logs_helper.rb
+++ b/app/helpers/admin/action_logs_helper.rb
@@ -15,15 +15,15 @@ module Admin::ActionLogsHelper
       link_to log.human_identifier, admin_roles_path(log.target_id)
     when 'Report'
       link_to "##{log.human_identifier.presence || log.target_id}", admin_report_path(log.target_id)
-    when 'DomainBlock', 'DomainAllow', 'EmailDomainBlock', 'UnavailableDomain'
-      link_to log.human_identifier, "https://#{log.human_identifier.presence}"
+    when 'Instance', 'DomainBlock', 'DomainAllow', 'UnavailableDomain'
+      log.human_identifier.present? ? link_to(log.human_identifier, admin_instance_path(log.human_identifier)) : I18n.t('admin.action_logs.unavailable_instance')
     when 'Status'
       link_to log.human_identifier, log.permalink
     when 'AccountWarning'
       link_to log.human_identifier, disputes_strike_path(log.target_id)
     when 'Announcement'
       link_to truncate(log.human_identifier), edit_admin_announcement_path(log.target_id)
-    when 'IpBlock', 'Instance', 'CustomEmoji'
+    when 'IpBlock', 'EmailDomainBlock', 'CustomEmoji'
       log.human_identifier
     when 'CanonicalEmailBlock'
       content_tag(:samp, (log.human_identifier.presence || '')[0...7], title: log.human_identifier)
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 43aa8481c..20df80c27 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -293,6 +293,7 @@ en:
       filter_by_action: Filter by action
       filter_by_user: Filter by user
       title: Audit log
+      unavailable_instance: "(domain name unavailable)"
     announcements:
       destroyed_msg: Announcement successfully deleted!
       edit:

From e1f8cd5b0fe2509e507d4f03de05e0ef6ddd28cc Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 14 Jun 2024 10:14:00 +0200
Subject: [PATCH 121/133] chore(deps): update dependency aws-sdk-s3 to v1.152.3
 (#30701)

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 75fcf2417..de7e88d4d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -109,7 +109,7 @@ GEM
     aws-sdk-kms (1.83.0)
       aws-sdk-core (~> 3, >= 3.197.0)
       aws-sigv4 (~> 1.1)
-    aws-sdk-s3 (1.152.2)
+    aws-sdk-s3 (1.152.3)
       aws-sdk-core (~> 3, >= 3.197.0)
       aws-sdk-kms (~> 1)
       aws-sigv4 (~> 1.8)

From a7264a2b42631bd876d114b96f689492e2908a8d Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 14 Jun 2024 11:08:17 +0200
Subject: [PATCH 122/133] New Crowdin Translations (automated) (#30704)

Co-authored-by: GitHub Actions <noreply@github.com>
---
 app/javascript/mastodon/locales/ca.json    | 1 +
 app/javascript/mastodon/locales/da.json    | 1 +
 app/javascript/mastodon/locales/de.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/fi.json    | 1 +
 app/javascript/mastodon/locales/fo.json    | 1 +
 app/javascript/mastodon/locales/gl.json    | 1 +
 app/javascript/mastodon/locales/is.json    | 1 +
 app/javascript/mastodon/locales/it.json    | 1 +
 app/javascript/mastodon/locales/nl.json    | 1 +
 app/javascript/mastodon/locales/nn.json    | 2 ++
 app/javascript/mastodon/locales/pl.json    | 1 +
 app/javascript/mastodon/locales/pt-PT.json | 1 +
 app/javascript/mastodon/locales/sl.json    | 1 +
 app/javascript/mastodon/locales/sv.json    | 1 +
 app/javascript/mastodon/locales/zh-CN.json | 1 +
 app/javascript/mastodon/locales/zh-TW.json | 1 +
 config/locales/doorkeeper.fr-CA.yml        | 2 ++
 config/locales/doorkeeper.fr.yml           | 2 ++
 config/locales/doorkeeper.nn.yml           | 2 ++
 22 files changed, 26 insertions(+)

diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index 68429b093..e178c4736 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Aquest perfil l'han amagat els moderadors de {domain}.",
   "link_preview.author": "Per {name}",
   "link_preview.more_from_author": "Més de {name}",
+  "link_preview.shares": "{count, plural, one {{counter} publicació} other {{counter} publicacions}}",
   "lists.account.add": "Afegeix a la llista",
   "lists.account.remove": "Elimina de la llista",
   "lists.delete": "Elimina la llista",
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index ae2968087..1b6bd3642 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Denne profil er blevet skjult af {domain}-moderatorerne.",
   "link_preview.author": "Af {name}",
   "link_preview.more_from_author": "Mere fra {name}",
+  "link_preview.shares": "{count, plural, one {{counter} indlæg} other {{counter} indlæg}}",
   "lists.account.add": "Føj til liste",
   "lists.account.remove": "Fjern fra liste",
   "lists.delete": "Slet liste",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index ef08e9b6d..5a6e15b09 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Dieses Profil wurde von den Moderator*innen von {domain} ausgeblendet.",
   "link_preview.author": "Von {name}",
   "link_preview.more_from_author": "Mehr von {name}",
+  "link_preview.shares": "{count, plural, one {{counter} Beitrag} other {{counter} Beiträge}}",
   "lists.account.add": "Zur Liste hinzufügen",
   "lists.account.remove": "Von der Liste entfernen",
   "lists.delete": "Liste löschen",
diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json
index 4c30bfa25..1cbd7b18e 100644
--- a/app/javascript/mastodon/locales/es-AR.json
+++ b/app/javascript/mastodon/locales/es-AR.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Este perfil fue ocultado por los moderadores de {domain}.",
   "link_preview.author": "Por {name}",
   "link_preview.more_from_author": "Más de {name}",
+  "link_preview.shares": "{count, plural, one {{counter} mensaje} other {{counter} mensajes}}",
   "lists.account.add": "Agregar a lista",
   "lists.account.remove": "Quitar de lista",
   "lists.delete": "Eliminar lista",
diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json
index 564d7ec57..dd7dd2bea 100644
--- a/app/javascript/mastodon/locales/es-MX.json
+++ b/app/javascript/mastodon/locales/es-MX.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Este perfil ha sido ocultado por los moderadores de {domain}.",
   "link_preview.author": "Por {name}",
   "link_preview.more_from_author": "Más de {name}",
+  "link_preview.shares": "{count, plural, one {{counter} publicación} other {{counter} publicaciones}}",
   "lists.account.add": "Añadir a lista",
   "lists.account.remove": "Quitar de lista",
   "lists.delete": "Borrar lista",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 14d3bf0dd..e1ba41347 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Este perfil ha sido ocultado por los moderadores de {domain}.",
   "link_preview.author": "Por {name}",
   "link_preview.more_from_author": "Más de {name}",
+  "link_preview.shares": "{count, plural, one {{counter} publicación} other {{counter} publicaciones}}",
   "lists.account.add": "Añadir a lista",
   "lists.account.remove": "Quitar de lista",
   "lists.delete": "Borrar lista",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index 6c2162e52..68ea3925b 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Palvelimen {domain} valvojat ovat piilottaneet tämän käyttäjätilin.",
   "link_preview.author": "Julkaissut {name}",
   "link_preview.more_from_author": "Lisää käyttäjältä {name}",
+  "link_preview.shares": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}",
   "lists.account.add": "Lisää listalle",
   "lists.account.remove": "Poista listalta",
   "lists.delete": "Poista lista",
diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json
index b77f609a2..f4ae229d4 100644
--- a/app/javascript/mastodon/locales/fo.json
+++ b/app/javascript/mastodon/locales/fo.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Hesin vangin er fjaldur av kjakleiðarunum á {domain}.",
   "link_preview.author": "Av {name}",
   "link_preview.more_from_author": "Meira frá {name}",
+  "link_preview.shares": "{count, plural, one {{counter} postur} other {{counter} postar}}",
   "lists.account.add": "Legg afturat lista",
   "lists.account.remove": "Tak av lista",
   "lists.delete": "Strika lista",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index 0847b8bf0..925e4dd6f 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Este perfil foi agochado pola moderación de {domain}.",
   "link_preview.author": "Por {name}",
   "link_preview.more_from_author": "Máis de {name}",
+  "link_preview.shares": "{count, plural, one {{counter} publicación} other {{counter} publicacións}}",
   "lists.account.add": "Engadir á listaxe",
   "lists.account.remove": "Eliminar da listaxe",
   "lists.delete": "Eliminar listaxe",
diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json
index 6ce72b43f..2a33c160a 100644
--- a/app/javascript/mastodon/locales/is.json
+++ b/app/javascript/mastodon/locales/is.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Þetta notandasnið hefur verið falið af umsjónarmönnum {domain}.",
   "link_preview.author": "Eftir {name}",
   "link_preview.more_from_author": "Meira frá {name}",
+  "link_preview.shares": "{count, plural, one {{counter} færsla} other {{counter} færslur}}",
   "lists.account.add": "Bæta á lista",
   "lists.account.remove": "Fjarlægja af lista",
   "lists.delete": "Eyða lista",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index f66497e0a..4c183e32c 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Questo profilo è stato nascosto dai moderatori di {domain}.",
   "link_preview.author": "Di {name}",
   "link_preview.more_from_author": "Altro da {name}",
+  "link_preview.shares": "{count, plural,one {{counter} post}other {{counter} post}}",
   "lists.account.add": "Aggiungi all'elenco",
   "lists.account.remove": "Rimuovi dall'elenco",
   "lists.delete": "Elimina elenco",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index bf081ad58..01610b565 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Dit profiel is door de moderatoren van {domain} verborgen.",
   "link_preview.author": "Door {name}",
   "link_preview.more_from_author": "Meer van {name}",
+  "link_preview.shares": "{count, plural, one {{counter} bericht} other {{counter} berichten}}",
   "lists.account.add": "Aan lijst toevoegen",
   "lists.account.remove": "Uit lijst verwijderen",
   "lists.delete": "Lijst verwijderen",
diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json
index 3711cc0ae..2f214bc99 100644
--- a/app/javascript/mastodon/locales/nn.json
+++ b/app/javascript/mastodon/locales/nn.json
@@ -414,6 +414,8 @@
   "limited_account_hint.action": "Vis profilen likevel",
   "limited_account_hint.title": "Denne profilen er skjult av moderatorane på {domain}.",
   "link_preview.author": "Av {name}",
+  "link_preview.more_from_author": "Meir frå {name}",
+  "link_preview.shares": "{count, plural,one {{counter} innlegg} other {{counter} innlegg}}",
   "lists.account.add": "Legg til i liste",
   "lists.account.remove": "Fjern frå liste",
   "lists.delete": "Slett liste",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index 6f67e8f74..26dbbf51a 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Ten profil został ukryty przez moderatorów {domain}.",
   "link_preview.author": "{name}",
   "link_preview.more_from_author": "Więcej od {name}",
+  "link_preview.shares": "{count, plural, one {{counter} wpis} few {{counter} wpisy} many {{counter} wpisów} other {{counter} wpisów}}",
   "lists.account.add": "Dodaj do listy",
   "lists.account.remove": "Usunąć z listy",
   "lists.delete": "Usuń listę",
diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json
index c389d4f4f..a06e4c6d1 100644
--- a/app/javascript/mastodon/locales/pt-PT.json
+++ b/app/javascript/mastodon/locales/pt-PT.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Este perfil foi ocultado pelos moderadores de {domain}.",
   "link_preview.author": "Por {name}",
   "link_preview.more_from_author": "Mais de {name}",
+  "link_preview.shares": "{count, plural, one {{counter} publicação} other {{counter} publicações}}",
   "lists.account.add": "Adicionar à lista",
   "lists.account.remove": "Remover da lista",
   "lists.delete": "Eliminar lista",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index a8cce3202..46e6b384a 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Profil so moderatorji strežnika {domain} skrili.",
   "link_preview.author": "Avtor_ica {name}",
   "link_preview.more_from_author": "Več od {name}",
+  "link_preview.shares": "{count, plural, one {{counter} objava} two {{counter} objavi} few {{counter} objave} other {{counter} objav}}",
   "lists.account.add": "Dodaj na seznam",
   "lists.account.remove": "Odstrani s seznama",
   "lists.delete": "Izbriši seznam",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index a1d478b7a..4e80c5103 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "Denna profil har dolts av {domain}s moderatorer.",
   "link_preview.author": "Av {name}",
   "link_preview.more_from_author": "Mer från {name}",
+  "link_preview.shares": "{count, plural, one {{counter} inlägg} other {{counter} inlägg}}",
   "lists.account.add": "Lägg till i lista",
   "lists.account.remove": "Ta bort från lista",
   "lists.delete": "Radera lista",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index 0f8bcae6f..def950044 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "此账号资料已被 {domain} 管理员隐藏。",
   "link_preview.author": "由 {name}",
   "link_preview.more_from_author": "查看 {name} 的更多内容",
+  "link_preview.shares": "{count, plural, other {{counter} 条嘟文}}",
   "lists.account.add": "添加到列表",
   "lists.account.remove": "从列表中移除",
   "lists.delete": "删除列表",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 1d20034db..70b8d51df 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -415,6 +415,7 @@
   "limited_account_hint.title": "此個人檔案已被 {domain} 的管理員隱藏。",
   "link_preview.author": "來自 {name}",
   "link_preview.more_from_author": "來自 {name} 之更多內容",
+  "link_preview.shares": "{count, plural, other {{count} 則嘟文}}",
   "lists.account.add": "新增至列表",
   "lists.account.remove": "自列表中移除",
   "lists.delete": "刪除列表",
diff --git a/config/locales/doorkeeper.fr-CA.yml b/config/locales/doorkeeper.fr-CA.yml
index 9d671569f..00df58be7 100644
--- a/config/locales/doorkeeper.fr-CA.yml
+++ b/config/locales/doorkeeper.fr-CA.yml
@@ -135,6 +135,7 @@ fr-CA:
         media: Fichiers médias
         mutes: Masqués
         notifications: Notifications
+        profile: Votre profil Mastodon
         push: Notifications push
         reports: Signalements
         search: Recherche
@@ -165,6 +166,7 @@ fr-CA:
       admin:write:reports: effectuer des actions de modération sur les signalements
       crypto: utiliser le chiffrement de bout-en-bout
       follow: modifier les relations du compte
+      profile: lire uniquement les informations de votre compte
       push: recevoir vos notifications poussées
       read: lire toutes les données de votre compte
       read:accounts: voir les informations des comptes
diff --git a/config/locales/doorkeeper.fr.yml b/config/locales/doorkeeper.fr.yml
index 2c37b651a..a5d70030a 100644
--- a/config/locales/doorkeeper.fr.yml
+++ b/config/locales/doorkeeper.fr.yml
@@ -135,6 +135,7 @@ fr:
         media: Fichiers médias
         mutes: Masqués
         notifications: Notifications
+        profile: Votre profil Mastodon
         push: Notifications push
         reports: Signalements
         search: Recherche
@@ -165,6 +166,7 @@ fr:
       admin:write:reports: effectuer des actions de modération sur les signalements
       crypto: utiliser le chiffrement de bout-en-bout
       follow: modifier les relations du compte
+      profile: lire uniquement les informations de votre compte
       push: recevoir vos notifications poussées
       read: lire toutes les données de votre compte
       read:accounts: voir les informations des comptes
diff --git a/config/locales/doorkeeper.nn.yml b/config/locales/doorkeeper.nn.yml
index 0e5d1ca45..0779a0a13 100644
--- a/config/locales/doorkeeper.nn.yml
+++ b/config/locales/doorkeeper.nn.yml
@@ -135,6 +135,7 @@ nn:
         media: Mediavedlegg
         mutes: Dempingar
         notifications: Varsel
+        profile: Mastodon-profilen din
         push: Pushvarsel
         reports: Rapportar
         search: Søk
@@ -165,6 +166,7 @@ nn:
       admin:write:reports: utføre moderatorhandlingar på rapportar
       crypto: bruk ende-til-ende-kryptering
       follow: fylg, blokkér, avblokkér, avfylg brukarar
+      profile: les berre den grunnlejggande informasjonen til brukarkontoen din
       push: motta pushvarsla dine
       read: lese alle dine kontodata
       read:accounts: sjå informasjon om kontoar

From 8d5ed19c6deded7b82a0023d078681e8203951a5 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Fri, 14 Jun 2024 05:49:10 -0400
Subject: [PATCH 123/133] Migrate `form_tag` to `form_with` in admin and auth
 views (#30692)

---
 app/views/admin/accounts/index.html.haml      | 29 +++++++------
 app/views/admin/action_logs/index.html.haml   | 10 +++--
 app/views/admin/custom_emojis/index.html.haml |  9 ++--
 .../follow_recommendations/show.html.haml     |  8 ++--
 app/views/admin/instances/index.html.haml     |  9 ++--
 app/views/admin/reports/_actions.html.haml    | 22 +++++++---
 .../admin/reports/actions/preview.html.haml   | 13 ++++--
 app/views/admin/reports/index.html.haml       |  9 ++--
 app/views/admin/trends/links/index.html.haml  | 10 ++---
 .../admin/trends/statuses/index.html.haml     |  8 ++--
 .../auth/confirmations/captcha.html.haml      | 12 ++++--
 app/views/mail_subscriptions/show.html.haml   | 11 +++--
 app/views/oauth/authorizations/new.html.haml  | 41 ++++++++++++-------
 13 files changed, 122 insertions(+), 69 deletions(-)

diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml
index 01b072938..f32a4ac81 100644
--- a/app/views/admin/accounts/index.html.haml
+++ b/app/views/admin/accounts/index.html.haml
@@ -1,38 +1,41 @@
 - content_for :page_title do
   = t('admin.accounts.title')
 
-= form_tag admin_accounts_url, method: 'GET', class: 'simple_form' do
+= form_with url: admin_accounts_url, method: :get, class: :simple_form do |form|
   .filters
     .filter-subset.filter-subset--with-select
       %strong= t('admin.accounts.location.title')
       .input.select.optional
-        = select_tag :origin,
-                     options_for_select([[t('admin.accounts.location.local'), 'local'], [t('admin.accounts.location.remote'), 'remote']], params[:origin]),
-                     prompt: I18n.t('generic.all')
+        = form.select :origin,
+                      options_for_select([[t('admin.accounts.location.local'), 'local'], [t('admin.accounts.location.remote'), 'remote']], params[:origin]),
+                      prompt: I18n.t('generic.all')
     .filter-subset.filter-subset--with-select
       %strong= t('admin.accounts.moderation.title')
       .input.select.optional
-        = select_tag :status,
-                     options_for_select(admin_accounts_moderation_options, params[:status]),
-                     prompt: I18n.t('generic.all')
+        = form.select :status,
+                      options_for_select(admin_accounts_moderation_options, params[:status]),
+                      prompt: I18n.t('generic.all')
     .filter-subset.filter-subset--with-select
       %strong= t('admin.accounts.role')
       .input.select.optional
-        = select_tag :role_ids,
-                     options_from_collection_for_select(UserRole.assignable, :id, :name, params[:role_ids]),
-                     prompt: I18n.t('admin.accounts.moderation.all')
+        = form.select :role_ids,
+                      options_from_collection_for_select(UserRole.assignable, :id, :name, params[:role_ids]),
+                      prompt: I18n.t('admin.accounts.moderation.all')
     .filter-subset.filter-subset--with-select
       %strong= t 'generic.order_by'
       .input.select
-        = select_tag :order,
-                     options_for_select([[t('relationships.most_recent'), 'recent'], [t('relationships.last_active'), 'active']], params[:order])
+        = form.select :order,
+                      options_for_select([[t('relationships.most_recent'), 'recent'], [t('relationships.last_active'), 'active']], params[:order])
 
   .fields-group
     - %i(username by_domain display_name email ip).each do |key|
       - next if key == :by_domain && params[:origin] != 'remote'
 
       .input.string.optional
-        = text_field_tag key, params[key], class: 'string optional', placeholder: I18n.t("admin.accounts.#{key}")
+        = form.text_field key,
+                          value: params[key],
+                          class: 'string optional',
+                          placeholder: I18n.t("admin.accounts.#{key}")
 
   .actions
     %button.button= t('admin.accounts.search')
diff --git a/app/views/admin/action_logs/index.html.haml b/app/views/admin/action_logs/index.html.haml
index c4929cc42..c02c8f0ad 100644
--- a/app/views/admin/action_logs/index.html.haml
+++ b/app/views/admin/action_logs/index.html.haml
@@ -1,19 +1,23 @@
 - content_for :page_title do
   = t('admin.action_logs.title')
 
-= form_tag admin_action_logs_url, method: 'GET', class: 'simple_form' do
+= form_with url: admin_action_logs_url, method: :get, class: :simple_form do |form|
   = hidden_field_tag :target_account_id, params[:target_account_id] if params[:target_account_id].present?
 
   .filters
     .filter-subset.filter-subset--with-select
       %strong= t('admin.action_logs.filter_by_user')
       .input.select.optional
-        = select_tag :account_id, options_from_collection_for_select(@auditable_accounts, :id, :username, params[:account_id]), prompt: I18n.t('admin.accounts.moderation.all')
+        = form.select :account_id,
+                      options_from_collection_for_select(@auditable_accounts, :id, :username, params[:account_id]),
+                      prompt: I18n.t('admin.accounts.moderation.all')
 
     .filter-subset.filter-subset--with-select
       %strong= t('admin.action_logs.filter_by_action')
       .input.select.optional
-        = select_tag :action_type, options_for_select(Admin::ActionLogFilter::ACTION_TYPE_MAP.keys.map { |key| [I18n.t("admin.action_logs.action_types.#{key}"), key] }, params[:action_type]), prompt: I18n.t('admin.accounts.moderation.all')
+        = form.select :action_type,
+                      options_for_select(Admin::ActionLogFilter::ACTION_TYPE_MAP.keys.map { |key| [I18n.t("admin.action_logs.action_types.#{key}"), key] }, params[:action_type]),
+                      prompt: I18n.t('admin.accounts.moderation.all')
 
 - if @action_logs.empty?
   .muted-hint.center-text
diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml
index e87dd4128..d76726a73 100644
--- a/app/views/admin/custom_emojis/index.html.haml
+++ b/app/views/admin/custom_emojis/index.html.haml
@@ -21,14 +21,17 @@
         - else
           = filter_link_to t('admin.accounts.location.remote'), remote: '1', local: nil
 
-= form_tag admin_custom_emojis_url, method: 'GET', class: 'simple_form' do
+= form_with url: admin_custom_emojis_url, method: :get, class: :simple_form do |form|
   .fields-group
     - CustomEmojiFilter::KEYS.each do |key|
-      = hidden_field_tag key, params[key] if params[key].present?
+      = form.hidden_field key, value: params[key] if params[key].present?
 
     - %i(shortcode by_domain).each do |key|
       .input.string.optional
-        = text_field_tag key, params[key], class: 'string optional', placeholder: I18n.t("admin.custom_emojis.#{key}")
+        = form.text_field key,
+                          value: params[key],
+                          class: 'string optional',
+                          placeholder: I18n.t("admin.custom_emojis.#{key}")
 
     .actions
       %button.button= t('admin.accounts.search')
diff --git a/app/views/admin/follow_recommendations/show.html.haml b/app/views/admin/follow_recommendations/show.html.haml
index c8ad653a8..7d787152b 100644
--- a/app/views/admin/follow_recommendations/show.html.haml
+++ b/app/views/admin/follow_recommendations/show.html.haml
@@ -5,16 +5,16 @@
 
 %hr.spacer/
 
-= form_tag admin_follow_recommendations_path, method: 'GET', class: 'simple_form' do
+= form_with url: admin_follow_recommendations_path, method: :get, class: :simple_form do |form|
   - RelationshipFilter::KEYS.each do |key|
-    = hidden_field_tag key, params[key] if params[key].present?
+    = form.hidden_field key, value: params[key] if params[key].present?
 
   .filters
     .filter-subset.filter-subset--with-select
       %strong= t('admin.follow_recommendations.language')
       .input.select.optional
-        = select_tag :language,
-                     options_for_select(Trends.available_locales.map { |key| [standard_locale_name(key), key] }, @language)
+        = form.select :language,
+                      options_for_select(Trends.available_locales.map { |key| [standard_locale_name(key), key] }, @language)
     .filter-subset
       %strong= t('admin.follow_recommendations.status')
       %ul
diff --git a/app/views/admin/instances/index.html.haml b/app/views/admin/instances/index.html.haml
index 7e43b4c53..b5f084f88 100644
--- a/app/views/admin/instances/index.html.haml
+++ b/app/views/admin/instances/index.html.haml
@@ -28,14 +28,17 @@
       %li= filter_link_to t('admin.instances.delivery.unavailable'), availability: 'unavailable'
 
 - unless limited_federation_mode?
-  = form_tag admin_instances_url, method: 'GET', class: 'simple_form' do
+  = form_with url: admin_instances_url, method: :get, class: :simple_form do |form|
     .fields-group
       - InstanceFilter::KEYS.each do |key|
-        = hidden_field_tag key, params[key] if params[key].present?
+        = form.hidden_field key, value: params[key] if params[key].present?
 
       - %i(by_domain).each do |key|
         .input.string.optional
-          = text_field_tag key, params[key], class: 'string optional', placeholder: I18n.t("admin.instances.#{key}")
+          = form.text_field key,
+                            value: params[key],
+                            class: 'string optional',
+                            placeholder: I18n.t("admin.instances.#{key}")
 
       .actions
         %button.button= t('admin.accounts.search')
diff --git a/app/views/admin/reports/_actions.html.haml b/app/views/admin/reports/_actions.html.haml
index da9ac8931..5fb540931 100644
--- a/app/views/admin/reports/_actions.html.haml
+++ b/app/views/admin/reports/_actions.html.haml
@@ -1,4 +1,4 @@
-= form_tag preview_admin_report_actions_path(report), method: :post do
+= form_with url: preview_admin_report_actions_path(report) do |form|
   .report-actions
     .report-actions__item
       .report-actions__item__button
@@ -8,26 +8,36 @@
     - if statuses.any? { |status| (status.with_media? || status.with_preview_card?) && !status.discarded? }
       .report-actions__item
         .report-actions__item__button
-          = button_tag t('admin.reports.mark_as_sensitive'), name: :mark_as_sensitive, class: 'button'
+          = form.button t('admin.reports.mark_as_sensitive'),
+                        name: :mark_as_sensitive,
+                        class: 'button'
         .report-actions__item__description
           = t('admin.reports.actions.mark_as_sensitive_description_html')
     .report-actions__item
       .report-actions__item__button
-        = button_tag t('admin.reports.delete_and_resolve'), name: :delete, class: 'button button--destructive'
+        = form.button t('admin.reports.delete_and_resolve'),
+                      name: :delete,
+                      class: 'button button--destructive'
       .report-actions__item__description
         = t('admin.reports.actions.delete_description_html')
     .report-actions__item
       .report-actions__item__button
-        = button_tag t('admin.accounts.silence'), name: :silence, class: 'button button--destructive'
+        = form.button t('admin.accounts.silence'),
+                      name: :silence,
+                      class: 'button button--destructive'
       .report-actions__item__description
         = t('admin.reports.actions.silence_description_html')
     .report-actions__item
       .report-actions__item__button
-        = button_tag t('admin.accounts.suspend'), name: :suspend, class: 'button button--destructive'
+        = form.button t('admin.accounts.suspend'),
+                      name: :suspend,
+                      class: 'button button--destructive'
       .report-actions__item__description
         = t('admin.reports.actions.suspend_description_html')
     .report-actions__item
       .report-actions__item__button
-        = link_to t('admin.accounts.custom'), new_admin_account_action_path(report.target_account_id, report_id: report.id), class: 'button'
+        = link_to t('admin.accounts.custom'),
+                  new_admin_account_action_path(report.target_account_id, report_id: report.id),
+                  class: 'button'
       .report-actions__item__description
         = t('admin.reports.actions.other_description_html')
diff --git a/app/views/admin/reports/actions/preview.html.haml b/app/views/admin/reports/actions/preview.html.haml
index 7a737d4f7..79c444453 100644
--- a/app/views/admin/reports/actions/preview.html.haml
+++ b/app/views/admin/reports/actions/preview.html.haml
@@ -4,8 +4,8 @@
 - content_for :page_title do
   = t('admin.reports.confirm_action', acct: target_acct)
 
-= form_tag admin_report_actions_path(@report), class: 'simple_form', method: :post do
-  = hidden_field_tag :moderation_action, @moderation_action
+= form_with url: admin_report_actions_path(@report), class: :simple_form do |form|
+  = form.hidden_field :moderation_action, value: @moderation_action
 
   %p.hint= t("admin.reports.summary.action_preambles.#{@moderation_action}_html", acct: target_acct)
   %ul.hint
@@ -30,7 +30,9 @@
         %p= t "user_mailer.warning.explanation.#{warning_action}", instance: Rails.configuration.x.local_domain
 
       .fields-group
-        = text_area_tag :text, nil, placeholder: t('admin.reports.summary.warning_placeholder')
+        = form.text_area :text,
+                         value: nil,
+                         placeholder: t('admin.reports.summary.warning_placeholder')
 
       - unless @report.other?
         %p
@@ -75,4 +77,7 @@
 
   .actions
     = link_to t('admin.reports.cancel'), admin_report_path(@report), class: 'button button-tertiary'
-    = button_tag t('admin.reports.confirm'), name: :confirm, class: 'button', type: :submit
+    = form.button t('admin.reports.confirm'),
+                  name: :confirm,
+                  class: 'button',
+                  type: :submit
diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml
index e2a9868aa..dae2c1aa5 100644
--- a/app/views/admin/reports/index.html.haml
+++ b/app/views/admin/reports/index.html.haml
@@ -14,14 +14,17 @@
       %li= filter_link_to t('admin.accounts.location.local'), target_origin: 'local'
       %li= filter_link_to t('admin.accounts.location.remote'), target_origin: 'remote'
 
-= form_tag admin_reports_url, method: 'GET', class: 'simple_form' do
+= form_with url: admin_reports_url, method: :get, class: :simple_form do |form|
   .fields-group
     - ReportFilter::KEYS.each do |key|
-      = hidden_field_tag key, params[key] if params[key].present?
+      = form.hidden_field key, value: params[key] if params[key].present?
 
     - %i(by_target_domain).each do |key|
       .input.string.optional
-        = text_field_tag key, params[key], class: 'string optional', placeholder: I18n.t("admin.reports.#{key}")
+        = form.text_field key,
+                          value: params[key],
+                          class: 'string optional',
+                          placeholder: I18n.t("admin.reports.#{key}")
 
     .actions
       %button.button= t('admin.accounts.search')
diff --git a/app/views/admin/trends/links/index.html.haml b/app/views/admin/trends/links/index.html.haml
index c503b2d39..20d8ecf85 100644
--- a/app/views/admin/trends/links/index.html.haml
+++ b/app/views/admin/trends/links/index.html.haml
@@ -5,17 +5,17 @@
 
 %hr.spacer/
 
-= form_tag admin_trends_links_path, method: 'GET', class: 'simple_form' do
+= form_with url: admin_trends_links_path, method: :get, class: :simple_form do |form|
   - Trends::PreviewCardFilter::KEYS.each do |key|
-    = hidden_field_tag key, params[key] if params[key].present?
+    = form.hidden_field key, value: params[key] if params[key].present?
 
   .filters
     .filter-subset.filter-subset--with-select
       %strong= t('admin.follow_recommendations.language')
       .input.select.optional
-        = select_tag :locale,
-                     options_for_select(@locales.map { |key| [standard_locale_name(key), key] }, params[:locale]),
-                     include_blank: true
+        = form.select :locale,
+                      options_for_select(@locales.map { |key| [standard_locale_name(key), key] }, params[:locale]),
+                      include_blank: true
     .filter-subset
       %strong= t('admin.trends.trending')
       %ul
diff --git a/app/views/admin/trends/statuses/index.html.haml b/app/views/admin/trends/statuses/index.html.haml
index 66151ad31..8fbc0ba5b 100644
--- a/app/views/admin/trends/statuses/index.html.haml
+++ b/app/views/admin/trends/statuses/index.html.haml
@@ -5,15 +5,17 @@
 
 %hr.spacer/
 
-= form_tag admin_trends_statuses_path, method: 'GET', class: 'simple_form' do
+= form_with url: admin_trends_statuses_path, method: :get, class: :simple_form do |form|
   - Trends::StatusFilter::KEYS.each do |key|
-    = hidden_field_tag key, params[key] if params[key].present?
+    = form.hidden_field key, value: params[key] if params[key].present?
 
   .filters
     .filter-subset.filter-subset--with-select
       %strong= t('admin.follow_recommendations.language')
       .input.select.optional
-        = select_tag :locale, options_for_select(@locales.map { |key| [standard_locale_name(key), key] }, params[:locale]), include_blank: true
+        = form.select :locale,
+                      options_for_select(@locales.map { |key| [standard_locale_name(key), key] }, params[:locale]),
+                      include_blank: true
     .filter-subset
       %strong= t('admin.trends.trending')
       %ul
diff --git a/app/views/auth/confirmations/captcha.html.haml b/app/views/auth/confirmations/captcha.html.haml
index 964d0e63e..035ac3a86 100644
--- a/app/views/auth/confirmations/captcha.html.haml
+++ b/app/views/auth/confirmations/captcha.html.haml
@@ -1,11 +1,13 @@
 - content_for :page_title do
   = t('auth.captcha_confirmation.title')
 
-= form_tag auth_captcha_confirmation_url, method: 'POST', class: 'simple_form' do
+= form_with url: auth_captcha_confirmation_url, class: :simple_form do |form|
   = render 'auth/shared/progress', stage: 'confirm'
 
-  = hidden_field_tag :confirmation_token, params[:confirmation_token]
-  = hidden_field_tag :redirect_to_app, params[:redirect_to_app]
+  = form.hidden_field :confirmation_token,
+                      value: params[:confirmation_token]
+  = form.hidden_field :redirect_to_app,
+                      value: params[:redirect_to_app]
 
   %h1.title= t('auth.captcha_confirmation.title')
   %p.lead= t('auth.captcha_confirmation.hint_html')
@@ -15,4 +17,6 @@
   %p.lead= t('auth.captcha_confirmation.help_html', email: mail_to(Setting.site_contact_email, nil))
 
   .actions
-    = button_tag t('challenge.confirm'), class: 'button', type: :submit
+    = form.button t('challenge.confirm'),
+                  class: 'button',
+                  type: :submit
diff --git a/app/views/mail_subscriptions/show.html.haml b/app/views/mail_subscriptions/show.html.haml
index 776d561d7..a09dacc4d 100644
--- a/app/views/mail_subscriptions/show.html.haml
+++ b/app/views/mail_subscriptions/show.html.haml
@@ -10,7 +10,10 @@
         email: content_tag(:strong, @user.email),
         settings_path: settings_preferences_notifications_path
 
-  = form_tag unsubscribe_path, method: :post do
-    = hidden_field_tag :token, params[:token]
-    = hidden_field_tag :type, params[:type]
-    = button_tag t('mail_subscriptions.unsubscribe.action'), type: :submit
+  = form_with url: unsubscribe_path do |form|
+    = form.hidden_field :token,
+                        value: params[:token]
+    = form.hidden_field :type,
+                        value: params[:type]
+    = form.button t('mail_subscriptions.unsubscribe.action'),
+                  type: :submit
diff --git a/app/views/oauth/authorizations/new.html.haml b/app/views/oauth/authorizations/new.html.haml
index 50f671b26..c50c224cc 100644
--- a/app/views/oauth/authorizations/new.html.haml
+++ b/app/views/oauth/authorizations/new.html.haml
@@ -21,18 +21,31 @@
               = t(scope.access, scope: [:doorkeeper, :grouped_scopes, :access])
 
     .actions
-      = form_tag oauth_authorization_path, method: :post do
-        = hidden_field_tag :client_id, @pre_auth.client.uid
-        = hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
-        = hidden_field_tag :state, @pre_auth.state
-        = hidden_field_tag :response_type, @pre_auth.response_type
-        = hidden_field_tag :scope, @pre_auth.scope
-        = button_tag t('doorkeeper.authorizations.buttons.authorize'), type: :submit
+      = form_with url: oauth_authorization_path do |form|
+        = form.hidden_field :client_id,
+                            value: @pre_auth.client.uid
+        = form.hidden_field :redirect_uri,
+                            value: @pre_auth.redirect_uri
+        = form.hidden_field :state,
+                            value: @pre_auth.state
+        = form.hidden_field :response_type,
+                            value: @pre_auth.response_type
+        = form.hidden_field :scope,
+                            value: @pre_auth.scope
+        = form.button t('doorkeeper.authorizations.buttons.authorize'),
+                      type: :submit
 
-      = form_tag oauth_authorization_path, method: :delete do
-        = hidden_field_tag :client_id, @pre_auth.client.uid
-        = hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
-        = hidden_field_tag :state, @pre_auth.state
-        = hidden_field_tag :response_type, @pre_auth.response_type
-        = hidden_field_tag :scope, @pre_auth.scope
-        = button_tag t('doorkeeper.authorizations.buttons.deny'), type: :submit, class: 'negative'
+      = form_with url: oauth_authorization_path, method: :delete do |form|
+        = form.hidden_field :client_id,
+                            value: @pre_auth.client.uid
+        = form.hidden_field :redirect_uri,
+                            value: @pre_auth.redirect_uri
+        = form.hidden_field :state,
+                            value: @pre_auth.state
+        = form.hidden_field :response_type,
+                            value: @pre_auth.response_type
+        = form.hidden_field :scope,
+                            value: @pre_auth.scope
+        = form.button t('doorkeeper.authorizations.buttons.deny'),
+                      type: :submit,
+                      class: 'negative'

From ab8474fd7fb321d235f3a3ebe277baed6da9cd62 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Fri, 14 Jun 2024 05:49:49 -0400
Subject: [PATCH 124/133] Fix (relax) remaining `RSpec/*` cops (#30693)

---
 .rubocop/rspec.yml | 10 ++++++++++
 .rubocop_todo.yml  | 15 ---------------
 2 files changed, 10 insertions(+), 15 deletions(-)

diff --git a/.rubocop/rspec.yml b/.rubocop/rspec.yml
index a6f8a7aee..d2d2f8325 100644
--- a/.rubocop/rspec.yml
+++ b/.rubocop/rspec.yml
@@ -1,10 +1,20 @@
 ---
 RSpec/ExampleLength:
   CountAsOne: ['array', 'heredoc', 'method_call']
+  Max: 20 # Override default of 5
+
+RSpec/MultipleExpectations:
+  Max: 10 # Overrides default of 1
+
+RSpec/MultipleMemoizedHelpers:
+  Max: 20 # Overrides default of 5
 
 RSpec/NamedSubject:
   EnforcedStyle: named_only
 
+RSpec/NestedGroups:
+  Max: 10 # Overrides default of 3
+
 RSpec/NotToNot:
   EnforcedStyle: to_not
 
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index a70caad8c..8a4e59803 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -27,21 +27,6 @@ Metrics/CyclomaticComplexity:
 Metrics/PerceivedComplexity:
   Max: 27
 
-# Configuration parameters: CountAsOne.
-RSpec/ExampleLength:
-  Max: 18
-
-RSpec/MultipleExpectations:
-  Max: 7
-
-# Configuration parameters: AllowSubject.
-RSpec/MultipleMemoizedHelpers:
-  Max: 17
-
-# Configuration parameters: AllowedGroups.
-RSpec/NestedGroups:
-  Max: 6
-
 Rails/OutputSafety:
   Exclude:
     - 'config/initializers/simple_form.rb'

From 222ab80557cfe3dac1b520bfc3a49f3b1bb88c55 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Fri, 14 Jun 2024 05:50:33 -0400
Subject: [PATCH 125/133] Fix `Style/GlobalStdStream` cop in environments/*
 files (#30694)

---
 config/environments/development.rb | 2 +-
 config/environments/production.rb  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/config/environments/development.rb b/config/environments/development.rb
index cc601bde3..e4da60ac8 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -37,7 +37,7 @@ Rails.application.configure do
 
   config.action_controller.forgery_protection_origin_check = ENV['DISABLE_FORGERY_REQUEST_PROTECTION'].nil?
 
-  ActiveSupport::Logger.new(STDOUT).tap do |logger|
+  ActiveSupport::Logger.new($stdout).tap do |logger|
     logger.formatter = config.log_formatter
     config.logger = ActiveSupport::TaggedLogging.new(logger)
   end
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 6686a23d6..b42f78b14 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -94,7 +94,7 @@ Rails.application.configure do
   # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name")
 
   # Log to STDOUT by default
-  config.logger = ActiveSupport::Logger.new(STDOUT)
+  config.logger = ActiveSupport::Logger.new($stdout)
                                        .tap  { |logger| logger.formatter = ::Logger::Formatter.new }
                                        .then { |logger| ActiveSupport::TaggedLogging.new(logger) }
 

From 980034e2e14a8a27156009b88ef7d7d58aef5d14 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Fri, 14 Jun 2024 05:50:50 -0400
Subject: [PATCH 126/133] Fix `Style/NilLambda` cop in paperclip initializer
 (#30695)

---
 config/initializers/paperclip.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb
index 070d250bf..6b14985ae 100644
--- a/config/initializers/paperclip.rb
+++ b/config/initializers/paperclip.rb
@@ -72,7 +72,7 @@ if ENV['S3_ENABLED'] == 'true'
     }
   )
 
-  Paperclip::Attachment.default_options[:s3_permissions] = ->(*) { nil } if ENV['S3_PERMISSION'] == ''
+  Paperclip::Attachment.default_options[:s3_permissions] = ->(*) {} if ENV['S3_PERMISSION'] == ''
 
   if ENV.has_key?('S3_ENDPOINT')
     Paperclip::Attachment.default_options[:s3_options].merge!(

From b5d1d4826656db3103711a200a04cb1f2fa7da83 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Fri, 14 Jun 2024 05:54:22 -0400
Subject: [PATCH 127/133] Convert `form_for` -> `form_with` in views (#30700)

---
 app/views/admin/accounts/index.html.haml                        | 2 +-
 app/views/admin/custom_emojis/index.html.haml                   | 2 +-
 app/views/admin/email_domain_blocks/index.html.haml             | 2 +-
 app/views/admin/export_domain_blocks/import.html.haml           | 2 +-
 app/views/admin/follow_recommendations/show.html.haml           | 2 +-
 app/views/admin/ip_blocks/index.html.haml                       | 2 +-
 app/views/admin/relationships/index.html.haml                   | 2 +-
 app/views/admin/reports/show.html.haml                          | 2 +-
 app/views/admin/statuses/index.html.haml                        | 2 +-
 app/views/admin/trends/links/index.html.haml                    | 2 +-
 .../admin/trends/links/preview_card_providers/index.html.haml   | 2 +-
 app/views/admin/trends/statuses/index.html.haml                 | 2 +-
 app/views/admin/trends/tags/index.html.haml                     | 2 +-
 app/views/filters/statuses/index.html.haml                      | 2 +-
 app/views/relationships/show.html.haml                          | 2 +-
 15 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml
index f32a4ac81..9dd4f0e4e 100644
--- a/app/views/admin/accounts/index.html.haml
+++ b/app/views/admin/accounts/index.html.haml
@@ -43,7 +43,7 @@
 
 %hr.spacer/
 
-= form_for(@form, url: batch_admin_accounts_path) do |f|
+= form_with model: @form, url: batch_admin_accounts_path do |f|
   = hidden_field_tag :page, params[:page] || 1
   = hidden_field_tag :select_all_matching, '0'
 
diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml
index d76726a73..82fec554b 100644
--- a/app/views/admin/custom_emojis/index.html.haml
+++ b/app/views/admin/custom_emojis/index.html.haml
@@ -37,7 +37,7 @@
       %button.button= t('admin.accounts.search')
       = link_to t('admin.accounts.reset'), admin_custom_emojis_path, class: 'button negative'
 
-= form_for(@form, url: batch_admin_custom_emojis_path) do |f|
+= form_with model: @form, url: batch_admin_custom_emojis_path do |f|
   = hidden_field_tag :page, params[:page] || 1
 
   - CustomEmojiFilter::KEYS.each do |key|
diff --git a/app/views/admin/email_domain_blocks/index.html.haml b/app/views/admin/email_domain_blocks/index.html.haml
index 684735c20..4fae6557a 100644
--- a/app/views/admin/email_domain_blocks/index.html.haml
+++ b/app/views/admin/email_domain_blocks/index.html.haml
@@ -4,7 +4,7 @@
 - content_for :heading_actions do
   = link_to t('admin.email_domain_blocks.add_new'), new_admin_email_domain_block_path, class: 'button'
 
-= form_for(@form, url: batch_admin_email_domain_blocks_path) do |f|
+= form_with model: @form, url: batch_admin_email_domain_blocks_path do |f|
   = hidden_field_tag :page, params[:page] || 1
 
   .batch-table
diff --git a/app/views/admin/export_domain_blocks/import.html.haml b/app/views/admin/export_domain_blocks/import.html.haml
index 52ffc3d46..2b0d2c5eb 100644
--- a/app/views/admin/export_domain_blocks/import.html.haml
+++ b/app/views/admin/export_domain_blocks/import.html.haml
@@ -6,7 +6,7 @@
 - if defined?(@global_private_comment) && @global_private_comment.present?
   %p= t('admin.export_domain_blocks.import.private_comment_description_html', comment: @global_private_comment)
 
-= form_for(@form, url: batch_admin_domain_blocks_path) do |f|
+= form_with model: @form, url: batch_admin_domain_blocks_path do |f|
   .batch-table
     .batch-table__toolbar
       %label.batch-table__toolbar__select.batch-checkbox-all
diff --git a/app/views/admin/follow_recommendations/show.html.haml b/app/views/admin/follow_recommendations/show.html.haml
index 7d787152b..62cd31572 100644
--- a/app/views/admin/follow_recommendations/show.html.haml
+++ b/app/views/admin/follow_recommendations/show.html.haml
@@ -21,7 +21,7 @@
         %li= filter_link_to t('admin.accounts.moderation.active'), status: nil
         %li= filter_link_to t('admin.follow_recommendations.suppressed'), status: 'suppressed'
 
-= form_for(@form, url: admin_follow_recommendations_path, method: :patch) do |f|
+= form_with model: @form, url: admin_follow_recommendations_path, method: :patch do |f|
   - RelationshipFilter::KEYS.each do |key|
     = hidden_field_tag key, params[key] if params[key].present?
 
diff --git a/app/views/admin/ip_blocks/index.html.haml b/app/views/admin/ip_blocks/index.html.haml
index 9eba6c68f..207d23aee 100644
--- a/app/views/admin/ip_blocks/index.html.haml
+++ b/app/views/admin/ip_blocks/index.html.haml
@@ -5,7 +5,7 @@
   - content_for :heading_actions do
     = link_to t('admin.ip_blocks.add_new'), new_admin_ip_block_path, class: 'button'
 
-= form_for(@form, url: batch_admin_ip_blocks_path) do |f|
+= form_with model: @form, url: batch_admin_ip_blocks_path do |f|
   = hidden_field_tag :page, params[:page] || 1
 
   .batch-table
diff --git a/app/views/admin/relationships/index.html.haml b/app/views/admin/relationships/index.html.haml
index c2daefb42..83ffd139d 100644
--- a/app/views/admin/relationships/index.html.haml
+++ b/app/views/admin/relationships/index.html.haml
@@ -24,7 +24,7 @@
 
 %hr.spacer/
 
-= form_for(@form, url: batch_admin_accounts_path) do |f|
+= form_with model: @form, url: batch_admin_accounts_path do |f|
   .batch-table
     .batch-table__toolbar
       %label.batch-table__toolbar__select.batch-checkbox-all
diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml
index 842aa5159..ca1edea0f 100644
--- a/app/views/admin/reports/show.html.haml
+++ b/app/views/admin/reports/show.html.haml
@@ -45,7 +45,7 @@
             admin_account_statuses_path(@report.target_account_id, report_id: @report.id),
             class: 'table-action-link'
 
-= form_for(@form, url: batch_admin_account_statuses_path(@report.target_account_id, report_id: @report.id)) do |f|
+= form_with model: @form, url: batch_admin_account_statuses_path(@report.target_account_id, report_id: @report.id) do |f|
   .batch-table
     .batch-table__toolbar
       %label.batch-table__toolbar__select.batch-checkbox-all
diff --git a/app/views/admin/statuses/index.html.haml b/app/views/admin/statuses/index.html.haml
index a41a6332d..770d972d9 100644
--- a/app/views/admin/statuses/index.html.haml
+++ b/app/views/admin/statuses/index.html.haml
@@ -21,7 +21,7 @@
 
 %hr.spacer/
 
-= form_for(@status_batch_action, url: batch_admin_account_statuses_path(@account.id)) do |f|
+= form_with model: @status_batch_action, url: batch_admin_account_statuses_path(@account.id) do |f|
   = hidden_field_tag :page, params[:page] || 1
 
   - Admin::StatusFilter::KEYS.each do |key|
diff --git a/app/views/admin/trends/links/index.html.haml b/app/views/admin/trends/links/index.html.haml
index 20d8ecf85..647c24b1e 100644
--- a/app/views/admin/trends/links/index.html.haml
+++ b/app/views/admin/trends/links/index.html.haml
@@ -26,7 +26,7 @@
         = t('admin.trends.preview_card_providers.title')
         = material_symbol 'chevron_right'
 
-= form_for(@form, url: batch_admin_trends_links_path) do |f|
+= form_with model: @form, url: batch_admin_trends_links_path do |f|
   = hidden_field_tag :page, params[:page] || 1
 
   - Trends::PreviewCardFilter::KEYS.each do |key|
diff --git a/app/views/admin/trends/links/preview_card_providers/index.html.haml b/app/views/admin/trends/links/preview_card_providers/index.html.haml
index 706c60701..b43b8dfff 100644
--- a/app/views/admin/trends/links/preview_card_providers/index.html.haml
+++ b/app/views/admin/trends/links/preview_card_providers/index.html.haml
@@ -20,7 +20,7 @@
 
 %hr.spacer/
 
-= form_for(@form, url: batch_admin_trends_links_preview_card_providers_path) do |f|
+= form_with model: @form, url: batch_admin_trends_links_preview_card_providers_path do |f|
   = hidden_field_tag :page, params[:page] || 1
 
   - Trends::PreviewCardProviderFilter::KEYS.each do |key|
diff --git a/app/views/admin/trends/statuses/index.html.haml b/app/views/admin/trends/statuses/index.html.haml
index 8fbc0ba5b..4713f8c2a 100644
--- a/app/views/admin/trends/statuses/index.html.haml
+++ b/app/views/admin/trends/statuses/index.html.haml
@@ -22,7 +22,7 @@
         %li= filter_link_to t('generic.all'), trending: nil
         %li= filter_link_to t('admin.trends.only_allowed'), trending: 'allowed'
 
-= form_for(@form, url: batch_admin_trends_statuses_path) do |f|
+= form_with model: @form, url: batch_admin_trends_statuses_path do |f|
   = hidden_field_tag :page, params[:page] || 1
 
   - Trends::StatusFilter::KEYS.each do |key|
diff --git a/app/views/admin/trends/tags/index.html.haml b/app/views/admin/trends/tags/index.html.haml
index 655955f7f..3a44cf3a7 100644
--- a/app/views/admin/trends/tags/index.html.haml
+++ b/app/views/admin/trends/tags/index.html.haml
@@ -14,7 +14,7 @@
       %li= filter_link_to t('admin.trends.rejected'), status: 'rejected'
       %li= filter_link_to safe_join([t('admin.accounts.moderation.pending'), "(#{Tag.pending_review.count})"], ' '), status: 'pending_review'
 
-= form_for(@form, url: batch_admin_trends_tags_path) do |f|
+= form_with model: @form, url: batch_admin_trends_tags_path do |f|
   = hidden_field_tag :page, params[:page] || 1
 
   - Trends::TagFilter::KEYS.each do |key|
diff --git a/app/views/filters/statuses/index.html.haml b/app/views/filters/statuses/index.html.haml
index eaa39e170..915ec59ca 100644
--- a/app/views/filters/statuses/index.html.haml
+++ b/app/views/filters/statuses/index.html.haml
@@ -13,7 +13,7 @@
 
 %hr.spacer/
 
-= form_for(@status_filter_batch_action, url: batch_filter_statuses_path(@filter.id)) do |f|
+= form_with model: @status_filter_batch_action, url: batch_filter_statuses_path(@filter.id) do |f|
   = hidden_field_tag :page, params[:page] || 1
 
   - Admin::StatusFilter::KEYS.each do |key|
diff --git a/app/views/relationships/show.html.haml b/app/views/relationships/show.html.haml
index 97ba49eb5..6a866b13f 100644
--- a/app/views/relationships/show.html.haml
+++ b/app/views/relationships/show.html.haml
@@ -31,7 +31,7 @@
       %li= filter_link_to t('relationships.most_recent'), order: nil
       %li= filter_link_to t('relationships.last_active'), order: 'active'
 
-= form_for(@form, url: relationships_path, method: :patch) do |f|
+= form_with model: @form, url: relationships_path, method: :patch do |f|
   = hidden_field_tag :page, params[:page] || 1
 
   - RelationshipFilter::KEYS.each do |key|

From 8eb27c60e117f9fbfe33a0b9bde6dd4c1015ef4f Mon Sep 17 00:00:00 2001
From: Claire <claire.github-309c@sitedethib.com>
Date: Fri, 14 Jun 2024 12:33:06 +0200
Subject: [PATCH 128/133] Add `most_recent_notification_id` to
 `NotificationGroup` (#30707)

---
 app/models/notification_group.rb                      | 10 +++++++---
 app/serializers/rest/notification_group_serializer.rb |  2 +-
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/app/models/notification_group.rb b/app/models/notification_group.rb
index 07967f9dc..43612d49b 100644
--- a/app/models/notification_group.rb
+++ b/app/models/notification_group.rb
@@ -1,14 +1,17 @@
 # frozen_string_literal: true
 
 class NotificationGroup < ActiveModelSerializers::Model
-  attributes :group_key, :sample_accounts, :notifications_count, :notification
+  attributes :group_key, :sample_accounts, :notifications_count, :notification, :most_recent_notification_id
 
   def self.from_notification(notification)
     if notification.group_key.present?
       # TODO: caching and preloading
-      sample_accounts = notification.account.notifications.where(group_key: notification.group_key).order(id: :desc).limit(3).map(&:from_account)
+      most_recent_notifications = notification.account.notifications.where(group_key: notification.group_key).order(id: :desc).take(3)
+      most_recent_id = most_recent_notifications.first.id
+      sample_accounts = most_recent_notifications.map(&:from_account)
       notifications_count = notification.account.notifications.where(group_key: notification.group_key).count
     else
+      most_recent_id = notification.id
       sample_accounts = [notification.from_account]
       notifications_count = 1
     end
@@ -17,7 +20,8 @@ class NotificationGroup < ActiveModelSerializers::Model
       notification: notification,
       group_key: notification.group_key || "ungrouped-#{notification.id}",
       sample_accounts: sample_accounts,
-      notifications_count: notifications_count
+      notifications_count: notifications_count,
+      most_recent_notification_id: most_recent_id
     )
   end
 
diff --git a/app/serializers/rest/notification_group_serializer.rb b/app/serializers/rest/notification_group_serializer.rb
index ce1950c5a..9aa5663f4 100644
--- a/app/serializers/rest/notification_group_serializer.rb
+++ b/app/serializers/rest/notification_group_serializer.rb
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class REST::NotificationGroupSerializer < ActiveModel::Serializer
-  attributes :group_key, :notifications_count, :type
+  attributes :group_key, :notifications_count, :type, :most_recent_notification_id
 
   attribute :page_min_id, if: :paginated?
   attribute :page_max_id, if: :paginated?

From b9fd7571aedbfda524ab48ba15c03bd3936b8b2c Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Fri, 14 Jun 2024 15:04:20 +0200
Subject: [PATCH 129/133] Change sidebar text in web UI (#30696)

---
 .../mastodon/components/server_banner.jsx         | 10 ++++------
 .../features/ui/components/sign_in_banner.jsx     |  6 ++++--
 app/javascript/mastodon/locales/en.json           |  6 +++---
 app/javascript/styles/mastodon/components.scss    | 15 ++++++++++++---
 4 files changed, 23 insertions(+), 14 deletions(-)

diff --git a/app/javascript/mastodon/components/server_banner.jsx b/app/javascript/mastodon/components/server_banner.jsx
index 63eec5349..baa220af5 100644
--- a/app/javascript/mastodon/components/server_banner.jsx
+++ b/app/javascript/mastodon/components/server_banner.jsx
@@ -42,10 +42,12 @@ class ServerBanner extends PureComponent {
     return (
       <div className='server-banner'>
         <div className='server-banner__introduction'>
-          <FormattedMessage id='server_banner.introduction' defaultMessage='{domain} is part of the decentralized social network powered by {mastodon}.' values={{ domain: <strong>{domain}</strong>, mastodon: <a href='https://joinmastodon.org' target='_blank'>Mastodon</a> }} />
+          <FormattedMessage id='server_banner.is_one_of_many' defaultMessage='{domain} is one of the many independent Mastodon servers you can use to participate in the fediverse.' values={{ domain: <strong>{domain}</strong>, mastodon: <a href='https://joinmastodon.org' target='_blank'>Mastodon</a> }} />
         </div>
 
-        <ServerHeroImage blurhash={server.getIn(['thumbnail', 'blurhash'])} src={server.getIn(['thumbnail', 'url'])} className='server-banner__hero' />
+        <Link to='/about'>
+          <ServerHeroImage blurhash={server.getIn(['thumbnail', 'blurhash'])} src={server.getIn(['thumbnail', 'url'])} className='server-banner__hero' />
+        </Link>
 
         <div className='server-banner__description'>
           {isLoading ? (
@@ -84,10 +86,6 @@ class ServerBanner extends PureComponent {
             )}
           </div>
         </div>
-
-        <hr className='spacer' />
-
-        <Link className='button button--block button-secondary' to='/about'><FormattedMessage id='server_banner.learn_more' defaultMessage='Learn more' /></Link>
       </div>
     );
   }
diff --git a/app/javascript/mastodon/features/ui/components/sign_in_banner.jsx b/app/javascript/mastodon/features/ui/components/sign_in_banner.jsx
index 4216f3da3..74a8fdb84 100644
--- a/app/javascript/mastodon/features/ui/components/sign_in_banner.jsx
+++ b/app/javascript/mastodon/features/ui/components/sign_in_banner.jsx
@@ -22,7 +22,8 @@ const SignInBanner = () => {
   if (sso_redirect) {
     return (
       <div className='sign-in-banner'>
-        <p><FormattedMessage id='sign_in_banner.text' defaultMessage='Login to follow profiles or hashtags, favorite, share and reply to posts. You can also interact from your account on a different server.' /></p>
+        <p><strong><FormattedMessage id='sign_in_banner.mastodon_is' defaultMessage="Mastodon is the best way to keep up with what's happening." /></strong></p>
+        <p><FormattedMessage id='sign_in_banner.follow_anyone' defaultMessage='Follow anyone across the fediverse and see it all in chronological order. No algorithms, ads, or clickbait in sight.' /></p>
         <a href={sso_redirect} data-method='post' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sso_redirect' defaultMessage='Login or Register' /></a>
       </div>
     );
@@ -44,7 +45,8 @@ const SignInBanner = () => {
 
   return (
     <div className='sign-in-banner'>
-      <p><FormattedMessage id='sign_in_banner.text' defaultMessage='Login to follow profiles or hashtags, favorite, share and reply to posts. You can also interact from your account on a different server.' /></p>
+      <p><strong><FormattedMessage id='sign_in_banner.mastodon_is' defaultMessage="Mastodon is the best way to keep up with what's happening." /></strong></p>
+      <p><FormattedMessage id='sign_in_banner.follow_anyone' defaultMessage='Follow anyone across the fediverse and see it all in chronological order. No algorithms, ads, or clickbait in sight.' /></p>
       {signupButton}
       <a href='/auth/sign_in' className='button button--block button-tertiary'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Login' /></a>
     </div>
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 4f5caeb6a..f0c27ad70 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -696,13 +696,13 @@
   "server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)",
   "server_banner.active_users": "active users",
   "server_banner.administered_by": "Administered by:",
-  "server_banner.introduction": "{domain} is part of the decentralized social network powered by {mastodon}.",
-  "server_banner.learn_more": "Learn more",
+  "server_banner.is_one_of_many": "{domain} is one of the many independent Mastodon servers you can use to participate in the fediverse.",
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
+  "sign_in_banner.follow_anyone": "Follow anyone across the fediverse and see it all in chronological order. No algorithms, ads, or clickbait in sight.",
+  "sign_in_banner.mastodon_is": "Mastodon is the best way to keep up with what's happening.",
   "sign_in_banner.sign_in": "Login",
   "sign_in_banner.sso_redirect": "Login or Register",
-  "sign_in_banner.text": "Login to follow profiles or hashtags, favorite, share and reply to posts. You can also interact from your account on a different server.",
   "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",
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index d5c3d2605..73d0e6220 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -903,9 +903,15 @@ body > [data-popper-placement] {
   padding: 10px;
 
   p {
+    font-size: 15px;
+    line-height: 22px;
     color: $darker-text-color;
     margin-bottom: 20px;
 
+    strong {
+      font-weight: 700;
+    }
+
     a {
       color: $secondary-text-color;
       text-decoration: none;
@@ -8934,14 +8940,14 @@ noscript {
 }
 
 .server-banner {
-  padding: 20px 0;
-
   &__introduction {
+    font-size: 15px;
+    line-height: 22px;
     color: $darker-text-color;
     margin-bottom: 20px;
 
     strong {
-      font-weight: 600;
+      font-weight: 700;
     }
 
     a {
@@ -8969,6 +8975,9 @@ noscript {
   }
 
   &__description {
+    font-size: 15px;
+    line-height: 22px;
+    color: $darker-text-color;
     margin-bottom: 20px;
   }
 

From 09dca8ba3d47b07be8230185ff5068bdcd492ad1 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 14 Jun 2024 15:24:26 +0200
Subject: [PATCH 130/133] chore(deps): update dependency fog-openstack to
 v1.1.3 (#30671)

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 de7e88d4d..21c43a828 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -272,7 +272,7 @@ GEM
     fog-json (1.2.0)
       fog-core
       multi_json (~> 1.10)
-    fog-openstack (1.1.1)
+    fog-openstack (1.1.3)
       fog-core (~> 2.1)
       fog-json (>= 1.0)
     formatador (1.1.0)
@@ -422,7 +422,7 @@ GEM
     memory_profiler (1.0.1)
     mime-types (3.5.2)
       mime-types-data (~> 3.2015)
-    mime-types-data (3.2024.0507)
+    mime-types-data (3.2024.0604)
     mini_mime (1.1.5)
     mini_portile2 (2.8.7)
     minitest (5.23.1)

From 4a5442edaa5d598eea969ac023dc394761f38d7b Mon Sep 17 00:00:00 2001
From: Michael Stanclift <mx@vmstan.com>
Date: Fri, 14 Jun 2024 10:36:57 -0500
Subject: [PATCH 131/133] Add ffmpeg and ImageMagick versions to admin
 dashboard (#30710)

---
 .../dimension/software_versions_dimension.rb  | 30 ++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/app/lib/admin/metrics/dimension/software_versions_dimension.rb b/app/lib/admin/metrics/dimension/software_versions_dimension.rb
index 9dd0d393f..8a9183db3 100644
--- a/app/lib/admin/metrics/dimension/software_versions_dimension.rb
+++ b/app/lib/admin/metrics/dimension/software_versions_dimension.rb
@@ -10,7 +10,7 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim
   protected
 
   def perform_query
-    [mastodon_version, ruby_version, postgresql_version, redis_version, elasticsearch_version, libvips_version].compact
+    [mastodon_version, ruby_version, postgresql_version, redis_version, elasticsearch_version, libvips_version, imagemagick_version, ffmpeg_version].compact
   end
 
   def mastodon_version
@@ -82,6 +82,34 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim
     }
   end
 
+  def imagemagick_version
+    return if Rails.configuration.x.use_vips
+
+    version = `convert -version`.match(/Version: ImageMagick ([\d\.]+)/)[1]
+
+    {
+      key: 'imagemagick',
+      human_key: 'ImageMagick',
+      value: version,
+      human_value: version,
+    }
+  rescue Errno::ENOENT
+    nil
+  end
+
+  def ffmpeg_version
+    version = `ffmpeg -version`.match(/ffmpeg version ([\d\.]+)/)[1]
+
+    {
+      key: 'ffmpeg',
+      human_key: 'FFmpeg',
+      value: version,
+      human_value: version,
+    }
+  rescue Errno::ENOENT
+    nil
+  end
+
   def redis_info
     @redis_info ||= if redis.is_a?(Redis::Namespace)
                       redis.redis.info

From 2b10b0e027cbab1ab66c3ad2c11b189ba4e7c041 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Fri, 14 Jun 2024 11:49:08 -0400
Subject: [PATCH 132/133] Reduce docker service container health check wait
 times (#30703)

---
 .github/workflows/test-migrations.yml | 12 +++---
 .github/workflows/test-ruby.yml       | 60 +++++++++++++--------------
 2 files changed, 36 insertions(+), 36 deletions(-)

diff --git a/.github/workflows/test-migrations.yml b/.github/workflows/test-migrations.yml
index f057efc11..3eaf2c2d7 100644
--- a/.github/workflows/test-migrations.yml
+++ b/.github/workflows/test-migrations.yml
@@ -41,9 +41,9 @@ jobs:
           POSTGRES_USER: postgres
         options: >-
           --health-cmd pg_isready
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
+          --health-interval 10ms
+          --health-timeout 3s
+          --health-retries 50
         ports:
           - 5432:5432
 
@@ -51,9 +51,9 @@ jobs:
         image: redis:7-alpine
         options: >-
           --health-cmd "redis-cli ping"
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
+          --health-interval 10ms
+          --health-timeout 3s
+          --health-retries 50
         ports:
           - 6379:6379
 
diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml
index 8f05dcab3..513de2072 100644
--- a/.github/workflows/test-ruby.yml
+++ b/.github/workflows/test-ruby.yml
@@ -73,9 +73,9 @@ jobs:
           POSTGRES_USER: postgres
         options: >-
           --health-cmd pg_isready
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
+          --health-interval 10ms
+          --health-timeout 3s
+          --health-retries 50
         ports:
           - 5432:5432
 
@@ -83,9 +83,9 @@ jobs:
         image: redis:7-alpine
         options: >-
           --health-cmd "redis-cli ping"
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
+          --health-interval 10ms
+          --health-timeout 3s
+          --health-retries 50
         ports:
           - 6379:6379
 
@@ -159,9 +159,9 @@ jobs:
           POSTGRES_USER: postgres
         options: >-
           --health-cmd pg_isready
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
+          --health-interval 10ms
+          --health-timeout 3s
+          --health-retries 50
         ports:
           - 5432:5432
 
@@ -169,9 +169,9 @@ jobs:
         image: redis:7-alpine
         options: >-
           --health-cmd "redis-cli ping"
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
+          --health-interval 10ms
+          --health-timeout 3s
+          --health-retries 50
         ports:
           - 6379:6379
 
@@ -246,9 +246,9 @@ jobs:
           POSTGRES_USER: postgres
         options: >-
           --health-cmd pg_isready
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
+          --health-interval 10ms
+          --health-timeout 3s
+          --health-retries 50
         ports:
           - 5432:5432
 
@@ -256,9 +256,9 @@ jobs:
         image: redis:7-alpine
         options: >-
           --health-cmd "redis-cli ping"
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
+          --health-interval 10ms
+          --health-timeout 3s
+          --health-retries 50
         ports:
           - 6379:6379
 
@@ -331,9 +331,9 @@ jobs:
           POSTGRES_USER: postgres
         options: >-
           --health-cmd pg_isready
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
+          --health-interval 10ms
+          --health-timeout 3s
+          --health-retries 50
         ports:
           - 5432:5432
 
@@ -341,9 +341,9 @@ jobs:
         image: redis:7-alpine
         options: >-
           --health-cmd "redis-cli ping"
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
+          --health-interval 10ms
+          --health-timeout 3s
+          --health-retries 50
         ports:
           - 6379:6379
 
@@ -354,9 +354,9 @@ jobs:
           xpack.security.enabled: false
         options: >-
           --health-cmd "curl http://localhost:9200/_cluster/health"
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 10
+          --health-interval 2s
+          --health-timeout 3s
+          --health-retries 50
         ports:
           - 9200:9200
 
@@ -368,9 +368,9 @@ jobs:
           DISABLE_SECURITY_PLUGIN: true
         options: >-
           --health-cmd "curl http://localhost:9200/_cluster/health"
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 10
+          --health-interval 2s
+          --health-timeout 3s
+          --health-retries 50
         ports:
           - 9200:9200
 

From a777f7e3cc3cc936c7876c1d006e1954158d1e27 Mon Sep 17 00:00:00 2001
From: Michael Stanclift <mx@vmstan.com>
Date: Fri, 14 Jun 2024 11:10:16 -0500
Subject: [PATCH 133/133] Restore short Ruby version on admin dashboard
 (#30711)

---
 .../admin/metrics/dimension/software_versions_dimension.rb    | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/app/lib/admin/metrics/dimension/software_versions_dimension.rb b/app/lib/admin/metrics/dimension/software_versions_dimension.rb
index 8a9183db3..a260a66e2 100644
--- a/app/lib/admin/metrics/dimension/software_versions_dimension.rb
+++ b/app/lib/admin/metrics/dimension/software_versions_dimension.rb
@@ -28,8 +28,8 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim
     {
       key: 'ruby',
       human_key: 'Ruby',
-      value: "#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}",
-      human_value: RUBY_DESCRIPTION,
+      value: RUBY_DESCRIPTION,
+      human_value: "#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}",
     }
   end