Merge pull request #3288 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to 366856f3bcdc2ff008b04e493a5de317ab83d5d0
This commit is contained in:
commit
4612014192
@ -68,6 +68,7 @@ docker-compose.override.yml
|
|||||||
|
|
||||||
# Ignore vendored CSS reset
|
# Ignore vendored CSS reset
|
||||||
app/javascript/styles/mastodon/reset.scss
|
app/javascript/styles/mastodon/reset.scss
|
||||||
|
app/javascript/styles_new/mastodon/reset.scss
|
||||||
|
|
||||||
# Ignore Javascript pending https://github.com/mastodon/mastodon/pull/23631
|
# Ignore Javascript pending https://github.com/mastodon/mastodon/pull/23631
|
||||||
*.js
|
*.js
|
||||||
|
|||||||
2
Gemfile
2
Gemfile
@ -24,7 +24,7 @@ gem 'ruby-vips', '~> 2.2', require: false
|
|||||||
|
|
||||||
gem 'active_model_serializers', '~> 0.10'
|
gem 'active_model_serializers', '~> 0.10'
|
||||||
gem 'addressable', '~> 2.8'
|
gem 'addressable', '~> 2.8'
|
||||||
gem 'bootsnap', '~> 1.18.0', require: false
|
gem 'bootsnap', '~> 1.19.0', require: false
|
||||||
gem 'browser'
|
gem 'browser'
|
||||||
gem 'charlock_holmes', '~> 0.7.7'
|
gem 'charlock_holmes', '~> 0.7.7'
|
||||||
gem 'chewy', '~> 7.3'
|
gem 'chewy', '~> 7.3'
|
||||||
|
|||||||
16
Gemfile.lock
16
Gemfile.lock
@ -129,7 +129,7 @@ GEM
|
|||||||
binding_of_caller (1.0.1)
|
binding_of_caller (1.0.1)
|
||||||
debug_inspector (>= 1.2.0)
|
debug_inspector (>= 1.2.0)
|
||||||
blurhash (0.1.8)
|
blurhash (0.1.8)
|
||||||
bootsnap (1.18.6)
|
bootsnap (1.19.0)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
brakeman (7.1.1)
|
brakeman (7.1.1)
|
||||||
racc
|
racc
|
||||||
@ -349,7 +349,7 @@ GEM
|
|||||||
azure-blob (~> 0.5.2)
|
azure-blob (~> 0.5.2)
|
||||||
hashie (~> 5.0)
|
hashie (~> 5.0)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.15.2)
|
json (2.16.0)
|
||||||
json-canonicalization (1.0.0)
|
json-canonicalization (1.0.0)
|
||||||
json-jwt (1.17.0)
|
json-jwt (1.17.0)
|
||||||
activesupport (>= 4.2)
|
activesupport (>= 4.2)
|
||||||
@ -446,7 +446,7 @@ GEM
|
|||||||
mime-types-data (3.2025.0924)
|
mime-types-data (3.2025.0924)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.9)
|
mini_portile2 (2.8.9)
|
||||||
minitest (5.26.0)
|
minitest (5.26.1)
|
||||||
msgpack (1.8.0)
|
msgpack (1.8.0)
|
||||||
multi_json (1.17.0)
|
multi_json (1.17.0)
|
||||||
mutex_m (0.3.0)
|
mutex_m (0.3.0)
|
||||||
@ -759,7 +759,7 @@ GEM
|
|||||||
rubocop-ast (>= 1.47.1, < 2.0)
|
rubocop-ast (>= 1.47.1, < 2.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 2.4.0, < 4.0)
|
unicode-display_width (>= 2.4.0, < 4.0)
|
||||||
rubocop-ast (1.47.1)
|
rubocop-ast (1.48.0)
|
||||||
parser (>= 3.3.7.2)
|
parser (>= 3.3.7.2)
|
||||||
prism (~> 1.4)
|
prism (~> 1.4)
|
||||||
rubocop-capybara (2.22.1)
|
rubocop-capybara (2.22.1)
|
||||||
@ -778,10 +778,10 @@ GEM
|
|||||||
rack (>= 1.1)
|
rack (>= 1.1)
|
||||||
rubocop (>= 1.75.0, < 2.0)
|
rubocop (>= 1.75.0, < 2.0)
|
||||||
rubocop-ast (>= 1.44.0, < 2.0)
|
rubocop-ast (>= 1.44.0, < 2.0)
|
||||||
rubocop-rspec (3.7.0)
|
rubocop-rspec (3.8.0)
|
||||||
lint_roller (~> 1.1)
|
lint_roller (~> 1.1)
|
||||||
rubocop (~> 1.72, >= 1.72.1)
|
rubocop (~> 1.81)
|
||||||
rubocop-rspec_rails (2.31.0)
|
rubocop-rspec_rails (2.32.0)
|
||||||
lint_roller (~> 1.1)
|
lint_roller (~> 1.1)
|
||||||
rubocop (~> 1.72, >= 1.72.1)
|
rubocop (~> 1.72, >= 1.72.1)
|
||||||
rubocop-rspec (~> 3.5)
|
rubocop-rspec (~> 3.5)
|
||||||
@ -941,7 +941,7 @@ DEPENDENCIES
|
|||||||
better_errors (~> 2.9)
|
better_errors (~> 2.9)
|
||||||
binding_of_caller (~> 1.0)
|
binding_of_caller (~> 1.0)
|
||||||
blurhash (~> 0.1)
|
blurhash (~> 0.1)
|
||||||
bootsnap (~> 1.18.0)
|
bootsnap (~> 1.19.0)
|
||||||
brakeman (~> 7.0)
|
brakeman (~> 7.0)
|
||||||
browser
|
browser
|
||||||
bundler-audit (~> 0.9)
|
bundler-audit (~> 0.9)
|
||||||
|
|||||||
@ -9,7 +9,7 @@ module Admin
|
|||||||
|
|
||||||
@site_upload.destroy!
|
@site_upload.destroy!
|
||||||
|
|
||||||
redirect_back fallback_location: admin_settings_path, notice: I18n.t('admin.site_uploads.destroyed_msg')
|
redirect_back_or_to admin_settings_path, notice: I18n.t('admin.site_uploads.destroyed_msg')
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@ -6,11 +6,11 @@ module ThemeHelper
|
|||||||
|
|
||||||
if theme == 'system'
|
if theme == 'system'
|
||||||
''.html_safe.tap do |tags|
|
''.html_safe.tap do |tags|
|
||||||
tags << vite_stylesheet_tag("skins/#{flavour}/mastodon-light", type: :virtual, media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous')
|
tags << vite_stylesheet_tag(theme_path_for(flavour, 'mastodon-light'), type: :virtual, media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous')
|
||||||
tags << vite_stylesheet_tag("skins/#{flavour}/default", type: :virtual, media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous')
|
tags << vite_stylesheet_tag(theme_path_for(flavour, 'default'), type: :virtual, media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous')
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
vite_stylesheet_tag "skins/#{flavour}/#{theme}", type: :virtual, media: 'all', crossorigin: 'anonymous'
|
vite_stylesheet_tag theme_path_for(flavour, theme), type: :virtual, media: 'all', crossorigin: 'anonymous'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -57,4 +57,8 @@ module ThemeHelper
|
|||||||
def theme_color_for(theme)
|
def theme_color_for(theme)
|
||||||
theme == 'mastodon-light' ? Themes::THEME_COLORS[:light] : Themes::THEME_COLORS[:dark]
|
theme == 'mastodon-light' ? Themes::THEME_COLORS[:light] : Themes::THEME_COLORS[:dark]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def theme_path_for(flavour, theme)
|
||||||
|
"skins/#{flavour}/#{theme}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -180,25 +180,24 @@ export function useHotkeys<T extends HTMLElement>(handlers: HandlerMap) {
|
|||||||
|
|
||||||
if (shouldHandleEvent) {
|
if (shouldHandleEvent) {
|
||||||
const matchCandidates: {
|
const matchCandidates: {
|
||||||
handler: (event: KeyboardEvent) => void;
|
// A candidate will be have an undefined handler if it's matched,
|
||||||
|
// but handled in a parent component rather than this one.
|
||||||
|
handler: ((event: KeyboardEvent) => void) | undefined;
|
||||||
priority: number;
|
priority: number;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
|
|
||||||
(Object.keys(hotkeyMatcherMap) as HotkeyName[]).forEach(
|
(Object.keys(hotkeyMatcherMap) as HotkeyName[]).forEach(
|
||||||
(handlerName) => {
|
(handlerName) => {
|
||||||
const handler = handlersRef.current[handlerName];
|
const handler = handlersRef.current[handlerName];
|
||||||
|
const hotkeyMatcher = hotkeyMatcherMap[handlerName];
|
||||||
|
|
||||||
if (handler) {
|
const { isMatch, priority } = hotkeyMatcher(
|
||||||
const hotkeyMatcher = hotkeyMatcherMap[handlerName];
|
event,
|
||||||
|
bufferedKeys.current,
|
||||||
|
);
|
||||||
|
|
||||||
const { isMatch, priority } = hotkeyMatcher(
|
if (isMatch) {
|
||||||
event,
|
matchCandidates.push({ handler, priority });
|
||||||
bufferedKeys.current,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isMatch) {
|
|
||||||
matchCandidates.push({ handler, priority });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -144,7 +144,7 @@ export const HandledLink: FC<HandledLinkProps & ComponentProps<'a'>> = ({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
className={classNames('mention hashtag', className)}
|
className={classNames('mention hashtag', className)}
|
||||||
to={`/tags/${hashtag}`}
|
to={`/tags/${encodeURIComponent(hashtag)}`}
|
||||||
rel='tag'
|
rel='tag'
|
||||||
data-menu-hashtag={hashtagAccountId}
|
data-menu-hashtag={hashtagAccountId}
|
||||||
>
|
>
|
||||||
@ -194,7 +194,7 @@ export const HandledLink: FC<HandledLinkProps & ComponentProps<'a'>> = ({
|
|||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
{...props}
|
{...props}
|
||||||
href={encodeURI(href)}
|
href={href}
|
||||||
title={href}
|
title={href}
|
||||||
className={classNames('unhandled-link', className)}
|
className={classNames('unhandled-link', className)}
|
||||||
target='_blank'
|
target='_blank'
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {
|
|||||||
import { pasteLinkCompose } from 'flavours/glitch/actions/compose_typed';
|
import { pasteLinkCompose } from 'flavours/glitch/actions/compose_typed';
|
||||||
import { openModal } from 'flavours/glitch/actions/modal';
|
import { openModal } from 'flavours/glitch/actions/modal';
|
||||||
import { PRIVATE_QUOTE_MODAL_ID } from 'flavours/glitch/features/ui/components/confirmation_modals/private_quote_notify';
|
import { PRIVATE_QUOTE_MODAL_ID } from 'flavours/glitch/features/ui/components/confirmation_modals/private_quote_notify';
|
||||||
|
import { me } from 'flavours/glitch/initial_state';
|
||||||
import { privacyPreference } from 'flavours/glitch/utils/privacy_preference';
|
import { privacyPreference } from 'flavours/glitch/utils/privacy_preference';
|
||||||
|
|
||||||
import ComposeForm from '../components/compose_form';
|
import ComposeForm from '../components/compose_form';
|
||||||
@ -73,6 +74,7 @@ const mapStateToProps = state => ({
|
|||||||
quoteToPrivate:
|
quoteToPrivate:
|
||||||
!!state.getIn(['compose', 'quoted_status_id'])
|
!!state.getIn(['compose', 'quoted_status_id'])
|
||||||
&& state.getIn(['compose', 'privacy']) === 'private'
|
&& state.getIn(['compose', 'privacy']) === 'private'
|
||||||
|
&& state.getIn(['statuses', state.getIn(['compose', 'quoted_status_id']), 'account']) !== me
|
||||||
&& !state.getIn(['settings', 'dismissed_banners', PRIVATE_QUOTE_MODAL_ID]),
|
&& !state.getIn(['settings', 'dismissed_banners', PRIVATE_QUOTE_MODAL_ID]),
|
||||||
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
|
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
|
||||||
lang: state.getIn(['compose', 'language']),
|
lang: state.getIn(['compose', 'language']),
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { initialState } from '@/flavours/glitch/initial_state';
|
import { initialState } from '@/flavours/glitch/initial_state';
|
||||||
|
|
||||||
import { toSupportedLocale } from './locale';
|
import { toSupportedLocale } from './locale';
|
||||||
|
import type { LocaleOrCustom } from './types';
|
||||||
import { emojiLogger } from './utils';
|
import { emojiLogger } from './utils';
|
||||||
// eslint-disable-next-line import/default -- Importing via worker loader.
|
// eslint-disable-next-line import/default -- Importing via worker loader.
|
||||||
import EmojiWorker from './worker?worker&inline';
|
import EmojiWorker from './worker?worker&inline';
|
||||||
@ -24,19 +25,17 @@ export function initializeEmoji() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (worker) {
|
if (worker) {
|
||||||
// Assign worker to const to make TS happy inside the event listener.
|
|
||||||
const thisWorker = worker;
|
|
||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = setTimeout(() => {
|
||||||
log('worker is not ready after timeout');
|
log('worker is not ready after timeout');
|
||||||
worker = null;
|
worker = null;
|
||||||
void fallbackLoad();
|
void fallbackLoad();
|
||||||
}, WORKER_TIMEOUT);
|
}, WORKER_TIMEOUT);
|
||||||
thisWorker.addEventListener('message', (event: MessageEvent<string>) => {
|
worker.addEventListener('message', (event: MessageEvent<string>) => {
|
||||||
const { data: message } = event;
|
const { data: message } = event;
|
||||||
if (message === 'ready') {
|
if (message === 'ready') {
|
||||||
log('worker ready, loading data');
|
log('worker ready, loading data');
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
thisWorker.postMessage('custom');
|
messageWorker('custom');
|
||||||
void loadEmojiLocale(userLocale);
|
void loadEmojiLocale(userLocale);
|
||||||
// Load English locale as well, because people are still used to
|
// Load English locale as well, because people are still used to
|
||||||
// using it from before we supported other locales.
|
// using it from before we supported other locales.
|
||||||
@ -55,20 +54,35 @@ export function initializeEmoji() {
|
|||||||
async function fallbackLoad() {
|
async function fallbackLoad() {
|
||||||
log('falling back to main thread for loading');
|
log('falling back to main thread for loading');
|
||||||
const { importCustomEmojiData } = await import('./loader');
|
const { importCustomEmojiData } = await import('./loader');
|
||||||
await importCustomEmojiData();
|
const emojis = await importCustomEmojiData();
|
||||||
|
if (emojis) {
|
||||||
|
log('loaded %d custom emojis', emojis.length);
|
||||||
|
}
|
||||||
await loadEmojiLocale(userLocale);
|
await loadEmojiLocale(userLocale);
|
||||||
if (userLocale !== 'en') {
|
if (userLocale !== 'en') {
|
||||||
await loadEmojiLocale('en');
|
await loadEmojiLocale('en');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadEmojiLocale(localeString: string) {
|
async function loadEmojiLocale(localeString: string) {
|
||||||
const locale = toSupportedLocale(localeString);
|
const locale = toSupportedLocale(localeString);
|
||||||
|
const { importEmojiData, localeToPath } = await import('./loader');
|
||||||
|
|
||||||
if (worker) {
|
if (worker) {
|
||||||
worker.postMessage(locale);
|
const path = await localeToPath(locale);
|
||||||
|
log('asking worker to load locale %s from %s', locale, path);
|
||||||
|
messageWorker(locale, path);
|
||||||
} else {
|
} else {
|
||||||
const { importEmojiData } = await import('./loader');
|
const emojis = await importEmojiData(locale);
|
||||||
await importEmojiData(locale);
|
if (emojis) {
|
||||||
|
log('loaded %d emojis to locale %s', emojis.length, locale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function messageWorker(locale: LocaleOrCustom, path?: string) {
|
||||||
|
if (!worker) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
worker.postMessage({ locale, path });
|
||||||
|
}
|
||||||
|
|||||||
@ -8,44 +8,64 @@ import {
|
|||||||
putLatestEtag,
|
putLatestEtag,
|
||||||
} from './database';
|
} from './database';
|
||||||
import { toSupportedLocale, toSupportedLocaleOrCustom } from './locale';
|
import { toSupportedLocale, toSupportedLocaleOrCustom } from './locale';
|
||||||
import type { CustomEmojiData, LocaleOrCustom } from './types';
|
import type { CustomEmojiData } from './types';
|
||||||
import { emojiLogger } from './utils';
|
|
||||||
|
|
||||||
const log = emojiLogger('loader');
|
export async function importEmojiData(localeString: string, path?: string) {
|
||||||
|
|
||||||
export async function importEmojiData(localeString: string) {
|
|
||||||
const locale = toSupportedLocale(localeString);
|
const locale = toSupportedLocale(localeString);
|
||||||
const emojis = await fetchAndCheckEtag<CompactEmoji[]>(locale);
|
|
||||||
|
// Validate the provided path.
|
||||||
|
if (path && !/^[/a-z]*\/packs\/assets\/compact-\w+\.json$/.test(path)) {
|
||||||
|
throw new Error('Invalid path for emoji data');
|
||||||
|
} else {
|
||||||
|
// Otherwise get the path if not provided.
|
||||||
|
path ??= await localeToPath(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
const emojis = await fetchAndCheckEtag<CompactEmoji[]>(locale, path);
|
||||||
if (!emojis) {
|
if (!emojis) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const flattenedEmojis: FlatCompactEmoji[] = flattenEmojiData(emojis);
|
const flattenedEmojis: FlatCompactEmoji[] = flattenEmojiData(emojis);
|
||||||
log('loaded %d for %s locale', flattenedEmojis.length, locale);
|
|
||||||
await putEmojiData(flattenedEmojis, locale);
|
await putEmojiData(flattenedEmojis, locale);
|
||||||
|
return flattenedEmojis;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function importCustomEmojiData() {
|
export async function importCustomEmojiData() {
|
||||||
const emojis = await fetchAndCheckEtag<CustomEmojiData[]>('custom');
|
const emojis = await fetchAndCheckEtag<CustomEmojiData[]>(
|
||||||
|
'custom',
|
||||||
|
'/api/v1/custom_emojis',
|
||||||
|
);
|
||||||
if (!emojis) {
|
if (!emojis) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log('loaded %d custom emojis', emojis.length);
|
|
||||||
await putCustomEmojiData(emojis);
|
await putCustomEmojiData(emojis);
|
||||||
|
return emojis;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchAndCheckEtag<ResultType extends object[]>(
|
const modules = import.meta.glob<string>(
|
||||||
localeOrCustom: LocaleOrCustom,
|
'../../../../../../node_modules/emojibase-data/**/compact.json',
|
||||||
|
{
|
||||||
|
query: '?url',
|
||||||
|
import: 'default',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export function localeToPath(locale: Locale) {
|
||||||
|
const key = `../../../../../../node_modules/emojibase-data/${locale}/compact.json`;
|
||||||
|
if (!modules[key] || typeof modules[key] !== 'function') {
|
||||||
|
throw new Error(`Unsupported locale: ${locale}`);
|
||||||
|
}
|
||||||
|
return modules[key]();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAndCheckEtag<ResultType extends object[]>(
|
||||||
|
localeString: string,
|
||||||
|
path: string,
|
||||||
): Promise<ResultType | null> {
|
): Promise<ResultType | null> {
|
||||||
const locale = toSupportedLocaleOrCustom(localeOrCustom);
|
const locale = toSupportedLocaleOrCustom(localeString);
|
||||||
|
|
||||||
// Use location.origin as this script may be loaded from a CDN domain.
|
// Use location.origin as this script may be loaded from a CDN domain.
|
||||||
const url = new URL(location.origin);
|
const url = new URL(path, location.origin);
|
||||||
if (locale === 'custom') {
|
|
||||||
url.pathname = '/api/v1/custom_emojis';
|
|
||||||
} else {
|
|
||||||
const modulePath = await localeToPath(locale);
|
|
||||||
url.pathname = modulePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldEtag = await loadLatestEtag(locale);
|
const oldEtag = await loadLatestEtag(locale);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
@ -60,38 +80,20 @@ async function fetchAndCheckEtag<ResultType extends object[]>(
|
|||||||
}
|
}
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to fetch emoji data for ${localeOrCustom}: ${response.statusText}`,
|
`Failed to fetch emoji data for ${locale}: ${response.statusText}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = (await response.json()) as ResultType;
|
const data = (await response.json()) as ResultType;
|
||||||
if (!Array.isArray(data)) {
|
if (!Array.isArray(data)) {
|
||||||
throw new Error(
|
throw new Error(`Unexpected data format for ${locale}: expected an array`);
|
||||||
`Unexpected data format for ${localeOrCustom}: expected an array`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the ETag for future requests
|
// Store the ETag for future requests
|
||||||
const etag = response.headers.get('ETag');
|
const etag = response.headers.get('ETag');
|
||||||
if (etag) {
|
if (etag) {
|
||||||
await putLatestEtag(etag, localeOrCustom);
|
await putLatestEtag(etag, localeString);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const modules = import.meta.glob<string>(
|
|
||||||
'../../../../../../node_modules/emojibase-data/**/compact.json',
|
|
||||||
{
|
|
||||||
query: '?url',
|
|
||||||
import: 'default',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
function localeToPath(locale: Locale) {
|
|
||||||
const key = `../../../../../../node_modules/emojibase-data/${locale}/compact.json`;
|
|
||||||
if (!modules[key] || typeof modules[key] !== 'function') {
|
|
||||||
throw new Error(`Unsupported locale: ${locale}`);
|
|
||||||
}
|
|
||||||
return modules[key]();
|
|
||||||
}
|
|
||||||
|
|||||||
@ -162,7 +162,7 @@ describe('loadEmojiDataToState', () => {
|
|||||||
const dbCall = vi
|
const dbCall = vi
|
||||||
.spyOn(db, 'loadEmojiByHexcode')
|
.spyOn(db, 'loadEmojiByHexcode')
|
||||||
.mockRejectedValue(new db.LocaleNotLoadedError('en'));
|
.mockRejectedValue(new db.LocaleNotLoadedError('en'));
|
||||||
vi.spyOn(loader, 'importEmojiData').mockResolvedValueOnce();
|
vi.spyOn(loader, 'importEmojiData').mockResolvedValueOnce(undefined);
|
||||||
const consoleCall = vi
|
const consoleCall = vi
|
||||||
.spyOn(console, 'warn')
|
.spyOn(console, 'warn')
|
||||||
.mockImplementationOnce(() => null);
|
.mockImplementationOnce(() => null);
|
||||||
|
|||||||
@ -1,18 +1,25 @@
|
|||||||
import { importEmojiData, importCustomEmojiData } from './loader';
|
import { importCustomEmojiData, importEmojiData } from './loader';
|
||||||
|
|
||||||
addEventListener('message', handleMessage);
|
addEventListener('message', handleMessage);
|
||||||
self.postMessage('ready'); // After the worker is ready, notify the main thread
|
self.postMessage('ready'); // After the worker is ready, notify the main thread
|
||||||
|
|
||||||
function handleMessage(event: MessageEvent<string>) {
|
function handleMessage(event: MessageEvent<{ locale: string; path?: string }>) {
|
||||||
const { data: locale } = event;
|
const {
|
||||||
void loadData(locale);
|
data: { locale, path },
|
||||||
|
} = event;
|
||||||
|
void loadData(locale, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadData(locale: string) {
|
async function loadData(locale: string, path?: string) {
|
||||||
if (locale !== 'custom') {
|
let importCount: number | undefined;
|
||||||
await importEmojiData(locale);
|
if (locale === 'custom') {
|
||||||
|
importCount = (await importCustomEmojiData())?.length;
|
||||||
|
} else if (path) {
|
||||||
|
importCount = (await importEmojiData(locale, path))?.length;
|
||||||
} else {
|
} else {
|
||||||
await importCustomEmojiData();
|
throw new Error('Path is required for loading locale emoji data');
|
||||||
|
}
|
||||||
|
if (importCount) {
|
||||||
|
self.postMessage(`loaded ${importCount} emojis into ${locale}`);
|
||||||
}
|
}
|
||||||
self.postMessage(`loaded ${locale}`);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
@typescript-eslint/no-unsafe-assignment */
|
@typescript-eslint/no-unsafe-assignment */
|
||||||
|
|
||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties } from 'react';
|
||||||
import { useState, useRef, useCallback } from 'react';
|
import { useState, useRef, useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
@ -57,6 +57,8 @@ export const DetailedStatus: React.FC<{
|
|||||||
pictureInPicture: any;
|
pictureInPicture: any;
|
||||||
onToggleHidden?: (status: any) => void;
|
onToggleHidden?: (status: any) => void;
|
||||||
onToggleMediaVisibility?: () => void;
|
onToggleMediaVisibility?: () => void;
|
||||||
|
ancestors?: number;
|
||||||
|
multiColumn?: boolean;
|
||||||
expanded: boolean;
|
expanded: boolean;
|
||||||
}> = ({
|
}> = ({
|
||||||
status,
|
status,
|
||||||
@ -72,6 +74,8 @@ export const DetailedStatus: React.FC<{
|
|||||||
pictureInPicture,
|
pictureInPicture,
|
||||||
onToggleMediaVisibility,
|
onToggleMediaVisibility,
|
||||||
onToggleHidden,
|
onToggleHidden,
|
||||||
|
ancestors = 0,
|
||||||
|
multiColumn = false,
|
||||||
expanded,
|
expanded,
|
||||||
}) => {
|
}) => {
|
||||||
const properStatus = status?.get('reblog') ?? status;
|
const properStatus = status?.get('reblog') ?? status;
|
||||||
@ -136,6 +140,30 @@ export const DetailedStatus: React.FC<{
|
|||||||
if (onTranslate) onTranslate(status);
|
if (onTranslate) onTranslate(status);
|
||||||
}, [onTranslate, status]);
|
}, [onTranslate, status]);
|
||||||
|
|
||||||
|
// The component is managed and will change if the status changes
|
||||||
|
// Ancestors can increase when loading a thread, in which case we want to scroll,
|
||||||
|
// or decrease if a post is deleted, in which case we don't want to mess with it
|
||||||
|
const previousAncestors = useRef(-1);
|
||||||
|
useEffect(() => {
|
||||||
|
if (nodeRef.current && previousAncestors.current < ancestors) {
|
||||||
|
nodeRef.current.scrollIntoView(true);
|
||||||
|
|
||||||
|
// In the single-column interface, `scrollIntoView` will put the post behind the header, so compensate for that.
|
||||||
|
if (!multiColumn) {
|
||||||
|
const offset = document
|
||||||
|
.querySelector('.column-header__wrapper')
|
||||||
|
?.getBoundingClientRect().bottom;
|
||||||
|
|
||||||
|
if (offset) {
|
||||||
|
const scrollingElement = document.scrollingElement ?? document.body;
|
||||||
|
scrollingElement.scrollBy(0, -offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousAncestors.current = ancestors;
|
||||||
|
}, [ancestors, multiColumn]);
|
||||||
|
|
||||||
if (!properStatus) {
|
if (!properStatus) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -162,7 +162,6 @@ class Status extends ImmutablePureComponent {
|
|||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
attachFullscreenListener(this.onFullScreenChange);
|
attachFullscreenListener(this.onFullScreenChange);
|
||||||
this.props.dispatch(fetchStatus(this.props.params.statusId, { forceFetch: true }));
|
this.props.dispatch(fetchStatus(this.props.params.statusId, { forceFetch: true }));
|
||||||
this._scrollStatusIntoView();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(props, state) {
|
static getDerivedStateFromProps(props, state) {
|
||||||
@ -512,35 +511,11 @@ class Status extends ImmutablePureComponent {
|
|||||||
this.statusNode = c;
|
this.statusNode = c;
|
||||||
};
|
};
|
||||||
|
|
||||||
_scrollStatusIntoView () {
|
|
||||||
const { status, multiColumn } = this.props;
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
requestIdleCallback(() => {
|
|
||||||
this.statusNode?.scrollIntoView(true);
|
|
||||||
|
|
||||||
// In the single-column interface, `scrollIntoView` will put the post behind the header,
|
|
||||||
// so compensate for that.
|
|
||||||
if (!multiColumn) {
|
|
||||||
const offset = document.querySelector('.column-header__wrapper')?.getBoundingClientRect()?.bottom;
|
|
||||||
if (offset) {
|
|
||||||
const scrollingElement = document.scrollingElement || document.body;
|
|
||||||
scrollingElement.scrollBy(0, -offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate (prevProps) {
|
componentDidUpdate (prevProps) {
|
||||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
const { status, descendantsIds } = this.props;
|
||||||
|
|
||||||
const isSameStatus = status && (prevProps.status?.get('id') === status.get('id'));
|
const isSameStatus = status && (prevProps.status?.get('id') === status.get('id'));
|
||||||
|
|
||||||
if (status && (ancestorsIds.length > prevProps.ancestorsIds.length || !isSameStatus)) {
|
|
||||||
this._scrollStatusIntoView();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only highlight replies after the initial load
|
// Only highlight replies after the initial load
|
||||||
if (prevProps.descendantsIds.length && isSameStatus) {
|
if (prevProps.descendantsIds.length && isSameStatus) {
|
||||||
const newRepliesIds = difference(descendantsIds, prevProps.descendantsIds);
|
const newRepliesIds = difference(descendantsIds, prevProps.descendantsIds);
|
||||||
@ -653,6 +628,8 @@ class Status extends ImmutablePureComponent {
|
|||||||
showMedia={this.state.showMedia}
|
showMedia={this.state.showMedia}
|
||||||
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
||||||
pictureInPicture={pictureInPicture}
|
pictureInPicture={pictureInPicture}
|
||||||
|
ancestors={this.props.ancestorsIds.length}
|
||||||
|
multiColumn={multiColumn}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ActionBar
|
<ActionBar
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import { me, reduceMotion } from 'flavours/glitch/initial_state';
|
|||||||
import ready from 'flavours/glitch/ready';
|
import ready from 'flavours/glitch/ready';
|
||||||
import { store } from 'flavours/glitch/store';
|
import { store } from 'flavours/glitch/store';
|
||||||
|
|
||||||
import { initializeEmoji } from './features/emoji';
|
|
||||||
import { isProduction, isDevelopment } from './utils/environment';
|
import { isProduction, isDevelopment } from './utils/environment';
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
@ -30,6 +29,7 @@ function main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { initializeEmoji } = await import('./features/emoji/index');
|
||||||
initializeEmoji();
|
initializeEmoji();
|
||||||
|
|
||||||
const root = createRoot(mountNode);
|
const root = createRoot(mountNode);
|
||||||
|
|||||||
@ -418,8 +418,8 @@ export const composeReducer = (state = initialState, action) => {
|
|||||||
const isDirect = state.get('privacy') === 'direct';
|
const isDirect = state.get('privacy') === 'direct';
|
||||||
return state
|
return state
|
||||||
.set('quoted_status_id', isDirect ? null : status.get('id'))
|
.set('quoted_status_id', isDirect ? null : status.get('id'))
|
||||||
.set('spoiler', status.get('sensitive'))
|
.update('spoiler', spoiler => (spoiler) || !!status.get('spoiler_text'))
|
||||||
.set('spoiler_text', status.get('spoiler_text'))
|
.update('spoiler_text', (spoiler_text) => spoiler_text || status.get('spoiler_text'))
|
||||||
.update('privacy', (visibility) => {
|
.update('privacy', (visibility) => {
|
||||||
if (['public', 'unlisted'].includes(visibility) && status.get('visibility') === 'private') {
|
if (['public', 'unlisted'].includes(visibility) && status.get('visibility') === 'private') {
|
||||||
return 'private';
|
return 'private';
|
||||||
|
|||||||
@ -327,9 +327,9 @@ $content-width: 840px;
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: $primary-text-color;
|
color: $primary-text-color;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
padding-bottom: 0;
|
padding-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
border-bottom: 0;
|
border-top: 0;
|
||||||
|
|
||||||
.comment {
|
.comment {
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
@ -180,25 +180,24 @@ export function useHotkeys<T extends HTMLElement>(handlers: HandlerMap) {
|
|||||||
|
|
||||||
if (shouldHandleEvent) {
|
if (shouldHandleEvent) {
|
||||||
const matchCandidates: {
|
const matchCandidates: {
|
||||||
handler: (event: KeyboardEvent) => void;
|
// A candidate will be have an undefined handler if it's matched,
|
||||||
|
// but handled in a parent component rather than this one.
|
||||||
|
handler: ((event: KeyboardEvent) => void) | undefined;
|
||||||
priority: number;
|
priority: number;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
|
|
||||||
(Object.keys(hotkeyMatcherMap) as HotkeyName[]).forEach(
|
(Object.keys(hotkeyMatcherMap) as HotkeyName[]).forEach(
|
||||||
(handlerName) => {
|
(handlerName) => {
|
||||||
const handler = handlersRef.current[handlerName];
|
const handler = handlersRef.current[handlerName];
|
||||||
|
const hotkeyMatcher = hotkeyMatcherMap[handlerName];
|
||||||
|
|
||||||
if (handler) {
|
const { isMatch, priority } = hotkeyMatcher(
|
||||||
const hotkeyMatcher = hotkeyMatcherMap[handlerName];
|
event,
|
||||||
|
bufferedKeys.current,
|
||||||
|
);
|
||||||
|
|
||||||
const { isMatch, priority } = hotkeyMatcher(
|
if (isMatch) {
|
||||||
event,
|
matchCandidates.push({ handler, priority });
|
||||||
bufferedKeys.current,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isMatch) {
|
|
||||||
matchCandidates.push({ handler, priority });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -553,7 +553,6 @@ class Status extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
|
const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Hotkeys handlers={handlers} focusable={!unfocusable}>
|
<Hotkeys handlers={handlers} focusable={!unfocusable}>
|
||||||
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), unread, focusable: !this.props.muted })} tabIndex={this.props.muted || unfocusable ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader({intl, status, rebloggedByText, isQuote: isQuotedPost})} ref={this.handleRef} data-nosnippet={status.getIn(['account', 'noindex'], true) || undefined}>
|
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), unread, focusable: !this.props.muted })} tabIndex={this.props.muted || unfocusable ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader({intl, status, rebloggedByText, isQuote: isQuotedPost})} ref={this.handleRef} data-nosnippet={status.getIn(['account', 'noindex'], true) || undefined}>
|
||||||
|
|||||||
@ -64,6 +64,7 @@ const StandaloneBoostButton: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
title={intl.formatMessage(meta ?? title)}
|
title={intl.formatMessage(meta ?? title)}
|
||||||
icon='retweet'
|
icon='retweet'
|
||||||
iconComponent={iconComponent}
|
iconComponent={iconComponent}
|
||||||
|
className='status__action-bar__button'
|
||||||
onClick={!disabled ? handleClick : undefined}
|
onClick={!disabled ? handleClick : undefined}
|
||||||
counter={
|
counter={
|
||||||
counters
|
counters
|
||||||
@ -195,6 +196,7 @@ const BoostOrQuoteMenu: FC<ReblogButtonProps> = ({ status, counters }) => {
|
|||||||
isMenuDisabled ? messages.all_disabled : messages.reblog_or_quote,
|
isMenuDisabled ? messages.all_disabled : messages.reblog_or_quote,
|
||||||
)}
|
)}
|
||||||
icon='retweet'
|
icon='retweet'
|
||||||
|
className='status__action-bar__button'
|
||||||
iconComponent={boostIcon}
|
iconComponent={boostIcon}
|
||||||
counter={
|
counter={
|
||||||
counters
|
counters
|
||||||
|
|||||||
@ -38,7 +38,7 @@ export const HandledLink: FC<HandledLinkProps & ComponentProps<'a'>> = ({
|
|||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
className={classNames('mention hashtag', className)}
|
className={classNames('mention hashtag', className)}
|
||||||
to={`/tags/${hashtag}`}
|
to={`/tags/${encodeURIComponent(hashtag)}`}
|
||||||
rel='tag'
|
rel='tag'
|
||||||
data-menu-hashtag={hashtagAccountId}
|
data-menu-hashtag={hashtagAccountId}
|
||||||
>
|
>
|
||||||
@ -71,7 +71,7 @@ export const HandledLink: FC<HandledLinkProps & ComponentProps<'a'>> = ({
|
|||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
{...props}
|
{...props}
|
||||||
href={encodeURI(href)}
|
href={href}
|
||||||
title={href}
|
title={href}
|
||||||
className={classNames('unhandled-link', className)}
|
className={classNames('unhandled-link', className)}
|
||||||
target='_blank'
|
target='_blank'
|
||||||
|
|||||||
@ -406,15 +406,19 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
status={status}
|
status={status}
|
||||||
needsStatusRefresh={quickBoosting && status.get('quote_approval') === null}
|
needsStatusRefresh={quickBoosting && status.get('quote_approval') === null}
|
||||||
items={menu}
|
items={menu}
|
||||||
icon='ellipsis-h'
|
|
||||||
iconComponent={MoreHorizIcon}
|
|
||||||
direction='right'
|
direction='right'
|
||||||
title={intl.formatMessage(messages.more)}
|
|
||||||
onOpen={() => {
|
onOpen={() => {
|
||||||
dismissQuoteHint();
|
dismissQuoteHint();
|
||||||
return true;
|
return true;
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
|
<IconButton
|
||||||
|
className='status__action-bar__button'
|
||||||
|
icon='ellipsis-h'
|
||||||
|
iconComponent={MoreHorizIcon}
|
||||||
|
title={intl.formatMessage(messages.more)}
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
)}
|
)}
|
||||||
</RemoveQuoteHint>
|
</RemoveQuoteHint>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -104,17 +104,19 @@ export const RulesSection: FC<RulesSectionProps> = ({ isLoading = false }) => {
|
|||||||
defaultMessage='Language'
|
defaultMessage='Language'
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<select onChange={handleLocaleChange} id='language-select'>
|
<div className='select-wrapper'>
|
||||||
{localeOptions.map((option) => (
|
<select onChange={handleLocaleChange} id='language-select'>
|
||||||
<option
|
{localeOptions.map((option) => (
|
||||||
key={option.value}
|
<option
|
||||||
value={option.value}
|
key={option.value}
|
||||||
selected={option.value === selectedLocale}
|
value={option.value}
|
||||||
>
|
selected={option.value === selectedLocale}
|
||||||
{option.text}
|
>
|
||||||
</option>
|
{option.text}
|
||||||
))}
|
</option>
|
||||||
</select>
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Section>
|
</Section>
|
||||||
|
|||||||
@ -24,12 +24,12 @@ export default class FollowRequestNote extends ImmutablePureComponent {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='follow-request-banner__action'>
|
<div className='follow-request-banner__action'>
|
||||||
<button type='button' className='button button-tertiary button--confirmation' onClick={onAuthorize}>
|
<button type='button' className='button button-secondary button--confirmation' onClick={onAuthorize}>
|
||||||
<Icon id='check' icon={CheckIcon} />
|
<Icon id='check' icon={CheckIcon} />
|
||||||
<FormattedMessage id='follow_request.authorize' defaultMessage='Authorize' />
|
<FormattedMessage id='follow_request.authorize' defaultMessage='Authorize' />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type='button' className='button button-tertiary button--destructive' onClick={onReject}>
|
<button type='button' className='button button-secondary button--destructive' onClick={onReject}>
|
||||||
<Icon id='times' icon={CloseIcon} />
|
<Icon id='times' icon={CloseIcon} />
|
||||||
<FormattedMessage id='follow_request.reject' defaultMessage='Reject' />
|
<FormattedMessage id='follow_request.reject' defaultMessage='Reject' />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -1,38 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import { PureComponent } from 'react';
|
|
||||||
|
|
||||||
const iconStyle = {
|
|
||||||
height: null,
|
|
||||||
lineHeight: '27px',
|
|
||||||
minWidth: `${18 * 1.28571429}px`,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class TextIconButton extends PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
label: PropTypes.string.isRequired,
|
|
||||||
title: PropTypes.string,
|
|
||||||
active: PropTypes.bool,
|
|
||||||
onClick: PropTypes.func.isRequired,
|
|
||||||
ariaControls: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { label, title, active, ariaControls } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type='button'
|
|
||||||
title={title}
|
|
||||||
aria-label={title}
|
|
||||||
className={`text-icon-button ${active ? 'active' : ''}`}
|
|
||||||
aria-expanded={active}
|
|
||||||
onClick={this.props.onClick}
|
|
||||||
aria-controls={ariaControls} style={iconStyle}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -13,6 +13,7 @@ import {
|
|||||||
import { pasteLinkCompose } from 'mastodon/actions/compose_typed';
|
import { pasteLinkCompose } from 'mastodon/actions/compose_typed';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
import { PRIVATE_QUOTE_MODAL_ID } from 'mastodon/features/ui/components/confirmation_modals/private_quote_notify';
|
import { PRIVATE_QUOTE_MODAL_ID } from 'mastodon/features/ui/components/confirmation_modals/private_quote_notify';
|
||||||
|
import { me } from 'mastodon/initial_state';
|
||||||
|
|
||||||
import ComposeForm from '../components/compose_form';
|
import ComposeForm from '../components/compose_form';
|
||||||
|
|
||||||
@ -53,6 +54,7 @@ const mapStateToProps = state => ({
|
|||||||
quoteToPrivate:
|
quoteToPrivate:
|
||||||
!!state.getIn(['compose', 'quoted_status_id'])
|
!!state.getIn(['compose', 'quoted_status_id'])
|
||||||
&& state.getIn(['compose', 'privacy']) === 'private'
|
&& state.getIn(['compose', 'privacy']) === 'private'
|
||||||
|
&& state.getIn(['statuses', state.getIn(['compose', 'quoted_status_id']), 'account']) !== me
|
||||||
&& !state.getIn(['settings', 'dismissed_banners', PRIVATE_QUOTE_MODAL_ID]),
|
&& !state.getIn(['settings', 'dismissed_banners', PRIVATE_QUOTE_MODAL_ID]),
|
||||||
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
|
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
|
||||||
lang: state.getIn(['compose', 'language']),
|
lang: state.getIn(['compose', 'language']),
|
||||||
|
|||||||
@ -166,7 +166,7 @@ const Compose: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => {
|
|||||||
<div className='drawer__inner'>
|
<div className='drawer__inner'>
|
||||||
<ComposeFormContainer />
|
<ComposeFormContainer />
|
||||||
|
|
||||||
<div className='drawer__inner__mastodon'>
|
<div className='drawer__inner__mastodon with-zig-zag-decoration'>
|
||||||
<img alt='' draggable='false' src={mascot ?? elephantUIPlane} />
|
<img alt='' draggable='false' src={mascot ?? elephantUIPlane} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { initialState } from '@/mastodon/initial_state';
|
import { initialState } from '@/mastodon/initial_state';
|
||||||
|
|
||||||
import { toSupportedLocale } from './locale';
|
import { toSupportedLocale } from './locale';
|
||||||
|
import type { LocaleOrCustom } from './types';
|
||||||
import { emojiLogger } from './utils';
|
import { emojiLogger } from './utils';
|
||||||
// eslint-disable-next-line import/default -- Importing via worker loader.
|
// eslint-disable-next-line import/default -- Importing via worker loader.
|
||||||
import EmojiWorker from './worker?worker&inline';
|
import EmojiWorker from './worker?worker&inline';
|
||||||
@ -24,19 +25,17 @@ export function initializeEmoji() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (worker) {
|
if (worker) {
|
||||||
// Assign worker to const to make TS happy inside the event listener.
|
|
||||||
const thisWorker = worker;
|
|
||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = setTimeout(() => {
|
||||||
log('worker is not ready after timeout');
|
log('worker is not ready after timeout');
|
||||||
worker = null;
|
worker = null;
|
||||||
void fallbackLoad();
|
void fallbackLoad();
|
||||||
}, WORKER_TIMEOUT);
|
}, WORKER_TIMEOUT);
|
||||||
thisWorker.addEventListener('message', (event: MessageEvent<string>) => {
|
worker.addEventListener('message', (event: MessageEvent<string>) => {
|
||||||
const { data: message } = event;
|
const { data: message } = event;
|
||||||
if (message === 'ready') {
|
if (message === 'ready') {
|
||||||
log('worker ready, loading data');
|
log('worker ready, loading data');
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
thisWorker.postMessage('custom');
|
messageWorker('custom');
|
||||||
void loadEmojiLocale(userLocale);
|
void loadEmojiLocale(userLocale);
|
||||||
// Load English locale as well, because people are still used to
|
// Load English locale as well, because people are still used to
|
||||||
// using it from before we supported other locales.
|
// using it from before we supported other locales.
|
||||||
@ -55,20 +54,35 @@ export function initializeEmoji() {
|
|||||||
async function fallbackLoad() {
|
async function fallbackLoad() {
|
||||||
log('falling back to main thread for loading');
|
log('falling back to main thread for loading');
|
||||||
const { importCustomEmojiData } = await import('./loader');
|
const { importCustomEmojiData } = await import('./loader');
|
||||||
await importCustomEmojiData();
|
const emojis = await importCustomEmojiData();
|
||||||
|
if (emojis) {
|
||||||
|
log('loaded %d custom emojis', emojis.length);
|
||||||
|
}
|
||||||
await loadEmojiLocale(userLocale);
|
await loadEmojiLocale(userLocale);
|
||||||
if (userLocale !== 'en') {
|
if (userLocale !== 'en') {
|
||||||
await loadEmojiLocale('en');
|
await loadEmojiLocale('en');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadEmojiLocale(localeString: string) {
|
async function loadEmojiLocale(localeString: string) {
|
||||||
const locale = toSupportedLocale(localeString);
|
const locale = toSupportedLocale(localeString);
|
||||||
|
const { importEmojiData, localeToPath } = await import('./loader');
|
||||||
|
|
||||||
if (worker) {
|
if (worker) {
|
||||||
worker.postMessage(locale);
|
const path = await localeToPath(locale);
|
||||||
|
log('asking worker to load locale %s from %s', locale, path);
|
||||||
|
messageWorker(locale, path);
|
||||||
} else {
|
} else {
|
||||||
const { importEmojiData } = await import('./loader');
|
const emojis = await importEmojiData(locale);
|
||||||
await importEmojiData(locale);
|
if (emojis) {
|
||||||
|
log('loaded %d emojis to locale %s', emojis.length, locale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function messageWorker(locale: LocaleOrCustom, path?: string) {
|
||||||
|
if (!worker) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
worker.postMessage({ locale, path });
|
||||||
|
}
|
||||||
|
|||||||
@ -8,44 +8,64 @@ import {
|
|||||||
putLatestEtag,
|
putLatestEtag,
|
||||||
} from './database';
|
} from './database';
|
||||||
import { toSupportedLocale, toSupportedLocaleOrCustom } from './locale';
|
import { toSupportedLocale, toSupportedLocaleOrCustom } from './locale';
|
||||||
import type { CustomEmojiData, LocaleOrCustom } from './types';
|
import type { CustomEmojiData } from './types';
|
||||||
import { emojiLogger } from './utils';
|
|
||||||
|
|
||||||
const log = emojiLogger('loader');
|
export async function importEmojiData(localeString: string, path?: string) {
|
||||||
|
|
||||||
export async function importEmojiData(localeString: string) {
|
|
||||||
const locale = toSupportedLocale(localeString);
|
const locale = toSupportedLocale(localeString);
|
||||||
const emojis = await fetchAndCheckEtag<CompactEmoji[]>(locale);
|
|
||||||
|
// Validate the provided path.
|
||||||
|
if (path && !/^[/a-z]*\/packs\/assets\/compact-\w+\.json$/.test(path)) {
|
||||||
|
throw new Error('Invalid path for emoji data');
|
||||||
|
} else {
|
||||||
|
// Otherwise get the path if not provided.
|
||||||
|
path ??= await localeToPath(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
const emojis = await fetchAndCheckEtag<CompactEmoji[]>(locale, path);
|
||||||
if (!emojis) {
|
if (!emojis) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const flattenedEmojis: FlatCompactEmoji[] = flattenEmojiData(emojis);
|
const flattenedEmojis: FlatCompactEmoji[] = flattenEmojiData(emojis);
|
||||||
log('loaded %d for %s locale', flattenedEmojis.length, locale);
|
|
||||||
await putEmojiData(flattenedEmojis, locale);
|
await putEmojiData(flattenedEmojis, locale);
|
||||||
|
return flattenedEmojis;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function importCustomEmojiData() {
|
export async function importCustomEmojiData() {
|
||||||
const emojis = await fetchAndCheckEtag<CustomEmojiData[]>('custom');
|
const emojis = await fetchAndCheckEtag<CustomEmojiData[]>(
|
||||||
|
'custom',
|
||||||
|
'/api/v1/custom_emojis',
|
||||||
|
);
|
||||||
if (!emojis) {
|
if (!emojis) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log('loaded %d custom emojis', emojis.length);
|
|
||||||
await putCustomEmojiData(emojis);
|
await putCustomEmojiData(emojis);
|
||||||
|
return emojis;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchAndCheckEtag<ResultType extends object[]>(
|
const modules = import.meta.glob<string>(
|
||||||
localeOrCustom: LocaleOrCustom,
|
'../../../../../node_modules/emojibase-data/**/compact.json',
|
||||||
|
{
|
||||||
|
query: '?url',
|
||||||
|
import: 'default',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export function localeToPath(locale: Locale) {
|
||||||
|
const key = `../../../../../node_modules/emojibase-data/${locale}/compact.json`;
|
||||||
|
if (!modules[key] || typeof modules[key] !== 'function') {
|
||||||
|
throw new Error(`Unsupported locale: ${locale}`);
|
||||||
|
}
|
||||||
|
return modules[key]();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAndCheckEtag<ResultType extends object[]>(
|
||||||
|
localeString: string,
|
||||||
|
path: string,
|
||||||
): Promise<ResultType | null> {
|
): Promise<ResultType | null> {
|
||||||
const locale = toSupportedLocaleOrCustom(localeOrCustom);
|
const locale = toSupportedLocaleOrCustom(localeString);
|
||||||
|
|
||||||
// Use location.origin as this script may be loaded from a CDN domain.
|
// Use location.origin as this script may be loaded from a CDN domain.
|
||||||
const url = new URL(location.origin);
|
const url = new URL(path, location.origin);
|
||||||
if (locale === 'custom') {
|
|
||||||
url.pathname = '/api/v1/custom_emojis';
|
|
||||||
} else {
|
|
||||||
const modulePath = await localeToPath(locale);
|
|
||||||
url.pathname = modulePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldEtag = await loadLatestEtag(locale);
|
const oldEtag = await loadLatestEtag(locale);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
@ -60,38 +80,20 @@ async function fetchAndCheckEtag<ResultType extends object[]>(
|
|||||||
}
|
}
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to fetch emoji data for ${localeOrCustom}: ${response.statusText}`,
|
`Failed to fetch emoji data for ${locale}: ${response.statusText}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = (await response.json()) as ResultType;
|
const data = (await response.json()) as ResultType;
|
||||||
if (!Array.isArray(data)) {
|
if (!Array.isArray(data)) {
|
||||||
throw new Error(
|
throw new Error(`Unexpected data format for ${locale}: expected an array`);
|
||||||
`Unexpected data format for ${localeOrCustom}: expected an array`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the ETag for future requests
|
// Store the ETag for future requests
|
||||||
const etag = response.headers.get('ETag');
|
const etag = response.headers.get('ETag');
|
||||||
if (etag) {
|
if (etag) {
|
||||||
await putLatestEtag(etag, localeOrCustom);
|
await putLatestEtag(etag, localeString);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const modules = import.meta.glob<string>(
|
|
||||||
'../../../../../node_modules/emojibase-data/**/compact.json',
|
|
||||||
{
|
|
||||||
query: '?url',
|
|
||||||
import: 'default',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
function localeToPath(locale: Locale) {
|
|
||||||
const key = `../../../../../node_modules/emojibase-data/${locale}/compact.json`;
|
|
||||||
if (!modules[key] || typeof modules[key] !== 'function') {
|
|
||||||
throw new Error(`Unsupported locale: ${locale}`);
|
|
||||||
}
|
|
||||||
return modules[key]();
|
|
||||||
}
|
|
||||||
|
|||||||
@ -162,7 +162,7 @@ describe('loadEmojiDataToState', () => {
|
|||||||
const dbCall = vi
|
const dbCall = vi
|
||||||
.spyOn(db, 'loadEmojiByHexcode')
|
.spyOn(db, 'loadEmojiByHexcode')
|
||||||
.mockRejectedValue(new db.LocaleNotLoadedError('en'));
|
.mockRejectedValue(new db.LocaleNotLoadedError('en'));
|
||||||
vi.spyOn(loader, 'importEmojiData').mockResolvedValueOnce();
|
vi.spyOn(loader, 'importEmojiData').mockResolvedValueOnce(undefined);
|
||||||
const consoleCall = vi
|
const consoleCall = vi
|
||||||
.spyOn(console, 'warn')
|
.spyOn(console, 'warn')
|
||||||
.mockImplementationOnce(() => null);
|
.mockImplementationOnce(() => null);
|
||||||
|
|||||||
@ -1,18 +1,25 @@
|
|||||||
import { importEmojiData, importCustomEmojiData } from './loader';
|
import { importCustomEmojiData, importEmojiData } from './loader';
|
||||||
|
|
||||||
addEventListener('message', handleMessage);
|
addEventListener('message', handleMessage);
|
||||||
self.postMessage('ready'); // After the worker is ready, notify the main thread
|
self.postMessage('ready'); // After the worker is ready, notify the main thread
|
||||||
|
|
||||||
function handleMessage(event: MessageEvent<string>) {
|
function handleMessage(event: MessageEvent<{ locale: string; path?: string }>) {
|
||||||
const { data: locale } = event;
|
const {
|
||||||
void loadData(locale);
|
data: { locale, path },
|
||||||
|
} = event;
|
||||||
|
void loadData(locale, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadData(locale: string) {
|
async function loadData(locale: string, path?: string) {
|
||||||
if (locale !== 'custom') {
|
let importCount: number | undefined;
|
||||||
await importEmojiData(locale);
|
if (locale === 'custom') {
|
||||||
|
importCount = (await importCustomEmojiData())?.length;
|
||||||
|
} else if (path) {
|
||||||
|
importCount = (await importEmojiData(locale, path))?.length;
|
||||||
} else {
|
} else {
|
||||||
await importCustomEmojiData();
|
throw new Error('Path is required for loading locale emoji data');
|
||||||
|
}
|
||||||
|
if (importCount) {
|
||||||
|
self.postMessage(`loaded ${importCount} emojis into ${locale}`);
|
||||||
}
|
}
|
||||||
self.postMessage(`loaded ${locale}`);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,7 +75,7 @@ export const DisabledAccountBanner: React.FC = () => {
|
|||||||
</a>
|
</a>
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
className='button button--block button-tertiary'
|
className='button button--block button-secondary'
|
||||||
onClick={handleLogOutClick}
|
onClick={handleLogOutClick}
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
|||||||
@ -46,7 +46,7 @@ export const SignInBanner: React.FC = () => {
|
|||||||
<a
|
<a
|
||||||
href={sso_redirect}
|
href={sso_redirect}
|
||||||
data-method='post'
|
data-method='post'
|
||||||
className='button button--block button-tertiary'
|
className='button button--block button-secondary'
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='sign_in_banner.sso_redirect'
|
id='sign_in_banner.sso_redirect'
|
||||||
@ -98,7 +98,7 @@ export const SignInBanner: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
{signupButton}
|
{signupButton}
|
||||||
<a href='/auth/sign_in' className='button button--block button-tertiary'>
|
<a href='/auth/sign_in' className='button button--block button-secondary'>
|
||||||
<FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Login' />
|
<FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Login' />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
@typescript-eslint/no-unsafe-assignment */
|
@typescript-eslint/no-unsafe-assignment */
|
||||||
|
|
||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties } from 'react';
|
||||||
import { useState, useRef, useCallback } from 'react';
|
import { useState, useRef, useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
@ -55,6 +55,8 @@ export const DetailedStatus: React.FC<{
|
|||||||
pictureInPicture: any;
|
pictureInPicture: any;
|
||||||
onToggleHidden?: (status: any) => void;
|
onToggleHidden?: (status: any) => void;
|
||||||
onToggleMediaVisibility?: () => void;
|
onToggleMediaVisibility?: () => void;
|
||||||
|
ancestors?: number;
|
||||||
|
multiColumn?: boolean;
|
||||||
}> = ({
|
}> = ({
|
||||||
status,
|
status,
|
||||||
onOpenMedia,
|
onOpenMedia,
|
||||||
@ -69,6 +71,8 @@ export const DetailedStatus: React.FC<{
|
|||||||
pictureInPicture,
|
pictureInPicture,
|
||||||
onToggleMediaVisibility,
|
onToggleMediaVisibility,
|
||||||
onToggleHidden,
|
onToggleHidden,
|
||||||
|
ancestors = 0,
|
||||||
|
multiColumn = false,
|
||||||
}) => {
|
}) => {
|
||||||
const properStatus = status?.get('reblog') ?? status;
|
const properStatus = status?.get('reblog') ?? status;
|
||||||
const [height, setHeight] = useState(0);
|
const [height, setHeight] = useState(0);
|
||||||
@ -123,6 +127,30 @@ export const DetailedStatus: React.FC<{
|
|||||||
if (onTranslate) onTranslate(status);
|
if (onTranslate) onTranslate(status);
|
||||||
}, [onTranslate, status]);
|
}, [onTranslate, status]);
|
||||||
|
|
||||||
|
// The component is managed and will change if the status changes
|
||||||
|
// Ancestors can increase when loading a thread, in which case we want to scroll,
|
||||||
|
// or decrease if a post is deleted, in which case we don't want to mess with it
|
||||||
|
const previousAncestors = useRef(-1);
|
||||||
|
useEffect(() => {
|
||||||
|
if (nodeRef.current && previousAncestors.current < ancestors) {
|
||||||
|
nodeRef.current.scrollIntoView(true);
|
||||||
|
|
||||||
|
// In the single-column interface, `scrollIntoView` will put the post behind the header, so compensate for that.
|
||||||
|
if (!multiColumn) {
|
||||||
|
const offset = document
|
||||||
|
.querySelector('.column-header__wrapper')
|
||||||
|
?.getBoundingClientRect().bottom;
|
||||||
|
|
||||||
|
if (offset) {
|
||||||
|
const scrollingElement = document.scrollingElement ?? document.body;
|
||||||
|
scrollingElement.scrollBy(0, -offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousAncestors.current = ancestors;
|
||||||
|
}, [ancestors, multiColumn]);
|
||||||
|
|
||||||
if (!properStatus) {
|
if (!properStatus) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -164,8 +164,6 @@ class Status extends ImmutablePureComponent {
|
|||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
attachFullscreenListener(this.onFullScreenChange);
|
attachFullscreenListener(this.onFullScreenChange);
|
||||||
|
|
||||||
this._scrollStatusIntoView();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps (nextProps) {
|
UNSAFE_componentWillReceiveProps (nextProps) {
|
||||||
@ -487,35 +485,11 @@ class Status extends ImmutablePureComponent {
|
|||||||
this.statusNode = c;
|
this.statusNode = c;
|
||||||
};
|
};
|
||||||
|
|
||||||
_scrollStatusIntoView () {
|
|
||||||
const { status, multiColumn } = this.props;
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
requestIdleCallback(() => {
|
|
||||||
this.statusNode?.scrollIntoView(true);
|
|
||||||
|
|
||||||
// In the single-column interface, `scrollIntoView` will put the post behind the header,
|
|
||||||
// so compensate for that.
|
|
||||||
if (!multiColumn) {
|
|
||||||
const offset = document.querySelector('.column-header__wrapper')?.getBoundingClientRect()?.bottom;
|
|
||||||
if (offset) {
|
|
||||||
const scrollingElement = document.scrollingElement || document.body;
|
|
||||||
scrollingElement.scrollBy(0, -offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate (prevProps) {
|
componentDidUpdate (prevProps) {
|
||||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
const { status, descendantsIds } = this.props;
|
||||||
|
|
||||||
const isSameStatus = status && (prevProps.status?.get('id') === status.get('id'));
|
const isSameStatus = status && (prevProps.status?.get('id') === status.get('id'));
|
||||||
|
|
||||||
if (status && (ancestorsIds.length > prevProps.ancestorsIds.length || !isSameStatus)) {
|
|
||||||
this._scrollStatusIntoView();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only highlight replies after the initial load
|
// Only highlight replies after the initial load
|
||||||
if (prevProps.descendantsIds.length && isSameStatus) {
|
if (prevProps.descendantsIds.length && isSameStatus) {
|
||||||
const newRepliesIds = difference(descendantsIds, prevProps.descendantsIds);
|
const newRepliesIds = difference(descendantsIds, prevProps.descendantsIds);
|
||||||
@ -619,6 +593,8 @@ class Status extends ImmutablePureComponent {
|
|||||||
showMedia={this.state.showMedia}
|
showMedia={this.state.showMedia}
|
||||||
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
||||||
pictureInPicture={pictureInPicture}
|
pictureInPicture={pictureInPicture}
|
||||||
|
ancestors={this.props.ancestorsIds.length}
|
||||||
|
multiColumn={multiColumn}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ActionBar
|
<ActionBar
|
||||||
|
|||||||
@ -98,7 +98,7 @@ class BundleColumnError extends PureComponent {
|
|||||||
<div className='error-column__message__actions'>
|
<div className='error-column__message__actions'>
|
||||||
{errorType === 'network' && <Button onClick={this.handleRetry}><FormattedMessage id='bundle_column_error.retry' defaultMessage='Try again' /></Button>}
|
{errorType === 'network' && <Button onClick={this.handleRetry}><FormattedMessage id='bundle_column_error.retry' defaultMessage='Try again' /></Button>}
|
||||||
{errorType === 'error' && <CopyButton value={stacktrace}><FormattedMessage id='bundle_column_error.copy_stacktrace' defaultMessage='Copy error report' /></CopyButton>}
|
{errorType === 'error' && <CopyButton value={stacktrace}><FormattedMessage id='bundle_column_error.copy_stacktrace' defaultMessage='Copy error report' /></CopyButton>}
|
||||||
<Link to='/' className={classNames('button', { 'button-tertiary': errorType !== 'routing' })}><FormattedMessage id='bundle_column_error.return' defaultMessage='Go back home' /></Link>
|
<Link to='/' className={classNames('button', { 'button-secondary': errorType !== 'routing' })}><FormattedMessage id='bundle_column_error.return' defaultMessage='Go back home' /></Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -46,7 +46,7 @@ export const ModalPlaceholder: React.FC<{
|
|||||||
defaultMessage='Try again'
|
defaultMessage='Try again'
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={handleClose} className='button button-tertiary'>
|
<Button onClick={handleClose} className='button button-secondary'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='bundle_modal_error.close'
|
id='bundle_modal_error.close'
|
||||||
defaultMessage='Close'
|
defaultMessage='Close'
|
||||||
|
|||||||
@ -104,7 +104,7 @@ const LoginOrSignUp: React.FC = () => {
|
|||||||
<a
|
<a
|
||||||
href={sso_redirect}
|
href={sso_redirect}
|
||||||
data-method='post'
|
data-method='post'
|
||||||
className='button button--block button-tertiary'
|
className='button button--block button-secondary'
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='sign_in_banner.sso_redirect'
|
id='sign_in_banner.sso_redirect'
|
||||||
@ -143,7 +143,7 @@ const LoginOrSignUp: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className='ui__navigation-bar__sign-up'>
|
<div className='ui__navigation-bar__sign-up'>
|
||||||
{signupButton}
|
{signupButton}
|
||||||
<a href='/auth/sign_in' className='button button-tertiary'>
|
<a href='/auth/sign_in' className='button button-secondary'>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='sign_in_banner.sign_in'
|
id='sign_in_banner.sign_in'
|
||||||
defaultMessage='Login'
|
defaultMessage='Login'
|
||||||
|
|||||||
@ -903,6 +903,7 @@
|
|||||||
"status.edited_x_times": "Upraveno {count, plural, one {{count}krát} few {{count}krát} many {{count}krát} other {{count}krát}}",
|
"status.edited_x_times": "Upraveno {count, plural, one {{count}krát} few {{count}krát} many {{count}krát} other {{count}krát}}",
|
||||||
"status.embed": "Získejte kód pro vložení",
|
"status.embed": "Získejte kód pro vložení",
|
||||||
"status.favourite": "Oblíbit",
|
"status.favourite": "Oblíbit",
|
||||||
|
"status.favourites_count": "{count, plural, one {{counter} oblíbený} few {{counter} oblíbené} many {{counter} oblíbených} other {{counter} oblíbených}}",
|
||||||
"status.filter": "Filtrovat tento příspěvek",
|
"status.filter": "Filtrovat tento příspěvek",
|
||||||
"status.history.created": "Uživatel {name} vytvořil {date}",
|
"status.history.created": "Uživatel {name} vytvořil {date}",
|
||||||
"status.history.edited": "Uživatel {name} upravil {date}",
|
"status.history.edited": "Uživatel {name} upravil {date}",
|
||||||
@ -937,12 +938,14 @@
|
|||||||
"status.quotes.empty": "Tento příspěvek zatím nikdo necitoval. Pokud tak někdo učiní, uvidíte to zde.",
|
"status.quotes.empty": "Tento příspěvek zatím nikdo necitoval. Pokud tak někdo učiní, uvidíte to zde.",
|
||||||
"status.quotes.local_other_disclaimer": "Citace zamítnuté autorem nebudou zobrazeny.",
|
"status.quotes.local_other_disclaimer": "Citace zamítnuté autorem nebudou zobrazeny.",
|
||||||
"status.quotes.remote_other_disclaimer": "Pouze citace z {domain} zde budou zaručeně ukázány. Citace zamítnuté autorem nebudou zobrazeny.",
|
"status.quotes.remote_other_disclaimer": "Pouze citace z {domain} zde budou zaručeně ukázány. Citace zamítnuté autorem nebudou zobrazeny.",
|
||||||
|
"status.quotes_count": "{count, plural, one {{counter} citace} few {{counter} citace} many {{counter} citací} other {{counter} citací}}",
|
||||||
"status.read_more": "Číst více",
|
"status.read_more": "Číst více",
|
||||||
"status.reblog": "Boostnout",
|
"status.reblog": "Boostnout",
|
||||||
"status.reblog_or_quote": "Boostnout nebo citovat",
|
"status.reblog_or_quote": "Boostnout nebo citovat",
|
||||||
"status.reblog_private": "Sdílejte znovu se svými sledujícími",
|
"status.reblog_private": "Sdílejte znovu se svými sledujícími",
|
||||||
"status.reblogged_by": "Uživatel {name} boostnul",
|
"status.reblogged_by": "Uživatel {name} boostnul",
|
||||||
"status.reblogs.empty": "Tento příspěvek ještě nikdo neboostnul. Pokud to někdo udělá, zobrazí se zde.",
|
"status.reblogs.empty": "Tento příspěvek ještě nikdo neboostnul. Pokud to někdo udělá, zobrazí se zde.",
|
||||||
|
"status.reblogs_count": "{count, plural, one {{counter} boost} few {{counter} boosty} many {{counter} boostů} other {{counter} boostů}}",
|
||||||
"status.redraft": "Smazat a přepsat",
|
"status.redraft": "Smazat a přepsat",
|
||||||
"status.remove_bookmark": "Odstranit ze záložek",
|
"status.remove_bookmark": "Odstranit ze záložek",
|
||||||
"status.remove_favourite": "Odebrat z oblíbených",
|
"status.remove_favourite": "Odebrat z oblíbených",
|
||||||
|
|||||||
@ -903,6 +903,7 @@
|
|||||||
"status.edited_x_times": "Golygwyd {count, plural, one {{count} gwaith} other {{count} gwaith}}",
|
"status.edited_x_times": "Golygwyd {count, plural, one {{count} gwaith} other {{count} gwaith}}",
|
||||||
"status.embed": "Cael y cod mewnblannu",
|
"status.embed": "Cael y cod mewnblannu",
|
||||||
"status.favourite": "Ffafrio",
|
"status.favourite": "Ffafrio",
|
||||||
|
"status.favourites_count": "{count, plural, one {{counter} ffefryn} other {{counter} ffefryn}}",
|
||||||
"status.filter": "Hidlo'r postiad hwn",
|
"status.filter": "Hidlo'r postiad hwn",
|
||||||
"status.history.created": "Crëwyd gan {name} {date}",
|
"status.history.created": "Crëwyd gan {name} {date}",
|
||||||
"status.history.edited": "Golygwyd gan {name} {date}",
|
"status.history.edited": "Golygwyd gan {name} {date}",
|
||||||
@ -937,12 +938,14 @@
|
|||||||
"status.quotes.empty": "Does neb wedi dyfynnu'r postiad hwn eto. Pan fydd rhywun yn gwneud hynny, bydd yn ymddangos yma.",
|
"status.quotes.empty": "Does neb wedi dyfynnu'r postiad hwn eto. Pan fydd rhywun yn gwneud hynny, bydd yn ymddangos yma.",
|
||||||
"status.quotes.local_other_disclaimer": "Bydd dyfyniadau wedi'u gwrthod gan yr awdur ddim yn cael eu dangos.",
|
"status.quotes.local_other_disclaimer": "Bydd dyfyniadau wedi'u gwrthod gan yr awdur ddim yn cael eu dangos.",
|
||||||
"status.quotes.remote_other_disclaimer": "Dim ond dyfyniadau o {domain} sy'n siŵr o gael eu dangos yma. Bydd dyfyniadau wedi'u gwrthod gan yr awdur ddim yn cael eu dangos.",
|
"status.quotes.remote_other_disclaimer": "Dim ond dyfyniadau o {domain} sy'n siŵr o gael eu dangos yma. Bydd dyfyniadau wedi'u gwrthod gan yr awdur ddim yn cael eu dangos.",
|
||||||
|
"status.quotes_count": "{count, plural, one {{counter} dyfyniad} other {{counter} dyfyniad}}",
|
||||||
"status.read_more": "Darllen rhagor",
|
"status.read_more": "Darllen rhagor",
|
||||||
"status.reblog": "Hybu",
|
"status.reblog": "Hybu",
|
||||||
"status.reblog_or_quote": "Hybu neu ddyfynnu",
|
"status.reblog_or_quote": "Hybu neu ddyfynnu",
|
||||||
"status.reblog_private": "Rhannwch eto gyda'ch dilynwyr",
|
"status.reblog_private": "Rhannwch eto gyda'ch dilynwyr",
|
||||||
"status.reblogged_by": "Hybodd {name}",
|
"status.reblogged_by": "Hybodd {name}",
|
||||||
"status.reblogs.empty": "Does neb wedi hybio'r post yma eto. Pan y bydd rhywun yn gwneud, byddent yn ymddangos yma.",
|
"status.reblogs.empty": "Does neb wedi hybio'r post yma eto. Pan y bydd rhywun yn gwneud, byddent yn ymddangos yma.",
|
||||||
|
"status.reblogs_count": "{count, plural, one {{counter} hwb} other {{counter} hwb}}",
|
||||||
"status.redraft": "Dileu ac ail lunio",
|
"status.redraft": "Dileu ac ail lunio",
|
||||||
"status.remove_bookmark": "Tynnu nod tudalen",
|
"status.remove_bookmark": "Tynnu nod tudalen",
|
||||||
"status.remove_favourite": "Tynnu o'r ffefrynnau",
|
"status.remove_favourite": "Tynnu o'r ffefrynnau",
|
||||||
|
|||||||
@ -231,7 +231,7 @@
|
|||||||
"confirmations.delete_list.title": "Slet liste?",
|
"confirmations.delete_list.title": "Slet liste?",
|
||||||
"confirmations.discard_draft.confirm": "Kassér og fortsæt",
|
"confirmations.discard_draft.confirm": "Kassér og fortsæt",
|
||||||
"confirmations.discard_draft.edit.cancel": "Fortsæt redigering",
|
"confirmations.discard_draft.edit.cancel": "Fortsæt redigering",
|
||||||
"confirmations.discard_draft.edit.message": "Hvis du fortsætter, kasseres alle ændringer, du har foretaget i det indlæg, du er i gang med at redigere.",
|
"confirmations.discard_draft.edit.message": "Hvis du fortsætter, vil alle ændringer, du har foretaget i det indlæg, du er er ved at redigere, blive slettet.",
|
||||||
"confirmations.discard_draft.edit.title": "Kassér ændringer til dit indlæg?",
|
"confirmations.discard_draft.edit.title": "Kassér ændringer til dit indlæg?",
|
||||||
"confirmations.discard_draft.post.cancel": "Genoptag udkast",
|
"confirmations.discard_draft.post.cancel": "Genoptag udkast",
|
||||||
"confirmations.discard_draft.post.message": "Hvis du fortsætter, kasseres det indlæg, du er i gang med at udforme.",
|
"confirmations.discard_draft.post.message": "Hvis du fortsætter, kasseres det indlæg, du er i gang med at udforme.",
|
||||||
@ -507,7 +507,7 @@
|
|||||||
"keyboard_shortcuts.pinned": "Åbn liste over fastgjorte indlæg",
|
"keyboard_shortcuts.pinned": "Åbn liste over fastgjorte indlæg",
|
||||||
"keyboard_shortcuts.profile": "Åbn forfatters profil",
|
"keyboard_shortcuts.profile": "Åbn forfatters profil",
|
||||||
"keyboard_shortcuts.quote": "Citér indlæg",
|
"keyboard_shortcuts.quote": "Citér indlæg",
|
||||||
"keyboard_shortcuts.reply": "Besvar indlægget",
|
"keyboard_shortcuts.reply": "Besvar indlæg",
|
||||||
"keyboard_shortcuts.requests": "Åbn liste over følgeanmodninger",
|
"keyboard_shortcuts.requests": "Åbn liste over følgeanmodninger",
|
||||||
"keyboard_shortcuts.search": "Fokusér søgebjælke",
|
"keyboard_shortcuts.search": "Fokusér søgebjælke",
|
||||||
"keyboard_shortcuts.spoilers": "Vis/skjul indholdsadvarsel-felt",
|
"keyboard_shortcuts.spoilers": "Vis/skjul indholdsadvarsel-felt",
|
||||||
@ -675,7 +675,7 @@
|
|||||||
"notifications.column_settings.filter_bar.category": "Hurtigfiltreringsbjælke",
|
"notifications.column_settings.filter_bar.category": "Hurtigfiltreringsbjælke",
|
||||||
"notifications.column_settings.follow": "Nye følgere:",
|
"notifications.column_settings.follow": "Nye følgere:",
|
||||||
"notifications.column_settings.follow_request": "Nye følgeanmodninger:",
|
"notifications.column_settings.follow_request": "Nye følgeanmodninger:",
|
||||||
"notifications.column_settings.group": "Gruppere",
|
"notifications.column_settings.group": "Gruppér",
|
||||||
"notifications.column_settings.mention": "Omtaler:",
|
"notifications.column_settings.mention": "Omtaler:",
|
||||||
"notifications.column_settings.poll": "Afstemningsresultater:",
|
"notifications.column_settings.poll": "Afstemningsresultater:",
|
||||||
"notifications.column_settings.push": "Push-notifikationer",
|
"notifications.column_settings.push": "Push-notifikationer",
|
||||||
@ -764,7 +764,7 @@
|
|||||||
"privacy_policy.last_updated": "Senest opdateret {date}",
|
"privacy_policy.last_updated": "Senest opdateret {date}",
|
||||||
"privacy_policy.title": "Privatlivspolitik",
|
"privacy_policy.title": "Privatlivspolitik",
|
||||||
"quote_error.edit": "Citater kan ikke tilføjes ved redigering af et indlæg.",
|
"quote_error.edit": "Citater kan ikke tilføjes ved redigering af et indlæg.",
|
||||||
"quote_error.poll": "Citering ikke tilladt i afstemninger.",
|
"quote_error.poll": "Citering er ikke tilladt med afstemninger.",
|
||||||
"quote_error.private_mentions": "Citering er ikke tilladt med direkte omtaler.",
|
"quote_error.private_mentions": "Citering er ikke tilladt med direkte omtaler.",
|
||||||
"quote_error.quote": "Kun ét citat ad gangen er tilladt.",
|
"quote_error.quote": "Kun ét citat ad gangen er tilladt.",
|
||||||
"quote_error.unauthorized": "Du har ikke tilladelse til at citere dette indlæg.",
|
"quote_error.unauthorized": "Du har ikke tilladelse til at citere dette indlæg.",
|
||||||
@ -867,7 +867,7 @@
|
|||||||
"search_results.title": "Søg efter \"{q}\"",
|
"search_results.title": "Søg efter \"{q}\"",
|
||||||
"server_banner.about_active_users": "Personer, som brugte denne server de seneste 30 dage (månedlige aktive brugere)",
|
"server_banner.about_active_users": "Personer, som brugte denne server de seneste 30 dage (månedlige aktive brugere)",
|
||||||
"server_banner.active_users": "aktive brugere",
|
"server_banner.active_users": "aktive brugere",
|
||||||
"server_banner.administered_by": "Håndteres af:",
|
"server_banner.administered_by": "Administreret af:",
|
||||||
"server_banner.is_one_of_many": "{domain} er en af de mange uafhængige Mastodon-servere, du kan bruge for at deltage i fediverset.",
|
"server_banner.is_one_of_many": "{domain} er en af de mange uafhængige Mastodon-servere, du kan bruge for at deltage i fediverset.",
|
||||||
"server_banner.server_stats": "Serverstatstik:",
|
"server_banner.server_stats": "Serverstatstik:",
|
||||||
"sign_in_banner.create_account": "Opret konto",
|
"sign_in_banner.create_account": "Opret konto",
|
||||||
@ -902,7 +902,7 @@
|
|||||||
"status.edited": "Senest redigeret {date}",
|
"status.edited": "Senest redigeret {date}",
|
||||||
"status.edited_x_times": "Redigeret {count, plural, one {{count} gang} other {{count} gange}}",
|
"status.edited_x_times": "Redigeret {count, plural, one {{count} gang} other {{count} gange}}",
|
||||||
"status.embed": "Hent indlejringskode",
|
"status.embed": "Hent indlejringskode",
|
||||||
"status.favourite": "Favorit",
|
"status.favourite": "Favoritmarkér",
|
||||||
"status.favourites_count": "{count, plural, one {{counter} favorit} other {{counter} favoritter}}",
|
"status.favourites_count": "{count, plural, one {{counter} favorit} other {{counter} favoritter}}",
|
||||||
"status.filter": "Filtrér dette indlæg",
|
"status.filter": "Filtrér dette indlæg",
|
||||||
"status.history.created": "{name} oprettet {date}",
|
"status.history.created": "{name} oprettet {date}",
|
||||||
@ -991,7 +991,7 @@
|
|||||||
"units.short.million": "{count} mio.",
|
"units.short.million": "{count} mio.",
|
||||||
"units.short.thousand": "{count} tusind",
|
"units.short.thousand": "{count} tusind",
|
||||||
"upload_area.title": "Træk og slip for at uploade",
|
"upload_area.title": "Træk og slip for at uploade",
|
||||||
"upload_button.label": "Tilføj billed-, video- eller lydfil(er)",
|
"upload_button.label": "Tilføj billeder, en video- eller lydfil",
|
||||||
"upload_error.limit": "Grænse for filupload nået.",
|
"upload_error.limit": "Grænse for filupload nået.",
|
||||||
"upload_error.poll": "Filupload ikke tilladt for afstemninger.",
|
"upload_error.poll": "Filupload ikke tilladt for afstemninger.",
|
||||||
"upload_error.quote": "Fil-upload ikke tilladt i citater.",
|
"upload_error.quote": "Fil-upload ikke tilladt i citater.",
|
||||||
|
|||||||
@ -242,7 +242,7 @@
|
|||||||
"confirmations.follow_to_list.message": "Du musst {name} folgen, um das Profil zu einer Liste hinzufügen zu können.",
|
"confirmations.follow_to_list.message": "Du musst {name} folgen, um das Profil zu einer Liste hinzufügen zu können.",
|
||||||
"confirmations.follow_to_list.title": "Profil folgen?",
|
"confirmations.follow_to_list.title": "Profil folgen?",
|
||||||
"confirmations.logout.confirm": "Abmelden",
|
"confirmations.logout.confirm": "Abmelden",
|
||||||
"confirmations.logout.message": "Möchtest du dich wirklich abmelden?",
|
"confirmations.logout.message": "Bist du sicher, dass du dich abmelden möchtest?",
|
||||||
"confirmations.logout.title": "Abmelden?",
|
"confirmations.logout.title": "Abmelden?",
|
||||||
"confirmations.missing_alt_text.confirm": "Bildbeschreibung hinzufügen",
|
"confirmations.missing_alt_text.confirm": "Bildbeschreibung hinzufügen",
|
||||||
"confirmations.missing_alt_text.message": "Dein Beitrag enthält Medien ohne Bildbeschreibung. Mit ALT-Texten erreichst Du auch Menschen, die blind oder sehbehindert sind.",
|
"confirmations.missing_alt_text.message": "Dein Beitrag enthält Medien ohne Bildbeschreibung. Mit ALT-Texten erreichst Du auch Menschen, die blind oder sehbehindert sind.",
|
||||||
@ -304,12 +304,12 @@
|
|||||||
"domain_pill.activitypub_lets_connect": "Somit kannst du dich nicht nur auf Mastodon mit Leuten verbinden und mit ihnen interagieren, sondern über alle sozialen Apps hinweg.",
|
"domain_pill.activitypub_lets_connect": "Somit kannst du dich nicht nur auf Mastodon mit Leuten verbinden und mit ihnen interagieren, sondern über alle sozialen Apps hinweg.",
|
||||||
"domain_pill.activitypub_like_language": "ActivityPub ist sozusagen die Sprache, die Mastodon mit anderen sozialen Netzwerken spricht.",
|
"domain_pill.activitypub_like_language": "ActivityPub ist sozusagen die Sprache, die Mastodon mit anderen sozialen Netzwerken spricht.",
|
||||||
"domain_pill.server": "Server",
|
"domain_pill.server": "Server",
|
||||||
"domain_pill.their_handle": "Deren Adresse:",
|
"domain_pill.their_handle": "Die vollständige Adresse:",
|
||||||
"domain_pill.their_server": "Deren digitale Heimat. Hier „leben“ alle Beiträge von diesem Profil.",
|
"domain_pill.their_server": "Die digitale Heimat, in der sich alle Beiträge dieses Profils befinden.",
|
||||||
"domain_pill.their_username": "Deren eindeutigen Identität auf dem betreffenden Server. Es ist möglich, Profile mit dem gleichen Profilnamen auf verschiedenen Servern zu finden.",
|
"domain_pill.their_username": "Die eindeutige Identifizierung auf einem Server. Es ist möglich, denselben Profilnamen auf verschiedenen Servern im Fediverse zu finden.",
|
||||||
"domain_pill.username": "Profilname",
|
"domain_pill.username": "Profilname",
|
||||||
"domain_pill.whats_in_a_handle": "Woraus besteht eine Adresse?",
|
"domain_pill.whats_in_a_handle": "Woraus besteht eine Adresse?",
|
||||||
"domain_pill.who_they_are": "Adressen teilen mit, wer jemand ist und wo sich jemand aufhält. Daher kannst du mit Leuten im gesamten Social Web interagieren, wenn es eine durch <button>ActivityPub angetriebene Plattform</button> ist.",
|
"domain_pill.who_they_are": "Adressen teilen mit, wer jemand ist und wo sich jemand im Fediverse aufhält. Daher kannst du mit Leuten im gesamten Social Web interagieren, wenn es eine durch <button>ActivityPub angetriebene Plattform</button> ist.",
|
||||||
"domain_pill.who_you_are": "Deine Adresse teilt mit, wer du bist und wo du dich aufhältst. Daher können andere Leute im gesamten Social Web mit dir interagieren, wenn es eine durch <button>ActivityPub angetriebene Plattform</button> ist.",
|
"domain_pill.who_you_are": "Deine Adresse teilt mit, wer du bist und wo du dich aufhältst. Daher können andere Leute im gesamten Social Web mit dir interagieren, wenn es eine durch <button>ActivityPub angetriebene Plattform</button> ist.",
|
||||||
"domain_pill.your_handle": "Deine Adresse:",
|
"domain_pill.your_handle": "Deine Adresse:",
|
||||||
"domain_pill.your_server": "Deine digitale Heimat. Hier „leben“ alle Beiträge von dir. Falls es dir hier nicht gefällt, kannst du jederzeit den Server wechseln und ebenso deine Follower übertragen.",
|
"domain_pill.your_server": "Deine digitale Heimat. Hier „leben“ alle Beiträge von dir. Falls es dir hier nicht gefällt, kannst du jederzeit den Server wechseln und ebenso deine Follower übertragen.",
|
||||||
@ -584,8 +584,8 @@
|
|||||||
"navigation_bar.follows_and_followers": "Follower und Folge ich",
|
"navigation_bar.follows_and_followers": "Follower und Folge ich",
|
||||||
"navigation_bar.import_export": "Importieren und exportieren",
|
"navigation_bar.import_export": "Importieren und exportieren",
|
||||||
"navigation_bar.lists": "Listen",
|
"navigation_bar.lists": "Listen",
|
||||||
"navigation_bar.live_feed_local": "Live-Feed (lokal)",
|
"navigation_bar.live_feed_local": "Live-Feed (Dieser Server)",
|
||||||
"navigation_bar.live_feed_public": "Live-Feed (öffentlich)",
|
"navigation_bar.live_feed_public": "Live-Feed (Alle Server)",
|
||||||
"navigation_bar.logout": "Abmelden",
|
"navigation_bar.logout": "Abmelden",
|
||||||
"navigation_bar.moderation": "Moderation",
|
"navigation_bar.moderation": "Moderation",
|
||||||
"navigation_bar.more": "Mehr",
|
"navigation_bar.more": "Mehr",
|
||||||
@ -693,7 +693,7 @@
|
|||||||
"notifications.filter.follows": "Folgt",
|
"notifications.filter.follows": "Folgt",
|
||||||
"notifications.filter.mentions": "Erwähnungen",
|
"notifications.filter.mentions": "Erwähnungen",
|
||||||
"notifications.filter.polls": "Umfrageergebnisse",
|
"notifications.filter.polls": "Umfrageergebnisse",
|
||||||
"notifications.filter.statuses": "Neue Beiträge von Personen, denen du folgst",
|
"notifications.filter.statuses": "Neue Beiträge von Profilen, denen du folgst",
|
||||||
"notifications.grant_permission": "Berechtigung erteilen.",
|
"notifications.grant_permission": "Berechtigung erteilen.",
|
||||||
"notifications.group": "{count} Benachrichtigungen",
|
"notifications.group": "{count} Benachrichtigungen",
|
||||||
"notifications.mark_as_read": "Alle Benachrichtigungen als gelesen markieren",
|
"notifications.mark_as_read": "Alle Benachrichtigungen als gelesen markieren",
|
||||||
@ -903,7 +903,7 @@
|
|||||||
"status.edited_x_times": "{count, plural, one {{count}-mal} other {{count}-mal}} bearbeitet",
|
"status.edited_x_times": "{count, plural, one {{count}-mal} other {{count}-mal}} bearbeitet",
|
||||||
"status.embed": "Code zum Einbetten",
|
"status.embed": "Code zum Einbetten",
|
||||||
"status.favourite": "Favorisieren",
|
"status.favourite": "Favorisieren",
|
||||||
"status.favourites_count": "{count, plural, one {{counter} Favorit} other {{counter} Favoriten}}",
|
"status.favourites_count": "{count, plural, one {{counter} Mal favorisiert} other {{counter} Mal favorisiert}}",
|
||||||
"status.filter": "Beitrag filtern",
|
"status.filter": "Beitrag filtern",
|
||||||
"status.history.created": "{name} erstellte {date}",
|
"status.history.created": "{name} erstellte {date}",
|
||||||
"status.history.edited": "{name} bearbeitete {date}",
|
"status.history.edited": "{name} bearbeitete {date}",
|
||||||
@ -938,14 +938,14 @@
|
|||||||
"status.quotes.empty": "Diesen Beitrag hat bisher noch niemand zitiert. Sobald es jemand tut, wird das Profil hier erscheinen.",
|
"status.quotes.empty": "Diesen Beitrag hat bisher noch niemand zitiert. Sobald es jemand tut, wird das Profil hier erscheinen.",
|
||||||
"status.quotes.local_other_disclaimer": "Durch Autor*in abgelehnte Zitate werden nicht angezeigt.",
|
"status.quotes.local_other_disclaimer": "Durch Autor*in abgelehnte Zitate werden nicht angezeigt.",
|
||||||
"status.quotes.remote_other_disclaimer": "Nur Zitate von {domain} werden hier garantiert angezeigt. Durch Autor*in abgelehnte Zitate werden nicht angezeigt.",
|
"status.quotes.remote_other_disclaimer": "Nur Zitate von {domain} werden hier garantiert angezeigt. Durch Autor*in abgelehnte Zitate werden nicht angezeigt.",
|
||||||
"status.quotes_count": "{count, plural, one {{counter} zitierter Beitrag} other {{counter} zitierte Beiträge}}",
|
"status.quotes_count": "{count, plural, one {{counter} Mal zitiert} other {{counter} Mal zitiert}}",
|
||||||
"status.read_more": "Gesamten Beitrag anschauen",
|
"status.read_more": "Gesamten Beitrag anschauen",
|
||||||
"status.reblog": "Teilen",
|
"status.reblog": "Teilen",
|
||||||
"status.reblog_or_quote": "Teilen oder zitieren",
|
"status.reblog_or_quote": "Teilen oder zitieren",
|
||||||
"status.reblog_private": "Erneut mit deinen Followern teilen",
|
"status.reblog_private": "Erneut mit deinen Followern teilen",
|
||||||
"status.reblogged_by": "{name} teilte",
|
"status.reblogged_by": "{name} teilte",
|
||||||
"status.reblogs.empty": "Diesen Beitrag hat bisher noch niemand geteilt. Sobald es jemand tut, wird das Profil hier erscheinen.",
|
"status.reblogs.empty": "Diesen Beitrag hat bisher noch niemand geteilt. Sobald es jemand tut, wird das Profil hier erscheinen.",
|
||||||
"status.reblogs_count": "{count, plural, one {{counter} geteilter Beitrag} other {{counter} geteilte Beiträge}}",
|
"status.reblogs_count": "{count, plural, one {{counter} Mal geteilt} other {{counter} Mal geteilt}}",
|
||||||
"status.redraft": "Löschen und neu erstellen",
|
"status.redraft": "Löschen und neu erstellen",
|
||||||
"status.remove_bookmark": "Lesezeichen entfernen",
|
"status.remove_bookmark": "Lesezeichen entfernen",
|
||||||
"status.remove_favourite": "Aus Favoriten entfernen",
|
"status.remove_favourite": "Aus Favoriten entfernen",
|
||||||
|
|||||||
@ -903,6 +903,7 @@
|
|||||||
"status.edited_x_times": "Muudetud {count, plural, one{{count} kord} other {{count} korda}}",
|
"status.edited_x_times": "Muudetud {count, plural, one{{count} kord} other {{count} korda}}",
|
||||||
"status.embed": "Hangi manustamiskood",
|
"status.embed": "Hangi manustamiskood",
|
||||||
"status.favourite": "Lemmik",
|
"status.favourite": "Lemmik",
|
||||||
|
"status.favourites_count": "{count, plural, one {{counter} lemmik} other {{counter} lemmikut}}",
|
||||||
"status.filter": "Filtreeri seda postitust",
|
"status.filter": "Filtreeri seda postitust",
|
||||||
"status.history.created": "{name} lõi {date}",
|
"status.history.created": "{name} lõi {date}",
|
||||||
"status.history.edited": "{name} muutis {date}",
|
"status.history.edited": "{name} muutis {date}",
|
||||||
@ -937,12 +938,14 @@
|
|||||||
"status.quotes.empty": "Keegi pole seda postitust veel tsiteerinud. Kui keegi seda teeb, siis on ta nähtav siin.",
|
"status.quotes.empty": "Keegi pole seda postitust veel tsiteerinud. Kui keegi seda teeb, siis on ta nähtav siin.",
|
||||||
"status.quotes.local_other_disclaimer": "Autori poolt tagasilükatud tsitaate ei kuvata.",
|
"status.quotes.local_other_disclaimer": "Autori poolt tagasilükatud tsitaate ei kuvata.",
|
||||||
"status.quotes.remote_other_disclaimer": "Kui kasutaja on {domain} domeenist, siis siin on tagatud vaid tema tsitaatide näitamine. Autori poolt tagasilükatud tsitaate ei kuvata.",
|
"status.quotes.remote_other_disclaimer": "Kui kasutaja on {domain} domeenist, siis siin on tagatud vaid tema tsitaatide näitamine. Autori poolt tagasilükatud tsitaate ei kuvata.",
|
||||||
|
"status.quotes_count": "{count, plural, one {{counter} tsiteerimine} other {{counter} tsiteerimist}}",
|
||||||
"status.read_more": "Loe veel",
|
"status.read_more": "Loe veel",
|
||||||
"status.reblog": "Jaga",
|
"status.reblog": "Jaga",
|
||||||
"status.reblog_or_quote": "Anna hoogu või tsiteeri",
|
"status.reblog_or_quote": "Anna hoogu või tsiteeri",
|
||||||
"status.reblog_private": "Jaga uuesti oma jälgijatele",
|
"status.reblog_private": "Jaga uuesti oma jälgijatele",
|
||||||
"status.reblogged_by": "{name} jagas",
|
"status.reblogged_by": "{name} jagas",
|
||||||
"status.reblogs.empty": "Keegi pole seda postitust veel jaganud. Kui keegi seda teeb, siis on ta nähtav siin.",
|
"status.reblogs.empty": "Keegi pole seda postitust veel jaganud. Kui keegi seda teeb, siis on ta nähtav siin.",
|
||||||
|
"status.reblogs_count": "{count, plural, one {{counter} jagamine} other {{counter} jagamist}}",
|
||||||
"status.redraft": "Kustuta & alga uuesti",
|
"status.redraft": "Kustuta & alga uuesti",
|
||||||
"status.remove_bookmark": "Eemalda järjehoidja",
|
"status.remove_bookmark": "Eemalda järjehoidja",
|
||||||
"status.remove_favourite": "Eemalda lemmikute seast",
|
"status.remove_favourite": "Eemalda lemmikute seast",
|
||||||
|
|||||||
@ -233,7 +233,7 @@
|
|||||||
"confirmations.discard_draft.edit.cancel": "Palaa muokkaamaan",
|
"confirmations.discard_draft.edit.cancel": "Palaa muokkaamaan",
|
||||||
"confirmations.discard_draft.edit.message": "Jatkaminen tuhoaa kaikki muutokset, joita olet tehnyt julkaisuun, jota olet parhaillaan muokkaamassa.",
|
"confirmations.discard_draft.edit.message": "Jatkaminen tuhoaa kaikki muutokset, joita olet tehnyt julkaisuun, jota olet parhaillaan muokkaamassa.",
|
||||||
"confirmations.discard_draft.edit.title": "Hylätäänkö luonnosjulkaisusi muutokset?",
|
"confirmations.discard_draft.edit.title": "Hylätäänkö luonnosjulkaisusi muutokset?",
|
||||||
"confirmations.discard_draft.post.cancel": "Palaa lunnokseen",
|
"confirmations.discard_draft.post.cancel": "Palaa luonnokseen",
|
||||||
"confirmations.discard_draft.post.message": "Jatkaminen tuhoaa julkaisun, jota olet parhaillaan laatimassa.",
|
"confirmations.discard_draft.post.message": "Jatkaminen tuhoaa julkaisun, jota olet parhaillaan laatimassa.",
|
||||||
"confirmations.discard_draft.post.title": "Hylätäänkö luonnosjulkaisusi?",
|
"confirmations.discard_draft.post.title": "Hylätäänkö luonnosjulkaisusi?",
|
||||||
"confirmations.discard_edit_media.confirm": "Hylkää",
|
"confirmations.discard_edit_media.confirm": "Hylkää",
|
||||||
@ -903,7 +903,7 @@
|
|||||||
"status.edited_x_times": "Muokattu {count, plural, one {{count} kerran} other {{count} kertaa}}",
|
"status.edited_x_times": "Muokattu {count, plural, one {{count} kerran} other {{count} kertaa}}",
|
||||||
"status.embed": "Hanki upotuskoodi",
|
"status.embed": "Hanki upotuskoodi",
|
||||||
"status.favourite": "Suosikki",
|
"status.favourite": "Suosikki",
|
||||||
"status.favourites_count": "{count, plural, one {{counter} suosikki} other {{counter} suoksikkia}}",
|
"status.favourites_count": "{count, plural, one {{counter} suosikki} other {{counter} suosikkia}}",
|
||||||
"status.filter": "Suodata tämä julkaisu",
|
"status.filter": "Suodata tämä julkaisu",
|
||||||
"status.history.created": "{name} loi {date}",
|
"status.history.created": "{name} loi {date}",
|
||||||
"status.history.edited": "{name} muokkasi {date}",
|
"status.history.edited": "{name} muokkasi {date}",
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
"account.blocked": "Bloqué·e",
|
"account.blocked": "Bloqué·e",
|
||||||
"account.blocking": "Bloqué",
|
"account.blocking": "Bloqué",
|
||||||
"account.cancel_follow_request": "Retirer cette demande d'abonnement",
|
"account.cancel_follow_request": "Retirer cette demande d'abonnement",
|
||||||
"account.copy": "Copier le lien vers le profil",
|
"account.copy": "Copier le lien du profil",
|
||||||
"account.direct": "Mention privée @{name}",
|
"account.direct": "Mention privée @{name}",
|
||||||
"account.disable_notifications": "Ne plus me notifier quand @{name} publie",
|
"account.disable_notifications": "Ne plus me notifier quand @{name} publie",
|
||||||
"account.domain_blocking": "Domaine bloqué",
|
"account.domain_blocking": "Domaine bloqué",
|
||||||
@ -45,7 +45,7 @@
|
|||||||
"account.follow_request": "Demande d’abonnement",
|
"account.follow_request": "Demande d’abonnement",
|
||||||
"account.follow_request_cancel": "Annuler la demande",
|
"account.follow_request_cancel": "Annuler la demande",
|
||||||
"account.follow_request_cancel_short": "Annuler",
|
"account.follow_request_cancel_short": "Annuler",
|
||||||
"account.follow_request_short": "Requête",
|
"account.follow_request_short": "Demander à suivre",
|
||||||
"account.followers": "abonné·e·s",
|
"account.followers": "abonné·e·s",
|
||||||
"account.followers.empty": "Personne ne suit ce compte pour l'instant.",
|
"account.followers.empty": "Personne ne suit ce compte pour l'instant.",
|
||||||
"account.followers_counter": "{count, plural, one {{counter} abonné·e} other {{counter} abonné·e·s}}",
|
"account.followers_counter": "{count, plural, one {{counter} abonné·e} other {{counter} abonné·e·s}}",
|
||||||
@ -528,7 +528,7 @@
|
|||||||
"limited_account_hint.action": "Afficher le profil quand même",
|
"limited_account_hint.action": "Afficher le profil quand même",
|
||||||
"limited_account_hint.title": "Ce profil a été masqué par la modération de {domain}.",
|
"limited_account_hint.title": "Ce profil a été masqué par la modération de {domain}.",
|
||||||
"link_preview.author": "Par {name}",
|
"link_preview.author": "Par {name}",
|
||||||
"link_preview.more_from_author": "Plus via {name}",
|
"link_preview.more_from_author": "Voir plus de {name}",
|
||||||
"link_preview.shares": "{count, plural, one {{counter} message} other {{counter} messages}}",
|
"link_preview.shares": "{count, plural, one {{counter} message} other {{counter} messages}}",
|
||||||
"lists.add_member": "Ajouter",
|
"lists.add_member": "Ajouter",
|
||||||
"lists.add_to_list": "Ajouter à la liste",
|
"lists.add_to_list": "Ajouter à la liste",
|
||||||
@ -759,7 +759,7 @@
|
|||||||
"privacy.quote.disabled": "{visibility}, citations désactivées",
|
"privacy.quote.disabled": "{visibility}, citations désactivées",
|
||||||
"privacy.quote.limited": "{visibility}, citations limitées",
|
"privacy.quote.limited": "{visibility}, citations limitées",
|
||||||
"privacy.unlisted.additional": "Se comporte exactement comme « public », sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, explorer ou la recherche Mastodon, même si vous les avez activé au niveau de votre compte.",
|
"privacy.unlisted.additional": "Se comporte exactement comme « public », sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, explorer ou la recherche Mastodon, même si vous les avez activé au niveau de votre compte.",
|
||||||
"privacy.unlisted.long": "Caché des résultats de recherche de Mastodon, aux tendances et aux échéanciers publics",
|
"privacy.unlisted.long": "Caché des résultats de recherche de Mastodon, des tendances et des fils publics",
|
||||||
"privacy.unlisted.short": "Public discret",
|
"privacy.unlisted.short": "Public discret",
|
||||||
"privacy_policy.last_updated": "Dernière mise à jour {date}",
|
"privacy_policy.last_updated": "Dernière mise à jour {date}",
|
||||||
"privacy_policy.title": "Politique de confidentialité",
|
"privacy_policy.title": "Politique de confidentialité",
|
||||||
@ -903,6 +903,7 @@
|
|||||||
"status.edited_x_times": "Modifiée {count, plural, one {{count} fois} other {{count} fois}}",
|
"status.edited_x_times": "Modifiée {count, plural, one {{count} fois} other {{count} fois}}",
|
||||||
"status.embed": "Obtenir le code d'intégration",
|
"status.embed": "Obtenir le code d'intégration",
|
||||||
"status.favourite": "Ajouter aux favoris",
|
"status.favourite": "Ajouter aux favoris",
|
||||||
|
"status.favourites_count": "{count, plural, one {{counter} favori} other {{counter} favoris}}",
|
||||||
"status.filter": "Filtrer cette publication",
|
"status.filter": "Filtrer cette publication",
|
||||||
"status.history.created": "créé par {name} {date}",
|
"status.history.created": "créé par {name} {date}",
|
||||||
"status.history.edited": "modifié par {name} {date}",
|
"status.history.edited": "modifié par {name} {date}",
|
||||||
@ -937,12 +938,14 @@
|
|||||||
"status.quotes.empty": "Personne n'a encore cité ce message. Quand quelqu'un le fera, il apparaîtra ici.",
|
"status.quotes.empty": "Personne n'a encore cité ce message. Quand quelqu'un le fera, il apparaîtra ici.",
|
||||||
"status.quotes.local_other_disclaimer": "Les citations rejetées par l'auteur ne seront pas affichées.",
|
"status.quotes.local_other_disclaimer": "Les citations rejetées par l'auteur ne seront pas affichées.",
|
||||||
"status.quotes.remote_other_disclaimer": "Seules les citations de {domain} sont garanties d'être affichées ici. Les citations rejetées par l'auteur ne seront pas affichées.",
|
"status.quotes.remote_other_disclaimer": "Seules les citations de {domain} sont garanties d'être affichées ici. Les citations rejetées par l'auteur ne seront pas affichées.",
|
||||||
|
"status.quotes_count": "{count, plural, one {{counter} citation} other {{counter} citations}}",
|
||||||
"status.read_more": "En savoir plus",
|
"status.read_more": "En savoir plus",
|
||||||
"status.reblog": "Booster",
|
"status.reblog": "Booster",
|
||||||
"status.reblog_or_quote": "Boost ou citation",
|
"status.reblog_or_quote": "Boost ou citation",
|
||||||
"status.reblog_private": "Partagez à nouveau avec vos abonnés",
|
"status.reblog_private": "Partagez à nouveau avec vos abonnés",
|
||||||
"status.reblogged_by": "{name} a boosté",
|
"status.reblogged_by": "{name} a boosté",
|
||||||
"status.reblogs.empty": "Personne n’a encore boosté cette publication. Lorsque quelqu’un le fera, elle apparaîtra ici.",
|
"status.reblogs.empty": "Personne n’a encore boosté cette publication. Lorsque quelqu’un le fera, elle apparaîtra ici.",
|
||||||
|
"status.reblogs_count": "{count, plural, one {{counter} partage} other {{counter} partages}}",
|
||||||
"status.redraft": "Supprimer et réécrire",
|
"status.redraft": "Supprimer et réécrire",
|
||||||
"status.remove_bookmark": "Retirer des signets",
|
"status.remove_bookmark": "Retirer des signets",
|
||||||
"status.remove_favourite": "Retirer des favoris",
|
"status.remove_favourite": "Retirer des favoris",
|
||||||
@ -1023,8 +1026,8 @@
|
|||||||
"visibility_modal.helper.privacy_editing": "La visibilité ne peut pas être modifiée après la publication d'un message.",
|
"visibility_modal.helper.privacy_editing": "La visibilité ne peut pas être modifiée après la publication d'un message.",
|
||||||
"visibility_modal.helper.privacy_private_self_quote": "Les auto-citations de messages privés ne peuvent pas être rendues publiques.",
|
"visibility_modal.helper.privacy_private_self_quote": "Les auto-citations de messages privés ne peuvent pas être rendues publiques.",
|
||||||
"visibility_modal.helper.private_quoting": "Les posts accessible uniquement par les followers sur Mastodon ne peuvent être cités par d'autres personnes.",
|
"visibility_modal.helper.private_quoting": "Les posts accessible uniquement par les followers sur Mastodon ne peuvent être cités par d'autres personnes.",
|
||||||
"visibility_modal.helper.unlisted_quoting": "Lorsque les gens vous citent, leur message sera également caché dans les calendriers tendances.",
|
"visibility_modal.helper.unlisted_quoting": "Lorsque les gens vous citent, leur message sera également caché des fils tendances.",
|
||||||
"visibility_modal.instructions": "Contrôlez qui peut interagir avec ce post. Vous pouvez également appliquer ces paramètres à tous les futurs messages en allant dans <link>Préférences > Valeurs par d'éfaut de publication</link>.",
|
"visibility_modal.instructions": "Contrôlez qui peut interagir avec ce post. Vous pouvez également appliquer ces paramètres à tous les futurs messages en allant dans <link>Préférences > Valeurs par défaut de publication</link>.",
|
||||||
"visibility_modal.privacy_label": "Visibilité",
|
"visibility_modal.privacy_label": "Visibilité",
|
||||||
"visibility_modal.quote_followers": "Abonné·e·s seulement",
|
"visibility_modal.quote_followers": "Abonné·e·s seulement",
|
||||||
"visibility_modal.quote_label": "Autoriser les citations pour",
|
"visibility_modal.quote_label": "Autoriser les citations pour",
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"about.default_locale": "Défaut",
|
"about.default_locale": "Défaut",
|
||||||
"about.disclaimer": "Mastodon est un logiciel libre, open-source et une marque déposée de Mastodon gGmbH.",
|
"about.disclaimer": "Mastodon est un logiciel libre, open-source et une marque déposée de Mastodon gGmbH.",
|
||||||
"about.domain_blocks.no_reason_available": "Raison non disponible",
|
"about.domain_blocks.no_reason_available": "Raison non disponible",
|
||||||
"about.domain_blocks.preamble": "Mastodon vous permet généralement de visualiser le contenu et d'interagir avec les utilisateur⋅rices de n'importe quel autre serveur dans le fédivers. Voici les exceptions qui ont été faites sur ce serveur-là.",
|
"about.domain_blocks.preamble": "Mastodon vous permet généralement de visualiser le contenu et d'interagir avec les utilisateurs et utilisatrices de n'importe quel autre serveur dans le fédivers. Voici les exceptions qui ont été faites sur ce serveur.",
|
||||||
"about.domain_blocks.silenced.explanation": "Vous ne verrez généralement pas les profils et le contenu de ce serveur, à moins que vous ne les recherchiez explicitement ou que vous ne choisissiez de les suivre.",
|
"about.domain_blocks.silenced.explanation": "Vous ne verrez généralement pas les profils et le contenu de ce serveur, à moins que vous ne les recherchiez explicitement ou que vous ne choisissiez de les suivre.",
|
||||||
"about.domain_blocks.silenced.title": "Limité",
|
"about.domain_blocks.silenced.title": "Limité",
|
||||||
"about.domain_blocks.suspended.explanation": "Aucune donnée de ce serveur ne sera traitée, enregistrée ou échangée, rendant impossible toute interaction ou communication avec les comptes de ce serveur.",
|
"about.domain_blocks.suspended.explanation": "Aucune donnée de ce serveur ne sera traitée, enregistrée ou échangée, rendant impossible toute interaction ou communication avec les comptes de ce serveur.",
|
||||||
@ -23,7 +23,7 @@
|
|||||||
"account.blocked": "Bloqué·e",
|
"account.blocked": "Bloqué·e",
|
||||||
"account.blocking": "Bloqué",
|
"account.blocking": "Bloqué",
|
||||||
"account.cancel_follow_request": "Annuler l'abonnement",
|
"account.cancel_follow_request": "Annuler l'abonnement",
|
||||||
"account.copy": "Copier le lien vers le profil",
|
"account.copy": "Copier le lien du profil",
|
||||||
"account.direct": "Mention privée @{name}",
|
"account.direct": "Mention privée @{name}",
|
||||||
"account.disable_notifications": "Ne plus me notifier quand @{name} publie quelque chose",
|
"account.disable_notifications": "Ne plus me notifier quand @{name} publie quelque chose",
|
||||||
"account.domain_blocking": "Domaine bloqué",
|
"account.domain_blocking": "Domaine bloqué",
|
||||||
@ -45,7 +45,7 @@
|
|||||||
"account.follow_request": "Demande d’abonnement",
|
"account.follow_request": "Demande d’abonnement",
|
||||||
"account.follow_request_cancel": "Annuler la demande",
|
"account.follow_request_cancel": "Annuler la demande",
|
||||||
"account.follow_request_cancel_short": "Annuler",
|
"account.follow_request_cancel_short": "Annuler",
|
||||||
"account.follow_request_short": "Requête",
|
"account.follow_request_short": "Demander à suivre",
|
||||||
"account.followers": "Abonné·e·s",
|
"account.followers": "Abonné·e·s",
|
||||||
"account.followers.empty": "Personne ne suit cet·te utilisateur·rice pour l’instant.",
|
"account.followers.empty": "Personne ne suit cet·te utilisateur·rice pour l’instant.",
|
||||||
"account.followers_counter": "{count, plural, one {{counter} abonné·e} other {{counter} abonné·e·s}}",
|
"account.followers_counter": "{count, plural, one {{counter} abonné·e} other {{counter} abonné·e·s}}",
|
||||||
@ -528,7 +528,7 @@
|
|||||||
"limited_account_hint.action": "Afficher le profil quand même",
|
"limited_account_hint.action": "Afficher le profil quand même",
|
||||||
"limited_account_hint.title": "Ce profil a été masqué par la modération de {domain}.",
|
"limited_account_hint.title": "Ce profil a été masqué par la modération de {domain}.",
|
||||||
"link_preview.author": "Par {name}",
|
"link_preview.author": "Par {name}",
|
||||||
"link_preview.more_from_author": "Plus via {name}",
|
"link_preview.more_from_author": "Voir plus de {name}",
|
||||||
"link_preview.shares": "{count, plural, one {{counter} message} other {{counter} messages}}",
|
"link_preview.shares": "{count, plural, one {{counter} message} other {{counter} messages}}",
|
||||||
"lists.add_member": "Ajouter",
|
"lists.add_member": "Ajouter",
|
||||||
"lists.add_to_list": "Ajouter à la liste",
|
"lists.add_to_list": "Ajouter à la liste",
|
||||||
@ -759,7 +759,7 @@
|
|||||||
"privacy.quote.disabled": "{visibility}, citations désactivées",
|
"privacy.quote.disabled": "{visibility}, citations désactivées",
|
||||||
"privacy.quote.limited": "{visibility}, citations limitées",
|
"privacy.quote.limited": "{visibility}, citations limitées",
|
||||||
"privacy.unlisted.additional": "Se comporte exactement comme « public », sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, explorer ou la recherche Mastodon, même si vous les avez activé au niveau de votre compte.",
|
"privacy.unlisted.additional": "Se comporte exactement comme « public », sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, explorer ou la recherche Mastodon, même si vous les avez activé au niveau de votre compte.",
|
||||||
"privacy.unlisted.long": "Caché des résultats de recherche de Mastodon, aux tendances et aux échéanciers publics",
|
"privacy.unlisted.long": "Caché des résultats de recherche de Mastodon, des tendances et des fils publics",
|
||||||
"privacy.unlisted.short": "Public discret",
|
"privacy.unlisted.short": "Public discret",
|
||||||
"privacy_policy.last_updated": "Dernière mise à jour {date}",
|
"privacy_policy.last_updated": "Dernière mise à jour {date}",
|
||||||
"privacy_policy.title": "Politique de confidentialité",
|
"privacy_policy.title": "Politique de confidentialité",
|
||||||
@ -903,6 +903,7 @@
|
|||||||
"status.edited_x_times": "Modifié {count, plural, one {{count} fois} other {{count} fois}}",
|
"status.edited_x_times": "Modifié {count, plural, one {{count} fois} other {{count} fois}}",
|
||||||
"status.embed": "Obtenir le code d'intégration",
|
"status.embed": "Obtenir le code d'intégration",
|
||||||
"status.favourite": "Ajouter aux favoris",
|
"status.favourite": "Ajouter aux favoris",
|
||||||
|
"status.favourites_count": "{count, plural, one {{counter} favori} other {{counter} favoris}}",
|
||||||
"status.filter": "Filtrer ce message",
|
"status.filter": "Filtrer ce message",
|
||||||
"status.history.created": "créé par {name} {date}",
|
"status.history.created": "créé par {name} {date}",
|
||||||
"status.history.edited": "modifié par {name} {date}",
|
"status.history.edited": "modifié par {name} {date}",
|
||||||
@ -937,12 +938,14 @@
|
|||||||
"status.quotes.empty": "Personne n'a encore cité ce message. Quand quelqu'un le fera, il apparaîtra ici.",
|
"status.quotes.empty": "Personne n'a encore cité ce message. Quand quelqu'un le fera, il apparaîtra ici.",
|
||||||
"status.quotes.local_other_disclaimer": "Les citations rejetées par l'auteur ne seront pas affichées.",
|
"status.quotes.local_other_disclaimer": "Les citations rejetées par l'auteur ne seront pas affichées.",
|
||||||
"status.quotes.remote_other_disclaimer": "Seules les citations de {domain} sont garanties d'être affichées ici. Les citations rejetées par l'auteur ne seront pas affichées.",
|
"status.quotes.remote_other_disclaimer": "Seules les citations de {domain} sont garanties d'être affichées ici. Les citations rejetées par l'auteur ne seront pas affichées.",
|
||||||
|
"status.quotes_count": "{count, plural, one {{counter} citation} other {{counter} citations}}",
|
||||||
"status.read_more": "Lire la suite",
|
"status.read_more": "Lire la suite",
|
||||||
"status.reblog": "Partager",
|
"status.reblog": "Partager",
|
||||||
"status.reblog_or_quote": "Boost ou citation",
|
"status.reblog_or_quote": "Boost ou citation",
|
||||||
"status.reblog_private": "Partagez à nouveau avec vos abonnés",
|
"status.reblog_private": "Partagez à nouveau avec vos abonnés",
|
||||||
"status.reblogged_by": "{name} a partagé",
|
"status.reblogged_by": "{name} a partagé",
|
||||||
"status.reblogs.empty": "Personne n’a encore partagé ce message. Lorsque quelqu’un le fera, il apparaîtra ici.",
|
"status.reblogs.empty": "Personne n’a encore partagé ce message. Lorsque quelqu’un le fera, il apparaîtra ici.",
|
||||||
|
"status.reblogs_count": "{count, plural, one {{counter} partage} other {{counter} partages}}",
|
||||||
"status.redraft": "Supprimer et réécrire",
|
"status.redraft": "Supprimer et réécrire",
|
||||||
"status.remove_bookmark": "Retirer des marque-pages",
|
"status.remove_bookmark": "Retirer des marque-pages",
|
||||||
"status.remove_favourite": "Retirer des favoris",
|
"status.remove_favourite": "Retirer des favoris",
|
||||||
@ -1023,8 +1026,8 @@
|
|||||||
"visibility_modal.helper.privacy_editing": "La visibilité ne peut pas être modifiée après la publication d'un message.",
|
"visibility_modal.helper.privacy_editing": "La visibilité ne peut pas être modifiée après la publication d'un message.",
|
||||||
"visibility_modal.helper.privacy_private_self_quote": "Les auto-citations de messages privés ne peuvent pas être rendues publiques.",
|
"visibility_modal.helper.privacy_private_self_quote": "Les auto-citations de messages privés ne peuvent pas être rendues publiques.",
|
||||||
"visibility_modal.helper.private_quoting": "Les posts accessible uniquement par les followers sur Mastodon ne peuvent être cités par d'autres personnes.",
|
"visibility_modal.helper.private_quoting": "Les posts accessible uniquement par les followers sur Mastodon ne peuvent être cités par d'autres personnes.",
|
||||||
"visibility_modal.helper.unlisted_quoting": "Lorsque les gens vous citent, leur message sera également caché dans les calendriers tendances.",
|
"visibility_modal.helper.unlisted_quoting": "Lorsque les gens vous citent, leur message sera également caché des fils tendances.",
|
||||||
"visibility_modal.instructions": "Contrôlez qui peut interagir avec ce post. Vous pouvez également appliquer ces paramètres à tous les futurs messages en allant dans <link>Préférences > Valeurs par d'éfaut de publication</link>.",
|
"visibility_modal.instructions": "Contrôlez qui peut interagir avec ce post. Vous pouvez également appliquer ces paramètres à tous les futurs messages en allant dans <link>Préférences > Valeurs par défaut de publication</link>.",
|
||||||
"visibility_modal.privacy_label": "Visibilité",
|
"visibility_modal.privacy_label": "Visibilité",
|
||||||
"visibility_modal.quote_followers": "Abonné·e·s seulement",
|
"visibility_modal.quote_followers": "Abonné·e·s seulement",
|
||||||
"visibility_modal.quote_label": "Autoriser les citations pour",
|
"visibility_modal.quote_label": "Autoriser les citations pour",
|
||||||
|
|||||||
@ -189,7 +189,7 @@
|
|||||||
"notification.update": "{name} nuntium correxit",
|
"notification.update": "{name} nuntium correxit",
|
||||||
"notification_requests.accept": "Accipe",
|
"notification_requests.accept": "Accipe",
|
||||||
"notification_requests.confirm_accept_multiple.message": "Tu es accepturus {count, plural, one {una notitia petitionem} other {# notitia petitiones}}. Certus esne procedere vis?",
|
"notification_requests.confirm_accept_multiple.message": "Tu es accepturus {count, plural, one {una notitia petitionem} other {# notitia petitiones}}. Certus esne procedere vis?",
|
||||||
"notification_requests.confirm_dismiss_multiple.message": "Tu {count, plural, one {unam petitionem notificationis} other {# petitiones notificationum}} abrogāre prōximum es. {count, plural, one {Illa} other {Eae}} facile accessū nōn erit. Certus es tē procedere velle?",
|
"notification_requests.confirm_dismiss_multiple.message": "Tu {count, plural, one {unam petitionem notificationis} other {# petitiones notificationum}} abrogāre prōximum es. {count, plural, one {it} other {Eae}} facile accessū nōn erit. Certus es tē procedere velle?",
|
||||||
"notifications.filter.all": "Omnia",
|
"notifications.filter.all": "Omnia",
|
||||||
"notifications.filter.polls": "Eventus electionis",
|
"notifications.filter.polls": "Eventus electionis",
|
||||||
"notifications.group": "{count} Notificātiōnēs",
|
"notifications.group": "{count} Notificātiōnēs",
|
||||||
@ -246,19 +246,102 @@
|
|||||||
"status.history.created": "{name} creatum {date}",
|
"status.history.created": "{name} creatum {date}",
|
||||||
"status.history.edited": "{name} correxit {date}",
|
"status.history.edited": "{name} correxit {date}",
|
||||||
"status.open": "Expand this status",
|
"status.open": "Expand this status",
|
||||||
|
"status.quotes.empty": "Nemo hanc commentationem adhuc citavit. Cum quis citaverit, hic apparebit.",
|
||||||
|
"status.quotes.local_other_disclaimer": "Citationes ab auctore reiectæ non monstrabuntur.",
|
||||||
|
"status.quotes.remote_other_disclaimer": "Tantum citae ex {domain} hic exhiberi praestantur. Citae ab auctore reiectae non exhibebuntur.",
|
||||||
|
"status.quotes_count": "{count, plural, one {{counter} citatio} other {{counter} citationes}}",
|
||||||
|
"status.read_more": "Plura lege",
|
||||||
|
"status.reblog": "Promovere",
|
||||||
|
"status.reblog_or_quote": "Promovere aut cita",
|
||||||
|
"status.reblog_private": "Iterum cum sectatoribus tuis communica",
|
||||||
"status.reblogged_by": "{name} adiuvavit",
|
"status.reblogged_by": "{name} adiuvavit",
|
||||||
|
"status.reblogs.empty": "Nemo hanc publicationem adhuc promovit. Cum quis eam promoveat, hic apparebunt.",
|
||||||
|
"status.reblogs_count": "{count, plural, one {{counter} incrementum} other {{counter} incrementa}}",
|
||||||
|
"status.redraft": "Dele et redig",
|
||||||
|
"status.remove_bookmark": "Tolle signum",
|
||||||
|
"status.remove_favourite": "Tolle ad delectis",
|
||||||
|
"status.remove_quote": "Tolle",
|
||||||
|
"status.replied_in_thread": "In filo responsum",
|
||||||
|
"status.replied_to": "{name} respondit",
|
||||||
|
"status.reply": "Respondere",
|
||||||
|
"status.replyAll": "Responde ad filum",
|
||||||
|
"status.report": "Referre @{name}",
|
||||||
|
"status.request_quote": "Pretium petere",
|
||||||
|
"status.revoke_quote": "Tolle nuntium meum ex nuntio @{name}",
|
||||||
|
"status.sensitive_warning": "Materia delicata",
|
||||||
|
"status.share": "Communica",
|
||||||
|
"status.show_less_all": "Omnibus minus monstra",
|
||||||
|
"status.show_more_all": "Omnibus plura monstra",
|
||||||
|
"status.show_original": "Monstra originalem",
|
||||||
"status.title.with_attachments": "{user} publicavit {attachmentCount, plural, one {unum annexum} other {{attachmentCount} annexa}}",
|
"status.title.with_attachments": "{user} publicavit {attachmentCount, plural, one {unum annexum} other {{attachmentCount} annexa}}",
|
||||||
|
"status.translate": "Converte",
|
||||||
|
"status.translated_from_with": "Translatum ex {lang} per {provider}",
|
||||||
|
"status.uncached_media_warning": "Praevisum non praesto est",
|
||||||
|
"status.unmute_conversation": "Conversationem reserare",
|
||||||
|
"subscribed_languages.lead": "Tantum epistolae in linguis selectis in domo tua apparebunt et indices temporum post mutationem. Neminem eligatis qui epistolas in omnibus linguis recipiat.",
|
||||||
|
"subscribed_languages.save": "Servare mutationes",
|
||||||
|
"subscribed_languages.target": "Muta linguas subscriptas pro {target}",
|
||||||
"tabs_bar.home": "Domi",
|
"tabs_bar.home": "Domi",
|
||||||
|
"tabs_bar.menu": "Elenchus",
|
||||||
|
"tabs_bar.notifications": "Acta Vicimediorum",
|
||||||
|
"tabs_bar.publish": "Nova publicatio",
|
||||||
|
"tabs_bar.search": "Quaere",
|
||||||
|
"terms_of_service.effective_as_of": "Valet ex {date}",
|
||||||
|
"terms_of_service.title": "Termini servitii",
|
||||||
|
"terms_of_service.upcoming_changes_on": "Mutationes venturae die {date}",
|
||||||
"time_remaining.days": "{number, plural, one {# die} other {# dies}} restant",
|
"time_remaining.days": "{number, plural, one {# die} other {# dies}} restant",
|
||||||
"time_remaining.hours": "{number, plural, one {# hora} other {# horae}} restant",
|
"time_remaining.hours": "{number, plural, one {# hora} other {# horae}} restant",
|
||||||
"time_remaining.minutes": "{number, plural, one {# minutum} other {# minuta}} restant",
|
"time_remaining.minutes": "{number, plural, one {# minutum} other {# minuta}} restant",
|
||||||
|
"time_remaining.moments": "Momenta reliqua",
|
||||||
"time_remaining.seconds": "{number, plural, one {# secundum} other {# secunda}} restant",
|
"time_remaining.seconds": "{number, plural, one {# secundum} other {# secunda}} restant",
|
||||||
"trends.counter_by_accounts": "{count, plural, one {{counter} persōna} other {{counter} persōnae}} in {days, plural, one {diē prīdiē} other {diēbus praeteritīs {days}}}",
|
"trends.counter_by_accounts": "{count, plural, one {{counter} persōna} other {{counter} persōnae}} in {days, plural, one {days} other {diēbus praeteritīs {days}}}",
|
||||||
|
"trends.trending_now": "Nunc in usu",
|
||||||
"ui.beforeunload": "Si Mastodon discesseris, tua epitome peribit.",
|
"ui.beforeunload": "Si Mastodon discesseris, tua epitome peribit.",
|
||||||
"units.short.billion": "{count} millia milionum",
|
"units.short.billion": "{count} millia milionum",
|
||||||
"units.short.million": "{count} milionum",
|
"units.short.million": "{count} milionum",
|
||||||
"units.short.thousand": "{count} millia",
|
"units.short.thousand": "{count} millia",
|
||||||
"upload_button.label": "Imaginēs, vīdeō aut fīle audītūs adde",
|
"upload_area.title": "Trahe et depone ad imponendum",
|
||||||
|
"upload_button.label": "Adde imagines, pelliculam, aut fasciculum sonorum.",
|
||||||
|
"upload_error.limit": "Limes onerationis superatus est.",
|
||||||
|
"upload_error.poll": "Nullis suffragiis licet fascicula imponere.",
|
||||||
|
"upload_error.quote": "Nullum oneramentum fasciculi cum citationibus permittitur.",
|
||||||
|
"upload_form.drag_and_drop.instructions": "Ad annexum mediorum tollendum, preme clavem \"Space\" aut \"Enter\". Dum traheis, utere clavibus sagittariis ad annexum mediorum in quamlibet partem movendum. Preme iterum \"Space\" aut \"Enter\" ad annexum mediorum in novo loco deponendum, aut preme \"Escape\" ad desinendum.",
|
||||||
|
"upload_form.drag_and_drop.on_drag_cancel": "Tractatio revocata est. Adiunctum medium {item} demissum est.",
|
||||||
|
"upload_form.drag_and_drop.on_drag_end": "Adhaesum medium {item} demissum est.",
|
||||||
|
"upload_form.drag_and_drop.on_drag_over": "Adhaesum medium {item} motum est.",
|
||||||
|
"upload_form.drag_and_drop.on_drag_start": "Adhaesum medium {item} sublatum est.",
|
||||||
"upload_form.edit": "Recolere",
|
"upload_form.edit": "Recolere",
|
||||||
"upload_progress.label": "Uploading…"
|
"upload_progress.label": "Oneratur...",
|
||||||
|
"upload_progress.processing": "Processus…",
|
||||||
|
"username.taken": "Illud nomen usoris occupatum est. Aliud tenta.",
|
||||||
|
"video.close": "Pelliculam claude",
|
||||||
|
"video.download": "Prehendere fasciculus",
|
||||||
|
"video.exit_fullscreen": "Exitus ex plenum monitorium",
|
||||||
|
"video.expand": "Expande pelliculam",
|
||||||
|
"video.fullscreen": "Plenum monitorium",
|
||||||
|
"video.hide": "celare pellicula",
|
||||||
|
"video.mute": "Mutus",
|
||||||
|
"video.pause": "intermittere",
|
||||||
|
"video.play": "gignere",
|
||||||
|
"video.skip_backward": "Redire",
|
||||||
|
"video.skip_forward": "Progredi",
|
||||||
|
"video.unmute": "Sordes tollere",
|
||||||
|
"video.volume_down": "Volumen deminui",
|
||||||
|
"video.volume_up": "Volumen augete",
|
||||||
|
"visibility_modal.button_title": "Visibilitatem statuere",
|
||||||
|
"visibility_modal.direct_quote_warning.text": "Si praesentia configuramenta servaveris, sententia inserta in nexum convertetur.",
|
||||||
|
"visibility_modal.direct_quote_warning.title": "Citatio in mentitionibus privatis inseriri non possunt.",
|
||||||
|
"visibility_modal.header": "Visibilitas et interactio",
|
||||||
|
"visibility_modal.helper.direct_quoting": "Mentiones privatae in Mastodon scriptae ab aliis citari non possunt.",
|
||||||
|
"visibility_modal.helper.privacy_editing": "Visibilitas mutari non potest postquam nuntius publicatus est.",
|
||||||
|
"visibility_modal.helper.privacy_private_self_quote": "Citationes propriae nuntiorum privatorum publicari non possunt.",
|
||||||
|
"visibility_modal.helper.private_quoting": "Nuntii in Mastodon a sequacibus tantum scriptī ab aliis citari non possunt.",
|
||||||
|
"visibility_modal.helper.unlisted_quoting": "Cum te citant, eorum scriptum etiam ex indicibus popularibus celabitur.",
|
||||||
|
"visibility_modal.instructions": "Régula quis cum hoc scripto agere possit. Potes etiam ordinationes omnibus scriptis futuris adhibere, navigando ad <link>Praeferentias > Regulas scriptionis praedefinitas</link>.",
|
||||||
|
"visibility_modal.privacy_label": "Visibilitas",
|
||||||
|
"visibility_modal.quote_followers": "Sectatores tantum",
|
||||||
|
"visibility_modal.quote_label": "Quis citare potest",
|
||||||
|
"visibility_modal.quote_nobody": "Sicut me",
|
||||||
|
"visibility_modal.quote_public": "quisquis",
|
||||||
|
"visibility_modal.save": "Servare"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -638,12 +638,14 @@
|
|||||||
"privacy.direct.long": "Visi ierakstā pieminētie",
|
"privacy.direct.long": "Visi ierakstā pieminētie",
|
||||||
"privacy.private.long": "Tikai Tavi sekotāji",
|
"privacy.private.long": "Tikai Tavi sekotāji",
|
||||||
"privacy.private.short": "Sekotāji",
|
"privacy.private.short": "Sekotāji",
|
||||||
"privacy.public.long": "Jebkurš Mastodon un ārpus tā",
|
"privacy.public.long": "Jebkurš Mastodon platformā un ārpus tās",
|
||||||
"privacy.public.short": "Publisks",
|
"privacy.public.short": "Publisks",
|
||||||
"privacy.quote.anyone": "{visibility}, jebkurš var citēt",
|
"privacy.quote.anyone": "{visibility}, jebkurš var citēt",
|
||||||
"privacy.quote.disabled": "{visibility}, aizliegta citēšana",
|
"privacy.quote.disabled": "{visibility}, aizliegta citēšana",
|
||||||
"privacy.quote.limited": "{visibility}, ierobežota citēšana",
|
"privacy.quote.limited": "{visibility}, ierobežota citēšana",
|
||||||
"privacy.unlisted.additional": "Šis uzvedas tieši kā publisks, izņemot to, ka ieraksts neparādīsies tiešraides barotnēs vai tēmturos, izpētē vai Mastodon meklēšanā, pat ja esi to norādījis visa konta ietvaros.",
|
"privacy.unlisted.additional": "Šis uzvedas tieši kā publisks, izņemot to, ka ieraksts neparādīsies tiešraides barotnēs vai tēmturos, izpētē vai Mastodon meklēšanā, pat ja esi to norādījis visa konta ietvaros.",
|
||||||
|
"privacy.unlisted.long": "Netiks rādīts Mastodon meklēšanas rezultātos, populārākajos, un publiskajās laikjoslās",
|
||||||
|
"privacy.unlisted.short": "Klusi publisks",
|
||||||
"privacy_policy.last_updated": "Pēdējo reizi atjaunināta {date}",
|
"privacy_policy.last_updated": "Pēdējo reizi atjaunināta {date}",
|
||||||
"privacy_policy.title": "Privātuma politika",
|
"privacy_policy.title": "Privātuma politika",
|
||||||
"recommended": "Ieteicams",
|
"recommended": "Ieteicams",
|
||||||
@ -837,7 +839,7 @@
|
|||||||
"video.volume_down": "Pagriezt klusāk",
|
"video.volume_down": "Pagriezt klusāk",
|
||||||
"video.volume_up": "Pagriezt skaļāk",
|
"video.volume_up": "Pagriezt skaļāk",
|
||||||
"visibility_modal.button_title": "Iestatīt redzamību",
|
"visibility_modal.button_title": "Iestatīt redzamību",
|
||||||
"visibility_modal.header": "Redzamība un mijjiedarbība",
|
"visibility_modal.header": "Redzamība un mijiedarbība",
|
||||||
"visibility_modal.quote_followers": "Tikai sekotāji",
|
"visibility_modal.quote_followers": "Tikai sekotāji",
|
||||||
"visibility_modal.quote_label": "Kurš var citēt",
|
"visibility_modal.quote_label": "Kurš var citēt",
|
||||||
"visibility_modal.quote_nobody": "Tikai es",
|
"visibility_modal.quote_nobody": "Tikai es",
|
||||||
|
|||||||
@ -903,6 +903,7 @@
|
|||||||
"status.edited_x_times": "有編輯 {count, plural, one {{count} kái} other {{count} kái}}",
|
"status.edited_x_times": "有編輯 {count, plural, one {{count} kái} other {{count} kái}}",
|
||||||
"status.embed": "The̍h相tàu ê (embed)程式碼",
|
"status.embed": "The̍h相tàu ê (embed)程式碼",
|
||||||
"status.favourite": "收藏",
|
"status.favourite": "收藏",
|
||||||
|
"status.favourites_count": "{count, plural, one {{counter} 篇} other {{counter} 篇}} 收藏",
|
||||||
"status.filter": "過濾tsit 篇 PO文",
|
"status.filter": "過濾tsit 篇 PO文",
|
||||||
"status.history.created": "{name} 佇 {date} 建立",
|
"status.history.created": "{name} 佇 {date} 建立",
|
||||||
"status.history.edited": "{name} 佇 {date} 編輯",
|
"status.history.edited": "{name} 佇 {date} 編輯",
|
||||||
@ -936,12 +937,14 @@
|
|||||||
"status.quotes.empty": "Iáu無lâng引用tsit篇PO文。Nā是有lâng引用,ē佇tsia顯示。.",
|
"status.quotes.empty": "Iáu無lâng引用tsit篇PO文。Nā是有lâng引用,ē佇tsia顯示。.",
|
||||||
"status.quotes.local_other_disclaimer": "Hōo作者拒絕引用ê引文bē當顯示。",
|
"status.quotes.local_other_disclaimer": "Hōo作者拒絕引用ê引文bē當顯示。",
|
||||||
"status.quotes.remote_other_disclaimer": "Kan-ta tuì {domain} 來ê引文tsiah保證佇tsia顯示。Hōo作者拒絕ê引文buē顯示。",
|
"status.quotes.remote_other_disclaimer": "Kan-ta tuì {domain} 來ê引文tsiah保證佇tsia顯示。Hōo作者拒絕ê引文buē顯示。",
|
||||||
|
"status.quotes_count": "{count, plural, one {{counter} 篇} other {{counter} 篇}} 引用",
|
||||||
"status.read_more": "讀詳細",
|
"status.read_more": "讀詳細",
|
||||||
"status.reblog": "轉送",
|
"status.reblog": "轉送",
|
||||||
"status.reblog_or_quote": "轉送á是引用",
|
"status.reblog_or_quote": "轉送á是引用",
|
||||||
"status.reblog_private": "Koh再hām跟tuè ê分享",
|
"status.reblog_private": "Koh再hām跟tuè ê分享",
|
||||||
"status.reblogged_by": "{name} kā轉送ah",
|
"status.reblogged_by": "{name} kā轉送ah",
|
||||||
"status.reblogs.empty": "Iáu無lâng轉送tsit篇PO文。Nā是有lâng轉送,ē佇tsia顯示。",
|
"status.reblogs.empty": "Iáu無lâng轉送tsit篇PO文。Nā是有lâng轉送,ē佇tsia顯示。",
|
||||||
|
"status.reblogs_count": "{count, plural, one {{counter} 篇} other {{counter} 篇}} 轉送",
|
||||||
"status.redraft": "Thâi掉了後重寫",
|
"status.redraft": "Thâi掉了後重寫",
|
||||||
"status.remove_bookmark": "Thâi掉冊籤",
|
"status.remove_bookmark": "Thâi掉冊籤",
|
||||||
"status.remove_favourite": "Tuì收藏內suá掉",
|
"status.remove_favourite": "Tuì收藏內suá掉",
|
||||||
@ -1014,6 +1017,8 @@
|
|||||||
"video.volume_down": "變khah細聲",
|
"video.volume_down": "變khah細聲",
|
||||||
"video.volume_up": "變khah大聲",
|
"video.volume_up": "變khah大聲",
|
||||||
"visibility_modal.button_title": "設定通看ê程度",
|
"visibility_modal.button_title": "設定通看ê程度",
|
||||||
|
"visibility_modal.direct_quote_warning.text": "Nā是lí儲存目前ê設定,引用êPO文ē轉做連結。",
|
||||||
|
"visibility_modal.direct_quote_warning.title": "引用bē當tàu入去私人ê提起",
|
||||||
"visibility_modal.header": "通看ê程度kap互動",
|
"visibility_modal.header": "通看ê程度kap互動",
|
||||||
"visibility_modal.helper.direct_quoting": "Mastodon頂發布ê私人提起bē當hōo別lâng引用。",
|
"visibility_modal.helper.direct_quoting": "Mastodon頂發布ê私人提起bē當hōo別lâng引用。",
|
||||||
"visibility_modal.helper.privacy_editing": "Po文發布了後,bē當改通看ê程度。",
|
"visibility_modal.helper.privacy_editing": "Po文發布了後,bē當改通看ê程度。",
|
||||||
|
|||||||
@ -628,7 +628,7 @@
|
|||||||
"notification.moderation_warning.action_disable": "Ваша учётная запись была отключена.",
|
"notification.moderation_warning.action_disable": "Ваша учётная запись была отключена.",
|
||||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Некоторые ваши посты были отмечены как содержимое деликатного характера.",
|
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Некоторые ваши посты были отмечены как содержимое деликатного характера.",
|
||||||
"notification.moderation_warning.action_none": "Модераторы вынесли вам предупреждение.",
|
"notification.moderation_warning.action_none": "Модераторы вынесли вам предупреждение.",
|
||||||
"notification.moderation_warning.action_sensitive": "С этого момента все ваши новые посты будут отмечены как содержимое деликатного характера.",
|
"notification.moderation_warning.action_sensitive": "С этого момента все ваши посты будут отмечены как содержимое деликатного характера.",
|
||||||
"notification.moderation_warning.action_silence": "Ваша учётная запись была ограничена.",
|
"notification.moderation_warning.action_silence": "Ваша учётная запись была ограничена.",
|
||||||
"notification.moderation_warning.action_suspend": "Ваша учётная запись была заблокирована.",
|
"notification.moderation_warning.action_suspend": "Ваша учётная запись была заблокирована.",
|
||||||
"notification.own_poll": "Ваш опрос завершился",
|
"notification.own_poll": "Ваш опрос завершился",
|
||||||
|
|||||||
@ -1027,7 +1027,7 @@
|
|||||||
"visibility_modal.helper.privacy_private_self_quote": "Không thể công khai trích dẫn tút của bản thân hoặc tút riêng tư.",
|
"visibility_modal.helper.privacy_private_self_quote": "Không thể công khai trích dẫn tút của bản thân hoặc tút riêng tư.",
|
||||||
"visibility_modal.helper.private_quoting": "Tút chỉ dành cho người theo dõi trên Mastodon không thể được người khác trích dẫn.",
|
"visibility_modal.helper.private_quoting": "Tút chỉ dành cho người theo dõi trên Mastodon không thể được người khác trích dẫn.",
|
||||||
"visibility_modal.helper.unlisted_quoting": "Khi ai đó trích dẫn bạn, tút của họ cũng sẽ bị ẩn khỏi bảng tin công khai.",
|
"visibility_modal.helper.unlisted_quoting": "Khi ai đó trích dẫn bạn, tút của họ cũng sẽ bị ẩn khỏi bảng tin công khai.",
|
||||||
"visibility_modal.instructions": "Kiểm soát những ai có thể tương tác với tút này. Bạn cũng có thể áp dụng cài đặt cho tất cả các tút trong tương lai bằng cách điều hướng đến <link>Thiết lập > Đăng</link>.",
|
"visibility_modal.instructions": "Kiểm soát những ai có thể tương tác với tút này. Bạn cũng có thể đặt sẵn cho tất cả tút trong tương lai bằng cách truy cập <link>Thiết lập > Mặc định cho tút</link>.",
|
||||||
"visibility_modal.privacy_label": "Hiển thị",
|
"visibility_modal.privacy_label": "Hiển thị",
|
||||||
"visibility_modal.quote_followers": "Chỉ người theo dõi",
|
"visibility_modal.quote_followers": "Chỉ người theo dõi",
|
||||||
"visibility_modal.quote_label": "Ai có thể trích dẫn",
|
"visibility_modal.quote_label": "Ai có thể trích dẫn",
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import { me, reduceMotion } from 'mastodon/initial_state';
|
|||||||
import ready from 'mastodon/ready';
|
import ready from 'mastodon/ready';
|
||||||
import { store } from 'mastodon/store';
|
import { store } from 'mastodon/store';
|
||||||
|
|
||||||
import { initializeEmoji } from './features/emoji';
|
|
||||||
import { isProduction, isDevelopment } from './utils/environment';
|
import { isProduction, isDevelopment } from './utils/environment';
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
@ -30,6 +29,7 @@ function main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { initializeEmoji } = await import('./features/emoji/index');
|
||||||
initializeEmoji();
|
initializeEmoji();
|
||||||
|
|
||||||
const root = createRoot(mountNode);
|
const root = createRoot(mountNode);
|
||||||
|
|||||||
@ -341,8 +341,8 @@ export const composeReducer = (state = initialState, action) => {
|
|||||||
const isDirect = state.get('privacy') === 'direct';
|
const isDirect = state.get('privacy') === 'direct';
|
||||||
return state
|
return state
|
||||||
.set('quoted_status_id', isDirect ? null : status.get('id'))
|
.set('quoted_status_id', isDirect ? null : status.get('id'))
|
||||||
.set('spoiler', status.get('sensitive'))
|
.update('spoiler', spoiler => (spoiler) || !!status.get('spoiler_text'))
|
||||||
.set('spoiler_text', status.get('spoiler_text'))
|
.update('spoiler_text', (spoiler_text) => spoiler_text || status.get('spoiler_text'))
|
||||||
.update('privacy', (visibility) => {
|
.update('privacy', (visibility) => {
|
||||||
if (['public', 'unlisted'].includes(visibility) && status.get('visibility') === 'private') {
|
if (['public', 'unlisted'].includes(visibility) && status.get('visibility') === 'private') {
|
||||||
return 'private';
|
return 'private';
|
||||||
|
|||||||
@ -327,9 +327,9 @@ $content-width: 840px;
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: $primary-text-color;
|
color: $primary-text-color;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
padding-bottom: 0;
|
padding-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
border-bottom: 0;
|
border-top: 0;
|
||||||
|
|
||||||
.comment {
|
.comment {
|
||||||
display: block;
|
display: block;
|
||||||
@ -1040,10 +1040,6 @@ a.name-tag,
|
|||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-role {
|
|
||||||
color: var(--user-role-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.applications-list {
|
.applications-list {
|
||||||
.icon {
|
.icon {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|||||||
7
app/javascript/styles_new/application.scss
Normal file
7
app/javascript/styles_new/application.scss
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
@use 'mastodon/css_variables';
|
||||||
|
@use 'mastodon/variables';
|
||||||
|
@use 'common';
|
||||||
|
|
||||||
|
html {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
24
app/javascript/styles_new/common.scss
Normal file
24
app/javascript/styles_new/common.scss
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
@use 'mastodon/mixins';
|
||||||
|
@use 'fonts/roboto';
|
||||||
|
@use 'fonts/roboto-mono';
|
||||||
|
|
||||||
|
@use 'mastodon/reset';
|
||||||
|
@use 'mastodon/basics';
|
||||||
|
@use 'mastodon/branding';
|
||||||
|
@use 'mastodon/containers';
|
||||||
|
@use 'mastodon/lists';
|
||||||
|
@use 'mastodon/widgets';
|
||||||
|
@use 'mastodon/forms';
|
||||||
|
@use 'mastodon/accounts';
|
||||||
|
@use 'mastodon/components';
|
||||||
|
@use 'mastodon/polls';
|
||||||
|
@use 'mastodon/modal';
|
||||||
|
@use 'mastodon/emoji_picker';
|
||||||
|
@use 'mastodon/annual_reports';
|
||||||
|
@use 'mastodon/about';
|
||||||
|
@use 'mastodon/tables';
|
||||||
|
@use 'mastodon/admin';
|
||||||
|
@use 'mastodon/dashboard';
|
||||||
|
@use 'mastodon/rtl';
|
||||||
|
@use 'mastodon/accessibility';
|
||||||
|
@use 'mastodon/rich_text';
|
||||||
8
app/javascript/styles_new/contrast.scss
Normal file
8
app/javascript/styles_new/contrast.scss
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
@use 'mastodon/css_variables';
|
||||||
|
@use 'mastodon/variables';
|
||||||
|
@use 'common';
|
||||||
|
@use 'contrast/diff';
|
||||||
|
|
||||||
|
html {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
54
app/javascript/styles_new/contrast/diff.scss
Normal file
54
app/javascript/styles_new/contrast/diff.scss
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
:root {
|
||||||
|
/* TEXT TOKENS */
|
||||||
|
|
||||||
|
--color-text-primary: var(--color-grey-50);
|
||||||
|
--color-text-secondary: var(--color-grey-300);
|
||||||
|
--color-text-tertiary: var(--color-grey-400);
|
||||||
|
--color-text-brand: var(--color-indigo-300);
|
||||||
|
--color-text-status-links: var(--color-text-brand);
|
||||||
|
|
||||||
|
/* BORDER TOKENS */
|
||||||
|
|
||||||
|
--border-strength-primary: 18%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status__content a,
|
||||||
|
.reply-indicator__content a,
|
||||||
|
.edit-indicator__content a,
|
||||||
|
.link-footer a,
|
||||||
|
.status__content__read-more-button,
|
||||||
|
.status__content__translate-button {
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mention {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
span {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
span {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-button:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
app/javascript/styles_new/entrypoints/inert.scss
Normal file
14
app/javascript/styles_new/entrypoints/inert.scss
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/* This is needed for the wicg-inert polyfill */
|
||||||
|
|
||||||
|
[inert] {
|
||||||
|
pointer-events: none;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
[inert],
|
||||||
|
[inert] * {
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
}
|
||||||
1030
app/javascript/styles_new/entrypoints/mailer.scss
Normal file
1030
app/javascript/styles_new/entrypoints/mailer.scss
Normal file
File diff suppressed because it is too large
Load Diff
8
app/javascript/styles_new/fonts/inter.scss
Normal file
8
app/javascript/styles_new/fonts/inter.scss
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: Inter;
|
||||||
|
src: url('../../fonts/inter/inter-variable-font-slnt-wght.woff2')
|
||||||
|
format('woff2-variations');
|
||||||
|
font-weight: 100 900;
|
||||||
|
font-style: normal;
|
||||||
|
mso-generic-font-family: swiss;
|
||||||
|
}
|
||||||
13
app/javascript/styles_new/fonts/roboto-mono.scss
Normal file
13
app/javascript/styles_new/fonts/roboto-mono.scss
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: mastodon-font-monospace;
|
||||||
|
src:
|
||||||
|
local('Roboto Mono'),
|
||||||
|
url('@/fonts/roboto-mono/robotomono-regular-webfont.woff2') format('woff2'),
|
||||||
|
url('@/fonts/roboto-mono/robotomono-regular-webfont.woff') format('woff'),
|
||||||
|
url('@/fonts/roboto-mono/robotomono-regular-webfont.ttf') format('truetype'),
|
||||||
|
url('@/fonts/roboto-mono/robotomono-regular-webfont.svg#roboto_monoregular')
|
||||||
|
format('svg');
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
55
app/javascript/styles_new/fonts/roboto.scss
Normal file
55
app/javascript/styles_new/fonts/roboto.scss
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: mastodon-font-sans-serif;
|
||||||
|
src:
|
||||||
|
local('Roboto Italic'),
|
||||||
|
url('@/fonts/roboto/roboto-italic-webfont.woff2') format('woff2'),
|
||||||
|
url('@/fonts/roboto/roboto-italic-webfont.woff') format('woff'),
|
||||||
|
url('@/fonts/roboto/roboto-italic-webfont.ttf') format('truetype'),
|
||||||
|
url('@/fonts/roboto/roboto-italic-webfont.svg#roboto-italic-webfont')
|
||||||
|
format('svg');
|
||||||
|
font-weight: normal;
|
||||||
|
font-display: swap;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: mastodon-font-sans-serif;
|
||||||
|
src:
|
||||||
|
local('Roboto Bold'),
|
||||||
|
url('@/fonts/roboto/roboto-bold-webfont.woff2') format('woff2'),
|
||||||
|
url('@/fonts/roboto/roboto-bold-webfont.woff') format('woff'),
|
||||||
|
url('@/fonts/roboto/roboto-bold-webfont.ttf') format('truetype'),
|
||||||
|
url('@/fonts/roboto/roboto-bold-webfont.svg#roboto-bold-webfont')
|
||||||
|
format('svg');
|
||||||
|
font-weight: bold;
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: mastodon-font-sans-serif;
|
||||||
|
src:
|
||||||
|
local('Roboto Medium'),
|
||||||
|
url('@/fonts/roboto/roboto-medium-webfont.woff2') format('woff2'),
|
||||||
|
url('@/fonts/roboto/roboto-medium-webfont.woff') format('woff'),
|
||||||
|
url('@/fonts/roboto/roboto-medium-webfont.ttf') format('truetype'),
|
||||||
|
url('@/fonts/roboto/roboto-medium-webfont.svg#roboto-medium-webfont')
|
||||||
|
format('svg');
|
||||||
|
font-weight: 500;
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: mastodon-font-sans-serif;
|
||||||
|
src:
|
||||||
|
local('Roboto'),
|
||||||
|
url('@/fonts/roboto/roboto-regular-webfont.woff2') format('woff2'),
|
||||||
|
url('@/fonts/roboto/roboto-regular-webfont.woff') format('woff'),
|
||||||
|
url('@/fonts/roboto/roboto-regular-webfont.ttf') format('truetype'),
|
||||||
|
url('@/fonts/roboto/roboto-regular-webfont.svg#roboto-regular-webfont')
|
||||||
|
format('svg');
|
||||||
|
font-weight: normal;
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
9
app/javascript/styles_new/mastodon-light.scss
Normal file
9
app/javascript/styles_new/mastodon-light.scss
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
@use 'mastodon-light/css_variables';
|
||||||
|
@use 'mastodon/variables' with (
|
||||||
|
$emojis-requiring-inversion: 'chains'
|
||||||
|
);
|
||||||
|
@use 'common';
|
||||||
|
|
||||||
|
html {
|
||||||
|
color-scheme: light;
|
||||||
|
}
|
||||||
214
app/javascript/styles_new/mastodon-light/css_variables.scss
Normal file
214
app/javascript/styles_new/mastodon-light/css_variables.scss
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
@use '../mastodon/theme_utils' as utils;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--color-black: #000;
|
||||||
|
--color-grey-950: #181821;
|
||||||
|
--color-grey-800: #292938;
|
||||||
|
--color-grey-700: #444664;
|
||||||
|
--color-grey-600: #545778;
|
||||||
|
--color-grey-500: #696d91;
|
||||||
|
--color-grey-400: #8b8dac;
|
||||||
|
--color-grey-300: #b4b6cb;
|
||||||
|
--color-grey-200: #d8d9e3;
|
||||||
|
--color-grey-100: #f0f0f5;
|
||||||
|
--color-grey-50: #f0f1ff;
|
||||||
|
--color-white: #fff;
|
||||||
|
--color-indigo-600: #6147e6;
|
||||||
|
--color-indigo-400: #8886ff;
|
||||||
|
--color-indigo-300: #a5abfd;
|
||||||
|
--color-indigo-200: #c8cdfe;
|
||||||
|
--color-indigo-100: #e0e3ff;
|
||||||
|
--color-indigo-50: #f0f1ff;
|
||||||
|
--color-red-500: #ff637e;
|
||||||
|
--color-red-600: #ec003f;
|
||||||
|
--color-yellow-400: #ffb900;
|
||||||
|
--color-yellow-600: #e17100;
|
||||||
|
--color-green-400: #05df72;
|
||||||
|
--color-green-600: #00a63e;
|
||||||
|
|
||||||
|
/* TEXT TOKENS */
|
||||||
|
|
||||||
|
--color-text-primary: var(--color-grey-950);
|
||||||
|
--color-text-secondary: var(--color-grey-600);
|
||||||
|
--color-text-tertiary: var(--color-grey-500);
|
||||||
|
--color-text-on-inverted: var(--color-white);
|
||||||
|
--color-text-brand: var(--color-indigo-600);
|
||||||
|
--color-text-brand-soft: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-text-primary),
|
||||||
|
var(--color-text-brand)
|
||||||
|
);
|
||||||
|
--color-text-on-brand-base: var(--color-white);
|
||||||
|
--color-text-error: var(--color-red-600);
|
||||||
|
--color-text-on-error-base: var(--color-white);
|
||||||
|
--color-text-warning: var(--color-yellow-600);
|
||||||
|
--color-text-on-warning-base: var(--color-white);
|
||||||
|
--color-text-success: var(--color-green-600);
|
||||||
|
--color-text-on-success-base: var(--color-white);
|
||||||
|
--color-text-disabled: var(--color-grey-300);
|
||||||
|
--color-text-on-disabled: var(--color-grey-200);
|
||||||
|
--color-text-bookmark-highlight: var(--color-text-error);
|
||||||
|
--color-text-favourite-highlight: var(--color-text-warning);
|
||||||
|
--color-text-on-media: var(--color-white);
|
||||||
|
--color-text-status-links: var(--color-text-brand);
|
||||||
|
|
||||||
|
/* BACKGROUND TOKENS */
|
||||||
|
|
||||||
|
// Neutrals
|
||||||
|
--color-bg-primary: var(--color-white);
|
||||||
|
--overlay-strength-secondary: 5%;
|
||||||
|
--color-bg-secondary-base: var(--color-grey-600);
|
||||||
|
--color-bg-secondary: #{color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-bg-primary),
|
||||||
|
var(--color-bg-secondary-base) var(--overlay-strength-secondary)
|
||||||
|
)};
|
||||||
|
--color-bg-secondary-solid: #{color-mix(
|
||||||
|
in srgb,
|
||||||
|
var(--color-bg-primary),
|
||||||
|
var(--color-bg-secondary-base) var(--overlay-strength-secondary)
|
||||||
|
)};
|
||||||
|
--color-bg-tertiary: #{color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-bg-primary),
|
||||||
|
var(--color-bg-secondary-base) calc(2 * var(--overlay-strength-secondary))
|
||||||
|
)};
|
||||||
|
|
||||||
|
// Utility
|
||||||
|
--color-bg-ambient: var(--color-bg-primary);
|
||||||
|
--color-bg-elevated: var(--color-bg-primary);
|
||||||
|
--color-bg-inverted: var(--color-grey-950);
|
||||||
|
--color-bg-media-base: var(--color-black);
|
||||||
|
--color-bg-media-strength: 65%;
|
||||||
|
--color-bg-media: #{utils.css-alpha(
|
||||||
|
var(--color-bg-media-base),
|
||||||
|
var(--color-bg-media-strength)
|
||||||
|
)};
|
||||||
|
--color-bg-overlay: var(--color-bg-primary);
|
||||||
|
--color-bg-disabled: var(--color-grey-400);
|
||||||
|
|
||||||
|
// Brand
|
||||||
|
--overlay-strength-brand: 8%;
|
||||||
|
--color-bg-brand-base: var(--color-indigo-600);
|
||||||
|
--color-bg-brand-base-hover: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-bg-brand-base),
|
||||||
|
black var(--overlay-strength-brand)
|
||||||
|
);
|
||||||
|
--color-bg-brand-soft: #{utils.css-alpha(
|
||||||
|
var(--color-bg-brand-base),
|
||||||
|
calc(var(--overlay-strength-brand) * 1.5)
|
||||||
|
)};
|
||||||
|
--color-bg-brand-softer: #{utils.css-alpha(
|
||||||
|
var(--color-bg-brand-base),
|
||||||
|
var(--overlay-strength-brand)
|
||||||
|
)};
|
||||||
|
|
||||||
|
// Error
|
||||||
|
--overlay-strength-error: 12%;
|
||||||
|
--color-bg-error-base: var(--color-red-600);
|
||||||
|
--color-bg-error-base-hover: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-bg-error-base),
|
||||||
|
black var(--overlay-strength-error)
|
||||||
|
);
|
||||||
|
--color-bg-error-soft: #{utils.css-alpha(
|
||||||
|
var(--color-bg-error-base),
|
||||||
|
calc(var(--overlay-strength-error) * 1.5)
|
||||||
|
)};
|
||||||
|
--color-bg-error-softer: #{utils.css-alpha(
|
||||||
|
var(--color-bg-error-base),
|
||||||
|
var(--overlay-strength-error)
|
||||||
|
)};
|
||||||
|
|
||||||
|
// Warning
|
||||||
|
--overlay-strength-warning: 10%;
|
||||||
|
--color-bg-warning-base: var(--color-yellow-600);
|
||||||
|
--color-bg-warning-base-hover: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-bg-warning-base),
|
||||||
|
black var(--overlay-strength-warning)
|
||||||
|
);
|
||||||
|
--color-bg-warning-soft: #{utils.css-alpha(
|
||||||
|
var(--color-bg-warning-base),
|
||||||
|
calc(var(--overlay-strength-warning) * 1.5)
|
||||||
|
)};
|
||||||
|
--color-bg-warning-softer: #{utils.css-alpha(
|
||||||
|
var(--color-bg-warning-base),
|
||||||
|
var(--overlay-strength-warning)
|
||||||
|
)};
|
||||||
|
|
||||||
|
// Success
|
||||||
|
--overlay-strength-success: 15%;
|
||||||
|
--color-bg-success-base: var(--color-green-600);
|
||||||
|
--color-bg-success-base-hover: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-bg-success-base),
|
||||||
|
black var(--overlay-strength-success)
|
||||||
|
);
|
||||||
|
--color-bg-success-soft: #{utils.css-alpha(
|
||||||
|
var(--color-bg-success-base),
|
||||||
|
calc(var(--overlay-strength-success) * 1.5)
|
||||||
|
)};
|
||||||
|
--color-bg-success-softer: #{utils.css-alpha(
|
||||||
|
var(--color-bg-success-base),
|
||||||
|
var(--overlay-strength-success)
|
||||||
|
)};
|
||||||
|
|
||||||
|
/* BORDER TOKENS */
|
||||||
|
|
||||||
|
--border-strength-primary: 15%;
|
||||||
|
--color-border-primary: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-bg-primary),
|
||||||
|
var(--color-grey-950) var(--border-strength-primary)
|
||||||
|
);
|
||||||
|
--color-border-media: rgb(252 248 255 / 15%);
|
||||||
|
--color-border-on-bg-secondary: var(--color-grey-200);
|
||||||
|
--color-border-on-bg-brand-softer: var(--color-indigo-200);
|
||||||
|
--color-border-on-bg-error-softer: #{utils.css-alpha(
|
||||||
|
var(--color-text-error),
|
||||||
|
50%
|
||||||
|
)};
|
||||||
|
--color-border-on-bg-warning-softer: #{utils.css-alpha(
|
||||||
|
var(--color-text-warning),
|
||||||
|
50%
|
||||||
|
)};
|
||||||
|
--color-border-on-bg-success-softer: #{utils.css-alpha(
|
||||||
|
var(--color-text-success),
|
||||||
|
50%
|
||||||
|
)};
|
||||||
|
--color-border-on-bg-inverted: var(--color-border-primary);
|
||||||
|
|
||||||
|
/* SHADOW TOKENS */
|
||||||
|
|
||||||
|
--shadow-strength-primary: 30%;
|
||||||
|
--color-shadow-primary: #{utils.css-alpha(
|
||||||
|
var(--color-black),
|
||||||
|
var(--shadow-strength-primary)
|
||||||
|
)};
|
||||||
|
--dropdown-shadow:
|
||||||
|
0 20px 25px -5px var(--color-shadow-primary),
|
||||||
|
0 8px 10px -6px var(--color-shadow-primary);
|
||||||
|
--overlay-icon-shadow: drop-shadow(0 0 8px var(--color-shadow-primary));
|
||||||
|
|
||||||
|
/* GRAPHS/CHARTS TOKENS */
|
||||||
|
|
||||||
|
--color-graph-primary-stroke: var(--color-text-brand);
|
||||||
|
--color-graph-primary-fill: var(--color-bg-brand-softer);
|
||||||
|
--color-graph-warning-stroke: var(--color-text-warning);
|
||||||
|
--color-graph-warning-fill: var(--color-bg-warning-softer);
|
||||||
|
--color-graph-disabled-stroke: var(--color-text-disabled);
|
||||||
|
--color-graph-disabled-fill: var(--color-bg-disabled);
|
||||||
|
|
||||||
|
/* LEGACY TOKENS */
|
||||||
|
|
||||||
|
--rich-text-container-color: rgb(255 216 231 / 100%);
|
||||||
|
--rich-text-text-color: rgb(114 47 83 / 100%);
|
||||||
|
--rich-text-decorations-color: rgb(255 175 212 / 100%);
|
||||||
|
|
||||||
|
/* MISCELLANEOUS */
|
||||||
|
|
||||||
|
--outline-focus-default: 2px solid var(--color-text-brand);
|
||||||
|
--avatar-border-radius: 8px;
|
||||||
|
}
|
||||||
45
app/javascript/styles_new/mastodon/_mixins.scss
Normal file
45
app/javascript/styles_new/mastodon/_mixins.scss
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
@mixin search-input {
|
||||||
|
outline: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: none;
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--color-border-on-bg-secondary);
|
||||||
|
font-size: 17px;
|
||||||
|
line-height: normal;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin search-popout {
|
||||||
|
background: var(--color-bg-elevated);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
padding-bottom: 14px;
|
||||||
|
margin-top: 10px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
box-shadow: 2px 4px 15px var(--color-shadow-primary);
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
app/javascript/styles_new/mastodon/_theme_utils.scss
Normal file
3
app/javascript/styles_new/mastodon/_theme_utils.scss
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@function css-alpha($base-color, $amount) {
|
||||||
|
@return #{rgb(from $base-color r g b / $amount)};
|
||||||
|
}
|
||||||
27
app/javascript/styles_new/mastodon/_variables.scss
Normal file
27
app/javascript/styles_new/mastodon/_variables.scss
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Keep this filter a SCSS variable rather than
|
||||||
|
// a CSS Custom Property due to this Safari bug:
|
||||||
|
// https://github.com/mdn/browser-compat-data/issues/25914#issuecomment-2676190245
|
||||||
|
$backdrop-blur-filter: blur(10px) saturate(180%) contrast(75%) brightness(70%);
|
||||||
|
|
||||||
|
// Language codes that uses CJK fonts
|
||||||
|
$cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW;
|
||||||
|
|
||||||
|
// Variables for components
|
||||||
|
$media-modal-media-max-width: 100%;
|
||||||
|
|
||||||
|
// put margins on top and bottom of image to avoid the screen covered by image.
|
||||||
|
$media-modal-media-max-height: 80%;
|
||||||
|
|
||||||
|
$no-gap-breakpoint: 1175px;
|
||||||
|
$mobile-menu-breakpoint: 760px;
|
||||||
|
$mobile-breakpoint: 630px;
|
||||||
|
$no-columns-breakpoint: 600px;
|
||||||
|
|
||||||
|
$font-sans-serif: 'mastodon-font-sans-serif' !default;
|
||||||
|
$font-display: 'mastodon-font-display' !default;
|
||||||
|
$font-monospace: 'mastodon-font-monospace' !default;
|
||||||
|
|
||||||
|
$emojis-requiring-inversion: 'back' 'copyright' 'curly_loop' 'currency_exchange'
|
||||||
|
'end' 'heavy_check_mark' 'heavy_division_sign' 'heavy_dollar_sign'
|
||||||
|
'heavy_minus_sign' 'heavy_multiplication_x' 'heavy_plus_sign' 'on'
|
||||||
|
'registered' 'soon' 'spider' 'telephone_receiver' 'tm' 'top' 'wavy_dash' !default;
|
||||||
130
app/javascript/styles_new/mastodon/about.scss
Normal file
130
app/javascript/styles_new/mastodon/about.scss
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
$maximum-width: 1235px;
|
||||||
|
$fluid-breakpoint: $maximum-width + 20px;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: $maximum-width;
|
||||||
|
margin: 0 auto;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
@media screen and (max-width: $fluid-breakpoint) {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
position: relative;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-list {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 22px;
|
||||||
|
counter-reset: list-counter;
|
||||||
|
|
||||||
|
li {
|
||||||
|
position: relative;
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
padding: 1em 1.75em;
|
||||||
|
padding-inline-start: 3em;
|
||||||
|
font-weight: 500;
|
||||||
|
counter-increment: list-counter;
|
||||||
|
min-height: 4ch;
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
text-align: start;
|
||||||
|
font: inherit;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[aria-expanded='false'] .rules-list__hint {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
@supports (-webkit-line-clamp: 2) {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: counter(list-counter);
|
||||||
|
position: absolute;
|
||||||
|
inset-inline-start: 0;
|
||||||
|
top: 1em;
|
||||||
|
background: var(--color-bg-brand-base);
|
||||||
|
color: var(--color-text-on-brand-base);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 4ch;
|
||||||
|
height: 4ch;
|
||||||
|
font-weight: 500;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__hint {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-languages {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
appearance: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
outline: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
resize: vertical;
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding-inline-start: 10px;
|
||||||
|
padding-inline-end: 30px;
|
||||||
|
height: 41px;
|
||||||
|
|
||||||
|
@media screen and (width <= 600px) {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
app/javascript/styles_new/mastodon/accessibility.scss
Normal file
13
app/javascript/styles_new/mastodon/accessibility.scss
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
%emoji-color-inversion {
|
||||||
|
filter: invert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.emojione {
|
||||||
|
@each $emoji in $emojis-requiring-inversion {
|
||||||
|
&[title=':#{$emoji}:'] {
|
||||||
|
@extend %emoji-color-inversion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
411
app/javascript/styles_new/mastodon/accounts.scss
Normal file
411
app/javascript/styles_new/mastodon/accounts.scss
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
@use 'sass:color';
|
||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
.card {
|
||||||
|
& > a {
|
||||||
|
display: block;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
.card__bar {
|
||||||
|
background: var(--color-bg-brand-softer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__img {
|
||||||
|
height: 130px;
|
||||||
|
position: relative;
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-bottom: none;
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (width <= 600px) {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__bar {
|
||||||
|
position: relative;
|
||||||
|
padding: 15px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-top: none;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
padding-top: 2px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-name {
|
||||||
|
margin-inline-start: 15px;
|
||||||
|
text-align: start;
|
||||||
|
|
||||||
|
svg[data-hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-size: 15px;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
font-weight: 500;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-weight: 400;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
padding: 30px 0;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
a,
|
||||||
|
.current,
|
||||||
|
.newer,
|
||||||
|
.older,
|
||||||
|
.page,
|
||||||
|
.gap {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
font-weight: 500;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current {
|
||||||
|
color: var(--color-bg-inverted);
|
||||||
|
background: var(--color-text-on-inverted);
|
||||||
|
border-radius: 100px;
|
||||||
|
cursor: default;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gap {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.older,
|
||||||
|
.newer {
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.older {
|
||||||
|
float: left;
|
||||||
|
padding-inline-start: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.newer {
|
||||||
|
float: right;
|
||||||
|
padding-inline-end: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
cursor: default;
|
||||||
|
color: var(--color-text-disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (width <= 700px) {
|
||||||
|
padding: 30px 20px;
|
||||||
|
|
||||||
|
.page {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.newer,
|
||||||
|
.older {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nothing-here {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: default;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 20px;
|
||||||
|
min-height: 30vh;
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
|
||||||
|
@media screen and (min-width: ($no-gap-breakpoint - 1)) {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--no-toolbar {
|
||||||
|
border-top: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--under-tabs {
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--flexible {
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.information-badge,
|
||||||
|
.simple_form .overridden,
|
||||||
|
.simple_form .recommended,
|
||||||
|
.simple_form .not_recommended {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 6px;
|
||||||
|
cursor: default;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.information-badge,
|
||||||
|
.simple_form .overridden,
|
||||||
|
.simple_form .recommended,
|
||||||
|
.simple_form .not_recommended {
|
||||||
|
background-color: var(--color-bg-secondary);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.information-badge {
|
||||||
|
&.superapp {
|
||||||
|
color: var(--color-text-success);
|
||||||
|
background-color: var(--color-bg-success-softer);
|
||||||
|
border-color: var(--color-border-on-bg-success-softer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-role {
|
||||||
|
display: inline-flex;
|
||||||
|
padding: 4px;
|
||||||
|
padding-inline-end: 8px;
|
||||||
|
border: 1px solid var(--color-text-brand);
|
||||||
|
color: var(--color-text-brand);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
line-height: 16px;
|
||||||
|
gap: 4px;
|
||||||
|
border-radius: 6px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: auto;
|
||||||
|
height: 15px;
|
||||||
|
opacity: 0.85;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__domain {
|
||||||
|
font-weight: 400;
|
||||||
|
opacity: 0.75;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple_form .not_recommended {
|
||||||
|
color: var(--color-text-error);
|
||||||
|
background-color: var(--color-bg-error-softer);
|
||||||
|
border-color: var(--color-border-on-bg-error-softer);
|
||||||
|
}
|
||||||
|
|
||||||
|
.account__header__fields {
|
||||||
|
max-width: 100vw;
|
||||||
|
padding: 0;
|
||||||
|
margin: 15px -15px -15px;
|
||||||
|
border: 0 none;
|
||||||
|
border-top: 1px solid var(--color-border-primary);
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
|
||||||
|
dl {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
dt,
|
||||||
|
dd {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 14px;
|
||||||
|
text-align: center;
|
||||||
|
max-height: 48px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: 500;
|
||||||
|
width: 120px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-text-brand);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.verified {
|
||||||
|
border: 1px solid var(--color-border-on-bg-success-softer);
|
||||||
|
background: var(--color-bg-success-softer);
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-text-success);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__mark {
|
||||||
|
color: var(--color-text-success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dl:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.directory__tag .trends__item__current {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pending-account {
|
||||||
|
&__header {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-hint {
|
||||||
|
font-weight: normal !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-table__row--muted {
|
||||||
|
color: var(--color-text-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-table__row--muted .pending-account__header,
|
||||||
|
.batch-table__row--muted .accounts-table,
|
||||||
|
.batch-table__row--muted .name-tag {
|
||||||
|
&,
|
||||||
|
a,
|
||||||
|
strong {
|
||||||
|
color: var(--color-text-tertiary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-table__row--muted .name-tag .avatar {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-table__row--muted .accounts-table {
|
||||||
|
tbody td.accounts-table__extra,
|
||||||
|
&__count,
|
||||||
|
&__count small {
|
||||||
|
color: var(--color-text-tertiary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-table__row--attention {
|
||||||
|
color: var(--color-text-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-table__row--attention .pending-account__header,
|
||||||
|
.batch-table__row--attention .accounts-table,
|
||||||
|
.batch-table__row--attention .name-tag {
|
||||||
|
&,
|
||||||
|
a,
|
||||||
|
strong {
|
||||||
|
color: var(--color-text-warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-table__row--attention .accounts-table {
|
||||||
|
tbody td.accounts-table__extra,
|
||||||
|
&__count,
|
||||||
|
&__count small {
|
||||||
|
color: var(--color-text-warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
2173
app/javascript/styles_new/mastodon/admin.scss
Normal file
2173
app/javascript/styles_new/mastodon/admin.scss
Normal file
File diff suppressed because it is too large
Load Diff
342
app/javascript/styles_new/mastodon/annual_reports.scss
Normal file
342
app/javascript/styles_new/mastodon/annual_reports.scss
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--indigo-1: #17063b;
|
||||||
|
--indigo-2: #2f0c7a;
|
||||||
|
--indigo-3: #562cfc;
|
||||||
|
--indigo-5: #858afa;
|
||||||
|
--indigo-6: #cccfff;
|
||||||
|
--lime: #baff3b;
|
||||||
|
--goldenrod-2: #ffc954;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annual-report {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
background: var(--indigo-1);
|
||||||
|
padding: 24px;
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 25px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 30px;
|
||||||
|
color: var(--lime);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 20px;
|
||||||
|
color: var(--indigo-6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__bento {
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
|
||||||
|
grid-template-rows: minmax(0, auto) minmax(0, 1fr) minmax(0, auto) minmax(
|
||||||
|
0,
|
||||||
|
auto
|
||||||
|
);
|
||||||
|
|
||||||
|
&__box {
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--indigo-2);
|
||||||
|
color: var(--indigo-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__summary {
|
||||||
|
&__most-boosted-post {
|
||||||
|
grid-column: span 2;
|
||||||
|
grid-row: span 2;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.status__content,
|
||||||
|
.content-warning {
|
||||||
|
color: var(--indigo-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailed-status {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-warning {
|
||||||
|
border: 0;
|
||||||
|
background: var(--indigo-1);
|
||||||
|
|
||||||
|
.link-button {
|
||||||
|
color: var(--indigo-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailed-status__meta__line {
|
||||||
|
border-bottom-color: var(--indigo-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailed-status__meta {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailed-status__meta,
|
||||||
|
.poll__footer,
|
||||||
|
.poll__link,
|
||||||
|
.detailed-status .logo,
|
||||||
|
.detailed-status__display-name {
|
||||||
|
color: var(--indigo-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailed-status__meta .animated-number,
|
||||||
|
.detailed-status__display-name strong {
|
||||||
|
color: var(--indigo-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.poll__chart {
|
||||||
|
background-color: var(--indigo-3);
|
||||||
|
|
||||||
|
&.leading {
|
||||||
|
background-color: var(--goldenrod-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-card,
|
||||||
|
.hashtag-bar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__followers {
|
||||||
|
grid-column: span 1;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-block-start: 24px;
|
||||||
|
padding-block-end: 24px;
|
||||||
|
|
||||||
|
--sparkline-gradient-top: rgba(86, 44, 252, 50%);
|
||||||
|
--sparkline-gradient-bottom: rgba(86, 44, 252, 0%);
|
||||||
|
|
||||||
|
&__foreground {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__number {
|
||||||
|
font-size: 31px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 37px;
|
||||||
|
color: var(--lime);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 17px;
|
||||||
|
color: var(--indigo-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footnote {
|
||||||
|
display: block;
|
||||||
|
font-weight: 400;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
inset-inline-end: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
height: 70%;
|
||||||
|
width: auto;
|
||||||
|
|
||||||
|
path:first-child {
|
||||||
|
fill: url('#gradient') !important;
|
||||||
|
fill-opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
path:last-child {
|
||||||
|
stroke: var(--color-graph-primary-stroke) !important;
|
||||||
|
fill: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__archetype {
|
||||||
|
grid-column: span 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
padding: 16px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 17px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--lime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__most-used-app {
|
||||||
|
grid-column: span 1;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 17px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--indigo-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 17px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--goldenrod-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__percentile {
|
||||||
|
grid-row: span 2;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
text-align: center;
|
||||||
|
text-wrap: balance;
|
||||||
|
padding: 16px 8px;
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__number {
|
||||||
|
font-size: 54px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 73px;
|
||||||
|
color: var(--goldenrod-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footnote {
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 14px;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__new-posts {
|
||||||
|
grid-column: span 2;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px;
|
||||||
|
color: var(--indigo-6);
|
||||||
|
z-index: 1;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__number {
|
||||||
|
font-size: 76px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 91px;
|
||||||
|
color: var(--goldenrod-2);
|
||||||
|
z-index: 1;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
position: absolute;
|
||||||
|
inset-inline-start: -7px;
|
||||||
|
top: -4px;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__most-used-hashtag {
|
||||||
|
grid-column: span 2;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__hashtag {
|
||||||
|
font-size: 42px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 58px;
|
||||||
|
color: var(--indigo-6);
|
||||||
|
margin-inline-start: -100%;
|
||||||
|
margin-inline-end: -100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 17px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.annual-report-modal {
|
||||||
|
max-width: 600px;
|
||||||
|
background: var(--indigo-1);
|
||||||
|
border-radius: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.loading-indicator .circular-progress {
|
||||||
|
color: var(--lime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: $no-columns-breakpoint) {
|
||||||
|
border-bottom: 0;
|
||||||
|
border-radius: 16px 16px 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-group--annual-report {
|
||||||
|
.notification-group__icon {
|
||||||
|
color: var(--lime);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-group__main .link-button {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--lime);
|
||||||
|
}
|
||||||
|
}
|
||||||
300
app/javascript/styles_new/mastodon/basics.scss
Normal file
300
app/javascript/styles_new/mastodon/basics.scss
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
html.has-modal {
|
||||||
|
&,
|
||||||
|
body {
|
||||||
|
touch-action: none;
|
||||||
|
overscroll-behavior: none;
|
||||||
|
-webkit-overflow-scrolling: auto;
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: $font-sans-serif, sans-serif;
|
||||||
|
background: var(--color-bg-ambient);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 18px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
text-rendering: optimizelegibility;
|
||||||
|
|
||||||
|
// Disable kerning for Japanese text to preserve monospaced alignment for readability
|
||||||
|
&:not(:lang(ja)) {
|
||||||
|
font-feature-settings: 'kern';
|
||||||
|
}
|
||||||
|
|
||||||
|
text-size-adjust: none;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0%);
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
|
||||||
|
&.system-font {
|
||||||
|
// system-ui => standard property (Chrome/Android WebView 56+, Opera 43+, Safari 11+)
|
||||||
|
// -apple-system => Safari <11 specific
|
||||||
|
// BlinkMacSystemFont => Chrome <56 on macOS specific
|
||||||
|
// Segoe UI => Windows 7/8/10
|
||||||
|
// Oxygen => KDE
|
||||||
|
// Ubuntu => Unity/Ubuntu
|
||||||
|
// Cantarell => GNOME
|
||||||
|
// Fira Sans => Firefox OS
|
||||||
|
// Droid Sans => Older Androids (<4.0)
|
||||||
|
// Helvetica Neue => Older macOS <10.11
|
||||||
|
// $font-sans-serif => web-font (Roboto) fallback and newer Androids (>=4.0)
|
||||||
|
font-family:
|
||||||
|
system-ui,
|
||||||
|
-apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
'Segoe UI',
|
||||||
|
Oxygen,
|
||||||
|
Ubuntu,
|
||||||
|
Cantarell,
|
||||||
|
'Fira Sans',
|
||||||
|
'Droid Sans',
|
||||||
|
'Helvetica Neue',
|
||||||
|
$font-sans-serif,
|
||||||
|
sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.app-body {
|
||||||
|
padding: 0;
|
||||||
|
padding-left: env(safe-area-inset-left);
|
||||||
|
padding-right: env(safe-area-inset-right);
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&.layout-single-column {
|
||||||
|
height: auto;
|
||||||
|
min-height: 100vh;
|
||||||
|
min-height: 100dvh;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.layout-multiple-columns {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.player {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-player video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
max-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-gallery {
|
||||||
|
margin-top: 0;
|
||||||
|
height: 100% !important;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-gallery__item {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.embed {
|
||||||
|
margin: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.admin {
|
||||||
|
padding: 0;
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: 20px;
|
||||||
|
|
||||||
|
&__illustration {
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
max-width: 470px;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin-top: -120px;
|
||||||
|
margin-bottom: -45px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 28px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
&:focus {
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: var(--outline-focus-default);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus:not(:focus-visible) {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-family: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:focus:not(:focus-visible) {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-holder {
|
||||||
|
&,
|
||||||
|
& > div,
|
||||||
|
& > noscript {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
outline: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > noscript {
|
||||||
|
min-height: 100vh;
|
||||||
|
min-height: 100dvh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-single-column .app-holder {
|
||||||
|
&,
|
||||||
|
& > div {
|
||||||
|
min-height: 100vh;
|
||||||
|
min-height: 100dvh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-multiple-columns .app-holder {
|
||||||
|
&,
|
||||||
|
& > div {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-boundary,
|
||||||
|
.app-holder noscript {
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: var(--color-text-error);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 0.85em;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-text-brand);
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: inline;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font: inherit;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
line-height: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: 0;
|
||||||
|
transition: color 300ms linear;
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.copied {
|
||||||
|
color: var(--mas-status-success-color);
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-resources {
|
||||||
|
// Not using display: none because of https://bugs.chromium.org/p/chromium/issues/detail?id=258029
|
||||||
|
visibility: hidden;
|
||||||
|
user-select: none;
|
||||||
|
pointer-events: none;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
inset-inline-start: 0;
|
||||||
|
z-index: -1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoScript adds a __ns__pop2top class to the full ancestry of blocked elements,
|
||||||
|
// to set the z-index to a high value, which messes with modals and dropdowns.
|
||||||
|
// Blocked elements can in theory only be media and frames/embeds, so they
|
||||||
|
// should only appear in statuses, under divs and articles.
|
||||||
|
body,
|
||||||
|
div,
|
||||||
|
article {
|
||||||
|
.__ns__pop2top {
|
||||||
|
z-index: unset !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
5
app/javascript/styles_new/mastodon/branding.scss
Normal file
5
app/javascript/styles_new/mastodon/branding.scss
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
11433
app/javascript/styles_new/mastodon/components.scss
Normal file
11433
app/javascript/styles_new/mastodon/components.scss
Normal file
File diff suppressed because it is too large
Load Diff
166
app/javascript/styles_new/mastodon/containers.scss
Normal file
166
app/javascript/styles_new/mastodon/containers.scss
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
.container-alt {
|
||||||
|
width: 700px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
@media screen and (width <= 740px) {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-container {
|
||||||
|
margin: 50px auto;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
height: 42px;
|
||||||
|
margin-inline-end: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
outline: 0;
|
||||||
|
padding: 12px 16px;
|
||||||
|
line-height: 32px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.compose-standalone {
|
||||||
|
.compose-form {
|
||||||
|
width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 10px 0;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
@media screen and (width <= 400px) {
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-header {
|
||||||
|
width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 20px 0;
|
||||||
|
margin-top: 40px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
|
||||||
|
@media screen and (width <= 440px) {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: var(--avatar-border-radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
|
||||||
|
.username {
|
||||||
|
display: block;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logout-link {
|
||||||
|
display: block;
|
||||||
|
font-size: 32px;
|
||||||
|
line-height: 40px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.redirect {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 18px;
|
||||||
|
|
||||||
|
&__logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__message {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 17px;
|
||||||
|
line-height: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-text-brand);
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
228
app/javascript/styles_new/mastodon/css_variables.scss
Normal file
228
app/javascript/styles_new/mastodon/css_variables.scss
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
@use 'theme_utils' as utils;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--color-black: #000;
|
||||||
|
--color-grey-950: #181821;
|
||||||
|
--color-grey-800: #292938;
|
||||||
|
--color-grey-700: #444664;
|
||||||
|
--color-grey-600: #545778;
|
||||||
|
--color-grey-500: #696d91;
|
||||||
|
--color-grey-400: #8b8dac;
|
||||||
|
--color-grey-300: #b4b6cb;
|
||||||
|
--color-grey-200: #d8d9e3;
|
||||||
|
--color-grey-100: #f0f0f5;
|
||||||
|
--color-grey-50: #f0f1ff;
|
||||||
|
--color-white: #fff;
|
||||||
|
--color-indigo-600: #6147e6;
|
||||||
|
--color-indigo-400: #8886ff;
|
||||||
|
--color-indigo-300: #a5abfd;
|
||||||
|
--color-indigo-200: #c8cdfe;
|
||||||
|
--color-indigo-100: #e0e3ff;
|
||||||
|
--color-indigo-50: #f0f1ff;
|
||||||
|
--color-red-500: #ff637e;
|
||||||
|
--color-red-600: #ec003f;
|
||||||
|
--color-yellow-400: #ffb900;
|
||||||
|
--color-yellow-600: #e17100;
|
||||||
|
--color-green-400: #05df72;
|
||||||
|
--color-green-600: #00a63e;
|
||||||
|
|
||||||
|
/* TEXT TOKENS */
|
||||||
|
|
||||||
|
--color-text-primary: var(--color-grey-50);
|
||||||
|
--color-text-secondary: var(--color-grey-400);
|
||||||
|
--color-text-tertiary: var(--color-grey-500);
|
||||||
|
--color-text-on-inverted: var(--color-grey-950);
|
||||||
|
--color-text-brand: var(--color-indigo-400);
|
||||||
|
--color-text-brand-soft: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-text-primary),
|
||||||
|
var(--color-text-brand)
|
||||||
|
);
|
||||||
|
--color-text-on-brand-base: var(--color-white);
|
||||||
|
--color-text-error: var(--color-red-500);
|
||||||
|
--color-text-on-error-base: var(--color-white);
|
||||||
|
--color-text-warning: var(--color-yellow-400);
|
||||||
|
--color-text-on-warning-base: var(--color-white);
|
||||||
|
--color-text-success: var(--color-green-400);
|
||||||
|
--color-text-on-success-base: var(--color-white);
|
||||||
|
--color-text-disabled: var(--color-grey-600);
|
||||||
|
--color-text-on-disabled: var(--color-grey-400);
|
||||||
|
--color-text-bookmark-highlight: var(--color-text-error);
|
||||||
|
--color-text-favourite-highlight: var(--color-text-warning);
|
||||||
|
--color-text-on-media: var(--color-white);
|
||||||
|
--color-text-status-links: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-text-primary),
|
||||||
|
var(--color-text-secondary)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* BACKGROUND TOKENS */
|
||||||
|
|
||||||
|
// Neutrals
|
||||||
|
--color-bg-primary: var(--color-grey-950);
|
||||||
|
--overlay-strength-secondary: 10%;
|
||||||
|
--color-bg-secondary-base: var(--color-indigo-200);
|
||||||
|
--color-bg-secondary: #{utils.css-alpha(
|
||||||
|
var(--color-bg-secondary-base),
|
||||||
|
var(--overlay-strength-secondary)
|
||||||
|
)};
|
||||||
|
--color-bg-secondary-solid: color-mix(
|
||||||
|
in srgb,
|
||||||
|
var(--color-bg-primary),
|
||||||
|
var(--color-bg-secondary-base) var(--overlay-strength-secondary)
|
||||||
|
);
|
||||||
|
--color-bg-tertiary: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-bg-primary),
|
||||||
|
var(--color-bg-secondary-base) calc(2 * var(--overlay-strength-secondary))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Utility
|
||||||
|
--color-bg-ambient: var(--color-bg-primary);
|
||||||
|
--color-bg-elevated: var(--color-grey-800);
|
||||||
|
--color-bg-inverted: var(--color-grey-50);
|
||||||
|
--color-bg-media-base: var(--color-black);
|
||||||
|
--color-bg-media-strength: 65%;
|
||||||
|
--color-bg-media: #{utils.css-alpha(
|
||||||
|
var(--color-bg-media-base),
|
||||||
|
var(--color-bg-media-strength)
|
||||||
|
)};
|
||||||
|
--color-bg-overlay: var(--color-bg-primary);
|
||||||
|
--color-bg-disabled: var(--color-grey-700);
|
||||||
|
|
||||||
|
// Brand
|
||||||
|
--overlay-strength-brand: 10%;
|
||||||
|
--color-bg-brand-base: var(--color-indigo-600);
|
||||||
|
--color-bg-brand-base-hover: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-bg-brand-base),
|
||||||
|
black var(--overlay-strength-brand)
|
||||||
|
);
|
||||||
|
--color-bg-brand-soft: #{utils.css-alpha(
|
||||||
|
var(--color-bg-brand-base),
|
||||||
|
calc(var(--overlay-strength-brand) * 1.5)
|
||||||
|
)};
|
||||||
|
--color-bg-brand-softer: #{utils.css-alpha(
|
||||||
|
var(--color-bg-brand-base),
|
||||||
|
var(--overlay-strength-brand)
|
||||||
|
)};
|
||||||
|
|
||||||
|
// Error
|
||||||
|
--overlay-strength-error: 12%;
|
||||||
|
--color-bg-error-base: var(--color-red-600);
|
||||||
|
--color-bg-error-base-hover: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-bg-error-base),
|
||||||
|
black var(--overlay-strength-error)
|
||||||
|
);
|
||||||
|
--color-bg-error-soft: #{utils.css-alpha(
|
||||||
|
var(--color-bg-error-base),
|
||||||
|
calc(var(--overlay-strength-error) * 1.5)
|
||||||
|
)};
|
||||||
|
--color-bg-error-softer: #{utils.css-alpha(
|
||||||
|
var(--color-bg-error-base),
|
||||||
|
var(--overlay-strength-error)
|
||||||
|
)};
|
||||||
|
|
||||||
|
// Warning
|
||||||
|
--overlay-strength-warning: 10%;
|
||||||
|
--color-bg-warning-base: var(--color-yellow-600);
|
||||||
|
--color-bg-warning-base-hover: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-bg-warning-base),
|
||||||
|
black var(--overlay-strength-warning)
|
||||||
|
);
|
||||||
|
--color-bg-warning-soft: #{utils.css-alpha(
|
||||||
|
var(--color-bg-warning-base),
|
||||||
|
calc(var(--overlay-strength-warning) * 1.5)
|
||||||
|
)};
|
||||||
|
--color-bg-warning-softer: #{utils.css-alpha(
|
||||||
|
var(--color-bg-warning-base),
|
||||||
|
var(--overlay-strength-warning)
|
||||||
|
)};
|
||||||
|
|
||||||
|
// Success
|
||||||
|
--overlay-strength-success: 15%;
|
||||||
|
--color-bg-success-base: var(--color-green-600);
|
||||||
|
--color-bg-success-base-hover: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-bg-success-base),
|
||||||
|
black var(--overlay-strength-success)
|
||||||
|
);
|
||||||
|
--color-bg-success-soft: #{utils.css-alpha(
|
||||||
|
var(--color-bg-success-base),
|
||||||
|
calc(var(--overlay-strength-success) * 1.5)
|
||||||
|
)};
|
||||||
|
--color-bg-success-softer: #{utils.css-alpha(
|
||||||
|
var(--color-bg-success-base),
|
||||||
|
var(--overlay-strength-success)
|
||||||
|
)};
|
||||||
|
|
||||||
|
/* BORDER TOKENS */
|
||||||
|
|
||||||
|
--border-strength-primary: 18%;
|
||||||
|
--color-border-primary: #{utils.css-alpha(
|
||||||
|
var(--color-indigo-200),
|
||||||
|
var(--border-strength-primary)
|
||||||
|
)};
|
||||||
|
--color-border-media: rgb(252 248 255 / 15%);
|
||||||
|
--color-border-on-bg-secondary: #{utils.css-alpha(
|
||||||
|
var(--color-indigo-200),
|
||||||
|
calc(var(--border-strength-primary) / 1.5)
|
||||||
|
)};
|
||||||
|
--color-border-on-bg-brand-softer: var(--color-border-primary);
|
||||||
|
--color-border-on-bg-error-softer: #{utils.css-alpha(
|
||||||
|
var(--color-text-error),
|
||||||
|
50%
|
||||||
|
)};
|
||||||
|
--color-border-on-bg-warning-softer: #{utils.css-alpha(
|
||||||
|
var(--color-text-warning),
|
||||||
|
50%
|
||||||
|
)};
|
||||||
|
--color-border-on-bg-success-softer: #{utils.css-alpha(
|
||||||
|
var(--color-text-success),
|
||||||
|
50%
|
||||||
|
)};
|
||||||
|
--color-border-on-bg-inverted: var(--color-border-primary);
|
||||||
|
|
||||||
|
/* SHADOW TOKENS */
|
||||||
|
|
||||||
|
--shadow-strength-primary: 80%;
|
||||||
|
--color-shadow-primary: #{utils.css-alpha(
|
||||||
|
var(--color-black),
|
||||||
|
var(--shadow-strength-primary)
|
||||||
|
)};
|
||||||
|
--dropdown-shadow:
|
||||||
|
0 20px 25px -5px var(--color-shadow-primary),
|
||||||
|
0 8px 10px -6px var(--color-shadow-primary);
|
||||||
|
--overlay-icon-shadow: drop-shadow(0 0 8px var(--color-shadow-primary));
|
||||||
|
|
||||||
|
/* GRAPHS/CHARTS TOKENS */
|
||||||
|
|
||||||
|
--color-graph-primary-stroke: var(--color-text-brand);
|
||||||
|
--color-graph-primary-fill: var(--color-bg-brand-softer);
|
||||||
|
--color-graph-warning-stroke: var(--color-text-warning);
|
||||||
|
--color-graph-warning-fill: var(--color-bg-warning-softer);
|
||||||
|
--color-graph-disabled-stroke: var(--color-text-disabled);
|
||||||
|
--color-graph-disabled-fill: var(--color-bg-disabled);
|
||||||
|
|
||||||
|
/* LEGACY TOKENS */
|
||||||
|
|
||||||
|
--rich-text-container-color: rgb(87 24 60 / 100%);
|
||||||
|
--rich-text-text-color: rgb(255 175 212 / 100%);
|
||||||
|
--rich-text-decorations-color: rgb(128 58 95 / 100%);
|
||||||
|
|
||||||
|
/* MISCELLANEOUS */
|
||||||
|
|
||||||
|
--outline-focus-default: 2px solid var(--color-text-brand);
|
||||||
|
--avatar-border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
// Variable for easily inverting directional UI elements,
|
||||||
|
--text-x-direction: 1;
|
||||||
|
|
||||||
|
&.rtl {
|
||||||
|
--text-x-direction: -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
120
app/javascript/styles_new/mastodon/dashboard.scss
Normal file
120
app/javascript/styles_new/mastodon/dashboard.scss
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
.dashboard__counters {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 0 -5px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex: 0 0 33.333%;
|
||||||
|
padding: 0 5px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
& > div,
|
||||||
|
& > a {
|
||||||
|
padding: 20px;
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
background: var(--color-bg-brand-softer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__num,
|
||||||
|
&__text {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 24px;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
@media screen and (width <= 1350px) {
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
&--span-double-column {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--span-double-row {
|
||||||
|
grid-row: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__quick-access {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--color-bg-brand-base);
|
||||||
|
color: var(--color-text-on-brand-base);
|
||||||
|
transition: all 100ms ease-in;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-bg-brand-base-hover);
|
||||||
|
transition: all 200ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.positive {
|
||||||
|
background: var(--color-bg-success-softer);
|
||||||
|
color: var(--color-text-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.negative {
|
||||||
|
background: var(--color-bg-error-softer);
|
||||||
|
color: var(--color-text-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
248
app/javascript/styles_new/mastodon/emoji_picker.scss
Normal file
248
app/javascript/styles_new/mastodon/emoji_picker.scss
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
.emoji-mart {
|
||||||
|
font-size: 13px;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
&,
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-emoji {
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-bar {
|
||||||
|
&:first-child {
|
||||||
|
background: var(--color-bg-tertiary);
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-anchors {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 6px;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-anchor {
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: 12px 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: color 0.1s ease-out;
|
||||||
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: color-mix(
|
||||||
|
in oklab,
|
||||||
|
var(--color-text-primary),
|
||||||
|
var(--color-text-secondary)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-anchor-selected {
|
||||||
|
color: var(--color-text-brand);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-brand-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-anchor-bar {
|
||||||
|
bottom: -1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-anchor-bar {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -5px;
|
||||||
|
inset-inline-start: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 4px;
|
||||||
|
background-color: var(--color-text-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-anchors {
|
||||||
|
i {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: currentColor;
|
||||||
|
max-height: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-scroll {
|
||||||
|
overflow-y: scroll;
|
||||||
|
height: 270px;
|
||||||
|
max-height: 35vh;
|
||||||
|
padding: 0 6px 6px;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-search {
|
||||||
|
padding: 10px;
|
||||||
|
padding-inline-end: 45px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
input {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
padding: 7px 9px;
|
||||||
|
padding-inline-end: 25px;
|
||||||
|
font-family: inherit;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&::-moz-focus-inner {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
outline: none !important;
|
||||||
|
border-width: 1px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-search-cancel-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-search-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 18px;
|
||||||
|
inset-inline-end: 45px + 5px;
|
||||||
|
z-index: 2;
|
||||||
|
padding: 2px 5px 1px;
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
transition: all 100ms linear;
|
||||||
|
transition-property: opacity;
|
||||||
|
pointer-events: auto;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-category .emoji-mart-emoji {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
span {
|
||||||
|
z-index: 1;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::before {
|
||||||
|
z-index: -1;
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
inset-inline-start: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--color-bg-brand-softer);
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-category-label {
|
||||||
|
z-index: 2;
|
||||||
|
position: relative;
|
||||||
|
position: -webkit-sticky;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 5px 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For screenreaders only, via https://stackoverflow.com/a/19758620 */
|
||||||
|
.emoji-mart-sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip-path: inset(50%);
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-category-list {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-category-list li {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-emoji {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 0;
|
||||||
|
|
||||||
|
span {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-no-results {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-text-tertiary);
|
||||||
|
text-align: center;
|
||||||
|
padding: 5px 6px;
|
||||||
|
padding-top: 70px;
|
||||||
|
|
||||||
|
.emoji-mart-no-results-label {
|
||||||
|
margin-top: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-emoji:hover::before {
|
||||||
|
cursor: default;
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.emoji-mart-preview {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
1449
app/javascript/styles_new/mastodon/forms.scss
Normal file
1449
app/javascript/styles_new/mastodon/forms.scss
Normal file
File diff suppressed because it is too large
Load Diff
19
app/javascript/styles_new/mastodon/lists.scss
Normal file
19
app/javascript/styles_new/mastodon/lists.scss
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
.no-list {
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.recovery-codes {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
li {
|
||||||
|
font-size: 125%;
|
||||||
|
line-height: 1.5;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
53
app/javascript/styles_new/mastodon/modal.scss
Normal file
53
app/javascript/styles_new/mastodon/modal.scss
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
.modal-layout {
|
||||||
|
background: var(--color-bg-brand-softer);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-layout__mastodon {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
flex: 1;
|
||||||
|
max-height: 235px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
inset-inline-start: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (width <= 600px) {
|
||||||
|
.account-header {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.with-zig-zag-decoration {
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: auto 0 0;
|
||||||
|
height: 32px;
|
||||||
|
background-color: var(--color-bg-brand-softer);
|
||||||
|
|
||||||
|
/* Decorative zig-zag pattern at the bottom of the page */
|
||||||
|
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234.80078 31.757813" width="234.80078" height="31.757812"><path d="M19.599609 0c-1.05 0-2.10039.375-2.90039 1.125L0 16.925781v14.832031h234.80078V17.025391l-16.5-15.900391c-1.6-1.5-4.20078-1.5-5.80078 0l-13.80078 13.099609c-1.6 1.5-4.19883 1.5-5.79883 0L179.09961 1.125c-1.6-1.5-4.19883-1.5-5.79883 0L159.5 14.224609c-1.6 1.5-4.20078 1.5-5.80078 0L139.90039 1.125c-1.6-1.5-4.20078-1.5-5.80078 0l-13.79883 13.099609c-1.6 1.5-4.20078 1.5-5.80078 0L100.69922 1.125c-1.600001-1.5-4.198829-1.5-5.798829 0l-13.59961 13.099609c-1.6 1.5-4.200781 1.5-5.800781 0L61.699219 1.125c-1.6-1.5-4.198828-1.5-5.798828 0L42.099609 14.224609c-1.6 1.5-4.198828 1.5-5.798828 0L22.5 1.125C21.7.375 20.649609 0 19.599609 0z" fill="black"/></svg>');
|
||||||
|
mask-position: bottom;
|
||||||
|
mask-repeat: repeat-x;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
232
app/javascript/styles_new/mastodon/polls.scss
Normal file
232
app/javascript/styles_new/mastodon/polls.scss
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
@use 'sass:color';
|
||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
.poll {
|
||||||
|
margin-top: 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__chart {
|
||||||
|
border-radius: 4px;
|
||||||
|
display: block;
|
||||||
|
background: rgb(from var(--color-text-brand) r g b / 60%);
|
||||||
|
height: 5px;
|
||||||
|
min-width: 1%;
|
||||||
|
|
||||||
|
&.leading {
|
||||||
|
background: var(--color-text-brand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
progress {
|
||||||
|
border: 0;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 5px;
|
||||||
|
appearance: none;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&::-webkit-progress-bar {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Those rules need to be entirely separate or they won't work, hence the
|
||||||
|
// duplication
|
||||||
|
&::-moz-progress-bar {
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgb(from var(--color-text-brand) r g b / 60%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-progress-value {
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgb(from var(--color-text-brand) r g b / 60%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__option {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 6px 0;
|
||||||
|
line-height: 18px;
|
||||||
|
cursor: default;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
display: inline-block;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
max-width: calc(100% - 45px - 25px);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='radio'],
|
||||||
|
input[type='checkbox'] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autosuggest-input {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='text'] {
|
||||||
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
outline: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
border: 1px solid var(--color-text-secondary);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: var(--color-text-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (width <= 600px) {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selectable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.editable,
|
||||||
|
&.disabled {
|
||||||
|
align-items: center;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__input {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid var(--color-text-secondary);
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
border-radius: 50%;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
|
&.checkbox {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--color-text-success);
|
||||||
|
border-width: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: var(--color-bg-success-base);
|
||||||
|
border-color: var(--color-text-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-moz-focus-inner {
|
||||||
|
outline: 0 !important;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
outline: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
border-color: var(--color-text-disabled);
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: var(--color-text-disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--color-text-disabled);
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__option.editable &__input,
|
||||||
|
&__option.disabled &__input {
|
||||||
|
&:active,
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--color-text-primary);
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__number {
|
||||||
|
display: inline-block;
|
||||||
|
width: 45px;
|
||||||
|
font-weight: 700;
|
||||||
|
flex: 0 0 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__voted {
|
||||||
|
padding: 0 5px;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
&__mark {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
padding-top: 6px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
color: var(--color-text-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
display: inline;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
color: var(--color-text-tertiary);
|
||||||
|
text-decoration: underline;
|
||||||
|
font-size: inherit;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
background-color: var(--color-bg-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
height: 36px;
|
||||||
|
padding: 0 16px;
|
||||||
|
margin-inline-end: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.muted .poll {
|
||||||
|
color: var(--color-text-tertiary);
|
||||||
|
|
||||||
|
&__chart {
|
||||||
|
background: rgb(from var(--color-text-brand) r g b / 40%);
|
||||||
|
|
||||||
|
&.leading {
|
||||||
|
background: rgb(from var(--color-text-brand) r g b / 60%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
app/javascript/styles_new/mastodon/reset.scss
Normal file
58
app/javascript/styles_new/mastodon/reset.scss
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/* http://meyerweb.com/eric/tools/css/reset/
|
||||||
|
v2.0 | 20110126
|
||||||
|
License: none (public domain)
|
||||||
|
*/
|
||||||
|
|
||||||
|
html, body, div, span, applet, object, iframe,
|
||||||
|
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||||
|
a, abbr, acronym, address, big, cite, code,
|
||||||
|
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||||
|
small, strike, strong, sub, sup, tt, var,
|
||||||
|
b, u, i, center,
|
||||||
|
dl, dt, dd, ol, ul, li,
|
||||||
|
fieldset, form, label, legend,
|
||||||
|
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||||
|
article, aside, canvas, details, embed,
|
||||||
|
figure, figcaption, footer, header, hgroup,
|
||||||
|
menu, nav, output, ruby, section, summary,
|
||||||
|
time, mark, audio, video {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HTML5 display-role reset for older browsers */
|
||||||
|
article, aside, details, figcaption, figure,
|
||||||
|
footer, header, hgroup, menu, nav, section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol, ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote, q {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote:before, blockquote:after,
|
||||||
|
q:before, q:after {
|
||||||
|
content: '';
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html:has(body.custom-scrollbars) {
|
||||||
|
scrollbar-color: var(--color-text-secondary) var(--color-bg-secondary);
|
||||||
|
}
|
||||||
116
app/javascript/styles_new/mastodon/rich_text.scss
Normal file
116
app/javascript/styles_new/mastodon/rich_text.scss
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
.status__content__text,
|
||||||
|
.e-content,
|
||||||
|
.edit-indicator__content,
|
||||||
|
.reply-indicator__content {
|
||||||
|
code {
|
||||||
|
background: var(--rich-text-container-color);
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--rich-text-text-color);
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: var(--rich-text-container-color);
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--rich-text-text-color);
|
||||||
|
|
||||||
|
code {
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pre,
|
||||||
|
blockquote {
|
||||||
|
margin-bottom: 22px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
unicode-bidi: plaintext;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
padding-inline-start: 32px;
|
||||||
|
color: var(--rich-text-text-color);
|
||||||
|
white-space: normal;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
display: block;
|
||||||
|
content: '';
|
||||||
|
width: 24px;
|
||||||
|
height: 20px;
|
||||||
|
mask-image: url('@/images/quote.svg');
|
||||||
|
background-color: var(--rich-text-decorations-color);
|
||||||
|
position: absolute;
|
||||||
|
inset-inline-start: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin-top: 4px;
|
||||||
|
border-inline-start: 3px solid var(--rich-text-decorations-color);
|
||||||
|
padding-inline-start: 16px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > ul,
|
||||||
|
& > ol {
|
||||||
|
margin-bottom: 22px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
em,
|
||||||
|
i {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
padding-inline-start: 24px;
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding-inline-start: 8px;
|
||||||
|
|
||||||
|
&::marker {
|
||||||
|
text-align: end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: '•';
|
||||||
|
|
||||||
|
li::marker {
|
||||||
|
text-align: start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
list-style-type: decimal;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
app/javascript/styles_new/mastodon/rtl.scss
Normal file
50
app/javascript/styles_new/mastodon/rtl.scss
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
body.rtl {
|
||||||
|
direction: rtl;
|
||||||
|
|
||||||
|
.reactions-bar {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcements__mastodon,
|
||||||
|
.drawer__inner__mastodon > img {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.compose-form .autosuggest-textarea__textarea {
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-left: 10px + 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.columns-area {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account__avatar-wrapper {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-header__setting-arrows {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-wrapper {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-swipeable-view-container > * {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-back-button__icon {
|
||||||
|
transform: scale(-1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dismissable-banner,
|
||||||
|
.warning-banner {
|
||||||
|
&__action {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
375
app/javascript/styles_new/mastodon/tables.scss
Normal file
375
app/javascript/styles_new/mastodon/tables.scss
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
.table {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
border-spacing: 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 8px;
|
||||||
|
line-height: 18px;
|
||||||
|
vertical-align: top;
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
text-align: start;
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
|
||||||
|
&.critical {
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--color-text-warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > thead > tr > th {
|
||||||
|
vertical-align: bottom;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > tbody > tr > th {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > tbody > tr:nth-child(odd) > td,
|
||||||
|
& > tbody > tr:nth-child(odd) > th {
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > tbody > tr:last-child > td,
|
||||||
|
& > tbody > tr:last-child > th {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-brand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
@each $lang in $cjk-langs {
|
||||||
|
&:lang(#{$lang}) {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.inline-table {
|
||||||
|
& > tbody > tr:nth-child(odd) {
|
||||||
|
& > td,
|
||||||
|
& > th {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > tbody > tr:first-child {
|
||||||
|
& > td,
|
||||||
|
& > th {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.horizontal-table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-style: hidden;
|
||||||
|
|
||||||
|
& > tbody > tr > th,
|
||||||
|
& > tbody > tr > td {
|
||||||
|
padding: 11px 10px;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > tbody > tr > th {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.batch-table {
|
||||||
|
& > thead > tr > th {
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
border-top: 1px solid var(--color-border-primary);
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-radius: 4px 0 0;
|
||||||
|
border-inline-start: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-radius: 0 4px 0 0;
|
||||||
|
border-inline-end: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--invites tbody td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-wrapper {
|
||||||
|
overflow: auto;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
samp {
|
||||||
|
font-family: $font-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.table-action-link {
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.table-action-link,
|
||||||
|
a.table-action-link {
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
margin-inline-end: 5px;
|
||||||
|
padding: 0 10px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
padding-inline-start: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-table {
|
||||||
|
&--no-toolbar {
|
||||||
|
.batch-table__toolbar {
|
||||||
|
position: static;
|
||||||
|
height: 4px;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__toolbar,
|
||||||
|
&__row {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&__select {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 8px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
min-height: 100%;
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--aligned {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__actions,
|
||||||
|
&__content {
|
||||||
|
padding: 8px 0;
|
||||||
|
padding-inline-end: 16px;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__toolbar {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 200;
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
height: 47px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
text-align: end;
|
||||||
|
padding-inline-end: 16px - 5px;
|
||||||
|
|
||||||
|
.table-action-link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__select-all {
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
height: 47px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-top: 0;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected,
|
||||||
|
.not-selected {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
padding: 8px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
font: inherit;
|
||||||
|
color: var(--color-text-brand);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__form {
|
||||||
|
padding: 16px;
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-top: 0;
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
|
||||||
|
.fields-row {
|
||||||
|
padding-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__row {
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-top: 0;
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
|
||||||
|
@media screen and (max-width: $no-gap-breakpoint) {
|
||||||
|
.optional &:first-child {
|
||||||
|
border-top: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&--unpadded {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--padded {
|
||||||
|
padding: 12px 16px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--with-image {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__image {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-inline-end: 10px;
|
||||||
|
|
||||||
|
.emojione {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__quote {
|
||||||
|
padding: 12px;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__extra {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
text-align: end;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.directory__tag {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
a {
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.optional .batch-table__toolbar,
|
||||||
|
&.optional .batch-table__row__select {
|
||||||
|
@media screen and (max-width: $no-gap-breakpoint) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the status card to not have borders, background or padding when
|
||||||
|
// inline in the table of statuses
|
||||||
|
.batch-table__row__content > .status__card {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (width <= 870px) {
|
||||||
|
.accounts-table tbody td.optional {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.one-liner {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
183
app/javascript/styles_new/mastodon/widgets.scss
Normal file
183
app/javascript/styles_new/mastodon/widgets.scss
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
@use 'sass:color';
|
||||||
|
@use 'variables' as *;
|
||||||
|
|
||||||
|
.directory {
|
||||||
|
&__tag {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
& > a,
|
||||||
|
& > div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 15px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
box-shadow: 0 0 15px var(--color-shadow-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
& > a {
|
||||||
|
&:hover,
|
||||||
|
&:active,
|
||||||
|
&:focus {
|
||||||
|
background: var(--color-bg-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active > a {
|
||||||
|
background: var(--color-bg-brand-base);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled > div {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
display: block;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-top: 8px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active h4 {
|
||||||
|
&,
|
||||||
|
.fa,
|
||||||
|
small,
|
||||||
|
.trends__item__current {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-stack {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: (36px + 4px) * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active .avatar-stack .account__avatar {
|
||||||
|
border-color: var(--color-text-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trends__item__current {
|
||||||
|
padding-inline-end: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.accounts-table {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.account {
|
||||||
|
max-width: calc(56px + 30ch);
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th {
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
text-align: start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td {
|
||||||
|
padding: 15px 0;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:last-child td {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__count {
|
||||||
|
width: 120px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
|
||||||
|
small {
|
||||||
|
display: block;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td.accounts-table__extra {
|
||||||
|
width: 120px;
|
||||||
|
text-align: end;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
padding-inline-end: 16px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
color: var(--color-text-brand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__comment {
|
||||||
|
width: 50%;
|
||||||
|
vertical-align: initial !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td.accounts-table__interrelationships {
|
||||||
|
width: 21px;
|
||||||
|
padding-inline-end: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
&.active {
|
||||||
|
color: var(--color-text-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.passive {
|
||||||
|
color: var(--color-text-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active.passive {
|
||||||
|
color: var(--color-text-success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: $no-gap-breakpoint) {
|
||||||
|
tbody td.optional {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -31,9 +31,10 @@ class IpBlock < ApplicationRecord
|
|||||||
|
|
||||||
after_commit :reset_cache
|
after_commit :reset_cache
|
||||||
|
|
||||||
def to_log_human_identifier
|
def to_cidr
|
||||||
"#{ip}/#{ip.prefix}"
|
"#{ip}/#{ip.prefix}"
|
||||||
end
|
end
|
||||||
|
alias to_log_human_identifier to_cidr
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def blocked?(remote_ip)
|
def blocked?(remote_ip)
|
||||||
|
|||||||
@ -9,6 +9,6 @@ class REST::Admin::IpBlockSerializer < ActiveModel::Serializer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def ip
|
def ip
|
||||||
"#{object.ip}/#{object.ip.prefix}"
|
object.to_cidr
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -18,5 +18,5 @@
|
|||||||
domain: @domain_block.domain
|
domain: @domain_block.domain
|
||||||
|
|
||||||
.actions
|
.actions
|
||||||
= link_to t('.cancel'), admin_instances_path, class: 'button button-tertiary'
|
= link_to t('.cancel'), admin_instances_path, class: 'button button-secondary'
|
||||||
= f.button :submit, t('.confirm'), class: 'button button--dangerous', name: :confirm
|
= f.button :submit, t('.confirm'), class: 'button button--dangerous', name: :confirm
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
= f.check_box :ip_block_ids, { multiple: true, include_hidden: false }, ip_block.id
|
= f.check_box :ip_block_ids, { multiple: true, include_hidden: false }, ip_block.id
|
||||||
.batch-table__row__content.pending-account
|
.batch-table__row__content.pending-account
|
||||||
.pending-account__header
|
.pending-account__header
|
||||||
%samp= link_to "#{ip_block.ip}/#{ip_block.ip.prefix}", admin_accounts_path(ip: "#{ip_block.ip}/#{ip_block.ip.prefix}")
|
%samp= link_to ip_block.to_cidr, admin_accounts_path(ip: ip_block.to_cidr)
|
||||||
- if ip_block.comment.present?
|
- if ip_block.comment.present?
|
||||||
·
|
·
|
||||||
= ip_block.comment
|
= ip_block.comment
|
||||||
|
|||||||
@ -76,7 +76,7 @@
|
|||||||
%hr.spacer/
|
%hr.spacer/
|
||||||
|
|
||||||
.actions
|
.actions
|
||||||
= link_to t('admin.reports.cancel'), admin_report_path(@report), class: 'button button-tertiary'
|
= link_to t('admin.reports.cancel'), admin_report_path(@report), class: 'button button-secondary'
|
||||||
= form.button t('admin.reports.confirm'),
|
= form.button t('admin.reports.confirm'),
|
||||||
name: :confirm,
|
name: :confirm,
|
||||||
class: 'button',
|
class: 'button',
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
.announcements-list__item
|
.announcements-list__item
|
||||||
- if can?(:update, role)
|
- if can?(:update, role)
|
||||||
= link_to edit_admin_role_path(role), class: 'announcements-list__item__title' do
|
= link_to edit_admin_role_path(role), class: 'announcements-list__item__title' do
|
||||||
%span.user-role
|
%span
|
||||||
= material_symbol 'group'
|
= material_symbol 'group'
|
||||||
|
|
||||||
- if role.everyone?
|
- if role.everyone?
|
||||||
@ -10,13 +10,12 @@
|
|||||||
= role.name
|
= role.name
|
||||||
- else
|
- else
|
||||||
%span.announcements-list__item__title
|
%span.announcements-list__item__title
|
||||||
%span.user-role
|
= material_symbol 'group'
|
||||||
= material_symbol 'group'
|
|
||||||
|
|
||||||
- if role.everyone?
|
- if role.everyone?
|
||||||
= t('admin.roles.everyone')
|
= t('admin.roles.everyone')
|
||||||
- else
|
- else
|
||||||
= role.name
|
= role.name
|
||||||
|
|
||||||
.announcements-list__item__action-bar
|
.announcements-list__item__action-bar
|
||||||
.announcements-list__item__meta
|
.announcements-list__item__meta
|
||||||
|
|||||||
@ -27,4 +27,4 @@
|
|||||||
.stacked-actions
|
.stacked-actions
|
||||||
- accept_path = @invite_code.present? ? public_invite_url(invite_code: @invite_code, accept: @accept_token) : new_user_registration_path(accept: @accept_token)
|
- accept_path = @invite_code.present? ? public_invite_url(invite_code: @invite_code, accept: @accept_token) : new_user_registration_path(accept: @accept_token)
|
||||||
= link_to t('auth.rules.accept'), accept_path, class: 'button'
|
= link_to t('auth.rules.accept'), accept_path, class: 'button'
|
||||||
= link_to t('auth.rules.back'), root_path, class: 'button button-tertiary'
|
= link_to t('auth.rules.back'), root_path, class: 'button button-secondary'
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
- content_for :header_tags do
|
- content_for :header_tags do
|
||||||
= flavoured_vite_typescript_tag 'public.tsx', crossorigin: 'anonymous'
|
= flavoured_vite_typescript_tag 'public.tsx', crossorigin: 'anonymous'
|
||||||
|
|
||||||
- content_for :body_classes, 'modal-layout compose-standalone'
|
- content_for :body_classes, 'modal-layout with-zig-zag-decoration compose-standalone'
|
||||||
|
|
||||||
- content_for :content do
|
- content_for :content do
|
||||||
- if user_signed_in? && !@hide_header
|
- if user_signed_in? && !@hide_header
|
||||||
|
|||||||
@ -11,5 +11,5 @@
|
|||||||
|
|
||||||
.simple_form
|
.simple_form
|
||||||
.actions
|
.actions
|
||||||
= link_to t('generic.cancel'), settings_import_path(@bulk_import), method: :delete, class: 'button button-tertiary'
|
= link_to t('generic.cancel'), settings_import_path(@bulk_import), method: :delete, class: 'button button-secondary'
|
||||||
= link_to t('generic.confirm'), confirm_settings_import_path(@bulk_import), method: :post, class: 'button'
|
= link_to t('generic.confirm'), confirm_settings_import_path(@bulk_import), method: :post, class: 'button'
|
||||||
|
|||||||
@ -14,3 +14,9 @@ nan:
|
|||||||
username: 用者ê名
|
username: 用者ê名
|
||||||
user/invite_request:
|
user/invite_request:
|
||||||
text: 原因
|
text: 原因
|
||||||
|
errors:
|
||||||
|
models:
|
||||||
|
terms_of_service:
|
||||||
|
attributes:
|
||||||
|
effective_date:
|
||||||
|
too_soon: 傷緊ah,著khah uànn佇 %{date}
|
||||||
|
|||||||
@ -477,7 +477,7 @@ da:
|
|||||||
no_file: Ingen fil valgt
|
no_file: Ingen fil valgt
|
||||||
export_domain_blocks:
|
export_domain_blocks:
|
||||||
import:
|
import:
|
||||||
description_html: En liste over domæneblokeringer er ved at blive importeret. Gennemgå listen meget nøje, især hvis man ikke selv har oprettet den.
|
description_html: Du er ved at importere en liste over domæneblokeringer. Gennemgå denne liste meget omhyggeligt, især hvis du ikke selv har udarbejdet den.
|
||||||
existing_relationships_warning: Eksisterende følge-relationer
|
existing_relationships_warning: Eksisterende følge-relationer
|
||||||
private_comment_description_html: 'For at man lettere kan holde styr på, hvorfra importerede blokeringer kommer, oprettes disse med flg. private kommentar: <q>%{comment}</q>'
|
private_comment_description_html: 'For at man lettere kan holde styr på, hvorfra importerede blokeringer kommer, oprettes disse med flg. private kommentar: <q>%{comment}</q>'
|
||||||
private_comment_template: Importeret fra %{source} d. %{date}
|
private_comment_template: Importeret fra %{source} d. %{date}
|
||||||
@ -514,7 +514,7 @@ da:
|
|||||||
select_capabilities: Vælg kapaciteter
|
select_capabilities: Vælg kapaciteter
|
||||||
sign_in: Log ind
|
sign_in: Log ind
|
||||||
status: Status
|
status: Status
|
||||||
title: Fediverse Auxiliary Service Providers
|
title: Udbydere af Fediverse-hjælpetjenester
|
||||||
title: FASP
|
title: FASP
|
||||||
follow_recommendations:
|
follow_recommendations:
|
||||||
description_html: "<strong>Følg-anbefalinger hjælpe nye brugere til hurtigt at finde interessant indhold</strong>. Når en bruger ikke har interageret nok med andre til at generere personlige følg-anbefalinger, anbefales disse konti i stedet. De revurderes dagligt baseret på en blanding af konti med de flest nylige engagementer og fleste lokale følger-antal for et givet sprog."
|
description_html: "<strong>Følg-anbefalinger hjælpe nye brugere til hurtigt at finde interessant indhold</strong>. Når en bruger ikke har interageret nok med andre til at generere personlige følg-anbefalinger, anbefales disse konti i stedet. De revurderes dagligt baseret på en blanding af konti med de flest nylige engagementer og fleste lokale følger-antal for et givet sprog."
|
||||||
@ -1186,7 +1186,7 @@ da:
|
|||||||
new_trending_statuses:
|
new_trending_statuses:
|
||||||
title: Indlæg, der trender
|
title: Indlæg, der trender
|
||||||
new_trending_tags:
|
new_trending_tags:
|
||||||
title: Hashtags, der trender
|
title: Populære hashtags
|
||||||
subject: Nye tendenser klar til gennemgang på %{instance}
|
subject: Nye tendenser klar til gennemgang på %{instance}
|
||||||
aliases:
|
aliases:
|
||||||
add_new: Opret alias
|
add_new: Opret alias
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user