+
-
- Quoting not allowed by the author.
-
+ {_(unfulfilledText[unfulfilledState])}
);
+ }
+
const Parent = q.native ? Fragment : LazyShazam;
return (
@@ -3951,7 +3956,6 @@ const QuoteStatuses = memo(({ id, instance, level = 0 }) => {
size="s"
quoted={level + 1}
enableCommentHint
- showFilteredHidden
/>
diff --git a/src/locales/en.po b/src/locales/en.po
index 47eeca6ef8..73842b857d 100644
--- a/src/locales/en.po
+++ b/src/locales/en.po
@@ -34,7 +34,7 @@ msgstr ""
#: src/components/account-block.jsx:170
#: src/components/account-info.jsx:715
-#: src/components/status.jsx:601
+#: src/components/status.jsx:590
msgid "Group"
msgstr ""
@@ -111,11 +111,11 @@ msgstr ""
#: src/components/compose.jsx:2792
#: src/components/media-alt-modal.jsx:55
#: src/components/media-modal.jsx:363
-#: src/components/status.jsx:1847
-#: src/components/status.jsx:1864
-#: src/components/status.jsx:1989
-#: src/components/status.jsx:2610
-#: src/components/status.jsx:2613
+#: src/components/status.jsx:1836
+#: src/components/status.jsx:1853
+#: src/components/status.jsx:1978
+#: src/components/status.jsx:2599
+#: src/components/status.jsx:2602
#: src/pages/account-statuses.jsx:531
#: src/pages/accounts.jsx:118
#: src/pages/hashtag.jsx:203
@@ -203,7 +203,7 @@ msgid "Original"
msgstr ""
#: src/components/account-info.jsx:946
-#: src/components/status.jsx:2394
+#: src/components/status.jsx:2383
#: src/pages/catchup.jsx:71
#: src/pages/catchup.jsx:1448
#: src/pages/catchup.jsx:2061
@@ -329,30 +329,30 @@ msgid "Add/Remove from Lists"
msgstr ""
#: src/components/account-info.jsx:1490
-#: src/components/status.jsx:1269
+#: src/components/status.jsx:1258
msgid "Link copied"
msgstr ""
#: src/components/account-info.jsx:1493
-#: src/components/status.jsx:1272
+#: src/components/status.jsx:1261
msgid "Unable to copy link"
msgstr ""
#: src/components/account-info.jsx:1499
#: src/components/shortcuts-settings.jsx:1059
-#: src/components/status.jsx:1278
-#: src/components/status.jsx:3388
+#: src/components/status.jsx:1267
+#: src/components/status.jsx:3377
msgid "Copy"
msgstr ""
#: src/components/account-info.jsx:1514
#: src/components/shortcuts-settings.jsx:1077
-#: src/components/status.jsx:1294
+#: src/components/status.jsx:1283
msgid "Sharing doesn't seem to work."
msgstr ""
#: src/components/account-info.jsx:1520
-#: src/components/status.jsx:1300
+#: src/components/status.jsx:1289
msgid "Share…"
msgstr ""
@@ -466,9 +466,9 @@ msgstr ""
#: src/components/shortcuts-settings.jsx:230
#: src/components/shortcuts-settings.jsx:583
#: src/components/shortcuts-settings.jsx:783
-#: src/components/status.jsx:3112
-#: src/components/status.jsx:3352
-#: src/components/status.jsx:3861
+#: src/components/status.jsx:3101
+#: src/components/status.jsx:3341
+#: src/components/status.jsx:3850
#: src/pages/accounts.jsx:45
#: src/pages/catchup.jsx:1584
#: src/pages/filters.jsx:225
@@ -718,7 +718,7 @@ msgid "Attachment #{i} failed"
msgstr "Attachment #{i} failed"
#: src/components/compose.jsx:1221
-#: src/components/status.jsx:2177
+#: src/components/status.jsx:2166
#: src/components/timeline.jsx:1023
msgid "Content warning"
msgstr ""
@@ -754,7 +754,7 @@ msgstr ""
#: src/components/compose.jsx:1288
#: src/components/status.jsx:100
-#: src/components/status.jsx:2053
+#: src/components/status.jsx:2042
msgid "Private mention"
msgstr ""
@@ -791,10 +791,10 @@ msgstr "Schedule"
#: src/components/compose.jsx:1677
#: src/components/keyboard-shortcuts-help.jsx:155
-#: src/components/status.jsx:1041
-#: src/components/status.jsx:1827
-#: src/components/status.jsx:1828
-#: src/components/status.jsx:2514
+#: src/components/status.jsx:1030
+#: src/components/status.jsx:1816
+#: src/components/status.jsx:1817
+#: src/components/status.jsx:2503
msgid "Reply"
msgstr ""
@@ -1016,7 +1016,7 @@ msgstr ""
#: src/components/drafts.jsx:128
#: src/components/list-add-edit.jsx:188
-#: src/components/status.jsx:1444
+#: src/components/status.jsx:1433
#: src/pages/filters.jsx:603
#: src/pages/scheduled-posts.jsx:369
msgid "Delete…"
@@ -1225,10 +1225,10 @@ msgid "<0>l0> or <1>f1>"
msgstr ""
#: src/components/keyboard-shortcuts-help.jsx:176
-#: src/components/status.jsx:1049
-#: src/components/status.jsx:2541
-#: src/components/status.jsx:2564
-#: src/components/status.jsx:2565
+#: src/components/status.jsx:1038
+#: src/components/status.jsx:2530
+#: src/components/status.jsx:2553
+#: src/components/status.jsx:2554
msgid "Boost"
msgstr ""
@@ -1237,9 +1237,9 @@ msgid "<0>Shift0> + <1>b1>"
msgstr ""
#: src/components/keyboard-shortcuts-help.jsx:184
-#: src/components/status.jsx:1112
-#: src/components/status.jsx:2589
-#: src/components/status.jsx:2590
+#: src/components/status.jsx:1101
+#: src/components/status.jsx:2578
+#: src/components/status.jsx:2579
msgid "Bookmark"
msgstr ""
@@ -1304,14 +1304,14 @@ msgid "Media description"
msgstr ""
#: src/components/media-alt-modal.jsx:67
-#: src/components/status.jsx:1155
-#: src/components/status.jsx:1164
+#: src/components/status.jsx:1144
+#: src/components/status.jsx:1153
#: src/components/translation-block.jsx:239
msgid "Translate"
msgstr ""
#: src/components/media-alt-modal.jsx:78
-#: src/components/status.jsx:1183
+#: src/components/status.jsx:1172
msgid "Speak"
msgstr ""
@@ -1348,9 +1348,9 @@ msgid "Filtered: {filterTitleStr}"
msgstr ""
#: src/components/media-post.jsx:133
-#: src/components/status.jsx:3691
-#: src/components/status.jsx:3787
-#: src/components/status.jsx:3865
+#: src/components/status.jsx:3680
+#: src/components/status.jsx:3776
+#: src/components/status.jsx:3854
#: src/components/timeline.jsx:1012
#: src/pages/catchup.jsx:75
#: src/pages/catchup.jsx:1880
@@ -1662,8 +1662,8 @@ msgid "[Unknown notification type: {type}]"
msgstr ""
#: src/components/notification.jsx:451
-#: src/components/status.jsx:1126
-#: src/components/status.jsx:1136
+#: src/components/status.jsx:1115
+#: src/components/status.jsx:1125
msgid "Boosted/Liked by…"
msgstr ""
@@ -1993,7 +1993,7 @@ msgid "Move down"
msgstr ""
#: src/components/shortcuts-settings.jsx:379
-#: src/components/status.jsx:1406
+#: src/components/status.jsx:1395
#: src/pages/list.jsx:195
msgid "Edit"
msgstr ""
@@ -2192,345 +2192,346 @@ msgstr ""
msgid "Import/export settings from/to instance server (Very experimental)"
msgstr ""
-#: src/components/status.jsx:508
-msgid "Post hidden by your filters"
-msgstr "Post hidden by your filters"
-
-#: src/components/status.jsx:625
+#: src/components/status.jsx:614
msgid "<0/> <1>boosted1>"
msgstr ""
-#: src/components/status.jsx:724
+#: src/components/status.jsx:713
msgid "Sorry, your current logged-in instance can't interact with this post from another instance."
msgstr ""
#. placeholder {0}: username || acct
-#: src/components/status.jsx:878
+#: src/components/status.jsx:867
msgid "Unliked @{0}'s post"
msgstr ""
#. placeholder {0}: username || acct
-#: src/components/status.jsx:879
+#: src/components/status.jsx:868
msgid "Liked @{0}'s post"
msgstr "Liked @{0}'s post"
#. placeholder {0}: username || acct
-#: src/components/status.jsx:918
+#: src/components/status.jsx:907
msgid "Unbookmarked @{0}'s post"
msgstr "Unbookmarked @{0}'s post"
#. placeholder {0}: username || acct
-#: src/components/status.jsx:919
+#: src/components/status.jsx:908
msgid "Bookmarked @{0}'s post"
msgstr "Bookmarked @{0}'s post"
-#: src/components/status.jsx:1018
+#: src/components/status.jsx:1007
msgid "Some media have no descriptions."
msgstr ""
#. placeholder {0}: rtf.format(-statusMonthsAgo, 'month')
-#: src/components/status.jsx:1025
+#: src/components/status.jsx:1014
msgid "Old post (<0>{0}0>)"
msgstr ""
-#: src/components/status.jsx:1049
-#: src/components/status.jsx:1089
-#: src/components/status.jsx:2541
-#: src/components/status.jsx:2564
+#: src/components/status.jsx:1038
+#: src/components/status.jsx:1078
+#: src/components/status.jsx:2530
+#: src/components/status.jsx:2553
msgid "Unboost"
msgstr ""
-#: src/components/status.jsx:1065
-#: src/components/status.jsx:2556
+#: src/components/status.jsx:1054
+#: src/components/status.jsx:2545
msgid "Quote"
msgstr ""
#. placeholder {0}: username || acct
-#: src/components/status.jsx:1077
-#: src/components/status.jsx:1543
+#: src/components/status.jsx:1066
+#: src/components/status.jsx:1532
msgid "Unboosted @{0}'s post"
msgstr "Unboosted @{0}'s post"
#. placeholder {0}: username || acct
-#: src/components/status.jsx:1078
-#: src/components/status.jsx:1544
+#: src/components/status.jsx:1067
+#: src/components/status.jsx:1533
msgid "Boosted @{0}'s post"
msgstr "Boosted @{0}'s post"
-#: src/components/status.jsx:1090
+#: src/components/status.jsx:1079
msgid "Boost…"
msgstr ""
-#: src/components/status.jsx:1102
-#: src/components/status.jsx:1837
-#: src/components/status.jsx:2577
+#: src/components/status.jsx:1091
+#: src/components/status.jsx:1826
+#: src/components/status.jsx:2566
msgid "Unlike"
msgstr ""
-#: src/components/status.jsx:1103
-#: src/components/status.jsx:1837
-#: src/components/status.jsx:1838
-#: src/components/status.jsx:2577
-#: src/components/status.jsx:2578
+#: src/components/status.jsx:1092
+#: src/components/status.jsx:1826
+#: src/components/status.jsx:1827
+#: src/components/status.jsx:2566
+#: src/components/status.jsx:2567
msgid "Like"
msgstr ""
-#: src/components/status.jsx:1112
-#: src/components/status.jsx:2589
+#: src/components/status.jsx:1101
+#: src/components/status.jsx:2578
msgid "Unbookmark"
msgstr ""
-#: src/components/status.jsx:1195
+#: src/components/status.jsx:1184
msgid "Post text copied"
msgstr "Post text copied"
-#: src/components/status.jsx:1198
+#: src/components/status.jsx:1187
msgid "Unable to copy post text"
msgstr "Unable to copy post text"
-#: src/components/status.jsx:1204
+#: src/components/status.jsx:1193
msgid "Copy post text"
msgstr "Copy post text"
#. placeholder {0}: username || acct
-#: src/components/status.jsx:1222
+#: src/components/status.jsx:1211
msgid "View post by <0>@{0}0>"
msgstr ""
-#: src/components/status.jsx:1243
+#: src/components/status.jsx:1232
msgid "Show Edit History"
msgstr ""
-#: src/components/status.jsx:1246
+#: src/components/status.jsx:1235
msgid "Edited: {editedDateText}"
msgstr ""
-#: src/components/status.jsx:1313
-#: src/components/status.jsx:3357
+#: src/components/status.jsx:1302
+#: src/components/status.jsx:3346
msgid "Embed post"
msgstr ""
-#: src/components/status.jsx:1327
+#: src/components/status.jsx:1316
msgid "Conversation unmuted"
msgstr ""
-#: src/components/status.jsx:1327
+#: src/components/status.jsx:1316
msgid "Conversation muted"
msgstr ""
-#: src/components/status.jsx:1333
+#: src/components/status.jsx:1322
msgid "Unable to unmute conversation"
msgstr ""
-#: src/components/status.jsx:1334
+#: src/components/status.jsx:1323
msgid "Unable to mute conversation"
msgstr ""
-#: src/components/status.jsx:1343
+#: src/components/status.jsx:1332
msgid "Unmute conversation"
msgstr ""
-#: src/components/status.jsx:1350
+#: src/components/status.jsx:1339
msgid "Mute conversation"
msgstr ""
-#: src/components/status.jsx:1366
+#: src/components/status.jsx:1355
msgid "Post unpinned from profile"
msgstr ""
-#: src/components/status.jsx:1367
+#: src/components/status.jsx:1356
msgid "Post pinned to profile"
msgstr ""
-#: src/components/status.jsx:1372
+#: src/components/status.jsx:1361
msgid "Unable to unpin post"
msgstr ""
-#: src/components/status.jsx:1372
+#: src/components/status.jsx:1361
msgid "Unable to pin post"
msgstr ""
-#: src/components/status.jsx:1381
+#: src/components/status.jsx:1370
msgid "Unpin from profile"
msgstr ""
-#: src/components/status.jsx:1388
+#: src/components/status.jsx:1377
msgid "Pin to profile"
msgstr ""
-#: src/components/status.jsx:1417
+#: src/components/status.jsx:1406
msgid "Delete this post?"
msgstr ""
-#: src/components/status.jsx:1433
+#: src/components/status.jsx:1422
msgid "Post deleted"
msgstr ""
-#: src/components/status.jsx:1436
+#: src/components/status.jsx:1425
msgid "Unable to delete post"
msgstr ""
-#: src/components/status.jsx:1464
+#: src/components/status.jsx:1453
msgid "Report post…"
msgstr ""
-#: src/components/status.jsx:1838
-#: src/components/status.jsx:1874
-#: src/components/status.jsx:2578
+#: src/components/status.jsx:1827
+#: src/components/status.jsx:1863
+#: src/components/status.jsx:2567
msgid "Liked"
msgstr ""
-#: src/components/status.jsx:1871
-#: src/components/status.jsx:2565
+#: src/components/status.jsx:1860
+#: src/components/status.jsx:2554
msgid "Boosted"
msgstr ""
-#: src/components/status.jsx:1881
-#: src/components/status.jsx:2590
+#: src/components/status.jsx:1870
+#: src/components/status.jsx:2579
msgid "Bookmarked"
msgstr ""
-#: src/components/status.jsx:1885
+#: src/components/status.jsx:1874
msgid "Pinned"
msgstr ""
-#: src/components/status.jsx:1931
-#: src/components/status.jsx:2402
+#: src/components/status.jsx:1920
+#: src/components/status.jsx:2391
msgid "Deleted"
msgstr ""
-#: src/components/status.jsx:1972
+#: src/components/status.jsx:1961
msgid "{repliesCount, plural, one {# reply} other {# replies}}"
msgstr ""
#. placeholder {0}: snapStates.statusThreadNumber[sKey] ? ` ${snapStates.statusThreadNumber[sKey]}/X` : ''
-#: src/components/status.jsx:2062
+#: src/components/status.jsx:2051
msgid "Thread{0}"
msgstr ""
-#: src/components/status.jsx:2140
-#: src/components/status.jsx:2202
-#: src/components/status.jsx:2298
+#: src/components/status.jsx:2129
+#: src/components/status.jsx:2191
+#: src/components/status.jsx:2287
msgid "Show less"
msgstr ""
-#: src/components/status.jsx:2140
-#: src/components/status.jsx:2202
+#: src/components/status.jsx:2129
+#: src/components/status.jsx:2191
msgid "Show content"
msgstr ""
#. placeholder {0}: filterInfo.titlesStr
#. placeholder {0}: filterInfo?.titlesStr
-#: src/components/status.jsx:2294
+#: src/components/status.jsx:2283
#: src/pages/catchup.jsx:1879
msgid "Filtered: {0}"
msgstr "Filtered: {0}"
-#: src/components/status.jsx:2298
+#: src/components/status.jsx:2287
msgid "Show media"
msgstr ""
-#: src/components/status.jsx:2438
+#: src/components/status.jsx:2427
msgid "Edited"
msgstr ""
-#: src/components/status.jsx:2515
+#: src/components/status.jsx:2504
msgid "Comments"
msgstr ""
#. More from [Author]
-#: src/components/status.jsx:2815
+#: src/components/status.jsx:2804
msgid "More from <0/>"
msgstr "More from <0/>"
-#: src/components/status.jsx:3117
+#: src/components/status.jsx:3106
msgid "Edit History"
msgstr ""
-#: src/components/status.jsx:3121
+#: src/components/status.jsx:3110
msgid "Failed to load history"
msgstr ""
-#: src/components/status.jsx:3126
+#: src/components/status.jsx:3115
#: src/pages/annual-report.jsx:45
msgid "Loading…"
msgstr ""
-#: src/components/status.jsx:3362
+#: src/components/status.jsx:3351
msgid "HTML Code"
msgstr ""
-#: src/components/status.jsx:3379
+#: src/components/status.jsx:3368
msgid "HTML code copied"
msgstr ""
-#: src/components/status.jsx:3382
+#: src/components/status.jsx:3371
msgid "Unable to copy HTML code"
msgstr ""
-#: src/components/status.jsx:3394
+#: src/components/status.jsx:3383
msgid "Media attachments:"
msgstr ""
-#: src/components/status.jsx:3416
+#: src/components/status.jsx:3405
msgid "Account Emojis:"
msgstr ""
-#: src/components/status.jsx:3447
-#: src/components/status.jsx:3492
+#: src/components/status.jsx:3436
+#: src/components/status.jsx:3481
msgid "static URL"
msgstr ""
-#: src/components/status.jsx:3461
+#: src/components/status.jsx:3450
msgid "Emojis:"
msgstr ""
-#: src/components/status.jsx:3506
+#: src/components/status.jsx:3495
msgid "Notes:"
msgstr ""
-#: src/components/status.jsx:3510
+#: src/components/status.jsx:3499
msgid "This is static, unstyled and scriptless. You may need to apply your own styles and edit as needed."
msgstr ""
-#: src/components/status.jsx:3516
+#: src/components/status.jsx:3505
msgid "Polls are not interactive, becomes a list with vote counts."
msgstr ""
-#: src/components/status.jsx:3521
+#: src/components/status.jsx:3510
msgid "Media attachments can be images, videos, audios or any file types."
msgstr ""
-#: src/components/status.jsx:3527
+#: src/components/status.jsx:3516
msgid "Post could be edited or deleted later."
msgstr ""
-#: src/components/status.jsx:3533
+#: src/components/status.jsx:3522
msgid "Preview"
msgstr ""
-#: src/components/status.jsx:3542
+#: src/components/status.jsx:3531
msgid "Note: This preview is lightly styled."
msgstr ""
#. [Name] [Visibility icon] boosted
-#: src/components/status.jsx:3795
+#: src/components/status.jsx:3784
msgid "<0/> <1/> boosted"
msgstr ""
-#: src/components/status.jsx:3908
+#: src/components/status.jsx:3886
+msgid "Post hidden by your filters"
+msgstr "Post hidden by your filters"
+
+#: src/components/status.jsx:3887
msgid "Post removed by author."
msgstr "Post removed by author."
-#: src/components/status.jsx:3917
+#: src/components/status.jsx:3888
msgid "You’re not authorized to view this post."
msgstr "You’re not authorized to view this post."
-#: src/components/status.jsx:3926
+#: src/components/status.jsx:3889
msgid "Post pending author approval."
msgstr "Post pending author approval."
-#: src/components/status.jsx:3935
+#: src/components/status.jsx:3890
+#: src/components/status.jsx:3891
msgid "Quoting not allowed by the author."
msgstr "Quoting not allowed by the author."
diff --git a/src/pages/sandbox.jsx b/src/pages/sandbox.jsx
index d70f27d3b3..e5981be6b9 100644
--- a/src/pages/sandbox.jsx
+++ b/src/pages/sandbox.jsx
@@ -311,6 +311,7 @@ const INITIAL_STATE = {
showQuotes: false,
quotesCount: '1',
quoteNestingLevel: '0',
+ quoteState: 'accepted', // State for all quote posts
size: 'medium',
filters: [false, false, false], // hide, blur, warn
quoteFilters: [false, false, false], // hide, blur, warn for quotes
@@ -398,6 +399,7 @@ export default function Sandbox() {
showQuotes: toggleState.showQuotes,
quotesCount: toggleState.quotesCount,
quoteNestingLevel: toggleState.quoteNestingLevel,
+ quoteState: toggleState.quoteState,
size: toggleState.size,
filters: toggleState.filters,
quoteFilters: toggleState.quoteFilters,
@@ -528,162 +530,173 @@ export default function Sandbox() {
id: quoteId,
instance: DEFAULT_INSTANCE,
url: `https://example.social/s/${quoteId}`, // Include URL to ensure uniqueness check works
+ state:
+ toggleState.quoteState === 'accepted'
+ ? undefined
+ : toggleState.quoteState, // Only set state if not accepted
};
// First, delete any existing status with this ID to avoid duplicates
- delete states.statuses[quoteId];
-
- // Create the actual status object that will be retrieved by QuoteStatuses
- const quoteStatus = {
- id: quoteId,
- content: `
This is quote post ${i + 1}${i % 2 === 0 ? '' : ' with some extra text'}
`,
- account: {
- id: `quote-account-${i}`,
- username: `quote${i}`,
- name: `Quote User ${i}`,
- avatar: '/logo-192.png',
- acct: `quote${i}@example.social`,
- url: `https://example.social/@quote${i}`,
- },
- visibility: 'public',
- createdAt: new Date(Date.now() - i * 3600000).toISOString(), // Each post 1 hour older
- emojis: [],
- // First quote post should be plain (no media, no poll)
- mediaAttachments:
- i > 0 && i % 2 === 0
- ? [
- {
- // Only non-first posts can have media (every 3rd post after the 1st)
- id: `quote-media-${i}`,
- type: 'image',
- url: `https://picsum.photos/seed/quote-${i}/600/400`,
- previewUrl: `https://picsum.photos/seed/quote-${i}/300/200`,
- meta: {
- original: { width: 600, height: 400 },
- small: { width: 300, height: 200 },
- },
- },
- ]
- : [],
- poll:
- i > 0 && i % 3 === 0
- ? {
- // Only non-first posts can have polls (every 4th post after the 1st)
- id: `quote-poll-${i}`,
- options: [
- {
- title: 'Option A',
- votesCount: Math.floor(Math.random() * 50),
- },
- {
- title: 'Option B',
- votesCount: Math.floor(Math.random() * 50),
- },
- ],
- expiresAt: new Date(
- Date.now() + 24 * 60 * 60 * 1000,
- ).toISOString(),
- multiple: false,
- votesCount: Math.floor(Math.random() * 100),
- }
- : null,
- };
-
- // Add filtering to quote posts if enabled
- if (
- toggleState.quoteFilters &&
- toggleState.quoteFilters.some((f) => f)
- ) {
- quoteStatus.filtered = toggleState.quoteFilters
- .map((enabled, filterIndex) => {
- if (!enabled) return null;
- const filterTypes = ['hide', 'blur', 'warn'];
- return {
- filter: {
- id: `quote-filter-${i}-${filterIndex}`,
- title: `Quote ${filterTypes[filterIndex]} filter`,
- context: ['home', 'public', 'thread', 'account'],
- filterAction: filterTypes[filterIndex],
- },
- keywordMatches: [],
- statusMatches: [],
- };
- })
- .filter(Boolean);
- }
-
- // Assign the quote status to the states
- states.statuses[quoteId] = quoteStatus;
-
- // If nesting level > 0, add nested quotes to each quote post
- if (nestingLevel > 0 && i % 2 === 0) {
- // Add nested quotes to every other quote - use stable ID
- const nestedQuoteId = `nested-quote-${i}-12345`;
-
- // Add the nested quote post to states.statuses
- states.statuses[nestedQuoteId] = {
- id: nestedQuoteId,
- content: `
This is a nested quote inside quote ${i + 1}
`,
+ const quoteStatusKey = statusKey(quoteId, DEFAULT_INSTANCE);
+ delete states.statuses[quoteStatusKey];
+
+ // Create the actual status object for all quote states
+ // This allows filtering logic to run even for non-accepted states
+ {
+ // Create the actual status object that will be retrieved by QuoteStatuses
+ const quoteStatus = {
+ id: quoteId,
+ content: `
This is quote post ${i + 1}${i % 2 === 0 ? '' : ' with some extra text'}
`,
account: {
- id: `nested-account-${i}`,
- username: `nested${i}`,
- name: `Nested User ${i}`,
+ id: `quote-account-${i}`,
+ username: `quote${i}`,
+ name: `Quote User ${i}`,
avatar: '/logo-192.png',
- acct: `nested${i}@example.social`,
- url: `https://example.social/@nested${i}`,
+ acct: `quote${i}@example.social`,
+ url: `https://example.social/@quote${i}`,
},
visibility: 'public',
- createdAt: new Date(Date.now() - (i + 1) * 3600000).toISOString(),
+ createdAt: new Date(Date.now() - i * 3600000).toISOString(), // Each post 1 hour older
emojis: [],
- mediaAttachments: [], // No media in nested quotes for simplicity
+ // First quote post should be plain (no media, no poll)
+ mediaAttachments:
+ i > 0 && i % 2 === 0
+ ? [
+ {
+ // Only non-first posts can have media (every 3rd post after the 1st)
+ id: `quote-media-${i}`,
+ type: 'image',
+ url: `https://picsum.photos/seed/quote-${i}/600/400`,
+ previewUrl: `https://picsum.photos/seed/quote-${i}/300/200`,
+ meta: {
+ original: { width: 600, height: 400 },
+ small: { width: 300, height: 200 },
+ },
+ },
+ ]
+ : [],
+ poll:
+ i > 0 && i % 3 === 0
+ ? {
+ // Only non-first posts can have polls (every 4th post after the 1st)
+ id: `quote-poll-${i}`,
+ options: [
+ {
+ title: 'Option A',
+ votesCount: Math.floor(Math.random() * 50),
+ },
+ {
+ title: 'Option B',
+ votesCount: Math.floor(Math.random() * 50),
+ },
+ ],
+ expiresAt: new Date(
+ Date.now() + 24 * 60 * 60 * 1000,
+ ).toISOString(),
+ multiple: false,
+ votesCount: Math.floor(Math.random() * 100),
+ }
+ : null,
};
- // Create reference object for nested quote - critical for proper rendering
- const nestedQuoteRef = {
- id: nestedQuoteId,
- instance: DEFAULT_INSTANCE,
- url: `https://example.social/s/${nestedQuoteId}`,
- };
+ // Add filtering to quote posts if enabled
+ if (
+ toggleState.quoteFilters &&
+ toggleState.quoteFilters.some((f) => f)
+ ) {
+ quoteStatus.filtered = toggleState.quoteFilters
+ .map((enabled, filterIndex) => {
+ if (!enabled) return null;
+ const filterTypes = ['hide', 'blur', 'warn'];
+ return {
+ filter: {
+ id: `quote-filter-${i}-${filterIndex}`,
+ title: `Quote ${filterTypes[filterIndex]} filter`,
+ context: ['home', 'public', 'thread', 'account'],
+ filterAction: filterTypes[filterIndex],
+ },
+ keywordMatches: [],
+ statusMatches: [],
+ };
+ })
+ .filter(Boolean);
+ }
+
+ // Assign the quote status to the states using the correct key format
+ states.statuses[quoteStatusKey] = quoteStatus;
- // Add another level of nesting if specified
- if (nestingLevel > 1 && i === 0) {
- // Only add deepest nesting to first quote
- const deepNestedId = `deep-nested-${i}-12345`;
+ // If nesting level > 0, add nested quotes to each quote post
+ if (nestingLevel > 0 && i % 2 === 0) {
+ // Add nested quotes to every other quote - use stable ID
+ const nestedQuoteId = `nested-quote-${i}-12345`;
- states.statuses[deepNestedId] = {
- id: deepNestedId,
- content: `
This is a deeply nested quote (level 2)
`,
+ // Add the nested quote post to states.statuses
+ states.statuses[nestedQuoteId] = {
+ id: nestedQuoteId,
+ content: `
This is a nested quote inside quote ${i + 1}
`,
account: {
- id: `deep-account-${i}`,
- username: `deep${i}`,
- name: `Deep User ${i}`,
+ id: `nested-account-${i}`,
+ username: `nested${i}`,
+ name: `Nested User ${i}`,
avatar: '/logo-192.png',
- acct: `deep${i}@example.social`,
- url: `https://example.social/@deep${i}`,
+ acct: `nested${i}@example.social`,
+ url: `https://example.social/@nested${i}`,
},
visibility: 'public',
createdAt: new Date(
- Date.now() - (i + 2) * 3600000,
+ Date.now() - (i + 1) * 3600000,
).toISOString(),
emojis: [],
+ mediaAttachments: [], // No media in nested quotes for simplicity
};
- // Create deep nested reference
- const deepNestedRef = {
- id: deepNestedId,
+ // Create reference object for nested quote - critical for proper rendering
+ const nestedQuoteRef = {
+ id: nestedQuoteId,
instance: DEFAULT_INSTANCE,
- url: `https://example.social/s/${deepNestedId}`,
+ url: `https://example.social/s/${nestedQuoteId}`,
};
- // Important: Use the proper key format for the nested quote
- const nestedKey = statusKey(nestedQuoteId, DEFAULT_INSTANCE);
- states.statusQuotes[nestedKey] = [deepNestedRef];
- }
+ // Add another level of nesting if specified
+ if (nestingLevel > 1 && i === 0) {
+ // Only add deepest nesting to first quote
+ const deepNestedId = `deep-nested-${i}-12345`;
+
+ states.statuses[deepNestedId] = {
+ id: deepNestedId,
+ content: `
This is a deeply nested quote (level 2)
`,
+ account: {
+ id: `deep-account-${i}`,
+ username: `deep${i}`,
+ name: `Deep User ${i}`,
+ avatar: '/logo-192.png',
+ acct: `deep${i}@example.social`,
+ url: `https://example.social/@deep${i}`,
+ },
+ visibility: 'public',
+ createdAt: new Date(
+ Date.now() - (i + 2) * 3600000,
+ ).toISOString(),
+ emojis: [],
+ };
- // Add nested quote to the quote's quotes using the proper key format
- const quoteKey = statusKey(quoteId, DEFAULT_INSTANCE);
- states.statusQuotes[quoteKey] = [nestedQuoteRef];
- }
+ // Create deep nested reference
+ const deepNestedRef = {
+ id: deepNestedId,
+ instance: DEFAULT_INSTANCE,
+ url: `https://example.social/s/${deepNestedId}`,
+ };
+
+ // Important: Use the proper key format for the nested quote
+ const nestedKey = statusKey(nestedQuoteId, DEFAULT_INSTANCE);
+ states.statusQuotes[nestedKey] = [deepNestedRef];
+ }
+
+ // Add nested quote to the quote's quotes using the proper key format
+ const quoteKey = statusKey(quoteId, DEFAULT_INSTANCE);
+ states.statusQuotes[quoteKey] = [nestedQuoteRef];
+ }
+ } // Close the quote status creation block
return quoteRef;
});
@@ -699,6 +712,7 @@ export default function Sandbox() {
toggleState.showQuotes,
toggleState.quotesCount,
toggleState.quoteNestingLevel,
+ toggleState.quoteState,
toggleState.quoteFilters,
]);
@@ -1251,6 +1265,98 @@ export default function Sandbox() {
/>
+
+
+ Quote state
+
+
Quote Filters
From d5763442c59aaa3ab32d23ac891b3a190f972aff Mon Sep 17 00:00:00 2001
From: Lim Chee Aun
Date: Fri, 11 Jul 2025 21:07:17 +0800
Subject: [PATCH 17/32] Hide .quote-inline if there's native quote
---
src/components/status.css | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/components/status.css b/src/components/status.css
index f9de945ab4..3d4bc5e71b 100644
--- a/src/components/status.css
+++ b/src/components/status.css
@@ -973,6 +973,11 @@
> :is(div, blockquote) > :is(ul, ol) li > :is(ul, ol) {
padding-inline-start: 1.5em;
}
+
+ /* Hide inline quote (RE: [LINK]) when there's a native quote */
+ &:has(~ .quote-post-native) .quote-inline {
+ display: none;
+ }
}
.status .content ul {
list-style-type: disc;
From 6223420cdb4ac8b65043bbb104dd8e558185b207 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Sun, 13 Jul 2025 00:02:42 +0000
Subject: [PATCH 18/32] Update README.md
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 46ab4e1a43..aabe903a24 100644
--- a/README.md
+++ b/README.md
@@ -392,6 +392,7 @@ Costs involved in running and developing this web app:
- SadmL (Russian)
- SadmL_AI (Russian)
- Schishka71 (Russian)
+- seizeheures (Esperanto)
- shuuji3 (Japanese)
- Sky_NiniKo (French)
- Steffo99 (Italian)
From d561e675345d42010a030f82fe1ae8369ad663c6 Mon Sep 17 00:00:00 2001
From: Lim Chee Aun
Date: Sat, 12 Jul 2025 12:01:56 +0800
Subject: [PATCH 19/32] Upgrade deps
Reimplement splitVendorChunkPlugin
---
package-lock.json | 548 ++++++++++++----------------------------------
package.json | 22 +-
vite.config.js | 10 +-
3 files changed, 150 insertions(+), 430 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 4a7a7bd4e6..39e71bb361 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,9 +14,9 @@
"@github/text-expander-element": "~2.9.2",
"@iconify-icons/mingcute": "~1.2.9",
"@justinribeiro/lite-youtube": "~1.8.2",
- "@lingui/detect-locale": "~5.3.2",
- "@lingui/macro": "~5.3.2",
- "@lingui/react": "~5.3.2",
+ "@lingui/detect-locale": "~5.3.3",
+ "@lingui/macro": "~5.3.3",
+ "@lingui/react": "~5.3.3",
"@szhsin/react-menu": "~4.4.1",
"chroma-js": "~3.1.2",
"compare-versions": "~6.1.1",
@@ -51,21 +51,21 @@
},
"devDependencies": {
"@biomejs/biome": "2.1.1",
- "@lingui/babel-plugin-lingui-macro": "~5.3.2",
- "@lingui/cli": "~5.3.2",
- "@lingui/vite-plugin": "~5.3.2",
- "@playwright/test": "~1.53.2",
- "@preact/preset-vite": "~2.10.1",
- "@types/node": "~24.0.12",
+ "@lingui/babel-plugin-lingui-macro": "~5.3.3",
+ "@lingui/cli": "~5.3.3",
+ "@lingui/vite-plugin": "~5.3.3",
+ "@playwright/test": "~1.54.1",
+ "@preact/preset-vite": "~2.10.2",
+ "@types/node": "~24.0.13",
"postcss": "~8.5.6",
"postcss-dark-theme-class": "~1.3.0",
"postcss-preset-env": "~10.2.4",
"sonda": "~0.9.0",
"twitter-text": "~3.1.0",
- "vite": "~6.3.5",
+ "vite": "~7.0.4",
"vite-plugin-generate-file": "~0.3.1",
"vite-plugin-html-config": "~2.0.2",
- "vite-plugin-pwa": "~1.0.0",
+ "vite-plugin-pwa": "~1.0.1",
"vite-plugin-remove-console": "~2.2.0",
"vite-plugin-run": "~0.6.1",
"workbox-cacheable-response": "~7.3.0",
@@ -3497,9 +3497,9 @@
"license": "MIT"
},
"node_modules/@lingui/babel-plugin-extract-messages": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-5.3.2.tgz",
- "integrity": "sha512-uOPHYHyKn37P/YYl4kJo7Fu4yo6IqKsJJtNaOsYoZov9iKSPWTDzU7cVWIrm7s2K+C9vvyLwIfqZukSKC5gNCQ==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-5.3.3.tgz",
+ "integrity": "sha512-Cgac9D9ZrTrNdQPxRc5gmZXVUnofBoSUC7CHSEuua5tPolr20oP4snYEnpOvs2D/sM6AWTbM199i7F2e5m4HYA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3507,18 +3507,18 @@
}
},
"node_modules/@lingui/babel-plugin-lingui-macro": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-lingui-macro/-/babel-plugin-lingui-macro-5.3.2.tgz",
- "integrity": "sha512-NdXrq8aZlPjN4jeA/LkSLNyx5vPGmrW+r2ywMNQDPQPVP28Hq8c3hF9SQc1t7hwBorGQ3qzIQ7i2Vm6Y8PnjQw==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-lingui-macro/-/babel-plugin-lingui-macro-5.3.3.tgz",
+ "integrity": "sha512-LSdJVBchjHPtFemQJiykDlpSksN9jusKcLbkuhdpgMETNMS5EyFwLTud7YUo5qkOIhtpYIf9mj94t1vGRVeSYQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/core": "^7.20.12",
"@babel/runtime": "^7.20.13",
"@babel/types": "^7.20.7",
- "@lingui/conf": "5.3.2",
- "@lingui/core": "5.3.2",
- "@lingui/message-utils": "5.3.2"
+ "@lingui/conf": "5.3.3",
+ "@lingui/core": "5.3.3",
+ "@lingui/message-utils": "5.3.3"
},
"engines": {
"node": ">=20.0.0"
@@ -3533,9 +3533,9 @@
}
},
"node_modules/@lingui/cli": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-5.3.2.tgz",
- "integrity": "sha512-pilromjjdDM2hQ6J5O8p9UlV/HDuFyHnyYGnKbI3BmsCNU0zKw1nf4WIYQZKDLuWNVRGQg93vjNSs9tWAAZQ/w==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-5.3.3.tgz",
+ "integrity": "sha512-db3aI637XO7z5QeZkrj9pEI3diTuXp0FVHn8TdFgKzQqmKLcv0CgJNftMoQ+DiibifOApyl01ZX81UVXtnQNdA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3544,14 +3544,12 @@
"@babel/parser": "^7.22.0",
"@babel/runtime": "^7.21.0",
"@babel/types": "^7.21.2",
- "@lingui/babel-plugin-extract-messages": "5.3.2",
- "@lingui/babel-plugin-lingui-macro": "5.3.2",
- "@lingui/conf": "5.3.2",
- "@lingui/core": "5.3.2",
- "@lingui/format-po": "5.3.2",
- "@lingui/message-utils": "5.3.2",
- "babel-plugin-macros": "^3.0.1",
- "chalk": "^4.1.0",
+ "@lingui/babel-plugin-extract-messages": "5.3.3",
+ "@lingui/babel-plugin-lingui-macro": "5.3.3",
+ "@lingui/conf": "5.3.3",
+ "@lingui/core": "5.3.3",
+ "@lingui/format-po": "5.3.3",
+ "@lingui/message-utils": "5.3.3",
"chokidar": "3.5.1",
"cli-table": "^0.3.11",
"commander": "^10.0.0",
@@ -3559,12 +3557,10 @@
"date-fns": "^3.6.0",
"esbuild": "^0.25.1",
"glob": "^11.0.0",
- "inquirer": "^7.3.3",
"micromatch": "^4.0.7",
"normalize-path": "^3.0.0",
"ora": "^5.1.0",
- "pathe": "^1.1.0",
- "pkg-up": "^3.1.0",
+ "picocolors": "^1.1.1",
"pofile": "^1.1.4",
"pseudolocale": "^2.0.0",
"source-map": "^0.8.0-beta.0"
@@ -3645,37 +3641,36 @@
}
},
"node_modules/@lingui/conf": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-5.3.2.tgz",
- "integrity": "sha512-c0Dfovr9BLuwAnY5GADxKcwBUQdVl0Jo/JUa3cumIXFhHzZGb78kfhCHjWWQdX8+WQD8qzSl/YkVDbxhcQJGmg==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-5.3.3.tgz",
+ "integrity": "sha512-YVjGeGQg4BrHrC+/s7kHYYjcBzAPFoGWl/ujdp05J6+PjV7yXnwUaMa3A7XhKFdRqpJReXf9FOWZUsIIBcPSEQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.20.13",
- "chalk": "^4.1.0",
"cosmiconfig": "^8.0.0",
"jest-validate": "^29.4.3",
- "jiti": "^1.17.1"
+ "jiti": "^1.17.1",
+ "picocolors": "^1.1.1"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@lingui/core": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/@lingui/core/-/core-5.3.2.tgz",
- "integrity": "sha512-rLtpZvs5RYlebwjb047PldmiuFBbbVOhEofA90N8pgTCIlfnJRTxfevd6gx3Qp0/uG+AV0DWcZxtba6H/MPYug==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@lingui/core/-/core-5.3.3.tgz",
+ "integrity": "sha512-H2PAJrbcPBjJg2q3JqMS9OndPkx0kUV+zDLSguONNFGYbe/G7vcNpgEfyYfcCoJBrWaZzJ0iy3Se6LdFbvn6nA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@lingui/message-utils": "5.3.2",
- "unraw": "^3.0.0"
+ "@lingui/message-utils": "5.3.3"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
- "@lingui/babel-plugin-lingui-macro": "5.3.2",
+ "@lingui/babel-plugin-lingui-macro": "5.3.3",
"babel-plugin-macros": "2 || 3"
},
"peerDependenciesMeta": {
@@ -3688,23 +3683,23 @@
}
},
"node_modules/@lingui/detect-locale": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-5.3.2.tgz",
- "integrity": "sha512-UNZcl7k84uHIfOojlP7SKglRsB3I4pUcCDyn2fJvViMjAWWlFSiXWWNPro51GX2uwOGAqad0Uu1yQ8j8pzo5AA==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-5.3.3.tgz",
+ "integrity": "sha512-a+zeFZpyp375s1Ffo2/si/kNTQa1pmwOfKy15Z8+BT9/2+M/rrrTnmkRizxVWdfl8SvNWo76e+e7skic0bHy9A==",
"license": "MIT",
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@lingui/format-po": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/@lingui/format-po/-/format-po-5.3.2.tgz",
- "integrity": "sha512-YZxk0CqdTNV6Mwo/PRsx4SHoNxfGEMEK6OHDk42/MGJiUQ6/Od6+2qlAW5z9TvapHnzWbDbU3EdBcqLvgD7Rqw==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@lingui/format-po/-/format-po-5.3.3.tgz",
+ "integrity": "sha512-sKGJqsIJLPMKwtSP1js7cfFzEXsNSppzYxDPVO06i71Xd0jn8a+KXLMaSAQj9jY/dpxN8wKsCt26gOnHYi5c5g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@lingui/conf": "5.3.2",
- "@lingui/message-utils": "5.3.2",
+ "@lingui/conf": "5.3.3",
+ "@lingui/message-utils": "5.3.3",
"date-fns": "^3.6.0",
"pofile": "^1.1.4"
},
@@ -3713,19 +3708,19 @@
}
},
"node_modules/@lingui/macro": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-5.3.2.tgz",
- "integrity": "sha512-K+G24xhbWFaYVxJRz+I8qXUISfXJ+T9BN+b9RMR+0pwsIshK4TWJNti8WiG8cnPxQmMeQ2waZqCJUPVObGbGvw==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-5.3.3.tgz",
+ "integrity": "sha512-682DGZWQmK0u9sDgESpSoZjx1uHRroecOq844SOr+24cnunGFG6Z7f4LXjbDphPwKAsjoKPW6tEHnd+eysTP7Q==",
"license": "MIT",
"dependencies": {
- "@lingui/core": "5.3.2",
- "@lingui/react": "5.3.2"
+ "@lingui/core": "5.3.3",
+ "@lingui/react": "5.3.3"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
- "@lingui/babel-plugin-lingui-macro": "5.3.2",
+ "@lingui/babel-plugin-lingui-macro": "5.3.3",
"babel-plugin-macros": "2 || 3"
},
"peerDependenciesMeta": {
@@ -3738,9 +3733,9 @@
}
},
"node_modules/@lingui/message-utils": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-5.3.2.tgz",
- "integrity": "sha512-YUBrXApa3kxcL9oimJdw7oiAJ2ESkI24uyTMaRp9XYNaWgWqpEgoxC5bDN2Fwnk5THFjUp/l64kAD5LJGQhJxQ==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-5.3.3.tgz",
+ "integrity": "sha512-Xn1jFX9pworCEKvPwyEo/YJEp8qjELoRkFMUQ+D9AMi8jpaBUbcJpKdJ/TcIt7SKfk8koR/TMxoayMVsh++KCw==",
"bundleDependencies": [
"@messageformat/date-skeleton"
],
@@ -3754,19 +3749,19 @@
}
},
"node_modules/@lingui/react": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/@lingui/react/-/react-5.3.2.tgz",
- "integrity": "sha512-wKvgVIKmlz4pIu+mlixjKDLXJoqyG3N3WPB76EScNuDMN0L9T1xMA/bjO9SxgEaUypflALHRsTA2rzViskLFHg==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@lingui/react/-/react-5.3.3.tgz",
+ "integrity": "sha512-DEFmI24pDdy/wsIDYtmYg5qwAPmgZIjoy9q10GVGmjq952D0sQJoGBGz+ucGQFSNmT7SnktauJHH+xEUdO/Cgg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@lingui/core": "5.3.2"
+ "@lingui/core": "5.3.3"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
- "@lingui/babel-plugin-lingui-macro": "5.3.2",
+ "@lingui/babel-plugin-lingui-macro": "5.3.3",
"babel-plugin-macros": "2 || 3",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
},
@@ -3780,20 +3775,20 @@
}
},
"node_modules/@lingui/vite-plugin": {
- "version": "5.3.2",
- "resolved": "https://registry.npmjs.org/@lingui/vite-plugin/-/vite-plugin-5.3.2.tgz",
- "integrity": "sha512-9itYbNBLMEgGSfPmgdvG6gjvYV/Ql2K07gad9m5alVy13GLBGmMSFjlX+1TsI7JIk96neKlxjJwysGGSFbn8bQ==",
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@lingui/vite-plugin/-/vite-plugin-5.3.3.tgz",
+ "integrity": "sha512-CHn/8+2SdLxDVYdtUrUQ0DUVhdG0e+U83bqr8OG6JMmlLxLccWGu9JLLloDN34bsGAZqJ1vkEaZ59UHfbtWUaw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@lingui/cli": "5.3.2",
- "@lingui/conf": "5.3.2"
+ "@lingui/cli": "5.3.3",
+ "@lingui/conf": "5.3.3"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
- "vite": "^3 || ^4 || ^5.0.9 || ^6"
+ "vite": "^3 || ^4 || ^5.0.9 || ^6 || ^7"
}
},
"node_modules/@lukeed/csprng": {
@@ -3815,13 +3810,13 @@
}
},
"node_modules/@playwright/test": {
- "version": "1.53.2",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz",
- "integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==",
+ "version": "1.54.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz",
+ "integrity": "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright": "1.53.2"
+ "playwright": "1.54.1"
},
"bin": {
"playwright": "cli.js"
@@ -3831,10 +3826,11 @@
}
},
"node_modules/@preact/preset-vite": {
- "version": "2.10.1",
- "resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.10.1.tgz",
- "integrity": "sha512-59lyGBXNfZIr5OOuBUB4/IB8AqF/ULbvYnyItgK/2BJnsGJqaeaJobRVtMp1129obHQuj8oZ/dVxB9inmH8Xig==",
+ "version": "2.10.2",
+ "resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.10.2.tgz",
+ "integrity": "sha512-K9wHlJOtkE+cGqlyQ5v9kL3Ge0Ql4LlIZjkUTL+1zf3nNdF88F9UZN6VTV8jdzBX9Fl7WSzeNMSDG7qECPmSmg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/plugin-transform-react-jsx": "^7.22.15",
"@babel/plugin-transform-react-jsx-development": "^7.22.5",
@@ -3842,12 +3838,12 @@
"@rollup/pluginutils": "^4.1.1",
"babel-plugin-transform-hook-names": "^1.0.2",
"debug": "^4.3.4",
- "kolorist": "^1.8.0",
+ "picocolors": "^1.1.1",
"vite-prerender-plugin": "^0.5.3"
},
"peerDependencies": {
"@babel/core": "7.x",
- "vite": "2.x || 3.x || 4.x || 5.x || 6.x"
+ "vite": "2.x || 3.x || 4.x || 5.x || 6.x || 7.x"
}
},
"node_modules/@prefresh/babel-plugin": {
@@ -4308,9 +4304,9 @@
}
},
"node_modules/@types/node": {
- "version": "24.0.12",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.12.tgz",
- "integrity": "sha512-LtOrbvDf5ndC9Xi+4QZjVL0woFymF/xSTKZKPgrrl7H7XoeDvnD+E2IclKVDyaK9UM756W/3BXqSU+JEHopA9g==",
+ "version": "24.0.13",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.13.tgz",
+ "integrity": "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
@@ -4321,7 +4317,8 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
- "devOptional": true
+ "optional": true,
+ "peer": true
},
"node_modules/@types/resolve": {
"version": "1.20.2",
@@ -4386,33 +4383,6 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
- "node_modules/ansi-escapes": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
- "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
- "dev": true,
- "dependencies": {
- "type-fest": "^0.21.3"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/ansi-escapes/node_modules/type-fest": {
- "version": "0.21.3",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
- "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/ansi-regex": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
@@ -4571,7 +4541,8 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
- "devOptional": true,
+ "optional": true,
+ "peer": true,
"dependencies": {
"@babel/runtime": "^7.12.5",
"cosmiconfig": "^7.0.0",
@@ -4586,7 +4557,8 @@
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
- "devOptional": true,
+ "optional": true,
+ "peer": true,
"dependencies": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.2.1",
@@ -4602,7 +4574,8 @@
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
- "devOptional": true,
+ "optional": true,
+ "peer": true,
"engines": {
"node": ">= 6"
}
@@ -4934,12 +4907,6 @@
"tslib": "^2.0.3"
}
},
- "node_modules/chardet": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
- "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
- "dev": true
- },
"node_modules/chokidar": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
@@ -5002,15 +4969,6 @@
"node": ">= 0.2.0"
}
},
- "node_modules/cli-width": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
- "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
- "dev": true,
- "engines": {
- "node": ">= 10"
- }
- },
"node_modules/clone": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
@@ -5816,15 +5774,6 @@
"node": ">=6"
}
},
- "node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true,
- "engines": {
- "node": ">=0.8.0"
- }
- },
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
@@ -5870,20 +5819,6 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
- "node_modules/external-editor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
- "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
- "dev": true,
- "dependencies": {
- "chardet": "^0.7.0",
- "iconv-lite": "^0.4.24",
- "tmp": "^0.0.33"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/fast-blurhash": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/fast-blurhash/-/fast-blurhash-1.1.4.tgz",
@@ -5915,21 +5850,6 @@
"integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==",
"dev": true
},
- "node_modules/figures": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
- "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
- "dev": true,
- "dependencies": {
- "escape-string-regexp": "^1.0.5"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/filelist": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
@@ -5975,18 +5895,6 @@
"node": ">=8"
}
},
- "node_modules/find-up": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
- "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
- "dev": true,
- "dependencies": {
- "locate-path": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@@ -6384,18 +6292,6 @@
"node": ">=10.17.0"
}
},
- "node_modules/iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
- "dev": true,
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/idb": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz",
@@ -6460,51 +6356,6 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
- "node_modules/inquirer": {
- "version": "7.3.3",
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
- "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==",
- "dev": true,
- "dependencies": {
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.1.0",
- "cli-cursor": "^3.1.0",
- "cli-width": "^3.0.0",
- "external-editor": "^3.0.3",
- "figures": "^3.0.0",
- "lodash": "^4.17.19",
- "mute-stream": "0.0.8",
- "run-async": "^2.4.0",
- "rxjs": "^6.6.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0",
- "through": "^2.3.6"
- },
- "engines": {
- "node": ">=8.0.0"
- }
- },
- "node_modules/inquirer/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/inquirer/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/internal-slot": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
@@ -7132,19 +6983,6 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"devOptional": true
},
- "node_modules/locate-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
- "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
- "dev": true,
- "dependencies": {
- "p-locate": "^3.0.0",
- "path-exists": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -7349,12 +7187,6 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"devOptional": true
},
- "node_modules/mute-stream": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
- "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
- "dev": true
- },
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@@ -7580,42 +7412,6 @@
"node": ">=8"
}
},
- "node_modules/os-tmpdir": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
- "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "dependencies": {
- "p-try": "^2.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-locate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
- "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
- "dev": true,
- "dependencies": {
- "p-limit": "^2.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/p-retry": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz",
@@ -7643,15 +7439,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/package-json-from-dist": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
@@ -7718,15 +7505,6 @@
"tslib": "^2.0.3"
}
},
- "node_modules/path-exists": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
- "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -7786,12 +7564,6 @@
"node": ">=8"
}
},
- "node_modules/pathe": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
- "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
- "dev": true
- },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -7812,26 +7584,14 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
- "node_modules/pkg-up": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz",
- "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==",
- "dev": true,
- "dependencies": {
- "find-up": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/playwright": {
- "version": "1.53.2",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz",
- "integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==",
+ "version": "1.54.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz",
+ "integrity": "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.53.2"
+ "playwright-core": "1.54.1"
},
"bin": {
"playwright": "cli.js"
@@ -7844,9 +7604,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.53.2",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz",
- "integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==",
+ "version": "1.54.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz",
+ "integrity": "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -9113,33 +8873,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/run-async": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
- "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
- "dev": true,
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/rxjs": {
- "version": "6.6.7",
- "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
- "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
- "dev": true,
- "dependencies": {
- "tslib": "^1.9.0"
- },
- "engines": {
- "npm": ">=2.0.0"
- }
- },
- "node_modules/rxjs/node_modules/tslib": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
- "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
- "dev": true
- },
"node_modules/safe-array-concat": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz",
@@ -9195,12 +8928,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "dev": true
- },
"node_modules/scheduler": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
@@ -9718,12 +9445,6 @@
"node": ">=10"
}
},
- "node_modules/through": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
- "dev": true
- },
"node_modules/tinyglobby": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
@@ -9784,18 +9505,6 @@
"yarn": ">= 1.20.0"
}
},
- "node_modules/tmp": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
- "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
- "dev": true,
- "dependencies": {
- "os-tmpdir": "~1.0.2"
- },
- "engines": {
- "node": ">=0.6.0"
- }
- },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -10044,12 +9753,6 @@
"node": ">= 10.0.0"
}
},
- "node_modules/unraw": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/unraw/-/unraw-3.0.0.tgz",
- "integrity": "sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==",
- "license": "MIT"
- },
"node_modules/upath": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
@@ -10174,24 +9877,24 @@
}
},
"node_modules/vite": {
- "version": "6.3.5",
- "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
- "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz",
+ "integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
- "fdir": "^6.4.4",
+ "fdir": "^6.4.6",
"picomatch": "^4.0.2",
- "postcss": "^8.5.3",
- "rollup": "^4.34.9",
- "tinyglobby": "^0.2.13"
+ "postcss": "^8.5.6",
+ "rollup": "^4.40.0",
+ "tinyglobby": "^0.2.14"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
- "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
@@ -10200,14 +9903,14 @@
"fsevents": "~2.3.3"
},
"peerDependencies": {
- "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "@types/node": "^20.19.0 || >=22.12.0",
"jiti": ">=1.21.0",
- "less": "*",
+ "less": "^4.0.0",
"lightningcss": "^1.21.0",
- "sass": "*",
- "sass-embedded": "*",
- "stylus": "*",
- "sugarss": "*",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
@@ -10274,9 +9977,9 @@
}
},
"node_modules/vite-plugin-pwa": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-1.0.0.tgz",
- "integrity": "sha512-X77jo0AOd5OcxmWj3WnVti8n7Kw2tBgV1c8MCXFclrSlDV23ePzv2eTDIALXI2Qo6nJ5pZJeZAuX0AawvRfoeA==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-1.0.1.tgz",
+ "integrity": "sha512-STyUomQbydj7vGamtgQYIJI0YsUZ3T4pJLGBQDQPhzMse6aGSncmEN21OV35PrFsmCvmtiH+Nu1JS1ke4RqBjQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10294,7 +9997,7 @@
},
"peerDependencies": {
"@vite-pwa/assets-generator": "^1.0.0",
- "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0",
+ "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
"workbox-build": "^7.3.0",
"workbox-window": "^7.3.0"
},
@@ -10379,9 +10082,9 @@
}
},
"node_modules/vite/node_modules/fdir": {
- "version": "6.4.4",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
- "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
+ "version": "6.4.6",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
+ "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -10406,6 +10109,23 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/vite/node_modules/tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
"node_modules/wcwidth": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
diff --git a/package.json b/package.json
index 125fcc6fdb..8c17a48ff3 100644
--- a/package.json
+++ b/package.json
@@ -26,9 +26,9 @@
"@github/text-expander-element": "~2.9.2",
"@iconify-icons/mingcute": "~1.2.9",
"@justinribeiro/lite-youtube": "~1.8.2",
- "@lingui/detect-locale": "~5.3.2",
- "@lingui/macro": "~5.3.2",
- "@lingui/react": "~5.3.2",
+ "@lingui/detect-locale": "~5.3.3",
+ "@lingui/macro": "~5.3.3",
+ "@lingui/react": "~5.3.3",
"@szhsin/react-menu": "~4.4.1",
"chroma-js": "~3.1.2",
"compare-versions": "~6.1.1",
@@ -63,21 +63,21 @@
},
"devDependencies": {
"@biomejs/biome": "2.1.1",
- "@lingui/babel-plugin-lingui-macro": "~5.3.2",
- "@lingui/cli": "~5.3.2",
- "@lingui/vite-plugin": "~5.3.2",
- "@playwright/test": "~1.53.2",
- "@preact/preset-vite": "~2.10.1",
- "@types/node": "~24.0.12",
+ "@lingui/babel-plugin-lingui-macro": "~5.3.3",
+ "@lingui/cli": "~5.3.3",
+ "@lingui/vite-plugin": "~5.3.3",
+ "@playwright/test": "~1.54.1",
+ "@preact/preset-vite": "~2.10.2",
+ "@types/node": "~24.0.13",
"postcss": "~8.5.6",
"postcss-dark-theme-class": "~1.3.0",
"postcss-preset-env": "~10.2.4",
"sonda": "~0.9.0",
"twitter-text": "~3.1.0",
- "vite": "~6.3.5",
+ "vite": "~7.0.4",
"vite-plugin-generate-file": "~0.3.1",
"vite-plugin-html-config": "~2.0.2",
- "vite-plugin-pwa": "~1.0.0",
+ "vite-plugin-pwa": "~1.0.1",
"vite-plugin-remove-console": "~2.2.0",
"vite-plugin-run": "~0.6.1",
"workbox-cacheable-response": "~7.3.0",
diff --git a/vite.config.js b/vite.config.js
index 284f69a253..79778f0ddf 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -6,7 +6,7 @@ import { lingui } from '@lingui/vite-plugin';
import preact from '@preact/preset-vite';
import Sonda from 'sonda/vite';
import { uid } from 'uid/single';
-import { defineConfig, loadEnv, splitVendorChunkPlugin } from 'vite';
+import { defineConfig, loadEnv } from 'vite';
import generateFile from 'vite-plugin-generate-file';
import htmlPlugin from 'vite-plugin-html-config';
import { VitePWA } from 'vite-plugin-pwa';
@@ -83,7 +83,6 @@ export default defineConfig({
// },
],
}),
- splitVendorChunkPlugin(),
removeConsole({
includes: ['log', 'debug', 'info', 'warn', 'error'],
}),
@@ -177,9 +176,10 @@ export default defineConfig({
compose: resolve(__dirname, 'compose/index.html'),
},
output: {
- manualChunks: {
- // 'intl-segmenter-polyfill': ['@formatjs/intl-segmenter/polyfill'],
- 'tinyld-light': ['tinyld/light'],
+ manualChunks: (id) => {
+ // if (id.includes('@formatjs/intl-segmenter/polyfill')) return 'intl-segmenter-polyfill';
+ if (id.includes('tinyld/light')) return 'tinyld-light';
+ if (id.includes('node_modules')) return 'vendor';
},
chunkFileNames: (chunkInfo) => {
const { facadeModuleId } = chunkInfo;
From e416b853c932b38e33701ca4d334a777d6b3f1b8 Mon Sep 17 00:00:00 2001
From: Lim Chee Aun
Date: Sat, 12 Jul 2025 15:21:41 +0800
Subject: [PATCH 20/32] Add initial check before throwing error below
---
src/utils/browser-translator.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/utils/browser-translator.js b/src/utils/browser-translator.js
index 325cea465a..ad9a8e51c0 100644
--- a/src/utils/browser-translator.js
+++ b/src/utils/browser-translator.js
@@ -37,6 +37,11 @@ export const translate = async (text, source, target) => {
let detectedSourceLanguage;
const originalSource = source;
if (source === 'auto') {
+ if (!langDetector?.detect) {
+ return {
+ error: 'No language detector',
+ };
+ }
try {
const results = await langDetector.detect(text);
source = results[0].detectedLanguage;
From 47125476d123c57767a20babcbb2cbfe2e22edfe Mon Sep 17 00:00:00 2001
From: Lim Chee Aun
Date: Sun, 13 Jul 2025 18:02:12 +0800
Subject: [PATCH 21/32] Try possible crash fix
---
src/components/media.jsx | 10 ++++++++--
src/locales/en.po | 2 +-
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/components/media.jsx b/src/components/media.jsx
index 376c086131..94395a3cc2 100644
--- a/src/components/media.jsx
+++ b/src/components/media.jsx
@@ -291,10 +291,16 @@ function Media({
e.preventDefault();
el.style.viewTransitionName = mediaVTN;
setTimeout(() => {
- document.startViewTransition(() => {
+ try {
+ document.startViewTransition(() => {
+ el.style.viewTransitionName = '';
+ location.hash = `#${to}`;
+ });
+ } catch (e) {
+ console.error(e);
el.style.viewTransitionName = '';
location.hash = `#${to}`;
- });
+ }
}, 1);
}
} else {
diff --git a/src/locales/en.po b/src/locales/en.po
index 73842b857d..9730f7943d 100644
--- a/src/locales/en.po
+++ b/src/locales/en.po
@@ -1357,7 +1357,7 @@ msgstr ""
msgid "Filtered"
msgstr ""
-#: src/components/media.jsx:471
+#: src/components/media.jsx:477
msgid "Open file"
msgstr "Open file"
From f2368c94e3c321bb5168add2e6fde9611d93c7a1 Mon Sep 17 00:00:00 2001
From: Lim Chee Aun
Date: Sun, 13 Jul 2025 18:04:05 +0800
Subject: [PATCH 22/32] Fix vendor includes even the async import ones
---
vite.config.js | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/vite.config.js b/vite.config.js
index 79778f0ddf..362b7f9a3c 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -176,10 +176,23 @@ export default defineConfig({
compose: resolve(__dirname, 'compose/index.html'),
},
output: {
- manualChunks: (id) => {
+ manualChunks: (id, { getModuleInfo }) => {
// if (id.includes('@formatjs/intl-segmenter/polyfill')) return 'intl-segmenter-polyfill';
- if (id.includes('tinyld/light')) return 'tinyld-light';
- if (id.includes('node_modules')) return 'vendor';
+ if (/tiny.*light/.test(id)) return 'tinyld-light';
+
+ // Implement logic similar to splitVendorChunkPlugin
+ if (id.includes('node_modules')) {
+ // Check if this module is dynamically imported
+ const moduleInfo = getModuleInfo(id);
+ if (moduleInfo) {
+ // If it's imported dynamically, don't put in vendor
+ const isDynamicOnly =
+ moduleInfo.importers.length === 0 &&
+ moduleInfo.dynamicImporters.length > 0;
+ if (isDynamicOnly) return null;
+ }
+ return 'vendor';
+ }
},
chunkFileNames: (chunkInfo) => {
const { facadeModuleId } = chunkInfo;
From 11b05c20d0b16681d17d5482032e7e94db39b861 Mon Sep 17 00:00:00 2001
From: Lim Chee Aun
Date: Tue, 15 Jul 2025 16:43:43 +0800
Subject: [PATCH 23/32] Disable manualChunks
---
vite.config.js | 36 +++++++++++++++++++-----------------
1 file changed, 19 insertions(+), 17 deletions(-)
diff --git a/vite.config.js b/vite.config.js
index 362b7f9a3c..635204b678 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -176,24 +176,26 @@ export default defineConfig({
compose: resolve(__dirname, 'compose/index.html'),
},
output: {
- manualChunks: (id, { getModuleInfo }) => {
- // if (id.includes('@formatjs/intl-segmenter/polyfill')) return 'intl-segmenter-polyfill';
- if (/tiny.*light/.test(id)) return 'tinyld-light';
+ // NOTE: Comment this for now. This messes up async imports.
+ // Without SplitVendorChunkPlugin, pushing everything to vendor is not "smart" enough
+ // manualChunks: (id, { getModuleInfo }) => {
+ // // if (id.includes('@formatjs/intl-segmenter/polyfill')) return 'intl-segmenter-polyfill';
+ // if (/tiny.*light/.test(id)) return 'tinyld-light';
- // Implement logic similar to splitVendorChunkPlugin
- if (id.includes('node_modules')) {
- // Check if this module is dynamically imported
- const moduleInfo = getModuleInfo(id);
- if (moduleInfo) {
- // If it's imported dynamically, don't put in vendor
- const isDynamicOnly =
- moduleInfo.importers.length === 0 &&
- moduleInfo.dynamicImporters.length > 0;
- if (isDynamicOnly) return null;
- }
- return 'vendor';
- }
- },
+ // // Implement logic similar to splitVendorChunkPlugin
+ // if (id.includes('node_modules')) {
+ // // Check if this module is dynamically imported
+ // const moduleInfo = getModuleInfo(id);
+ // if (moduleInfo) {
+ // // If it's imported dynamically, don't put in vendor
+ // const isDynamicOnly =
+ // moduleInfo.importers.length === 0 &&
+ // moduleInfo.dynamicImporters.length > 0;
+ // if (isDynamicOnly) return null;
+ // }
+ // return 'vendor';
+ // }
+ // },
chunkFileNames: (chunkInfo) => {
const { facadeModuleId } = chunkInfo;
if (facadeModuleId && facadeModuleId.includes('icon')) {
From 6f42fd254d6540a93ab056f8706220c6eaa63544 Mon Sep 17 00:00:00 2001
From: Lim Chee Aun
Date: Tue, 15 Jul 2025 16:45:57 +0800
Subject: [PATCH 24/32] On-demand math formatting
---
package-lock.json | 10 ++
package.json | 1 +
src/components/ICONS.jsx | 1 +
src/components/status.css | 48 ++++++
src/components/status.jsx | 87 +++++++++++
src/locales/en.po | 310 ++++++++++++++++++++------------------
src/pages/sandbox.jsx | 23 ++-
7 files changed, 329 insertions(+), 151 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 39e71bb361..2b25b10546 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -41,6 +41,7 @@
"react-router-dom": "6.6.2",
"string-length": "6.0.0",
"swiped-events": "~1.2.0",
+ "temml": "~0.11.9",
"tinyld": "~1.3.4",
"toastify-js": "~1.12.0",
"uid": "~2.0.2",
@@ -9400,6 +9401,15 @@
"resolved": "https://registry.npmjs.org/swiped-events/-/swiped-events-1.2.0.tgz",
"integrity": "sha512-KRu67z1hb4sPxMdFIF2kaufYHTcWOb8NVLbIl2g5dPWZkEQ6D3wfSIVJ7iXbicTt9cO3e0vARqgx9fITtTZxQw=="
},
+ "node_modules/temml": {
+ "version": "0.11.9",
+ "resolved": "https://registry.npmjs.org/temml/-/temml-0.11.9.tgz",
+ "integrity": "sha512-+k+J6u+OGRroUAE07ynDc6jtmRocEYrRIIJvA182OBoY60nrosSA6hBvLPWdKwgXZNJQeUEdX5maJYICkCLjjw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.13.0"
+ }
+ },
"node_modules/temp-dir": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
diff --git a/package.json b/package.json
index 8c17a48ff3..68f6898dde 100644
--- a/package.json
+++ b/package.json
@@ -53,6 +53,7 @@
"react-router-dom": "6.6.2",
"string-length": "6.0.0",
"swiped-events": "~1.2.0",
+ "temml": "~0.11.9",
"tinyld": "~1.3.4",
"toastify-js": "~1.12.0",
"uid": "~2.0.2",
diff --git a/src/components/ICONS.jsx b/src/components/ICONS.jsx
index d0295475cd..b77c7adf8b 100644
--- a/src/components/ICONS.jsx
+++ b/src/components/ICONS.jsx
@@ -183,4 +183,5 @@ export const ICONS = {
module: () => import('@iconify-icons/mingcute/user-star-line'),
rtl: true,
},
+ formula: () => import('@iconify-icons/mingcute/formula-line'),
};
diff --git a/src/components/status.css b/src/components/status.css
index 3d4bc5e71b..4f28aa528f 100644
--- a/src/components/status.css
+++ b/src/components/status.css
@@ -2941,3 +2941,51 @@ a.card:is(:hover, :focus):visited {
transition-duration: 0.1s;
transition-behavior: allow-discrete;
}
+
+/* MATH */
+
+.math-block {
+ color: var(--text-insignificant-color);
+ margin: 8px 0;
+ border-top: 1px dashed var(--outline-color);
+ padding-top: 8px;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-size: 90%;
+}
+.status .inner-content math[display='block'] {
+ background-image:
+ linear-gradient(
+ var(--divider-color) var(--hairline-width),
+ transparent var(--hairline-width)
+ ),
+ linear-gradient(
+ to right,
+ var(--divider-color) var(--hairline-width),
+ transparent var(--hairline-width)
+ );
+ background-size: var(--text-size) var(--text-size);
+ overflow: auto;
+
+ --padding: 8px;
+ background-position: center var(--padding);
+ &:has(mrow) {
+ /* 0.5ex is for mrow from Temml-Local.css */
+ background-position-y: calc(var(--padding) + 0.5ex);
+ }
+ padding: var(--padding);
+ /* mask the padding with gradients */
+ mask-image:
+ linear-gradient(to top, transparent, black var(--padding)),
+ linear-gradient(to bottom, transparent, black var(--padding)),
+ linear-gradient(to left, transparent, black var(--padding)),
+ linear-gradient(to right, transparent, black var(--padding));
+ mask-composite: intersect;
+
+ animation: appear-smooth 0.3s ease-out;
+ *:nth-child(even):not(:has(*)) {
+ /* sprinkle a bit more gradual magical feel */
+ animation: appear 1s ease-out;
+ }
+}
diff --git a/src/components/status.jsx b/src/components/status.jsx
index 45f1c32bdf..6328d42479 100644
--- a/src/components/status.jsx
+++ b/src/components/status.jsx
@@ -1,4 +1,6 @@
import './status.css';
+import 'temml/dist/Temml-Local.css';
+
import '@justinribeiro/lite-youtube';
import { msg, plural } from '@lingui/core/macro';
@@ -22,6 +24,7 @@ import {
useEffect,
useLayoutEffect,
useMemo,
+ useReducer,
useRef,
useState,
} from 'preact/hooks';
@@ -222,6 +225,78 @@ function getHTMLTextForDetectLang(content, emojis) {
}
const HTTP_REGEX = /^http/i;
+
+// Follow https://mathstodon.xyz/about
+// > You can use LaTeX in toots here! Use \( and \) for inline, and \[ and \] for display mode.
+const DELIMITERS_PATTERNS = [
+ // '\\$\\$[\\s\\S]*?\\$\\$', // $$...$$
+ '\\\\\\[[\\s\\S]*?\\\\\\]', // \[...\]
+ '\\\\\\([\\s\\S]*?\\\\\\)', // \(...\)
+ // '\\\\begin\\{(?:equation\\*?|align\\*?|alignat\\*?|gather\\*?|CD)\\}[\\s\\S]*?\\\\end\\{(?:equation\\*?|align\\*?|alignat\\*?|gather\\*?|CD)\\}', // AMS environments
+ // '\\\\(?:ref|eqref)\\{[^}]*\\}', // \ref{...}, \eqref{...}
+];
+const DELIMITERS_REGEX = new RegExp(DELIMITERS_PATTERNS.join('|'), 'g');
+
+const MathBlock = ({ content, contentRef, onRevert }) => {
+ const { t } = useLingui();
+ const [mathRendered, setMathRendered] = useState(false);
+ const hasLatexContent = useMemo(
+ () => DELIMITERS_REGEX.test(content),
+ [content],
+ );
+
+ const toggleMathRendering = useCallback(
+ async (e) => {
+ e.preventDefault();
+ if (mathRendered) {
+ // Revert to original content by refreshing PostContent
+ setMathRendered(false);
+ onRevert();
+ } else {
+ // Render math
+ try {
+ // This needs global because the codebase inside temml is calling a function from global.temml 🤦♂️
+ const temml =
+ window.temml || (window.temml = (await import('temml'))?.default);
+
+ const oriignalContentRefHTML = contentRef.current.innerHTML;
+ temml.renderMathInElement(contentRef.current, {
+ fences: '(', // This should sync with DELIMITERS_REGEX
+ throwOnError: true,
+ errorCallback: (err) => {
+ console.warn('Failed to render LaTeX:', err);
+ },
+ });
+
+ const hasMath = contentRef.current.querySelector('math.tml-display');
+ const htmlChanged =
+ contentRef.current.innerHTML !== oriignalContentRefHTML;
+ if (hasMath && htmlChanged) {
+ setMathRendered(true);
+ } else {
+ showToast(t`Unable to format math`);
+ setMathRendered(false);
+ }
+ } catch (e) {
+ console.error('Failed to LaTeX:', e);
+ }
+ }
+ },
+ [mathRendered],
+ );
+
+ if (!hasLatexContent) return null;
+
+ return (
+
+ {t`Math expressions found.`} {' '}
+
+ {mathRendered ? t`Show markup` : t`Format math`}
+
+
+ );
+};
+
const PostContent =
/*memo(*/
({ post, instance, previewMode }) => {
@@ -709,6 +784,10 @@ function Status({
const mediaContainerRef = useTruncated();
const statusRef = useRef(null);
+ const [reloadPostContentCount, reloadPostContent] = useReducer(
+ (c) => c + 1,
+ 0,
+ );
const unauthInteractionErrorMessage = t`Sorry, your current logged-in instance can't interact with this post from another instance.`;
@@ -2201,6 +2280,7 @@ function Status({
inert={!!spoilerText && !showSpoiler ? true : undefined}
>
)}
+ {!!content && (
+