diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 96f7eb1e..588ffa70 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -21,8 +21,21 @@ jobs:
node-version: 22
cache: 'yarn'
+ - name: Cache electron binaries
+ uses: actions/cache@v4
+ with:
+ path: ~/.cache/electron
+ key: ${{ runner.os }}-electron-${{ hashFiles('**/yarn.lock') }}
+ restore-keys: ${{ runner.os }}-electron-
+
- name: Install dependencies
- run: yarn install --frozen-lockfile --ignore-engines
+ run: |
+ for i in 1 2 3; do
+ echo "yarn install attempt $i"
+ yarn install --frozen-lockfile --ignore-engines --network-timeout 100000 --network-concurrency 1 && break
+ sleep 5
+ [ "$i" = "3" ] && exit 1
+ done
- name: Download IPFS
run: node electron/download-ipfs && chmod +x bin/linux/ipfs
@@ -75,8 +88,21 @@ jobs:
# Use pip with --break-system-packages for CI environment
pip3 install --break-system-packages setuptools || pip3 install --user setuptools || true
+ - name: Cache electron binaries
+ uses: actions/cache@v4
+ with:
+ path: ~/Library/Caches/electron
+ key: ${{ runner.os }}-electron-${{ hashFiles('**/yarn.lock') }}
+ restore-keys: ${{ runner.os }}-electron-
+
- name: Install dependencies
- run: yarn install --frozen-lockfile --ignore-engines
+ run: |
+ for i in 1 2 3; do
+ echo "yarn install attempt $i"
+ yarn install --frozen-lockfile --ignore-engines --network-timeout 100000 --network-concurrency 1 && break
+ sleep 5
+ [ "$i" = "3" ] && exit 1
+ done
- name: Download IPFS
run: node electron/download-ipfs && chmod +x bin/mac/ipfs
@@ -135,8 +161,21 @@ jobs:
# Use pip with --break-system-packages for CI environment
pip3 install --break-system-packages setuptools || pip3 install --user setuptools || true
+ - name: Cache electron binaries
+ uses: actions/cache@v4
+ with:
+ path: ~/Library/Caches/electron
+ key: ${{ runner.os }}-electron-${{ hashFiles('**/yarn.lock') }}
+ restore-keys: ${{ runner.os }}-electron-
+
- name: Install dependencies
- run: yarn install --frozen-lockfile --ignore-engines
+ run: |
+ for i in 1 2 3; do
+ echo "yarn install attempt $i"
+ yarn install --frozen-lockfile --ignore-engines --network-timeout 100000 --network-concurrency 1 && break
+ sleep 5
+ [ "$i" = "3" ] && exit 1
+ done
- name: Download IPFS
run: node electron/download-ipfs && chmod +x bin/mac/ipfs
@@ -195,8 +234,22 @@ jobs:
node-version: 22
cache: 'yarn'
+ - name: Cache electron binaries
+ uses: actions/cache@v4
+ with:
+ path: ~/AppData/Local/electron/Cache
+ key: ${{ runner.os }}-electron-${{ hashFiles('**/yarn.lock') }}
+ restore-keys: ${{ runner.os }}-electron-
+
- name: Install dependencies
- run: yarn install --frozen-lockfile --ignore-engines --network-timeout 100000 --network-concurrency 1
+ shell: bash
+ run: |
+ for i in 1 2 3; do
+ echo "yarn install attempt $i"
+ yarn install --frozen-lockfile --ignore-engines --network-timeout 100000 --network-concurrency 1 && break
+ sleep 5
+ [ "$i" = "3" ] && exit 1
+ done
- name: Download IPFS
run: node electron/download-ipfs
@@ -235,51 +288,3 @@ jobs:
ls -R dist
exit 1
fi
-
- test-android:
- name: Test Android
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Setup Node.js v22
- uses: actions/setup-node@v4
- with:
- node-version: 22
- cache: 'yarn'
-
- - name: Setup Java 17
- uses: actions/setup-java@v4
- with:
- distribution: 'temurin'
- java-version: '17'
-
- - name: Install dependencies
- run: yarn install --frozen-lockfile --ignore-engines
-
- - name: Build React App
- run: yarn build
- env:
- CI: ''
-
- - name: Sync Capacitor
- run: npx cap sync android
-
- - name: Build Android APK
- run: |
- cd android
- chmod +x gradlew
- ./gradlew assembleDebug
-
- - name: Verify APK exists
- run: |
- APK=$(find android/app/build/outputs/apk -name "*.apk" | head -n 1)
- if [ -n "$APK" ]; then
- echo "✓ APK built successfully: $APK"
- ls -lh "$APK"
- else
- echo "✗ No APK found"
- exit 1
- fi
diff --git a/package.json b/package.json
index 1851ff0f..84a9fff7 100644
--- a/package.json
+++ b/package.json
@@ -34,13 +34,13 @@
"lodash": "4.17.21",
"memoizee": "0.4.15",
"prettier": "3.0.3",
- "react": "19.1.0",
- "react-dom": "19.1.0",
+ "react": "19.1.2",
+ "react-dom": "19.1.2",
"react-i18next": "13.2.2",
"react-markdown": "8.0.6",
"react-router-dom": "6.16.0",
"react-router-hash-link": "2.4.3",
- "react-virtuoso": "4.12.3",
+ "react-virtuoso": "4.17.0",
"rehype-raw": "7.0.0",
"rehype-sanitize": "5.0.1",
"remark-gfm": "3.0.1",
@@ -93,9 +93,9 @@
]
},
"devDependencies": {
- "@capacitor/android": "7.2.0",
- "@capacitor/cli": "7.2.0",
- "@capacitor/core": "7.2.0",
+ "@capacitor/android": "7.4.4",
+ "@capacitor/cli": "7.4.4",
+ "@capacitor/core": "7.4.4",
"@electron/rebuild": "4.0.0",
"@react-scan/vite-plugin-react-scan": "0.1.8",
"@types/memoizee": "0.4.9",
@@ -112,9 +112,9 @@
"crypto-browserify": "3.12.1",
"cz-conventional-changelog": "3.3.0",
"decompress": "4.2.1",
- "electron": "36.0.0",
+ "electron": "36.9.5",
"electron-builder": "24.13.2",
- "eslint": "8.56.0",
+ "eslint": "8.57.1",
"eslint-config-react-app": "7.0.1",
"eslint-plugin-react": "7.37.4",
"eslint-plugin-react-compiler": "19.1.0-rc.2",
@@ -126,11 +126,14 @@
"react-grab": "0.0.18",
"react-scan": "0.4.3",
"stream-browserify": "3.0.0",
- "vite": "6.2.1",
+ "vite": "6.4.1",
"vite-plugin-eslint": "1.8.1",
"vite-plugin-node-polyfills": "0.23.0",
"vite-plugin-pwa": "0.21.1",
- "wait-on": "7.0.1"
+ "wait-on": "9.0.3"
+ },
+ "resolutions": {
+ "js-yaml": "4.1.1"
},
"main": "electron/main.js",
"build": {
diff --git a/public/assets/banners/banner-3.gif b/public/assets/banners/banner-3.gif
index c1564f10..cceba530 100644
Binary files a/public/assets/banners/banner-3.gif and b/public/assets/banners/banner-3.gif differ
diff --git a/public/assets/banners/banner-4.gif b/public/assets/banners/banner-4.gif
deleted file mode 100644
index cceba530..00000000
Binary files a/public/assets/banners/banner-4.gif and /dev/null differ
diff --git a/public/assets/banners/banner-5.png b/public/assets/banners/banner-4.png
similarity index 100%
rename from public/assets/banners/banner-5.png
rename to public/assets/banners/banner-4.png
diff --git a/public/translations/ar/default.json b/public/translations/ar/default.json
index e56a340b..fd5d3ecd 100644
--- a/public/translations/ar/default.json
+++ b/public/translations/ar/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "المنتديات التي تقوم بالإشراف عليها",
"more_threads_last_year": "{{count}} مواضيع منذ {{currentTimeFilterName}} — [<1>عرض المزيد من المواضيع من السنة الماضية1>]",
"moderator_of_this_board": "هذا المستخدم هو مشرف على هذا المنتدى",
- "administrator_of_this_board": "هذا المستخدم هو مسؤول عن هذا المنتدى"
+ "administrator_of_this_board": "هذا المستخدم هو مسؤول عن هذا المنتدى",
+ "copy_content_id": "نسخ معرف المحتوى"
}
diff --git a/public/translations/bn/default.json b/public/translations/bn/default.json
index fc37522d..4e230c99 100644
--- a/public/translations/bn/default.json
+++ b/public/translations/bn/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "আপনি যে বোর্ডগুলি পরিচালনা করেন",
"more_threads_last_year": "{{count}} থ্রেড {{currentTimeFilterName}} থেকে — [<1>গত বছরের আরও থ্রেড দেখান1>]",
"moderator_of_this_board": "এই ব্যবহারকারী এই বোর্ডের একজন মডারেটর",
- "administrator_of_this_board": "এই ব্যবহারকারী এই বোর্ডের একজন প্রশাসক"
+ "administrator_of_this_board": "এই ব্যবহারকারী এই বোর্ডের একজন প্রশাসক",
+ "copy_content_id": "কনটেন্ট আইডি কপি করুন"
}
diff --git a/public/translations/cs/default.json b/public/translations/cs/default.json
index 90fbf6a1..e1f7e5d8 100644
--- a/public/translations/cs/default.json
+++ b/public/translations/cs/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "Fóra, která moderujete",
"more_threads_last_year": "{{count}} příspěvků od {{currentTimeFilterName}} — [<1>Zobrazit více příspěvků z minulého roku1>]",
"moderator_of_this_board": "Tento uživatel je moderátorem tohoto fóra",
- "administrator_of_this_board": "Tento uživatel je administrátorem tohoto fóra"
+ "administrator_of_this_board": "Tento uživatel je administrátorem tohoto fóra",
+ "copy_content_id": "kopírovat ID obsahu"
}
diff --git a/public/translations/da/default.json b/public/translations/da/default.json
index f2fa04d2..46ceeb0e 100644
--- a/public/translations/da/default.json
+++ b/public/translations/da/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "Boards du modererer",
"more_threads_last_year": "{{count}} tråde siden {{currentTimeFilterName}} — [<1>Vis flere tråde fra sidste år1>]",
"moderator_of_this_board": "Denne bruger er moderator for dette board",
- "administrator_of_this_board": "Denne bruger er administrator for dette board"
+ "administrator_of_this_board": "Denne bruger er administrator for dette board",
+ "copy_content_id": "kopier indhold-ID"
}
diff --git a/public/translations/de/default.json b/public/translations/de/default.json
index 093ccb45..854bd4c2 100644
--- a/public/translations/de/default.json
+++ b/public/translations/de/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "Boards, die Sie moderieren",
"more_threads_last_year": "{{count}} Threads seit {{currentTimeFilterName}} — [<1>Mehr Threads vom letzten Jahr anzeigen1>]",
"moderator_of_this_board": "Dieser Benutzer ist Moderator dieses Boards",
- "administrator_of_this_board": "Dieser Benutzer ist Administrator dieses Boards"
+ "administrator_of_this_board": "Dieser Benutzer ist Administrator dieses Boards",
+ "copy_content_id": "Inhalts-ID kopieren"
}
diff --git a/public/translations/el/default.json b/public/translations/el/default.json
index 2891a27d..81f2d598 100644
--- a/public/translations/el/default.json
+++ b/public/translations/el/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "Boards που διαχειρίζεστε",
"more_threads_last_year": "{{count}} νήματα από {{currentTimeFilterName}} — [<1>Προβολή περισσότερων νημάτων από το περασμένο έτος1>]",
"moderator_of_this_board": "Αυτός ο χρήστης είναι συντονιστής αυτού του πίνακα",
- "administrator_of_this_board": "Αυτός ο χρήστης είναι διαχειριστής αυτού του πίνακα"
+ "administrator_of_this_board": "Αυτός ο χρήστης είναι διαχειριστής αυτού του πίνακα",
+ "copy_content_id": "αντιγραφή ID περιεχομένου"
}
diff --git a/public/translations/en/default.json b/public/translations/en/default.json
index 8d89dea4..6e778db7 100644
--- a/public/translations/en/default.json
+++ b/public/translations/en/default.json
@@ -296,5 +296,6 @@
"boards_you_moderate": "boards you moderate",
"more_threads_last_year": "{{count}} threads since {{currentTimeFilterName}} — [<1>Show More Threads from Last Year1>]",
"moderator_of_this_board": "This user is a moderator of this board",
- "administrator_of_this_board": "This user is an administrator of this board"
+ "administrator_of_this_board": "This user is an administrator of this board",
+ "copy_content_id": "Copy content ID"
}
diff --git a/public/translations/es/default.json b/public/translations/es/default.json
index 40bed6fa..1ea2d451 100644
--- a/public/translations/es/default.json
+++ b/public/translations/es/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "tableros que moderas",
"more_threads_last_year": "{{count}} hilos desde {{currentTimeFilterName}} — [<1>Ver más hilos del año pasado1>]",
"moderator_of_this_board": "Este usuario es moderador de este tablero",
- "administrator_of_this_board": "Este usuario es administrador de este tablero"
+ "administrator_of_this_board": "Este usuario es administrador de este tablero",
+ "copy_content_id": "Copiar ID de contenido"
}
diff --git a/public/translations/fa/default.json b/public/translations/fa/default.json
index bf1750f2..efe055d4 100644
--- a/public/translations/fa/default.json
+++ b/public/translations/fa/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "تابلوهایی که مدیریت میکنید",
"more_threads_last_year": "{{count}} پست از {{currentTimeFilterName}} — [<1>نمایش بیشتر پستهای سال گذشته1>]",
"moderator_of_this_board": "این کاربر مدیر این انجمن است",
- "administrator_of_this_board": "این کاربر مدیرکل این انجمن است"
+ "administrator_of_this_board": "این کاربر مدیرکل این انجمن است",
+ "copy_content_id": "کپی شناسه محتوا"
}
diff --git a/public/translations/fi/default.json b/public/translations/fi/default.json
index a7331be5..5812e9ac 100644
--- a/public/translations/fi/default.json
+++ b/public/translations/fi/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "lautakunnat, joita valvot",
"more_threads_last_year": "{{count}} ketjua sitten {{currentTimeFilterName}} — [<1>Näytä lisää viime vuoden ketjuja1>]",
"moderator_of_this_board": "Tämä käyttäjä on tämän taulun moderaattori",
- "administrator_of_this_board": "Tämä käyttäjä on tämän taulun järjestelmänvalvoja"
+ "administrator_of_this_board": "Tämä käyttäjä on tämän taulun järjestelmänvalvoja",
+ "copy_content_id": "kopioi sisältö-ID"
}
diff --git a/public/translations/fil/default.json b/public/translations/fil/default.json
index 44caa9fc..cef59755 100644
--- a/public/translations/fil/default.json
+++ b/public/translations/fil/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "mga board na iyong minomoderate",
"more_threads_last_year": "{{count}} mga thread mula {{currentTimeFilterName}} — [<1>Ipakita ang higit pang mga thread mula noong nakaraang taon1>]",
"moderator_of_this_board": "Ang user na ito ay moderator ng board na ito",
- "administrator_of_this_board": "Ang user na ito ay administrator ng board na ito"
+ "administrator_of_this_board": "Ang user na ito ay administrator ng board na ito",
+ "copy_content_id": "kopyahin ang content ID"
}
diff --git a/public/translations/fr/default.json b/public/translations/fr/default.json
index 2d40dbfa..bf427dff 100644
--- a/public/translations/fr/default.json
+++ b/public/translations/fr/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "boards que vous modérez",
"more_threads_last_year": "{{count}} fils depuis {{currentTimeFilterName}} — [<1>Afficher plus de fils de l'année dernière1>]",
"moderator_of_this_board": "Cet utilisateur est modérateur de ce tableau",
- "administrator_of_this_board": "Cet utilisateur est administrateur de ce tableau"
+ "administrator_of_this_board": "Cet utilisateur est administrateur de ce tableau",
+ "copy_content_id": "Copier l'ID du contenu"
}
diff --git a/public/translations/he/default.json b/public/translations/he/default.json
index 3df2c535..71d414a1 100644
--- a/public/translations/he/default.json
+++ b/public/translations/he/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "לוחות שאתה מפקח עליהם",
"more_threads_last_year": "{{count}} חוטים מאז {{currentTimeFilterName}} — [<1>הצג עוד חוטים מהשנה שעברה1>]",
"moderator_of_this_board": "משתמש זה הוא מנהל של הלוח הזה",
- "administrator_of_this_board": "משתמש זה הוא מנהל מערכת של הלוח הזה"
+ "administrator_of_this_board": "משתמש זה הוא מנהל מערכת של הלוח הזה",
+ "copy_content_id": "העתק מזהה תוכן"
}
diff --git a/public/translations/hi/default.json b/public/translations/hi/default.json
index 6e928097..a863332b 100644
--- a/public/translations/hi/default.json
+++ b/public/translations/hi/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "बोर्ड जो आप मॉडरेट करते हैं",
"more_threads_last_year": "{{count}} थ्रेड्स {{currentTimeFilterName}} से — [<1>पिछले साल के अधिक थ्रेड्स दिखाएं1>]",
"moderator_of_this_board": "यह उपयोगकर्ता इस बोर्ड का मॉडरेटर है",
- "administrator_of_this_board": "यह उपयोगकर्ता इस बोर्ड का प्रशासक है"
+ "administrator_of_this_board": "यह उपयोगकर्ता इस बोर्ड का प्रशासक है",
+ "copy_content_id": "कंटेंट ID कॉपी करें"
}
diff --git a/public/translations/hu/default.json b/public/translations/hu/default.json
index cb312722..361d0ec3 100644
--- a/public/translations/hu/default.json
+++ b/public/translations/hu/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "boardok, amelyeket moderálsz",
"more_threads_last_year": "{{count}} szál {{currentTimeFilterName}} óta — [<1>Több szál megjelenítése a múlt évből1>]",
"moderator_of_this_board": "Ez a felhasználó ennek a táblának a moderátora",
- "administrator_of_this_board": "Ez a felhasználó ennek a táblának a rendszergazdája"
+ "administrator_of_this_board": "Ez a felhasználó ennek a táblának a rendszergazdája",
+ "copy_content_id": "tartalom ID másolása"
}
diff --git a/public/translations/id/default.json b/public/translations/id/default.json
index 667a8b49..ded3510c 100644
--- a/public/translations/id/default.json
+++ b/public/translations/id/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "board yang Anda moderasi",
"more_threads_last_year": "{{count}} thread sejak {{currentTimeFilterName}} — [<1>Tampilkan lebih banyak thread dari tahun lalu1>]",
"moderator_of_this_board": "Pengguna ini adalah moderator papan ini",
- "administrator_of_this_board": "Pengguna ini adalah administrator papan ini"
+ "administrator_of_this_board": "Pengguna ini adalah administrator papan ini",
+ "copy_content_id": "salin ID konten"
}
diff --git a/public/translations/it/default.json b/public/translations/it/default.json
index 56403309..20bbbd47 100644
--- a/public/translations/it/default.json
+++ b/public/translations/it/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "board che moderi",
"more_threads_last_year": "{{count}} thread da {{currentTimeFilterName}} — [<1>Mostra altri thread dell'anno scorso1>]",
"moderator_of_this_board": "Questo utente è moderatore di questa bacheca",
- "administrator_of_this_board": "Questo utente è amministratore di questa bacheca"
+ "administrator_of_this_board": "Questo utente è amministratore di questa bacheca",
+ "copy_content_id": "Copia ID contenuto"
}
diff --git a/public/translations/ja/default.json b/public/translations/ja/default.json
index 7c2ce43b..68febef2 100644
--- a/public/translations/ja/default.json
+++ b/public/translations/ja/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "あなたが管理するボード",
"more_threads_last_year": "{{count}} スレッド {{currentTimeFilterName}} から — [<1>去年のスレッドをさらに表示1>]",
"moderator_of_this_board": "このユーザーはこのボードのモデレーターです",
- "administrator_of_this_board": "このユーザーはこのボードの管理者です"
+ "administrator_of_this_board": "このユーザーはこのボードの管理者です",
+ "copy_content_id": "コンテンツIDをコピー"
}
diff --git a/public/translations/ko/default.json b/public/translations/ko/default.json
index defb7881..b0def60b 100644
--- a/public/translations/ko/default.json
+++ b/public/translations/ko/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "당신이 관리하는 게시판",
"more_threads_last_year": "{{count}} 스레드 {{currentTimeFilterName}} 이후 — [<1>작년의 스레드 더 보기1>]",
"moderator_of_this_board": "이 사용자는 이 게시판의 관리자입니다",
- "administrator_of_this_board": "이 사용자는 이 게시판의 관리자입니다"
+ "administrator_of_this_board": "이 사용자는 이 게시판의 관리자입니다",
+ "copy_content_id": "콘텐츠 ID 복사"
}
diff --git a/public/translations/mr/default.json b/public/translations/mr/default.json
index d6cf0b71..34fda63c 100644
--- a/public/translations/mr/default.json
+++ b/public/translations/mr/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "तुम्ही ज्यांचे नियंत्रण करता अशा बोर्ड्स",
"more_threads_last_year": "{{count}} ध糀ं {{currentTimeFilterName}} पासून — [<1>गेल्या वर्षातील आणखी थ्रेड्स दाखवा1>]",
"moderator_of_this_board": "हा वापरकर्ता या बोर्डचा मॉडरेटर आहे",
- "administrator_of_this_board": "हा वापरकर्ता या बोर्डचा प्रशासक आहे"
+ "administrator_of_this_board": "हा वापरकर्ता या बोर्डचा प्रशासक आहे",
+ "copy_content_id": "कंटेंट ID कॉपी करा"
}
diff --git a/public/translations/nl/default.json b/public/translations/nl/default.json
index 4589533a..1909770e 100644
--- a/public/translations/nl/default.json
+++ b/public/translations/nl/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "boards die je beheert",
"more_threads_last_year": "{{count}} threads sinds {{currentTimeFilterName}} — [<1>Meer threads van afgelopen jaar tonen1>]",
"moderator_of_this_board": "Deze gebruiker is moderator van dit bord",
- "administrator_of_this_board": "Deze gebruiker is beheerder van dit bord"
+ "administrator_of_this_board": "Deze gebruiker is beheerder van dit bord",
+ "copy_content_id": "kopieer inhoud-ID"
}
diff --git a/public/translations/no/default.json b/public/translations/no/default.json
index 1328437f..ca400413 100644
--- a/public/translations/no/default.json
+++ b/public/translations/no/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "boards du modererer",
"more_threads_last_year": "{{count}} tråder siden {{currentTimeFilterName}} — [<1>Vis flere tråder fra forrige år1>]",
"moderator_of_this_board": "Denne brukeren er moderator for dette tavlet",
- "administrator_of_this_board": "Denne brukeren er administrator for dette tavlet"
+ "administrator_of_this_board": "Denne brukeren er administrator for dette tavlet",
+ "copy_content_id": "kopier innhold-ID"
}
diff --git a/public/translations/pl/default.json b/public/translations/pl/default.json
index c6f46331..7fe844cf 100644
--- a/public/translations/pl/default.json
+++ b/public/translations/pl/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "boards, które moderujesz",
"more_threads_last_year": "{{count}} wątki od {{currentTimeFilterName}} — [<1>Pokaż więcej wątków z zeszłego roku1>]",
"moderator_of_this_board": "Ten użytkownik jest moderatorem tego forum",
- "administrator_of_this_board": "Ten użytkownik jest administratorem tego forum"
+ "administrator_of_this_board": "Ten użytkownik jest administratorem tego forum",
+ "copy_content_id": "skopiuj ID treści"
}
diff --git a/public/translations/pt/default.json b/public/translations/pt/default.json
index 8bc42dba..a0ece426 100644
--- a/public/translations/pt/default.json
+++ b/public/translations/pt/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "boards que você modera",
"more_threads_last_year": "{{count}} tópicos desde {{currentTimeFilterName}} — [<1>Mostrar mais tópicos do ano passado1>]",
"moderator_of_this_board": "Este usuário é moderador deste quadro",
- "administrator_of_this_board": "Este usuário é administrador deste quadro"
+ "administrator_of_this_board": "Este usuário é administrador deste quadro",
+ "copy_content_id": "Copiar ID do conteúdo"
}
diff --git a/public/translations/ro/default.json b/public/translations/ro/default.json
index 4b49f6ae..1d9f92e8 100644
--- a/public/translations/ro/default.json
+++ b/public/translations/ro/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "boards pe care le moderezi",
"more_threads_last_year": "{{count}} subiecte din {{currentTimeFilterName}} — [<1>Afișați mai multe subiecte de anul trecut1>]",
"moderator_of_this_board": "Acest utilizator este moderator al acestui forum",
- "administrator_of_this_board": "Acest utilizator este administrator al acestui forum"
+ "administrator_of_this_board": "Acest utilizator este administrator al acestui forum",
+ "copy_content_id": "copiați ID-ul conținutului"
}
diff --git a/public/translations/ru/default.json b/public/translations/ru/default.json
index 35807a87..6c812038 100644
--- a/public/translations/ru/default.json
+++ b/public/translations/ru/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "boards, которые вы модерируете",
"more_threads_last_year": "{{count}} темы с {{currentTimeFilterName}} — [<1>Показать больше тем с прошлого года1>]",
"moderator_of_this_board": "Этот пользователь является модератором этого форума",
- "administrator_of_this_board": "Этот пользователь является администратором этого форума"
+ "administrator_of_this_board": "Этот пользователь является администратором этого форума",
+ "copy_content_id": "копировать ID контента"
}
diff --git a/public/translations/sq/default.json b/public/translations/sq/default.json
index 68d5b71b..990ce141 100644
--- a/public/translations/sq/default.json
+++ b/public/translations/sq/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "boards që i moderoni",
"more_threads_last_year": "{{count}} tema nga {{currentTimeFilterName}} — [<1>Shfaq më shumë tema nga viti i kaluar1>]",
"moderator_of_this_board": "Ky përdorues është moderator i këtij bordi",
- "administrator_of_this_board": "Ky përdorues është administrator i këtij bordi"
+ "administrator_of_this_board": "Ky përdorues është administrator i këtij bordi",
+ "copy_content_id": "kopjo ID e përmbajtjes"
}
diff --git a/public/translations/sv/default.json b/public/translations/sv/default.json
index 63f51e35..4108c506 100644
--- a/public/translations/sv/default.json
+++ b/public/translations/sv/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "boards du modererar",
"more_threads_last_year": "{{count}} trådar sedan {{currentTimeFilterName}} — [<1>Visa fler trådar från förra året1>]",
"moderator_of_this_board": "Denna användare är moderator för detta forum",
- "administrator_of_this_board": "Denna användare är administratör för detta forum"
+ "administrator_of_this_board": "Denna användare är administratör för detta forum",
+ "copy_content_id": "kopiera innehålls-ID"
}
diff --git a/public/translations/te/default.json b/public/translations/te/default.json
index 703f6c34..c574e05c 100644
--- a/public/translations/te/default.json
+++ b/public/translations/te/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "మీరు మోడరేట్ చేసే బోర్డులు",
"more_threads_last_year": "{{count}} థ్రెడ్లు {{currentTimeFilterName}} నుండి — [<1>గత సంవత్సరం నుండి మరిన్ని థ్రెడ్లను చూపించండి1>]",
"moderator_of_this_board": "ఈ వినియోగదారు ఈ బోర్డ్ యొక్క మోడరేటర్",
- "administrator_of_this_board": "ఈ వినియోగదారు ఈ బోర్డ్ యొక్క నిర్వాహకుడు"
+ "administrator_of_this_board": "ఈ వినియోగదారు ఈ బోర్డ్ యొక్క నిర్వాహకుడు",
+ "copy_content_id": "కంటెంట్ ID కాపీ చేయండి"
}
diff --git a/public/translations/th/default.json b/public/translations/th/default.json
index bf847efd..17799c5d 100644
--- a/public/translations/th/default.json
+++ b/public/translations/th/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "บอร์ดที่คุณดูแล",
"more_threads_last_year": "{{count}} กระทู้ตั้งแต่ {{currentTimeFilterName}} — [<1>แสดงกระทู้จากปีที่แล้วเพิ่มเติม1>]",
"moderator_of_this_board": "ผู้ใช้รายนี้เป็นผู้ดูแลของบอร์ดนี้",
- "administrator_of_this_board": "ผู้ใช้รายนี้เป็นผู้ดูแลระบบของบอร์ดนี้"
+ "administrator_of_this_board": "ผู้ใช้รายนี้เป็นผู้ดูแลระบบของบอร์ดนี้",
+ "copy_content_id": "คัดลอก ID เนื้อหา"
}
diff --git a/public/translations/tr/default.json b/public/translations/tr/default.json
index 59ff603a..9d26f691 100644
--- a/public/translations/tr/default.json
+++ b/public/translations/tr/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "modere ettiğiniz panolar",
"more_threads_last_year": "{{count}} konu {{currentTimeFilterName}} itibariyle — [<1>Geçen yıldan daha fazla konu göster1>]",
"moderator_of_this_board": "Bu kullanıcı bu panonun moderatörüdür",
- "administrator_of_this_board": "Bu kullanıcı bu panonun yöneticisidir"
+ "administrator_of_this_board": "Bu kullanıcı bu panonun yöneticisidir",
+ "copy_content_id": "içerik ID'sini kopyala"
}
diff --git a/public/translations/uk/default.json b/public/translations/uk/default.json
index 89de9c18..430203fa 100644
--- a/public/translations/uk/default.json
+++ b/public/translations/uk/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "boards, які ви модеруватимете",
"more_threads_last_year": "{{count}} тем з {{currentTimeFilterName}} — [<1>Показати більше тем з минулого року1>]",
"moderator_of_this_board": "Цей користувач є модератором цього форуму",
- "administrator_of_this_board": "Цей користувач є адміністратором цього форуму"
+ "administrator_of_this_board": "Цей користувач є адміністратором цього форуму",
+ "copy_content_id": "копіювати ID контенту"
}
diff --git a/public/translations/ur/default.json b/public/translations/ur/default.json
index 8cf794e9..96b60bd3 100644
--- a/public/translations/ur/default.json
+++ b/public/translations/ur/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "وہ بورڈز جن کی آپ نگرانی کرتے ہیں",
"more_threads_last_year": "{{count}} تھریڈز {{currentTimeFilterName}} سے — [<1>گزشتہ سال کے مزید تھریڈز دکھائیں1>]",
"moderator_of_this_board": "یہ صارف اس بورڈ کا ماڈریٹر ہے",
- "administrator_of_this_board": "یہ صارف اس بورڈ کا منتظم ہے"
+ "administrator_of_this_board": "یہ صارف اس بورڈ کا منتظم ہے",
+ "copy_content_id": "مواد کی شناخت کاپی کریں"
}
diff --git a/public/translations/vi/default.json b/public/translations/vi/default.json
index f80e4f77..696a1102 100644
--- a/public/translations/vi/default.json
+++ b/public/translations/vi/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "boards bạn điều hành",
"more_threads_last_year": "{{count}} chủ đề từ {{currentTimeFilterName}} — [<1>Hiển thị thêm các chủ đề từ năm ngoái1>]",
"moderator_of_this_board": "Người dùng này là người điều hành của bảng này",
- "administrator_of_this_board": "Người dùng này là quản trị viên của bảng này"
+ "administrator_of_this_board": "Người dùng này là quản trị viên của bảng này",
+ "copy_content_id": "sao chép ID nội dung"
}
diff --git a/public/translations/zh/default.json b/public/translations/zh/default.json
index 717ef31f..6ce7a1ba 100644
--- a/public/translations/zh/default.json
+++ b/public/translations/zh/default.json
@@ -295,5 +295,6 @@
"boards_you_moderate": "您管理的板块",
"more_threads_last_year": "{{count}} 主题自 {{currentTimeFilterName}} — [<1>显示更多去年的主题1>]",
"moderator_of_this_board": "此用户是此版块的版主",
- "administrator_of_this_board": "此用户是此版块的管理员"
+ "administrator_of_this_board": "此用户是此版块的管理员",
+ "copy_content_id": "复制内容ID"
}
diff --git a/src/app.tsx b/src/app.tsx
index f65f2e08..cadb588f 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -9,10 +9,8 @@ import useSpecialThemeStore from './stores/use-special-theme-store';
import useIsMobile from './hooks/use-is-mobile';
import useTheme from './hooks/use-theme';
import { useDefaultSubplebbits } from './hooks/use-default-subplebbits';
-import { getSubplebbitAddress } from './lib/utils/route-utils';
+import { getSubplebbitAddress, isPostRoute, isPendingPostRoute } from './lib/utils/route-utils';
import styles from './app.module.css';
-import Board from './views/board';
-import Catalog from './views/catalog';
import FAQ from './views/faq';
import Home from './views/home';
import NotFound from './views/not-found';
@@ -22,6 +20,7 @@ import { DesktopBoardButtons, MobileBoardButtons } from './components/board-butt
import BoardHeader from './components/board-header';
import ChallengeModal from './components/challenge-modal';
import CreateBoardModal from './components/create-board-modal';
+import FeedCacheContainer from './components/feed-cache-container';
import ReplyModal from './components/reply-modal';
import PostForm from './components/post-form';
import SubplebbitStats from './components/subplebbit-stats';
@@ -43,6 +42,9 @@ const BoardLayout = () => {
const pendingPost = useAccountComment({ commentIndex: accountCommentIndex ? parseInt(accountCommentIndex) : undefined });
const { closeCreateBoardModal } = useCreateBoardModalStore();
+ const isOnPostRoute = isPostRoute(location.pathname);
+ const isOnPendingPostRoute = isPendingPostRoute(location.pathname);
+
// Christmas theme
const { isEnabled: isSpecialEnabled } = useSpecialThemeStore();
useEffect(() => {
@@ -75,9 +77,8 @@ const BoardLayout = () => {
{isMobile
? (subplebbitAddress || isInAllView || isInModView || isInSubscriptionsView || pendingPost?.subplebbitAddress) && (
<>
-
-
+
>
)
: (subplebbitAddress || isInAllView || isInModView || isInSubscriptionsView || pendingPost?.subplebbitAddress) && (
@@ -87,7 +88,8 @@ const BoardLayout = () => {
>
)}
-
+
+ {(isOnPostRoute || isOnPendingPostRoute) && }
);
};
@@ -142,36 +144,36 @@ const App = () => (
} />
} />
}>
- } />
- } />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
} />
- } />
- } />
-
- } />
- } />
- } />
- } />
-
- } />
- } />
- } />
- } />
-
- } />
- } />
-
- } />
- } />
- } />
- } />
-
} />
} />
} />
} />
} />
} />
+
+ } />
+ } />
} />
} />
diff --git a/src/components/catalog-row/catalog-row.tsx b/src/components/catalog-row/catalog-row.tsx
index e3569adf..084a2616 100644
--- a/src/components/catalog-row/catalog-row.tsx
+++ b/src/components/catalog-row/catalog-row.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useRef, useState } from 'react';
+import { useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';
import { Link, useLocation, useParams } from 'react-router-dom';
@@ -24,6 +24,7 @@ import { ContentPreview } from '../../views/home/popular-threads-box';
import PostMenuDesktop from '../post-desktop/post-menu-desktop';
import styles from './catalog-row.module.css';
import _ from 'lodash';
+import { selectPostMenuProps } from '../../lib/utils/post-menu-props';
interface CatalogPostMediaProps {
cid: string;
@@ -142,6 +143,7 @@ const CatalogPost = ({ post }: { post: Comment }) => {
const isInSubscriptionsView = isSubscriptionsView(location.pathname, params);
const defaultSubplebbits = useDefaultSubplebbits();
const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, defaultSubplebbits) : '';
+ const postMenuProps = useMemo(() => selectPostMenuProps(post), [post]);
const postLink = isInAllView && isDescription ? '/all/description' : `/${boardPath}/${isDescription ? 'description' : isRules ? 'rules' : `thread/${cid}`}`;
@@ -287,7 +289,7 @@ const CatalogPost = ({ post }: { post: Comment }) => {
)}
-
+
{(showOPComment || isTextOnlyThread) && (hasThumbnail ? postContent : {postContent})}
diff --git a/src/components/challenge-modal/challenge-modal.tsx b/src/components/challenge-modal/challenge-modal.tsx
index d104fb61..1eb1b3b2 100644
--- a/src/components/challenge-modal/challenge-modal.tsx
+++ b/src/components/challenge-modal/challenge-modal.tsx
@@ -293,7 +293,9 @@ const Challenge = ({ challenge, closeModal }: ChallengeProps) => {
{showIframeConfirmation ? (
<>
-
board wants to open {readableUrl || 'an external site'}
+
+ {shortSubplebbitAddress || subplebbitAddress || 'unknown board'} wants to open {readableUrl || 'an external site'}
+
diff --git a/src/components/feed-cache-container/feed-cache-container.module.css b/src/components/feed-cache-container/feed-cache-container.module.css
new file mode 100644
index 00000000..79003f5c
--- /dev/null
+++ b/src/components/feed-cache-container/feed-cache-container.module.css
@@ -0,0 +1,13 @@
+.visible {
+}
+
+.hidden {
+ visibility: hidden;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ pointer-events: none;
+ overflow: hidden;
+ height: 0;
+}
diff --git a/src/components/feed-cache-container/feed-cache-container.tsx b/src/components/feed-cache-container/feed-cache-container.tsx
new file mode 100644
index 00000000..3547e1ec
--- /dev/null
+++ b/src/components/feed-cache-container/feed-cache-container.tsx
@@ -0,0 +1,99 @@
+import { useEffect } from 'react';
+import { useLocation } from 'react-router-dom';
+import useFeedCacheStore, { CachedFeed } from '../../stores/use-feed-cache-store';
+import { getFeedCacheKey, getFeedType, isFeedRoute } from '../../lib/utils/route-utils';
+import Board from '../../views/board';
+import Catalog from '../../views/catalog';
+import styles from './feed-cache-container.module.css';
+
+interface FeedContextFromKey {
+ viewType: 'all' | 'subs' | 'mod' | 'board';
+ boardIdentifier?: string;
+ timeFilterName?: string;
+}
+
+const parseFeedKey = (key: string): FeedContextFromKey => {
+ const segments = key.split('/').filter(Boolean);
+
+ const filteredSegments = segments.filter((s) => s !== 'catalog');
+ if (filteredSegments[0] === 'all') {
+ return {
+ viewType: 'all',
+ timeFilterName: filteredSegments[1],
+ };
+ }
+ if (filteredSegments[0] === 'subs') {
+ return {
+ viewType: 'subs',
+ timeFilterName: filteredSegments[1],
+ };
+ }
+ if (filteredSegments[0] === 'mod') {
+ return {
+ viewType: 'mod',
+ timeFilterName: filteredSegments[1],
+ };
+ }
+
+ return {
+ viewType: 'board',
+ boardIdentifier: filteredSegments[0],
+ timeFilterName: filteredSegments[1],
+ };
+};
+
+interface CachedFeedWrapperProps {
+ feed: CachedFeed;
+ isVisible: boolean;
+}
+
+const CachedFeedWrapper = ({ feed, isVisible }: CachedFeedWrapperProps) => {
+ const context = parseFeedKey(feed.key);
+
+ return (
+
+ {feed.type === 'catalog' ? (
+
+ ) : (
+
+ )}
+
+ );
+};
+
+const FeedCacheContainer = () => {
+ const location = useLocation();
+ const { cachedFeeds, accessFeed } = useFeedCacheStore();
+
+ const currentFeedKey = getFeedCacheKey(location.pathname);
+ const isOnFeedRoute = isFeedRoute(location.pathname);
+ const feedType = getFeedType(location.pathname);
+
+ useEffect(() => {
+ if (isOnFeedRoute && currentFeedKey && feedType) {
+ accessFeed(currentFeedKey, feedType);
+ }
+ }, [currentFeedKey, isOnFeedRoute, feedType, accessFeed]);
+
+ return (
+ <>
+ {cachedFeeds.map((feed) => (
+
+ ))}
+ >
+ );
+};
+
+export default FeedCacheContainer;
diff --git a/src/components/feed-cache-container/index.ts b/src/components/feed-cache-container/index.ts
new file mode 100644
index 00000000..74e8a166
--- /dev/null
+++ b/src/components/feed-cache-container/index.ts
@@ -0,0 +1 @@
+export { default } from './feed-cache-container';
diff --git a/src/components/post-desktop/post-desktop.tsx b/src/components/post-desktop/post-desktop.tsx
index fac937ec..bc30671b 100644
--- a/src/components/post-desktop/post-desktop.tsx
+++ b/src/components/post-desktop/post-desktop.tsx
@@ -1,4 +1,4 @@
-import { useState } from 'react';
+import { useMemo, useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { Link, useLocation, useParams } from 'react-router-dom';
import { Comment, useAuthorAvatar, useEditedComment } from '@plebbit/plebbit-react-hooks';
@@ -33,6 +33,7 @@ import { create } from 'zustand';
import _ from 'lodash';
import { shouldShowSnow } from '../../lib/snow';
import useReplyModalStore from '../../stores/use-reply-modal-store';
+import { selectPostMenuProps } from '../../lib/utils/post-menu-props';
interface ShowOmittedRepliesState {
showOmittedReplies: Record;
@@ -66,6 +67,7 @@ const PostInfo = ({ post, postReplyCount = 0, roles, isHidden }: PostProps) => {
const { hideAvatars } = useAvatarVisibilityStore();
const defaultSubplebbits = useDefaultSubplebbits();
const boardPath = subplebbitAddress ? getBoardPath(subplebbitAddress, defaultSubplebbits) : undefined;
+ const postMenuProps = useMemo(() => selectPostMenuProps(post), [post]);
const params = useParams();
const location = useLocation();
@@ -133,9 +135,9 @@ const PostInfo = ({ post, postReplyCount = 0, roles, isHidden }: PostProps) => {
{' '}
+ />
- )}
+ )}{' '}
{!(isDescription || isRules) && (
<>
@@ -226,7 +228,7 @@ const PostInfo = ({ post, postReplyCount = 0, roles, isHidden }: PostProps) => {
)}
- {!(removed || deleted) &&
}
+ {!(removed || deleted) &&
}
{cid &&
parentCid &&
replies &&
diff --git a/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx b/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx
index 73ee6665..1c44cc95 100644
--- a/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx
+++ b/src/components/post-desktop/post-menu-desktop/post-menu-desktop.tsx
@@ -1,31 +1,38 @@
-import { useState } from 'react';
+import { memo, useState } from 'react';
import { createPortal } from 'react-dom';
import { useLocation, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react';
-import { Comment, useBlock } from '@plebbit/plebbit-react-hooks';
+import { useBlock } from '@plebbit/plebbit-react-hooks';
import styles from './post-menu-desktop.module.css';
import { getCommentMediaInfo } from '../../../lib/utils/media-utils';
-import { copyShareLinkToClipboard, isValidURL } from '../../../lib/utils/url-utils';
+import { copyShareLinkToClipboard, isValidURL, type ShareLinkType } from '../../../lib/utils/url-utils';
+import { copyToClipboard } from '../../../lib/utils/clipboard-utils';
+import { getBoardPath } from '../../../lib/utils/route-utils';
+import { useDefaultSubplebbits } from '../../../hooks/use-default-subplebbits';
import { isAllView, isCatalogView, isPostPageView, isSubscriptionsView } from '../../../lib/utils/view-utils';
import useHide from '../../../hooks/use-hide';
import _ from 'lodash';
+import { PostMenuProps } from '../../../lib/utils/post-menu-props';
-interface PostMenuDesktopProps {
- cid: string;
- isDescription?: boolean;
- isRules?: boolean;
- subplebbitAddress: string;
- onClose: () => void;
-}
+type CopyLinkButtonProps =
+ | { cid: string; subplebbitAddress: string; linkType: 'thread'; onClose: () => void }
+ | { subplebbitAddress: string; linkType: Exclude
; onClose: () => void; cid?: undefined };
-const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuDesktopProps) => {
+const CopyLinkButton = ({ cid, subplebbitAddress, linkType, onClose }: CopyLinkButtonProps) => {
const { t } = useTranslation();
+ const defaultSubplebbits = useDefaultSubplebbits();
+ const boardIdentifier = getBoardPath(subplebbitAddress, defaultSubplebbits);
return (
{
- copyShareLinkToClipboard(subplebbitAddress, cid);
- onClose();
+ onClick={async () => {
+ try {
+ await copyShareLinkToClipboard(boardIdentifier, linkType, cid);
+ } catch (error) {
+ console.error('Failed to copy share link', error);
+ } finally {
+ onClose();
+ }
}}
>
{t('copy_link')}
@@ -33,6 +40,25 @@ const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuDesktopProp
);
};
+const CopyContentIdButton = ({ cid, onClose }: { cid: string; onClose: () => void }) => {
+ const { t } = useTranslation();
+ return (
+
{
+ try {
+ await copyToClipboard(cid);
+ } catch (error) {
+ console.error('Failed to copy content id', error);
+ } finally {
+ onClose();
+ }
+ }}
+ >
+
{t('copy_content_id')}
+
+ );
+};
+
const ImageSearchButton = ({ url, onClose }: { url: string; onClose: () => void }) => {
const { t } = useTranslation();
const [isImageSearchMenuOpen, setIsImageSearchMenuOpen] = useState(false);
@@ -88,14 +114,18 @@ const BlockBoardButton = ({ address }: { address: string }) => {
);
};
-const PostMenuDesktop = ({ post }: { post: Comment }) => {
+type PostMenuDesktopProps = {
+ postMenu: PostMenuProps;
+};
+
+const PostMenuDesktop = ({ postMenu }: PostMenuDesktopProps) => {
const { t } = useTranslation();
- const { author, cid, isDescription, isRules, link, thumbnailUrl, linkWidth, linkHeight, postCid, subplebbitAddress } = post || {};
- const commentMediaInfo = getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
+ const { authorAddress, cid, isDescription, isRules, link, thumbnailUrl, linkWidth, linkHeight, postCid, subplebbitAddress } = postMenu || {};
+ const commentMediaInfo = getCommentMediaInfo(link || '', thumbnailUrl || '', linkWidth ?? 0, linkHeight ?? 0);
const { thumbnail, type, url } = commentMediaInfo || {};
const [menuBtnRotated, setMenuBtnRotated] = useState(false);
- const { hidden, unhide, hide } = useHide({ cid });
+ const { hidden, unhide, hide } = useHide({ cid: cid || '' });
const location = useLocation();
const params = useParams();
@@ -118,7 +148,7 @@ const PostMenuDesktop = ({ post }: { post: Comment }) => {
const headingId = useId();
const handleMenuClick = () => {
- if (cid) {
+ if (cid || isDescription || isRules) {
setMenuBtnRotated((prev) => !prev);
}
};
@@ -142,7 +172,10 @@ const PostMenuDesktop = ({ post }: { post: Comment }) => {
createPortal(
- {cid && subplebbitAddress &&
}
+ {cid && subplebbitAddress &&
}
+ {cid &&
}
+ {!cid && isDescription && subplebbitAddress &&
}
+ {!cid && isRules && subplebbitAddress &&
}
{!(isInPostPageView && postCid === cid) && !isDescription && !isRules && (
{
)}
{link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url &&
}
- {!isDescription && !isRules &&
}
- {(isInAllView || isInSubscriptionsView) &&
}
+ {!isDescription && !isRules && authorAddress &&
}
+ {!isDescription && !isRules && (isInAllView || isInSubscriptionsView) && subplebbitAddress &&
}
,
document.body,
@@ -165,4 +198,4 @@ const PostMenuDesktop = ({ post }: { post: Comment }) => {
);
};
-export default PostMenuDesktop;
+export default memo(PostMenuDesktop);
diff --git a/src/components/post-form/post-form.module.css b/src/components/post-form/post-form.module.css
index 649c43c1..6bf1341c 100644
--- a/src/components/post-form/post-form.module.css
+++ b/src/components/post-form/post-form.module.css
@@ -137,6 +137,15 @@
.postFormTable button {
padding: 3px 6px;
}
+
+ .postFormMobile {
+ margin-bottom: 6px;
+ }
+
+ .postFormTable {
+ padding-top: 20px;
+ padding-bottom: 3px;
+ }
}
@media (min-width: 640px) {
diff --git a/src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx b/src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx
index 86f175a0..27288e7b 100644
--- a/src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx
+++ b/src/components/post-mobile/post-menu-mobile/post-menu-mobile.tsx
@@ -1,36 +1,46 @@
-import { useState } from 'react';
+import { memo, useState } from 'react';
import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';
import { Comment, useBlock } from '@plebbit/plebbit-react-hooks';
import { autoUpdate, flip, FloatingFocusManager, offset, shift, useClick, useDismiss, useFloating, useId, useInteractions, useRole } from '@floating-ui/react';
import styles from './post-menu-mobile.module.css';
import { getCommentMediaInfo } from '../../../lib/utils/media-utils';
-import { copyShareLinkToClipboard, isValidURL } from '../../../lib/utils/url-utils';
+import { copyShareLinkToClipboard, isValidURL, type ShareLinkType } from '../../../lib/utils/url-utils';
+import { copyToClipboard } from '../../../lib/utils/clipboard-utils';
+import { getBoardPath } from '../../../lib/utils/route-utils';
+import { useDefaultSubplebbits } from '../../../hooks/use-default-subplebbits';
import useEditCommentPrivileges from '../../../hooks/use-author-privileges';
import useHide from '../../../hooks/use-hide';
import EditMenu from '../../edit-menu/edit-menu';
import { isBoardView, isPostPageView } from '../../../lib/utils/view-utils';
import { useLocation, useParams } from 'react-router-dom';
+import { PostMenuProps } from '../../../lib/utils/post-menu-props';
-interface PostMenuMobileProps {
- cid: string;
- isDescription?: boolean;
+type HideButtonProps = {
+ cid?: string;
isReply?: boolean;
- isRules?: boolean;
postCid?: string;
- subplebbitAddress?: string;
onClose?: () => void;
-}
+};
-const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuMobileProps) => {
+type CopyLinkButtonProps =
+ | { cid: string; subplebbitAddress: string; linkType: 'thread'; onClose: () => void }
+ | { subplebbitAddress: string; linkType: Exclude
; onClose: () => void; cid?: undefined };
+
+const CopyLinkButton = ({ cid, subplebbitAddress, linkType, onClose }: CopyLinkButtonProps) => {
const { t } = useTranslation();
+ const defaultSubplebbits = useDefaultSubplebbits();
+ const boardIdentifier = getBoardPath(subplebbitAddress, defaultSubplebbits);
return (
{
- if (subplebbitAddress) {
- copyShareLinkToClipboard(subplebbitAddress, cid);
+ onClick={async () => {
+ try {
+ await copyShareLinkToClipboard(boardIdentifier, linkType, cid);
+ } catch (error) {
+ console.error('Failed to copy share link', error);
+ } finally {
+ onClose();
}
- onClose && onClose();
}}
>
{t('copy_link')}
@@ -38,6 +48,25 @@ const CopyLinkButton = ({ cid, subplebbitAddress, onClose }: PostMenuMobileProps
);
};
+const CopyContentIdButton = ({ cid, onClose }: { cid: string; onClose: () => void }) => {
+ const { t } = useTranslation();
+ return (
+
{
+ try {
+ await copyToClipboard(cid);
+ } catch (error) {
+ console.error('Failed to copy content id', error);
+ } finally {
+ onClose();
+ }
+ }}
+ >
+
{t('copy_content_id')}
+
+ );
+};
+
const ImageSearchButtons = ({ url, onClose }: { url: string; onClose: () => void }) => {
const { t } = useTranslation();
return (
@@ -55,7 +84,7 @@ const ImageSearchButtons = ({ url, onClose }: { url: string; onClose: () => void
);
};
-const HidePostButton = ({ cid, isReply, onClose, postCid }: PostMenuMobileProps) => {
+const HidePostButton = ({ cid, isReply, onClose, postCid }: HideButtonProps) => {
const { t } = useTranslation();
const { hide, hidden, unhide } = useHide({ cid });
const isInPostView = isPostPageView(useLocation().pathname, useParams());
@@ -96,9 +125,15 @@ const BlockBoardButton = ({ address }: { address: string }) => {
);
};
-const PostMenuMobile = ({ post }: { post: Comment }) => {
- const { author, cid, deleted, isDescription, isRules, link, linkHeight, linkWidth, parentCid, postCid, removed, subplebbitAddress, thumbnailUrl } = post || {};
- const { isAccountMod, isAccountCommentAuthor } = useEditCommentPrivileges({ commentAuthorAddress: author?.address, subplebbitAddress });
+type PostMenuMobileProps = {
+ postMenu: PostMenuProps;
+ editMenuPost: Comment;
+};
+
+const PostMenuMobile = ({ postMenu, editMenuPost }: PostMenuMobileProps) => {
+ const { authorAddress, cid, deleted, isDescription, isRules, link, linkHeight, linkWidth, parentCid, postCid, removed, subplebbitAddress, thumbnailUrl } =
+ postMenu || {};
+ const { isAccountMod, isAccountCommentAuthor } = useEditCommentPrivileges({ commentAuthorAddress: authorAddress, subplebbitAddress });
const commentMediaInfo = getCommentMediaInfo(link, thumbnailUrl, linkWidth, linkHeight);
const { thumbnail, type, url } = commentMediaInfo || {};
const [isMenuOpen, setIsMenuOpen] = useState(false);
@@ -116,7 +151,7 @@ const PostMenuMobile = ({ post }: { post: Comment }) => {
const headingId = useId();
const handleMenuClick = () => {
- if (cid) {
+ if (cid || isDescription || isRules) {
setIsMenuOpen((prev) => !prev);
}
};
@@ -137,9 +172,12 @@ const PostMenuMobile = ({ post }: { post: Comment }) => {
createPortal(
- {cid && subplebbitAddress && }
- {cid && subplebbitAddress && }
- {cid && subplebbitAddress && !isDescription && !isRules && }
+ {cid && subplebbitAddress && }
+ {cid && }
+ {!cid && isDescription && subplebbitAddress && }
+ {!cid && isRules && subplebbitAddress && }
+ {cid && subplebbitAddress && }
+ {cid && subplebbitAddress && !isDescription && !isRules && authorAddress && }
{cid && subplebbitAddress && !isInBoardView && !isDescription && !isRules && }
{link && isValidURL(link) && (type === 'image' || type === 'gif' || thumbnail) && url && }
@@ -150,11 +188,11 @@ const PostMenuMobile = ({ post }: { post: Comment }) => {
)}
{(isAccountMod || isAccountCommentAuthor) && cid && (
-
+
)}
>
);
};
-export default PostMenuMobile;
+export default memo(PostMenuMobile);
diff --git a/src/components/post-mobile/post-mobile.tsx b/src/components/post-mobile/post-mobile.tsx
index e158758a..87ab6f93 100644
--- a/src/components/post-mobile/post-mobile.tsx
+++ b/src/components/post-mobile/post-mobile.tsx
@@ -1,4 +1,4 @@
-import { useState } from 'react';
+import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Link, useLocation, useParams } from 'react-router-dom';
import { Comment, useAuthorAvatar, useEditedComment } from '@plebbit/plebbit-react-hooks';
@@ -28,6 +28,7 @@ import Tooltip from '../tooltip';
import { PostProps } from '../../views/post/post';
import _ from 'lodash';
import useReplyModalStore from '../../stores/use-reply-modal-store';
+import { selectPostMenuProps } from '../../lib/utils/post-menu-props';
const PostInfoAndMedia = ({ post, postReplyCount = 0, roles }: PostProps) => {
const { t } = useTranslation();
@@ -71,6 +72,7 @@ const PostInfoAndMedia = ({ post, postReplyCount = 0, roles }: PostProps) => {
const hasThumbnail = getHasThumbnail(commentMediaInfo, link);
const stateString = useStateString(post);
+ const postMenuProps = useMemo(() => selectPostMenuProps(post), [post]);
const handleUserAddressClick = useAuthorAddressClick();
const numberOfPostsByAuthor = document.querySelectorAll(`[data-author-address="${shortAddress}"][data-post-cid="${postCid}"]`).length;
@@ -98,7 +100,7 @@ const PostInfoAndMedia = ({ post, postReplyCount = 0, roles }: PostProps) => {
return (
<>
-
+
diff --git a/src/components/subplebbit-description/subplebbit-description.tsx b/src/components/subplebbit-description/subplebbit-description.tsx
index 041d86eb..046d25b0 100644
--- a/src/components/subplebbit-description/subplebbit-description.tsx
+++ b/src/components/subplebbit-description/subplebbit-description.tsx
@@ -20,10 +20,11 @@ const SubplebbitDescription = ({ avatarUrl, createdAt, description, replyCount,
const location = useLocation();
const isInAllView = isAllView(location.pathname);
const multisubMetadata = useMultisubMetadata();
+ const descriptionSubplebbitAddress = isInAllView ? 'all' : subplebbitAddress;
const post = {
isDescription: true,
- subplebbitAddress,
+ subplebbitAddress: descriptionSubplebbitAddress,
timestamp: isInAllView ? multisubMetadata?.createdAt : createdAt,
author: { displayName: _.capitalize(t('anonymous')) },
content: isInAllView ? multisubMetadata?.description : description,
diff --git a/src/generated/asset-manifest.ts b/src/generated/asset-manifest.ts
index f72d68c3..3271841f 100644
--- a/src/generated/asset-manifest.ts
+++ b/src/generated/asset-manifest.ts
@@ -2,12 +2,6 @@
// Run 'node scripts/generate-asset-manifest.js' to regenerate
// This file is generated from public/assets/ directory
-export const BANNERS = [
- 'assets/banners/banner-1.jpg',
- 'assets/banners/banner-2.jpg',
- 'assets/banners/banner-3.gif',
- 'assets/banners/banner-4.gif',
- 'assets/banners/banner-5.png',
-] as const;
+export const BANNERS = ['assets/banners/banner-1.jpg', 'assets/banners/banner-2.jpg', 'assets/banners/banner-3.gif', 'assets/banners/banner-4.png'] as const;
export const NOT_FOUND_IMAGES = ['assets/not-found/not-found-1.png'] as const;
diff --git a/src/hooks/use-time-filter.ts b/src/hooks/use-time-filter.ts
index ccfda5b5..9c34c564 100644
--- a/src/hooks/use-time-filter.ts
+++ b/src/hooks/use-time-filter.ts
@@ -53,6 +53,34 @@ if (secondsSinceLastVisit > 30 * day) {
export const timeFilterNames = [lastVisitTimeFilterName, '1h', '12h', '24h', '48h', '1w', '1m', '1y', 'all'];
+export const timeFilterNameToSeconds = (timeFilterName: string | undefined): number | undefined => {
+ if (!timeFilterName || timeFilterName === 'all') return undefined;
+
+ if (timeFilterName in timeFilterNamesToSeconds) {
+ return timeFilterNamesToSeconds[timeFilterName as keyof typeof timeFilterNamesToSeconds];
+ }
+
+ const match = timeFilterName.match(/^(\d+)([hdwmy])$/);
+ if (match) {
+ const [, value, unit] = match;
+ const numValue = parseInt(value, 10);
+ switch (unit) {
+ case 'h':
+ return numValue * 60 * 60;
+ case 'd':
+ return numValue * 24 * 60 * 60;
+ case 'w':
+ return numValue * 7 * 24 * 60 * 60;
+ case 'm':
+ return numValue * 30 * 24 * 60 * 60;
+ case 'y':
+ return numValue * 365 * 24 * 60 * 60;
+ }
+ }
+
+ return undefined;
+};
+
function convertTimeStringToSeconds(timeString: string): number {
const match = timeString.match(/^(\d+)([hdwmy])$/);
if (!match) {
diff --git a/src/lib/utils/post-menu-props.ts b/src/lib/utils/post-menu-props.ts
new file mode 100644
index 00000000..c1bc50d1
--- /dev/null
+++ b/src/lib/utils/post-menu-props.ts
@@ -0,0 +1,33 @@
+import { Comment } from '@plebbit/plebbit-react-hooks';
+
+export type PostMenuProps = {
+ cid?: string;
+ postCid?: string;
+ parentCid?: string;
+ subplebbitAddress?: string;
+ isDescription?: boolean;
+ isRules?: boolean;
+ authorAddress?: string;
+ link?: string;
+ linkWidth?: number;
+ linkHeight?: number;
+ thumbnailUrl?: string;
+ deleted?: boolean;
+ removed?: boolean;
+};
+
+export const selectPostMenuProps = (post?: Comment): PostMenuProps => ({
+ cid: post?.cid,
+ postCid: post?.postCid,
+ parentCid: post?.parentCid,
+ subplebbitAddress: post?.subplebbitAddress,
+ isDescription: post?.isDescription,
+ isRules: post?.isRules,
+ authorAddress: post?.author?.address,
+ link: post?.link,
+ linkWidth: post?.linkWidth,
+ linkHeight: post?.linkHeight,
+ thumbnailUrl: post?.thumbnailUrl,
+ deleted: post?.deleted,
+ removed: post?.removed,
+});
diff --git a/src/lib/utils/route-utils.ts b/src/lib/utils/route-utils.ts
index 265154fa..bdef32f6 100644
--- a/src/lib/utils/route-utils.ts
+++ b/src/lib/utils/route-utils.ts
@@ -100,3 +100,85 @@ export const isDirectoryBoard = (identifier: string, subplebbits: MultisubSubple
const directoryToAddress = getDirectoryToAddressMap(subplebbits);
return directoryToAddress.has(identifier);
};
+
+export const isFeedRoute = (pathname: string): boolean => {
+ const normalizedPath = pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
+
+ if (normalizedPath.includes('/thread/')) return false;
+ if (normalizedPath.endsWith('/description')) return false;
+ if (normalizedPath.endsWith('/rules')) return false;
+ if (normalizedPath.startsWith('/pending/')) return false;
+
+ const pathWithoutSettings = normalizedPath.replace(/\/settings$/, '');
+
+ if (pathWithoutSettings.startsWith('/all')) return true;
+ if (pathWithoutSettings.startsWith('/subs')) return true;
+ if (pathWithoutSettings.startsWith('/mod')) return true;
+
+ const segments = pathWithoutSettings.split('/').filter(Boolean);
+ if (segments.length >= 1) {
+ if (segments.length === 1) return true;
+ if (segments.length === 2 && segments[1] === 'catalog') return true;
+ if (segments.length === 2 && /^(?:\d+(?:h|d|w|m|y)|all)$/.test(segments[1])) return true;
+ if (segments.length === 3 && segments[1] === 'catalog' && /^(?:\d+(?:h|d|w|m|y)|all)$/.test(segments[2])) return true;
+ }
+
+ return false;
+};
+
+export const isPostRoute = (pathname: string): boolean => {
+ const normalizedPath = pathname.replace(/\/settings$/, '');
+
+ if (normalizedPath.includes('/thread/')) return true;
+ if (normalizedPath.endsWith('/description')) return true;
+ if (normalizedPath.endsWith('/rules')) return true;
+
+ return false;
+};
+
+export const isPendingPostRoute = (pathname: string): boolean => {
+ const normalizedPath = pathname.replace(/\/settings$/, '');
+ return normalizedPath.startsWith('/pending/');
+};
+
+export const getFeedCacheKey = (pathname: string): string | null => {
+ let normalizedPath = pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
+ normalizedPath = normalizedPath.replace(/\/settings$/, '');
+
+ if (normalizedPath.includes('/thread/')) {
+ const parts = normalizedPath.split('/thread/');
+ return parts[0] || null;
+ }
+
+ if (normalizedPath.endsWith('/description') || normalizedPath.endsWith('/rules')) {
+ return normalizedPath.replace(/\/(description|rules)$/, '');
+ }
+
+ if (normalizedPath.startsWith('/pending/')) {
+ return null;
+ }
+
+ if (isFeedRoute(pathname)) {
+ return normalizedPath;
+ }
+
+ return null;
+};
+
+export const getFeedType = (pathname: string): 'board' | 'catalog' | null => {
+ const normalizedPath = pathname.replace(/\/settings$/, '');
+
+ if (normalizedPath.includes('/catalog')) {
+ return 'catalog';
+ }
+
+ if (isFeedRoute(pathname)) {
+ return 'board';
+ }
+
+ if (isPostRoute(pathname)) {
+ return 'board';
+ }
+
+ return null;
+};
diff --git a/src/lib/utils/url-utils.ts b/src/lib/utils/url-utils.ts
index 2443ac45..a8f05747 100644
--- a/src/lib/utils/url-utils.ts
+++ b/src/lib/utils/url-utils.ts
@@ -17,10 +17,24 @@ export const isValidURL = (url: string) => {
}
};
-export const copyShareLinkToClipboard = async (subplebbitAddress: string, cid: string) => {
- const shareLink = `https://pleb.bz/p/${subplebbitAddress}/c/${cid}?redirect=5chan.app`;
+export type ShareLinkType = 'thread' | 'description' | 'rules';
+
+// Copies a share link to clipboard for a board, thread, description, or rules page
+export function copyShareLinkToClipboard(boardIdentifier: string, linkType: 'thread', cid: string): Promise;
+export function copyShareLinkToClipboard(boardIdentifier: string, linkType: Exclude, cid?: undefined): Promise;
+export async function copyShareLinkToClipboard(boardIdentifier: string, linkType: ShareLinkType, cid?: string): Promise {
+ if (linkType === 'thread') {
+ if (!cid) {
+ throw new Error('copyShareLinkToClipboard: thread links require a cid');
+ }
+ const shareLink = `https://5chan.app/${boardIdentifier}/thread/${cid}`;
+ await copyToClipboard(shareLink);
+ return;
+ }
+
+ const shareLink = `https://5chan.app/${boardIdentifier}/${linkType}`;
await copyToClipboard(shareLink);
-};
+}
const CHAN_5_HOSTNAMES = ['pleb.bz', '5chan.app', '5chan.eth.limo', '5chan.eth.link', '5chan.eth.sucks', '5chan.netlify.app'];
diff --git a/src/stores/use-feed-cache-store.ts b/src/stores/use-feed-cache-store.ts
new file mode 100644
index 00000000..5fbbafc5
--- /dev/null
+++ b/src/stores/use-feed-cache-store.ts
@@ -0,0 +1,54 @@
+import { create } from 'zustand';
+
+export interface CachedFeed {
+ key: string;
+ type: 'board' | 'catalog';
+ lastAccessed: number;
+}
+
+interface FeedCacheState {
+ cachedFeeds: CachedFeed[];
+ maxCacheSize: number;
+ accessFeed: (key: string, type: 'board' | 'catalog') => void;
+ removeFeed: (key: string) => void;
+ isFeedCached: (key: string) => boolean;
+}
+
+const useFeedCacheStore = create((set, get) => ({
+ cachedFeeds: [],
+ maxCacheSize: 2,
+
+ accessFeed: (key: string, type: 'board' | 'catalog') => {
+ const { cachedFeeds, maxCacheSize } = get();
+ const now = Date.now();
+ const existingIndex = cachedFeeds.findIndex((feed) => feed.key === key);
+
+ if (existingIndex !== -1) {
+ const updatedFeeds = [...cachedFeeds];
+ updatedFeeds[existingIndex] = { ...updatedFeeds[existingIndex], lastAccessed: now };
+ set({ cachedFeeds: updatedFeeds });
+ } else {
+ const newFeed: CachedFeed = { key, type, lastAccessed: now };
+ let updatedFeeds = [...cachedFeeds, newFeed];
+
+ if (updatedFeeds.length > maxCacheSize) {
+ updatedFeeds.sort((a, b) => a.lastAccessed - b.lastAccessed);
+ updatedFeeds = updatedFeeds.slice(1);
+ }
+
+ set({ cachedFeeds: updatedFeeds });
+ }
+ },
+
+ removeFeed: (key: string) => {
+ const { cachedFeeds } = get();
+ set({ cachedFeeds: cachedFeeds.filter((feed) => feed.key !== key) });
+ },
+
+ isFeedCached: (key: string) => {
+ const { cachedFeeds } = get();
+ return cachedFeeds.some((feed) => feed.key === key);
+ },
+}));
+
+export default useFeedCacheStore;
diff --git a/src/views/board/board.tsx b/src/views/board/board.tsx
index a9ef89dc..18a7d27b 100644
--- a/src/views/board/board.tsx
+++ b/src/views/board/board.tsx
@@ -1,19 +1,19 @@
import { useEffect, useMemo, useRef, useState } from 'react';
-import { Link, useLocation, useParams } from 'react-router-dom';
+import { Link, useLocation, useNavigationType, useParams } from 'react-router-dom';
import { Comment, useAccount, useAccountComments, useAccountSubplebbits, useBlock, useFeed, useSubplebbit } from '@plebbit/plebbit-react-hooks';
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
import { Trans, useTranslation } from 'react-i18next';
import styles from './board.module.css';
import { shouldShowSnow } from '../../lib/snow';
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
-import { isAllView, isSubscriptionsView, isModView } from '../../lib/utils/view-utils';
-import { useDefaultSubplebbitAddresses } from '../../hooks/use-default-subplebbits';
+import { useDefaultSubplebbitAddresses, useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
import { useResolvedSubplebbitAddress, useBoardPath } from '../../hooks/use-resolved-subplebbit-address';
import { useFeedStateString } from '../../hooks/use-state-string';
-import useTimeFilter from '../../hooks/use-time-filter';
+import useTimeFilter, { timeFilterNameToSeconds } from '../../hooks/use-time-filter';
import useInterfaceSettingsStore from '../../stores/use-interface-settings-store';
import useFeedResetStore from '../../stores/use-feed-reset-store';
import useSortingStore from '../../stores/use-sorting-store';
+import { getSubplebbitAddress } from '../../lib/utils/route-utils';
import ErrorDisplay from '../../components/error-display/error-display';
import LoadingEllipsis from '../../components/loading-ellipsis';
import SubplebbitDescription from '../../components/subplebbit-description';
@@ -33,21 +33,39 @@ const createThreadsWithoutImagesFilter = () => ({
key: 'threads-with-images-only',
});
-const Board = () => {
+export interface BoardProps {
+ feedCacheKey?: string;
+ viewType?: 'all' | 'subs' | 'mod' | 'board';
+ boardIdentifier?: string;
+ timeFilterNameFromCache?: string;
+ isVisible?: boolean;
+}
+
+const Board = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, timeFilterNameFromCache, isVisible = true }: BoardProps) => {
const { t } = useTranslation();
const location = useLocation();
- const subplebbitAddress = useResolvedSubplebbitAddress();
- const boardPath = useBoardPath(subplebbitAddress);
+ const params = useParams();
const { hideThreadsWithoutImages } = useInterfaceSettingsStore();
- const isInAllView = isAllView(location.pathname);
+ const isInAllView = viewType ? viewType === 'all' : false;
+ const isInSubscriptionsView = viewType ? viewType === 'subs' : false;
+ const isInModView = viewType ? viewType === 'mod' : false;
+
+ const defaultSubplebbits = useDefaultSubplebbits();
+ const resolvedAddressFromUrl = useResolvedSubplebbitAddress();
+ const subplebbitAddress = useMemo(() => {
+ if (boardIdentifierProp) {
+ return getSubplebbitAddress(boardIdentifierProp, defaultSubplebbits);
+ }
+ return resolvedAddressFromUrl;
+ }, [boardIdentifierProp, defaultSubplebbits, resolvedAddressFromUrl]);
+
+ const boardPath = useBoardPath(subplebbitAddress);
const defaultSubplebbitAddresses = useDefaultSubplebbitAddresses();
const account = useAccount();
const subscriptions = account?.subscriptions;
- const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams());
- const isInModView = isModView(location.pathname);
const { accountSubplebbits } = useAccountSubplebbits();
const accountSubplebbitAddresses = Object.keys(accountSubplebbits);
@@ -65,7 +83,9 @@ const Board = () => {
}, [isInAllView, isInSubscriptionsView, isInModView, subplebbitAddress, defaultSubplebbitAddresses, subscriptions, accountSubplebbitAddresses]);
const { sortType } = useSortingStore();
- const { timeFilterSeconds, timeFilterName } = useTimeFilter();
+ const { timeFilterSeconds: timeFilterSecondsFromHook, timeFilterName: timeFilterNameFromHook } = useTimeFilter();
+ const timeFilterName = timeFilterNameFromCache || timeFilterNameFromHook;
+ const timeFilterSeconds = timeFilterNameFromCache ? timeFilterNameToSeconds(timeFilterNameFromCache) : timeFilterSecondsFromHook;
const feedOptions = {
subplebbitAddresses,
@@ -82,8 +102,10 @@ const Board = () => {
const setResetFunction = useFeedResetStore((state) => state.setResetFunction);
useEffect(() => {
- setResetFunction(reset);
- }, [reset, setResetFunction, feed]);
+ if (isVisible) {
+ setResetFunction(reset);
+ }
+ }, [reset, setResetFunction, feed, isVisible]);
// show account comments instantly in the feed once published (cid defined), instead of waiting for the feed to update
const filteredComments = useMemo(
@@ -175,7 +197,6 @@ const Board = () => {
return () => clearTimeout(timer);
}, []);
- const params = useParams();
const currentTimeFilterName = params?.timeFilterName || timeFilterName;
const Footer = () => {
@@ -272,26 +293,42 @@ const Board = () => {
);
};
- // save the last Virtuoso state to restore it when navigating back
const virtuosoRef = useRef(null);
+ const virtuosoStateKey = feedCacheKey ? `${feedCacheKey}-${sortType}-${timeFilterSeconds}` : `${location.pathname}-${sortType}-${timeFilterSeconds}`;
+ const navigationType = useNavigationType();
+
+ const hasBeenVisibleRef = useRef(false);
useEffect(() => {
+ if (isVisible && !hasBeenVisibleRef.current) {
+ hasBeenVisibleRef.current = true;
+ if (navigationType !== 'POP') {
+ window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
+ }
+ }
+ }, [isVisible, navigationType]);
+
+ useEffect(() => {
+ if (!isVisible) return;
+
+ const currentKey = virtuosoStateKey;
const setLastVirtuosoState = () => {
virtuosoRef.current?.getState((snapshot: StateSnapshot) => {
if (snapshot?.ranges?.length) {
- lastVirtuosoStates[sortType + timeFilterSeconds] = snapshot;
+ lastVirtuosoStates[currentKey] = snapshot;
}
});
};
window.addEventListener('scroll', setLastVirtuosoState);
return () => window.removeEventListener('scroll', setLastVirtuosoState);
- }, [sortType, timeFilterSeconds]);
+ }, [virtuosoStateKey, isVisible]);
- const lastVirtuosoState = lastVirtuosoStates?.[sortType + timeFilterSeconds];
+ const lastVirtuosoState = navigationType === 'POP' ? lastVirtuosoStates?.[virtuosoStateKey] : undefined;
useEffect(() => {
+ if (!isVisible) return;
const boardTitle = title ? title : shortAddress || subplebbitAddress;
document.title = boardTitle + ' - 5chan';
- }, [title, shortAddress, subplebbitAddress]);
+ }, [title, shortAddress, subplebbitAddress, isVisible]);
const shouldShowErrorToUser = error?.message && feed.length === 0;
diff --git a/src/views/catalog/catalog.tsx b/src/views/catalog/catalog.tsx
index f2448ecd..c218e6a5 100644
--- a/src/views/catalog/catalog.tsx
+++ b/src/views/catalog/catalog.tsx
@@ -1,21 +1,21 @@
import { useEffect, useMemo, useRef, useState, useCallback } from 'react';
-import { Link, useLocation, useParams } from 'react-router-dom';
+import { Link, useLocation, useNavigationType, useParams } from 'react-router-dom';
import { Trans, useTranslation } from 'react-i18next';
import { Comment, useAccount, useFeed, useSubplebbit, useBlock, useAccountComments } from '@plebbit/plebbit-react-hooks';
import { Virtuoso, VirtuosoHandle, StateSnapshot } from 'react-virtuoso';
import { getCommentMediaInfo, getHasThumbnail } from '../../lib/utils/media-utils';
-import { isAllView, isSubscriptionsView } from '../../lib/utils/view-utils';
import useCatalogFeedRows from '../../hooks/use-catalog-feed-rows';
import { useDefaultSubplebbits } from '../../hooks/use-default-subplebbits';
import { useResolvedSubplebbitAddress, useBoardPath } from '../../hooks/use-resolved-subplebbit-address';
import { useFeedStateString } from '../../hooks/use-state-string';
-import useTimeFilter from '../../hooks/use-time-filter';
+import useTimeFilter, { timeFilterNameToSeconds } from '../../hooks/use-time-filter';
import useWindowWidth from '../../hooks/use-window-width';
import useCatalogStyleStore from '../../stores/use-catalog-style-store';
import useFeedResetStore from '../../stores/use-feed-reset-store';
import useInterfaceSettingsStore from '../../stores/use-interface-settings-store';
import useSortingStore from '../../stores/use-sorting-store';
import useCatalogFiltersStore from '../../stores/use-catalog-filters-store';
+import { getSubplebbitAddress } from '../../lib/utils/route-utils';
import CatalogRow from '../../components/catalog-row';
import LoadingEllipsis from '../../components/loading-ellipsis';
import styles from './catalog.module.css';
@@ -123,20 +123,37 @@ const createCombinedFilter = (
};
};
-const Catalog = () => {
+export interface CatalogProps {
+ feedCacheKey?: string;
+ viewType?: 'all' | 'subs' | 'mod' | 'board';
+ boardIdentifier?: string;
+ timeFilterNameFromCache?: string;
+ isVisible?: boolean;
+}
+
+const Catalog = ({ feedCacheKey, viewType, boardIdentifier: boardIdentifierProp, timeFilterNameFromCache, isVisible = true }: CatalogProps) => {
const { t } = useTranslation();
const location = useLocation();
- const subplebbitAddress = useResolvedSubplebbitAddress();
- const boardPath = useBoardPath(subplebbitAddress);
+ const params = useParams();
+
+ const isInAllView = viewType ? viewType === 'all' : false;
+ const isInSubscriptionsView = viewType ? viewType === 'subs' : false;
- const isInAllView = isAllView(location.pathname);
const defaultSubplebbits = useDefaultSubplebbits();
+ const resolvedAddressFromUrl = useResolvedSubplebbitAddress();
+ const subplebbitAddress = useMemo(() => {
+ if (boardIdentifierProp) {
+ return getSubplebbitAddress(boardIdentifierProp, defaultSubplebbits);
+ }
+ return resolvedAddressFromUrl;
+ }, [boardIdentifierProp, defaultSubplebbits, resolvedAddressFromUrl]);
+
+ const boardPath = useBoardPath(subplebbitAddress);
const { hideAdultBoards } = useInterfaceSettingsStore();
const { showTextOnlyThreads, filterItems, searchText, clearMatchedFilters } = useCatalogFiltersStore();
const account = useAccount();
const subscriptions = account?.subscriptions;
- const isInSubscriptionsView = isSubscriptionsView(location.pathname, useParams());
const subplebbitAddresses = useMemo(() => {
const filteredDefaultSubplebbits = defaultSubplebbits
@@ -166,8 +183,10 @@ const Catalog = () => {
const columnCount = Math.floor(useWindowWidth() / columnWidth);
const postsPerPage = columnCount <= 2 ? 10 : columnCount === 3 ? 15 : columnCount === 4 ? 20 : 25;
- const { timeFilterSeconds, timeFilterName } = useTimeFilter();
+ const { timeFilterSeconds: timeFilterSecondsFromHook, timeFilterName: timeFilterNameFromHook } = useTimeFilter();
const { sortType } = useSortingStore();
+ const timeFilterName = timeFilterNameFromCache || timeFilterNameFromHook;
+ const timeFilterSeconds = timeFilterNameFromCache ? timeFilterNameToSeconds(timeFilterNameFromCache) : timeFilterSecondsFromHook;
// Create a stable callback for filter matching
const handleFilterMatch = useCallback((filterIndex: number, cid: string, subplebbitAddress: string) => {
@@ -303,8 +322,10 @@ const Catalog = () => {
const setResetFunction = useFeedResetStore((state) => state.setResetFunction);
useEffect(() => {
- setResetFunction(reset);
- }, [reset, setResetFunction]);
+ if (isVisible) {
+ setResetFunction(reset);
+ }
+ }, [reset, setResetFunction, isVisible]);
const subplebbit = useSubplebbit({ subplebbitAddress });
const { error, shortAddress, state, title } = subplebbit || {};
@@ -344,7 +365,6 @@ const Catalog = () => {
);
- const params = useParams<{ sortType?: string; timeFilterName?: string }>();
const currentTimeFilterName = params?.timeFilterName || timeFilterName;
const Footer = () => {
@@ -450,27 +470,43 @@ const Catalog = () => {
const rows = useCatalogFeedRows(columnCount, processedFeed, isFeedLoaded, subplebbit);
- // save the last Virtuoso state to restore it when navigating back
const virtuosoRef = useRef(null);
+ const virtuosoStateKey = feedCacheKey ? `${feedCacheKey}-${sortType}-${timeFilterSeconds}` : `${location.pathname}-${sortType}-${timeFilterSeconds}-catalog`;
+ const navigationType = useNavigationType();
+
+ const hasBeenVisibleRef = useRef(false);
useEffect(() => {
+ if (isVisible && !hasBeenVisibleRef.current) {
+ hasBeenVisibleRef.current = true;
+ if (navigationType !== 'POP') {
+ window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
+ }
+ }
+ }, [isVisible, navigationType]);
+
+ useEffect(() => {
+ if (!isVisible) return;
+
+ const currentKey = virtuosoStateKey;
const setLastVirtuosoState = () =>
virtuosoRef.current?.getState((snapshot: StateSnapshot) => {
if (snapshot?.ranges?.length) {
- lastVirtuosoStates[sortType + timeFilterSeconds + 'catalog'] = snapshot;
+ lastVirtuosoStates[currentKey] = snapshot;
}
});
window.addEventListener('scroll', setLastVirtuosoState);
return () => window.removeEventListener('scroll', setLastVirtuosoState);
- }, [sortType, timeFilterSeconds]);
+ }, [virtuosoStateKey, isVisible]);
- const lastVirtuosoState = lastVirtuosoStates?.[sortType + timeFilterSeconds + 'catalog'];
+ const lastVirtuosoState = navigationType === 'POP' ? lastVirtuosoStates?.[virtuosoStateKey] : undefined;
useEffect(() => {
+ if (!isVisible) return;
let documentTitle = title ? title : shortAddress;
if (isInAllView) documentTitle = t('all');
else if (isInSubscriptionsView) documentTitle = t('subscriptions');
document.title = documentTitle + ` - ${t('catalog')} - 5chan`;
- }, [title, shortAddress, isInAllView, isInSubscriptionsView, t]);
+ }, [title, shortAddress, isInAllView, isInSubscriptionsView, t, isVisible]);
// Clear matched filters when component mounts or when subplebbit changes
useEffect(() => {
diff --git a/src/views/home/boards-list/boards-list.tsx b/src/views/home/boards-list/boards-list.tsx
index ade581f7..c685fb7f 100644
--- a/src/views/home/boards-list/boards-list.tsx
+++ b/src/views/home/boards-list/boards-list.tsx
@@ -2,7 +2,6 @@ import { Link, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useDefaultSubplebbitsState, useDefaultSubplebbits, MultisubSubplebbit } from '../../../hooks/use-default-subplebbits';
import { getBoardPath } from '../../../lib/utils/route-utils';
-import LoadingEllipsis from '../../../components/loading-ellipsis';
import useDisclaimerModalStore from '../../../stores/use-disclaimer-modal-store';
import useDirectoryModalStore from '../../../stores/use-directory-modal-store';
import useBoardsFilterStore from '../../../stores/use-boards-filter-store';
@@ -31,7 +30,7 @@ const NSFWBadge = () => {
const BoardsList = ({ multisub }: { multisub: MultisubSubplebbit[] }) => {
const { t } = useTranslation();
const navigate = useNavigate();
- const { loading, error } = useDefaultSubplebbitsState();
+ const { error } = useDefaultSubplebbitsState();
const { showDisclaimerModal } = useDisclaimerModalStore();
const { openDirectoryModal } = useDirectoryModalStore();
const { useCatalogLinks, boardFilter } = useBoardsFilterStore();
@@ -56,37 +55,10 @@ const BoardsList = ({ multisub }: { multisub: MultisubSubplebbit[] }) => {
openDirectoryModal();
};
- if (loading) {
- return (
-
-
-
{t('boards')}
-
-
-
-
-
-
- );
- }
-
- if (error) {
- return (
-
-
-
{t('boards')}
-
-
-
-
- );
- }
-
// Find active boards
const bizAddress = findBoardAddress(multisub, '/biz/ - Business & Finance');
const polAddress = findBoardAddress(multisub, '/pol/ - Politically Incorrect');
+ const errorMessage = error?.message;
// Filtering logic: determine which categories to show
const showAll = boardFilter === 'all';
@@ -110,6 +82,7 @@ const BoardsList = ({ multisub }: { multisub: MultisubSubplebbit[] }) => {
+ {errorMessage &&
{errorMessage}
}
{/* Column 1: Japanese Culture + Video Games */}
{(showJapaneseCulture || showVideoGames) && (
diff --git a/src/views/home/home.module.css b/src/views/home/home.module.css
index a4e2f51b..2f845310 100644
--- a/src/views/home/home.module.css
+++ b/src/views/home/home.module.css
@@ -190,6 +190,12 @@
text-align: center;
}
+.popularThreadsLoading {
+ text-align: left;
+ /* Reserve space only while loading: max 8 posts, ~2 rows of 4 cards on desktop */
+ min-height: 500px;
+}
+
.popularThread {
vertical-align: top;
display: inline-block;
@@ -351,6 +357,11 @@
.boardsColumn a {
font-size: 1.2em;
}
+
+ /* Mobile: popular threads cards stack more, so taller min-height needed */
+ .popularThreadsLoading {
+ min-height: 1200px;
+ }
}
@media (min-width: 640px) {
diff --git a/src/views/home/popular-threads-box/popular-threads-box.tsx b/src/views/home/popular-threads-box/popular-threads-box.tsx
index 11b52aab..6ddb8178 100644
--- a/src/views/home/popular-threads-box/popular-threads-box.tsx
+++ b/src/views/home/popular-threads-box/popular-threads-box.tsx
@@ -78,6 +78,7 @@ const PopularThreadsBox = ({ multisub, subplebbits }: { multisub: MultisubSubple
const filteredSubplebbits = useMemo(getFilteredSubplebbits, [subplebbits, showWorksafeContentOnly, showNsfwContentOnly, multisub]);
const { popularPosts } = usePopularPosts(filteredSubplebbits);
+ const isLoading = popularPosts.length === 0;
return (
@@ -85,12 +86,8 @@ const PopularThreadsBox = ({ multisub, subplebbits }: { multisub: MultisubSubple
{t('popular_threads')}
-
- {popularPosts.length === 0 ? (
-
- ) : (
- popularPosts.map((post: any) =>
)
- )}
+
+ {isLoading ?
: popularPosts.map((post: any) =>
)}
);
diff --git a/yarn.lock b/yarn.lock
index b4e3e496..5292c23e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1199,20 +1199,20 @@
ipaddr.js "^2.2.0"
punycode "^2.3.1"
-"@capacitor/android@7.2.0":
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/@capacitor/android/-/android-7.2.0.tgz#f30e313315ab92c500bd57d9f0c4716a46b42f40"
- integrity sha512-zdhEy3jZPG5Toe/pGzKtDgIiBGywjaoEuQWnGVjBYPlSAEUtAhpZ2At7V0SCb26yluAuzrAUV0Ue+LQeEtHwFQ==
+"@capacitor/android@7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@capacitor/android/-/android-7.4.4.tgz#a14a1e844bd5079982427e247fdd17555b5fbedd"
+ integrity sha512-y8knfV1JXNrd6XZZLZireGT+EBCN0lvOo+HZ/s7L8LkrPBu4nY5UZn0Wxz4yOezItEII9rqYJSHsS5fMJG9gdw==
"@capacitor/app@7.0.1":
version "7.0.1"
resolved "https://registry.yarnpkg.com/@capacitor/app/-/app-7.0.1.tgz#0d0709fb4dde5046c24853f2d6b77a7ea411f748"
integrity sha512-ArlVZAAla4MwQoKh26x2AaTDOBh5Vhp1VhMKR3RwqZSsZnazKTFGNrPbr9Ez5r1knnEDfApyjwp1uZnXK1WTYQ==
-"@capacitor/cli@7.2.0":
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/@capacitor/cli/-/cli-7.2.0.tgz#ea279f14e575b059edb7fe8dc64a8e3379088616"
- integrity sha512-RNW9vtYYYSDmOdguYBSW0VpRnG/d6lGydlc9DLrJ7qbSPxFrotTz9IjkM48O+SruUma61DyuSqJttdbay2xSxg==
+"@capacitor/cli@7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@capacitor/cli/-/cli-7.4.4.tgz#bfce7db8b15d6f44c9d58dc338a5e3d662fecfbb"
+ integrity sha512-J7ciBE7GlJ70sr2s8oz1+H4ZdNk4MGG41fsakUlDHWva5UWgFIZYMiEdDvGbYazAYTaxN3lVZpH9zil9FfZj+Q==
dependencies:
"@ionic/cli-framework-output" "^2.2.8"
"@ionic/utils-subprocess" "^3.0.1"
@@ -1232,10 +1232,10 @@
tslib "^2.8.1"
xml2js "^0.6.2"
-"@capacitor/core@7.2.0":
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/@capacitor/core/-/core-7.2.0.tgz#fdd1c5d2fbfa257887ce6065f66151a51f0e6d1b"
- integrity sha512-2zCnA6RJeZ9ec4470o8QMZEQTWpekw9FNoqm5TLc10jeCrhvHVI8MPgxdZVc3mOdFlyieYu4AS1fNxSqbS57Pw==
+"@capacitor/core@7.4.4":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@capacitor/core/-/core-7.4.4.tgz#b752dd7a12141e40d1c8ce2e37a79571d9f83518"
+ integrity sha512-xzjxpr+d2zwTpCaN0k+C6wKSZzWFAb9OVEUtmO72ihjr/NEDoLvsGl4WLfjWPcCO2zOy0b2X52tfRWjECFUjtw==
dependencies:
tslib "^2.1.0"
@@ -1629,10 +1629,10 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
-"@eslint/js@8.56.0":
- version "8.56.0"
- resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b"
- integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==
+"@eslint/js@8.57.1":
+ version "8.57.1"
+ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2"
+ integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==
"@ethersproject/abi@5.6.4":
version "5.6.4"
@@ -2279,17 +2279,39 @@
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
-"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
- version "9.3.0"
- resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
- integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==
-
-"@hapi/topo@^5.1.0":
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012"
- integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==
+"@hapi/address@^5.1.1":
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/@hapi/address/-/address-5.1.1.tgz#e9925fc1b65f5cc3fbea821f2b980e4652e84cb6"
+ integrity sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==
dependencies:
- "@hapi/hoek" "^9.0.0"
+ "@hapi/hoek" "^11.0.2"
+
+"@hapi/formula@^3.0.2":
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/@hapi/formula/-/formula-3.0.2.tgz#81b538060ee079481c906f599906d163c4badeaf"
+ integrity sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==
+
+"@hapi/hoek@^11.0.2", "@hapi/hoek@^11.0.7":
+ version "11.0.7"
+ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-11.0.7.tgz#56a920793e0a42d10e530da9a64cc0d3919c4002"
+ integrity sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==
+
+"@hapi/pinpoint@^2.0.1":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/@hapi/pinpoint/-/pinpoint-2.0.1.tgz#32077e715655fc00ab8df74b6b416114287d6513"
+ integrity sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==
+
+"@hapi/tlds@^1.1.1":
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/@hapi/tlds/-/tlds-1.1.4.tgz#df4a7b59082b54ba4f3b7b38f781e2ac3cbc359a"
+ integrity sha512-Fq+20dxsxLaUn5jSSWrdtSRcIUba2JquuorF9UW1wIJS5cSUwxIsO2GIhaWynPRflvxSzFN+gxKte2HEW1OuoA==
+
+"@hapi/topo@^6.0.2":
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-6.0.2.tgz#f219c1c60da8430228af4c1f2e40c32a0d84bbb4"
+ integrity sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==
+ dependencies:
+ "@hapi/hoek" "^11.0.2"
"@helia/bitswap@^3.0.4", "@helia/bitswap@^3.0.7":
version "3.0.7"
@@ -2510,12 +2532,12 @@
race-signal "^2.0.0"
uint8arrays "^5.1.0"
-"@humanwhocodes/config-array@^0.11.13":
- version "0.11.14"
- resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b"
- integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==
+"@humanwhocodes/config-array@^0.13.0":
+ version "0.13.0"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748"
+ integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==
dependencies:
- "@humanwhocodes/object-schema" "^2.0.2"
+ "@humanwhocodes/object-schema" "^2.0.3"
debug "^4.3.1"
minimatch "^3.0.5"
@@ -2524,7 +2546,7 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
-"@humanwhocodes/object-schema@^2.0.2":
+"@humanwhocodes/object-schema@^2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
@@ -4319,115 +4341,115 @@
estree-walker "^2.0.2"
picomatch "^4.0.2"
-"@rollup/rollup-android-arm-eabi@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz#59e7478d310f7e6a7c72453978f562483828112f"
- integrity sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==
+"@rollup/rollup-android-arm-eabi@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz#7e478b66180c5330429dd161bf84dad66b59c8eb"
+ integrity sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==
-"@rollup/rollup-android-arm64@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz#a825192a0b1b2f27a5c950c439e7e37a33c5d056"
- integrity sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==
+"@rollup/rollup-android-arm64@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz#2b025510c53a5e3962d3edade91fba9368c9d71c"
+ integrity sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==
-"@rollup/rollup-darwin-arm64@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz#4ee37078bccd725ae3c5f30ef92efc8e1bf886f3"
- integrity sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==
+"@rollup/rollup-darwin-arm64@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz#3577c38af68ccf34c03e84f476bfd526abca10a0"
+ integrity sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==
-"@rollup/rollup-darwin-x64@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz#43cc08bd05bf9f388f125e7210a544e62d368d90"
- integrity sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==
+"@rollup/rollup-darwin-x64@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz#2bf5f2520a1f3b551723d274b9669ba5b75ed69c"
+ integrity sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==
-"@rollup/rollup-freebsd-arm64@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz#bc8e640e28abe52450baf3fc80d9b26d9bb6587d"
- integrity sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==
+"@rollup/rollup-freebsd-arm64@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz#4bb9cc80252564c158efc0710153c71633f1927c"
+ integrity sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==
-"@rollup/rollup-freebsd-x64@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz#e981a22e057cc8c65bb523019d344d3a66b15bbc"
- integrity sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==
+"@rollup/rollup-freebsd-x64@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz#2301289094d49415a380cf942219ae9d8b127440"
+ integrity sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==
-"@rollup/rollup-linux-arm-gnueabihf@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz#4036b68904f392a20f3499d63b33e055b67eb274"
- integrity sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==
+"@rollup/rollup-linux-arm-gnueabihf@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz#1d03d776f2065e09fc141df7d143476e94acca88"
+ integrity sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==
-"@rollup/rollup-linux-arm-musleabihf@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz#d3b1b9589606e0ff916801c855b1ace9e733427a"
- integrity sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==
+"@rollup/rollup-linux-arm-musleabihf@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz#8623de0e040b2fd52a541c602688228f51f96701"
+ integrity sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==
-"@rollup/rollup-linux-arm64-gnu@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz#cbf0943c477e3b96340136dd3448eaf144378cf2"
- integrity sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==
+"@rollup/rollup-linux-arm64-gnu@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz#ce2d1999bc166277935dde0301cde3dd0417fb6e"
+ integrity sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==
-"@rollup/rollup-linux-arm64-musl@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz#837f5a428020d5dce1c3b4cc049876075402cf78"
- integrity sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==
+"@rollup/rollup-linux-arm64-musl@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz#88c2523778444da952651a2219026416564a4899"
+ integrity sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==
-"@rollup/rollup-linux-loong64-gnu@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz#532c214ababb32ab4bc21b4054278b9a8979e516"
- integrity sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==
+"@rollup/rollup-linux-loong64-gnu@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz#578ca2220a200ac4226c536c10c8cc6e4f276714"
+ integrity sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==
-"@rollup/rollup-linux-ppc64-gnu@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz#93900163b61b49cee666d10ee38257a8b1dd161a"
- integrity sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==
+"@rollup/rollup-linux-ppc64-gnu@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz#aa338d3effd4168a20a5023834a74ba2c3081293"
+ integrity sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==
-"@rollup/rollup-linux-riscv64-gnu@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz#f0ffdcc7066ca04bc972370c74289f35c7a7dc42"
- integrity sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==
+"@rollup/rollup-linux-riscv64-gnu@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz#16ba582f9f6cff58119aa242782209b1557a1508"
+ integrity sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==
-"@rollup/rollup-linux-riscv64-musl@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz#361695c39dbe96773509745d77a870a32a9f8e48"
- integrity sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==
+"@rollup/rollup-linux-riscv64-musl@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz#e404a77ebd6378483888b8064c703adb011340ab"
+ integrity sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==
-"@rollup/rollup-linux-s390x-gnu@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz#09fc6cc2e266a2324e366486ae5d1bca48c43a6a"
- integrity sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==
+"@rollup/rollup-linux-s390x-gnu@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz#92ad52d306227c56bec43d96ad2164495437ffe6"
+ integrity sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==
-"@rollup/rollup-linux-x64-gnu@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz#aa9d5b307c08f05d3454225bb0a2b4cc87eeb2e1"
- integrity sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==
+"@rollup/rollup-linux-x64-gnu@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz#fd0dea3bb9aa07e7083579f25e1c2285a46cb9fa"
+ integrity sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==
-"@rollup/rollup-linux-x64-musl@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz#26949e5b4645502a61daba2f7a8416bd17cb5382"
- integrity sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==
+"@rollup/rollup-linux-x64-musl@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz#37a3efb09f18d555f8afc490e1f0444885de8951"
+ integrity sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==
-"@rollup/rollup-openharmony-arm64@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz#ef493c072f9dac7e0edb6c72d63366846b6ffcd9"
- integrity sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==
+"@rollup/rollup-openharmony-arm64@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz#c489bec9f4f8320d42c9b324cca220c90091c1f7"
+ integrity sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==
-"@rollup/rollup-win32-arm64-msvc@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz#56e1aaa6a630d2202ee7ec0adddd05cf384ffd44"
- integrity sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==
+"@rollup/rollup-win32-arm64-msvc@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz#152832b5f79dc22d1606fac3db946283601b7080"
+ integrity sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==
-"@rollup/rollup-win32-ia32-msvc@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz#0a44bbf933a9651c7da2b8569fa448dec0de7480"
- integrity sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==
+"@rollup/rollup-win32-ia32-msvc@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz#54d91b2bb3bf3e9f30d32b72065a4e52b3a172a5"
+ integrity sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==
-"@rollup/rollup-win32-x64-gnu@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz#730e12f0b60b234a7c02d5d3179ca3ec7972033d"
- integrity sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==
+"@rollup/rollup-win32-x64-gnu@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz#df9df03e61a003873efec8decd2034e7f135c71e"
+ integrity sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==
-"@rollup/rollup-win32-x64-msvc@4.52.4":
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz#5b2dd648a960b8fa00d76f2cc4eea2f03daa80f4"
- integrity sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==
+"@rollup/rollup-win32-x64-msvc@4.53.3":
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz#38ae84f4c04226c1d56a3b17296ef1e0460ecdfe"
+ integrity sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==
"@rtsao/scc@^1.1.0":
version "1.1.0"
@@ -4483,23 +4505,6 @@
"@noble/hashes" "~1.8.0"
"@scure/base" "~1.2.5"
-"@sideway/address@^4.1.5":
- version "4.1.5"
- resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5"
- integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==
- dependencies:
- "@hapi/hoek" "^9.0.0"
-
-"@sideway/formula@^3.0.1":
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
- integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
-
-"@sideway/pinpoint@^2.0.0":
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
- integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
-
"@sinclair/typebox@^0.27.8":
version "0.27.8"
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
@@ -4737,6 +4742,11 @@
rpc-websockets "^9.0.2"
superstruct "^2.0.2"
+"@standard-schema/spec@^1.0.0":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c"
+ integrity sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==
+
"@surma/rollup-plugin-off-main-thread@^2.2.3":
version "2.2.3"
resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz#ee34985952ca21558ab0d952f00298ad2190c053"
@@ -5917,13 +5927,14 @@ axe-core@^4.10.0:
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.11.0.tgz#16f74d6482e343ff263d4f4503829e9ee91a86b6"
integrity sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==
-axios@^0.27.2:
- version "0.27.2"
- resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
- integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
+axios@^1.13.2:
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-1.13.2.tgz#9ada120b7b5ab24509553ec3e40123521117f687"
+ integrity sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==
dependencies:
- follow-redirects "^1.14.9"
- form-data "^4.0.0"
+ follow-redirects "^1.15.6"
+ form-data "^4.0.4"
+ proxy-from-env "^1.1.0"
axios@^1.7.2:
version "1.12.2"
@@ -7896,10 +7907,10 @@ electron-to-chromium@^1.5.227:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.237.tgz#eacf61cef3f6345d0069ab427585c5a04d7084f0"
integrity sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==
-electron@36.0.0:
- version "36.0.0"
- resolved "https://registry.yarnpkg.com/electron/-/electron-36.0.0.tgz#ffbe7ca9edecaee53617f29a088be10bbed9a45f"
- integrity sha512-MhBL5tgzqLsiw++YXxzXRvF5s90gelcEZP4Upz/aaRmmoscmCw/EtimxMSH6EPnKt+8KwBY9RVAdlcffFPYkyw==
+electron@36.9.5:
+ version "36.9.5"
+ resolved "https://registry.yarnpkg.com/electron/-/electron-36.9.5.tgz#78e117e8cf97d53687f8d5a412e8bf9336229320"
+ integrity sha512-1UCss2IqxqujSzg/2jkRjuiT3G+EEXgd6UKB5kUekwQW1LJ6d4QCr8YItfC3Rr9VIGRDJ29eOERmnRNO1Eh+NA==
dependencies:
"@electron/get" "^2.0.0"
"@types/node" "^22.7.7"
@@ -8473,16 +8484,16 @@ eslint-visitor-keys@^4.2.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1"
integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==
-eslint@8.56.0:
- version "8.56.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15"
- integrity sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==
+eslint@8.57.1:
+ version "8.57.1"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9"
+ integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==
dependencies:
"@eslint-community/eslint-utils" "^4.2.0"
"@eslint-community/regexpp" "^4.6.1"
"@eslint/eslintrc" "^2.1.4"
- "@eslint/js" "8.56.0"
- "@humanwhocodes/config-array" "^0.11.13"
+ "@eslint/js" "8.57.1"
+ "@humanwhocodes/config-array" "^0.13.0"
"@humanwhocodes/module-importer" "^1.0.1"
"@nodelib/fs.walk" "^1.2.8"
"@ungap/structured-clone" "^1.2.0"
@@ -8839,7 +8850,7 @@ fd-slicer@~1.1.0:
dependencies:
pend "~1.2.0"
-fdir@^6.5.0:
+fdir@^6.4.4, fdir@^6.5.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
@@ -8960,7 +8971,7 @@ flatted@^3.2.9:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358"
integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==
-follow-redirects@^1.0.0, follow-redirects@^1.14.9, follow-redirects@^1.15.11, follow-redirects@^1.15.6:
+follow-redirects@^1.0.0, follow-redirects@^1.15.11, follow-redirects@^1.15.6:
version "1.15.11"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340"
integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==
@@ -11007,16 +11018,18 @@ jiti@^2.6.1:
resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92"
integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==
-joi@^17.7.0:
- version "17.13.3"
- resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.3.tgz#0f5cc1169c999b30d344366d384b12d92558bcec"
- integrity sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==
+joi@^18.0.1:
+ version "18.0.2"
+ resolved "https://registry.yarnpkg.com/joi/-/joi-18.0.2.tgz#30ced6aed00a7848cc11f92859515258301dc3a4"
+ integrity sha512-RuCOQMIt78LWnktPoeBL0GErkNaJPTBGcYuyaBvUOQSpcpcLfWrHPPihYdOGbV5pam9VTWbeoF7TsGiHugcjGA==
dependencies:
- "@hapi/hoek" "^9.3.0"
- "@hapi/topo" "^5.1.0"
- "@sideway/address" "^4.1.5"
- "@sideway/formula" "^3.0.1"
- "@sideway/pinpoint" "^2.0.0"
+ "@hapi/address" "^5.1.1"
+ "@hapi/formula" "^3.0.2"
+ "@hapi/hoek" "^11.0.7"
+ "@hapi/pinpoint" "^2.0.1"
+ "@hapi/tlds" "^1.1.1"
+ "@hapi/topo" "^6.0.2"
+ "@standard-schema/spec" "^1.0.0"
jose@4.11.0:
version "4.11.0"
@@ -11053,10 +11066,10 @@ js-sizeof@0.0.1:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
-js-yaml@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
- integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+js-yaml@4.1.1, js-yaml@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b"
+ integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==
dependencies:
argparse "^2.0.1"
@@ -12273,7 +12286,7 @@ minimist@1.2.7:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
-minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.7:
+minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
@@ -13622,10 +13635,10 @@ rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
-react-dom@19.1.0:
- version "19.1.0"
- resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.1.0.tgz#133558deca37fa1d682708df8904b25186793623"
- integrity sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==
+react-dom@19.1.2:
+ version "19.1.2"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.1.2.tgz#18ce6b14f682f4871a69cc6b2568d42af2cd5a33"
+ integrity sha512-dEoydsCp50i7kS1xHOmPXq4zQYoGWedUsvqv9H6zdif2r7yLHygyfP9qou71TulRN0d6ng9EbRVsQhSqfUc19g==
dependencies:
scheduler "^0.26.0"
@@ -13741,15 +13754,15 @@ react-scan@0.4.3:
optionalDependencies:
unplugin "2.1.0"
-react-virtuoso@4.12.3:
- version "4.12.3"
- resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.12.3.tgz#beecf0582b31058c5a6ed3ec58fc43fd780e5844"
- integrity sha512-6X1p/sU7hecmjDZMAwN+r3go9EVjofKhwkUbVlL8lXhBZecPv9XVCkZ/kBPYOr0Mv0Vl5+Ziwgexg9Kh7+NNXQ==
+react-virtuoso@4.17.0:
+ version "4.17.0"
+ resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.17.0.tgz#e81f2da99792cfd9317e910b243d847ebeb09248"
+ integrity sha512-od3pi2v13v31uzn5zPXC2u3ouISFCVhjFVFch2VvS2Cx7pWA2F1aJa3XhNTN2F07M3lhfnMnsmGeH+7wZICr7w==
-react@19.1.0:
- version "19.1.0"
- resolved "https://registry.yarnpkg.com/react/-/react-19.1.0.tgz#926864b6c48da7627f004795d6cce50e90793b75"
- integrity sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==
+react@19.1.2:
+ version "19.1.2"
+ resolved "https://registry.yarnpkg.com/react/-/react-19.1.2.tgz#fd75c4f684b52fe45101381535c58486f4407545"
+ integrity sha512-MdWVitvLbQULD+4DP8GYjZUrepGW7d+GQkNVqJEzNxE+e9WIa4egVFE/RDfVb1u9u/Jw7dNMmPB4IqxzbFYJ0w==
read-binary-file-arch@^1.0.6:
version "1.0.6"
@@ -14118,35 +14131,35 @@ rollup@^2.43.1, rollup@^2.77.2:
optionalDependencies:
fsevents "~2.3.2"
-rollup@^4.30.1:
- version "4.52.4"
- resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.52.4.tgz#71e64cce96a865fcbaa6bb62c6e82807f4e378a1"
- integrity sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==
+rollup@^4.34.9:
+ version "4.53.3"
+ resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.53.3.tgz#dbc8cd8743b38710019fb8297e8d7a76e3faa406"
+ integrity sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==
dependencies:
"@types/estree" "1.0.8"
optionalDependencies:
- "@rollup/rollup-android-arm-eabi" "4.52.4"
- "@rollup/rollup-android-arm64" "4.52.4"
- "@rollup/rollup-darwin-arm64" "4.52.4"
- "@rollup/rollup-darwin-x64" "4.52.4"
- "@rollup/rollup-freebsd-arm64" "4.52.4"
- "@rollup/rollup-freebsd-x64" "4.52.4"
- "@rollup/rollup-linux-arm-gnueabihf" "4.52.4"
- "@rollup/rollup-linux-arm-musleabihf" "4.52.4"
- "@rollup/rollup-linux-arm64-gnu" "4.52.4"
- "@rollup/rollup-linux-arm64-musl" "4.52.4"
- "@rollup/rollup-linux-loong64-gnu" "4.52.4"
- "@rollup/rollup-linux-ppc64-gnu" "4.52.4"
- "@rollup/rollup-linux-riscv64-gnu" "4.52.4"
- "@rollup/rollup-linux-riscv64-musl" "4.52.4"
- "@rollup/rollup-linux-s390x-gnu" "4.52.4"
- "@rollup/rollup-linux-x64-gnu" "4.52.4"
- "@rollup/rollup-linux-x64-musl" "4.52.4"
- "@rollup/rollup-openharmony-arm64" "4.52.4"
- "@rollup/rollup-win32-arm64-msvc" "4.52.4"
- "@rollup/rollup-win32-ia32-msvc" "4.52.4"
- "@rollup/rollup-win32-x64-gnu" "4.52.4"
- "@rollup/rollup-win32-x64-msvc" "4.52.4"
+ "@rollup/rollup-android-arm-eabi" "4.53.3"
+ "@rollup/rollup-android-arm64" "4.53.3"
+ "@rollup/rollup-darwin-arm64" "4.53.3"
+ "@rollup/rollup-darwin-x64" "4.53.3"
+ "@rollup/rollup-freebsd-arm64" "4.53.3"
+ "@rollup/rollup-freebsd-x64" "4.53.3"
+ "@rollup/rollup-linux-arm-gnueabihf" "4.53.3"
+ "@rollup/rollup-linux-arm-musleabihf" "4.53.3"
+ "@rollup/rollup-linux-arm64-gnu" "4.53.3"
+ "@rollup/rollup-linux-arm64-musl" "4.53.3"
+ "@rollup/rollup-linux-loong64-gnu" "4.53.3"
+ "@rollup/rollup-linux-ppc64-gnu" "4.53.3"
+ "@rollup/rollup-linux-riscv64-gnu" "4.53.3"
+ "@rollup/rollup-linux-riscv64-musl" "4.53.3"
+ "@rollup/rollup-linux-s390x-gnu" "4.53.3"
+ "@rollup/rollup-linux-x64-gnu" "4.53.3"
+ "@rollup/rollup-linux-x64-musl" "4.53.3"
+ "@rollup/rollup-openharmony-arm64" "4.53.3"
+ "@rollup/rollup-win32-arm64-msvc" "4.53.3"
+ "@rollup/rollup-win32-ia32-msvc" "4.53.3"
+ "@rollup/rollup-win32-x64-gnu" "4.53.3"
+ "@rollup/rollup-win32-x64-msvc" "4.53.3"
fsevents "~2.3.2"
rpc-websockets@10.0.0:
@@ -14193,7 +14206,7 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
-rxjs@^7.5.5, rxjs@^7.8.0:
+rxjs@^7.5.5, rxjs@^7.8.0, rxjs@^7.8.2:
version "7.8.2"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b"
integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==
@@ -15233,7 +15246,7 @@ tinycache@1.1.2:
dependencies:
js-sizeof "0.0.1"
-tinyglobby@^0.2.10:
+tinyglobby@^0.2.10, tinyglobby@^0.2.13:
version "0.2.15"
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2"
integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==
@@ -15998,14 +16011,17 @@ vite-plugin-pwa@0.21.1:
workbox-build "^7.3.0"
workbox-window "^7.3.0"
-vite@6.2.1:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/vite/-/vite-6.2.1.tgz#ae865d4bb93a11844be1bc647c8b2dd1856ea180"
- integrity sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==
+vite@6.4.1:
+ version "6.4.1"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-6.4.1.tgz#afbe14518cdd6887e240a4b0221ab6d0ce733f96"
+ integrity sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==
dependencies:
esbuild "^0.25.0"
+ fdir "^6.4.4"
+ picomatch "^4.0.2"
postcss "^8.5.3"
- rollup "^4.30.1"
+ rollup "^4.34.9"
+ tinyglobby "^0.2.13"
optionalDependencies:
fsevents "~2.3.3"
@@ -16019,16 +16035,16 @@ void-elements@3.1.0:
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==
-wait-on@7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-7.0.1.tgz#5cff9f8427e94f4deacbc2762e6b0a489b19eae9"
- integrity sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog==
+wait-on@9.0.3:
+ version "9.0.3"
+ resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-9.0.3.tgz#3ea858db0b854039e6aff5f323885aaef25e32bf"
+ integrity sha512-13zBnyYvFDW1rBvWiJ6Av3ymAaq8EDQuvxZnPIw3g04UqGi4TyoIJABmfJ6zrvKo9yeFQExNkOk7idQbDJcuKA==
dependencies:
- axios "^0.27.2"
- joi "^17.7.0"
+ axios "^1.13.2"
+ joi "^18.0.1"
lodash "^4.17.21"
- minimist "^1.2.7"
- rxjs "^7.8.0"
+ minimist "^1.2.8"
+ rxjs "^7.8.2"
wcwidth@^1.0.1:
version "1.0.1"