diff --git a/.github/release.yml b/.github/release.yml
new file mode 100644
index 00000000..8973334c
--- /dev/null
+++ b/.github/release.yml
@@ -0,0 +1,4 @@
+changelog:
+ exclude:
+ labels:
+ - 'i18n'
diff --git a/.github/workflows/i18n-automerge.yml b/.github/workflows/i18n-automerge.yml
index 155869e4..f3beff2b 100644
--- a/.github/workflows/i18n-automerge.yml
+++ b/.github/workflows/i18n-automerge.yml
@@ -3,6 +3,8 @@ name: i18n PR auto-merge
on:
pull_request:
types: [opened, synchronize, reopened, labeled]
+ branches:
+ - main
jobs:
run-and-merge:
@@ -10,14 +12,21 @@ jobs:
github.event.pull_request.base.ref == 'main' &&
github.event.pull_request.head.ref == 'l10n_main'
runs-on: ubuntu-latest
- # concurrency:
- # group: ${{ github.workflow }}-${{ github.ref }}
- # cancel-in-progress: true
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
+ - run: sleep 15
+
+ - name: Check if the branch is dirty
+ run: |
+ git fetch origin ${{ github.event.pull_request.head.ref }}
+ if [ $(git rev-parse HEAD) != $(git rev-parse origin/${{ github.event.pull_request.head.ref }}) ]; then
+ echo "Branch is dirty. Exiting..."
+ exit 0
+ fi
+
- name: Check auto-merge conditions
run: |
BASE_SHA="${{ github.event.pull_request.base.sha }}"
@@ -47,8 +56,16 @@ jobs:
exit 0
else
echo "More than 50 lines have been changed. Merging pull request."
+
+ # List of locales changed
+ LOCALES_CHANGED=$(git diff --name-only $BASE_SHA $HEAD_SHA | grep '\.po$' | awk -F '/' '{print $NF}' | sed 's/\.po$//' | tr '\n' ',' | sed 's/,$//')
+
+ # Better subject
+ # "i18n updates ([LOCALES_CHANGED])"
+ SUBJECT="i18n updates ($LOCALES_CHANGED)"
+
PR_NUMBER=$(echo ${{ github.event.pull_request.number }})
- gh pr merge $PR_NUMBER --auto --squash || true
+ gh pr merge $PR_NUMBER --author "github-actions[bot]@users.noreply.github.com" --squash --subject "$SUBJECT" || true
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/i18n-update-readme.yml b/.github/workflows/i18n-update-readme.yml
new file mode 100644
index 00000000..78b50c69
--- /dev/null
+++ b/.github/workflows/i18n-update-readme.yml
@@ -0,0 +1,34 @@
+name: Update README with list of i18n volunteers
+
+on:
+ schedule:
+ # Every week
+ - cron: '0 0 * * 0'
+ workflow_dispatch:
+
+jobs:
+ update-readme:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ - run: npm ci
+ - run: |
+ npm run fetch-i18n-volunteers
+ npm run readme:i18n-volunteers
+
+ # Commit & push if there are changes
+ if git diff --quiet README.md; then
+ echo "No changes to README.md"
+ else
+ echo "Changes to README.md"
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git add README.md
+ git commit -m "Update README.md"
+ git push
+ fi
+ env:
+ CROWDIN_ACCESS_TOKEN: ${{ secrets.CROWDIN_ACCESS_TOKEN }}
diff --git a/.github/workflows/update-catalogs.yml b/.github/workflows/update-catalogs.yml
index 55291fea..5a635c65 100644
--- a/.github/workflows/update-catalogs.yml
+++ b/.github/workflows/update-catalogs.yml
@@ -1,17 +1,18 @@
-name: Update catalogs
+name: Update Catalogs
on:
- pull_request_target:
- types:
- - closed
+ push:
+ branches:
+ - l10n_main
workflow_dispatch:
jobs:
update-catalogs:
- if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
+ with:
+ ref: l10n_main
- uses: actions/setup-node@v4
with:
node-version: 20
@@ -27,5 +28,5 @@ jobs:
git config --global user.name "github-actions[bot]"
git add src/data/catalogs.json
git commit -m "Update catalogs.json"
- git push origin HEAD:main
+ git push origin HEAD:l10n_main || true
fi
diff --git a/README.md b/README.md
index 7cbf9988..18529e68 100644
--- a/README.md
+++ b/README.md
@@ -292,6 +292,59 @@ Costs involved in running and developing this web app:
[](https://github.com/cheeaun/phanpy/graphs/contributors)
+### Translation volunteers
+
+
+-
alidsds11 (Arabic)
+-
BoFFire (Arabic, French, Kabyle)
+-
Brawaru (Russian)
+-
cbasje (Dutch)
+-
cbo92 (French)
+-
CDN (Chinese Simplified)
+-
dannypsnl (Chinese Traditional)
+-
databio (Catalan)
+-
drydenwu (Chinese Traditional)
+-
elissarc (French)
+-
ElPamplina (Spanish)
+-
Fitik (Esperanto, Hebrew)
+-
Freeesia (Japanese)
+-
ghose (Galician)
+-
hongminhee (Korean)
+-
isard (Catalan)
+-
karlafej (Czech)
+-
katullo11 (Italian)
+-
Kytta (German)
+-
llun (Thai)
+-
lucasofchirst (Occitan, Portuguese, Portuguese, Brazilian)
+-
marcin.kozinski (Polish)
+-
mojosoeun (Korean)
+-
moreal (Korean)
+-
MrWillCom (Chinese Simplified)
+-
nclm (French)
+-
pazpi (Italian)
+-
punkrockgirl (Basque)
+-
radecos (French)
+-
Razem (Czech)
+-
realpixelcode (German)
+-
rezahosseinzadeh (Persian)
+-
rwmpelstilzchen (Esperanto, Hebrew)
+-
SadmL (Russian)
+-
Sky_NiniKo (French)
+-
Su5hicz (Czech)
+-
Talos00 (Italian)
+-
tferrermo (Spanish)
+-
tux93 (German)
+-
Urbestro (Esperanto, Spanish)
+-
UsualUsername (Russian)
+-
Vac31. (Lithuanian)
+-
valtlai (Finnish)
+-
xabi_itzultzaile (Basque)
+-
xen4n (Ukrainian)
+-
xqueralt (Catalan)
+-
ZiriSut (Kabyle)
+-
zkreml (Czech)
+
+
## Backstory
I am one of the earliest users of Twitter. Twitter was launched on [15 July 2006](https://en.wikipedia.org/wiki/Twitter). I joined on December 2006 and my [first tweet](https://twitter.com/cheeaun/status/1298723) was posted on 18 December 2006.
diff --git a/i18n-volunteers.json b/i18n-volunteers.json
new file mode 100644
index 00000000..4c9aa6a5
--- /dev/null
+++ b/i18n-volunteers.json
@@ -0,0 +1,345 @@
+[
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/12571163/medium/9f3ea938f4243f5ffe2a43f814ddc9e8_default.png",
+ "username": "alidsds11",
+ "languages": [
+ "Arabic"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/13170041/medium/603136896af17fc005fd592ce3f48717_default.png",
+ "username": "BoFFire",
+ "languages": [
+ "Arabic",
+ "French",
+ "Kabyle"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/12898464/medium/d3758a76b894bade4bf271c9b32ea69b.png",
+ "username": "Brawaru",
+ "languages": [
+ "Russian"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/15460040/medium/1cfcfe5f5511b783b5d9f2b968bad819.png",
+ "username": "cbasje",
+ "languages": [
+ "Dutch"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/15525631/medium/51293156034d0236f1a1020c10f7d539_default.png",
+ "username": "cbo92",
+ "languages": [
+ "French"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/15910131/medium/67fab7eeab5551853450e76e2ef19e59.jpeg",
+ "username": "CDN",
+ "languages": [
+ "Chinese Simplified"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16556801/medium/ed5e501ca1f3cc6525d2da28db646346.jpeg",
+ "username": "dannypsnl",
+ "languages": [
+ "Chinese Traditional"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/3711/medium/d95ddd44e8dcb3a039f8a3463aed781d_default.png",
+ "username": "databio",
+ "languages": [
+ "Catalan"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/12618120/medium/ccb11bd042bbf4c7189033f7af2dbd32_default.png",
+ "username": "drydenwu",
+ "languages": [
+ "Chinese Traditional"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/13557465/medium/8feebf3677fa80c01e8c54c4fbe097e0_default.png",
+ "username": "elissarc",
+ "languages": [
+ "French"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16528627/medium/9036f6eced0257f4e1ea4c5bd499de2d_default.png",
+ "username": "ElPamplina",
+ "languages": [
+ "Spanish"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/14277386/medium/29b30d2c73a214000e3941c9978f49e4_default.png",
+ "username": "Fitik",
+ "languages": [
+ "Esperanto",
+ "Hebrew"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/14444512/medium/99d0e7a3076deccbdfe0aa0b0612308c.jpeg",
+ "username": "Freeesia",
+ "languages": [
+ "Japanese"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/12617257/medium/a201650da44fed28890b0e0d8477a663.jpg",
+ "username": "ghose",
+ "languages": [
+ "Galician"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/15248754/medium/0dac6334ea0f4e8d4194a605c0a5594a.jpeg",
+ "username": "hongminhee",
+ "languages": [
+ "Korean"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/13454728/medium/1f78b7124b3c962bc4ae55e8d701fc91_default.png",
+ "username": "isard",
+ "languages": [
+ "Catalan"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16532403/medium/4cefb19623bcc44d7cdb9e25aebf5250.jpeg",
+ "username": "karlafej",
+ "languages": [
+ "Czech"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/15791971/medium/88bdda3090339f16f6083390d32bb434_default.png",
+ "username": "katullo11",
+ "languages": [
+ "Italian"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/14677260/medium/e53420d200961f48602324e18c091bdc.png",
+ "username": "Kytta",
+ "languages": [
+ "German"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16529521/medium/ae6add93a901b0fefa2d9b1077920d73.png",
+ "username": "llun",
+ "languages": [
+ "Thai"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16291756/medium/e1c4210f15537394cc764b8bc2dffe37.jpg",
+ "username": "lucasofchirst",
+ "languages": [
+ "Occitan",
+ "Portuguese",
+ "Portuguese, Brazilian"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16537713/medium/825f0bf1a14fc545a76891a52839d86e_default.png",
+ "username": "marcin.kozinski",
+ "languages": [
+ "Polish"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/12882812/medium/77744d8db46e9a3e09030e1a02b7a572.jpeg",
+ "username": "mojosoeun",
+ "languages": [
+ "Korean"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/13613969/medium/c7834ddc0ada84a79671697a944bb274.png",
+ "username": "moreal",
+ "languages": [
+ "Korean"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/14158861/medium/ba1ff31dc5743b067ea6685f735229a5_default.png",
+ "username": "MrWillCom",
+ "languages": [
+ "Chinese Simplified"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/15652333/medium/7f36f289f9e2fe41d89ad534a1047f0e.png",
+ "username": "nclm",
+ "languages": [
+ "French"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16539461/medium/2f41b9f0b802c1d200a6ab62167a7229_default.png",
+ "username": "pazpi",
+ "languages": [
+ "Italian"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/15106977/medium/54bf93b19af8bbfdee579ea51685bafa.jpeg",
+ "username": "punkrockgirl",
+ "languages": [
+ "Basque"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16536247/medium/f010c8e718a36229733a8b58f6bad2a4_default.png",
+ "username": "radecos",
+ "languages": [
+ "French"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16538917/medium/092ec03f56f9dd1cbce94379fa4d4d38.png",
+ "username": "Razem",
+ "languages": [
+ "Czech"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/14345134/medium/89a299239890c79a1d791d08ec3951dc.png",
+ "username": "realpixelcode",
+ "languages": [
+ "German"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16527325/medium/37ebb27e7a50f7f85ae93beafc7028a2.jpg",
+ "username": "rezahosseinzadeh",
+ "languages": [
+ "Persian"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/13422319/medium/66632a98d73d48e36753d94ebcec9d4f.png",
+ "username": "rwmpelstilzchen",
+ "languages": [
+ "Esperanto",
+ "Hebrew"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16538605/medium/bcdb6e3286b7d6237923f3a9383eed29.png",
+ "username": "SadmL",
+ "languages": [
+ "Russian"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/14565190/medium/79100599131b7776e9803e4b696915a3_default.png",
+ "username": "Sky_NiniKo",
+ "languages": [
+ "French"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16532441/medium/1a47e8d80c95636e02d2260f6e233ca5.png",
+ "username": "Su5hicz",
+ "languages": [
+ "Czech"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16533843/medium/7314c15492ef90118c33a80a427e6c87_default.png",
+ "username": "Talos00",
+ "languages": [
+ "Italian"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16530049/medium/683f3581620c6b4a5c753b416ed695a7.jpeg",
+ "username": "tferrermo",
+ "languages": [
+ "Spanish"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16527851/medium/649e5a9a8a8cc61ced670d89e9cca082.png",
+ "username": "tux93",
+ "languages": [
+ "German"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16529833/medium/2991a65722acd721849656223014cd49.png",
+ "username": "Urbestro",
+ "languages": [
+ "Esperanto",
+ "Spanish"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16539171/medium/db6fb87481026c72b895adfb94e17d2c_default.png",
+ "username": "UsualUsername",
+ "languages": [
+ "Russian"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/14427566/medium/ab733b5044c21867fc5a9d1b22cd2c03.png",
+ "username": "Vac31.",
+ "languages": [
+ "Lithuanian"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16026914/medium/e3ca187f354a298ef0c9d02a0ed17be7.jpg",
+ "username": "valtlai",
+ "languages": [
+ "Finnish"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/15982109/medium/9c03062bdc1d3c6d384dbfead97c26ba.jpeg",
+ "username": "xabi_itzultzaile",
+ "languages": [
+ "Basque"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16556017/medium/216e0f7a0c35b079920366939a3aaca7_default.png",
+ "username": "xen4n",
+ "languages": [
+ "Ukrainian"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16532657/medium/f309f319266e1ff95f3070eab0c9a9d9_default.png",
+ "username": "xqueralt",
+ "languages": [
+ "Catalan"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/14041603/medium/6ab77a0467b06aeb49927c6d9c409f89.jpg",
+ "username": "ZiriSut",
+ "languages": [
+ "Kabyle"
+ ]
+ },
+ {
+ "avatarUrl": "https://crowdin-static.downloads.crowdin.com/avatar/16530601/medium/e1b6d5c24953b6405405c1ab33c0fa46.jpeg",
+ "username": "zkreml",
+ "languages": [
+ "Czech"
+ ]
+ }
+]
\ No newline at end of file
diff --git a/lingui.config.js b/lingui.config.js
index 14841e5d..e612248d 100644
--- a/lingui.config.js
+++ b/lingui.config.js
@@ -1,7 +1,8 @@
-import { LOCALES } from './src/locales';
+import { ALL_LOCALES } from './src/locales';
const config = {
- locales: LOCALES,
+ locales: ALL_LOCALES,
+ sourceLocale: 'en',
pseudoLocale: 'pseudo-LOCALE',
fallbackLocales: {
default: 'en',
diff --git a/package-lock.json b/package-lock.json
index 93fb2726..1a18e531 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,9 +14,9 @@
"@github/text-expander-element": "~2.7.1",
"@iconify-icons/mingcute": "~1.2.9",
"@justinribeiro/lite-youtube": "~1.5.0",
- "@lingui/detect-locale": "~4.11.3",
- "@lingui/macro": "~4.11.3",
- "@lingui/react": "~4.11.3",
+ "@lingui/detect-locale": "~4.11.4",
+ "@lingui/macro": "~4.11.4",
+ "@lingui/react": "~4.11.4",
"@szhsin/react-menu": "~4.2.2",
"compare-versions": "~6.1.1",
"fast-blurhash": "~1.1.4",
@@ -25,6 +25,7 @@
"html-prettify": "~1.0.7",
"idb-keyval": "~6.2.1",
"intl-locale-textinfo-polyfill": "~2.1.1",
+ "js-cookie": "~3.0.5",
"just-debounce-it": "~3.2.0",
"lz-string": "~1.5.0",
"masto": "~6.8.0",
@@ -33,7 +34,7 @@
"p-throttle": "~6.2.0",
"preact": "~10.23.2",
"punycode": "~2.3.1",
- "react-hotkeys-hook": "~4.5.0",
+ "react-hotkeys-hook": "~4.5.1",
"react-intersection-observer": "~9.13.0",
"react-quick-pinch-zoom": "~5.1.0",
"react-router-dom": "6.6.2",
@@ -42,25 +43,25 @@
"tinyld": "~1.3.4",
"toastify-js": "~1.12.0",
"uid": "~2.0.2",
- "use-debounce": "~10.0.2",
+ "use-debounce": "~10.0.3",
"use-long-press": "~3.2.0",
"use-resize-observer": "~9.1.0",
- "valtio": "1.13.2"
+ "valtio": "2.0.0"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "~4.3.1",
- "@lingui/cli": "~4.11.3",
- "@lingui/vite-plugin": "~4.11.3",
+ "@lingui/cli": "~4.11.4",
+ "@lingui/vite-plugin": "~4.11.4",
"@preact/preset-vite": "~2.9.0",
"babel-plugin-macros": "~3.1.0",
- "postcss": "~8.4.41",
+ "postcss": "~8.4.45",
"postcss-dark-theme-class": "~1.3.0",
- "postcss-preset-env": "~10.0.1",
+ "postcss-preset-env": "~10.0.2",
"twitter-text": "~3.1.0",
- "vite": "~5.4.1",
+ "vite": "~5.4.3",
"vite-plugin-generate-file": "~0.2.0",
- "vite-plugin-html-config": "~1.0.11",
- "vite-plugin-pwa": "~0.20.1",
+ "vite-plugin-html-config": "~2.0.2",
+ "vite-plugin-pwa": "~0.20.3",
"vite-plugin-remove-console": "~2.2.0",
"vite-plugin-run": "~0.5.2",
"workbox-cacheable-response": "~7.1.0",
@@ -1866,9 +1867,9 @@
}
},
"node_modules/@csstools/cascade-layer-name-parser": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.0.tgz",
- "integrity": "sha512-9GEQIvTMrjXfYaVnw1+FteDX5yF65CZq4ttYP75O3CANQevaCJ9jVVTiZt9YTpjYIk8C1mmf52y2S4Hr/CaE/g==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.1.tgz",
+ "integrity": "sha512-G9ZYN5+yr/E6xYSiy1BwOEFP5p88ZtWo8sL4NztKBkRRAwRkzVGa70M+D+fYHugMID5jkLeNt5X9jYd5EaVuyg==",
"dev": true,
"funding": [
{
@@ -1884,8 +1885,8 @@
"node": ">=18"
},
"peerDependencies": {
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0"
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1"
}
},
"node_modules/@csstools/color-helpers": {
@@ -1908,9 +1909,9 @@
}
},
"node_modules/@csstools/css-calc": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.0.0.tgz",
- "integrity": "sha512-fxPxNrEVGeej4F35Xt69Q7gPMKa7oEGNxeP1DpA01sWpTF3Yhgux/0slVX3jLHd7dhlszeQlNAUhpAorVxoHdQ==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.0.1.tgz",
+ "integrity": "sha512-e59V+sNp6e5m+9WnTUydA1DQO70WuKUdseflRpWmXxocF/h5wWGIxUjxfvLtajcmwstH0vm6l0reKMzcyI757Q==",
"dev": true,
"funding": [
{
@@ -1926,14 +1927,14 @@
"node": ">=18"
},
"peerDependencies": {
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0"
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1"
}
},
"node_modules/@csstools/css-color-parser": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.1.tgz",
- "integrity": "sha512-++7I+Z7S/BWedPlR4z8aW1zsvtJFufFbpdPwdx5+W50dq5EYLV3sulitSNMry0BNmNMzeczdQij/f4C+ch01vQ==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.2.tgz",
+ "integrity": "sha512-mNg7A6HnNjlm0we/pDS9dUafOuBxcanN0TBhEGeIk6zZincuk0+mAbnBqfVs29NlvWHZ8diwTG6g5FeU8246sA==",
"dev": true,
"funding": [
{
@@ -1947,20 +1948,20 @@
],
"dependencies": {
"@csstools/color-helpers": "^5.0.1",
- "@csstools/css-calc": "^2.0.0"
+ "@csstools/css-calc": "^2.0.1"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0"
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1"
}
},
"node_modules/@csstools/css-parser-algorithms": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.0.tgz",
- "integrity": "sha512-20hEErXV9GEx15qRbsJVzB91ryayx1F2duHPBrfZXQAHz/dJG0u/611URpr28+sFjm3EI7U17Pj9SVA9NSAGJA==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz",
+ "integrity": "sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==",
"dev": true,
"funding": [
{
@@ -1976,13 +1977,13 @@
"node": ">=18"
},
"peerDependencies": {
- "@csstools/css-tokenizer": "^3.0.0"
+ "@csstools/css-tokenizer": "^3.0.1"
}
},
"node_modules/@csstools/css-tokenizer": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.0.tgz",
- "integrity": "sha512-efZvfJyYrqH9hPCKtOBywlTsCXnEzAI9sLHFzUsDpBb+1bQ+bxJnwL9V2bRKv9w4cpIp75yxGeZRaVKoMQnsEg==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz",
+ "integrity": "sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==",
"dev": true,
"funding": [
{
@@ -1999,9 +2000,9 @@
}
},
"node_modules/@csstools/media-query-list-parser": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.0.tgz",
- "integrity": "sha512-W0JlkUFwXjo703wt06AcaWuUcS+6x6IEDyxV6W65Sw+vLCYp+uPsrps+PXTiIfN0V1Pqj5snPzN7EYLmbz1zjg==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz",
+ "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==",
"dev": true,
"funding": [
{
@@ -2017,8 +2018,8 @@
"node": ">=18"
},
"peerDependencies": {
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0"
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1"
}
},
"node_modules/@csstools/postcss-cascade-layers": {
@@ -2048,9 +2049,9 @@
}
},
"node_modules/@csstools/postcss-color-function": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.1.tgz",
- "integrity": "sha512-nRnwVdqdMUIsE7cGbI+La4fxME6tT9bVDRYfBHW/0QTLwCVJN4+DC/3kqiU6AdTne31hUBGPwcH1uzkuc4fO5A==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.2.tgz",
+ "integrity": "sha512-q/W3RXh66SM7WqxW3/KU6koL8nOgqyB/wrcU3+ThXnNtXY2+k8UgdE301ISJpMt6PDyYgC7eMaIBo535RvFIgw==",
"dev": true,
"funding": [
{
@@ -2063,9 +2064,9 @@
}
],
"dependencies": {
- "@csstools/css-color-parser": "^3.0.1",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/css-color-parser": "^3.0.2",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
"@csstools/postcss-progressive-custom-properties": "^4.0.0",
"@csstools/utilities": "^2.0.0"
},
@@ -2077,9 +2078,9 @@
}
},
"node_modules/@csstools/postcss-color-mix-function": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.1.tgz",
- "integrity": "sha512-RRdu3CppF9dTn3AvDkeEkOL8ZDpDh/TF6YLV1JKl768BQk0XJ026xWfttoL911k0g8yprES3wFujjLsK0XhsEg==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.2.tgz",
+ "integrity": "sha512-zG9PHNzZVCRk6eprm+T/ybrnuiwLdO+RR7+GCtNut+NZJGtPJj6bfPOEX23aOlMslLcRAlN6QOpxH3tovn+WpA==",
"dev": true,
"funding": [
{
@@ -2092,9 +2093,9 @@
}
],
"dependencies": {
- "@csstools/css-color-parser": "^3.0.1",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/css-color-parser": "^3.0.2",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
"@csstools/postcss-progressive-custom-properties": "^4.0.0",
"@csstools/utilities": "^2.0.0"
},
@@ -2106,9 +2107,9 @@
}
},
"node_modules/@csstools/postcss-content-alt-text": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.0.tgz",
- "integrity": "sha512-1pPjMaSUftwn/4N7RtJif91cB6BBEo0LQX2vryrDMF5uKDqt4RMpIi9ZFTsKtcXBFZexNGEWXZzPABnooJGkzQ==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.1.tgz",
+ "integrity": "sha512-TWjjewVZqdkjavsi8a2THuXgkhUum1k/m4QJpZpzOv72q6WnaoQZGSj5t5uCs7ymJr0H3qj6JcXMwMApSWUOGQ==",
"dev": true,
"funding": [
{
@@ -2121,8 +2122,8 @@
}
],
"dependencies": {
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
"@csstools/postcss-progressive-custom-properties": "^4.0.0",
"@csstools/utilities": "^2.0.0"
},
@@ -2134,9 +2135,9 @@
}
},
"node_modules/@csstools/postcss-exponential-functions": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.0.tgz",
- "integrity": "sha512-sH7MBlsn6yft6xQ8uQ9MCWFHbZCUL3HIN3IntUabv75syl0dPldECTqLJix5q5ilSQxDQ1L+LajeZk84S6GG9w==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.1.tgz",
+ "integrity": "sha512-A/MG8es3ylFzZ30oYIQUyJcMOfTfCs0dqqBMzeuzaPRlx4q/72WG+BbKe/pL9BUNIWsM0Q8jn3e3la8enjHJJA==",
"dev": true,
"funding": [
{
@@ -2149,9 +2150,9 @@
}
],
"dependencies": {
- "@csstools/css-calc": "^2.0.0",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0"
+ "@csstools/css-calc": "^2.0.1",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1"
},
"engines": {
"node": ">=18"
@@ -2187,9 +2188,9 @@
}
},
"node_modules/@csstools/postcss-gamut-mapping": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.1.tgz",
- "integrity": "sha512-bCQ609PZsGUmrTVeGaPgYF27DFQ7gg2no3j6qXY3MOAVjfPRvMIlGdpLejhgYra1VUoTNA1SUqHLNgFWoJ/pRA==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.2.tgz",
+ "integrity": "sha512-/1ur3ca9RWg/KnbLlxaDswyjLSGoaHNDruAzrVhkn5axgd7LOH6JHCBRhrKDafdMw9bf4MQrYFoaLfHAPekLFg==",
"dev": true,
"funding": [
{
@@ -2202,9 +2203,9 @@
}
],
"dependencies": {
- "@csstools/css-color-parser": "^3.0.1",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0"
+ "@csstools/css-color-parser": "^3.0.2",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1"
},
"engines": {
"node": ">=18"
@@ -2214,9 +2215,9 @@
}
},
"node_modules/@csstools/postcss-gradients-interpolation-method": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.1.tgz",
- "integrity": "sha512-ZINUsXxFrJ5bpfpq772BQzu5K23dDFoQwvyeQRHHQpDOS8hMIoBMmjDjtZV5fGJ/gtL/blKUvytAyrgBzaqvUQ==",
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.2.tgz",
+ "integrity": "sha512-qRpvA4sduAfiV9yZG4OM7q/h2Qhr3lg+GrHe9NZwuzWnfSDLGh+Dh4Ea6fQ+1++jdKXW/Cb4/vHRp0ssQYra4w==",
"dev": true,
"funding": [
{
@@ -2229,9 +2230,9 @@
}
],
"dependencies": {
- "@csstools/css-color-parser": "^3.0.1",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/css-color-parser": "^3.0.2",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
"@csstools/postcss-progressive-custom-properties": "^4.0.0",
"@csstools/utilities": "^2.0.0"
},
@@ -2243,9 +2244,9 @@
}
},
"node_modules/@csstools/postcss-hwb-function": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.1.tgz",
- "integrity": "sha512-1SluTV2F2WiWPw5CHQ/UOsUrO5y89VDQlOICzHIF3Mx50YdTf0qYZ/dTXL/Fa+1AgzSn4IYt51XXjgxI7pe/jw==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.2.tgz",
+ "integrity": "sha512-RUBVCyJE1hTsf9vGp3zrALeMollkAlHRFKm+T36y67nLfOOf+6GNQsdTGFAyLrY65skcm8ddC26Jp1n9ZIauEA==",
"dev": true,
"funding": [
{
@@ -2258,9 +2259,9 @@
}
],
"dependencies": {
- "@csstools/css-color-parser": "^3.0.1",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/css-color-parser": "^3.0.2",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
"@csstools/postcss-progressive-custom-properties": "^4.0.0",
"@csstools/utilities": "^2.0.0"
},
@@ -2347,9 +2348,9 @@
}
},
"node_modules/@csstools/postcss-light-dark-function": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.1.tgz",
- "integrity": "sha512-RHliBdalIg7KZNwv5B1VwF1qFEhmz3ZIbZXyxOH1g7W72S0oazMKIvYVgHenCxHCxWOxSR0ipZ+8kHa+fm4O5A==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.2.tgz",
+ "integrity": "sha512-QAWWDJtJ7ywzhaMe09QwhjhuwB0XN04fW1MFwoEJMcYyiQub4a57mVFV+ngQEekUhsqe/EtKVCzyOx4q3xshag==",
"dev": true,
"funding": [
{
@@ -2362,8 +2363,8 @@
}
],
"dependencies": {
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
"@csstools/postcss-progressive-custom-properties": "^4.0.0",
"@csstools/utilities": "^2.0.0"
},
@@ -2466,9 +2467,9 @@
}
},
"node_modules/@csstools/postcss-logical-viewport-units": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.0.tgz",
- "integrity": "sha512-7a0d7TLfHP3k7n+XGj5NJopgyKgl/VKyAPapYIo97aujB7+8M4dBE1Og0OmWng+H/drQWXoSlCI3pov5XwVtxQ==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.1.tgz",
+ "integrity": "sha512-JsfaoTiBqIuRE+CYL4ZpYKOqJ965GyiMH4b8UrY0Z7i5GfMiHZrK7xtTB29piuyKQzrW+Z8w3PAExhwND9cuAQ==",
"dev": true,
"funding": [
{
@@ -2481,7 +2482,7 @@
}
],
"dependencies": {
- "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/css-tokenizer": "^3.0.1",
"@csstools/utilities": "^2.0.0"
},
"engines": {
@@ -2492,9 +2493,9 @@
}
},
"node_modules/@csstools/postcss-media-minmax": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.0.tgz",
- "integrity": "sha512-21Cmy5QWbexbpKAAJntGomjn644BWWs7gXkx/Vid1SjqxIRmPUB/dcJ4xBWwjpFuhrPKzT8a3Pr+IJv9R9v9Yg==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.1.tgz",
+ "integrity": "sha512-EMa3IgUip+F/MwH4r2KfIA9ym9hQkT2PpR9MOukdomfGGCFuw9V3n/iIOBKziN1qfeddsYoOvtYOKQcHU2yIjg==",
"dev": true,
"funding": [
{
@@ -2507,10 +2508,10 @@
}
],
"dependencies": {
- "@csstools/css-calc": "^2.0.0",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
- "@csstools/media-query-list-parser": "^3.0.0"
+ "@csstools/css-calc": "^2.0.1",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
+ "@csstools/media-query-list-parser": "^3.0.1"
},
"engines": {
"node": ">=18"
@@ -2520,9 +2521,9 @@
}
},
"node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.0.tgz",
- "integrity": "sha512-TV8Q7ec0zbCxlmTmUF8CvAWWbK3q9ops3+sGCc6rHAGrfkoA+HyMGwJBZudddZQOV9MZS949mhtYIV4AgIRizw==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.1.tgz",
+ "integrity": "sha512-JTzMQz//INahTALkvXnC5lC2fJKzwb5PY443T2zaM9hAzM7nzHMLIlEfFgdtBahVIBtBSalMefdxNr99LGW1lQ==",
"dev": true,
"funding": [
{
@@ -2535,9 +2536,9 @@
}
],
"dependencies": {
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
- "@csstools/media-query-list-parser": "^3.0.0"
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
+ "@csstools/media-query-list-parser": "^3.0.1"
},
"engines": {
"node": ">=18"
@@ -2598,9 +2599,9 @@
}
},
"node_modules/@csstools/postcss-oklab-function": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.1.tgz",
- "integrity": "sha512-hEJ83YhqNII3/TBGcJLjSkNx65p4Zbz6YFm6ww2BRRO223/GTFOHT2ElicWmnBtoZWKORgysI4wtLv3p6LZSFQ==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.2.tgz",
+ "integrity": "sha512-2iSK/T77PHMeorakBAk/WLxSodfIJ/lmi6nxEkuruXfhGH7fByZim4Fw6ZJf4B73SVieRSH2ep8zvYkA2ZfRtA==",
"dev": true,
"funding": [
{
@@ -2613,9 +2614,9 @@
}
],
"dependencies": {
- "@csstools/css-color-parser": "^3.0.1",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/css-color-parser": "^3.0.2",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
"@csstools/postcss-progressive-custom-properties": "^4.0.0",
"@csstools/utilities": "^2.0.0"
},
@@ -2652,9 +2653,9 @@
}
},
"node_modules/@csstools/postcss-relative-color-syntax": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.1.tgz",
- "integrity": "sha512-GbcQPmfBOjKomHuOVB6troujQg65ykCMt4OGot75Bdev7jAHC9hd0AX5qMprpG9AF0dA012curAVqY0ehmdYwQ==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.2.tgz",
+ "integrity": "sha512-aBpuUdpJBswNGfw6lOkhown2cZ0YXrMjASye56nkoRpgRe9yDF4BM1fvEuakrCDiaeoUzVaI4SF6+344BflXfQ==",
"dev": true,
"funding": [
{
@@ -2667,9 +2668,9 @@
}
],
"dependencies": {
- "@csstools/css-color-parser": "^3.0.1",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/css-color-parser": "^3.0.2",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
"@csstools/postcss-progressive-custom-properties": "^4.0.0",
"@csstools/utilities": "^2.0.0"
},
@@ -2706,9 +2707,9 @@
}
},
"node_modules/@csstools/postcss-stepped-value-functions": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.0.tgz",
- "integrity": "sha512-sJUW1axQuxRyD59zr9hMJ6MoM/99UkxNc7fxJ1kFdTl1B5dS3TxvVzY1fRq1C/JsgBw6uNzfy/i52SrVNtbbXw==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.1.tgz",
+ "integrity": "sha512-dk3KqVcIEYzy9Mvx8amoBbk123BWgd5DfjXDiPrEqxGma37PG7m/MoMmHQhuVHIjvPDHoJwyIZi2yy7j0RA5fw==",
"dev": true,
"funding": [
{
@@ -2721,9 +2722,9 @@
}
],
"dependencies": {
- "@csstools/css-calc": "^2.0.0",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0"
+ "@csstools/css-calc": "^2.0.1",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1"
},
"engines": {
"node": ">=18"
@@ -2759,9 +2760,9 @@
}
},
"node_modules/@csstools/postcss-trigonometric-functions": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.0.tgz",
- "integrity": "sha512-M7CivX++ZOQvnF+eZ8FHg2X8GYOfSUFH6GRtr7mGeIgd38WmT1WCBogqBvz/Y5x9VUeor9EuJX2K06bP7p4BuA==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.1.tgz",
+ "integrity": "sha512-QHOYuN3bzS/rcpAygFhJxJUtD8GuJEWF6f9Zm518Tq/cSMlcTgU+v0geyi5EqbmYxKMig2oKCKUSGqOj9gehkg==",
"dev": true,
"funding": [
{
@@ -2774,9 +2775,9 @@
}
],
"dependencies": {
- "@csstools/css-calc": "^2.0.0",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0"
+ "@csstools/css-calc": "^2.0.1",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1"
},
"engines": {
"node": ">=18"
@@ -3537,18 +3538,18 @@
"license": "MIT"
},
"node_modules/@lingui/babel-plugin-extract-messages": {
- "version": "4.11.3",
- "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-4.11.3.tgz",
- "integrity": "sha512-wLiquhtxE7qUmoKl4UStFI1XgrCkk9zwxc8z62LPpbutkyxO21B5k8fBUGlgWoKJaXbpvS8VIU8j2663q99JnQ==",
+ "version": "4.11.4",
+ "resolved": "https://registry.npmjs.org/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-4.11.4.tgz",
+ "integrity": "sha512-7gUOsYJ4wIjv/0tGxAGiGpgWKCybFPP0tCQMz6baa9xcsk8Vp7Xmuf9og1AD6EMawjStibQsQyE6xaRnJgpoHg==",
"dev": true,
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/@lingui/cli": {
- "version": "4.11.3",
- "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-4.11.3.tgz",
- "integrity": "sha512-ykJLmQciK81I0Cd/iLg8dSpESV9Hnsbw5+G98IEAf4exvoUGRJ2UzkeNc/HeGx3D5Fg+TI8YNWwCbZ7NAOsDCQ==",
+ "version": "4.11.4",
+ "resolved": "https://registry.npmjs.org/@lingui/cli/-/cli-4.11.4.tgz",
+ "integrity": "sha512-PauBkvi++YkYAYq6w9MwkBmE6KiDE9wRh5DkN8yFPRcfj64vkE2l1HFENCqL/jg63kr8esOAiueD9+CtUGVyDg==",
"dev": true,
"dependencies": {
"@babel/core": "^7.21.0",
@@ -3556,11 +3557,11 @@
"@babel/parser": "^7.21.2",
"@babel/runtime": "^7.21.0",
"@babel/types": "^7.21.2",
- "@lingui/babel-plugin-extract-messages": "4.11.3",
- "@lingui/conf": "4.11.3",
- "@lingui/core": "4.11.3",
- "@lingui/format-po": "4.11.3",
- "@lingui/message-utils": "4.11.3",
+ "@lingui/babel-plugin-extract-messages": "4.11.4",
+ "@lingui/conf": "4.11.4",
+ "@lingui/core": "4.11.4",
+ "@lingui/format-po": "4.11.4",
+ "@lingui/message-utils": "4.11.4",
"babel-plugin-macros": "^3.0.1",
"chalk": "^4.1.0",
"chokidar": "3.5.1",
@@ -3571,7 +3572,7 @@
"esbuild": "^0.17.10",
"glob": "^7.1.4",
"inquirer": "^7.3.3",
- "micromatch": "4.0.2",
+ "micromatch": "^4.0.2",
"normalize-path": "^3.0.0",
"ora": "^5.1.0",
"pathe": "^1.1.0",
@@ -4069,9 +4070,9 @@
}
},
"node_modules/@lingui/conf": {
- "version": "4.11.3",
- "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-4.11.3.tgz",
- "integrity": "sha512-KwUJDrbzlZEXmlmqttpB/Sd9hiIo0sqccsZaYTHzW/uULZT9T11aw/f6RcPLCVJeSKazg/7dJhR1cKlxKzvjKA==",
+ "version": "4.11.4",
+ "resolved": "https://registry.npmjs.org/@lingui/conf/-/conf-4.11.4.tgz",
+ "integrity": "sha512-FC12yP0MHzu2QN5/4JkFHdz25l4Yu2ucjj3K12Y8tW/75oPh+n8k2u1+3/M68zWoqf5yyFvU4m2A+gxEmeR0iw==",
"dependencies": {
"@babel/runtime": "^7.20.13",
"chalk": "^4.1.0",
@@ -4149,12 +4150,12 @@
}
},
"node_modules/@lingui/core": {
- "version": "4.11.3",
- "resolved": "https://registry.npmjs.org/@lingui/core/-/core-4.11.3.tgz",
- "integrity": "sha512-IjJxn0Kvzv+ICnGlMqn8wRIQLikCJVrolb1oyi6GqtbiuPiwKYeU0D6Hbe146lMaTN8juc3tOCBS+Fr02XqFIQ==",
+ "version": "4.11.4",
+ "resolved": "https://registry.npmjs.org/@lingui/core/-/core-4.11.4.tgz",
+ "integrity": "sha512-W0bBIFe44s//Qs+RQ+NMfzK5vAm9oEKyDddlN94Db6rzeUT/IJo7N+T75A6Bya8v/BrtF2G/W4b77eS3sd0utw==",
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@lingui/message-utils": "4.11.3",
+ "@lingui/message-utils": "4.11.4",
"unraw": "^3.0.0"
},
"engines": {
@@ -4162,21 +4163,21 @@
}
},
"node_modules/@lingui/detect-locale": {
- "version": "4.11.3",
- "resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-4.11.3.tgz",
- "integrity": "sha512-5QJsNOzRcuT97gkgMk/yUqt52adXdd+yzs/29yleWpFEANO/Z9Zt/ozwdpThf8zeFsi8TM5GRZFQ1ScpKxuPOQ==",
+ "version": "4.11.4",
+ "resolved": "https://registry.npmjs.org/@lingui/detect-locale/-/detect-locale-4.11.4.tgz",
+ "integrity": "sha512-JmIuFSyB8KitO02iAWV3+v0brkHYx72akiwhe2Jta9gzRVPeDFj2uyzO+UJXzzhPylAkX9o3suXIMXpy1dmXAQ==",
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/@lingui/format-po": {
- "version": "4.11.3",
- "resolved": "https://registry.npmjs.org/@lingui/format-po/-/format-po-4.11.3.tgz",
- "integrity": "sha512-RgEkoo0aEAk7X1xGrApcpqkz6GLdzkRLGw2jo3mmCVR0P7P9sWbJL/cd01GmR+HzAOo8Zx5oIayaKh9iyJS8tA==",
+ "version": "4.11.4",
+ "resolved": "https://registry.npmjs.org/@lingui/format-po/-/format-po-4.11.4.tgz",
+ "integrity": "sha512-PiWbTiiNgYZTFVuBHYirtAG98cDxrT0IwmSvETQk4YbaqCdn28/J7fRFZScsKqe8LmpnxX4EXZLs/R0MP2jLHA==",
"dev": true,
"dependencies": {
- "@lingui/conf": "4.11.3",
- "@lingui/message-utils": "4.11.3",
+ "@lingui/conf": "4.11.4",
+ "@lingui/message-utils": "4.11.4",
"date-fns": "^3.6.0",
"pofile": "^1.1.4"
},
@@ -4185,15 +4186,15 @@
}
},
"node_modules/@lingui/macro": {
- "version": "4.11.3",
- "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-4.11.3.tgz",
- "integrity": "sha512-D0me8ZRtH0ylSavhKZu0FYf5mJ1y6kDMMPjYVDyiT5ooOI/5jjv9LIAqrdYGCBygnwsxOG1dzDw6+3s5GTs+Bg==",
+ "version": "4.11.4",
+ "resolved": "https://registry.npmjs.org/@lingui/macro/-/macro-4.11.4.tgz",
+ "integrity": "sha512-mgfyBpp/UCiaJxr+DTBtaCUKnq2fV9JrmUmBumC9PaFDCXYfjB0A2gaq2XEgn9PmUKuzC7PGs1sPJ3TBJ8uGTw==",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@babel/types": "^7.20.7",
- "@lingui/conf": "4.11.3",
- "@lingui/core": "4.11.3",
- "@lingui/message-utils": "4.11.3"
+ "@lingui/conf": "4.11.4",
+ "@lingui/core": "4.11.4",
+ "@lingui/message-utils": "4.11.4"
},
"engines": {
"node": ">=16.0.0"
@@ -4204,9 +4205,9 @@
}
},
"node_modules/@lingui/message-utils": {
- "version": "4.11.3",
- "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-4.11.3.tgz",
- "integrity": "sha512-ZSw3OoKbknOw3nSrqt194g2F8r0guKow9csb46zlL7zX/yOWCaj767wvSvMoglZtVvurfQs4NPv2cohYlWORNw==",
+ "version": "4.11.4",
+ "resolved": "https://registry.npmjs.org/@lingui/message-utils/-/message-utils-4.11.4.tgz",
+ "integrity": "sha512-ZTCDhGbj5EN+P9Ajcj0Gq9uDP3HZTRW6/kT09WkiFgL4NayYLksPvgBk29sIglsS6M+Y6Iw2BrUK403SZjZKgw==",
"dependencies": {
"@messageformat/parser": "^5.0.0",
"js-sha256": "^0.10.1"
@@ -4216,12 +4217,12 @@
}
},
"node_modules/@lingui/react": {
- "version": "4.11.3",
- "resolved": "https://registry.npmjs.org/@lingui/react/-/react-4.11.3.tgz",
- "integrity": "sha512-FuorwDsz5zDpUNpyj7J8ZKqJrrVxOz1EtQ3aJGJsmnTtVO01N3nR3ckMzpYvZ71XXdDEvhUC9ihmiKbFvpaZ/w==",
+ "version": "4.11.4",
+ "resolved": "https://registry.npmjs.org/@lingui/react/-/react-4.11.4.tgz",
+ "integrity": "sha512-f7re4HhjI6CLBV1CY/PcI3VYP5zS4rtfU33speWnfkymsxGIXQv4ol3BqrgPLGhypMl2nKcL5nfL+LewrLIW8g==",
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@lingui/core": "4.11.3"
+ "@lingui/core": "4.11.4"
},
"engines": {
"node": ">=16.0.0"
@@ -4231,13 +4232,13 @@
}
},
"node_modules/@lingui/vite-plugin": {
- "version": "4.11.3",
- "resolved": "https://registry.npmjs.org/@lingui/vite-plugin/-/vite-plugin-4.11.3.tgz",
- "integrity": "sha512-CNPtcXN/pdM8jXKLZFwazCczK7DagwcLvYL8WRt6m0wxpaMcR2s15/Sp/S6gL0PN8OXHykSzcg9nBMgXfgMaHw==",
+ "version": "4.11.4",
+ "resolved": "https://registry.npmjs.org/@lingui/vite-plugin/-/vite-plugin-4.11.4.tgz",
+ "integrity": "sha512-491PbqPyeb3E5+vDyOlMJsNZZIvhkH7A5F8RgaBk3lKSU5w7k1aI5nTqhOcYpZNEzT3tIWNZCUQ3u5znoP3cEQ==",
"dev": true,
"dependencies": {
- "@lingui/cli": "4.11.3",
- "@lingui/conf": "4.11.3"
+ "@lingui/cli": "4.11.4",
+ "@lingui/conf": "4.11.4"
},
"engines": {
"node": ">=16.0.0"
@@ -5869,14 +5870,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/derive-valtio": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/derive-valtio/-/derive-valtio-0.1.0.tgz",
- "integrity": "sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==",
- "peerDependencies": {
- "valtio": "*"
- }
- },
"node_modules/dom-input-range": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/dom-input-range/-/dom-input-range-1.1.6.tgz",
@@ -7472,6 +7465,14 @@
"jiti": "bin/jiti.js"
}
},
+ "node_modules/js-cookie": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
+ "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/js-sha256": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.10.1.tgz",
@@ -8364,9 +8365,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.41",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz",
- "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==",
+ "version": "8.4.45",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz",
+ "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==",
"dev": true,
"funding": [
{
@@ -8433,9 +8434,9 @@
}
},
"node_modules/postcss-color-functional-notation": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.1.tgz",
- "integrity": "sha512-8/wf01pTH3XHT37wre+E2GNcsttZ62PWSJ0DE66GO+Uzk+uyr9DH+V3cdJG+BqezCD/T5lBC1s5/t7Y12ps8QQ==",
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.2.tgz",
+ "integrity": "sha512-c2WkR0MS73s+P5SgY1KBaSEE61Rj+miW095rkWDnMQxbTCQkp6y/jft8U0QMxEsI4k1Pd4PdV+TP9/1zIDR6XQ==",
"dev": true,
"funding": [
{
@@ -8448,9 +8449,9 @@
}
],
"dependencies": {
- "@csstools/css-color-parser": "^3.0.1",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/css-color-parser": "^3.0.2",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
"@csstools/postcss-progressive-custom-properties": "^4.0.0",
"@csstools/utilities": "^2.0.0"
},
@@ -8514,9 +8515,9 @@
}
},
"node_modules/postcss-custom-media": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.0.tgz",
- "integrity": "sha512-tZ4qTYSOqH7YFi8psEQB2v2zBRbbJex9FgPef2Qss8DlWxnYpBNHquvMmVBR8uIt6hW0+prDsg7UJDp6XLIf8w==",
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.1.tgz",
+ "integrity": "sha512-vfBliYVgEEJUFXCRPQ7jYt1wlD322u+/5GT0tZqMVYFInkpDHfjhU3nk2quTRW4uFc/umOOqLlxvrEOZRvloMw==",
"dev": true,
"funding": [
{
@@ -8529,10 +8530,10 @@
}
],
"dependencies": {
- "@csstools/cascade-layer-name-parser": "^2.0.0",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
- "@csstools/media-query-list-parser": "^3.0.0"
+ "@csstools/cascade-layer-name-parser": "^2.0.1",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
+ "@csstools/media-query-list-parser": "^3.0.1"
},
"engines": {
"node": ">=18"
@@ -8542,9 +8543,9 @@
}
},
"node_modules/postcss-custom-properties": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.0.tgz",
- "integrity": "sha512-GD/suWYQAplXJujsyOswYP+oX9xs29eBNwGloPj4Ub+3/Rq1Set+ZeGmHJfN2Y2+x9vUxAX4eeNJFmtk6VBv4A==",
+ "version": "14.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.1.tgz",
+ "integrity": "sha512-SB4GjuZjIq5GQFNbxFrirQPbkdbJooyNy8bh+fcJ8ZG0oasJTflTTtR4geb56h+FBVDIb9Hx4v/NiG2caOj8nQ==",
"dev": true,
"funding": [
{
@@ -8557,9 +8558,9 @@
}
],
"dependencies": {
- "@csstools/cascade-layer-name-parser": "^2.0.0",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/cascade-layer-name-parser": "^2.0.1",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
"@csstools/utilities": "^2.0.0",
"postcss-value-parser": "^4.2.0"
},
@@ -8571,9 +8572,9 @@
}
},
"node_modules/postcss-custom-selectors": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.0.tgz",
- "integrity": "sha512-nW6RWjH+jaWvXEgm/AzMhtVjMXcKmrTWsM/eJn/ujnJI5uEOPTxvl3eCFFCFKC2DiZcOP5HLH5EeX0DIemFzBQ==",
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.1.tgz",
+ "integrity": "sha512-2McIpyhAeKhUzVqrP4ZyMBpK5FuD+Y9tpQwhcof49652s7gez8057cSaOg/epYcKlztSYxb0GHfi7W5h3JoGUg==",
"dev": true,
"funding": [
{
@@ -8586,9 +8587,9 @@
}
],
"dependencies": {
- "@csstools/cascade-layer-name-parser": "^2.0.0",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/cascade-layer-name-parser": "^2.0.1",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
"postcss-selector-parser": "^6.1.0"
},
"engines": {
@@ -8784,9 +8785,9 @@
}
},
"node_modules/postcss-lab-function": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.1.tgz",
- "integrity": "sha512-G9ecsdU+TtdHJvaTrfbIyOa3iHHJMZXdvsLXJSN8IP4cfg3XraozacAt6P7xzaILIC3XPGMM149oKhf2tjPubg==",
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.2.tgz",
+ "integrity": "sha512-h4ARGLIBtC1PmCHsLgTWWj8j1i1CXoaht4A5RlITDX2z9AeFBak0YlY6sdF4oJGljrep+Dg2SSccIj4QnFbRDg==",
"dev": true,
"funding": [
{
@@ -8799,9 +8800,9 @@
}
],
"dependencies": {
- "@csstools/css-color-parser": "^3.0.1",
- "@csstools/css-parser-algorithms": "^3.0.0",
- "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/css-color-parser": "^3.0.2",
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1",
"@csstools/postcss-progressive-custom-properties": "^4.0.0",
"@csstools/utilities": "^2.0.0"
},
@@ -8948,9 +8949,9 @@
}
},
"node_modules/postcss-preset-env": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.0.1.tgz",
- "integrity": "sha512-I8m4pBJWcUmPLu1z8HySEiAROKIMA0nwUYCGr1gJOGFP9BdwPWPBSYvA2KGH0VjUZ8AAAvwwXXWnxHTWq3f7UQ==",
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.0.2.tgz",
+ "integrity": "sha512-PMxqnz0RQYMUmUi6p4P7BhC9EVGyEUCIdwn4vJ7Fy1jvc2QP4mMH75BSBB1mBFqjl3x4xYwyCNMhGZ8y0+/qOA==",
"dev": true,
"funding": [
{
@@ -8964,34 +8965,34 @@
],
"dependencies": {
"@csstools/postcss-cascade-layers": "^5.0.0",
- "@csstools/postcss-color-function": "^4.0.1",
- "@csstools/postcss-color-mix-function": "^3.0.1",
- "@csstools/postcss-content-alt-text": "^2.0.0",
- "@csstools/postcss-exponential-functions": "^2.0.0",
+ "@csstools/postcss-color-function": "^4.0.2",
+ "@csstools/postcss-color-mix-function": "^3.0.2",
+ "@csstools/postcss-content-alt-text": "^2.0.1",
+ "@csstools/postcss-exponential-functions": "^2.0.1",
"@csstools/postcss-font-format-keywords": "^4.0.0",
- "@csstools/postcss-gamut-mapping": "^2.0.1",
- "@csstools/postcss-gradients-interpolation-method": "^5.0.1",
- "@csstools/postcss-hwb-function": "^4.0.1",
+ "@csstools/postcss-gamut-mapping": "^2.0.2",
+ "@csstools/postcss-gradients-interpolation-method": "^5.0.2",
+ "@csstools/postcss-hwb-function": "^4.0.2",
"@csstools/postcss-ic-unit": "^4.0.0",
"@csstools/postcss-initial": "^2.0.0",
"@csstools/postcss-is-pseudo-class": "^5.0.0",
- "@csstools/postcss-light-dark-function": "^2.0.1",
+ "@csstools/postcss-light-dark-function": "^2.0.2",
"@csstools/postcss-logical-float-and-clear": "^3.0.0",
"@csstools/postcss-logical-overflow": "^2.0.0",
"@csstools/postcss-logical-overscroll-behavior": "^2.0.0",
"@csstools/postcss-logical-resize": "^3.0.0",
- "@csstools/postcss-logical-viewport-units": "^3.0.0",
- "@csstools/postcss-media-minmax": "^2.0.0",
- "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.0",
+ "@csstools/postcss-logical-viewport-units": "^3.0.1",
+ "@csstools/postcss-media-minmax": "^2.0.1",
+ "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.1",
"@csstools/postcss-nested-calc": "^4.0.0",
"@csstools/postcss-normalize-display-values": "^4.0.0",
- "@csstools/postcss-oklab-function": "^4.0.1",
+ "@csstools/postcss-oklab-function": "^4.0.2",
"@csstools/postcss-progressive-custom-properties": "^4.0.0",
- "@csstools/postcss-relative-color-syntax": "^3.0.1",
+ "@csstools/postcss-relative-color-syntax": "^3.0.2",
"@csstools/postcss-scope-pseudo-class": "^4.0.0",
- "@csstools/postcss-stepped-value-functions": "^4.0.0",
+ "@csstools/postcss-stepped-value-functions": "^4.0.1",
"@csstools/postcss-text-decoration-shorthand": "^4.0.1",
- "@csstools/postcss-trigonometric-functions": "^4.0.0",
+ "@csstools/postcss-trigonometric-functions": "^4.0.1",
"@csstools/postcss-unset-value": "^4.0.0",
"autoprefixer": "^10.4.19",
"browserslist": "^4.23.1",
@@ -9001,12 +9002,12 @@
"cssdb": "^8.1.0",
"postcss-attribute-case-insensitive": "^7.0.0",
"postcss-clamp": "^4.1.0",
- "postcss-color-functional-notation": "^7.0.1",
+ "postcss-color-functional-notation": "^7.0.2",
"postcss-color-hex-alpha": "^10.0.0",
"postcss-color-rebeccapurple": "^10.0.0",
- "postcss-custom-media": "^11.0.0",
- "postcss-custom-properties": "^14.0.0",
- "postcss-custom-selectors": "^8.0.0",
+ "postcss-custom-media": "^11.0.1",
+ "postcss-custom-properties": "^14.0.1",
+ "postcss-custom-selectors": "^8.0.1",
"postcss-dir-pseudo-class": "^9.0.0",
"postcss-double-position-gradients": "^6.0.0",
"postcss-focus-visible": "^10.0.0",
@@ -9014,7 +9015,7 @@
"postcss-font-variant": "^5.0.0",
"postcss-gap-properties": "^6.0.0",
"postcss-image-set-function": "^7.0.0",
- "postcss-lab-function": "^7.0.1",
+ "postcss-lab-function": "^7.0.2",
"postcss-logical": "^8.0.0",
"postcss-nesting": "^13.0.0",
"postcss-opacity-percentage": "^2.0.0",
@@ -9192,9 +9193,9 @@
}
},
"node_modules/proxy-compare": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.6.0.tgz",
- "integrity": "sha512-8xuCeM3l8yqdmbPoYeLbrAXCBWu19XEYc5/F28f5qOaoAIMyfmBUkl5axiK+x9olUvRlcekvnm98AP9RDngOIw=="
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-3.0.0.tgz",
+ "integrity": "sha512-y44MCkgtZUCT9tZGuE278fB7PWVf7fRYy0vbRXAts2o5F0EfC4fIQrvQQGBJo1WJbFcVLXzApOscyJuZqHQc1w=="
},
"node_modules/pseudolocale": {
"version": "2.1.0",
@@ -9271,9 +9272,9 @@
}
},
"node_modules/react-hotkeys-hook": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.5.0.tgz",
- "integrity": "sha512-Samb85GSgAWFQNvVt3PS90LPPGSf9mkH/r4au81ZP1yOIFayLC3QAvqTgGtJ8YEDMXtPmaVBs6NgipHO6h4Mug==",
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/react-hotkeys-hook/-/react-hotkeys-hook-4.5.1.tgz",
+ "integrity": "sha512-scAEJOh3Irm0g95NIn6+tQVf/OICCjsQsC9NBHfQws/Vxw4sfq1tDQut5fhTEvPraXhu/sHxRd9lOtxzyYuNAg==",
"peerDependencies": {
"react": ">=16.8.1",
"react-dom": ">=16.8.1"
@@ -10477,14 +10478,14 @@
}
},
"node_modules/use-debounce": {
- "version": "10.0.2",
- "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.2.tgz",
- "integrity": "sha512-MwBiJK2dk+2qhMDVDCPRPeLuIekKfH2t1UYMnrW9pwcJJGFDbTLliSMBz2UKGmE1PJs8l3XoMqbIU1MemMAJ8g==",
+ "version": "10.0.3",
+ "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.3.tgz",
+ "integrity": "sha512-DxQSI9ZKso689WM1mjgGU3ozcxU1TJElBJ3X6S4SMzMNcm2lVH0AHmyXB+K7ewjz2BSUKJTDqTcwtSMRfB89dg==",
"engines": {
"node": ">= 16.0.0"
},
"peerDependencies": {
- "react": ">=16.8.0"
+ "react": "*"
}
},
"node_modules/use-long-press": {
@@ -10509,15 +10510,6 @@
"react-dom": "16.8.0 - 18"
}
},
- "node_modules/use-sync-external-store": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
- "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
- "license": "MIT",
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- }
- },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -10525,20 +10517,18 @@
"dev": true
},
"node_modules/valtio": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.13.2.tgz",
- "integrity": "sha512-Qik0o+DSy741TmkqmRfjq+0xpZBXi/Y6+fXZLn0xNF1z/waFMbE3rkivv5Zcf9RrMUp6zswf2J7sbh2KBlba5A==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/valtio/-/valtio-2.0.0.tgz",
+ "integrity": "sha512-SzUU5UUK/vBRfHWXihwkJE55YNj8zhOkzxPOexcz0xIIT6Oux5VLynCmzyME2bYuEWcktW2NTaaLbpUydEsHiw==",
"dependencies": {
- "derive-valtio": "0.1.0",
- "proxy-compare": "2.6.0",
- "use-sync-external-store": "1.2.0"
+ "proxy-compare": "^3.0.0"
},
"engines": {
"node": ">=12.20.0"
},
"peerDependencies": {
- "@types/react": ">=16.8",
- "react": ">=16.8"
+ "@types/react": ">=18.0.0",
+ "react": ">=18.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
@@ -10550,14 +10540,14 @@
}
},
"node_modules/vite": {
- "version": "5.4.1",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.1.tgz",
- "integrity": "sha512-1oE6yuNXssjrZdblI9AfBbHCC41nnyoVoEZxQnID6yvQZAFBzxxkqoFLtHUMkYunL8hwOLEjgTuxpkRxvba3kA==",
+ "version": "5.4.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz",
+ "integrity": "sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==",
"dev": true,
"dependencies": {
"esbuild": "^0.21.3",
- "postcss": "^8.4.41",
- "rollup": "^4.13.0"
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
@@ -10621,22 +10611,21 @@
}
},
"node_modules/vite-plugin-html-config": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/vite-plugin-html-config/-/vite-plugin-html-config-1.0.11.tgz",
- "integrity": "sha512-hUybhgI+/LQQ5q6xoMMsTvI4PBuQD/Wv6Z1vtDPVWjanS8weCIexXuLLYNGD/93f0v8W2hpNfXpmxgpZMahJ0g==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/vite-plugin-html-config/-/vite-plugin-html-config-2.0.2.tgz",
+ "integrity": "sha512-g09u0XsmgKyMUIp1RZSyNSkJWvIusaXxw3KylyxU3vkCq7/G8hyemLctT+4IvO42fCPlNySmrNC9g0qSoKmvpw==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
- "vite": ">=2.0.0"
+ "vite": ">=5.0.0"
}
},
"node_modules/vite-plugin-pwa": {
- "version": "0.20.1",
- "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.20.1.tgz",
- "integrity": "sha512-M6Pk4b18i5ryrhKgiIF8Zud0HGphYiCbEfLsCdlvmwn/CEnS6noVwfIDG/+3V7r6yxpPV/gLiKw+rIlCCiCCoQ==",
+ "version": "0.20.3",
+ "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.20.3.tgz",
+ "integrity": "sha512-aqCOWWSwfX4o6H+6NyEvhzFs3eENBqYFKUK4FYx5OZ3jGio73BE189bPz9+BBgjHBLozldQVSmZTHySVC2zNkg==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
diff --git a/package.json b/package.json
index 1fc4f75b..0160af88 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,9 @@
"bundle-visualizer": "npx vite-bundle-visualizer",
"messages:extract": "lingui extract",
"messages:extract:clean": "lingui extract --locale en --clean",
- "messages:compile": "lingui compile"
+ "messages:compile": "lingui compile",
+ "fetch-i18n-volunteers": "env $(cat .env.local | grep -v \"#\" | xargs) node scripts/fetch-i18n-volunteers.js",
+ "readme:i18n-volunteers": "node scripts/update-i18n-volunteers-readme.js"
},
"dependencies": {
"@formatjs/intl-localematcher": "~0.5.4",
@@ -20,9 +22,9 @@
"@github/text-expander-element": "~2.7.1",
"@iconify-icons/mingcute": "~1.2.9",
"@justinribeiro/lite-youtube": "~1.5.0",
- "@lingui/detect-locale": "~4.11.3",
- "@lingui/macro": "~4.11.3",
- "@lingui/react": "~4.11.3",
+ "@lingui/detect-locale": "~4.11.4",
+ "@lingui/macro": "~4.11.4",
+ "@lingui/react": "~4.11.4",
"@szhsin/react-menu": "~4.2.2",
"compare-versions": "~6.1.1",
"fast-blurhash": "~1.1.4",
@@ -31,6 +33,7 @@
"html-prettify": "~1.0.7",
"idb-keyval": "~6.2.1",
"intl-locale-textinfo-polyfill": "~2.1.1",
+ "js-cookie": "~3.0.5",
"just-debounce-it": "~3.2.0",
"lz-string": "~1.5.0",
"masto": "~6.8.0",
@@ -39,7 +42,7 @@
"p-throttle": "~6.2.0",
"preact": "~10.23.2",
"punycode": "~2.3.1",
- "react-hotkeys-hook": "~4.5.0",
+ "react-hotkeys-hook": "~4.5.1",
"react-intersection-observer": "~9.13.0",
"react-quick-pinch-zoom": "~5.1.0",
"react-router-dom": "6.6.2",
@@ -48,25 +51,25 @@
"tinyld": "~1.3.4",
"toastify-js": "~1.12.0",
"uid": "~2.0.2",
- "use-debounce": "~10.0.2",
+ "use-debounce": "~10.0.3",
"use-long-press": "~3.2.0",
"use-resize-observer": "~9.1.0",
- "valtio": "1.13.2"
+ "valtio": "2.0.0"
},
"devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "~4.3.1",
- "@lingui/cli": "~4.11.3",
- "@lingui/vite-plugin": "~4.11.3",
+ "@lingui/cli": "~4.11.4",
+ "@lingui/vite-plugin": "~4.11.4",
"@preact/preset-vite": "~2.9.0",
"babel-plugin-macros": "~3.1.0",
- "postcss": "~8.4.41",
+ "postcss": "~8.4.45",
"postcss-dark-theme-class": "~1.3.0",
- "postcss-preset-env": "~10.0.1",
+ "postcss-preset-env": "~10.0.2",
"twitter-text": "~3.1.0",
- "vite": "~5.4.1",
+ "vite": "~5.4.3",
"vite-plugin-generate-file": "~0.2.0",
- "vite-plugin-html-config": "~1.0.11",
- "vite-plugin-pwa": "~0.20.1",
+ "vite-plugin-html-config": "~2.0.2",
+ "vite-plugin-pwa": "~0.20.3",
"vite-plugin-remove-console": "~2.2.0",
"vite-plugin-run": "~0.5.2",
"workbox-cacheable-response": "~7.1.0",
diff --git a/scripts/catalogs.js b/scripts/catalogs.js
index 39e19450..debb0d43 100644
--- a/scripts/catalogs.js
+++ b/scripts/catalogs.js
@@ -73,18 +73,21 @@ function IDN(inputCode, outputCode) {
return result;
}
-// Sort by percentage
-const sortedCatalogs = Object.entries(catalogs)
- .sort((a, b) => b[1] - a[1])
+const fullCatalogs = Object.entries(catalogs)
+ // sort by key
+ .sort((a, b) => a[0].localeCompare(b[0]))
.map(([code, completion]) => {
const nativeName = IDN(code, code);
const name = IDN('en', code);
- // let names = {};
return { code, nativeName, name, completion };
});
+// Sort by completion
+const sortedCatalogs = [...fullCatalogs].sort(
+ (a, b) => b.completion - a.completion,
+);
console.table(sortedCatalogs);
const path = 'src/data/catalogs.json';
-fs.writeFileSync(path, JSON.stringify(sortedCatalogs, null, 2));
+fs.writeFileSync(path, JSON.stringify(fullCatalogs, null, 2));
console.log('File written:', path);
diff --git a/scripts/fetch-i18n-volunteers.js b/scripts/fetch-i18n-volunteers.js
new file mode 100644
index 00000000..b0e4132e
--- /dev/null
+++ b/scripts/fetch-i18n-volunteers.js
@@ -0,0 +1,131 @@
+import fs from 'fs';
+
+const { CROWDIN_ACCESS_TOKEN } = process.env;
+
+const PROJECT_ID = '703337';
+
+if (!CROWDIN_ACCESS_TOKEN) {
+ throw new Error('CROWDIN_ACCESS_TOKEN is not set');
+}
+
+// Generate Report
+
+let REPORT_ID = null;
+{
+ const response = await fetch(
+ `https://api.crowdin.com/api/v2/projects/${PROJECT_ID}/reports`,
+ {
+ headers: {
+ Authorization: `Bearer ${CROWDIN_ACCESS_TOKEN}`,
+ 'Content-Type': 'application/json',
+ },
+ method: 'POST',
+ body: JSON.stringify({
+ name: 'top-members',
+ schema: {
+ format: 'json',
+ },
+ }),
+ },
+ );
+ const json = await response.json();
+ console.log(`Report ID: ${json?.data?.identifier}`);
+ REPORT_ID = json?.data?.identifier;
+}
+
+if (!REPORT_ID) {
+ throw new Error('Report ID is not found');
+}
+
+// Check Report Generation Status
+let finished = false;
+{
+ let maxPolls = 10;
+ do {
+ maxPolls--;
+ if (maxPolls < 0) break;
+
+ // Wait for 1 second
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+
+ const status = await fetch(
+ `https://api.crowdin.com/api/v2/projects/${PROJECT_ID}/reports/${REPORT_ID}`,
+ {
+ headers: {
+ Authorization: `Bearer ${CROWDIN_ACCESS_TOKEN}`,
+ 'Content-Type': 'application/json',
+ },
+ },
+ );
+ const json = await status.json();
+ const progress = json?.data?.progress;
+ console.log(`Progress: ${progress}% (${maxPolls} retries left)`);
+ finished = json?.data?.status === 'finished';
+ } while (!finished);
+}
+
+if (!finished) {
+ throw new Error('Failed to generate report');
+}
+
+// Download Report
+let reportURL = null;
+{
+ const response = await fetch(
+ `https://api.crowdin.com/api/v2/projects/${PROJECT_ID}/reports/${REPORT_ID}/download`,
+ {
+ headers: {
+ Authorization: `Bearer ${CROWDIN_ACCESS_TOKEN}`,
+ 'Content-Type': 'application/json',
+ },
+ },
+ );
+ const json = await response.json();
+ reportURL = json?.data?.url;
+ console.log(`Report URL: ${reportURL}`);
+}
+
+if (!reportURL) {
+ throw new Error('Report URL is not found');
+}
+
+// Actually download the report
+let members = null;
+{
+ const response = await fetch(reportURL);
+ const json = await response.json();
+
+ const { data } = json;
+
+ if (!data?.length) {
+ throw new Error('No data found');
+ }
+
+ // Sort by 'user.fullName'
+ data.sort((a, b) => a.user.username.localeCompare(b.user.username));
+ members = data
+ .filter((item) => {
+ const isMyself = item.user.username === 'cheeaun';
+ const translatedMoreThanZero = item.translated > 0;
+
+ return !isMyself && translatedMoreThanZero;
+ })
+ .map((item) => ({
+ avatarUrl: item.user.avatarUrl,
+ username: item.user.username,
+ languages: item.languages.map((lang) => lang.name),
+ }));
+
+ console.log(members);
+
+ if (members?.length) {
+ fs.writeFileSync(
+ 'i18n-volunteers.json',
+ JSON.stringify(members, null, '\t'),
+ );
+ }
+}
+
+if (!members?.length) {
+ throw new Error('No members found');
+}
diff --git a/scripts/update-i18n-volunteers-readme.js b/scripts/update-i18n-volunteers-readme.js
new file mode 100644
index 00000000..571c8677
--- /dev/null
+++ b/scripts/update-i18n-volunteers-readme.js
@@ -0,0 +1,27 @@
+// Find for and inject list of i18n volunteers in between
+
+import fs from 'fs';
+
+const i18nVolunteers = JSON.parse(fs.readFileSync('i18n-volunteers.json'));
+
+const readme = fs.readFileSync('README.md', 'utf8');
+
+const i18nVolunteersStart = '';
+const i18nVolunteersEnd = '';
+
+const i18nVolunteersList = i18nVolunteers
+ .map((member) => {
+ return `-
${
+ member.username
+ } (${member.languages.join(', ')})`;
+ })
+ .join('\n');
+
+const readmeUpdated = readme.replace(
+ new RegExp(`${i18nVolunteersStart}.*${i18nVolunteersEnd}`, 's'),
+ `${i18nVolunteersStart}\n${i18nVolunteersList}\n${i18nVolunteersEnd}`,
+);
+
+fs.writeFileSync('README.md', readmeUpdated);
+
+console.log('Updated README.md');
diff --git a/src/app.css b/src/app.css
index 857d7fbe..7d13d40e 100644
--- a/src/app.css
+++ b/src/app.css
@@ -1810,16 +1810,17 @@ body > .szh-menu-container {
env(safe-area-inset-bottom) env(safe-area-inset-left);
}
.szh-menu {
- padding: 8px 0;
+ padding: 4px 0;
margin: 0;
font-size: var(--text-size);
background-color: var(--bg-color);
- border: 1px solid var(--outline-color);
+ border: 1px solid var(--outline-stronger-color);
border-radius: 8px;
- box-shadow: 0 3px 16px -3px var(--drop-shadow-color);
+ box-shadow: 0 3px 8px var(--drop-shadow-color),
+ 0 6px 32px -6px var(--drop-shadow-color);
text-align: start;
/* animation: appear-smooth 0.15s ease-in-out; */
- width: 16em;
+ min-width: 16em;
max-width: 90vw;
/* overflow: hidden; */
}
@@ -1874,13 +1875,16 @@ body > .szh-menu-container {
display: flex;
gap: 8px;
align-items: center;
- line-height: 1.1;
+ line-height: 1.3;
padding: 8px 16px !important;
/* transition: all 0.1s ease-in-out; */
text-decoration: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+
+ --menu-item-bg-inset: 0 4px;
+ --menu-item-bg-color: var(--button-bg-color);
}
.szh-menu .szh-menu__item--focusable {
background-color: transparent;
@@ -1917,9 +1921,30 @@ body > .szh-menu-container {
.szh-menu__item:not(.szh-menu__item--disabled, .szh-menu__item--hover) {
color: var(--text-color);
}
+.szh-menu .szh-menu__item:not(.menu-field) {
+ position: relative;
+ & > * {
+ /* z-index: 1; */
+ }
+
+ &:before {
+ content: '';
+ background-color: var(--menu-item-bg-color);
+ position: absolute;
+ inset: var(--menu-item-bg-inset);
+ border-radius: 4px;
+ z-index: -1;
+ opacity: 0;
+ }
+}
.szh-menu .szh-menu__item--hover:not(.menu-field) {
color: var(--button-text-color);
- background-color: var(--button-bg-color);
+ /* background-color: var(--button-bg-color); */
+ background-color: transparent;
+
+ &:before {
+ opacity: 1;
+ }
}
.szh-menu__divider {
background-color: var(--divider-color);
@@ -1995,10 +2020,12 @@ body > .szh-menu-container {
}
.szh-menu
.szh-menu__item.danger:not(.szh-menu__item--disabled).szh-menu__item--hover {
- background-color: var(--red-text-color);
+ /* background-color: var(--red-text-color); */
+ --menu-item-bg-color: var(--red-text-color);
@media (prefers-color-scheme: dark) {
- background-color: var(--red-color);
+ /* background-color: var(--red-color); */
+ --menu-item-bg-color: var(--red-color);
}
}
.szh-menu
@@ -2038,12 +2065,20 @@ body > .szh-menu-container {
);
}
}
+
+ &:before {
+ content: '';
+ }
+ }
+
+ .szh-menu__item--hover {
+ background-color: var(--menu-item-bg-color);
}
}
.menu-control-group-horizontal:first-child,
li[role='none'] + .menu-control-group-horizontal {
- margin-top: -8px;
+ margin-top: -4px;
margin-bottom: -4px;
.szh-menu__item {
@@ -2078,6 +2113,8 @@ body > .szh-menu-container {
}
.szh-menu .menu-wrap {
+ min-width: 16em;
+ width: min-content;
display: flex;
flex-wrap: wrap;
}
@@ -2092,11 +2129,10 @@ body > .szh-menu-container {
background-color: var(--bg-blur-color);
backdrop-filter: blur(8px) saturate(3);
border: var(--hairline-width) solid var(--bg-color);
- box-shadow: 0 3px 8px -1px var(--drop-shadow-color);
text-shadow: 0 var(--hairline-width) var(--bg-color), 0 0 8px var(--bg-color);
}
.glass-menu .szh-menu__item--hover {
- background-color: var(--button-bg-blur-color);
+ /* background-color: var(--button-bg-blur-color); */
text-shadow: none;
}
diff --git a/src/app.jsx b/src/app.jsx
index d3f1a45e..4db0a8cb 100644
--- a/src/app.jsx
+++ b/src/app.jsx
@@ -56,7 +56,11 @@ import { getAccessToken } from './utils/auth';
import focusDeck from './utils/focus-deck';
import states, { initStates, statusKey } from './utils/states';
import store from './utils/store';
-import { getCurrentAccount, setCurrentAccountID } from './utils/store-utils';
+import {
+ getAccount,
+ getCurrentAccount,
+ setCurrentAccountID,
+} from './utils/store-utils';
import './utils/toast-alert';
@@ -317,9 +321,10 @@ function App() {
window.location.pathname || '/',
);
- const clientID = store.session.get('clientID');
- const clientSecret = store.session.get('clientSecret');
- const vapidKey = store.session.get('vapidKey');
+ const clientID = store.sessionCookie.get('clientID');
+ const clientSecret = store.sessionCookie.get('clientSecret');
+ const vapidKey = store.sessionCookie.get('vapidKey');
+ const verifier = store.sessionCookie.get('codeVerifier');
(async () => {
setUIState('loading');
@@ -328,22 +333,46 @@ function App() {
client_id: clientID,
client_secret: clientSecret,
code,
+ code_verifier: verifier || undefined,
});
- const client = initClient({ instance: instanceURL, accessToken });
- await Promise.allSettled([
- initPreferences(client),
- initInstance(client, instanceURL),
- initAccount(client, instanceURL, accessToken, vapidKey),
- ]);
- initStates();
+ if (accessToken) {
+ const client = initClient({ instance: instanceURL, accessToken });
+ await Promise.allSettled([
+ initPreferences(client),
+ initInstance(client, instanceURL),
+ initAccount(client, instanceURL, accessToken, vapidKey),
+ ]);
+ initStates();
+ window.__IGNORE_GET_ACCOUNT_ERROR__ = true;
- setIsLoggedIn(true);
- setUIState('default');
+ setIsLoggedIn(true);
+ setUIState('default');
+ } else {
+ setUIState('error');
+ }
})();
} else {
window.__IGNORE_GET_ACCOUNT_ERROR__ = true;
- const account = getCurrentAccount();
+ const searchAccount = decodeURIComponent(
+ (window.location.search.match(/account=([^&]+)/) || [, ''])[1],
+ );
+ let account;
+ if (searchAccount) {
+ account = getAccount(searchAccount);
+ console.log('searchAccount', searchAccount, account);
+ if (account) {
+ setCurrentAccountID(account.info.id);
+ window.history.replaceState(
+ {},
+ document.title,
+ window.location.pathname || '/',
+ );
+ }
+ }
+ if (!account) {
+ account = getCurrentAccount();
+ }
if (account) {
setCurrentAccountID(account.info.id);
const { client } = api({ account });
@@ -365,6 +394,11 @@ function App() {
setUIState('default');
}
}
+
+ // Cleanup
+ store.sessionCookie.del('clientID');
+ store.sessionCookie.del('clientSecret');
+ store.sessionCookie.del('codeVerifier');
}, []);
let location = useLocation();
diff --git a/src/components/account-info.jsx b/src/components/account-info.jsx
index 4b0c58ce..c036190b 100644
--- a/src/components/account-info.jsx
+++ b/src/components/account-info.jsx
@@ -63,7 +63,6 @@ const MUTE_DURATIONS_LABELS = {
259_200: i18nDuration(3, 'day'),
604_800: i18nDuration(1, 'week'),
};
-console.log({ MUTE_DURATIONS_LABELS });
const LIMIT = 80;
diff --git a/src/components/background-service.jsx b/src/components/background-service.jsx
index 5b9a7ef1..64144832 100644
--- a/src/components/background-service.jsx
+++ b/src/components/background-service.jsx
@@ -16,7 +16,18 @@ export default memo(function BackgroundService({ isLoggedIn }) {
// Notifications service
// - WebSocket to receive notifications when page is visible
const [visible, setVisible] = useState(true);
- usePageVisibility(setVisible);
+ const visibleTimeout = useRef();
+ usePageVisibility((visible) => {
+ clearTimeout(visibleTimeout.current);
+ if (visible) {
+ setVisible(true);
+ } else {
+ visibleTimeout.current = setTimeout(() => {
+ setVisible(false);
+ }, POLL_INTERVAL);
+ }
+ });
+
const checkLatestNotification = async (masto, instance, skipCheckMarkers) => {
if (states.notificationsLast) {
const notificationsIterator = masto.v1.notifications.list({
diff --git a/src/components/compose.jsx b/src/components/compose.jsx
index 7a7fb6de..04c9747c 100644
--- a/src/components/compose.jsx
+++ b/src/components/compose.jsx
@@ -1154,7 +1154,7 @@ function Compose({
class={`toolbar-button ${
visibility !== 'public' && !sensitive ? 'show-field' : ''
} ${visibility !== 'public' ? 'highlight' : ''}`}
- title={`Visibility: ${visibility}`}
+ title={visibility}
>
);
diff --git a/src/components/nav-menu.css b/src/components/nav-menu.css
index 5432e5dd..5a69c777 100644
--- a/src/components/nav-menu.css
+++ b/src/components/nav-menu.css
@@ -1,7 +1,18 @@
-.nav-menu section:last-child {
- background-color: var(--bg-faded-color);
- margin-bottom: -8px;
- padding-bottom: 8px;
+.nav-menu {
+ overflow: hidden;
+
+ section:last-child {
+ background-color: var(--bg-faded-color);
+ margin-bottom: -4px;
+ padding-bottom: 4px;
+
+ .szh-menu__item:before {
+ z-index: 0;
+ }
+ .szh-menu__item > * {
+ z-index: 1;
+ }
+ }
}
@media (min-width: 23em) {
@@ -13,16 +24,16 @@
'top top'
'left right';
padding: 0;
- width: 22em;
+ /* min-width: 22em; */
max-width: calc(100vw - 16px);
}
.nav-menu .top-menu {
grid-area: top;
- padding-top: 8px;
- margin-bottom: -8px;
+ padding-top: 4px;
+ margin-bottom: -4px;
}
.nav-menu section {
- padding: 8px 0;
+ padding: 4px 0;
/* width: 50%; */
}
@keyframes phanpying {
diff --git a/src/components/poll.jsx b/src/components/poll.jsx
index 713b92ce..7a8cfe7b 100644
--- a/src/components/poll.jsx
+++ b/src/components/poll.jsx
@@ -1,4 +1,4 @@
-import { Plural, t, Trans } from '@lingui/macro';
+import { Plural, plural, t, Trans } from '@lingui/macro';
import { useState } from 'preact/hooks';
import shortenNumber from '../utils/shorten-number';
@@ -113,9 +113,10 @@ export default function Poll({
{percentage}
diff --git a/src/components/relative-time.jsx b/src/components/relative-time.jsx
index af3b669c..9308c643 100644
--- a/src/components/relative-time.jsx
+++ b/src/components/relative-time.jsx
@@ -16,7 +16,8 @@ function isValidDate(value) {
const resolvedLocale = new Intl.DateTimeFormat().resolvedOptions().locale;
const DTF = mem((locale, opts = {}) => {
- const lang = localeMatch([locale], [resolvedLocale]);
+ const regionlessLocale = locale.replace(/-[a-z]+$/i, '');
+ const lang = localeMatch([regionlessLocale], [resolvedLocale], locale);
try {
return new Intl.DateTimeFormat(lang, opts);
} catch (e) {}
diff --git a/src/components/shortcuts.css b/src/components/shortcuts.css
index 99ddf153..d701fd37 100644
--- a/src/components/shortcuts.css
+++ b/src/components/shortcuts.css
@@ -121,13 +121,31 @@
text-overflow: ellipsis;
overflow: hidden;
}
+#shortcuts .tab-bar li a {
+ position: relative;
+ &:before {
+ content: '';
+ position: absolute;
+ inset: 4px 0;
+ border-radius: 8px;
+ background-color: var(--bg-color);
+ z-index: -1;
+ transform: scale(0.5);
+ opacity: 0;
+ transition: all 0.1s ease-in-out;
+ }
+}
#shortcuts .tab-bar li a.is-active {
color: var(--link-color);
- background-image: radial-gradient(
+ /* background-image: radial-gradient(
closest-side at 50% 50%,
var(--bg-color),
transparent
- );
+ ); */
+ &:before {
+ transform: scale(1);
+ opacity: 1;
+ }
}
#app:has(#home-page):not(:has(#home-page ~ .deck-container)):has(header[hidden])
#shortcuts
diff --git a/src/components/status.css b/src/components/status.css
index 226aaafd..1d6ee4ff 100644
--- a/src/components/status.css
+++ b/src/components/status.css
@@ -430,6 +430,7 @@
> span + span {
position: static;
+ width: auto;
&:empty {
display: none;
@@ -1895,6 +1896,7 @@ a:focus-visible .card img {
.meta-container {
align-self: flex-start;
flex-grow: 0;
+ max-width: 100%;
}
.card .title {
line-height: 1.25;
diff --git a/src/components/status.jsx b/src/components/status.jsx
index 85c72c14..fffd6269 100644
--- a/src/components/status.jsx
+++ b/src/components/status.jsx
@@ -1061,7 +1061,14 @@ function Status({
)}
-
+
{uiState === 'start' && (
diff --git a/src/pages/favourites.jsx b/src/pages/favourites.jsx
index a68f1613..08b805f2 100644
--- a/src/pages/favourites.jsx
+++ b/src/pages/favourites.jsx
@@ -22,7 +22,7 @@ function Favourites() {
{
setFollowUIState('loading');
diff --git a/src/pages/login.jsx b/src/pages/login.jsx
index 8956385c..f0a85b42 100644
--- a/src/pages/login.jsx
+++ b/src/pages/login.jsx
@@ -11,7 +11,12 @@ import LangSelector from '../components/lang-selector';
import Link from '../components/link';
import Loader from '../components/loader';
import instancesListURL from '../data/instances.json?url';
-import { getAuthorizationURL, registerApplication } from '../utils/auth';
+import {
+ getAuthorizationURL,
+ getPKCEAuthorizationURL,
+ registerApplication,
+} from '../utils/auth';
+import { supportsPKCE } from '../utils/oauth-pkce';
import store from '../utils/store';
import useTitle from '../utils/useTitle';
@@ -63,17 +68,36 @@ function Login() {
instanceURL,
});
- if (client_id && client_secret) {
- store.session.set('clientID', client_id);
- store.session.set('clientSecret', client_secret);
- store.session.set('vapidKey', vapid_key);
+ const authPKCE = await supportsPKCE({ instanceURL });
+ console.log({ authPKCE });
+ if (authPKCE) {
+ if (client_id && client_secret) {
+ store.sessionCookie.set('clientID', client_id);
+ store.sessionCookie.set('clientSecret', client_secret);
+ store.sessionCookie.set('vapidKey', vapid_key);
- location.href = await getAuthorizationURL({
- instanceURL,
- client_id,
- });
+ const [url, verifier] = await getPKCEAuthorizationURL({
+ instanceURL,
+ client_id,
+ });
+ store.sessionCookie.set('codeVerifier', verifier);
+ location.href = url;
+ } else {
+ alert(t`Failed to register application`);
+ }
} else {
- alert('Failed to register application');
+ if (client_id && client_secret) {
+ store.sessionCookie.set('clientID', client_id);
+ store.sessionCookie.set('clientSecret', client_secret);
+ store.sessionCookie.set('vapidKey', vapid_key);
+
+ location.href = await getAuthorizationURL({
+ instanceURL,
+ client_id,
+ });
+ } else {
+ alert(t`Failed to register application`);
+ }
}
setUIState('default');
} catch (e) {
@@ -158,7 +182,7 @@ function Login() {
autocapitalize="off"
autocomplete="off"
spellCheck={false}
- placeholder={`instance domain`}
+ placeholder={t`instance domain`}
onInput={(e) => {
setInstanceText(e.target.value);
}}
diff --git a/src/pages/settings.jsx b/src/pages/settings.jsx
index c2fec0e6..43587e93 100644
--- a/src/pages/settings.jsx
+++ b/src/pages/settings.jsx
@@ -232,9 +232,21 @@ function Settings({ onClose }) {
-
+
+
+
+
+
+ Volunteer translations
+
+
+
diff --git a/src/utils/auth.js b/src/utils/auth.js
index f03ee345..564ec012 100644
--- a/src/utils/auth.js
+++ b/src/utils/auth.js
@@ -1,12 +1,32 @@
-const { PHANPY_CLIENT_NAME: CLIENT_NAME, PHANPY_WEBSITE: WEBSITE } = import.meta
- .env;
+import { generateCodeChallenge, verifier } from './oauth-pkce';
+
+const {
+ DEV,
+ PHANPY_CLIENT_NAME: CLIENT_NAME,
+ PHANPY_WEBSITE: WEBSITE,
+} = import.meta.env;
const SCOPES = 'read write follow push';
+/*
+ PHANPY_WEBSITE is set to the default official site.
+ It's used in pre-built releases, so there's no way to change it dynamically
+ without rebuilding.
+ Therefore, we can't use it as redirect_uri.
+ We only use PHANPY_WEBSITE if it's "same" as current location URL.
+
+ Very basic check based on location.hostname for now
+*/
+const sameSite = WEBSITE
+ ? WEBSITE.toLowerCase().includes(location.hostname)
+ : false;
+const currentLocation = location.origin + location.pathname;
+const REDIRECT_URI = DEV || !sameSite ? currentLocation : WEBSITE;
+
export async function registerApplication({ instanceURL }) {
const registrationParams = new URLSearchParams({
client_name: CLIENT_NAME,
- redirect_uris: location.origin + location.pathname,
+ redirect_uris: REDIRECT_URI,
scopes: SCOPES,
website: WEBSITE,
});
@@ -25,11 +45,26 @@ export async function registerApplication({ instanceURL }) {
return registrationJSON;
}
+export async function getPKCEAuthorizationURL({ instanceURL, client_id }) {
+ const codeVerifier = verifier();
+ const codeChallenge = await generateCodeChallenge(codeVerifier);
+ const params = new URLSearchParams({
+ client_id,
+ code_challenge_method: 'S256',
+ code_challenge: codeChallenge,
+ redirect_uri: REDIRECT_URI,
+ response_type: 'code',
+ scope: SCOPES,
+ });
+ const authorizationURL = `https://${instanceURL}/oauth/authorize?${params.toString()}`;
+ return [authorizationURL, codeVerifier];
+}
+
export async function getAuthorizationURL({ instanceURL, client_id }) {
const authorizationParams = new URLSearchParams({
client_id,
scope: SCOPES,
- redirect_uri: location.origin + location.pathname,
+ redirect_uri: REDIRECT_URI,
// redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
response_type: 'code',
});
@@ -42,15 +77,23 @@ export async function getAccessToken({
client_id,
client_secret,
code,
+ code_verifier,
}) {
const params = new URLSearchParams({
client_id,
- client_secret,
- redirect_uri: location.origin + location.pathname,
+ redirect_uri: REDIRECT_URI,
grant_type: 'authorization_code',
code,
scope: SCOPES,
+ // client_secret,
+ // code_verifier,
});
+ if (client_secret) {
+ params.append('client_secret', client_secret);
+ }
+ if (code_verifier) {
+ params.append('code_verifier', code_verifier);
+ }
const tokenResponse = await fetch(`https://${instanceURL}/oauth/token`, {
method: 'POST',
headers: {
diff --git a/src/utils/get-instance-status-url.js b/src/utils/get-instance-status-url.js
index 3f4cc6ee..157c08bd 100644
--- a/src/utils/get-instance-status-url.js
+++ b/src/utils/get-instance-status-url.js
@@ -5,6 +5,7 @@ const statusPostRegexes = [
/^\/@[^@\/]+\/(?:statuses|posts)\/([^\/]+)/i, // GoToSocial, Takahe
/\/notes\/([^\/]+)/i, // Misskey, Firefish
/^\/(?:notice|objects)\/([a-z0-9-]+)/i, // Pleroma
+ /\/@[^@\/]+\/post\/([^\/]+)/i, // Threads
/\/@[^@\/]+@?[^\/]+?\/([^\/]+)/i, // Mastodon
/^\/p\/[^\/]+\/([^\/]+)/i, // Pixelfed
];
diff --git a/src/utils/isMastodonLinkMaybe.jsx b/src/utils/isMastodonLinkMaybe.jsx
index ab18c6d4..753fc3bb 100644
--- a/src/utils/isMastodonLinkMaybe.jsx
+++ b/src/utils/isMastodonLinkMaybe.jsx
@@ -6,7 +6,7 @@ export default function isMastodonLinkMaybe(url) {
/^\/(@[^/]+|users\/[^/]+)\/(statuses|posts)\/\w+\/?$/i.test(pathname) || // GoToSocial, Takahe
/^\/notes\/[a-z0-9]+$/i.test(pathname) || // Misskey, Firefish
/^\/(notice|objects)\/[a-z0-9-]+$/i.test(pathname) || // Pleroma
- /^\/@[^/]+\/post\/[a-z0-9]+$/i.test(pathname) || // Threads
+ /^\/@[^/]+\/post\/[a-z0-9\-_]+$/i.test(pathname) || // Threads
/^\/@[^/]+\/[a-z0-9]+[a-z0-9\-]+[a-z0-9]+$/i.test(pathname) || // Hollo
(hostname === 'fed.brid.gy' && pathname.startsWith('/r/http')) || // Bridgy Fed
/#\/[^\/]+\.[^\/]+\/s\/.+/i.test(hash) // Phanpy 🫣
diff --git a/src/utils/lang.js b/src/utils/lang.js
index 5140522b..2ef1b135 100644
--- a/src/utils/lang.js
+++ b/src/utils/lang.js
@@ -7,7 +7,7 @@ import {
} from '@lingui/detect-locale';
import Locale from 'intl-locale-textinfo-polyfill';
-import { DEFAULT_LANG, LOCALES } from '../locales';
+import { ALL_LOCALES, DEFAULT_LANG } from '../locales';
import { messages } from '../locales/en.po';
import localeMatch from '../utils/locale-match';
@@ -62,7 +62,7 @@ export function initActivateLang() {
DEFAULT_LANG,
);
const matchedLang =
- LOCALES.find((l) => l === lang) || localeMatch(lang, LOCALES);
+ ALL_LOCALES.find((l) => l === lang) || localeMatch(lang, ALL_LOCALES);
activateLang(matchedLang);
// const yes = confirm(t`Reload to apply language setting?`);
diff --git a/src/utils/nice-date-time.js b/src/utils/nice-date-time.js
index a1d5baff..adf81c88 100644
--- a/src/utils/nice-date-time.js
+++ b/src/utils/nice-date-time.js
@@ -1,12 +1,14 @@
import { i18n } from '@lingui/core';
+import localeMatch from './locale-match';
import mem from './mem';
const defaultLocale = new Intl.DateTimeFormat().resolvedOptions().locale;
const _DateTimeFormat = (opts) => {
const { locale, dateYear, hideTime, formatOpts } = opts || {};
- const loc = locale && !/pseudo/i.test(locale) ? locale : defaultLocale;
+ const regionlessLocale = locale.replace(/-[a-z]+$/i, '');
+ const loc = localeMatch([regionlessLocale], [defaultLocale], locale);
const currentYear = new Date().getFullYear();
const options = {
// Show year if not current year
@@ -20,9 +22,11 @@ const _DateTimeFormat = (opts) => {
};
try {
return Intl.DateTimeFormat(loc, options);
- } catch (e) {
- return Intl.DateTimeFormat(undefined, options);
- }
+ } catch (e) {}
+ try {
+ return Intl.DateTimeFormat(locale, options);
+ } catch (e) {}
+ return Intl.DateTimeFormat(undefined, options);
};
const DateTimeFormat = mem(_DateTimeFormat);
diff --git a/src/utils/oauth-pkce.js b/src/utils/oauth-pkce.js
new file mode 100644
index 00000000..084309b2
--- /dev/null
+++ b/src/utils/oauth-pkce.js
@@ -0,0 +1,46 @@
+function dec2hex(dec) {
+ return ('0' + dec.toString(16)).slice(-2);
+}
+export function verifier() {
+ var array = new Uint32Array(56 / 2);
+ window.crypto.getRandomValues(array);
+ return Array.from(array, dec2hex).join('');
+}
+function sha256(plain) {
+ // returns promise ArrayBuffer
+ const encoder = new TextEncoder();
+ const data = encoder.encode(plain);
+ return window.crypto.subtle.digest('SHA-256', data);
+}
+function base64urlencode(a) {
+ let str = '';
+ const bytes = new Uint8Array(a);
+ const len = bytes.byteLength;
+ for (var i = 0; i < len; i++) {
+ str += String.fromCharCode(bytes[i]);
+ }
+ return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
+}
+export async function generateCodeChallenge(v) {
+ const hashed = await sha256(v);
+ return base64urlencode(hashed);
+}
+
+// If /.well-known/oauth-authorization-server exists and code_challenge_methods_supported includes "S256", means support PKCE
+export async function supportsPKCE({ instanceURL }) {
+ if (!instanceURL) return false;
+ try {
+ const res = await fetch(
+ `https://${instanceURL}/.well-known/oauth-authorization-server`,
+ );
+ if (!res.ok || res.status !== 200) return false;
+ const json = await res.json();
+ if (json.code_challenge_methods_supported?.includes('S256')) return true;
+ return false;
+ } catch (e) {
+ return false;
+ }
+}
+
+// For debugging
+window.__generateCodeChallenge = generateCodeChallenge;
diff --git a/src/utils/push-notifications.js b/src/utils/push-notifications.js
index 598e54fe..b4621e51 100644
--- a/src/utils/push-notifications.js
+++ b/src/utils/push-notifications.js
@@ -1,6 +1,6 @@
// Utils for push notifications
import { api } from './api';
-import { getCurrentAccount } from './store-utils';
+import { getVapidKey } from './store-utils';
// Subscription is an object with the following structure:
// {
@@ -113,7 +113,7 @@ export async function initSubscription() {
// Check if the subscription changed
if (backendSubscription && subscription) {
const sameEndpoint = backendSubscription.endpoint === subscription.endpoint;
- const { vapidKey } = getCurrentAccount();
+ const vapidKey = getVapidKey();
const sameKey = backendSubscription.serverKey === vapidKey;
if (!sameEndpoint) {
throw new Error('Backend subscription endpoint changed');
@@ -146,7 +146,7 @@ export async function initSubscription() {
if (subscription && !backendSubscription) {
// check if account's vapidKey is same as subscription's applicationServerKey
- const { vapidKey } = getCurrentAccount();
+ const vapidKey = getVapidKey();
if (vapidKey) {
const { applicationServerKey } = subscription.options;
const vapidKeyStr = urlBase64ToUint8Array(vapidKey).toString();
@@ -210,7 +210,7 @@ export async function updateSubscription({ data, policy }) {
}
} else {
// User is not subscribed
- const { vapidKey } = getCurrentAccount();
+ const vapidKey = getVapidKey();
if (!vapidKey) throw new Error('No server key found');
subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
diff --git a/src/utils/store-utils.js b/src/utils/store-utils.js
index 042b2fa0..cc236215 100644
--- a/src/utils/store-utils.js
+++ b/src/utils/store-utils.js
@@ -154,6 +154,13 @@ export function getCurrentInstanceConfiguration() {
return getInstanceConfiguration(instance);
}
+export function getVapidKey() {
+ // Vapid key has moved from account to instance config
+ const config = getCurrentInstanceConfiguration();
+ const vapidKey = config?.vapid?.publicKey || config?.vapid?.public_key;
+ return vapidKey || getCurrentAccount()?.vapidKey;
+}
+
export function isMediaFirstInstance() {
const instance = getCurrentInstance();
return /pixelfed/i.test(instance?.version);
diff --git a/src/utils/store.js b/src/utils/store.js
index fb587b11..19309885 100644
--- a/src/utils/store.js
+++ b/src/utils/store.js
@@ -1,5 +1,9 @@
+import Cookies from 'js-cookie';
+
import { getCurrentAccountNS } from './store-utils';
+const cookies = Cookies.withAttributes({ sameSite: 'strict', secure: true });
+
const local = {
get: (key) => {
try {
@@ -86,6 +90,38 @@ const session = {
},
};
+// Session secure cookie
+const cookie = {
+ get: (key) => cookies.get(key),
+ set: (key, value) => cookies.set(key, value),
+ del: (key) => cookies.remove(key),
+};
+
+// Cookie with sessionStorage fallback
+const sessionCookie = {
+ get: (key) => {
+ if (navigator.cookieEnabled) {
+ return cookie.get(key);
+ } else {
+ return session.get(key);
+ }
+ },
+ set: (key, value) => {
+ if (navigator.cookieEnabled) {
+ return cookie.set(key, value);
+ } else {
+ return session.set(key, value);
+ }
+ },
+ del: (key) => {
+ if (navigator.cookieEnabled) {
+ return cookie.del(key);
+ } else {
+ return session.del(key);
+ }
+ },
+};
+
// Store with account namespace (id@domain.tld) <- uses id, not username
const account = {
get: (key) => {
@@ -118,4 +154,4 @@ const account = {
},
};
-export default { local, session, account };
+export default { local, session, sessionCookie, cookie, account };
diff --git a/vite.config.js b/vite.config.js
index fc085f66..ed3dbc1d 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -12,9 +12,12 @@ import { VitePWA } from 'vite-plugin-pwa';
import removeConsole from 'vite-plugin-remove-console';
import { run } from 'vite-plugin-run';
+import { ALL_LOCALES } from './src/locales';
+
const allowedEnvPrefixes = ['VITE_', 'PHANPY_'];
const { NODE_ENV } = process.env;
const {
+ PHANPY_WEBSITE: WEBSITE,
PHANPY_CLIENT_NAME: CLIENT_NAME,
PHANPY_APP_ERROR_LOGGING: ERROR_LOGGING,
} = loadEnv('production', process.cwd(), allowedEnvPrefixes);
@@ -70,6 +73,11 @@ export default defineConfig({
run: ['npm', 'run', 'messages:extract:clean'],
pattern: 'src/**/*.{js,jsx,ts,tsx}',
},
+ // {
+ // name: 'update-catalogs',
+ // run: ['node', 'scripts/catalogs.js'],
+ // pattern: 'src/locales/*.po',
+ // },
],
}),
splitVendorChunkPlugin(),
@@ -78,6 +86,20 @@ export default defineConfig({
}),
htmlPlugin({
headScripts: ERROR_LOGGING ? [rollbarCode] : [],
+ links: [
+ ...ALL_LOCALES.map((lang) => ({
+ rel: 'alternate',
+ hreflang: lang,
+ // *Fully-qualified* URLs
+ href: `${WEBSITE}/?lang=${lang}`,
+ })),
+ // https://developers.google.com/search/docs/specialty/international/localized-versions#xdefault
+ {
+ rel: 'alternate',
+ hreflang: 'x-default',
+ href: `${WEBSITE}`,
+ },
+ ],
}),
generateFile([
{