= ({ active, onClose }) => {
},
reverse: !active,
config: config.wobbly,
- immediate: reduceMotion,
});
return (
diff --git a/app/javascript/mastodon/features/ui/containers/status_list_container.js b/app/javascript/mastodon/features/ui/containers/status_list_container.js
index 5ffa4353f1..a624796a08 100644
--- a/app/javascript/mastodon/features/ui/containers/status_list_container.js
+++ b/app/javascript/mastodon/features/ui/containers/status_list_container.js
@@ -17,19 +17,22 @@ const makeGetStatusIds = (pending = false) => createSelector([
if (id === null || id === 'inline-follow-suggestions') return true;
const statusForId = statuses.get(id);
- let showStatus = true;
if (statusForId.get('account') === me) return true;
- if (columnSettings.getIn(['shows', 'reblog']) === false) {
- showStatus = showStatus && statusForId.get('reblog') === null;
+ if (columnSettings.getIn(['shows', 'reblog']) === false && statusForId.get('reblog') !== null) {
+ return false;
}
- if (columnSettings.getIn(['shows', 'reply']) === false) {
- showStatus = showStatus && (statusForId.get('in_reply_to_id') === null || statusForId.get('in_reply_to_account_id') === me);
+ if (columnSettings.getIn(['shows', 'reply']) === false && statusForId.get('in_reply_to_id') !== null && statusForId.get('in_reply_to_account_id') !== me) {
+ return false;
}
- return showStatus;
+ if (columnSettings.getIn(['shows', 'quote']) === false && statusForId.get('quote') !== null) {
+ return false;
+ }
+
+ return true;
});
});
diff --git a/app/javascript/mastodon/features/ui/hooks/useBreakpoint.tsx b/app/javascript/mastodon/features/ui/hooks/useBreakpoint.tsx
new file mode 100644
index 0000000000..af96ab3766
--- /dev/null
+++ b/app/javascript/mastodon/features/ui/hooks/useBreakpoint.tsx
@@ -0,0 +1,53 @@
+import { useState, useEffect } from 'react';
+
+const breakpoints = {
+ openable: 759, // Device width at which the sidebar becomes an openable hamburger menu
+ full: 1174, // Device width at which all 3 columns can be displayed
+};
+
+type Breakpoint = 'openable' | 'full';
+
+export const useBreakpoint = (breakpoint: Breakpoint) => {
+ const [isMatching, setIsMatching] = useState(false);
+
+ useEffect(() => {
+ const mediaWatcher = window.matchMedia(
+ `(max-width: ${breakpoints[breakpoint]}px)`,
+ );
+
+ setIsMatching(mediaWatcher.matches);
+
+ const handleChange = (e: MediaQueryListEvent) => {
+ setIsMatching(e.matches);
+ };
+
+ mediaWatcher.addEventListener('change', handleChange);
+
+ return () => {
+ mediaWatcher.removeEventListener('change', handleChange);
+ };
+ }, [breakpoint, setIsMatching]);
+
+ return isMatching;
+};
+
+interface WithBreakpointType {
+ matchesBreakpoint: boolean;
+}
+
+export function withBreakpoint(
+ Component: React.ComponentType
,
+ breakpoint: Breakpoint = 'full',
+) {
+ const displayName = `withMobileLayout(${Component.displayName ?? Component.name})`;
+
+ const ComponentWithBreakpoint = (props: P) => {
+ const matchesBreakpoint = useBreakpoint(breakpoint);
+
+ return ;
+ };
+
+ ComponentWithBreakpoint.displayName = displayName;
+
+ return ComponentWithBreakpoint;
+}
diff --git a/app/javascript/mastodon/features/ui/index.jsx b/app/javascript/mastodon/features/ui/index.jsx
index 7d190b3ce7..d6d7b5e2e8 100644
--- a/app/javascript/mastodon/features/ui/index.jsx
+++ b/app/javascript/mastodon/features/ui/index.jsx
@@ -29,7 +29,7 @@ import { expandHomeTimeline } from '../../actions/timelines';
import initialState, { me, owner, singleUserMode, trendsEnabled, trendsAsLanding, disableHoverCards } from '../../initial_state';
import BundleColumnError from './components/bundle_column_error';
-import Header from './components/header';
+import { NavigationBar } from './components/navigation_bar';
import { UploadArea } from './components/upload_area';
import { HashtagMenuController } from './components/hashtag_menu_controller';
import ColumnsAreaContainer from './containers/columns_area_container';
@@ -652,12 +652,11 @@ class UI extends PureComponent {
return (
-
-
{children}
+
{layout !== 'mobile' &&
}
{!disableHoverCards &&
}
diff --git a/app/javascript/mastodon/features/video/index.tsx b/app/javascript/mastodon/features/video/index.tsx
index e9c3cdefb6..65f26cedad 100644
--- a/app/javascript/mastodon/features/video/index.tsx
+++ b/app/javascript/mastodon/features/video/index.tsx
@@ -27,11 +27,7 @@ import {
attachFullscreenListener,
detachFullscreenListener,
} from 'mastodon/features/ui/util/fullscreen';
-import {
- displayMedia,
- useBlurhash,
- reduceMotion,
-} from 'mastodon/initial_state';
+import { displayMedia, useBlurhash } from 'mastodon/initial_state';
import { playerSettings } from 'mastodon/settings';
import { HotkeyIndicator } from './components/hotkey_indicator';
@@ -260,7 +256,6 @@ export const Video: React.FC<{
setMuted(videoRef.current.muted);
void api.start({
volume: `${videoRef.current.volume * 100}%`,
- immediate: reduceMotion,
});
}
},
@@ -350,7 +345,6 @@ export const Video: React.FC<{
videoRef.current.currentTime / videoRef.current.duration;
void api.start({
progress: isNaN(progress) ? '0%' : `${progress * 100}%`,
- immediate: reduceMotion,
config: config.stiff,
});
}
@@ -738,7 +732,6 @@ export const Video: React.FC<{
if (lastTimeRange > -1) {
void api.start({
buffer: `${Math.ceil(videoRef.current.buffered.end(lastTimeRange) / videoRef.current.duration) * 100}%`,
- immediate: reduceMotion,
});
}
}, [api]);
@@ -753,7 +746,6 @@ export const Video: React.FC<{
void api.start({
volume: `${videoRef.current.muted ? 0 : videoRef.current.volume * 100}%`,
- immediate: reduceMotion,
});
persistVolume(videoRef.current.volume, videoRef.current.muted);
diff --git a/app/javascript/mastodon/hooks/useAudioContext.ts b/app/javascript/mastodon/hooks/useAudioContext.ts
new file mode 100644
index 0000000000..84acf5ac7f
--- /dev/null
+++ b/app/javascript/mastodon/hooks/useAudioContext.ts
@@ -0,0 +1,62 @@
+import { useCallback, useEffect, useRef } from 'react';
+
+interface AudioContextOptions {
+ audioElementRef: React.MutableRefObject
;
+}
+
+/**
+ * Create and return an audio context instance for a given audio element [0].
+ * Also returns an associated audio source, a gain node, and play and pause actions
+ * which should be used instead of `audioElementRef.current.play/pause()`.
+ *
+ * [0] https://developer.mozilla.org/en-US/docs/Web/API/AudioContext
+ */
+
+export const useAudioContext = ({ audioElementRef }: AudioContextOptions) => {
+ const audioContextRef = useRef();
+ const sourceRef = useRef();
+ const gainNodeRef = useRef();
+
+ useEffect(() => {
+ if (!audioElementRef.current) {
+ return;
+ }
+
+ const context = audioContextRef.current ?? new AudioContext();
+ const source =
+ sourceRef.current ??
+ context.createMediaElementSource(audioElementRef.current);
+
+ const gainNode = context.createGain();
+ gainNode.connect(context.destination);
+ source.connect(gainNode);
+
+ audioContextRef.current = context;
+ gainNodeRef.current = gainNode;
+ sourceRef.current = source;
+
+ return () => {
+ if (context.state !== 'closed') {
+ void context.close();
+ }
+ };
+ }, [audioElementRef]);
+
+ const playAudio = useCallback(() => {
+ void audioElementRef.current?.play();
+ void audioContextRef.current?.resume();
+ }, [audioElementRef]);
+
+ const pauseAudio = useCallback(() => {
+ audioElementRef.current?.pause();
+ void audioContextRef.current?.suspend();
+ }, [audioElementRef]);
+
+ return {
+ audioContextRef,
+ sourceRef,
+ gainNodeRef,
+ playAudio,
+ pauseAudio,
+ };
+};
diff --git a/app/javascript/mastodon/hooks/useAudioVisualizer.ts b/app/javascript/mastodon/hooks/useAudioVisualizer.ts
index 50111a81d7..efc0647d8d 100644
--- a/app/javascript/mastodon/hooks/useAudioVisualizer.ts
+++ b/app/javascript/mastodon/hooks/useAudioVisualizer.ts
@@ -1,4 +1,4 @@
-import { useState, useEffect, useRef, useCallback } from 'react';
+import { useState, useEffect, useRef } from 'react';
const normalizeFrequencies = (arr: Float32Array): number[] => {
return new Array(...arr).map((value: number) => {
@@ -10,12 +10,17 @@ const normalizeFrequencies = (arr: Float32Array): number[] => {
});
};
-export const useAudioVisualizer = (
- ref: React.MutableRefObject,
- numBands: number,
-) => {
- const audioContextRef = useRef();
- const sourceRef = useRef();
+interface AudioVisualiserOptions {
+ audioContextRef: React.MutableRefObject;
+ sourceRef: React.MutableRefObject;
+ numBands: number;
+}
+
+export const useAudioVisualizer = ({
+ audioContextRef,
+ sourceRef,
+ numBands,
+}: AudioVisualiserOptions) => {
const analyzerRef = useRef();
const [frequencyBands, setFrequencyBands] = useState(
@@ -23,47 +28,31 @@ export const useAudioVisualizer = (
);
useEffect(() => {
- if (!audioContextRef.current) {
- audioContextRef.current = new AudioContext();
+ if (audioContextRef.current) {
analyzerRef.current = audioContextRef.current.createAnalyser();
analyzerRef.current.smoothingTimeConstant = 0.6;
analyzerRef.current.fftSize = 2048;
}
-
- return () => {
- if (audioContextRef.current) {
- void audioContextRef.current.close();
- }
- };
- }, []);
+ }, [audioContextRef]);
useEffect(() => {
- if (
- audioContextRef.current &&
- analyzerRef.current &&
- !sourceRef.current &&
- ref.current
- ) {
- sourceRef.current = audioContextRef.current.createMediaElementSource(
- ref.current,
- );
+ if (analyzerRef.current && sourceRef.current) {
sourceRef.current.connect(analyzerRef.current);
- sourceRef.current.connect(audioContextRef.current.destination);
}
+ const currentSource = sourceRef.current;
return () => {
- if (sourceRef.current) {
- sourceRef.current.disconnect();
+ if (currentSource && analyzerRef.current) {
+ currentSource.disconnect(analyzerRef.current);
}
};
- }, [ref]);
+ }, [audioContextRef, sourceRef]);
useEffect(() => {
- const source = sourceRef.current;
const analyzer = analyzerRef.current;
const context = audioContextRef.current;
- if (!source || !analyzer || !context) {
+ if (!analyzer || !context) {
return;
}
@@ -94,19 +83,7 @@ export const useAudioVisualizer = (
return () => {
clearInterval(updateInterval);
};
- }, [numBands]);
+ }, [numBands, audioContextRef]);
- const resume = useCallback(() => {
- if (audioContextRef.current) {
- void audioContextRef.current.resume();
- }
- }, []);
-
- const suspend = useCallback(() => {
- if (audioContextRef.current) {
- void audioContextRef.current.suspend();
- }
- }, []);
-
- return [resume, suspend, frequencyBands] as const;
+ return frequencyBands;
};
diff --git a/app/javascript/mastodon/hooks/useLayout.ts b/app/javascript/mastodon/hooks/useLayout.ts
new file mode 100644
index 0000000000..fc1cf136bf
--- /dev/null
+++ b/app/javascript/mastodon/hooks/useLayout.ts
@@ -0,0 +1,13 @@
+import type { LayoutType } from '../is_mobile';
+import { useAppSelector } from '../store';
+
+export const useLayout = () => {
+ const layout = useAppSelector(
+ (state) => state.meta.get('layout') as LayoutType,
+ );
+
+ return {
+ singleColumn: layout === 'single-column' || layout === 'mobile',
+ layout,
+ };
+};
diff --git a/app/javascript/mastodon/hooks/usePrevious.ts b/app/javascript/mastodon/hooks/usePrevious.ts
new file mode 100644
index 0000000000..95f42e2ed6
--- /dev/null
+++ b/app/javascript/mastodon/hooks/usePrevious.ts
@@ -0,0 +1,16 @@
+import { useRef, useEffect } from 'react';
+
+/**
+ * Returns the previous state of the passed in value.
+ * On first render, undefined is returned.
+ */
+
+export function usePrevious(value: T): T | undefined {
+ const ref = useRef();
+
+ useEffect(() => {
+ ref.current = value;
+ }, [value]);
+
+ return ref.current;
+}
diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json
index d7446fdd49..49d15f9cba 100644
--- a/app/javascript/mastodon/locales/br.json
+++ b/app/javascript/mastodon/locales/br.json
@@ -301,6 +301,7 @@
"hashtag.follow": "Heuliañ ar ger-klik",
"hashtag.unfollow": "Paouez heuliañ an hashtag",
"hashtags.and_other": "…{count, plural, one {hag # all} other {ha # all}}",
+ "home.column_settings.show_quotes": "Diskouez an arroudennoù",
"home.column_settings.show_reblogs": "Diskouez ar skignadennoù",
"home.column_settings.show_replies": "Diskouez ar respontoù",
"home.hide_announcements": "Kuzhat ar c'hemennoù",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index f52c24c70f..9d1fdbabae 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -430,6 +430,7 @@
"hints.profiles.see_more_posts": "Vegeu més publicacions a {domain}",
"hints.threads.replies_may_be_missing": "Es poden haver perdut respostes d'altres servidors.",
"hints.threads.see_more": "Vegeu més respostes a {domain}",
+ "home.column_settings.show_quotes": "Mostrar les cites",
"home.column_settings.show_reblogs": "Mostra els impulsos",
"home.column_settings.show_replies": "Mostra les respostes",
"home.hide_announcements": "Amaga els anuncis",
diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json
index 0933b65563..cfc2591276 100644
--- a/app/javascript/mastodon/locales/cs.json
+++ b/app/javascript/mastodon/locales/cs.json
@@ -430,6 +430,7 @@
"hints.profiles.see_more_posts": "Zobrazit další příspěvky na {domain}",
"hints.threads.replies_may_be_missing": "Odpovědi z jiných serverů mohou chybět.",
"hints.threads.see_more": "Zobrazit další odpovědi na {domain}",
+ "home.column_settings.show_quotes": "Zobrazit citace",
"home.column_settings.show_reblogs": "Zobrazit boosty",
"home.column_settings.show_replies": "Zobrazit odpovědi",
"home.hide_announcements": "Skrýt oznámení",
diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json
index d129bfa5e6..769eecb1d4 100644
--- a/app/javascript/mastodon/locales/cy.json
+++ b/app/javascript/mastodon/locales/cy.json
@@ -1,6 +1,7 @@
{
- "about.blocks": "Gweinyddion a gyfyngir",
+ "about.blocks": "Gweinyddion wedi'u cymedroli",
"about.contact": "Cysylltwch â:",
+ "about.default_locale": "Rhagosodedig",
"about.disclaimer": "Mae Mastodon yn feddalwedd cod agored rhydd ac o dan hawlfraint Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Dyw'r rheswm ddim ar gael",
"about.domain_blocks.preamble": "Fel rheol, mae Mastodon yn caniatáu i chi weld cynnwys gan unrhyw weinyddwr arall yn y ffedysawd a rhyngweithio â hi. Dyma'r eithriadau a wnaed ar y gweinydd penodol hwn.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "Cyfyngedig",
"about.domain_blocks.suspended.explanation": "Fydd data o'r gweinydd hwn ddim yn cael ei brosesu, ei gadw na'i gyfnewid, gan wneud unrhyw ryngweithio neu gyfathrebu gyda defnyddwyr o'r gweinydd hwn yn amhosibl.",
"about.domain_blocks.suspended.title": "Wedi'i atal",
+ "about.language_label": "Iaith",
"about.not_available": "Dyw'r wybodaeth yma heb ei wneud ar gael ar y gweinydd hwn.",
"about.powered_by": "Cyfrwng cymdeithasol datganoledig wedi ei yrru gan {mastodon}",
"about.rules": "Rheolau'r gweinydd",
@@ -40,7 +42,7 @@
"account.follow_back": "Dilyn nôl",
"account.followers": "Dilynwyr",
"account.followers.empty": "Does neb yn dilyn y defnyddiwr hwn eto.",
- "account.followers_counter": "{count, plural, one {{counter} dilynwr} two {{counter} ddilynwr} other {{counter} dilynwyr}}",
+ "account.followers_counter": "{count, plural, one {{counter} dilynwr} two {{counter} ddilynwr} other {{counter} dilynwr}}",
"account.followers_you_know_counter": "{counter} rydych chi'n adnabod",
"account.following": "Yn dilyn",
"account.following_counter": "{count, plural, one {Yn dilyn {counter}} other {Yn dilyn {counter} arall}}",
@@ -127,7 +129,7 @@
"annual_report.summary.thanks": "Diolch am fod yn rhan o Mastodon!",
"attachments_list.unprocessed": "(heb eu prosesu)",
"audio.hide": "Cuddio sain",
- "block_modal.remote_users_caveat": "Byddwn yn gofyn i'r gweinydd {domain} barchu eich penderfyniad. Fodd bynnag, nid yw cydymffurfiad wedi'i warantu gan y gall rhai gweinyddwyr drin rhwystro mewn ffyrdd gwahanol. Mae'n bosibl y bydd postiadau cyhoeddus yn dal i fod yn weladwy i ddefnyddwyr nad ydynt wedi mewngofnodi.",
+ "block_modal.remote_users_caveat": "Byddwn yn gofyn i'r gweinydd {domain} barchu eich penderfyniad. Fodd bynnag, nid yw cydymffurfiad wedi'i warantu gan y gall rhai gweinyddwyr drin rhwystrau mewn ffyrdd gwahanol. Mae'n bosibl y bydd postiadau cyhoeddus yn dal i fod yn weladwy i ddefnyddwyr nad ydynt wedi mewngofnodi.",
"block_modal.show_less": "Dangos llai",
"block_modal.show_more": "Dangos rhagor",
"block_modal.they_cant_mention": "Dydyn nhw ddim yn gallu eich crybwyll na'ch dilyn.",
@@ -236,8 +238,8 @@
"confirmations.missing_alt_text.title": "Ychwanegu testun amgen?",
"confirmations.mute.confirm": "Tewi",
"confirmations.redraft.confirm": "Dileu ac ailddrafftio",
- "confirmations.redraft.message": "Ydych chi wir eisiau'r dileu'r postiad hwn a'i ailddrafftio? Bydd ffefrynnau a hybiau'n cael eu colli, a bydd atebion i'r post gwreiddiol yn mynd yn amddifad.",
- "confirmations.redraft.title": "Dileu ac ailddraftio'r postiad?",
+ "confirmations.redraft.message": "Ydych chi wir eisiau'r dileu'r postiad hwn a'i ail lunio? Bydd ffefrynnau a hybiau'n cael eu colli, a bydd atebion i'r postiad gwreiddiol yn mynd yn amddifad.",
+ "confirmations.redraft.title": "Dileu ac ail lunio'r postiad?",
"confirmations.remove_from_followers.confirm": "Dileu dilynwr",
"confirmations.remove_from_followers.message": "Bydd {name} yn rhoi'r gorau i'ch dilyn. A ydych yn siŵr eich bod am fwrw ymlaen?",
"confirmations.remove_from_followers.title": "Tynnu dilynwr?",
@@ -286,8 +288,8 @@
"domain_pill.their_username": "Eu dynodwr unigryw ar eu gweinydd. Mae'n bosibl dod o hyd i ddefnyddwyr gyda'r un enw defnyddiwr ar wahanol weinyddion.",
"domain_pill.username": "Enw Defnyddiwr",
"domain_pill.whats_in_a_handle": "Beth sydd mewn handlen?",
- "domain_pill.who_they_are": "Gan fod handlen yn dweud pwy yw rhywun a ble maen nhw, gallwch chi ryngweithio â phobl ar draws gwe gymdeithasol llwyfannau wedi'u pweru gan ActivityPub .",
- "domain_pill.who_you_are": "Oherwydd bod eich handlen yn dweud pwy ydych chi a ble rydych chi, gall pobl ryngweithio â chi ar draws gwe gymdeithasol llwyfannau wedi'u pweru gan ActivityPub .",
+ "domain_pill.who_they_are": "Gan fod handlen yn dweud pwy yw rhywun a ble maen nhw, gallwch chi ryngweithio â phobl ar draws gwe gymdeithasol llwyfannau wedi'u pweru gan ActivityPub .",
+ "domain_pill.who_you_are": "Oherwydd bod eich handlen yn dweud pwy ydych chi a ble rydych chi, gall pobl ryngweithio â chi ar draws gwe gymdeithasol llwyfannau wedi'u pweru gan ActivityPub .",
"domain_pill.your_handle": "Eich handlen:",
"domain_pill.your_server": "Eich cartref digidol, lle mae'ch holl bostiadau'n byw. Ddim yn hoffi'r un hon? Trosglwyddwch weinyddion ar unrhyw adeg a dewch â'ch dilynwyr hefyd.",
"domain_pill.your_username": "Eich dynodwr unigryw ar y gweinydd hwn. Mae'n bosibl dod o hyd i ddefnyddwyr gyda'r un enw defnyddiwr ar wahanol weinyddion.",
@@ -314,26 +316,26 @@
"empty_column.account_hides_collections": "Mae'r defnyddiwr wedi dewis i beidio rhannu'r wybodaeth yma",
"empty_column.account_suspended": "Cyfrif wedi'i atal",
"empty_column.account_timeline": "Dim postiadau yma!",
- "empty_column.account_unavailable": "Nid yw'r proffil ar gael",
+ "empty_column.account_unavailable": "Dyw'r proffil ddim ar gael",
"empty_column.blocks": "Dydych chi heb rwystro unrhyw ddefnyddwyr eto.",
"empty_column.bookmarked_statuses": "Does gennych chi ddim unrhyw bostiad wedi'u cadw fel nod tudalen eto. Pan fyddwch yn gosod nod tudalen i un, mi fydd yn ymddangos yma.",
"empty_column.community": "Mae'r ffrwd lleol yn wag. Beth am ysgrifennu rhywbeth cyhoeddus!",
"empty_column.direct": "Does gennych chi unrhyw grybwylliadau preifat eto. Pan fyddwch chi'n anfon neu'n derbyn un, bydd yn ymddangos yma.",
- "empty_column.domain_blocks": "Nid oes unrhyw barthau wedi'u blocio eto.",
+ "empty_column.domain_blocks": "Does dim parthau wedi'u rhwystro eto.",
"empty_column.explore_statuses": "Does dim pynciau llosg ar hyn o bryd. Dewch nôl nes ymlaen!",
"empty_column.favourited_statuses": "Rydych chi heb ffafrio unrhyw bostiadau eto. Pan byddwch chi'n ffafrio un, bydd yn ymddangos yma.",
- "empty_column.favourites": "Nid oes unrhyw un wedi ffafrio'r postiad hwn eto. Pan fydd rhywun yn gwneud hynny, byddan nhw'n ymddangos yma.",
- "empty_column.follow_requests": "Nid oes gennych unrhyw geisiadau dilyn eto. Pan fyddwch yn derbyn un, byddan nhw'n ymddangos yma.",
- "empty_column.followed_tags": "Nid ydych wedi dilyn unrhyw hashnodau eto. Pan fyddwch chi'n gwneud hynny, byddan nhw'n ymddangos yma.",
- "empty_column.hashtag": "Nid oes dim ar yr hashnod hwn eto.",
- "empty_column.home": "Mae eich ffrwd gartref yn wag! Dilynwch fwy o bobl i'w llenwi.",
+ "empty_column.favourites": "Does neb wedi ffafrio'r postiad hwn eto. Pan fydd rhywun yn gwneud hynny, byddan nhw'n ymddangos yma.",
+ "empty_column.follow_requests": "Does gennych chi ddim ceisiadau dilyn eto. Pan fyddwch yn derbyn un, byddan nhw'n ymddangos yma.",
+ "empty_column.followed_tags": "Dydych chi heb ddilyn unrhyw hashnodau eto. Pan fyddwch chi'n gwneud hynny, byddan nhw'n ymddangos yma.",
+ "empty_column.hashtag": "Does dim ar yr hashnod hwn eto.",
+ "empty_column.home": "Mae eich ffrwd gartref yn wag! Dilynwch ragor o bobl i'w llenwi.",
"empty_column.list": "Does dim yn y rhestr yma eto. Pan fydd aelodau'r rhestr yn cyhoeddi postiad newydd, mi fydd yn ymddangos yma.",
- "empty_column.mutes": "Nid ydych wedi tewi unrhyw ddefnyddwyr eto.",
+ "empty_column.mutes": "Dydych chi heb dewi unrhyw ddefnyddwyr eto.",
"empty_column.notification_requests": "Dim i boeni amdano! Does dim byd yma. Pan fyddwch yn derbyn hysbysiadau newydd, byddan nhw'n ymddangos yma yn ôl eich gosodiadau.",
- "empty_column.notifications": "Nid oes gennych unrhyw hysbysiadau eto. Rhyngweithiwch ag eraill i ddechrau'r sgwrs.",
+ "empty_column.notifications": "Does gennych chi ddim hysbysiadau eto. Pan fyddwch chi'n rhyngweithio ag eraill, byddwch yn ei weld yma.",
"empty_column.public": "Does dim byd yma! Ysgrifennwch rywbeth cyhoeddus, neu dilynwch ddefnyddwyr o weinyddion eraill i'w lanw",
"error.unexpected_crash.explanation": "Oherwydd gwall yn ein cod neu oherwydd problem cysondeb porwr, nid oedd y dudalen hon gallu cael ei dangos yn gywir.",
- "error.unexpected_crash.explanation_addons": "Nid oes modd dangos y dudalen hon yn gywir. Mae'r gwall hwn yn debygol o gael ei achosi gan ategyn porwr neu offer cyfieithu awtomatig.",
+ "error.unexpected_crash.explanation_addons": "Does dim modd dangos y dudalen hon yn gywir. Mae'r gwall hwn yn debygol o gael ei achosi gan ategyn porwr neu offer cyfieithu awtomatig.",
"error.unexpected_crash.next_steps": "Ceisiwch ail-lwytho'r dudalen. Os nad yw hyn yn eich helpu, efallai gallwch ddefnyddio Mastodon trwy borwr neu ap brodorol gwahanol.",
"error.unexpected_crash.next_steps_addons": "Ceisiwch eu hanalluogi ac adnewyddu'r dudalen. Os nad yw hynny'n helpu, efallai y byddwch yn dal i allu defnyddio Mastodon trwy borwr neu ap cynhenid arall.",
"errors.unexpected_crash.copy_stacktrace": "Copïo'r olrhain stac i'r clipfwrdd",
@@ -343,11 +345,12 @@
"explore.trending_links": "Newyddion",
"explore.trending_statuses": "Postiadau",
"explore.trending_tags": "Hashnodau",
+ "featured_carousel.header": "{count, plural, one {Postiad wedi'i binio} other {Postiadau wedi'u pinio}}",
"featured_carousel.next": "Nesaf",
"featured_carousel.post": "Postiad",
"featured_carousel.previous": "Blaenorol",
"featured_carousel.slide": "{index} o {total}",
- "filter_modal.added.context_mismatch_explanation": "Nid yw'r categori hidlo hwn yn berthnasol i'r cyd-destun yr ydych wedi cyrchu'r postiad hwn ynddo. Os ydych chi am i'r postiad gael ei hidlo yn y cyd-destun hwn hefyd, bydd yn rhaid i chi olygu'r hidlydd.",
+ "filter_modal.added.context_mismatch_explanation": "Dyw'r categori hidlo hwn ddim yn berthnasol i'r cyd-destun yr ydych wedi cyrchu'r postiad hwn ynddo. Os ydych chi am i'r postiad gael ei hidlo yn y cyd-destun hwn hefyd, bydd yn rhaid i chi olygu'r hidlydd.",
"filter_modal.added.context_mismatch_title": "Diffyg cyfatebiaeth cyd-destun!",
"filter_modal.added.expired_explanation": "Mae'r categori hidlydd hwn wedi dod i ben, bydd angen i chi newid y dyddiad dod i ben er mwyn iddo fod yn berthnasol.",
"filter_modal.added.expired_title": "Hidlydd wedi dod i ben!",
@@ -367,11 +370,11 @@
"filtered_notifications_banner.pending_requests": "Oddi wrth {count, plural, =0 {no one} one {un person} two {# berson} few {# pherson} other {# person}} efallai eich bod yn eu hadnabod",
"filtered_notifications_banner.title": "Hysbysiadau wedi'u hidlo",
"firehose.all": "Popeth",
- "firehose.local": "Gweinydd hwn",
+ "firehose.local": "Y gweinydd hwn",
"firehose.remote": "Gweinyddion eraill",
"follow_request.authorize": "Awdurdodi",
"follow_request.reject": "Gwrthod",
- "follow_requests.unlocked_explanation": "Er nid yw eich cyfrif wedi'i gloi, roedd y staff {domain} yn meddwl efallai hoffech adolygu ceisiadau dilyn o'r cyfrifau rhain wrth law.",
+ "follow_requests.unlocked_explanation": "Er nad yw eich cyfrif wedi'i gloi, roedd y staff {domain} yn meddwl efallai hoffech adolygu ceisiadau dilyn o'r cyfrifau rhain wrth law.",
"follow_suggestions.curated_suggestion": "Dewis staff",
"follow_suggestions.dismiss": "Peidio â dangos hwn eto",
"follow_suggestions.featured_longer": "Wedi'i ddewis â llaw gan dîm {domain}",
@@ -384,32 +387,32 @@
"follow_suggestions.personalized_suggestion": "Awgrym personol",
"follow_suggestions.popular_suggestion": "Awgrym poblogaidd",
"follow_suggestions.popular_suggestion_longer": "Yn boblogaidd ar {domain}",
- "follow_suggestions.similar_to_recently_followed_longer": "Yn debyg i broffiliau y gwnaethoch chi eu dilyn yn ddiweddar",
+ "follow_suggestions.similar_to_recently_followed_longer": "Yn debyg i broffiliau rydych wedi'u dilyn yn ddiweddar",
"follow_suggestions.view_all": "Gweld y cyfan",
"follow_suggestions.who_to_follow": "Pwy i ddilyn",
"followed_tags": "Hashnodau rydych yn eu dilyn",
"footer.about": "Ynghylch",
"footer.directory": "Cyfeiriadur proffiliau",
- "footer.get_app": "Lawrlwytho'r ap",
+ "footer.get_app": "Llwytho'r ap i lawr",
"footer.keyboard_shortcuts": "Bysellau brys",
"footer.privacy_policy": "Polisi preifatrwydd",
"footer.source_code": "Gweld y cod ffynhonnell",
"footer.status": "Statws",
"footer.terms_of_service": "Telerau gwasanaeth",
"generic.saved": "Wedi'i Gadw",
- "getting_started.heading": "Dechrau",
+ "getting_started.heading": "Dechrau arni",
"hashtag.admin_moderation": "Agor rhyngwyneb cymedroli #{name}",
"hashtag.browse": "Pori postiadau yn #{hashtag}",
"hashtag.browse_from_account": "Pori postiadau gan @{name} yn #{hashtag}",
"hashtag.column_header.tag_mode.all": "a {additional}",
"hashtag.column_header.tag_mode.any": "neu {additional}",
"hashtag.column_header.tag_mode.none": "heb {additional}",
- "hashtag.column_settings.select.no_options_message": "Dim awgrymiadau i'w weld",
+ "hashtag.column_settings.select.no_options_message": "Dim awgrymiadau i'w gweld",
"hashtag.column_settings.select.placeholder": "Mewnbynnu hashnodau…",
"hashtag.column_settings.tag_mode.all": "Pob un o'r rhain",
"hashtag.column_settings.tag_mode.any": "Unrhyw un o'r rhain",
"hashtag.column_settings.tag_mode.none": "Dim o'r rhain",
- "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
+ "hashtag.column_settings.tag_toggle": "Cynnwys tagiau ychwanegol ar gyfer y golofn hon",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} cyfranogwr} other {{counter} cyfranogwr}}",
"hashtag.counter_by_uses": "{count, plural, one {postiad {counter}} other {postiad {counter}}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} postiad} other {{counter} postiad}} heddiw",
@@ -434,7 +437,7 @@
"home.pending_critical_update.link": "Gweld diweddariadau",
"home.pending_critical_update.title": "Mae diweddariad diogelwch hanfodol ar gael!",
"home.show_announcements": "Dangos cyhoeddiadau",
- "ignore_notifications_modal.disclaimer": "Ni all Mastodon hysbysu defnyddwyr eich bod wedi anwybyddu eu hysbysiadau. Ni fydd anwybyddu hysbysiadau yn atal y negeseuon eu hunain rhag cael eu hanfon.",
+ "ignore_notifications_modal.disclaimer": "Dyw Mastodon ddim yn gallu hysbysu defnyddwyr eich bod wedi anwybyddu eu hysbysiadau. Bydd anwybyddu hysbysiadau ddim yn atal y negeseuon eu hunain rhag cael eu hanfon.",
"ignore_notifications_modal.filter_instead": "Hidlo yn lle hynny",
"ignore_notifications_modal.filter_to_act_users": "Byddwch yn dal i allu derbyn, gwrthod neu adrodd ar ddefnyddwyr",
"ignore_notifications_modal.filter_to_avoid_confusion": "Mae hidlo yn helpu i osgoi dryswch posibl",
@@ -461,12 +464,12 @@
"interaction_modal.title.reblog": "Hybu postiad {name}",
"interaction_modal.title.reply": "Ymateb i bostiad {name}",
"interaction_modal.title.vote": "Pleidleisiwch ym mhleidlais {name}",
- "interaction_modal.username_prompt": "E.e. {example}",
+ "interaction_modal.username_prompt": "e.e. {example}",
"intervals.full.days": "{number, plural, one {# diwrnod} two {# ddiwrnod} other {# diwrnod}}",
- "intervals.full.hours": "{number, plural, one {# awr} other {# o oriau}}",
- "intervals.full.minutes": "{number, plural, one {# funud} other {# o funudau}}",
- "keyboard_shortcuts.back": "Llywio nôl",
- "keyboard_shortcuts.blocked": "Agor rhestr defnyddwyr a flociwyd",
+ "intervals.full.hours": "{number, plural, one {# awr} other {# awr}}",
+ "intervals.full.minutes": "{number, plural, one {# funud} other {# munud}}",
+ "keyboard_shortcuts.back": "Symud nôl",
+ "keyboard_shortcuts.blocked": "Agor rhestr defnyddwyr sydd wedi'i rwystro",
"keyboard_shortcuts.boost": "Hybu postiad",
"keyboard_shortcuts.column": "Ffocysu colofn",
"keyboard_shortcuts.compose": "Ffocysu ar ardal cyfansoddi testun",
@@ -480,7 +483,7 @@
"keyboard_shortcuts.heading": "Bysellau brys",
"keyboard_shortcuts.home": "Agor ffrwd gartref",
"keyboard_shortcuts.hotkey": "Bysell boeth",
- "keyboard_shortcuts.legend": "Dangos y rhestr hon",
+ "keyboard_shortcuts.legend": "Dangos yr allwedd hon",
"keyboard_shortcuts.local": "Agor ffrwd lleol",
"keyboard_shortcuts.mention": "Crybwyll yr awdur",
"keyboard_shortcuts.muted": "Agor rhestr defnyddwyr rydych wedi'u tewi",
@@ -489,7 +492,7 @@
"keyboard_shortcuts.open_media": "Agor cyfryngau",
"keyboard_shortcuts.pinned": "Agor rhestr postiadau wedi'u pinio",
"keyboard_shortcuts.profile": "Agor proffil yr awdur",
- "keyboard_shortcuts.reply": "Ymateb i bostiad",
+ "keyboard_shortcuts.reply": "Ateb postiad",
"keyboard_shortcuts.requests": "Agor rhestr ceisiadau dilyn",
"keyboard_shortcuts.search": "Ffocysu ar y bar chwilio",
"keyboard_shortcuts.spoilers": "Dangos/cuddio'r maes CW",
@@ -557,7 +560,7 @@
"navigation_bar.compose": "Cyfansoddi post newydd",
"navigation_bar.direct": "Crybwylliadau preifat",
"navigation_bar.discover": "Darganfod",
- "navigation_bar.domain_blocks": "Parthau wedi'u blocio",
+ "navigation_bar.domain_blocks": "Parthau wedi'u rhwystro",
"navigation_bar.explore": "Darganfod",
"navigation_bar.favourites": "Ffefrynnau",
"navigation_bar.filters": "Geiriau wedi'u tewi",
@@ -609,7 +612,7 @@
"notification.moderation_warning.action_silence": "Mae eich cyfrif wedi'i gyfyngu.",
"notification.moderation_warning.action_suspend": "Mae eich cyfrif wedi'i atal.",
"notification.own_poll": "Mae eich pleidlais wedi dod i ben",
- "notification.poll": "Mae arolwg y gwnaethoch bleidleisio ynddo wedi dod i ben",
+ "notification.poll": "Mae arolwg rydych wedi pleidleisio ynddo wedi dod i ben",
"notification.reblog": "Hybodd {name} eich post",
"notification.reblog.name_and_others_with_link": "Mae {name} a {count, plural, one {# arall} other {# arall}} wedi hybu eich postiad",
"notification.relationships_severance_event": "Wedi colli cysylltiad â {name}",
@@ -652,7 +655,7 @@
"notifications.column_settings.group": "Grŵp",
"notifications.column_settings.mention": "Crybwylliadau:",
"notifications.column_settings.poll": "Canlyniadau pleidlais:",
- "notifications.column_settings.push": "Hysbysiadau gwthiadwy",
+ "notifications.column_settings.push": "Hysbysiadau gwthio",
"notifications.column_settings.reblog": "Hybiau:",
"notifications.column_settings.show": "Dangos yn y golofn",
"notifications.column_settings.sound": "Chwarae sain",
@@ -665,25 +668,25 @@
"notifications.filter.favourites": "Ffefrynnau",
"notifications.filter.follows": "Yn dilyn",
"notifications.filter.mentions": "Crybwylliadau",
- "notifications.filter.polls": "Canlyniadau polau",
+ "notifications.filter.polls": "Canlyniadau pleidleisio",
"notifications.filter.statuses": "Diweddariadau gan bobl rydych chi'n eu dilyn",
"notifications.grant_permission": "Caniatáu.",
"notifications.group": "{count} hysbysiad",
"notifications.mark_as_read": "Marciwch bob hysbysiad wedi'i ddarllen",
- "notifications.permission_denied": "Nid oes hysbysiadau bwrdd gwaith ar gael oherwydd cais am ganiatâd porwr a wrthodwyd yn flaenorol",
- "notifications.permission_denied_alert": "Nid oes modd galluogi hysbysiadau bwrdd gwaith, gan fod caniatâd porwr wedi'i wrthod o'r blaen",
- "notifications.permission_required": "Nid oes hysbysiadau bwrdd gwaith ar gael oherwydd na roddwyd y caniatâd gofynnol.",
+ "notifications.permission_denied": "Does dim hysbysiadau bwrdd gwaith ar gael oherwydd cais am ganiatâd porwr a wrthodwyd yn flaenorol",
+ "notifications.permission_denied_alert": "Does dim modd galluogi hysbysiadau bwrdd gwaith, gan fod caniatâd porwr wedi'i wrthod o'r blaen",
+ "notifications.permission_required": "Does dim hysbysiadau bwrdd gwaith ar gael oherwydd na roddwyd y caniatâd gofynnol.",
"notifications.policy.accept": "Derbyn",
"notifications.policy.accept_hint": "Dangos mewn hysbysiadau",
"notifications.policy.drop": "Anwybyddu",
"notifications.policy.drop_hint": "Anfon i'r gwagle, byth i'w gweld eto",
"notifications.policy.filter": "Hidlo",
"notifications.policy.filter_hint": "Anfon i flwch derbyn hysbysiadau wedi'u hidlo",
- "notifications.policy.filter_limited_accounts_hint": "Cyfyngedig gan gymedrolwyr gweinydd",
+ "notifications.policy.filter_limited_accounts_hint": "Cyfyngwyd gan gymedrolwyr gweinydd",
"notifications.policy.filter_limited_accounts_title": "Cyfrifon wedi'u cymedroli",
- "notifications.policy.filter_new_accounts.hint": "Crëwyd o fewn {days, lluosog, un {yr un diwrnod} arall {y # diwrnod}} diwethaf",
+ "notifications.policy.filter_new_accounts.hint": "Crëwyd o fewn {days, plural, one {yr un diwrnod} other {y # diwrnod}} diwethaf",
"notifications.policy.filter_new_accounts_title": "Cyfrifon newydd",
- "notifications.policy.filter_not_followers_hint": "Gan gynnwys pobl sydd wedi bod yn eich dilyn am llai {days, plural, un {nag un diwrnod} arall {na # diwrnod}}",
+ "notifications.policy.filter_not_followers_hint": "Gan gynnwys pobl sydd wedi bod yn eich dilyn am llai {days, plural, one {nag un diwrnod} other {na # diwrnod}}",
"notifications.policy.filter_not_followers_title": "Pobl sydd ddim yn eich dilyn",
"notifications.policy.filter_not_following_hint": "Hyd nes i chi eu cymeradwyo â llaw",
"notifications.policy.filter_not_following_title": "Pobl nad ydych yn eu dilyn",
@@ -699,7 +702,7 @@
"onboarding.follows.search": "Chwilio",
"onboarding.follows.title": "Dilynwch bobl i gychwyn arni",
"onboarding.profile.discoverable": "Gwnewch fy mhroffil yn un y gellir ei ddarganfod",
- "onboarding.profile.discoverable_hint": "Pan fyddwch yn optio i mewn i ddarganfodadwyedd ar Mastodon, gall eich postiadau ymddangos mewn canlyniadau chwilio a threndiau, ac efallai y bydd eich proffil yn cael ei awgrymu i bobl sydd â diddordebau tebyg i chi.",
+ "onboarding.profile.discoverable_hint": "Pan fyddwch yn dewis ymuno â darganfod ar Mastodon, gall eich postiadau ymddangos mewn canlyniadau chwilio a threndiau, ac efallai y bydd eich proffil yn cael ei awgrymu i bobl sydd â diddordebau tebyg i chi.",
"onboarding.profile.display_name": "Enw dangos",
"onboarding.profile.display_name_hint": "Eich enw llawn neu'ch enw hwyl…",
"onboarding.profile.note": "Bywgraffiad",
@@ -710,7 +713,7 @@
"onboarding.profile.upload_header": "Llwytho pennyn proffil",
"password_confirmation.exceeds_maxlength": "Mae'r cadarnhad cyfrinair yn fwy nag uchafswm hyd y cyfrinair",
"password_confirmation.mismatching": "Nid yw'r cadarnhad cyfrinair yn cyfateb",
- "picture_in_picture.restore": "Rhowch ef yn ôl",
+ "picture_in_picture.restore": "Rhowch e nôl",
"poll.closed": "Ar gau",
"poll.refresh": "Adnewyddu",
"poll.reveal": "Gweld y canlyniadau",
@@ -724,9 +727,9 @@
"privacy.change": "Addasu preifatrwdd y post",
"privacy.direct.long": "Pawb sydd â sôn amdanyn nhw yn y postiad",
"privacy.direct.short": "Crybwylliad preifat",
- "privacy.private.long": "Eich dilynwyr yn unig",
+ "privacy.private.long": "Dim ond eich dilynwyr",
"privacy.private.short": "Dilynwyr",
- "privacy.public.long": "Unrhyw ar ac oddi ar Mastodon",
+ "privacy.public.long": "Unrhyw un ar ac oddi ar Mastodon",
"privacy.public.short": "Cyhoeddus",
"privacy.unlisted.additional": "Mae hwn yn ymddwyn yn union fel y cyhoeddus, ac eithrio na fydd y postiad yn ymddangos mewn ffrydiau byw neu hashnodau, archwilio, neu chwiliad Mastodon, hyd yn oed os ydych wedi eich cynnwys ar draws y cyfrif.",
"privacy.unlisted.long": "Llai o ddathliadau algorithmig",
@@ -736,7 +739,7 @@
"recommended": "Argymhellwyd",
"refresh": "Adnewyddu",
"regeneration_indicator.please_stand_by": "Arhoswch am dipyn.",
- "regeneration_indicator.preparing_your_home_feed": "Paratoi eich llif cartref…",
+ "regeneration_indicator.preparing_your_home_feed": "Yn paratoi eich ffrwd gartref…",
"relative_time.days": "{number}d",
"relative_time.full.days": "{number, plural, one {# diwrnod} other {# diwrnod}} yn ôl",
"relative_time.full.hours": "{number, plural, one {# awr} other {# awr}} yn ôl",
@@ -749,16 +752,16 @@
"relative_time.seconds": "{number} eiliad",
"relative_time.today": "heddiw",
"reply_indicator.attachments": "{count, plural, one {# atodiad} other {# atodiad}}",
- "reply_indicator.cancel": "Canslo",
- "reply_indicator.poll": "Arolwg",
- "report.block": "Blocio",
+ "reply_indicator.cancel": "Diddymu",
+ "reply_indicator.poll": "Pleidlais",
+ "report.block": "Rhwystro",
"report.block_explanation": "Ni welwch chi eu postiadau. Ni allan nhw weld eich postiadau na'ch dilyn. Byddan nhw'n gallu gweld eu bod nhw wedi'u rhwystro.",
"report.categories.legal": "Cyfreithiol",
"report.categories.other": "Arall",
"report.categories.spam": "Sbam",
"report.categories.violation": "Mae cynnwys yn torri un neu fwy o reolau'r gweinydd",
"report.category.subtitle": "Dewiswch yr ateb gorau",
- "report.category.title": "Beth sy'n digwydd gyda'r {type} yma?",
+ "report.category.title": "Beth sy'n digwydd gyda'r {type} yma",
"report.category.title_account": "proffil",
"report.category.title_status": "post",
"report.close": "Iawn",
@@ -770,11 +773,11 @@
"report.next": "Nesaf",
"report.placeholder": "Sylwadau ychwanegol",
"report.reasons.dislike": "Dydw i ddim yn ei hoffi",
- "report.reasons.dislike_description": "Nid yw'n rhywbeth yr ydych am ei weld",
+ "report.reasons.dislike_description": "Dyw e ddim yn rhywbeth rydych am ei weld",
"report.reasons.legal": "Mae'n anghyfreithlon",
"report.reasons.legal_description": "Rydych chi'n credu ei fod yn torri cyfraith eich gwlad chi neu wlad y gweinydd",
"report.reasons.other": "Mae'n rhywbeth arall",
- "report.reasons.other_description": "Nid yw'r mater yn ffitio i gategorïau eraill",
+ "report.reasons.other_description": "Dyw'r mater ddim yn ffitio i gategorïau eraill",
"report.reasons.spam": "Sbam yw e",
"report.reasons.spam_description": "Dolenni maleisus, ymgysylltu ffug, neu ymatebion ailadroddus",
"report.reasons.violation": "Mae'n torri rheolau'r gweinydd",
@@ -801,7 +804,7 @@
"report_notification.categories.violation": "Torri rheol",
"report_notification.categories.violation_sentence": "torri rheolau",
"report_notification.open": "Agor adroddiad",
- "search.no_recent_searches": "Does dim chwiliadau diweddar",
+ "search.no_recent_searches": "Does dim chwilio diweddar",
"search.placeholder": "Chwilio",
"search.quick_action.account_search": "Proffiliau sy'n cyfateb i {x}",
"search.quick_action.go_to_account": "Mynd i broffil {x}",
@@ -835,13 +838,13 @@
"sign_in_banner.mastodon_is": "Mastodon yw'r ffordd orau o gadw i fyny â'r hyn sy'n digwydd.",
"sign_in_banner.sign_in": "Mewngofnodi",
"sign_in_banner.sso_redirect": "Mewngofnodi neu Gofrestru",
- "status.admin_account": "Agor rhyngwyneb cymedroli ar gyfer @{name}",
+ "status.admin_account": "Agor rhyngwyneb cymedroli @{name}",
"status.admin_domain": "Agor rhyngwyneb cymedroli {domain}",
"status.admin_status": "Agor y postiad hwn yn y rhyngwyneb cymedroli",
- "status.block": "Blocio @{name}",
- "status.bookmark": "Llyfrnodi",
+ "status.block": "Rhwystro @{name}",
+ "status.bookmark": "Nod tudalen",
"status.cancel_reblog_private": "Dadhybu",
- "status.cannot_reblog": "Nid oes modd hybu'r postiad hwn",
+ "status.cannot_reblog": "Does dim modd hybu'r postiad hwn",
"status.continued_thread": "Edefyn parhaus",
"status.copy": "Copïo dolen i'r post",
"status.delete": "Dileu",
@@ -850,7 +853,7 @@
"status.direct_indicator": "Crybwyll preifat",
"status.edit": "Golygu",
"status.edited": "Golygwyd ddiwethaf {date}",
- "status.edited_x_times": "Golygwyd {count, plural, one {count} two {count} other {{count} gwaith}}",
+ "status.edited_x_times": "Golygwyd {count, plural, one {{count} gwaith} other {{count} gwaith}}",
"status.embed": "Cael y cod mewnblannu",
"status.favourite": "Ffafrio",
"status.favourites": "{count, plural, one {ffefryn} other {ffefryn}}",
@@ -880,13 +883,13 @@
"status.reblogged_by": "Hybodd {name}",
"status.reblogs": "{count, plural, one {# hwb} other {# hwb}}",
"status.reblogs.empty": "Does neb wedi hybio'r post yma eto. Pan y bydd rhywun yn gwneud, byddent yn ymddangos yma.",
- "status.redraft": "Dileu ac ailddrafftio",
+ "status.redraft": "Dileu ac ail lunio",
"status.remove_bookmark": "Tynnu nod tudalen",
"status.remove_favourite": "Tynnu o'r ffefrynnau",
- "status.replied_in_thread": "Atebodd mewn edefyn",
+ "status.replied_in_thread": "Wedi ateb mewn edefyn",
"status.replied_to": "Wedi ymateb i {name}",
"status.reply": "Ymateb",
- "status.replyAll": "Ymateb i edefyn",
+ "status.replyAll": "Ateb edefyn",
"status.report": "Adrodd ar @{name}",
"status.sensitive_warning": "Cynnwys sensitif",
"status.share": "Rhannu",
@@ -910,23 +913,23 @@
"time_remaining.days": "{number, plural, one {# diwrnod} other {# diwrnod}} ar ôl",
"time_remaining.hours": "{number, plural, one {# awr} other {# awr}} ar ôl",
"time_remaining.minutes": "{number, plural, one {# munud} other {# munud}} ar ôl",
- "time_remaining.moments": "Munudau yn weddill",
+ "time_remaining.moments": "Munudau'n weddill",
"time_remaining.seconds": "{number, plural, one {# eiliad} other {# eiliad}} ar ôl",
"trends.counter_by_accounts": "{count, plural, zero {neb} one {{counter} person} two {{counter} berson} few {{counter} pherson} other {{counter} o bobl}} yn y {days, plural, one {diwrnod diwethaf} two {ddeuddydd diwethaf} other {{days} diwrnod diwethaf}}",
- "trends.trending_now": "Pynciau llosg",
+ "trends.trending_now": "Wrthi'n trendio",
"ui.beforeunload": "Byddwch yn colli eich drafft os byddwch yn gadael Mastodon.",
"units.short.billion": "{count}biliwn",
"units.short.million": "{count}miliwn",
"units.short.thousand": "{count}mil",
"upload_area.title": "Llusgwch a gollwng i lwytho",
- "upload_button.label": "Ychwanegwch gyfryngau (JPEG, PNG, GIF, WebM, MP4, MOV)",
- "upload_error.limit": "Wedi pasio'r uchafswm llwytho.",
- "upload_error.poll": "Nid oes modd llwytho ffeiliau â phleidleisiau.",
+ "upload_button.label": "Ychwanegwch delweddau, fideo neu ffeil sain",
+ "upload_error.limit": "Wedi mynd heibio'r uchafswm llwytho.",
+ "upload_error.poll": "Does dim modd llwytho ffeiliau â phleidleisiau.",
"upload_form.drag_and_drop.instructions": "I godi atodiad cyfryngau, pwyswch y space neu enter. Wrth lusgo, defnyddiwch y bysellau saeth i symud yr atodiad cyfryngau i unrhyw gyfeiriad penodol. Pwyswch space neu enter eto i ollwng yr atodiad cyfryngau yn ei safle newydd, neu pwyswch escape i ddiddymu.",
- "upload_form.drag_and_drop.on_drag_cancel": "Cafodd llusgo ei ddiddymu. Cafodd atodiad cyfryngau {item} ei ollwng.",
- "upload_form.drag_and_drop.on_drag_end": "Cafodd atodiad cyfryngau {item} ei ollwng.",
+ "upload_form.drag_and_drop.on_drag_cancel": "Cafodd llusgo ei ddiddymu. Cafodd atodi cyfryngau {item} ei ollwng.",
+ "upload_form.drag_and_drop.on_drag_end": "Cafodd atodi cyfryngau {item} ei ollwng.",
"upload_form.drag_and_drop.on_drag_over": "Symudwyd atodiad cyfryngau {item}.",
- "upload_form.drag_and_drop.on_drag_start": "Atodiad cyfryngau godwyd {item}.",
+ "upload_form.drag_and_drop.on_drag_start": "Wedi codi atodiad cyfryngau {item}.",
"upload_form.edit": "Golygu",
"upload_progress.label": "Yn llwytho...",
"upload_progress.processing": "Wrthi'n prosesu…",
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 066a368b9c..6053408b9b 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -430,6 +430,7 @@
"hints.profiles.see_more_posts": "Se flere indlæg på {domain}",
"hints.threads.replies_may_be_missing": "Der kan mangle svar fra andre servere.",
"hints.threads.see_more": "Se flere svar på {domain}",
+ "home.column_settings.show_quotes": "Vis citater",
"home.column_settings.show_reblogs": "Vis fremhævelser",
"home.column_settings.show_replies": "Vis svar",
"home.hide_announcements": "Skjul bekendtgørelser",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index 1aef03e323..695ed571da 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -430,6 +430,7 @@
"hints.profiles.see_more_posts": "Weitere Beiträge auf {domain} ansehen",
"hints.threads.replies_may_be_missing": "Möglicherweise werden nicht alle Antworten von anderen Servern angezeigt.",
"hints.threads.see_more": "Weitere Antworten auf {domain} ansehen",
+ "home.column_settings.show_quotes": "Zitierte Beiträge anzeigen",
"home.column_settings.show_reblogs": "Geteilte Beiträge anzeigen",
"home.column_settings.show_replies": "Antworten anzeigen",
"home.hide_announcements": "Ankündigungen ausblenden",
diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json
index e5ca4d968f..5bb855983e 100644
--- a/app/javascript/mastodon/locales/en-GB.json
+++ b/app/javascript/mastodon/locales/en-GB.json
@@ -1,6 +1,7 @@
{
"about.blocks": "Moderated servers",
"about.contact": "Contact:",
+ "about.default_locale": "Default",
"about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Reason not available",
"about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the Fediverse. These are the exceptions that have been made on this particular server.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "Limited",
"about.domain_blocks.suspended.explanation": "No data from this server will be processed, stored or exchanged, making any interaction or communication with users from this server impossible.",
"about.domain_blocks.suspended.title": "Suspended",
+ "about.language_label": "Language",
"about.not_available": "This information has not been made available on this server.",
"about.powered_by": "Decentralised social media powered by {mastodon}",
"about.rules": "Server rules",
@@ -19,13 +21,21 @@
"account.block_domain": "Block domain {domain}",
"account.block_short": "Block",
"account.blocked": "Blocked",
+ "account.blocking": "Blocking",
"account.cancel_follow_request": "Cancel follow",
"account.copy": "Copy link to profile",
"account.direct": "Privately mention @{name}",
"account.disable_notifications": "Stop notifying me when @{name} posts",
+ "account.domain_blocking": "Blocking domain",
"account.edit_profile": "Edit profile",
"account.enable_notifications": "Notify me when @{name} posts",
"account.endorse": "Feature on profile",
+ "account.familiar_followers_many": "Followed by {name1}, {name2}, and {othersCount, plural, one {one other you know} other {# others you know}}",
+ "account.familiar_followers_one": "Followed by {name1}",
+ "account.familiar_followers_two": "Followed by {name1} and {name2}",
+ "account.featured": "Featured",
+ "account.featured.accounts": "Profiles",
+ "account.featured.hashtags": "Hashtags",
"account.featured_tags.last_status_at": "Last post on {date}",
"account.featured_tags.last_status_never": "No posts",
"account.follow": "Follow",
@@ -33,9 +43,11 @@
"account.followers": "Followers",
"account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} follower} other {{counter} followers}}",
+ "account.followers_you_know_counter": "{counter} you know",
"account.following": "Following",
"account.following_counter": "{count, plural, one {{counter} following} other {{counter} following}}",
"account.follows.empty": "This user doesn't follow anyone yet.",
+ "account.follows_you": "Follows you",
"account.go_to_profile": "Go to profile",
"account.hide_reblogs": "Hide boosts from @{name}",
"account.in_memoriam": "In Memoriam.",
@@ -50,18 +62,23 @@
"account.mute_notifications_short": "Mute notifications",
"account.mute_short": "Mute",
"account.muted": "Muted",
+ "account.muting": "Muting",
+ "account.mutual": "You follow each other",
"account.no_bio": "No description provided.",
"account.open_original_page": "Open original page",
"account.posts": "Posts",
"account.posts_with_replies": "Posts and replies",
+ "account.remove_from_followers": "Remove {name} from followers",
"account.report": "Report @{name}",
"account.requested": "Awaiting approval. Click to cancel follow request",
"account.requested_follow": "{name} has requested to follow you",
+ "account.requests_to_follow_you": "Requests to follow you",
"account.share": "Share @{name}'s profile",
"account.show_reblogs": "Show boosts from @{name}",
"account.statuses_counter": "{count, plural, one {{counter} post} other {{counter} posts}}",
"account.unblock": "Unblock @{name}",
"account.unblock_domain": "Unblock domain {domain}",
+ "account.unblock_domain_short": "Unblock",
"account.unblock_short": "Unblock",
"account.unendorse": "Don't feature on profile",
"account.unfollow": "Unfollow",
@@ -223,6 +240,9 @@
"confirmations.redraft.confirm": "Delete & redraft",
"confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
"confirmations.redraft.title": "Delete & redraft post?",
+ "confirmations.remove_from_followers.confirm": "Remove follower",
+ "confirmations.remove_from_followers.message": "{name} will stop following you. Are you sure you want to proceed?",
+ "confirmations.remove_from_followers.title": "Remove follower?",
"confirmations.reply.confirm": "Reply",
"confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
"confirmations.reply.title": "Overwrite post?",
@@ -290,6 +310,9 @@
"emoji_button.search_results": "Search results",
"emoji_button.symbols": "Symbols",
"emoji_button.travel": "Travel & Places",
+ "empty_column.account_featured.me": "You have not featured anything yet. Did you know that you can feature your hashtags you use the most, and even your friend’s accounts on your profile?",
+ "empty_column.account_featured.other": "{acct} has not featured anything yet. Did you know that you can feature your hashtags you use the most, and even your friend’s accounts on your profile?",
+ "empty_column.account_featured_other.unknown": "This account has not featured anything yet.",
"empty_column.account_hides_collections": "This user has chosen to not make this information available",
"empty_column.account_suspended": "Account suspended",
"empty_column.account_timeline": "No posts here!",
@@ -322,6 +345,11 @@
"explore.trending_links": "News",
"explore.trending_statuses": "Posts",
"explore.trending_tags": "Hashtags",
+ "featured_carousel.header": "{count, plural, one {Pinned Post} other {Pinned Posts}}",
+ "featured_carousel.next": "Next",
+ "featured_carousel.post": "Post",
+ "featured_carousel.previous": "Previous",
+ "featured_carousel.slide": "{index} of {total}",
"filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.",
"filter_modal.added.context_mismatch_title": "Context mismatch!",
"filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.",
@@ -374,6 +402,8 @@
"generic.saved": "Saved",
"getting_started.heading": "Getting started",
"hashtag.admin_moderation": "Open moderation interface for #{name}",
+ "hashtag.browse": "Browse posts in #{hashtag}",
+ "hashtag.browse_from_account": "Browse posts from @{name} in #{hashtag}",
"hashtag.column_header.tag_mode.all": "and {additional}",
"hashtag.column_header.tag_mode.any": "or {additional}",
"hashtag.column_header.tag_mode.none": "without {additional}",
@@ -386,7 +416,10 @@
"hashtag.counter_by_accounts": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} post} other {{counter} posts}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} post} other {{counter} posts}} today",
+ "hashtag.feature": "Feature on profile",
"hashtag.follow": "Follow hashtag",
+ "hashtag.mute": "Mute #{hashtag}",
+ "hashtag.unfeature": "Don't feature on profile",
"hashtag.unfollow": "Unfollow hashtag",
"hashtags.and_other": "…and {count, plural, one {one more} other {# more}}",
"hints.profiles.followers_may_be_missing": "Followers for this profile may be missing.",
@@ -397,6 +430,7 @@
"hints.profiles.see_more_posts": "See more posts on {domain}",
"hints.threads.replies_may_be_missing": "Replies from other servers may be missing.",
"hints.threads.see_more": "See more replies on {domain}",
+ "home.column_settings.show_quotes": "Show quotes",
"home.column_settings.show_reblogs": "Show boosts",
"home.column_settings.show_replies": "Show replies",
"home.hide_announcements": "Hide announcements",
@@ -837,6 +871,13 @@
"status.mute_conversation": "Mute conversation",
"status.open": "Expand this post",
"status.pin": "Pin on profile",
+ "status.quote_error.filtered": "Hidden due to one of your filters",
+ "status.quote_error.not_found": "This post cannot be displayed.",
+ "status.quote_error.pending_approval": "This post is pending approval from the original author.",
+ "status.quote_error.rejected": "This post cannot be displayed as the original author does not allow it to be quoted.",
+ "status.quote_error.removed": "This post was removed by its author.",
+ "status.quote_error.unauthorized": "This post cannot be displayed as you are not authorised",
+ "status.quote_post_author": "Post by {name}",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost with original visibility",
@@ -867,7 +908,9 @@
"subscribed_languages.target": "Change subscribed languages for {target}",
"tabs_bar.home": "Home",
"tabs_bar.notifications": "Notifications",
+ "terms_of_service.effective_as_of": "Effective as of {date}",
"terms_of_service.title": "Terms of Service",
+ "terms_of_service.upcoming_changes_on": "Upcoming changes on {date}",
"time_remaining.days": "{number, plural, one {# day} other {# days}} left",
"time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left",
"time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
@@ -898,6 +941,12 @@
"video.expand": "Expand video",
"video.fullscreen": "Full screen",
"video.hide": "Hide video",
+ "video.mute": "Mute",
"video.pause": "Pause",
- "video.play": "Play"
+ "video.play": "Play",
+ "video.skip_backward": "Skip backward",
+ "video.skip_forward": "Skip forward",
+ "video.unmute": "Unmute",
+ "video.volume_down": "Volume down",
+ "video.volume_up": "Volume up"
}
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 25b737d2bd..da9d6f42c1 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -336,7 +336,6 @@
"compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
"compose_form.poll.type": "Style",
"compose_form.publish": "Post",
- "compose_form.publish_form": "New post",
"compose_form.reply": "Reply",
"compose_form.save_changes": "Update",
"compose_form.searchability_warning": "Self only searchability is not available other mastodon servers. Others can search your post.",
@@ -575,6 +574,7 @@
"hints.profiles.see_more_posts": "See more posts on {domain}",
"hints.threads.replies_may_be_missing": "Replies from other servers may be missing.",
"hints.threads.see_more": "See more replies on {domain}",
+ "home.column_settings.show_quotes": "Show quotes",
"home.column_settings.show_reblogs": "Show boosts",
"home.column_settings.show_replies": "Show replies",
"home.hide_announcements": "Hide announcements",
@@ -705,9 +705,11 @@
"mute_modal.you_wont_see_mentions": "You won't see posts that mention them.",
"mute_modal.you_wont_see_posts": "They can still see your posts, but you won't see theirs.",
"navigation_bar.about": "About",
+ "navigation_bar.account_settings": "Password and security",
"navigation_bar.administration": "Administration",
"navigation_bar.advanced_interface": "Open in advanced web interface",
"navigation_bar.antennas": "Antenna",
+ "navigation_bar.automated_deletion": "Automated post deletion",
"navigation_bar.blocks": "Blocked users",
"navigation_bar.bookmarks": "Bookmarks",
"navigation_bar.circles": "Circles",
@@ -717,26 +719,30 @@
"navigation_bar.direct": "Private mentions",
"navigation_bar.discover": "Discover",
"navigation_bar.domain_blocks": "Blocked domains",
- "navigation_bar.emoji_reactions": "Stamps",
+ "navigation_bar.emoji_reactions": "Emoji reactions",
"navigation_bar.explore": "Explore",
"navigation_bar.favourites": "Favorites",
"navigation_bar.filters": "Muted words",
"navigation_bar.follow_requests": "Follow requests",
"navigation_bar.followed_tags": "Followed hashtags",
"navigation_bar.follows_and_followers": "Follows and followers",
+ "navigation_bar.import_export": "Import and export",
"navigation_bar.lists": "Lists",
"navigation_bar.logout": "Logout",
"navigation_bar.moderation": "Moderation",
+ "navigation_bar.more": "More",
"navigation_bar.mutes": "Muted users",
"navigation_bar.opened_in_classic_interface": "Posts, accounts, and other specific pages are opened by default in the classic web interface.",
"navigation_bar.personal": "Personal",
"navigation_bar.pins": "Pinned posts",
"navigation_bar.preferences": "Preferences",
+ "navigation_bar.privacy_and_reach": "Privacy and reach",
"navigation_bar.public_timeline": "Federated timeline",
"navigation_bar.reaction_deck": "Reaction deck",
- "navigation_bar.refresh": "Refresh",
"navigation_bar.search": "Search",
"navigation_bar.security": "Security",
+ "navigation_panel.collapse_lists": "Collapse list menu",
+ "navigation_panel.expand_lists": "Expand list menu",
"not_signed_in_indicator.not_signed_in": "You need to login to access this resource.",
"notification.admin.report": "{name} reported {target}",
"notification.admin.report_account": "{name} reported {count, plural, one {one post} other {# posts}} from {target} for {category}",
@@ -1121,7 +1127,10 @@
"subscribed_languages.save": "Save changes",
"subscribed_languages.target": "Change subscribed languages for {target}",
"tabs_bar.home": "Home",
+ "tabs_bar.menu": "Menu",
"tabs_bar.notifications": "Notifications",
+ "tabs_bar.publish": "New Post",
+ "tabs_bar.search": "Search",
"terms_of_service.effective_as_of": "Effective as of {date}",
"terms_of_service.title": "Terms of Service",
"terms_of_service.upcoming_changes_on": "Upcoming changes on {date}",
diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json
index a3e346e971..4c2496ec8c 100644
--- a/app/javascript/mastodon/locales/es-AR.json
+++ b/app/javascript/mastodon/locales/es-AR.json
@@ -430,6 +430,7 @@
"hints.profiles.see_more_posts": "Ver más mensajes en {domain}",
"hints.threads.replies_may_be_missing": "Es posible que falten respuestas de otros servidores.",
"hints.threads.see_more": "Ver más respuestas en {domain}",
+ "home.column_settings.show_quotes": "Mostrar citas",
"home.column_settings.show_reblogs": "Mostrar adhesiones",
"home.column_settings.show_replies": "Mostrar respuestas",
"home.hide_announcements": "Ocultar anuncios",
diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json
index c7abc6cb5f..f2a8215e89 100644
--- a/app/javascript/mastodon/locales/es-MX.json
+++ b/app/javascript/mastodon/locales/es-MX.json
@@ -345,9 +345,9 @@
"explore.trending_links": "Noticias",
"explore.trending_statuses": "Publicaciones",
"explore.trending_tags": "Etiquetas",
- "featured_carousel.header": "{count, plural,one {Publicación fijada} other {Publicaciones fijada}}",
+ "featured_carousel.header": "{count, plural,one {Publicación fijada} other {Publicaciones fijadas}}",
"featured_carousel.next": "Siguiente",
- "featured_carousel.post": "Publicar",
+ "featured_carousel.post": "Publicación",
"featured_carousel.previous": "Anterior",
"featured_carousel.slide": "{index} de {total}",
"filter_modal.added.context_mismatch_explanation": "Esta categoría de filtro no se aplica al contexto en el que has accedido a esta publlicación. Si quieres que la publicación sea filtrada también en este contexto, tendrás que editar el filtro.",
@@ -430,6 +430,7 @@
"hints.profiles.see_more_posts": "Ver más publicaciones en {domain}",
"hints.threads.replies_may_be_missing": "Puede que no se muestren algunas respuestas de otros servidores.",
"hints.threads.see_more": "Ver más respuestas en {domain}",
+ "home.column_settings.show_quotes": "Mostrar citas",
"home.column_settings.show_reblogs": "Mostrar impulsos",
"home.column_settings.show_replies": "Mostrar respuestas",
"home.hide_announcements": "Ocultar anuncios",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 376aac1f70..95914b60dc 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -345,9 +345,9 @@
"explore.trending_links": "Noticias",
"explore.trending_statuses": "Publicaciones",
"explore.trending_tags": "Etiquetas",
- "featured_carousel.header": "{count, plural,one {Publicación fijada} other {Publicaciones fijada}}",
+ "featured_carousel.header": "{count, plural,one {Publicación fijada} other {Publicaciones fijadas}}",
"featured_carousel.next": "Siguiente",
- "featured_carousel.post": "Publicar",
+ "featured_carousel.post": "Publicación",
"featured_carousel.previous": "Anterior",
"featured_carousel.slide": "{index} de {total}",
"filter_modal.added.context_mismatch_explanation": "Esta categoría de filtro no se aplica al contexto en el que ha accedido a esta publlicación. Si quieres que la publicación sea filtrada también en este contexto, tendrás que editar el filtro.",
@@ -430,6 +430,7 @@
"hints.profiles.see_more_posts": "Ver más publicaciones en {domain}",
"hints.threads.replies_may_be_missing": "Puede que no se muestren algunas respuestas de otros servidores.",
"hints.threads.see_more": "Ver más respuestas en {domain}",
+ "home.column_settings.show_quotes": "Mostrar citas",
"home.column_settings.show_reblogs": "Mostrar impulsos",
"home.column_settings.show_replies": "Mostrar respuestas",
"home.hide_announcements": "Ocultar comunicaciones",
diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json
index f2205d3d35..fb9673c5ea 100644
--- a/app/javascript/mastodon/locales/et.json
+++ b/app/javascript/mastodon/locales/et.json
@@ -1,6 +1,7 @@
{
"about.blocks": "Modereeritavad serverid",
"about.contact": "Kontakt:",
+ "about.default_locale": "Vaikimisi",
"about.disclaimer": "Mastodon on tasuta ja vaba tarkvara ning Mastodon gGmbH kaubamärk.",
"about.domain_blocks.no_reason_available": "Põhjus teadmata",
"about.domain_blocks.preamble": "Mastodon lubab tavaliselt vaadata sisu ning suhelda kasutajatega ükskõik millisest teisest fediversumi serverist. Need on erandid, mis on paika pandud sellel kindlal serveril.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "Piiratud",
"about.domain_blocks.suspended.explanation": "Mitte mingeid andmeid sellelt serverilt ei töödelda, salvestata ega vahetata, tehes igasuguse interaktsiooni või kirjavahetuse selle serveri kasutajatega võimatuks.",
"about.domain_blocks.suspended.title": "Peatatud",
+ "about.language_label": "Keel",
"about.not_available": "See info ei ole sellel serveril saadavaks tehtud.",
"about.powered_by": "Hajutatud sotsiaalmeedia, mille taga on {mastodon}",
"about.rules": "Serveri reeglid",
@@ -26,6 +28,12 @@
"account.edit_profile": "Muuda profiili",
"account.enable_notifications": "Teavita mind @{name} postitustest",
"account.endorse": "Too profiilil esile",
+ "account.familiar_followers_many": "Jälgijateks {name1}, {name2} ja veel {othersCount, plural, one {üks kasutaja, keda tead} other {# kasutajat, keda tead}}",
+ "account.familiar_followers_one": "Jälgijaks {name1}",
+ "account.familiar_followers_two": "Jälgijateks {name1} ja {name2}",
+ "account.featured": "Esiletõstetud",
+ "account.featured.accounts": "Profiilid",
+ "account.featured.hashtags": "Sildid",
"account.featured_tags.last_status_at": "Viimane postitus {date}",
"account.featured_tags.last_status_never": "Postitusi pole",
"account.follow": "Jälgi",
@@ -36,6 +44,7 @@
"account.following": "Jälgib",
"account.following_counter": "{count, plural, one {{counter} jälgib} other {{counter} jälgib}}",
"account.follows.empty": "See kasutaja ei jälgi veel kedagi.",
+ "account.follows_you": "Jälgib sind",
"account.go_to_profile": "Mine profiilile",
"account.hide_reblogs": "Peida @{name} jagamised",
"account.in_memoriam": "In Memoriam.",
@@ -50,18 +59,22 @@
"account.mute_notifications_short": "Vaigista teavitused",
"account.mute_short": "Vaigista",
"account.muted": "Vaigistatud",
+ "account.mutual": "Te jälgite teineteist",
"account.no_bio": "Kirjeldust pole lisatud.",
"account.open_original_page": "Ava algne leht",
"account.posts": "Postitused",
"account.posts_with_replies": "Postitused ja vastused",
+ "account.remove_from_followers": "Eemalda {name} jälgijate seast",
"account.report": "Raporteeri @{name}",
"account.requested": "Ootab kinnitust. Klõpsa jälgimise soovi tühistamiseks",
"account.requested_follow": "{name} on taodelnud sinu jälgimist",
+ "account.requests_to_follow_you": "soovib sind jälgida",
"account.share": "Jaga @{name} profiili",
"account.show_reblogs": "Näita @{name} jagamisi",
"account.statuses_counter": "{count, plural, one {{counter} postitus} other {{counter} postitust}}",
"account.unblock": "Eemalda blokeering @{name}",
"account.unblock_domain": "Tee {domain} nähtavaks",
+ "account.unblock_domain_short": "Lõpeta blokeerimine",
"account.unblock_short": "Eemalda blokeering",
"account.unendorse": "Ära kuva profiilil",
"account.unfollow": "Jälgid",
@@ -223,6 +236,9 @@
"confirmations.redraft.confirm": "Kustuta & taasalusta",
"confirmations.redraft.message": "Kindel, et soovid postituse kustutada ja võtta uue aluseks? Lemmikuks märkimised ja jagamised lähevad kaotsi ning vastused jäävad ilma algse postituseta.",
"confirmations.redraft.title": "Kustudada ja luua postituse mustand?",
+ "confirmations.remove_from_followers.confirm": "Eemalda jälgija",
+ "confirmations.remove_from_followers.message": "{name} lõpetab sellega sinu jälgimise. Kas oled kindel, et soovid jätkata?",
+ "confirmations.remove_from_followers.title": "Kas eemaldame jälgija?",
"confirmations.reply.confirm": "Vasta",
"confirmations.reply.message": "Praegu vastamine kirjutab hetkel koostatava sõnumi üle. Oled kindel, et soovid jätkata?",
"confirmations.reply.title": "Kirjutada postitus üle?",
@@ -290,6 +306,9 @@
"emoji_button.search_results": "Otsitulemused",
"emoji_button.symbols": "Sümbolid",
"emoji_button.travel": "Reisimine & kohad",
+ "empty_column.account_featured.me": "Sa pole veel midagi esile tõstnud. Kas sa teadsid, et oma profiilis saad esile tõsta enamkasutatavaid silte või või sõbra kasutajakontot?",
+ "empty_column.account_featured.other": "{acct} pole veel midagi esile tõstnud. Kas sa teadsid, et oma profiilis saad esile tõsta enamkasutatavaid silte või või sõbra kasutajakontot?",
+ "empty_column.account_featured_other.unknown": "See kasutajakonto pole veel midagi esile tõstnud.",
"empty_column.account_hides_collections": "See kasutaja otsustas mitte teha seda infot saadavaks",
"empty_column.account_suspended": "Konto kustutatud",
"empty_column.account_timeline": "Siin postitusi ei ole!",
@@ -322,6 +341,10 @@
"explore.trending_links": "Uudised",
"explore.trending_statuses": "Postitused",
"explore.trending_tags": "Sildid",
+ "featured_carousel.header": "{count, plural, one {Esiletõstetud postitus} other {Esiletõstetud postitust}}",
+ "featured_carousel.next": "Järgmine",
+ "featured_carousel.previous": "Eelmine",
+ "featured_carousel.slide": "{index} / {total}",
"filter_modal.added.context_mismatch_explanation": "See filtrikategooria ei rakendu kontekstis, kuidas postituseni jõudsid. Kui tahad postitust ka selles kontekstis filtreerida, pead muutma filtrit.",
"filter_modal.added.context_mismatch_title": "Konteksti mittesobivus!",
"filter_modal.added.expired_explanation": "Selle filtri kategooria on aegunud. pead muutma aegumiskuupäeva, kui tahad, et filter kehtiks.",
@@ -374,6 +397,8 @@
"generic.saved": "Salvestatud",
"getting_started.heading": "Alustamine",
"hashtag.admin_moderation": "Ava modereerimisliides #{name} jaoks",
+ "hashtag.browse": "Sirvi #{hashtag} sildiga postitusi",
+ "hashtag.browse_from_account": "Sirvi @{name} kasutaja #{hashtag} sildiga postitusi",
"hashtag.column_header.tag_mode.all": "ja {additional}",
"hashtag.column_header.tag_mode.any": "või {additional}",
"hashtag.column_header.tag_mode.none": "ilma {additional}",
@@ -386,7 +411,10 @@
"hashtag.counter_by_accounts": "{count, plural, one {{counter} osalejaga} other {{counter} osalejaga}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} postitusega} other {{counter} postitusega}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} postitust} other {{counter} postitust}} täna",
+ "hashtag.feature": "Tõsta profiilis esile",
"hashtag.follow": "Jälgi silti",
+ "hashtag.mute": "Vaigista @#{hashtag}",
+ "hashtag.unfeature": "Ära tõsta profiilis esile",
"hashtag.unfollow": "Lõpeta sildi jälgimine",
"hashtags.and_other": "…ja {count, plural, one {}other {# veel}}",
"hints.profiles.followers_may_be_missing": "Selle profiili jälgijaid võib olla puudu.",
@@ -397,6 +425,7 @@
"hints.profiles.see_more_posts": "Vaata rohkem postitusi kohas {domain}",
"hints.threads.replies_may_be_missing": "Vastuseid teistest serveritest võib olla puudu.",
"hints.threads.see_more": "Vaata rohkem vastuseid kohas {domain}",
+ "home.column_settings.show_quotes": "Näita tsiteeritut",
"home.column_settings.show_reblogs": "Näita jagamisi",
"home.column_settings.show_replies": "Näita vastuseid",
"home.hide_announcements": "Peida teadaanded",
@@ -705,6 +734,8 @@
"privacy_policy.title": "Isikuandmete kaitse",
"recommended": "Soovitatud",
"refresh": "Värskenda",
+ "regeneration_indicator.please_stand_by": "Palun oota.",
+ "regeneration_indicator.preparing_your_home_feed": "Valmistan ette sinu avalehe lõime…",
"relative_time.days": "{number}p",
"relative_time.full.days": "{number, plural, one {# päev} other {# päeva}} tagasi",
"relative_time.full.hours": "{number, plural, one {# tund} other {# tundi}} tagasi",
@@ -758,7 +789,7 @@
"report.thanks.title": "Ei taha seda näha?",
"report.thanks.title_actionable": "Täname teavitamise eest, uurime seda.",
"report.unfollow": "Lõpeta @{name} jälgimine",
- "report.unfollow_explanation": "Jälgid seda kontot. Et mitte näha tema postitusi oma koduvoos, lõpeta ta jälgimine.",
+ "report.unfollow_explanation": "Jälgid seda kontot. Et mitte näha tema postitusi oma avalehe lõimes, lõpeta ta jälgimine.",
"report_notification.attached_statuses": "{count, plural, one {{count} postitus} other {{count} postitust}} listatud",
"report_notification.categories.legal": "Õiguslik",
"report_notification.categories.legal_sentence": "ebaseaduslik sisu",
@@ -788,8 +819,11 @@
"search_results.accounts": "Profiilid",
"search_results.all": "Kõik",
"search_results.hashtags": "Sildid",
+ "search_results.no_results": "Tulemusi pole.",
+ "search_results.no_search_yet": "Proovi otsida postitusi, profiile või silte.",
"search_results.see_all": "Vaata kõiki",
"search_results.statuses": "Postitused",
+ "search_results.title": "Otsi märksõna: {q}",
"server_banner.about_active_users": "Inimesed, kes kasutavad seda serverit viimase 30 päeva jooksul (kuu aktiivsed kasutajad)",
"server_banner.active_users": "aktiivsed kasutajad",
"server_banner.administered_by": "Administraator:",
@@ -832,6 +866,13 @@
"status.mute_conversation": "Vaigista vestlus",
"status.open": "Laienda postitus",
"status.pin": "Kinnita profiilile",
+ "status.quote_error.filtered": "Peidetud mõne kasutatud filtri tõttu",
+ "status.quote_error.not_found": "Seda postitust ei saa näidata.",
+ "status.quote_error.pending_approval": "See postitus on algse autori kinnituse ootel.",
+ "status.quote_error.rejected": "Seda postitust ei saa näidata, kuina algne autor ei luba teda tsiteerida.",
+ "status.quote_error.removed": "Autor kustutas selle postituse.",
+ "status.quote_error.unauthorized": "Kuna sul pole luba selle postituse nägemiseks, siis seda ei saa kuvada.",
+ "status.quote_post_author": "Postitajaks {name}",
"status.read_more": "Loe veel",
"status.reblog": "Jaga",
"status.reblog_private": "Jaga algse nähtavusega",
@@ -840,6 +881,7 @@
"status.reblogs.empty": "Keegi pole seda postitust veel jaganud. Kui keegi seda teeb, näeb seda siin.",
"status.redraft": "Kustuta & alga uuesti",
"status.remove_bookmark": "Eemalda järjehoidja",
+ "status.remove_favourite": "Eemalda lemmikute seast",
"status.replied_in_thread": "Vastatud lõimes",
"status.replied_to": "Vastas kasutajale {name}",
"status.reply": "Vasta",
@@ -861,7 +903,9 @@
"subscribed_languages.target": "Muuda tellitud keeli {target} jaoks",
"tabs_bar.home": "Kodu",
"tabs_bar.notifications": "Teated",
+ "terms_of_service.effective_as_of": "Kehtib alates {date}",
"terms_of_service.title": "Teenuse tingimused",
+ "terms_of_service.upcoming_changes_on": "Muudatused alates {date}",
"time_remaining.days": "{number, plural, one {# päev} other {# päeva}} jäänud",
"time_remaining.hours": "{number, plural, one {# tund} other {# tundi}} jäänud",
"time_remaining.minutes": "{number, plural, one {# minut} other {# minutit}} jäänud",
@@ -892,6 +936,12 @@
"video.expand": "Suurenda video",
"video.fullscreen": "Täisekraan",
"video.hide": "Peida video",
+ "video.mute": "Vaigista",
"video.pause": "Paus",
- "video.play": "Mängi"
+ "video.play": "Mängi",
+ "video.skip_backward": "Keri tagasi",
+ "video.skip_forward": "Keri edasi",
+ "video.unmute": "Lõpeta vaigistamine",
+ "video.volume_down": "Heli vaiksemaks",
+ "video.volume_up": "Heli valjemaks"
}
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index c93a1bac73..2823ffd09d 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -430,6 +430,7 @@
"hints.profiles.see_more_posts": "Näytä lisää julkaisuja palvelimella {domain}",
"hints.threads.replies_may_be_missing": "Muiden palvelinten vastauksia saattaa puuttua.",
"hints.threads.see_more": "Näytä lisää vastauksia palvelimella {domain}",
+ "home.column_settings.show_quotes": "Näytä lainaukset",
"home.column_settings.show_reblogs": "Näytä tehostukset",
"home.column_settings.show_replies": "Näytä vastaukset",
"home.hide_announcements": "Piilota tiedotteet",
diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json
index 18bfe7acbd..d583ee264f 100644
--- a/app/javascript/mastodon/locales/fo.json
+++ b/app/javascript/mastodon/locales/fo.json
@@ -430,6 +430,7 @@
"hints.profiles.see_more_posts": "Sí fleiri postar á {domain}",
"hints.threads.replies_may_be_missing": "Svar frá øðrum ambætarum mangla møguliga.",
"hints.threads.see_more": "Sí fleiri svar á {domain}",
+ "home.column_settings.show_quotes": "Vís siteringar",
"home.column_settings.show_reblogs": "Vís lyft",
"home.column_settings.show_replies": "Vís svar",
"home.hide_announcements": "Fjal kunngerðir",
diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json
index e36d94c17b..9782a5ea24 100644
--- a/app/javascript/mastodon/locales/ga.json
+++ b/app/javascript/mastodon/locales/ga.json
@@ -1,6 +1,7 @@
{
"about.blocks": "Freastalaithe faoi stiúir",
"about.contact": "Teagmháil:",
+ "about.default_locale": "Réamhshocrú",
"about.disclaimer": "Bogearra foinse oscailte saor in aisce is ea Mastodon, agus is le Mastodon gGmbH an trádmharc.",
"about.domain_blocks.no_reason_available": "Níl an fáth ar fáil",
"about.domain_blocks.preamble": "Go hiondúil, tugann Mastadán cead duit a bheith ag plé le húsáideoirí as freastalaí ar bith eile sa chomhchruinne agus a gcuid inneachair a fheiceáil. Seo iad na heisceachtaí a rinneadh ar an bhfreastalaí áirithe seo.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "Teoranta",
"about.domain_blocks.suspended.explanation": "Ní dhéanfar aon sonra ón fhreastalaí seo a phróiseáil, a stóráil ná a mhalartú, rud a fhágann nach féidir aon teagmháil ná aon chumarsáid a dhéanamh le húsáideoirí ón fhreastalaí seo.",
"about.domain_blocks.suspended.title": "Ar fionraí",
+ "about.language_label": "Teanga",
"about.not_available": "Níor cuireadh an t-eolas seo ar fáil ar an bhfreastalaí seo.",
"about.powered_by": "Meáin shóisialta díláraithe faoi chumhacht {mastodon}",
"about.rules": "Rialacha an fhreastalaí",
@@ -308,6 +310,8 @@
"emoji_button.search_results": "Torthaí cuardaigh",
"emoji_button.symbols": "Comharthaí",
"emoji_button.travel": "Taisteal ⁊ Áiteanna",
+ "empty_column.account_featured.me": "Níl aon rud curtha i láthair agat go fóill. An raibh a fhios agat gur féidir leat na haischlibeanna is mó a úsáideann tú, agus fiú cuntais do chairde, a chur i láthair ar do phróifíl?",
+ "empty_column.account_featured.other": "Níl aon rud feicthe ag {acct} go fóill. An raibh a fhios agat gur féidir leat na hashtags is mó a úsáideann tú, agus fiú cuntais do chairde, a chur ar do phróifíl?",
"empty_column.account_featured_other.unknown": "Níl aon rud le feiceáil sa chuntas seo go fóill.",
"empty_column.account_hides_collections": "Roghnaigh an t-úsáideoir seo gan an fhaisnéis seo a chur ar fáil",
"empty_column.account_suspended": "Cuntas ar fionraí",
@@ -341,6 +345,11 @@
"explore.trending_links": "Nuacht",
"explore.trending_statuses": "Postálacha",
"explore.trending_tags": "Haischlibeanna",
+ "featured_carousel.header": "{count, plural, one {Postáil phinnáilte} two {Poist Phionáilte} few {Poist Phionáilte} many {Poist Phionáilte} other {Poist Phionáilte}}",
+ "featured_carousel.next": "Ar Aghaidh",
+ "featured_carousel.post": "Post",
+ "featured_carousel.previous": "Roimhe Seo",
+ "featured_carousel.slide": "{index} de {total}",
"filter_modal.added.context_mismatch_explanation": "Ní bhaineann an chatagóir scagaire seo leis an gcomhthéacs ina bhfuair tú rochtain ar an bpostáil seo. Más mian leat an postáil a scagadh sa chomhthéacs seo freisin, beidh ort an scagaire a chur in eagar.",
"filter_modal.added.context_mismatch_title": "Neamhréir comhthéacs!",
"filter_modal.added.expired_explanation": "Tá an chatagóir scagaire seo imithe in éag, beidh ort an dáta éaga a athrú chun é a chur i bhfeidhm.",
diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json
index 3cd86be449..89fad4b5b7 100644
--- a/app/javascript/mastodon/locales/gd.json
+++ b/app/javascript/mastodon/locales/gd.json
@@ -1,6 +1,7 @@
{
"about.blocks": "Frithealaichean fo mhaorsainneachd",
"about.contact": "Fios thugainn:",
+ "about.default_locale": "Bun-roghainn",
"about.disclaimer": "’S e bathar-bog saor le bun-tùs fosgailte a th’ ann am Mastodon agus ’na chomharra-mhalairt aig Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Chan eil an t-adhbhar ga thoirt seachad",
"about.domain_blocks.preamble": "San fharsaingeachd, leigidh Mastodon leat susbaint o fhrithealaiche sam bith sa cho-shaoghal a shealltainn agus eadar-ghìomh a ghabhail leis na cleachdaichean uapa-san. Seo na h-easgaidhean a tha an sàs air an fhrithealaiche shònraichte seo.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "Cuingichte",
"about.domain_blocks.suspended.explanation": "Cha dèid dàta sam bith on fhrithealaiche seo a phròiseasadh, a stòradh no iomlaid agus chan urrainn do na cleachdaichean on fhrithealaiche sin conaltradh no eadar-ghnìomh a ghabhail an-seo.",
"about.domain_blocks.suspended.title": "À rèim",
+ "about.language_label": "Cànan",
"about.not_available": "Cha deach am fiosrachadh seo a sholar air an fhrithealaiche seo.",
"about.powered_by": "Lìonra sòisealta sgaoilte le cumhachd {mastodon}",
"about.rules": "Riaghailtean an fhrithealaiche",
@@ -28,6 +30,9 @@
"account.edit_profile": "Deasaich a’ phròifil",
"account.enable_notifications": "Cuir brath thugam nuair a chuireas @{name} post ris",
"account.endorse": "Brosnaich air a’ phròifil",
+ "account.familiar_followers_many": "’Ga leantainn le {name1}, {name2}, and {othersCount, plural, one {# eile air a bheil thu eòlach} other {# eile air a bheil thu eòlach}}",
+ "account.familiar_followers_one": "’Ga leantainn le {name1}",
+ "account.familiar_followers_two": "’Ga leantainn le {name1} ’s {name2}",
"account.featured": "’Ga bhrosnachadh",
"account.featured.accounts": "Pròifilean",
"account.featured.hashtags": "Tagaichean hais",
@@ -38,6 +43,7 @@
"account.followers": "Luchd-leantainn",
"account.followers.empty": "Chan eil neach sam bith a’ leantainn air a’ chleachdaiche seo fhathast.",
"account.followers_counter": "{count, plural, one {{counter} neach-leantainn} other {{counter} luchd-leantainn}}",
+ "account.followers_you_know_counter": "{counter} air a bheil thu eòlach",
"account.following": "A’ leantainn",
"account.following_counter": "{count, plural, one {A’ leantainn {counter}} other {A’ leantainn {counter}}}",
"account.follows.empty": "Chan eil an cleachdaiche seo a’ leantainn neach sam bith fhathast.",
@@ -304,6 +310,8 @@
"emoji_button.search_results": "Toraidhean an luirg",
"emoji_button.symbols": "Samhlaidhean",
"emoji_button.travel": "Siubhal ⁊ àitichean",
+ "empty_column.account_featured.me": "Chan eil thu a’ brosnachadh dad fhathast. An robh fios agad gur urrainn dhut na tagaichean hais a chleachdas tu as trice agus fiù ’s cunntasan do charaidean a bhrosnachadh air a’ phròifil agad?",
+ "empty_column.account_featured.other": "Chan eil {acct} a’ brosnachadh dad fhathast. An robh fios agad gur urrainn dhut na tagaichean hais a chleachdas tu as trice agus fiù ’s cunntasan do charaidean a bhrosnachadh air a’ phròifil agad?",
"empty_column.account_featured_other.unknown": "Chan eil an cunntas seo a’ brosnachadh dad fhathast.",
"empty_column.account_hides_collections": "Chuir an cleachdaiche seo roimhe nach eil am fiosrachadh seo ri fhaighinn",
"empty_column.account_suspended": "Chaidh an cunntas a chur à rèim",
@@ -337,6 +345,11 @@
"explore.trending_links": "Naidheachdan",
"explore.trending_statuses": "Postaichean",
"explore.trending_tags": "Tagaichean hais",
+ "featured_carousel.header": "{count, plural, one {Post prìnichte} two {Postaichean prìnichte} few {Postaichean prìnichte} other {Postaichean prìnichte}}",
+ "featured_carousel.next": "Air adhart",
+ "featured_carousel.post": "Post",
+ "featured_carousel.previous": "Air ais",
+ "featured_carousel.slide": "{index} à {total}",
"filter_modal.added.context_mismatch_explanation": "Chan eil an roinn-seòrsa criathraidh iom seo chaidh dhan cho-theacs san do dh’inntrig thu am post seo. Ma tha thu airson am post a chriathradh sa cho-theacs seo cuideachd, feumaidh tu a’ chriathrag a dheasachadh.",
"filter_modal.added.context_mismatch_title": "Co-theacsa neo-iomchaidh!",
"filter_modal.added.expired_explanation": "Dh’fhalbh an ùine air an roinn-seòrsa criathraidh seo agus feumaidh tu an ceann-là crìochnachaidh atharrachadh mus cuir thu an sàs i.",
@@ -856,6 +869,13 @@
"status.mute_conversation": "Mùch an còmhradh",
"status.open": "Leudaich am post seo",
"status.pin": "Prìnich ris a’ phròifil",
+ "status.quote_error.filtered": "Falaichte le criathrag a th’ agad",
+ "status.quote_error.not_found": "Chan urrainn dhuinn am post seo a shealltainn.",
+ "status.quote_error.pending_approval": "Tha am post seo a’ feitheamh air aontachadh leis an ùghdar tùsail.",
+ "status.quote_error.rejected": "Chan urrainn dhuinn am post seo a shealltainn air sgàth ’s nach ceadaich an t-ùghdar tùsail aige gun dèid a luaidh.",
+ "status.quote_error.removed": "Chaidh am post seo a thoirt air falbh le ùghdar.",
+ "status.quote_error.unauthorized": "Chan urrainn dhuinn am post seo a shealltainn air sgàth ’s nach eil cead agad fhaicinn.",
+ "status.quote_post_author": "Post le {name}",
"status.read_more": "Leugh an còrr",
"status.reblog": "Brosnaich",
"status.reblog_private": "Brosnaich leis an t-so-fhaicsinneachd tùsail",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index 8fc45f84a1..6a70c8b750 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -1,6 +1,7 @@
{
"about.blocks": "Servidores suxeitos a moderación",
"about.contact": "Contacto:",
+ "about.default_locale": "Por defecto",
"about.disclaimer": "Mastodon é software libre, de código aberto, e unha marca comercial de Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Motivo non indicado",
"about.domain_blocks.preamble": "Mastodon de xeito xeral permíteche ver contidos doutros servidores do fediverso e interactuar coas súas usuarias. Estas son as excepcións que se estabeleceron neste servidor en particular.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "Limitado",
"about.domain_blocks.suspended.explanation": "Non se procesarán, almacenarán nin intercambiarán datos con este servidor, o que fai imposible calquera interacción ou comunicación coas usuarias deste servidor.",
"about.domain_blocks.suspended.title": "Suspendido",
+ "about.language_label": "Idioma",
"about.not_available": "Esta información non está dispoñible neste servidor.",
"about.powered_by": "Comunicación social descentralizada grazas a {mastodon}",
"about.rules": "Regras do servidor",
@@ -428,6 +430,7 @@
"hints.profiles.see_more_posts": "Mira máis publicacións en {domain}",
"hints.threads.replies_may_be_missing": "Poderían faltar respostas desde outros servidores.",
"hints.threads.see_more": "Mira máis respostas en {domain}",
+ "home.column_settings.show_quotes": "Mostrar citas",
"home.column_settings.show_reblogs": "Amosar compartidos",
"home.column_settings.show_replies": "Amosar respostas",
"home.hide_announcements": "Agochar anuncios",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index cb40a05911..c3b549033a 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -1,6 +1,7 @@
{
"about.blocks": "שרתים תחת פיקוח תוכן",
"about.contact": "יצירת קשר:",
+ "about.default_locale": "ברירת המחדל",
"about.disclaimer": "מסטודון היא תוכנת קוד פתוח חינמית וסימן מסחרי של Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "הסיבה אינה זמינה",
"about.domain_blocks.preamble": "ככלל מסטודון מאפשרת לך לצפות בתוכן ולתקשר עם משתמשים מכל שרת בפדיברס. אלו הם היוצאים מן הכלל שהוגדרו עבור השרת המסוים הזה.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "מוגבלים",
"about.domain_blocks.suspended.explanation": "שום מידע משרת זה לא יעובד, יישמר או יוחלף, מה שהופך כל תקשורת עם משתמשים משרת זה לבלתי אפשרית.",
"about.domain_blocks.suspended.title": "מושעים",
+ "about.language_label": "שפה",
"about.not_available": "המידע אינו זמין על שרת זה.",
"about.powered_by": "רשת חברתית מבוזרת המופעלת על ידי {mastodon}",
"about.rules": "כללי השרת",
@@ -308,6 +310,8 @@
"emoji_button.search_results": "תוצאות חיפוש",
"emoji_button.symbols": "סמלים",
"emoji_button.travel": "טיולים ואתרים",
+ "empty_column.account_featured.me": "עוד לא קידמת תכנים. הידעת שניתן לקדם תגיות שבשימושך התדיר או אפילו את החשבונות של חבריםות בפרופיל שלך?",
+ "empty_column.account_featured.other": "{acct} עוד לא קידם תכנים. הידעת שניתן לקדם תגיות שבשימושך התדיר או אפילו את החשבונות של חבריםות בפרופיל שלך?",
"empty_column.account_featured_other.unknown": "חשבון זה עוד לא קידם תכנים.",
"empty_column.account_hides_collections": "המשתמש.ת בחר.ה להסתיר מידע זה",
"empty_column.account_suspended": "חשבון מושעה",
@@ -341,6 +345,11 @@
"explore.trending_links": "חדשות",
"explore.trending_statuses": "הודעות",
"explore.trending_tags": "תגיות",
+ "featured_carousel.header": "{count, plural, one {הודעה אחת נעוצה} two {הודעותיים נעוצות} many {הודעות נעוצות} other {הודעות נעוצות}}",
+ "featured_carousel.next": "הבא",
+ "featured_carousel.post": "הודעה",
+ "featured_carousel.previous": "הקודם",
+ "featured_carousel.slide": "{index} מתוך {total}",
"filter_modal.added.context_mismatch_explanation": "קטגוריית המסנן הזאת לא חלה על ההקשר שממנו הגעת אל ההודעה הזו. אם תרצה/י שההודעה תסונן גם בהקשר זה, תצטרך/י לערוך את הסנן.",
"filter_modal.added.context_mismatch_title": "אין התאמה להקשר!",
"filter_modal.added.expired_explanation": "פג תוקפה של קטגוריית הסינון הזו, יש צורך לשנות את תאריך התפוגה כדי שהסינון יוחל.",
@@ -421,6 +430,7 @@
"hints.profiles.see_more_posts": "צפיה בעוד פרסומים בשרת {domain}",
"hints.threads.replies_may_be_missing": "תגובות משרתים אחרים עלולות להיות חסרות.",
"hints.threads.see_more": "צפיה בעוד תגובות משרת {domain}",
+ "home.column_settings.show_quotes": "הצגת ציטוטים",
"home.column_settings.show_reblogs": "הצגת הדהודים",
"home.column_settings.show_replies": "הצגת תגובות",
"home.hide_announcements": "הסתר הכרזות",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 9b25e334f1..f7f2fce2a0 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -1,6 +1,7 @@
{
"about.blocks": "Moderált kiszolgálók",
"about.contact": "Kapcsolat:",
+ "about.default_locale": "Alapértelmezett",
"about.disclaimer": "A Mastodon ingyenes, nyílt forráskódú szoftver, a Mastodon gGmbH védjegye.",
"about.domain_blocks.no_reason_available": "Nem áll rendelkezésre indoklás",
"about.domain_blocks.preamble": "A Mastodon általában mindenféle tartalomcserét és interakciót lehetővé tesz bármelyik másik kiszolgálóval a födiverzumban. Ezek azok a kivételek, amelyek a mi kiszolgálónkon érvényben vannak.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "Korlátozott",
"about.domain_blocks.suspended.explanation": "A kiszolgáló adatai nem lesznek feldolgozva, tárolva vagy megosztva, lehetetlenné téve mindennemű interakciót és kommunikációt a kiszolgáló felhasználóival.",
"about.domain_blocks.suspended.title": "Felfüggesztett",
+ "about.language_label": "Nyelv",
"about.not_available": "Ez az információ nem lett közzétéve ezen a kiszolgálón.",
"about.powered_by": "Decentralizált közösségi média a {mastodon} segítségével",
"about.rules": "Kiszolgáló szabályai",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index e4b1c9a2a9..e84fd2fbb6 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -1,6 +1,7 @@
{
"about.blocks": "Server moderati",
"about.contact": "Contatti:",
+ "about.default_locale": "Predefinito",
"about.disclaimer": "Mastodon è un software libero e open-source e un marchio di Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Motivo non disponibile",
"about.domain_blocks.preamble": "Mastodon, generalmente, ti consente di visualizzare i contenuti e interagire con gli utenti da qualsiasi altro server nel fediverso. Queste sono le eccezioni che sono state fatte su questo particolare server.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "Limitato",
"about.domain_blocks.suspended.explanation": "Nessun dato proveniente da questo server verrà elaborato, conservato o scambiato, rendendo impossibile qualsiasi interazione o comunicazione con gli utenti da questo server.",
"about.domain_blocks.suspended.title": "Sospeso",
+ "about.language_label": "Lingua",
"about.not_available": "Queste informazioni non sono state rese disponibili su questo server.",
"about.powered_by": "Social media decentralizzato alimentato da {mastodon}",
"about.rules": "Regole del server",
@@ -308,6 +310,8 @@
"emoji_button.search_results": "Risultati della ricerca",
"emoji_button.symbols": "Simboli",
"emoji_button.travel": "Viaggi & Luoghi",
+ "empty_column.account_featured.me": "Non hai ancora messo in evidenza nulla. Sapevi che puoi mettere in evidenza gli hashtag che usi più spesso e persino gli account dei tuoi amici sul tuo profilo?",
+ "empty_column.account_featured.other": "{acct} non ha ancora messo in evidenza nulla. Sapevi che puoi mettere in evidenza gli hashtag che usi più spesso e persino gli account dei tuoi amici sul tuo profilo?",
"empty_column.account_featured_other.unknown": "Questo account non ha ancora pubblicato nulla.",
"empty_column.account_hides_collections": "Questo utente ha scelto di non rendere disponibili queste informazioni",
"empty_column.account_suspended": "Profilo sospeso",
@@ -341,6 +345,11 @@
"explore.trending_links": "Notizie",
"explore.trending_statuses": "Post",
"explore.trending_tags": "Hashtag",
+ "featured_carousel.header": "{count, plural, one {Post appuntato} other {Post appuntati}}",
+ "featured_carousel.next": "Successivo",
+ "featured_carousel.post": "Post",
+ "featured_carousel.previous": "Precedente",
+ "featured_carousel.slide": "{index} di {total}",
"filter_modal.added.context_mismatch_explanation": "La categoria di questo filtro non si applica al contesto in cui hai acceduto a questo post. Se desideri che il post sia filtrato anche in questo contesto, dovrai modificare il filtro.",
"filter_modal.added.context_mismatch_title": "Contesto non corrispondente!",
"filter_modal.added.expired_explanation": "La categoria di questo filtro è scaduta, dovrvai modificarne la data di scadenza per applicarlo.",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 5c6327ddce..dacff16bbc 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -2,6 +2,7 @@
"about.blocks": "制限中のサーバー",
"about.contact": "連絡先",
"about.disabled": "無効",
+ "about.default_locale": "デフォルト",
"about.disclaimer": "Mastodonは自由なオープンソースソフトウェアであり、Mastodon gGmbHの商標です。",
"about.domain_blocks.no_reason_available": "理由未記載",
"about.domain_blocks.preamble": "Mastodonでは原則的にあらゆるサーバー同士で交流したり、互いの投稿を読んだりできますが、当サーバーでは例外的に次のような制限を設けています。",
@@ -15,6 +16,7 @@
"about.full_text_search": "全文検索",
"about.kmyblue_capabilities": "このサーバーで利用可能な機能",
"about.kmyblue_capability": "このサーバーは、kmyblueというMastodonフォークを利用しています。kmyblue独自機能の一部は、サーバー管理者によって有効・無効を切り替えることができます。",
+ "about.language_label": "言語",
"about.not_available": "この情報はこのサーバーでは利用できません。",
"about.powered_by": "{mastodon}による分散型ソーシャルメディア",
"about.public_visibility": "公開投稿を許可",
@@ -39,7 +41,11 @@
"account.edit_profile": "プロフィール編集",
"account.enable_notifications": "@{name}さんの投稿時に通知",
"account.endorse": "プロフィールで紹介する",
+ "account.familiar_followers_many": "{name1}、{name2}、他{othersCount, plural, one {one other you know} other {# others you know}}人のユーザーにフォローされています",
+ "account.familiar_followers_one": "{name1} さんがフォローしています",
+ "account.familiar_followers_two": "{name1} さんと {name2} さんもフォローしています",
"account.featured": "注目",
+ "account.featured.accounts": "プロフィール",
"account.featured.hashtags": "ハッシュタグ",
"account.featured_tags.last_status_at": "最終投稿 {date}",
"account.featured_tags.last_status_never": "投稿がありません",
@@ -48,6 +54,7 @@
"account.followers": "フォロワー",
"account.followers.empty": "まだ誰もフォローしていません。",
"account.followers_counter": "{count, plural, other {{counter} フォロワー}}",
+ "account.followers_you_know_counter": "あなたと知り合いの{counter}人",
"account.following": "フォロー中",
"account.following_counter": "{count, plural, other {{counter} フォロー}}",
"account.follows.empty": "まだ誰もフォローしていません。",
@@ -422,6 +429,8 @@
"emoji_button.search_results": "検索結果",
"emoji_button.symbols": "記号",
"emoji_button.travel": "旅行と場所",
+ "empty_column.account_featured.me": "まだ何もフィーチャーしていません。最もよく使うハッシュタグや、更には友達のアカウントまでプロフィール上でフィーチャーできると知っていましたか?",
+ "empty_column.account_featured.other": "{acct}ではまだ何もフィーチャーされていません。最もよく使うハッシュタグや、更には友達のアカウントまでプロフィール上でフィーチャーできると知っていましたか?",
"empty_column.account_featured_other.unknown": "このアカウントにはまだ何も投稿されていません。",
"empty_column.account_hides_collections": "このユーザーはこの情報を開示しないことにしています。",
"empty_column.account_suspended": "アカウントは停止されています",
@@ -462,6 +471,10 @@
"explore.trending_links": "ニュース",
"explore.trending_statuses": "投稿",
"explore.trending_tags": "ハッシュタグ",
+ "featured_carousel.next": "次へ",
+ "featured_carousel.post": "投稿",
+ "featured_carousel.previous": "前へ",
+ "featured_carousel.slide": "{index} / {total}",
"filter_modal.added.context_mismatch_explanation": "このフィルターカテゴリーはあなたがアクセスした投稿のコンテキストには適用されません。この投稿のコンテキストでもフィルターを適用するにはフィルターを編集する必要があります。",
"filter_modal.added.context_mismatch_title": "コンテキストが一致しません!",
"filter_modal.added.expired_explanation": "このフィルターカテゴリーは有効期限が切れています。適用するには有効期限を更新してください。",
@@ -528,8 +541,10 @@
"hashtag.counter_by_accounts": "{count, plural, other {{counter}人投稿}}",
"hashtag.counter_by_uses": "{count, plural, other {{counter}件}}",
"hashtag.counter_by_uses_today": "本日{count, plural, other {#件}}",
+ "hashtag.feature": "プロフィールで紹介する",
"hashtag.follow": "ハッシュタグをフォローする",
"hashtag.mute": "#{hashtag}をミュート",
+ "hashtag.unfeature": "プロフィールから外す",
"hashtag.unfollow": "ハッシュタグのフォローを解除",
"hashtags.and_other": "ほか{count, plural, other {#個}}",
"hints.profiles.followers_may_be_missing": "フォロワーの一覧は不正確な場合があります。",
@@ -1046,6 +1061,13 @@
"status.pin": "プロフィールに固定表示",
"status.quote": "リンク",
"status.quote_link": "引用リンクを挿入",
+ "status.quote_error.filtered": "あなたのフィルター設定によって非表示になっています",
+ "status.quote_error.not_found": "この投稿は表示できません。",
+ "status.quote_error.pending_approval": "この投稿は投稿者の承認待ちです。",
+ "status.quote_error.rejected": "この投稿は、オリジナルの投稿者が引用することを許可していないため、表示できません。",
+ "status.quote_error.removed": "この投稿は投稿者によって削除されました。",
+ "status.quote_error.unauthorized": "この投稿を表示する権限がないため、表示できません。",
+ "status.quote_post_author": "{name} の投稿",
"status.read_more": "もっと見る",
"status.reblog": "ブースト",
"status.reblog_private": "ブースト",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index f3a590f0d4..f2e5a2c486 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -1,6 +1,7 @@
{
"about.blocks": "제한된 서버들",
"about.contact": "연락처:",
+ "about.default_locale": "기본",
"about.disclaimer": "Mastodon은 자유 오픈소스 소프트웨어이며, Mastodon gGmbH의 상표입니다",
"about.domain_blocks.no_reason_available": "사유를 밝히지 않음",
"about.domain_blocks.preamble": "마스토돈은 일반적으로 연합우주에 있는 어떤 서버의 사용자와도 게시물을 보고 응답을 할 수 있도록 허용합니다. 다음 항목들은 특정한 서버에 대해 만들어 진 예외사항입니다.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "제한됨",
"about.domain_blocks.suspended.explanation": "이 서버의 어떤 데이터도 처리되거나, 저장 되거나 공유되지 않고, 이 서버의 어떤 유저와도 상호작용 하거나 대화할 수 없습니다.",
"about.domain_blocks.suspended.title": "정지됨",
+ "about.language_label": "언어",
"about.not_available": "이 정보는 이 서버에서 사용할 수 없습니다.",
"about.powered_by": "{mastodon}으로 구동되는 분산 소셜 미디어",
"about.rules": "서버 규칙",
@@ -41,6 +43,7 @@
"account.followers": "팔로워",
"account.followers.empty": "아직 아무도 이 사용자를 팔로우하고 있지 않습니다.",
"account.followers_counter": "{count, plural, other {팔로워 {counter}명}}",
+ "account.followers_you_know_counter": "내가 아는 {counter} 명",
"account.following": "팔로잉",
"account.following_counter": "{count, plural, other {팔로잉 {counter}명}}",
"account.follows.empty": "이 사용자는 아직 아무도 팔로우하고 있지 않습니다.",
@@ -307,6 +310,8 @@
"emoji_button.search_results": "검색 결과",
"emoji_button.symbols": "기호",
"emoji_button.travel": "여행과 장소",
+ "empty_column.account_featured.me": "아직 아무 것도 추천하지 않았습니다. 자주 사용하는 해시태그, 친구의 계정까지 내 계정에서 추천할 수 있다는 것을 알고 계셨나요?",
+ "empty_column.account_featured.other": "{acct} 님은 아직 아무 것도 추천하지 않았습니다. 자주 사용하는 해시태그, 친구의 계정까지 내 계정에서 추천할 수 있다는 것을 알고 계셨나요?",
"empty_column.account_featured_other.unknown": "이 계정은 아직 아무 것도 추천하지 않았습니다.",
"empty_column.account_hides_collections": "이 사용자는 이 정보를 사용할 수 없도록 설정했습니다",
"empty_column.account_suspended": "계정 정지됨",
@@ -340,6 +345,11 @@
"explore.trending_links": "소식",
"explore.trending_statuses": "게시물",
"explore.trending_tags": "해시태그",
+ "featured_carousel.header": "{count, plural, other {고정된 게시물}}",
+ "featured_carousel.next": "다음",
+ "featured_carousel.post": "게시물",
+ "featured_carousel.previous": "이전",
+ "featured_carousel.slide": "{total} 중 {index}",
"filter_modal.added.context_mismatch_explanation": "이 필터 카테고리는 당신이 이 게시물에 접근한 문맥에 적용되지 않습니다. 만약 이 문맥에서도 필터되길 원한다면, 필터를 수정해야 합니다.",
"filter_modal.added.context_mismatch_title": "문맥 불일치!",
"filter_modal.added.expired_explanation": "이 필터 카테고리는 만료되었습니다, 적용하려면 만료 일자를 변경할 필요가 있습니다.",
@@ -860,11 +870,13 @@
"status.mute_conversation": "대화 뮤트",
"status.open": "상세 정보 표시",
"status.pin": "고정",
+ "status.quote_error.filtered": "필터에 의해 가려짐",
"status.quote_error.not_found": "이 게시물은 표시할 수 없습니다.",
"status.quote_error.pending_approval": "이 게시물은 원작자의 승인을 기다리고 있습니다.",
"status.quote_error.rejected": "이 게시물은 원작자가 인용을 허용하지 않았기 때문에 표시할 수 없습니다.",
"status.quote_error.removed": "이 게시물은 작성자에 의해 삭제되었습니다.",
"status.quote_error.unauthorized": "이 게시물은 권한이 없기 때문에 볼 수 없습니다.",
+ "status.quote_post_author": "{name} 님의 게시물",
"status.read_more": "더 보기",
"status.reblog": "부스트",
"status.reblog_private": "원래의 수신자들에게 부스트",
diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json
index 72d1765ecc..3492d83cfa 100644
--- a/app/javascript/mastodon/locales/lv.json
+++ b/app/javascript/mastodon/locales/lv.json
@@ -160,7 +160,7 @@
"column.directory": "Pārlūkot profilus",
"column.domain_blocks": "Bloķētie domēni",
"column.edit_list": "Labot sarakstu",
- "column.favourites": "Iecienītie",
+ "column.favourites": "Izlase",
"column.firehose": "Tiešraides plūsmas",
"column.follow_requests": "Sekošanas pieprasījumi",
"column.home": "Sākums",
@@ -262,6 +262,7 @@
"dismissable_banner.community_timeline": "Šie ir jaunākie publiskie ieraksti no cilvēkiem, kuru konti ir mitināti {domain}.",
"dismissable_banner.dismiss": "Atcelt",
"dismissable_banner.explore_links": "Šie jaunumi šodien Fediversā tiek visvairāk kopīgoti. Jaunākas ziņas, kuras pievienoši vairāki dažādi cilvēki, tiek novietotas augstāk.",
+ "dismissable_banner.explore_statuses": "Šie ieraksti šodien gūst uzmanību fediversā. Jaunāki ieraksti ar vairāk pastirpinājumiem un pievienošanām izlasē tiek kārtoti augstāk.",
"dismissable_banner.public_timeline": "Šie ir jaunākie Fediverse lietotāju publiskie ieraksti, kuriem {domain} seko cilvēki.",
"domain_block_modal.block": "Bloķēt serveri",
"domain_block_modal.block_account_instead": "Tā vietā liegt @{name}",
@@ -306,7 +307,7 @@
"empty_column.domain_blocks": "Vēl nav neviena bloķēta domēna.",
"empty_column.explore_statuses": "Pašlaik nav nekā aktuāla. Ieskaties šeit vēlāk!",
"empty_column.favourited_statuses": "Tev vēl nav izlasei pievienotu ierakstu. Kad pievienosi kādu, tas tiks parādīts šeit.",
- "empty_column.favourites": "Šo ierakstu vēl neviens nav pievienojis izlasei. Kad kāds to izdarīs, šeit parādīsies ieraksti.",
+ "empty_column.favourites": "Šo ierakstu vēl neviens nav pievienojis izlasei. Kad kāds to izdarīs, tas parādīsies šeit.",
"empty_column.follow_requests": "Šobrīd Tev nav sekošanas pieprasījumu. Kad saņemsi kādu, tas parādīsies šeit.",
"empty_column.followed_tags": "Tu vēl neseko nevienam tēmturim. Kad to izdarīsi, tie tiks parādīti šeit.",
"empty_column.hashtag": "Ar šo tēmturi nekas nav atrodams.",
@@ -398,6 +399,7 @@
"hints.profiles.see_more_posts": "Skatīt vairāk ierakstu {domain}",
"hints.threads.replies_may_be_missing": "Var trūkt atbilžu no citiem serveriem.",
"hints.threads.see_more": "Skatīt vairāk atbilžu {domain}",
+ "home.column_settings.show_quotes": "Rādīt citātus",
"home.column_settings.show_reblogs": "Rādīt pastiprinātos ierakstus",
"home.column_settings.show_replies": "Rādīt atbildes",
"home.hide_announcements": "Slēpt paziņojumus",
@@ -435,7 +437,7 @@
"keyboard_shortcuts.down": "Pārvietoties lejup sarakstā",
"keyboard_shortcuts.enter": "Atvērt ierakstu",
"keyboard_shortcuts.favourite": "Pievienot ierakstu izlasei",
- "keyboard_shortcuts.favourites": "Atvērt izlašu sarakstu",
+ "keyboard_shortcuts.favourites": "Atvērt izlases sarakstu",
"keyboard_shortcuts.federated": "Atvērt apvienoto laika līniju",
"keyboard_shortcuts.heading": "Īsinājumtaustiņi",
"keyboard_shortcuts.home": "Atvērt mājas laika līniju",
@@ -525,6 +527,9 @@
"notification.admin.report_statuses": "{name} ziņoja par {target} ar iemeslu: {category}",
"notification.admin.sign_up": "{name} pierakstījās",
"notification.favourite": "{name} pievienoja izlasei Tavu ierakstu",
+ "notification.favourite.name_and_others_with_link": "{name} un {count, plural, one {# cits} other {# citi}} pievienoja Tavu ierakstu izlasē",
+ "notification.favourite_pm": "{name} pievienoja izlasē Tavu privāto pieminējumu",
+ "notification.favourite_pm.name_and_others_with_link": "{name} un {count, plural, one {# cits} other {# citi}} pievienoja Tavu privāto pieminējumu izlasē",
"notification.follow": "{name} uzsāka Tev sekot",
"notification.follow_request": "{name} nosūtīja Tev sekošanas pieprasījumu",
"notification.mentioned_you": "{name} pieminēja jūs",
@@ -573,7 +578,7 @@
"notifications.column_settings.update": "Labojumi:",
"notifications.filter.all": "Visi",
"notifications.filter.boosts": "Pastiprinātie ieraksti",
- "notifications.filter.favourites": "Izlases",
+ "notifications.filter.favourites": "Izlase",
"notifications.filter.follows": "Seko",
"notifications.filter.mentions": "Pieminēšanas",
"notifications.filter.polls": "Aptaujas rezultāti",
@@ -768,6 +773,7 @@
"status.reblogs.empty": "Neviens vēl nav pastiprinājis šo ierakstu. Kad kāds to izdarīs, šeit tiks parādīti lietotāji.",
"status.redraft": "Dzēst un pārrakstīt",
"status.remove_bookmark": "Noņemt grāmatzīmi",
+ "status.remove_favourite": "Noņemt no izlases",
"status.replied_to": "Atbildēja {name}",
"status.reply": "Atbildēt",
"status.replyAll": "Atbildēt uz tematu",
diff --git a/app/javascript/mastodon/locales/nan.json b/app/javascript/mastodon/locales/nan.json
index c4722f87a6..38f0790499 100644
--- a/app/javascript/mastodon/locales/nan.json
+++ b/app/javascript/mastodon/locales/nan.json
@@ -1,6 +1,7 @@
{
"about.blocks": "Siū 管制 ê 服侍器",
"about.contact": "聯絡lâng:",
+ "about.default_locale": "預設",
"about.disclaimer": "Mastodon是自由、開放原始碼ê軟體,mā是Mastodon gGmbH ê商標。",
"about.domain_blocks.no_reason_available": "原因bē-tàng用",
"about.domain_blocks.preamble": "Mastodon一般ē允准lí看別ê fediverse 服侍器來ê聯絡人kap hām用者交流。Tsiah ê 是本服侍器建立ê例外。",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "有限制",
"about.domain_blocks.suspended.explanation": "Uì tsit ê服侍器來ê資料lóng bē處理、儲存á是交換,無可能kap tsit ê服侍器ê用者互動á是溝通。.",
"about.domain_blocks.suspended.title": "權限中止",
+ "about.language_label": "言語",
"about.not_available": "Tsit ê資訊bē-tàng tī tsit ê服侍器使用。",
"about.powered_by": "由 {mastodon} 提供ê非中心化社群媒體",
"about.rules": "服侍器ê規則",
@@ -308,6 +310,8 @@
"emoji_button.search_results": "Tshiau-tshuē ê結果",
"emoji_button.symbols": "符號",
"emoji_button.travel": "旅行kap地點",
+ "empty_column.account_featured.me": "Lí iáu無任何ê特色內容。Lí kám知影lí ē當kā lí tsia̍p-tsia̍p用ê hashtag,甚至是朋友ê口座揀做特色ê內容,khǹg佇lí ê個人資料內底?",
+ "empty_column.account_featured.other": "{acct} iáu無任何ê特色內容。Lí kám知影lí ē當kā lí tsia̍p-tsia̍p用ê hashtag,甚至是朋友ê口座揀做特色ê內容,khǹg佇lí ê個人資料內底?",
"empty_column.account_featured_other.unknown": "Tsit ê口座iáu無任何ê特色內容。",
"empty_column.account_hides_collections": "Tsit位用者選擇無愛公開tsit ê資訊",
"empty_column.account_suspended": "口座已經受停止",
@@ -341,6 +345,11 @@
"explore.trending_links": "新聞",
"explore.trending_statuses": "PO文",
"explore.trending_tags": "Hashtag",
+ "featured_carousel.header": "{count, plural, one {{counter} 篇} other {{counter} 篇}} 釘起來ê PO文",
+ "featured_carousel.next": "下tsi̍t ê",
+ "featured_carousel.post": "PO文",
+ "featured_carousel.previous": "頂tsi̍t ê",
+ "featured_carousel.slide": "{total} 內底ê {index}",
"filter_modal.added.context_mismatch_explanation": "Tsit ê過濾器類別bē當適用佇lí所接近使用ê PO文ê情境。若是lí mā beh佇tsit ê情境過濾tsit ê PO文,lí著編輯過濾器。.",
"filter_modal.added.context_mismatch_title": "本文無sio合!",
"filter_modal.added.expired_explanation": "Tsit ê過濾器類別過期ah,lí需要改到期ê日期來繼續用。",
@@ -421,6 +430,7 @@
"hints.profiles.see_more_posts": "佇 {domain} 看koh khah tsē ê PO文",
"hints.threads.replies_may_be_missing": "Tuì其他ê服侍器來ê回應可能有phah m̄見。",
"hints.threads.see_more": "佇 {domain} 看koh khah tsē ê回應",
+ "home.column_settings.show_quotes": "顯示引用",
"home.column_settings.show_reblogs": "顯示轉PO",
"home.column_settings.show_replies": "顯示回應",
"home.hide_announcements": "Khàm掉公告",
@@ -601,13 +611,13 @@
"notification.moderation_warning.action_none": "Lí ê口座有收著審核ê警告。",
"notification.moderation_warning.action_sensitive": "Tuì tsit-má開始,lí êPO文ē標做敏感ê內容。",
"notification.moderation_warning.action_silence": "Lí ê口座hōo lâng限制ah。",
- "notification.moderation_warning.action_suspend": "Lí ê口座已經受停權。",
+ "notification.moderation_warning.action_suspend": "Lí ê口座ê權限已經停止ah。",
"notification.own_poll": "Lí ê投票結束ah",
"notification.poll": "Lí bat投ê投票結束ah",
"notification.reblog": "{name} 轉送lí ê PO文",
"notification.reblog.name_and_others_with_link": "{name} kap{count, plural, other {另外 # ê lâng}} 轉送lí ê PO文",
"notification.relationships_severance_event": "Kap {name} ê結連無去",
- "notification.relationships_severance_event.account_suspension": "{from} ê管理員kā {target} 停權ah,意思是lí bē koh再接受tuì in 來ê更新,á是hām in互動。",
+ "notification.relationships_severance_event.account_suspension": "{from} ê管理員kā {target} 停止權限ah,意思是lí bē koh再接受tuì in 來ê更新,á是hām in互動。",
"notification.relationships_severance_event.domain_block": "{from} ê 管理員kā {target} 封鎖ah,包含 {followersCount} 位跟tuè lí ê lâng,kap {followingCount, plural, other {#}} 位lí跟tuè ê口座。",
"notification.relationships_severance_event.learn_more": "看詳細",
"notification.relationships_severance_event.user_domain_block": "Lí已經kā {target} 封鎖ah,ē suá走 {followersCount} 位跟tuè lí ê lâng,kap {followingCount, plural, other {#}} 位lí跟tuè ê口座。",
@@ -891,12 +901,34 @@
"status.translated_from_with": "用 {provider} 翻譯 {lang}",
"status.uncached_media_warning": "Bē當先看māi",
"status.unmute_conversation": "Kā對話取消消音",
+ "subscribed_languages.lead": "Tī改變了後,kan-ta所揀ê語言ê PO文tsiah ē顯示佇lí ê厝ê時間線kap列單。揀「無」來接受所有語言êPO文。",
+ "subscribed_languages.save": "儲存改變",
+ "subscribed_languages.target": "改 {target} ê訂ê語言",
"tabs_bar.home": "頭頁",
"tabs_bar.notifications": "通知",
"terms_of_service.effective_as_of": "{date} 起實施",
"terms_of_service.title": "服務規定",
"terms_of_service.upcoming_changes_on": "Ē tī {date} 改變",
"time_remaining.days": "Tshun {number, plural, other {# kang}}",
+ "time_remaining.hours": "Tshun {number, plural, other {# 點鐘}}",
+ "time_remaining.minutes": "Tshun {number, plural, other {# 分鐘}}",
+ "time_remaining.moments": "Tshun ê時間",
+ "time_remaining.seconds": "Tshun {number, plural, other {# 秒}}",
+ "trends.counter_by_accounts": "{count, plural, one {{counter} ê} other {{counter} ê}} lâng tī過去 {days, plural, one {kang} other {{days} kang}}內底",
+ "trends.trending_now": "Tsit-má ê趨勢",
+ "ui.beforeunload": "Nā離開Mastodon,lí ê草稿ē無去。",
+ "units.short.billion": "{count}B",
+ "units.short.million": "{count}M",
+ "units.short.thousand": "{count}K",
+ "upload_area.title": "Giú放來傳起去",
+ "upload_button.label": "加圖片、影片á是聲音檔",
+ "upload_error.limit": "超過檔案傳起去ê限制",
+ "upload_error.poll": "Bô允準佇投票ê時kā檔案傳起去。",
+ "upload_form.drag_and_drop.instructions": "Nā beh選媒體附件,請tshi̍h空白key á是Enter key。Giú ê時,請用方向key照指定ê方向suá媒體附件。Beh khǹg媒體附件佇伊ê新位置,請koh tshi̍h空白key á是Enter key,或者tshi̍h Esc key來取消。",
+ "upload_form.drag_and_drop.on_drag_cancel": "Suá位取消ah,媒體附件 {item} khǹg落來ah。",
+ "upload_form.drag_and_drop.on_drag_end": "媒體附件 {item} khǹg落來ah。",
+ "upload_form.drag_and_drop.on_drag_over": "媒體附件 {item} suá tín動ah。",
+ "upload_form.drag_and_drop.on_drag_start": "媒體附件 {item} 揀起來ah。",
"upload_form.edit": "編輯",
"upload_progress.label": "Teh傳起去……",
"upload_progress.processing": "Teh處理……",
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index 071b8812ec..e28506f4fa 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -430,6 +430,7 @@
"hints.profiles.see_more_posts": "Bekijk meer berichten op {domain}",
"hints.threads.replies_may_be_missing": "Antwoorden van andere servers kunnen ontbreken.",
"hints.threads.see_more": "Bekijk meer reacties op {domain}",
+ "home.column_settings.show_quotes": "Citaten tonen",
"home.column_settings.show_reblogs": "Boosts tonen",
"home.column_settings.show_replies": "Reacties tonen",
"home.hide_announcements": "Mededelingen verbergen",
diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json
index 56acc462fd..67bf02c1ec 100644
--- a/app/javascript/mastodon/locales/nn.json
+++ b/app/javascript/mastodon/locales/nn.json
@@ -1,6 +1,7 @@
{
"about.blocks": "Modererte tenarar",
"about.contact": "Kontakt:",
+ "about.default_locale": "Standard",
"about.disclaimer": "Mastodon er gratis programvare med open kjeldekode, og eit varemerke frå Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Årsaka er ikkje tilgjengeleg",
"about.domain_blocks.preamble": "Mastodon gjev deg som regel lov til å sjå innhald og samhandla med brukarar frå alle andre tenarar i allheimen. Dette er unntaka som er valde for akkurat denne tenaren.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "Avgrensa",
"about.domain_blocks.suspended.explanation": "Ingen data frå denne tenaren vert handsama, lagra eller sende til andre, noko som gjer det umogeleg å samhandla eller kommunisera med brukarar på denne tenaren.",
"about.domain_blocks.suspended.title": "Utestengd",
+ "about.language_label": "Språk",
"about.not_available": "Denne informasjonen er ikkje gjort tilgjengeleg på denne tenaren.",
"about.powered_by": "Desentraliserte sosiale medium drive av {mastodon}",
"about.rules": "Tenarreglar",
@@ -308,6 +310,8 @@
"emoji_button.search_results": "Søkeresultat",
"emoji_button.symbols": "Symbol",
"emoji_button.travel": "Reise & stader",
+ "empty_column.account_featured.me": "Du har ikkje valt ut noko enno. Visste du at du kan velja ut merkelappar du bruker mykje, og til og med venekontoar på profilen din?",
+ "empty_column.account_featured.other": "{acct} har ikkje valt ut noko enno. Visste du at du kan velja ut merkelappar du bruker mykje, og til og med venekontoar på profilen din?",
"empty_column.account_featured_other.unknown": "Denne kontoen har ikkje valt ut noko enno.",
"empty_column.account_hides_collections": "Denne brukaren har valt å ikkje gjere denne informasjonen tilgjengeleg",
"empty_column.account_suspended": "Kontoen er utestengd",
@@ -341,6 +345,11 @@
"explore.trending_links": "Nytt",
"explore.trending_statuses": "Innlegg",
"explore.trending_tags": "Emneknaggar",
+ "featured_carousel.header": "{count, plural, one {Festa innlegg} other {Festa innlegg}}",
+ "featured_carousel.next": "Neste",
+ "featured_carousel.post": "Innlegg",
+ "featured_carousel.previous": "Førre",
+ "featured_carousel.slide": "{index} av {total}",
"filter_modal.added.context_mismatch_explanation": "Denne filterkategorien gjeld ikkje i den samanhengen du har lese dette innlegget. Viss du vil at innlegget skal filtrerast i denne samanhengen òg, må du endra filteret.",
"filter_modal.added.context_mismatch_title": "Konteksten passar ikkje!",
"filter_modal.added.expired_explanation": "Denne filterkategorien har gått ut på dato. Du må endre best før datoen for at den skal gjelde.",
@@ -421,6 +430,7 @@
"hints.profiles.see_more_posts": "Sjå fleire innlegg på {domain}",
"hints.threads.replies_may_be_missing": "Svar frå andre tenarar manglar kanskje.",
"hints.threads.see_more": "Sjå fleire svar på {domain}",
+ "home.column_settings.show_quotes": "Vis sitat",
"home.column_settings.show_reblogs": "Vis framhevingar",
"home.column_settings.show_replies": "Vis svar",
"home.hide_announcements": "Skjul kunngjeringar",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index e09d6c6ea0..c9c8e794de 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -1,6 +1,7 @@
{
"about.blocks": "Modererte servere",
"about.contact": "Kontakt:",
+ "about.default_locale": "Standard",
"about.disclaimer": "Mastodon er gratis, åpen kildekode-programvare og et varemerke fra Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Årsak ikke tilgjengelig",
"about.domain_blocks.preamble": "Mastodon lar deg normalt sett se innholdet fra og samhandle med brukere fra enhver annen tjener i fødiverset. Dette er unntakene som har blitt lagt inn på denne tjeneren.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "Begrenset",
"about.domain_blocks.suspended.explanation": "Ikke noe innhold fra denne tjeneren vil bli behandlet, lagret eller utvekslet. Det gjør det umulig å samhandle eller kommunisere med brukere fra denne tjeneren.",
"about.domain_blocks.suspended.title": "Suspendert",
+ "about.language_label": "Språk",
"about.not_available": "Denne informasjonen er ikke gjort tilgjengelig på denne tjeneren.",
"about.powered_by": "Desentraliserte sosiale medier drevet av {mastodon}",
"about.rules": "Regler for serveren",
@@ -200,6 +202,7 @@
"compose_form.poll.duration": "Avstemningens varighet",
"compose_form.poll.multiple": "Flervalg",
"compose_form.poll.option_placeholder": "Valg {number}",
+ "compose_form.poll.single": "Enkelt valg",
"compose_form.poll.switch_to_multiple": "Endre avstemning til å tillate flere valg",
"compose_form.poll.switch_to_single": "Endre avstemning til å tillate ett valg",
"compose_form.poll.type": "Stil",
@@ -223,14 +226,23 @@
"confirmations.edit.confirm": "Redigér",
"confirmations.edit.message": "Å redigere nå vil overskrive meldingen du skriver for øyeblikket. Er du sikker på at du vil fortsette?",
"confirmations.edit.title": "Overskriv innlegg?",
+ "confirmations.follow_to_list.confirm": "Følg og legg til i liste",
+ "confirmations.follow_to_list.message": "Du må følge {name} for å kunne legge vedkommende til i en liste.",
+ "confirmations.follow_to_list.title": "Følg bruker?",
"confirmations.logout.confirm": "Logg ut",
"confirmations.logout.message": "Er du sikker på at du vil logge ut?",
"confirmations.logout.title": "Logg ut?",
+ "confirmations.missing_alt_text.confirm": "Legg til bildebeskrivelse",
+ "confirmations.missing_alt_text.message": "Innlegget ditt mangler bildebeskrivelse. Legg til en tekst for å gjøre innholdet ditt tilgjengelig for flere brukere.",
"confirmations.missing_alt_text.secondary": "Legg ut likevel",
+ "confirmations.missing_alt_text.title": "Legg til bildebeskrivelse?",
"confirmations.mute.confirm": "Demp",
"confirmations.redraft.confirm": "Slett og skriv på nytt",
"confirmations.redraft.message": "Er du sikker på at du vil slette dette innlegget og lagre det på nytt? Favoritter og fremhevinger vil gå tapt, og svar til det originale innlegget vil bli foreldreløse.",
"confirmations.redraft.title": "Slett og skriv på nytt?",
+ "confirmations.remove_from_followers.confirm": "Fjern følger",
+ "confirmations.remove_from_followers.message": "{name} vil ikke lenger følge deg. Er du sikker på at du vil fortsette?",
+ "confirmations.remove_from_followers.title": "Fjern følger?",
"confirmations.reply.confirm": "Svar",
"confirmations.reply.message": "Å svare nå vil overskrive meldingen du skriver for øyeblikket. Er du sikker på at du vil fortsette?",
"confirmations.reply.title": "Overskriv innlegg?",
@@ -247,7 +259,7 @@
"copy_icon_button.copied": "Kopiert til utklippstavlen",
"copypaste.copied": "Kopiert",
"copypaste.copy_to_clipboard": "Kopier til utklippstavle",
- "directory.federated": "Fra det kjente strømiverset",
+ "directory.federated": "Fra det kjente fødiverset",
"directory.local": "Kun fra {domain}",
"directory.new_arrivals": "Nye ankomster",
"directory.recently_active": "Nylig aktiv",
@@ -255,12 +267,18 @@
"disabled_account_banner.text": "Din konto {disabledAccount} er for øyeblikket deaktivert.",
"dismissable_banner.community_timeline": "Dette er de nyeste offentlige innleggene fra personer med kontoer på {domain}.",
"dismissable_banner.dismiss": "Avvis",
+ "dismissable_banner.explore_links": "Disse nyhetene snakker folk om akkurat nå på denne og andre servere i det desentraliserte nettverket.",
+ "dismissable_banner.explore_statuses": "Disse innleggene fra denne og andre servere i det desentraliserte nettverket får økt oppmerksomhet på denne serveren akkurat nå. Nyere innlegg med flere fremhevinger og favoritter er rangert høyere.",
+ "dismissable_banner.explore_tags": "Disse emneknaggene snakker folk om akkurat nå på det desentraliserte nettverket. Emneknagger brukt av flere rangeres høyere.",
+ "dismissable_banner.public_timeline": "Dette er de nyeste offentlige innleggene fra folk brukerne av {domain} følger på det desentraliserte nettverket.",
"domain_block_modal.block": "Blokker server",
"domain_block_modal.block_account_instead": "Blokker @{name} i stedet",
"domain_block_modal.they_can_interact_with_old_posts": "Personer fra denne serveren kan samhandle med dine gamle innlegg.",
"domain_block_modal.they_cant_follow": "Ingen fra denne serveren kan følge deg.",
"domain_block_modal.they_wont_know": "De kommer ikke til å få vite at du har valgt å blokkere dem.",
"domain_block_modal.title": "Blokker domenet?",
+ "domain_block_modal.you_will_lose_num_followers": "Du kommer til å miste {followersCount, plural, one {{followersCountDisplay} følger} other {{followersCountDisplay} følgere}} og {followingCount, plural, one {{followingCountDisplay} du følger} other {{followingCountDisplay} du følger}}.",
+ "domain_block_modal.you_will_lose_relationships": "Du vil miste alle følgere og folk du følger fra denne serveren.",
"domain_block_modal.you_wont_see_posts": "Du vil ikke se innlegg eller få varsler fra brukere på denne serveren.",
"domain_pill.activitypub_lets_connect": "Den lar deg koble til og samhandle med folk ikke bare på Mastodon, men også på tvers av forskjellige sosiale apper.",
"domain_pill.activitypub_like_language": "ActivityPub er liksom språket Mastodon snakker med andre sosiale nettverk.",
@@ -292,6 +310,9 @@
"emoji_button.search_results": "Søkeresultat",
"emoji_button.symbols": "Symboler",
"emoji_button.travel": "Reise & steder",
+ "empty_column.account_featured.me": "Du har ikke fremhevet noe ennå. Visste du at du kan fremheve emneknaggene du bruker mest, eller til og med dine venners profilsider?",
+ "empty_column.account_featured.other": "{acct} har ikke fremhevet noe ennå. Visste du at du kan fremheve emneknaggene du bruker mest, eller til og med dine venners profilsider?",
+ "empty_column.account_featured_other.unknown": "Denne kontoen har ikke fremhevet noe ennå.",
"empty_column.account_hides_collections": "Denne brukeren har valgt å ikke gjøre denne informasjonen tilgjengelig",
"empty_column.account_suspended": "Kontoen er suspendert",
"empty_column.account_timeline": "Ingen innlegg her!",
@@ -324,6 +345,11 @@
"explore.trending_links": "Nyheter",
"explore.trending_statuses": "Innlegg",
"explore.trending_tags": "Emneknagger",
+ "featured_carousel.header": "{count, plural, one {{counter} festet innlegg} other {{counter} festede innlegg}}",
+ "featured_carousel.next": "Neste",
+ "featured_carousel.post": "Innlegg",
+ "featured_carousel.previous": "Forrige",
+ "featured_carousel.slide": "{index} av {total}",
"filter_modal.added.context_mismatch_explanation": "Denne filterkategorien gjelder ikke for den konteksten du har åpnet dette innlegget i. Hvis du vil at innlegget skal filtreres i denne konteksten også, må du redigere filteret.",
"filter_modal.added.context_mismatch_title": "Feil sammenheng!",
"filter_modal.added.expired_explanation": "Denne filterkategorien er utløpt, du må endre utløpsdato for at den skal gjelde.",
@@ -340,6 +366,7 @@
"filter_modal.select_filter.subtitle": "Bruk en eksisterende kategori eller opprett en ny",
"filter_modal.select_filter.title": "Filtrer dette innlegget",
"filter_modal.title.status": "Filtrer et innlegg",
+ "filter_warning.matches_filter": "Treff på filter \"{title} \"",
"filtered_notifications_banner.pending_requests": "Fra {count, plural, =0 {ingen} one {en person} other {# folk}} du kanskje kjenner",
"filtered_notifications_banner.title": "Filtrerte varsler",
"firehose.all": "Alt",
@@ -373,6 +400,9 @@
"footer.status": "Status",
"generic.saved": "Lagret",
"getting_started.heading": "Kom i gang",
+ "hashtag.admin_moderation": "Åpne modereringsgrensesnitt for #{name}",
+ "hashtag.browse": "Utforsk poster i #{hashtag}",
+ "hashtag.browse_from_account": "Utforsk poster av @{name} i #{hashtag}",
"hashtag.column_header.tag_mode.all": "og {additional}",
"hashtag.column_header.tag_mode.any": "eller {additional}",
"hashtag.column_header.tag_mode.none": "uten {additional}",
@@ -385,7 +415,10 @@
"hashtag.counter_by_accounts": "{count, plural, one {{counter} deltaker} other {{counter} deltakere}}",
"hashtag.counter_by_uses": "{count, plural, one {ett innlegg} other {{counter} innlegg}}",
"hashtag.counter_by_uses_today": "{count, plural, one {ett innlegg} other {{counter} innlegg}} i dag",
+ "hashtag.feature": "Fremhev på din profil",
"hashtag.follow": "Følg emneknagg",
+ "hashtag.mute": "Demp #{hashtag}",
+ "hashtag.unfeature": "Ikke fremhev på din profil",
"hashtag.unfollow": "Slutt å følge emneknagg",
"hashtags.and_other": "…og {count, plural, one{en til} other {# til}}",
"hints.profiles.followers_may_be_missing": "Følgere for denne profilen mangler kanskje.",
@@ -396,6 +429,7 @@
"hints.profiles.see_more_posts": "Se flere innlegg på {domain}",
"hints.threads.replies_may_be_missing": "Svar fra andre servere mangler kanskje.",
"hints.threads.see_more": "Se flere svar på {domain}",
+ "home.column_settings.show_quotes": "Vis sitater",
"home.column_settings.show_reblogs": "Vis fremhevinger",
"home.column_settings.show_replies": "Vis svar",
"home.hide_announcements": "Skjul kunngjøring",
@@ -415,6 +449,11 @@
"ignore_notifications_modal.not_following_title": "Overse varsler fra folk du ikke følger?",
"ignore_notifications_modal.private_mentions_title": "Overse varsler fra uoppfordrede private omtaler?",
"info_button.label": "Hjelp",
+ "info_button.what_is_alt_text": "Hva er alternativ tekst? Alternativ tekst gir en bildebeskrivelse til folk med nedsatt syn, lav båndbredde eller de som vil ha mer kontekst.
Du kan forbedre tilgjengeligheten og forståelsen for alle ved å skive en tydelig, konsis og objektiv alternativ tekst.
Få med deg viktige elementer Summér tekst i bilder Bruk vanlige setningsstrukturer Unngå overflødig informasjon Fokuser på trender og viktige funn i komplekse grafiske fremstillinger (som diagram og kart) ",
+ "interaction_modal.action.favourite": "Favoriser fra din konto for å fortsette.",
+ "interaction_modal.action.follow": "Følg fra din konto for å fortsette.",
+ "interaction_modal.action.reply": "Svar fra kontoen din for å fortsette.",
+ "interaction_modal.action.vote": "Stem fra kontoen din for å fortsette.",
"interaction_modal.go": "Gå",
"interaction_modal.no_account_yet": "Har du ikke en konto ennå?",
"interaction_modal.on_another_server": "På en annen server",
@@ -423,6 +462,8 @@
"interaction_modal.title.follow": "Følg {name}",
"interaction_modal.title.reblog": "Fremhev {name} sitt innlegg",
"interaction_modal.title.reply": "Svar på {name} sitt innlegg",
+ "interaction_modal.title.vote": "Stem på {name}s avstemning",
+ "interaction_modal.username_prompt": "For eksempel {example}",
"intervals.full.days": "{number, plural,one {# dag} other {# dager}}",
"intervals.full.hours": "{number, plural, one {# time} other {# timer}}",
"intervals.full.minutes": "{number, plural, one {# minutt} other {# minutter}}",
@@ -458,6 +499,7 @@
"keyboard_shortcuts.toggle_hidden": "Vis/skjul tekst bak innholdsvarsel",
"keyboard_shortcuts.toggle_sensitivity": "Vis/skjul media",
"keyboard_shortcuts.toot": "Start et nytt innlegg",
+ "keyboard_shortcuts.translate": "for å oversette et innlegg",
"keyboard_shortcuts.unfocus": "Fjern fokus fra komponerings-/søkefeltet",
"keyboard_shortcuts.up": "Flytt oppover i listen",
"lightbox.close": "Lukk",
@@ -470,10 +512,18 @@
"link_preview.shares": "{count, plural, one {{counter} innlegg} other {{counter} innlegg}}",
"lists.add_member": "Legg til",
"lists.add_to_list": "Legg til i listen",
+ "lists.add_to_lists": "Legg til {name} i liste",
"lists.create": "Opprett",
+ "lists.create_a_list_to_organize": "Lag en ny liste for å organisere hjemmetidslinjen",
+ "lists.create_list": "Lag liste",
"lists.delete": "Slett listen",
"lists.done": "Ferdig",
"lists.edit": "Rediger listen",
+ "lists.find_users_to_add": "Fin brukere å legge til",
+ "lists.list_name": "Listenavn",
+ "lists.new_list_name": "Nytt listenavn",
+ "lists.no_lists_yet": "Ingen lister ennå.",
+ "lists.no_members_yet": "Ingen medlemmer ennå.",
"lists.no_results_found": "Ingen treff.",
"lists.remove_member": "Fjern",
"lists.replies_policy.followed": "Enhver fulgt bruker",
@@ -524,15 +574,24 @@
"not_signed_in_indicator.not_signed_in": "Du må logge inn for å få tilgang til denne ressursen.",
"notification.admin.report": "{name} rapporterte {target}",
"notification.admin.report_account": "{name} rapporterte {count, plural, one {et innlegg} other {# innlegg}} fra {target} for {category}",
+ "notification.admin.report_statuses": "{name} rapporterte {target} for {category}",
+ "notification.admin.report_statuses_other": "{name} rapporterte {target}",
"notification.admin.sign_up": "{name} registrerte seg",
"notification.favourite": "{name} favorittmarkerte innlegget ditt",
"notification.follow": "{name} fulgte deg",
"notification.follow_request": "{name} har bedt om å få følge deg",
"notification.label.mention": "Nevn",
+ "notification.label.private_mention": "Private omtaler",
+ "notification.label.private_reply": "Private svar",
"notification.label.reply": "Svar",
"notification.mention": "Nevn",
"notification.mentioned_you": "{name} nevnte deg",
"notification.moderation-warning.learn_more": "Lær mer",
+ "notification.moderation_warning": "Du har fått en advarsel fra en moderator",
+ "notification.moderation_warning.action_delete_statuses": "Noen av innleggene dine har blitt fjernet.",
+ "notification.moderation_warning.action_disable": "Kontoen din har blitt deaktivert.",
+ "notification.moderation_warning.action_mark_statuses_as_sensitive": "Noen av innleggene dine har blitt markert som sensitive.",
+ "notification.moderation_warning.action_none": "Kontoen din har fått en advarsel fra en moderator.",
"notification.own_poll": "Avstemningen din er ferdig",
"notification.reblog": "{name} fremhevet ditt innlegg",
"notification.relationships_severance_event.learn_more": "Lær mer",
@@ -542,7 +601,11 @@
"notification_requests.dismiss": "Lukk",
"notification_requests.edit_selection": "Redigér",
"notification_requests.exit_selection": "Ferdig",
+ "notification_requests.explainer_for_limited_remote_account": "Varsler fra denne kontoen har blitt filtrert bort fordi kontoen eller serveren den er på har blitt sensurert av en moderator.",
+ "notification_requests.maximize": "Maksimer",
"notification_requests.minimize_banner": "Minimer banneret for filtrerte varsler",
+ "notification_requests.notifications_from": "Varsler fra {name}",
+ "notification_requests.title": "Filtrerte varlser",
"notification_requests.view": "Vis varsler",
"notifications.clear": "Fjern varsler",
"notifications.clear_confirmation": "Er du sikker på at du vil fjerne alle dine varsler permanent?",
@@ -602,6 +665,7 @@
"onboarding.follows.done": "Ferdig",
"onboarding.follows.empty": "Dessverre kan ingen resultater vises akkurat nå. Du kan prøve å bruke søk eller bla gjennom utforske-siden for å finne folk å følge, eller prøve igjen senere.",
"onboarding.follows.search": "Søk",
+ "onboarding.follows.title": "Følg folk for å komme i gang",
"onboarding.profile.discoverable": "Gjør min profil synlig",
"onboarding.profile.display_name": "Visningsnavn",
"onboarding.profile.display_name_hint": "Ditt fulle navn eller ditt morsomme navn…",
@@ -695,6 +759,7 @@
"report_notification.categories.other": "Annet",
"report_notification.categories.other_sentence": "annet",
"report_notification.categories.spam": "Søppelpost",
+ "report_notification.categories.spam_sentence": "spam",
"report_notification.categories.violation": "Regelbrudd",
"report_notification.open": "Åpne rapport",
"search.no_recent_searches": "Ingen søk nylig",
@@ -719,11 +784,14 @@
"search_results.no_results": "Ingen resultater.",
"search_results.see_all": "Se alle",
"search_results.statuses": "Innlegg",
+ "search_results.title": "Søk etter \"{q}\"",
"server_banner.about_active_users": "Personer som har brukt denne serveren i løpet av de siste 30 dagene (aktive brukere månedlig)",
"server_banner.active_users": "aktive brukere",
"server_banner.administered_by": "Administrert av:",
+ "server_banner.is_one_of_many": "{domain} er en av mange uavhengige Mastodon-servere du kan bruke for å delta i det desentraliserte sosiale nettet.",
"server_banner.server_stats": "Serverstatistikk:",
"sign_in_banner.create_account": "Opprett konto",
+ "sign_in_banner.follow_anyone": "Følg hvem som helst på tvers av det desentraliserte sosiale nettet. Ingen algoritmer, reklamer eller clickbait.",
"sign_in_banner.sign_in": "Logg inn",
"sign_in_banner.sso_redirect": "Logg inn eller registrer deg",
"status.admin_account": "Åpne moderatorgrensesnittet for @{name}",
@@ -733,12 +801,14 @@
"status.bookmark": "Bokmerke",
"status.cancel_reblog_private": "Fjern fremheving",
"status.cannot_reblog": "Denne posten kan ikke fremheves",
+ "status.continued_thread": "Fortsettelse av samtale",
"status.copy": "Kopier lenken til innlegget",
"status.delete": "Slett",
"status.detailed_status": "Detaljert samtalevisning",
"status.direct": "Nevn @{name} privat",
"status.direct_indicator": "Privat omtale",
"status.edit": "Rediger",
+ "status.edited": "Sist endret {date}",
"status.edited_x_times": "Redigert {count, plural,one {{count} gang} other {{count} ganger}}",
"status.favourite": "Favoritt",
"status.filter": "Filtrer dette innlegget",
@@ -754,6 +824,10 @@
"status.mute_conversation": "Demp samtale",
"status.open": "Utvid dette innlegget",
"status.pin": "Fest på profilen",
+ "status.quote_error.filtered": "Skjult på grunn av et av filterne dine",
+ "status.quote_error.not_found": "Dette innlegget kan ikke vises.",
+ "status.quote_error.pending_approval": "Dette innlegget venter på godkjenning fra den opprinnelige forfatteren.",
+ "status.quote_error.rejected": "Dette innlegget kan ikke vises fordi den opprinnelige forfatteren ikke har tillatt at det blir sitert.",
"status.read_more": "Les mer",
"status.reblog": "Fremhev",
"status.reblog_private": "Fremhev til det opprinnelige publikummet",
diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json
index 7755267c35..d3078108d2 100644
--- a/app/javascript/mastodon/locales/pt-PT.json
+++ b/app/javascript/mastodon/locales/pt-PT.json
@@ -1,13 +1,15 @@
{
"about.blocks": "Servidores moderados",
"about.contact": "Contacto:",
- "about.disclaimer": "O Mastodon é um software livre, de código aberto e uma marca registada de Mastodon gGmbH.",
+ "about.default_locale": "Padrão",
+ "about.disclaimer": "O Mastodon é um ‘software’ livre, de código aberto e uma marca registada de Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Motivo não disponível",
- "about.domain_blocks.preamble": "O Mastodon geralmente permite ver e interagir com o conteúdo de utilizadores de qualquer outra instância no fediverso. Estas são as exceções desta instância em específico.",
+ "about.domain_blocks.preamble": "O Mastodon ver e interagir com o conteúdo de utilizadores de qualquer outra instância no fediverso. Estas são as exceções desta instância em específico.",
"about.domain_blocks.silenced.explanation": "Normalmente não verás perfis e conteúdos deste servidor, a não ser que os procures explicitamente ou optes por segui-los.",
"about.domain_blocks.silenced.title": "Limitados",
"about.domain_blocks.suspended.explanation": "Nenhum dado deste servidor será processado, armazenado ou trocado, tornando impossível qualquer interação ou comunicação com os utilizadores a partir deste servidor.",
"about.domain_blocks.suspended.title": "Suspensos",
+ "about.language_label": "Idioma",
"about.not_available": "Esta informação não foi disponibilizada neste servidor.",
"about.powered_by": "Rede social descentralizada baseada no {mastodon}",
"about.rules": "Regras do servidor",
@@ -130,8 +132,8 @@
"block_modal.remote_users_caveat": "Vamos pedir ao servidor {domain} para respeitar a tua decisão. No entanto, não é garantido o seu cumprimento, uma vez que alguns servidores podem tratar os bloqueios de forma diferente. As publicações públicas podem continuar a ser visíveis para utilizadores não autenticados.",
"block_modal.show_less": "Mostrar menos",
"block_modal.show_more": "Mostrar mais",
- "block_modal.they_cant_mention": "Ele não te pode mencionar nem seguir.",
- "block_modal.they_cant_see_posts": "Não verás as publicações dele e ele não poderá ver as tuas publicações.",
+ "block_modal.they_cant_mention": "Ele não o pode mencionar nem seguir.",
+ "block_modal.they_cant_see_posts": "Eles não podem ver as suas publicações e você não verá as deles.",
"block_modal.they_will_know": "Ele pode ver que o bloqueaste.",
"block_modal.title": "Bloquear utilizador?",
"block_modal.you_wont_see_mentions": "Não verás publicações que mencionem este utilizador.",
@@ -308,6 +310,8 @@
"emoji_button.search_results": "Resultados da pesquisa",
"emoji_button.symbols": "Símbolos",
"emoji_button.travel": "Viagens e lugares",
+ "empty_column.account_featured.me": "Ainda não colocou nada em destaque. Sabia que pode destacar as etiquetas que mais utiliza e até as contas dos seus amigos no seu perfil?",
+ "empty_column.account_featured.other": "{acct} ainda não colocou nada em destaque. Sabia que pode destacar as etiquetas que mais utiliza e até as contas dos seus amigos no seu perfil?",
"empty_column.account_featured_other.unknown": "Esta conta ainda não colocou nada em destaque.",
"empty_column.account_hides_collections": "Este utilizador escolheu não disponibilizar esta informação",
"empty_column.account_suspended": "Conta suspensa",
@@ -341,6 +345,11 @@
"explore.trending_links": "Notícias",
"explore.trending_statuses": "Publicações",
"explore.trending_tags": "#Etiquetas",
+ "featured_carousel.header": "{count, plural, one {Publicação Afixada} other {Publicações Afixadas}}",
+ "featured_carousel.next": "Seguinte",
+ "featured_carousel.post": "Publicação",
+ "featured_carousel.previous": "Anterior",
+ "featured_carousel.slide": "{index} de {total}",
"filter_modal.added.context_mismatch_explanation": "Esta categoria de filtro não se aplica ao contexto em que acedeste a esta publicação. Se pretenderes que esta publicação seja filtrada também neste contexto, terás que editar o filtro.",
"filter_modal.added.context_mismatch_title": "O contexto não coincide!",
"filter_modal.added.expired_explanation": "Esta categoria de filtro expirou, tens de alterar a data de validade para que ele seja aplicado.",
@@ -421,6 +430,7 @@
"hints.profiles.see_more_posts": "Ver mais publicações em {domain}",
"hints.threads.replies_may_be_missing": "É possível que não estejam a ser mostradas todas as respostas de outros servidores.",
"hints.threads.see_more": "Ver mais respostas em {domain}",
+ "home.column_settings.show_quotes": "Mostrar citações",
"home.column_settings.show_reblogs": "Mostrar impulsos",
"home.column_settings.show_replies": "Mostrar respostas",
"home.hide_announcements": "Ocultar mensagens de manutenção",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index 1a800189b2..d69be9ed9c 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -8,6 +8,7 @@
"about.domain_blocks.silenced.title": "Obmedzený",
"about.domain_blocks.suspended.explanation": "Žiadne údaje z tohto servera nebudú spracovávané, ukladané ani vymieňané, čo znemožní akúkoľvek interakciu alebo komunikáciu s používateľmi z tohto servera.",
"about.domain_blocks.suspended.title": "Vylúčený",
+ "about.language_label": "Jazyk",
"about.not_available": "Tieto informácie neboli sprístupnené na tomto serveri.",
"about.powered_by": "Decentralizovaná sociálna sieť na základe technológie {mastodon}",
"about.rules": "Pravidlá servera",
@@ -53,6 +54,7 @@
"account.mute_notifications_short": "Stíšiť upozornenia",
"account.mute_short": "Stíšiť",
"account.muted": "Účet stíšený",
+ "account.mutual": "Nasledujete sa navzájom",
"account.no_bio": "Nie je uvedený žiadny popis.",
"account.open_original_page": "Otvoriť pôvodnú stránku",
"account.posts": "Príspevky",
diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json
index 7eb8c1744f..92cca9e41d 100644
--- a/app/javascript/mastodon/locales/sq.json
+++ b/app/javascript/mastodon/locales/sq.json
@@ -1,6 +1,7 @@
{
"about.blocks": "Shërbyes të moderuar",
"about.contact": "Kontakt:",
+ "about.default_locale": "Parazgjedhje",
"about.disclaimer": "Mastodon-i është software i lirë, me burim të hapët dhe shenjë tregtare e Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "S’ka arsye",
"about.domain_blocks.preamble": "Mastodon-i ju lë përgjithësisht të shihni lëndë prej përdoruesish dhe të ndërveproni me ta nga cilido shërbyes tjetër qofshin në fedivers. Ka përjashtime që janë bërë në këtë shërbyes të dhënë.",
@@ -8,6 +9,7 @@
"about.domain_blocks.silenced.title": "E kufizuar",
"about.domain_blocks.suspended.explanation": "S’do të përpunohen, depozitohen apo shkëmbehen të dhëna të këtij shërbyesi, duke e bërë të pamundur çfarëdo ndërveprimi apo komunikimi me përdorues nga ky shërbyes.",
"about.domain_blocks.suspended.title": "E pezulluar",
+ "about.language_label": "Gjuhë",
"about.not_available": "Ky informacion, në këtë shërbyes, nuk jepet.",
"about.powered_by": "Media shoqërore e decentralizuar, bazuar në {mastodon}",
"about.rules": "Rregulla shërbyesi",
@@ -423,6 +425,7 @@
"hints.profiles.see_more_posts": "Shihni më tepër postime në {domain}",
"hints.threads.replies_may_be_missing": "Mund të mungojnë përgjigje nga shërbyes të tjerë.",
"hints.threads.see_more": "Shihni më tepër përgjigje në {domain}",
+ "home.column_settings.show_quotes": "Shfaq thonjëza",
"home.column_settings.show_reblogs": "Shfaq përforcime",
"home.column_settings.show_replies": "Shfaq përgjigje",
"home.hide_announcements": "Fshihi lajmërimet",
diff --git a/app/javascript/mastodon/locales/tok.json b/app/javascript/mastodon/locales/tok.json
index 99a280a8ed..696954eea3 100644
--- a/app/javascript/mastodon/locales/tok.json
+++ b/app/javascript/mastodon/locales/tok.json
@@ -8,6 +8,7 @@
"about.domain_blocks.silenced.title": "ken lukin lili ",
"about.domain_blocks.suspended.explanation": "sona ale pi ma ni li kama pali ala, li kama esun ala, li kama awen ala la sina ken ala toki tawa jan pi ma ni.",
"about.domain_blocks.suspended.title": "weka",
+ "about.language_label": "toki",
"about.not_available": "lon kulupu ni la sina ken alasa ala e sona ni.",
"about.powered_by": "lipu kulupu pi jan lawa mute tan {mastodon}",
"about.rules": "lawa kulupu",
@@ -15,37 +16,47 @@
"account.add_or_remove_from_list": "o ante e lipu jan",
"account.badges.bot": "ilo nanpa li lawa e ni",
"account.badges.group": "kulupu",
- "account.block": "o weka e @{name}",
- "account.block_domain": "o weka e ma {domain}",
- "account.block_short": "o weka e jan tawa mi",
- "account.blocked": "jan li weka tawa mi",
+ "account.block": "o len e @{name}",
+ "account.block_domain": "o len e ma {domain}",
+ "account.block_short": "o len",
+ "account.blocked": "jan li len",
+ "account.blocking": "mi len e jan ni",
"account.cancel_follow_request": "o kute ala",
"account.copy": "o pali same e linja pi lipu jan",
"account.direct": "len la o mu e @{name}",
"account.disable_notifications": "@{name} li toki la o mu ala e mi",
+ "account.domain_blocking": "mi len e ma ni",
"account.edit_profile": "o ante e lipu mi",
"account.enable_notifications": "@{name} li toki la o toki e toki ona tawa mi",
"account.endorse": "lipu jan la o suli e ni",
- "account.featured_tags.last_status_at": "sitelen pini pi jan ni li lon tenpo {date}",
+ "account.familiar_followers_many": "{name1} en {name2} en {othersCount, plural, other {jan ante #}} li kute e jan ni",
+ "account.familiar_followers_one": "{name1} li kute e jan ni",
+ "account.familiar_followers_two": "{name1} en {name2} li kute e jan ni",
+ "account.featured": "suli",
+ "account.featured.accounts": "lipu jan",
+ "account.featured.hashtags": "kulupu lipu",
+ "account.featured_tags.last_status_at": "sitelen pini pi jan ni li tan {date}",
"account.featured_tags.last_status_never": "toki ala li lon",
"account.follow": "o kute",
"account.follow_back": "jan ni li kute e sina. o kute",
"account.followers": "jan kute",
"account.followers.empty": "jan ala li kute e jan ni",
"account.followers_counter": "{count, plural, other {jan {counter} li kute e ona}}",
+ "account.followers_you_know_counter": "jan {counter} pi kute sama",
"account.following": "sina kute e jan ni",
"account.following_counter": "{count, plural, other {ona li kute e jan {counter}}}",
"account.follows.empty": "jan ni li kute e jan ala",
+ "account.follows_you": "ona li kute e sina",
"account.go_to_profile": "o tawa lipu jan",
"account.hide_reblogs": "o lukin ala e pana toki tan @{name}",
"account.in_memoriam": "jan ni li moli. pona o tawa ona.",
- "account.joined_short": "li kama",
+ "account.joined_short": "ona li kama lon tenpo",
"account.languages": "sina wile lukin e sitelen pi toki seme",
"account.link_verified_on": "{date} la mi sona e ni: jan seme li jo e lipu ni",
"account.locked_info": "sina wile kute e jan ni la ona o toki e ken",
"account.media": "sitelen",
- "account.mention": "o toki e jan @{name}",
- "account.moved_to": "lipu jan sin pi jan {name} li ni:",
+ "account.mention": "o mu e jan @{name}",
+ "account.moved_to": "jan ni la lipu sin li ni:",
"account.mute": "o len e @{name}",
"account.mute_notifications_short": "o kute ala e mu tan jan ni",
"account.mute_short": "o kute ala",
@@ -58,11 +69,12 @@
"account.requested": "jan ni o ken e kute sina",
"account.requested_follow": "jan {name} li wile kute e sina",
"account.share": "o pana e lipu jan @{name}",
- "account.show_reblogs": "o lukin e pana toki tan @{name}",
+ "account.show_reblogs": "o lukin e toki sike tan @{name}",
"account.statuses_counter": "{count, plural, other {toki {counter}}}",
- "account.unblock": "o weka ala e jan {name}",
- "account.unblock_domain": "o weka ala e ma {domain}",
- "account.unblock_short": "o pini weka",
+ "account.unblock": "o len ala e jan {name}",
+ "account.unblock_domain": "o len ala e ma {domain}",
+ "account.unblock_domain_short": "o len ala e jan ni",
+ "account.unblock_short": "o len ala",
"account.unendorse": "lipu jan la o suli ala e ni",
"account.unfollow": "o kute ala",
"account.unmute": "o len ala e @{name}",
@@ -72,7 +84,7 @@
"admin.dashboard.daily_retention": "nanpa pi awen jan lon tenpo suno",
"admin.dashboard.monthly_retention": "nanpa pi awen jan lon tenpo mun",
"admin.dashboard.retention.average": "sama",
- "admin.dashboard.retention.cohort": "tenpo mun open",
+ "admin.dashboard.retention.cohort": "kama sijelo la tenpo mun",
"admin.dashboard.retention.cohort_size": "jan sin",
"admin.impact_report.instance_accounts": "ni li pakala li weka e lipu jan ni",
"admin.impact_report.instance_followers": "jan pi ma mi li weka tan jan kute ni",
@@ -82,14 +94,14 @@
"alert.rate_limited.title": "ilo ni li lili e ken sina",
"alert.unexpected.message": "pakala li lon",
"alert.unexpected.title": "pakala a!",
- "alt_text_badge.title": "toki sona sitelen",
+ "alt_text_badge.title": "toki pi sona lukin",
"alt_text_modal.add_alt_text": "o pana e toki pi sona lukin",
- "alt_text_modal.add_text_from_image": "o kama jo e toki sitelen tan sitelen ni",
- "alt_text_modal.cancel": "weka",
+ "alt_text_modal.add_text_from_image": "o pana e nimi tan sitelen ni",
+ "alt_text_modal.cancel": "o weka",
"alt_text_modal.change_thumbnail": "o ante e sitelen lili",
"alt_text_modal.describe_for_people_with_hearing_impairments": "jan li ken ala kute la o pana e toki pi sona kalama…",
"alt_text_modal.describe_for_people_with_visual_impairments": "jan li ken ala lukin la o pana e toki pi sona lukin…",
- "alt_text_modal.done": "pini",
+ "alt_text_modal.done": "o pana",
"announcement.announcement": "toki suli",
"annual_report.summary.archetype.booster": "jan ni li alasa e pona",
"annual_report.summary.archetype.lurker": "jan ni li lukin taso",
@@ -103,6 +115,8 @@
"annual_report.summary.highlighted_post.by_reblogs": "toki pi sike nanpa wan",
"annual_report.summary.highlighted_post.by_replies": "toki li jo e toki kama pi nanpa wan",
"annual_report.summary.highlighted_post.possessive": "tan jan {name}",
+ "annual_report.summary.most_used_app.most_used_app": "ilo pi kepeken suli",
+ "annual_report.summary.most_used_hashtag.most_used_hashtag": "kulupu toki pi kepeken suli",
"annual_report.summary.most_used_hashtag.none": "ala",
"annual_report.summary.new_posts.new_posts": "toki suli sin",
"annual_report.summary.percentile.text": "ni la sina nanpa sewi pi jan ale lon {domain}. ",
@@ -111,17 +125,17 @@
"attachments_list.unprocessed": "(nasin open)",
"audio.hide": "o len e kalama",
"block_modal.remote_users_caveat": "mi pana e wile sina tawa ma {domain}. taso, o sona: ma li ken kepeken nasin len ante la pakala li ken lon. toki pi lukin ale la jan pi ma ala li ken lukin.",
- "block_modal.show_less": "o pana e lili",
- "block_modal.show_more": "o pana e mute",
+ "block_modal.show_less": "o lili e toki",
+ "block_modal.show_more": "o suli e toki",
"block_modal.they_cant_mention": "ona li ken ala toki tawa sina li ken ala kute e sina.",
"block_modal.they_cant_see_posts": "ona li ken ala lukin e toki sina. sina ken ala lukin e toki ona.",
- "block_modal.they_will_know": "ona li sona e ni: sina weka e lukin ona.",
- "block_modal.title": "o weka ala weka e jan",
+ "block_modal.they_will_know": "ona li ken sona e ni: sina len e ona.",
+ "block_modal.title": "o len ala len e jan?",
"block_modal.you_wont_see_mentions": "jan li toki e nimi ona la sina lukin ala e toki ni.",
"boost_modal.combo": "sina ken luka e nena {combo} tawa ni: sina wile ala luka e nena lon tenpo kama",
"boost_modal.reblog": "o wawa ala wawa e toki?",
"boost_modal.undo_reblog": "o weka ala weka e wawa toki?",
- "bundle_column_error.copy_stacktrace": "o awen e sona pakala lon ilo sina",
+ "bundle_column_error.copy_stacktrace": "o jo e sona pakala lon ilo sina",
"bundle_column_error.error.body": "ilo li ken ala pana e lipu ni. ni li ken tan pakala ilo.",
"bundle_column_error.error.title": "pakala a!",
"bundle_column_error.network.body": "mi lukin pana e lipu la, pakala li lon. ken la, pakala li tan ilo nanpa sina. ken la, pakala li tan ilo nanpa suli pi ma kulupu ni.",
@@ -129,7 +143,7 @@
"bundle_column_error.retry": "o alasa sin",
"bundle_column_error.return": "o tawa open",
"bundle_column_error.routing.body": "ilo li sona ala e lipu wile. sina pana ala pana e nasin pona tawa lipu?",
- "bundle_column_error.routing.title": "pakala nanpa 404",
+ "bundle_column_error.routing.title": "pakala #404",
"bundle_modal_error.close": "o pini",
"bundle_modal_error.message": "ilo li wile kama e ijo ni, taso pakala li lon.",
"bundle_modal_error.retry": "o alasa sin",
@@ -139,13 +153,13 @@
"closed_registrations_modal.preamble": "ilo Masoton li lon ilo wan ala. sina kepeken ma ante la sina ken lukin li ken kute e jan pi ma ni. sina wile la, sina ken pali e ma sin!",
"closed_registrations_modal.title": "sina kama lon kulupu Masoton",
"column.about": "sona",
- "column.blocks": "kulupu pi jan weka",
+ "column.blocks": "jan len",
"column.bookmarks": "awen toki",
"column.community": "linja tenpo pi ma ni",
"column.create_list": "o pali e kulupu",
"column.direct": "mu len",
"column.directory": "o lukin e jan",
- "column.domain_blocks": "ma pi wile ala lukin",
+ "column.domain_blocks": "ma len",
"column.edit_list": "o ante e kulupu",
"column.favourites": "ijo pona",
"column.firehose": "toki pi tenpo ni",
@@ -174,27 +188,27 @@
"compose.published.body": "toki li pana.",
"compose.published.open": "o lukin",
"compose.saved.body": "ilo li awen e ijo pana sina.",
- "compose_form.direct_message_warning_learn_more": "o kama sona e ijo ante",
+ "compose_form.direct_message_warning_learn_more": "o kama sona",
"compose_form.encryption_warning": "toki li len ala lon ilo Masoton ꞏ o pana ala e sona suli len lon ilo Masoton",
"compose_form.lock_disclaimer": "lipu sina li open, li {locked} ala. jan ale li ken kama kute e sina, li ken lukin e toki sama ni.",
"compose_form.lock_disclaimer.lock": "pini",
"compose_form.placeholder": "sina wile toki e seme?",
"compose_form.poll.duration": "tenpo pana",
- "compose_form.poll.multiple": "pana mute",
+ "compose_form.poll.multiple": "mute pana",
"compose_form.poll.option_placeholder": "ken nanpa {number}",
- "compose_form.poll.single": "toki pi wan taso",
+ "compose_form.poll.single": "ken pi wan taso",
"compose_form.poll.switch_to_multiple": "o ante e nasin pana. pana mute o ken",
"compose_form.poll.switch_to_single": "o ante e nasin pana. pana wan taso o lon",
"compose_form.poll.type": "nasin",
"compose_form.publish": "o toki",
- "compose_form.publish_form": "o open toki sin",
+ "compose_form.publish_form": "o toki sin",
"compose_form.reply": "o toki lon ijo ni",
- "compose_form.save_changes": "o sin e ni",
+ "compose_form.save_changes": "o sin",
"compose_form.spoiler.marked": "o weka e toki pi ijo ike ken",
"compose_form.spoiler.unmarked": "o pali e toki pi ijo ike ken",
"compose_form.spoiler_placeholder": "toki pi ijo ike ken (sina ken ala e ni)",
- "confirmation_modal.cancel": "o weka",
- "confirmations.block.confirm": "o weka",
+ "confirmation_modal.cancel": "ala",
+ "confirmations.block.confirm": "o len",
"confirmations.delete.confirm": "o weka",
"confirmations.delete.message": "sina wile ala wile weka e toki ni?",
"confirmations.delete.title": "o weka ala weka e toki?",
@@ -211,46 +225,57 @@
"confirmations.follow_to_list.title": "sina wile ala wile kute?",
"confirmations.logout.confirm": "o weka",
"confirmations.logout.message": "sina wile ala wile weka",
- "confirmations.logout.title": "o weka?",
- "confirmations.missing_alt_text.confirm": "pana e toki pi sona lukin",
+ "confirmations.logout.title": "o weka ala weka?",
+ "confirmations.missing_alt_text.confirm": "o pana e toki pi sona lukin",
"confirmations.missing_alt_text.message": "toki ni la sitelen li lon. taso toki pi sona lukin li lon ala. toki pi sona lukin li pona tan ni: jan ale li ken sona e toki.",
- "confirmations.missing_alt_text.title": "o pana e toki pi sona lukin",
+ "confirmations.missing_alt_text.secondary": "o pana a",
+ "confirmations.missing_alt_text.title": "o pana ala pana e toki pi sona lukin?",
"confirmations.mute.confirm": "o len",
"confirmations.redraft.confirm": "o weka o pali sin e toki",
"confirmations.redraft.message": "pali sin e toki ni la sina wile ala wile weka e ona? sina ni la suli pi toki ni en wawa pi toki ni li weka. kin la toki lon toki ni li jo e mama ala.",
"confirmations.redraft.title": "ni li weka li pali sin e toki ni.",
- "confirmations.reply.confirm": "toki lon toki ni",
- "confirmations.reply.message": "sina toki lon toki ni la toki pali sina li weka. sina wile ala wile e ni?",
+ "confirmations.remove_from_followers.confirm": "o kama kute ala e jan",
+ "confirmations.remove_from_followers.message": "{name} li kama kute ala e sina. sina wile ala wile e ni?",
+ "confirmations.remove_from_followers.title": "o kama ala kama kute ala e jan?",
+ "confirmations.reply.confirm": "o weka",
+ "confirmations.reply.message": "sina pana e toki tawa lipu ante la ni li weka e toki sina lon. sina wile ala wile weka e toki ni?",
"confirmations.reply.title": "sina wile ala wile weka e toki lon?",
"confirmations.unfollow.confirm": "o kute ala",
"confirmations.unfollow.message": "sina o wile ala wile pini kute e jan {name}?",
"confirmations.unfollow.title": "sina wile ala wile pini kute?",
"content_warning.hide": "o len",
- "content_warning.show": "o lukin",
+ "content_warning.show": "o lukin a",
"content_warning.show_more": "o lukin",
"conversation.delete": "o weka e toki ni",
"conversation.mark_as_read": "ni o sin ala",
"conversation.open": "o lukin e toki",
"conversation.with": "lon {names}",
- "copy_icon_button.copied": "toki li awen lon ilo sina",
+ "copy_icon_button.copied": "sina jo e toki",
"copypaste.copied": "sina jo e toki",
- "copypaste.copy_to_clipboard": "o awen lon ilo sina",
+ "copypaste.copy_to_clipboard": "o jo e toki",
"directory.federated": "tan lipu ante sona",
- "directory.local": "tan {domain} taso",
+ "directory.local": "tan ma {domain} taso",
"directory.new_arrivals": "jan pi kama sin",
"directory.recently_active": "jan lon tenpo poka",
"disabled_account_banner.account_settings": "wile pi lipu jan",
- "disabled_account_banner.text": "sina ken ala kepeken e lipu jan sina pi nimi {disabledAccount}.",
- "dismissable_banner.community_timeline": "ni li toki pi tenpo poka tawa ale tan jan lon ma lawa pi nimi {domain}.",
+ "disabled_account_banner.text": "sina ken ala lon sijelo {disabledAccount}.",
+ "dismissable_banner.community_timeline": "ni li toki suli pi len ala lon ma {domain} tan tenpo poka.",
"dismissable_banner.dismiss": "o weka",
- "dismissable_banner.explore_links": "tenpo suno ni la jan pi kulupu ale li toki e ijo sin ni. ijo sin pi jan ante mute li sewi lon lipu ni.",
- "dismissable_banner.explore_statuses": "jan mute li lukin e toki ni tan ma ilo weka. toki sin en toki pi wawa mute li lon sewi.",
- "domain_block_modal.block": "o weka e ma",
- "domain_block_modal.they_wont_know": "ona li sona ala e ni: sina weka e ona.",
- "domain_block_modal.title": "sina wile weka ala weka e ma?",
+ "dismissable_banner.explore_links": "tenpo poka la jan pi kulupu ale li toki e ijo sin ni. ijo sin pi jan ante mute li sewi lon lipu ni.",
+ "dismissable_banner.explore_statuses": "tenpo poka la jan pi kulupu ale li toki e ijo ni. ijo sin pi jan ante mute li sewi lon lipu ni.",
+ "dismissable_banner.explore_tags": "tenpo poka la jan pi kulupu ale li toki e ijo ni. ijo sin pi jan ante mute li sewi lon lipu ni.",
+ "dismissable_banner.public_timeline": "toki ni li sin. jan li pali e toki ni la jan ante mute pi ma {domain} li kute e jan ni.",
+ "domain_block_modal.block": "o len e ma",
+ "domain_block_modal.block_account_instead": "o len e @{name} a",
+ "domain_block_modal.they_can_interact_with_old_posts": "jan pi ma ni li ken ijo e toki sina.",
+ "domain_block_modal.they_cant_follow": "jan pi ma ni li ken ala kute e sina.",
+ "domain_block_modal.they_wont_know": "ona li sona ala e ni: sina len e ona.",
+ "domain_block_modal.title": "sina wile ala wile len e ma?",
"domain_block_modal.you_will_lose_num_followers": "{followersCount, plural, other {jan {followersCountDisplay}}} li kute e sina la, ona kama kute ala e sina. sina kute e {followingCount, plural,other {jan {followingCountDisplay}}} la, sina kama kute ala e ona.",
"domain_block_modal.you_will_lose_relationships": "jan li lon kulupu ni la ona kute e sina la, ona li kama kute ala e sina. jan li lon kulupu ni la sina kute e ona la, sina kama kute ala e ona.",
"domain_block_modal.you_wont_see_posts": "sina ken ala lukin e toki tan jan pi ma ni",
+ "domain_pill.activitypub_lets_connect": "ilo ni la sina ken toki tawa jan ante. ni li lon ma Masoton taso ala li lon ma mute a.",
+ "domain_pill.activitypub_like_language": "ilo Masoton li toki kepeken nasin ActivityPub tawa kulupu ilo ante.",
"domain_pill.server": "ma",
"domain_pill.their_handle": "nimi pi ona taso li ni:",
"domain_pill.their_server": "ni li ma ona lon ilo. toki ale ona li lon ma ni.",
@@ -281,14 +306,14 @@
"empty_column.account_suspended": "lipu ni li weka",
"empty_column.account_timeline": "toki ala li lon!",
"empty_column.account_unavailable": "ken ala lukin e lipu jan",
- "empty_column.blocks": "jan ala li weka tawa sina.",
+ "empty_column.blocks": "sina len ala e jan.",
"empty_column.direct": "jan ala li toki len e sina. jan li toki len e sina la sina ken lukin e ni lon ni.",
- "empty_column.domain_blocks": "ma ala li weka tawa sina.",
+ "empty_column.domain_blocks": "sina len ala e ma.",
"empty_column.favourited_statuses": "sina suli ala e toki. sina suli e toki la sina ken lukin e toki ni lon ni.",
"empty_column.favourites": "jan ala li suli e toki ni. jan li suli e toki ni la sina ken lukin e ona lon ni.",
"empty_column.follow_requests": "jan ala li toki pi wile kute tawa sina. jan li toki pi wile kute tawa sina la sina ken lukin e toki ni lon ni.",
- "empty_column.followed_tags": "sina alasa ala e toki ꞏ sina alasa e toki la toki li lon ni",
- "empty_column.hashtag": "ala li lon toki ni",
+ "empty_column.followed_tags": "sina kute ala e kulupu lipu. sina kute la toki li kama lon ni.",
+ "empty_column.hashtag": "toki ala li lon kulupu ni.",
"empty_column.home": "ala a li lon lipu open sina! sina wile lon e ijo lon ni la o kute e jan pi toki suli.",
"empty_column.list": "ala li lon kulupu lipu ni. jan pi kulupu lipu ni li toki sin la toki ni li lon ni.",
"empty_column.mutes": "jan ala li len tawa sina.",
@@ -299,15 +324,21 @@
"explore.trending_links": "sin",
"explore.trending_statuses": "toki",
"explore.trending_tags": "kulupu pi lipu suli",
+ "featured_carousel.next": "kama",
+ "featured_carousel.post": "toki",
+ "featured_carousel.previous": "pini",
+ "featured_carousel.slide": "lipu {total} la lipu nanpa {index}",
"filter_modal.added.settings_link": "lipu lawa",
"filter_modal.select_filter.expired": "tenpo pini",
"filter_modal.select_filter.search": "o alasa anu pali",
+ "filtered_notifications_banner.pending_requests": "ni li tan {count, plural, =0 {jan sina ala} other {jan sina #}}",
"firehose.all": "ale",
"firehose.local": "kulupu ni",
"firehose.remote": "kulupu ante",
"follow_request.authorize": "o ken",
- "follow_request.reject": "o ala",
+ "follow_request.reject": "o weka kute",
"follow_suggestions.dismiss": "mi wile lukin sin ala e ni",
+ "follow_suggestions.friends_of_friends_longer": "ni li suli tawa kulupu jan ni: sina kute e ona",
"follow_suggestions.hints.friends_of_friends": "jan kute sina li lukin mute e toki pi jan ni.",
"follow_suggestions.hints.most_followed": "jan mute lon ma {domain} li kute e jan ni.",
"follow_suggestions.hints.most_interactions": "tenpo poka la jan mute pi ma {domain} li lukin mute e toki pi jan ni.",
@@ -320,67 +351,103 @@
"footer.privacy_policy": "lawa len",
"footer.source_code": "o lukin e toki ilo",
"footer.status": "lon",
- "generic.saved": "ni li awen",
+ "footer.terms_of_service": "lipu lawa",
+ "generic.saved": "mi awen e ni",
+ "getting_started.heading": "mi open",
+ "hashtag.admin_moderation": "o lawa e kulupu #{name}",
+ "hashtag.browse": "o lukin e kulupu toki #{hashtag}",
+ "hashtag.browse_from_account": "o lukin e kulupu toki #{hashtag} tan @{name}",
"hashtag.column_header.tag_mode.all": "en {additional}",
"hashtag.column_header.tag_mode.any": "anu {additional}",
"hashtag.column_header.tag_mode.none": "en {additional} ala",
+ "hashtag.column_settings.select.placeholder": "o alasa e kulupu…",
"hashtag.column_settings.tag_mode.all": "ale ni",
"hashtag.column_settings.tag_mode.any": "wan ni",
"hashtag.column_settings.tag_mode.none": "ala ni",
"hashtag.counter_by_accounts": "{count, plural, other {jan {counter}}}",
"hashtag.counter_by_uses": "{count, plural, other {toki {counter}}}",
"hashtag.follow": "o kute e kulupu lipu",
+ "hashtag.mute": "o kute ala e kulupu #{hashtag}",
"hashtag.unfollow": "o kute ala e kulupu lipu",
- "home.column_settings.show_reblogs": "lukin e wawa",
+ "hints.profiles.followers_may_be_missing": "jan kute li ken weka.",
+ "hints.profiles.see_more_posts": "o lukin e toki ante lon ma {domain}",
+ "hints.threads.see_more": "o lukin e toki ante lon ma {domain}",
+ "home.column_settings.show_reblogs": "o lukin e wawa toki",
+ "home.hide_announcements": "o lukin ala e toki lawa suli",
"home.pending_critical_update.link": "o lukin e ijo ilo sin",
+ "home.show_announcements": "o lukin e toki lawa suli",
"info_button.label": "sona",
- "interaction_modal.go": "o tawa ma ni",
+ "interaction_modal.go": "o tawa",
+ "interaction_modal.no_account_yet": "sina jo ala e sijelo anu seme?",
"interaction_modal.on_another_server": "lon ma ante",
"interaction_modal.on_this_server": "lon ma ni",
- "interaction_modal.title.favourite": "o suli e toki {name}",
+ "interaction_modal.title.favourite": "o pona tawa {name}",
"interaction_modal.title.follow": "o kute e {name}",
- "interaction_modal.title.reblog": "o wawa e toki {name}",
- "interaction_modal.title.reply": "o toki lon toki pi jan {name}",
- "interaction_modal.title.vote": "o pana tawa wile sona pi jan {name}",
+ "interaction_modal.title.reblog": "o pana wawa e toki tan {name}",
+ "interaction_modal.title.reply": "o toki lon lipu tawa {name}",
+ "interaction_modal.title.vote": "o pana e sona wile tawa {name}",
"interaction_modal.username_prompt": "ni li sama ni: {example}",
- "intervals.full.days": "{number, plural, other {suni #}}",
- "intervals.full.hours": "{number, plural, other {tenpo suli #}}",
- "keyboard_shortcuts.blocked": "o lukin e lipu sina pi jan weka",
+ "intervals.full.days": "ni li pini lon {number, plural, other {tenpo suno #}}",
+ "intervals.full.hours": "ni li pini lon {number, plural, other {tenpo ilo #}}",
+ "intervals.full.minutes": "ni li pini lon {number, plural, other {tenpo ilo lili #}}",
+ "keyboard_shortcuts.back": "o tawa pini",
+ "keyboard_shortcuts.blocked": "o lukin e lipu pi len sina",
"keyboard_shortcuts.boost": "o pana sin e toki",
"keyboard_shortcuts.down": "o tawa anpa lon lipu",
"keyboard_shortcuts.enter": "o lukin e toki",
"keyboard_shortcuts.favourite": "o sitelen pona e toki",
"keyboard_shortcuts.favourites": "o lukin e lipu sina pi toki suli",
+ "keyboard_shortcuts.federated": "o lukin e linja toki pi ma ale",
"keyboard_shortcuts.muted": "o lukin e lipu sina pi jan len",
"keyboard_shortcuts.my_profile": "o lukin e lipu sina",
"keyboard_shortcuts.open_media": "o lukin e sitelen",
"keyboard_shortcuts.pinned": "o lukin pi lipu sina pi toki sewi",
- "keyboard_shortcuts.toggle_sensitivity": "o ante e ken lukin",
- "keyboard_shortcuts.toot": "o toki",
+ "keyboard_shortcuts.reply": "o toki lon ijo ni",
+ "keyboard_shortcuts.toggle_hidden": "o lukin ala lukin e toki len",
+ "keyboard_shortcuts.toggle_sensitivity": "o lukin ala lukin e sitelen",
+ "keyboard_shortcuts.toot": "o toki sin",
+ "keyboard_shortcuts.translate": "o ante e toki lipu",
"keyboard_shortcuts.up": "o tawa sewi lon lipu",
"lightbox.close": "o pini",
"lightbox.next": "sinpin",
"lightbox.previous": "monsi",
"link_preview.author": "tan {name}",
+ "link_preview.shares": "{count, plural, other {toki {counter}}}",
+ "lists.add_member": "o pana",
+ "lists.add_to_list": "o pana tawa kulupu jan",
+ "lists.add_to_lists": "o pana e {name} tawa kulupu jan",
+ "lists.create": "o lipu e kulupu jan",
+ "lists.create_a_list_to_organize": "o lipu e kulupu tawa nasin pi ilo sina",
+ "lists.create_list": "o lipu e kulupu jan",
"lists.delete": "o weka e kulupu lipu",
"lists.done": "ale li pini",
"lists.edit": "o ante e kulupu lipu",
+ "lists.find_users_to_add": "o alasa e jan",
"lists.list_members_count": "{count, plural, other {jan #}}",
"lists.list_name": "nimi kulupu",
"lists.new_list_name": "nimi pi kulupu sin",
"lists.no_lists_yet": "kulupu li lon ala.",
+ "lists.no_members_yet": "jan ala li lon.",
+ "lists.no_results_found": "jan ala li lon.",
"lists.remove_member": "o weka",
"lists.replies_policy.followed": "jan kute ale",
"lists.replies_policy.list": "jan pi kulupu ni taso",
"lists.replies_policy.none": "jan ala",
+ "lists.save": "o awen",
"lists.search": "o alasa",
"load_pending": "{count, plural, other {ijo sin #}}",
"loading_indicator.label": "ni li kama…",
+ "media_gallery.hide": "o len",
+ "moved_to_account_banner.text": "sina ante e sijelo tawa sijelo {movedToAccount}. ni la sijelo {disabledAccount} li pini.",
+ "mute_modal.hide_from_notifications": "o len tan mu",
+ "mute_modal.indefinite": "ni li pini ala",
"mute_modal.title": "sina wile ala wile kute e jan ni?",
"navigation_bar.about": "sona",
- "navigation_bar.blocks": "jan weka",
+ "navigation_bar.blocks": "jan len",
"navigation_bar.compose": "o pali e toki sin",
- "navigation_bar.domain_blocks": "kulupu pi ma weka",
+ "navigation_bar.discover": "o alasa",
+ "navigation_bar.domain_blocks": "ma len",
+ "navigation_bar.explore": "o alasa",
"navigation_bar.favourites": "ijo pona",
"navigation_bar.filters": "nimi len",
"navigation_bar.lists": "kulupu lipu",
@@ -395,11 +462,13 @@
"notification.follow_request": "{name} li wile kute e sina",
"notification.label.mention": "jan li toki e sina",
"notification.label.private_mention": "jan li toki e sina lon len",
- "notification.label.private_reply": "Jan li toki tawa toki sina lon len",
+ "notification.label.private_reply": "toki len",
"notification.label.reply": "jan li toki tawa toki sina",
"notification.mentioned_you": "jan {name} li toki e sina",
"notification.moderation-warning.learn_more": "o kama sona e ijo ante",
"notification.reblog": "{name} li wawa e toki sina",
+ "notification.relationships_severance_event.domain_block": "ma {from} la jan lawa li len e ma {target}. ma ni la jan {followersCount} li kute e sina. sina kute e {followingCount, plural, other {jan #}} tan ma ni. kama la ona ale li len tawa sina.",
+ "notification.relationships_severance_event.user_domain_block": "sina len e ma {target}. ma ni la jan {followersCount} li kute e sina. sina kute e {followingCount, plural, other {jan #}} tan ma ni. kama la ona ale li len tawa sina.",
"notification.status": "{name} li toki",
"notification.update": "{name} li ante e toki",
"notification_requests.dismiss": "o weka",
@@ -432,8 +501,9 @@
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"relative_time.today": "tenpo suno ni",
- "reply_indicator.cancel": "o ala",
- "report.block": "o weka e jan",
+ "reply_indicator.attachments": "{count, plural, other {sitelen #}}",
+ "reply_indicator.cancel": "o pana ala",
+ "report.block": "o len e jan",
"report.block_explanation": "sina kama lukin ala e toki ona. ona li kama ala ken lukin e toki sina li kama ala ken kute e sina. ona li ken sona e kama ni.",
"report.categories.other": "ante",
"report.categories.spam": "ike tan toki mute",
@@ -465,8 +535,8 @@
"search_results.see_all": "ale",
"search_results.statuses": "toki",
"server_banner.administered_by": "jan lawa:",
- "status.block": "o weka e @{name}",
- "status.cancel_reblog_private": "o pini e pana",
+ "status.block": "o len e @{name}",
+ "status.cancel_reblog_private": "o pana ala",
"status.delete": "o weka",
"status.edit": "o ante",
"status.favourite": "o sitelen pona",
@@ -500,6 +570,7 @@
"units.short.thousand": "{count}K",
"upload_button.label": "o pana e sitelen anu kalama",
"upload_error.limit": "ilo li ken ala e suli pi ijo ni.",
+ "upload_form.drag_and_drop.on_drag_cancel": "sina wile ala pana e sitelen. mi weka e sitelen.",
"upload_form.edit": "o ante",
"upload_progress.label": "ilo li kama jo e ijo sina...",
"upload_progress.processing": "ilo li pali…",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index 7d22877030..b68854b883 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -430,6 +430,7 @@
"hints.profiles.see_more_posts": "{domain} adresinde daha fazla gönderi gör",
"hints.threads.replies_may_be_missing": "Diğer sunuculardan yanıtlar eksik olabilir.",
"hints.threads.see_more": "{domain} adresinde daha fazla yanıt gör",
+ "home.column_settings.show_quotes": "Alıntıları göster",
"home.column_settings.show_reblogs": "Yeniden paylaşımları göster",
"home.column_settings.show_replies": "Yanıtları göster",
"home.hide_announcements": "Duyuruları gizle",
diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json
index 5c79bcd7fa..747d02a473 100644
--- a/app/javascript/mastodon/locales/vi.json
+++ b/app/javascript/mastodon/locales/vi.json
@@ -430,6 +430,7 @@
"hints.profiles.see_more_posts": "Xem thêm tút ở {domain}",
"hints.threads.replies_may_be_missing": "Lượt trả lời từ máy chủ khác có thể không đầy đủ.",
"hints.threads.see_more": "Xem thêm ở {domain}",
+ "home.column_settings.show_quotes": "Hiện các trích dẫn",
"home.column_settings.show_reblogs": "Hiện những lượt đăng lại",
"home.column_settings.show_replies": "Hiện những tút dạng trả lời",
"home.hide_announcements": "Ẩn thông báo máy chủ",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index a9e5844fe9..4f4909ad7f 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -341,6 +341,8 @@
"explore.trending_links": "新闻",
"explore.trending_statuses": "嘟文",
"explore.trending_tags": "话题",
+ "featured_carousel.next": "下一步",
+ "featured_carousel.previous": "上一步",
"filter_modal.added.context_mismatch_explanation": "这条过滤规则不适用于你当前访问此嘟文的场景。要在此场景下过滤嘟文,你必须编辑此过滤规则。",
"filter_modal.added.context_mismatch_title": "场景不匹配!",
"filter_modal.added.expired_explanation": "此过滤规则类别已过期,你需要修改到期日期才能应用。",
@@ -418,6 +420,7 @@
"hints.profiles.see_more_posts": "在 {domain} 查看更多嘟文",
"hints.threads.replies_may_be_missing": "来自其它实例的回复可能没有完全显示。",
"hints.threads.see_more": "在 {domain} 查看更多回复",
+ "home.column_settings.show_quotes": "显示引用",
"home.column_settings.show_reblogs": "显示转嘟",
"home.column_settings.show_replies": "显示回复",
"home.hide_announcements": "隐藏公告",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index 3abfbffb56..f776439fcc 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -84,6 +84,7 @@
"alt_text_modal.cancel": "取消",
"alt_text_modal.done": "完成",
"announcement.announcement": "公告",
+ "annual_report.summary.thanks": "感謝您成為 Mastodon 的一份子!",
"attachments_list.unprocessed": "(未處理)",
"audio.hide": "隱藏音訊",
"block_modal.remote_users_caveat": "我們會要求 {domain} 伺服器尊重你的決定。然而,由於部份伺服器可能以不同方式處理封鎖,因此無法保證一定會成功。公開帖文仍然有機會被未登入的使用者看見。",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index ce32d89a17..6c83fb55ce 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -430,6 +430,7 @@
"hints.profiles.see_more_posts": "於 {domain} 檢視更多嘟文",
"hints.threads.replies_may_be_missing": "來自其他站點之回覆或有缺失。",
"hints.threads.see_more": "於 {domain} 檢視更多回覆",
+ "home.column_settings.show_quotes": "顯示引用嘟文",
"home.column_settings.show_reblogs": "顯示轉嘟",
"home.column_settings.show_replies": "顯示回覆",
"home.hide_announcements": "隱藏公告",
diff --git a/app/javascript/mastodon/main.tsx b/app/javascript/mastodon/main.tsx
index a9696ac50e..70e6391bee 100644
--- a/app/javascript/mastodon/main.tsx
+++ b/app/javascript/mastodon/main.tsx
@@ -1,8 +1,10 @@
import { createRoot } from 'react-dom/client';
+import { Globals } from '@react-spring/web';
+
import { setupBrowserNotifications } from 'mastodon/actions/notifications';
import Mastodon from 'mastodon/containers/mastodon';
-import { me } from 'mastodon/initial_state';
+import { me, reduceMotion } from 'mastodon/initial_state';
import * as perf from 'mastodon/performance';
import ready from 'mastodon/ready';
import { store } from 'mastodon/store';
@@ -21,6 +23,12 @@ function main() {
mountNode.getAttribute('data-props') ?? '{}',
) as Record;
+ if (reduceMotion) {
+ Globals.assign({
+ skipAnimation: true,
+ });
+ }
+
const root = createRoot(mountNode);
root.render( );
store.dispatch(setupBrowserNotifications());
diff --git a/app/javascript/mastodon/reducers/index.ts b/app/javascript/mastodon/reducers/index.ts
index 2a831804ab..4f725c3f64 100644
--- a/app/javascript/mastodon/reducers/index.ts
+++ b/app/javascript/mastodon/reducers/index.ts
@@ -24,6 +24,7 @@ import { markersReducer } from './markers';
import media_attachments from './media_attachments';
import meta from './meta';
import { modalReducer } from './modal';
+import { navigationReducer } from './navigation';
import { notificationGroupsReducer } from './notification_groups';
import { notificationPolicyReducer } from './notification_policy';
import { notificationRequestsReducer } from './notification_requests';
@@ -84,6 +85,7 @@ const reducers = {
reaction_deck,
notificationPolicy: notificationPolicyReducer,
notificationRequests: notificationRequestsReducer,
+ navigation: navigationReducer,
};
// We want the root state to be an ImmutableRecord, which is an object with a defined list of keys,
diff --git a/app/javascript/mastodon/reducers/navigation.ts b/app/javascript/mastodon/reducers/navigation.ts
new file mode 100644
index 0000000000..3f245603a1
--- /dev/null
+++ b/app/javascript/mastodon/reducers/navigation.ts
@@ -0,0 +1,28 @@
+import { createReducer } from '@reduxjs/toolkit';
+
+import {
+ openNavigation,
+ closeNavigation,
+ toggleNavigation,
+} from 'mastodon/actions/navigation';
+
+interface State {
+ open: boolean;
+}
+
+const initialState: State = {
+ open: false,
+};
+
+export const navigationReducer = createReducer(initialState, (builder) => {
+ builder
+ .addCase(openNavigation, (state) => {
+ state.open = true;
+ })
+ .addCase(closeNavigation, (state) => {
+ state.open = false;
+ })
+ .addCase(toggleNavigation, (state) => {
+ state.open = !state.open;
+ });
+});
diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js
index 891d6ac994..f2486c1bbb 100644
--- a/app/javascript/mastodon/reducers/settings.js
+++ b/app/javascript/mastodon/reducers/settings.js
@@ -24,6 +24,7 @@ const initialState = ImmutableMap({
home: ImmutableMap({
shows: ImmutableMap({
+ quote: true,
reblog: true,
reply: true,
}),
diff --git a/app/javascript/mastodon/selectors/antennas.ts b/app/javascript/mastodon/selectors/antennas.ts
index 7194bf9a19..daa19f7508 100644
--- a/app/javascript/mastodon/selectors/antennas.ts
+++ b/app/javascript/mastodon/selectors/antennas.ts
@@ -1,15 +1,18 @@
-import { createSelector } from '@reduxjs/toolkit';
-import type { Map as ImmutableMap } from 'immutable';
+import type { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import type { Antenna } from 'mastodon/models/antenna';
-import type { RootState } from 'mastodon/store';
+import { createAppSelector } from 'mastodon/store';
-export const getOrderedAntennas = createSelector(
- [(state: RootState) => state.antennas],
- (antennas: ImmutableMap) =>
+const getAntennas = createAppSelector(
+ [(state) => state.antennas],
+ (antennas: ImmutableMap): ImmutableList =>
+ antennas.toList().filter((item: Antenna | null): item is Antenna => !!item),
+);
+
+export const getOrderedAntennas = createAppSelector(
+ [(state) => getAntennas(state)],
+ (antennas) =>
antennas
- .toList()
- .filter((item: Antenna | null) => !!item)
.sort((a: Antenna, b: Antenna) => a.title.localeCompare(b.title))
.toArray(),
);
diff --git a/app/javascript/mastodon/selectors/lists.ts b/app/javascript/mastodon/selectors/lists.ts
index f93e90ce68..9b79a880a9 100644
--- a/app/javascript/mastodon/selectors/lists.ts
+++ b/app/javascript/mastodon/selectors/lists.ts
@@ -1,15 +1,16 @@
-import { createSelector } from '@reduxjs/toolkit';
-import type { Map as ImmutableMap } from 'immutable';
+import type { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import type { List } from 'mastodon/models/list';
-import type { RootState } from 'mastodon/store';
+import { createAppSelector } from 'mastodon/store';
-export const getOrderedLists = createSelector(
- [(state: RootState) => state.lists],
- (lists: ImmutableMap) =>
- lists
- .toList()
- .filter((item: List | null) => !!item)
- .sort((a: List, b: List) => a.title.localeCompare(b.title))
- .toArray(),
+const getLists = createAppSelector(
+ [(state) => state.lists],
+ (lists: ImmutableMap): ImmutableList =>
+ lists.toList().filter((item: List | null): item is List => !!item),
+);
+
+export const getOrderedLists = createAppSelector(
+ [(state) => getLists(state)],
+ (lists) =>
+ lists.sort((a: List, b: List) => a.title.localeCompare(b.title)).toArray(),
);
diff --git a/app/javascript/material-icons/400-24px/arrow_left-fill.svg b/app/javascript/material-icons/400-24px/arrow_left-fill.svg
new file mode 100644
index 0000000000..bf9b2aef3f
--- /dev/null
+++ b/app/javascript/material-icons/400-24px/arrow_left-fill.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/material-icons/400-24px/arrow_left.svg b/app/javascript/material-icons/400-24px/arrow_left.svg
new file mode 100644
index 0000000000..bf9b2aef3f
--- /dev/null
+++ b/app/javascript/material-icons/400-24px/arrow_left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/material-icons/400-24px/edit_square-fill.svg b/app/javascript/material-icons/400-24px/edit_square-fill.svg
new file mode 100644
index 0000000000..4f931de0f2
--- /dev/null
+++ b/app/javascript/material-icons/400-24px/edit_square-fill.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/material-icons/400-24px/edit_square.svg b/app/javascript/material-icons/400-24px/edit_square.svg
new file mode 100644
index 0000000000..dccfaa9f3c
--- /dev/null
+++ b/app/javascript/material-icons/400-24px/edit_square.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/material-icons/400-24px/unfold_less-fill.svg b/app/javascript/material-icons/400-24px/unfold_less-fill.svg
new file mode 100644
index 0000000000..8136d615b2
--- /dev/null
+++ b/app/javascript/material-icons/400-24px/unfold_less-fill.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/material-icons/400-24px/unfold_less.svg b/app/javascript/material-icons/400-24px/unfold_less.svg
new file mode 100644
index 0000000000..8136d615b2
--- /dev/null
+++ b/app/javascript/material-icons/400-24px/unfold_less.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/material-icons/400-24px/unfold_more-fill.svg b/app/javascript/material-icons/400-24px/unfold_more-fill.svg
new file mode 100644
index 0000000000..3e245d2090
--- /dev/null
+++ b/app/javascript/material-icons/400-24px/unfold_more-fill.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/material-icons/400-24px/unfold_more.svg b/app/javascript/material-icons/400-24px/unfold_more.svg
new file mode 100644
index 0000000000..3e245d2090
--- /dev/null
+++ b/app/javascript/material-icons/400-24px/unfold_more.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/javascript/styles/mastodon-light/diff.scss b/app/javascript/styles/mastodon-light/diff.scss
index c6e3f42799..ebaac66a5e 100644
--- a/app/javascript/styles/mastodon-light/diff.scss
+++ b/app/javascript/styles/mastodon-light/diff.scss
@@ -283,36 +283,6 @@
}
}
-.activity-stream {
- border: 1px solid var(--background-border-color);
-
- &--under-tabs {
- border-top: 0;
- }
-
- .entry {
- background: $white;
-
- .detailed-status.light,
- .more.light,
- .status.light {
- border-bottom-color: lighten($ui-base-color, 8%);
- }
- }
-
- .status.light {
- .status__content {
- color: $primary-text-color;
- }
-
- .display-name {
- strong {
- color: $primary-text-color;
- }
- }
- }
-}
-
.accounts-grid {
.account-grid-card {
.controls {
@@ -416,13 +386,6 @@
}
}
-.mute-modal select {
- border: 1px solid var(--background-border-color);
- background: $simple-background-color
- url("data:image/svg+xml;utf8, ")
- no-repeat right 8px center / auto 16px;
-}
-
.status__wrapper-direct {
background-color: rgba($ui-highlight-color, 0.1);
diff --git a/app/javascript/styles/mastodon/_functions.scss b/app/javascript/styles/mastodon/_functions.scss
index 7190a6233e..a9911edb9d 100644
--- a/app/javascript/styles/mastodon/_functions.scss
+++ b/app/javascript/styles/mastodon/_functions.scss
@@ -1,4 +1,6 @@
@use 'sass:color';
+@use 'sass:string';
+@use 'sass:meta';
$darken-multiplier: -1 !default;
$lighten-multiplier: 1 !default;
@@ -19,3 +21,11 @@ $lighten-multiplier: 1 !default;
$space: hsl
);
}
+
+@function hex-color($color) {
+ @if meta.type-of($color) == 'color' {
+ $color: string.slice(color.ie-hex-str($color), 4);
+ }
+
+ @return '%23' + string.unquote($color);
+}
diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss
index 91c3ddb4be..d1795621ae 100644
--- a/app/javascript/styles/mastodon/admin.scss
+++ b/app/javascript/styles/mastodon/admin.scss
@@ -585,16 +585,6 @@ body,
.account-status {
display: flex;
margin-bottom: 10px;
-
- .activity-stream {
- flex: 2 0 0;
- margin-inline-end: 20px;
- max-width: calc(100% - 60px);
-
- .entry {
- border-radius: 4px;
- }
- }
}
.report-status__actions,
diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss
index 7ae8cb2d08..e123b6276e 100644
--- a/app/javascript/styles/mastodon/basics.scss
+++ b/app/javascript/styles/mastodon/basics.scss
@@ -1,14 +1,6 @@
@use 'variables' as *;
@use 'functions' as *;
-@function hex-color($color) {
- @if type-of($color) == 'color' {
- $color: str-slice(ie-hex-str($color), 4);
- }
-
- @return '%23' + unquote($color);
-}
-
body {
font-family: $font-sans-serif, sans-serif;
background: var(--background-color);
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index fe568432e8..508468d5b2 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -1473,43 +1473,6 @@ body > [data-popper-placement] {
margin-top: 16px;
}
- &.light {
- .status__relative-time,
- .status__visibility-icon {
- color: $light-text-color;
- }
-
- .status__display-name {
- color: $inverted-text-color;
- }
-
- .display-name {
- color: $light-text-color;
-
- strong {
- color: $inverted-text-color;
- }
- }
-
- .status__content {
- color: $inverted-text-color;
-
- a {
- color: $highlight-text-color;
- }
-
- &__spoiler-link {
- color: $primary-text-color;
- background: $ui-primary-color;
-
- &:hover,
- &:focus {
- background: lighten($ui-primary-color, 8%);
- }
- }
- }
- }
-
&--is-quote {
border: none;
}
@@ -1952,16 +1915,18 @@ body > [data-popper-placement] {
}
.status__quote {
+ --quote-margin: 36px;
+
position: relative;
margin-block-start: 16px;
- margin-inline-start: 36px;
+ margin-inline-start: calc(var(--quote-margin) + var(--thread-margin, 0px));
border-radius: 8px;
color: var(--nested-card-text);
background: var(--nested-card-background);
border: var(--nested-card-border);
- @media screen and (min-width: $mobile-breakpoint) {
- margin-inline-start: 56px;
+ @container (width > 460px) {
+ --quote-margin: 56px;
}
}
@@ -2021,7 +1986,7 @@ body > [data-popper-placement] {
transform: translateY(-50%);
}
- @media screen and (min-width: $mobile-breakpoint) {
+ @container (width > 460px) {
inset-inline-start: -50px;
}
}
@@ -2238,6 +2203,16 @@ body > [data-popper-placement] {
display: flex;
gap: 10px;
align-items: center;
+ justify-content: end;
+}
+
+.account__wrapper--with-bio {
+ align-items: start;
+}
+
+.account__info-wrapper {
+ flex: 1 1 auto;
+ min-width: 0;
}
.account__avatar {
@@ -2246,6 +2221,11 @@ body > [data-popper-placement] {
border-radius: var(--avatar-border-radius);
background: var(--surface-background-color);
+ @container (width < 360px) {
+ width: 35px !important;
+ height: 35px !important;
+ }
+
img {
width: 100%;
height: 100%;
@@ -2371,7 +2351,7 @@ a .account__avatar {
}
.account__relationship,
-.explore__suggestions__card {
+.explore-suggestions-card {
.icon-button {
border: 1px solid var(--background-border-color);
border-radius: 4px;
@@ -2717,15 +2697,13 @@ a.account__display-name {
min-width: 0;
&__display-name {
- font-size: 16px;
- line-height: 24px;
- letter-spacing: 0.15px;
+ font-size: 14px;
+ line-height: 20px;
font-weight: 500;
.display-name__account {
font-size: 14px;
- line-height: 20px;
- letter-spacing: 0.1px;
+ font-weight: 400;
}
}
}
@@ -2953,6 +2931,7 @@ a.account__display-name {
display: flex;
flex-direction: column;
contain: inline-size layout paint style;
+ container: column / inline-size;
@media screen and (min-width: $no-gap-breakpoint) {
max-width: 600px;
@@ -2961,45 +2940,32 @@ a.account__display-name {
}
}
-$ui-header-height: 55px;
-$ui-header-logo-wordmark-width: 99px;
-
-.ui__header {
- display: none;
- box-sizing: border-box;
- height: $ui-header-height;
+.ui__navigation-bar {
position: sticky;
- top: 0;
- z-index: 3;
- justify-content: space-between;
- align-items: center;
+ bottom: 0;
+ background: var(--background-color);
backdrop-filter: var(--background-filter);
+ border-top: 1px solid var(--background-border-color);
+ z-index: 3;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ padding-bottom: env(safe-area-inset-bottom);
- &__logo {
- display: inline-flex;
- padding: 15px;
- flex-grow: 1;
- flex-shrink: 1;
- overflow: hidden;
- container: header-logo / inline-size;
+ .layout-multiple-columns & {
+ display: none;
+ }
- .logo {
- height: $ui-header-height - 30px;
- width: auto;
- }
+ &__items {
+ display: grid;
+ grid-auto-columns: minmax(0, 1fr);
+ grid-auto-flow: column;
+ padding: 0 16px;
- .logo--wordmark {
- display: none;
- }
-
- @container header-logo (min-width: #{$ui-header-logo-wordmark-width}) {
- .logo--wordmark {
- display: block;
- }
-
- .logo--icon {
- display: none;
- }
+ &.active {
+ flex: 1;
+ padding: 0;
}
@media screen and (width < 330px) {
@@ -3013,25 +2979,42 @@ $ui-header-logo-wordmark-width: 99px;
}
}
- &__links {
+ &__sign-up {
display: flex;
align-items: center;
gap: 8px;
- padding: 0 9px;
+ padding-inline-start: 16px;
overflow: hidden;
flex-shrink: 0;
+ }
- .button {
- flex: 0 0 auto;
+ &__item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background: transparent;
+ border: none;
+ gap: 8px;
+ font-size: 12px;
+ font-weight: 500;
+ line-height: 16px;
+ padding-top: 11px;
+ padding-bottom: 15px;
+ border-top: 4px solid transparent;
+ text-decoration: none;
+ color: inherit;
+
+ &.active {
+ color: $highlight-text-color;
}
- .button-tertiary {
- flex-shrink: 1;
+ &:focus {
+ outline: 0;
}
- .icon {
- width: 22px;
- height: 22px;
+ &:focus-visible {
+ border-top-color: $ui-button-focus-outline-color;
+ border-radius: 0;
}
}
}
@@ -3040,13 +3023,12 @@ $ui-header-logo-wordmark-width: 99px;
background: var(--background-color);
backdrop-filter: var(--background-filter);
position: sticky;
- top: $ui-header-height;
+ top: 0;
z-index: 2;
padding-top: 0;
@media screen and (min-width: $no-gap-breakpoint) {
padding-top: 10px;
- top: 0;
}
}
@@ -3215,8 +3197,11 @@ $ui-header-logo-wordmark-width: 99px;
display: none;
}
- .navigation-panel__legal {
- display: none;
+ .navigation-panel__legal,
+ .navigation-panel__compose-button,
+ .navigation-panel .navigation-bar,
+ .navigation-panel__refresh {
+ display: none !important;
}
}
@@ -3228,7 +3213,7 @@ $ui-header-logo-wordmark-width: 99px;
}
.columns-area__panels {
- min-height: calc(100vh - $ui-header-height);
+ min-height: 100vh;
gap: 0;
}
@@ -3246,28 +3231,18 @@ $ui-header-logo-wordmark-width: 99px;
}
.navigation-panel__sign-in-banner,
- .navigation-panel__logo,
.navigation-panel__banner,
- .getting-started__trends {
+ .getting-started__trends,
+ .navigation-panel__logo {
display: none;
}
.navigation-panel__legal {
margin-bottom: 100px;
}
-
- .column-link__icon {
- font-size: 18px;
- }
}
.layout-single-column {
- .ui__header {
- display: flex;
- background: var(--background-color);
- border-bottom: 1px solid var(--background-border-color);
- }
-
.column > .scrollable,
.tabs-bar__wrapper .column-header,
.tabs-bar__wrapper .column-back-button,
@@ -3291,31 +3266,65 @@ $ui-header-logo-wordmark-width: 99px;
}
}
-@media screen and (max-width: $no-gap-breakpoint - 285px - 1px) {
- $sidebar-width: 55px;
-
+@media screen and (width <= 759px) {
.columns-area__panels__main {
- width: calc(100% - $sidebar-width);
+ width: 100%;
}
.columns-area__panels__pane--navigational {
- min-width: $sidebar-width;
+ position: fixed;
+ inset-inline-end: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ }
- .columns-area__panels__pane__inner {
- width: $sidebar-width;
- }
+ .columns-area__panels__pane--navigational .columns-area__panels__pane__inner {
+ pointer-events: auto;
+ background: var(--background-color);
+ position: fixed;
+ width: 284px + 70px;
+ inset-inline-end: -70px;
+ touch-action: pan-y;
- .column-link span {
- display: none;
- }
+ .navigation-panel {
+ width: 284px;
+ overflow-y: auto;
- .list-panel {
- display: none;
+ &__menu {
+ flex-shrink: 0;
+ min-height: none;
+ overflow: hidden;
+ padding-bottom: calc(65px + env(safe-area-inset-bottom));
+ }
+
+ &__logo {
+ display: none;
+ }
}
}
}
-.explore__suggestions__card {
+.columns-area__panels__pane--navigational {
+ transition: background 500ms;
+}
+
+.columns-area__panels__pane--overlay {
+ pointer-events: auto;
+ background: rgba($base-overlay-background, 0.5);
+
+ .columns-area__panels__pane__inner {
+ box-shadow: var(--dropdown-shadow);
+ }
+}
+
+@media screen and (width >= 760px) {
+ .ui__navigation-bar {
+ display: none;
+ }
+}
+
+.explore-suggestions-card {
padding: 12px 16px;
gap: 8px;
display: flex;
@@ -3327,60 +3336,77 @@ $ui-header-logo-wordmark-width: 99px;
}
&__source {
- padding-inline-start: 60px;
font-size: 13px;
line-height: 16px;
color: $dark-text-color;
- text-overflow: ellipsis;
- overflow: hidden;
- white-space: nowrap;
+
+ @container (width >= 400px) {
+ padding-inline-start: 60px;
+ }
}
&__body {
display: flex;
gap: 12px;
align-items: center;
+ justify-content: end;
+ }
- &__main {
- flex: 1 1 auto;
- display: flex;
- flex-direction: column;
- gap: 8px;
- min-width: 0;
+ &__avatar {
+ flex-shrink: 0;
- &__name-button {
- display: flex;
- align-items: center;
- gap: 8px;
+ @container (width < 360px) {
+ width: 35px !important;
+ height: 35px !important;
+ }
+ }
- &__name {
- display: block;
- color: inherit;
- text-decoration: none;
- flex: 1 1 auto;
- min-width: 0;
- }
+ &__link {
+ flex: 1 1 auto;
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ text-decoration: none;
+ min-width: 0;
- .button {
- min-width: 80px;
- }
-
- .display-name {
- font-size: 15px;
- line-height: 20px;
- color: $secondary-text-color;
-
- strong {
- font-weight: 700;
- }
-
- &__account {
- color: $darker-text-color;
- display: block;
- }
- }
+ &:hover,
+ &:focus-visible {
+ .display-name__html {
+ text-decoration: underline;
}
}
+
+ .display-name {
+ font-size: 15px;
+ line-height: 20px;
+ color: $secondary-text-color;
+
+ strong {
+ font-weight: 700;
+ }
+
+ &__account {
+ color: $darker-text-color;
+ display: block;
+ }
+ }
+ }
+
+ &__actions {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ justify-content: end;
+
+ .button {
+ min-width: 80px;
+ }
+ }
+
+ &__dismiss-button {
+ @container (width < 400px) {
+ display: none;
+ }
}
}
@@ -3541,6 +3567,49 @@ $ui-header-logo-wordmark-width: 99px;
overflow-y: auto;
}
+ &__list-panel {
+ &__header {
+ display: flex;
+ align-items: center;
+ padding-inline-end: 12px;
+
+ .column-link {
+ flex: 1 1 auto;
+ }
+ }
+
+ &__items {
+ padding-inline-start: 24px + 5px;
+
+ .icon {
+ display: none;
+ }
+ }
+ }
+
+ &__compose-button {
+ display: flex;
+ justify-content: flex-start;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ padding-inline-start: 13px - 7px;
+ padding-inline-end: 13px;
+ gap: 5px;
+ margin: 12px;
+ margin-bottom: 4px;
+ border-radius: 6px;
+
+ .icon {
+ width: 24px;
+ height: 24px;
+ }
+ }
+
+ .navigation-bar {
+ padding: 16px;
+ border-bottom: 1px solid var(--background-border-color);
+ }
+
.logo {
height: 30px;
width: auto;
@@ -3573,12 +3642,6 @@ $ui-header-logo-wordmark-width: 99px;
display: none;
}
}
-
- // @media screen and (height <= 1040px) {
- // .list-panel {
- // display: none;
- // }
- // }
}
.navigation-panel,
@@ -4506,6 +4569,10 @@ a.status-card {
&:focus-visible {
outline: $ui-button-icon-focus-outline;
}
+
+ .logo {
+ height: 24px;
+ }
}
.column-header__back-button + &__title {
@@ -4589,10 +4656,6 @@ a.status-card {
&:hover {
color: $primary-text-color;
}
-
- .icon-sliders {
- transform: rotate(60deg);
- }
}
&:disabled {
@@ -6096,6 +6159,7 @@ a.status-card {
position: absolute;
top: 0;
bottom: 0;
+ transform: scaleX(var(--text-x-direction));
&:hover,
&:focus,
@@ -6104,11 +6168,11 @@ a.status-card {
}
}
-.media-modal__nav--left {
+.media-modal__nav--prev {
inset-inline-start: 0;
}
-.media-modal__nav--right {
+.media-modal__nav--next {
inset-inline-end: 0;
}
@@ -6122,7 +6186,9 @@ a.status-card {
.picture-in-picture__footer {
border-radius: 0;
+ border: none;
background: transparent;
+ backdrop-filter: none;
padding: 16px;
.icon-button {
@@ -6590,6 +6656,7 @@ a.status-card {
@media screen and (max-width: $no-columns-breakpoint) {
border-bottom: 0;
border-radius: 4px 4px 0 0;
+ padding-bottom: env(safe-area-inset-bottom);
}
}
@@ -6911,7 +6978,6 @@ a.status-card {
ul {
overflow-y: auto;
- flex-shrink: 0;
padding-bottom: 8px;
}
@@ -6950,24 +7016,6 @@ a.status-card {
}
}
}
-
- select {
- appearance: none;
- box-sizing: border-box;
- font-size: 14px;
- color: $inverted-text-color;
- display: inline-block;
- width: auto;
- outline: 0;
- font-family: inherit;
- background: $simple-background-color
- url("data:image/svg+xml;utf8, ")
- no-repeat right 8px center / auto 16px;
- border: 1px solid darken($simple-background-color, 14%);
- border-radius: 4px;
- padding: 6px 10px;
- padding-inline-end: 30px;
- }
}
.report-modal__target {
@@ -7397,18 +7445,19 @@ a.status-card {
}
.audio-player {
- overflow: hidden;
box-sizing: border-box;
+ container: audio-player / inline-size;
position: relative;
- background: var(--player-background-color, var(--background-color));
- color: var(--player-foreground-color);
- border-radius: 8px;
- padding-bottom: 44px;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
width: 100%;
+ aspect-ratio: 16 / 9;
+ color: var(--player-foreground-color);
+ background: var(--player-background-color, var(--background-color));
+ border-radius: 8px;
outline: 1px solid var(--media-outline-color);
outline-offset: -1px;
- aspect-ratio: 16 / 9;
- container: audio-player / inline-size;
&__controls {
display: grid;
@@ -7457,9 +7506,17 @@ a.status-card {
}
&__visualizer {
+ width: 100%;
max-width: 200px;
}
+ .video-player__seek {
+ position: absolute;
+ inset: 0 0 auto;
+ height: 24px;
+ z-index: 1; /* Ensure this renders on top of audio player controls */
+ }
+
&.inactive {
.video-player__seek,
.audio-player__controls,
@@ -8421,7 +8478,6 @@ noscript {
.avatar {
display: block;
flex: 0 0 auto;
- width: 94px;
.account__avatar {
background: var(--background-color);
@@ -8655,6 +8711,20 @@ noscript {
font-weight: 400;
margin-bottom: 10px;
+ &__loading-indicator-wrapper {
+ position: relative;
+ height: 37px;
+
+ .loading-indicator {
+ left: 10px;
+ }
+
+ .circular-progress {
+ width: 14px;
+ height: 14px;
+ }
+ }
+
label {
display: block;
font-size: 12px;
@@ -11245,7 +11315,7 @@ noscript {
display: flex;
align-items: center;
flex-wrap: wrap;
- gap: 10px;
+ gap: 2px 10px;
}
&__numbers {
@@ -11542,7 +11612,7 @@ noscript {
overflow: hidden;
flex-shrink: 0;
border-bottom: 1px solid var(--background-border-color);
- touch-action: pan-x;
+ touch-action: pan-y;
&__slides {
display: flex;
@@ -11553,6 +11623,8 @@ noscript {
&__slide {
flex: 0 0 auto;
flex-basis: 100%;
+ width: 100%;
+ overflow: hidden;
}
.status {
@@ -11573,5 +11645,13 @@ noscript {
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+
+ .icon {
+ width: 16px;
+ height: 16px;
+ }
}
}
diff --git a/app/javascript/styles/mastodon/css_variables.scss b/app/javascript/styles/mastodon/css_variables.scss
index 3214c02a19..c7223136ae 100644
--- a/app/javascript/styles/mastodon/css_variables.scss
+++ b/app/javascript/styles/mastodon/css_variables.scss
@@ -41,3 +41,12 @@
--detail-content-emoji-size: 24px;
--detail-content-line-height: 24px;
}
+
+body {
+ // Variable for easily inverting directional UI elements,
+ --text-x-direction: 1;
+
+ &.rtl {
+ --text-x-direction: -1;
+ }
+}
diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb
index b15e8097fe..e18d9bc55c 100644
--- a/app/lib/activitypub/activity/create.rb
+++ b/app/lib/activitypub/activity/create.rb
@@ -297,14 +297,12 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end
def process_quote
- return unless Mastodon::Feature.inbound_quotes_enabled?
-
@quote_uri = @status_parser.quote_uri
return if @quote_uri.blank?
approval_uri = @status_parser.quote_approval_uri
approval_uri = nil if unsupported_uri_scheme?(approval_uri)
- @quote = Quote.new(account: @account, approval_uri: approval_uri)
+ @quote = Quote.new(account: @account, approval_uri: approval_uri, legacy: @status_parser.legacy_quote?)
end
def process_hashtag(tag)
diff --git a/app/lib/activitypub/activity/quote_request.rb b/app/lib/activitypub/activity/quote_request.rb
index 6c5d805159..2de03df158 100644
--- a/app/lib/activitypub/activity/quote_request.rb
+++ b/app/lib/activitypub/activity/quote_request.rb
@@ -4,7 +4,6 @@ class ActivityPub::Activity::QuoteRequest < ActivityPub::Activity
include Payloadable
def perform
- return unless Mastodon::Feature.inbound_quotes_enabled?
return if non_matching_uri_hosts?(@account.uri, @json['id'])
quoted_status = status_from_uri(object_uri)
diff --git a/app/lib/activitypub/parser/media_attachment_parser.rb b/app/lib/activitypub/parser/media_attachment_parser.rb
index bcbf92214f..1f4f43cb15 100644
--- a/app/lib/activitypub/parser/media_attachment_parser.rb
+++ b/app/lib/activitypub/parser/media_attachment_parser.rb
@@ -15,7 +15,7 @@ class ActivityPub::Parser::MediaAttachmentParser
end
def remote_url
- url = Addressable::URI.parse(@json['url'])&.normalize&.to_s
+ url = Addressable::URI.parse(url_to_href(@json['url']))&.normalize&.to_s
url unless unsupported_uri_scheme?(url)
rescue Addressable::URI::InvalidURIError
nil
@@ -43,7 +43,7 @@ class ActivityPub::Parser::MediaAttachmentParser
end
def file_content_type
- @json['mediaType']
+ @json['mediaType'] || url_to_media_type(@json['url'])
end
private
diff --git a/app/lib/activitypub/parser/status_parser.rb b/app/lib/activitypub/parser/status_parser.rb
index c21cfc10e3..ce7057b88a 100644
--- a/app/lib/activitypub/parser/status_parser.rb
+++ b/app/lib/activitypub/parser/status_parser.rb
@@ -157,6 +157,10 @@ class ActivityPub::Parser::StatusParser
end.first
end
+ def legacy_quote?
+ !@object.key?('quote')
+ end
+
# The inlined quote; out of the attributes we support, only `https://w3id.org/fep/044f#quote` explicitly supports inlined objects
def quoted_object
as_array(@object['quote']).first
diff --git a/app/lib/admin/system_check/media_privacy_check.rb b/app/lib/admin/system_check/media_privacy_check.rb
index 378a8ce294..01bffcff16 100644
--- a/app/lib/admin/system_check/media_privacy_check.rb
+++ b/app/lib/admin/system_check/media_privacy_check.rb
@@ -13,7 +13,7 @@ class Admin::SystemCheck::MediaPrivacyCheck < Admin::SystemCheck::BaseCheck
end
def message
- Admin::SystemCheck::Message.new(@failure_message, @failure_value, @failure_action, true)
+ Admin::SystemCheck::Message.new(@failure_message, @failure_value, @failure_action, critical: true)
end
private
diff --git a/app/lib/admin/system_check/message.rb b/app/lib/admin/system_check/message.rb
index ad8d4b6073..60f14bdfd9 100644
--- a/app/lib/admin/system_check/message.rb
+++ b/app/lib/admin/system_check/message.rb
@@ -3,7 +3,7 @@
class Admin::SystemCheck::Message
attr_reader :key, :value, :action, :critical
- def initialize(key, value = nil, action = nil, critical = false)
+ def initialize(key, value = nil, action = nil, critical: false)
@key = key
@value = value
@action = action
diff --git a/app/lib/admin/system_check/sidekiq_process_check.rb b/app/lib/admin/system_check/sidekiq_process_check.rb
index 4c7447e4da..d0c57159a2 100644
--- a/app/lib/admin/system_check/sidekiq_process_check.rb
+++ b/app/lib/admin/system_check/sidekiq_process_check.rb
@@ -9,6 +9,7 @@ class Admin::SystemCheck::SidekiqProcessCheck < Admin::SystemCheck::BaseCheck
scheduler
ingress
perishable
+ fasp
).freeze
def skip?
diff --git a/app/lib/admin/system_check/software_version_check.rb b/app/lib/admin/system_check/software_version_check.rb
index e5cacfe354..014c3290d8 100644
--- a/app/lib/admin/system_check/software_version_check.rb
+++ b/app/lib/admin/system_check/software_version_check.rb
@@ -13,7 +13,7 @@ class Admin::SystemCheck::SoftwareVersionCheck < Admin::SystemCheck::BaseCheck
def message
if software_updates.any?(&:urgent?)
- Admin::SystemCheck::Message.new(:software_version_critical_check, nil, admin_software_updates_path, true)
+ Admin::SystemCheck::Message.new(:software_version_critical_check, nil, admin_software_updates_path, critical: true)
elsif software_updates.any?(&:patch_type?)
Admin::SystemCheck::Message.new(:software_version_patch_check, nil, admin_software_updates_path)
else
diff --git a/app/lib/request.rb b/app/lib/request.rb
index 212acf64d0..4858aa4bc2 100644
--- a/app/lib/request.rb
+++ b/app/lib/request.rb
@@ -20,7 +20,7 @@ class PerOperationWithDeadline < HTTP::Timeout::PerOperation
@read_deadline = options.fetch(:read_deadline, READ_DEADLINE)
end
- def connect(socket_class, host, port, nodelay = false)
+ def connect(socket_class, host, port, nodelay = false) # rubocop:disable Style/OptionalBooleanParameter
@socket = socket_class.open(host, port)
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if nodelay
end
diff --git a/app/lib/signed_request.rb b/app/lib/signed_request.rb
new file mode 100644
index 0000000000..066411c680
--- /dev/null
+++ b/app/lib/signed_request.rb
@@ -0,0 +1,270 @@
+# frozen_string_literal: true
+
+class SignedRequest
+ include DomainControlHelper
+
+ EXPIRATION_WINDOW_LIMIT = 12.hours
+ CLOCK_SKEW_MARGIN = 1.hour
+
+ class HttpSignature
+ REQUIRED_PARAMETERS = %w(keyId signature).freeze
+
+ def initialize(request)
+ @request = request
+ end
+
+ def key_id
+ signature_params['keyId']
+ end
+
+ def missing_signature_parameters
+ REQUIRED_PARAMETERS if REQUIRED_PARAMETERS.any? { |p| signature_params[p].blank? }
+ end
+
+ def algorithm_supported?
+ %w(rsa-sha256 hs2019).include?(signature_algorithm)
+ end
+
+ def verified?(actor)
+ signature = Base64.decode64(signature_params['signature'])
+ compare_signed_string = build_signed_string(include_query_string: true)
+
+ return true unless verify_signature(actor, signature, compare_signed_string).nil?
+
+ compare_signed_string = build_signed_string(include_query_string: false)
+ return true unless verify_signature(actor, signature, compare_signed_string).nil?
+
+ false
+ end
+
+ def created_time
+ if signature_algorithm == 'hs2019' && signature_params['created'].present?
+ Time.at(signature_params['created'].to_i).utc
+ elsif @request.headers['Date'].present?
+ Time.httpdate(@request.headers['Date']).utc
+ end
+ rescue ArgumentError => e
+ raise Mastodon::SignatureVerificationError, "Invalid Date header: #{e.message}"
+ end
+
+ def expires_time
+ Time.at(signature_params['expires'].to_i).utc if signature_params['expires'].present?
+ rescue ArgumentError => e
+ raise Mastodon::SignatureVerificationError, "Invalid Date header: #{e.message}"
+ end
+
+ def verify_signature_strength!
+ raise Mastodon::SignatureVerificationError, 'Mastodon requires the Date header or (created) pseudo-header to be signed' unless signed_headers.include?('date') || signed_headers.include?('(created)')
+ raise Mastodon::SignatureVerificationError, 'Mastodon requires the Digest header or (request-target) pseudo-header to be signed' unless signed_headers.include?(HttpSignatureDraft::REQUEST_TARGET) || signed_headers.include?('digest')
+ raise Mastodon::SignatureVerificationError, 'Mastodon requires the Host header to be signed when doing a GET request' if @request.get? && !signed_headers.include?('host')
+ raise Mastodon::SignatureVerificationError, 'Mastodon requires the Digest header to be signed when doing a POST request' if @request.post? && !signed_headers.include?('digest')
+ end
+
+ def verify_body_digest!
+ return unless signed_headers.include?('digest')
+ raise Mastodon::SignatureVerificationError, 'Digest header missing' unless @request.headers.key?('Digest')
+
+ digests = @request.headers['Digest'].split(',').map { |digest| digest.split('=', 2) }.map { |key, value| [key.downcase, value] }
+ sha256 = digests.assoc('sha-256')
+ raise Mastodon::SignatureVerificationError, "Mastodon only supports SHA-256 in Digest header. Offered algorithms: #{digests.map(&:first).join(', ')}" if sha256.nil?
+
+ return if body_digest == sha256[1]
+
+ digest_size = begin
+ Base64.strict_decode64(sha256[1].strip).length
+ rescue ArgumentError
+ raise Mastodon::SignatureVerificationError, "Invalid Digest value. The provided Digest value is not a valid base64 string. Given digest: #{sha256[1]}"
+ end
+
+ raise Mastodon::SignatureVerificationError, "Invalid Digest value. The provided Digest value is not a SHA-256 digest. Given digest: #{sha256[1]}" if digest_size != 32
+
+ raise Mastodon::SignatureVerificationError, "Invalid Digest value. Computed SHA-256 digest: #{body_digest}; given: #{sha256[1]}"
+ end
+
+ private
+
+ def request_body
+ @request_body ||= @request.raw_post
+ end
+
+ def signature_params
+ @signature_params ||= SignatureParser.parse(@request.headers['Signature'])
+ rescue SignatureParser::ParsingError
+ raise Mastodon::SignatureVerificationError, 'Error parsing signature parameters'
+ end
+
+ def signature_algorithm
+ signature_params.fetch('algorithm', 'hs2019')
+ end
+
+ def signed_headers
+ signature_params.fetch('headers', signature_algorithm == 'hs2019' ? '(created)' : 'date').downcase.split
+ end
+
+ def verify_signature(actor, signature, compare_signed_string)
+ true if actor.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), signature, compare_signed_string)
+ rescue OpenSSL::PKey::RSAError
+ nil
+ end
+
+ def build_signed_string(include_query_string: true)
+ signed_headers.map do |signed_header|
+ case signed_header
+ when HttpSignatureDraft::REQUEST_TARGET
+ if include_query_string
+ "#{HttpSignatureDraft::REQUEST_TARGET}: #{@request.method.downcase} #{@request.original_fullpath}"
+ else
+ # Current versions of Mastodon incorrectly omit the query string from the (request-target) pseudo-header.
+ # Therefore, temporarily support such incorrect signatures for compatibility.
+ # TODO: remove eventually some time after release of the fixed version
+ "#{HttpSignatureDraft::REQUEST_TARGET}: #{@request.method.downcase} #{@request.path}"
+ end
+ when '(created)'
+ raise Mastodon::SignatureVerificationError, 'Invalid pseudo-header (created) for rsa-sha256' unless signature_algorithm == 'hs2019'
+ raise Mastodon::SignatureVerificationError, 'Pseudo-header (created) used but corresponding argument missing' if signature_params['created'].blank?
+
+ "(created): #{signature_params['created']}"
+ when '(expires)'
+ raise Mastodon::SignatureVerificationError, 'Invalid pseudo-header (expires) for rsa-sha256' unless signature_algorithm == 'hs2019'
+ raise Mastodon::SignatureVerificationError, 'Pseudo-header (expires) used but corresponding argument missing' if signature_params['expires'].blank?
+
+ "(expires): #{signature_params['expires']}"
+ else
+ "#{signed_header}: #{@request.headers[to_header_name(signed_header)]}"
+ end
+ end.join("\n")
+ end
+
+ def body_digest
+ @body_digest ||= Digest::SHA256.base64digest(request_body)
+ end
+
+ def to_header_name(name)
+ name.split('-').map(&:capitalize).join('-')
+ end
+ end
+
+ class HttpMessageSignature
+ REQUIRED_PARAMETERS = %w(keyid created).freeze
+
+ def initialize(request)
+ @request = request
+ @signature = Linzer::Signature.build({
+ 'signature-input' => @request.headers['signature-input'],
+ 'signature' => @request.headers['signature'],
+ })
+ end
+
+ def key_id
+ @signature.parameters['keyid']
+ end
+
+ def missing_signature_parameters
+ REQUIRED_PARAMETERS if REQUIRED_PARAMETERS.any? { |p| @signature.parameters[p].blank? }
+ end
+
+ # This method can lie as we only support one specific algorith for now.
+ # But HTTP Message Signatures do not need to specify an algorithm (as
+ # this can be inferred from the key used). Using an unsupported
+ # algorithm will fail anyway further down the line.
+ def algorithm_supported?
+ true
+ end
+
+ def verified?(actor)
+ key = Linzer.new_rsa_v1_5_sha256_public_key(actor.public_key)
+
+ Linzer.verify!(@request.rack_request, key:)
+ rescue Linzer::VerifyError
+ false
+ end
+
+ def verify_signature_strength!
+ raise Mastodon::SignatureVerificationError, 'Mastodon requires the (created) parameter to be signed' if @signature.parameters['created'].blank?
+ raise Mastodon::SignatureVerificationError, 'Mastodon requires the @method and @target-uri derived components to be signed' unless @signature.components.include?('@method') && @signature.components.include?('@target-uri')
+ raise Mastodon::SignatureVerificationError, 'Mastodon requires the Content-Digest header to be signed when doing a POST request' if @request.post? && !signed_headers.include?('content-digest')
+ end
+
+ def verify_body_digest!
+ return unless signed_headers.include?('content-digest')
+ raise Mastodon::SignatureVerificationError, 'Content-Digest header missing' unless @request.headers.key?('content-digest')
+
+ digests = Starry.parse_dictionary(@request.headers['content-digest'])
+ raise Mastodon::SignatureVerificationError, "Mastodon only supports SHA-256 in Content-Digest header. Offered algorithms: #{digests.keys.join(', ')}" unless digests.key?('sha-256')
+
+ received_digest = Base64.strict_encode64(digests['sha-256'].value)
+ return if body_digest == received_digest
+
+ raise Mastodon::SignatureVerificationError, "Invalid Digest value. Computed SHA-256 digest: #{body_digest}; given: #{received_digest}"
+ end
+
+ def created_time
+ Time.at(@signature.parameters['created'].to_i).utc
+ rescue ArgumentError => e
+ raise Mastodon::SignatureVerificationError, "Invalid Date header: #{e.message}"
+ end
+
+ def expires_time
+ Time.at(@signature.parameters['expires'].to_i).utc if @signature.parameters['expires'].present?
+ rescue ArgumentError => e
+ raise Mastodon::SignatureVerificationError, "Invalid Date header: #{e.message}"
+ end
+
+ private
+
+ def request_body
+ @request_body ||= @request.raw_post
+ end
+
+ def signed_headers
+ @signed_headers ||= @signature.components.reject { |c| c.start_with?('@') }
+ end
+
+ def body_digest
+ @body_digest ||= Digest::SHA256.base64digest(request_body)
+ end
+
+ def missing_required_signature_parameters?
+ @signature.parameters['keyid'].blank?
+ end
+ end
+
+ attr_reader :signature
+
+ delegate :key_id, to: :signature
+
+ def initialize(request)
+ @signature =
+ if Mastodon::Feature.http_message_signatures_enabled? && request.headers['signature-input'].present?
+ HttpMessageSignature.new(request)
+ else
+ HttpSignature.new(request)
+ end
+ end
+
+ def verified?(actor)
+ missing_signature_parameters = @signature.missing_signature_parameters
+ raise Mastodon::SignatureVerificationError, "Incompatible request signature. #{missing_signature_parameters.to_sentence} are required" if missing_signature_parameters
+ raise Mastodon::SignatureVerificationError, 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)' unless @signature.algorithm_supported?
+ raise Mastodon::SignatureVerificationError, 'Signed request date outside acceptable time window' unless matches_time_window?
+
+ @signature.verify_signature_strength!
+ @signature.verify_body_digest!
+ @signature.verified?(actor)
+ end
+
+ private
+
+ def matches_time_window?
+ created_time = @signature.created_time
+ expires_time = @signature.expires_time
+
+ expires_time ||= created_time + 5.minutes unless created_time.nil?
+ expires_time = [expires_time, created_time + EXPIRATION_WINDOW_LIMIT].min unless created_time.nil?
+
+ return false if created_time.present? && created_time > Time.now.utc + CLOCK_SKEW_MARGIN
+ return false if expires_time.present? && Time.now.utc > expires_time + CLOCK_SKEW_MARGIN
+
+ true
+ end
+end
diff --git a/app/lib/status_cache_hydrator.rb b/app/lib/status_cache_hydrator.rb
index c9b52c53b3..61ffde0d9d 100644
--- a/app/lib/status_cache_hydrator.rb
+++ b/app/lib/status_cache_hydrator.rb
@@ -5,7 +5,7 @@ class StatusCacheHydrator
@status = status
end
- def hydrate(account_id)
+ def hydrate(account_id, nested: false)
account = Account.find(account_id)
# The cache of the serialized hash is generated by the fan-out-on-write service
@@ -18,17 +18,17 @@ class StatusCacheHydrator
# We take advantage of the fact that some relationships can only occur with an original status, not
# the reblog that wraps it, so we can assume that some values are always false
if payload[:reblog]
- hydrate_reblog_payload(payload, account_id, account)
+ hydrate_reblog_payload(payload, account_id, account, nested:)
else
- hydrate_non_reblog_payload(payload, account_id, account)
+ hydrate_non_reblog_payload(payload, account_id, account, nested:)
end
end
private
- def hydrate_non_reblog_payload(empty_payload, account_id, account)
+ def hydrate_non_reblog_payload(empty_payload, account_id, account, nested: false)
empty_payload.tap do |payload|
- fill_status_payload(payload, @status, account_id, account)
+ fill_status_payload(payload, @status, account_id, account, nested:)
if payload[:poll]
payload[:poll][:voted] = @status.account_id == account_id
@@ -37,7 +37,7 @@ class StatusCacheHydrator
end
end
- def hydrate_reblog_payload(empty_payload, account_id, account)
+ def hydrate_reblog_payload(empty_payload, account_id, account, nested: false)
empty_payload.tap do |payload|
payload[:muted] = false
payload[:bookmarked] = false
@@ -47,7 +47,7 @@ class StatusCacheHydrator
# used to create the status, we need to hydrate it here too
payload[:reblog][:application] = payload_reblog_application if payload[:reblog][:application].nil? && @status.reblog.account_id == account_id
- fill_status_payload(payload[:reblog], @status.reblog, account_id, account)
+ fill_status_payload(payload[:reblog], @status.reblog, account_id, account, nested:)
if payload[:reblog][:poll]
if @status.reblog.account_id == account_id
@@ -66,7 +66,7 @@ class StatusCacheHydrator
end
end
- def fill_status_payload(payload, status, account_id, account)
+ def fill_status_payload(payload, status, account_id, account, nested: false)
payload[:favourited] = Favourite.exists?(account_id: account_id, status_id: status.id)
payload[:reblogged] = Status.exists?(account_id: account_id, reblog_of_id: status.id)
payload[:muted] = ConversationMute.exists?(account_id: account_id, conversation_id: status.conversation_id)
@@ -74,21 +74,33 @@ class StatusCacheHydrator
payload[:pinned] = StatusPin.exists?(account_id: account_id, status_id: status.id) if status.account_id == account_id
payload[:filtered] = mapped_applied_custom_filter(account_id, status)
payload[:emoji_reactions] = status.emoji_reactions_grouped_by_name(account)
- payload[:quote] = hydrate_quote_payload(payload[:quote], status.quote, account_id) if payload[:quote]
+ payload[:quote] = hydrate_quote_payload(payload[:quote], status.quote, account_id, nested:) if payload[:quote]
end
- def hydrate_quote_payload(empty_payload, quote, account_id)
- # TODO: properly handle quotes, including visibility and access control
+ def hydrate_quote_payload(empty_payload, quote, account_id, nested: false)
+ return unless quote&.acceptable?
empty_payload.tap do |payload|
- # Nothing to do if we're in the shallow (depth limit) case
- next unless payload.key?(:quoted_status)
+ payload.delete(:quoted_status) if nested
- # TODO: handle hiding a rendered status or showing a non-rendered status according to visibility
- if quote&.quoted_status.nil?
- payload[:quoted_status] = nil
- elsif payload[:quoted_status].present?
- payload[:quoted_status] = StatusCacheHydrator.new(quote.quoted_status).hydrate(account_id)
+ # TODO: performance improvements
+ if quote.accepted?
+ if quote.quoted_status.nil?
+ payload[nested ? :quoted_status_id : :quoted_status] = nil
+ payload[:state] = 'deleted'
+ elsif StatusFilter.new(quote.quoted_status, Account.find_by(id: account_id)).filtered?
+ payload[nested ? :quoted_status_id : :quoted_status] = nil
+ payload[:state] = 'unauthorized'
+ else
+ payload[:state] = 'accepted'
+ if nested
+ payload[:quoted_status_id] = quote.quoted_status_id&.to_s
+ else
+ payload[:quoted_status] = StatusCacheHydrator.new(quote.quoted_status).hydrate(account_id, nested: true)
+ end
+ end
+ else
+ payload[nested ? :quoted_status_id : :quoted_status] = nil
end
end
end
diff --git a/app/lib/status_filter.rb b/app/lib/status_filter.rb
index 70656f9c8d..8df80b7d63 100644
--- a/app/lib/status_filter.rb
+++ b/app/lib/status_filter.rb
@@ -39,7 +39,7 @@ class StatusFilter
end
def silenced_account?
- !account&.silenced? && status_account_silenced? && !account_following_status_account?
+ status_account_silenced? && !account_following_status_account?
end
def status_account_silenced?
diff --git a/app/lib/text_formatter.rb b/app/lib/text_formatter.rb
index 6ff4ea5651..6337a60230 100644
--- a/app/lib/text_formatter.rb
+++ b/app/lib/text_formatter.rb
@@ -34,26 +34,21 @@ class TextFormatter
def to_s
return ''.html_safe if text.blank?
- html = nil
- MastodonOTELTracer.in_span('TextFormatter#to_s extract_and_rewrite') do
- html = rewrite do |entity|
- if entity[:url]
- link_to_url(entity)
- elsif entity[:hashtag]
- link_to_hashtag(entity)
- elsif entity[:screen_name]
- link_to_mention(entity)
- end
+ html = rewrite do |entity|
+ if entity[:url]
+ link_to_url(entity)
+ elsif entity[:hashtag]
+ link_to_hashtag(entity)
+ elsif entity[:screen_name]
+ link_to_mention(entity)
end
end
- MastodonOTELTracer.in_span('TextFormatter#to_s simple_format') do
- # line first letter for blockquote
- html = markdownify(html.gsub(/^>/, '>')) if markdown?
+ # line first letter for blockquote
+ html = markdownify(html.gsub(/^>/, '>')) if markdown?
- html = simple_format(html, {}, sanitize: false).delete("\n") if !markdown? && multiline?
- html = html.delete("\n")
- end
+ html = simple_format(html, {}, sanitize: false).delete("\n") if !markdown? && multiline?
+ html = html.delete("\n")
html.html_safe # rubocop:disable Rails/OutputSafety
end
@@ -109,54 +104,48 @@ class TextFormatter
end
def link_to_url(entity)
- MastodonOTELTracer.in_span('TextFormatter#link_to_url') do
- TextFormatter.shortened_link(entity[:url], rel_me: with_rel_me?)
- end
+ TextFormatter.shortened_link(entity[:url], rel_me: with_rel_me?)
end
def link_to_hashtag(entity)
- MastodonOTELTracer.in_span('TextFormatter#link_to_hashtag') do
- hashtag = entity[:hashtag]
- url = tag_url(hashtag)
+ hashtag = entity[:hashtag]
+ url = tag_url(hashtag)
- <<~HTML.squish
- ##{h(hashtag)}
- HTML
- end
+ <<~HTML.squish
+ ##{h(hashtag)}
+ HTML
end
def link_to_mention(entity)
- MastodonOTELTracer.in_span('TextFormatter#link_to_mention') do
- username, domain = entity[:screen_name].split('@')
- domain = nil if local_domain?(domain)
- account = nil
+ username, domain = entity[:screen_name].split('@')
+ domain = nil if local_domain?(domain)
+ account = nil
- if preloaded_accounts?
- same_username_hits = 0
+ if preloaded_accounts?
+ same_username_hits = 0
- preloaded_accounts.each do |other_account|
- same_username = other_account.username.casecmp(username).zero?
- same_domain = other_account.domain.nil? ? domain.nil? : other_account.domain.casecmp(domain)&.zero?
+ preloaded_accounts.each do |other_account|
+ same_username = other_account.username.casecmp(username).zero?
+ same_domain = other_account.domain.nil? ? domain.nil? : other_account.domain.casecmp(domain)&.zero?
- if same_username && !same_domain
- same_username_hits += 1
- elsif same_username && same_domain
- account = other_account
- end
+ if same_username && !same_domain
+ same_username_hits += 1
+ elsif same_username && same_domain
+ account = other_account
end
- else
- account = entity_cache.mention(username, domain)
end
-
- return "@#{h(entity[:screen_name])}" if account.nil?
-
- url = ActivityPub::TagManager.instance.url_for(account)
- display_username = same_username_hits&.positive? || with_domains? ? account.pretty_acct : account.username
-
- <<~HTML.squish
- @#{h(display_username)}
- HTML
+ else
+ account = entity_cache.mention(username, domain)
end
+
+ return "@#{h(entity[:screen_name])}" if account.nil?
+
+ url = ActivityPub::TagManager.instance.url_for(account)
+ display_username = same_username_hits&.positive? || with_domains? ? account.pretty_acct : account.username
+
+ <<~HTML.squish
+ @#{h(display_username)}
+ HTML
end
def entity_cache
diff --git a/app/lib/web_push_request.rb b/app/lib/web_push_request.rb
index 85e8ab6bb5..416a629429 100644
--- a/app/lib/web_push_request.rb
+++ b/app/lib/web_push_request.rb
@@ -79,8 +79,8 @@ class WebPushRequest
def vapid_key
@vapid_key ||= Webpush::VapidKey.from_keys(
- Rails.configuration.x.vapid_public_key,
- Rails.configuration.x.vapid_private_key
+ Rails.configuration.x.vapid.public_key,
+ Rails.configuration.x.vapid.private_key
)
end
diff --git a/app/lib/webfinger.rb b/app/lib/webfinger.rb
index 01a5dbc21d..83b5415a33 100644
--- a/app/lib/webfinger.rb
+++ b/app/lib/webfinger.rb
@@ -65,7 +65,7 @@ class Webfinger
private
- def body_from_webfinger(url = standard_url, use_fallback = true)
+ def body_from_webfinger(url = standard_url, use_fallback: true)
webfinger_request(url).perform do |res|
if res.code == 200
body = res.body_with_limit
@@ -85,7 +85,7 @@ class Webfinger
def body_from_host_meta
host_meta_request.perform do |res|
if res.code == 200
- body_from_webfinger(url_from_template(res.body_with_limit), false)
+ body_from_webfinger(url_from_template(res.body_with_limit), use_fallback: false)
else
raise Webfinger::Error, "Request for #{@uri} returned HTTP #{res.code}"
end
diff --git a/app/models/concerns/attachmentable.rb b/app/models/concerns/attachmentable.rb
index a83e178fc4..783054b850 100644
--- a/app/models/concerns/attachmentable.rb
+++ b/app/models/concerns/attachmentable.rb
@@ -22,7 +22,7 @@ module Attachmentable
).freeze
included do
- def self.has_attached_file(name, options = {}) # rubocop:disable Naming/PredicateName
+ def self.has_attached_file(name, options = {}) # rubocop:disable Naming/PredicatePrefix
super
send(:"before_#{name}_validate", prepend: true) do
diff --git a/app/models/concerns/status/safe_reblog_insert.rb b/app/models/concerns/status/safe_reblog_insert.rb
index 48d585ea18..94bed5b39a 100644
--- a/app/models/concerns/status/safe_reblog_insert.rb
+++ b/app/models/concerns/status/safe_reblog_insert.rb
@@ -16,7 +16,7 @@ module Status::SafeReblogInsert
# The code is kept similar to ActiveRecord::Persistence code and calls it
# directly when we are not handling a reblog.
#
- # https://github.com/rails/rails/blob/v7.2.1.1/activerecord/lib/active_record/persistence.rb#L238-L263
+ # https://github.com/rails/rails/blob/v8.0.2/activerecord/lib/active_record/persistence.rb#L238-L261
def _insert_record(connection, values, returning)
return super unless values.is_a?(Hash) && values['reblog_of_id']&.value.present?
@@ -36,15 +36,13 @@ module Status::SafeReblogInsert
# Instead, we use a custom builder when a reblog is happening:
im = _compile_reblog_insert(values)
- with_connection do |_c|
- connection.insert(
- im, "#{self} Create", primary_key || false, primary_key_value,
- returning: returning
- ).tap do |result|
- # Since we are using SELECT instead of VALUES, a non-error `nil` return is possible.
- # For our purposes, it's equivalent to a foreign key constraint violation
- raise ActiveRecord::InvalidForeignKey, "(reblog_of_id)=(#{values['reblog_of_id'].value}) is not present in table \"statuses\"" if result.nil?
- end
+ connection.insert(
+ im, "#{self} Create", primary_key || false, primary_key_value,
+ returning: returning
+ ).tap do |result|
+ # Since we are using SELECT instead of VALUES, a non-error `nil` return is possible.
+ # For our purposes, it's equivalent to a foreign key constraint violation
+ raise ActiveRecord::InvalidForeignKey, "(reblog_of_id)=(#{values['reblog_of_id'].value}) is not present in table \"statuses\"" if result.nil?
end
end
diff --git a/app/models/featured_tag.rb b/app/models/featured_tag.rb
index 74fc72ba5c..9a91ab3bed 100644
--- a/app/models/featured_tag.rb
+++ b/app/models/featured_tag.rb
@@ -26,6 +26,8 @@ class FeaturedTag < ApplicationRecord
normalizes :name, with: ->(name) { name.strip.delete_prefix('#') }
+ scope :by_name, ->(name) { joins(:tag).where(tag: { name: HashtagNormalizer.new.normalize(name) }) }
+
before_validation :set_tag
before_create :reset_data
diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb
index 2256900d02..9331e52845 100644
--- a/app/models/media_attachment.rb
+++ b/app/models/media_attachment.rb
@@ -422,7 +422,7 @@ class MediaAttachment < ApplicationRecord
# Record the cache keys to burst before the file get actually deleted
def prepare_cache_bust!
- return unless Rails.configuration.x.cache_buster_enabled
+ return unless Rails.configuration.x.cache_buster.enabled
@paths_to_cache_bust = MediaAttachment.attachment_definitions.keys.flat_map do |attachment_name|
attachment = public_send(attachment_name)
@@ -439,7 +439,7 @@ class MediaAttachment < ApplicationRecord
# Once Paperclip has deleted the files, we can't recover the cache keys,
# so use the previously-saved ones
def bust_cache!
- return unless Rails.configuration.x.cache_buster_enabled
+ return unless Rails.configuration.x.cache_buster.enabled
CacheBusterWorker.push_bulk(@paths_to_cache_bust) { |path| [path] }
rescue => e
diff --git a/app/models/quote.rb b/app/models/quote.rb
index 8e21d9b481..c981591e17 100644
--- a/app/models/quote.rb
+++ b/app/models/quote.rb
@@ -7,6 +7,7 @@
# id :bigint(8) not null, primary key
# activity_uri :string
# approval_uri :string
+# legacy :boolean default(FALSE), not null
# state :integer default("pending"), not null
# created_at :datetime not null
# updated_at :datetime not null
@@ -46,6 +47,10 @@ class Quote < ApplicationRecord
end
end
+ def acceptable?
+ accepted? || !legacy?
+ end
+
def schedule_refresh_if_stale!
return unless quoted_status_id.present? && approval_uri.present? && updated_at <= BACKGROUND_REFRESH_INTERVAL.ago
diff --git a/app/models/trends/statuses.rb b/app/models/trends/statuses.rb
index 14c5acba75..e61e82252a 100644
--- a/app/models/trends/statuses.rb
+++ b/app/models/trends/statuses.rb
@@ -91,9 +91,14 @@ class Trends::Statuses < Trends::Base
def eligible?(status)
status.created_at.past? &&
(status.public_visibility? || status.public_unlisted_visibility?) &&
- status.account.discoverable? && !status.account.silenced? && !status.account.sensitized? &&
- status.spoiler_text.blank? && (!status.sensitive? || status.media_attachments.none?) &&
- !status.reply? && valid_locale?(status.language) && !domain_blocked?(status)
+ status.account.discoverable? &&
+ !status.account.silenced? &&
+ !status.account.sensitized? &&
+ status.spoiler_text.blank? &&
+ (!status.sensitive? || status.media_attachments.none?) &&
+ !status.reply? &&
+ valid_locale?(status.language) &&
+ !domain_blocked?(status)
end
def domain_blocked?(status)
diff --git a/app/models/user_settings/glue.rb b/app/models/user_settings/glue.rb
index 02066a4110..c5ee1283e2 100644
--- a/app/models/user_settings/glue.rb
+++ b/app/models/user_settings/glue.rb
@@ -17,7 +17,7 @@ module UserSettings::Glue
self.class.definition_for(key)&.type
end
- def has_attribute?(key) # rubocop:disable Naming/PredicateName
+ def has_attribute?(key) # rubocop:disable Naming/PredicatePrefix
self.class.definition_for?(key)
end
end
diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb
index ffac02be76..f52a97c4f6 100644
--- a/app/serializers/initial_state_serializer.rb
+++ b/app/serializers/initial_state_serializer.rb
@@ -26,7 +26,7 @@ class InitialStateSerializer < ActiveModel::Serializer
store[:display_media] = object_account_user.setting_display_media
store[:expand_spoilers] = object_account_user.setting_expand_spoilers
store[:enable_emoji_reaction] = object_account_user.setting_enable_emoji_reaction && Setting.enable_emoji_reaction
- store[:enable_dtl_menu] = object_account_user.setting_enable_dtl_menu
+ store[:enable_dtl_menu] = object_account_user.setting_enable_dtl_menu && dtl_enabled?
store[:reduce_motion] = object_account_user.setting_reduce_motion
store[:disable_swiping] = object_account_user.setting_disable_swiping
store[:disable_hover_cards] = object_account_user.setting_disable_hover_cards
diff --git a/app/serializers/rest/application_serializer.rb b/app/serializers/rest/application_serializer.rb
index 1a7b9265f1..96573c94f2 100644
--- a/app/serializers/rest/application_serializer.rb
+++ b/app/serializers/rest/application_serializer.rb
@@ -18,6 +18,6 @@ class REST::ApplicationSerializer < ActiveModel::Serializer
end
def vapid_key
- Rails.configuration.x.vapid_public_key
+ Rails.configuration.x.vapid.public_key
end
end
diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb
index 2b41d21b7a..6974a764bd 100644
--- a/app/serializers/rest/instance_serializer.rb
+++ b/app/serializers/rest/instance_serializer.rb
@@ -67,7 +67,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
},
vapid: {
- public_key: Rails.configuration.x.vapid_public_key,
+ public_key: Rails.configuration.x.vapid.public_key,
},
accounts: {
diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb
index f17807f55b..71e3eec251 100644
--- a/app/serializers/rest/status_serializer.rb
+++ b/app/serializers/rest/status_serializer.rb
@@ -37,6 +37,10 @@ class REST::StatusSerializer < ActiveModel::Serializer
has_one :preview_card, key: :card, serializer: REST::PreviewCardSerializer
has_one :preloadable_poll, key: :poll, serializer: REST::PollSerializer
+ def quote
+ object.quote if object.quote&.acceptable?
+ end
+
def id
object.id.to_s
end
diff --git a/app/serializers/rest/web_push_subscription_serializer.rb b/app/serializers/rest/web_push_subscription_serializer.rb
index 01825a3bb0..11893f7c48 100644
--- a/app/serializers/rest/web_push_subscription_serializer.rb
+++ b/app/serializers/rest/web_push_subscription_serializer.rb
@@ -10,7 +10,7 @@ class REST::WebPushSubscriptionSerializer < ActiveModel::Serializer
end
def server_key
- Rails.configuration.x.vapid_public_key
+ Rails.configuration.x.vapid.public_key
end
def policy
diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb
index 0f527eea2e..5cd73f703c 100644
--- a/app/services/activitypub/process_status_update_service.rb
+++ b/app/services/activitypub/process_status_update_service.rb
@@ -369,8 +369,6 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
end
def update_quote!
- return unless Mastodon::Feature.inbound_quotes_enabled?
-
quote_uri = @status_parser.quote_uri
if quote_uri.present?
@@ -381,14 +379,14 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
# If the quoted post has changed, discard the old object and create a new one
if @status.quote.quoted_status.present? && ActivityPub::TagManager.instance.uri_for(@status.quote.quoted_status) != quote_uri
@status.quote.destroy
- quote = Quote.create(status: @status, approval_uri: approval_uri)
+ quote = Quote.create(status: @status, approval_uri: approval_uri, legacy: @status_parser.legacy_quote?)
@quote_changed = true
else
quote = @status.quote
- quote.update(approval_uri: approval_uri, state: :pending) if quote.approval_uri != @status_parser.quote_approval_uri
+ quote.update(approval_uri: approval_uri, state: :pending, legacy: @status_parser.legacy_quote?) if quote.approval_uri != @status_parser.quote_approval_uri
end
else
- quote = Quote.create(status: @status, approval_uri: approval_uri)
+ quote = Quote.create(status: @status, approval_uri: approval_uri, legacy: @status_parser.legacy_quote?)
@quote_changed = true
end
diff --git a/app/services/block_domain_service.rb b/app/services/block_domain_service.rb
index 1db15d9ccb..715aba6bee 100644
--- a/app/services/block_domain_service.rb
+++ b/app/services/block_domain_service.rb
@@ -3,7 +3,7 @@
class BlockDomainService < BaseService
attr_reader :domain_block
- def call(domain_block, update = false)
+ def call(domain_block, update: false)
@domain_block = domain_block
@domain_block_event = nil
diff --git a/app/services/fetch_resource_service.rb b/app/services/fetch_resource_service.rb
index 3fde78455c..2382e126bd 100644
--- a/app/services/fetch_resource_service.rb
+++ b/app/services/fetch_resource_service.rb
@@ -22,7 +22,7 @@ class FetchResourceService < BaseService
def process(url, terminal: false)
@url = url
- perform_request { |response| process_response(response, terminal) }
+ perform_request { |response| process_response(response, terminal:) }
end
def perform_request(&block)
@@ -40,7 +40,7 @@ class FetchResourceService < BaseService
end.perform(&block)
end
- def process_response(response, terminal = false)
+ def process_response(response, terminal: false)
@response_code = response.code
return nil if response.code != 200
diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb
index 9c6efb17b5..013a4afa01 100644
--- a/app/services/post_status_service.rb
+++ b/app/services/post_status_service.rb
@@ -185,7 +185,8 @@ class PostStatusService < BaseService
# we only support incoming quotes so far
status.quote = Quote.new(quoted_status: @quoted_status)
- status.quote.accept! if @status.account == @quoted_status.account || @quoted_status.active_mentions.exists?(mentions: { account_id: status.account_id })
+ status.quote.accept!
+ # status.quote.accept! if @status.account == @quoted_status.account || @quoted_status.active_mentions.exists?(mentions: { account_id: status.account_id })
# TODO: the following has yet to be implemented:
# - handle approval of local users (requires the interactionPolicy PR)
diff --git a/app/services/suspend_account_service.rb b/app/services/suspend_account_service.rb
index 3934a738f7..1b9d051b38 100644
--- a/app/services/suspend_account_service.rb
+++ b/app/services/suspend_account_service.rb
@@ -15,6 +15,7 @@ class SuspendAccountService < BaseService
unmerge_from_home_timelines!
unmerge_from_list_timelines!
privatize_media_attachments!
+ remove_from_trends!
end
private
@@ -95,12 +96,16 @@ class SuspendAccountService < BaseService
end
end
- CacheBusterWorker.perform_async(attachment.url(style)) if Rails.configuration.x.cache_buster_enabled
+ CacheBusterWorker.perform_async(attachment.url(style)) if Rails.configuration.x.cache_buster.enabled
end
end
end
end
+ def remove_from_trends!
+ StatusTrend.where(account: @account).delete_all
+ end
+
def signed_activity_json
@signed_activity_json ||= Oj.dump(serialize_payload(@account, ActivityPub::UpdateSerializer, signer: @account))
end
diff --git a/app/services/unsuspend_account_service.rb b/app/services/unsuspend_account_service.rb
index 7d3bb806a6..8ad01737ae 100644
--- a/app/services/unsuspend_account_service.rb
+++ b/app/services/unsuspend_account_service.rb
@@ -91,7 +91,7 @@ class UnsuspendAccountService < BaseService
end
end
- CacheBusterWorker.perform_async(attachment.url(style)) if Rails.configuration.x.cache_buster_enabled
+ CacheBusterWorker.perform_async(attachment.url(style)) if Rails.configuration.x.cache_buster.enabled
end
end
end
diff --git a/app/views/shared/_web_app.html.haml b/app/views/shared/_web_app.html.haml
index 25bd7926ce..5e6815165f 100644
--- a/app/views/shared/_web_app.html.haml
+++ b/app/views/shared/_web_app.html.haml
@@ -3,7 +3,7 @@
- if user_signed_in?
%meta{ name: 'initialPath', content: request.path }
- %meta{ name: 'applicationServerKey', content: Rails.configuration.x.vapid_public_key }
+ %meta{ name: 'applicationServerKey', content: Rails.configuration.x.vapid.public_key }
= render_initial_state
= vite_typescript_tag 'application.ts', crossorigin: 'anonymous'
diff --git a/app/views/user_mailer/welcome.html.haml b/app/views/user_mailer/welcome.html.haml
index c37104da79..fd37cfe26a 100644
--- a/app/views/user_mailer/welcome.html.haml
+++ b/app/views/user_mailer/welcome.html.haml
@@ -28,7 +28,6 @@
= render 'application/mailer/checklist', key: 'edit_profile', checked: @has_account_fields, button_text: t('user_mailer.welcome.edit_profile_action'), button_url: web_url('start/profile')
= render 'application/mailer/checklist', key: 'follow', checked: @has_active_relationships, button_text: t('user_mailer.welcome.follow_action'), button_url: web_url('start/follows')
= render 'application/mailer/checklist', key: 'post', checked: @has_statuses, button_text: t('user_mailer.welcome.post_action'), button_url: web_url
- = render 'application/mailer/checklist', key: 'share', checked: false, button_text: t('user_mailer.welcome.share_action'), button_url: web_url('start/share')
= render 'application/mailer/checklist', key: 'apps', checked: false, show_apps_buttons: true
%table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' }
%tr
diff --git a/app/views/user_mailer/welcome.text.erb b/app/views/user_mailer/welcome.text.erb
index 383f436f8e..ac7c0f09f2 100644
--- a/app/views/user_mailer/welcome.text.erb
+++ b/app/views/user_mailer/welcome.text.erb
@@ -24,11 +24,7 @@
<%= t('user_mailer.welcome.post_step') %>
* <%= web_url %>
-4. <%= t('user_mailer.welcome.share_title') %>
- <%= t('user_mailer.welcome.share_step') %>
- * <%= web_url('start/share') %>
-
-5. <%= t('user_mailer.welcome.apps_title') %>
+4. <%= t('user_mailer.welcome.apps_title') %>
<%= t('user_mailer.welcome.apps_step') %>
* iOS: <%= app_store_url_ios %>
* Android: <%= app_store_url_android %>
diff --git a/app/workers/domain_block_worker.rb b/app/workers/domain_block_worker.rb
index 3c601cd832..d89eba7c13 100644
--- a/app/workers/domain_block_worker.rb
+++ b/app/workers/domain_block_worker.rb
@@ -3,10 +3,10 @@
class DomainBlockWorker
include Sidekiq::Worker
- def perform(domain_block_id, update = false)
+ def perform(domain_block_id, update = false) # rubocop:disable Style/OptionalBooleanParameter
domain_block = DomainBlock.find_by(id: domain_block_id)
return true if domain_block.nil?
- BlockDomainService.new.call(domain_block, update)
+ BlockDomainService.new.call(domain_block, update:)
end
end
diff --git a/app/workers/unfollow_follow_worker.rb b/app/workers/unfollow_follow_worker.rb
index a4d57839de..accfcc903e 100644
--- a/app/workers/unfollow_follow_worker.rb
+++ b/app/workers/unfollow_follow_worker.rb
@@ -5,7 +5,7 @@ class UnfollowFollowWorker
sidekiq_options queue: 'pull'
- def perform(follower_account_id, old_target_account_id, new_target_account_id, bypass_locked = false)
+ def perform(follower_account_id, old_target_account_id, new_target_account_id, bypass_locked = false) # rubocop:disable Style/OptionalBooleanParameter
follower_account = Account.find(follower_account_id)
old_target_account = Account.find(old_target_account_id)
new_target_account = Account.find(new_target_account_id)
diff --git a/config/application.rb b/config/application.rb
index 675a3c0c19..1195b9215c 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -103,9 +103,11 @@ module Mastodon
end
end
+ config.x.cache_buster = config_for(:cache_buster)
config.x.captcha = config_for(:captcha)
config.x.mastodon = config_for(:mastodon)
config.x.translation = config_for(:translation)
+ config.x.vapid = config_for(:vapid)
if ENV.fetch('QUERY_LOG_TAGS_ENABLED', 'false') == 'true'
config.active_record.query_log_tags_enabled = ENV.fetch('QUERY_LOG_TAGS_ENABLED', 'false') == 'true'
diff --git a/config/cache_buster.yml b/config/cache_buster.yml
new file mode 100644
index 0000000000..709c0eba88
--- /dev/null
+++ b/config/cache_buster.yml
@@ -0,0 +1,5 @@
+shared:
+ enabled: <%= ENV.fetch('CACHE_BUSTER_ENABLED', 'false') == 'true' %>
+ secret_header: <%= ENV.fetch('CACHE_BUSTER_SECRET_HEADER', nil) %>
+ secret: <%= ENV.fetch('CACHE_BUSTER_SECRET', nil) %>
+ http_method: <%= ENV.fetch('CACHE_BUSTER_HTTP_METHOD', 'GET') %>
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 4f43259d7d..ca9e876e26 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -40,10 +40,10 @@ Rails.application.configure do
# Override default file logging in favor of STDOUT logging in dev environment
config.logger = ActiveSupport::TaggedLogging.logger($stdout, formatter: config.log_formatter)
- # Generate random VAPID keys
+ # Generate random VAPID keys when needed
Webpush.generate_key.tap do |vapid_key|
- config.x.vapid_private_key = vapid_key.private_key
- config.x.vapid_public_key = vapid_key.public_key
+ config.x.vapid.private_key ||= vapid_key.private_key
+ config.x.vapid.public_key ||= vapid_key.public_key
end
# Don't care if the mailer can't send.
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 14f0e6d085..efe422c3c8 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -113,7 +113,7 @@ Rails.application.configure do
enable_starttls = nil
enable_starttls_auto = nil
- case ENV['SMTP_ENABLE_STARTTLS']
+ case ENV.fetch('SMTP_ENABLE_STARTTLS', nil)
when 'always'
enable_starttls = true
when 'never'
@@ -125,14 +125,14 @@ Rails.application.configure do
end
config.action_mailer.smtp_settings = {
- port: ENV['SMTP_PORT'],
- address: ENV['SMTP_SERVER'],
+ port: ENV.fetch('SMTP_PORT', nil),
+ address: ENV.fetch('SMTP_SERVER', nil),
user_name: ENV['SMTP_LOGIN'].presence,
password: ENV['SMTP_PASSWORD'].presence,
- domain: ENV['SMTP_DOMAIN'] || ENV['LOCAL_DOMAIN'],
+ domain: ENV['SMTP_DOMAIN'] || ENV.fetch('LOCAL_DOMAIN', nil),
authentication: ENV['SMTP_AUTH_METHOD'] == 'none' ? nil : ENV['SMTP_AUTH_METHOD'] || :plain,
ca_file: ENV['SMTP_CA_FILE'].presence || '/etc/ssl/certs/ca-certificates.crt',
- openssl_verify_mode: ENV['SMTP_OPENSSL_VERIFY_MODE'],
+ openssl_verify_mode: ENV.fetch('SMTP_OPENSSL_VERIFY_MODE', nil),
enable_starttls: enable_starttls,
enable_starttls_auto: enable_starttls_auto,
tls: ENV['SMTP_TLS'].presence && ENV['SMTP_TLS'] == 'true',
diff --git a/config/environments/test.rb b/config/environments/test.rb
index ccd4bf7dcf..0c4f1de41e 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -48,10 +48,11 @@ Rails.application.configure do
# Print deprecation notices to the stderr.
config.active_support.deprecation = :stderr
- # Generate random VAPID keys
- vapid_key = Webpush.generate_key
- config.x.vapid_private_key = vapid_key.private_key
- config.x.vapid_public_key = vapid_key.public_key
+ # Generate random VAPID keys when needed
+ Webpush.generate_key.tap do |vapid_key|
+ config.x.vapid.private_key ||= vapid_key.private_key
+ config.x.vapid.public_key ||= vapid_key.public_key
+ end
# Raise exceptions when a reorder occurs in in_batches
config.active_record.error_on_ignored_order = true
diff --git a/config/initializers/1_hosts.rb b/config/initializers/1_hosts.rb
index 5c59e28bd1..638f6f6e55 100644
--- a/config/initializers/1_hosts.rb
+++ b/config/initializers/1_hosts.rb
@@ -21,7 +21,7 @@ Rails.application.configure do
config.x.streaming_api_base_url = ENV.fetch('STREAMING_API_BASE_URL') do
if Rails.env.production?
- "ws#{https ? 's' : ''}://#{web_host}"
+ "ws#{'s' if https}://#{web_host}"
else
"ws://#{host.split(':').first}:4000"
end
diff --git a/config/initializers/3_omniauth.rb b/config/initializers/3_omniauth.rb
index 0f8378ee14..607d9c15ba 100644
--- a/config/initializers/3_omniauth.rb
+++ b/config/initializers/3_omniauth.rb
@@ -12,7 +12,7 @@ Devise.setup do |config|
# CAS strategy
if ENV['CAS_ENABLED'] == 'true'
cas_options = {}
- cas_options[:display_name] = ENV['CAS_DISPLAY_NAME']
+ cas_options[:display_name] = ENV.fetch('CAS_DISPLAY_NAME', nil)
cas_options[:url] = ENV['CAS_URL'] if ENV['CAS_URL']
cas_options[:host] = ENV['CAS_HOST'] if ENV['CAS_HOST']
cas_options[:port] = ENV['CAS_PORT'] if ENV['CAS_PORT']
@@ -41,7 +41,7 @@ Devise.setup do |config|
# SAML strategy
if ENV['SAML_ENABLED'] == 'true'
saml_options = {}
- saml_options[:display_name] = ENV['SAML_DISPLAY_NAME']
+ saml_options[:display_name] = ENV.fetch('SAML_DISPLAY_NAME', nil)
saml_options[:assertion_consumer_service_url] = ENV['SAML_ACS_URL'] if ENV['SAML_ACS_URL']
saml_options[:issuer] = ENV['SAML_ISSUER'] if ENV['SAML_ISSUER']
saml_options[:idp_sso_target_url] = ENV['SAML_IDP_SSO_TARGET_URL'] if ENV['SAML_IDP_SSO_TARGET_URL']
@@ -73,7 +73,7 @@ Devise.setup do |config|
# OpenID Connect Strategy
if ENV['OIDC_ENABLED'] == 'true'
oidc_options = {}
- oidc_options[:display_name] = ENV['OIDC_DISPLAY_NAME'] # OPTIONAL
+ oidc_options[:display_name] = ENV.fetch('OIDC_DISPLAY_NAME', nil) # OPTIONAL
oidc_options[:issuer] = ENV['OIDC_ISSUER'] if ENV['OIDC_ISSUER'] # NEED
oidc_options[:discovery] = ENV['OIDC_DISCOVERY'] == 'true' if ENV['OIDC_DISCOVERY'] # OPTIONAL (default: false)
oidc_options[:client_auth_method] = ENV['OIDC_CLIENT_AUTH_METHOD'] if ENV['OIDC_CLIENT_AUTH_METHOD'] # OPTIONAL (default: basic)
diff --git a/config/initializers/cache_buster.rb b/config/initializers/cache_buster.rb
deleted file mode 100644
index a49fba671b..0000000000
--- a/config/initializers/cache_buster.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-Rails.application.configure do
- config.x.cache_buster_enabled = ENV['CACHE_BUSTER_ENABLED'] == 'true'
-
- config.x.cache_buster = {
- secret_header: ENV['CACHE_BUSTER_SECRET_HEADER'],
- secret: ENV['CACHE_BUSTER_SECRET'],
- http_method: ENV['CACHE_BUSTER_HTTP_METHOD'] || 'GET',
- }
-end
diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb
index 931dc1d9b9..33894db93f 100644
--- a/config/initializers/content_security_policy.rb
+++ b/config/initializers/content_security_policy.rb
@@ -32,7 +32,7 @@ Rails.application.config.content_security_policy do |p|
if Rails.env.development?
vite_public_host = ENV.fetch('VITE_DEV_SERVER_PUBLIC', "localhost:#{ViteRuby.config.port}")
- front_end_build_urls = %w(ws http).map { |protocol| "#{protocol}#{ViteRuby.config.https ? 's' : ''}://#{vite_public_host}" }
+ front_end_build_urls = %w(ws http).map { |protocol| "#{protocol}#{'s' if ViteRuby.config.https}://#{vite_public_host}" }
p.connect_src :self, :data, :blob, *media_hosts, Rails.configuration.x.streaming_api_base_url, *front_end_build_urls
p.script_src :self, :unsafe_inline, :unsafe_eval, assets_host
diff --git a/config/initializers/cookie_rotator.rb b/config/initializers/cookie_rotator.rb
new file mode 100644
index 0000000000..ccc2c6b21f
--- /dev/null
+++ b/config/initializers/cookie_rotator.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+# TODO: remove this file some time after 4.3.0
+
+Rails.application.config.after_initialize do
+ Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
+ authenticated_encrypted_cookie_salt = Rails.application.config.action_dispatch.authenticated_encrypted_cookie_salt
+ signed_cookie_salt = Rails.application.config.action_dispatch.signed_cookie_salt
+
+ secret_key_base = Rails.application.secret_key_base
+
+ key_generator = ActiveSupport::KeyGenerator.new(
+ secret_key_base, iterations: 1000, hash_digest_class: OpenSSL::Digest::SHA1
+ )
+ key_len = ActiveSupport::MessageEncryptor.key_len
+
+ old_encrypted_secret = key_generator.generate_key(authenticated_encrypted_cookie_salt, key_len)
+ old_signed_secret = key_generator.generate_key(signed_cookie_salt)
+
+ cookies.rotate :encrypted, old_encrypted_secret
+ cookies.rotate :signed, old_signed_secret
+ end
+end
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index a277ee3059..f69f7519c8 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -377,7 +377,7 @@ Devise.setup do |config|
config.usernamefield = nil
config.emailfield = 'email'
config.check_at_sign = true
- config.pam_default_suffix = ENV.fetch('PAM_EMAIL_DOMAIN') { ENV['LOCAL_DOMAIN'] }
+ config.pam_default_suffix = ENV.fetch('PAM_EMAIL_DOMAIN') { ENV.fetch('LOCAL_DOMAIN', nil) }
config.pam_default_service = ENV.fetch('PAM_DEFAULT_SERVICE') { 'rpam' }
config.pam_controlled_service = ENV.fetch('PAM_CONTROLLED_SERVICE', nil).presence
end
@@ -394,7 +394,7 @@ Devise.setup do |config|
config.ldap_uid = ENV.fetch('LDAP_UID', 'cn')
config.ldap_mail = ENV.fetch('LDAP_MAIL', 'mail')
config.ldap_tls_no_verify = ENV['LDAP_TLS_NO_VERIFY'] == 'true'
- config.ldap_search_filter = ENV.fetch('LDAP_SEARCH_FILTER', '(|(%{uid}=%{email})(%{mail}=%{email}))')
+ config.ldap_search_filter = ENV.fetch('LDAP_SEARCH_FILTER', '(|(%s=%s)(%s=%s))')
config.ldap_uid_conversion_enabled = ENV['LDAP_UID_CONVERSION_ENABLED'] == 'true'
config.ldap_uid_conversion_search = ENV.fetch('LDAP_UID_CONVERSION_SEARCH', '.,- ')
config.ldap_uid_conversion_replace = ENV.fetch('LDAP_UID_CONVERSION_REPLACE', '_')
diff --git a/config/initializers/prometheus_exporter.rb b/config/initializers/prometheus_exporter.rb
index fdfee59dc8..16b408977d 100644
--- a/config/initializers/prometheus_exporter.rb
+++ b/config/initializers/prometheus_exporter.rb
@@ -5,16 +5,12 @@ if ENV['MASTODON_PROMETHEUS_EXPORTER_ENABLED'] == 'true'
require 'prometheus_exporter/middleware'
if ENV['MASTODON_PROMETHEUS_EXPORTER_LOCAL'] == 'true'
- require 'prometheus_exporter/server'
- require 'prometheus_exporter/client'
+ require 'mastodon/prometheus_exporter/local_server'
# bind is the address, on which the webserver will listen
# port is the port that will provide the /metrics route
- server = PrometheusExporter::Server::WebServer.new bind: ENV.fetch('MASTODON_PROMETHEUS_EXPORTER_HOST', 'localhost'), port: ENV.fetch('MASTODON_PROMETHEUS_EXPORTER_PORT', '9394').to_i
- server.start
-
- # wire up a default local client
- PrometheusExporter::Client.default = PrometheusExporter::LocalClient.new(collector: server.collector)
+ Mastodon::PrometheusExporter::LocalServer.bind = ENV.fetch('MASTODON_PROMETHEUS_EXPORTER_HOST', 'localhost')
+ Mastodon::PrometheusExporter::LocalServer.port = ENV.fetch('MASTODON_PROMETHEUS_EXPORTER_PORT', '9394').to_i
end
if ENV['MASTODON_PROMETHEUS_EXPORTER_WEB_DETAILED_METRICS'] == 'true'
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index b2ebdbf078..3c2f12780c 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -26,6 +26,12 @@ Sidekiq.configure_server do |config|
require 'prometheus_exporter'
require 'prometheus_exporter/instrumentation'
+ if ENV['MASTODON_PROMETHEUS_EXPORTER_LOCAL'] == 'true'
+ config.on :startup do
+ Mastodon::PrometheusExporter::LocalServer.setup!
+ end
+ end
+
config.on :startup do
# Ruby process metrics (memory, GC, etc)
PrometheusExporter::Instrumentation::Process.start type: 'sidekiq'
diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb
index 8191825a9a..86abdb9c0f 100644
--- a/config/initializers/simple_form.rb
+++ b/config/initializers/simple_form.rb
@@ -5,7 +5,7 @@
module AppendComponent
def append(_wrapper_options = nil)
@append ||= begin
- options[:append].to_s.html_safe if options[:append].present?
+ options[:append].to_s.html_safe if options[:append].present? # rubocop:disable Rails/OutputSafety
end
end
end
@@ -35,7 +35,7 @@ end
module WarningHintComponent
def warning_hint(_wrapper_options = nil)
@warning_hint ||= begin
- options[:warning_hint].to_s.html_safe if options[:warning_hint].present?
+ options[:warning_hint].to_s.html_safe if options[:warning_hint].present? # rubocop:disable Rails/OutputSafety
end
end
end
diff --git a/config/initializers/vapid.rb b/config/initializers/vapid.rb
deleted file mode 100644
index 551ede34fb..0000000000
--- a/config/initializers/vapid.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-Rails.application.configure do
- # You can generate the keys using the following command (first is the private key, second is the public one)
- # You should only generate this once per instance. If you later decide to change it, all push subscription will
- # be invalidated, requiring the users to access the website again to resubscribe.
- #
- # Generate with `bundle exec rails mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web bundle exec rails mastodon:webpush:generate_vapid_key` if you use docker compose)
- #
- # For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html
-
- if Rails.env.production?
- config.x.vapid_private_key = ENV['VAPID_PRIVATE_KEY']
- config.x.vapid_public_key = ENV['VAPID_PUBLIC_KEY']
- end
-end
diff --git a/config/locales/activerecord.et.yml b/config/locales/activerecord.et.yml
index 308a97cb3e..4e49ae9683 100644
--- a/config/locales/activerecord.et.yml
+++ b/config/locales/activerecord.et.yml
@@ -23,6 +23,8 @@ et:
models:
account:
attributes:
+ fields:
+ fields_with_values_missing_labels: sisaldab väärtusi, kus väljade nimed on puudu
username:
invalid: ainult tähtmärgid, numbrid ja alakriipsud
reserved: on reserveeritud
@@ -47,8 +49,14 @@ et:
attributes:
reblog:
taken: postitusel on juba
+ terms_of_service:
+ attributes:
+ effective_date:
+ too_soon: on liiga vara, peab olema hiljem, kui %{date}
user:
attributes:
+ date_of_birth:
+ below_limit: on allpool vanuse alampiiri
email:
blocked: kasutab mitte lubatud e-posti teenusepakkujat
unreachable: ei paista eksisteerivat
diff --git a/config/locales/ar.yml b/config/locales/ar.yml
index b6bba218db..6df034d5b7 100644
--- a/config/locales/ar.yml
+++ b/config/locales/ar.yml
@@ -1942,7 +1942,6 @@ ar:
post_action: إنشاء
post_step: قل مرحبا للعالَم عبر نصّ أو صور أو فيديوهات أو استطلاعات رأي.
post_title: قم بإنشاء منشورك الأول
- share_action: شارِك
share_step: أخبر أصدقائك بكيفية العثور عليك على مَستُدون.
share_title: شارك مِلَفّ مَستُدون التعريفي الخاص بك
sign_in_action: تسجيل الدخول
diff --git a/config/locales/be.yml b/config/locales/be.yml
index 1e6394fa42..978328ffd6 100644
--- a/config/locales/be.yml
+++ b/config/locales/be.yml
@@ -1974,7 +1974,6 @@ be:
post_action: Стварыць
post_step: Скажыце ўсім прывітанне з дапамогай тэксту, фатаграфій, відэа і апытанняў.
post_title: Стварыце свой першы допіс
- share_action: Абагуліць
share_step: Няхай вашыя сябры ведаюць, як знайсці вас у Mastodon.
share_title: Абагульце ваш профіль у Mastodon
sign_in_action: Увайсці
diff --git a/config/locales/bg.yml b/config/locales/bg.yml
index a55603516d..5a41164c06 100644
--- a/config/locales/bg.yml
+++ b/config/locales/bg.yml
@@ -2031,7 +2031,6 @@ bg:
post_action: Съставяне
post_step: Поздравете света с текст, снимки, видео или анкети.
post_title: Направете първата си публикация
- share_action: Споделяне
share_step: Позволете на приятелите си да знаят как да ви намират в Mastodon.
share_title: Споделете профила си в Mastodon
sign_in_action: Вход
diff --git a/config/locales/br.yml b/config/locales/br.yml
index 5d039852d0..9f0cf07ab9 100644
--- a/config/locales/br.yml
+++ b/config/locales/br.yml
@@ -607,7 +607,6 @@ br:
edit_profile_title: Personelaat ho profil
feature_action: Gouzout hiroc'h
follow_action: Heuliañ
- share_action: Rannañ
sign_in_action: Kevreañ
subject: Donemat e Mastodon
title: Degemer mat e bourzh, %{name}!
diff --git a/config/locales/ca.yml b/config/locales/ca.yml
index 0c113febeb..0d0d60cf52 100644
--- a/config/locales/ca.yml
+++ b/config/locales/ca.yml
@@ -2043,7 +2043,6 @@ ca:
post_action: Redacteu
post_step: Saludeu el món amb text, fotos, vídeos o enquestes.
post_title: Feu la primera publicació
- share_action: Compartiu
share_step: Permeteu als vostres amics de saber com trobar-vos a Mastodon.
share_title: Compartiu el perfil
sign_in_action: Inicieu sessió
diff --git a/config/locales/cs.yml b/config/locales/cs.yml
index d557d1e6e3..c06917629c 100644
--- a/config/locales/cs.yml
+++ b/config/locales/cs.yml
@@ -2156,7 +2156,6 @@ cs:
post_action: Sepsat
post_step: Řekněte světu ahoj pomocí textu, fotografií, videí nebo anket.
post_title: Vytvořte svůj první příspěvek
- share_action: Sdílet
share_step: Dejte přátelům vědět, jak vás mohou na Mastodonu najít.
share_title: Sdílejte svůj Mastodon profil
sign_in_action: Přihlásit se
diff --git a/config/locales/cy.yml b/config/locales/cy.yml
index a624be6e4f..87db8b6031 100644
--- a/config/locales/cy.yml
+++ b/config/locales/cy.yml
@@ -15,7 +15,7 @@ cy:
two: Dilynwyr
zero: Dilynwyr
following: Yn dilyn
- instance_actor_flash: Mae'r cyfrif hwn yn actor rhithwir sy'n cael ei ddefnyddio i gynrychioli'r gweinydd ei hun ac nid unrhyw ddefnyddiwr unigol. Fe'i defnyddir at ddibenion ffederasiwn ac ni ddylid ei atal.
+ instance_actor_flash: Mae'r cyfrif hwn yn actor rhithwir sy'n cael ei ddefnyddio i gynrychioli'r gweinydd ei hun ac nid unrhyw ddefnyddiwr unigol. Mae'n cael ei ddefnyddio at ddibenion ffederasiwn ac ni ddylid ei atal.
last_active: gweithgar ddiwethaf
link_verified_on: Gwiriwyd perchnogaeth y ddolen yma ar %{date}
nothing_here: Does dim byd yma!
@@ -332,7 +332,7 @@ cy:
title: Cyhoeddiad newydd
preview:
disclaimer: Gan nad oes modd i ddefnyddwyr eu hosgoi, dylai hysbysiadau e-bost gael eu cyfyngu i gyhoeddiadau pwysig fel tor-data personol neu hysbysiadau cau gweinydd.
- explanation_html: 'Bydd yr e-bost yn cael ei anfon at %{display_count} defnyddiwr . Bydd y testun canlynol yn cael ei gynnwys yn yr e-bost:'
+ explanation_html: 'Bydd yr e-bost yn cael ei anfon at %{display_count} defnyddiwr . Bydd y testun canlynol yn cael ei gynnwys yn yr e-bost:'
title: Hysbysiad rhagolwg cyhoeddiad
publish: Cyhoeddi
published_msg: Cyhoeddiad wedi'i gyhoeddi'n llwyddiannus!
@@ -1352,7 +1352,7 @@ cy:
date:
formats:
default: "%b %d %Y"
- with_month_name: "%b %d %Y"
+ with_month_name: "%B %d, %Y"
datetime:
distance_in_words:
about_x_hours: "%{count}a"
@@ -1725,7 +1725,7 @@ cy:
unsubscribe:
action: Iawn, dad-danysgrifio
complete: Dad-danysgrifiwyd
- confirmation_html: Ydych chi'n siŵr eich bod am ddad-danysgrifio rhag derbyn %{type} Mastodon ar %{domain} i'ch e-bost yn %{email}? Gallwch ail-danysgrifio o'ch gosodiadau hysbysu e-bost rhywbryd eto .
+ confirmation_html: Ydych chi'n siŵr eich bod am ddad-danysgrifio rhag derbyn %{type} Mastodon ar %{domain} i'ch e-bost yn %{email}? Gallwch ail-danysgrifio o'ch gosodiadau hysbysu e-bost rhywbryd eto.
emails:
notification_emails:
favourite: e-bost hysbysu hoffi
@@ -2169,7 +2169,7 @@ cy:
agreement: Drwy barhau i ddefnyddio %{domain}, rydych yn cytuno i'r telerau hyn. Os ydych yn anghytuno â'r telerau a ddiweddarwyd, gallwch derfynu eich cytundeb â %{domain} ar unrhyw adeg drwy ddileu eich cyfrif.
changelog: 'Yn fyr, dyma beth mae''r diweddariad hwn yn ei olygu i chi:'
description: 'Rydych yn derbyn yr e-bost hwn oherwydd ein bod yn gwneud rhai newidiadau i''n telerau gwasanaeth yn %{domain}. Bydd y diweddariadau hyn yn dod i rym ar %{date}. Rydym yn eich annog i adolygu''r telerau diweddaraf yn llawn yma:'
- description_html: Rydych yn derbyn yr e-bost hwn oherwydd ein bod yn gwneud rhai newidiadau i'n telerau gwasanaeth yn %{domain}. Bydd y diweddariadau hyn yn dod i rym ar %{date} . Rydym yn eich annog i adolygu'r telerau diweddaraf yn llawn yma .
+ description_html: Rydych yn derbyn yr e-bost hwn oherwydd ein bod yn gwneud rhai newidiadau i'n telerau gwasanaeth yn %{domain}. Bydd y diweddariadau hyn yn dod i rym ar %{date} . Rydym yn eich annog i adolygu'r telerau diweddaraf yn llawn yma .
sign_off: Tîm %{domain}
subject: Diweddariadau i'n telerau gwasanaeth
subtitle: Mae telerau gwasanaeth %{domain} yn newid
@@ -2244,7 +2244,6 @@ cy:
post_action: Creu
post_step: Dywedwch helo wrth y byd gyda thestun, lluniau, fideos neu arolygon barn.
post_title: Creu'ch postiad cyntaf
- share_action: Rhannu
share_step: Gadewch i'ch ffrindiau wybod sut i ddod o hyd i chi ar Mastodon.
share_title: Rhannwch eich proffil Mastodon
sign_in_action: Mewngofnodi
diff --git a/config/locales/da.yml b/config/locales/da.yml
index e192f57c74..5257d9dee7 100644
--- a/config/locales/da.yml
+++ b/config/locales/da.yml
@@ -2068,7 +2068,6 @@ da:
post_action: Skriv
post_step: Sig hej til verden med tekst, fotos, videoer eller afstemninger.
post_title: Opret det første indlæg
- share_action: Del
share_step: Lad vennerne vide, hvor man kan findes på Mastodon.
share_title: Del Mastodon-profilen
sign_in_action: Log ind
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 7b7d0a431c..ad025b95e7 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -1588,7 +1588,7 @@ de:
too_many: Mehr als vier Dateien können nicht angehängt werden
migrations:
acct: umgezogen nach
- cancel: Weiterleitung beenden
+ cancel: Nicht mehr weiterleiten
cancel_explanation: Das Abbrechen der Weiterleitung wird dein aktuelles Konto erneut aktivieren, aber keine Follower zurückholen, die auf dieses Konto verschoben wurden.
cancelled_msg: Die Weiterleitung wurde erfolgreich beendet.
errors:
@@ -2068,7 +2068,6 @@ de:
post_action: Verfassen
post_step: Begrüße die Welt mit Text, Fotos, Videos oder Umfragen.
post_title: Erstelle deinen ersten Beitrag
- share_action: Teilen
share_step: Lass deine Freund*innen wissen, wie sie dich auf Mastodon finden können.
share_title: Teile dein Mastodon-Profil
sign_in_action: Anmelden
diff --git a/config/locales/devise.et.yml b/config/locales/devise.et.yml
index f09ad35b39..eb2e2951da 100644
--- a/config/locales/devise.et.yml
+++ b/config/locales/devise.et.yml
@@ -22,7 +22,7 @@ et:
action: Kinnita e-postiaadress
action_with_app: Kinnita ja naase %{app}
explanation: Oled loonud %{host} konto selle e-postiaadressiga. Oled konto aktiveerimisest ühe kliki kaugusel. Kui see polnud sina, palun eira seda kirja.
- explanation_when_pending: "Selle e-postiaadressiga on esitatud taotlus saada %{host} kasutajaks. E-postiaadress vajab kinnitamist. Pärast seda vaatame me taotluse üle. Saad siseneda, et oma andmeid muuta või konto kustutada, aga enamustele tegevustele ei pääse enne ligi, kui konto on meie moderaatorite poolt kinnitatud. Kui konto on kinnitatud, saabub selle kohta eraldi e-kiri.\nKui taotlus lükatakse aga tagasi, kustutatakse ka andmed ja mingit järeltegevust pole vaja. \nKui see polnud sina, siis palume seda kirja eirata."
+ explanation_when_pending: "Selle e-postiaadressiga on esitatud taotlus saada %{host} kasutajaks. E-postiaadress vajab kinnitamist. Pärast seda vaatame me taotluse üle. Saad siseneda, et oma andmeid muuta või konto kustutada, aga enamustele tegevustele ei pääse enne ligi, kui konto on meie moderaatorite poolt kinnitatud. Kui konto on kinnitatud, saabub selle kohta eraldi e-kiri.\nKui taotlus lükatakse aga tagasi, kustutatakse ka andmed ja mingit järeltegevust pole vaja. \nKui see polnud sina, siis palun eira seda kirja."
extra_html: Palun tutvu meie serveri reeglitega ning meie kasutustingimustega .
subject: 'Mastodon: %{instance} kinnitamisjuhised'
title: Kinnita e-postiaadress
@@ -97,9 +97,9 @@ et:
update_needs_confirmation: Konto uuendamine õnnestus, kuid e-postiaadress tuleb veel kinnitada. Palun kontrolli oma e-posti ning järgi kirjas olevat kinnituslinki, et e-postiaadress kinnitada. Palun kontrolli rämpspostikausta, kui selline kiri ei saabunud.
updated: Konto uuendamine õnnestus.
sessions:
- already_signed_out: Väljumine õnnestus.
+ already_signed_out: Välja logimine õnnestus.
signed_in: Sisenemine õnnestus.
- signed_out: Väljumine õnnestus.
+ signed_out: Välja logimine õnnestus.
unlocks:
send_instructions: Saad paari minuti pärast juhistega e-kirja, kuidas oma konto lukust lahti teha. Palun kontrolli oma rämpsposti kausta, kui selline kiri ei saabunud.
send_paranoid_instructions: Kui konto on olemas, saabub paari minuti pärast e-kiri juhistega, kuidas konto lukust lahti teha. Palun kontrolli rämpsposti kausta, kui selline e-kiri ei saabunud.
diff --git a/config/locales/el.yml b/config/locales/el.yml
index 2f7b66796a..f5a01af592 100644
--- a/config/locales/el.yml
+++ b/config/locales/el.yml
@@ -2047,7 +2047,6 @@ el:
post_action: Σύνθεση
post_step: Πες γεια στον κόσμο με κείμενο, φωτογραφίες, βίντεο ή δημοσκοπήσεις.
post_title: Κάνε την πρώτη σου ανάρτηση
- share_action: Κοινοποίηση
share_step: Πες στους φίλους σου πώς να σε βρουν στο Mastodon.
share_title: Μοιραστείτε το προφίλ σας στο Mastodon
sign_in_action: Σύνδεση
diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml
index 52e2ca68b9..f44404502e 100644
--- a/config/locales/en-GB.yml
+++ b/config/locales/en-GB.yml
@@ -2005,7 +2005,6 @@ en-GB:
post_action: Compose
post_step: Say hello to the world with text, photos, videos, or polls.
post_title: Make your first post
- share_action: Share
share_step: Let your friends know how to find you on Mastodon.
share_title: Share your Mastodon profile
sign_in_action: Sign in
diff --git a/config/locales/en.yml b/config/locales/en.yml
index a16c321530..87a214fe1c 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -2440,7 +2440,6 @@ en:
post_action: Compose
post_step: Say hello to the world with text, photos, videos, or polls.
post_title: Make your first post
- share_action: Share
share_step: Let your friends know how to find you on Mastodon.
share_title: Share your Mastodon profile
sign_in_action: Sign in
diff --git a/config/locales/eo.yml b/config/locales/eo.yml
index d0a75c7db7..69c6c361bf 100644
--- a/config/locales/eo.yml
+++ b/config/locales/eo.yml
@@ -2055,7 +2055,6 @@ eo:
post_action: Redakti
post_step: Salutu la mondon per teksto, fotoj, filmetoj aŭ balotenketoj.
post_title: Faru vian unuan afiŝon
- share_action: Kundividi
share_step: Sciigu viajn amikojn kiel trovi vin sur Mastodon.
share_title: Kunhavigu vian Mastodon-profilon
sign_in_action: Ensaluti
diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml
index b61b9b0b9e..0be9b84699 100644
--- a/config/locales/es-AR.yml
+++ b/config/locales/es-AR.yml
@@ -2068,7 +2068,6 @@ es-AR:
post_action: Redactar
post_step: Decile "Hola" al mundo con textos, fotos, videos o encuestas.
post_title: Escribí tu primer mensaje
- share_action: Compartir
share_step: Hacé que tus amistades sepan cómo encontrarte en Mastodon.
share_title: Compartí tu perfil de Mastodon
sign_in_action: Iniciá sesión
diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml
index 38a9b7f1e7..24a3af1d30 100644
--- a/config/locales/es-MX.yml
+++ b/config/locales/es-MX.yml
@@ -2068,7 +2068,6 @@ es-MX:
post_action: Redacta
post_step: Di hola al mundo con texto, fotos, vídeos o encuestas.
post_title: Haz tu primera publicación
- share_action: Comparte
share_step: Dile a tus amigos cómo encontrarte en Mastodon.
share_title: Comparte tu perfil de Mastodon
sign_in_action: Regístrate
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 2c7698548d..749104d691 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -2068,7 +2068,6 @@ es:
post_action: Redactar
post_step: Di hola al mundo con texto, fotos, vídeos o encuestas.
post_title: Escribe tu primera publicación
- share_action: Compartir
share_step: Dile a tus amigos cómo encontrarte en Mastodon.
share_title: Comparte tu perfil de Mastodon
sign_in_action: Regístrate
diff --git a/config/locales/et.yml b/config/locales/et.yml
index 399b5ceab0..afbb429bb5 100644
--- a/config/locales/et.yml
+++ b/config/locales/et.yml
@@ -210,6 +210,7 @@ et:
enable_user: Lubas kasutaja
memorialize_account: Igaveselt lahkunuks märkimine
promote_user: Edendas kasutaja
+ publish_terms_of_service: Avalda kasutustingimused
reject_appeal: Lükka vaidlustus tagasi
reject_user: Kasutaja tagasilükkamine
remove_avatar_user: Kustutas profiilipildi
@@ -270,6 +271,7 @@ et:
enable_user_html: "%{name} lubas %{target} sisenemise"
memorialize_account_html: "%{name} märkis %{target} igaveselt lahkunuks"
promote_user_html: "%{name} ülendas kasutajat %{target}"
+ publish_terms_of_service_html: "%{name} teatas kasutustingimuste muudatusest"
reject_appeal_html: "%{name} lükkas %{target} modereerimisotsuse kaebuse tagasi"
reject_user_html: "%{name} lükkas %{target} liitumissoovi tagasi"
remove_avatar_user_html: "%{name} eemaldas %{target} avatari"
@@ -299,6 +301,7 @@ et:
title: Auditilogi
unavailable_instance: "(domeeni nimi pole saadaval)"
announcements:
+ back: Tagasi teadaannete juurde
destroyed_msg: Teadaande kustutamine õnnestus!
edit:
title: Teate muutmine
@@ -465,6 +468,10 @@ et:
new:
title: Domeenikeeldude import
no_file: Faili pole valitud
+ fasp:
+ providers:
+ sign_in: Logi sisse
+ status: Olek
follow_recommendations:
description_html: "Jälgimissoovitused aitavad uutel kasutajatel kiirelt leida huvipakkuvat sisu . Kui kasutaja pole teistega piisavalt läbi käinud, et saaks luua personaalseid soovitusi, soovitatakse neid kontosid. Need arvutatakse ümber igapäevaselt konkreetse keele populaarseimate postituste ja enim jälgitavate kontode seast."
language: Keel
@@ -741,11 +748,16 @@ et:
title: Rollid
rules:
add_new: Lisa reegel
+ add_translation: Lisa tõlge
delete: Kustuta
description_html: Kuigi enamik väidab, et on teenusetingimused läbi lugenud ja nõustub nendega, loevad inimesed tavaliselt need läbi alles pärast probleemi tekkimist. Muuda oma serveri reeglite ühe pilguga haaramine hõlpsaks, esitades need lihtsa täpploendina. Püüa hoida reegli punktid lühikesed ja lihtsad, kuid ära jaga neid ka paljudeks eraldi üksusteks.
edit: Reegli muutmine
empty: Serveri reegleid pole veel defineeritud.
+ move_down: Liiguta alla
+ move_up: Liiguta üles
title: Serveri reeglid
+ translation: Tõlge
+ translations: Tõlked
settings:
about:
manage_rules: Halda serveri reegleid
@@ -771,6 +783,7 @@ et:
discovery:
follow_recommendations: Jälgi soovitusi
preamble: Huvitava sisu esiletoomine on oluline uute kasutajate kaasamisel, kes ei pruugi Mastodonist kedagi tunda. Kontrolli, kuidas erinevad avastamisfunktsioonid serveris töötavad.
+ privacy: Privaatsus
profile_directory: Kasutajate kataloog
public_timelines: Avalikud ajajooned
publish_statistics: Avalda statistika
@@ -819,6 +832,7 @@ et:
batch:
remove_from_report: Eemalda raportist
report: Raport
+ contents: Sisu
deleted: Kustutatud
favourites: Lemmikud
history: Versiooniajalugu
@@ -927,6 +941,9 @@ et:
explanation_html: Esitatud teenusetingimuste näidis on mõeldud ainult teavitamise eesmärgil ja seda ei tohiks tõlgendada kui juriidilist nõuannet mis tahes küsimuses. Palun konsulteeri olukorra ja konkreetsete juriidiliste küsimuste osas oma õigusnõustajaga.
title: Teenuse tingimuste seadistamine
history: Ajalugu
+ publish: Postita
+ published_on_html: Postitatud %{date}
+ title: Kasutustingimused
title: Administreerimine
trends:
allow: Luba
@@ -1163,8 +1180,8 @@ et:
new_confirmation_instructions_sent: Saad mõne minuti pärast uue kinnituslingiga e-kirja!
title: Kontrolli sisendkasti
sign_in:
- preamble_html: Logi sisse oma %{domain} volitustega. Kui konto asub teises serveris, ei saa siin sisse logida.
- title: Logi sisse kohta %{domain}
+ preamble_html: Logi sisse oma kasutajakontoga serveris %{domain} . Kui konto asub teises serveris, siis sa ei saa siin sisse logida.
+ title: Logi sisse serverisse %{domain}
sign_up:
manual_review: Liitumised kohas %{domain} vaadatakse meie moderaatorite poolt käsitsi läbi. Aitamaks meil sinu taotlust läbi vaadata, kirjuta palun natuke endast ja miks soovid kontot kohas %{domain}.
preamble: Selle Mastodoni serveri kontoga saad jälgida mistahes teist isikut fediversumis, sõltumata sellest, kus ta konto on majutatud.
@@ -1677,6 +1694,7 @@ et:
scheduled_statuses:
over_daily_limit: Lubatud ajastatud postituste arv %{limit} päevas on tänaseks ületatud
over_total_limit: Oled jõudnud ajastatud postituste lubatud maksimumarvuni %{limit}
+ too_soon: kuupäev peab olema tulevikus
self_destruct:
lead_html: Kahjuks suletakse %{domain} lõplikult. Kui sul oli seal konto, ei saa sa seda enam kasutada, kuid siiski võid taotleda oma andmete varukoopiat.
title: See server suletakse
@@ -1788,6 +1806,8 @@ et:
limit: Kinnitatud on juba maksimaalne arv postitusi
ownership: Kellegi teise postitust ei saa kinnitada
reblog: Jagamist ei saa kinnitada
+ quote_policies:
+ followers: Jälgijad ja mainitud kasutajad
title: '%{name}: "%{quote}"'
visibilities:
direct: Otsene
@@ -1841,6 +1861,8 @@ et:
too_late: On hilja seda juhtumit vaidlustada
tags:
does_not_match_previous_name: ei ühti eelmise nimega
+ terms_of_service:
+ title: Kasutustingimused
themes:
contrast: Mastodon (Kõrge kontrast)
default: Mastodon (Tume)
@@ -1864,7 +1886,7 @@ et:
enabled: Kaheastmeline autentimine on lubatud
enabled_success: Kaheastmeline autentimine lubatud
generate_recovery_codes: Loo taastekoodid
- lost_recovery_codes: Taastekoodide abil on võimalik telefoni kaotsimineku puhul kontole siseneda. Taastekoodide puudumisel saab need siin luua. Eelnevad taastekoodid kaotavad kehtivuse.
+ lost_recovery_codes: Taastekoodide abil on võimalik telefoni kaotsimineku puhul kontole sisse logida. Taastekoodide puudumisel saad need siin luua. Eelnevad taastekoodid kaotavad kehtivuse.
methods: Kaheastmelised meetodid
otp: Autentimisrakendus
recovery_codes: Taastekoodide varundamine
@@ -1872,6 +1894,9 @@ et:
recovery_instructions_html: Kui telefon peaks kaotsi minema, on võimalik kontole sisenemisel kasutada ühte järgnevatest taastekoodidest. Hoia taastekoode turvaliselt . Näiteks võib neid prindituna hoida koos teiste tähtsate dokumentidega.
webauthn: Turvavõtmed
user_mailer:
+ announcement_published:
+ subject: Saidi teadaanne teenuste kohta
+ title: "%{domain} saidi teadaanne teenuste kohta"
appeal_approved:
action: Konto seaded
explanation: "%{appeal_date} esitatud vaidlustus %{strike_date} otsuse kohta on rahuldatud. Konto on ennistatud."
@@ -1901,6 +1926,11 @@ et:
further_actions_html: Kui see tuleb üllatusena, soovitame viivitamata %{action} ja lülitada konto turvamiseks sisse kaheastmeline autentimine.
subject: Kontole sisenemine uuelt IP-aadressilt
title: Uus sisenemine
+ terms_of_service_changed:
+ sign_off: "%{domain} saidi tiim"
+ subject: Meie kasutustingimuste uuendused
+ subtitle: "%{domain} saidi kasutustingimused muutuvad"
+ title: Oluline uuendus
warning:
appeal: Vaidlustuse esitamine
appeal_description: Kui see võib olla eksitus, on võimalik %{instance} haldajatele esitada vaidlustus.
@@ -1967,7 +1997,6 @@ et:
post_action: Postita
post_step: Tervita maailma teksti, fotode, videote või küsitlustega.
post_title: Tee oma esimene postitus
- share_action: Jaga
share_step: Anna sõpradele teada, kuidas Sind Mastodonis leida.
share_title: Jaga oma Mastodoni profiili
sign_in_action: Sisene
diff --git a/config/locales/eu.yml b/config/locales/eu.yml
index 90a3ef4938..79080c7bfb 100644
--- a/config/locales/eu.yml
+++ b/config/locales/eu.yml
@@ -1814,7 +1814,6 @@ eu:
post_action: Idatzi
post_step: Agurtu munduari testu, argazki, bideo zein inkesta batekin.
post_title: Idatzi zeure lehen argitalpena
- share_action: Partekatu
share_step: Esaiezu lagunei nola aurki zaitzaketen Mastodon-en.
share_title: Partekatu Mastodon profila
sign_in_action: Hasi saioa
diff --git a/config/locales/fa.yml b/config/locales/fa.yml
index 0ee4e6dd22..4d341939a6 100644
--- a/config/locales/fa.yml
+++ b/config/locales/fa.yml
@@ -2058,7 +2058,6 @@ fa:
post_action: ایجاد
post_step: سلام کردن به جهان با متن، عکس، ویدیو یا نظرسنجی.
post_title: ساخت نخستین نظرسنجیتان
- share_action: همرسانی
share_step: بگذارید دوستانتان بدانند چگونه روی ماستودون بیابندتان.
share_title: همرسانی نمایهٔ ماستودونتان
sign_in_action: ورود
diff --git a/config/locales/fi.yml b/config/locales/fi.yml
index d23e43e84e..5e64c035f2 100644
--- a/config/locales/fi.yml
+++ b/config/locales/fi.yml
@@ -2066,7 +2066,6 @@ fi:
post_action: Kirjoita
post_step: Tervehdi maailmaa sanoin, kuvin, videoin ja äänestyksin.
post_title: Tee ensimmäinen julkaisusi
- share_action: Jaa
share_step: Kerro ystävillesi, kuinka sinut voi löytää Mastodonista.
share_title: Jaa Mastodon-profiilisi
sign_in_action: Kirjaudu sisään
diff --git a/config/locales/fo.yml b/config/locales/fo.yml
index ca10cbe710..7e3866dcb1 100644
--- a/config/locales/fo.yml
+++ b/config/locales/fo.yml
@@ -2063,7 +2063,6 @@ fo:
post_action: Skriva
post_step: Sig hey við verðina við teksti, myndum, video ella spurnarkanningum.
post_title: Stovna tín fyrsta post
- share_action: Deil
share_step: Lat vinir tínar vita, hvussu tey finna teg á Mastodon.
share_title: Deil tín Mastodon vanga
sign_in_action: Rita inn
diff --git a/config/locales/fr-CA.yml b/config/locales/fr-CA.yml
index 4de99762d8..a011723461 100644
--- a/config/locales/fr-CA.yml
+++ b/config/locales/fr-CA.yml
@@ -2032,7 +2032,6 @@ fr-CA:
post_action: Rédiger
post_step: Dites bonjour au monde avec du texte, des photos, des vidéos ou des sondages.
post_title: Rédiger votre premier message
- share_action: Partager
share_step: Faites savoir à vos ami·e·s comment vous trouver sur Mastodon.
share_title: Partager votre profil Mastodon
sign_in_action: Se connecter
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index b89fed362e..30090fef2c 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -2032,7 +2032,6 @@ fr:
post_action: Rédiger
post_step: Dites bonjour au monde avec du texte, des photos, des vidéos ou des sondages.
post_title: Rédiger votre premier message
- share_action: Partager
share_step: Faites savoir à vos ami·e·s comment vous trouver sur Mastodon.
share_title: Partager votre profil Mastodon
sign_in_action: Se connecter
diff --git a/config/locales/fy.yml b/config/locales/fy.yml
index 9fe53b7951..71d9f64285 100644
--- a/config/locales/fy.yml
+++ b/config/locales/fy.yml
@@ -2049,7 +2049,6 @@ fy:
post_action: Opstelle
post_step: Sis hallo tsjin de wrâld mei tekst, foto’s, fideo’s of peilingen.
post_title: Jo earste berjocht meitsje
- share_action: Diele
share_step: Lit jo freonen witte hoe’t jo te finen binne op Mastodon.
share_title: Jo Mastodon-profyl diele
sign_in_action: Oanmelde
diff --git a/config/locales/ga.yml b/config/locales/ga.yml
index 230f9c92bf..223b0cd08d 100644
--- a/config/locales/ga.yml
+++ b/config/locales/ga.yml
@@ -2200,7 +2200,6 @@ ga:
post_action: Cum
post_step: Abair hello leis an domhan le téacs, grianghraif, físeáin, nó pobalbhreith.
post_title: Déan do chéad phostáil
- share_action: Comhroinn
share_step: Cuir in iúl do do chairde conas tú a aimsiú ar Mastodon.
share_title: Roinn do phróifíl Mastodon
sign_in_action: Sínigh isteach
diff --git a/config/locales/gd.yml b/config/locales/gd.yml
index 2baf397715..32cf7403e9 100644
--- a/config/locales/gd.yml
+++ b/config/locales/gd.yml
@@ -814,17 +814,26 @@ gd:
title: Dreuchdan
rules:
add_new: Cuir riaghailt ris
+ add_translation: Cuir eadar-theangachadh ris
delete: Sguab às
description_html: Ged a dh’innseas a’ mhòrchuid gun do leugh iad teirmichean na seirbheise is gu bheil iad ag aontachadh riutha, ’s ann mar as trice nach lean daoine orra ’gan leughadh gun deireadh nuair a thachras iad ri duilgheadas. Dèan e nas fhasa dhaibh gun tuig iad riaghailtean an fhrithealaiche ann am priobadh na sùla is tu a’ toirt liosta peilearaichte dhaibh. Feuch an cùm thu gach riaghailt goirid is sìmplidh ach feuch nach sgaoil thu ann an iomadh nì iad nas motha.
edit: Deasaich an riaghailt
empty: Cha deach riaghailtean an fhrithealaiche a mhìneachadh fhathast.
+ move_down: Gluais sìos
+ move_up: Gluais suas
title: Riaghailtean an fhrithealaiche
+ translation: Eadar-theangachadh
+ translations: Eadar-theangachaidhean
+ translations_explanation: "’S urrainn dhut na riaghailtean eadar-theangachadh ma thogras tu. Thèid an luach bunaiteach a shealltainn mur eil eadar-theangachadh ann. Dèan cinnteach an-còmhnaidh gur ionnann an t-eadar-theangachadh ’s an luach bunaiteach."
settings:
about:
manage_rules: Stiùirich riaghailtean an fhrithealaiche
preamble: Solair fiosrachadh domhainn mu sholar, maorsainneachd is maoineachadh an fhrithealaiche seo.
rules_hint: Tha roinn sònraichte ann dha na riaghailtean air am bu chòir an luchd-cleachdaidh agad a leantainn.
title: Mu dhèidhinn
+ allow_referrer_origin:
+ desc: Nuair a bhriogas an luchd-cleachdaidh agad ceanglaichean gu làraichean air ann taobh a-muigh, dh’fhaoidte gun cuir am brabhsair aca seòladh an fhrithealaiche Mastodon agad thuca mar an referrer. Cuir seo à comas ma dh’aithnicheadh sin an luchd-cleachdaidh fa leth agad, can mas e frithealaiche Mastodon pearsanta a th’ ann.
+ title: Ceadaich gum faic làraichean air an taobh a-muigh an fhrithealaiche Mastodon agad mar thùs trafaige
appearance:
preamble: Gnàthaich eadar-aghaidh-lìn Mhastodon.
title: Coltas
@@ -844,6 +853,7 @@ gd:
discovery:
follow_recommendations: Molaidhean leantainn
preamble: Tha tighinn an uachdar susbainte inntinniche fìor-chudromach airson toiseach-tòiseachaidh an luchd-cleachdaidh ùr nach eil eòlach air duine sam bith air Mastodon, ma dh’fhaoidte. Stiùirich mar a dh’obraicheas gleusan an rùrachaidh air an fhrithealaiche agad.
+ privacy: Prìobhaideachd
profile_directory: Eòlaire nam pròifil
public_timelines: Loidhnichean-ama poblach
publish_statistics: Foillsich an stadastaireachd
@@ -1943,6 +1953,10 @@ gd:
limit: Tha an àireamh as motha de phostaichean prìnichte agad a tha ceadaichte
ownership: Chan urrainn dhut post càich a phrìneachadh
reblog: Chan urrainn dhut brosnachadh a phrìneachadh
+ quote_policies:
+ followers: Luchd-leantainn ’s cleachdaichean le iomradh orra
+ nobody: Cleachdaichean le iomradh orra a-mhàin
+ public: A h-uile duine
title: "%{name}: “%{quote}”"
visibilities:
direct: Dìreach
@@ -1996,6 +2010,11 @@ gd:
does_not_match_previous_name: "– chan eil seo a-rèir an ainm roimhe"
terms_of_service:
title: Teirmichean na seirbheise
+ terms_of_service_interstitial:
+ future_preamble_html: Tha sinn ag atharrachadh teirmichean na seirbheise againn a bhios èifeachdach on %{date} a-mach. Mholamaid gun dèan thu lèirmheas air na teirmichean ùra.
+ past_preamble_html: Dh’atharraich sinn teirmichean na seirbheise againn on turas mu dheireadh a thadhail thu oirnn. Mholamaid gun dèan thu lèirmheas air na teirmichean ùra.
+ review_link: Dèan lèirmheas air teirmichean na seirbheise
+ title: Tha teirmichean na seirbheise aig %{domain} gu bhith atharrachadh
themes:
contrast: Mastodon (iomsgaradh àrd)
default: Mastodon (dorcha)
@@ -2137,7 +2156,6 @@ gd:
post_action: Sgrìobh
post_step: Cuir an aithne air an t-saoghal le teacsa, dealbhan, videothan no cunntasan-bheachd.
post_title: Cruthaich a’ chiad phost agad
- share_action: Co-roinn
share_step: Leig fios dha do charaidean mar a gheibh iad grèim ort air Mastodon.
share_title: Co-roinn a’ phròifil Mastodon agad
sign_in_action: Clàraich a-steach
diff --git a/config/locales/gl.yml b/config/locales/gl.yml
index b9fc4ee911..420e26e285 100644
--- a/config/locales/gl.yml
+++ b/config/locales/gl.yml
@@ -2068,7 +2068,6 @@ gl:
post_action: Escribir
post_step: Saúda a todo o mundo con texto, fotos, vídeos ou enquisas.
post_title: Escribe a túa primeira publicación
- share_action: Compartir
share_step: Dille ás amizades como poden atoparte en Mastodon.
share_title: Comparte o teu perfil en Mastodon
sign_in_action: Acceder
diff --git a/config/locales/he.yml b/config/locales/he.yml
index 4a2de26fd1..12287c7256 100644
--- a/config/locales/he.yml
+++ b/config/locales/he.yml
@@ -2156,7 +2156,6 @@ he:
post_action: חיבור הודעה
post_step: ברכו לשלום את העולם עם מלל, תמונות, חוזי או משאלים.
post_title: כתבו את הפוסט הראשון שלכםן
- share_action: שיתוף
share_step: ספרו לחברים איך למצוא אתכם במסטודון.
share_title: שיתוף פרופיל מסטודון
sign_in_action: התחברות
diff --git a/config/locales/hu.yml b/config/locales/hu.yml
index 8e66e934f8..c224f5da4a 100644
--- a/config/locales/hu.yml
+++ b/config/locales/hu.yml
@@ -2068,7 +2068,6 @@ hu:
post_action: Bejegyzés írása
post_step: Köszöntsd a világot szöveggel, fotókkal, videókkal vagy szavazásokkal.
post_title: Az első bejegyzés létrehozása
- share_action: Megosztás
share_step: Tudasd az ismerőseiddel, hogyan találhatnak meg a Mastodonon.
share_title: Oszd meg a Mastodon profilodat
sign_in_action: Bejelentkezés
diff --git a/config/locales/ia.yml b/config/locales/ia.yml
index 592f66864a..35dd56aad1 100644
--- a/config/locales/ia.yml
+++ b/config/locales/ia.yml
@@ -2032,7 +2032,6 @@ ia:
post_action: Scriber
post_step: Saluta le mundo con texto, photos, videos o sondages.
post_title: Face tu prime message
- share_action: Compartir
share_step: Face saper a tu amicos como trovar te sur Mastodon.
share_title: Compartir tu profilo de Mastodon
sign_in_action: Initiar session
diff --git a/config/locales/ie.yml b/config/locales/ie.yml
index a694ae95ad..2ca8e9ed0a 100644
--- a/config/locales/ie.yml
+++ b/config/locales/ie.yml
@@ -1797,7 +1797,6 @@ ie:
post_action: Composir
post_step: Saluta li munde con textu, images, videos o balotationes.
post_title: Crear tui unesim posta
- share_action: Compartir
share_step: Informar tui amics qualmen trovar te che Mastodon.
share_title: Partir tui profil Mastodon
sign_in_action: Intrar
diff --git a/config/locales/is.yml b/config/locales/is.yml
index 3dae3a3aa1..440df415c2 100644
--- a/config/locales/is.yml
+++ b/config/locales/is.yml
@@ -2072,7 +2072,6 @@ is:
post_action: Skrifa
post_step: Heilsaðu heiminum með texta, ljósmyndum, myndskeiðum eða könnunum.
post_title: Gerðu fyrstu færsluna þína
- share_action: Deila
share_step: Láttu vini þína vita hvernig þeir geta fundið þig á Mastodon.
share_title: Deildu notandasniðinu þínu
sign_in_action: Skrá inn
diff --git a/config/locales/it.yml b/config/locales/it.yml
index 7a03a993a0..26f1c2f168 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -2070,7 +2070,6 @@ it:
post_action: Scrivi
post_step: Salutate il mondo con testo, foto, video o sondaggi.
post_title: Scrivi il tuo primo post
- share_action: Condividi
share_step: Fai sapere ai tuoi amici come trovarti su Mastodon.
share_title: Condividi il tuo profilo Mastodon
sign_in_action: Accedi
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index 1c685dc682..e8e3d99a5e 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -1000,11 +1000,17 @@ ja:
title: ロール
rules:
add_new: ルールを追加
+ add_translation: 翻訳を追加
delete: 削除
description_html: たいていの人が利用規約を読んで同意したと言いますが、普通は問題が発生するまで読みません。箇条書きにして、サーバーのルールが一目で分かるようにしましょう 。個々のルールは短くシンプルなものにし、多くの項目に分割しないようにしましょう。
edit: ルールを編集
empty: サーバーのルールが定義されていません。
+ move_down: 下へ移動
+ move_up: 上へ移動
title: サーバーのルール
+ translation: 翻訳
+ translations: 翻訳
+ translations_explanation: オプションとしてルールに翻訳を追加できます。翻訳されたバージョンがない場合、デフォルトの値が表示されます。表示される翻訳がデフォルトの値と対応していることを常に確認しておいてください。
sensitive_words:
alert: この投稿にはセンシティブなキーワードが含まれるため、警告文が追加されました
auto_warning_text: カスタム警告文
@@ -1024,6 +1030,9 @@ ja:
preamble: サーバーの運営、管理、資金調達の方法について、詳細な情報を提供します。
rules_hint: ユーザーが守るべきルールのための専用エリアがあります。
title: このサーバーについて
+ allow_referrer_origin:
+ desc: ユーザが外部サイトへのリンクをクリックする際、ユーザーのブラウザはあなたのMastodonサーバーのアドレスを紹介者として送信することがあります。 これによりあなたのユーザーが特定されてしまう場合、例えば個人用のMastodonサーバーなどである場合などは無効にしてください。
+ title: 外部サイトが Mastodon サーバーをトラフィックソースとして表示することを許可する
appearance:
preamble: ウェブインターフェースをカスタマイズします。
title: 外観
@@ -1045,6 +1054,7 @@ ja:
follow_recommendations: おすすめフォロー
friend_servers: フレンドサーバー
preamble: Mastodon を知らないユーザーを取り込むには、興味深いコンテンツを浮上させることが重要です。サーバー上で様々なディスカバリー機能がどのように機能するかを制御します。
+ privacy: プライバシー
profile_directory: ディレクトリ
public_timelines: 公開タイムライン
publish_statistics: 統計情報を公開する
@@ -1150,6 +1160,8 @@ ja:
system_checks:
database_schema_check:
message_html: 未実行のデータベースマイグレーションがあります。実行して正常に動作するようにしてください。
+ elasticsearch_analysis_index_mismatch:
+ message_html: Elasticsearch インデックスアナライザの設定が古くなっています。 tootctl search deploy --only-mapping --only=%{value}
を実行してください
elasticsearch_health_red:
message_html: 'Elasticsearchクラスターに異常があります(status: red)。検索機能が利用できなくなっています'
elasticsearch_health_yellow:
@@ -2135,6 +2147,10 @@ ja:
limit: 固定できる投稿数の上限に達しました
ownership: 他人の投稿を固定することはできません
reblog: ブーストを固定することはできません
+ quote_policies:
+ followers: フォロワーとメンションされたユーザー
+ nobody: メンションされたユーザーのみ
+ public: 全員
searchabilities:
direct: 反応者
direct_long: この投稿に反応した人しか検索できません
@@ -2222,6 +2238,11 @@ ja:
does_not_match_previous_name: 以前の名前と一致しません
terms_of_service:
title: サービス利用規約
+ terms_of_service_interstitial:
+ future_preamble_html: サービス利用規約にいくつかの変更が加えられています。これは %{date} に適用されます。 更新された条件を確認することをお勧めします。
+ past_preamble_html: 前回の訪問時から利用規約が変更されました。更新された条件を確認することをお勧めします。
+ review_link: 利用規約を確認する
+ title: "%{domain} の利用規約が変更されています"
themes:
contrast: Mastodon (ハイコントラスト)
default: Mastodon (ダーク)
@@ -2364,7 +2385,6 @@ ja:
post_action: 作成
post_step: 試しになにか書いてみましょう。写真、ビデオ、アンケートなど、なんでも大丈夫です.
post_title: はじめての投稿
- share_action: 共有
share_step: Mastodon アカウントをほかの人に紹介しましょう。
share_title: プロフィールをシェアする
sign_in_action: ログイン
diff --git a/config/locales/kab.yml b/config/locales/kab.yml
index 4860655c90..a7ad9b356f 100644
--- a/config/locales/kab.yml
+++ b/config/locales/kab.yml
@@ -908,7 +908,6 @@ kab:
hashtags_view_more: Sken-d ugar n yihacṭagen mucaɛen
post_step: Ini-as azul i umaḍal s uḍris, s tiwlafin, s tividyutin neɣ s tefranin.
post_title: Aru tasuffeɣt-inek·inem tamezwarut
- share_action: Bḍu
share_step: Init-asen i yimeddukal-nwen amek ara ken-id-afen deg Mastodon.
share_title: Bḍu amaɣnu-inek·inem n Mastodon
sign_in_action: Qqen
diff --git a/config/locales/ko.yml b/config/locales/ko.yml
index 8e30c4ced6..7f2267e2e5 100644
--- a/config/locales/ko.yml
+++ b/config/locales/ko.yml
@@ -1884,6 +1884,7 @@ ko:
terms_of_service:
title: 이용 약관
terms_of_service_interstitial:
+ future_preamble_html: 이용약관에 몇가지 변경사항이 있으며, %{date} 부터 효력이 발생합니다. 변경된 약관을 확인하시기 바랍니다.
review_link: 이용 약관 검토하기
title: "%{domain} 도메인의 이용 약관에 변경 있음"
themes:
@@ -2024,7 +2025,6 @@ ko:
post_action: 작성
post_step: 글, 사진, 영상, 또는 투표로 세상에 인사해보세요.
post_title: 첫번째 게시물 쓰기
- share_action: 공유
share_step: 친구에게 마스토돈에서 나를 찾을 수 있는 방법을 알려주세요.
share_title: 마스토돈 프로필을 공유하세요
sign_in_action: 로그인
diff --git a/config/locales/lad.yml b/config/locales/lad.yml
index c83d336338..642c234892 100644
--- a/config/locales/lad.yml
+++ b/config/locales/lad.yml
@@ -1888,7 +1888,6 @@ lad:
post_action: Eskrive
post_step: Puedes introdusirte al mundo kon teksto, fotos, videos o anketas.
post_title: Eskrive tu primera publikasyon
- share_action: Partaja
share_step: Informa a tus amigos komo toparte en Mastodon.
share_title: Partaja tu profil de Mastodon
sign_in_action: Konektate
diff --git a/config/locales/lt.yml b/config/locales/lt.yml
index 5d97bf5246..41cf7cae96 100644
--- a/config/locales/lt.yml
+++ b/config/locales/lt.yml
@@ -1300,7 +1300,6 @@ lt:
post_action: Sukurti
post_step: Sakyk labas pasauliui tekstu, nuotraukomis, vaizdo įrašais arba apklausomis.
post_title: Sukūrk savo pirmąjį įrašą
- share_action: Bendrinti
share_step: Leisk draugams sužinoti, kaip tave rasti Mastodon.
share_title: Bendrink savo Mastodon profilį
sign_in_action: Prisijungti
diff --git a/config/locales/lv.yml b/config/locales/lv.yml
index 4d00631e8c..f7db52a40f 100644
--- a/config/locales/lv.yml
+++ b/config/locales/lv.yml
@@ -2100,7 +2100,6 @@ lv:
post_action: Rakstīt
post_step: Pasveicini pasauli ar tekstu, fotoattēliem, video vai aptaujām!
post_title: Izveido savu pirmo ierakstu
- share_action: Kopīgot
share_step: Dari saviem draugiem zināmu, kā Tevi atrast Mastodon!
share_title: Kopīgo savu Mastodon profilu
sign_in_action: Pieteikties
diff --git a/config/locales/nan.yml b/config/locales/nan.yml
index ecbf544c04..6f9824e3b1 100644
--- a/config/locales/nan.yml
+++ b/config/locales/nan.yml
@@ -10,7 +10,7 @@ nan:
followers:
other: 跟tuè ê
following: Leh跟tuè
- instance_actor_flash: Tsit ê口座是虛ê,用來代表tsit臺服侍器,毋是個人用者ê。伊用來做聯邦ê路用,毋好kā停權。
+ instance_actor_flash: Tsit ê口座是虛ê,用來代表tsit臺服侍器,毋是個人用者ê。伊用來做聯邦ê路用,毋好kā伊ê權限停止。
last_active: 頂kái活動ê時間
link_verified_on: Tsit ê連結ê所有權佇 %{date} 受檢查
nothing_here: Tsia內底無物件!
@@ -24,7 +24,7 @@ nan:
account_actions:
action: 執行動作
already_silenced: Tsit ê口座有受著限制。
- already_suspended: Tsit ê口座已經受停權。
+ already_suspended: Tsit ê口座ê權限已經hōo lâng停止。
title: Kā %{acct} 做審核ê動作
account_moderation_notes:
create: 留記錄
@@ -110,13 +110,30 @@ nan:
previous_strikes_description_html:
other: Tsit ê口座有 %{count} kái警告。
promote: 權限the̍h懸
+ protocol: 協定
+ public: 公開ê
+ push_subscription_expires: 訂PuSH ê期間過ah
+ redownload: 重頭整理個人檔案
+ redownloaded_msg: Tuì來源站kā %{username} ê個人資料成功重頭整理
+ reject: 拒絕
+ rejected_msg: 成功拒絕 %{username} ê註冊申請ah
+ remote_suspension_irreversible: Tsit ê口座ê資料已經hōo lâng thâi掉,bē當復原。
+ remote_suspension_reversible_hint_html: Tsit ê口座ê權限佇tsit ê服侍器hōo lâng停止ah,資料ē佇 %{date} lóng總thâi掉。佇hit日前,遠距離ê服侍器ē當復原tsit ê口座,無任何pháinn作用。Nā lí想beh liâm-mī thâi掉tsit ê口座ê任何資料,ē當佇下跤操作。
remove_avatar: Thâi掉標頭
+ remove_header: Thâi掉封面ê圖
removed_avatar_msg: 成功thâi掉 %{username} ê 標頭影像
+ removed_header_msg: 成功thâi掉 %{username} ê封面ê圖
+ resend_confirmation:
+ already_confirmed: Tsit ê用者有受tio̍h確認
+ send: 重送確認ê連結
+ success: 確認連結傳成功ah!
reset: 重頭設
reset_password: Kā密碼重頭設
resubscribe: 重頭訂
role: 角色
search: Tshiau-tshuē
+ search_same_email_domain: 其他電子phue域名相kâng ê用者
+ search_same_ip: 其他IP相kâng ê用者
security: 安全
security_measures:
only_password: Kan-ta用密碼
@@ -126,13 +143,25 @@ nan:
shared_inbox_url: 做伙用ê收件kheh-á (Shared Inbox) ê URL
show:
created_reports: 檢舉記錄
+ targeted_reports: Hōo別lâng檢舉
+ silence: 靜音
+ silenced: 受靜音
+ statuses: PO文
+ strikes: Khah早ê處份
+ subscribe: 訂
+ suspend: 中止權限
+ suspended: 權限中止ah
+ suspension_irreversible: Tsit ê口座ê資料已經thâi掉,bē當回復。Lí ē當取消停止tsit ê口座ê權限,予伊ē當使用,但是bē當回復任何khah早ê資料。
+ title: 口座
+ unblock_email: 取消封鎖電子phue ê地址
unblocked_email_msg: 成功取消封鎖 %{username} ê電子phue地址
unconfirmed_email: 無驗證ê電子phue
undo_sensitized: 取消強制標做敏感ê
undo_silenced: 取消限制
- undo_suspension: 取消停權
+ undo_suspension: 取消停止權限
+ unsilenced_msg: 成功kā %{username} ê口座取消限制
unsubscribe: 取消訂
- unsuspended_msg: 成功kā %{username} ê口座取消停權
+ unsuspended_msg: 成功kā %{username} ê口座取消停止權限
username: 用者ê名
view_domain: 看域名ê摘要
warn: 警告
@@ -154,6 +183,11 @@ nan:
create_domain_block: 加添封鎖ê域名
create_email_domain_block: 加添電子phue域名ê封鎖
create_ip_block: 加添IP規則
+ create_relay: 建立中繼
+ create_unavailable_domain: 建立bē當用ê域名
+ create_user_role: 建立角色
+ demote_user: Kā用者降級
+ destroy_announcement: Thâi掉公告
remove_avatar_user: Thâi掉標頭
actions:
remove_avatar_user_html: "%{name} thâi掉 %{target} ê標頭"
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 7b72df5f3e..246642109e 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -2068,7 +2068,6 @@ nl:
post_action: Opstellen
post_step: Zeg hallo tegen de wereld met tekst, foto's, video's of peilingen.
post_title: Je eerste bericht schrijven
- share_action: Delen
share_step: Laat je vrienden weten waar je op Mastodon bent te vinden.
share_title: Je Mastodonprofiel delen
sign_in_action: Inloggen
diff --git a/config/locales/nn.yml b/config/locales/nn.yml
index 29893bb21c..e52768469d 100644
--- a/config/locales/nn.yml
+++ b/config/locales/nn.yml
@@ -2068,7 +2068,6 @@ nn:
post_action: Skriv
post_step: Sei hei til verda med tekst, bilete, filmar eller meiningsmålingar.
post_title: Skriv ditt fyrste innlegg
- share_action: Del
share_step: Fortel venene dine korleis dei finn deg på Mastodon.
share_title: Del Mastodon-profilen din
sign_in_action: Logg inn
diff --git a/config/locales/no.yml b/config/locales/no.yml
index 620fe828ea..ddf087b7f9 100644
--- a/config/locales/no.yml
+++ b/config/locales/no.yml
@@ -1788,7 +1788,6 @@
post_action: Sett sammen
post_step: Si hallo til verdenen med tekst, bilder, videoer, eller meningsmålinger.
post_title: Lag ditt første innlegg
- share_action: Del
share_step: La vennene dine vite hvordan finne deg på Mastodon.
share_title: Del Mastadon-profilen din
sign_in_action: Logg inn
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index a77f82d634..36897280f5 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -2093,7 +2093,6 @@ pl:
post_action: Utwórz wpis
post_step: Przywitaj się ze światem.
post_title: Utwórz swój pierwszy post
- share_action: Udostępnij
share_step: Poinformuj swoich przyjaciół jak znaleźć cię na Mastodonie.
share_title: Udostępnij swój profil
sign_in_action: Zaloguj się
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index 6f6b9258d4..27d2c860e1 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -2068,7 +2068,6 @@ pt-BR:
post_action: Escrever
post_step: Diga olá para o mundo com texto, fotos, vídeos ou enquetes.
post_title: Crie sua primeira publicação
- share_action: Compartilhar
share_step: Deixe seus amigos saberem como te encontrar no Mastodon.
share_title: Compartilhe seu perfil do Mastodon
sign_in_action: Entrar
diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml
index b391f65315..8a5d2771ff 100644
--- a/config/locales/pt-PT.yml
+++ b/config/locales/pt-PT.yml
@@ -2068,7 +2068,6 @@ pt-PT:
post_action: Compor
post_step: Diz olá para o mundo com texto, fotos, vídeos ou sondagens.
post_title: Faz a tua primeira publicação
- share_action: Partilhar
share_step: Diz aos teus amigos como te podem encontrar no Mastodon.
share_title: Partilha o teu perfil de Mastodon
sign_in_action: Iniciar sessão
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index 67b3aa09d4..bb9d274a47 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -2112,7 +2112,6 @@ ru:
post_action: Составить
post_step: Поприветствуйте мир с помощью текста, фотографий, видео или опросов.
post_title: Сделайте свой первый пост
- share_action: Поделиться
share_step: Пусть ваши друзья знают, как найти вас на Mastodon.
share_title: Поделитесь информацией о компании Mastodon
sign_in_action: Зарегистрироваться
diff --git a/config/locales/sc.yml b/config/locales/sc.yml
index c9f0053bc4..c6b80847a4 100644
--- a/config/locales/sc.yml
+++ b/config/locales/sc.yml
@@ -1242,7 +1242,6 @@ sc:
follow_title: Personaliza s'ischermu printzipale
post_action: Iscrie
post_title: Pùblica pro sa primu borta
- share_action: Cumpartzi
share_title: Cumpartzi su profilu tuo de Mastodon
sign_in_action: Intra
subject: Ti donamus su benebènnidu a Mastodon
diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml
index 305aa6f817..9f65a474b0 100644
--- a/config/locales/simple_form.de.yml
+++ b/config/locales/simple_form.de.yml
@@ -54,9 +54,9 @@ de:
password: Verwende mindestens 8 Zeichen
phrase: Wird unabhängig von der Groß- und Kleinschreibung im Text oder der Inhaltswarnung eines Beitrags abgeglichen
scopes: Welche Schnittstellen der Applikation erlaubt sind. Wenn du einen Top-Level-Scope auswählst, dann musst du nicht jeden einzelnen darunter auswählen.
- setting_aggregate_reblogs: Beiträge, die erst kürzlich geteilt wurden, werden nicht noch einmal angezeigt (wirkt sich nur auf zukünftige geteilte Beiträge aus)
+ setting_aggregate_reblogs: Beiträge, die erst kürzlich geteilt wurden, werden nicht noch einmal angezeigt (betrifft nur zukünftig geteilte Beiträge)
setting_always_send_emails: Normalerweise werden Benachrichtigungen nicht per E-Mail versendet, wenn du gerade auf Mastodon aktiv bist
- setting_default_quote_policy: Erwähnte Profile dürfen immer zitieren. Diese Einstellung wirkt sich nur für Beiträge aus, die mit der zukünftigen Mastodon-Version erstellt wurden. Als Vorbereitung darauf kannst du bereits jetzt die Einstellung vornehmen.
+ setting_default_quote_policy: Erwähnte Profile dürfen immer zitieren. Diese Einstellung gilt nur für Beiträge, die mit der zukünftigen Mastodon-Version erstellt wurden. Als Vorbereitung darauf kannst du bereits jetzt die Einstellung vornehmen
setting_default_sensitive: Medien, die mit einer Inhaltswarnung versehen worden sind, werden – je nach Einstellung – erst nach einem zusätzlichen Klick angezeigt
setting_display_media_default: Medien mit Inhaltswarnung ausblenden
setting_display_media_hide_all: Medien immer ausblenden
diff --git a/config/locales/simple_form.et.yml b/config/locales/simple_form.et.yml
index 1eda0ec67b..f61f7d8a84 100644
--- a/config/locales/simple_form.et.yml
+++ b/config/locales/simple_form.et.yml
@@ -3,6 +3,7 @@ et:
simple_form:
hints:
account:
+ attribution_domains: Üks rea kohta. See kaitseb pahatahtlike viidete eest.
discoverable: Su profiili ja avalikke postitusi võidakse Mastodoni erinevates piirkondades esile tõsta või soovitada ning su profiili soovitada teistele kasutajatele.
display_name: Su täisnimi või naljanimi.
fields: Su koduleht, sugu, vanus. Mistahes, mida soovid.
@@ -317,6 +318,13 @@ et:
name: Silt
trendable: Luba sellel sildil trendida
usable: Luba seda märksõna postitustes kasutada lokaalselt
+ terms_of_service:
+ changelog: Mis on muutunud?
+ effective_date: Jõustumise kuupäev
+ text: Kasutustingimused
+ terms_of_service_generator:
+ admin_email: E-posti aadress juriidiliste teadete jaoks
+ jurisdiction: Jurisdiktsioon
user:
role: Roll
time_zone: Ajavöönd
diff --git a/config/locales/simple_form.gd.yml b/config/locales/simple_form.gd.yml
index bb87ba712c..19ca3cbaff 100644
--- a/config/locales/simple_form.gd.yml
+++ b/config/locales/simple_form.gd.yml
@@ -56,6 +56,7 @@ gd:
scopes: Na APIan a dh’fhaodas an aplacaid inntrigeadh. Ma thaghas tu sgòp air ìre as àirde, cha leig thu leas sgòpaichean fa leth a thaghadh.
setting_aggregate_reblogs: Na seall brosnachaidhean ùra do phostaichean a chaidh a bhrosnachadh o chionn goirid (cha doir seo buaidh ach air brosnachaidhean ùra o seo a-mach)
setting_always_send_emails: Mar as àbhaist, cha dèid brathan puist-d a chur nuair a a bhios tu ri Mastodon gu cunbhalach
+ setting_default_quote_policy: Faodaidh luchd-cleachdaidh le iomradh orra luaidh an-còmhnaidh. Cha bhi an roghainn seo ann sàs ach air postaichean a thèid a chruthachadh leis an ath-thionndadh de Mhastodon ach ’s urrainn dhut do roghainn a thaghadh airson ullachadh dha.
setting_default_sensitive: Thèid meadhanan frionasach fhalach a ghnàth is gabhaidh an nochdadh le briogadh orra
setting_display_media_default: Falaich meadhanan ris a bheil comharra gu bheil iad frionasach
setting_display_media_hide_all: Falaich na meadhanan an-còmhnaidh
@@ -148,6 +149,11 @@ gd:
min_age: Cha bu chòir seo a bhith fon aois as lugha a dh’iarras laghain an t-uachdranais laghail agad.
user:
chosen_languages: Nuair a bhios cromag ris, cha nochd ach postaichean sna cànain a thagh thu air loidhnichean-ama poblach
+ date_of_birth:
+ few: Feumaidh sinn dèanamh cinnteach gu bheil thu %{count} bliadhnaichean a dh’aois air a char as lugha mus cleachd thu Mastodon. Cha chlàraich sinn seo.
+ one: Feumaidh sinn dèanamh cinnteach gu bheil thu %{count} bhliadhna a dh’aois air a char as lugha mus cleachd thu Mastodon. Cha chlàraich sinn seo.
+ other: Feumaidh sinn dèanamh cinnteach gu bheil thu %{count} bliadhna a dh’aois air a char as lugha mus cleachd thu Mastodon. Cha chlàraich sinn seo.
+ two: Feumaidh sinn dèanamh cinnteach gu bheil thu %{count} bhliadhna a dh’aois air a char as lugha mus cleachd thu Mastodon. Cha chlàraich sinn seo.
role: Stiùiridh an dreuchd dè na ceadan a bhios aig cleachdaiche.
user_role:
color: An datha a bhios air an dreuchd air feadh na h-eadar-aghaidh, ’na RGB san fhòrmat sia-dheicheach
@@ -228,6 +234,7 @@ gd:
setting_boost_modal: Seall còmhradh dearbhaidh mus dèan thu brosnachadh
setting_default_language: Cànan postaidh
setting_default_privacy: Prìobhaideachd postaidh
+ setting_default_quote_policy: Cò dh’fhaodas luaidh
setting_default_sensitive: Cuir comharra ri meadhanan an-còmhnaidh gu bheil iad frionasach
setting_delete_modal: Seall còmhradh dearbhaidh mus sguab thu às post
setting_disable_hover_cards: Na ro-sheall pròifil nuair a dh’fhanas mi os a cionn
diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml
index 89c1eab5bd..60725e4661 100644
--- a/config/locales/simple_form.ja.yml
+++ b/config/locales/simple_form.ja.yml
@@ -63,6 +63,7 @@ ja:
setting_always_send_emails: 通常、Mastodon からメール通知は行われません。
setting_bookmark_category_needed: すべてのカテゴリから削除したとき、ブックマークが自動で外れるようになります
setting_custom_css_lead: '必ず覚えてください: 万が一カスタムCSSの入力を誤り、画面が正常に表示されなくなった場合は、サインイン画面の下にあるリンクよりカスタムCSSを無効化することができます。ブラウザのプライベートモードなどでサインイン画面を開き、無効化してください。'
+ setting_default_quote_policy: メンションされたユーザーが常にその投稿を引用できるようになる。 この設定はMastodonの次のバージョンからしか効力を発揮しませんが、現時点で設定を選択しておくことができます
setting_default_searchability: kmyblue・Fedibirdでは検索許可設定に基づき検索されます。Misskeyでは当設定に関係なく、全ての公開・ローカル公開・非収載投稿が検索されます。Mastodon・Firefishでは検索許可の代わりにプロフィール設定の「公開投稿を他のサーバーで自由に検索できるようにする」設定が適用されます
setting_default_sensitive: 閲覧注意状態のメディアはデフォルトでは内容が伏せられ、クリックして初めて閲覧できるようになります
setting_disallow_unlisted_public_searchability: この設定を有効にすると、非収載投稿と検索範囲「誰でも」は両立できず不特定多数からの検索が不可になります。Fedibirdと同じ挙動になります
@@ -181,6 +182,8 @@ ja:
min_age: お住まいの国や地域の法律によって定められている最低年齢を下回ってはなりません。
user:
chosen_languages: 選択すると、選択した言語の投稿のみが公開タイムラインに表示されるようになります
+ date_of_birth:
+ other: Mastodonを利用するには少なくとも%{count}歳以上であることを確認する必要があります。この情報は保存されません。
role: そのロールは、ユーザーが持つ権限を制御します。
user_role:
color: UI 全体でロールの表示に使用される色(16進数RGB形式)
@@ -283,6 +286,7 @@ ja:
setting_custom_css: カスタムCSS
setting_default_language: 投稿する言語
setting_default_privacy: 投稿の公開範囲
+ setting_default_quote_policy: 引用できるユーザー
setting_default_reblog_privacy: BTの公開範囲
setting_default_searchability: 自分の投稿の検索を許可する範囲
setting_default_searchability_of_search: 自分が検索するときの検索許可設定
diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml
index 44a40f9ff8..fd0b243d6f 100644
--- a/config/locales/simple_form.ko.yml
+++ b/config/locales/simple_form.ko.yml
@@ -56,6 +56,7 @@ ko:
scopes: 애플리케이션에 허용할 API들입니다. 최상위 스코프를 선택하면 개별적인 것은 선택하지 않아도 됩니다.
setting_aggregate_reblogs: 최근에 부스트 됐던 게시물은 새로 부스트 되어도 보여주지 않기 (새로 받은 부스트에만 적용됩니다)
setting_always_send_emails: 기본적으로 마스토돈을 활동적으로 사용하고 있을 때에는 이메일 알림이 보내지지 않습니다
+ setting_default_quote_policy: 멘션된 사용자는 항상 인용할 수 있도록 허용됩니다. 이 설정은 다음 마스토돈 버전부터 효과가 적용되지만 미리 준비할 수 있도록 설정을 제공합니다
setting_default_sensitive: 민감한 미디어는 기본적으로 가려져 있으며 클릭해서 볼 수 있습니다
setting_display_media_default: 민감함으로 표시된 미디어 가리기
setting_display_media_hide_all: 모든 미디어를 가리기
@@ -146,6 +147,8 @@ ko:
min_age: 관할지역의 법률에서 요구하는 최저 연령보다 작으면 안 됩니다.
user:
chosen_languages: 체크하면, 선택 된 언어로 작성된 게시물들만 공개 타임라인에 보여집니다
+ date_of_birth:
+ other: 마스토돈을 사용하려면 %{count}세 이상임을 확인해야 합니다. 이 정보는 저장되지 않습니다.
role: 역할은 사용자가 어떤 권한을 가지게 될 지 결정합니다.
user_role:
color: 색상은 사용자 인터페이스에서 역할을 나타내기 위해 사용되며, RGB 16진수 형식입니다
diff --git a/config/locales/simple_form.no.yml b/config/locales/simple_form.no.yml
index c93c4d40bc..f9b60ed796 100644
--- a/config/locales/simple_form.no.yml
+++ b/config/locales/simple_form.no.yml
@@ -80,7 +80,7 @@
closed_registrations_message: Vises når det er stengt for registrering
custom_css: Du kan bruke egendefinerte stiler på nettversjonen av Mastodon.
mascot: Overstyrer illustrasjonen i det avanserte webgrensesnittet.
- peers_api_enabled: En liste over domenenavn denne serveren har oppstått i fødiverset. Det finnes ikke data om du føderer med en gitt server, for akkurat det serveren din vet om. Dette brukes av tjenester som i all hovedsak innhenter føderasjonsstatistikk.
+ peers_api_enabled: En liste over domenenavn denne serveren har oppdaget i fødiverset. Dette er ikke noen indikasjon på om du fødererer med en gitt server, kun at din server vet om den. Dette brukes av tjenester som samler statistikk om fødiverset generelt.
profile_directory: Profilkatalogen viser alle brukere som har valgt å kunne bli oppdaget.
require_invite_text: Når registreringer krever manuell godkjenning, må du gjøre «Hvorfor vil du bli med?»-tekstinput obligatorisk i stedet for valgfritt
site_contact_email: Hvordan mennesker får tak i deg for rettslige spørsmål eller brukerstøtte.
diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml
index 41ac513f39..ab2a193558 100644
--- a/config/locales/simple_form.sv.yml
+++ b/config/locales/simple_form.sv.yml
@@ -56,6 +56,7 @@ sv:
scopes: 'Vilka API: er applikationen kommer tillåtas åtkomst till. Om du väljer en omfattning på högstanivån behöver du inte välja individuella sådana.'
setting_aggregate_reblogs: Visa inte nya boostar för inlägg som nyligen blivit boostade (påverkar endast nymottagna boostar)
setting_always_send_emails: E-postnotiser kommer vanligtvis inte skickas när du aktivt använder Mastodon
+ setting_default_quote_policy: Nämnda användare får alltid citeras. Denna inställning kommer att träda i kraft för inlägg som skapats med nästa Mastodon-version, men förbereda dina inställningar för det redan nu
setting_default_sensitive: Känslig media döljs som standard och kan visas med ett klick
setting_display_media_default: Dölj media markerad som känslig
setting_display_media_hide_all: Dölj alltid all media
@@ -75,6 +76,7 @@ sv:
filters:
action: Välj vilken åtgärd som ska utföras när ett inlägg matchar filtret
actions:
+ blur: Dölj media bakom en varning utan att dölja själva texten
hide: Dölj det filtrerade innehållet helt, beter sig som om det inte fanns
warn: Dölj det filtrerade innehållet bakom en varning som visar filtrets rubrik
form_admin_settings:
@@ -88,6 +90,7 @@ sv:
favicon: WEBP, PNG, GIF eller JPG. Används på mobila enheter istället för appens egen ikon.
mascot: Åsidosätter illustrationen i det avancerade webbgränssnittet.
media_cache_retention_period: Mediafiler från inlägg som gjorts av fjärranvändare cachas på din server. När inställd på ett positivt värde kommer media att raderas efter det angivna antalet dagar. Om mediadatat begärs efter att det har raderats, kommer det att laddas ned igen om källinnehållet fortfarande är tillgängligt. På grund av begränsningar för hur ofta förhandsgranskningskort för länkar hämtas från tredjepartswebbplatser, rekommenderas det att ange detta värde till minst 14 dagar, annars kommer förhandsgranskningskorten inte att uppdateras på begäran före den tiden.
+ min_age: Användare kommer att bli ombedda att bekräfta sitt födelsedatum under registreringen
peers_api_enabled: En lista över domänen den här servern har stött på i fediversum. Ingen data inkluderas om du har federerat med servern, bara att din server känner till den. Detta används av tjänster som samlar statistik om federering i allmänhet.
profile_directory: Profilkatalogen visar alla användare som har samtyckt till att bli upptäckbara.
require_invite_text: Gör fältet "Varför vill du gå med?" obligatoriskt när nyregistreringar kräver manuellt godkännande
@@ -132,11 +135,23 @@ sv:
name: Du kan bara ändra skriftläget av bokstäverna, till exempel, för att göra det mer läsbart
terms_of_service:
changelog: Kan struktureras med Markdown syntax.
+ effective_date: En rimlig tidsram kan vara allt från 10 till 30 dagar från det datum du meddelat dina användare.
text: Kan struktureras med Markdown syntax.
terms_of_service_generator:
+ admin_email: Juridiska meddelanden inkluderar motanmälningar, domstolsbeslut, begäran om borttagning och begäran från brottsbekämpande myndigheter.
+ arbitration_address: Ovan kan vara samma som fysisk adress, eller “N/A” om du använder e-post.
+ arbitration_website: Kan vara ett webbforum, eller ”N/A” om du använder e-post.
+ choice_of_law: Stad, region, territorium eller stat där det är de interna lagar som ska styra samtliga krav.
+ dmca_address: För amerikanska operatörer, använd den adress som är registrerad i DMCA:s katalog över utsedda agenter. En P.O. Box-lista finns tillgänglig på begäran; använd DMCA:s begäran om undantag för postbox för att mejla Copyright Office och förklara att du är en hembaserad innehållsmoderator som fruktar hämnd eller vedergällning för dina handlingar och behöver använda en P.O. Box för att ta bort din hemadress från offentlig visning.
+ dmca_email: Kan vara samma e-postadress som används för “E-postadress för juridiska meddelanden” ovan.
+ domain: Unik identifiering av onlinetjänsten du tillhandahåller.
jurisdiction: Lista det land där vem som än betalar räkningarna bor. Om det är ett företag eller annan enhet, lista landet där det är inkorporerat, och staden, regionen, territoriet eller staten på lämpligt sätt.
+ min_age: Bör inte vara lägre än den minimiålder som krävs enligt lagarna i din jurisdiktion.
user:
chosen_languages: Vid aktivering visas bara inlägg på dina valda språk i offentliga tidslinjer
+ date_of_birth:
+ one: Vi måste se till att du är minst %{count} för att använda Mastodon. Vi lagrar inte denna information.
+ other: Vi måste se till att du är minst %{count} för att använda Mastodon. Vi lagrar inte denna information.
role: Rollen styr vilka behörigheter användaren har.
user_role:
color: Färgen som ska användas för rollen i användargränssnittet, som RGB i hex-format
@@ -150,6 +165,7 @@ sv:
url: Dit händelser kommer skickas
labels:
account:
+ attribution_domains: Webbplatser som är tillåtna att kreditera dig
discoverable: Presentera profil och inlägg med upptäcktsalgoritmer
fields:
name: Etikett
@@ -216,6 +232,7 @@ sv:
setting_boost_modal: Visa bekräftelsedialog innan boostning
setting_default_language: Inläggsspråk
setting_default_privacy: Inläggsintegritet
+ setting_default_quote_policy: Vem kan citera
setting_default_sensitive: Markera alltid media som känsligt
setting_delete_modal: Visa bekräftelsedialog innan radering av inlägg
setting_disable_hover_cards: Inaktivera profilförhandsgranskning vid hovring
@@ -226,6 +243,7 @@ sv:
setting_display_media_show_all: Visa alla
setting_expand_spoilers: Utöka alltid tutningar markerade med innehållsvarningar
setting_hide_network: Göm ditt nätverk
+ setting_missing_alt_text_modal: Visa bekräftelsedialog innan du skickar media utan alt-text
setting_reduce_motion: Minska rörelser i animationer
setting_system_font_ui: Använd systemets standardfont
setting_system_scrollbars_ui: Använd systemets standardrullningsfält
@@ -247,6 +265,7 @@ sv:
name: Hashtag
filters:
actions:
+ blur: Dölj media med en varning
hide: Dölj helt
warn: Dölj med en varning
form_admin_settings:
@@ -260,6 +279,7 @@ sv:
favicon: Favicon
mascot: Anpassad maskot (tekniskt arv)
media_cache_retention_period: Tid för bibehållande av mediecache
+ min_age: Åldersgräns
peers_api_enabled: Publicera lista över upptäckta servrar i API:et
profile_directory: Aktivera profilkatalog
registrations_mode: Vem kan registrera sig
@@ -325,12 +345,17 @@ sv:
usable: Tillåt inlägg att använda denna fyrkantstagg
terms_of_service:
changelog: Vad har ändrats?
+ effective_date: Datum för ikraftträdande
text: Användarvillkor
terms_of_service_generator:
admin_email: E-postadress för juridiska meddelanden
+ arbitration_address: Fysisk adress för meddelanden om skiljeförfarande
+ arbitration_website: Webbplats för att skicka in skiljedomsmeddelanden
+ choice_of_law: Lagval
dmca_address: Fysisk adress för meddelanden om DMCA/upphovsrätt
dmca_email: Fysisk adress för meddelanden om DMCA/upphovsrätt
domain: Domän
+ jurisdiction: Rättslig jurisdiktion
min_age: Minimiålder
user:
date_of_birth_1i: Dag
diff --git a/config/locales/sk.yml b/config/locales/sk.yml
index a34013bd61..c3c091dd39 100644
--- a/config/locales/sk.yml
+++ b/config/locales/sk.yml
@@ -1373,7 +1373,6 @@ sk:
follow_title: Prispôsob svoj domáci kanál
follows_title: Koho nasledovať
post_title: Vytvor svoj prvý príspevok
- share_action: Zdieľaj
sign_in_action: Prihlás sa
subject: Vitaj na Mastodone
title: Vitaj na palube, %{name}!
diff --git a/config/locales/sl.yml b/config/locales/sl.yml
index 9cbd5453b8..e6339e2249 100644
--- a/config/locales/sl.yml
+++ b/config/locales/sl.yml
@@ -2104,7 +2104,6 @@ sl:
post_action: Sestavi
post_step: Pozdravite cel svet z besedilom, fotografijami, videoposnetki ali anketami.
post_title: Ustvarite svojo prvo objavo
- share_action: Delite
share_step: Naj prijatelji izvejo, kako vas najdejo na Mastodonu.
share_title: Delite svoj profil Mastodon z drugimi
sign_in_action: Prijava
diff --git a/config/locales/sq.yml b/config/locales/sq.yml
index f6ee3b0f28..ea9a2ea4cc 100644
--- a/config/locales/sq.yml
+++ b/config/locales/sq.yml
@@ -2058,7 +2058,6 @@ sq:
post_action: Hartoni
post_step: Përshëndetni botën me tekst, foto, video, ose pyetësorë.
post_title: Shkruani postimin tuaj të parë
- share_action: Ndajeni me të tjerë
share_step: Bëjuni të ditur shokëve si t’ju gjejnë në Mastodon.
share_title: Ndani me të tjerët profilin tuaj Mastodon
sign_in_action: Hyni
diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml
index 6833b7fdc1..79d823ebb8 100644
--- a/config/locales/sr-Latn.yml
+++ b/config/locales/sr-Latn.yml
@@ -1831,7 +1831,6 @@ sr-Latn:
post_action: Napišite
post_step: Pozdravite svet tekstom, fotografijama, video zapisima ili anketama.
post_title: Napišite svoju prvu objavu
- share_action: Podelite
share_step: Neka vaši prijatelji znaju kako da vas pronađu na Mastodon-u.
share_title: Podelite svoj Mastodon profil
sign_in_action: Prijavite se
diff --git a/config/locales/sr.yml b/config/locales/sr.yml
index a2b6559515..2e8a3364be 100644
--- a/config/locales/sr.yml
+++ b/config/locales/sr.yml
@@ -1861,7 +1861,6 @@ sr:
post_action: Напишите
post_step: Поздравите свет текстом, фотографијама, видео записима или анкетама.
post_title: Напишите своју прву објаву
- share_action: Поделите
share_step: Нека ваши пријатељи знају како да вас пронађу на Mastodon-у.
share_title: Поделите свој Mastodon профил
sign_in_action: Пријавите се
diff --git a/config/locales/sv.yml b/config/locales/sv.yml
index df75ace088..055c3457e2 100644
--- a/config/locales/sv.yml
+++ b/config/locales/sv.yml
@@ -2066,7 +2066,6 @@ sv:
post_action: Skriv
post_step: Säg hej till världen med text, foton, videor eller omröstningar.
post_title: Skapa ditt första inlägg
- share_action: Dela
share_step: Låt dina vänner veta hur de hittar dig på Mastodon.
share_title: Dela din Mastodon-profil
sign_in_action: Logga in
diff --git a/config/locales/th.yml b/config/locales/th.yml
index 6e4e769b5d..558e95249b 100644
--- a/config/locales/th.yml
+++ b/config/locales/th.yml
@@ -1949,7 +1949,6 @@ th:
post_action: เขียน
post_step: กล่าวสวัสดีชาวโลกด้วยข้อความ, รูปภาพ, วิดีโอ หรือการสำรวจความคิดเห็น
post_title: สร้างโพสต์แรกของคุณ
- share_action: แชร์
share_step: แจ้งให้เพื่อน ๆ ของคุณทราบวิธีค้นหาคุณใน Mastodon
share_title: แชร์โปรไฟล์ Mastodon ของคุณ
sign_in_action: ลงชื่อเข้า
diff --git a/config/locales/tr.yml b/config/locales/tr.yml
index 1dfb8980a0..383d185dc9 100644
--- a/config/locales/tr.yml
+++ b/config/locales/tr.yml
@@ -2068,7 +2068,6 @@ tr:
post_action: Oluştur
post_step: Dünyaya metin, fotoğraf, video ve anketlerle merhaba deyin.
post_title: İlk gönderinizi oluşturun
- share_action: Paylaş
share_step: Arkadaşlarınıza Mastodon'da size nasıl ulaşabileceklerini söyleyin.
share_title: Mastodon profilinizi paylaşın
sign_in_action: Oturum aç
diff --git a/config/locales/uk.yml b/config/locales/uk.yml
index 55cc9820e2..6ca9a39944 100644
--- a/config/locales/uk.yml
+++ b/config/locales/uk.yml
@@ -2038,7 +2038,6 @@ uk:
post_action: Створити
post_step: Привітайтеся зі світом, з текстом, світлинами, відео та опитуваннями.
post_title: Напишіть свій перший допис
- share_action: Поділитися
share_step: Розкажіть друзям, як знайти вас на Mastodon.
share_title: Поділіться своїм профілем Mastodon
sign_in_action: Увійти
diff --git a/config/locales/vi.yml b/config/locales/vi.yml
index 8db46b22ba..9ecefe3e4f 100644
--- a/config/locales/vi.yml
+++ b/config/locales/vi.yml
@@ -2024,7 +2024,6 @@ vi:
post_action: Soạn tút
post_step: Chào cộng đồng bằng lời nói, ảnh hoặc video.
post_title: Đăng tút đầu tiên
- share_action: Chia sẻ
share_step: Hãy để bạn bè của bạn biết cách tìm thấy bạn trên Mastodon.
share_title: Chia sẻ hồ sơ Mastodon của bạn
sign_in_action: Đăng nhập
diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml
index bba3eea3e5..cce57c1285 100644
--- a/config/locales/zh-CN.yml
+++ b/config/locales/zh-CN.yml
@@ -2019,7 +2019,6 @@ zh-CN:
post_action: 撰写
post_step: 向世界打个招呼吧。
post_title: 发布你的第一条嘟文
- share_action: 分享
share_step: 让你的朋友知道如何在 Mastodon 找到你。
share_title: 分享你的 Mastodon 个人资料
sign_in_action: 登录
diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml
index 10228b4245..2e45461038 100644
--- a/config/locales/zh-HK.yml
+++ b/config/locales/zh-HK.yml
@@ -1774,7 +1774,6 @@ zh-HK:
post_action: 撰寫
post_step: 用文字、相片、影片或投票跟大家打個招呼吧。
post_title: 發表你的第一則帖文
- share_action: 分享
share_step: 讓你的朋友知道如何在 Mastodon 上找到你。
share_title: 分享你的 Mastodon 個人檔案
sign_in_action: 登入
diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml
index d71f4c271d..9d2441e5b9 100644
--- a/config/locales/zh-TW.yml
+++ b/config/locales/zh-TW.yml
@@ -2030,7 +2030,6 @@ zh-TW:
post_action: 撰寫
post_step: 透過文字、照片、影片或投票向新世界打聲招呼吧。
post_title: 撰寫您第一則嘟文
- share_action: 分享
share_step: 讓您的朋友們知道如何於 Mastodon 找到您。
share_title: 分享您 Mastodon 個人檔案
sign_in_action: 登入
diff --git a/config/puma.rb b/config/puma.rb
index 4fe8f802b9..16c481a2ae 100644
--- a/config/puma.rb
+++ b/config/puma.rb
@@ -21,6 +21,12 @@ if ENV['MASTODON_PROMETHEUS_EXPORTER_ENABLED'] == 'true'
require 'prometheus_exporter'
require 'prometheus_exporter/instrumentation'
+ if ENV['MASTODON_PROMETHEUS_EXPORTER_LOCAL'] == 'true'
+ before_fork do
+ Mastodon::PrometheusExporter::LocalServer.setup!
+ end
+ end
+
on_worker_boot do
# Ruby process metrics (memory, GC, etc)
PrometheusExporter::Instrumentation::Process.start(type: 'puma')
diff --git a/config/vapid.yml b/config/vapid.yml
new file mode 100644
index 0000000000..c3ee806fd6
--- /dev/null
+++ b/config/vapid.yml
@@ -0,0 +1,17 @@
+# You can generate the private and public keys using the following task. You
+# should only generate this once per instance. If you later decide to change it,
+# all push subscriptions will be invalidated, requiring users to access the
+# website again to resubscribe.
+#
+# Generate on the CLI:
+# `bundle exec rails mastodon:webpush:generate_vapid_key`
+#
+# Generate via Docker Compose:
+# `docker-compose run --rm web bundle exec rails mastodon:webpush:generate_vapid_key`
+#
+# For more information visit
+# https://rossta.net/blog/using-the-web-push-api-with-vapid.html
+#
+shared:
+ private_key: <%= ENV.fetch('VAPID_PRIVATE_KEY', nil) %>
+ public_key: <%= ENV.fetch('VAPID_PUBLIC_KEY', nil) %>
diff --git a/db/migrate/20250605110215_add_legacy_to_quotes.rb b/db/migrate/20250605110215_add_legacy_to_quotes.rb
new file mode 100644
index 0000000000..65a23dab8f
--- /dev/null
+++ b/db/migrate/20250605110215_add_legacy_to_quotes.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddLegacyToQuotes < ActiveRecord::Migration[8.0]
+ def change
+ add_column :quotes, :legacy, :boolean, null: false, default: false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 9d2948ad56..8c442e4b7e 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.0].define(version: 2025_05_20_204643) do
+ActiveRecord::Schema[8.0].define(version: 2025_06_05_110215) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
@@ -1215,6 +1215,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_05_20_204643) do
t.string "activity_uri"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.boolean "legacy", default: false, null: false
t.index ["account_id", "quoted_account_id"], name: "index_quotes_on_account_id_and_quoted_account_id"
t.index ["activity_uri"], name: "index_quotes_on_activity_uri", unique: true, where: "(activity_uri IS NOT NULL)"
t.index ["approval_uri"], name: "index_quotes_on_approval_uri", where: "(approval_uri IS NOT NULL)"
diff --git a/docker-compose.yml b/docker-compose.yml
index 300dc64392..8be0ed76e9 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -114,7 +114,7 @@ services:
volumes:
- ./public/system:/mastodon/public/system
healthcheck:
- test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ 6' || false"]
+ test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ 7' || false"]
## Uncomment to enable federation with tor instances along with adding the following ENV variables
## http_hidden_proxy=http://privoxy:8118
diff --git a/eslint.config.mjs b/eslint.config.mjs
index cef7c91a31..06b70aee54 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -12,6 +12,7 @@ import jsxA11Y from 'eslint-plugin-jsx-a11y';
import promisePlugin from 'eslint-plugin-promise';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
+import storybook from 'eslint-plugin-storybook';
import globals from 'globals';
import tseslint from 'typescript-eslint';
@@ -187,6 +188,7 @@ export default tseslint.config([
importPlugin.flatConfigs.react,
// @ts-expect-error -- For some reason the formatjs package exports an empty object?
formatjs.configs.strict,
+ storybook.configs['flat/recommended'],
{
languageOptions: {
globals: {
@@ -252,6 +254,11 @@ export default tseslint.config([
'app/javascript/mastodon/test_setup.js',
'app/javascript/mastodon/test_helpers.tsx',
'app/javascript/**/__tests__/**',
+ 'app/javascript/**/*.stories.ts',
+ 'app/javascript/**/*.stories.tsx',
+ 'app/javascript/**/*.test.ts',
+ 'app/javascript/**/*.test.tsx',
+ '.storybook/**/*.ts',
],
},
],
@@ -398,4 +405,18 @@ export default tseslint.config([
globals: globals.vitest,
},
},
+ {
+ files: ['**/*.stories.ts', '**/*.stories.tsx', '.storybook/**/*.ts'],
+ rules: {
+ 'import/no-default-export': 'off',
+ },
+ },
+ {
+ files: ['vitest.shims.d.ts'],
+ rules: {
+ '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off',
+ '@typescript-eslint/no-unnecessary-condition': 'off',
+ '@typescript-eslint/prefer-nullish-coalescing': 'off',
+ },
+ },
]);
diff --git a/lib/mastodon/migration_warning.rb b/lib/mastodon/migration_warning.rb
index b90ceb2916..2a9a013891 100644
--- a/lib/mastodon/migration_warning.rb
+++ b/lib/mastodon/migration_warning.rb
@@ -23,7 +23,7 @@ module Mastodon
def announce_countdown
WARNING_SECONDS.downto(1) do |i|
- say "Continuing in #{i} second#{i == 1 ? '' : 's'}...", true
+ say "Continuing in #{i} second#{'s' unless i == 1}...", true
sleep 1
end
end
diff --git a/lib/mastodon/prometheus_exporter/local_server.rb b/lib/mastodon/prometheus_exporter/local_server.rb
new file mode 100644
index 0000000000..31954a473b
--- /dev/null
+++ b/lib/mastodon/prometheus_exporter/local_server.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'prometheus_exporter/server'
+require 'prometheus_exporter/client'
+
+module Mastodon::PrometheusExporter
+ module LocalServer
+ mattr_accessor :bind, :port
+
+ def self.setup!
+ server = PrometheusExporter::Server::WebServer.new(bind:, port:)
+ server.start
+
+ # wire up a default local client
+ PrometheusExporter::Client.default = PrometheusExporter::LocalClient.new(collector: server.collector)
+ end
+ end
+end
diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index cc67182e7c..290eba8bb3 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -35,7 +35,7 @@ module Mastodon
end
def default_prerelease
- 'alpha.5'
+ 'beta.1'
end
def prerelease
@@ -127,7 +127,7 @@ module Mastodon
end
def user_agent
- @user_agent ||= "Mastodon/#{Version} (#{HTTP::Request::USER_AGENT}; +http#{Rails.configuration.x.use_https ? 's' : ''}://#{Rails.configuration.x.web_domain}/)"
+ @user_agent ||= "Mastodon/#{Version} (#{HTTP::Request::USER_AGENT}; +http#{'s' if Rails.configuration.x.use_https}://#{Rails.configuration.x.web_domain}/)"
end
def version_configuration
diff --git a/lib/paperclip/color_extractor.rb b/lib/paperclip/color_extractor.rb
index fba32ba4cb..e0cdfdb523 100644
--- a/lib/paperclip/color_extractor.rb
+++ b/lib/paperclip/color_extractor.rb
@@ -239,7 +239,7 @@ module Paperclip
end
def rgb_to_hex(rgb)
- format('#%02x%02x%02x', rgb.r, rgb.g, rgb.b)
+ format('#%02x%02x%02x', rgb.r, rgb.g, rgb.b) # rubocop:disable Style/FormatStringToken
end
end
end
diff --git a/lib/tasks/repo.rake b/lib/tasks/repo.rake
index c8f977f651..6c696a9097 100644
--- a/lib/tasks/repo.rake
+++ b/lib/tasks/repo.rake
@@ -57,7 +57,7 @@ namespace :repo do
response = nil
loop do
- response = HTTP.headers('Authorization' => "token #{ENV['GITHUB_API_TOKEN']}").get("https://api.github.com/repos/#{REPOSITORY_NAME}/pulls/#{pull_request_number}")
+ response = HTTP.headers('Authorization' => "token #{ENV.fetch('GITHUB_API_TOKEN')}").get("https://api.github.com/repos/#{REPOSITORY_NAME}/pulls/#{pull_request_number}")
if response.code == 403
sleep_for = (response.headers['X-RateLimit-Reset'].to_i - Time.now.to_i).abs
diff --git a/package.json b/package.json
index e9c024cf73..7b4475d0e8 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "@mastodon/mastodon",
"license": "AGPL-3.0-or-later",
- "packageManager": "yarn@4.9.1",
+ "packageManager": "yarn@4.9.2",
"engines": {
"node": ">=20"
},
@@ -30,8 +30,12 @@
"postinstall": "test -d node_modules/husky && husky || echo \"husky is not installed\"",
"start": "node ./streaming/index.js",
"test": "yarn lint && yarn run typecheck && yarn test:js run",
- "test:js": "vitest",
- "typecheck": "tsc --noEmit"
+ "test:js": "vitest --project=legacy-tests",
+ "test:storybook": "vitest --project=storybook",
+ "typecheck": "tsc --noEmit",
+ "storybook": "storybook dev -p 6006",
+ "build-storybook": "VITE_RUBY_PUBLIC_OUTPUT_DIR='.' VITE_RUBY_PUBLIC_DIR='./storybook-static' storybook build",
+ "chromatic": "npx chromatic -d storybook-static"
},
"repository": {
"type": "git",
@@ -128,6 +132,10 @@
"devDependencies": {
"@eslint/js": "^9.23.0",
"@formatjs/cli": "^6.1.1",
+ "@storybook/addon-a11y": "^9.0.4",
+ "@storybook/addon-docs": "^9.0.4",
+ "@storybook/addon-vitest": "^9.0.4",
+ "@storybook/react-vite": "^9.0.4",
"@testing-library/dom": "^10.2.0",
"@testing-library/react": "^16.0.0",
"@types/emoji-mart": "3.0.14",
@@ -153,6 +161,10 @@
"@types/react-toggle": "^4.0.3",
"@types/redux-immutable": "^4.0.3",
"@types/requestidlecallback": "^0.3.5",
+ "@vitest/browser": "^3.2.1",
+ "@vitest/coverage-v8": "^3.2.0",
+ "@vitest/ui": "^3.2.1",
+ "chromatic": "^12.1.0",
"eslint": "^9.23.0",
"eslint-import-resolver-typescript": "^4.2.5",
"eslint-plugin-formatjs": "^5.3.1",
@@ -162,11 +174,14 @@
"eslint-plugin-promise": "~7.2.1",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-storybook": "^9.0.4",
"globals": "^16.0.0",
"husky": "^9.0.11",
"lint-staged": "^16.0.0",
+ "playwright": "^1.52.0",
"prettier": "^3.3.3",
"react-test-renderer": "^18.2.0",
+ "storybook": "^9.0.4",
"stylelint": "^16.19.1",
"stylelint-config-prettier-scss": "^1.0.0",
"stylelint-config-standard-scss": "^15.0.1",
@@ -174,13 +189,14 @@
"typescript-eslint": "^8.29.1",
"vite-plugin-rails": "^0.5.0",
"vite-plugin-svgr": "^4.2.0",
- "vitest": "^3.1.3"
+ "vitest": "^3.2.1"
},
"resolutions": {
"@types/react": "^18.2.7",
"@types/react-dom": "^18.2.4",
"kind-of": "^6.0.3",
- "vite-plugin-ruby": "^5.1.0"
+ "vite-plugin-ruby": "^5.1.0",
+ "vite": "^6.3.5"
},
"peerDependenciesMeta": {
"react": {
diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb
index 0cd3b28aaa..8be2879c91 100644
--- a/spec/lib/activitypub/activity/create_spec.rb
+++ b/spec/lib/activitypub/activity/create_spec.rb
@@ -1620,7 +1620,7 @@ RSpec.describe ActivityPub::Activity::Create do
end
end
- context 'with an unverifiable quote of a known post', feature: :inbound_quotes do
+ context 'with an unverifiable quote of a known post' do
let(:quoted_status) { Fabricate(:status) }
let(:object_json) do
@@ -1644,7 +1644,7 @@ RSpec.describe ActivityPub::Activity::Create do
end
end
- context 'with an unverifiable unknown post', feature: :inbound_quotes do
+ context 'with an unverifiable unknown post' do
let(:unknown_post_uri) { 'https://unavailable.example.com/unavailable-post' }
let(:object_json) do
@@ -1672,7 +1672,7 @@ RSpec.describe ActivityPub::Activity::Create do
end
end
- context 'with a verifiable quote of a known post', feature: :inbound_quotes do
+ context 'with a verifiable quote of a known post' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let(:approval_uri) { 'https://quoted.example.com/quote-approval' }
diff --git a/spec/lib/activitypub/activity/quote_request_spec.rb b/spec/lib/activitypub/activity/quote_request_spec.rb
index bda6388b12..dac0b438cb 100644
--- a/spec/lib/activitypub/activity/quote_request_spec.rb
+++ b/spec/lib/activitypub/activity/quote_request_spec.rb
@@ -2,7 +2,7 @@
require 'rails_helper'
-RSpec.describe ActivityPub::Activity::QuoteRequest, feature: :inbound_quotes do
+RSpec.describe ActivityPub::Activity::QuoteRequest do
let(:sender) { Fabricate(:account, domain: 'example.com') }
let(:recipient) { Fabricate(:account) }
let(:quoted_post) { Fabricate(:status, account: recipient) }
diff --git a/spec/lib/activitypub/activity/remove_spec.rb b/spec/lib/activitypub/activity/remove_spec.rb
index fc12aec8c1..937b938e4f 100644
--- a/spec/lib/activitypub/activity/remove_spec.rb
+++ b/spec/lib/activitypub/activity/remove_spec.rb
@@ -4,29 +4,60 @@ require 'rails_helper'
RSpec.describe ActivityPub::Activity::Remove do
let(:sender) { Fabricate(:account, featured_collection_url: 'https://example.com/featured') }
- let(:status) { Fabricate(:status, account: sender) }
-
- let(:json) do
- {
- '@context': 'https://www.w3.org/ns/activitystreams',
- id: 'foo',
- type: 'Add',
- actor: ActivityPub::TagManager.instance.uri_for(sender),
- object: ActivityPub::TagManager.instance.uri_for(status),
- target: sender.featured_collection_url,
- }.with_indifferent_access
- end
describe '#perform' do
subject { described_class.new(json, sender) }
- before do
- StatusPin.create!(account: sender, status: status)
- subject.perform
+ context 'when removing a pinned status' do
+ let(:status) { Fabricate(:status, account: sender) }
+
+ let(:json) do
+ {
+ '@context': 'https://www.w3.org/ns/activitystreams',
+ id: 'foo',
+ type: 'Remove',
+ actor: ActivityPub::TagManager.instance.uri_for(sender),
+ object: ActivityPub::TagManager.instance.uri_for(status),
+ target: sender.featured_collection_url,
+ }.deep_stringify_keys
+ end
+
+ before do
+ StatusPin.create!(account: sender, status: status)
+ end
+
+ it 'removes a pin' do
+ expect { subject.perform }
+ .to change { sender.pinned?(status) }.to(false)
+ end
end
- it 'removes a pin' do
- expect(sender.pinned?(status)).to be false
+ context 'when removing a featured tag' do
+ let(:tag) { Fabricate(:tag) }
+
+ let(:json) do
+ {
+ '@context': 'https://www.w3.org/ns/activitystreams',
+ id: 'foo',
+ type: 'Remove',
+ actor: ActivityPub::TagManager.instance.uri_for(sender),
+ object: {
+ type: 'Hashtag',
+ name: "##{tag.display_name}",
+ href: "https://example.com/tags/#{tag.name}",
+ },
+ target: sender.featured_collection_url,
+ }.deep_stringify_keys
+ end
+
+ before do
+ sender.featured_tags.find_or_create_by!(tag: tag)
+ end
+
+ it 'removes a pin' do
+ expect { subject.perform }
+ .to change { sender.featured_tags.exists?(tag: tag) }.to(false)
+ end
end
end
end
diff --git a/spec/lib/activitypub/parser/media_attachment_parser_spec.rb b/spec/lib/activitypub/parser/media_attachment_parser_spec.rb
new file mode 100644
index 0000000000..9456b5e648
--- /dev/null
+++ b/spec/lib/activitypub/parser/media_attachment_parser_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe ActivityPub::Parser::MediaAttachmentParser do
+ subject { described_class.new(json) }
+
+ let(:json) do
+ {
+ '@context': 'https://www.w3.org/ns/activitystreams',
+ type: 'Document',
+ mediaType: 'image/png',
+ url: 'http://example.com/attachment.png',
+ }.deep_stringify_keys
+ end
+
+ it 'correctly parses media attachment' do
+ expect(subject).to have_attributes(
+ remote_url: 'http://example.com/attachment.png',
+ file_content_type: 'image/png'
+ )
+ end
+
+ context 'when the URL is a link with multiple options' do
+ let(:json) do
+ {
+ '@context': 'https://www.w3.org/ns/activitystreams',
+ type: 'Document',
+ url: [
+ {
+ type: 'Link',
+ mediaType: 'image/png',
+ href: 'http://example.com/attachment.png',
+ },
+ {
+ type: 'Link',
+ mediaType: 'image/avif',
+ href: 'http://example.com/attachment.avif',
+ },
+ ],
+ }.deep_stringify_keys
+ end
+
+ it 'returns the first option' do
+ expect(subject).to have_attributes(
+ remote_url: 'http://example.com/attachment.png',
+ file_content_type: 'image/png'
+ )
+ end
+ end
+end
diff --git a/spec/lib/admin/system_check/media_privacy_check_spec.rb b/spec/lib/admin/system_check/media_privacy_check_spec.rb
index 0d5bcdb3e8..34eb79e36c 100644
--- a/spec/lib/admin/system_check/media_privacy_check_spec.rb
+++ b/spec/lib/admin/system_check/media_privacy_check_spec.rb
@@ -23,11 +23,11 @@ RSpec.describe Admin::SystemCheck::MediaPrivacyCheck do
describe 'message' do
it 'sends values to message instance' do
- allow(Admin::SystemCheck::Message).to receive(:new).with(nil, nil, nil, true)
+ allow(Admin::SystemCheck::Message).to receive(:new).with(nil, nil, nil, critical: true)
check.message
- expect(Admin::SystemCheck::Message).to have_received(:new).with(nil, nil, nil, true)
+ expect(Admin::SystemCheck::Message).to have_received(:new).with(nil, nil, nil, critical: true)
end
end
end
diff --git a/spec/lib/admin/system_check/message_spec.rb b/spec/lib/admin/system_check/message_spec.rb
index 81ef4f2f09..d64c17354c 100644
--- a/spec/lib/admin/system_check/message_spec.rb
+++ b/spec/lib/admin/system_check/message_spec.rb
@@ -3,7 +3,7 @@
require 'rails_helper'
RSpec.describe Admin::SystemCheck::Message do
- subject(:check) { described_class.new(:key_value, :value_value, :action_value, :critical_value) }
+ subject(:check) { described_class.new(:key_value, :value_value, :action_value, critical: :critical_value) }
it 'providers readers when initialized' do
expect(check.key).to eq :key_value
diff --git a/spec/lib/admin/system_check/sidekiq_process_check_spec.rb b/spec/lib/admin/system_check/sidekiq_process_check_spec.rb
index 75871baabc..e0b1070148 100644
--- a/spec/lib/admin/system_check/sidekiq_process_check_spec.rb
+++ b/spec/lib/admin/system_check/sidekiq_process_check_spec.rb
@@ -35,11 +35,11 @@ RSpec.describe Admin::SystemCheck::SidekiqProcessCheck do
describe 'message' do
it 'sends values to message instance' do
- allow(Admin::SystemCheck::Message).to receive(:new).with(:sidekiq_process_check, 'default, push, mailers, pull, scheduler, ingress, perishable')
+ allow(Admin::SystemCheck::Message).to receive(:new).with(:sidekiq_process_check, 'default, push, mailers, pull, scheduler, ingress, perishable, fasp')
check.message
- expect(Admin::SystemCheck::Message).to have_received(:new).with(:sidekiq_process_check, 'default, push, mailers, pull, scheduler, ingress, perishable')
+ expect(Admin::SystemCheck::Message).to have_received(:new).with(:sidekiq_process_check, 'default, push, mailers, pull, scheduler, ingress, perishable, fasp')
end
end
end
diff --git a/spec/lib/admin/system_check/software_version_check_spec.rb b/spec/lib/admin/system_check/software_version_check_spec.rb
index 75dc1a4732..1f95284747 100644
--- a/spec/lib/admin/system_check/software_version_check_spec.rb
+++ b/spec/lib/admin/system_check/software_version_check_spec.rb
@@ -127,7 +127,7 @@ RSpec.describe Admin::SystemCheck::SoftwareVersionCheck do
check.message
expect(Admin::SystemCheck::Message).to have_received(:new)
- .with(:software_version_critical_check, nil, admin_software_updates_path, true)
+ .with(:software_version_critical_check, nil, admin_software_updates_path, critical: true)
end
end
end
diff --git a/spec/lib/status_cache_hydrator_spec.rb b/spec/lib/status_cache_hydrator_spec.rb
index a0a82e3923..e56393da1d 100644
--- a/spec/lib/status_cache_hydrator_spec.rb
+++ b/spec/lib/status_cache_hydrator_spec.rb
@@ -50,14 +50,16 @@ RSpec.describe StatusCacheHydrator do
it 'renders the same attributes as full render' do
expect(subject).to eql(compare_to_hash)
expect(subject[:quote]).to_not be_nil
+ expect(subject[:quote_status]).to be_nil
end
end
context 'when handling an approved quote' do
let(:quoted_status) { Fabricate(:status) }
+ let(:legacy) { false }
before do
- Fabricate(:quote, status: status, quoted_status: quoted_status, state: :accepted)
+ Fabricate(:quote, status: status, quoted_status: quoted_status, state: :accepted, legacy: legacy)
end
it 'renders the same attributes as full render' do
@@ -65,6 +67,81 @@ RSpec.describe StatusCacheHydrator do
expect(subject[:quote]).to_not be_nil
end
+ context 'when the quote post is recursive' do
+ let(:quoted_status) { status }
+
+ it 'renders the same attributes as full render' do
+ expect(subject).to eql(compare_to_hash)
+ expect(subject[:quote]).to_not be_nil
+ end
+ end
+
+ context 'when the quote post is a legacy quote' do
+ let(:legacy) { true }
+
+ it 'renders the same attributes as full render' do
+ expect(subject).to eql(compare_to_hash)
+ expect(subject[:quote]).to_not be_nil
+ end
+ end
+
+ context 'when the quoted post is a private post the viewer is not authorized to see' do
+ let(:quoted_status) { Fabricate(:status, account: status.account, visibility: :private) }
+
+ it 'renders the same attributes as full render' do
+ expect(subject).to eql(compare_to_hash)
+ expect(subject[:quote]).to_not be_nil
+ expect(subject[:quote][:quoted_status]).to be_nil
+ end
+ end
+
+ context 'when the quoted post is a private post the viewer is authorized to see' do
+ let(:quoted_status) { Fabricate(:status, account: status.account, visibility: :private) }
+
+ before do
+ account.follow!(quoted_status.account)
+ end
+
+ it 'renders the same attributes as full render' do
+ expect(subject).to eql(compare_to_hash)
+ expect(subject[:quote]).to_not be_nil
+ expect(subject[:quote][:quoted_status]).to_not be_nil
+ end
+ end
+
+ context 'when the quoted post has been deleted' do
+ let(:quoted_status) { nil }
+
+ it 'returns the same attributes as full render' do
+ expect(subject).to eql(compare_to_hash)
+ expect(subject[:quote]).to_not be_nil
+ expect(subject[:quote][:quoted_status]).to be_nil
+ end
+ end
+
+ context 'when the quoted post author has blocked the viewer' do
+ before do
+ quoted_status.account.block!(account)
+ end
+
+ it 'returns the same attributes as full render' do
+ expect(subject).to eql(compare_to_hash)
+ expect(subject[:quote]).to_not be_nil
+ expect(subject[:quote][:quoted_status]).to be_nil
+ end
+ end
+
+ context 'when the viewer has blocked the quoted post author' do
+ before do
+ account.block!(quoted_status.account)
+ end
+
+ it 'returns the same attributes as full render' do
+ expect(subject).to eql(compare_to_hash)
+ expect(subject[:quote]).to_not be_nil
+ end
+ end
+
context 'when the quoted post has been favourited' do
before do
FavouriteService.new.call(account, quoted_status)
diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb
index 43e9ed087b..910eac4e9a 100644
--- a/spec/models/media_attachment_spec.rb
+++ b/spec/models/media_attachment_spec.rb
@@ -291,7 +291,7 @@ RSpec.describe MediaAttachment, :attachment_processing do
let(:media) { Fabricate(:media_attachment) }
before do
- allow(Rails.configuration.x).to receive(:cache_buster_enabled).and_return(true)
+ allow(Rails.configuration.x.cache_buster).to receive(:enabled).and_return(true)
end
it 'queues CacheBusterWorker jobs' do
diff --git a/spec/requests/api/v1/apps/credentials_spec.rb b/spec/requests/api/v1/apps/credentials_spec.rb
index 3aca53ed0a..8c0292d8c3 100644
--- a/spec/requests/api/v1/apps/credentials_spec.rb
+++ b/spec/requests/api/v1/apps/credentials_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe 'Credentials' do
redirect_uris: token.application.redirect_uris,
# Deprecated properties as of 4.3:
redirect_uri: token.application.redirect_uri.split.first,
- vapid_key: Rails.configuration.x.vapid_public_key
+ vapid_key: Rails.configuration.x.vapid.public_key
)
)
end
@@ -69,7 +69,7 @@ RSpec.describe 'Credentials' do
redirect_uris: token.application.redirect_uris,
# Deprecated properties as of 4.3:
redirect_uri: token.application.redirect_uri.split.first,
- vapid_key: Rails.configuration.x.vapid_public_key
+ vapid_key: Rails.configuration.x.vapid.public_key
)
)
end
diff --git a/spec/requests/api/v1/apps_spec.rb b/spec/requests/api/v1/apps_spec.rb
index 3120ab9c64..d8ff0a83d8 100644
--- a/spec/requests/api/v1/apps_spec.rb
+++ b/spec/requests/api/v1/apps_spec.rb
@@ -49,7 +49,7 @@ RSpec.describe 'Apps' do
redirect_uris: redirect_uris,
# Deprecated properties as of 4.3:
redirect_uri: redirect_uri,
- vapid_key: Rails.configuration.x.vapid_public_key
+ vapid_key: Rails.configuration.x.vapid.public_key
)
)
end
diff --git a/spec/requests/signature_verification_spec.rb b/spec/requests/signature_verification_spec.rb
index 128e7c0787..9f516d154d 100644
--- a/spec/requests/signature_verification_spec.rb
+++ b/spec/requests/signature_verification_spec.rb
@@ -69,284 +69,558 @@ RSpec.describe 'signature verification concern' do
end
end
- context 'with an HTTP Signature from a known account' do
- let!(:actor) { Fabricate(:account, domain: 'remote.domain', uri: 'https://remote.domain/users/bob', private_key: nil, public_key: actor_keypair.public_key.to_pem) }
+ context 'with an HTTP Signature (draft version)' do
+ context 'with a known account' do
+ let!(:actor) { Fabricate(:account, domain: 'remote.domain', uri: 'https://remote.domain/users/bob', private_key: nil, public_key: actor_keypair.public_key.to_pem) }
- context 'with a valid signature on a GET request' do
- let(:signature_header) do
- 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="' # rubocop:disable Layout/LineLength
+ context 'with a valid signature on a GET request' do
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'successfuly verifies signature', :aggregate_failures do
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
+
+ get '/activitypub/success', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Signature' => signature_header,
+ }
+
+ expect(response).to have_http_status(200)
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: actor.id.to_s
+ )
+ end
end
- it 'successfuly verifies signature', :aggregate_failures do
- expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
+ context 'with a valid signature on a GET request that has a query string' do
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="SDMa4r/DQYMXYxVgYO2yEqGWWUXugKjVuz0I8dniQAk+aunzBaF2aPu+4grBfawAshlx1Xytl8lhb0H2MllEz16/tKY7rUrb70MK0w8ohXgpb0qs3YvQgdj4X24L1x2MnkFfKHR/J+7TBlnivq0HZqXm8EIkPWLv+eQxu8fbowLwHIVvRd/3t6FzvcfsE0UZKkoMEX02542MhwSif6cu7Ec/clsY9qgKahb9JVGOGS1op9Lvg/9y1mc8KCgD83U5IxVygYeYXaVQ6gixA9NgZiTCwEWzHM5ELm7w5hpdLFYxYOHg/3G3fiqJzpzNQAcCD4S4JxfE7hMI0IzVlNLT6A=="' # rubocop:disable Layout/LineLength
+ end
+ it 'successfuly verifies signature', :aggregate_failures do
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success?foo=42', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
+
+ get '/activitypub/success?foo=42', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Signature' => signature_header,
+ }
+
+ expect(response).to have_http_status(200)
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: actor.id.to_s
+ )
+ end
+ end
+
+ context 'when the query string is missing from the signature verification (compatibility quirk)' do
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'successfuly verifies signature', :aggregate_failures do
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
+
+ get '/activitypub/success?foo=42', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Signature' => signature_header,
+ }
+
+ expect(response).to have_http_status(200)
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: actor.id.to_s
+ )
+ end
+ end
+
+ context 'with mismatching query string' do
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="SDMa4r/DQYMXYxVgYO2yEqGWWUXugKjVuz0I8dniQAk+aunzBaF2aPu+4grBfawAshlx1Xytl8lhb0H2MllEz16/tKY7rUrb70MK0w8ohXgpb0qs3YvQgdj4X24L1x2MnkFfKHR/J+7TBlnivq0HZqXm8EIkPWLv+eQxu8fbowLwHIVvRd/3t6FzvcfsE0UZKkoMEX02542MhwSif6cu7Ec/clsY9qgKahb9JVGOGS1op9Lvg/9y1mc8KCgD83U5IxVygYeYXaVQ6gixA9NgZiTCwEWzHM5ELm7w5hpdLFYxYOHg/3G3fiqJzpzNQAcCD4S4JxfE7hMI0IzVlNLT6A=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success?foo=42', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
+
+ get '/activitypub/success?foo=43', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Signature' => signature_header,
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: anything
+ )
+ end
+ end
+
+ context 'with a mismatching path' do
+ it 'fails to verify signature', :aggregate_failures do
+ get '/activitypub/alternative-path', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: anything
+ )
+ end
+ end
+
+ context 'with a mismatching method' do
+ it 'fails to verify signature', :aggregate_failures do
+ post '/activitypub/success', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: anything
+ )
+ end
+ end
+
+ context 'with an unparsable date' do
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="d4B7nfx8RJcfdJDu1J//5WzPzK/hgtPkdzZx49lu5QhnE7qdV3lgyVimmhCFrO16bwvzIp9iRMyRLkNFxLiEeVaa1gqeKbldGSnU0B0OMjx7rFBa65vLuzWQOATDitVGiBEYqoK4v0DMuFCz2DtFaA/DIUZ3sty8bZ/Ea3U1nByLOO6MacARA3zhMSI0GNxGqsSmZmG0hPLavB3jIXoE3IDoQabMnC39jrlcO/a8h1iaxBm2WD8TejrImJullgqlJIFpKhIHI3ipQkvTGPlm9dx0y+beM06qBvWaWQcmT09eRIUefVsOAzIhUtS/7FVb/URhZvircIJDa7vtiFcmZQ=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'wrong date', 'Host' => 'www.example.com' })
+
+ get '/activitypub/success', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'wrong date',
+ 'Signature' => signature_header,
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: 'Invalid Date header: not RFC 2616 compliant date: "wrong date"'
+ )
+ end
+ end
+
+ context 'with a request older than a day' do
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="G1NuJv4zgoZ3B/ZIjzDWZHK4RC+5pYee74q8/LJEMCWXhcnAomcb9YHaqk1QYfQvcBUIXw3UZ3Q9xO8F9y0i8G5mzJHfQ+OgHqCoJk8EmGwsUXJMh5s1S5YFCRt8TT12TmJZz0VMqLq85ubueSYBM7QtUE/FzFIVLvz4RysgXxaXQKzdnM6+gbUEEKdCURpXdQt2NXQhp4MAmZH3+0lQoR6VxdsK0hx0Ji2PNp1nuqFTlYqNWZazVdLBN+9rETLRmvGXknvg9jOxTTppBVWnkAIl26HtLS3wwFVvz4pJzi9OQDOvLziehVyLNbU61hky+oJ215e2HuKSe2hxHNl1MA=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'Wed, 18 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
+
+ get '/activitypub/success', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 18 Dec 2023 10:00:00 GMT',
+ 'Signature' => signature_header,
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: 'Signed request date outside acceptable time window'
+ )
+ end
+ end
+
+ context 'with a valid signature on a POST request' do
+ let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' }
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'successfuly verifies signature', :aggregate_failures do
+ expect(digest_header).to eq digest_value('Hello world')
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Digest' => digest_header })
+
+ post '/activitypub/success', params: 'Hello world', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Digest' => digest_header,
+ 'Signature' => signature_header,
+ }
+
+ expect(response).to have_http_status(200)
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: actor.id.to_s
+ )
+ end
+ end
+
+ context 'when the Digest of a POST request is not signed' do
+ let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' }
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date (request-target)",signature="CPD704CG8aCm8X8qIP8kkkiGp1qwFLk/wMVQHOGP0Txxan8c2DZtg/KK7eN8RG8tHx8br/yS2hJs51x4kXImYukGzNJd7ihE3T8lp+9RI1tCcdobTzr/VcVJHDFySdQkg266GCMijRQRZfNvqlJLiisr817PI+gNVBI5qV+vnVd1XhWCEZ+YSmMe8UqYARXAYNqMykTheojqGpTeTFGPUpTQA2Fmt2BipwIjcFDm2Hpihl2kB0MUS0x3zPmHDuadvzoBbN6m3usPDLgYrpALlh+wDs1dYMntcwdwawRKY1oE1XNtgOSum12wntDq3uYL4gya2iPdcw3c929b4koUzw=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ expect(digest_header).to eq digest_value('Hello world')
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT' })
+
+ post '/activitypub/success', params: 'Hello world', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Digest' => digest_header,
+ 'Signature' => signature_header,
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: 'Mastodon requires the Digest header to be signed when doing a POST request'
+ )
+ end
+ end
+
+ context 'with a tampered body on a POST request' do
+ let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' }
+ let(:signature_header) do
+ 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ expect(digest_header).to_not eq digest_value('Hello world!')
+ expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Digest' => digest_header })
+
+ post '/activitypub/success', params: 'Hello world!', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Digest' => 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=',
+ 'Signature' => signature_header,
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: 'Invalid Digest value. Computed SHA-256 digest: wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro=; given: ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw='
+ )
+ end
+ end
+
+ context 'with a tampered path in a POST request' do
+ it 'fails to verify signature', :aggregate_failures do
+ post '/activitypub/alternative-path', params: 'Hello world', headers: {
+ 'Host' => 'www.example.com',
+ 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
+ 'Digest' => 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=',
+ 'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="', # rubocop:disable Layout/LineLength
+ }
+
+ expect(response).to have_http_status(200)
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: anything
+ )
+ end
+ end
+ end
+
+ context 'with an inaccessible key' do
+ before do
+ stub_request(:get, 'https://remote.domain/users/alice#main-key').to_return(status: 404)
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
get '/activitypub/success', headers: {
'Host' => 'www.example.com',
'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
- 'Signature' => signature_header,
- }
-
- expect(response).to have_http_status(200)
- expect(response.parsed_body).to match(
- signed_request: true,
- signature_actor_id: actor.id.to_s
- )
- end
- end
-
- context 'with a valid signature on a GET request that has a query string' do
- let(:signature_header) do
- 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="SDMa4r/DQYMXYxVgYO2yEqGWWUXugKjVuz0I8dniQAk+aunzBaF2aPu+4grBfawAshlx1Xytl8lhb0H2MllEz16/tKY7rUrb70MK0w8ohXgpb0qs3YvQgdj4X24L1x2MnkFfKHR/J+7TBlnivq0HZqXm8EIkPWLv+eQxu8fbowLwHIVvRd/3t6FzvcfsE0UZKkoMEX02542MhwSif6cu7Ec/clsY9qgKahb9JVGOGS1op9Lvg/9y1mc8KCgD83U5IxVygYeYXaVQ6gixA9NgZiTCwEWzHM5ELm7w5hpdLFYxYOHg/3G3fiqJzpzNQAcCD4S4JxfE7hMI0IzVlNLT6A=="' # rubocop:disable Layout/LineLength
- end
-
- it 'successfuly verifies signature', :aggregate_failures do
- expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success?foo=42', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
-
- get '/activitypub/success?foo=42', headers: {
- 'Host' => 'www.example.com',
- 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
- 'Signature' => signature_header,
- }
-
- expect(response).to have_http_status(200)
- expect(response.parsed_body).to match(
- signed_request: true,
- signature_actor_id: actor.id.to_s
- )
- end
- end
-
- context 'when the query string is missing from the signature verification (compatibility quirk)' do
- let(:signature_header) do
- 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="' # rubocop:disable Layout/LineLength
- end
-
- it 'successfuly verifies signature', :aggregate_failures do
- expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
-
- get '/activitypub/success?foo=42', headers: {
- 'Host' => 'www.example.com',
- 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
- 'Signature' => signature_header,
- }
-
- expect(response).to have_http_status(200)
- expect(response.parsed_body).to match(
- signed_request: true,
- signature_actor_id: actor.id.to_s
- )
- end
- end
-
- context 'with mismatching query string' do
- let(:signature_header) do
- 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="SDMa4r/DQYMXYxVgYO2yEqGWWUXugKjVuz0I8dniQAk+aunzBaF2aPu+4grBfawAshlx1Xytl8lhb0H2MllEz16/tKY7rUrb70MK0w8ohXgpb0qs3YvQgdj4X24L1x2MnkFfKHR/J+7TBlnivq0HZqXm8EIkPWLv+eQxu8fbowLwHIVvRd/3t6FzvcfsE0UZKkoMEX02542MhwSif6cu7Ec/clsY9qgKahb9JVGOGS1op9Lvg/9y1mc8KCgD83U5IxVygYeYXaVQ6gixA9NgZiTCwEWzHM5ELm7w5hpdLFYxYOHg/3G3fiqJzpzNQAcCD4S4JxfE7hMI0IzVlNLT6A=="' # rubocop:disable Layout/LineLength
- end
-
- it 'fails to verify signature', :aggregate_failures do
- expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success?foo=42', { 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
-
- get '/activitypub/success?foo=43', headers: {
- 'Host' => 'www.example.com',
- 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
- 'Signature' => signature_header,
+ 'Signature' => 'keyId="https://remote.domain/users/alice#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength
}
expect(response.parsed_body).to match(
signed_request: true,
signature_actor_id: nil,
- error: anything
- )
- end
- end
-
- context 'with a mismatching path' do
- it 'fails to verify signature', :aggregate_failures do
- get '/activitypub/alternative-path', headers: {
- 'Host' => 'www.example.com',
- 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
- 'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength
- }
-
- expect(response.parsed_body).to match(
- signed_request: true,
- signature_actor_id: nil,
- error: anything
- )
- end
- end
-
- context 'with a mismatching method' do
- it 'fails to verify signature', :aggregate_failures do
- post '/activitypub/success', headers: {
- 'Host' => 'www.example.com',
- 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
- 'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength
- }
-
- expect(response.parsed_body).to match(
- signed_request: true,
- signature_actor_id: nil,
- error: anything
- )
- end
- end
-
- context 'with an unparsable date' do
- let(:signature_header) do
- 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="d4B7nfx8RJcfdJDu1J//5WzPzK/hgtPkdzZx49lu5QhnE7qdV3lgyVimmhCFrO16bwvzIp9iRMyRLkNFxLiEeVaa1gqeKbldGSnU0B0OMjx7rFBa65vLuzWQOATDitVGiBEYqoK4v0DMuFCz2DtFaA/DIUZ3sty8bZ/Ea3U1nByLOO6MacARA3zhMSI0GNxGqsSmZmG0hPLavB3jIXoE3IDoQabMnC39jrlcO/a8h1iaxBm2WD8TejrImJullgqlJIFpKhIHI3ipQkvTGPlm9dx0y+beM06qBvWaWQcmT09eRIUefVsOAzIhUtS/7FVb/URhZvircIJDa7vtiFcmZQ=="' # rubocop:disable Layout/LineLength
- end
-
- it 'fails to verify signature', :aggregate_failures do
- expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'wrong date', 'Host' => 'www.example.com' })
-
- get '/activitypub/success', headers: {
- 'Host' => 'www.example.com',
- 'Date' => 'wrong date',
- 'Signature' => signature_header,
- }
-
- expect(response.parsed_body).to match(
- signed_request: true,
- signature_actor_id: nil,
- error: 'Invalid Date header: not RFC 2616 compliant date: "wrong date"'
- )
- end
- end
-
- context 'with a request older than a day' do
- let(:signature_header) do
- 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="G1NuJv4zgoZ3B/ZIjzDWZHK4RC+5pYee74q8/LJEMCWXhcnAomcb9YHaqk1QYfQvcBUIXw3UZ3Q9xO8F9y0i8G5mzJHfQ+OgHqCoJk8EmGwsUXJMh5s1S5YFCRt8TT12TmJZz0VMqLq85ubueSYBM7QtUE/FzFIVLvz4RysgXxaXQKzdnM6+gbUEEKdCURpXdQt2NXQhp4MAmZH3+0lQoR6VxdsK0hx0Ji2PNp1nuqFTlYqNWZazVdLBN+9rETLRmvGXknvg9jOxTTppBVWnkAIl26HtLS3wwFVvz4pJzi9OQDOvLziehVyLNbU61hky+oJ215e2HuKSe2hxHNl1MA=="' # rubocop:disable Layout/LineLength
- end
-
- it 'fails to verify signature', :aggregate_failures do
- expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'get /activitypub/success', { 'Date' => 'Wed, 18 Dec 2023 10:00:00 GMT', 'Host' => 'www.example.com' })
-
- get '/activitypub/success', headers: {
- 'Host' => 'www.example.com',
- 'Date' => 'Wed, 18 Dec 2023 10:00:00 GMT',
- 'Signature' => signature_header,
- }
-
- expect(response.parsed_body).to match(
- signed_request: true,
- signature_actor_id: nil,
- error: 'Signed request date outside acceptable time window'
- )
- end
- end
-
- context 'with a valid signature on a POST request' do
- let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' }
- let(:signature_header) do
- 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="' # rubocop:disable Layout/LineLength
- end
-
- it 'successfuly verifies signature', :aggregate_failures do
- expect(digest_header).to eq digest_value('Hello world')
- expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Digest' => digest_header })
-
- post '/activitypub/success', params: 'Hello world', headers: {
- 'Host' => 'www.example.com',
- 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
- 'Digest' => digest_header,
- 'Signature' => signature_header,
- }
-
- expect(response).to have_http_status(200)
- expect(response.parsed_body).to match(
- signed_request: true,
- signature_actor_id: actor.id.to_s
- )
- end
- end
-
- context 'when the Digest of a POST request is not signed' do
- let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' }
- let(:signature_header) do
- 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date (request-target)",signature="CPD704CG8aCm8X8qIP8kkkiGp1qwFLk/wMVQHOGP0Txxan8c2DZtg/KK7eN8RG8tHx8br/yS2hJs51x4kXImYukGzNJd7ihE3T8lp+9RI1tCcdobTzr/VcVJHDFySdQkg266GCMijRQRZfNvqlJLiisr817PI+gNVBI5qV+vnVd1XhWCEZ+YSmMe8UqYARXAYNqMykTheojqGpTeTFGPUpTQA2Fmt2BipwIjcFDm2Hpihl2kB0MUS0x3zPmHDuadvzoBbN6m3usPDLgYrpALlh+wDs1dYMntcwdwawRKY1oE1XNtgOSum12wntDq3uYL4gya2iPdcw3c929b4koUzw=="' # rubocop:disable Layout/LineLength
- end
-
- it 'fails to verify signature', :aggregate_failures do
- expect(digest_header).to eq digest_value('Hello world')
- expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT' })
-
- post '/activitypub/success', params: 'Hello world', headers: {
- 'Host' => 'www.example.com',
- 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
- 'Digest' => digest_header,
- 'Signature' => signature_header,
- }
-
- expect(response.parsed_body).to match(
- signed_request: true,
- signature_actor_id: nil,
- error: 'Mastodon requires the Digest header to be signed when doing a POST request'
- )
- end
- end
-
- context 'with a tampered body on a POST request' do
- let(:digest_header) { 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=' }
- let(:signature_header) do
- 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="' # rubocop:disable Layout/LineLength
- end
-
- it 'fails to verify signature', :aggregate_failures do
- expect(digest_header).to_not eq digest_value('Hello world!')
- expect(signature_header).to eq build_signature_string(actor_keypair, 'https://remote.domain/users/bob#main-key', 'post /activitypub/success', { 'Host' => 'www.example.com', 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT', 'Digest' => digest_header })
-
- post '/activitypub/success', params: 'Hello world!', headers: {
- 'Host' => 'www.example.com',
- 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
- 'Digest' => 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=',
- 'Signature' => signature_header,
- }
-
- expect(response.parsed_body).to match(
- signed_request: true,
- signature_actor_id: nil,
- error: 'Invalid Digest value. Computed SHA-256 digest: wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro=; given: ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw='
- )
- end
- end
-
- context 'with a tampered path in a POST request' do
- it 'fails to verify signature', :aggregate_failures do
- post '/activitypub/alternative-path', params: 'Hello world', headers: {
- 'Host' => 'www.example.com',
- 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
- 'Digest' => 'SHA-256=ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=',
- 'Signature' => 'keyId="https://remote.domain/users/bob#main-key",algorithm="rsa-sha256",headers="host date digest (request-target)",signature="gmhMjgMROGElJU3fpehV2acD5kMHeELi8EFP2UPHOdQ54H0r55AxIpji+J3lPe+N2qSb/4H1KXIh6f0lRu8TGSsu12OQmg5hiO8VA9flcA/mh9Lpk+qwlQZIPRqKP9xUEfqD+Z7ti5wPzDKrWAUK/7FIqWgcT/mlqB1R1MGkpMFc/q4CIs2OSNiWgA4K+Kp21oQxzC2kUuYob04gAZ7cyE/FTia5t08uv6lVYFdRsn4XNPn1MsHgFBwBMRG79ng3SyhoG4PrqBEi5q2IdLq3zfre/M6He3wlCpyO2VJNdGVoTIzeZ0Zz8jUscPV3XtWUchpGclLGSaKaq/JyNZeiYQ=="', # rubocop:disable Layout/LineLength
- }
-
- expect(response).to have_http_status(200)
- expect(response.parsed_body).to match(
- signed_request: true,
- signature_actor_id: nil,
- error: anything
+ error: 'Unable to fetch key JSON at https://remote.domain/users/alice#main-key'
)
end
end
end
- context 'with an inaccessible key' do
- before do
- stub_request(:get, 'https://remote.domain/users/alice#main-key').to_return(status: 404)
+ context 'with an HTTP Message Signature (final RFC version)', feature: :http_message_signatures do
+ context 'with a known account' do
+ let!(:actor) { Fabricate(:account, domain: 'remote.domain', uri: 'https://remote.domain/users/bob', private_key: nil, public_key: actor_keypair.public_key.to_pem) }
+
+ context 'with a valid signature on a GET request' do
+ let(:signature_input) do
+ 'sig1=("@method" "@target-uri");created=1703066400;keyid="https://remote.domain/users/bob#main-key"'
+ end
+ let(:signature_header) do
+ 'sig1=:WfM6q/qBqhUyqPUDt9metjadJGtLLpmMTBzk/t+R3byKe4/TGAXC6vBB/M6NsD5qv8GCmQGtisCMQxJQO0IGODGzi+Jv+eqDJ50agMVXNV6nUOzY44c4/XTPoI98qyx1oEMa4Hefy3vSYKq96iDVAc+RDLCMTeGP3wn9wizjD1SNmU0RZI1bTB+eCkywMP9mM5zXzUOYF+Qkuf+WdEpPR1XUGPlnqfdvPalcKVfaI/VThBjI91D/lmUGoa69x4EBEHM+aJmW6086e7/dVh+FndKkdGfXslZXFZKi2flTGQZgEWLn948SqAaJQROkJg8B14Sb1NONS1qZBhK3Mum8Pg==:' # rubocop:disable Layout/LineLength
+ end
+
+ it 'successfully verifies signature', :aggregate_failures do
+ get '/activitypub/success', headers: {
+ 'Host' => 'www.example.com',
+ 'Signature-Input' => signature_input,
+ 'Signature' => signature_header,
+ }
+
+ expect(response).to have_http_status(200)
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: actor.id.to_s
+ )
+ end
+ end
+
+ context 'with a valid signature on a GET request that has a query string' do
+ let(:signature_input) do
+ 'sig1=("@method" "@target-uri");created=1703066400;keyid="https://remote.domain/users/bob#main-key"'
+ end
+ let(:signature_header) do
+ 'sig1=:JbC0oqoruKkT5p3bTASZZbHQP+EwUmT6+vBjEBw/KSkLPn+tKjEj0HHIMLA2Rw3bshZyzmsVD8+2UkPcwZYnE3gzuX0r0/gC8v4dSBfwGe7EBwpekB2xU8yHW4jawxiof2LmErvEocqcnI2uiA4IlJ09uz2Os/ARmf60lj+0Qf1qqzFeM7KoXJ331BUGMJ4cQ7iS4aO9RG4P8EJ+upe7Ik1LB/q9CZmk/6MFaB2lIemV0pcg2MwctpzMw9GWN1wL10hGxx+BPT2WCXdlPQmetVSoJ89WVV8S/4lQaCA1IucYUVDvBEFgMM//VJBuw7kg8wSTeAg9oKzbR2otLqv8Lg==:' # rubocop:disable Layout/LineLength
+ end
+
+ it 'successfuly verifies signature', :aggregate_failures do
+ get '/activitypub/success?foo=42', headers: {
+ 'Host' => 'www.example.com',
+ 'Signature-Input' => signature_input,
+ 'Signature' => signature_header,
+ }
+
+ expect(response).to have_http_status(200)
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: actor.id.to_s
+ )
+ end
+ end
+
+ context 'with mismatching query string' do
+ let(:signature_input) do
+ 'sig1=("@method" "@target-uri");created=1703066400;keyid="https://remote.domain/users/bob#main-key"'
+ end
+ let(:signature_header) do
+ 'sig1=:JbC0oqoruKkT5p3bTASZZbHQP+EwUmT6+vBjEBw/KSkLPn+tKjEj0HHIMLA2Rw3bshZyzmsVD8+2UkPcwZYnE3gzuX0r0/gC8v4dSBfwGe7EBwpekB2xU8yHW4jawxiof2LmErvEocqcnI2uiA4IlJ09uz2Os/ARmf60lj+0Qf1qqzFeM7KoXJ331BUGMJ4cQ7iS4aO9RG4P8EJ+upe7Ik1LB/q9CZmk/6MFaB2lIemV0pcg2MwctpzMw9GWN1wL10hGxx+BPT2WCXdlPQmetVSoJ89WVV8S/4lQaCA1IucYUVDvBEFgMM//VJBuw7kg8wSTeAg9oKzbR2otLqv8Lg==:' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ get '/activitypub/success?foo=43', headers: {
+ 'Host' => 'www.example.com',
+ 'Signature-Input' => signature_input,
+ 'Signature' => signature_header,
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: anything
+ )
+ end
+ end
+
+ context 'with a mismatching path' do
+ let(:signature_input) do
+ 'sig1=("@method" "@target-uri");created=1703066400;keyid="https://remote.domain/users/bob#main-key"'
+ end
+ let(:signature_header) do
+ 'sig1=:WfM6q/qBqhUyqPUDt9metjadJGtLLpmMTBzk/t+R3byKe4/TGAXC6vBB/M6NsD5qv8GCmQGtisCMQxJQO0IGODGzi+Jv+eqDJ50agMVXNV6nUOzY44c4/XTPoI98qyx1oEMa4Hefy3vSYKq96iDVAc+RDLCMTeGP3wn9wizjD1SNmU0RZI1bTB+eCkywMP9mM5zXzUOYF+Qkuf+WdEpPR1XUGPlnqfdvPalcKVfaI/VThBjI91D/lmUGoa69x4EBEHM+aJmW6086e7/dVh+FndKkdGfXslZXFZKi2flTGQZgEWLn948SqAaJQROkJg8B14Sb1NONS1qZBhK3Mum8Pg==:' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ get '/activitypub/alternative-path', headers: {
+ 'Host' => 'www.example.com',
+ 'Signature-Input' => signature_input,
+ 'Signature' => signature_header,
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: anything
+ )
+ end
+ end
+
+ context 'with a mismatching method' do
+ let(:signature_input) do
+ 'sig1=("@method" "@target-uri");created=1703066400;keyid="https://remote.domain/users/bob#main-key"'
+ end
+ let(:signature_header) do
+ 'sig1=:WfM6q/qBqhUyqPUDt9metjadJGtLLpmMTBzk/t+R3byKe4/TGAXC6vBB/M6NsD5qv8GCmQGtisCMQxJQO0IGODGzi+Jv+eqDJ50agMVXNV6nUOzY44c4/XTPoI98qyx1oEMa4Hefy3vSYKq96iDVAc+RDLCMTeGP3wn9wizjD1SNmU0RZI1bTB+eCkywMP9mM5zXzUOYF+Qkuf+WdEpPR1XUGPlnqfdvPalcKVfaI/VThBjI91D/lmUGoa69x4EBEHM+aJmW6086e7/dVh+FndKkdGfXslZXFZKi2flTGQZgEWLn948SqAaJQROkJg8B14Sb1NONS1qZBhK3Mum8Pg==:' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ post '/activitypub/success', headers: {
+ 'Host' => 'www.example.com',
+ 'Signature-Input' => signature_input,
+ 'Signature' => signature_header,
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: anything
+ )
+ end
+ end
+
+ context 'with a request older than a day' do
+ let(:signature_input) do
+ 'sig1=("@method" "@target-uri");created=1702893600;keyid="https://remote.domain/users/bob#main-key"'
+ end
+ let(:signature_header) do
+ 'sig1=:LtvwxwAAyiP7fGzsgKDRUHaZNNclAq6ScmH7RY+KERgaJcrjHFuqaYraQ3d9JVNsDJzZhdtJs+7UDPfIUjYNwWj/5KRQscB2sMQ9+EYR2tBDen+K5TILv/SXoWUdvVU/3vbGMiVIACgynaXokySNrE8AGFWdrzT5NbxE+/pJ0tkB3uWO7LfFpm0ipzo0NN07CGC2AUVl6WxsiTGWtFRqVrrHFmYmRcVYn7NxkKytx8eDg95cyIsB4xAHz8i++NqZHiXaooh79OdhOy10kMWHFDbuy/AijjI3aGtGriAbXdxb8O3nwoSCvfEJ5f7qQ+iMJl/fLvFOUZElZgRo3mk2lA==:' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ get '/activitypub/success', headers: {
+ 'Host' => 'www.example.com',
+ 'Signature-Input' => signature_input,
+ 'Signature' => signature_header,
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: 'Signed request date outside acceptable time window'
+ )
+ end
+ end
+
+ context 'with a valid signature on a POST request' do
+ let(:digest_header) { 'sha-256=:ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=:' }
+ let(:signature_input) do
+ 'sig1=("@method" "@target-uri" "content-digest");created=1703066400;keyid="https://remote.domain/users/bob#main-key"'
+ end
+ let(:signature_header) do
+ 'sig1=:c4jGY/PnOV4CwyvNnAmY6NLX0sf6EtbKu7kYseNARRZaq128PrP0GNQ4cd3XsX9cbMfJMw1ntI4zuEC81ncW8g+90OHP02bX0LkT57RweUtN4CSA01hRqSVe/MW32tjGixCiItvWqjNHoIZnZApu1bd+M3zMR+VCEue4/8a0D2eRrvfQxJUUBXZR1ZTRFlf1LNFDW3U7cuTbAKYr2zWVr7on+h2vA+vzEND9WE8z1SHd6SIFFgP0QRqrCXYx+vsTs3aLusTsamRWissoycJGexb64mI9iqiD8SD+uN1xk6iRU3nkUmhUquugjlOFyjxbbLo5ZnYjsECMt/BW+Catxw==:' # rubocop:disable Layout/LineLength
+ end
+
+ it 'successfuly verifies signature', :aggregate_failures do
+ post '/activitypub/success', params: 'Hello world', headers: {
+ 'Host' => 'www.example.com',
+ 'Content-Digest' => digest_header,
+ 'Signature-Input' => signature_input,
+ 'Signature' => signature_header,
+ }
+
+ expect(response).to have_http_status(200)
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: actor.id.to_s
+ )
+ end
+ end
+
+ context 'when the Digest of a POST request is not signed' do
+ let(:digest_header) { 'sha-256=:ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=:' }
+ let(:signature_input) do
+ 'sig1=("@method" "@target-uri");created=1703066400;keyid="https://remote.domain/users/bob#main-key"'
+ end
+ let(:signature_header) do
+ 'sig1=:Rhc5CzxdUYCzwo3V7y5wjEIN4o2XD90Bhf7lTDg2TIlp33ygl6ufwZpQ156fLJ0aUCkJ4+9KQsHBIkxF4PZJn8d/ZIfz3dpHJAVyMErAToSw+36V61mbnnnJxIPZPvmTT3zYCL7HPv+3GItOA4SqBhjJZRRJwOIW6NmmyrmSpc8xF9klnkeyGbYYRusaG7w6BDzM7ECCttxk120v1rHkGyqVON9fQADqs2LNqPa9WM9kWKiC5LhnZSYgoojhPmhniiA4NpgprncEBo4dOIC8CJihafWVSf+CZp3eogb/hn3Yd0//Pz0ta/lVtLGdb7t9f0rQFiqZfwIcCCz51nDMKw==:' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ post '/activitypub/success', params: 'Hello world', headers: {
+ 'Host' => 'www.example.com',
+ 'Content-Digest' => digest_header,
+ 'Signature-Input' => signature_input,
+ 'Signature' => signature_header,
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: 'Mastodon requires the Content-Digest header to be signed when doing a POST request'
+ )
+ end
+ end
+
+ context 'with a tampered body on a POST request' do
+ let(:digest_header) { 'sha-256=:ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=:' }
+ let(:signature_input) do
+ 'sig1=("@method" "@target-uri" "content-digest");created=1703066400;keyid="https://remote.domain/users/bob#main-key"'
+ end
+ let(:signature_header) do
+ 'sig1=:c4jGY/PnOV4CwyvNnAmY6NLX0sf6EtbKu7kYseNARRZaq128PrP0GNQ4cd3XsX9cbMfJMw1ntI4zuEC81ncW8g+90OHP02bX0LkT57RweUtN4CSA01hRqSVe/MW32tjGixCiItvWqjNHoIZnZApu1bd+M3zMR+VCEue4/8a0D2eRrvfQxJUUBXZR1ZTRFlf1LNFDW3U7cuTbAKYr2zWVr7on+h2vA+vzEND9WE8z1SHd6SIFFgP0QRqrCXYx+vsTs3aLusTsamRWissoycJGexb64mI9iqiD8SD+uN1xk6iRU3nkUmhUquugjlOFyjxbbLo5ZnYjsECMt/BW+Catxw==:' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ post '/activitypub/success', params: 'Hello world!', headers: {
+ 'Host' => 'www.example.com',
+ 'Content-Digest' => digest_header,
+ 'Signature-Input' => signature_input,
+ 'Signature' => signature_header,
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: 'Invalid Digest value. Computed SHA-256 digest: wFNeS+K3n/2TKRMFQ2v4iTFOSj+uwF7P/Lt98xrZ5Ro=; given: ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw='
+ )
+ end
+ end
+
+ context 'with a tampered path in a POST request' do
+ let(:digest_header) { 'sha-256=:ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=:' }
+ let(:signature_input) do
+ 'sig1=("@method" "@target-uri" "content-digest");created=1703066400;keyid="https://remote.domain/users/bob#main-key"'
+ end
+ let(:signature_header) do
+ 'sig1=:aJmJgCOuAJD1su2QeeD7Y9wfda8dqReyuau1EBWAz1DYwKWx5kVosONGgJb+XZFugh6CQ15XDFz0vRJRYdt6GyquloOFYLzPYWp3mYRlMhvehR64ALeGIwJbb460/tOeX2PwaFNVBrqLBAHf8PZDAPCxE8Q9cPWhewwQQirBZTm0xhOy8nRkSEfMish87JEQLkEzH+pZQDYIpv+oE+Tz6gow6bllCmjUd8vgLABpc7sZJTz5qklfOMqFczW6HvVxvQK/9G7V509u2z5I2PC/q+XdEs+jC0uzuer5baTJgL2q37gvnKzmz7pB+kbtBz5tmGNEMVLtPYQIEYjVI4Y34Q==:' # rubocop:disable Layout/LineLength
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ post '/activitypub/alternative-path', params: 'Hello world', headers: {
+ 'Host' => 'www.example.com',
+ 'Content-Digest' => digest_header,
+ 'Signature-Input' => signature_input,
+ 'Signature' => signature_header,
+ }
+
+ expect(response).to have_http_status(200)
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: anything
+ )
+ end
+ end
end
- it 'fails to verify signature', :aggregate_failures do
- get '/activitypub/success', headers: {
- 'Host' => 'www.example.com',
- 'Date' => 'Wed, 20 Dec 2023 10:00:00 GMT',
- 'Signature' => 'keyId="https://remote.domain/users/alice#main-key",algorithm="rsa-sha256",headers="date host (request-target)",signature="Z8ilar3J7bOwqZkMp7sL8sRs4B1FT+UorbmvWoE+A5UeoOJ3KBcUmbsh+k3wQwbP5gMNUrra9rEWabpasZGphLsbDxfbsWL3Cf0PllAc7c1c7AFEwnewtExI83/qqgEkfWc2z7UDutXc2NfgAx89Ox8DXU/fA2GG0jILjB6UpFyNugkY9rg6oI31UnvfVi3R7sr3/x8Ea3I9thPvqI2byF6cojknSpDAwYzeKdngX3TAQEGzFHz3SDWwyp3jeMWfwvVVbM38FxhvAnSumw7YwWW4L7M7h4M68isLimoT3yfCn2ucBVL5Dz8koBpYf/40w7QidClAwCafZQFC29yDOg=="', # rubocop:disable Layout/LineLength
- }
+ context 'with an inaccessible key' do
+ let(:signature_input) do
+ 'sig1=("@method" "@target-uri");created=1703066400;keyid="https://remote.domain/users/alice#main-key"'
+ end
+ let(:signature_header) do
+ 'sig1=:WfM6q/qBqhUyqPUDt9metjadJGtLLpmMTBzk/t+R3byKe4/TGAXC6vBB/M6NsD5qv8GCmQGtisCMQxJQO0IGODGzi+Jv+eqDJ50agMVXNV6nUOzY44c4/XTPoI98qyx1oEMa4Hefy3vSYKq96iDVAc+RDLCMTeGP3wn9wizjD1SNmU0RZI1bTB+eCkywMP9mM5zXzUOYF+Qkuf+WdEpPR1XUGPlnqfdvPalcKVfaI/VThBjI91D/lmUGoa69x4EBEHM+aJmW6086e7/dVh+FndKkdGfXslZXFZKi2flTGQZgEWLn948SqAaJQROkJg8B14Sb1NONS1qZBhK3Mum8Pg==:' # rubocop:disable Layout/LineLength
+ end
- expect(response.parsed_body).to match(
- signed_request: true,
- signature_actor_id: nil,
- error: 'Unable to fetch key JSON at https://remote.domain/users/alice#main-key'
- )
+ before do
+ stub_request(:get, 'https://remote.domain/users/alice#main-key').to_return(status: 404)
+ end
+
+ it 'fails to verify signature', :aggregate_failures do
+ get '/activitypub/success', headers: {
+ 'Host' => 'www.example.com',
+ 'Signature-Input' => signature_input,
+ 'Signature' => signature_header,
+ }
+
+ expect(response.parsed_body).to match(
+ signed_request: true,
+ signature_actor_id: nil,
+ error: 'Unable to fetch key JSON at https://remote.domain/users/alice#main-key'
+ )
+ end
end
end
diff --git a/spec/serializers/rest/instance_serializer_spec.rb b/spec/serializers/rest/instance_serializer_spec.rb
index d23ac8341e..964eee8372 100644
--- a/spec/serializers/rest/instance_serializer_spec.rb
+++ b/spec/serializers/rest/instance_serializer_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe REST::InstanceSerializer do
describe 'configuration' do
it 'returns the VAPID public key' do
expect(serialization['configuration']['vapid']).to eq({
- 'public_key' => Rails.configuration.x.vapid_public_key,
+ 'public_key' => Rails.configuration.x.vapid.public_key,
})
end
diff --git a/spec/services/activitypub/process_status_update_service_spec.rb b/spec/services/activitypub/process_status_update_service_spec.rb
index 0eed809dd7..864883257f 100644
--- a/spec/services/activitypub/process_status_update_service_spec.rb
+++ b/spec/services/activitypub/process_status_update_service_spec.rb
@@ -754,7 +754,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
- context 'when the status has an existing unverified quote and adds an approval link', feature: :inbound_quotes do
+ context 'when the status has an existing unverified quote and adds an approval link' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: nil) }
@@ -819,7 +819,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
- context 'when the status has an existing verified quote and removes an approval link', feature: :inbound_quotes do
+ context 'when the status has an existing verified quote and removes an approval link' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri, state: :accepted) }
@@ -854,7 +854,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
- context 'when the status adds a verifiable quote', feature: :inbound_quotes do
+ context 'when the status adds a verifiable quote' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
@@ -919,7 +919,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
- context 'when the status adds a unverifiable quote', feature: :inbound_quotes do
+ context 'when the status adds a unverifiable quote' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let(:approval_uri) { 'https://quoted.example.com/approvals/1' }
@@ -954,7 +954,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
- context 'when the status removes a verified quote', feature: :inbound_quotes do
+ context 'when the status removes a verified quote' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: approval_uri, state: :accepted) }
@@ -979,7 +979,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
- context 'when the status removes an unverified quote', feature: :inbound_quotes do
+ context 'when the status removes an unverified quote' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let!(:quote) { Fabricate(:quote, status: status, quoted_status: quoted_status, approval_uri: nil, state: :pending) }
@@ -1003,7 +1003,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
- context 'when the status swaps a verified quote with an unverifiable quote', feature: :inbound_quotes do
+ context 'when the status swaps a verified quote with an unverifiable quote' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
let(:second_quoted_status) { Fabricate(:status, account: quoted_account) }
@@ -1071,7 +1071,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService do
end
end
- context 'when the status swaps a verified quote with another verifiable quote', feature: :inbound_quotes do
+ context 'when the status swaps a verified quote with another verifiable quote' do
let(:quoted_account) { Fabricate(:account, domain: 'quoted.example.com') }
let(:second_quoted_account) { Fabricate(:account, domain: 'second-quoted.example.com') }
let(:quoted_status) { Fabricate(:status, account: quoted_account) }
diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb
index 2b1455e9a8..f4578128cc 100644
--- a/spec/services/suspend_account_service_spec.rb
+++ b/spec/services/suspend_account_service_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe SuspendAccountService do
before do
allow(FeedManager.instance).to receive_messages(unmerge_from_home: nil, unmerge_from_list: nil)
- allow(Rails.configuration.x).to receive(:cache_buster_enabled).and_return(true)
+ allow(Rails.configuration.x.cache_buster).to receive(:enabled).and_return(true)
local_follower.follow!(account)
list.accounts << account
diff --git a/spec/support/browser_errors.rb b/spec/support/browser_errors.rb
index 8a31ba3ab6..6c101540a3 100644
--- a/spec/support/browser_errors.rb
+++ b/spec/support/browser_errors.rb
@@ -9,29 +9,36 @@ end
RSpec.configure do |config|
config.include BrowserErrorsHelpers, :js, type: :system
- config.before(:each, :js, type: :system) do
+ config.before(:each, :js, type: :system) do |example|
@ignored_js_errors_for_spec = []
+
+ example.metadata[:js_console_messages] ||= []
+ Capybara.current_session.driver.with_playwright_page do |page|
+ page.on('console', lambda { |msg|
+ example.metadata[:js_console_messages] << { type: msg.type, text: msg.text, location: msg.location }
+ })
+ end
end
- config.after(:each, :js, type: :system) do
+ config.after(:each, :js, type: :system) do |example|
# Classes of intermittent ignorable errors
ignored_errors = [
/Error while trying to use the following icon from the Manifest/, # https://github.com/mastodon/mastodon/pull/30793
/Manifest: Line: 1, column: 1, Syntax error/, # Similar parsing/interruption issue as above
].concat(@ignored_js_errors_for_spec)
- errors = page.driver.browser.logs.get(:browser).reject do |error|
- ignored_errors.any? { |pattern| pattern.match(error.message) }
+ errors = example.metadata[:js_console_messages].reject do |msg|
+ ignored_errors.any? { |pattern| pattern.match(msg[:text]) }
end
if errors.present?
aggregate_failures 'browser errrors' do
errors.each do |error|
- expect(error.level).to_not eq('SEVERE'), error.message
- next unless error.level == 'WARNING'
+ expect(error[:type]).to_not eq('error'), error[:text]
+ next unless error[:type] == 'warning'
warn 'WARN: browser warning'
- warn error.message
+ warn error[:text]
end
end
end
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index 4357d51382..aa548c3782 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -4,32 +4,16 @@ Capybara.server_host = 'localhost'
Capybara.server_port = 3000
Capybara.app_host = "http://#{Capybara.server_host}:#{Capybara.server_port}"
-require 'selenium/webdriver'
-
-def common_chrome_options
- options = Selenium::WebDriver::Chrome::Options.new
- options.add_argument '--window-size=1680,1050'
- options.add_argument '--disable-search-engine-choice-screen'
- options
+Capybara.register_driver(:playwright) do |app|
+ Capybara::Playwright::Driver.new(app)
end
+Capybara.javascript_driver = :playwright
-Capybara.register_driver :chrome do |app|
- Capybara::Selenium::Driver.new(app, browser: :chrome, options: common_chrome_options)
+if ENV['CI'].present?
+ # Reduce intermittent failures from slow CI runner environment
+ Capybara.default_max_wait_time = 2**3
end
-Capybara.register_driver :headless_chrome do |app|
- options = common_chrome_options
- options.add_argument '--headless=new'
-
- Capybara::Selenium::Driver.new(
- app,
- browser: :chrome,
- options: options
- )
-end
-
-Capybara.javascript_driver = :headless_chrome
-
RSpec.configure do |config|
config.before(:each, type: :system) do
driven_by :rack_test
diff --git a/spec/system/account_notes_spec.rb b/spec/system/account_notes_spec.rb
index 1d125e1984..eaef248347 100644
--- a/spec/system/account_notes_spec.rb
+++ b/spec/system/account_notes_spec.rb
@@ -11,13 +11,13 @@ RSpec.describe 'Account notes', :inline_jobs, :js, :streaming do
let(:finished_onboarding) { true }
let!(:other_account) { Fabricate(:account) }
+ let(:note_text) { 'This is a personal note' }
before { as_a_logged_in_user }
it 'can be written and viewed' do
visit_profile(other_account)
- note_text = 'This is a personal note'
fill_in frontend_translations('account_note.placeholder'), with: note_text
# This is a bit awkward since there is no button to save the change
diff --git a/spec/system/log_out_spec.rb b/spec/system/log_out_spec.rb
index ebbf5a5772..43fbde7eea 100644
--- a/spec/system/log_out_spec.rb
+++ b/spec/system/log_out_spec.rb
@@ -26,14 +26,14 @@ RSpec.describe 'Log out' do
describe 'Logging out from the JS app', :js, :streaming do
it 'logs the user out' do
# The frontend tries to load announcements after a short delay, but the session might be expired by then, and the browser will output an error.
- ignore_js_error(/Failed to load resource: the server responded with a status of 422/)
+ ignore_js_error(/Failed to load resource: the server responded with a status/)
visit root_path
expect(page)
.to have_css('body', class: 'app-body')
- within '.navigation-bar' do
- click_on 'Menu'
+ within '.navigation-panel' do
+ click_on 'More'
end
within '.dropdown-menu' do
diff --git a/spec/system/new_statuses_spec.rb b/spec/system/new_statuses_spec.rb
index 87b29004b2..5af061806d 100644
--- a/spec/system/new_statuses_spec.rb
+++ b/spec/system/new_statuses_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe 'NewStatuses', :inline_jobs, :js, :streaming do
let(:password) { 'password' }
let(:confirmed_at) { Time.zone.now }
let(:finished_onboarding) { true }
+ let(:status_text) { 'This is a new status!' }
before do
as_a_logged_in_user
@@ -17,7 +18,6 @@ RSpec.describe 'NewStatuses', :inline_jobs, :js, :streaming do
it 'can be posted' do
visit_homepage
- status_text = 'This is a new status!'
within('.compose-form') do
fill_in frontend_translations('compose_form.placeholder'), with: status_text
diff --git a/spec/workers/domain_block_worker_spec.rb b/spec/workers/domain_block_worker_spec.rb
index c55aa2c0c3..cb0f98a29b 100644
--- a/spec/workers/domain_block_worker_spec.rb
+++ b/spec/workers/domain_block_worker_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe DomainBlockWorker do
result = subject.perform(domain_block.id)
expect(result).to be_nil
- expect(service).to have_received(:call).with(domain_block, false)
+ expect(service).to have_received(:call).with(domain_block, update: false)
end
it 'returns true for non-existent domain block' do
diff --git a/spec/workers/web/push_notification_worker_spec.rb b/spec/workers/web/push_notification_worker_spec.rb
index 6ee8ae53f8..d18d6c4d68 100644
--- a/spec/workers/web/push_notification_worker_spec.rb
+++ b/spec/workers/web/push_notification_worker_spec.rb
@@ -38,13 +38,13 @@ RSpec.describe Web::PushNotificationWorker do
describe 'perform' do
around do |example|
- original_private = Rails.configuration.x.vapid_private_key
- original_public = Rails.configuration.x.vapid_public_key
- Rails.configuration.x.vapid_private_key = vapid_private_key
- Rails.configuration.x.vapid_public_key = vapid_public_key
+ original_private = Rails.configuration.x.vapid.private_key
+ original_public = Rails.configuration.x.vapid.public_key
+ Rails.configuration.x.vapid.private_key = vapid_private_key
+ Rails.configuration.x.vapid.public_key = vapid_public_key
example.run
- Rails.configuration.x.vapid_private_key = original_private
- Rails.configuration.x.vapid_public_key = original_public
+ Rails.configuration.x.vapid.private_key = original_private
+ Rails.configuration.x.vapid.public_key = original_public
end
before do
diff --git a/streaming/package.json b/streaming/package.json
index a8dbe6802b..dd332230ef 100644
--- a/streaming/package.json
+++ b/streaming/package.json
@@ -1,7 +1,7 @@
{
"name": "@mastodon/streaming",
"license": "AGPL-3.0-or-later",
- "packageManager": "yarn@4.9.1",
+ "packageManager": "yarn@4.9.2",
"engines": {
"node": ">=20"
},
diff --git a/tsconfig.json b/tsconfig.json
index 39a241ea9c..80745b43bb 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -27,9 +27,12 @@
},
"include": [
"vite.config.mts",
+ "vitest.config.mts",
"config/vite",
"app/javascript/mastodon",
"app/javascript/entrypoints",
- "app/javascript/types"
+ "app/javascript/types",
+ ".storybook/*.ts",
+ ".storybook/*.tsx"
]
}
diff --git a/vitest.config.mts b/vitest.config.mts
index 7ad3fc99f0..14aa54778b 100644
--- a/vitest.config.mts
+++ b/vitest.config.mts
@@ -1,26 +1,69 @@
-import { configDefaults, defineConfig } from 'vitest/config';
+import { resolve } from 'node:path';
+
+import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
+import react from '@vitejs/plugin-react';
+import svgr from 'vite-plugin-svgr';
+import tsconfigPaths from 'vite-tsconfig-paths';
+import {
+ configDefaults,
+ defineConfig,
+ TestProjectInlineConfiguration,
+} from 'vitest/config';
import { config as viteConfig } from './vite.config.mjs';
+const storybookTests: TestProjectInlineConfiguration = {
+ plugins: [
+ // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
+ storybookTest({
+ configDir: '.storybook',
+ storybookScript: 'yarn run storybook',
+ }),
+ react(),
+ svgr(),
+ tsconfigPaths(),
+ ],
+ test: {
+ name: 'storybook',
+ browser: {
+ enabled: true,
+ headless: true,
+ provider: 'playwright',
+ instances: [{ browser: 'chromium' }],
+ },
+ setupFiles: [resolve(__dirname, '.storybook/vitest.setup.ts')],
+ },
+};
+
+const legacyTests: TestProjectInlineConfiguration = {
+ extends: true,
+ test: {
+ name: 'legacy-tests',
+ environment: 'jsdom',
+ include: [
+ ...configDefaults.include,
+ '**/__tests__/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}',
+ ],
+ exclude: [
+ ...configDefaults.exclude,
+ '**/node_modules/**',
+ 'vendor/**',
+ 'config/**',
+ 'log/**',
+ 'public/**',
+ 'tmp/**',
+ ],
+ globals: true,
+ },
+};
+
export default defineConfig(async (context) => {
+ const baseConfig = await viteConfig(context);
+
return {
- ...(await viteConfig(context)),
+ ...baseConfig,
test: {
- environment: 'jsdom',
- include: [
- ...configDefaults.include,
- '**/__tests__/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}',
- ],
- exclude: [
- ...configDefaults.exclude,
- '**/node_modules/**',
- 'vendor/**',
- 'config/**',
- 'log/**',
- 'public/**',
- 'tmp/**',
- ],
- globals: true,
+ projects: [legacyTests, storybookTests],
},
};
});
diff --git a/vitest.shims.d.ts b/vitest.shims.d.ts
new file mode 100644
index 0000000000..a1d31e5a7b
--- /dev/null
+++ b/vitest.shims.d.ts
@@ -0,0 +1 @@
+///
diff --git a/yarn.lock b/yarn.lock
index aa9e1cb40c..0b76b9449c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12,13 +12,20 @@ __metadata:
languageName: node
linkType: hard
-"@ampproject/remapping@npm:^2.2.0":
- version: 2.2.1
- resolution: "@ampproject/remapping@npm:2.2.1"
+"@adobe/css-tools@npm:^4.4.0":
+ version: 4.4.3
+ resolution: "@adobe/css-tools@npm:4.4.3"
+ checksum: 10c0/6d16c4d4b6752d73becf6e58611f893c7ed96e04017ff7084310901ccdbe0295171b722b158f6a2b0aa77182ef3446ffd62b39488fa5a7adab1f0dfe5ffafbae
+ languageName: node
+ linkType: hard
+
+"@ampproject/remapping@npm:^2.2.0, @ampproject/remapping@npm:^2.3.0":
+ version: 2.3.0
+ resolution: "@ampproject/remapping@npm:2.3.0"
dependencies:
- "@jridgewell/gen-mapping": "npm:^0.3.0"
- "@jridgewell/trace-mapping": "npm:^0.3.9"
- checksum: 10c0/92ce5915f8901d8c7cd4f4e6e2fe7b9fd335a29955b400caa52e0e5b12ca3796ada7c2f10e78c9c5b0f9c2539dff0ffea7b19850a56e1487aa083531e1e46d43
+ "@jridgewell/gen-mapping": "npm:^0.3.5"
+ "@jridgewell/trace-mapping": "npm:^0.3.24"
+ checksum: 10c0/81d63cca5443e0f0c72ae18b544cc28c7c0ec2cea46e7cb888bb0e0f411a1191d0d6b7af798d54e30777d8d1488b2ec0732aac2be342d3d7d3ffd271c6f489ed
languageName: node
linkType: hard
@@ -48,7 +55,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.26.2, @babel/code-frame@npm:^7.27.1":
+"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.27.1":
version: 7.27.1
resolution: "@babel/code-frame@npm:7.27.1"
dependencies:
@@ -66,39 +73,52 @@ __metadata:
languageName: node
linkType: hard
-"@babel/core@npm:^7.21.3, @babel/core@npm:^7.24.4, @babel/core@npm:^7.26.10":
- version: 7.26.10
- resolution: "@babel/core@npm:7.26.10"
+"@babel/core@npm:^7.18.9, @babel/core@npm:^7.21.3, @babel/core@npm:^7.24.4, @babel/core@npm:^7.26.10, @babel/core@npm:^7.27.4":
+ version: 7.27.4
+ resolution: "@babel/core@npm:7.27.4"
dependencies:
"@ampproject/remapping": "npm:^2.2.0"
- "@babel/code-frame": "npm:^7.26.2"
- "@babel/generator": "npm:^7.26.10"
- "@babel/helper-compilation-targets": "npm:^7.26.5"
- "@babel/helper-module-transforms": "npm:^7.26.0"
- "@babel/helpers": "npm:^7.26.10"
- "@babel/parser": "npm:^7.26.10"
- "@babel/template": "npm:^7.26.9"
- "@babel/traverse": "npm:^7.26.10"
- "@babel/types": "npm:^7.26.10"
+ "@babel/code-frame": "npm:^7.27.1"
+ "@babel/generator": "npm:^7.27.3"
+ "@babel/helper-compilation-targets": "npm:^7.27.2"
+ "@babel/helper-module-transforms": "npm:^7.27.3"
+ "@babel/helpers": "npm:^7.27.4"
+ "@babel/parser": "npm:^7.27.4"
+ "@babel/template": "npm:^7.27.2"
+ "@babel/traverse": "npm:^7.27.4"
+ "@babel/types": "npm:^7.27.3"
convert-source-map: "npm:^2.0.0"
debug: "npm:^4.1.0"
gensync: "npm:^1.0.0-beta.2"
json5: "npm:^2.2.3"
semver: "npm:^6.3.1"
- checksum: 10c0/e046e0e988ab53841b512ee9d263ca409f6c46e2a999fe53024688b92db394346fa3aeae5ea0866331f62133982eee05a675d22922a4603c3f603aa09a581d62
+ checksum: 10c0/d2d17b106a8d91d3eda754bb3f26b53a12eb7646df73c2b2d2e9b08d90529186bc69e3823f70a96ec6e5719dc2372fb54e14ad499da47ceeb172d2f7008787b5
languageName: node
linkType: hard
-"@babel/generator@npm:^7.24.5, @babel/generator@npm:^7.26.10, @babel/generator@npm:^7.27.1":
- version: 7.27.1
- resolution: "@babel/generator@npm:7.27.1"
+"@babel/generator@npm:^7.24.5":
+ version: 7.27.5
+ resolution: "@babel/generator@npm:7.27.5"
dependencies:
- "@babel/parser": "npm:^7.27.1"
- "@babel/types": "npm:^7.27.1"
+ "@babel/parser": "npm:^7.27.5"
+ "@babel/types": "npm:^7.27.3"
"@jridgewell/gen-mapping": "npm:^0.3.5"
"@jridgewell/trace-mapping": "npm:^0.3.25"
jsesc: "npm:^3.0.2"
- checksum: 10c0/c4156434b21818f558ebd93ce45f027c53ee570ce55a84fd2d9ba45a79ad204c17e0bff753c886fb6c07df3385445a9e34dc7ccb070d0ac7e80bb91c8b57f423
+ checksum: 10c0/8f649ef4cd81765c832bb11de4d6064b035ffebdecde668ba7abee68a7b0bce5c9feabb5dc5bb8aeba5bd9e5c2afa3899d852d2bd9ca77a711ba8c8379f416f0
+ languageName: node
+ linkType: hard
+
+"@babel/generator@npm:^7.27.3":
+ version: 7.27.3
+ resolution: "@babel/generator@npm:7.27.3"
+ dependencies:
+ "@babel/parser": "npm:^7.27.3"
+ "@babel/types": "npm:^7.27.3"
+ "@jridgewell/gen-mapping": "npm:^0.3.5"
+ "@jridgewell/trace-mapping": "npm:^0.3.25"
+ jsesc: "npm:^3.0.2"
+ checksum: 10c0/341622e17c61d008fc746b655ab95ef7febb543df8efb4148f57cf06e60ade1abe091ed7d6811df17b064d04d64f69bb7f35ab0654137116d55c54a73145a61a
languageName: node
linkType: hard
@@ -111,7 +131,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.26.5, @babel/helper-compilation-targets@npm:^7.27.1, @babel/helper-compilation-targets@npm:^7.27.2":
+"@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.27.1, @babel/helper-compilation-targets@npm:^7.27.2":
version: 7.27.2
resolution: "@babel/helper-compilation-targets@npm:7.27.2"
dependencies:
@@ -189,16 +209,16 @@ __metadata:
languageName: node
linkType: hard
-"@babel/helper-module-transforms@npm:^7.26.0, @babel/helper-module-transforms@npm:^7.27.1":
- version: 7.27.1
- resolution: "@babel/helper-module-transforms@npm:7.27.1"
+"@babel/helper-module-transforms@npm:^7.27.1, @babel/helper-module-transforms@npm:^7.27.3":
+ version: 7.27.3
+ resolution: "@babel/helper-module-transforms@npm:7.27.3"
dependencies:
"@babel/helper-module-imports": "npm:^7.27.1"
"@babel/helper-validator-identifier": "npm:^7.27.1"
- "@babel/traverse": "npm:^7.27.1"
+ "@babel/traverse": "npm:^7.27.3"
peerDependencies:
"@babel/core": ^7.0.0
- checksum: 10c0/196ab29635fe6eb5ba6ead2972d41b1c0d40f400f99bd8fc109cef21440de24c26c972fabf932585e618694d590379ab8d22def8da65a54459d38ec46112ead7
+ checksum: 10c0/fccb4f512a13b4c069af51e1b56b20f54024bcf1591e31e978a30f3502567f34f90a80da6a19a6148c249216292a8074a0121f9e52602510ef0f32dbce95ca01
languageName: node
linkType: hard
@@ -286,24 +306,24 @@ __metadata:
languageName: node
linkType: hard
-"@babel/helpers@npm:^7.26.10":
- version: 7.27.0
- resolution: "@babel/helpers@npm:7.27.0"
+"@babel/helpers@npm:^7.27.4":
+ version: 7.27.4
+ resolution: "@babel/helpers@npm:7.27.4"
dependencies:
- "@babel/template": "npm:^7.27.0"
- "@babel/types": "npm:^7.27.0"
- checksum: 10c0/a3c64fd2d8b164c041808826cc00769d814074ea447daaacaf2e3714b66d3f4237ef6e420f61d08f463d6608f3468c2ac5124ab7c68f704e20384def5ade95f4
+ "@babel/template": "npm:^7.27.2"
+ "@babel/types": "npm:^7.27.3"
+ checksum: 10c0/3463551420926b3f403c1a30d66ac67bba5c4f73539a8ccb71544da129c4709ac37c57fac740ed8a261b3e6bbbf353b05e03b36ea1a6bf1081604b2a94ca43c1
languageName: node
linkType: hard
-"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.26.10, @babel/parser@npm:^7.27.1":
- version: 7.27.1
- resolution: "@babel/parser@npm:7.27.1"
+"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.27.3, @babel/parser@npm:^7.27.4":
+ version: 7.27.4
+ resolution: "@babel/parser@npm:7.27.4"
dependencies:
- "@babel/types": "npm:^7.27.1"
+ "@babel/types": "npm:^7.27.3"
bin:
parser: ./bin/babel-parser.js
- checksum: 10c0/ae4a5eda3ada3fd54c9942d9f14385df7a18e71b386cf2652505bb9a40a32250dfde3bdda71fb08af00b1e154f0a6213e6cdaaa88e9941229ec0003f7fead759
+ checksum: 10c0/d1bf17e7508585235e2a76594ba81828e48851877112bb8abbecd7161a31fb66654e993e458ddaedb18a3d5fa31970e5f3feca5ae2900f51e6d8d3d35da70dbf
languageName: node
linkType: hard
@@ -318,6 +338,17 @@ __metadata:
languageName: node
linkType: hard
+"@babel/parser@npm:^7.27.5":
+ version: 7.27.5
+ resolution: "@babel/parser@npm:7.27.5"
+ dependencies:
+ "@babel/types": "npm:^7.27.3"
+ bin:
+ parser: ./bin/babel-parser.js
+ checksum: 10c0/f7faaebf21cc1f25d9ca8ac02c447ed38ef3460ea95be7ea760916dcf529476340d72a5a6010c6641d9ed9d12ad827c8424840277ec2295c5b082ba0f291220a
+ languageName: node
+ linkType: hard
+
"@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.27.1":
version: 7.27.1
resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.27.1"
@@ -881,7 +912,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/plugin-transform-react-jsx-self@npm:^7.25.9":
+"@babel/plugin-transform-react-jsx-self@npm:^7.27.1":
version: 7.27.1
resolution: "@babel/plugin-transform-react-jsx-self@npm:7.27.1"
dependencies:
@@ -892,7 +923,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/plugin-transform-react-jsx-source@npm:^7.25.9":
+"@babel/plugin-transform-react-jsx-source@npm:^7.27.1":
version: 7.27.1
resolution: "@babel/plugin-transform-react-jsx-source@npm:7.27.1"
dependencies:
@@ -1150,39 +1181,56 @@ __metadata:
languageName: node
linkType: hard
-"@babel/template@npm:^7.26.9, @babel/template@npm:^7.27.0, @babel/template@npm:^7.27.1":
- version: 7.27.1
- resolution: "@babel/template@npm:7.27.1"
+"@babel/template@npm:^7.27.1, @babel/template@npm:^7.27.2":
+ version: 7.27.2
+ resolution: "@babel/template@npm:7.27.2"
dependencies:
"@babel/code-frame": "npm:^7.27.1"
- "@babel/parser": "npm:^7.27.1"
+ "@babel/parser": "npm:^7.27.2"
"@babel/types": "npm:^7.27.1"
- checksum: 10c0/155a8e056e82f1f1e2413b7bf9d96890e371d617c7f77f25621fb0ddb32128958d86bc5c3356f00be266e9f8c121d886de5b4143dbb72eac362377f53aba72a2
+ checksum: 10c0/ed9e9022651e463cc5f2cc21942f0e74544f1754d231add6348ff1b472985a3b3502041c0be62dc99ed2d12cfae0c51394bf827452b98a2f8769c03b87aadc81
languageName: node
linkType: hard
-"@babel/traverse@npm:^7.24.5, @babel/traverse@npm:^7.26.10, @babel/traverse@npm:^7.27.1":
- version: 7.27.1
- resolution: "@babel/traverse@npm:7.27.1"
+"@babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.24.5, @babel/traverse@npm:^7.26.10, @babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.27.3, @babel/traverse@npm:^7.27.4":
+ version: 7.27.4
+ resolution: "@babel/traverse@npm:7.27.4"
dependencies:
"@babel/code-frame": "npm:^7.27.1"
- "@babel/generator": "npm:^7.27.1"
- "@babel/parser": "npm:^7.27.1"
- "@babel/template": "npm:^7.27.1"
- "@babel/types": "npm:^7.27.1"
+ "@babel/generator": "npm:^7.27.3"
+ "@babel/parser": "npm:^7.27.4"
+ "@babel/template": "npm:^7.27.2"
+ "@babel/types": "npm:^7.27.3"
debug: "npm:^4.3.1"
globals: "npm:^11.1.0"
- checksum: 10c0/d912110037b03b1d70a2436cfd51316d930366a5f54252da2bced1ba38642f644f848240a951e5caf12f1ef6c40d3d96baa92ea6e84800f2e891c15e97b25d50
+ checksum: 10c0/6de8aa2a0637a6ee6d205bf48b9e923928a02415771fdec60085ed754dcdf605e450bb3315c2552fa51c31a4662275b45d5ae4ad527ce55a7db9acebdbbbb8ed
languageName: node
linkType: hard
-"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.24.5, @babel/types@npm:^7.26.10, @babel/types@npm:^7.27.0, @babel/types@npm:^7.27.1, @babel/types@npm:^7.4.4":
- version: 7.27.1
- resolution: "@babel/types@npm:7.27.1"
+"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.3, @babel/types@npm:^7.25.4, @babel/types@npm:^7.26.10, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.4.4":
+ version: 7.27.3
+ resolution: "@babel/types@npm:7.27.3"
dependencies:
"@babel/helper-string-parser": "npm:^7.27.1"
"@babel/helper-validator-identifier": "npm:^7.27.1"
- checksum: 10c0/ed736f14db2fdf0d36c539c8e06b6bb5e8f9649a12b5c0e1c516fed827f27ef35085abe08bf4d1302a4e20c9a254e762eed453bce659786d4a6e01ba26a91377
+ checksum: 10c0/bafdfc98e722a6b91a783b6f24388f478fd775f0c0652e92220e08be2cc33e02d42088542f1953ac5e5ece2ac052172b3dadedf12bec9aae57899e92fb9a9757
+ languageName: node
+ linkType: hard
+
+"@babel/types@npm:^7.24.5":
+ version: 7.27.6
+ resolution: "@babel/types@npm:7.27.6"
+ dependencies:
+ "@babel/helper-string-parser": "npm:^7.27.1"
+ "@babel/helper-validator-identifier": "npm:^7.27.1"
+ checksum: 10c0/39d556be114f2a6d874ea25ad39826a9e3a0e98de0233ae6d932f6d09a4b222923a90a7274c635ed61f1ba49bbd345329226678800900ad1c8d11afabd573aaf
+ languageName: node
+ linkType: hard
+
+"@bcoe/v8-coverage@npm:^1.0.2":
+ version: 1.0.2
+ resolution: "@bcoe/v8-coverage@npm:1.0.2"
+ checksum: 10c0/1eb1dc93cc17fb7abdcef21a6e7b867d6aa99a7ec88ec8207402b23d9083ab22a8011213f04b2cf26d535f1d22dc26139b7929e6c2134c254bd1e14ba5e678c3
languageName: node
linkType: hard
@@ -1413,15 +1461,15 @@ __metadata:
languageName: node
linkType: hard
-"@csstools/postcss-is-pseudo-class@npm:^5.0.1":
- version: 5.0.1
- resolution: "@csstools/postcss-is-pseudo-class@npm:5.0.1"
+"@csstools/postcss-is-pseudo-class@npm:^5.0.3":
+ version: 5.0.3
+ resolution: "@csstools/postcss-is-pseudo-class@npm:5.0.3"
dependencies:
"@csstools/selector-specificity": "npm:^5.0.0"
postcss-selector-parser: "npm:^7.0.0"
peerDependencies:
postcss: ^8.4
- checksum: 10c0/3aaab18ebb2dcf5565efa79813eaa987d40de1e086765358524392a09631c68ad1ee952e6aff8f42513b2c18ab84891787e065fe287f696128498fc641520b6c
+ checksum: 10c0/7980f1cabf32850bac72552e4e9de47412359e36e259a92b9b9af25dae4cce42bbcc5fdca8f384a589565bf383ecb23dec3af9f084d8df18b82552318b2841b6
languageName: node
linkType: hard
@@ -1664,12 +1712,12 @@ __metadata:
languageName: node
linkType: hard
-"@csstools/selector-resolve-nested@npm:^3.0.0":
- version: 3.0.0
- resolution: "@csstools/selector-resolve-nested@npm:3.0.0"
+"@csstools/selector-resolve-nested@npm:^3.1.0":
+ version: 3.1.0
+ resolution: "@csstools/selector-resolve-nested@npm:3.1.0"
peerDependencies:
postcss-selector-parser: ^7.0.0
- checksum: 10c0/2b01c36b3fa81388d5bddd8db962766465d76b021a815c8bb5a48c3a42c530154cc155fc496707ade627dbba6745eb8ecd9fa840c1972133c0f7d8811e0a959d
+ checksum: 10c0/c2b1a930ad03c1427ab90b28c4940424fb39e8175130148f16209be3a3937f7a146d5483ca1da1dfc100aa7ae86df713f0ee82d4bbaa9b986e7f47f35cb67cca
languageName: node
linkType: hard
@@ -1925,189 +1973,189 @@ __metadata:
languageName: node
linkType: hard
-"@esbuild/aix-ppc64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/aix-ppc64@npm:0.25.2"
+"@esbuild/aix-ppc64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/aix-ppc64@npm:0.25.5"
conditions: os=aix & cpu=ppc64
languageName: node
linkType: hard
-"@esbuild/android-arm64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/android-arm64@npm:0.25.2"
+"@esbuild/android-arm64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/android-arm64@npm:0.25.5"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/android-arm@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/android-arm@npm:0.25.2"
+"@esbuild/android-arm@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/android-arm@npm:0.25.5"
conditions: os=android & cpu=arm
languageName: node
linkType: hard
-"@esbuild/android-x64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/android-x64@npm:0.25.2"
+"@esbuild/android-x64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/android-x64@npm:0.25.5"
conditions: os=android & cpu=x64
languageName: node
linkType: hard
-"@esbuild/darwin-arm64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/darwin-arm64@npm:0.25.2"
+"@esbuild/darwin-arm64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/darwin-arm64@npm:0.25.5"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/darwin-x64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/darwin-x64@npm:0.25.2"
+"@esbuild/darwin-x64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/darwin-x64@npm:0.25.5"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
-"@esbuild/freebsd-arm64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/freebsd-arm64@npm:0.25.2"
+"@esbuild/freebsd-arm64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/freebsd-arm64@npm:0.25.5"
conditions: os=freebsd & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/freebsd-x64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/freebsd-x64@npm:0.25.2"
+"@esbuild/freebsd-x64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/freebsd-x64@npm:0.25.5"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
-"@esbuild/linux-arm64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/linux-arm64@npm:0.25.2"
+"@esbuild/linux-arm64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/linux-arm64@npm:0.25.5"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/linux-arm@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/linux-arm@npm:0.25.2"
+"@esbuild/linux-arm@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/linux-arm@npm:0.25.5"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
-"@esbuild/linux-ia32@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/linux-ia32@npm:0.25.2"
+"@esbuild/linux-ia32@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/linux-ia32@npm:0.25.5"
conditions: os=linux & cpu=ia32
languageName: node
linkType: hard
-"@esbuild/linux-loong64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/linux-loong64@npm:0.25.2"
+"@esbuild/linux-loong64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/linux-loong64@npm:0.25.5"
conditions: os=linux & cpu=loong64
languageName: node
linkType: hard
-"@esbuild/linux-mips64el@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/linux-mips64el@npm:0.25.2"
+"@esbuild/linux-mips64el@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/linux-mips64el@npm:0.25.5"
conditions: os=linux & cpu=mips64el
languageName: node
linkType: hard
-"@esbuild/linux-ppc64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/linux-ppc64@npm:0.25.2"
+"@esbuild/linux-ppc64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/linux-ppc64@npm:0.25.5"
conditions: os=linux & cpu=ppc64
languageName: node
linkType: hard
-"@esbuild/linux-riscv64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/linux-riscv64@npm:0.25.2"
+"@esbuild/linux-riscv64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/linux-riscv64@npm:0.25.5"
conditions: os=linux & cpu=riscv64
languageName: node
linkType: hard
-"@esbuild/linux-s390x@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/linux-s390x@npm:0.25.2"
+"@esbuild/linux-s390x@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/linux-s390x@npm:0.25.5"
conditions: os=linux & cpu=s390x
languageName: node
linkType: hard
-"@esbuild/linux-x64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/linux-x64@npm:0.25.2"
+"@esbuild/linux-x64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/linux-x64@npm:0.25.5"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
-"@esbuild/netbsd-arm64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/netbsd-arm64@npm:0.25.2"
+"@esbuild/netbsd-arm64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/netbsd-arm64@npm:0.25.5"
conditions: os=netbsd & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/netbsd-x64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/netbsd-x64@npm:0.25.2"
+"@esbuild/netbsd-x64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/netbsd-x64@npm:0.25.5"
conditions: os=netbsd & cpu=x64
languageName: node
linkType: hard
-"@esbuild/openbsd-arm64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/openbsd-arm64@npm:0.25.2"
+"@esbuild/openbsd-arm64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/openbsd-arm64@npm:0.25.5"
conditions: os=openbsd & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/openbsd-x64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/openbsd-x64@npm:0.25.2"
+"@esbuild/openbsd-x64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/openbsd-x64@npm:0.25.5"
conditions: os=openbsd & cpu=x64
languageName: node
linkType: hard
-"@esbuild/sunos-x64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/sunos-x64@npm:0.25.2"
+"@esbuild/sunos-x64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/sunos-x64@npm:0.25.5"
conditions: os=sunos & cpu=x64
languageName: node
linkType: hard
-"@esbuild/win32-arm64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/win32-arm64@npm:0.25.2"
+"@esbuild/win32-arm64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/win32-arm64@npm:0.25.5"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
-"@esbuild/win32-ia32@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/win32-ia32@npm:0.25.2"
+"@esbuild/win32-ia32@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/win32-ia32@npm:0.25.5"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
-"@esbuild/win32-x64@npm:0.25.2":
- version: 0.25.2
- resolution: "@esbuild/win32-x64@npm:0.25.2"
+"@esbuild/win32-x64@npm:0.25.5":
+ version: 0.25.5
+ resolution: "@esbuild/win32-x64@npm:0.25.5"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
-"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0":
- version: 4.4.0
- resolution: "@eslint-community/eslint-utils@npm:4.4.0"
+"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0, @eslint-community/eslint-utils@npm:^4.7.0":
+ version: 4.7.0
+ resolution: "@eslint-community/eslint-utils@npm:4.7.0"
dependencies:
- eslint-visitor-keys: "npm:^3.3.0"
+ eslint-visitor-keys: "npm:^3.4.3"
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
- checksum: 10c0/7e559c4ce59cd3a06b1b5a517b593912e680a7f981ae7affab0d01d709e99cd5647019be8fafa38c350305bc32f1f7d42c7073edde2ab536c745e365f37b607e
+ checksum: 10c0/c0f4f2bd73b7b7a9de74b716a664873d08ab71ab439e51befe77d61915af41a81ecec93b408778b3a7856185244c34c2c8ee28912072ec14def84ba2dec70adf
languageName: node
linkType: hard
@@ -2423,7 +2471,31 @@ __metadata:
languageName: node
linkType: hard
-"@jridgewell/gen-mapping@npm:^0.3.0, @jridgewell/gen-mapping@npm:^0.3.5":
+"@istanbuljs/schema@npm:^0.1.2":
+ version: 0.1.3
+ resolution: "@istanbuljs/schema@npm:0.1.3"
+ checksum: 10c0/61c5286771676c9ca3eb2bd8a7310a9c063fb6e0e9712225c8471c582d157392c88f5353581c8c9adbe0dff98892317d2fdfc56c3499aa42e0194405206a963a
+ languageName: node
+ linkType: hard
+
+"@joshwooding/vite-plugin-react-docgen-typescript@npm:0.6.0":
+ version: 0.6.0
+ resolution: "@joshwooding/vite-plugin-react-docgen-typescript@npm:0.6.0"
+ dependencies:
+ glob: "npm:^10.0.0"
+ magic-string: "npm:^0.30.0"
+ react-docgen-typescript: "npm:^2.2.2"
+ peerDependencies:
+ typescript: ">= 4.3.x"
+ vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ checksum: 10c0/cbb76545214929e628de661985f69f9b79f324ad8db0aa19b2937c52730be57eb37848a7b7d5986ccc00f09d8bc0623ec16f83c9c13aaca3ef5afd0bc322da2e
+ languageName: node
+ linkType: hard
+
+"@jridgewell/gen-mapping@npm:^0.3.5":
version: 0.3.5
resolution: "@jridgewell/gen-mapping@npm:0.3.5"
dependencies:
@@ -2465,7 +2537,7 @@ __metadata:
languageName: node
linkType: hard
-"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.9":
+"@jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25":
version: 0.3.25
resolution: "@jridgewell/trace-mapping@npm:0.3.25"
dependencies:
@@ -2502,6 +2574,10 @@ __metadata:
"@rails/ujs": "npm:7.1.501"
"@react-spring/web": "npm:^9.7.5"
"@reduxjs/toolkit": "npm:^2.0.1"
+ "@storybook/addon-a11y": "npm:^9.0.4"
+ "@storybook/addon-docs": "npm:^9.0.4"
+ "@storybook/addon-vitest": "npm:^9.0.4"
+ "@storybook/react-vite": "npm:^9.0.4"
"@testing-library/dom": "npm:^10.2.0"
"@testing-library/react": "npm:^16.0.0"
"@types/emoji-mart": "npm:3.0.14"
@@ -2530,12 +2606,16 @@ __metadata:
"@use-gesture/react": "npm:^10.3.1"
"@vitejs/plugin-legacy": "npm:^6.1.1"
"@vitejs/plugin-react": "npm:^4.2.1"
+ "@vitest/browser": "npm:^3.2.1"
+ "@vitest/coverage-v8": "npm:^3.2.0"
+ "@vitest/ui": "npm:^3.2.1"
arrow-key-navigation: "npm:^1.2.0"
async-mutex: "npm:^0.5.0"
axios: "npm:^1.4.0"
babel-plugin-formatjs: "npm:^10.5.37"
babel-plugin-transform-react-remove-prop-types: "npm:^0.4.24"
blurhash: "npm:^2.0.5"
+ chromatic: "npm:^12.1.0"
classnames: "npm:^2.3.2"
cocoon-js-vanilla: "npm:^1.5.1"
color-blend: "npm:^4.0.0"
@@ -2553,6 +2633,7 @@ __metadata:
eslint-plugin-promise: "npm:~7.2.1"
eslint-plugin-react: "npm:^7.37.4"
eslint-plugin-react-hooks: "npm:^5.2.0"
+ eslint-plugin-storybook: "npm:^9.0.4"
fuzzysort: "npm:^3.0.0"
globals: "npm:^16.0.0"
history: "npm:^4.10.1"
@@ -2567,6 +2648,7 @@ __metadata:
lodash: "npm:^4.17.21"
marky: "npm:^1.2.5"
path-complete-extname: "npm:^1.0.0"
+ playwright: "npm:^1.52.0"
postcss-preset-env: "npm:^10.1.5"
prettier: "npm:^3.3.3"
prop-types: "npm:^15.8.1"
@@ -2596,6 +2678,7 @@ __metadata:
rollup-plugin-visualizer: "npm:^6.0.0"
sass: "npm:^1.62.1"
stacktrace-js: "npm:^2.0.2"
+ storybook: "npm:^9.0.4"
stringz: "npm:^2.1.0"
stylelint: "npm:^16.19.1"
stylelint-config-prettier-scss: "npm:^1.0.0"
@@ -2614,7 +2697,7 @@ __metadata:
vite-plugin-ruby: "npm:^5.1.1"
vite-plugin-svgr: "npm:^4.2.0"
vite-tsconfig-paths: "npm:^5.1.4"
- vitest: "npm:^3.1.3"
+ vitest: "npm:^3.2.1"
wicg-inert: "npm:^3.1.2"
workbox-expiration: "npm:^7.0.0"
workbox-routing: "npm:^7.0.0"
@@ -2666,6 +2749,18 @@ __metadata:
languageName: unknown
linkType: soft
+"@mdx-js/react@npm:^3.0.0":
+ version: 3.1.0
+ resolution: "@mdx-js/react@npm:3.1.0"
+ dependencies:
+ "@types/mdx": "npm:^2.0.0"
+ peerDependencies:
+ "@types/react": ">=16"
+ react: ">=16"
+ checksum: 10c0/381ed1211ba2b8491bf0ad9ef0d8d1badcdd114e1931d55d44019d4b827cc2752586708f9c7d2f9c3244150ed81f1f671a6ca95fae0edd5797fb47a22e06ceca
+ languageName: node
+ linkType: hard
+
"@napi-rs/wasm-runtime@npm:^0.2.7":
version: 0.2.7
resolution: "@napi-rs/wasm-runtime@npm:0.2.7"
@@ -2913,6 +3008,13 @@ __metadata:
languageName: node
linkType: hard
+"@polka/url@npm:^1.0.0-next.24":
+ version: 1.0.0-next.29
+ resolution: "@polka/url@npm:1.0.0-next.29"
+ checksum: 10c0/0d58e081844095cb029d3c19a659bfefd09d5d51a2f791bc61eba7ea826f13d6ee204a8a448c2f5a855c17df07b37517373ff916dd05801063c0568ae9937684
+ languageName: node
+ linkType: hard
+
"@popperjs/core@npm:^2.11.6":
version: 2.11.8
resolution: "@popperjs/core@npm:2.11.8"
@@ -3026,10 +3128,10 @@ __metadata:
languageName: node
linkType: hard
-"@rolldown/pluginutils@npm:1.0.0-beta.9":
- version: 1.0.0-beta.9
- resolution: "@rolldown/pluginutils@npm:1.0.0-beta.9"
- checksum: 10c0/21aebb7ebd093282efd96f63ddd465f76746b1d70282366d6ccc7fff6eb4da5c2f8f4bfaaaeb4283c2432600e5609e39e9897864575e593efc11d376ca1a6fa1
+"@rolldown/pluginutils@npm:1.0.0-beta.11":
+ version: 1.0.0-beta.11
+ resolution: "@rolldown/pluginutils@npm:1.0.0-beta.11"
+ checksum: 10c0/140088e33a4dd3bc21d06fa0cbe79b52e95487c9737d425aa5729e52446dc70f066fbce632489a53e45bb567f1e86c19835677c98fe5d4123ae1e2fef53f8d97
languageName: node
linkType: hard
@@ -3109,7 +3211,7 @@ __metadata:
languageName: node
linkType: hard
-"@rollup/pluginutils@npm:^5.0.1, @rollup/pluginutils@npm:^5.0.5, @rollup/pluginutils@npm:^5.1.0":
+"@rollup/pluginutils@npm:^5.0.1, @rollup/pluginutils@npm:^5.0.2, @rollup/pluginutils@npm:^5.0.5, @rollup/pluginutils@npm:^5.1.0":
version: 5.1.4
resolution: "@rollup/pluginutils@npm:5.1.4"
dependencies:
@@ -3286,6 +3388,151 @@ __metadata:
languageName: node
linkType: hard
+"@storybook/addon-a11y@npm:^9.0.4":
+ version: 9.0.4
+ resolution: "@storybook/addon-a11y@npm:9.0.4"
+ dependencies:
+ "@storybook/global": "npm:^5.0.0"
+ axe-core: "npm:^4.2.0"
+ peerDependencies:
+ storybook: ^9.0.4
+ checksum: 10c0/558a71244ce6eb18eb08e95c47d94fa81d29f70226f37b92d1cc52a86fe6389ce978f0ad3a2e90d90dfedb6476d5c7bc7218b66201d3f94627b789783d53e808
+ languageName: node
+ linkType: hard
+
+"@storybook/addon-docs@npm:^9.0.4":
+ version: 9.0.4
+ resolution: "@storybook/addon-docs@npm:9.0.4"
+ dependencies:
+ "@mdx-js/react": "npm:^3.0.0"
+ "@storybook/csf-plugin": "npm:9.0.4"
+ "@storybook/icons": "npm:^1.2.12"
+ "@storybook/react-dom-shim": "npm:9.0.4"
+ react: "npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ react-dom: "npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ ts-dedent: "npm:^2.0.0"
+ peerDependencies:
+ storybook: ^9.0.4
+ checksum: 10c0/c70937abe73ec77e80017f14f459224fcefae457999be7be25cd198d49bdff31d181a99d3ec7b72fa494063e4f229c7c4e324173b416a2710208c8a12882e2bd
+ languageName: node
+ linkType: hard
+
+"@storybook/addon-vitest@npm:^9.0.4":
+ version: 9.0.4
+ resolution: "@storybook/addon-vitest@npm:9.0.4"
+ dependencies:
+ "@storybook/global": "npm:^5.0.0"
+ "@storybook/icons": "npm:^1.4.0"
+ prompts: "npm:^2.4.0"
+ ts-dedent: "npm:^2.2.0"
+ peerDependencies:
+ "@vitest/browser": ^3.0.0
+ "@vitest/runner": ^3.0.0
+ storybook: ^9.0.4
+ vitest: ^3.0.0
+ peerDependenciesMeta:
+ "@vitest/browser":
+ optional: true
+ "@vitest/runner":
+ optional: true
+ vitest:
+ optional: true
+ checksum: 10c0/7379826120c7d2aa4161abd978038161d0ffe2670d40315144ef8b8b1efa9afc793457fe9439928fc039565de2d4da4a109936bd7275518ee7f2fccfce66df21
+ languageName: node
+ linkType: hard
+
+"@storybook/builder-vite@npm:9.0.4":
+ version: 9.0.4
+ resolution: "@storybook/builder-vite@npm:9.0.4"
+ dependencies:
+ "@storybook/csf-plugin": "npm:9.0.4"
+ ts-dedent: "npm:^2.0.0"
+ peerDependencies:
+ storybook: ^9.0.4
+ vite: ^5.0.0 || ^6.0.0
+ checksum: 10c0/137c1b114d96f1e12f0d76b38d7d4ec10842b8d8284ead906e56d91d61dfbd9a82b84e7643be7cb226040d5829a72d580b441cb26cfa8d6634a7ebe4eff14071
+ languageName: node
+ linkType: hard
+
+"@storybook/csf-plugin@npm:9.0.4":
+ version: 9.0.4
+ resolution: "@storybook/csf-plugin@npm:9.0.4"
+ dependencies:
+ unplugin: "npm:^1.3.1"
+ peerDependencies:
+ storybook: ^9.0.4
+ checksum: 10c0/3988920c425b5ea8c2fd6d7148ae8d009ec4556fab2e14b6223ea6ae7422a0242a16c2e333f28218f85b7c0781c3540ecefbeaa94a492aaf9132e98a5785b254
+ languageName: node
+ linkType: hard
+
+"@storybook/global@npm:^5.0.0":
+ version: 5.0.0
+ resolution: "@storybook/global@npm:5.0.0"
+ checksum: 10c0/8f1b61dcdd3a89584540896e659af2ecc700bc740c16909a7be24ac19127ea213324de144a141f7caf8affaed017d064fea0618d453afbe027cf60f54b4a6d0b
+ languageName: node
+ linkType: hard
+
+"@storybook/icons@npm:^1.2.12, @storybook/icons@npm:^1.4.0":
+ version: 1.4.0
+ resolution: "@storybook/icons@npm:1.4.0"
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+ checksum: 10c0/fd0514fb3fa431a8b5939fe1d9fc336b253ef2c25b34792d2d4ee59e13321108d34f8bf223a0981482f54f83c5ef47ffd1a98c376ca9071011c1b8afe2b01d43
+ languageName: node
+ linkType: hard
+
+"@storybook/react-dom-shim@npm:9.0.4":
+ version: 9.0.4
+ resolution: "@storybook/react-dom-shim@npm:9.0.4"
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+ storybook: ^9.0.4
+ checksum: 10c0/abee05aa500c90b15a850163965f6631c1cd95f688eb12f5c629de0b2ce47f340b540e1f76b15bb84219a8961c0389316f39f5cc10cf807f76ae7e8d0c33a1c7
+ languageName: node
+ linkType: hard
+
+"@storybook/react-vite@npm:^9.0.4":
+ version: 9.0.4
+ resolution: "@storybook/react-vite@npm:9.0.4"
+ dependencies:
+ "@joshwooding/vite-plugin-react-docgen-typescript": "npm:0.6.0"
+ "@rollup/pluginutils": "npm:^5.0.2"
+ "@storybook/builder-vite": "npm:9.0.4"
+ "@storybook/react": "npm:9.0.4"
+ find-up: "npm:^5.0.0"
+ magic-string: "npm:^0.30.0"
+ react-docgen: "npm:^8.0.0"
+ resolve: "npm:^1.22.8"
+ tsconfig-paths: "npm:^4.2.0"
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+ storybook: ^9.0.4
+ vite: ^5.0.0 || ^6.0.0
+ checksum: 10c0/79031f1d6f07c5830fbc3d21e6483f090b75b20cbfe9ee4d26efa3c6346c70c1c462751ce1d110b100ace383a0adc0e159535d5de0d399980c07be95c21d170a
+ languageName: node
+ linkType: hard
+
+"@storybook/react@npm:9.0.4":
+ version: 9.0.4
+ resolution: "@storybook/react@npm:9.0.4"
+ dependencies:
+ "@storybook/global": "npm:^5.0.0"
+ "@storybook/react-dom-shim": "npm:9.0.4"
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
+ storybook: ^9.0.4
+ typescript: ">= 4.9.x"
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ checksum: 10c0/d1b7f3c2ccd9d8eeb7fd029a74a0011fb4ddaf7b46ea8d6bbd51a4dedecf9959aed5db44725169e134fb931a3c2d7edfc29d3c6e63a0d1f1535317609bed1a97
+ languageName: node
+ linkType: hard
+
"@surma/rollup-plugin-off-main-thread@npm:^2.2.3":
version: 2.2.3
resolution: "@surma/rollup-plugin-off-main-thread@npm:2.2.3"
@@ -3425,7 +3672,7 @@ __metadata:
languageName: node
linkType: hard
-"@testing-library/dom@npm:^10.2.0":
+"@testing-library/dom@npm:^10.2.0, @testing-library/dom@npm:^10.4.0":
version: 10.4.0
resolution: "@testing-library/dom@npm:10.4.0"
dependencies:
@@ -3441,6 +3688,21 @@ __metadata:
languageName: node
linkType: hard
+"@testing-library/jest-dom@npm:^6.6.3":
+ version: 6.6.3
+ resolution: "@testing-library/jest-dom@npm:6.6.3"
+ dependencies:
+ "@adobe/css-tools": "npm:^4.4.0"
+ aria-query: "npm:^5.0.0"
+ chalk: "npm:^3.0.0"
+ css.escape: "npm:^1.5.1"
+ dom-accessibility-api: "npm:^0.6.3"
+ lodash: "npm:^4.17.21"
+ redent: "npm:^3.0.0"
+ checksum: 10c0/5566b6c0b7b0709bc244aec3aa3dc9e5f4663e8fb2b99d8cd456fc07279e59db6076cbf798f9d3099a98fca7ef4cd50e4e1f4c4dec5a60a8fad8d24a638a5bf6
+ languageName: node
+ linkType: hard
+
"@testing-library/react@npm:^16.0.0":
version: 16.1.0
resolution: "@testing-library/react@npm:16.1.0"
@@ -3461,6 +3723,15 @@ __metadata:
languageName: node
linkType: hard
+"@testing-library/user-event@npm:^14.6.1":
+ version: 14.6.1
+ resolution: "@testing-library/user-event@npm:14.6.1"
+ peerDependencies:
+ "@testing-library/dom": ">=7.21.4"
+ checksum: 10c0/75fea130a52bf320d35d46ed54f3eec77e71a56911b8b69a3fe29497b0b9947b2dc80d30f04054ad4ce7f577856ae3e5397ea7dff0ef14944d3909784c7a93fe
+ languageName: node
+ linkType: hard
+
"@tybys/wasm-util@npm:^0.9.0":
version: 0.9.0
resolution: "@tybys/wasm-util@npm:0.9.0"
@@ -3477,7 +3748,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/babel__core@npm:*, @types/babel__core@npm:^7.20.5":
+"@types/babel__core@npm:*, @types/babel__core@npm:^7.18.0, @types/babel__core@npm:^7.20.5":
version: 7.20.5
resolution: "@types/babel__core@npm:7.20.5"
dependencies:
@@ -3518,7 +3789,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.20.6":
+"@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.18.0, @types/babel__traverse@npm:^7.20.6":
version: 7.20.7
resolution: "@types/babel__traverse@npm:7.20.7"
dependencies:
@@ -3537,6 +3808,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/chai@npm:^5.2.2":
+ version: 5.2.2
+ resolution: "@types/chai@npm:5.2.2"
+ dependencies:
+ "@types/deep-eql": "npm:*"
+ checksum: 10c0/49282bf0e8246800ebb36f17256f97bd3a8c4fb31f92ad3c0eaa7623518d7e87f1eaad4ad206960fcaf7175854bdff4cb167e4fe96811e0081b4ada83dd533ec
+ languageName: node
+ linkType: hard
+
"@types/connect@npm:*":
version: 3.4.38
resolution: "@types/connect@npm:3.4.38"
@@ -3547,11 +3827,25 @@ __metadata:
linkType: hard
"@types/cors@npm:^2.8.16":
- version: 2.8.17
- resolution: "@types/cors@npm:2.8.17"
+ version: 2.8.18
+ resolution: "@types/cors@npm:2.8.18"
dependencies:
"@types/node": "npm:*"
- checksum: 10c0/457364c28c89f3d9ed34800e1de5c6eaaf344d1bb39af122f013322a50bc606eb2aa6f63de4e41a7a08ba7ef454473926c94a830636723da45bf786df032696d
+ checksum: 10c0/9dd1075de0e3a40c304826668960c797e67e597a734fb8e8ab404561f31ef2bd553ef5500eb86da7e91a344bee038a59931d2fbf182fbce09f13816f51fdd80e
+ languageName: node
+ linkType: hard
+
+"@types/deep-eql@npm:*":
+ version: 4.0.2
+ resolution: "@types/deep-eql@npm:4.0.2"
+ checksum: 10c0/bf3f811843117900d7084b9d0c852da9a044d12eb40e6de73b552598a6843c21291a8a381b0532644574beecd5e3491c5ff3a0365ab86b15d59862c025384844
+ languageName: node
+ linkType: hard
+
+"@types/doctrine@npm:^0.0.9":
+ version: 0.0.9
+ resolution: "@types/doctrine@npm:0.0.9"
+ checksum: 10c0/cdaca493f13c321cf0cacd1973efc0ae74569633145d9e6fc1128f32217a6968c33bea1f858275239fe90c98f3be57ec8f452b416a9ff48b8e8c1098b20fa51c
languageName: node
linkType: hard
@@ -3618,14 +3912,14 @@ __metadata:
linkType: hard
"@types/express@npm:^4.17.17":
- version: 4.17.21
- resolution: "@types/express@npm:4.17.21"
+ version: 4.17.22
+ resolution: "@types/express@npm:4.17.22"
dependencies:
"@types/body-parser": "npm:*"
"@types/express-serve-static-core": "npm:^4.17.33"
"@types/qs": "npm:*"
"@types/serve-static": "npm:*"
- checksum: 10c0/12e562c4571da50c7d239e117e688dc434db1bac8be55613294762f84fd77fbd0658ccd553c7d3ab02408f385bc93980992369dd30e2ecd2c68c358e6af8fabf
+ checksum: 10c0/15c10a5ebb40a0356baa95ed374a2150d862786c9fccbdd724df12acc9c8cb08fbe1d34b446b1bcef2dbe5305cb3013fb39fba791baa54ef6df8056482776abb
languageName: node
linkType: hard
@@ -3698,9 +3992,16 @@ __metadata:
linkType: hard
"@types/lodash@npm:^4.14.195":
- version: 4.17.16
- resolution: "@types/lodash@npm:4.17.16"
- checksum: 10c0/cf017901b8ab1d7aabc86d5189d9288f4f99f19a75caf020c0e2c77b8d4cead4db0d0b842d009b029339f92399f49f34377dd7c2721053388f251778b4c23534
+ version: 4.17.17
+ resolution: "@types/lodash@npm:4.17.17"
+ checksum: 10c0/8e75df02a15f04d4322c5a503e4efd0e7a92470570ce80f17e9f11ce2b1f1a7c994009c9bcff39f07e0f9ffd8ccaff09b3598997c404b801abd5a7eee5a639dc
+ languageName: node
+ linkType: hard
+
+"@types/mdx@npm:^2.0.0":
+ version: 2.0.13
+ resolution: "@types/mdx@npm:2.0.13"
+ checksum: 10c0/5edf1099505ac568da55f9ae8a93e7e314e8cbc13d3445d0be61b75941226b005e1390d9b95caecf5dcb00c9d1bab2f1f60f6ff9876dc091a48b547495007720
languageName: node
linkType: hard
@@ -3742,13 +4043,13 @@ __metadata:
linkType: hard
"@types/pg@npm:^8.6.6":
- version: 8.11.11
- resolution: "@types/pg@npm:8.11.11"
+ version: 8.15.4
+ resolution: "@types/pg@npm:8.15.4"
dependencies:
"@types/node": "npm:*"
pg-protocol: "npm:*"
- pg-types: "npm:^4.0.1"
- checksum: 10c0/18c2585e1ba7a5dd5f849d49410d53fdfe9a6c3cbc4ae46c51fd728264d6ecf9a84a5cd82d89cb1f870a74383bad88effce1eed888f16accbcbde56a53d23a69
+ pg-types: "npm:^2.2.0"
+ checksum: 10c0/7f9295cb2d934681bba84f7caad529c3b100d87e83ad0732c7fe496f4f79e42a795097321db54e010fcff22cb5e410cf683b4c9941907ee4564c822242816e91
languageName: node
linkType: hard
@@ -3795,11 +4096,11 @@ __metadata:
linkType: hard
"@types/react-dom@npm:^18.2.4":
- version: 18.3.5
- resolution: "@types/react-dom@npm:18.3.5"
+ version: 18.3.7
+ resolution: "@types/react-dom@npm:18.3.7"
peerDependencies:
"@types/react": ^18.0.0
- checksum: 10c0/b163d35a6b32a79f5782574a7aeb12a31a647e248792bf437e6d596e2676961c394c5e3c6e91d1ce44ae90441dbaf93158efb4f051c0d61e2612f1cb04ce4faa
+ checksum: 10c0/8bd309e2c3d1604a28a736a24f96cbadf6c05d5288cfef8883b74f4054c961b6b3a5e997fd5686e492be903c8f3380dba5ec017eff3906b1256529cd2d39603e
languageName: node
linkType: hard
@@ -3889,12 +4190,12 @@ __metadata:
linkType: hard
"@types/react@npm:^18.2.7":
- version: 18.3.19
- resolution: "@types/react@npm:18.3.19"
+ version: 18.3.23
+ resolution: "@types/react@npm:18.3.23"
dependencies:
"@types/prop-types": "npm:*"
csstype: "npm:^3.0.2"
- checksum: 10c0/236bfe0c4748ada1a640f13573eca3e0fc7c9d847b442947adb352b0718d6d285357fd84c33336c8ffb8cbfabc0d58a43a647c7fd79857fecd61fb58ab6f7918
+ checksum: 10c0/49331800b76572eb2992a5c44801dbf8c612a5f99c8f4e4200f06c7de6f3a6e9455c661784a6c5469df96fa45622cb4a9d0982c44e6a0d5719be5f2ef1f545ed
languageName: node
linkType: hard
@@ -3922,6 +4223,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/resolve@npm:^1.20.2":
+ version: 1.20.6
+ resolution: "@types/resolve@npm:1.20.6"
+ checksum: 10c0/a9b0549d816ff2c353077365d865a33655a141d066d0f5a3ba6fd4b28bc2f4188a510079f7c1f715b3e7af505a27374adce2a5140a3ece2a059aab3d6e1a4244
+ languageName: node
+ linkType: hard
+
"@types/send@npm:*":
version: 0.17.4
resolution: "@types/send@npm:0.17.4"
@@ -3972,11 +4280,11 @@ __metadata:
linkType: hard
"@types/ws@npm:^8.5.9":
- version: 8.18.0
- resolution: "@types/ws@npm:8.18.0"
+ version: 8.18.1
+ resolution: "@types/ws@npm:8.18.1"
dependencies:
"@types/node": "npm:*"
- checksum: 10c0/a56d2e0d1da7411a1f3548ce02b51a50cbe9e23f025677d03df48f87e4a3c72e1342fbf1d12e487d7eafa8dc670c605152b61bbf9165891ec0e9694b0d3ea8d4
+ checksum: 10c0/61aff1129143fcc4312f083bc9e9e168aa3026b7dd6e70796276dcfb2c8211c4292603f9c4864fae702f2ed86e4abd4d38aa421831c2fd7f856c931a481afbab
languageName: node
linkType: hard
@@ -4017,6 +4325,17 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/project-service@npm:8.33.0":
+ version: 8.33.0
+ resolution: "@typescript-eslint/project-service@npm:8.33.0"
+ dependencies:
+ "@typescript-eslint/tsconfig-utils": "npm:^8.33.0"
+ "@typescript-eslint/types": "npm:^8.33.0"
+ debug: "npm:^4.3.4"
+ checksum: 10c0/a863d9e3be5ffb53c9d57b25b7a35149dae01afd942dd7fc36bd72a4230676ae12d0f37a789cddaf1baf71e3b35f09436bebbd081336e667b4181b48d0afe8f5
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/scope-manager@npm:8.29.1":
version: 8.29.1
resolution: "@typescript-eslint/scope-manager@npm:8.29.1"
@@ -4027,6 +4346,25 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/scope-manager@npm:8.33.0":
+ version: 8.33.0
+ resolution: "@typescript-eslint/scope-manager@npm:8.33.0"
+ dependencies:
+ "@typescript-eslint/types": "npm:8.33.0"
+ "@typescript-eslint/visitor-keys": "npm:8.33.0"
+ checksum: 10c0/eb259add242ce40642e7272b414c92ae9407d97cb304981f17f0de0846d5c4ab47d41816ef13da3d3976fe0b7a74df291525be27e4fe4f0ab5d35e86d340faa0
+ languageName: node
+ linkType: hard
+
+"@typescript-eslint/tsconfig-utils@npm:8.33.0, @typescript-eslint/tsconfig-utils@npm:^8.33.0":
+ version: 8.33.0
+ resolution: "@typescript-eslint/tsconfig-utils@npm:8.33.0"
+ peerDependencies:
+ typescript: ">=4.8.4 <5.9.0"
+ checksum: 10c0/6e9a8e73e65b925f908f31e00be4f1b8d7e89f45d97fa703f468115943c297fc2cc6f9daa0c12b9607f39186f033ac244515f11710df7e1df8302c815ed57389
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/type-utils@npm:8.29.1":
version: 8.29.1
resolution: "@typescript-eslint/type-utils@npm:8.29.1"
@@ -4049,6 +4387,13 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/types@npm:8.33.0, @typescript-eslint/types@npm:^8.33.0":
+ version: 8.33.0
+ resolution: "@typescript-eslint/types@npm:8.33.0"
+ checksum: 10c0/348b64eb408719d7711a433fc9716e0c2aab8b3f3676f5a1cc2e00269044132282cf655deb6d0dd9817544116909513de3b709005352d186949d1014fad1a3cb
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/typescript-estree@npm:8.29.1":
version: 8.29.1
resolution: "@typescript-eslint/typescript-estree@npm:8.29.1"
@@ -4067,7 +4412,27 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/utils@npm:8.29.1, @typescript-eslint/utils@npm:^8.27.0":
+"@typescript-eslint/typescript-estree@npm:8.33.0":
+ version: 8.33.0
+ resolution: "@typescript-eslint/typescript-estree@npm:8.33.0"
+ dependencies:
+ "@typescript-eslint/project-service": "npm:8.33.0"
+ "@typescript-eslint/tsconfig-utils": "npm:8.33.0"
+ "@typescript-eslint/types": "npm:8.33.0"
+ "@typescript-eslint/visitor-keys": "npm:8.33.0"
+ debug: "npm:^4.3.4"
+ fast-glob: "npm:^3.3.2"
+ is-glob: "npm:^4.0.3"
+ minimatch: "npm:^9.0.4"
+ semver: "npm:^7.6.0"
+ ts-api-utils: "npm:^2.1.0"
+ peerDependencies:
+ typescript: ">=4.8.4 <5.9.0"
+ checksum: 10c0/677b12b2e5780ffaef508bddbf8712fe2c3413f3d14fd8fd0cfbe22952a81c6642b3cc26984cf27fdfc3dd2457ae5f8aa04437d3b0ae32987a1895f9648ca7b2
+ languageName: node
+ linkType: hard
+
+"@typescript-eslint/utils@npm:8.29.1":
version: 8.29.1
resolution: "@typescript-eslint/utils@npm:8.29.1"
dependencies:
@@ -4082,6 +4447,21 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/utils@npm:^8.27.0, @typescript-eslint/utils@npm:^8.8.1":
+ version: 8.33.0
+ resolution: "@typescript-eslint/utils@npm:8.33.0"
+ dependencies:
+ "@eslint-community/eslint-utils": "npm:^4.7.0"
+ "@typescript-eslint/scope-manager": "npm:8.33.0"
+ "@typescript-eslint/types": "npm:8.33.0"
+ "@typescript-eslint/typescript-estree": "npm:8.33.0"
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: ">=4.8.4 <5.9.0"
+ checksum: 10c0/a0adb9e13d8f8d8f86ae2e905f3305ad60732e760364b291de66a857a551485d37c23e923299078a47f75d3cca643e1f2aefa010a0beb4cb0d08d0507c1038e1
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/visitor-keys@npm:8.29.1":
version: 8.29.1
resolution: "@typescript-eslint/visitor-keys@npm:8.29.1"
@@ -4092,6 +4472,16 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/visitor-keys@npm:8.33.0":
+ version: 8.33.0
+ resolution: "@typescript-eslint/visitor-keys@npm:8.33.0"
+ dependencies:
+ "@typescript-eslint/types": "npm:8.33.0"
+ eslint-visitor-keys: "npm:^4.2.0"
+ checksum: 10c0/41660f241e78314f69d251792f369ef1eeeab3b40fe4ab11b794d402c95bcb82b61d3e91763e7ab9b0f22011a7ac9c8f9dfd91734d61c9f4eaf4f7660555b53b
+ languageName: node
+ linkType: hard
+
"@unrs/resolver-binding-darwin-arm64@npm:1.3.2":
version: 1.3.2
resolution: "@unrs/resolver-binding-darwin-arm64@npm:1.3.2"
@@ -4237,99 +4627,232 @@ __metadata:
linkType: hard
"@vitejs/plugin-react@npm:^4.2.1":
- version: 4.5.0
- resolution: "@vitejs/plugin-react@npm:4.5.0"
+ version: 4.5.2
+ resolution: "@vitejs/plugin-react@npm:4.5.2"
dependencies:
- "@babel/core": "npm:^7.26.10"
- "@babel/plugin-transform-react-jsx-self": "npm:^7.25.9"
- "@babel/plugin-transform-react-jsx-source": "npm:^7.25.9"
- "@rolldown/pluginutils": "npm:1.0.0-beta.9"
+ "@babel/core": "npm:^7.27.4"
+ "@babel/plugin-transform-react-jsx-self": "npm:^7.27.1"
+ "@babel/plugin-transform-react-jsx-source": "npm:^7.27.1"
+ "@rolldown/pluginutils": "npm:1.0.0-beta.11"
"@types/babel__core": "npm:^7.20.5"
react-refresh: "npm:^0.17.0"
peerDependencies:
- vite: ^4.2.0 || ^5.0.0 || ^6.0.0
- checksum: 10c0/c9f75cde098b9aac62cb512103d7f898a0a173cb78dc9fcf79ca4b3f21a1458cd1955a4383c8c9e3841ce23c5e7f02ed1455e445c9574879b143d40734121fd8
+ vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0
+ checksum: 10c0/37c58e6a9c953ab27eb6de42f0d317d26901117d4e4bec067b098c48353065888d240b819efc5b47e325f83532305d3cc51996fd3eb53f8649b199ecc4424746
languageName: node
linkType: hard
-"@vitest/expect@npm:3.1.3":
- version: 3.1.3
- resolution: "@vitest/expect@npm:3.1.3"
+"@vitest/browser@npm:^3.2.1":
+ version: 3.2.1
+ resolution: "@vitest/browser@npm:3.2.1"
dependencies:
- "@vitest/spy": "npm:3.1.3"
- "@vitest/utils": "npm:3.1.3"
+ "@testing-library/dom": "npm:^10.4.0"
+ "@testing-library/user-event": "npm:^14.6.1"
+ "@vitest/mocker": "npm:3.2.1"
+ "@vitest/utils": "npm:3.2.1"
+ magic-string: "npm:^0.30.17"
+ sirv: "npm:^3.0.1"
+ tinyrainbow: "npm:^2.0.0"
+ ws: "npm:^8.18.2"
+ peerDependencies:
+ playwright: "*"
+ vitest: 3.2.1
+ webdriverio: ^7.0.0 || ^8.0.0 || ^9.0.0
+ peerDependenciesMeta:
+ playwright:
+ optional: true
+ safaridriver:
+ optional: true
+ webdriverio:
+ optional: true
+ checksum: 10c0/304ae5107113230cc80b0b4eedcb3167ae776036797c77d97315ce4022e6c94553c557a0db6aff6de596fe77a0808b1e79b2576bd8e01386c010ea1c4a97a52b
+ languageName: node
+ linkType: hard
+
+"@vitest/coverage-v8@npm:^3.2.0":
+ version: 3.2.2
+ resolution: "@vitest/coverage-v8@npm:3.2.2"
+ dependencies:
+ "@ampproject/remapping": "npm:^2.3.0"
+ "@bcoe/v8-coverage": "npm:^1.0.2"
+ ast-v8-to-istanbul: "npm:^0.3.3"
+ debug: "npm:^4.4.1"
+ istanbul-lib-coverage: "npm:^3.2.2"
+ istanbul-lib-report: "npm:^3.0.1"
+ istanbul-lib-source-maps: "npm:^5.0.6"
+ istanbul-reports: "npm:^3.1.7"
+ magic-string: "npm:^0.30.17"
+ magicast: "npm:^0.3.5"
+ std-env: "npm:^3.9.0"
+ test-exclude: "npm:^7.0.1"
+ tinyrainbow: "npm:^2.0.0"
+ peerDependencies:
+ "@vitest/browser": 3.2.2
+ vitest: 3.2.2
+ peerDependenciesMeta:
+ "@vitest/browser":
+ optional: true
+ checksum: 10c0/d807f006ab9d4d3fb78c34586ab057a8e588746430b2d3ab07cfb972b5fabe65fde7033a9718ee598d0e3001085fa83edee76ff3f03a05e50a8879beee3ae37a
+ languageName: node
+ linkType: hard
+
+"@vitest/expect@npm:3.0.9":
+ version: 3.0.9
+ resolution: "@vitest/expect@npm:3.0.9"
+ dependencies:
+ "@vitest/spy": "npm:3.0.9"
+ "@vitest/utils": "npm:3.0.9"
chai: "npm:^5.2.0"
tinyrainbow: "npm:^2.0.0"
- checksum: 10c0/3a61e5526ed57491c9c230cb592849a2c15e6b4376bfaec4f623ac75fdcf5c24c322949cfb5362136fc8be5eb19be88d094917ea5f700bd3da0ea0c68ee4a8d9
+ checksum: 10c0/4e5eef8fbc9c3e47f3fb69dbbd5b51aabdf1b6de2f781556d37d79731678fc83cf4a01d146226b12a27df051a4110153a6172506c9c74ae08e5b924a9c947f08
languageName: node
linkType: hard
-"@vitest/mocker@npm:3.1.3":
- version: 3.1.3
- resolution: "@vitest/mocker@npm:3.1.3"
+"@vitest/expect@npm:3.2.1":
+ version: 3.2.1
+ resolution: "@vitest/expect@npm:3.2.1"
dependencies:
- "@vitest/spy": "npm:3.1.3"
+ "@types/chai": "npm:^5.2.2"
+ "@vitest/spy": "npm:3.2.1"
+ "@vitest/utils": "npm:3.2.1"
+ chai: "npm:^5.2.0"
+ tinyrainbow: "npm:^2.0.0"
+ checksum: 10c0/ea02306fff2e657412ac36169621d742898d95cb2a4922f0a81e1fcfc81d755f337f176ddc2a2ed9281e0f2c1648bb6b08b09d4fd523d203d1238e62344c0385
+ languageName: node
+ linkType: hard
+
+"@vitest/mocker@npm:3.2.1":
+ version: 3.2.1
+ resolution: "@vitest/mocker@npm:3.2.1"
+ dependencies:
+ "@vitest/spy": "npm:3.2.1"
estree-walker: "npm:^3.0.3"
magic-string: "npm:^0.30.17"
peerDependencies:
msw: ^2.4.9
- vite: ^5.0.0 || ^6.0.0
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0
peerDependenciesMeta:
msw:
optional: true
vite:
optional: true
- checksum: 10c0/6e6a62e27aa6cd146d14ae64eb9acfc0f49e7479ca426af1fb4df362456aa3456abf29731247659032e4bfb7ac9482fca1d1c7e1501e1a186eb211221e1f613a
+ checksum: 10c0/bcd8865e8e8f45fdf59bb817b788bebe13c509e0220eee723bc6b8ee139352b30e074e674e8f9092ae75db0a66c1ca3887ee078df27ea2d5d7889c9d45cfb675
languageName: node
linkType: hard
-"@vitest/pretty-format@npm:3.1.3, @vitest/pretty-format@npm:^3.1.3":
- version: 3.1.3
- resolution: "@vitest/pretty-format@npm:3.1.3"
+"@vitest/pretty-format@npm:3.0.9":
+ version: 3.0.9
+ resolution: "@vitest/pretty-format@npm:3.0.9"
dependencies:
tinyrainbow: "npm:^2.0.0"
- checksum: 10c0/eba164d2c0b2babbcf6bb054da3b326d08cc3a0289ade3c64309bfe5e7c3124cd4d45a60b2f673cf4f5b3a97381fb7af7009780a5d9665afdf7f8263fa34c068
+ checksum: 10c0/56ae7b1f14df2905b3205d4e121727631c4938ec44f76c1e9fa49923919010378f0dad70b1d277672f3ef45ddf6372140c8d1da95e45df8282f70b74328fce47
languageName: node
linkType: hard
-"@vitest/runner@npm:3.1.3":
- version: 3.1.3
- resolution: "@vitest/runner@npm:3.1.3"
+"@vitest/pretty-format@npm:3.2.1":
+ version: 3.2.1
+ resolution: "@vitest/pretty-format@npm:3.2.1"
dependencies:
- "@vitest/utils": "npm:3.1.3"
+ tinyrainbow: "npm:^2.0.0"
+ checksum: 10c0/24c9d380900d0e2c2296f7a0a86b9efdd02034f1b84a93c0fc01a17ff6aa3b7e80d6bc4fe07e8e78404e6b66d1b8dc5a7d4199e7ed4f89f1874c3f74b731e48c
+ languageName: node
+ linkType: hard
+
+"@vitest/pretty-format@npm:3.2.2, @vitest/pretty-format@npm:^3.2.1":
+ version: 3.2.2
+ resolution: "@vitest/pretty-format@npm:3.2.2"
+ dependencies:
+ tinyrainbow: "npm:^2.0.0"
+ checksum: 10c0/bc74488e6d56b1f86a0066cc7ea53bdd9c062886bb060c37cd4c312d674c1bc3ba9d1350b748a07d2ae9558223f817735edf920496cafadf98a214b683ea0d0e
+ languageName: node
+ linkType: hard
+
+"@vitest/runner@npm:3.2.1":
+ version: 3.2.1
+ resolution: "@vitest/runner@npm:3.2.1"
+ dependencies:
+ "@vitest/utils": "npm:3.2.1"
pathe: "npm:^2.0.3"
- checksum: 10c0/f03c26e72657242ce68a93b46ee8a4e6fa1a290850be608988622a3efef744ffadc0436123acafe61977608b287b1637f4f781d27107ee0c33937c54f547159d
+ checksum: 10c0/b0c4b75627852c56a67aef10176880def0e6e785e96f6e7a1ad632d799e202528de62cab6c8c0c8e1d2afc8255c40225f2cb8ab6fa99925db8c8aca28b6bba3c
languageName: node
linkType: hard
-"@vitest/snapshot@npm:3.1.3":
- version: 3.1.3
- resolution: "@vitest/snapshot@npm:3.1.3"
+"@vitest/snapshot@npm:3.2.1":
+ version: 3.2.1
+ resolution: "@vitest/snapshot@npm:3.2.1"
dependencies:
- "@vitest/pretty-format": "npm:3.1.3"
+ "@vitest/pretty-format": "npm:3.2.1"
magic-string: "npm:^0.30.17"
pathe: "npm:^2.0.3"
- checksum: 10c0/60b70c1d878c3d9a4fe3464d14be2318a7a3be24131beb801712735d5dcbc7db7b798f21c98c6fbad4998554992038b29655e1b6e2503242627f203fd89c97c3
+ checksum: 10c0/7428cfe239c40a146a5e6c73fdefa9167496524aeefd01db28f55dea945ec14f00c982ff4ccf47208e870b020e0edd0f17416cd8db9ae80d2332fb925d4bac94
languageName: node
linkType: hard
-"@vitest/spy@npm:3.1.3":
- version: 3.1.3
- resolution: "@vitest/spy@npm:3.1.3"
+"@vitest/spy@npm:3.0.9":
+ version: 3.0.9
+ resolution: "@vitest/spy@npm:3.0.9"
dependencies:
tinyspy: "npm:^3.0.2"
- checksum: 10c0/6a8c187069827c56f3492f212ccf76c797fe52392849948af736a0f579e4533fa91041d829e2574b252af4aaadec066ca0714450d6457b31526153978bc55192
+ checksum: 10c0/993085dbaf9e651ca9516f88e440424d29279def998186628a1ebcab5558a3045fee8562630608f58303507135f6f3bf9970f65639f3b9baa8bf86cab3eb4742
languageName: node
linkType: hard
-"@vitest/utils@npm:3.1.3":
- version: 3.1.3
- resolution: "@vitest/utils@npm:3.1.3"
+"@vitest/spy@npm:3.2.1":
+ version: 3.2.1
+ resolution: "@vitest/spy@npm:3.2.1"
dependencies:
- "@vitest/pretty-format": "npm:3.1.3"
+ tinyspy: "npm:^4.0.3"
+ checksum: 10c0/5b6e36c5e21cb8ed4b5f8e95c24379846168a719125cc189e53ccd9717bae8a6a63e16a04a57fb8736b6523b1b870df691a27a04e86c487f388d66af669672ca
+ languageName: node
+ linkType: hard
+
+"@vitest/ui@npm:^3.2.1":
+ version: 3.2.2
+ resolution: "@vitest/ui@npm:3.2.2"
+ dependencies:
+ "@vitest/utils": "npm:3.2.2"
+ fflate: "npm:^0.8.2"
+ flatted: "npm:^3.3.3"
+ pathe: "npm:^2.0.3"
+ sirv: "npm:^3.0.1"
+ tinyglobby: "npm:^0.2.14"
+ tinyrainbow: "npm:^2.0.0"
+ peerDependencies:
+ vitest: 3.2.2
+ checksum: 10c0/343ccf49f4ef449d7bde80eb2f815e6548e5fab25bba1923d9fc8483fae9469b477782fe1eaee61d613aca2eef2718ca880d8e6d2135c6f4b09fe653921bf119
+ languageName: node
+ linkType: hard
+
+"@vitest/utils@npm:3.0.9":
+ version: 3.0.9
+ resolution: "@vitest/utils@npm:3.0.9"
+ dependencies:
+ "@vitest/pretty-format": "npm:3.0.9"
loupe: "npm:^3.1.3"
tinyrainbow: "npm:^2.0.0"
- checksum: 10c0/1c4ea711b87a8b2c7dc2da91f20427dccc34c0d1d0e81b8142780d24b6caa3c724e8287f7e01e9e875262b6bb912d55711fb99e66f718ba30cc21706a335829d
+ checksum: 10c0/b966dfb3b926ee9bea59c1fb297abc67adaa23a8a582453ee81167b238446394693617a5e0523eb2791d6983173ef1c07bf28a76bd5a63b49a100610ed6b6a6c
+ languageName: node
+ linkType: hard
+
+"@vitest/utils@npm:3.2.1":
+ version: 3.2.1
+ resolution: "@vitest/utils@npm:3.2.1"
+ dependencies:
+ "@vitest/pretty-format": "npm:3.2.1"
+ loupe: "npm:^3.1.3"
+ tinyrainbow: "npm:^2.0.0"
+ checksum: 10c0/a1fbdf1f16f7df2aabda9a96516481f5ef52eff38b69cbf3d11725fb30351dd1c3d480678c040cf25d4a01238f8f8d5650b554c5790078f8770f54acbc54411a
+ languageName: node
+ linkType: hard
+
+"@vitest/utils@npm:3.2.2":
+ version: 3.2.2
+ resolution: "@vitest/utils@npm:3.2.2"
+ dependencies:
+ "@vitest/pretty-format": "npm:3.2.2"
+ loupe: "npm:^3.1.3"
+ tinyrainbow: "npm:^2.0.0"
+ checksum: 10c0/0274a1f060006616a8dad7ef11fb13d81ca00df8104eb015d4832cc6fc2217c37d0eec083afa2eb5118a8879942c6e055aec7f23325ce8f791b3b4b2c7fa16c3
languageName: node
linkType: hard
@@ -4725,7 +5248,7 @@ __metadata:
languageName: node
linkType: hard
-"aria-query@npm:^5.3.2":
+"aria-query@npm:^5.0.0, aria-query@npm:^5.3.2":
version: 5.3.2
resolution: "aria-query@npm:5.3.2"
checksum: 10c0/003c7e3e2cff5540bf7a7893775fc614de82b0c5dde8ae823d47b7a28a9d4da1f7ed85f340bdb93d5649caa927755f0e31ecc7ab63edfdfc00c8ef07e505e03e
@@ -4871,6 +5394,26 @@ __metadata:
languageName: node
linkType: hard
+"ast-types@npm:^0.16.1":
+ version: 0.16.1
+ resolution: "ast-types@npm:0.16.1"
+ dependencies:
+ tslib: "npm:^2.0.1"
+ checksum: 10c0/abcc49e42eb921a7ebc013d5bec1154651fb6dbc3f497541d488859e681256901b2990b954d530ba0da4d0851271d484f7057d5eff5e07cb73e8b10909f711bf
+ languageName: node
+ linkType: hard
+
+"ast-v8-to-istanbul@npm:^0.3.3":
+ version: 0.3.3
+ resolution: "ast-v8-to-istanbul@npm:0.3.3"
+ dependencies:
+ "@jridgewell/trace-mapping": "npm:^0.3.25"
+ estree-walker: "npm:^3.0.3"
+ js-tokens: "npm:^9.0.1"
+ checksum: 10c0/ffc39bc3ab4b8c1f7aea945960ce6b1e518bab3da7c800277eab2da07d397eeae4a2cb8a5a5f817225646c8ea495c1e4434fbe082c84bae8042abddef53f50b2
+ languageName: node
+ linkType: hard
+
"astral-regex@npm:^2.0.0":
version: 2.0.0
resolution: "astral-regex@npm:2.0.0"
@@ -4942,10 +5485,10 @@ __metadata:
languageName: node
linkType: hard
-"axe-core@npm:^4.10.0":
- version: 4.10.0
- resolution: "axe-core@npm:4.10.0"
- checksum: 10c0/732c171d48caaace5e784895c4dacb8ca6155e9d98045138ebe3952f78457dd05b92c57d05b41ce2a570aff87dbd0471e8398d2c0f6ebe79617b746c8f658998
+"axe-core@npm:^4.10.0, axe-core@npm:^4.2.0":
+ version: 4.10.3
+ resolution: "axe-core@npm:4.10.3"
+ checksum: 10c0/1b1c24f435b2ffe89d76eca0001cbfff42dbf012ad9bd37398b70b11f0d614281a38a28bc3069e8972e3c90ec929a8937994bd24b0ebcbaab87b8d1e241ab0c7
languageName: node
linkType: hard
@@ -5061,6 +5604,15 @@ __metadata:
languageName: node
linkType: hard
+"better-opn@npm:^3.0.2":
+ version: 3.0.2
+ resolution: "better-opn@npm:3.0.2"
+ dependencies:
+ open: "npm:^8.0.4"
+ checksum: 10c0/911ef25d44da75aabfd2444ce7a4294a8000ebcac73068c04a60298b0f7c7506b60421aa4cd02ac82502fb42baaff7e4892234b51e6923eded44c5a11185f2f5
+ languageName: node
+ linkType: hard
+
"bintrees@npm:1.0.2":
version: 1.0.2
resolution: "bintrees@npm:1.0.2"
@@ -5143,17 +5695,17 @@ __metadata:
languageName: node
linkType: hard
-"browserslist@npm:^4.24.0, browserslist@npm:^4.24.4, browserslist@npm:^4.24.5":
- version: 4.24.5
- resolution: "browserslist@npm:4.24.5"
+"browserslist@npm:^4.24.0, browserslist@npm:^4.24.4, browserslist@npm:^4.25.0":
+ version: 4.25.0
+ resolution: "browserslist@npm:4.25.0"
dependencies:
- caniuse-lite: "npm:^1.0.30001716"
- electron-to-chromium: "npm:^1.5.149"
+ caniuse-lite: "npm:^1.0.30001718"
+ electron-to-chromium: "npm:^1.5.160"
node-releases: "npm:^2.0.19"
update-browserslist-db: "npm:^1.1.3"
bin:
browserslist: cli.js
- checksum: 10c0/f4c1ce1a7d8fdfab5e5b88bb6e93d09e8a883c393f86801537a252da0362dbdcde4dbd97b318246c5d84c6607b2f6b47af732c1b000d6a8a881ee024bad29204
+ checksum: 10c0/cc16c55b4468b18684a0e1ca303592b38635b1155d6724f172407192737a2f405b8030d87a05813729592793445b3d15e737b0055f901cdecccb29b1e580a1c5
languageName: node
linkType: hard
@@ -5274,10 +5826,10 @@ __metadata:
languageName: node
linkType: hard
-"caniuse-lite@npm:^1.0.30001702, caniuse-lite@npm:^1.0.30001716":
- version: 1.0.30001718
- resolution: "caniuse-lite@npm:1.0.30001718"
- checksum: 10c0/67f9ad09bc16443e28d14f265d6e468480cd8dc1900d0d8b982222de80c699c4f2306599c3da8a3fa7139f110d4b30d49dbac78f215470f479abb6ffe141d5d3
+"caniuse-lite@npm:^1.0.30001702, caniuse-lite@npm:^1.0.30001718":
+ version: 1.0.30001721
+ resolution: "caniuse-lite@npm:1.0.30001721"
+ checksum: 10c0/fa3a8926899824b385279f1f886fe34c5efb1321c9ece1b9df25c8d567a2706db8450cc5b4d969e769e641593e08ea644909324aba93636a43e4949a75f81c4c
languageName: node
linkType: hard
@@ -5294,6 +5846,16 @@ __metadata:
languageName: node
linkType: hard
+"chalk@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "chalk@npm:3.0.0"
+ dependencies:
+ ansi-styles: "npm:^4.1.0"
+ supports-color: "npm:^7.1.0"
+ checksum: 10c0/ee650b0a065b3d7a6fda258e75d3a86fc8e4effa55871da730a9e42ccb035bf5fd203525e5a1ef45ec2582ecc4f65b47eb11357c526b84dd29a14fb162c414d2
+ languageName: node
+ linkType: hard
+
"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.2":
version: 4.1.2
resolution: "chalk@npm:4.1.2"
@@ -5341,6 +5903,25 @@ __metadata:
languageName: node
linkType: hard
+"chromatic@npm:^12.1.0":
+ version: 12.1.0
+ resolution: "chromatic@npm:12.1.0"
+ peerDependencies:
+ "@chromatic-com/cypress": ^0.*.* || ^1.0.0
+ "@chromatic-com/playwright": ^0.*.* || ^1.0.0
+ peerDependenciesMeta:
+ "@chromatic-com/cypress":
+ optional: true
+ "@chromatic-com/playwright":
+ optional: true
+ bin:
+ chroma: dist/bin.js
+ chromatic: dist/bin.js
+ chromatic-cli: dist/bin.js
+ checksum: 10c0/4acb70a4a84605f1963a823beed4f3062ec91e373104500f4295af2298b8d0b49f864d06ca81bc9389e44cae3a284332aac07c6cbfc123aa6457f3b52a4c4b78
+ languageName: node
+ linkType: hard
+
"chrome-trace-event@npm:^1.0.2":
version: 1.0.4
resolution: "chrome-trace-event@npm:1.0.4"
@@ -5548,9 +6129,9 @@ __metadata:
linkType: hard
"core-js@npm:^3.30.2, core-js@npm:^3.41.0":
- version: 3.42.0
- resolution: "core-js@npm:3.42.0"
- checksum: 10c0/2913d3d5452d54ad92f058d66046782d608c05e037bcc523aab79c04454fe640998f94e6011292969d66dfa472f398b085ce843dcb362056532a5799c627184e
+ version: 3.43.0
+ resolution: "core-js@npm:3.43.0"
+ checksum: 10c0/9d4ad66296e60380777de51d019b5c3e6cce023b7999750a5094f9a4b0ea53bf3600beb4ef11c56548f2c8791d43d4056e270d1cf55ba87273011aa7d4597871
languageName: node
linkType: hard
@@ -5691,6 +6272,13 @@ __metadata:
languageName: node
linkType: hard
+"css.escape@npm:^1.5.1":
+ version: 1.5.1
+ resolution: "css.escape@npm:1.5.1"
+ checksum: 10c0/5e09035e5bf6c2c422b40c6df2eb1529657a17df37fda5d0433d722609527ab98090baf25b13970ca754079a0f3161dd3dfc0e743563ded8cfa0749d861c1525
+ languageName: node
+ linkType: hard
+
"cssdb@npm:^8.3.0":
version: 8.3.0
resolution: "cssdb@npm:8.3.0"
@@ -5790,15 +6378,15 @@ __metadata:
languageName: node
linkType: hard
-"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.6, debug@npm:^4.3.7, debug@npm:^4.4.0":
- version: 4.4.0
- resolution: "debug@npm:4.4.0"
+"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.6, debug@npm:^4.3.7, debug@npm:^4.4.0, debug@npm:^4.4.1":
+ version: 4.4.1
+ resolution: "debug@npm:4.4.1"
dependencies:
ms: "npm:^2.1.3"
peerDependenciesMeta:
supports-color:
optional: true
- checksum: 10c0/db94f1a182bf886f57b4755f85b3a74c39b5114b9377b7ab375dc2cfa3454f09490cc6c30f829df3fc8042bc8b8995f6567ce5cd96f3bc3688bd24027197d9de
+ checksum: 10c0/d2b44bc1afd912b49bb7ebb0d50a860dc93a4dd7d946e8de94abc957bb63726b7dd5aa48c18c2386c379ec024c46692e15ed3ed97d481729f929201e671fcd55
languageName: node
linkType: hard
@@ -5946,6 +6534,15 @@ __metadata:
languageName: node
linkType: hard
+"doctrine@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "doctrine@npm:3.0.0"
+ dependencies:
+ esutils: "npm:^2.0.2"
+ checksum: 10c0/c96bdccabe9d62ab6fea9399fdff04a66e6563c1d6fb3a3a063e8d53c3bb136ba63e84250bbf63d00086a769ad53aef92d2bd483f03f837fc97b71cbee6b2520
+ languageName: node
+ linkType: hard
+
"dom-accessibility-api@npm:^0.5.9":
version: 0.5.16
resolution: "dom-accessibility-api@npm:0.5.16"
@@ -5953,6 +6550,13 @@ __metadata:
languageName: node
linkType: hard
+"dom-accessibility-api@npm:^0.6.3":
+ version: 0.6.3
+ resolution: "dom-accessibility-api@npm:0.6.3"
+ checksum: 10c0/10bee5aa514b2a9a37c87cd81268db607a2e933a050074abc2f6fa3da9080ebed206a320cbc123567f2c3087d22292853bdfdceaffdd4334ffe2af9510b29360
+ languageName: node
+ linkType: hard
+
"dom-helpers@npm:^3.4.0":
version: 3.4.0
resolution: "dom-helpers@npm:3.4.0"
@@ -6025,10 +6629,10 @@ __metadata:
languageName: node
linkType: hard
-"electron-to-chromium@npm:^1.5.149":
- version: 1.5.159
- resolution: "electron-to-chromium@npm:1.5.159"
- checksum: 10c0/dc5b60a235ad04b1637b3b2af4914ac900c42813b02262a91a41d950223316f7b12de715697cf9c2d9f572f716f9422bf259ee65d86599cd2cc66e92c499ebd1
+"electron-to-chromium@npm:^1.5.160":
+ version: 1.5.165
+ resolution: "electron-to-chromium@npm:1.5.165"
+ checksum: 10c0/20b91e67e7a8829a358c4a488e9b59b0e5f8d4cb075a70b9757bb21acf0fc751ca58ca7d9c6018bec74ac4bd42f7859e4ef37421c252a2275f642e12a32271d6
languageName: node
linkType: hard
@@ -6299,35 +6903,46 @@ __metadata:
languageName: node
linkType: hard
-"esbuild@npm:^0.25.0":
- version: 0.25.2
- resolution: "esbuild@npm:0.25.2"
+"esbuild-register@npm:^3.5.0":
+ version: 3.6.0
+ resolution: "esbuild-register@npm:3.6.0"
dependencies:
- "@esbuild/aix-ppc64": "npm:0.25.2"
- "@esbuild/android-arm": "npm:0.25.2"
- "@esbuild/android-arm64": "npm:0.25.2"
- "@esbuild/android-x64": "npm:0.25.2"
- "@esbuild/darwin-arm64": "npm:0.25.2"
- "@esbuild/darwin-x64": "npm:0.25.2"
- "@esbuild/freebsd-arm64": "npm:0.25.2"
- "@esbuild/freebsd-x64": "npm:0.25.2"
- "@esbuild/linux-arm": "npm:0.25.2"
- "@esbuild/linux-arm64": "npm:0.25.2"
- "@esbuild/linux-ia32": "npm:0.25.2"
- "@esbuild/linux-loong64": "npm:0.25.2"
- "@esbuild/linux-mips64el": "npm:0.25.2"
- "@esbuild/linux-ppc64": "npm:0.25.2"
- "@esbuild/linux-riscv64": "npm:0.25.2"
- "@esbuild/linux-s390x": "npm:0.25.2"
- "@esbuild/linux-x64": "npm:0.25.2"
- "@esbuild/netbsd-arm64": "npm:0.25.2"
- "@esbuild/netbsd-x64": "npm:0.25.2"
- "@esbuild/openbsd-arm64": "npm:0.25.2"
- "@esbuild/openbsd-x64": "npm:0.25.2"
- "@esbuild/sunos-x64": "npm:0.25.2"
- "@esbuild/win32-arm64": "npm:0.25.2"
- "@esbuild/win32-ia32": "npm:0.25.2"
- "@esbuild/win32-x64": "npm:0.25.2"
+ debug: "npm:^4.3.4"
+ peerDependencies:
+ esbuild: ">=0.12 <1"
+ checksum: 10c0/77193b7ca32ba9f81b35ddf3d3d0138efb0b1429d71b39480cfee932e1189dd2e492bd32bf04a4d0bc3adfbc7ec7381ceb5ffd06efe35f3e70904f1f686566d5
+ languageName: node
+ linkType: hard
+
+"esbuild@npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0, esbuild@npm:^0.25.0":
+ version: 0.25.5
+ resolution: "esbuild@npm:0.25.5"
+ dependencies:
+ "@esbuild/aix-ppc64": "npm:0.25.5"
+ "@esbuild/android-arm": "npm:0.25.5"
+ "@esbuild/android-arm64": "npm:0.25.5"
+ "@esbuild/android-x64": "npm:0.25.5"
+ "@esbuild/darwin-arm64": "npm:0.25.5"
+ "@esbuild/darwin-x64": "npm:0.25.5"
+ "@esbuild/freebsd-arm64": "npm:0.25.5"
+ "@esbuild/freebsd-x64": "npm:0.25.5"
+ "@esbuild/linux-arm": "npm:0.25.5"
+ "@esbuild/linux-arm64": "npm:0.25.5"
+ "@esbuild/linux-ia32": "npm:0.25.5"
+ "@esbuild/linux-loong64": "npm:0.25.5"
+ "@esbuild/linux-mips64el": "npm:0.25.5"
+ "@esbuild/linux-ppc64": "npm:0.25.5"
+ "@esbuild/linux-riscv64": "npm:0.25.5"
+ "@esbuild/linux-s390x": "npm:0.25.5"
+ "@esbuild/linux-x64": "npm:0.25.5"
+ "@esbuild/netbsd-arm64": "npm:0.25.5"
+ "@esbuild/netbsd-x64": "npm:0.25.5"
+ "@esbuild/openbsd-arm64": "npm:0.25.5"
+ "@esbuild/openbsd-x64": "npm:0.25.5"
+ "@esbuild/sunos-x64": "npm:0.25.5"
+ "@esbuild/win32-arm64": "npm:0.25.5"
+ "@esbuild/win32-ia32": "npm:0.25.5"
+ "@esbuild/win32-x64": "npm:0.25.5"
dependenciesMeta:
"@esbuild/aix-ppc64":
optional: true
@@ -6381,7 +6996,7 @@ __metadata:
optional: true
bin:
esbuild: bin/esbuild
- checksum: 10c0/87ce0b78699c4d192b8cf7e9b688e9a0da10e6f58ff85a368bf3044ca1fa95626c98b769b5459352282e0065585b6f994a5e6699af5cccf9d31178960e2b58fd
+ checksum: 10c0/aba8cbc11927fa77562722ed5e95541ce2853f67ad7bdc40382b558abc2e0ec57d92ffb820f082ba2047b4ef9f3bc3da068cdebe30dfd3850cfa3827a78d604e
languageName: node
linkType: hard
@@ -6594,6 +7209,18 @@ __metadata:
languageName: node
linkType: hard
+"eslint-plugin-storybook@npm:^9.0.4":
+ version: 9.0.4
+ resolution: "eslint-plugin-storybook@npm:9.0.4"
+ dependencies:
+ "@typescript-eslint/utils": "npm:^8.8.1"
+ peerDependencies:
+ eslint: ">=8"
+ storybook: ^9.0.4
+ checksum: 10c0/b5dbcd15feab63d71f4bd5da26306043339620ddf64bb623de3a7542ee81828b4137af93e199c3e49fb0e5a76d582a21fb580626011ae2340dd6fc684f438358
+ languageName: node
+ linkType: hard
+
"eslint-scope@npm:5.1.1":
version: 5.1.1
resolution: "eslint-scope@npm:5.1.1"
@@ -6614,7 +7241,7 @@ __metadata:
languageName: node
linkType: hard
-"eslint-visitor-keys@npm:^3.3.0":
+"eslint-visitor-keys@npm:^3.4.3":
version: 3.4.3
resolution: "eslint-visitor-keys@npm:3.4.3"
checksum: 10c0/92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820
@@ -6689,6 +7316,16 @@ __metadata:
languageName: node
linkType: hard
+"esprima@npm:~4.0.0":
+ version: 4.0.1
+ resolution: "esprima@npm:4.0.1"
+ bin:
+ esparse: ./bin/esparse.js
+ esvalidate: ./bin/esvalidate.js
+ checksum: 10c0/ad4bab9ead0808cf56501750fd9d3fb276f6b105f987707d059005d57e182d18a7c9ec7f3a01794ebddcca676773e42ca48a32d67a250c9d35e009ca613caba3
+ languageName: node
+ linkType: hard
+
"esquery@npm:^1.5.0, esquery@npm:^1.6.0":
version: 1.6.0
resolution: "esquery@npm:1.6.0"
@@ -6915,6 +7552,13 @@ __metadata:
languageName: node
linkType: hard
+"fflate@npm:^0.8.2":
+ version: 0.8.2
+ resolution: "fflate@npm:0.8.2"
+ checksum: 10c0/03448d630c0a583abea594835a9fdb2aaf7d67787055a761515bf4ed862913cfd693b4c4ffd5c3f3b355a70cf1e19033e9ae5aedcca103188aaff91b8bd6e293
+ languageName: node
+ linkType: hard
+
"file-entry-cache@npm:^10.0.8":
version: 10.1.0
resolution: "file-entry-cache@npm:10.1.0"
@@ -7109,6 +7753,16 @@ __metadata:
languageName: node
linkType: hard
+"fsevents@npm:2.3.2":
+ version: 2.3.2
+ resolution: "fsevents@npm:2.3.2"
+ dependencies:
+ node-gyp: "npm:latest"
+ checksum: 10c0/be78a3efa3e181cda3cf7a4637cb527bcebb0bd0ea0440105a3bb45b86f9245b307dc10a2507e8f4498a7d4ec349d1910f4d73e4d4495b16103106e07eee735b
+ conditions: os=darwin
+ languageName: node
+ linkType: hard
+
"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3":
version: 2.3.3
resolution: "fsevents@npm:2.3.3"
@@ -7119,6 +7773,15 @@ __metadata:
languageName: node
linkType: hard
+"fsevents@patch:fsevents@npm%3A2.3.2#optional!builtin":
+ version: 2.3.2
+ resolution: "fsevents@patch:fsevents@npm%3A2.3.2#optional!builtin::version=2.3.2&hash=df0bf1"
+ dependencies:
+ node-gyp: "npm:latest"
+ conditions: os=darwin
+ languageName: node
+ linkType: hard
+
"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin":
version: 2.3.3
resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1"
@@ -7264,7 +7927,7 @@ __metadata:
languageName: node
linkType: hard
-"glob@npm:^10.2.2, glob@npm:^10.3.10":
+"glob@npm:^10.0.0, glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.4.1":
version: 10.4.5
resolution: "glob@npm:10.4.5"
dependencies:
@@ -7504,6 +8167,13 @@ __metadata:
languageName: node
linkType: hard
+"html-escaper@npm:^2.0.0":
+ version: 2.0.2
+ resolution: "html-escaper@npm:2.0.2"
+ checksum: 10c0/208e8a12de1a6569edbb14544f4567e6ce8ecc30b9394fcaa4e7bb1e60c12a7c9a1ed27e31290817157e8626f3a4f29e76c8747030822eb84a6abb15c255f0a0
+ languageName: node
+ linkType: hard
+
"html-tags@npm:^3.3.1":
version: 3.3.1
resolution: "html-tags@npm:3.3.1"
@@ -8125,6 +8795,45 @@ __metadata:
languageName: node
linkType: hard
+"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.2":
+ version: 3.2.2
+ resolution: "istanbul-lib-coverage@npm:3.2.2"
+ checksum: 10c0/6c7ff2106769e5f592ded1fb418f9f73b4411fd5a084387a5410538332b6567cd1763ff6b6cadca9b9eb2c443cce2f7ea7d7f1b8d315f9ce58539793b1e0922b
+ languageName: node
+ linkType: hard
+
+"istanbul-lib-report@npm:^3.0.0, istanbul-lib-report@npm:^3.0.1":
+ version: 3.0.1
+ resolution: "istanbul-lib-report@npm:3.0.1"
+ dependencies:
+ istanbul-lib-coverage: "npm:^3.0.0"
+ make-dir: "npm:^4.0.0"
+ supports-color: "npm:^7.1.0"
+ checksum: 10c0/84323afb14392de8b6a5714bd7e9af845cfbd56cfe71ed276cda2f5f1201aea673c7111901227ee33e68e4364e288d73861eb2ed48f6679d1e69a43b6d9b3ba7
+ languageName: node
+ linkType: hard
+
+"istanbul-lib-source-maps@npm:^5.0.6":
+ version: 5.0.6
+ resolution: "istanbul-lib-source-maps@npm:5.0.6"
+ dependencies:
+ "@jridgewell/trace-mapping": "npm:^0.3.23"
+ debug: "npm:^4.1.1"
+ istanbul-lib-coverage: "npm:^3.0.0"
+ checksum: 10c0/ffe75d70b303a3621ee4671554f306e0831b16f39ab7f4ab52e54d356a5d33e534d97563e318f1333a6aae1d42f91ec49c76b6cd3f3fb378addcb5c81da0255f
+ languageName: node
+ linkType: hard
+
+"istanbul-reports@npm:^3.1.7":
+ version: 3.1.7
+ resolution: "istanbul-reports@npm:3.1.7"
+ dependencies:
+ html-escaper: "npm:^2.0.0"
+ istanbul-lib-report: "npm:^3.0.0"
+ checksum: 10c0/a379fadf9cf8dc5dfe25568115721d4a7eb82fbd50b005a6672aff9c6989b20cc9312d7865814e0859cd8df58cbf664482e1d3604be0afde1f7fc3ccc1394a51
+ languageName: node
+ linkType: hard
+
"iterator.prototype@npm:^1.1.4":
version: 1.1.5
resolution: "iterator.prototype@npm:1.1.5"
@@ -8191,6 +8900,13 @@ __metadata:
languageName: node
linkType: hard
+"js-tokens@npm:^9.0.1":
+ version: 9.0.1
+ resolution: "js-tokens@npm:9.0.1"
+ checksum: 10c0/68dcab8f233dde211a6b5fd98079783cbcd04b53617c1250e3553ee16ab3e6134f5e65478e41d82f6d351a052a63d71024553933808570f04dbf828d7921e80e
+ languageName: node
+ linkType: hard
+
"js-yaml@npm:^4.1.0":
version: 4.1.0
resolution: "js-yaml@npm:4.1.0"
@@ -8317,7 +9033,7 @@ __metadata:
languageName: node
linkType: hard
-"json5@npm:^2.2.0, json5@npm:^2.2.3":
+"json5@npm:^2.2.0, json5@npm:^2.2.2, json5@npm:^2.2.3":
version: 2.2.3
resolution: "json5@npm:2.2.3"
bin:
@@ -8397,6 +9113,13 @@ __metadata:
languageName: node
linkType: hard
+"kleur@npm:^3.0.3":
+ version: 3.0.3
+ resolution: "kleur@npm:3.0.3"
+ checksum: 10c0/cd3a0b8878e7d6d3799e54340efe3591ca787d9f95f109f28129bdd2915e37807bf8918bb295ab86afb8c82196beec5a1adcaf29042ce3f2bd932b038fe3aa4b
+ languageName: node
+ linkType: hard
+
"known-css-properties@npm:^0.36.0":
version: 0.36.0
resolution: "known-css-properties@npm:0.36.0"
@@ -8670,6 +9393,26 @@ __metadata:
languageName: node
linkType: hard
+"magicast@npm:^0.3.5":
+ version: 0.3.5
+ resolution: "magicast@npm:0.3.5"
+ dependencies:
+ "@babel/parser": "npm:^7.25.4"
+ "@babel/types": "npm:^7.25.4"
+ source-map-js: "npm:^1.2.0"
+ checksum: 10c0/a6cacc0a848af84f03e3f5bda7b0de75e4d0aa9ddce5517fd23ed0f31b5ddd51b2d0ff0b7e09b51f7de0f4053c7a1107117edda6b0732dca3e9e39e6c5a68c64
+ languageName: node
+ linkType: hard
+
+"make-dir@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "make-dir@npm:4.0.0"
+ dependencies:
+ semver: "npm:^7.5.3"
+ checksum: 10c0/69b98a6c0b8e5c4fe9acb61608a9fbcfca1756d910f51e5dbe7a9e5cfb74fca9b8a0c8a0ffdf1294a740826c1ab4871d5bf3f62f72a3049e5eac6541ddffed68
+ languageName: node
+ linkType: hard
+
"make-fetch-happen@npm:^13.0.0":
version: 13.0.0
resolution: "make-fetch-happen@npm:13.0.0"
@@ -8815,6 +9558,13 @@ __metadata:
languageName: node
linkType: hard
+"min-indent@npm:^1.0.0, min-indent@npm:^1.0.1":
+ version: 1.0.1
+ resolution: "min-indent@npm:1.0.1"
+ checksum: 10c0/7e207bd5c20401b292de291f02913230cb1163abca162044f7db1d951fa245b174dc00869d40dd9a9f32a885ad6a5f3e767ee104cf278f399cb4e92d3f582d5c
+ languageName: node
+ linkType: hard
+
"minimatch@npm:^3.1.1, minimatch@npm:^3.1.2":
version: 3.1.2
resolution: "minimatch@npm:3.1.2"
@@ -8949,6 +9699,13 @@ __metadata:
languageName: node
linkType: hard
+"mrmime@npm:^2.0.0":
+ version: 2.0.1
+ resolution: "mrmime@npm:2.0.1"
+ checksum: 10c0/af05afd95af202fdd620422f976ad67dc18e6ee29beb03dd1ce950ea6ef664de378e44197246df4c7cdd73d47f2e7143a6e26e473084b9e4aa2095c0ad1e1761
+ languageName: node
+ linkType: hard
+
"ms@npm:2.0.0":
version: 2.0.0
resolution: "ms@npm:2.0.0"
@@ -9184,13 +9941,6 @@ __metadata:
languageName: node
linkType: hard
-"obuf@npm:~1.1.2":
- version: 1.1.2
- resolution: "obuf@npm:1.1.2"
- checksum: 10c0/520aaac7ea701618eacf000fc96ae458e20e13b0569845800fc582f81b386731ab22d55354b4915d58171db00e79cfcd09c1638c02f89577ef092b38c65b7d81
- languageName: node
- linkType: hard
-
"on-exit-leak-free@npm:^2.1.0":
version: 2.1.2
resolution: "on-exit-leak-free@npm:2.1.2"
@@ -9225,7 +9975,7 @@ __metadata:
languageName: node
linkType: hard
-"open@npm:^8.0.0":
+"open@npm:^8.0.0, open@npm:^8.0.4":
version: 8.4.2
resolution: "open@npm:8.4.2"
dependencies:
@@ -9454,13 +10204,6 @@ __metadata:
languageName: node
linkType: hard
-"pg-numeric@npm:1.0.2":
- version: 1.0.2
- resolution: "pg-numeric@npm:1.0.2"
- checksum: 10c0/43dd9884e7b52c79ddc28d2d282d7475fce8bba13452d33c04ceb2e0a65f561edf6699694e8e1c832ff9093770496363183c950dd29608e1bdd98f344b25bca9
- languageName: node
- linkType: hard
-
"pg-pool@npm:^3.10.0":
version: 3.10.0
resolution: "pg-pool@npm:3.10.0"
@@ -9477,7 +10220,7 @@ __metadata:
languageName: node
linkType: hard
-"pg-types@npm:2.2.0":
+"pg-types@npm:2.2.0, pg-types@npm:^2.2.0":
version: 2.2.0
resolution: "pg-types@npm:2.2.0"
dependencies:
@@ -9490,21 +10233,6 @@ __metadata:
languageName: node
linkType: hard
-"pg-types@npm:^4.0.1":
- version: 4.0.1
- resolution: "pg-types@npm:4.0.1"
- dependencies:
- pg-int8: "npm:1.0.1"
- pg-numeric: "npm:1.0.2"
- postgres-array: "npm:~3.0.1"
- postgres-bytea: "npm:~3.0.0"
- postgres-date: "npm:~2.0.1"
- postgres-interval: "npm:^3.0.0"
- postgres-range: "npm:^1.1.1"
- checksum: 10c0/e2126b2775554ae8bacb3b104814487c2af2caff44cc52bee786b3887c65fe4c1fe031237e51e30ffed1cbb13b71776bd60cc1e65ac800c9946df4030849a074
- languageName: node
- linkType: hard
-
"pg@npm:^8.5.0":
version: 8.16.0
resolution: "pg@npm:8.16.0"
@@ -9576,14 +10304,14 @@ __metadata:
linkType: hard
"pino-http@npm:^10.0.0":
- version: 10.4.0
- resolution: "pino-http@npm:10.4.0"
+ version: 10.5.0
+ resolution: "pino-http@npm:10.5.0"
dependencies:
get-caller-file: "npm:^2.0.5"
pino: "npm:^9.0.0"
pino-std-serializers: "npm:^7.0.0"
- process-warning: "npm:^4.0.0"
- checksum: 10c0/64144e2c94e939070f56ad82dfb012b6a98d21582e0660cf821e7cee64d4e06f7724aa40bc5bf9cd1254d58ab7cbd972dec287b7989eba647d384f6edd8d95fd
+ process-warning: "npm:^5.0.0"
+ checksum: 10c0/17597d653a4088f7faed4d58500a5ef51d4d05247307696760006313c33c1d23177af98fb902e15b8e2cd92d81306c884673f841ba5b9bf0c064802f3c0bd775
languageName: node
linkType: hard
@@ -9638,6 +10366,30 @@ __metadata:
languageName: node
linkType: hard
+"playwright-core@npm:1.52.0":
+ version: 1.52.0
+ resolution: "playwright-core@npm:1.52.0"
+ bin:
+ playwright-core: cli.js
+ checksum: 10c0/640945507e6ca2144e9f596b2a6ecac042c2fd3683ff99e6271e9a7b38f3602d415f282609d569456f66680aab8b3c5bb1b257d8fb63a7fc0ed648261110421f
+ languageName: node
+ linkType: hard
+
+"playwright@npm:^1.52.0":
+ version: 1.52.0
+ resolution: "playwright@npm:1.52.0"
+ dependencies:
+ fsevents: "npm:2.3.2"
+ playwright-core: "npm:1.52.0"
+ dependenciesMeta:
+ fsevents:
+ optional: true
+ bin:
+ playwright: cli.js
+ checksum: 10c0/2c6edf1e15e59bbaf77f3fa0fe0ac975793c17cff835d9c8b8bc6395a3b6f1c01898b3058ab37891b2e4d424bcc8f1b4844fe70d943e0143d239d7451408c579
+ languageName: node
+ linkType: hard
+
"possible-typed-array-names@npm:^1.0.0":
version: 1.0.0
resolution: "possible-typed-array-names@npm:1.0.0"
@@ -9720,9 +10472,9 @@ __metadata:
languageName: node
linkType: hard
-"postcss-custom-properties@npm:^14.0.5":
- version: 14.0.5
- resolution: "postcss-custom-properties@npm:14.0.5"
+"postcss-custom-properties@npm:^14.0.6":
+ version: 14.0.6
+ resolution: "postcss-custom-properties@npm:14.0.6"
dependencies:
"@csstools/cascade-layer-name-parser": "npm:^2.0.5"
"@csstools/css-parser-algorithms": "npm:^3.0.5"
@@ -9731,7 +10483,7 @@ __metadata:
postcss-value-parser: "npm:^4.2.0"
peerDependencies:
postcss: ^8.4
- checksum: 10c0/ddee0545075dc0888cd54a9bb5791a98719a5d4f31d1de33823841efb540fa79f65e48b7c4aaba753d3214102e419536c5bc46c72e6e1579e5352da4e042ef3b
+ checksum: 10c0/0eeef77bc713551f5cb8fa5982d24da4e854075f3af020f1c94366c47a23a4cc225ebfecc978bdb17f00ee0bdee9d2c784e0d01adc64a447321e408abbe2c83b
languageName: node
linkType: hard
@@ -9858,16 +10610,16 @@ __metadata:
languageName: node
linkType: hard
-"postcss-nesting@npm:^13.0.1":
- version: 13.0.1
- resolution: "postcss-nesting@npm:13.0.1"
+"postcss-nesting@npm:^13.0.2":
+ version: 13.0.2
+ resolution: "postcss-nesting@npm:13.0.2"
dependencies:
- "@csstools/selector-resolve-nested": "npm:^3.0.0"
+ "@csstools/selector-resolve-nested": "npm:^3.1.0"
"@csstools/selector-specificity": "npm:^5.0.0"
postcss-selector-parser: "npm:^7.0.0"
peerDependencies:
postcss: ^8.4
- checksum: 10c0/549307c272cdd4cb5105d8fbcd582f15a1cb74e5bba240b05b27f77fe0422730be966699a49a9ad15fd9d1bc551c1edbaefb21a69686a9b131b585dbc9d90ebf
+ checksum: 10c0/bfa0578b3b686c6374f5a7b2f6ef955cb7e13400de95a919975a982ae43c1e25db37385618f210715ff15393dc7ff8c26c7b156f06b8fb3118a426099cf7f1f2
languageName: node
linkType: hard
@@ -9912,8 +10664,8 @@ __metadata:
linkType: hard
"postcss-preset-env@npm:^10.1.5":
- version: 10.2.0
- resolution: "postcss-preset-env@npm:10.2.0"
+ version: 10.2.3
+ resolution: "postcss-preset-env@npm:10.2.3"
dependencies:
"@csstools/postcss-cascade-layers": "npm:^5.0.1"
"@csstools/postcss-color-function": "npm:^4.0.10"
@@ -9927,7 +10679,7 @@ __metadata:
"@csstools/postcss-hwb-function": "npm:^4.0.10"
"@csstools/postcss-ic-unit": "npm:^4.0.2"
"@csstools/postcss-initial": "npm:^2.0.1"
- "@csstools/postcss-is-pseudo-class": "npm:^5.0.1"
+ "@csstools/postcss-is-pseudo-class": "npm:^5.0.3"
"@csstools/postcss-light-dark-function": "npm:^2.0.9"
"@csstools/postcss-logical-float-and-clear": "npm:^3.0.0"
"@csstools/postcss-logical-overflow": "npm:^2.0.0"
@@ -9949,7 +10701,7 @@ __metadata:
"@csstools/postcss-trigonometric-functions": "npm:^4.0.9"
"@csstools/postcss-unset-value": "npm:^4.0.0"
autoprefixer: "npm:^10.4.21"
- browserslist: "npm:^4.24.5"
+ browserslist: "npm:^4.25.0"
css-blank-pseudo: "npm:^7.0.1"
css-has-pseudo: "npm:^7.0.2"
css-prefers-color-scheme: "npm:^10.0.0"
@@ -9960,7 +10712,7 @@ __metadata:
postcss-color-hex-alpha: "npm:^10.0.0"
postcss-color-rebeccapurple: "npm:^10.0.0"
postcss-custom-media: "npm:^11.0.6"
- postcss-custom-properties: "npm:^14.0.5"
+ postcss-custom-properties: "npm:^14.0.6"
postcss-custom-selectors: "npm:^8.0.5"
postcss-dir-pseudo-class: "npm:^9.0.1"
postcss-double-position-gradients: "npm:^6.0.2"
@@ -9971,7 +10723,7 @@ __metadata:
postcss-image-set-function: "npm:^7.0.0"
postcss-lab-function: "npm:^7.0.10"
postcss-logical: "npm:^8.1.0"
- postcss-nesting: "npm:^13.0.1"
+ postcss-nesting: "npm:^13.0.2"
postcss-opacity-percentage: "npm:^3.0.0"
postcss-overflow-shorthand: "npm:^6.0.0"
postcss-page-break: "npm:^3.0.4"
@@ -9981,7 +10733,7 @@ __metadata:
postcss-selector-not: "npm:^8.0.1"
peerDependencies:
postcss: ^8.4
- checksum: 10c0/33406dcdd1d63fd3810f12cd97bf0d6a09a51917943ee7d75e2ccaf1d8cbce363abcb90ee68f14509d287d00de4afd6664dbdecaa4f4b95509e3578e27f54a24
+ checksum: 10c0/f3d2ea8b95083acad2cf74aca93904dd3158639bf692d1d471598b538e0c6b4447ae306e7bc1c2426dd465e7c9715373678855b7e211e194b507ef8184e83f99
languageName: node
linkType: hard
@@ -10076,13 +10828,6 @@ __metadata:
languageName: node
linkType: hard
-"postgres-array@npm:~3.0.1":
- version: 3.0.2
- resolution: "postgres-array@npm:3.0.2"
- checksum: 10c0/644aa071f67a66a59f641f8e623887d2b915bc102a32643e2aa8b54c11acd343c5ad97831ea444dd37bd4b921ba35add4aa2cb0c6b76700a8252c2324aeba5b4
- languageName: node
- linkType: hard
-
"postgres-bytea@npm:~1.0.0":
version: 1.0.0
resolution: "postgres-bytea@npm:1.0.0"
@@ -10090,15 +10835,6 @@ __metadata:
languageName: node
linkType: hard
-"postgres-bytea@npm:~3.0.0":
- version: 3.0.0
- resolution: "postgres-bytea@npm:3.0.0"
- dependencies:
- obuf: "npm:~1.1.2"
- checksum: 10c0/41c79cc48aa730c5ba3eda6ab989a940034f07a1f57b8f2777dce56f1b8cca16c5870582932b5b10cc605048aef9b6157e06253c871b4717cafc6d00f55376aa
- languageName: node
- linkType: hard
-
"postgres-date@npm:~1.0.4":
version: 1.0.7
resolution: "postgres-date@npm:1.0.7"
@@ -10106,13 +10842,6 @@ __metadata:
languageName: node
linkType: hard
-"postgres-date@npm:~2.0.1":
- version: 2.0.1
- resolution: "postgres-date@npm:2.0.1"
- checksum: 10c0/2d3698958f858b7d1df0a3929fb8750ccb43fa2c8ee9fec7a021e7926291f6c85ddd9d94d87cd6529d70bd2444f3e14fb5bb323af19ceaa733542cc05c5c653a
- languageName: node
- linkType: hard
-
"postgres-interval@npm:^1.1.0":
version: 1.2.0
resolution: "postgres-interval@npm:1.2.0"
@@ -10122,20 +10851,6 @@ __metadata:
languageName: node
linkType: hard
-"postgres-interval@npm:^3.0.0":
- version: 3.0.0
- resolution: "postgres-interval@npm:3.0.0"
- checksum: 10c0/8b570b30ea37c685e26d136d34460f246f98935a1533defc4b53bb05ee23ae3dc7475b718ec7ea607a57894d8c6b4f1adf67ca9cc83a75bdacffd427d5c68de8
- languageName: node
- linkType: hard
-
-"postgres-range@npm:^1.1.1":
- version: 1.1.3
- resolution: "postgres-range@npm:1.1.3"
- checksum: 10c0/f46bc379a198a9e3282a222c8e432d77494854bd4fa0706dff01641846db0bf4f09a9723e7fbb202da34ec3b2d88fc50e26e4bbeded7df19646e3acd6a7465ce
- languageName: node
- linkType: hard
-
"prelude-ls@npm:^1.2.1":
version: 1.2.1
resolution: "prelude-ls@npm:1.2.1"
@@ -10184,13 +10899,6 @@ __metadata:
languageName: node
linkType: hard
-"process-warning@npm:^4.0.0":
- version: 4.0.0
- resolution: "process-warning@npm:4.0.0"
- checksum: 10c0/5312a72b69d37a1b82ad03f3dfa0090dab3804a8fd995d06c28e3c002852bd82f5584217d9f4a3f197892bb2afc22d57e2c662c7e906b5abb48c0380c7b0880d
- languageName: node
- linkType: hard
-
"process-warning@npm:^5.0.0":
version: 5.0.0
resolution: "process-warning@npm:5.0.0"
@@ -10218,6 +10926,16 @@ __metadata:
languageName: node
linkType: hard
+"prompts@npm:^2.4.0":
+ version: 2.4.2
+ resolution: "prompts@npm:2.4.2"
+ dependencies:
+ kleur: "npm:^3.0.3"
+ sisteransi: "npm:^1.0.5"
+ checksum: 10c0/16f1ac2977b19fe2cf53f8411cc98db7a3c8b115c479b2ca5c82b5527cd937aa405fa04f9a5960abeb9daef53191b53b4d13e35c1f5d50e8718c76917c5f1ea4
+ languageName: node
+ linkType: hard
+
"prop-types@npm:^15.5.10, prop-types@npm:^15.5.4, prop-types@npm:^15.6.0, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1":
version: 15.8.1
resolution: "prop-types@npm:15.8.1"
@@ -10321,6 +11039,44 @@ __metadata:
languageName: node
linkType: hard
+"react-docgen-typescript@npm:^2.2.2":
+ version: 2.2.2
+ resolution: "react-docgen-typescript@npm:2.2.2"
+ peerDependencies:
+ typescript: ">= 4.3.x"
+ checksum: 10c0/d31a061a21b5d4b67d4af7bc742541fd9e16254bd32861cd29c52565bc2175f40421a3550d52b6a6b0d0478e7cc408558eb0060a0bdd2957b02cfceeb0ee1e88
+ languageName: node
+ linkType: hard
+
+"react-docgen@npm:^8.0.0":
+ version: 8.0.0
+ resolution: "react-docgen@npm:8.0.0"
+ dependencies:
+ "@babel/core": "npm:^7.18.9"
+ "@babel/traverse": "npm:^7.18.9"
+ "@babel/types": "npm:^7.18.9"
+ "@types/babel__core": "npm:^7.18.0"
+ "@types/babel__traverse": "npm:^7.18.0"
+ "@types/doctrine": "npm:^0.0.9"
+ "@types/resolve": "npm:^1.20.2"
+ doctrine: "npm:^3.0.0"
+ resolve: "npm:^1.22.1"
+ strip-indent: "npm:^4.0.0"
+ checksum: 10c0/2e3c187bed074895ac3420910129f23b30fe8f7faf984cbf6e210dd3914fa03a910583c5a4c4564edbef7461c37dfd6cd967c3bfc5d83c6f8c02cacedda38014
+ languageName: node
+ linkType: hard
+
+"react-dom@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0":
+ version: 19.1.0
+ resolution: "react-dom@npm:19.1.0"
+ dependencies:
+ scheduler: "npm:^0.26.0"
+ peerDependencies:
+ react: ^19.1.0
+ checksum: 10c0/3e26e89bb6c67c9a6aa86cb888c7a7f8258f2e347a6d2a15299c17eb16e04c19194e3452bc3255bd34000a61e45e2cb51e46292392340432f133e5a5d2dfb5fc
+ languageName: node
+ linkType: hard
+
"react-dom@npm:^18.2.0":
version: 18.3.1
resolution: "react-dom@npm:18.3.1"
@@ -10712,6 +11468,13 @@ __metadata:
languageName: node
linkType: hard
+"react@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0":
+ version: 19.1.0
+ resolution: "react@npm:19.1.0"
+ checksum: 10c0/530fb9a62237d54137a13d2cfb67a7db6a2156faed43eecc423f4713d9b20c6f2728b026b45e28fcd72e8eadb9e9ed4b089e99f5e295d2f0ad3134251bdd3698
+ languageName: node
+ linkType: hard
+
"react@npm:^18.2.0":
version: 18.3.1
resolution: "react@npm:18.3.1"
@@ -10735,6 +11498,29 @@ __metadata:
languageName: node
linkType: hard
+"recast@npm:^0.23.5":
+ version: 0.23.11
+ resolution: "recast@npm:0.23.11"
+ dependencies:
+ ast-types: "npm:^0.16.1"
+ esprima: "npm:~4.0.0"
+ source-map: "npm:~0.6.1"
+ tiny-invariant: "npm:^1.3.3"
+ tslib: "npm:^2.0.1"
+ checksum: 10c0/45b520a8f0868a5a24ecde495be9de3c48e69a54295d82a7331106554b75cfba75d16c909959d056e9ceed47a1be5e061e2db8b9ecbcd6ba44c2f3ef9a47bd18
+ languageName: node
+ linkType: hard
+
+"redent@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "redent@npm:3.0.0"
+ dependencies:
+ indent-string: "npm:^4.0.0"
+ strip-indent: "npm:^3.0.0"
+ checksum: 10c0/d64a6b5c0b50eb3ddce3ab770f866658a2b9998c678f797919ceb1b586bab9259b311407280bd80b804e2a7c7539b19238ae6a2a20c843f1a7fcff21d48c2eae
+ languageName: node
+ linkType: hard
+
"redis-errors@npm:^1.0.0, redis-errors@npm:^1.2.0":
version: 1.2.0
resolution: "redis-errors@npm:1.2.0"
@@ -10940,7 +11726,7 @@ __metadata:
languageName: node
linkType: hard
-"resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4":
+"resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4, resolve@npm:^1.22.8":
version: 1.22.10
resolution: "resolve@npm:1.22.10"
dependencies:
@@ -10966,7 +11752,7 @@ __metadata:
languageName: node
linkType: hard
-"resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin":
+"resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin":
version: 1.22.10
resolution: "resolve@patch:resolve@npm%3A1.22.10#optional!builtin::version=1.22.10&hash=c3c19d"
dependencies:
@@ -11033,15 +11819,15 @@ __metadata:
linkType: hard
"rollup-plugin-visualizer@npm:^6.0.0":
- version: 6.0.1
- resolution: "rollup-plugin-visualizer@npm:6.0.1"
+ version: 6.0.3
+ resolution: "rollup-plugin-visualizer@npm:6.0.3"
dependencies:
open: "npm:^8.0.0"
picomatch: "npm:^4.0.2"
source-map: "npm:^0.7.4"
yargs: "npm:^17.5.1"
peerDependencies:
- rolldown: 1.x
+ rolldown: 1.x || ^1.0.0-beta
rollup: 2.x || 3.x || 4.x
peerDependenciesMeta:
rolldown:
@@ -11050,7 +11836,7 @@ __metadata:
optional: true
bin:
rollup-plugin-visualizer: dist/bin/cli.js
- checksum: 10c0/c938224b2027b3f490569c439d400b8fc80a0ad07b58720771b23623efe1efc7eb61f3de4506fb4520ce95ef4ea3c93153c31130559e017606c046c052690d1c
+ checksum: 10c0/595d68936a6338744e8facd165fceedf7f2ebedc44863e640e725198001ed62948cc4a5d8403aa74e679de92957e4def3b1dffc4a9f8de71e4245929566553a3
languageName: node
linkType: hard
@@ -11215,8 +12001,8 @@ __metadata:
linkType: hard
"sass@npm:^1.62.1":
- version: 1.89.0
- resolution: "sass@npm:1.89.0"
+ version: 1.89.2
+ resolution: "sass@npm:1.89.2"
dependencies:
"@parcel/watcher": "npm:^2.4.1"
chokidar: "npm:^4.0.0"
@@ -11227,7 +12013,7 @@ __metadata:
optional: true
bin:
sass: sass.js
- checksum: 10c0/8e31b48c5e0abd9d437edb201919a82863f605ace1c1536ccdc6b3133937a70fe29b92ad536af632ecae2f140733931e4ec971e1bfbf8b835c54d392b28331df
+ checksum: 10c0/752ccc7581b0c6395f63918116c20924e99943a86d79e94f5c4a0d41b1e981fe1f0ecd1ee82fff21496f81dbc91f68fb35a498166562ec8ec53e7aad7c3dbd9d
languageName: node
linkType: hard
@@ -11249,6 +12035,13 @@ __metadata:
languageName: node
linkType: hard
+"scheduler@npm:^0.26.0":
+ version: 0.26.0
+ resolution: "scheduler@npm:0.26.0"
+ checksum: 10c0/5b8d5bfddaae3513410eda54f2268e98a376a429931921a81b5c3a2873aab7ca4d775a8caac5498f8cbc7d0daeab947cf923dbd8e215d61671f9f4e392d34356
+ languageName: node
+ linkType: hard
+
"schema-utils@npm:^4.3.0, schema-utils@npm:^4.3.2":
version: 4.3.2
resolution: "schema-utils@npm:4.3.2"
@@ -11287,12 +12080,12 @@ __metadata:
languageName: node
linkType: hard
-"semver@npm:^7.3.5, semver@npm:^7.6.0, semver@npm:^7.6.3, semver@npm:^7.7.1":
- version: 7.7.1
- resolution: "semver@npm:7.7.1"
+"semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.6.0, semver@npm:^7.6.2, semver@npm:^7.6.3, semver@npm:^7.7.1":
+ version: 7.7.2
+ resolution: "semver@npm:7.7.2"
bin:
semver: bin/semver.js
- checksum: 10c0/fd603a6fb9c399c6054015433051bdbe7b99a940a8fb44b85c2b524c4004b023d7928d47cb22154f8d054ea7ee8597f586605e05b52047f048278e4ac56ae958
+ checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea
languageName: node
linkType: hard
@@ -11467,6 +12260,24 @@ __metadata:
languageName: node
linkType: hard
+"sirv@npm:^3.0.1":
+ version: 3.0.1
+ resolution: "sirv@npm:3.0.1"
+ dependencies:
+ "@polka/url": "npm:^1.0.0-next.24"
+ mrmime: "npm:^2.0.0"
+ totalist: "npm:^3.0.0"
+ checksum: 10c0/7cf64b28daa69b15f77b38b0efdd02c007b72bb3ec5f107b208ebf59f01b174ef63a1db3aca16d2df925501831f4c209be6ece3302b98765919ef5088b45bf80
+ languageName: node
+ linkType: hard
+
+"sisteransi@npm:^1.0.5":
+ version: 1.0.5
+ resolution: "sisteransi@npm:1.0.5"
+ checksum: 10c0/230ac975cca485b7f6fe2b96a711aa62a6a26ead3e6fb8ba17c5a00d61b8bed0d7adc21f5626b70d7c33c62ff4e63933017a6462942c719d1980bb0b1207ad46
+ languageName: node
+ linkType: hard
+
"slash@npm:^3.0.0":
version: 3.0.0
resolution: "slash@npm:3.0.0"
@@ -11566,7 +12377,7 @@ __metadata:
languageName: node
linkType: hard
-"source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.1":
+"source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.1, source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1":
version: 1.2.1
resolution: "source-map-js@npm:1.2.1"
checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf
@@ -11597,7 +12408,7 @@ __metadata:
languageName: node
linkType: hard
-"source-map@npm:^0.6.0":
+"source-map@npm:^0.6.0, source-map@npm:~0.6.1":
version: 0.6.1
resolution: "source-map@npm:0.6.1"
checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011
@@ -11746,6 +12557,32 @@ __metadata:
languageName: node
linkType: hard
+"storybook@npm:^9.0.4":
+ version: 9.0.4
+ resolution: "storybook@npm:9.0.4"
+ dependencies:
+ "@storybook/global": "npm:^5.0.0"
+ "@testing-library/jest-dom": "npm:^6.6.3"
+ "@testing-library/user-event": "npm:^14.6.1"
+ "@vitest/expect": "npm:3.0.9"
+ "@vitest/spy": "npm:3.0.9"
+ better-opn: "npm:^3.0.2"
+ esbuild: "npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0"
+ esbuild-register: "npm:^3.5.0"
+ recast: "npm:^0.23.5"
+ semver: "npm:^7.6.2"
+ ws: "npm:^8.18.0"
+ peerDependencies:
+ prettier: ^2 || ^3
+ peerDependenciesMeta:
+ prettier:
+ optional: true
+ bin:
+ storybook: ./bin/index.cjs
+ checksum: 10c0/52aa44ac9ba73bf3ab2718669faa02c515ef91243f7d39bcac815efc1b72ac2f2c3b1ea4586ec37beb8f6044c8c5b1c9e2d46405a8832974f3cee37f4b3c3821
+ languageName: node
+ linkType: hard
+
"string-argv@npm:^0.3.2":
version: 0.3.2
resolution: "string-argv@npm:0.3.2"
@@ -11918,6 +12755,24 @@ __metadata:
languageName: node
linkType: hard
+"strip-indent@npm:^3.0.0":
+ version: 3.0.0
+ resolution: "strip-indent@npm:3.0.0"
+ dependencies:
+ min-indent: "npm:^1.0.0"
+ checksum: 10c0/ae0deaf41c8d1001c5d4fbe16cb553865c1863da4fae036683b474fa926af9fc121e155cb3fc57a68262b2ae7d5b8420aa752c97a6428c315d00efe2a3875679
+ languageName: node
+ linkType: hard
+
+"strip-indent@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "strip-indent@npm:4.0.0"
+ dependencies:
+ min-indent: "npm:^1.0.1"
+ checksum: 10c0/6b1fb4e22056867f5c9e7a6f3f45922d9a2436cac758607d58aeaac0d3b16ec40b1c43317de7900f1b8dd7a4107352fa47fb960f2c23566538c51e8585c8870e
+ languageName: node
+ linkType: hard
+
"strip-json-comments@npm:^3.1.1":
version: 3.1.1
resolution: "strip-json-comments@npm:3.1.1"
@@ -12279,6 +13134,17 @@ __metadata:
languageName: node
linkType: hard
+"test-exclude@npm:^7.0.1":
+ version: 7.0.1
+ resolution: "test-exclude@npm:7.0.1"
+ dependencies:
+ "@istanbuljs/schema": "npm:^0.1.2"
+ glob: "npm:^10.4.1"
+ minimatch: "npm:^9.0.4"
+ checksum: 10c0/6d67b9af4336a2e12b26a68c83308c7863534c65f27ed4ff7068a56f5a58f7ac703e8fc80f698a19bb154fd8f705cdf7ec347d9512b2c522c737269507e7b263
+ languageName: node
+ linkType: hard
+
"thread-stream@npm:^3.0.0":
version: 3.0.0
resolution: "thread-stream@npm:3.0.0"
@@ -12288,10 +13154,10 @@ __metadata:
languageName: node
linkType: hard
-"tiny-invariant@npm:^1.0.2":
- version: 1.3.1
- resolution: "tiny-invariant@npm:1.3.1"
- checksum: 10c0/5b87c1d52847d9452b60d0dcb77011b459044e0361ca8253bfe7b43d6288106e12af926adb709a6fc28900e3864349b91dad9a4ac93c39aa15f360b26c2ff4db
+"tiny-invariant@npm:^1.0.2, tiny-invariant@npm:^1.3.3":
+ version: 1.3.3
+ resolution: "tiny-invariant@npm:1.3.3"
+ checksum: 10c0/65af4a07324b591a059b35269cd696aba21bef2107f29b9f5894d83cc143159a204b299553435b03874ebb5b94d019afa8b8eff241c8a4cfee95872c2e1c1c4a
languageName: node
linkType: hard
@@ -12323,20 +13189,20 @@ __metadata:
languageName: node
linkType: hard
-"tinyglobby@npm:^0.2.10, tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.13":
- version: 0.2.13
- resolution: "tinyglobby@npm:0.2.13"
+"tinyglobby@npm:^0.2.10, tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.13, tinyglobby@npm:^0.2.14":
+ version: 0.2.14
+ resolution: "tinyglobby@npm:0.2.14"
dependencies:
fdir: "npm:^6.4.4"
picomatch: "npm:^4.0.2"
- checksum: 10c0/ef07dfaa7b26936601d3f6d999f7928a4d1c6234c5eb36896bb88681947c0d459b7ebe797022400e555fe4b894db06e922b95d0ce60cb05fd827a0a66326b18c
+ checksum: 10c0/f789ed6c924287a9b7d3612056ed0cda67306cd2c80c249fd280cf1504742b12583a2089b61f4abbd24605f390809017240e250241f09938054c9b363e51c0a6
languageName: node
linkType: hard
-"tinypool@npm:^1.0.2":
- version: 1.0.2
- resolution: "tinypool@npm:1.0.2"
- checksum: 10c0/31ac184c0ff1cf9a074741254fe9ea6de95026749eb2b8ec6fd2b9d8ca94abdccda731f8e102e7f32e72ed3b36d32c6975fd5f5523df3f1b6de6c3d8dfd95e63
+"tinypool@npm:^1.1.0":
+ version: 1.1.0
+ resolution: "tinypool@npm:1.1.0"
+ checksum: 10c0/deb6bde5e3d85d4ba043806c66f43fb5b649716312a47b52761a83668ffc71cd0ea4e24254c1b02a3702e5c27e02605f0189a1460f6284a5930a08bd0c06435c
languageName: node
linkType: hard
@@ -12354,6 +13220,13 @@ __metadata:
languageName: node
linkType: hard
+"tinyspy@npm:^4.0.3":
+ version: 4.0.3
+ resolution: "tinyspy@npm:4.0.3"
+ checksum: 10c0/0a92a18b5350945cc8a1da3a22c9ad9f4e2945df80aaa0c43e1b3a3cfb64d8501e607ebf0305e048e3c3d3e0e7f8eb10cea27dc17c21effb73e66c4a3be36373
+ languageName: node
+ linkType: hard
+
"tldts-core@npm:^6.1.47":
version: 6.1.47
resolution: "tldts-core@npm:6.1.47"
@@ -12388,6 +13261,13 @@ __metadata:
languageName: node
linkType: hard
+"totalist@npm:^3.0.0":
+ version: 3.0.1
+ resolution: "totalist@npm:3.0.1"
+ checksum: 10c0/4bb1fadb69c3edbef91c73ebef9d25b33bbf69afe1e37ce544d5f7d13854cda15e47132f3e0dc4cafe300ddb8578c77c50a65004d8b6e97e77934a69aa924863
+ languageName: node
+ linkType: hard
+
"tough-cookie@npm:^5.1.1":
version: 5.1.2
resolution: "tough-cookie@npm:5.1.2"
@@ -12429,12 +13309,19 @@ __metadata:
languageName: node
linkType: hard
-"ts-api-utils@npm:^2.0.1":
- version: 2.0.1
- resolution: "ts-api-utils@npm:2.0.1"
+"ts-api-utils@npm:^2.0.1, ts-api-utils@npm:^2.1.0":
+ version: 2.1.0
+ resolution: "ts-api-utils@npm:2.1.0"
peerDependencies:
typescript: ">=4.8.4"
- checksum: 10c0/23fd56a958b332cac00150a652e4c84730df30571bd2faa1ba6d7b511356d1a61656621492bb6c7f15dd6e18847a1408357a0e406671d358115369a17f5bfedd
+ checksum: 10c0/9806a38adea2db0f6aa217ccc6bc9c391ddba338a9fe3080676d0d50ed806d305bb90e8cef0276e793d28c8a929f400abb184ddd7ff83a416959c0f4d2ce754f
+ languageName: node
+ linkType: hard
+
+"ts-dedent@npm:^2.0.0, ts-dedent@npm:^2.2.0":
+ version: 2.2.0
+ resolution: "ts-dedent@npm:2.2.0"
+ checksum: 10c0/175adea838468cc2ff7d5e97f970dcb798bbcb623f29c6088cb21aa2880d207c5784be81ab1741f56b9ac37840cbaba0c0d79f7f8b67ffe61c02634cafa5c303
languageName: node
linkType: hard
@@ -12464,7 +13351,18 @@ __metadata:
languageName: node
linkType: hard
-"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.4.0, tslib@npm:^2.6.2, tslib@npm:^2.8.0":
+"tsconfig-paths@npm:^4.2.0":
+ version: 4.2.0
+ resolution: "tsconfig-paths@npm:4.2.0"
+ dependencies:
+ json5: "npm:^2.2.2"
+ minimist: "npm:^1.2.6"
+ strip-bom: "npm:^3.0.0"
+ checksum: 10c0/09a5877402d082bb1134930c10249edeebc0211f36150c35e1c542e5b91f1047b1ccf7da1e59babca1ef1f014c525510f4f870de7c9bda470c73bb4e2721b3ea
+ languageName: node
+ linkType: hard
+
+"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.4.0, tslib@npm:^2.6.2, tslib@npm:^2.8.0":
version: 2.8.1
resolution: "tslib@npm:2.8.1"
checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62
@@ -12737,6 +13635,16 @@ __metadata:
languageName: node
linkType: hard
+"unplugin@npm:^1.3.1":
+ version: 1.16.1
+ resolution: "unplugin@npm:1.16.1"
+ dependencies:
+ acorn: "npm:^8.14.0"
+ webpack-virtual-modules: "npm:^0.6.2"
+ checksum: 10c0/dd5f8c5727d0135847da73cf03fb199107f1acf458167034886fda3405737dab871ad3926431b4f70e1e82cdac482ac1383cea4019d782a68515c8e3e611b6cc
+ languageName: node
+ linkType: hard
+
"unrs-resolver@npm:^1.3.2":
version: 1.3.2
resolution: "unrs-resolver@npm:1.3.2"
@@ -12831,11 +13739,11 @@ __metadata:
linkType: hard
"use-debounce@npm:^10.0.0":
- version: 10.0.4
- resolution: "use-debounce@npm:10.0.4"
+ version: 10.0.5
+ resolution: "use-debounce@npm:10.0.5"
peerDependencies:
react: "*"
- checksum: 10c0/73494fc44b2bd58a7ec799a528fc20077c45fe2e94fedff6dcd88d136f7a39f417d77f584d5613aac615ed32aeb2ea393797ae1f7d5b2645eab57cb497a6d0cb
+ checksum: 10c0/8232538aa2e2b1252716ef2918204709f16bd53661b56a3a29ad790bf61bc7cbf3bb1126d888408db276a2b05fa6a941e9452149be14bfe438aa22054ccf4e4a
languageName: node
linkType: hard
@@ -12921,18 +13829,18 @@ __metadata:
languageName: node
linkType: hard
-"vite-node@npm:3.1.3":
- version: 3.1.3
- resolution: "vite-node@npm:3.1.3"
+"vite-node@npm:3.2.1":
+ version: 3.2.1
+ resolution: "vite-node@npm:3.2.1"
dependencies:
cac: "npm:^6.7.14"
- debug: "npm:^4.4.0"
+ debug: "npm:^4.4.1"
es-module-lexer: "npm:^1.7.0"
pathe: "npm:^2.0.3"
- vite: "npm:^5.0.0 || ^6.0.0"
+ vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0"
bin:
vite-node: vite-node.mjs
- checksum: 10c0/d69a1e52361bc0af22d1178db61674ef768cfd3c5610733794bb1e7a36af113da287dd89662a1ad57fd4f6c3360ca99678f5428ba837f239df4091d7891f2e4c
+ checksum: 10c0/e196bc4660baed4f18530b43ce896017adbe480c329f03ac72d2237788ddeaca50904e9e9a4fb8e65300d088ff2737227a00c0e3bae697067acebcd8f08f7faa
languageName: node
linkType: hard
@@ -13067,7 +13975,7 @@ __metadata:
languageName: node
linkType: hard
-"vite@npm:^5.0.0 || ^6.0.0, vite@npm:^6.3.5":
+"vite@npm:^6.3.5":
version: 6.3.5
resolution: "vite@npm:6.3.5"
dependencies:
@@ -13122,37 +14030,39 @@ __metadata:
languageName: node
linkType: hard
-"vitest@npm:^3.1.3":
- version: 3.1.3
- resolution: "vitest@npm:3.1.3"
+"vitest@npm:^3.2.1":
+ version: 3.2.1
+ resolution: "vitest@npm:3.2.1"
dependencies:
- "@vitest/expect": "npm:3.1.3"
- "@vitest/mocker": "npm:3.1.3"
- "@vitest/pretty-format": "npm:^3.1.3"
- "@vitest/runner": "npm:3.1.3"
- "@vitest/snapshot": "npm:3.1.3"
- "@vitest/spy": "npm:3.1.3"
- "@vitest/utils": "npm:3.1.3"
+ "@types/chai": "npm:^5.2.2"
+ "@vitest/expect": "npm:3.2.1"
+ "@vitest/mocker": "npm:3.2.1"
+ "@vitest/pretty-format": "npm:^3.2.1"
+ "@vitest/runner": "npm:3.2.1"
+ "@vitest/snapshot": "npm:3.2.1"
+ "@vitest/spy": "npm:3.2.1"
+ "@vitest/utils": "npm:3.2.1"
chai: "npm:^5.2.0"
- debug: "npm:^4.4.0"
+ debug: "npm:^4.4.1"
expect-type: "npm:^1.2.1"
magic-string: "npm:^0.30.17"
pathe: "npm:^2.0.3"
+ picomatch: "npm:^4.0.2"
std-env: "npm:^3.9.0"
tinybench: "npm:^2.9.0"
tinyexec: "npm:^0.3.2"
- tinyglobby: "npm:^0.2.13"
- tinypool: "npm:^1.0.2"
+ tinyglobby: "npm:^0.2.14"
+ tinypool: "npm:^1.1.0"
tinyrainbow: "npm:^2.0.0"
- vite: "npm:^5.0.0 || ^6.0.0"
- vite-node: "npm:3.1.3"
+ vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0"
+ vite-node: "npm:3.2.1"
why-is-node-running: "npm:^2.3.0"
peerDependencies:
"@edge-runtime/vm": "*"
"@types/debug": ^4.1.12
"@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0
- "@vitest/browser": 3.1.3
- "@vitest/ui": 3.1.3
+ "@vitest/browser": 3.2.1
+ "@vitest/ui": 3.2.1
happy-dom: "*"
jsdom: "*"
peerDependenciesMeta:
@@ -13172,7 +14082,7 @@ __metadata:
optional: true
bin:
vitest: vitest.mjs
- checksum: 10c0/954b3579a2d925606df7f78e367ae64eab52c8c5ba2bb2fed94d335a06c910202a4ce080bb02d8148c8b4782488c6d229e963617be8d0c7da96a1c944dd291d7
+ checksum: 10c0/1f4128f93fff708fa5bd7d1547a0877c4266466f0f91f5e1dd5d7f09267a0c171cf87c83acd86ebd53e561aa2bcef1311e984b8205370c7f596e6ff5a9c8cd6b
languageName: node
linkType: hard
@@ -13260,9 +14170,16 @@ __metadata:
linkType: hard
"webpack-sources@npm:^3.2.3":
- version: 3.3.0
- resolution: "webpack-sources@npm:3.3.0"
- checksum: 10c0/d9366eef6db95dffe154b6ffb50424c34f42d51ba3441efe9134642d13b0ccdf43897bdc7742f9f58eb4e834378f7ff67596137d3fca21ce34fe7cee8796e97a
+ version: 3.3.2
+ resolution: "webpack-sources@npm:3.3.2"
+ checksum: 10c0/b5308d8acba4c7c6710b6df77187b274800afe0856c1508cba8aa310304558634e745b7eac4991ea086175ea6da3c64d11d958cf508980e6cb7506aa5983913e
+ languageName: node
+ linkType: hard
+
+"webpack-virtual-modules@npm:^0.6.2":
+ version: 0.6.2
+ resolution: "webpack-virtual-modules@npm:0.6.2"
+ checksum: 10c0/5ffbddf0e84bf1562ff86cf6fcf039c74edf09d78358a6904a09bbd4484e8bb6812dc385fe14330b715031892dcd8423f7a88278b57c9f5002c84c2860179add
languageName: node
linkType: hard
@@ -13702,7 +14619,7 @@ __metadata:
languageName: node
linkType: hard
-"ws@npm:^8.12.1, ws@npm:^8.18.0":
+"ws@npm:^8.12.1, ws@npm:^8.18.0, ws@npm:^8.18.2":
version: 8.18.2
resolution: "ws@npm:8.18.2"
peerDependencies: