diff --git a/CHANGELOG.md b/CHANGELOG.md index c7b0f64146..a9819a6c79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,54 @@ All notable changes to this project will be documented in this file. +## [4.3.8] - 2025-05-06 + +### Security + +- Update dependencies +- Check scheme on account, profile, and media URLs ([GHSA-x2rc-v5wx-g3m5](https://github.com/mastodon/mastodon/security/advisories/GHSA-x2rc-v5wx-g3m5)) + +### Added + +- Add warning for REDIS_NAMESPACE deprecation at startup (#34581 by @ClearlyClaire) +- Add built-in context for interaction policies (#34574 by @ClearlyClaire) + +### Changed + +- Change activity distribution error handling to skip retrying for deleted accounts (#33617 by @ClearlyClaire) + +### Removed + +- Remove double-query for signed query strings (#34610 by @ClearlyClaire) + +### Fixed + +- Fix incorrect redirect in response to unauthenticated API requests in limited federation mode (#34549 by @ClearlyClaire) +- Fix sign-up e-mail confirmation page reloading on error or redirect (#34548 by @ClearlyClaire) + +## [4.3.7] - 2025-04-02 + +### Added + +- Add delay to profile updates to debounce them (#34137 by @ClearlyClaire) +- Add support for paginating partial collections in `SynchronizeFollowersService` (#34272 and #34277 by @ClearlyClaire) + +### Changed + +- Change account suspensions to be federated to recently-followed accounts as well (#34294 by @ClearlyClaire) +- Change `AccountReachFinder` to consider statuses based on suspension date (#32805 and #34291 by @ClearlyClaire and @mjankowski) +- Change user archive signed URL TTL from 10 seconds to 1 hour (#34254 by @ClearlyClaire) + +### Fixed + +- Fix static version of animated PNG emojis not being properly extracted (#34337 by @ClearlyClaire) +- Fix filters not applying in detailed view, favourites and bookmarks (#34259 and #34260 by @ClearlyClaire) +- Fix handling of malformed/unusual HTML (#34201 by @ClearlyClaire) +- Fix `CacheBuster` being queued for missing media attachments (#34253 by @ClearlyClaire) +- Fix incorrect URL being used when cache busting (#34189 by @ClearlyClaire) +- Fix streaming server refusing unix socket path in `DATABASE_URL` (#34091 by @ClearlyClaire) +- Fix “x” hotkey not working on boosted filtered posts (#33758 by @ClearlyClaire) + ## [4.3.6] - 2025-03-13 ### Security diff --git a/Gemfile.lock b/Gemfile.lock index 80049a7dc2..0fb210eaa4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -435,7 +435,7 @@ GEM mutex_m (0.3.0) net-http (0.6.0) uri - net-imap (0.5.6) + net-imap (0.5.7) date net-protocol net-ldap (0.19.0) @@ -446,7 +446,7 @@ GEM net-smtp (0.5.1) net-protocol nio4r (2.7.4) - nokogiri (1.18.7) + nokogiri (1.18.8) mini_portile2 (~> 2.8.2) racc (~> 1.4) oj (3.16.10) diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index 06a113511c..b73dae17e5 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -72,6 +72,13 @@ class Api::BaseController < ApplicationController end end + # Redefine `require_functional!` to properly output JSON instead of HTML redirects + def require_functional! + return if current_user.functional? + + require_user! + end + def render_empty render json: {}, status: 200 end diff --git a/app/controllers/api/v1/trends/tags_controller.rb b/app/controllers/api/v1/trends/tags_controller.rb index f84f1c0252..ecac3579fc 100644 --- a/app/controllers/api/v1/trends/tags_controller.rb +++ b/app/controllers/api/v1/trends/tags_controller.rb @@ -7,7 +7,7 @@ class Api::V1::Trends::TagsController < Api::BaseController after_action :insert_pagination_headers - DEFAULT_TAGS_LIMIT = 10 + DEFAULT_TAGS_LIMIT = (ENV['MAX_TRENDING_TAGS'] || 10).to_i deprecate_api '2022-03-30', only: :index, if: -> { request.path == '/api/v1/trends' } diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1b071e8655..c11fd2a635 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -72,10 +72,24 @@ class ApplicationController < ActionController::Base def require_functional! return if current_user.functional? - if current_user.confirmed? - redirect_to edit_user_registration_path - else - redirect_to auth_setup_path + respond_to do |format| + format.any do + if current_user.confirmed? + redirect_to edit_user_registration_path + else + redirect_to auth_setup_path + end + end + + format.json do + if !current_user.confirmed? + render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403 + elsif !current_user.approved? + render json: { error: 'Your login is currently pending approval' }, status: 403 + elsif !current_user.functional? + render json: { error: 'Your login is currently disabled' }, status: 403 + end + end end end diff --git a/app/helpers/context_helper.rb b/app/helpers/context_helper.rb index 077c5272a5..79080895a4 100644 --- a/app/helpers/context_helper.rb +++ b/app/helpers/context_helper.rb @@ -35,6 +35,13 @@ module ContextHelper suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' }, attribution_domains: { 'toot' => 'http://joinmastodon.org/ns#', 'attributionDomains' => { '@id' => 'toot:attributionDomains', '@type' => '@id' } }, misskey_license: { 'misskey' => 'https://misskey-hub.net/ns#', '_misskey_license' => 'misskey:_misskey_license' }, + interaction_policies: { + 'gts' => 'https://gotosocial.org/ns#', + 'interactionPolicy' => { '@id' => 'gts:interactionPolicy', '@type' => '@id' }, + 'canQuote' => { '@id' => 'gts:canQuote', '@type' => '@id' }, + 'automaticApproval' => { '@id' => 'gts:automaticApproval', '@type' => '@id' }, + 'manualApproval' => { '@id' => 'gts:manualApproval', '@type' => '@id' }, + }, }.freeze def full_context diff --git a/app/javascript/entrypoints/sign_up.ts b/app/javascript/entrypoints/sign_up.ts index 880738fcb7..87100be56d 100644 --- a/app/javascript/entrypoints/sign_up.ts +++ b/app/javascript/entrypoints/sign_up.ts @@ -4,9 +4,12 @@ import axios from 'axios'; import ready from '../mastodon/ready'; async function checkConfirmation() { - const response = await axios.get('/api/v1/emails/check_confirmation'); + const response = await axios.get('/api/v1/emails/check_confirmation', { + headers: { Accept: 'application/json' }, + withCredentials: true, + }); - if (response.data) { + if (response.status === 200 && response.data === true) { window.location.href = '/start'; } } diff --git a/app/javascript/icons/android-chrome-144x144.png b/app/javascript/icons/android-chrome-144x144.png old mode 100755 new mode 100644 index d636e94c43..698fb4a260 Binary files a/app/javascript/icons/android-chrome-144x144.png and b/app/javascript/icons/android-chrome-144x144.png differ diff --git a/app/javascript/icons/android-chrome-192x192.png b/app/javascript/icons/android-chrome-192x192.png old mode 100755 new mode 100644 index 4a2681ffb9..2b6b632648 Binary files a/app/javascript/icons/android-chrome-192x192.png and b/app/javascript/icons/android-chrome-192x192.png differ diff --git a/app/javascript/icons/android-chrome-256x256.png b/app/javascript/icons/android-chrome-256x256.png old mode 100755 new mode 100644 index 8fab493ede..51e3849a26 Binary files a/app/javascript/icons/android-chrome-256x256.png and b/app/javascript/icons/android-chrome-256x256.png differ diff --git a/app/javascript/icons/android-chrome-36x36.png b/app/javascript/icons/android-chrome-36x36.png old mode 100755 new mode 100644 index 335d012db1..925f69c4fc Binary files a/app/javascript/icons/android-chrome-36x36.png and b/app/javascript/icons/android-chrome-36x36.png differ diff --git a/app/javascript/icons/android-chrome-384x384.png b/app/javascript/icons/android-chrome-384x384.png old mode 100755 new mode 100644 index 02b1e6fced..9d256a83cb Binary files a/app/javascript/icons/android-chrome-384x384.png and b/app/javascript/icons/android-chrome-384x384.png differ diff --git a/app/javascript/icons/android-chrome-48x48.png b/app/javascript/icons/android-chrome-48x48.png old mode 100755 new mode 100644 index 43cf411b8c..bcfe7475d0 Binary files a/app/javascript/icons/android-chrome-48x48.png and b/app/javascript/icons/android-chrome-48x48.png differ diff --git a/app/javascript/icons/android-chrome-512x512.png b/app/javascript/icons/android-chrome-512x512.png old mode 100755 new mode 100644 index 1856b80c7c..bffacfb699 Binary files a/app/javascript/icons/android-chrome-512x512.png and b/app/javascript/icons/android-chrome-512x512.png differ diff --git a/app/javascript/icons/android-chrome-72x72.png b/app/javascript/icons/android-chrome-72x72.png old mode 100755 new mode 100644 index 335008bf85..16679d5731 Binary files a/app/javascript/icons/android-chrome-72x72.png and b/app/javascript/icons/android-chrome-72x72.png differ diff --git a/app/javascript/icons/android-chrome-96x96.png b/app/javascript/icons/android-chrome-96x96.png old mode 100755 new mode 100644 index d1cb095822..9ade87cf32 Binary files a/app/javascript/icons/android-chrome-96x96.png and b/app/javascript/icons/android-chrome-96x96.png differ diff --git a/app/javascript/icons/apple-touch-icon-1024x1024.png b/app/javascript/icons/apple-touch-icon-1024x1024.png old mode 100755 new mode 100644 index c2a2d516ef..8ec371eb27 Binary files a/app/javascript/icons/apple-touch-icon-1024x1024.png and b/app/javascript/icons/apple-touch-icon-1024x1024.png differ diff --git a/app/javascript/icons/apple-touch-icon-114x114.png b/app/javascript/icons/apple-touch-icon-114x114.png old mode 100755 new mode 100644 index 218b415439..e1563f51e5 Binary files a/app/javascript/icons/apple-touch-icon-114x114.png and b/app/javascript/icons/apple-touch-icon-114x114.png differ diff --git a/app/javascript/icons/apple-touch-icon-120x120.png b/app/javascript/icons/apple-touch-icon-120x120.png old mode 100755 new mode 100644 index be53bc7c10..e9a5f5b0e5 Binary files a/app/javascript/icons/apple-touch-icon-120x120.png and b/app/javascript/icons/apple-touch-icon-120x120.png differ diff --git a/app/javascript/icons/apple-touch-icon-144x144.png b/app/javascript/icons/apple-touch-icon-144x144.png old mode 100755 new mode 100644 index cbb055732f..698fb4a260 Binary files a/app/javascript/icons/apple-touch-icon-144x144.png and b/app/javascript/icons/apple-touch-icon-144x144.png differ diff --git a/app/javascript/icons/apple-touch-icon-152x152.png b/app/javascript/icons/apple-touch-icon-152x152.png old mode 100755 new mode 100644 index 3a7975c054..0cc93cc288 Binary files a/app/javascript/icons/apple-touch-icon-152x152.png and b/app/javascript/icons/apple-touch-icon-152x152.png differ diff --git a/app/javascript/icons/apple-touch-icon-167x167.png b/app/javascript/icons/apple-touch-icon-167x167.png old mode 100755 new mode 100644 index 25be4eb5f5..9bbbf53120 Binary files a/app/javascript/icons/apple-touch-icon-167x167.png and b/app/javascript/icons/apple-touch-icon-167x167.png differ diff --git a/app/javascript/icons/apple-touch-icon-180x180.png b/app/javascript/icons/apple-touch-icon-180x180.png old mode 100755 new mode 100644 index dc0e9bc20b..329b803b91 Binary files a/app/javascript/icons/apple-touch-icon-180x180.png and b/app/javascript/icons/apple-touch-icon-180x180.png differ diff --git a/app/javascript/icons/apple-touch-icon-192x192.png b/app/javascript/icons/apple-touch-icon-192x192.png new file mode 100644 index 0000000000..2b6b632648 Binary files /dev/null and b/app/javascript/icons/apple-touch-icon-192x192.png differ diff --git a/app/javascript/icons/apple-touch-icon-256x256.png b/app/javascript/icons/apple-touch-icon-256x256.png new file mode 100644 index 0000000000..51e3849a26 Binary files /dev/null and b/app/javascript/icons/apple-touch-icon-256x256.png differ diff --git a/app/javascript/icons/apple-touch-icon-36x36.png b/app/javascript/icons/apple-touch-icon-36x36.png new file mode 100644 index 0000000000..925f69c4fc Binary files /dev/null and b/app/javascript/icons/apple-touch-icon-36x36.png differ diff --git a/app/javascript/icons/apple-touch-icon-384x384.png b/app/javascript/icons/apple-touch-icon-384x384.png new file mode 100644 index 0000000000..9d256a83cb Binary files /dev/null and b/app/javascript/icons/apple-touch-icon-384x384.png differ diff --git a/app/javascript/icons/apple-touch-icon-48x48.png b/app/javascript/icons/apple-touch-icon-48x48.png new file mode 100644 index 0000000000..bcfe7475d0 Binary files /dev/null and b/app/javascript/icons/apple-touch-icon-48x48.png differ diff --git a/app/javascript/icons/apple-touch-icon-512x512.png b/app/javascript/icons/apple-touch-icon-512x512.png new file mode 100644 index 0000000000..bffacfb699 Binary files /dev/null and b/app/javascript/icons/apple-touch-icon-512x512.png differ diff --git a/app/javascript/icons/apple-touch-icon-57x57.png b/app/javascript/icons/apple-touch-icon-57x57.png old mode 100755 new mode 100644 index bb0dc957cd..e00e142c64 Binary files a/app/javascript/icons/apple-touch-icon-57x57.png and b/app/javascript/icons/apple-touch-icon-57x57.png differ diff --git a/app/javascript/icons/apple-touch-icon-60x60.png b/app/javascript/icons/apple-touch-icon-60x60.png old mode 100755 new mode 100644 index 9143a0bf07..011285b564 Binary files a/app/javascript/icons/apple-touch-icon-60x60.png and b/app/javascript/icons/apple-touch-icon-60x60.png differ diff --git a/app/javascript/icons/apple-touch-icon-72x72.png b/app/javascript/icons/apple-touch-icon-72x72.png old mode 100755 new mode 100644 index 2b7d19484c..16679d5731 Binary files a/app/javascript/icons/apple-touch-icon-72x72.png and b/app/javascript/icons/apple-touch-icon-72x72.png differ diff --git a/app/javascript/icons/apple-touch-icon-76x76.png b/app/javascript/icons/apple-touch-icon-76x76.png old mode 100755 new mode 100644 index 0985e33bcb..83c8748876 Binary files a/app/javascript/icons/apple-touch-icon-76x76.png and b/app/javascript/icons/apple-touch-icon-76x76.png differ diff --git a/app/javascript/icons/apple-touch-icon-96x96.png b/app/javascript/icons/apple-touch-icon-96x96.png new file mode 100644 index 0000000000..9ade87cf32 Binary files /dev/null and b/app/javascript/icons/apple-touch-icon-96x96.png differ diff --git a/app/javascript/icons/favicon-16x16.png b/app/javascript/icons/favicon-16x16.png old mode 100755 new mode 100644 index 1326ba0462..7f865cfe96 Binary files a/app/javascript/icons/favicon-16x16.png and b/app/javascript/icons/favicon-16x16.png differ diff --git a/app/javascript/icons/favicon-32x32.png b/app/javascript/icons/favicon-32x32.png old mode 100755 new mode 100644 index f5058cb0a5..7f865cfe96 Binary files a/app/javascript/icons/favicon-32x32.png and b/app/javascript/icons/favicon-32x32.png differ diff --git a/app/javascript/icons/favicon-48x48.png b/app/javascript/icons/favicon-48x48.png old mode 100755 new mode 100644 index 6253d054c7..7f865cfe96 Binary files a/app/javascript/icons/favicon-48x48.png and b/app/javascript/icons/favicon-48x48.png differ diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index b643cf5613..ff2e10c79d 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -94,6 +94,17 @@ export function normalizeStatus(status, normalOldStatus) { normalStatus.contentHtml = emojify(normalStatus.content, emojiMap); normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap); normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive; + + if (normalStatus.url && !(normalStatus.url.startsWith('http://') || normalStatus.url.startsWith('https://'))) { + normalStatus.url = null; + } + + normalStatus.url ||= normalStatus.uri; + + normalStatus.media_attachments.forEach(item => { + if (item.remote_url && !(item.remote_url.startsWith('http://') || item.remote_url.startsWith('https://'))) + item.remote_url = null; + }); } if (normalOldStatus) { diff --git a/app/javascript/mastodon/components/server_banner.jsx b/app/javascript/mastodon/components/server_banner.jsx index 989ac7f006..72e4489dc1 100644 --- a/app/javascript/mastodon/components/server_banner.jsx +++ b/app/javascript/mastodon/components/server_banner.jsx @@ -42,7 +42,7 @@ class ServerBanner extends PureComponent { return (
- {domain}, mastodon: Mastodon }} /> + {domain}, mastodon: Mastodon }} />
diff --git a/app/javascript/mastodon/features/about/index.jsx b/app/javascript/mastodon/features/about/index.jsx index 359fb2ff7a..6acffa3141 100644 --- a/app/javascript/mastodon/features/about/index.jsx +++ b/app/javascript/mastodon/features/about/index.jsx @@ -14,13 +14,13 @@ import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react' import DisabledIcon from '@/material-icons/400-24px/close-fill.svg?react'; import EnabledIcon from '@/material-icons/400-24px/done-fill.svg?react'; import ExpandMoreIcon from '@/material-icons/400-24px/expand_more.svg?react'; -import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'mastodon/actions/server'; +import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'mastodon/actions/server'; import { Account } from 'mastodon/components/account'; import Column from 'mastodon/components/column'; -import { Icon } from 'mastodon/components/icon'; +import { Icon } from 'mastodon/components/icon'; import { ServerHeroImage } from 'mastodon/components/server_hero_image'; import { Skeleton } from 'mastodon/components/skeleton'; -import { LinkFooter} from 'mastodon/features/ui/components/link_footer'; +import { LinkFooter } from 'mastodon/features/ui/components/link_footer'; const messages = defineMessages({ title: { id: 'column.about', defaultMessage: 'About' }, @@ -40,6 +40,10 @@ const messages = defineMessages({ enabled: { id: 'about.enabled', defaultMessage: 'Enabled' }, disabled: { id: 'about.disabled', defaultMessage: 'Disabled' }, capabilities: { id: 'about.kmyblue_capabilities', defaultMessage: 'Features available in this server' }, + joinFediverse: { + id: 'about.join_fediverse', + defaultMessage: "Join the Fediverse, become part of a community, and break free from Big Tech™'s stranglehold on public discourse." + }, }); const severityMessages = { @@ -59,14 +63,13 @@ const severityMessages = { }, }; -const mapStateToProps = state => ({ +const mapStateToProps = (state) => ({ server: state.getIn(['server', 'server']), extendedDescription: state.getIn(['server', 'extendedDescription']), domainBlocks: state.getIn(['server', 'domainBlocks']), }); class Section extends PureComponent { - static propTypes = { title: PropTypes.string, children: PropTypes.node, @@ -85,49 +88,64 @@ class Section extends PureComponent { this.setState({ collapsed: !collapsed }, () => onOpen && onOpen()); }; - render () { + handleKeyDown = (e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + this.handleClick(); + } + }; + + render() { const { title, children } = this.props; const { collapsed } = this.state; return (
-
+
{title}
- {!collapsed && ( -
{children}
- )} + {!collapsed &&
{children}
}
); } - } class CapabilityIcon extends PureComponent { - static propTypes = { intl: PropTypes.object.isRequired, state: PropTypes.bool, }; - render () { + render() { const { intl, state } = this.props; if (state) { return ( - {intl.formatMessage(messages.enabled)} + + + {intl.formatMessage(messages.enabled)} + ); } else { return ( - {intl.formatMessage(messages.disabled)} + + + {intl.formatMessage(messages.disabled)} + ); } } } class About extends PureComponent { - static propTypes = { server: ImmutablePropTypes.map, extendedDescription: ImmutablePropTypes.map, @@ -141,7 +159,7 @@ class About extends PureComponent { multiColumn: PropTypes.bool, }; - componentDidMount () { + componentDidMount() { const { dispatch } = this.props; dispatch(fetchServer()); dispatch(fetchExtendedDescription()); @@ -152,11 +170,11 @@ class About extends PureComponent { dispatch(fetchDomainBlocks()); }; - render () { + render() { const { multiColumn, intl, server, extendedDescription, domainBlocks } = this.props; const isLoading = server.get('isLoading'); - const fedibirdCapabilities = server.get('fedibird_capabilities') || []; // thinking about isLoading is true + const fedibirdCapabilities = server.get('fedibird_capabilities') || []; const isPublicUnlistedVisibility = fedibirdCapabilities.includes('kmyblue_visibility_public_unlisted'); const isPublicVisibility = !fedibirdCapabilities.includes('kmyblue_no_public_visibility'); const isEmojiReaction = fedibirdCapabilities.includes('emoji_reaction'); @@ -169,59 +187,88 @@ class About extends PureComponent { return ( -
-
- `${value} ${key.replace('@', '')}`).join(', ')} className='about__header__hero' /> -

{isLoading ? : server.get('domain')}

-

Mastodon }} />

+
+
+ `${value} ${key.replace('@', '')}`) + .join(', ')} + className="about__header__hero" + /> +

{isLoading ? : server.get('domain')}

+

+ +

-
-
-

+
+
+

+ +

-
+
-
-

+
+

+ +

- {isLoading ? : {server.getIn(['contact', 'email'])}} + {isLoading ? ( + + ) : ( + + {server.getIn(['contact', 'email'])} + + )}
{extendedDescription.get('isLoading') ? ( <> - +
- +
- +
- + - ) : (extendedDescription.get('content')?.length > 0 ? ( -
+ ) : extendedDescription.get('content')?.length > 0 ? ( +
) : ( -

- ))} +

+ +

+ )}
{!isLoading && (server.get('rules', ImmutableList()).isEmpty() ? ( -

+

+ +

) : ( -
    - {server.get('rules').map(rule => ( +
      + {server.get('rules').map((rule) => (
    1. -
      {rule.get('text')}
      - {rule.get('hint').length > 0 && (
      {rule.get('hint')}
      )} +
      {rule.get('text')}
      + {!!rule.get('hint') && rule.get('hint').length > 0 && ( +
      {rule.get('hint')}
      + )}
    2. ))}
    @@ -229,23 +276,38 @@ class About extends PureComponent {
-

+

+ +

{!isLoading && ( -
    +
    1. - {intl.formatMessage(messages.emojiReaction)}: + + {intl.formatMessage(messages.emojiReaction)}: +
    2. - {intl.formatMessage(messages.publicVisibility)}: + + {intl.formatMessage(messages.publicVisibility)}: +
    3. - {intl.formatMessage(messages.publicUnlistedVisibility)}: + + {intl.formatMessage(messages.publicUnlistedVisibility)}: +
    4. - {intl.formatMessage(messages.localTimeline)}: + + {intl.formatMessage(messages.localTimeline)}: +
    5. - {intl.formatMessage(messages.fullTextSearch)}: + + {intl.formatMessage(messages.fullTextSearch)}: +
    )} @@ -254,49 +316,75 @@ class About extends PureComponent {
    {domainBlocks.get('isLoading') ? ( <> - +
    - + - ) : (domainBlocks.get('isAvailable') ? ( + ) : domainBlocks.get('isAvailable') ? ( <> -

    +

    + +

    {domainBlocks.get('items').size > 0 && ( -
    - {domainBlocks.get('items').map(block => ( -
    -
    -
    {block.get('domain')}
    - {intl.formatMessage(severityMessages[block.get('severity_ex') || block.get('severity')].title)} +
    + {domainBlocks.get('items').map((block) => ( +
    +
    +
    + {block.get('domain')} +
    + + {intl.formatMessage( + severityMessages[block.get('severity_ex') || block.get('severity')].title + )} +
    -

    {(block.get('comment') || '').length > 0 ? block.get('comment') : }

    +

    + {(block.get('comment') || '').length > 0 ? ( + block.get('comment') + ) : ( + + )} +

    ))}
    )} ) : ( -

    - ))} +

    + +

    + )}
    -
    -

    +
    +

    + +

    {intl.formatMessage(messages.title)} - + ); } - } export default connect(mapStateToProps)(injectIntl(About)); diff --git a/app/javascript/mastodon/features/getting_started/index.jsx b/app/javascript/mastodon/features/getting_started/index.jsx index ff3166da35..48ce17881a 100644 --- a/app/javascript/mastodon/features/getting_started/index.jsx +++ b/app/javascript/mastodon/features/getting_started/index.jsx @@ -14,6 +14,8 @@ import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?re import BookmarksIcon from '@/material-icons/400-24px/bookmarks-fill.svg?react'; import ExploreIcon from '@/material-icons/400-24px/explore.svg?react'; import ModerationIcon from '@/material-icons/400-24px/gavel.svg?react'; +import HashtagIcon from '@/material-icons/400-24px/tag.svg?react'; +import Directory from '@/material-icons/400-24px/group.svg?react'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react'; import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react'; @@ -42,6 +44,8 @@ const messages = defineMessages({ home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' }, notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' }, public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' }, + followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed Hashtags' }, + directory: { id: 'navigation_bar.directory', defaultMessage: 'Profile directory' }, settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' }, community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' }, deep_timeline: { id: 'navigation_bar.deep_timeline', defaultMessage: 'Deep timeline' }, @@ -144,6 +148,7 @@ class GettingStarted extends ImmutablePureComponent { , , , + , , , , diff --git a/app/javascript/mastodon/features/ui/components/link_footer.tsx b/app/javascript/mastodon/features/ui/components/link_footer.tsx index cbfb6a3114..a70111fce6 100644 --- a/app/javascript/mastodon/features/ui/components/link_footer.tsx +++ b/app/javascript/mastodon/features/ui/components/link_footer.tsx @@ -1,101 +1,63 @@ import { FormattedMessage } from 'react-intl'; - import { Link } from 'react-router-dom'; import { domain, - version, source_url, statusPageUrl, - profile_directory as canProfileDirectory, termsOfServiceEnabled, } from 'mastodon/initial_state'; -const DividingCircle: React.FC = () => {' · '}; +const DividingCircle: React.FC = () => {' · '}; -export const LinkFooter: React.FC<{ - multiColumn: boolean; -}> = ({ multiColumn }) => { +export const LinkFooter: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => { return ( -
    +

    {domain}:{' '} + {statusPageUrl && ( <> - + )} - {canProfileDirectory && ( - <> - - - - - - )} + - - + + + {termsOfServiceEnabled && ( <> - - + + )} + + + + + + + + + +

    - Mastodon:{' '} - - - - - - - - - - - - - - - - - v{version} + + Made with ❤️ +

    -
    + ); }; diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx index 95828ef25e..f3bf123415 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx @@ -1,10 +1,7 @@ import PropTypes from 'prop-types'; import { Component, useEffect } from 'react'; - import { defineMessages, injectIntl, useIntl } from 'react-intl'; - import { Link } from 'react-router-dom'; - import { useSelector, useDispatch } from 'react-redux'; import CirclesIcon from '@/material-icons/400-24px/account_circle-fill.svg?react'; @@ -13,6 +10,8 @@ import BookmarksActiveIcon from '@/material-icons/400-24px/bookmarks-fill.svg?re import BookmarksIcon from '@/material-icons/400-24px/bookmarks.svg?react'; import ExploreActiveIcon from '@/material-icons/400-24px/explore-fill.svg?react'; import ExploreIcon from '@/material-icons/400-24px/explore.svg?react'; +import HashtagIcon from '@/material-icons/400-24px/tag.svg?react'; +import DirectoryIcon from '@/material-icons/400-24px/group.svg?react'; import ModerationIcon from '@/material-icons/400-24px/gavel.svg?react'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import HomeActiveIcon from '@/material-icons/400-24px/home-fill.svg?react'; @@ -31,6 +30,7 @@ import SettingsIcon from '@/material-icons/400-24px/settings.svg?react'; import StarActiveIcon from '@/material-icons/400-24px/star-fill.svg?react'; import StarIcon from '@/material-icons/400-24px/star.svg?react'; import AntennaIcon from '@/material-icons/400-24px/wifi.svg?react'; + import { fetchFollowRequests } from 'mastodon/actions/accounts'; import { IconWithBadge } from 'mastodon/components/icon_with_badge'; import { WordmarkLogo } from 'mastodon/components/logo'; @@ -50,6 +50,8 @@ const messages = defineMessages({ home: { id: 'tabs_bar.home', defaultMessage: 'Home' }, notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' }, explore: { id: 'explore.title', defaultMessage: 'Explore' }, + followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' }, + directory: { id: 'navigation_bar.directory', defaultMessage: 'Profiles directory' }, local: { id: 'column.local', defaultMessage: 'Local' }, deepLocal: { id: 'column.deep_local', defaultMessage: 'Deep' }, firehose: { id: 'column.firehose', defaultMessage: 'Live feeds' }, @@ -71,7 +73,6 @@ const messages = defineMessages({ }); const NotificationsLink = () => { - const count = useSelector(selectUnreadNotificationGroupsCount); const intl = useIntl(); @@ -129,25 +130,20 @@ class NavigationPanel extends Component { const { intl } = this.props; const { signedIn, disabledAccountId, permissions } = this.props.identity; - const explorer = (trendsEnabled ? ( + const explorer = trendsEnabled ? ( ) : ( - )); - - let banner = undefined; + ); - if (transientSingleColumn) { - banner = ( -
    - {intl.formatMessage(messages.openedInClassicInterface)} - {" "} - - {intl.formatMessage(messages.advancedInterface)} - -
    - ); - } + const banner = transientSingleColumn ? ( +
    + {intl.formatMessage(messages.openedInClassicInterface)}{' '} + + {intl.formatMessage(messages.advancedInterface)} + +
    + ) : null; return (
    @@ -155,90 +151,59 @@ class NavigationPanel extends Component {
    - {banner && -
    - {banner} -
    - } + {banner &&
    {banner}
    }
    {signedIn && ( <> - - )} - - {signedIn && enableLocalTimeline && ( - - )} - - {signedIn && enableDtlMenu && dtlTag && ( - - )} - - {!signedIn && explorer} - - {signedIn && ( - - )} - - {(!signedIn && timelinePreview) && ( - - )} - - {signedIn && ( - <> + {enableLocalTimeline && } + {enableDtlMenu && dtlTag && } +
    - - )} - - {signedIn && ( - <> + + - - )} - - {signedIn && explorer} - - {signedIn && ( - <> + {explorer} - { !isHideItem('favourite_menu') && } + {!isHideItem('favourite_menu') && }
    - - {canManageReports(permissions) && } {canViewAdminDashboard(permissions) && } )} {!signedIn && ( -
    -
    - { disabledAccountId ? : } -
    + <> + {explorer} + {(timelinePreview || enableLocalTimeline) && ( + + )} +
    +
    + {disabledAccountId ? : } +
    + )} +
    -
    -
    - -
    +
    +
    +
    -
    ); } - } export default injectIntl(withIdentity(NavigationPanel)); 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 74a8fdb841..b7a57a4d9a 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,7 @@ const SignInBanner = () => { if (sso_redirect) { return (
    -

    +

    @@ -45,7 +45,7 @@ const SignInBanner = () => { return (
    -

    +

    {signupButton} diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json index 2f461cd19d..f9d07793c1 100644 --- a/app/javascript/mastodon/locales/en-GB.json +++ b/app/javascript/mastodon/locales/en-GB.json @@ -1,7 +1,7 @@ { "about.blocks": "Moderated servers", "about.contact": "Contact:", - "about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.", + "about.disclaimer": "Join the Fediverse, become part of a community, and break free from Big Tech™'s stranglehold on public discourse.", "about.domain_blocks.no_reason_available": "Reason not available", "about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the Fediverse. These are the exceptions that have been made on this particular server.", "about.domain_blocks.silenced.explanation": "You will generally not see profiles and content from this server, unless you explicitly look it up or opt into it by following.", @@ -802,11 +802,11 @@ "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.is_one_of_many": "{domain} is one of the many independent Mastodon servers you can use to participate in the fediverse.", + "server_banner.is_one_of_many": "{domain} is one of the many independent 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.mastodon_is": "Join the Fediverse, become part of a community, and break free from Big Tech™'s stranglehold on public discourse.", "sign_in_banner.sign_in": "Sign in", "sign_in_banner.sso_redirect": "Login or Register", "status.admin_account": "Open moderation interface for @{name}", diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 77448bd493..4f5cdce223 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -2,7 +2,7 @@ "about.blocks": "Moderated servers", "about.contact": "Contact:", "about.disabled": "Disabled", - "about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.", + "about.disclaimer": "Join the Fediverse, become part of a community, and break free from Big Tech™'s stranglehold on public discourse.", "about.domain_blocks.no_reason_available": "Reason not available", "about.domain_blocks.noop.explanation": "This server is limited partically.", "about.domain_blocks.noop.title": "Soft limited", @@ -14,9 +14,9 @@ "about.enabled": "Enabled", "about.full_text_search": "Full text search", "about.kmyblue_capabilities": "Features available in this server", - "about.kmyblue_capability": "This server is using kmyblue, a fork of Mastodon. On this server, kmyblues unique features are configured as follows.", + "about.kmyblue_capability": "Server unique features are configured as follows.", "about.not_available": "This information has not been made available on this server.", - "about.powered_by": "Decentralized social media powered by {mastodon}", + "about.powered_by": "Social media powered by You!", "about.public_visibility": "Public visibility", "about.rules": "Server rules", "account.account_note_header": "Personal note", @@ -1000,11 +1000,11 @@ "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.is_one_of_many": "{domain} is one of the many independent Mastodon servers you can use to participate in the fediverse.", + "server_banner.is_one_of_many": "{domain} is one of the many independent 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.mastodon_is": "Join the Fediverse, become part of a community, and break free from Big Tech™'s stranglehold on public discourse.", "sign_in_banner.sign_in": "Login", "sign_in_banner.sso_redirect": "Login or Register", "status.admin_account": "Open moderation interface for @{name}", diff --git a/app/javascript/mastodon/locales/sco.json b/app/javascript/mastodon/locales/sco.json index 5960fb7760..ec8a92ac2e 100644 --- a/app/javascript/mastodon/locales/sco.json +++ b/app/javascript/mastodon/locales/sco.json @@ -1,7 +1,7 @@ { "about.blocks": "Moderatit servers", "about.contact": "Contack:", - "about.disclaimer": "Mastodon is free, open-soorced saftware, an a trademairk o Mastodon gGmbH.", + "about.disclaimer": "Join the Fediverse, become part of a community, and break free from Big Tech™'s stranglehold on public discourse.", "about.domain_blocks.no_reason_available": "Raison no available", "about.domain_blocks.preamble": "On the hail, Mastodon lats ye view content frae an interack wi uisers fae onie ither server in the fediverse.", "about.domain_blocks.silenced.explanation": "Ye'll generally no see profiles an content frae this server, unless ye explicitly luik it up or opt intae it bi follaein.", diff --git a/app/javascript/mastodon/models/account.ts b/app/javascript/mastodon/models/account.ts index 55dbbcbb34..88cbc3359b 100644 --- a/app/javascript/mastodon/models/account.ts +++ b/app/javascript/mastodon/models/account.ts @@ -178,5 +178,10 @@ export function createAccountFromServerJSON(serverJSON: ApiAccountJSON) { ), note_emojified: emojify(accountJSON.note, emojiMap), note_plain: unescapeHTML(accountJSON.note), + url: + accountJSON.url.startsWith('http://') || + accountJSON.url.startsWith('https://') + ? accountJSON.url + : accountJSON.uri, }); } diff --git a/app/javascript/styles/full-dark/variables.scss b/app/javascript/styles/full-dark/variables.scss index 6cedec7df9..1720d716fe 100644 --- a/app/javascript/styles/full-dark/variables.scss +++ b/app/javascript/styles/full-dark/variables.scss @@ -1,11 +1,14 @@ $classic-base-color: #282c37; // Midnight Express $classic-secondary-color: #d9e1e8; // Pattens Blue -// Variables for defaults in UI -$simple-background-color: $classic-base-color !default; +@use '../mastodon/variables' with ( + // Variables for defaults in UI + $simple-background-color: $classic-base-color, -// Tell UI to use selected colors -$ui-base-lighter-color: #969fbc !default; // Lighter darkest + // Tell UI to use selected colors + $ui-base-lighter-color: #969fbc, -// For texts on inverted backgrounds -$inverted-text-color: $classic-secondary-color !default; + // Lighter darkest + // For texts on inverted backgrounds + $inverted-text-color: $classic-secondary-color +); diff --git a/app/javascript/styles/modern-contrast.scss b/app/javascript/styles/modern-contrast.scss new file mode 100644 index 0000000000..23e7b346ff --- /dev/null +++ b/app/javascript/styles/modern-contrast.scss @@ -0,0 +1,7 @@ +// Mastodon Modern theme by Freeplay! Check the original repo for more info: https://git.gay/freeplay/Mastodon-Modern +// Everything in the "modern" directory is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License: http://creativecommons.org/licenses/by-sa/4.0/ + +@use 'contrast/variables'; +@use 'application'; +@use 'modern/style'; +@use 'contrast/diff'; \ No newline at end of file diff --git a/app/javascript/styles/modern-dark.scss b/app/javascript/styles/modern-dark.scss new file mode 100644 index 0000000000..208b714754 --- /dev/null +++ b/app/javascript/styles/modern-dark.scss @@ -0,0 +1,6 @@ +// Mastodon Modern theme by Freeplay! Check the original repo for more info: https://git.gay/freeplay/Mastodon-Modern +// Everything in the "modern" directory is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License: http://creativecommons.org/licenses/by-sa/4.0/ + +@use 'mastodon/variables'; +@use 'application'; +@use 'modern/style'; \ No newline at end of file diff --git a/app/javascript/styles/modern-light.scss b/app/javascript/styles/modern-light.scss new file mode 100644 index 0000000000..c8ecbd0bc8 --- /dev/null +++ b/app/javascript/styles/modern-light.scss @@ -0,0 +1,8 @@ +// Mastodon Modern theme by Freeplay! Check the original repo for more info: https://git.gay/freeplay/Mastodon-Modern +// Everything in the "modern" directory is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License: http://creativecommons.org/licenses/by-sa/4.0/ + +@use 'mastodon-light/variables'; +@use 'mastodon-light/css_variables'; +@use 'application'; +@use 'modern/style'; +@use 'mastodon-light/diff'; \ No newline at end of file diff --git a/app/javascript/styles/modern/style.scss b/app/javascript/styles/modern/style.scss new file mode 100644 index 0000000000..4902cad50e --- /dev/null +++ b/app/javascript/styles/modern/style.scss @@ -0,0 +1,2859 @@ +:root { + --tl-width: 720px; + --emoji-size: 2em; + --avatar-size: 46px; + --radius: 12px; + --radius-round: 24px; + --panel-radius: var(--radius); + --hover-color: rgba(170,170,170,0.07); + --elevated-color: rgba(150,150,200,0.1); + --elevated-tint: rgba(200,200,240,0.07); + --border-color: rgba(120,120,200,0.2); + --border-color-2: #787878; + --shadow: 0 10px 40px -10px rgba(0,0,0,0.15); + --shadow-low: 0 8px 24px -16px rgba(0,0,0,0.2); + --shadow-med: 0 8px 60px -30px rgba(0,0,0,0.1); + --column-shadow: 0 8px 24px 12px rgba(0,0,0,0.02); + --background-border-color: var(--border-color); + } + @media (max-width: 759px) { + :root { + --panel-radius: 0px; + } + } + .layout-multiple-columns { + --panel-radius: 0px; + } + body { + font-display: swap !important; + } + body:not(.admin)::before { + content: ""; + position: fixed; + inset: 0; + background: rgba(0,0,0,0.06); + z-index: -1; + } + p { + line-height: 1.5; + } + input { + text-align: start; + } + .button--block { + font-weight: bold; + } + .unhandled-link span, + .mention span { + text-decoration: none !important; + } + .unhandled-link:not(:focus):not(:hover) span, + .mention:not(:focus):not(:hover) span { + text-decoration: underline !important; + } + input, + .input-copy, + select, + textarea, + .compose-form__upload-thumbnail, + .button, + :not(.notification__filter-bar) > button:not(.column-header__button), + video, + .privacy-dropdown__dropdown, + .react-toggle-track, + .reply-indicator, + .compose-form__warning { + border-radius: var(--radius); + } + button:focus-visible, + .focusable:focus-visible, + a:focus-visible, + .media-gallery__item-thumbnail:focus-visible { + box-shadow: inset 0 0 0 2px #dc7b18; + outline: 2px #dc7b18 solid; + outline-offset: -2px; + } + :not(.radio-button__input):not(span) { + border-color: var(--border-color); + } + .nothing-here, + .column-inline-form, + .scrollable, + .detailed-status__action-bar, + .column-back-button, + .column-header__collapsible.collapsed, + .column-header__collapsible-inner, + .audio-player, + .search__input { + border: 0 !important; + } + .account__section-headline, + .notification__filter-bar, + .column-header { + border-inline: 0; + } + .account__section-headline, + .notification__filter-bar, + .column > .scrollable { + background: none; + } + .account__avatar, + #profile_page_avatar, + .account__avatar-composite, + .account-card__title__avatar img { + border-radius: 30%; + flex: none; + } + :not(body):not(.scrollable)::-webkit-scrollbar { + width: 6px; + margin-block: 10px; + } + :not(body):not(.scrollable)::-webkit-scrollbar-track { + background: none; + } + :not(body):not(.scrollable)::-webkit-scrollbar-thumb { + border-radius: 100px; + transition: background-color 0.2s; + } + :not(body):not(.scrollable):not(:hover)::-webkit-scrollbar-thumb { + background: none; + padding-block: 10px; + } + @media (prefers-reduced-motion: no-preference) { + body:not(.reduce-motion) .ui__navigation-bar__item, + body:not(.reduce-motion) .load-more, + body:not(.reduce-motion) .setting-toggle, + body:not(.reduce-motion) .column-header__back-button, + body:not(.reduce-motion) .column-back-button, + body:not(.reduce-motion) .trends__item, + body:not(.reduce-motion) .story, + body:not(.reduce-motion) .account__avatar, + body:not(.reduce-motion) .button, + body:not(.reduce-motion) .media-gallery__item, + body:not(.reduce-motion) .column-link, + body:not(.reduce-motion) select, + body:not(.reduce-motion) .status-card, + body:not(.reduce-motion) .audio-player, + body:not(.reduce-motion) .account { + transition: transform 0.4s cubic-bezier(0, 0, 0, 3), background 0.2s, opacity 0.2s !important; + } + body:not(.reduce-motion) .ui__navigation-bar__item:active, + body:not(.reduce-motion) .load-more:active, + body:not(.reduce-motion) .setting-toggle:active, + body:not(.reduce-motion) .column-header__back-button:active, + body:not(.reduce-motion) .column-back-button:active, + body:not(.reduce-motion) .trends__item:active, + body:not(.reduce-motion) .story:active, + body:not(.reduce-motion) .account__avatar:active, + body:not(.reduce-motion) .button:active, + body:not(.reduce-motion) .media-gallery__item:active, + body:not(.reduce-motion) .column-link:active, + body:not(.reduce-motion) select:active, + body:not(.reduce-motion) .status-card:active, + body:not(.reduce-motion) .audio-player:active, + body:not(.reduce-motion) .account:active, + body:not(.reduce-motion) .ui__navigation-bar__item:focus-visible, + body:not(.reduce-motion) .load-more:focus-visible, + body:not(.reduce-motion) .setting-toggle:focus-visible, + body:not(.reduce-motion) .column-header__back-button:focus-visible, + body:not(.reduce-motion) .column-back-button:focus-visible, + body:not(.reduce-motion) .trends__item:focus-visible, + body:not(.reduce-motion) .story:focus-visible, + body:not(.reduce-motion) .account__avatar:focus-visible, + body:not(.reduce-motion) .button:focus-visible, + body:not(.reduce-motion) .media-gallery__item:focus-visible, + body:not(.reduce-motion) .column-link:focus-visible, + body:not(.reduce-motion) select:focus-visible, + body:not(.reduce-motion) .status-card:focus-visible, + body:not(.reduce-motion) .audio-player:focus-visible, + body:not(.reduce-motion) .account:focus-visible { + transform: scale(0.99); + transition: transform 0.4s cubic-bezier(0, 0, 0, 1) !important; + } + body:not(.reduce-motion) .column-header__button, + body:not(.reduce-motion) .column-header__buttons > .column-header__back-button, + body:not(.reduce-motion) .react-toggle-track, + body:not(.reduce-motion) .icon-button, + body:not(.reduce-motion) .floating-action-button { + transition: transform 0.4s cubic-bezier(0, 0, 0, 4), background 0.2s !important; + } + body:not(.reduce-motion) .column-header__button:active, + body:not(.reduce-motion) .column-header__buttons > .column-header__back-button:active, + body:not(.reduce-motion) .react-toggle-track:active, + body:not(.reduce-motion) .icon-button:active, + body:not(.reduce-motion) .floating-action-button:active { + transform: scale(0.95); + transition: transform 0.4s cubic-bezier(0, 0, 0, 1) !important; + } + body:not(.reduce-motion) .status__content__spoiler-link span { + display: inline-block; + transition: transform 0.4s cubic-bezier(0, 0, 0, 4) !important; + } + body:not(.reduce-motion) .status__content__spoiler-link:active span { + transition: transform 0.4s cubic-bezier(0, 0, 0, 1) !important; + transform: translateY(1px); + } + .reduce-motion * { + animation-duration: 0s !important; + } + @-moz-keyframes bounceIn { + 0% { + transform: scale(1.1); + opacity: 0; + } + 30% { + transform: scale(0.99); + opacity: 1; + } + 60% { + transform: scale(1.005); + opacity: 1; + } + 100% { + transform: scale(1); + opacity: 1; + } + } + @-webkit-keyframes bounceIn { + 0% { + transform: scale(1.1); + opacity: 0; + } + 30% { + transform: scale(0.99); + opacity: 1; + } + 60% { + transform: scale(1.005); + opacity: 1; + } + 100% { + transform: scale(1); + opacity: 1; + } + } + @-o-keyframes bounceIn { + 0% { + transform: scale(1.1); + opacity: 0; + } + 30% { + transform: scale(0.99); + opacity: 1; + } + 60% { + transform: scale(1.005); + opacity: 1; + } + 100% { + transform: scale(1); + opacity: 1; + } + } + @keyframes bounceIn { + 0% { + transform: scale(1.1); + opacity: 0; + } + 30% { + transform: scale(0.99); + opacity: 1; + } + 60% { + transform: scale(1.005); + opacity: 1; + } + 100% { + transform: scale(1); + opacity: 1; + } + } + @-moz-keyframes slideUp { + from { + transform: translateY(20px); + } + } + @-webkit-keyframes slideUp { + from { + transform: translateY(20px); + } + } + @-o-keyframes slideUp { + from { + transform: translateY(20px); + } + } + @keyframes slideUp { + from { + transform: translateY(20px); + } + } + @-moz-keyframes slideUpFade { + from { + transform: translateY(20px); + filter: opacity(0); + } + } + @-webkit-keyframes slideUpFade { + from { + transform: translateY(20px); + filter: opacity(0); + } + } + @-o-keyframes slideUpFade { + from { + transform: translateY(20px); + filter: opacity(0); + } + } + @keyframes slideUpFade { + from { + transform: translateY(20px); + filter: opacity(0); + } + } + @-moz-keyframes slideUpFadeBig { + from { + transform: translateY(200px); + filter: opacity(0); + } + } + @-webkit-keyframes slideUpFadeBig { + from { + transform: translateY(200px); + filter: opacity(0); + } + } + @-o-keyframes slideUpFadeBig { + from { + transform: translateY(200px); + filter: opacity(0); + } + } + @keyframes slideUpFadeBig { + from { + transform: translateY(200px); + filter: opacity(0); + } + } + @-moz-keyframes slideDownFade { + from { + transform: translateY(-20px); + filter: opacity(0); + } + } + @-webkit-keyframes slideDownFade { + from { + transform: translateY(-20px); + filter: opacity(0); + } + } + @-o-keyframes slideDownFade { + from { + transform: translateY(-20px); + filter: opacity(0); + } + } + @keyframes slideDownFade { + from { + transform: translateY(-20px); + filter: opacity(0); + } + } + @-moz-keyframes slideUpBig { + from { + transform: translateY(50vh); + } + } + @-webkit-keyframes slideUpBig { + from { + transform: translateY(50vh); + } + } + @-o-keyframes slideUpBig { + from { + transform: translateY(50vh); + } + } + @keyframes slideUpBig { + from { + transform: translateY(50vh); + } + } + @-moz-keyframes fadeUp { + from { + transform: translateY(10px); + opacity: 0; + } + } + @-webkit-keyframes fadeUp { + from { + transform: translateY(10px); + opacity: 0; + } + } + @-o-keyframes fadeUp { + from { + transform: translateY(10px); + opacity: 0; + } + } + @keyframes fadeUp { + from { + transform: translateY(10px); + opacity: 0; + } + } + @-moz-keyframes scaleIn { + from { + transform: scale(0.98); + opacity: 0; + } + } + @-webkit-keyframes scaleIn { + from { + transform: scale(0.98); + opacity: 0; + } + } + @-o-keyframes scaleIn { + from { + transform: scale(0.98); + opacity: 0; + } + } + @keyframes scaleIn { + from { + transform: scale(0.98); + opacity: 0; + } + } + @-moz-keyframes fadeLeft { + from { + transform: translateX(20px) opacity(0); + } + } + @-webkit-keyframes fadeLeft { + from { + transform: translateX(20px) opacity(0); + } + } + @-o-keyframes fadeLeft { + from { + transform: translateX(20px) opacity(0); + } + } + @keyframes fadeLeft { + from { + transform: translateX(20px) opacity(0); + } + } + @-moz-keyframes rainbow { + to { + filter: hue-rotate(360deg); + } + } + @-webkit-keyframes rainbow { + to { + filter: hue-rotate(360deg); + } + } + @-o-keyframes rainbow { + to { + filter: hue-rotate(360deg); + } + } + @keyframes rainbow { + to { + filter: hue-rotate(360deg); + } + } + } + .columns-area__panels { + --top: 5px; + gap: 0; + } + @media (min-width: 1175px) { + .columns-area__panels { + padding-inline: 10px; + padding-top: var(--top); + box-sizing: border-box; + transition: padding 0.4s; + --top: 20px; + } + } + @media (min-width: 1320px) { + .columns-area__panels { + --top: 30px; + } + } + .compose-panel { + overflow-y: auto; + margin-top: calc(0px - var(--top)); + padding-top: var(--top); + padding-bottom: 20px; + padding-inline: 10px; + box-sizing: border-box; + max-height: unset !important; + height: 100%; + } + .compose-panel > * { + padding-inline: 0; + } + .compose-panel > .navigation-bar { + padding-top: 0 !important; + } + .compose-panel .search, + .drawer .search { + margin-bottom: 25px; + } + .compose-form__uploads { + padding: 0; + margin-block: 0 !important; + margin-inline: 12px; + width: unset; + } + .search { + border-radius: var(--radius); + margin-inline: -5px; + } + .search label { + box-sizing: border-box; + } + .search input { + border-radius: var(--radius-round) !important; + } + .search .search__icon > i { + margin-inline: 5px; + } + .search__popout { + border-radius: var(--radius); + animation: scaleIn 0.2s; + box-shadow: var(--shadow-low); + margin-top: 10px; + margin-inline: 4px; + width: calc(100% - 8px); + } + .navigation-bar .icon-button { + width: auto !important; + height: auto !important; + padding: 8px; + } + .compose-form { + min-height: unset; + overflow: unset; + gap: 15px; + flex: 1 0 auto !important; + } + .compose-form > * { + margin: 0 !important; + } + .compose-form > [aria-hidden="true"] { + display: none; + } + .compose-form > .navigation-bar { + margin-top: 10px; + } + .compose-form .reply-indicator { + position: relative; + transition: min-height 0.1s; + } + .compose-form .reply-indicator__display-name { + padding: 0; + } + .compose-form .spoiler-input__border { + display: none; + } + .compose-form #cw-spoiler-input { + padding-inline: 12px; + } + .compose-form .compose-form__autosuggest-wrapper, + .compose-form .autosuggest-textarea__textarea { + border-radius: var(--radius) var(--radius) 0 0 !important; + border-bottom: 0; + } + .compose-form .compose-form__buttons-wrapper { + border-radius: 0 0 var(--radius) var(--radius) !important; + } + .compose-form .compose-form__publish-button-wrapper { + overflow: visible !important; + max-width: 100%; + padding: 0; + } + .compose-form .compose-form__upload__actions { + z-index: 5; + position: relative; + } + .compose-form .compose-form__upload__actions button { + background: none; + } + .compose-form .compose-form__upload__thumbnail { + display: flex; + flex-direction: column; + } + .compose-form .compose-form__upload__warning { + position: relative; + flex-grow: 1; + display: flex; + } + .compose-form .compose-form__upload__warning button { + margin-top: auto; + } + .compose-form .compose-form__upload__warning button.active { + box-shadow: 0 0 0 100px rgba(0,0,0,0.75); + width: 100%; + height: 100%; + font-weight: bold; + font-size: 1.1em; + color: inherit; + transition: background 0.2s, transform 0.2s cubic-bezier(0, 0, 0, 1) !important; + } + .compose-form .compose-form__upload__warning button.active svg { + height: 1.2em; + width: 1.2em; + } + .compose-form .compose-form__upload__warning button.active:hover, + .compose-form .compose-form__upload__warning button.active:focus { + background: rgba(20,20,20,0.75); + } + .compose-form__highlightable { + border-radius: var(--radius); + overflow: visible !important; + } + .compose-form__highlightable #cw-spoiler-input { + border-radius: 0 !important; + } + .compose-form__highlightable textarea { + background: none !important; + } + .compose-form__highlightable .compose-form__dropdowns { + max-width: calc(100% - 7ch); + } + .compose-form__highlightable .compose-form__actions { + position: relative; + } + .compose-form__highlightable .compose-form__buttons { + display: flex; + flex-wrap: wrap; + flex-direction: row; + gap: 0; + flex-grow: 9999; + } + .compose-form__highlightable .compose-form__buttons * { + display: flex; + flex-grow: 1; + } + .compose-form__highlightable .compose-form__buttons label { + display: none; + } + .compose-form__highlightable .compose-form__buttons button { + flex-grow: 1; + padding: 5px; + } + .compose-form__highlightable .compose-form__submit button { + padding: 8px 16px; + } + .compose-form__highlightable .character-counter { + position: absolute; + inset-inline-end: 0; + bottom: calc(100% + 12px); + padding: 4px; + font-size: 13px; + } + .server-banner .server-banner__hero { + border-radius: var(--radius); + width: 100%; + border-radius: var(--radius) var(--radius) 0 0; + border: 1px solid var(--border-color); + box-sizing: border-box; + } + .server-banner .server-banner__description { + border: 1px solid var(--border-color); + padding: 14px 12px; + margin-top: -20px; + line-height: 1.5; + border-radius: 0 0 var(--radius) var(--radius); + } + .server-banner .server-banner__meta { + flex-direction: column; + } + .server-banner .server-banner__meta .server-banner__meta__column { + width: unset; + overflow: visible; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: flex-end; + } + .server-banner .server-banner__meta .server-banner__meta__column h4 { + margin: 0; + font-size: 0.9em !important; + width: 100%; + } + .server-banner .server-banner__meta .server-banner__meta__column .account { + margin-inline: -10px; + padding: 10px !important; + width: 100%; + } + .server-banner .server-banner__meta .server-banner__meta__column .account::after { + content: unset !important; + } + .server-banner .server-banner__meta .server-banner__meta__column .server-banner__number, + .server-banner .server-banner__meta .server-banner__meta__column .server-banner__number-label { + font-size: 1.1em; + line-height: 2; + margin-inline-end: 0.5em; + } + .server-banner .server-banner__meta .server-banner__meta__column .server-banner__number-label { + font-weight: 400 !important; + } + .navigation-panel__sign-in-banner { + margin-inline: 5px; + position: relative; + } + .link-footer { + margin-top: 20px; + } + .link-footer > p:last-child { + margin-bottom: 0; + } + .columns-area { + box-shadow: var(--column-shadow); + padding: 0; + overflow: visible; + } + @media (min-width: 1175px) { + .columns-area { + border-radius: var(--radius) var(--radius) 0 0 !important; + } + .columns-area > :first-child { + border-radius: var(--radius) var(--radius) 0 0 !important; + } + } + .columns-area__panels__main { + overflow: visible !important; + contain: inline-size style !important; + transition: max-width 0.2s cubic-bezier(0, 0, 0, 1.1), margin 0.2s cubic-bezier(0, 0, 0, 1.1); + } + @media (min-width: 1175px) { + .columns-area__panels__main { + width: 0; + flex-grow: 1; + margin-inline: 10px; + max-width: var(--tl-width) !important; + } + } + @media (min-width: 1320px) { + .columns-area__panels__main { + margin: 0 20px; + } + } + @media (min-width: 760px) { + .columns-area__panels__main { + display: grid; + grid-template-columns: 100%; + } + .columns-area__panels__main .column, + .columns-area__panels__main .columns-area { + grid-column: 1; + overflow: clip !important; + } + } + .columns-area__panels__main > div { + grid-row: 1; + } + .column { + background: var(--background-color); + overflow: clip; + } + .column::before { + content: ""; + position: absolute; + inset: 0; + background: var(--elevated-tint); + pointer-events: none; + } + @media (min-width: 760px) { + .layout-single-column .scrollable > [tabindex="-1"]:first-child { + margin-top: 10px; + } + .layout-single-column .item-list > article:first-of-type { + margin-top: 10px; + } + .layout-single-column .item-list > article::after { + inset-inline: calc(var(--radius) + 10px); + } + .layout-single-column .load-more, + .layout-single-column .trends__item, + .layout-single-column .focusable, + .layout-single-column .entry, + .layout-single-column .statuses-grid__item .detailed-status, + .layout-single-column .story, + .layout-single-column .scrollable :not(.focusable) > .account:not(.account--minimal), + .layout-single-column .timeline-hint, + .layout-single-column .notification-request { + margin-inline: 10px; + max-width: calc(100% - 20px); + } + } + .scrollable { + padding-bottom: 40vh !important; + } + .empty-column-indicator, + .error-column { + background: none; + } + .dismissable-banner { + display: flex; + align-items: center; + flex-direction: row-reverse; + gap: 20px; + margin: 0; + border-radius: 0; + border: 0; + padding: 25px; + } + .dismissable-banner > div { + padding: 0; + } + .dismissable-banner button { + padding: 16px; + margin: -16px -14px; + } + .tabs-bar__wrapper { + grid-column: 2; + border: 0 !important; + padding-top: 0; + transition: margin 0.2s cubic-bezier(0, 0, 0, 1.1), top 0.4s; + } + @media (min-width: 760px) { + .tabs-bar__wrapper { + margin-top: -100vh; + } + } + .column-header, + .column-inline-form { + font-weight: 600; + border-bottom-left-radius: 0 !important; + border-bottom-right-radius: 0 !important; + } + .column-header ~ .scrollable, + .column-inline-form ~ .scrollable { + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important; + } + .column-header__title { + gap: 12px; + } + .announcements, + .column-header__collapsible:not(.collapsed) { + flex-direction: column-reverse; + align-items: flex-start; + border: 0; + animation: slideDownFade 0.3s backwards cubic-bezier(0, 1, 0, 1.2); + } + .column-header__collapsible { + transition: none; + background: var(--surface-background-color); + overflow-y: auto !important; + } + .tabs-bar__wrapper .collapsed { + display: none; + } + .announcements { + background: var(--surface-background-color); + } + .announcements__container { + width: 100% !important; + } + .announcements__mastodon { + display: block !important; + z-index: -1; + position: relative; + } + .announcements__pagination { + bottom: unset; + padding-block: 0; + display: flex; + align-items: center; + } + .column-header__wrapper > :not(.column-header):not(.collapsed) { + border-top: 2px solid var(--background-color) !important; + border: 0; + } + .column-header { + overflow: hidden; + } + .column-header > button { + z-index: 2; + } + .column-header__buttons { + isolation: isolate; + } + .column-header__buttons button { + transition: background 0.2s, transform 0.3s !important; + position: relative; + border-radius: 100px !important; + min-width: 40px; + margin: 5px; + margin-inline-start: 0; + font-size: 0.9em; + padding-inline: 10px; + } + .column-header__buttons button:not(.active) { + background: var(--elevated-color) !important; + z-index: 2; + } + .column-header__buttons button svg { + margin: 0; + } + .column-header__buttons button span { + display: none; + } + .column-header__buttons button::before { + content: ""; + position: absolute; + inset: -20px -800px; + transform: scale(0); + transform-origin: bottom center; + background: var(--surface-background-color); + z-index: -1; + border-radius: 100px; + pointer-events: none; + opacity: 0; + transition: transform 0.3s, opacity 0.3s; + } + @media (prefers-reduced-motion) { + .column-header__buttons button::before { + transition: none !important; + } + } + .column-header__buttons button.active::before { + transform: scale(1, 5); + opacity: 1; + transition: transform 0.3s, opacity 0.1s; + } + @media (min-width: 760px) { + .tabs-bar__wrapper { + inset-inline: unset !important; + height: 50px !important; + top: 0; + top: var(--top) !important; + width: 285px; + border-radius: var(--radius) var(--radius) !important; + box-shadow: 0 12px 12px -12px rgba(0,0,0,0.1); + margin-inline-start: 20px; + } + .tabs-bar__wrapper .column-header__wrapper { + display: flex; + flex-direction: column; + border-radius: var(--radius); + overflow: hidden; + } + .tabs-bar__wrapper .column-header__wrapper > div { + background: var(--surface-background-color); + } + .tabs-bar__wrapper .column-header { + background: none !important; + overflow: hidden; + border: 0; + } + } + @media (min-width: 760px) and (max-width: 1319px) { + .tabs-bar__wrapper { + margin-inline-start: 10px; + } + } + @media (min-width: 760px) and (max-width: 1174px) { + .tabs-bar__wrapper { + width: 265px; + top: 10px !important; + } + } + @media (min-width: 760px) { + .column-back-button--slim { + margin-left: auto !important; + order: -1; + } + .column-back-button--slim > .column-back-button { + margin-top: 0 !important; + top: unset !important; + } + } + .column-settings__row, + .column-settings__hashtags { + gap: 0; + } + .column-settings h3 { + font-size: 1em; + margin-bottom: 8px; + } + .column-select__control { + border-radius: var(--radius); + } + .local-settings__page__item, + .glitch-setting-text.glitch-setting-text, + .setting-toggle, + .app-form__toggle { + display: flex; + align-items: center; + margin-bottom: 14px; + position: relative; + padding: 0.7em; + background: var(--elevated-color); + margin-block: 0 2px !important; + overflow: hidden; + } + .local-settings__page__item:first-of-type, + .glitch-setting-text.glitch-setting-text:first-of-type, + .setting-toggle:first-of-type, + .app-form__toggle:first-of-type { + border-top-left-radius: var(--radius); + border-top-right-radius: var(--radius); + } + .local-settings__page__item:last-of-type, + .glitch-setting-text.glitch-setting-text:last-of-type, + .setting-toggle:last-of-type, + .app-form__toggle:last-of-type { + border-bottom-left-radius: var(--radius); + border-bottom-right-radius: var(--radius); + } + .local-settings__page__item label, + .glitch-setting-text.glitch-setting-text label, + .setting-toggle label, + .app-form__toggle label, + .local-settings__page__item legend, + .glitch-setting-text.glitch-setting-text legend, + .setting-toggle legend, + .app-form__toggle legend { + padding-block: 2px !important; + } + .local-settings__page__item label span::before, + .glitch-setting-text.glitch-setting-text label span::before, + .setting-toggle label span::before, + .app-form__toggle label span::before { + content: ""; + position: absolute; + inset: -900px; + } + .local-settings__page__item .react-toggle, + .glitch-setting-text.glitch-setting-text .react-toggle, + .setting-toggle .react-toggle, + .app-form__toggle .react-toggle { + order: 2; + } + .local-settings__page__item .setting-toggle__label, + .glitch-setting-text.glitch-setting-text .setting-toggle__label, + .setting-toggle .setting-toggle__label, + .app-form__toggle .setting-toggle__label { + margin-bottom: 0 !important; + flex-grow: 1; + width: 0; + } + .local-settings__page__item::before, + .glitch-setting-text.glitch-setting-text::before, + .setting-toggle::before, + .app-form__toggle::before { + content: ""; + position: absolute; + inset: 0; + background: var(--hover-color); + opacity: 0; + transition: opacity 0.2s; + pointer-events: none; + } + .local-settings__page__item:hover::before, + .glitch-setting-text.glitch-setting-text:hover::before, + .setting-toggle:hover::before, + .app-form__toggle:hover::before, + .local-settings__page__item:focus-within::before, + .glitch-setting-text.glitch-setting-text:focus-within::before, + .setting-toggle:focus-within::before, + .app-form__toggle:focus-within::before { + opacity: 1; + } + @media (min-width: 760px) and (max-width: 1174px) { + .column-back-button--slim > .column-back-button { + margin-top: -55px !important; + top: unset !important; + } + } + .navigation-panel.navigation-panel { + box-sizing: border-box; + height: calc(100vh - var(--top) - 50px + var(--radius)); + padding-bottom: 10px; + margin: 0; + border: 0; + margin-top: calc(0px - var(--radius)); + padding-top: calc(10px + var(--radius)); + overflow: hidden auto; + } + .navigation-panel.navigation-panel hr { + display: none; + } + @media (min-width: 1175px) { + .navigation-panel.navigation-panel { + padding-top: calc(var(--radius) + 10px); + margin-top: calc(50px - var(--radius)); + } + .navigation-panel.navigation-panel .navigation-panel__logo { + margin: 0; + } + .navigation-panel.navigation-panel .navigation-panel__logo .column-link, + .navigation-panel.navigation-panel .navigation-panel__logo hr { + display: none !important; + } + .navigation-panel.navigation-panel .switch-to-advanced { + border-radius: var(--radius); + margin-top: 0; + } + } + .column-link { + border: 0; + gap: 12px; + } + .icon-with-badge__badge { + display: flex !important; + align-items: center; + justify-content: center; + padding: 0 0.3em !important; + width: 2em; + height: 2em; + min-width: max-content; + border-radius: 2em; + font-weight: 600; + font-size: 0.6em !important; + top: -10px; + box-sizing: border-box; + } + @media (min-width: 760px) { + .column-link { + flex-grow: 100 !important; + display: flex !important; + align-items: center !important; + align-content: center; + max-height: 3em; + min-height: 3em !important; + padding-block: 0; + border-radius: 100px; + position: relative; + box-sizing: border-box; + opacity: 0.9; + overflow: hidden; + background: none !important; + } + .column-link::before { + content: "" !important; + position: absolute; + border-radius: 100px; + width: unset !important; + height: unset !important; + inset: 0px 0px !important; + opacity: 0 !important; + background-color: rgba(150,150,150,0.1); + transition: opacity 0.2s; + } + .column-link.active { + opacity: 1 !important; + font-weight: 600; + } + .column-link:hover:before, + .column-link:focus:before { + opacity: 1 !important; + } + } + .navigation-panel.navigation-panel .trends__item { + margin: 0 !important; + } + .scrollable > div:first-child > [tabindex="-1"]:last-child .status__wrapper::after { + content: unset; + } + .focusable, + .entry, + .statuses-grid__item .detailed-status, + .trends__item, + .story, + .scrollable :not(.focusable) > .account:not(.account--minimal), + .timeline-hint, + .notification-request { + overflow: hidden; + contain: paint inline-size; + position: relative; + border-radius: var(--panel-radius); + border: 0; + } + .focusable.focusable, + .entry.focusable, + .statuses-grid__item .detailed-status.focusable, + .trends__item.focusable, + .story.focusable, + .scrollable :not(.focusable) > .account:not(.account--minimal).focusable, + .timeline-hint.focusable, + .notification-request.focusable { + background: none; + } + @media (pointer: fine) { + .focusable::before, + .entry::before, + .statuses-grid__item .detailed-status::before, + .trends__item::before, + .story::before, + .scrollable :not(.focusable) > .account:not(.account--minimal)::before, + .timeline-hint::before, + .notification-request::before { + content: ""; + position: absolute; + inset: 0px !important; + height: unset !important; + width: unset !important; + pointer-events: none; + transition: background-color 0.2s; + } + .focusable:hover::before, + .entry:hover::before, + .statuses-grid__item .detailed-status:hover::before, + .trends__item:hover::before, + .story:hover::before, + .scrollable :not(.focusable) > .account:not(.account--minimal):hover::before, + .timeline-hint:hover::before, + .notification-request:hover::before, + .focusable:focus-within::before, + .entry:focus-within::before, + .statuses-grid__item .detailed-status:focus-within::before, + .trends__item:focus-within::before, + .story:focus-within::before, + .scrollable :not(.focusable) > .account:not(.account--minimal):focus-within::before, + .timeline-hint:focus-within::before, + .notification-request:focus-within::before { + background-color: var(--hover-color); + } + } + .status:not(.status--first-in-thread) { + border: 0; + } + .detailed-status, + .status { + padding: 16px; + } + .status__info .account__avatar, + .status__info .status__avatar { + max-width: var(--avatar-size) !important; + max-height: var(--avatar-size) !important; + } + .status__line { + left: calc(16px + (var(--avatar-size) / 2)); + } + .status__prepend + .status:not(.status-direct) { + padding-top: 5px; + } + @media (max-width: 450px) { + .status--in-thread { + --avatar-size: 34px; + } + .status--in-thread .status__info ~ * { + margin-inline-start: calc(var(--avatar-size) + 10px); + width: calc(100% - (var(--avatar-size) + 10px)); + } + } + .status__content { + text-align: unset !important; + line-height: 1.5; + } + .status__content.status__content--with-spoiler { + overflow: visible; + } + .status__content.status__content--with-spoiler > p { + margin-inline: -100px; + padding-inline: 100px; + overflow: hidden; + } + .status__content.status__content--with-spoiler > p:first-child { + margin-bottom: 0; + } + .status__content p:empty { + max-height: 0; + } + .status__content picture { + display: contents; + } + .status__content .custom-emoji { + display: inline-block; + height: var(--emoji-size) !important; + min-width: var(--emoji-size) !important; + width: auto !important; + margin: -0.2ex 0 0.2ex; + } + @media (prefers-reduced-motion: no-preference) { + .custom-emoji { + transition: transform 1s cubic-bezier(0, 0.7, 0, 1); + } + .custom-emoji:hover { + transform: scale(1.7); + transition: transform 0.4s cubic-bezier(0, 0.7, 0, 1); + } + } + .status__content ~ [style*="aspect-ratio"] { + max-height: 80vh; + } + .detailed-status__wrapper-direct .status__content, + .status-direct .status__content, + .status__wrapper-direct .status__content, + .conversation .status__content { + position: relative !important; + background: var(--elevated-color); + padding: 12px 14px; + border-radius: var(--radius-round); + border-top-left-radius: 6px; + box-sizing: border-box; + margin-bottom: 0; + overflow: hidden !important; + max-width: max-content; + } + .detailed-status__wrapper-direct .status__content .media-gallery, + .status-direct .status__content .media-gallery, + .status__wrapper-direct .status__content .media-gallery, + .conversation .status__content .media-gallery { + width: 999px; + max-width: 100% !important; + } + .status__wrapper-direct:not(.detailed-status__wrapper-direct) .status__prepend { + position: absolute; + contain: strict; + } + .detailed-status { + border: 0; + padding-bottom: 4px; + } + .detailed-status__wrapper, + .detailed-status { + box-shadow: var(--shadow); + } + .detailed-status__wrapper .status__content, + .detailed-status .status__content { + min-height: unset !important; + } + .detailed-status__wrapper { + isolation: isolate; + background: none; + } + .detailed-status__wrapper::before { + content: ""; + position: absolute; + inset: 0; + background: var(--elevated-tint) !important; + pointer-events: none; + z-index: -1; + } + .detailed-status__wrapper .detailed-status { + box-shadow: none; + } + .detailed-status__meta { + margin-top: 14px; + line-height: 2; + } + .detailed-status__meta > * { + display: inline-flex; + border: 0 !important; + padding: 0 !important; + margin-inline-end: 8px; + } + .detailed-status__meta > *:not(:last-child)::after { + content: "·"; + } + .media-gallery, + .video-player, + .status-card.horizontal.interactive, + .status-card, + .audio-player, + .picture-in-picture-placeholder { + box-shadow: var(--shadow-low); + border-radius: var(--radius) !important; + margin-block: 10px; + animation: scaleIn 0.4s; + max-width: unset !important; + } + .media-gallery:has(.spoiler-button:not(.spoiler-button--minified)) { + height: 150px !important; + aspect-ratio: unset !important; + } + .media-gallery__item { + border-radius: 0; + outline: none; + } + .spoiler-button--minified button { + padding: 6px !important; + background: rgba(0,0,0,0.2) !important; + } + .spoiler-button--minified button::after { + content: ""; + position: absolute; + inset: -50px; + } + .spoiler-button--minified button:hover { + background: rgba(0,0,0,0.4) !important; + } + .spoiler-button--minified .icon { + width: 18px; + height: 18px; + } + .status-card { + align-items: stretch; + gap: 0; + } + .status-card:not(.horizontal) { + border: 1px solid var(--border-color) !important; + } + .status-card:not(.expanded) .status-card__image { + overflow: hidden; + } + .status-card:not(.expanded) .status-card__image img { + border-radius: 0; + } + .status-card:not(.interactive) .status-card__image { + position: relative; + aspect-ratio: unset !important; + } + .status-card__content { + margin-block: auto; + padding: 15px; + } + .status-card__host { + font-size: 0.85em; + line-height: 1.5; + margin: 0; + } + .status-card__title { + font-size: 1em; + margin-top: 0.2em; + margin-bottom: 0; + line-height: 1.4; + } + .status-card__description { + line-height: 1.4 !important; + margin: 0; + } + @supports (-webkit-line-clamp: 8) { + .status-card__description { + display: -webkit-box; + -webkit-line-clamp: 8; + -webkit-box-orient: vertical; + white-space: unset; + } + } + .status-card__author { + margin-top: 0.4em; + font-size: 0.85em; + } + .status-card:hover { + background-color: var(--hover-color); + } + .more-from-author { + background: none; + border: 0; + padding-top: 0; + border-radius: var(--radius); + } + .audio-player .video-player__seek { + margin: var(--radius); + } + .hashtag-bar { + margin-top: 10px; + } + .hashtag-bar a, + .hashtag-bar button { + color: var(--accent, #8c8dff); + transition: opacity 0.2s; + padding: 5px 10px; + } + .hashtag-bar a { + position: relative; + border-radius: var(--radius-round); + background: var(--elevated-color); + } + .hashtag-bar a::after { + content: ""; + position: absolute; + inset: 0; + background: var(--elevated-color); + border-radius: inherit; + opacity: 0; + transition: opacity 0.2s; + } + .hashtag-bar a:hover, + .hashtag-bar a:focus { + opacity: 1; + } + .hashtag-bar a:hover::after, + .hashtag-bar a:focus::after { + opacity: 1; + } + .hashtag-bar button { + padding-block: 0; + } + .status__action-bar { + flex-wrap: wrap; + margin-top: 0.4em; + margin-bottom: -6px; + gap: 0; + margin-inline-start: -8px; + } + .status__action-bar__button-wrapper { + flex-grow: 1; + max-width: 55px; + min-width: max-content; + } + .status__action-bar * { + display: flex !important; + justify-content: center !important; + flex-grow: 1 !important; + } + .status__action-bar .icon-button { + margin: 0; + } + .status__action-bar .icon-button::before { + content: ""; + position: absolute; + inset: -0.5em; + } + .status__action-bar, + .detailed-status__action-bar, + .picture-in-picture__footer { + position: relative; + z-index: 2; + justify-content: unset; + } + .status__action-bar .icon-button, + .detailed-status__action-bar .icon-button, + .picture-in-picture__footer .icon-button { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.5em 0.4em !important; + border-radius: var(--radius); + position: relative; + } + .status__action-bar .icon-button .icon-button__counter, + .detailed-status__action-bar .icon-button .icon-button__counter, + .picture-in-picture__footer .icon-button .icon-button__counter { + width: auto !important; + } + .status__action-bar .icon-button--with-counter, + .detailed-status__action-bar .icon-button--with-counter, + .picture-in-picture__footer .icon-button--with-counter { + padding-inline: 0.7em !important; + } + .detailed-status__action-bar, + .picture-in-picture__footer { + padding-inline: 15px !important; + gap: 0; + } + .detailed-status__action-bar .icon-button, + .picture-in-picture__footer .icon-button { + flex-grow: 1 !important; + } + .detailed-status__action-bar div, + .picture-in-picture__footer div { + display: flex; + justify-content: center; + flex-grow: 1; + } + .item-list > article > div { + position: relative; + } + .item-list > article > div::after { + content: ""; + position: absolute; + bottom: 0px; + inset-inline: 0; + border-top: 1px solid var(--border-color); + pointer-events: none; + } + @media (min-width: 760px) { + .layout-single-column .item-list > article > div::after { + inset-inline: calc(var(--radius) + 10px); + } + } + .account__wrapper { + line-height: 1.5; + } + .account__contents { + display: flex; + flex-wrap: wrap; + flex-grow: 1; + gap: 0 10px; + } + .display-name { + margin-bottom: 1px !important; + } + .account:not(.account--minimal) .display-name__account { + display: block; + width: 300px; + } + .account__details { + font-size: 0.9em; + opacity: 0.8; + width: 100px; + flex-grow: 1; + text-align: end; + align-items: center; + line-height: 1.2; + } + .account__details:has(.verified-badge) > :not(.verified-badge) { + display: none; + } + .account__wrapper button:not(:hover):not(:focus) { + background: var(--elevated-color); + color: inherit; + } + .notification-ungrouped { + padding: 0; + } + .notification-ungrouped__header { + padding-top: 16px; + margin-bottom: 0; + } + .notification-ungrouped .status__wrapper { + margin-inline: 0; + max-width: unset; + } + .notification-ungrouped .status__wrapper::before, + .notification-ungrouped .status__wrapper::after { + content: unset; + } + .notification-ungrouped .status { + padding: 16px; + } + .notification-group { + padding: 16px; + } + .notification-group__main__additional-content { + display: none; + } + .trends__item, + .story, + .account-card { + animation: slideUpFade backwards 0.4s 0.24s cubic-bezier(0, 1, 1, 1); + border-radius: var(--radius); + } + .trends__item:nth-child(1), + .story:nth-child(1), + .account-card:nth-child(1) { + animation-delay: 0.04s; + } + .trends__item:nth-child(2), + .story:nth-child(2), + .account-card:nth-child(2) { + animation-delay: 0.08s; + } + .trends__item:nth-child(3), + .story:nth-child(3), + .account-card:nth-child(3) { + animation-delay: 0.12s; + } + .trends__item:nth-child(4), + .story:nth-child(4), + .account-card:nth-child(4) { + animation-delay: 0.16s; + } + .trends__item:nth-child(5), + .story:nth-child(5), + .account-card:nth-child(5) { + animation-delay: 0.2s; + } + .trends__item:nth-child(6), + .story:nth-child(6), + .account-card:nth-child(6) { + animation-delay: 0.24s; + } + .explore__links { + padding: 10px; + display: flex; + flex-wrap: wrap; + align-content: flex-start; + } + .explore__links > .dismissable-banner { + margin: -10px; + margin-bottom: 10px; + } + .explore__links .story { + margin-inline: 0 !important; + } + .trends__item { + display: flex !important; + margin-inline: 0 !important; + } + .trends__item__name a::before { + content: ""; + position: absolute; + inset: 0; + } + .trends__item__current { + display: none; + } + .trends__item__sparkline { + overflow: visible !important; + pointer-events: none; + } + .trends__item__sparkline svg { + overflow: visible !important; + } + .trends__item__sparkline path:first-child { + filter: blur(8px); + } + .trends__item__sparkline path:last-child { + mask: linear-gradient(to left, #000, #000, transparent); + -webkit-mask: linear-gradient(to left, #000, #000, transparent); + } + .rtl .trends__item__sparkline { + transform: scaleX(-1); + } + .explore__links .trends__item { + margin: 7.5px !important; + padding: 25px !important; + box-shadow: var(--shadow-med); + width: 200px; + background: var(--elevated-color); + flex-grow: 1; + } + .explore__links .trends__item::after { + content: unset !important; + } + .explore__links .trends__item a { + font-size: 1.4em; + line-height: 1.7em; + } + .explore__links .trends__item a::before { + content: ""; + position: absolute; + inset: 0; + } + .explore__links .trends__item .trends__item__sparkline { + height: 100%; + } + .explore__links .trends__item .trends__item__sparkline svg { + height: 100%; + float: right; + overflow: visible !important; + position: relative; + } + .explore__links .trends__item .trends__item__sparkline svg path { + display: unset !important; + transition: transform 1s; + } + .explore__links .trends__item .trends__item__sparkline svg path:first-child { + transform-origin: center; + } + .explore__links .trends__item:hover svg path:first-child, + .explore__links .trends__item:focus-within svg path:first-child { + transform: scale(2); + } + .explore__links .story { + width: 100%; + margin: 0; + } + .account__header { + overflow: visible; + } + .follow-request-banner { + margin-bottom: -100px; + padding-bottom: 120px; + } + .account__header__image { + height: 0; + padding-bottom: 35%; + border-radius: var(--panel-radius) var(--panel-radius) 0 0; + position: sticky; + top: calc(0px - var(--panel-radius)); + overflow: clip; + } + .account__header__image img { + position: absolute; + } + .account__header__image .account__header__info { + position: absolute; + z-index: 2; + } + .account__header__image .account__header__info > span { + position: sticky; + top: var(--radius); + } + .account__header__bar { + position: relative; + z-index: 2; + border: 0; + padding-inline: 20px; + border-radius: var(--radius) var(--radius) 0 0; + margin-top: calc(0px - var(--radius)) !important; + display: flex; + flex-direction: column; + background: var(--background-color); + isolation: isolate; + } + @media (max-width: 760px) { + .account__header__bar { + padding-inline: 15px; + } + } + .account__header__bar::before { + content: ""; + background: var(--elevated-tint); + position: absolute; + inset: 0; + pointer-events: none; + } + .account__header__bar::after { + content: ""; + position: absolute; + inset-inline: 0; + height: 95px; + background: inherit; + z-index: -1; + border-radius: var(--radius); + mask: linear-gradient(to bottom, transparent, #000); + } + .account__header__tabs { + overflow: visible !important; + align-items: flex-end; + padding: 0; + height: unset !important; + margin-top: -55px !important; + } + .account__header__tabs::before { + content: ""; + position: absolute; + top: -55px; + inset-inline: 0; + height: 150px; + backdrop-filter: blur(40px); + filter: brightness(1.1); + pointer-events: none; + opacity: 0.7; + z-index: -2; + clip-path: inset(55px 0 0 0 round var(--radius)); + } + .account__header__tabs ~ div { + z-index: 2; + } + .account__header__bar .avatar { + margin-inline-start: 0 !important; + overflow: visible !important; + position: relative; + margin-top: 20px; + } + .account__header__bar .avatar .account-role { + position: absolute; + bottom: 0; + left: 110%; + border-radius: var(--radius); + } + .account__header__bar .account__avatar { + border: 0; + box-shadow: var(--shadow); + background: none; + background-size: cover !important; + } + .account__header__tabs__name { + margin-bottom: 0; + padding: 0; + margin-top: 16px; + } + .account__header__tabs__name h1 { + display: flex; + flex-wrap: wrap; + white-space: unset; + gap: 0 0.4em; + font-weight: 600; + } + .account__header__extra { + margin-top: 8px; + } + .account__header__fields, + .account__header__account-note { + display: flex; + flex-wrap: wrap; + gap: 2px; + background: none; + border-radius: var(--radius) !important; + overflow: hidden; + max-width: max-content; + border: 0 !important; + } + .account__header__fields dl { + display: inline; + border-radius: 0; + overflow: hidden; + border: 0 !important; + padding: 8px 12px !important; + margin: 0 !important; + position: relative; + overflow: hidden; + flex-grow: 1; + } + .account__header__fields dl:not(.verified) { + background-color: var(--elevated-color); + } + .account__header__fields dl dt { + all: unset !important; + color: unset !important; + opacity: 0.9 !important; + } + .account__header__fields dl dd { + padding: 0; + white-space: unset; + max-height: unset; + text-align: unset; + } + .account__header__fields dl dd > span > a:first-child:last-child::after, + .account__header__fields dl dd .h-card:first-child:last-child a::after { + content: ""; + position: absolute; + inset: 0; + background-color: var(--hover-color); + opacity: 0; + transition: opacity 0.2s; + } + .account__header__fields dl dd > span > a:first-child:last-child:hover:after, + .account__header__fields dl dd .h-card:first-child:last-child a:hover:after, + .account__header__fields dl dd > span > a:first-child:last-child:focus:after, + .account__header__fields dl dd .h-card:first-child:last-child a:focus:after { + opacity: 1; + } + .account__header__fields dl dd.verified { + overflow: visible !important; + border: 0; + background: none; + } + .account__header__fields dl dd.verified a::before, + .account__header__fields dl dd.verified a::after { + content: ""; + position: absolute; + inset: 0; + background: currentcolor; + opacity: 0.2; + } + .account__header__fields dl dd.verified a::after { + background: linear-gradient(20deg, currentcolor, transparent) !important; + opacity: 0.2 !important; + z-index: -2; + } + .account__header__account-note { + position: relative; + font-size: 0.9em; + max-width: unset; + padding: 0.5em 10px !important; + margin-block: -5px 10px; + margin-inline: -10px !important; + border-radius: var(--radius) !important; + } + .account__header__account-note::after { + content: ""; + position: absolute; + bottom: 0; + inset-inline: 10px; + border-top: 1px solid var(--border-color); + transition: opacity 0.2s; + } + .account__header__account-note:focus-within::after { + opacity: 0; + } + .account__header__account-note label { + z-index: 2; + margin: 0; + pointer-events: none; + font-size: inherit; + } + .account__header__account-note textarea { + margin: -100px !important; + padding: 100px !important; + padding-inline-end: 0.7em !important; + margin-inline-end: -0.7em !important; + box-sizing: content-box; + width: 100%; + font-size: inherit; + transition: background 0.2s; + } + .account__header__account-note textarea::placeholder { + font-weight: 600; + } + .account-gallery__container { + border-radius: var(--radius); + overflow: clip; + padding: 0; + margin: 4px; + gap: 4px; + } + .account-authorize__wrapper { + background: var(--elevated-color); + border-radius: var(--radius); + overflow: hidden; + flex-grow: 1; + margin: 10px; + display: flex; + flex-direction: column; + } + @media (max-width: 760px) { + .account-authorize__wrapper { + margin-inline: 10px; + } + } + .layout-multiple-columns .account-authorize__wrapper { + margin-inline: 10px; + } + .account-authorize__wrapper .account-authorize { + padding: 20px 15px 10px; + } + .account-authorize__wrapper .detailed-status__display-name { + margin-bottom: 10px !important; + } + .account-authorize__wrapper .account--panel { + margin-top: auto; + border-bottom: 0; + padding-inline: 15px; + gap: 10px; + background: none; + } + .account-authorize__wrapper br { + display: block; + } + .account-authorize__wrapper p { + margin-bottom: 0.2em; + } + .account-authorize__wrapper .account--panel__button:first-child .icon-button:not(:hover):not(:focus) { + background: var(--elevated-color); + } + .account-authorize__wrapper .icon-button { + width: 100% !important; + padding: 10px; + height: unset !important; + } + .about__meta { + border-radius: var(--radius); + } + .account--minimal { + max-width: 100%; + } + .about__section { + margin: 30px -20px; + padding-inline: 20px; + contain: inline-size paint; + } + .about__section.active .about__section__title { + margin-inline: -20px; + border-radius: 0; + border-inline: 0; + border-bottom: 0; + } + .about__section__title { + position: sticky; + top: -1px; + z-index: 2; + background: var(--background-color-tint); + border: 1px solid var(--border-color); + border-radius: var(--radius); + overflow: hidden; + transition: margin 0.2s cubic-bezier(0, 1, 0, 1), border-radius 0.2s; + } + .about__section__title::after { + content: ""; + position: absolute; + inset: 0; + background: var(--elevated-tint); + backdrop-filter: blur(10px); + z-index: -1; + } + .about__section__body { + border: 0; + padding: 0; + animation: slideDownFade 0.4s 0.1s backwards cubic-bezier(0, 1, 0, 1.2); + } + .explore__search-results { + border: 0; + } + .search-results__section__header { + margin: 0px 0px 10px; + color: unset; + background: none; + padding-inline: 25px; + font-weight: 600; + } + .search-results__section { + border: 0; + margin-bottom: 20px; + } + .admin-wrapper .content__heading { + margin-bottom: 2em; + } + .admin-wrapper h4 { + margin: 0; + border-bottom: 0; + } + .admin-wrapper form > h4 { + margin-top: 2em !important; + border-bottom: 0 !important; + margin-bottom: 0 !important; + } + .admin-wrapper .lead { + margin-bottom: 15px; + } + .admin-wrapper .flash-message, + .admin-wrapper .applications-list__item, + .admin-wrapper .filters-list__item { + border-radius: var(--radius); + border: 0; + overflow: clip; + } + .admin-wrapper .fields-row { + margin-inline: 0; + border-radius: var(--radius); + overflow: clip; + padding-top: 0; + gap: 2px; + display: flex; + flex-wrap: wrap; + } + .admin-wrapper .fields-group:not(.fields-row__column), + .admin-wrapper .fields-row { + margin-bottom: 1em !important; + } + .admin-wrapper .fields-row > .fields-row__column { + max-width: unset; + width: 300px; + border-radius: 0 !important; + display: flex; + flex-direction: column; + flex-grow: 1; + margin: 0 !important; + } + .admin-wrapper .fields-row > .fields-row__column .fields-group { + border-radius: 0 !important; + margin: 0 !important; + } + .admin-wrapper .fields-row > .fields-row__column .with_block_label { + display: flex; + flex-direction: column; + height: 100%; + } + .admin-wrapper .fields-row > .fields-row__column .with_block_label > .label_input { + display: flex; + flex-direction: column; + flex-grow: 1; + } + .admin-wrapper .fields-row > .fields-row__column .with_block_label > .label_input > textarea { + min-height: 300px; + flex-grow: 1; + } + .admin-wrapper .fields-row > .fields-row__column > :last-child { + flex-grow: 1; + align-items: flex-start; + border: 0; + } + .admin-wrapper .fields-row > .fields-row__column > :not(:first-child):not(:last-child) { + padding-block: 0.5em !important; + margin-block: -3px; + } + .admin-wrapper :not(.fields-row__column) > .fields-group, + .admin-wrapper .fields-row > *, + .admin-wrapper .label_input > ul, + .admin-wrapper .label_input__wrapper > ul, + .admin-wrapper .with_block_label.radio_buttons .label_input { + border-radius: var(--radius); + overflow: hidden; + padding: 0; + display: flex; + flex-direction: column; + gap: 2px; + } + .admin-wrapper :not(.fields-row__column) > .fields-group > *, + .admin-wrapper .fields-row > * > *, + .admin-wrapper .label_input > ul > *, + .admin-wrapper .label_input__wrapper > ul > *, + .admin-wrapper .with_block_label.radio_buttons .label_input > * { + background-color: var(--elevated-color); + padding: 0.8rem; + margin-block: 0px; + position: relative; + border-radius: 0 !important; + overflow: hidden; + } + .admin-wrapper :not(.fields-row__column) > .fields-group > *::after, + .admin-wrapper .fields-row > * > *::after, + .admin-wrapper .label_input > ul > *::after, + .admin-wrapper .label_input__wrapper > ul > *::after, + .admin-wrapper .with_block_label.radio_buttons .label_input > *::after { + content: ""; + position: absolute; + inset: 0; + background-color: var(--hover-color); + z-index: -1; + opacity: 0; + transition: opacity 0.2s; + } + .admin-wrapper :not(.fields-row__column) > .fields-group > *:hover::after, + .admin-wrapper .fields-row > * > *:hover::after, + .admin-wrapper .label_input > ul > *:hover::after, + .admin-wrapper .label_input__wrapper > ul > *:hover::after, + .admin-wrapper .with_block_label.radio_buttons .label_input > *:hover::after, + .admin-wrapper :not(.fields-row__column) > .fields-group > *:focus-within::after, + .admin-wrapper .fields-row > * > *:focus-within::after, + .admin-wrapper .label_input > ul > *:focus-within::after, + .admin-wrapper .label_input__wrapper > ul > *:focus-within::after, + .admin-wrapper .with_block_label.radio_buttons .label_input > *:focus-within::after { + opacity: 1; + } + .admin-wrapper :not(.fields-row__column) > .fields-group :not(.input.with_block_label) > label::before, + .admin-wrapper .fields-row > * :not(.input.with_block_label) > label::before, + .admin-wrapper .label_input > ul :not(.input.with_block_label) > label::before, + .admin-wrapper .label_input__wrapper > ul :not(.input.with_block_label) > label::before, + .admin-wrapper .with_block_label.radio_buttons .label_input :not(.input.with_block_label) > label::before { + content: ""; + position: absolute; + inset: -900px; + } + .admin-wrapper .label_input__wrapper > :not([type="checkbox"]):not(label) { + margin-top: 4px; + } + .admin-wrapper .label_input { + position: relative; + } + .admin-wrapper label { + margin: 0 !important; + display: flex; + align-items: center; + padding: 0 !important; + } + .admin-wrapper label input { + margin: 0; + margin-inline-end: 10px !important; + position: static !important; + } + .admin-wrapper input, + .admin-wrapper .select { + border-radius: var(--radius) !important; + z-index: 2; + } + .admin-wrapper .radio { + flex-grow: 1; + } + .admin-wrapper .radio:not(:last-child) { + margin-bottom: 0 !important; + } + .admin-wrapper .hint:last-child { + margin-bottom: 0 !important; + } + .admin-wrapper .input.with_block_label > .row { + flex-wrap: wrap; + margin: 0; + } + .admin-wrapper .input.with_block_label > .row > .string { + padding: 0; + width: 100%; + margin: 0; + } + .admin-wrapper .input.with_block_label > .row > .string:first-child input { + border-radius: var(--radius) var(--radius) 0 0 !important; + } + .admin-wrapper .input.with_block_label > .row > .string:last-child input { + border-radius: 0 0 var(--radius) var(--radius) !important; + } + .admin-wrapper .input.with_block_label > .row:not(:last-child) { + margin-bottom: 8px; + } + .admin-wrapper li.checkbox { + flex-grow: 1; + overflow: hidden; + } + .admin-wrapper ul { + flex-direction: row !important; + flex-wrap: wrap; + gap: 2px; + flex-grow: 1; + } + .admin-wrapper li.checkbox { + flex-basis: 45%; + } + .admin-wrapper .spacer { + border-top: 1px solid var(--border-color) !important; + } + .batch-table label { + padding-inline-start: 20px !important; + } + .batch-table, + .table, + :not(.batch-table__row__content) > table { + overflow: clip; + border-radius: var(--radius); + border-spacing: 0 2px; + border-collapse: separate; + } + .batch-table__toolbar, + .batch-table__row, + .batch-table tr > *, + .table tr > *, + :not(.batch-table__row__content) > table tr > * { + border: 0; + margin-bottom: 2px !important; + } + .batch-table td, + .table td, + :not(.batch-table__row__content) > table td, + .batch-table th, + .table th, + :not(.batch-table__row__content) > table th, + .batch-table__row { + position: relative; + } + .batch-table tr > td > div > span, + .table tr > td > div > span, + :not(.batch-table__row__content) > table tr > td > div > span, + .batch-table tr > th > div > span, + .table tr > th > div > span, + :not(.batch-table__row__content) > table tr > th > div > span { + padding-inline: 0.7em; + display: inline-block; + } + .keyboard-shortcuts { + padding: 0; + margin-top: -4px; + } + .keyboard-shortcuts table { + width: 100%; + border-radius: 0; + } + .keyboard-shortcuts td { + padding: 0.7em; + } + .batch-table__row, + .batch-table th, + .table th, + :not(.batch-table__row__content) > table th, + .batch-table > tbody > tr > td, + .table > tbody > tr > td, + :not(.batch-table__row__content) > table > tbody > tr > td, + .batch-table tfoot td, + .table tfoot td, + :not(.batch-table__row__content) > table tfoot td { + background: var(--elevated-color) !important; + vertical-align: middle; + } + .batch-table__row::after, + .batch-table th::after, + .table th::after, + :not(.batch-table__row__content) > table th::after, + .batch-table > tbody > tr > td::after, + .table > tbody > tr > td::after, + :not(.batch-table__row__content) > table > tbody > tr > td::after, + .batch-table tfoot td::after, + .table tfoot td::after, + :not(.batch-table__row__content) > table tfoot td::after { + content: ""; + position: absolute; + inset: 0 0; + background: var(--hover-color); + opacity: 0; + transition: 0.2s; + pointer-events: none; + } + .batch-table__row:hover::after, + .batch-table th:hover::after, + .table th:hover::after, + :not(.batch-table__row__content) > table th:hover::after, + .batch-table > tbody > tr > td:hover::after, + .table > tbody > tr > td:hover::after, + :not(.batch-table__row__content) > table > tbody > tr > td:hover::after, + .batch-table tfoot td:hover::after, + .table tfoot td:hover::after, + :not(.batch-table__row__content) > table tfoot td:hover::after, + .batch-table__row:focus-within::after, + .batch-table th:focus-within::after, + .table th:focus-within::after, + :not(.batch-table__row__content) > table th:focus-within::after, + .batch-table > tbody > tr > td:focus-within::after, + .table > tbody > tr > td:focus-within::after, + :not(.batch-table__row__content) > table > tbody > tr > td:focus-within::after, + .batch-table tfoot td:focus-within::after, + .table tfoot td:focus-within::after, + :not(.batch-table__row__content) > table tfoot td:focus-within::after { + opacity: 1; + } + .batch-table__row > a:first-child:last-child, + .batch-table th > a:first-child:last-child, + .table th > a:first-child:last-child, + :not(.batch-table__row__content) > table th > a:first-child:last-child, + .batch-table > tbody > tr > td > a:first-child:last-child, + .table > tbody > tr > td > a:first-child:last-child, + :not(.batch-table__row__content) > table > tbody > tr > td > a:first-child:last-child, + .batch-table tfoot td > a:first-child:last-child, + .table tfoot td > a:first-child:last-child, + :not(.batch-table__row__content) > table tfoot td > a:first-child:last-child { + margin: 0; + width: 100%; + padding: 0.5em; + } + .batch-table th:hover td:not([rowspan])::after, + .table th:hover td:not([rowspan])::after, + :not(.batch-table__row__content) > table th:hover td:not([rowspan])::after, + .batch-table tr:hover td:not([rowspan])::after, + .table tr:hover td:not([rowspan])::after, + :not(.batch-table__row__content) > table tr:hover td:not([rowspan])::after, + .batch-table th:hover th:not([rowspan])::after, + .table th:hover th:not([rowspan])::after, + :not(.batch-table__row__content) > table th:hover th:not([rowspan])::after, + .batch-table tr:hover th:not([rowspan])::after, + .table tr:hover th:not([rowspan])::after, + :not(.batch-table__row__content) > table tr:hover th:not([rowspan])::after { + opacity: 1 !important; + } + .batch-table th [rowspan]:hover ~ td::after, + .table th [rowspan]:hover ~ td::after, + :not(.batch-table__row__content) > table th [rowspan]:hover ~ td::after, + .batch-table tr [rowspan]:hover ~ td::after, + .table tr [rowspan]:hover ~ td::after, + :not(.batch-table__row__content) > table tr [rowspan]:hover ~ td::after { + opacity: 0 !important; + } + .batch-table th [rowspan]::after, + .table th [rowspan]::after, + :not(.batch-table__row__content) > table th [rowspan]::after, + .batch-table tr [rowspan]::after, + .table tr [rowspan]::after, + :not(.batch-table__row__content) > table tr [rowspan]::after { + inset-inline: -900px; + } + .layout-multiple-columns.layout-multiple-columns { + --column-header-height: 45px; + } + .layout-multiple-columns.layout-multiple-columns .column-header, + .layout-multiple-columns.layout-multiple-columns .column-header button { + background: none; + } + .layout-multiple-columns.layout-multiple-columns .column-header, + .layout-multiple-columns.layout-multiple-columns .scrollable, + .layout-multiple-columns.layout-multiple-columns .column-back-button, + .layout-multiple-columns.layout-multiple-columns .account__header__image { + border-radius: 0 !important; + gap: 0 !important; + } + .layout-multiple-columns.layout-multiple-columns .columns-area { + background: none !important; + height: 100%; + } + .layout-multiple-columns.layout-multiple-columns .columns-area > div { + border: 0 !important; + padding: 0 !important; + } + .layout-multiple-columns.layout-multiple-columns .columns-area > div:not(.drawer):not(:last-child) { + margin-inline-end: 2px !important; + } + .layout-multiple-columns.layout-multiple-columns .columns-area > div.column { + flex-grow: 1; + max-width: 600px; + } + .layout-multiple-columns.layout-multiple-columns .columns-area > div:first-child { + margin-inline-start: auto !important; + } + .layout-multiple-columns.layout-multiple-columns .columns-area > div:last-child { + margin-inline-end: auto !important; + } + .layout-multiple-columns.layout-multiple-columns .drawer.drawer { + padding-top: 15px !important; + overflow: clip; + flex-grow: 1; + max-width: 350px; + } + .drawer__header { + border-radius: var(--radius-round); + background: var(--elevated-color); + margin-inline: 15px; + overflow: hidden; + border: 0 !important; + } + .drawer__header a { + border: 0; + } + .drawer__header a:first-child { + padding-inline-start: 15px !important; + } + .drawer__header a:last-child { + padding-inline-end: 15px !important; + } + .layout-multiple-columns.layout-multiple-columns .drawer.drawer .search { + z-index: 2; + margin-inline: 15px; + margin-bottom: 0; + } + .layout-multiple-columns.layout-multiple-columns .drawer.drawer > .drawer__pager { + border: 0; + overflow: visible !important; + } + .layout-multiple-columns.layout-multiple-columns .drawer.drawer .drawer__inner:not(.darker) { + margin-top: -20px; + padding-top: 30px; + height: unset; + bottom: 0; + } + .layout-multiple-columns.layout-multiple-columns .drawer.drawer .drawer__inner__mastodon { + margin-inline: -6px; + z-index: -1; + } + .layout-multiple-columns.layout-multiple-columns .compose-form { + margin-inline: 5px; + } + .layout-multiple-columns.layout-multiple-columns .drawer__inner:not(.darker), + .layout-multiple-columns.layout-multiple-columns .drawer__inner__mastodon { + background-color: transparent !important; + } + .layout-multiple-columns.layout-multiple-columns .darker { + background-color: var(--surface-background-color); + border-radius: var(--radius) var(--radius) 0 0; + top: 10px; + width: unset; + inset-inline: 2px; + } + .layout-multiple-columns.layout-multiple-columns .column { + background: none; + } + .layout-multiple-columns.layout-multiple-columns .column::after { + content: ""; + position: absolute; + inset: 0; + top: var(--column-header-height); + background: var(--background-color); + z-index: -1; + } + .layout-multiple-columns.layout-multiple-columns .column::before, + .layout-multiple-columns.layout-multiple-columns .column::after { + top: var(--column-header-height); + border-radius: var(--radius) var(--radius) 0 0; + } + .layout-multiple-columns.layout-multiple-columns .column-back-button.active, + .layout-multiple-columns.layout-multiple-columns .column-header__wrapper.active { + box-shadow: none; + } + .layout-multiple-columns.layout-multiple-columns .column-back-button.active::before, + .layout-multiple-columns.layout-multiple-columns .column-header__wrapper.active::before { + inset-inline: var(--radius); + } + .layout-multiple-columns.layout-multiple-columns .column-back-button .column-header, + .layout-multiple-columns.layout-multiple-columns .column-header__wrapper .column-header { + border: 0 !important; + height: var(--column-header-height); + } + .layout-multiple-columns.layout-multiple-columns .column-back-button .column-header__buttons, + .layout-multiple-columns.layout-multiple-columns .column-header__wrapper .column-header__buttons { + height: 100%; + } + .layout-multiple-columns.layout-multiple-columns .column-back-button svg, + .layout-multiple-columns.layout-multiple-columns .column-header__wrapper svg { + height: 1.4em; + } + .layout-multiple-columns.layout-multiple-columns .column-back-button + .scrollable.scrollable, + .layout-multiple-columns.layout-multiple-columns .column-header__wrapper + .scrollable.scrollable { + border-radius: var(--radius) var(--radius) 0 0 !important; + overflow-y: scroll; + } + .layout-multiple-columns.layout-multiple-columns .getting-started__trends { + padding: 0px 20px; + } + .column[aria-labelledby="Misc"] > .scrollable, + .column[aria-labelledby="Getting-started"] > .scrollable, + .getting-started { + position: relative; + padding: 5px 10px !important; + } + .column[aria-labelledby="Misc"] > .scrollable .getting-started__wrapper, + .column[aria-labelledby="Getting-started"] > .scrollable .getting-started__wrapper, + .getting-started .getting-started__wrapper { + background: none; + } + .column[aria-labelledby="Misc"] > .scrollable .column-link, + .column[aria-labelledby="Getting-started"] > .scrollable .column-link, + .getting-started .column-link, + .column[aria-labelledby="Misc"] > .scrollable .column-subheading, + .column[aria-labelledby="Getting-started"] > .scrollable .column-subheading, + .getting-started .column-subheading { + border: 0 !important; + padding: 20px !important; + background: none; + } + .column[aria-labelledby="Misc"] > .scrollable .getting-started__footer, + .column[aria-labelledby="Getting-started"] > .scrollable .getting-started__footer, + .getting-started .getting-started__footer { + padding-inline: 20px; + } + .column[aria-labelledby="Misc"] > .scrollable .getting-started__footer a span, + .column[aria-labelledby="Getting-started"] > .scrollable .getting-started__footer a span, + .getting-started .getting-started__footer a span { + font-size: 1.1em !important; + line-height: 2; + } + @media (min-width: 760px) and (max-width: 1175px) { + .columns-area__panels__pane--navigational { + margin-top: 50px; + } + .navigation-panel__menu { + padding: 10px; + } + .navigation-panel__compose-button { + margin-block: 10px; + margin-inline: 6px; + padding-inline: 16px; + border-radius: 100px; + justify-content: center; + } + .navigation-panel__sign-in-banner { + display: block !important; + margin-block: 10px; + padding-block: 10px; + border-block: 1px solid var(--border-color); + } + } + @media (max-width: 759px) { + .tabs-bar__wrapper::before, + .ui__navigation-bar::before { + content: ""; + position: absolute; + inset: 0; + background: var(--elevated-color); + z-index: -1; + } + .ui__navigation-bar { + color: var(--on-input-color); + height: 70px; + padding-inline: 5px; + } + .ui__navigation-bar__item { + position: relative; + border: 0 !important; + padding-block: 12px; + gap: 6px; + opacity: 0.7; + } + .ui__navigation-bar__item::before { + content: ""; + position: absolute; + width: 60px; + top: 10px; + bottom: 30px; + background: currentColor; + border-radius: 100px; + z-index: -1; + opacity: 0; + transform: scaleX(0.8); + transition: opacity 0.2s, transform 0.2s; + } + .ui__navigation-bar__item::after { + content: attr(aria-label); + font-size: 12px; + max-width: 100%; + padding-inline: 4px; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + box-sizing: border-box; + } + .ui__navigation-bar__item.active { + opacity: 1; + } + .ui__navigation-bar__item.active::before { + opacity: 0.15; + transform: none; + } + .columns-area__panels__pane--navigational { + z-index: 100; + padding: 40px 10px; + padding-top: 60vh; + min-width: unset; + overflow-y: scroll; + box-sizing: border-box; + overscroll-behavior: contain; + visibility: hidden; + transition: visibility 0s 0.2s; + } + .columns-area__panels__pane--navigational .columns-area__panels__pane__inner { + width: 100%; + position: relative; + inset: unset; + height: max-content; + border-radius: 24px; + margin-top: auto; + transform: translateY(100vh) !important; + transition: transform 0.2s cubic-bezier(0, 0, 1, 0), opacity 1s; + } + .columns-area__panels__pane--navigational .navigation-panel__compose-button { + display: none; + } + html:not(:has(.sign-in-banner)) .columns-area__panels__pane--navigational [href="/home"], + html:not(:has(.sign-in-banner)) .columns-area__panels__pane--navigational [href="/explore"], + html:not(:has(.sign-in-banner)) .columns-area__panels__pane--navigational [href="/notifications"] { + display: none; + } + .columns-area__panels__pane--navigational .navigation-panel { + display: contents; + } + .columns-area__panels__pane--navigational .navigation-panel__menu { + padding: 10px 5px !important; + } + .columns-area__panels__pane--navigational.columns-area__panels__pane--overlay { + visibility: visible; + transition: none; + animation: slideUpFadeBig 0.3s cubic-bezier(0, 0.9, 0, 1.05) forwards; + } + .columns-area__panels__pane--navigational.columns-area__panels__pane--overlay .columns-area__panels__pane__inner { + transform: none !important; + transition: none; + } + } + #hover-card, + .dropdown-menu { + border-radius: var(--radius); + animation: scaleIn 0.2s cubic-bezier(0, 0, 0, 1.1); + } + .dropdown-menu__container__list { + overflow: hidden auto; + border-radius: var(--radius); + max-height: 70vh; + } + .dropdown-menu__item { + overflow: hidden; + } + .dropdown-menu__item a { + padding: 0.7em 1em !important; + transition: background-color 0.2s, color 0.2s; + min-width: 150px; + } + .dropdown-menu__separator { + margin: 0 !important; + } + .interaction-modal { + border-radius: var(--radius); + overflow-y: auto; + box-sizing: border-box; + width: 700px; + text-align: center; + } + .interaction-modal__choices { + gap: 10px; + display: flex; + flex-wrap: wrap; + } + .interaction-modal__choices .interaction-modal__choices__choice { + max-height: 50vh; + overflow-y: auto; + border: 1px solid var(--border-color); + padding: 24px; + margin: 0; + border-radius: var(--radius); + transition: background 0.2s; + position: relative; + } + .interaction-modal__choices .prose:last-child { + margin-bottom: 0; + } + .interaction-modal__choices h3 { + margin-bottom: 10px; + } + .modal-root__container { + animation: bounceIn 0.7s; + } + @media (max-width: 760px) { + .modal-root__modal { + margin-top: auto; + max-width: 100%; + border-radius: var(--radius) var(--radius) 0 0; + } + } + .picture-in-picture { + z-index: 101; + } + .picture-in-picture .picture-in-picture__header { + border-radius: var(--radius) var(--radius) 0 0; + } + .picture-in-picture .media-gallery, + .picture-in-picture .video-player, + .picture-in-picture .status-card.horizontal.interactive, + .picture-in-picture .status-card, + .picture-in-picture .audio-player, + .picture-in-picture .picture-in-picture-placeholder { + --radius: 0; + margin: 0 !important; + } + .picture-in-picture .picture-in-picture__footer { + border-radius: 0 0 var(--radius) var(--radius); + } + .modal-root__modal:has(.focal-point) { + width: unset; + max-width: 90vw; + } + .modal-root__modal:has(.focal-point) .dialog-modal__content { + overflow: hidden; + display: flex; + flex-direction: column; + } + .modal-root__modal:has(.focal-point) .dialog-modal__content__preview { + padding: 0 !important; + min-height: 0; + max-width: 100%; + } + .modal-root__modal:has(.focal-point) .dialog-modal__content__preview img { + max-width: 100% !important; + max-height: 100% !important; + border-radius: 0; + max-height: unset; + min-height: 0; + } + .modal-root__modal:has(.focal-point) .focal-point__reticle { + transition: box-shadow 0.2s; + } + .modal-root__modal:has(.focal-point) .focal-point { + min-height: 0 !important; + } + .modal-root__modal:has(.focal-point) .focal-point:not(:hover) .focal-point__reticle { + box-shadow: none; + } + .emoji-picker-dropdown__menu { + border-radius: var(--radius); + overflow: hidden; + resize: both; + width: 400px; + } + .emoji-mart { + display: flex !important; + flex-direction: column !important; + width: 100% !important; + height: 100% !important; + } + .emoji-mart-scroll { + flex-grow: 1; + max-height: unset !important; + } + .emoji-mart-bar { + order: 2; + } + .emoji-mart-category-list { + overflow: visible !important; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(calc(20px + 6%), 1fr)); + } + .emoji-mart-category-list li { + display: contents; + } + .emoji-mart-category-list button { + position: relative; + padding: 0 !important; + padding-top: 100% !important; + } + .emoji-mart-category-list button img, + .emoji-mart-category-list button span { + height: calc(100% - 10px) !important; + width: calc(100% - 10px) !important; + inset: 5px; + position: absolute !important; + transition: transform 0.1s cubic-bezier(0, 0, 0, 1); + } + .emoji-mart-category-list button:hover img, + .emoji-mart-category-list button:hover span { + transform: scale(1.2); + } + .emoji-picker-dropdown__modifiers { + top: 16px; + } \ No newline at end of file diff --git a/app/lib/activitypub/parser/media_attachment_parser.rb b/app/lib/activitypub/parser/media_attachment_parser.rb index 56b8b23f84..bcbf92214f 100644 --- a/app/lib/activitypub/parser/media_attachment_parser.rb +++ b/app/lib/activitypub/parser/media_attachment_parser.rb @@ -15,13 +15,15 @@ class ActivityPub::Parser::MediaAttachmentParser end def remote_url - Addressable::URI.parse(@json['url'])&.normalize&.to_s + url = Addressable::URI.parse(@json['url'])&.normalize&.to_s + url unless unsupported_uri_scheme?(url) rescue Addressable::URI::InvalidURIError nil end def thumbnail_remote_url - Addressable::URI.parse(@json['icon'].is_a?(Hash) ? @json['icon']['url'] : @json['icon'])&.normalize&.to_s + url = Addressable::URI.parse(@json['icon'].is_a?(Hash) ? @json['icon']['url'] : @json['icon'])&.normalize&.to_s + url unless unsupported_uri_scheme?(url) rescue Addressable::URI::InvalidURIError nil end diff --git a/app/lib/activitypub/parser/status_parser.rb b/app/lib/activitypub/parser/status_parser.rb index 1968f18468..03e3f789b5 100644 --- a/app/lib/activitypub/parser/status_parser.rb +++ b/app/lib/activitypub/parser/status_parser.rb @@ -33,7 +33,10 @@ class ActivityPub::Parser::StatusParser end def url - url_to_href(@object['url'], 'text/html') if @object['url'].present? + return if @object['url'].blank? + + url = url_to_href(@object['url'], 'text/html') + url unless unsupported_uri_scheme?(url) end def text diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb index 3ead162ec3..99d85a262a 100644 --- a/app/lib/activitypub/tag_manager.rb +++ b/app/lib/activitypub/tag_manager.rb @@ -4,6 +4,7 @@ require 'singleton' class ActivityPub::TagManager include Singleton + include JsonLdHelper include RoutingHelper CONTEXT = 'https://www.w3.org/ns/activitystreams' @@ -17,7 +18,7 @@ class ActivityPub::TagManager end def url_for(target) - return target.url if target.respond_to?(:local?) && !target.local? + return unsupported_uri_scheme?(target.url) ? nil : target.url if target.respond_to?(:local?) && !target.local? return unless target.respond_to?(:object_type) diff --git a/app/lib/http_signature_draft.rb b/app/lib/http_signature_draft.rb index fc0d498b29..cb794b223a 100644 --- a/app/lib/http_signature_draft.rb +++ b/app/lib/http_signature_draft.rb @@ -6,14 +6,13 @@ class HttpSignatureDraft REQUEST_TARGET = '(request-target)' - def initialize(keypair, key_id, full_path: true) + def initialize(keypair, key_id) @keypair = keypair @key_id = key_id - @full_path = full_path end def request_target(verb, url) - if url.query.nil? || !@full_path + if url.query.nil? "#{verb} #{url.path}" else "#{verb} #{url.path}?#{url.query}" diff --git a/app/lib/request.rb b/app/lib/request.rb index ad39f928db..212acf64d0 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -75,7 +75,6 @@ class Request @url = Addressable::URI.parse(url).normalize @http_client = options.delete(:http_client) @allow_local = options.delete(:allow_local) - @full_path = !options.delete(:omit_query_string) @options = { follow: { max_hops: 3, @@ -102,7 +101,7 @@ class Request key_id = ActivityPub::TagManager.instance.key_uri_for(actor) keypair = sign_with.present? ? OpenSSL::PKey::RSA.new(sign_with) : actor.keypair - @signing = HttpSignatureDraft.new(keypair, key_id, full_path: @full_path) + @signing = HttpSignatureDraft.new(keypair, key_id) self end diff --git a/app/services/activitypub/fetch_replies_service.rb b/app/services/activitypub/fetch_replies_service.rb index f2e4f45104..6a6d9e391a 100644 --- a/app/services/activitypub/fetch_replies_service.rb +++ b/app/services/activitypub/fetch_replies_service.rb @@ -57,20 +57,7 @@ class ActivityPub::FetchRepliesService < BaseService return unless @allow_synchronous_requests return if non_matching_uri_hosts?(@reference_uri, collection_or_uri) - # NOTE: For backward compatibility reasons, Mastodon signs outgoing - # queries incorrectly by default. - # - # While this is relevant for all URLs with query strings, this is - # the only code path where this happens in practice. - # - # Therefore, retry with correct signatures if this fails. - begin - fetch_resource_without_id_validation(collection_or_uri, nil, raise_on_error: :temporary) - rescue Mastodon::UnexpectedResponseError => e - raise unless e.response && e.response.code == 401 && Addressable::URI.parse(collection_or_uri).query.present? - - fetch_resource_without_id_validation(collection_or_uri, nil, raise_on_error: :temporary, request_options: { omit_query_string: false }) - end + fetch_resource_without_id_validation(collection_or_uri, nil, raise_on_error: :temporary) end def filter_replies(items) diff --git a/app/validators/status_length_validator.rb b/app/validators/status_length_validator.rb index 575aaf1869..d77a0ac610 100644 --- a/app/validators/status_length_validator.rb +++ b/app/validators/status_length_validator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class StatusLengthValidator < ActiveModel::Validator - MAX_CHARS = 500 + MAX_CHARS = (ENV['MAX_CHARS'] || 500).to_i URL_PLACEHOLDER_CHARS = 23 URL_PLACEHOLDER = 'x' * 23 diff --git a/config/initializers/deprecations.rb b/config/initializers/deprecations.rb new file mode 100644 index 0000000000..e0ad54d8c3 --- /dev/null +++ b/config/initializers/deprecations.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +if ENV['REDIS_NAMESPACE'] + es_configured = ENV['ES_ENABLED'] == 'true' || ENV.fetch('ES_HOST', 'localhost') != 'localhost' || ENV.fetch('ES_PORT', '9200') != '9200' || ENV.fetch('ES_PASS', 'password') != 'password' + + warn <<~MESSAGE + WARNING: the REDIS_NAMESPACE environment variable is deprecated and will be removed in Mastodon 4.4.0. + + Please see documentation at https://github.com/mastodon/redis_namespace_migration + MESSAGE + + warn <<~MESSAGE if es_configured && !ENV['ES_PREFIX'] + + In addition, as REDIS_NAMESPACE is being used as a prefix for Elasticsearch, please do not forget to set ES_PREFIX to "#{ENV.fetch('REDIS_NAMESPACE')}". + MESSAGE +end diff --git a/config/locales/en.yml b/config/locales/en.yml index fe8786cebe..66148cfefe 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -2280,7 +2280,10 @@ en: themes: contrast: Mastodon (High contrast) default: Mastodon (Dark) - full-dark: フルダーク + modern-dark: Modern Dark + modern-light: Modern Light + modern-contrast: Modern Contrast + full-dark: Full Dark mastodon-light: Mastodon (Light) system: Automatic (use system theme) time: diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 9264216121..ff17536ee1 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -66,18 +66,18 @@ en: setting_custom_css_lead: 'Be sure to remember: In the unlikely event that you make a mistake in entering your custom CSS and the screen does not display properly, you can disable your custom CSS from the link at the bottom of the sign-in screen. Open the sign-in screen in private mode of your browser, for example, and disable it.' setting_default_searchability: On kmyblue and Fedibird, the search is based on the search permission setting; on Misskey, all public, local public, and non-public posts are searched regardless of this setting; on Mastodon and Firefish, instead of search permission, the "Make public posts freely searchable on other servers" setting in the profile settings is applied. In Mastodon and Firefish, the "Make public posts freely searchable on other servers" setting in the profile settings is applied instead of the search permission. setting_default_sensitive: Sensitive media is hidden by default and can be revealed with a click - setting_disallow_unlisted_public_searchability: この設定を有効にすると、非収載投稿と検索範囲「誰でも」は両立できず不特定多数からの検索が不可になります。Fedibirdと同じ挙動になります + setting_disallow_unlisted_public_searchability: If you enable this setting, unlisted posts and the “everyone” search scope cannot coexist, making it impossible for unspecified users to search your posts. setting_display_media_default: Hide media marked as sensitive setting_display_media_hide_all: Always hide media setting_display_media_show_all: Always show media setting_dtl_force_searchability: 'With using #%{tag} tag, your post settings will be changed forcibly' setting_dtl_force_visibility: 'With using #%{tag} tag, your post settings will be changed forcibly' setting_emoji_reaction_policy: Even with this setting, users on non-kmyblue servers are free to put their emoji reaction on the post and share it within the same server. If you simply want to remove the emoji reaction from your own screen, you can disable it from the appearance settings - setting_emoji_reaction_streaming_notify_impl2: 当該サーバーの独自機能に対応したアプリを利用時に、絵文字リアクション機能を利用できます。動作確認していないため(そもそもそのようなアプリ自体を確認できていないため)正しく動かない場合があります + setting_emoji_reaction_streaming_notify_impl2: You can use the emoji reaction feature when using an app that supports this server’s unique functionality. However, since this has not been tested (and such apps have not even been identified), it may not work correctly. setting_enable_emoji_reaction: If turn off, other users still can react your posts setting_enabled_visibilities: If turn off, you cannot select and post the privacy. - setting_hide_network: フォローとフォロワーの情報がプロフィールページで見られないようにします - setting_public_post_to_unlisted: 未対応のサードパーティアプリからもローカル公開で投稿できますが、公開投稿はWeb以外できなくなります + setting_hide_network: It will hide the following and follower information from the profile page. + setting_public_post_to_unlisted: You can still post with local visibility using unsupported third-party apps, but public posts will no longer be possible outside of the web interface. setting_reject_send_limited_to_suspects: This applies to "Mutual Only" posts. Circle posts will be delivered without exception. Some Misskey servers have independently supported limited posting, but this is a setting for those who are concerned about it, as mutual-only posting exposes some of the users you are mutual with to Misskey users! setting_reject_unlisted_subscription: Misskey and its forks can **subscribe and search** for "non-following" posts from accounts they do not follow. This differs from kmyblue's behavior. It delivers posts in the specified public range to such servers as "followers only". Please understand, however, that due to its structure, it is difficult to handle perfectly and will occasionally be delivered as non-subscribed. setting_reverse_search_quote: Double-quotes will result in a search with a wider range of notation, which is the opposite of Mastodon's default behavior. @@ -121,10 +121,10 @@ en: peers_api_enabled: A list of domain names this server has encountered in the fediverse. No data is included here about whether you federate with a given server, just that your server knows about it. This is used by services that collect statistics on federation in a general sense. profile_directory: The profile directory lists all users who have opted-in to be discoverable. receive_other_servers_emoji_reaction: It can cause load. It is recommended to enable it only when there are few people. - registrations_end_hour: 新規登録が承認なしで可能な時間帯の開始時間を指定します。これより前の時間に登録することはできません。終了時間より後にすることはできません。この時間帯から外れた新規登録には、別途承認が必要となります。 - registrations_limit: 現在のユーザー数がこれを超過すると、管理者がこの数値を増やさない限り新規登録できません。0を指定すると、この制限を無効化します。 - registrations_limit_per_day: 本日登録されたユーザー数がこれを超過すると、UTC時刻で翌日0時にならない限り新規登録できません。0を指定すると、この制限を無効化します。 - registrations_start_hour: 新規登録が承認なしで可能な時間帯の終了時間を指定します。これより後の時間に登録することはできません。開始時間より前にすることはできません。この時間帯から外れた新規登録には、別途承認が必要となります。 + registrations_end_hour: Specifies the end of the time window during which new registrations can be made without approval. Registrations cannot be made before the start time or after this end time. Registrations outside this time window will require separate approval. + registrations_limit: If the current number of users exceeds this value, new registrations will not be possible unless the administrator increases the limit. Setting this to 0 disables the restriction. + registrations_limit_per_day: If the number of users registered today exceeds this value, no new registrations will be allowed until 00:00 UTC the next day. Setting this to 0 disables the restriction. + registrations_start_hour: Specifies the start of the time window during which new registrations can be made without approval. Registrations cannot be made before this time or after the end time. Registrations outside this time window will require separate approval. require_invite_text: When sign-ups require manual approval, make the “Why do you want to join?” text input mandatory rather than optional site_contact_email: How people can reach you for legal or support inquiries. site_contact_username: How people can reach you on Mastodon. @@ -201,8 +201,8 @@ en: discoverable: Feature profile and posts in discovery algorithms fields: examples: - name_1: 例) GitHub - value_1: 例) https://github.com/xxxxxx + name_1: Example Gitea + value_1: Example https://giteahub.com name: Label value: Content indexable: Include public posts in search results diff --git a/config/themes.yml b/config/themes.yml index 7ba74bbd35..4f4fbedb39 100644 --- a/config/themes.yml +++ b/config/themes.yml @@ -1,4 +1,10 @@ default: styles/application.scss contrast: styles/contrast.scss mastodon-light: styles/mastodon-light.scss +modern-dark: styles/modern-dark.scss +modern-light: styles/modern-light.scss +modern-contrast: styles/modern-contrast.scss full-dark: styles/full-dark.scss + + + diff --git a/docker-compose.yml b/docker-compose.yml index 0ec0c43bb6..b88ea761dc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -59,7 +59,7 @@ services: web: # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes build: . - image: kmyblue:18.0-dev + image: kmyblue:18.1 restart: always env_file: .env.production command: bundle exec puma -C config/puma.rb @@ -83,7 +83,7 @@ services: build: dockerfile: ./streaming/Dockerfile context: . - image: kmyblue-streaming:18.0-dev + image: kmyblue-streaming:18.1 restart: always env_file: .env.production command: node ./streaming/index.js @@ -101,7 +101,7 @@ services: sidekiq: build: . - image: kmyblue:18.0-dev + image: kmyblue:18.1 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index acf7a4e79a..e8eca96a71 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,13 +13,13 @@ module Mastodon end def kmyblue_minor - 0 + 1 end def kmyblue_flag # 'LTS' - 'dev' - # nil + # 'dev' + nil end def major @@ -35,7 +35,7 @@ module Mastodon end def default_prerelease - 'alpha.4' + 'alpha.5' end def prerelease diff --git a/public/avatars/original/missing.png b/public/avatars/original/missing.png index 781370782e..3b37e69c5d 100644 Binary files a/public/avatars/original/missing.png and b/public/avatars/original/missing.png differ diff --git a/public/favicon.ico b/public/favicon.ico old mode 100755 new mode 100644 index fc5e475d42..7f865cfe96 Binary files a/public/favicon.ico and b/public/favicon.ico differ