diff --git a/.gitignore b/.gitignore index 1eaa3a0..990ddae 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ coverage/ test-results/ playwright-report/ playwright/.cache/ +yt-storage.json # IDE and editor files .vscode/ diff --git a/README.md b/README.md index d8c65b3..ac24004 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,19 @@ -#  YT re:Watch - YouTube History Extension for Multiple Accounts & Privacy +#  YT re:Watch [](https://github.com/EdinUser/YouTubeLocalHistory/actions/workflows/ci.yml) [](https://github.com/EdinUser/YouTubeLocalHistory/releases) [](https://t.me/+eFftKWGVvSpiZjZk) -[](https://github.com/tannery495/youtube-history-converter) -# 📚 **[→ New User? Start Here! User-Friendly Guide](./docs/index.md) ←** +**Private local YouTube history, progress tracking, local subscriptions, playlists, and a YouTube-style feed inside your browser.** + +YT re:Watch keeps your watch progress on your device so you can switch YouTube accounts, use YouTube logged out, import your data, and browse a local subscription feed without relying on Google account history. + +📚 **[New user? Start with the guide](./docs/index.md)**
-
-```
-
-### Responsive Images
-```markdown
-
-*Figure 1: Extension popup interface showing all available tabs*
-```
-
----
-
-*Ready to contribute visual content? Pick an image from the priority list and start creating!*
diff --git a/docs/images/hero.jpg b/docs/images/hero.jpg
deleted file mode 100644
index bd8ecfe..0000000
Binary files a/docs/images/hero.jpg and /dev/null differ
diff --git a/docs/images/ytrw_overlay.jpg b/docs/images/ytrw_overlay.jpg
deleted file mode 100644
index 413342b..0000000
Binary files a/docs/images/ytrw_overlay.jpg and /dev/null differ
diff --git a/docs/images/ytrw_playlists.jpg b/docs/images/ytrw_playlists.jpg
deleted file mode 100644
index b3deb9c..0000000
Binary files a/docs/images/ytrw_playlists.jpg and /dev/null differ
diff --git a/docs/images/ytrw_settings.jpg b/docs/images/ytrw_settings.jpg
deleted file mode 100644
index ae7a149..0000000
Binary files a/docs/images/ytrw_settings.jpg and /dev/null differ
diff --git a/docs/images/ytrw_settings2.jpg b/docs/images/ytrw_settings2.jpg
deleted file mode 100644
index a8ae498..0000000
Binary files a/docs/images/ytrw_settings2.jpg and /dev/null differ
diff --git a/docs/images/ytrw_shorts.jpg b/docs/images/ytrw_shorts.jpg
deleted file mode 100644
index 89ef7c5..0000000
Binary files a/docs/images/ytrw_shorts.jpg and /dev/null differ
diff --git a/docs/images/ytrw_stats1.jpg b/docs/images/ytrw_stats1.jpg
deleted file mode 100644
index 18ef422..0000000
Binary files a/docs/images/ytrw_stats1.jpg and /dev/null differ
diff --git a/docs/images/ytrw_stats2.jpg b/docs/images/ytrw_stats2.jpg
deleted file mode 100644
index 4dc9658..0000000
Binary files a/docs/images/ytrw_stats2.jpg and /dev/null differ
diff --git a/docs/images/ytrw_stats3.jpg b/docs/images/ytrw_stats3.jpg
deleted file mode 100644
index bd1c8b9..0000000
Binary files a/docs/images/ytrw_stats3.jpg and /dev/null differ
diff --git a/docs/images/ytrw_stats4.jpg b/docs/images/ytrw_stats4.jpg
deleted file mode 100644
index d372cdc..0000000
Binary files a/docs/images/ytrw_stats4.jpg and /dev/null differ
diff --git a/docs/images/ytrw_videos.jpg b/docs/images/ytrw_videos.jpg
deleted file mode 100644
index bffca4a..0000000
Binary files a/docs/images/ytrw_videos.jpg and /dev/null differ
diff --git a/docs/index.md b/docs/index.md
index 2b3ef6f..30de85d 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,14 +1,13 @@
# 🎬 YT re:Watch — YouTube History Extension for Multi-Account Privacy
-
-A private YouTube history extension that keeps multi-account progress in sync while staying privacy-first and local-only.
+A private YouTube history extension that keeps multi-account progress local, adds local subscriptions, and provides a YouTube-style feed inside the browser.
[](https://chromewebstore.google.com/detail/local-youtube-video-histo/pebiokefjgdbfnkolmblaaladkmpilba)
[](https://addons.mozilla.org/firefox/addon/yt-rewatch/)
*Keep track of your YouTube journey — your data stays private on your device*
-YT re:Watch is a privacy-first YouTube history extension that works across multiple accounts (or no account), tracks your progress locally, and keeps your watch history independent from Google so you can resume anywhere without losing data.
+YT re:Watch is a privacy-first YouTube history extension that works across multiple accounts (or no account), tracks your progress locally, and keeps your watch history independent from your Google account so you can resume without losing data. It also includes a local feed, local channel subscriptions, playlists, history, analytics, and settings.
# ❤️ Support the Project
@@ -34,13 +33,11 @@ YT re:Watch solves both problems with **Account Independence + YouTube History P
- ✅ **Multi-account workflow** - perfect for work/personal account users
### 🔒 **YouTube History Privacy**
-- ✅ **Replaces YouTube's built-in history** - completely independent from Google
-- ✅ **Local storage only** - Google doesn't get your viewing progress data
-- ✅ **No history profiling** - your viewing progress stays yours
+- ✅ **Independent from YouTube's built-in history** - stored separately from your Google account
+- ✅ **Local storage only** - YT re:Watch does not upload saved progress to an app server
+- ✅ **No extension-owned history profiling** - your viewing progress stays in your browser profile
- ✅ **Unlimited local storage** - GB-scale capacity with hybrid IndexedDB system
-
-*Your videos, your progress, your way - regardless of which account you use*
## 🚀 Get Started in 30 Seconds
@@ -52,7 +49,7 @@ YT re:Watch solves both problems with **Account Independence + YouTube History P
### Step 2: That's It!
- Go to YouTube and start watching videos
- The extension works automatically in the background
-- Click the extension icon to see your watch history
+- Click the extension icon for quick actions, or open the full feed page for Home, Subscriptions, Shorts, Playlists, History, Channels, Analytics, and Settings
Need a deeper walkthrough? Read the [Detailed Guide](./detailed_guide.md). Quick answers live in the [FAQ](./faq.md), fixes are in [Troubleshooting](./troubleshooting.md), and release notes are in the [Changelog](../CHANGELOG.md).
@@ -77,8 +74,6 @@ Need a deeper walkthrough? Read the [Detailed Guide](./detailed_guide.md). Quick
- "I want to track my video watching without Google profiling"
- "I need consistent history for research projects"
-
-*View your video watching statistics across all accounts*
### 🙋♀️ **Anyone Who Wants Convenience**
- "I watch long videos but often get interrupted"
@@ -87,15 +82,13 @@ Need a deeper walkthrough? Read the [Detailed Guide](./detailed_guide.md). Quick
## 📱 What You'll See
-### 🏷️ **Visual Overlays on YouTube**
-The extension adds helpful visual indicators directly on YouTube:
+### 🏷️ **Viewed Indicators**
+The extension adds helpful visual indicators inside YT re:Watch, with best-effort labels on supported YouTube thumbnail layouts:
-
-*Smart overlays show your progress across all accounts*
- **"Viewed" labels** - See which videos you've already watched
- **Progress bars** - Visual indicator of how much you've completed
-- **Works everywhere** - Video listings, search results, recommendations
+- **Works across the local feed** - Home, subscriptions, playlists, search, and history
- **Fully customizable** - Change colors, text, and size in Settings
**Overlay Customization Options:**
@@ -105,26 +98,16 @@ The extension adds helpful visual indicators directly on YouTube:
- **Learn more**: [Complete customization guide](./detailed_guide.md#overlay-customization)
### 🎛️ **Extension Interface**
-**Videos Tab** - Your main viewing history:
-
+**History Tab** - Your local watch history:
Your history list shows the channel name under each video title to help you scan quickly.
**Shorts Tab** - Separate tracking for YouTube Shorts:
-
**Analytics Tab** - See your viewing patterns:
-
-*Summary cards and top channels in the Analytics tab*
-
-*Completion bar chart, unfinished videos, and skipped channels*
-
-*Watch activity by day and by hour in the Analytics tab*
-
-*Additional analytics showing watch time patterns and channel statistics*
These charts now prefer a locally persisted, privacy‑preserving stats snapshot (rebuilt from your full hybrid history) for better accuracy and responsiveness. Keys are local‑day `YYYY-MM-DD` and 24 hourly buckets, and the activity view focuses on the last 7 local days only.
@@ -134,10 +117,7 @@ These charts now prefer a locally persisted, privacy‑preserving stats snapshot
- **Completion Bar Chart**: See your completion rate for long videos (skipped, partial, completed) with a bar chart and legend
**Settings Tab** - Customize everything:
-
-
-*Additional settings for data management, privacy, and advanced features*
## 🔐 Account Independence + History Privacy = Perfect Combination
@@ -150,12 +130,12 @@ Traditional YouTube history is **tied to your account** - meaning:
**YT re:Watch fixes ALL of this** by storing data locally on your device, not tied to any account!
-### 🔒 **History Privacy Bonus: Google Doesn't Get Your Progress**
-Because we store everything locally:
-- ✅ **No Google servers** involved in your history tracking
-- ✅ **No profiling** based on your viewing progress
-- ✅ **No influence** on recommendations from your progress data
-- ✅ **No progress data collection** - we literally cannot see what you watch
+### 🔒 **History Privacy Bonus: Your Saved Progress Stays Local**
+Because the extension stores its own history locally:
+- ✅ **No extension-owned server** involved in your history tracking
+- ✅ **No app-side profiling** based on your saved progress
+- ✅ **No extension sync** that sends your history to a cloud service
+- ✅ **No progress data collection by YT re:Watch** - we cannot see what you watch
### 🏠 **Your Data, Your Rules**
- **Local storage only** - stays on your device
@@ -176,15 +156,15 @@ Because we store everything locally:
## 💡 Pro Tips
- **Dark Mode**: The extension automatically matches your system theme
-- **Search History**: Use the search box to find specific videos
-- **Export Data**: Back up your history from the Settings tab
+- **Search**: Use the feed search for YouTube results, or search local history in the History view
+- **Export Data**: Back up all local data from the Settings tab
- **Export/Import Data**: Manually transfer your history between devices via JSON files
## 🤝 Need Help?
### Quick Fixes
- **Extension not working?** Refresh the YouTube page
-- **History not showing?** Close and reopen the extension popup
+- **History not showing?** Refresh the feed page or reload the extension
- **Missing videos?** Check if you're on youtube.com (not youtube.tv or mobile)
### Get Support
@@ -196,7 +176,7 @@ Because we store everything locally:
## 📈 What's New
-- **v3.1.5**: Enhanced navigation detection, improved timestamp restoration for all YouTube navigation patterns, and intelligent save interval management
+- **v5.0.0**: Adds the full local feed, local subscriptions, channel pages, playlists, analytics updates, backup/restore improvements, and Firefox temporary build support
- **See all updates**: [CHANGELOG.md](../CHANGELOG.md)
## 🌟 Love YT re:Watch?
@@ -220,7 +200,7 @@ Because we store everything locally:
- **[Build Instructions](./build.md)** - Development setup
## ℹ️ About YT re:Watch
-YT re:Watch is a privacy-first YouTube history extension that keeps watch progress consistent across multiple accounts, stores data locally instead of on Google, and helps you track and resume videos with customizable overlays and stats.
+YT re:Watch is a privacy-first YouTube history extension that keeps watch progress consistent across multiple accounts, stores extension data locally, and helps you browse a local YouTube-style feed with subscriptions, playlists, history, analytics, and settings.
---
@@ -230,4 +210,4 @@ YT re:Watch is a privacy-first YouTube history extension that keeps watch progre
## 🌐 Multilanguage Support
-YT re:Watch is available in multiple languages. All non-English translations are currently machine-generated. If you're a native speaker, your help is welcome—see the technical docs for how to contribute!
\ No newline at end of file
+YT re:Watch is available in multiple languages. All non-English translations are currently machine-generated. If you're a native speaker, your help is welcome—see the technical docs for how to contribute!
diff --git a/docs/readme.md b/docs/readme.md
index af162f9..c9dc4cd 100644
--- a/docs/readme.md
+++ b/docs/readme.md
@@ -14,7 +14,8 @@ Welcome! Find the right guide for you:
**[→ Start Here: Quick Setup Guide](./index.md)**
- Same history across ALL YouTube accounts
- Get the extension working in 5 minutes
-- YouTube history privacy from Google tracking
+- Local history, subscriptions, playlists, and feed basics
+- What local storage protects, and what normal YouTube browsing still exposes
### ❓ **Have Questions?**
**[→ Frequently Asked Questions](./faq.md)**
@@ -59,4 +60,4 @@ Welcome! Find the right guide for you:
## Multilanguage Support
-This extension is available in several languages. See the main [README](../README.md) and [technical.md](./technical.md) for details on translation and contributing improvements.
\ No newline at end of file
+This extension is available in several languages. See the main [README](../README.md) and [technical.md](./technical.md) for details on translation and contributing improvements.
diff --git a/docs/technical.md b/docs/technical.md
index ef2ce57..ba8775c 100644
--- a/docs/technical.md
+++ b/docs/technical.md
@@ -454,9 +454,10 @@ await ytStorage.getAllVideos(); // Returns merged view
## 🛠️ Development Setup
### Prerequisites
-- Node.js 14+
-- NPM or Yarn
+- Node.js 18+
+- npm
- Chrome/Firefox for testing
+- Git Bash, WSL, or another Bash-compatible shell for `build.sh`
### Local Development
@@ -468,11 +469,10 @@ cd YouTubeLocalHistory
# Install dependencies
npm install
-# Build extension
-./build.sh
+# Recommended Firefox temporary build
+npm run prepare:firefox
-# Built extensions will be in dist/
-# Load dist/chrome or dist/firefox in browser
+# Load build/firefox/manifest.json as a temporary Firefox add-on
```
### Testing
@@ -481,6 +481,12 @@ npm install
# Run all tests
npm test
+# Recommended PR checks
+npm run lint
+npm test -- --runInBand
+npm run prepare:firefox
+npx web-ext lint --source-dir=build/firefox
+
# Run specific test suites
npm run test:unit
npm run test:integration
@@ -494,11 +500,7 @@ npm run test:coverage
```bash
# Build both Chrome and Firefox versions
-./build.sh
-
-# Build specific browser
-./build.sh chrome
-./build.sh firefox
+npm run build
```
---
@@ -812,4 +814,4 @@ _locales/
---
-*For more technical details, check the source code and inline documentation.*
\ No newline at end of file
+*For more technical details, check the source code and inline documentation.*
diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
index 8ac6c9c..1ac1a3d 100644
--- a/docs/troubleshooting.md
+++ b/docs/troubleshooting.md
@@ -14,7 +14,7 @@ This fixes 90% of all issues by clearing cached content and reloading the extens
### 2. Restart Extension
1. Click the extension icon in your browser toolbar
-2. Close the popup completely
+2. Close the popup or feed page completely
3. Click the extension icon again to reopen
### 3. Check Browser Compatibility
@@ -87,10 +87,10 @@ This fixes 90% of all issues by clearing cached content and reloading the extens
### History Display Problems
#### History Tab Empty or Missing Videos
-**Symptoms**: Extension popup shows no history or missing videos
+**Symptoms**: Extension feed or popup shows no history or missing videos
**Solutions**:
-1. **Refresh extension popup**: Close and reopen the extension
+1. **Refresh the extension UI**: Refresh the feed page or close and reopen the popup
2. **Check storage quota**: Extension has 5GB+ storage limit
3. **Browser data clearing**: If you cleared browser data, history is lost
4. **Incognito mode**: Extension doesn't work in private browsing
@@ -119,10 +119,10 @@ This fixes 90% of all issues by clearing cached content and reloading the extens
### Performance Issues
#### Extension Slow or Unresponsive
-**Symptoms**: Extension popup slow to open, videos slow to track
+**Symptoms**: Extension feed or popup slow to open, videos slow to track
**Solutions**:
-1. **Clear extension data**: Go to extension settings > Advanced > Reset Data
+1. **Backup first**: Go to Settings > Data Management and download a backup before clearing data
2. **Browser restart**: Complete restart frees up memory
3. **Check storage usage**: Large history can slow down the extension
4. **Disable unused features**: Turn off features you don't need
@@ -141,7 +141,7 @@ This fixes 90% of all issues by clearing cached content and reloading the extens
Enable debug mode for detailed logging to help troubleshoot complex issues:
### Enable Debug Mode
-1. Open YT re:Watch extension popup
+1. Open the YT re:Watch feed page
2. Go to **Settings** tab
3. Enable **"Debug Mode"**
4. Check browser console for detailed logs
@@ -149,7 +149,7 @@ Enable debug mode for detailed logging to help troubleshoot complex issues:
### Debug Information to Include in Bug Reports
When reporting issues, include:
- **Browser and version** (Chrome 120.0, Firefox 119.0)
-- **Extension version** (visible in extension popup)
+- **Extension version** (visible in the extension UI or browser add-on details)
- **Operating system** (Windows 11, macOS 14.0, Ubuntu 22.04)
- **Steps to reproduce** the issue
- **Expected vs actual behavior**
@@ -170,7 +170,7 @@ When reporting issues, include:
### Emergency Fixes
If nothing works:
-1. **Reset extension data**: Go to Settings > Advanced > Reset Data (loses all history)
+1. **Reset extension data**: Use your browser's extension storage controls only after exporting a backup (this loses local data)
2. **Reinstall extension**: Remove and reinstall YT re:Watch
3. **Check for updates**: Ensure you have the latest version
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..85b285e
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,44 @@
+module.exports = [
+ {
+ ignores: [
+ 'build/**',
+ 'coverage/**',
+ 'node_modules/**'
+ ]
+ },
+ {
+ files: ['src/**/*.js', 'tests/**/*.js'],
+ languageOptions: {
+ ecmaVersion: 2021,
+ sourceType: 'script',
+ globals: {
+ afterAll: 'readonly',
+ afterEach: 'readonly',
+ beforeAll: 'readonly',
+ beforeEach: 'readonly',
+ browser: 'readonly',
+ chrome: 'readonly',
+ clearInterval: 'readonly',
+ clearTimeout: 'readonly',
+ console: 'readonly',
+ document: 'readonly',
+ DOMParser: 'readonly',
+ expect: 'readonly',
+ global: 'readonly',
+ jest: 'readonly',
+ localStorage: 'readonly',
+ module: 'readonly',
+ MutationObserver: 'readonly',
+ navigator: 'readonly',
+ performance: 'readonly',
+ process: 'readonly',
+ require: 'readonly',
+ setInterval: 'readonly',
+ setTimeout: 'readonly',
+ URL: 'readonly',
+ window: 'readonly'
+ }
+ },
+ rules: {}
+ }
+];
diff --git a/package-lock.json b/package-lock.json
index bd14a71..5b2d27a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "youtube-local-history",
- "version": "2.4.0",
+ "version": "5.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "youtube-local-history",
- "version": "2.4.0",
+ "version": "5.0.0",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.24.0",
@@ -17,20 +17,8 @@
"babel-jest": "^29.7.0",
"eslint": "^9.15.0",
"jest": "^29.7.0",
- "jest-environment-jsdom": "^30.3.0"
- }
- },
- "node_modules/@ampproject/remapping": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
- "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
- "dev": true,
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.24"
- },
- "engines": {
- "node": ">=6.0.0"
+ "jest-environment-jsdom": "^30.3.0",
+ "web-ext": "^8.3.0"
}
},
"node_modules/@asamuzakjp/css-color": {
@@ -55,13 +43,13 @@
"license": "ISC"
},
"node_modules/@babel/code-frame": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
- "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/helper-validator-identifier": "^7.29.7",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
@@ -70,30 +58,32 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz",
- "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
- "version": "7.27.4",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz",
- "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.27.3",
- "@babel/helper-compilation-targets": "^7.27.2",
- "@babel/helper-module-transforms": "^7.27.3",
- "@babel/helpers": "^7.27.4",
- "@babel/parser": "^7.27.4",
- "@babel/template": "^7.27.2",
- "@babel/traverse": "^7.27.4",
- "@babel/types": "^7.27.3",
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helpers": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -109,14 +99,14 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.29.1",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
- "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.29.0",
- "@babel/types": "^7.29.0",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -138,13 +128,14 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
- "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@babel/compat-data": "^7.27.2",
- "@babel/helper-validator-option": "^7.27.1",
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
"semver": "^6.3.1"
@@ -208,10 +199,11 @@
}
},
"node_modules/@babel/helper-globals": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
- "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
@@ -230,29 +222,29 @@
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
- "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.28.6",
- "@babel/types": "^7.28.6"
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
- "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-module-imports": "^7.28.6",
- "@babel/helper-validator-identifier": "^7.28.5",
- "@babel/traverse": "^7.28.6"
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -331,18 +323,19 @@
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
- "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.28.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
- "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -350,10 +343,11 @@
}
},
"node_modules/@babel/helper-validator-option": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
- "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=6.9.0"
}
@@ -373,26 +367,27 @@
}
},
"node_modules/@babel/helpers": {
- "version": "7.27.6",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz",
- "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.27.6"
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
- "version": "7.29.3",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz",
- "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.29.0"
+ "@babel/types": "^7.29.7"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -1734,34 +1729,44 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/runtime": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
+ "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/template": {
- "version": "7.28.6",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
- "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.28.6",
- "@babel/parser": "^7.28.6",
- "@babel/types": "^7.28.6"
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
- "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.29.0",
- "@babel/generator": "^7.29.0",
- "@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.29.0",
- "@babel/template": "^7.28.6",
- "@babel/types": "^7.29.0",
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7",
"debug": "^4.3.1"
},
"engines": {
@@ -1769,14 +1774,14 @@
}
},
"node_modules/@babel/types": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
- "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.28.5"
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
@@ -1903,6 +1908,66 @@
"node": ">=18"
}
},
+ "node_modules/@devicefarmer/adbkit": {
+ "version": "3.3.8",
+ "resolved": "https://registry.npmjs.org/@devicefarmer/adbkit/-/adbkit-3.3.8.tgz",
+ "integrity": "sha512-7rBLLzWQnBwutH2WZ0EWUkQdihqrnLYCUMaB44hSol9e0/cdIhuNFcqZO0xNheAU6qqHVA8sMiLofkYTgb+lmw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@devicefarmer/adbkit-logcat": "^2.1.2",
+ "@devicefarmer/adbkit-monkey": "~1.2.1",
+ "bluebird": "~3.7",
+ "commander": "^9.1.0",
+ "debug": "~4.3.1",
+ "node-forge": "^1.3.1",
+ "split": "~1.0.1"
+ },
+ "bin": {
+ "adbkit": "bin/adbkit"
+ },
+ "engines": {
+ "node": ">= 0.10.4"
+ }
+ },
+ "node_modules/@devicefarmer/adbkit-logcat": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@devicefarmer/adbkit-logcat/-/adbkit-logcat-2.1.3.tgz",
+ "integrity": "sha512-yeaGFjNBc/6+svbDeul1tNHtNChw6h8pSHAt5D+JsedUrMTN7tla7B15WLDyekxsuS2XlZHRxpuC6m92wiwCNw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@devicefarmer/adbkit-monkey": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@devicefarmer/adbkit-monkey/-/adbkit-monkey-1.2.1.tgz",
+ "integrity": "sha512-ZzZY/b66W2Jd6NHbAhLyDWOEIBWC11VizGFk7Wx7M61JZRz7HR9Cq5P+65RKWUU7u6wgsE8Lmh9nE4Mz+U2eTg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.10.4"
+ }
+ },
+ "node_modules/@devicefarmer/adbkit/node_modules/debug": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
@@ -2007,10 +2072,20 @@
}
},
"node_modules/@eslint/eslintrc/node_modules/js-yaml": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
- "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
+ "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -2041,12 +2116,13 @@
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz",
- "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==",
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
+ "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.15.0",
+ "@eslint/core": "^0.15.2",
"levn": "^0.4.1"
},
"engines": {
@@ -2054,10 +2130,11 @@
}
},
"node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
- "version": "0.15.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz",
- "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==",
+ "version": "0.15.2",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
+ "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
@@ -2065,6 +2142,27 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@fluent/syntax": {
+ "version": "0.19.0",
+ "resolved": "https://registry.npmjs.org/@fluent/syntax/-/syntax-0.19.0.tgz",
+ "integrity": "sha512-5D2qVpZrgpjtqU4eNOcWGp1gnUCgjfM+vKGE2y03kKN6z5EBhtx0qdRFbg8QuNNj8wXNoX93KJoYb+NqoxswmQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=14.0.0",
+ "npm": ">=7.0.0"
+ }
+ },
+ "node_modules/@fregante/relaxed-json": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@fregante/relaxed-json/-/relaxed-json-2.0.0.tgz",
+ "integrity": "sha512-PyUXQWB42s4jBli435TDiYuVsadwRHnMc27YaLouINktvTWsL3FcKrRMGawTayFk46X+n5bE23RjUTWQwrukWw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -2100,6 +2198,22 @@
"url": "https://github.com/sponsors/nzakas"
}
},
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
+ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
+ "deprecated": "Use @eslint/config-array instead",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.3",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -2113,6 +2227,14 @@
"url": "https://github.com/sponsors/nzakas"
}
},
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "deprecated": "Use @eslint/object-schema instead",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
"node_modules/@humanwhocodes/retry": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
@@ -2713,6 +2835,17 @@
"@jridgewell/trace-mapping": "^0.3.24"
}
},
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
@@ -2738,6 +2871,51 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@mdn/browser-compat-data": {
+ "version": "7.1.5",
+ "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-7.1.5.tgz",
+ "integrity": "sha512-j7XpHbvSU3GOtXawlGq6TIETj0wgcE9o7FXu88ragQE/ak8/OsqOxn+wZ2v/2QUe8vrVhb9OJes+gHgyGEOYEQ==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/@playwright/test": {
"version": "1.56.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz",
@@ -2754,6 +2932,51 @@
"node": ">=18"
}
},
+ "node_modules/@pnpm/config.env-replace": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
+ "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.22.0"
+ }
+ },
+ "node_modules/@pnpm/network.ca-file": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz",
+ "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "4.2.10"
+ },
+ "engines": {
+ "node": ">=12.22.0"
+ }
+ },
+ "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/@pnpm/npm-conf": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-3.0.3.tgz",
+ "integrity": "sha512-//0sR/cow/s4ICQaYoAobOl4aU8cjU6x/V24V7XkKotb9+O+3zySIYp146vpaobYHnxa4pZX8NkV54Z5AwbDKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@pnpm/config.env-replace": "^1.1.0",
+ "@pnpm/network.ca-file": "^1.0.1",
+ "config-chain": "^1.1.11"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@sinclair/typebox": {
"version": "0.27.8",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
@@ -2886,6 +3109,13 @@
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true
},
+ "node_modules/@types/minimatch": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+ "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/node": {
"version": "24.0.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.3.tgz",
@@ -2923,6 +3153,23 @@
"integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
"dev": true
},
+ "node_modules/@types/yauzl": {
+ "version": "2.10.3",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
+ "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz",
+ "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
@@ -2944,21 +3191,76 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
- "node_modules/agent-base": {
- "version": "7.1.4",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
- "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "node_modules/addons-linter": {
+ "version": "7.20.0",
+ "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-7.20.0.tgz",
+ "integrity": "sha512-c6drrGsO91oGyqEdEZ3KIBFrSiSlhM5CKxoebBatH1l2vN1ByuRAlp9YUHLCjypLONThPcb+tVTA7X6yKIhpsw==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "@fluent/syntax": "0.19.0",
+ "@fregante/relaxed-json": "2.0.0",
+ "@mdn/browser-compat-data": "7.1.5",
+ "addons-moz-compare": "1.3.0",
+ "addons-scanner-utils": "9.13.0",
+ "ajv": "8.17.1",
+ "chalk": "4.1.2",
+ "cheerio": "1.0.0-rc.12",
+ "columnify": "1.6.0",
+ "common-tags": "1.8.2",
+ "deepmerge": "4.3.1",
+ "eslint": "8.57.1",
+ "eslint-plugin-no-unsanitized": "4.1.4",
+ "eslint-visitor-keys": "4.2.1",
+ "espree": "10.4.0",
+ "esprima": "4.0.1",
+ "fast-json-patch": "3.1.1",
+ "image-size": "2.0.2",
+ "json-merge-patch": "1.0.2",
+ "pino": "9.9.5",
+ "semver": "7.7.2",
+ "source-map-support": "0.5.21",
+ "upath": "2.0.1",
+ "yargs": "17.7.2",
+ "yauzl": "2.10.0"
+ },
+ "bin": {
+ "addons-linter": "bin/addons-linter"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/addons-linter/node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
"engines": {
- "node": ">= 14"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "node_modules/addons-linter/node_modules/@eslint/eslintrc/node_modules/ajv": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -2970,46 +3272,490 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
- "node_modules/ansi-escapes": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
- "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "node_modules/addons-linter/node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/addons-linter/node_modules/@eslint/eslintrc/node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
"dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
- "type-fest": "^0.21.3"
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
},
"engines": {
- "node": ">=8"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "node_modules/addons-linter/node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
+ "license": "MIT"
+ },
+ "node_modules/addons-linter/node_modules/@eslint/js": {
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
+ "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
+ "dev": true,
+ "license": "MIT",
"engines": {
- "node": ">=8"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "node_modules/addons-linter/node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
},
"funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
}
},
- "node_modules/anymatch": {
+ "node_modules/addons-linter/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/addons-linter/node_modules/eslint": {
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
+ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
+ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.1",
+ "@humanwhocodes/config-array": "^0.13.0",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/addons-linter/node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/addons-linter/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/addons-linter/node_modules/eslint/node_modules/ajv": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/addons-linter/node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/addons-linter/node_modules/eslint/node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/addons-linter/node_modules/eslint/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/addons-linter/node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/addons-linter/node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/addons-linter/node_modules/flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/addons-linter/node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/addons-linter/node_modules/js-yaml": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
+ "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/addons-linter/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/addons-linter/node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/addons-linter/node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/addons-linter/node_modules/semver": {
+ "version": "7.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/addons-linter/node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/addons-moz-compare": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/addons-moz-compare/-/addons-moz-compare-1.3.0.tgz",
+ "integrity": "sha512-/rXpQeaY0nOKhNx00pmZXdk5Mu+KhVlL3/pSBuAYwrxRrNiTvI/9xfQI8Lmm7DMMl+PDhtfAHY/0ibTpdeoQQQ==",
+ "dev": true,
+ "license": "MPL-2.0"
+ },
+ "node_modules/addons-scanner-utils": {
+ "version": "9.13.0",
+ "resolved": "https://registry.npmjs.org/addons-scanner-utils/-/addons-scanner-utils-9.13.0.tgz",
+ "integrity": "sha512-8OnHK/pbvgbCejGlnEYw+V3URSTVHLkMZmV270QtNh8N9pAgK10IaiJ9DcL0FsrufZ9HxRcR8/wkavh1FgK6Kg==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "@types/yauzl": "2.10.3",
+ "common-tags": "1.8.2",
+ "first-chunk-stream": "3.0.0",
+ "strip-bom-stream": "4.0.0",
+ "upath": "2.0.1",
+ "yauzl": "2.10.0"
+ },
+ "peerDependencies": {
+ "body-parser": "1.20.3",
+ "express": "4.21.2",
+ "node-fetch": "2.6.11",
+ "safe-compare": "1.1.4"
+ },
+ "peerDependenciesMeta": {
+ "body-parser": {
+ "optional": true
+ },
+ "express": {
+ "optional": true
+ },
+ "node-fetch": {
+ "optional": true
+ },
+ "safe-compare": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/adm-zip": {
+ "version": "0.5.18",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz",
+ "integrity": "sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-align": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
+ "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.1.0"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
@@ -3031,6 +3777,60 @@
"sprintf-js": "~1.0.2"
}
},
+ "node_modules/array-differ": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-4.0.0.tgz",
+ "integrity": "sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/array-union": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz",
+ "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/atomic-sleep": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
+ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/atomically": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.1.1.tgz",
+ "integrity": "sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "stubborn-fs": "^2.0.0",
+ "when-exit": "^2.1.4"
+ }
+ },
"node_modules/babel-jest": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
@@ -3170,11 +3970,173 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
+ "node_modules/bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/boxen": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz",
+ "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-align": "^3.0.1",
+ "camelcase": "^8.0.0",
+ "chalk": "^5.3.0",
+ "cli-boxes": "^3.0.0",
+ "string-width": "^7.2.0",
+ "type-fest": "^4.21.0",
+ "widest-line": "^5.0.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/boxen/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/boxen/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/boxen/node_modules/camelcase": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
+ "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/boxen/node_modules/chalk": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/boxen/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/boxen/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/boxen/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/boxen/node_modules/type-fest": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/boxen/node_modules/wrap-ansi": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
"node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "version": "1.1.16",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
+ "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -3233,12 +4195,38 @@
"node-int64": "^0.4.0"
}
},
- "node_modules/buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "dev": true
- },
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true
+ },
+ "node_modules/bundle-name": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
+ "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "run-applescript": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -3258,9 +4246,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001731",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz",
- "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==",
+ "version": "1.0.30001806",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz",
+ "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==",
"dev": true,
"funding": [
{
@@ -3275,7 +4263,8 @@
"type": "github",
"url": "https://github.com/sponsors/ai"
}
- ]
+ ],
+ "license": "CC-BY-4.0"
},
"node_modules/chalk": {
"version": "4.1.2",
@@ -3302,6 +4291,65 @@
"node": ">=10"
}
},
+ "node_modules/cheerio": {
+ "version": "1.0.0-rc.12",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
+ "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cheerio-select": "^2.1.0",
+ "dom-serializer": "^2.0.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1",
+ "htmlparser2": "^8.0.1",
+ "parse5": "^7.0.0",
+ "parse5-htmlparser2-tree-adapter": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/cheerio?sponsor=1"
+ }
+ },
+ "node_modules/cheerio-select": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
+ "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-select": "^5.1.0",
+ "css-what": "^6.1.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/chrome-launcher": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.2.0.tgz",
+ "integrity": "sha512-JbuGuBNss258bvGil7FT4HKdC3SC2K7UAEUqiPy3ACS3Yxo3hAW6bvFpCu2HsIJLgTqxgEX6BkujvzZfLpUD0Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/node": "*",
+ "escape-string-regexp": "^4.0.0",
+ "is-wsl": "^2.2.0",
+ "lighthouse-logger": "^2.0.1"
+ },
+ "bin": {
+ "print-chrome-path": "bin/print-chrome-path.cjs"
+ },
+ "engines": {
+ "node": ">=12.13.0"
+ }
+ },
"node_modules/ci-info": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
@@ -3323,6 +4371,19 @@
"integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
"dev": true
},
+ "node_modules/cli-boxes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
+ "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -3337,6 +4398,16 @@
"node": ">=12"
}
},
+ "node_modules/clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -3371,12 +4442,99 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+ "node_modules/columnify": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz",
+ "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "strip-ansi": "^6.0.1",
+ "wcwidth": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/commander": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
+ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || >=14"
+ }
+ },
+ "node_modules/common-tags": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
+ "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
+ "node_modules/concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "dev": true,
+ "engines": [
+ "node >= 0.8"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "node_modules/config-chain": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ini": "^1.3.4",
+ "proto-list": "~1.2.1"
+ }
+ },
+ "node_modules/config-chain/node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/configstore": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/configstore/-/configstore-7.1.0.tgz",
+ "integrity": "sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "atomically": "^2.0.3",
+ "dot-prop": "^9.0.0",
+ "graceful-fs": "^4.2.11",
+ "xdg-basedir": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@@ -3396,6 +4554,13 @@
"url": "https://opencollective.com/core-js"
}
},
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/create-jest": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
@@ -3431,6 +4596,36 @@
"node": ">= 8"
}
},
+ "node_modules/css-select": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
+ "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
+ "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
"node_modules/cssstyle": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
@@ -3459,6 +4654,13 @@
"node": ">=18"
}
},
+ "node_modules/debounce": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
+ "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
@@ -3476,6 +4678,19 @@
}
}
},
+ "node_modules/decamelize": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.1.tgz",
+ "integrity": "sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/decimal.js": {
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
@@ -3497,6 +4712,16 @@
}
}
},
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -3512,6 +4737,62 @@
"node": ">=0.10.0"
}
},
+ "node_modules/default-browser": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz",
+ "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bundle-name": "^4.1.0",
+ "default-browser-id": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/default-browser-id": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz",
+ "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/defaults": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clone": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/define-lazy-prop": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+ "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/detect-newline": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
@@ -3530,6 +4811,120 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/dom-serializer/node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/dot-prop": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz",
+ "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^4.18.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/dot-prop/node_modules/type-fest": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/electron-to-chromium": {
"version": "1.5.192",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.192.tgz",
@@ -3576,13 +4971,33 @@
"is-arrayish": "^0.2.1"
}
},
+ "node_modules/es6-error": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
+ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"engines": {
- "node": ">=6"
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-goat": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz",
+ "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/escape-string-regexp": {
@@ -3657,6 +5072,16 @@
}
}
},
+ "node_modules/eslint-plugin-no-unsanitized": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.1.4.tgz",
+ "integrity": "sha512-cjAoZoq3J+5KJuycYYOWrc0/OpZ7pl2Z3ypfFq4GtaAgheg+L7YGxUo2YS3avIvo/dYU5/zR2hXu3v81M9NxhQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "peerDependencies": {
+ "eslint": "^8 || ^9"
+ }
+ },
"node_modules/eslint-scope": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
@@ -3881,6 +5306,13 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
+ "node_modules/fast-json-patch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz",
+ "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -3893,6 +5325,43 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
+ "node_modules/fast-redact": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
+ "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz",
+ "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
"node_modules/fb-watchman": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
@@ -3902,6 +5371,16 @@
"bser": "2.1.1"
}
},
+ "node_modules/fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pend": "~1.2.0"
+ }
+ },
"node_modules/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@@ -3939,6 +5418,36 @@
"node": ">=8"
}
},
+ "node_modules/firefox-profile": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/firefox-profile/-/firefox-profile-4.7.0.tgz",
+ "integrity": "sha512-aGApEu5bfCNbA4PGUZiRJAIU6jKmghV2UVdklXAofnNtiDjqYw0czLS46W7IfFqVKgKhFB8Ao2YoNGHY4BoIMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "adm-zip": "~0.5.x",
+ "fs-extra": "^11.2.0",
+ "ini": "^4.1.3",
+ "minimist": "^1.2.8",
+ "xml2js": "^0.6.2"
+ },
+ "bin": {
+ "firefox-profile": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/first-chunk-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-3.0.0.tgz",
+ "integrity": "sha512-LNRvR4hr/S8cXXkIY5pTgVP7L3tq6LlYWcg9nWBuW7o1NMxKZo6oOVa/6GIekMGI0Iw7uC+HWimMe9u/VAeKqw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/flat-cache": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
@@ -3959,6 +5468,21 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/fs-extra": {
+ "version": "11.3.5",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz",
+ "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -3988,6 +5512,58 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/fx-runner": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/fx-runner/-/fx-runner-1.4.0.tgz",
+ "integrity": "sha512-rci1g6U0rdTg6bAaBboP7XdRu01dzTAaKXxFf+PUqGuCv6Xu7o8NZdY1D5MvKGIjb6EdS1g3VlXOgksir1uGkg==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "commander": "2.9.0",
+ "shell-quote": "1.7.3",
+ "spawn-sync": "1.0.15",
+ "when": "3.7.7",
+ "which": "1.2.4",
+ "winreg": "0.0.12"
+ },
+ "bin": {
+ "fx-runner": "bin/fx-runner"
+ }
+ },
+ "node_modules/fx-runner/node_modules/commander": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
+ "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-readlink": ">= 1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6.x"
+ }
+ },
+ "node_modules/fx-runner/node_modules/isexe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-1.1.2.tgz",
+ "integrity": "sha512-d2eJzK691yZwPHcv1LbeAOa91yMJ9QmfTgSO1oXB65ezVhXQsxBac2vEB4bMVms9cGzaA99n6V2viHMq82VLDw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fx-runner/node_modules/which": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.2.4.tgz",
+ "integrity": "sha512-zDRAqDSBudazdfM9zpiI30Fu9ve47htYXcGi3ln0wfKu2a7SmrT6F3VDoYONu//48V8Vz4TdCRNPjtvyRO3yBA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-absolute": "^0.1.7",
+ "isexe": "^1.1.1"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -4006,6 +5582,19 @@
"node": "6.* || 8.* || >= 10.*"
}
},
+ "node_modules/get-east-asian-width": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
+ "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/get-package-type": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
@@ -4060,12 +5649,95 @@
"node": ">=10.13.0"
}
},
+ "node_modules/glob-to-regexp": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/global-directory": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz",
+ "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ini": "4.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/global-directory/node_modules/ini": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz",
+ "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globals/node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true
},
+ "node_modules/graceful-readlink": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
+ "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/growly": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
+ "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -4106,6 +5778,39 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
+ "node_modules/htmlparser2": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
+ "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
+ "dev": true,
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1",
+ "entities": "^4.4.0"
+ }
+ },
+ "node_modules/htmlparser2/node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/http-proxy-agent": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
@@ -4165,6 +5870,26 @@
"node": ">= 4"
}
},
+ "node_modules/image-size": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz",
+ "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "image-size": "bin/image-size.js"
+ },
+ "engines": {
+ "node": ">=16.x"
+ }
+ },
+ "node_modules/immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/import-fresh": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@@ -4235,6 +5960,29 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
+ "node_modules/ini": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz",
+ "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/is-absolute": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz",
+ "integrity": "sha512-Xi9/ZSn4NFapG8RP98iNPMOeaV3mXPisxKxzKtHVqr3g56j/fBn+yZmnxSVAA8lmZbl2J9b/a4kJvfU3hqQYgA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-relative": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -4256,43 +6004,140 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"engines": {
- "node": ">=0.10.0"
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-generator-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
+ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-in-ci": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-1.0.0.tgz",
+ "integrity": "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "is-in-ci": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-inside-container": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
+ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-docker": "^3.0.0"
+ },
+ "bin": {
+ "is-inside-container": "cli.js"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/is-fullwidth-code-point": {
+ "node_modules/is-inside-container/node_modules/is-docker": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
"dev": true,
+ "license": "MIT",
+ "bin": {
+ "is-docker": "cli.js"
+ },
"engines": {
- "node": ">=8"
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/is-generator-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
- "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+ "node_modules/is-installed-globally": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.0.tgz",
+ "integrity": "sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==",
"dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "global-directory": "^4.0.1",
+ "is-path-inside": "^4.0.0"
+ },
"engines": {
- "node": ">=6"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "node_modules/is-npm": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.1.0.tgz",
+ "integrity": "sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==",
"dev": true,
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
+ "license": "MIT",
"engines": {
- "node": ">=0.10.0"
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-number": {
@@ -4304,6 +6149,19 @@
"node": ">=0.12.0"
}
},
+ "node_modules/is-path-inside": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz",
+ "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
@@ -4311,6 +6169,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/is-relative": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz",
+ "integrity": "sha512-wBOr+rNM4gkAZqoLRJI4myw5WzzIdQosFAAbnvfXP5z1LyzgAI3ivOKehC5KfqlQJZoihVhirgtCBj378Eg8GA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -4323,6 +6190,33 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/is-utf8": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-docker": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -5179,6 +7073,16 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
+ "node_modules/jose": {
+ "version": "5.9.6",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.6.tgz",
+ "integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -5186,9 +7090,9 @@
"dev": true
},
"node_modules/js-yaml": {
- "version": "3.14.2",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
- "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz",
+ "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5257,6 +7161,16 @@
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"dev": true
},
+ "node_modules/json-merge-patch": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-merge-patch/-/json-merge-patch-1.0.2.tgz",
+ "integrity": "sha512-M6Vp2GN9L7cfuMXiWOmHj9bEFbeC250iVtcKQbqVgEsDVYnIsrNsbU+h/Y/PkbBQCtEa4Bez+Ebv0zfbC8ObLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ }
+ },
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@@ -5287,6 +7201,32 @@
"node": ">=6"
}
},
+ "node_modules/jsonfile": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jszip": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+ "dev": true,
+ "license": "(MIT OR GPL-3.0-or-later)",
+ "dependencies": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "setimmediate": "^1.0.5"
+ }
+ },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -5305,6 +7245,35 @@
"node": ">=6"
}
},
+ "node_modules/ky": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/ky/-/ky-1.14.3.tgz",
+ "integrity": "sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/ky?sponsor=1"
+ }
+ },
+ "node_modules/latest-version": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-9.0.0.tgz",
+ "integrity": "sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "package-json": "^10.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -5327,6 +7296,27 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "node_modules/lighthouse-logger": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.2.tgz",
+ "integrity": "sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "debug": "^4.4.1",
+ "marky": "^1.2.2"
+ }
+ },
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -5393,6 +7383,13 @@
"node": ">=10"
}
},
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/makeerror": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
@@ -5402,6 +7399,13 @@
"tmpl": "1.0.5"
}
},
+ "node_modules/marky": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz",
+ "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -5443,24 +7447,91 @@
"node": "*"
}
},
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
+ "node_modules/multimatch": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-6.0.0.tgz",
+ "integrity": "sha512-I7tSVxHGPlmPN/enE3mS1aOSo6bWBfls+3HmuEeCUBCE7gWnm3cBXCBkpurzFjVRwC6Kld8lLaZ1Iv5vOcjvcQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/minimatch": "^3.0.5",
+ "array-differ": "^4.0.0",
+ "array-union": "^3.0.1",
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
+ "node_modules/node-forge": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
+ "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==",
+ "dev": true,
+ "license": "(BSD-3-Clause OR GPL-2.0)",
+ "engines": {
+ "node": ">= 6.13.0"
+ }
+ },
"node_modules/node-int64": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
"integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
"dev": true
},
+ "node_modules/node-notifier": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-10.0.1.tgz",
+ "integrity": "sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "growly": "^1.3.0",
+ "is-wsl": "^2.2.0",
+ "semver": "^7.3.5",
+ "shellwords": "^0.1.1",
+ "uuid": "^8.3.2",
+ "which": "^2.0.2"
+ }
+ },
+ "node_modules/node-notifier/node_modules/semver": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
+ "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/node-releases": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
@@ -5488,6 +7559,19 @@
"node": ">=8"
}
},
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
"node_modules/nwsapi": {
"version": "2.2.23",
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz",
@@ -5495,6 +7579,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/on-exit-leak-free": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
+ "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -5519,6 +7613,25 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/open": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz",
+ "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "default-browser": "^5.2.1",
+ "define-lazy-prop": "^3.0.0",
+ "is-inside-container": "^1.0.0",
+ "wsl-utils": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -5536,6 +7649,15 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/os-shim": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz",
+ "integrity": "sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -5587,6 +7709,45 @@
"node": ">=6"
}
},
+ "node_modules/package-json": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/package-json/-/package-json-10.0.1.tgz",
+ "integrity": "sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ky": "^1.2.0",
+ "registry-auth-token": "^5.0.2",
+ "registry-url": "^6.0.1",
+ "semver": "^7.6.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-json/node_modules/semver": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
+ "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "dev": true,
+ "license": "(MIT AND Zlib)"
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -5630,6 +7791,20 @@
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
+ "node_modules/parse5-htmlparser2-tree-adapter": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz",
+ "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "domhandler": "^5.0.3",
+ "parse5": "^7.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -5663,6 +7838,13 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -5675,13 +7857,53 @@
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pino": {
+ "version": "9.9.5",
+ "resolved": "https://registry.npmjs.org/pino/-/pino-9.9.5.tgz",
+ "integrity": "sha512-d1s98p8/4TfYhsJ09r/Azt30aYELRi6NNnZtEbqFw6BoGsdPVf5lKNK3kUwH8BmJJfpTLNuicjUQjaMbd93dVg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "atomic-sleep": "^1.0.0",
+ "fast-redact": "^3.1.1",
+ "on-exit-leak-free": "^2.1.0",
+ "pino-abstract-transport": "^2.0.0",
+ "pino-std-serializers": "^7.0.0",
+ "process-warning": "^5.0.0",
+ "quick-format-unescaped": "^4.0.3",
+ "real-require": "^0.2.0",
+ "safe-stable-stringify": "^2.3.1",
+ "sonic-boom": "^4.0.1",
+ "thread-stream": "^3.0.0"
+ },
+ "bin": {
+ "pino": "bin.js"
+ }
+ },
+ "node_modules/pino-abstract-transport": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz",
+ "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "split2": "^4.0.0"
}
},
+ "node_modules/pino-std-serializers": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz",
+ "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/pirates": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
@@ -5785,6 +8007,43 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/process-warning": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
+ "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/promise-toolbox": {
+ "version": "0.21.0",
+ "resolved": "https://registry.npmjs.org/promise-toolbox/-/promise-toolbox-0.21.0.tgz",
+ "integrity": "sha512-NV8aTmpwrZv+Iys54sSFOBx3tuVaOBvvrft5PNppnxy9xpU/akHbaWIril22AB22zaPgrgwKdD0KsrM0ptUtpg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "make-error": "^1.3.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/prompts": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
@@ -5798,6 +8057,13 @@
"node": ">= 6"
}
},
+ "node_modules/proto-list": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -5807,6 +8073,22 @@
"node": ">=6"
}
},
+ "node_modules/pupa": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.3.0.tgz",
+ "integrity": "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escape-goat": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/pure-rand": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
@@ -5823,12 +8105,99 @@
}
]
},
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/quick-format-unescaped": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
+ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/rc/node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/rc/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/react-is": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
"dev": true
},
+ "node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/real-require": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
+ "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12.13.0"
+ }
+ },
"node_modules/regenerate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@@ -5864,6 +8233,35 @@
"node": ">=4"
}
},
+ "node_modules/registry-auth-token": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.1.tgz",
+ "integrity": "sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@pnpm/npm-conf": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/registry-url": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz",
+ "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "rc": "1.2.8"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/regjsgen": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
@@ -5903,6 +8301,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@@ -5953,6 +8361,34 @@
"node": ">=10"
}
},
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/rrweb-cssom": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
@@ -5960,6 +8396,60 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/run-applescript": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
+ "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/safe-stable-stringify": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -5967,6 +8457,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/sax": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
+ "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=11.0.0"
+ }
+ },
"node_modules/saxes": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
@@ -5989,6 +8489,13 @@
"semver": "bin/semver.js"
}
},
+ "node_modules/setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -6010,6 +8517,20 @@
"node": ">=8"
}
},
+ "node_modules/shell-quote": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz",
+ "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/shellwords": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
+ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -6031,6 +8552,16 @@
"node": ">=8"
}
},
+ "node_modules/sonic-boom": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz",
+ "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "atomic-sleep": "^1.0.0"
+ }
+ },
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -6050,6 +8581,41 @@
"source-map": "^0.6.0"
}
},
+ "node_modules/spawn-sync": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz",
+ "integrity": "sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "concat-stream": "^1.4.7",
+ "os-shim": "^0.1.2"
+ }
+ },
+ "node_modules/split": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
+ "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "through": "2"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -6077,6 +8643,16 @@
"node": ">=8"
}
},
+ "node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
"node_modules/string-length": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
@@ -6125,6 +8701,33 @@
"node": ">=8"
}
},
+ "node_modules/strip-bom-buf": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-2.0.0.tgz",
+ "integrity": "sha512-gLFNHucd6gzb8jMsl5QmZ3QgnUJmp7qn4uUSHNwEXumAp7YizoGYw19ZUVfuq4aBOQUtyn2k8X/CwzWB73W2lQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-utf8": "^0.2.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom-stream": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-4.0.0.tgz",
+ "integrity": "sha512-0ApK3iAkHv6WbgLICw/J4nhwHeDZsBxIIsOD+gHgZICL6SeJ0S9f/WZqemka9cjkTyMN5geId6e8U5WGFAn3cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "first-chunk-stream": "^3.0.0",
+ "strip-bom-buf": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-final-newline": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
@@ -6146,6 +8749,23 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/stubborn-fs": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-2.0.0.tgz",
+ "integrity": "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "stubborn-utils": "^1.0.1"
+ }
+ },
+ "node_modules/stubborn-utils": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/stubborn-utils/-/stubborn-utils-1.0.2.tgz",
+ "integrity": "sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -6191,6 +8811,30 @@
"node": ">=8"
}
},
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/thread-stream": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.2.0.tgz",
+ "integrity": "sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "real-require": "^0.2.0"
+ }
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/tldts": {
"version": "6.1.86",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
@@ -6211,6 +8855,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/tmp": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
+ "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
"node_modules/tmpl": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@@ -6288,6 +8942,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/undici-types": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
@@ -6334,6 +8995,27 @@
"node": ">=4"
}
},
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/upath": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz",
+ "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4",
+ "yarn": "*"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
@@ -6364,6 +9046,57 @@
"browserslist": ">= 4.21.0"
}
},
+ "node_modules/update-notifier": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-7.3.1.tgz",
+ "integrity": "sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boxen": "^8.0.1",
+ "chalk": "^5.3.0",
+ "configstore": "^7.0.0",
+ "is-in-ci": "^1.0.0",
+ "is-installed-globally": "^1.0.0",
+ "is-npm": "^6.0.0",
+ "latest-version": "^9.0.0",
+ "pupa": "^3.1.0",
+ "semver": "^7.6.3",
+ "xdg-basedir": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/yeoman/update-notifier?sponsor=1"
+ }
+ },
+ "node_modules/update-notifier/node_modules/chalk": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+ "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/update-notifier/node_modules/semver": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
+ "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -6373,6 +9106,24 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/v8-to-istanbul": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
@@ -6409,6 +9160,176 @@
"makeerror": "1.0.12"
}
},
+ "node_modules/watchpack": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
+ "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.1.2"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/wcwidth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "defaults": "^1.0.3"
+ }
+ },
+ "node_modules/web-ext": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-8.10.0.tgz",
+ "integrity": "sha512-RM15PF8/xBBTe3pkgTvTkPaRoJbwE4HZcKy9NeD0lHIdir23oy9kd2oC4LuOqhrtarFvhTK4HlJ6NPYXt8Isxw==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "@babel/runtime": "7.28.4",
+ "@devicefarmer/adbkit": "3.3.8",
+ "addons-linter": "7.20.0",
+ "camelcase": "8.0.0",
+ "chrome-launcher": "1.2.0",
+ "debounce": "1.2.1",
+ "decamelize": "6.0.1",
+ "es6-error": "4.1.1",
+ "firefox-profile": "4.7.0",
+ "fx-runner": "1.4.0",
+ "https-proxy-agent": "^7.0.0",
+ "jose": "5.9.6",
+ "jszip": "3.10.1",
+ "multimatch": "6.0.0",
+ "node-notifier": "10.0.1",
+ "open": "10.2.0",
+ "parse-json": "7.1.1",
+ "pino": "9.9.5",
+ "promise-toolbox": "0.21.0",
+ "source-map-support": "0.5.21",
+ "strip-bom": "5.0.0",
+ "strip-json-comments": "5.0.3",
+ "tmp": "0.2.5",
+ "update-notifier": "7.3.1",
+ "watchpack": "2.4.4",
+ "yargs": "17.7.2",
+ "zip-dir": "2.0.0"
+ },
+ "bin": {
+ "web-ext": "bin/web-ext.js"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ }
+ },
+ "node_modules/web-ext/node_modules/camelcase": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
+ "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/web-ext/node_modules/json-parse-even-better-errors": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz",
+ "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/web-ext/node_modules/lines-and-columns": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz",
+ "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/web-ext/node_modules/parse-json": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.1.1.tgz",
+ "integrity": "sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.21.4",
+ "error-ex": "^1.3.2",
+ "json-parse-even-better-errors": "^3.0.0",
+ "lines-and-columns": "^2.0.3",
+ "type-fest": "^3.8.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/web-ext/node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/web-ext/node_modules/strip-bom": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-5.0.0.tgz",
+ "integrity": "sha512-p+byADHF7SzEcVnLvc/r3uognM1hUhObuHXxJcgLCfD194XAkaLbjq3Wzb0N5G2tgIjH0dgT708Z51QxMeu60A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/web-ext/node_modules/strip-json-comments": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz",
+ "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/web-ext/node_modules/type-fest": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz",
+ "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
@@ -6457,6 +9378,20 @@
"node": ">=18"
}
},
+ "node_modules/when": {
+ "version": "3.7.7",
+ "resolved": "https://registry.npmjs.org/when/-/when-3.7.7.tgz",
+ "integrity": "sha512-9lFZp/KHoqH6bPKjbWqa+3Dg/K/r2v0X/3/G2x4DBGchVS2QX2VXL3cZV994WQVnTM1/PD71Az25nAzryEUugw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/when-exit": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.5.tgz",
+ "integrity": "sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -6472,6 +9407,83 @@
"node": ">= 8"
}
},
+ "node_modules/widest-line": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz",
+ "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/widest-line/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/widest-line/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/widest-line/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/widest-line/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/winreg": {
+ "version": "0.0.12",
+ "resolved": "https://registry.npmjs.org/winreg/-/winreg-0.0.12.tgz",
+ "integrity": "sha512-typ/+JRmi7RqP1NanzFULK36vczznSNN8kWVA9vIqXyv8GhghUlwhGp1Xj3Nms1FsPcNnsQrJOR10N58/nQ9hQ==",
+ "dev": true,
+ "license": "BSD"
+ },
"node_modules/word-wrap": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
@@ -6518,9 +9530,9 @@
}
},
"node_modules/ws": {
- "version": "8.20.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
- "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
+ "version": "8.21.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz",
+ "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6539,6 +9551,51 @@
}
}
},
+ "node_modules/wsl-utils": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
+ "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-wsl": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/wsl-utils/node_modules/is-wsl": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
+ "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-inside-container": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xdg-basedir": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz",
+ "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/xml-name-validator": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
@@ -6549,6 +9606,30 @@
"node": ">=18"
}
},
+ "node_modules/xml2js": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
+ "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
"node_modules/xmlchars": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
@@ -6598,6 +9679,17 @@
"node": ">=12"
}
},
+ "node_modules/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
@@ -6609,6 +9701,17 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zip-dir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/zip-dir/-/zip-dir-2.0.0.tgz",
+ "integrity": "sha512-uhlsJZWz26FLYXOD6WVuq+fIcZ3aBPGo/cFdiLlv3KNwpa52IF3ISV8fLhQLiqVu5No3VhlqlgthN6gehil1Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async": "^3.2.0",
+ "jszip": "^3.2.2"
+ }
}
}
}
diff --git a/package.json b/package.json
index 0d6091f..5e2cb2a 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,11 @@
{
"name": "youtube-local-history",
- "version": "2.4.0",
+ "version": "5.0.0",
"description": "Store YouTube video timestamps using secure browser storage for larger storage capacity without expiry.",
"main": "src/content.js",
"scripts": {
+ "start": "web-ext run",
+ "start:debug": "web-ext run --browser-console",
"test": "jest",
"test:unit": "jest --testPathPattern=unit",
"test:integration": "jest --testPathPattern=integration",
@@ -12,6 +14,7 @@
"test:coverage": "jest --coverage",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
+ "prepare:firefox": "node prepare-firefox-build.js",
"build": "./build.sh",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix"
@@ -35,7 +38,8 @@
"babel-jest": "^29.7.0",
"eslint": "^9.15.0",
"jest": "^29.7.0",
- "jest-environment-jsdom": "^30.3.0"
+ "jest-environment-jsdom": "^30.3.0",
+ "web-ext": "^8.3.0"
},
"browserslist": [
"last 2 versions",
diff --git a/playwright.config.js b/playwright.config.js
index a8b7a4f..d541cc7 100644
--- a/playwright.config.js
+++ b/playwright.config.js
@@ -1,6 +1,9 @@
// @ts-check
const { defineConfig, devices } = require('@playwright/test');
const path = require('path');
+const fs = require('fs');
+
+const youtubeStorageState = path.join(__dirname, 'yt-storage.json');
/**
* @see https://playwright.dev/docs/test-configuration
@@ -46,8 +49,8 @@ module.exports = defineConfig({
use: {
...devices['Desktop Chrome'],
headless: false,
- // Reuse a profile where YouTube cookies/consent have been accepted
- storageState: path.join(__dirname, 'yt-storage.json'),
+ // Optional local-only profile state for YouTube cookies/consent.
+ ...(fs.existsSync(youtubeStorageState) ? { storageState: youtubeStorageState } : {}),
args: (() => {
const extensionPath = path.join(__dirname, 'build', 'chrome');
return [
diff --git a/prepare-firefox-build.js b/prepare-firefox-build.js
new file mode 100644
index 0000000..a930371
--- /dev/null
+++ b/prepare-firefox-build.js
@@ -0,0 +1,23 @@
+const fs = require('fs');
+const path = require('path');
+
+const root = __dirname;
+const source = path.join(root, 'src');
+const target = path.join(root, 'build', 'firefox');
+
+fs.rmSync(target, { recursive: true, force: true });
+fs.mkdirSync(target, { recursive: true });
+
+for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
+ if (entry.name === 'manifest.chrome.json' || entry.name === 'manifest.firefox.json') continue;
+ const from = path.join(source, entry.name);
+ const to = path.join(target, entry.name);
+ fs.cpSync(from, to, { recursive: true });
+}
+
+fs.copyFileSync(
+ path.join(source, 'manifest.firefox.json'),
+ path.join(target, 'manifest.json')
+);
+
+console.log('Prepared Firefox extension in build/firefox');
diff --git a/src/_locales/bg/feed.json b/src/_locales/bg/feed.json
new file mode 100644
index 0000000..81c6838
--- /dev/null
+++ b/src/_locales/bg/feed.json
@@ -0,0 +1,506 @@
+{
+ "feed_menu": {
+ "message": "????",
+ "description": "Menu button label"
+ },
+ "feed_back_to_feed": {
+ "message": "????? ??? ???? ?????",
+ "description": "Tooltip for returning to the feed"
+ },
+ "feed_search_youtube": {
+ "message": "??????? ? YouTube",
+ "description": "Search field placeholder"
+ },
+ "feed_refresh": {
+ "message": "??????",
+ "description": "Refresh button label"
+ },
+ "feed_create": {
+ "message": "??????",
+ "description": "Create button label"
+ },
+ "feed_save": {
+ "message": "??????",
+ "description": "Save button label"
+ },
+ "feed_clear_all": {
+ "message": "??????? ??????",
+ "description": "Clear all button label"
+ },
+ "feed_options": {
+ "message": "?????",
+ "description": "Options menu label"
+ },
+ "feed_unwatched_only": {
+ "message": "???? ?????????",
+ "description": "Filter label for only unwatched videos"
+ },
+ "feed_hide_members": {
+ "message": "Hide member videos",
+ "description": "Filter label for hiding members-only or members-first videos"
+ },
+ "feed_nav_home": {
+ "message": "??????",
+ "description": "Home navigation label"
+ },
+ "feed_nav_home_title": {
+ "message": "??????????????? ??????? ?? ????????? ??",
+ "description": "Home navigation tooltip"
+ },
+ "feed_nav_subscriptions": {
+ "message": "??????????",
+ "description": "Subscriptions navigation label"
+ },
+ "feed_nav_subscriptions_title": {
+ "message": "???-???? ????? ?? ??????????? ??????",
+ "description": "Subscriptions navigation tooltip"
+ },
+ "feed_nav_shorts": {
+ "message": "Shorts",
+ "description": "Shorts navigation label"
+ },
+ "feed_nav_playlists": {
+ "message": "?????????",
+ "description": "Playlists navigation label"
+ },
+ "feed_nav_playlists_title": {
+ "message": "??? ???????? ?????????",
+ "description": "Playlists navigation tooltip"
+ },
+ "feed_nav_history": {
+ "message": "???????",
+ "description": "History navigation label"
+ },
+ "feed_nav_history_title": {
+ "message": "??? ????????? ??????? ?? ???????",
+ "description": "History navigation tooltip"
+ },
+ "feed_nav_channels": {
+ "message": "??????",
+ "description": "Channels navigation label"
+ },
+ "feed_nav_channels_title": {
+ "message": "?????????? ??????? ????????? ??????",
+ "description": "Channels navigation tooltip"
+ },
+ "feed_nav_analytics": {
+ "message": "???????",
+ "description": "Analytics navigation label"
+ },
+ "feed_nav_analytics_title": {
+ "message": "??? ??????? ?? ?????????",
+ "description": "Analytics navigation tooltip"
+ },
+ "feed_nav_settings": {
+ "message": "?????????",
+ "description": "Settings navigation label"
+ },
+ "feed_nav_settings_title": {
+ "message": "?????? ??????????? ?? ????????????",
+ "description": "Settings navigation tooltip"
+ },
+ "feed_your_feed": {
+ "message": "????? ?????",
+ "description": "Main feed heading"
+ },
+ "feed_search_options": {
+ "message": "????? ?? ???????",
+ "description": "Search options aria label"
+ },
+ "feed_filters": {
+ "message": "??????",
+ "description": "Filters button label"
+ },
+ "feed_upload_date": {
+ "message": "???? ?? ???????",
+ "description": "Upload date filter label"
+ },
+ "feed_today": {
+ "message": "????",
+ "description": "Today filter label"
+ },
+ "feed_this_week": {
+ "message": "???? ???????",
+ "description": "This week filter label"
+ },
+ "feed_this_month": {
+ "message": "???? ?????",
+ "description": "This month filter label"
+ },
+ "feed_this_year": {
+ "message": "???? ??????",
+ "description": "This year filter label"
+ },
+ "feed_duration": {
+ "message": "???????????????",
+ "description": "Duration filter label"
+ },
+ "feed_under_4_minutes": {
+ "message": "??? 4 ??????",
+ "description": "Under 4 minutes filter label"
+ },
+ "feed_4_to_20_minutes": {
+ "message": "4-20 ??????",
+ "description": "4 to 20 minutes filter label"
+ },
+ "feed_over_20_minutes": {
+ "message": "??? 20 ??????",
+ "description": "Over 20 minutes filter label"
+ },
+ "feed_watch_status": {
+ "message": "?????? ?? ???????",
+ "description": "Watch status filter label"
+ },
+ "feed_unwatched": {
+ "message": "?????????",
+ "description": "Unwatched filter label"
+ },
+ "feed_in_progress": {
+ "message": "? ??????",
+ "description": "In progress filter label"
+ },
+ "feed_watched": {
+ "message": "???????",
+ "description": "Watched filter label"
+ },
+ "feed_sort_search_results": {
+ "message": "???????? ???????????",
+ "description": "Sort search results aria label"
+ },
+ "feed_sort": {
+ "message": "?????????",
+ "description": "Sort filter label"
+ },
+ "feed_newest": {
+ "message": "???-????",
+ "description": "Newest sort label"
+ },
+ "feed_oldest": {
+ "message": "???-?????",
+ "description": "Oldest sort label"
+ },
+ "feed_search_results_for": {
+ "message": "????????? ??",
+ "description": "Search results heading prefix"
+ },
+ "feed_back_to_results": {
+ "message": "????? ??? ???????????",
+ "description": "Back to results button label"
+ },
+ "feed_no_channel_videos": {
+ "message": "??? ??? ???? ???????? ????? ?? ???? ?????.",
+ "description": "Empty state for channel videos"
+ },
+ "feed_channels": {
+ "message": "??????",
+ "description": "Channels label"
+ },
+ "feed_no_local_channels": {
+ "message": "??? ???? ??????? ????????? ??????. ????? ? ???????????? ? ????????? ?????????.",
+ "description": "Empty state for local channels"
+ },
+ "feed_playlists": {
+ "message": "?????????",
+ "description": "Playlists label"
+ },
+ "feed_no_saved_playlists": {
+ "message": "??? ???? ???????? ?????????.",
+ "description": "Empty state for saved playlists"
+ },
+ "feed_history": {
+ "message": "???????",
+ "description": "History label"
+ },
+ "feed_no_history_matches": {
+ "message": "???? ???????, ????? ?? ??????? ? ???? ??????.",
+ "description": "Empty state for filtered history"
+ },
+ "feed_load_more": {
+ "message": "?????? ???",
+ "description": "Load more button label"
+ },
+ "feed_analytics": {
+ "message": "???????",
+ "description": "Analytics label"
+ },
+ "feed_analytics_subtitle": {
+ "message": "?????? ??????????, ???????? ???????",
+ "description": "Analytics heading subtitle"
+ },
+ "feed_watch_activity_7_days": {
+ "message": "????????? ?? ??????? (?????????? 7 ???)",
+ "description": "Watch activity chart title"
+ },
+ "feed_watch_time_by_hour": {
+ "message": "????? ?? ??????? ?? ???",
+ "description": "Watch time by hour chart title"
+ },
+ "feed_how_much_finish": {
+ "message": "????? ?????????",
+ "description": "Completion analytics title"
+ },
+ "feed_top_channels": {
+ "message": "??? ??????",
+ "description": "Top channels analytics title"
+ },
+ "feed_continue_watching": {
+ "message": "???????? ?????????",
+ "description": "Continue watching analytics title"
+ },
+ "feed_settings": {
+ "message": "?????????",
+ "description": "Settings label"
+ },
+ "feed_settings_subtitle": {
+ "message": "?????? ?????? ??????? ? ???? ???????.",
+ "description": "Settings subtitle"
+ },
+ "feed_settings_display": {
+ "message": "??????",
+ "description": "Display settings section title"
+ },
+ "feed_settings_theme": {
+ "message": "????",
+ "description": "Theme setting label"
+ },
+ "feed_system_default": {
+ "message": "???????? ?????????",
+ "description": "System default theme label"
+ },
+ "feed_dark": {
+ "message": "?????",
+ "description": "Dark theme label"
+ },
+ "feed_light": {
+ "message": "??????",
+ "description": "Light theme label"
+ },
+ "feed_accent_color": {
+ "message": "???????? ????",
+ "description": "Accent color setting label"
+ },
+ "feed_blue": {
+ "message": "?????",
+ "description": "Blue color label"
+ },
+ "feed_red": {
+ "message": "???????",
+ "description": "Red color label"
+ },
+ "feed_green": {
+ "message": "??????",
+ "description": "Green color label"
+ },
+ "feed_purple": {
+ "message": "??????",
+ "description": "Purple color label"
+ },
+ "feed_orange": {
+ "message": "????????",
+ "description": "Orange color label"
+ },
+ "feed_default_tab": {
+ "message": "?????? ?? ????????????",
+ "description": "Default tab setting label"
+ },
+ "feed_last_used_tab": {
+ "message": "???????? ????????? ??????",
+ "description": "Last used tab option label"
+ },
+ "feed_history_and_feed": {
+ "message": "??????? ? ?????",
+ "description": "History and feed settings section title"
+ },
+ "feed_check_new_videos": {
+ "message": "???????? ?? ???? ?????",
+ "description": "Check for new videos setting label"
+ },
+ "feed_every_15_minutes": {
+ "message": "?? ????? 15 ??????",
+ "description": "Every 15 minutes interval label"
+ },
+ "feed_every_30_minutes": {
+ "message": "?? ????? 30 ??????",
+ "description": "Every 30 minutes interval label"
+ },
+ "feed_every_hour": {
+ "message": "????? ???",
+ "description": "Every hour interval label"
+ },
+ "feed_every_3_hours": {
+ "message": "?? ????? 3 ????",
+ "description": "Every 3 hours interval label"
+ },
+ "feed_every_6_hours": {
+ "message": "?? ????? 6 ????",
+ "description": "Every 6 hours interval label"
+ },
+ "feed_every_12_hours": {
+ "message": "?? ????? 12 ????",
+ "description": "Every 12 hours interval label"
+ },
+ "feed_once_a_day": {
+ "message": "?????? ?? ???",
+ "description": "Once a day interval label"
+ },
+ "feed_keep_watch_history": {
+ "message": "???? ?????????",
+ "description": "Keep watch history setting label"
+ },
+ "feed_30_days": {
+ "message": "30 ???",
+ "description": "30 days option label"
+ },
+ "feed_90_days": {
+ "message": "90 ???",
+ "description": "90 days option label"
+ },
+ "feed_180_days": {
+ "message": "180 ???",
+ "description": "180 days option label"
+ },
+ "feed_forever": {
+ "message": "????????",
+ "description": "Forever option label"
+ },
+ "feed_data": {
+ "message": "?????",
+ "description": "Data settings section title"
+ },
+ "feed_backup_restore": {
+ "message": "????? ? ??????????????",
+ "description": "Backup and restore title"
+ },
+ "feed_backup_restore_help": {
+ "message": "??????? ??? ?????? JSON ???? ? ???????, ??????????, ????????? ? ?????????.",
+ "description": "Backup and restore help text"
+ },
+ "feed_backup": {
+ "message": "?????",
+ "description": "Backup button label"
+ },
+ "feed_restore": {
+ "message": "??????????",
+ "description": "Restore button label"
+ },
+ "feed_import_from_youtube": {
+ "message": "?????? ?? YouTube",
+ "description": "Import from YouTube title"
+ },
+ "feed_import_from_youtube_help": {
+ "message": "????????? watch-history.html ?? ????????? ? subscriptions.csv ?? ??????????? ?????? ?? Google Takeout.",
+ "description": "Import from YouTube help text"
+ },
+ "feed_reset_all_data": {
+ "message": "??????? ?????? ?????",
+ "description": "Reset all data title"
+ },
+ "feed_reset_all_data_help": {
+ "message": "?????? ??????, ??????????? ??????????, ????????? ? ??????.",
+ "description": "Reset all data help text"
+ },
+ "feed_reset_all": {
+ "message": "??????? ??????",
+ "description": "Reset all button label"
+ },
+ "feed_add_to_playlist": {
+ "message": "?????? ??? ????????",
+ "description": "Add to playlist dialog title"
+ },
+ "feed_close": {
+ "message": "???????",
+ "description": "Close button aria label"
+ },
+ "feed_create_new_playlist": {
+ "message": "?????? ??? ????????",
+ "description": "Create new playlist placeholder"
+ },
+ "feed_video_options": {
+ "message": "????? ?? ???????",
+ "description": "feed video options"
+ },
+ "feed_more_from_channel": {
+ "message": "??? ?? ???? ?????",
+ "description": "feed more from channel"
+ },
+ "feed_less_from_channel": {
+ "message": "??-????? ?? ???? ?????",
+ "description": "feed less from channel"
+ },
+ "feed_hide_this_video": {
+ "message": "????? ???? ?????",
+ "description": "feed hide this video"
+ },
+ "feed_unsubscribe": {
+ "message": "?????? ??",
+ "description": "feed unsubscribe"
+ },
+ "feed_not_subscribed": {
+ "message": "?? ?? ????????",
+ "description": "feed not subscribed"
+ },
+ "feed_unsubscribed": {
+ "message": "????????",
+ "description": "feed unsubscribed"
+ },
+ "feed_save_to_watch_later": {
+ "message": "?????? ?? ??-?????",
+ "description": "feed save to watch later"
+ },
+ "feed_saved_to_watch_later": {
+ "message": "???????? ?? ??-?????",
+ "description": "feed saved to watch later"
+ },
+ "feed_added_to_playlist": {
+ "message": "???????? ??? ????????",
+ "description": "feed added to playlist"
+ },
+ "feed_set_as_playlist_cover": {
+ "message": "????? ???? ?????? ?? ?????????",
+ "description": "feed set as playlist cover"
+ },
+ "feed_cover_updated": {
+ "message": "???????? ? ????????",
+ "description": "feed cover updated"
+ },
+ "feed_remove_from_playlist": {
+ "message": "???????? ?? ???? ????????",
+ "description": "feed remove from playlist"
+ },
+ "feed_this_channel": {
+ "message": "???? ?????",
+ "description": "feed this channel"
+ },
+ "feed_channel": {
+ "message": "?????",
+ "description": "feed channel"
+ },
+ "feed_recommend_more_from_status": {
+ "message": "?? ???????????? ?????? ?? $1.",
+ "description": "feed recommend more from status"
+ },
+ "feed_recommend_more_status": {
+ "message": "?? ???????????? ??????",
+ "description": "feed recommend more status"
+ },
+ "feed_unsubscribed_from_status": {
+ "message": "?????? ?? ?? $1.",
+ "description": "feed unsubscribed from status"
+ },
+ "feed_refreshing": {
+ "message": "??????????...",
+ "description": "feed refreshing"
+ },
+ "feed_updated": {
+ "message": "??????? ? ???????.",
+ "description": "feed updated"
+ },
+ "feed_no_videos_subscribe_first": {
+ "message": "???? ???????? ?????. ????? ?? ???????? ?? ??????.",
+ "description": "feed no videos subscribe first"
+ },
+ "feed_refresh_failed_status": {
+ "message": "???????????? ?? ????: $1. ??? YouTube ??????? ????? ?? ????????, ?????? youtube.com ?????? ? ???? ???????.",
+ "description": "feed refresh failed status"
+ }
+}
diff --git a/src/_locales/bg/messages-group.json b/src/_locales/bg/messages-group.json
index cd2a60a..efbaf12 100644
--- a/src/_locales/bg/messages-group.json
+++ b/src/_locales/bg/messages-group.json
@@ -47,10 +47,6 @@
"message": "Сигурни ли сте, че искате да изчистите цялата история? Това не може да се отмени.",
"description": "Confirmation for clearing all history."
},
- "message_confirm_full_sync": {
- "message": "Пълната синхронизация ще изчисти старите данни и ще синхронизира всичко наново. Може да отнеме време. Продължавате ли?",
- "description": "Confirmation for full sync."
- },
"message_warning_clear_all": {
"message": "ВНИМАНИЕ: Това ще изтрие цялата ви история и плейлисти в YouTube. Това не може да се отмени. Продължавате ли?",
"description": "Warning for clearing all history and playlists."
@@ -119,114 +115,6 @@
"message": "Режимът за отстраняване на грешки е деактивиран",
"description": "Message when debug mode is disabled"
},
- "message_sync_disabled": {
- "message": "Синхронизацията е изключена - Кликнете, за да я активирате",
- "description": "Tooltip for disabled sync status"
- },
- "message_sync_not_available": {
- "message": "Синхронизацията не е налична - Изисква се Firefox Sync",
- "description": "Tooltip for unavailable sync status"
- },
- "message_sync_initializing": {
- "message": "Инициализиране на синхронизацията...",
- "description": "Tooltip for initializing sync status"
- },
- "message_sync_syncing": {
- "message": "Синхронизиране на данни...",
- "description": "Tooltip for syncing data status"
- },
- "message_sync_ready": {
- "message": "Синхронизацията е готова",
- "description": "Tooltip for sync ready status"
- },
- "message_sync_error": {
- "message": "Грешка при синхронизация - Кликнете за повторен опит",
- "description": "Tooltip for sync error status"
- },
- "message_sync_off": {
- "message": "Изключено",
- "description": "Label for sync off status"
- },
- "message_sync_na": {
- "message": "Н/Д",
- "description": "Label for sync not available status"
- },
- "message_sync_init": {
- "message": "Инициализиране",
- "description": "Label for sync initializing status"
- },
- "message_sync_on": {
- "message": "Включено",
- "description": "Label for sync on status"
- },
- "message_sync": {
- "message": "Синхронизация",
- "description": "Label for sync status"
- },
- "message_syncing": {
- "message": "Синхронизиране...",
- "description": "Label for syncing status"
- },
- "message_synced": {
- "message": "Синхронизирано",
- "description": "Label for synced status"
- },
- "message_sync_now": {
- "message": "Синхронизирай сега",
- "description": "Button label for sync now"
- },
- "message_full_sync": {
- "message": "Пълна синхронизация",
- "description": "Button label for full sync"
- },
- "message_syncing_status": {
- "message": "Синхронизира се...",
- "description": "Status text for syncing"
- },
- "message_synced_status": {
- "message": "Синхронизирано",
- "description": "Status text for synced"
- },
- "message_never": {
- "message": "Никога",
- "description": "Label for never synced"
- },
- "message_just_now": {
- "message": "Току-що",
- "description": "Label for just now sync time"
- },
- "message_minutes_ago": {
- "message": "преди {MINUTES} минути",
- "description": "Label for minutes ago sync time"
- },
- "message_hours_ago": {
- "message": "преди {HOURS} часа",
- "description": "Label for hours ago sync time"
- },
- "message_days_ago": {
- "message": "преди {DAYS} дни",
- "description": "Label for days ago sync time"
- },
- "message_firefox_sync_not_available": {
- "message": "Firefox Sync не е наличен. Моля, активирайте Firefox Sync в настройките на браузъра си.",
- "description": "Error message when Firefox Sync is not available"
- },
- "message_failed_to_enable_sync": {
- "message": "Неуспешно активиране на синхронизацията. Моля, проверете настройките на Firefox Sync.",
- "description": "Error message when enabling sync fails"
- },
- "message_failed_to_disable_sync": {
- "message": "Неуспешно изключване на синхронизацията.",
- "description": "Error message when disabling sync fails"
- },
- "message_sync_currently_disabled": {
- "message": "Синхронизацията е изключена в момента.",
- "description": "Error message when sync is currently disabled"
- },
- "message_full_sync_initiated": {
- "message": "Стартира се пълна синхронизация... Това може да отнеме малко време.",
- "description": "Message when full sync is initiated"
- },
"settings_convert_history": {
"message": "Конвертиране на YouTube история",
"description": "Button label to open the community converter tool"
@@ -239,4 +127,4 @@
"message": "Неизвестна грешка",
"description": "Fallback for unknown error details"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json
index 7d81a60..5d022de 100644
--- a/src/_locales/bg/messages.json
+++ b/src/_locales/bg/messages.json
@@ -1,192 +1,166 @@
{
"extensionName": {
- "message": "YouTube Локална История",
- "description": "The name of the extension"
+ "message": "YouTube Локална История",
+ "description": "The name of the extension"
},
- "popup_title" : {
- "message": "YT re:Watch",
- "description": "Popup window title and main heading"
+ "popup_title": {
+ "message": "YT re:Watch",
+ "description": "Popup window title and main heading"
},
- "theme_toggle" : {
- "message": "Тема",
- "description": "Button label for toggling theme"
+ "theme_toggle": {
+ "message": "Тема",
+ "description": "Button label for toggling theme"
},
- "theme_toggle_tooltip" : {
- "message": "Превключване на темата",
- "description": "Tooltip for theme toggle button"
+ "theme_toggle_tooltip": {
+ "message": "Превключване на темата",
+ "description": "Tooltip for theme toggle button"
},
- "theme_light" : {
- "message": "Светла",
- "description": "Label for light theme option"
+ "theme_light": {
+ "message": "Светла",
+ "description": "Label for light theme option"
},
- "theme_dark" : {
- "message": "Тъмна",
- "description": "Label for dark theme option"
+ "theme_dark": {
+ "message": "Тъмна",
+ "description": "Label for dark theme option"
},
- "theme_auto" : {
- "message": "Автоматично",
- "description": "Label for auto/system theme option"
+ "theme_auto": {
+ "message": "Автоматично",
+ "description": "Label for auto/system theme option"
},
- "theme_switch_to_dark" : {
- "message": "Превключване на тъмна тема",
- "description": "Tooltip for switching to dark theme"
+ "theme_switch_to_dark": {
+ "message": "Превключване на тъмна тема",
+ "description": "Tooltip for switching to dark theme"
},
- "theme_switch_to_light" : {
- "message": "Превключване на светла тема",
- "description": "Tooltip for switching to light theme"
+ "theme_switch_to_light": {
+ "message": "Превключване на светла тема",
+ "description": "Tooltip for switching to light theme"
},
- "theme_system" : {
- "message": "Системни настройки",
- "description": "Label for system default theme option"
+ "theme_system": {
+ "message": "Системни настройки",
+ "description": "Label for system default theme option"
},
- "theme_currently_following_system" : {
- "message": "Следва системната тема - Кликнете за ръчно превключване",
- "description": "Tooltip for auto theme option"
+ "theme_currently_following_system": {
+ "message": "Следва системната тема - Кликнете за ръчно превключване",
+ "description": "Tooltip for auto theme option"
},
- "clear_label" : {
- "message": "Изчисти",
- "description": "Button label for clearing all history"
+ "clear_label": {
+ "message": "Изчисти",
+ "description": "Button label for clearing all history"
},
- "clear_tooltip" : {
- "message": "Изчисти цялата история",
- "description": "Tooltip for clear history button"
+ "clear_tooltip": {
+ "message": "Изчисти цялата история",
+ "description": "Tooltip for clear history button"
},
- "close_label" : {
- "message": "Затвори",
- "description": "Button label for closing the popup"
+ "close_label": {
+ "message": "Затвори",
+ "description": "Button label for closing the popup"
},
- "sync_status" : {
- "message": "Синхронизация",
- "description": "Label for sync status indicator"
+ "instructions_script_disabled_title": {
+ "message": "Изпълнението на скриптове е изключено",
+ "description": "Title for script execution disabled instructions"
},
- "sync_status_tooltip" : {
- "message": "Състояние на синхронизацията",
- "description": "Tooltip for sync status indicator"
+ "instructions_script_disabled_body": {
+ "message": "Ако виждате това съобщение, изпълнението на скриптове е изключено за това разширение. Моля, следвайте тези стъпки, за да го активирате:",
+ "description": "Body text for script execution disabled instructions"
},
-
- "instructions_script_disabled_title" : {
- "message": "Изпълнението на скриптове е изключено",
- "description": "Title for script execution disabled instructions"
+ "instructions_script_disabled_step1": {
+ "message": "Отидете в настройките на Firefox: about:preferences#privacy",
+ "description": "Step 1 for enabling script execution"
},
- "instructions_script_disabled_body" : {
- "message": "Ако виждате това съобщение, изпълнението на скриптове е изключено за това разширение. Моля, следвайте тези стъпки, за да го активирате:",
- "description": "Body text for script execution disabled instructions"
+ "instructions_script_disabled_step2": {
+ "message": "Уверете се, че \"Enhanced Tracking Protection\" е на \"Standard\" или \"Custom\" и не блокира скриптове",
+ "description": "Step 2 for enabling script execution"
},
- "instructions_script_disabled_step1" : {
- "message": "Отидете в настройките на Firefox: about:preferences#privacy",
- "description": "Step 1 for enabling script execution"
+ "instructions_script_disabled_step3": {
+ "message": "Проверете за блокиране на съдържание: кликнете върху иконата на щит в адресната лента, когато прозорецът е отворен, и се уверете, че няма блокирани скриптове",
+ "description": "Step 3 for enabling script execution"
},
- "instructions_script_disabled_step2" : {
- "message": "Уверете се, че \"Enhanced Tracking Protection\" е на \"Standard\" или \"Custom\" и не блокира скриптове",
- "description": "Step 2 for enabling script execution"
+ "delete_label": {
+ "message": "Изтрий",
+ "description": "Button label for deleting an item (video, playlist, short)"
},
- "instructions_script_disabled_step3" : {
- "message": "Проверете за блокиране на съдържание: кликнете върху иконата на щит в адресната лента, когато прозорецът е отворен, и се уверете, че няма блокирани скриптове",
- "description": "Step 3 for enabling script execution"
+ "pagination_page_info": {
+ "message": "Страница $1 от $2",
+ "description": "Pagination info, e.g. Page 1 of 11"
},
- "delete_label" : {
- "message": "Изтрий",
- "description": "Button label for deleting an item (video, playlist, short)"
+ "chart_skipped": {
+ "message": "Пропуснато",
+ "description": "Chart label: Skipped"
},
- "pagination_page_info" : {
- "message": "Страница $1 от $2",
- "description": "Pagination info, e.g. Page 1 of 11"
+ "chart_partial": {
+ "message": "Частично",
+ "description": "Chart label: Partial"
},
- "chart_skipped" : {
- "message": "Пропуснато",
- "description": "Chart label: Skipped"
+ "chart_completed": {
+ "message": "Завършено",
+ "description": "Chart label: Completed"
},
- "chart_partial" : {
- "message": "Частично",
- "description": "Chart label: Partial"
+ "chart_skipped_legend": {
+ "message": "Пропуснато (<10%)",
+ "description": "Chart legend: Skipped (<10%)"
},
- "chart_completed" : {
- "message": "Завършено",
- "description": "Chart label: Completed"
+ "chart_partial_legend": {
+ "message": "Частично (10-90%)",
+ "description": "Chart legend: Partial (10-90%)"
},
- "chart_skipped_legend" : {
- "message": "Пропуснато (<10%)",
- "description": "Chart legend: Skipped (<10%)"
+ "chart_completed_legend": {
+ "message": "Завършено (>=90%)",
+ "description": "Chart legend: Completed (>=90%)"
},
- "chart_partial_legend" : {
- "message": "Частично (10-90%)",
- "description": "Chart legend: Partial (10-90%)"
+ "chart_minutes": {
+ "message": "Минути",
+ "description": "Chart axis label: Minutes"
},
- "chart_completed_legend" : {
- "message": "Завършено (>=90%)",
- "description": "Chart legend: Completed (>=90%)"
+ "chart_hour_of_day": {
+ "message": "Час от деня",
+ "description": "Chart axis label: Hour of Day"
},
- "chart_minutes" : {
- "message": "Минути",
- "description": "Chart axis label: Minutes"
+ "date_today": {
+ "message": "Днес",
+ "description": "Label for today's date"
},
- "chart_hour_of_day" : {
- "message": "Час от деня",
- "description": "Chart axis label: Hour of Day"
+ "date_yesterday": {
+ "message": "Вчера",
+ "description": "Label for yesterday's date"
},
-
- "date_today" : {
- "message": "Днес",
- "description": "Label for today's date"
+ "date_unknown": {
+ "message": "Непозната",
+ "description": "Label for unknown date"
},
- "date_yesterday" : {
- "message": "Вчера",
- "description": "Label for yesterday's date"
+ "search_no_videos_found": {
+ "message": "Не са намерени видеа, отговарящи на търсенето ви.",
+ "description": "Message when no videos match the search"
},
- "date_unknown" : {
- "message": "Непозната",
- "description": "Label for unknown date"
- },
- "search_no_videos_found" : {
- "message": "Не са намерени видеа, отговарящи на търсенето ви.",
- "description": "Message when no videos match the search"
- },
- "history_no_history_found" : {
- "message": "Не е намерена история. Започнете да гледате видеа, за да се появи тук!",
- "description": "Message when no history is found"
+ "history_no_history_found": {
+ "message": "Не е намерена история. Започнете да гледате видеа, за да се появи тук!",
+ "description": "Message when no history is found"
},
"message_pause_in_playlists_enabled": {
- "message": "Пауза на историята в плейлисти е включена",
- "description": "Toast when global pause in playlists is enabled"
+ "message": "Пауза на историята в плейлисти е включена",
+ "description": "Toast when global pause in playlists is enabled"
},
"message_pause_in_playlists_disabled": {
- "message": "Пауза на историята в плейлисти е изключена",
- "description": "Toast when global pause in playlists is disabled"
+ "message": "Пауза на историята в плейлисти е изключена",
+ "description": "Toast when global pause in playlists is disabled"
},
"playlists_ignore_toggle_label": {
- "message": "Игнорирай",
- "description": "Label next to playlist ignore checkbox"
+ "message": "Игнорирай",
+ "description": "Label next to playlist ignore checkbox"
},
"playlists_ignore_toggle_tooltip": {
- "message": "Игнорирай видеата в този плейлист",
- "description": "Tooltip for playlist ignore checkbox"
+ "message": "Игнорирай видеата в този плейлист",
+ "description": "Tooltip for playlist ignore checkbox"
},
"playlists_ignore_enabled": {
- "message": "Плейлистът ще бъде игнориран",
- "description": "Toast when ignore is enabled"
+ "message": "Плейлистът ще бъде игнориран",
+ "description": "Toast when ignore is enabled"
},
"playlists_ignore_disabled": {
- "message": "Плейлистът ще бъде проследяван",
- "description": "Toast when ignore is disabled"
- },
- "content_toggle_pause": {
- "message": "re:Watch — Кликнете за пауза на историята",
- "description": "Button text when tracking is active"
- },
- "content_toggle_paused": {
- "message": "re:Watch — Историята е на пауза. Кликнете за активиране",
- "description": "Button text when tracking is paused"
- },
- "content_toggle_pause_title": {
- "message": "re:Watch — Кликнете за пауза на историята за този плейлист",
- "description": "Tooltip when tracking is active"
- },
- "content_toggle_paused_title": {
- "message": "re:Watch — Историята е на пауза за този плейлист. Кликнете за активиране",
- "description": "Tooltip when tracking is paused"
+ "message": "Плейлистът ще бъде проследяван",
+ "description": "Toast when ignore is disabled"
},
"global_search_placeholder": {
- "message": "Търсене във всички съдържание...",
- "description": "Placeholder text for the global search input"
+ "message": "Търсене във всички съдържание...",
+ "description": "Placeholder text for the global search input"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/bg/settings.json b/src/_locales/bg/settings.json
index 3c3fe1b..6130e32 100644
--- a/src/_locales/bg/settings.json
+++ b/src/_locales/bg/settings.json
@@ -44,8 +44,8 @@
"description": "Help text for overlay size"
},
"settings_data_sync": {
- "message": "Данни и Синхронизация",
- "description": "Settings section title for data and sync"
+ "message": "Data",
+ "description": "Settings section title for data"
},
"settings_pause_in_playlists_label": {
"message": "Пауза на историята в плейлисти",
@@ -79,30 +79,6 @@
"message": "Автоматично премахва история след избрания период",
"description": "Help text for auto-clean period"
},
- "settings_sync_label": {
- "message": "Firefox Sync",
- "description": "Label for Firefox Sync option"
- },
- "settings_sync_help": {
- "message": "Синхронизирайте вашите данни между инстанции на браузъра (само за Firefox)",
- "description": "Help text for Firefox Sync"
- },
- "settings_last_sync_label": {
- "message": "Последна синхронизация",
- "description": "Label for last sync time"
- },
- "settings_sync_now": {
- "message": "Синхронизирай сега",
- "description": "Button label for triggering sync"
- },
- "settings_full_sync": {
- "message": "Пълна синхронизация",
- "description": "Button label for triggering full sync"
- },
- "settings_full_sync_help": {
- "message": "Пълната синхронизация изчиства старите данни и ги синхронизира отново",
- "description": "Help text for full sync"
- },
"settings_debug_label": {
"message": "Режим за отстраняване на грешки",
"description": "Label for debug mode option"
@@ -118,5 +94,97 @@
"settings_save": {
"message": "Запазете настройките",
"description": "Button label for saving settings"
+ },
+ "settings_data_label": {
+ "message": "Data",
+ "description": "Label for the data section in settings"
+ },
+ "settings_import_history": {
+ "message": "Import History",
+ "description": "Button label to import watch history"
+ },
+ "settings_import_history_help": {
+ "message": "Import History accepts the watch-history.html file from Google Takeout.",
+ "description": "Help text for the import history button"
+ },
+ "settings_import_subs": {
+ "message": "Import Subscriptions",
+ "description": "Button label to import subscriptions"
+ },
+ "settings_import_subs_help": {
+ "message": "Import Subscriptions accepts the subscriptions.csv file from Google Takeout.",
+ "description": "Help text for the import subscriptions button"
+ },
+ "settings_clear_help": {
+ "message": "Clear removes all saved video history (this cannot be undone)",
+ "description": "Help text for the Clear history button"
+ },
+ "settings_local_feed_label": {
+ "message": "Local feed",
+ "description": "Label for the local subscriptions feed toggle"
+ },
+ "settings_local_feed_help": {
+ "message": "Use locally subscribed channels to build the YT re:Watch feed",
+ "description": "Help text for the local subscriptions feed toggle"
+ },
+ "settings_hide_recs_label": {
+ "message": "Hide recommendations & popups",
+ "description": "Label for the hide recommendations toggle"
+ },
+ "settings_hide_recs_help": {
+ "message": "Hide YouTube's home recommendations and \"history is off\" nag popups.",
+ "description": "Help text for the hide recommendations toggle"
+ },
+ "settings_hide_account_label": {
+ "message": "Hide login & account UI",
+ "description": "Label for the hide account UI toggle"
+ },
+ "settings_hide_account_help": {
+ "message": "Hide YouTube's sign-in buttons, account menu, and native Subscribe/Join buttons (your local Subscribe stays).",
+ "description": "Help text for the hide account UI toggle"
+ },
+ "settings_feed_refresh_label": {
+ "message": "Feed refresh interval (minutes)",
+ "description": "Label for the feed refresh interval input"
+ },
+ "settings_feed_refresh_help": {
+ "message": "How often the local feed checks subscribed channels for new uploads",
+ "description": "Help text for the feed refresh interval input"
+ },
+ "subscriptions_refresh": {
+ "message": "Refresh feed",
+ "description": "Button label to refresh the local subscriptions feed"
+ },
+ "subscriptions_open_feed": {
+ "message": "Open YT re:Watch",
+ "description": "Button label to open the dedicated feed page"
+ },
+ "subscriptions_clear_all": {
+ "message": "Clear all",
+ "description": "Button label to remove all local subscriptions"
+ },
+ "subscriptions_no_subs": {
+ "message": "No local subscriptions yet. Search in the extension and use Subscribe.",
+ "description": "Empty state for the subscriptions tab"
+ },
+ "subscriptions_unsubscribe": {
+ "message": "Unsubscribe",
+ "description": "Button label to remove a local subscription"
+ },
+ "subscriptions_refreshing": {
+ "message": "Refreshing…",
+ "description": "Refresh button label while a refresh is in progress"
+ },
+ "subscriptions_refresh_started": {
+ "message": "Refreshing feed in your YouTube tab…",
+ "description": "Message shown when a feed refresh starts"
+ },
+ "subscriptions_open_youtube": {
+ "message": "Open a YouTube tab to refresh the feed.",
+ "description": "Message shown when no YouTube tab is open to refresh the feed"
+ },
+ "message_settings_saved": {
+ "message": "Settings saved",
+ "description": "Generic confirmation that settings were saved"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/bg/tabs.json b/src/_locales/bg/tabs.json
index dd18a48..a3b45ae 100644
--- a/src/_locales/bg/tabs.json
+++ b/src/_locales/bg/tabs.json
@@ -226,5 +226,25 @@
"playlists_unknown_title": {
"message": "Неизвестен плейлист",
"description": "Fallback title for playlists with no title"
+ },
+ "tab_subscriptions": {
+ "message": "Subs",
+ "description": "Tab label for local subscriptions"
+ },
+ "tab_watchlater": {
+ "message": "Later",
+ "description": "Tab label for local Watch Later list"
+ },
+ "watchlater_empty": {
+ "message": "Nothing saved yet. On YouTube, right-click a video and choose \"Save to Watch Later (local)\".",
+ "description": "Empty state for the Watch Later tab"
+ },
+ "watchlater_clear_all": {
+ "message": "Clear all",
+ "description": "Button to remove all Watch Later items"
+ },
+ "watchlater_confirm_clear": {
+ "message": "Clear all Watch Later items?",
+ "description": "Confirmation prompt before clearing the Watch Later list"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/de/feed.json b/src/_locales/de/feed.json
new file mode 100644
index 0000000..fb43934
--- /dev/null
+++ b/src/_locales/de/feed.json
@@ -0,0 +1,506 @@
+{
+ "feed_menu": {
+ "message": "Men?",
+ "description": "Menu button label"
+ },
+ "feed_back_to_feed": {
+ "message": "Zur?ck zu deinem Feed",
+ "description": "Tooltip for returning to the feed"
+ },
+ "feed_search_youtube": {
+ "message": "YouTube suchen",
+ "description": "Search field placeholder"
+ },
+ "feed_refresh": {
+ "message": "Aktualisieren",
+ "description": "Refresh button label"
+ },
+ "feed_create": {
+ "message": "Erstellen",
+ "description": "Create button label"
+ },
+ "feed_save": {
+ "message": "Speichern",
+ "description": "Save button label"
+ },
+ "feed_clear_all": {
+ "message": "Alle l?schen",
+ "description": "Clear all button label"
+ },
+ "feed_options": {
+ "message": "Optionen",
+ "description": "Options menu label"
+ },
+ "feed_unwatched_only": {
+ "message": "Nur ungesehen",
+ "description": "Filter label for only unwatched videos"
+ },
+ "feed_hide_members": {
+ "message": "Mitglieder-Videos ausblenden",
+ "description": "Filter label for hiding members-only or members-first videos"
+ },
+ "feed_nav_home": {
+ "message": "Startseite",
+ "description": "Home navigation label"
+ },
+ "feed_nav_home_title": {
+ "message": "Lokal aus deinem Wiedergabeverlauf personalisiert",
+ "description": "Home navigation tooltip"
+ },
+ "feed_nav_subscriptions": {
+ "message": "Abos",
+ "description": "Subscriptions navigation label"
+ },
+ "feed_nav_subscriptions_title": {
+ "message": "Neueste Videos deiner abonnierten Kan?le",
+ "description": "Subscriptions navigation tooltip"
+ },
+ "feed_nav_shorts": {
+ "message": "Shorts",
+ "description": "Shorts navigation label"
+ },
+ "feed_nav_playlists": {
+ "message": "Playlists",
+ "description": "Playlists navigation label"
+ },
+ "feed_nav_playlists_title": {
+ "message": "Gespeicherte Playlists anzeigen",
+ "description": "Playlists navigation tooltip"
+ },
+ "feed_nav_history": {
+ "message": "Verlauf",
+ "description": "History navigation label"
+ },
+ "feed_nav_history_title": {
+ "message": "Deinen lokalen Wiedergabeverlauf anzeigen",
+ "description": "History navigation tooltip"
+ },
+ "feed_nav_channels": {
+ "message": "Kan?le",
+ "description": "Channels navigation label"
+ },
+ "feed_nav_channels_title": {
+ "message": "Lokal abonnierte Kan?le verwalten",
+ "description": "Channels navigation tooltip"
+ },
+ "feed_nav_analytics": {
+ "message": "Analysen",
+ "description": "Analytics navigation label"
+ },
+ "feed_nav_analytics_title": {
+ "message": "Deine Wiedergabeanalysen anzeigen",
+ "description": "Analytics navigation tooltip"
+ },
+ "feed_nav_settings": {
+ "message": "Einstellungen",
+ "description": "Settings navigation label"
+ },
+ "feed_nav_settings_title": {
+ "message": "Erweiterungseinstellungen ?ffnen",
+ "description": "Settings navigation tooltip"
+ },
+ "feed_your_feed": {
+ "message": "Dein Feed",
+ "description": "Main feed heading"
+ },
+ "feed_search_options": {
+ "message": "Suchoptionen",
+ "description": "Search options aria label"
+ },
+ "feed_filters": {
+ "message": "Filter",
+ "description": "Filters button label"
+ },
+ "feed_upload_date": {
+ "message": "Uploaddatum",
+ "description": "Upload date filter label"
+ },
+ "feed_today": {
+ "message": "Heute",
+ "description": "Today filter label"
+ },
+ "feed_this_week": {
+ "message": "Diese Woche",
+ "description": "This week filter label"
+ },
+ "feed_this_month": {
+ "message": "Diesen Monat",
+ "description": "This month filter label"
+ },
+ "feed_this_year": {
+ "message": "Dieses Jahr",
+ "description": "This year filter label"
+ },
+ "feed_duration": {
+ "message": "Dauer",
+ "description": "Duration filter label"
+ },
+ "feed_under_4_minutes": {
+ "message": "Unter 4 Minuten",
+ "description": "Under 4 minutes filter label"
+ },
+ "feed_4_to_20_minutes": {
+ "message": "4-20 Minuten",
+ "description": "4 to 20 minutes filter label"
+ },
+ "feed_over_20_minutes": {
+ "message": "?ber 20 Minuten",
+ "description": "Over 20 minutes filter label"
+ },
+ "feed_watch_status": {
+ "message": "Wiedergabestatus",
+ "description": "Watch status filter label"
+ },
+ "feed_unwatched": {
+ "message": "Ungesehen",
+ "description": "Unwatched filter label"
+ },
+ "feed_in_progress": {
+ "message": "Angefangen",
+ "description": "In progress filter label"
+ },
+ "feed_watched": {
+ "message": "Gesehen",
+ "description": "Watched filter label"
+ },
+ "feed_sort_search_results": {
+ "message": "Suchergebnisse sortieren",
+ "description": "Sort search results aria label"
+ },
+ "feed_sort": {
+ "message": "Sortieren",
+ "description": "Sort filter label"
+ },
+ "feed_newest": {
+ "message": "Neueste",
+ "description": "Newest sort label"
+ },
+ "feed_oldest": {
+ "message": "?lteste",
+ "description": "Oldest sort label"
+ },
+ "feed_search_results_for": {
+ "message": "Suchergebnisse f?r",
+ "description": "Search results heading prefix"
+ },
+ "feed_back_to_results": {
+ "message": "Zur?ck zu den Ergebnissen",
+ "description": "Back to results button label"
+ },
+ "feed_no_channel_videos": {
+ "message": "Noch keine Videos f?r diesen Kanal gefunden.",
+ "description": "Empty state for channel videos"
+ },
+ "feed_channels": {
+ "message": "Kan?le",
+ "description": "Channels label"
+ },
+ "feed_no_local_channels": {
+ "message": "Noch keine lokal abonnierten Kan?le. Suche in der Erweiterung und nutze Abonnieren.",
+ "description": "Empty state for local channels"
+ },
+ "feed_playlists": {
+ "message": "Playlists",
+ "description": "Playlists label"
+ },
+ "feed_no_saved_playlists": {
+ "message": "Noch keine gespeicherten Playlists.",
+ "description": "Empty state for saved playlists"
+ },
+ "feed_history": {
+ "message": "Verlauf",
+ "description": "History label"
+ },
+ "feed_no_history_matches": {
+ "message": "Kein Wiedergabeverlauf passt zu diesen Filtern.",
+ "description": "Empty state for filtered history"
+ },
+ "feed_load_more": {
+ "message": "Mehr laden",
+ "description": "Load more button label"
+ },
+ "feed_analytics": {
+ "message": "Analysen",
+ "description": "Analytics label"
+ },
+ "feed_analytics_subtitle": {
+ "message": "deine Wiedergabestatistiken, lokal gespeichert",
+ "description": "Analytics heading subtitle"
+ },
+ "feed_watch_activity_7_days": {
+ "message": "Wiedergabeaktivit?t (letzte 7 Tage)",
+ "description": "Watch activity chart title"
+ },
+ "feed_watch_time_by_hour": {
+ "message": "Wiedergabezeit nach Stunde",
+ "description": "Watch time by hour chart title"
+ },
+ "feed_how_much_finish": {
+ "message": "Wie viel du beendest",
+ "description": "Completion analytics title"
+ },
+ "feed_top_channels": {
+ "message": "Top-Kan?le",
+ "description": "Top channels analytics title"
+ },
+ "feed_continue_watching": {
+ "message": "Weiter ansehen",
+ "description": "Continue watching analytics title"
+ },
+ "feed_settings": {
+ "message": "Einstellungen",
+ "description": "Settings label"
+ },
+ "feed_settings_subtitle": {
+ "message": "Alles bleibt lokal in diesem Browser.",
+ "description": "Settings subtitle"
+ },
+ "feed_settings_display": {
+ "message": "Anzeige",
+ "description": "Display settings section title"
+ },
+ "feed_settings_theme": {
+ "message": "Design",
+ "description": "Theme setting label"
+ },
+ "feed_system_default": {
+ "message": "Systemstandard",
+ "description": "System default theme label"
+ },
+ "feed_dark": {
+ "message": "Dunkel",
+ "description": "Dark theme label"
+ },
+ "feed_light": {
+ "message": "Hell",
+ "description": "Light theme label"
+ },
+ "feed_accent_color": {
+ "message": "Akzentfarbe",
+ "description": "Accent color setting label"
+ },
+ "feed_blue": {
+ "message": "Blau",
+ "description": "Blue color label"
+ },
+ "feed_red": {
+ "message": "Rot",
+ "description": "Red color label"
+ },
+ "feed_green": {
+ "message": "Gr?n",
+ "description": "Green color label"
+ },
+ "feed_purple": {
+ "message": "Lila",
+ "description": "Purple color label"
+ },
+ "feed_orange": {
+ "message": "Orange",
+ "description": "Orange color label"
+ },
+ "feed_default_tab": {
+ "message": "Standard-Tab",
+ "description": "Default tab setting label"
+ },
+ "feed_last_used_tab": {
+ "message": "Zuletzt verwendeter Tab",
+ "description": "Last used tab option label"
+ },
+ "feed_history_and_feed": {
+ "message": "Verlauf & Feed",
+ "description": "History and feed settings section title"
+ },
+ "feed_check_new_videos": {
+ "message": "Nach neuen Videos suchen",
+ "description": "Check for new videos setting label"
+ },
+ "feed_every_15_minutes": {
+ "message": "Alle 15 Minuten",
+ "description": "Every 15 minutes interval label"
+ },
+ "feed_every_30_minutes": {
+ "message": "Alle 30 Minuten",
+ "description": "Every 30 minutes interval label"
+ },
+ "feed_every_hour": {
+ "message": "Jede Stunde",
+ "description": "Every hour interval label"
+ },
+ "feed_every_3_hours": {
+ "message": "Alle 3 Stunden",
+ "description": "Every 3 hours interval label"
+ },
+ "feed_every_6_hours": {
+ "message": "Alle 6 Stunden",
+ "description": "Every 6 hours interval label"
+ },
+ "feed_every_12_hours": {
+ "message": "Alle 12 Stunden",
+ "description": "Every 12 hours interval label"
+ },
+ "feed_once_a_day": {
+ "message": "Einmal t?glich",
+ "description": "Once a day interval label"
+ },
+ "feed_keep_watch_history": {
+ "message": "Wiedergabeverlauf behalten",
+ "description": "Keep watch history setting label"
+ },
+ "feed_30_days": {
+ "message": "30 Tage",
+ "description": "30 days option label"
+ },
+ "feed_90_days": {
+ "message": "90 Tage",
+ "description": "90 days option label"
+ },
+ "feed_180_days": {
+ "message": "180 Tage",
+ "description": "180 days option label"
+ },
+ "feed_forever": {
+ "message": "F?r immer",
+ "description": "Forever option label"
+ },
+ "feed_data": {
+ "message": "Daten",
+ "description": "Data settings section title"
+ },
+ "feed_backup_restore": {
+ "message": "Backup & Wiederherstellung",
+ "description": "Backup and restore title"
+ },
+ "feed_backup_restore_help": {
+ "message": "Lade eine JSON-Datei mit Verlauf, Abos, Playlists und Einstellungen herunter oder wiederher.",
+ "description": "Backup and restore help text"
+ },
+ "feed_backup": {
+ "message": "Backup",
+ "description": "Backup button label"
+ },
+ "feed_restore": {
+ "message": "Wiederherstellen",
+ "description": "Restore button label"
+ },
+ "feed_import_from_youtube": {
+ "message": "Von YouTube importieren",
+ "description": "Import from YouTube title"
+ },
+ "feed_import_from_youtube_help": {
+ "message": "Nutze watch-history.html f?r deinen Wiedergabeverlauf und subscriptions.csv f?r deine abonnierten Kan?le aus Google Takeout.",
+ "description": "Import from YouTube help text"
+ },
+ "feed_reset_all_data": {
+ "message": "Alle Daten zur?cksetzen",
+ "description": "Reset all data title"
+ },
+ "feed_reset_all_data_help": {
+ "message": "Alles l?schen, einschlie?lich Abos, Einstellungen und Backups.",
+ "description": "Reset all data help text"
+ },
+ "feed_reset_all": {
+ "message": "Alles zur?cksetzen",
+ "description": "Reset all button label"
+ },
+ "feed_add_to_playlist": {
+ "message": "Zur Playlist hinzuf?gen",
+ "description": "Add to playlist dialog title"
+ },
+ "feed_close": {
+ "message": "Schlie?en",
+ "description": "Close button aria label"
+ },
+ "feed_create_new_playlist": {
+ "message": "Neue Playlist erstellen",
+ "description": "Create new playlist placeholder"
+ },
+ "feed_video_options": {
+ "message": "Videooptionen",
+ "description": "feed video options"
+ },
+ "feed_more_from_channel": {
+ "message": "Mehr von diesem Kanal",
+ "description": "feed more from channel"
+ },
+ "feed_less_from_channel": {
+ "message": "Weniger von diesem Kanal",
+ "description": "feed less from channel"
+ },
+ "feed_hide_this_video": {
+ "message": "Dieses Video ausblenden",
+ "description": "feed hide this video"
+ },
+ "feed_unsubscribe": {
+ "message": "Abo beenden",
+ "description": "feed unsubscribe"
+ },
+ "feed_not_subscribed": {
+ "message": "Nicht abonniert",
+ "description": "feed not subscribed"
+ },
+ "feed_unsubscribed": {
+ "message": "Abbestellt",
+ "description": "feed unsubscribed"
+ },
+ "feed_save_to_watch_later": {
+ "message": "In Sp?ter ansehen speichern",
+ "description": "feed save to watch later"
+ },
+ "feed_saved_to_watch_later": {
+ "message": "In Sp?ter ansehen gespeichert",
+ "description": "feed saved to watch later"
+ },
+ "feed_added_to_playlist": {
+ "message": "Zur Playlist hinzugef?gt",
+ "description": "feed added to playlist"
+ },
+ "feed_set_as_playlist_cover": {
+ "message": "Als Playlist-Cover festlegen",
+ "description": "feed set as playlist cover"
+ },
+ "feed_cover_updated": {
+ "message": "Cover aktualisiert",
+ "description": "feed cover updated"
+ },
+ "feed_remove_from_playlist": {
+ "message": "Aus dieser Playlist entfernen",
+ "description": "feed remove from playlist"
+ },
+ "feed_this_channel": {
+ "message": "diesem Kanal",
+ "description": "feed this channel"
+ },
+ "feed_channel": {
+ "message": "Kanal",
+ "description": "feed channel"
+ },
+ "feed_recommend_more_from_status": {
+ "message": "Wir empfehlen mehr von $1.",
+ "description": "feed recommend more from status"
+ },
+ "feed_recommend_more_status": {
+ "message": "Wir empfehlen mehr",
+ "description": "feed recommend more status"
+ },
+ "feed_unsubscribed_from_status": {
+ "message": "Abo von $1 beendet.",
+ "description": "feed unsubscribed from status"
+ },
+ "feed_refreshing": {
+ "message": "Aktualisieren...",
+ "description": "feed refreshing"
+ },
+ "feed_updated": {
+ "message": "Feed aktualisiert.",
+ "description": "feed updated"
+ },
+ "feed_no_videos_subscribe_first": {
+ "message": "Keine Videos gefunden. Abonniere zuerst Kan?le.",
+ "description": "feed no videos subscribe first"
+ },
+ "feed_refresh_failed_status": {
+ "message": "Aktualisierung fehlgeschlagen: $1. Wenn YouTube eine Zustimmungsseite zeigt, ?ffne youtube.com einmal in diesem Browser.",
+ "description": "feed refresh failed status"
+ }
+}
diff --git a/src/_locales/de/messages-group.json b/src/_locales/de/messages-group.json
index f338c4d..1993448 100644
--- a/src/_locales/de/messages-group.json
+++ b/src/_locales/de/messages-group.json
@@ -47,10 +47,6 @@
"message": "Sind Sie sicher, dass Sie den gesamten Verlauf löschen möchten? Dies kann nicht rückgängig gemacht werden.",
"description": "Confirmation for clearing all history."
},
- "message_confirm_full_sync": {
- "message": "Vollständige Synchronisierung bereinigt alte Daten und synchronisiert alles neu. Dies kann dauern. Fortfahren?",
- "description": "Confirmation for full sync."
- },
"message_warning_clear_all": {
"message": "WARNUNG: Dadurch wird Ihr gesamter YouTube-Verlauf und alle Playlists gelöscht. Dies kann nicht rückgängig gemacht werden. Fortfahren?",
"description": "Warning for clearing all history and playlists."
@@ -119,114 +115,6 @@
"message": "Debug-Modus deaktiviert",
"description": "Message when debug mode is disabled"
},
- "message_sync_disabled": {
- "message": "Synchronisierung deaktiviert - Klicken zum Aktivieren",
- "description": "Tooltip for disabled sync status"
- },
- "message_sync_not_available": {
- "message": "Synchronisierung nicht verfügbar - Firefox Sync erforderlich",
- "description": "Tooltip for unavailable sync status"
- },
- "message_sync_initializing": {
- "message": "Synchronisierung wird initialisiert...",
- "description": "Tooltip for initializing sync status"
- },
- "message_sync_syncing": {
- "message": "Daten werden synchronisiert...",
- "description": "Tooltip for syncing data status"
- },
- "message_sync_ready": {
- "message": "Synchronisierung bereit",
- "description": "Tooltip for sync ready status"
- },
- "message_sync_error": {
- "message": "Synchronisierungsfehler - Klicken zum Wiederholen",
- "description": "Tooltip for sync error status"
- },
- "message_sync_off": {
- "message": "Aus",
- "description": "Label for sync off status"
- },
- "message_sync_na": {
- "message": "N/A",
- "description": "Label for sync not available status"
- },
- "message_sync_init": {
- "message": "Init",
- "description": "Label for sync initializing status"
- },
- "message_sync_on": {
- "message": "An",
- "description": "Label for sync on status"
- },
- "message_sync": {
- "message": "Synchronisierung",
- "description": "Label for sync status"
- },
- "message_syncing": {
- "message": "Synchronisieren...",
- "description": "Label for syncing status"
- },
- "message_synced": {
- "message": "Synchronisiert",
- "description": "Label for synced status"
- },
- "message_sync_now": {
- "message": "Jetzt synchronisieren",
- "description": "Button label for sync now"
- },
- "message_full_sync": {
- "message": "Vollständige Synchronisierung",
- "description": "Button label for full sync"
- },
- "message_syncing_status": {
- "message": "Synchronisieren...",
- "description": "Status text for syncing"
- },
- "message_synced_status": {
- "message": "Synchronisiert",
- "description": "Status text for synced"
- },
- "message_never": {
- "message": "Nie",
- "description": "Label for never synced"
- },
- "message_just_now": {
- "message": "Gerade eben",
- "description": "Label for just now sync time"
- },
- "message_minutes_ago": {
- "message": "vor {MINUTES}m",
- "description": "Label for minutes ago sync time"
- },
- "message_hours_ago": {
- "message": "vor {HOURS}h",
- "description": "Label for hours ago sync time"
- },
- "message_days_ago": {
- "message": "vor {DAYS}T",
- "description": "Label for days ago sync time"
- },
- "message_firefox_sync_not_available": {
- "message": "Firefox Sync ist nicht verfügbar. Bitte aktivieren Sie Firefox Sync in Ihren Browsereinstellungen.",
- "description": "Error message when Firefox Sync is not available"
- },
- "message_failed_to_enable_sync": {
- "message": "Synchronisierung konnte nicht aktiviert werden. Bitte überprüfen Sie Ihre Firefox Sync-Einstellungen.",
- "description": "Error message when enabling sync fails"
- },
- "message_failed_to_disable_sync": {
- "message": "Synchronisierung konnte nicht deaktiviert werden.",
- "description": "Error message when disabling sync fails"
- },
- "message_sync_currently_disabled": {
- "message": "Synchronisierung ist derzeit deaktiviert.",
- "description": "Error message when sync is currently disabled"
- },
- "message_full_sync_initiated": {
- "message": "Vollständige Synchronisierung gestartet... Dies kann einen Moment dauern.",
- "description": "Message when full sync is initiated"
- },
"settings_convert_history": {
"message": "YouTube-Verlauf konvertieren",
"description": "Button label to open the community converter tool"
@@ -239,4 +127,4 @@
"message": "Unbekannter Fehler",
"description": "Fallback for unknown error details"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json
index e1c2c18..5f42a19 100644
--- a/src/_locales/de/messages.json
+++ b/src/_locales/de/messages.json
@@ -55,14 +55,6 @@
"message": "Schließen",
"description": "Button label for closing the popup"
},
- "sync_status": {
- "message": "Sync",
- "description": "Label for sync status indicator"
- },
- "sync_status_tooltip": {
- "message": "Sync-Status",
- "description": "Tooltip for sync status indicator"
- },
"instructions_script_disabled_title": {
"message": "Skriptausführung deaktiviert",
"description": "Title for script execution disabled instructions"
@@ -167,24 +159,8 @@
"message": "Playlist wird verfolgt",
"description": "Toast when ignore is disabled"
},
- "content_toggle_pause": {
- "message": "re:Watch — Klicken, um Verlauf zu pausieren",
- "description": "Button text when tracking is active"
- },
- "content_toggle_paused": {
- "message": "re:Watch — Verlauf pausiert. Klicken zum Aktivieren",
- "description": "Button text when tracking is paused"
- },
- "content_toggle_pause_title": {
- "message": "re:Watch — Klicken, um den Verlauf für diese Playlist zu pausieren",
- "description": "Tooltip when tracking is active"
- },
- "content_toggle_paused_title": {
- "message": "re:Watch — Verlauf für diese Playlist pausiert. Klicken zum Aktivieren",
- "description": "Tooltip when tracking is paused"
- },
"global_search_placeholder": {
"message": "Alle Inhalte durchsuchen...",
"description": "Placeholder text for the global search input"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/de/settings.json b/src/_locales/de/settings.json
index 83e413a..edf4da9 100644
--- a/src/_locales/de/settings.json
+++ b/src/_locales/de/settings.json
@@ -44,8 +44,8 @@
"description": "Help text for overlay size"
},
"settings_data_sync": {
- "message": "Daten & Synchronisierung",
- "description": "Settings section title for data and sync"
+ "message": "Data",
+ "description": "Settings section title for data"
},
"settings_pause_in_playlists_label": {
"message": "Verlauf in Playlists pausieren",
@@ -79,30 +79,6 @@
"message": "Entfernt automatisch Verlauf nach dem ausgewählten Zeitraum",
"description": "Help text for auto-clean period"
},
- "settings_sync_label": {
- "message": "Firefox Sync",
- "description": "Label for Firefox Sync option"
- },
- "settings_sync_help": {
- "message": "Synchronisieren Sie Ihre Daten über Browser-Instanzen hinweg (nur Firefox)",
- "description": "Help text for Firefox Sync"
- },
- "settings_last_sync_label": {
- "message": "Letzte Synchronisierung",
- "description": "Label for last sync time"
- },
- "settings_sync_now": {
- "message": "Jetzt synchronisieren",
- "description": "Button label for triggering sync"
- },
- "settings_full_sync": {
- "message": "Vollständige Synchronisierung",
- "description": "Button label for triggering full sync"
- },
- "settings_full_sync_help": {
- "message": "Vollständige Synchronisierung bereinigt alte Daten und synchronisiert alles neu",
- "description": "Help text for full sync"
- },
"settings_debug_label": {
"message": "Debug-Modus",
"description": "Label for debug mode option"
@@ -118,5 +94,97 @@
"settings_save": {
"message": "Einstellungen speichern",
"description": "Button label for saving settings"
+ },
+ "settings_data_label": {
+ "message": "Data",
+ "description": "Label for the data section in settings"
+ },
+ "settings_import_history": {
+ "message": "Import History",
+ "description": "Button label to import watch history"
+ },
+ "settings_import_history_help": {
+ "message": "Import History accepts the watch-history.html file from Google Takeout.",
+ "description": "Help text for the import history button"
+ },
+ "settings_import_subs": {
+ "message": "Import Subscriptions",
+ "description": "Button label to import subscriptions"
+ },
+ "settings_import_subs_help": {
+ "message": "Import Subscriptions accepts the subscriptions.csv file from Google Takeout.",
+ "description": "Help text for the import subscriptions button"
+ },
+ "settings_clear_help": {
+ "message": "Clear removes all saved video history (this cannot be undone)",
+ "description": "Help text for the Clear history button"
+ },
+ "settings_local_feed_label": {
+ "message": "Local feed",
+ "description": "Label for the local subscriptions feed toggle"
+ },
+ "settings_local_feed_help": {
+ "message": "Use locally subscribed channels to build the YT re:Watch feed",
+ "description": "Help text for the local subscriptions feed toggle"
+ },
+ "settings_hide_recs_label": {
+ "message": "Hide recommendations & popups",
+ "description": "Label for the hide recommendations toggle"
+ },
+ "settings_hide_recs_help": {
+ "message": "Hide YouTube's home recommendations and \"history is off\" nag popups.",
+ "description": "Help text for the hide recommendations toggle"
+ },
+ "settings_hide_account_label": {
+ "message": "Hide login & account UI",
+ "description": "Label for the hide account UI toggle"
+ },
+ "settings_hide_account_help": {
+ "message": "Hide YouTube's sign-in buttons, account menu, and native Subscribe/Join buttons (your local Subscribe stays).",
+ "description": "Help text for the hide account UI toggle"
+ },
+ "settings_feed_refresh_label": {
+ "message": "Feed refresh interval (minutes)",
+ "description": "Label for the feed refresh interval input"
+ },
+ "settings_feed_refresh_help": {
+ "message": "How often the local feed checks subscribed channels for new uploads",
+ "description": "Help text for the feed refresh interval input"
+ },
+ "subscriptions_refresh": {
+ "message": "Refresh feed",
+ "description": "Button label to refresh the local subscriptions feed"
+ },
+ "subscriptions_open_feed": {
+ "message": "Open YT re:Watch",
+ "description": "Button label to open the dedicated feed page"
+ },
+ "subscriptions_clear_all": {
+ "message": "Clear all",
+ "description": "Button label to remove all local subscriptions"
+ },
+ "subscriptions_no_subs": {
+ "message": "No local subscriptions yet. Search in the extension and use Subscribe.",
+ "description": "Empty state for the subscriptions tab"
+ },
+ "subscriptions_unsubscribe": {
+ "message": "Unsubscribe",
+ "description": "Button label to remove a local subscription"
+ },
+ "subscriptions_refreshing": {
+ "message": "Refreshing…",
+ "description": "Refresh button label while a refresh is in progress"
+ },
+ "subscriptions_refresh_started": {
+ "message": "Refreshing feed in your YouTube tab…",
+ "description": "Message shown when a feed refresh starts"
+ },
+ "subscriptions_open_youtube": {
+ "message": "Open a YouTube tab to refresh the feed.",
+ "description": "Message shown when no YouTube tab is open to refresh the feed"
+ },
+ "message_settings_saved": {
+ "message": "Settings saved",
+ "description": "Generic confirmation that settings were saved"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/de/tabs.json b/src/_locales/de/tabs.json
index 2d65fbf..f82bfbf 100644
--- a/src/_locales/de/tabs.json
+++ b/src/_locales/de/tabs.json
@@ -226,5 +226,25 @@
"playlists_unknown_title": {
"message": "Unbekannte Playlist",
"description": "Fallback title for playlists with no title"
+ },
+ "tab_subscriptions": {
+ "message": "Subs",
+ "description": "Tab label for local subscriptions"
+ },
+ "tab_watchlater": {
+ "message": "Later",
+ "description": "Tab label for local Watch Later list"
+ },
+ "watchlater_empty": {
+ "message": "Nothing saved yet. On YouTube, right-click a video and choose \"Save to Watch Later (local)\".",
+ "description": "Empty state for the Watch Later tab"
+ },
+ "watchlater_clear_all": {
+ "message": "Clear all",
+ "description": "Button to remove all Watch Later items"
+ },
+ "watchlater_confirm_clear": {
+ "message": "Clear all Watch Later items?",
+ "description": "Confirmation prompt before clearing the Watch Later list"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/en/feed.json b/src/_locales/en/feed.json
new file mode 100644
index 0000000..e98e6ee
--- /dev/null
+++ b/src/_locales/en/feed.json
@@ -0,0 +1,506 @@
+{
+ "feed_menu": {
+ "message": "Menu",
+ "description": "Menu button label"
+ },
+ "feed_back_to_feed": {
+ "message": "Back to your feed",
+ "description": "Tooltip for returning to the feed"
+ },
+ "feed_search_youtube": {
+ "message": "Search YouTube",
+ "description": "Search field placeholder"
+ },
+ "feed_refresh": {
+ "message": "Refresh",
+ "description": "Refresh button label"
+ },
+ "feed_create": {
+ "message": "Create",
+ "description": "Create button label"
+ },
+ "feed_save": {
+ "message": "Save",
+ "description": "Save button label"
+ },
+ "feed_clear_all": {
+ "message": "Clear all",
+ "description": "Clear all button label"
+ },
+ "feed_options": {
+ "message": "Options",
+ "description": "Options menu label"
+ },
+ "feed_unwatched_only": {
+ "message": "Unwatched only",
+ "description": "Filter label for only unwatched videos"
+ },
+ "feed_hide_members": {
+ "message": "Hide member videos",
+ "description": "Filter label for hiding members-only or members-first videos"
+ },
+ "feed_nav_home": {
+ "message": "Home",
+ "description": "Home navigation label"
+ },
+ "feed_nav_home_title": {
+ "message": "Personalized locally from your watch history",
+ "description": "Home navigation tooltip"
+ },
+ "feed_nav_subscriptions": {
+ "message": "Subscriptions",
+ "description": "Subscriptions navigation label"
+ },
+ "feed_nav_subscriptions_title": {
+ "message": "Latest videos from your subscribed channels",
+ "description": "Subscriptions navigation tooltip"
+ },
+ "feed_nav_shorts": {
+ "message": "Shorts",
+ "description": "Shorts navigation label"
+ },
+ "feed_nav_playlists": {
+ "message": "Playlists",
+ "description": "Playlists navigation label"
+ },
+ "feed_nav_playlists_title": {
+ "message": "View saved playlists",
+ "description": "Playlists navigation tooltip"
+ },
+ "feed_nav_history": {
+ "message": "History",
+ "description": "History navigation label"
+ },
+ "feed_nav_history_title": {
+ "message": "View your local watch history",
+ "description": "History navigation tooltip"
+ },
+ "feed_nav_channels": {
+ "message": "Channels",
+ "description": "Channels navigation label"
+ },
+ "feed_nav_channels_title": {
+ "message": "Manage your locally subscribed channels",
+ "description": "Channels navigation tooltip"
+ },
+ "feed_nav_analytics": {
+ "message": "Analytics",
+ "description": "Analytics navigation label"
+ },
+ "feed_nav_analytics_title": {
+ "message": "View your watch analytics",
+ "description": "Analytics navigation tooltip"
+ },
+ "feed_nav_settings": {
+ "message": "Settings",
+ "description": "Settings navigation label"
+ },
+ "feed_nav_settings_title": {
+ "message": "Open extension settings",
+ "description": "Settings navigation tooltip"
+ },
+ "feed_your_feed": {
+ "message": "Your feed",
+ "description": "Main feed heading"
+ },
+ "feed_search_options": {
+ "message": "Search options",
+ "description": "Search options aria label"
+ },
+ "feed_filters": {
+ "message": "Filters",
+ "description": "Filters button label"
+ },
+ "feed_upload_date": {
+ "message": "Upload date",
+ "description": "Upload date filter label"
+ },
+ "feed_today": {
+ "message": "Today",
+ "description": "Today filter label"
+ },
+ "feed_this_week": {
+ "message": "This week",
+ "description": "This week filter label"
+ },
+ "feed_this_month": {
+ "message": "This month",
+ "description": "This month filter label"
+ },
+ "feed_this_year": {
+ "message": "This year",
+ "description": "This year filter label"
+ },
+ "feed_duration": {
+ "message": "Duration",
+ "description": "Duration filter label"
+ },
+ "feed_under_4_minutes": {
+ "message": "Under 4 minutes",
+ "description": "Under 4 minutes filter label"
+ },
+ "feed_4_to_20_minutes": {
+ "message": "4-20 minutes",
+ "description": "4 to 20 minutes filter label"
+ },
+ "feed_over_20_minutes": {
+ "message": "Over 20 minutes",
+ "description": "Over 20 minutes filter label"
+ },
+ "feed_watch_status": {
+ "message": "Watch status",
+ "description": "Watch status filter label"
+ },
+ "feed_unwatched": {
+ "message": "Unwatched",
+ "description": "Unwatched filter label"
+ },
+ "feed_in_progress": {
+ "message": "In progress",
+ "description": "In progress filter label"
+ },
+ "feed_watched": {
+ "message": "Watched",
+ "description": "Watched filter label"
+ },
+ "feed_sort_search_results": {
+ "message": "Sort search results",
+ "description": "Sort search results aria label"
+ },
+ "feed_sort": {
+ "message": "Sort",
+ "description": "Sort filter label"
+ },
+ "feed_newest": {
+ "message": "Newest",
+ "description": "Newest sort label"
+ },
+ "feed_oldest": {
+ "message": "Oldest",
+ "description": "Oldest sort label"
+ },
+ "feed_search_results_for": {
+ "message": "Search results for",
+ "description": "Search results heading prefix"
+ },
+ "feed_back_to_results": {
+ "message": "Back to results",
+ "description": "Back to results button label"
+ },
+ "feed_no_channel_videos": {
+ "message": "No videos found for this channel yet.",
+ "description": "Empty state for channel videos"
+ },
+ "feed_channels": {
+ "message": "Channels",
+ "description": "Channels label"
+ },
+ "feed_no_local_channels": {
+ "message": "No locally subscribed channels yet. Search in the extension and use Subscribe.",
+ "description": "Empty state for local channels"
+ },
+ "feed_playlists": {
+ "message": "Playlists",
+ "description": "Playlists label"
+ },
+ "feed_no_saved_playlists": {
+ "message": "No saved playlists yet.",
+ "description": "Empty state for saved playlists"
+ },
+ "feed_history": {
+ "message": "History",
+ "description": "History label"
+ },
+ "feed_no_history_matches": {
+ "message": "No watch history matches these filters.",
+ "description": "Empty state for filtered history"
+ },
+ "feed_load_more": {
+ "message": "Load more",
+ "description": "Load more button label"
+ },
+ "feed_analytics": {
+ "message": "Analytics",
+ "description": "Analytics label"
+ },
+ "feed_analytics_subtitle": {
+ "message": "your watch stats, kept locally",
+ "description": "Analytics heading subtitle"
+ },
+ "feed_watch_activity_7_days": {
+ "message": "Watch activity (last 7 days)",
+ "description": "Watch activity chart title"
+ },
+ "feed_watch_time_by_hour": {
+ "message": "Watch time by hour",
+ "description": "Watch time by hour chart title"
+ },
+ "feed_how_much_finish": {
+ "message": "How much you finish",
+ "description": "Completion analytics title"
+ },
+ "feed_top_channels": {
+ "message": "Top channels",
+ "description": "Top channels analytics title"
+ },
+ "feed_continue_watching": {
+ "message": "Continue watching",
+ "description": "Continue watching analytics title"
+ },
+ "feed_settings": {
+ "message": "Settings",
+ "description": "Settings label"
+ },
+ "feed_settings_subtitle": {
+ "message": "Everything stays local in this browser.",
+ "description": "Settings subtitle"
+ },
+ "feed_settings_display": {
+ "message": "Display",
+ "description": "Display settings section title"
+ },
+ "feed_settings_theme": {
+ "message": "Theme",
+ "description": "Theme setting label"
+ },
+ "feed_system_default": {
+ "message": "System default",
+ "description": "System default theme label"
+ },
+ "feed_dark": {
+ "message": "Dark",
+ "description": "Dark theme label"
+ },
+ "feed_light": {
+ "message": "Light",
+ "description": "Light theme label"
+ },
+ "feed_accent_color": {
+ "message": "Accent color",
+ "description": "Accent color setting label"
+ },
+ "feed_blue": {
+ "message": "Blue",
+ "description": "Blue color label"
+ },
+ "feed_red": {
+ "message": "Red",
+ "description": "Red color label"
+ },
+ "feed_green": {
+ "message": "Green",
+ "description": "Green color label"
+ },
+ "feed_purple": {
+ "message": "Purple",
+ "description": "Purple color label"
+ },
+ "feed_orange": {
+ "message": "Orange",
+ "description": "Orange color label"
+ },
+ "feed_default_tab": {
+ "message": "Default tab",
+ "description": "Default tab setting label"
+ },
+ "feed_last_used_tab": {
+ "message": "Last used tab",
+ "description": "Last used tab option label"
+ },
+ "feed_history_and_feed": {
+ "message": "History & feed",
+ "description": "History and feed settings section title"
+ },
+ "feed_check_new_videos": {
+ "message": "Check for new videos",
+ "description": "Check for new videos setting label"
+ },
+ "feed_every_15_minutes": {
+ "message": "Every 15 minutes",
+ "description": "Every 15 minutes interval label"
+ },
+ "feed_every_30_minutes": {
+ "message": "Every 30 minutes",
+ "description": "Every 30 minutes interval label"
+ },
+ "feed_every_hour": {
+ "message": "Every hour",
+ "description": "Every hour interval label"
+ },
+ "feed_every_3_hours": {
+ "message": "Every 3 hours",
+ "description": "Every 3 hours interval label"
+ },
+ "feed_every_6_hours": {
+ "message": "Every 6 hours",
+ "description": "Every 6 hours interval label"
+ },
+ "feed_every_12_hours": {
+ "message": "Every 12 hours",
+ "description": "Every 12 hours interval label"
+ },
+ "feed_once_a_day": {
+ "message": "Once a day",
+ "description": "Once a day interval label"
+ },
+ "feed_keep_watch_history": {
+ "message": "Keep watch history",
+ "description": "Keep watch history setting label"
+ },
+ "feed_30_days": {
+ "message": "30 days",
+ "description": "30 days option label"
+ },
+ "feed_90_days": {
+ "message": "90 days",
+ "description": "90 days option label"
+ },
+ "feed_180_days": {
+ "message": "180 days",
+ "description": "180 days option label"
+ },
+ "feed_forever": {
+ "message": "Forever",
+ "description": "Forever option label"
+ },
+ "feed_data": {
+ "message": "Data",
+ "description": "Data settings section title"
+ },
+ "feed_backup_restore": {
+ "message": "Backup & restore",
+ "description": "Backup and restore title"
+ },
+ "feed_backup_restore_help": {
+ "message": "Download or load a JSON file with history, subscriptions, playlists, and settings.",
+ "description": "Backup and restore help text"
+ },
+ "feed_backup": {
+ "message": "Backup",
+ "description": "Backup button label"
+ },
+ "feed_restore": {
+ "message": "Restore",
+ "description": "Restore button label"
+ },
+ "feed_import_from_youtube": {
+ "message": "Import from YouTube",
+ "description": "Import from YouTube title"
+ },
+ "feed_import_from_youtube_help": {
+ "message": "Use the watch-history.html file for your watch history and the subscriptions.csv file for your subscribed channels from Google Takeout.",
+ "description": "Import from YouTube help text"
+ },
+ "feed_reset_all_data": {
+ "message": "Reset all data",
+ "description": "Reset all data title"
+ },
+ "feed_reset_all_data_help": {
+ "message": "Delete everything, including subscriptions, settings, and backups.",
+ "description": "Reset all data help text"
+ },
+ "feed_reset_all": {
+ "message": "Reset all",
+ "description": "Reset all button label"
+ },
+ "feed_add_to_playlist": {
+ "message": "Add to playlist",
+ "description": "Add to playlist dialog title"
+ },
+ "feed_close": {
+ "message": "Close",
+ "description": "Close button aria label"
+ },
+ "feed_create_new_playlist": {
+ "message": "Create new playlist",
+ "description": "Create new playlist placeholder"
+ },
+ "feed_video_options": {
+ "message": "Video options",
+ "description": "feed video options"
+ },
+ "feed_more_from_channel": {
+ "message": "More from this channel",
+ "description": "feed more from channel"
+ },
+ "feed_less_from_channel": {
+ "message": "Less from this channel",
+ "description": "feed less from channel"
+ },
+ "feed_hide_this_video": {
+ "message": "Hide this video",
+ "description": "feed hide this video"
+ },
+ "feed_unsubscribe": {
+ "message": "Unsubscribe",
+ "description": "feed unsubscribe"
+ },
+ "feed_not_subscribed": {
+ "message": "Not subscribed",
+ "description": "feed not subscribed"
+ },
+ "feed_unsubscribed": {
+ "message": "Unsubscribed",
+ "description": "feed unsubscribed"
+ },
+ "feed_save_to_watch_later": {
+ "message": "Save to Watch Later",
+ "description": "feed save to watch later"
+ },
+ "feed_saved_to_watch_later": {
+ "message": "Saved to Watch Later",
+ "description": "feed saved to watch later"
+ },
+ "feed_added_to_playlist": {
+ "message": "Added to playlist",
+ "description": "feed added to playlist"
+ },
+ "feed_set_as_playlist_cover": {
+ "message": "Set as playlist cover",
+ "description": "feed set as playlist cover"
+ },
+ "feed_cover_updated": {
+ "message": "Cover updated",
+ "description": "feed cover updated"
+ },
+ "feed_remove_from_playlist": {
+ "message": "Remove from this playlist",
+ "description": "feed remove from playlist"
+ },
+ "feed_this_channel": {
+ "message": "this channel",
+ "description": "feed this channel"
+ },
+ "feed_channel": {
+ "message": "channel",
+ "description": "feed channel"
+ },
+ "feed_recommend_more_from_status": {
+ "message": "We'll recommend more from $1.",
+ "description": "feed recommend more from status"
+ },
+ "feed_recommend_more_status": {
+ "message": "We'll recommend more",
+ "description": "feed recommend more status"
+ },
+ "feed_unsubscribed_from_status": {
+ "message": "Unsubscribed from $1.",
+ "description": "feed unsubscribed from status"
+ },
+ "feed_refreshing": {
+ "message": "Refreshing...",
+ "description": "feed refreshing"
+ },
+ "feed_updated": {
+ "message": "Feed updated.",
+ "description": "feed updated"
+ },
+ "feed_no_videos_subscribe_first": {
+ "message": "No videos found. Subscribe to channels first.",
+ "description": "feed no videos subscribe first"
+ },
+ "feed_refresh_failed_status": {
+ "message": "Refresh failed: $1. If YouTube shows a consent wall, open youtube.com once in this browser.",
+ "description": "feed refresh failed status"
+ }
+}
diff --git a/src/_locales/en/messages-group.json b/src/_locales/en/messages-group.json
index d725633..dc2dfe1 100644
--- a/src/_locales/en/messages-group.json
+++ b/src/_locales/en/messages-group.json
@@ -1,242 +1,130 @@
{
- "message_no_active_tab": {
- "message": "No active tab found.",
- "description": "Error message when no active tab is found"
- },
- "message_video_removed": {
- "message": "Video removed from history",
- "description": "Success message when a video is removed from history"
- },
- "message_error_removing_video": {
- "message": "Error removing video: $1",
- "description": "Error message when removing a video fails, $1 is the error detail"
- },
- "message_history_cleared": {
- "message": "History cleared successfully",
- "description": "Success message when history is cleared"
- },
- "message_error_clearing_history": {
- "message": "Error clearing history: $1",
- "description": "Error message when clearing history fails, $1 is the error detail"
- },
- "message_export_success": {
- "message": "History exported successfully: $1 videos, $2 playlists",
- "description": "Success message when history is exported, $1 is video count, $2 is playlist count"
- },
- "message_error_exporting_history": {
- "message": "Error exporting history: $1",
- "description": "Error message when exporting history fails, $1 is the error detail"
- },
- "message_import_success": {
- "message": "History $1 successfully: $2 videos, $3 playlists",
- "description": "Success message when history is imported, $1 is mode, $2 is video count, $3 is playlist count"
- },
- "message_error_importing_history": {
- "message": "Error importing history: $1",
- "description": "Error message when importing history fails, $1 is the error detail"
- },
- "message_no_data_in_import": {
- "message": "No data found in import file",
- "description": "Error message when no data is found in import file"
- },
- "message_invalid_file_format": {
- "message": "Invalid file format: $1",
- "description": "Error message for invalid import file format, $1 is the detail"
- },
- "message_confirm_clear_history": {
- "message": "Are you sure you want to clear all history? This cannot be undone.",
- "description": "Confirmation for clearing all history."
- },
- "message_confirm_full_sync": {
- "message": "Full Sync will clean up old data and re-sync everything. This may take a moment. Continue?",
- "description": "Confirmation for full sync."
- },
- "message_warning_clear_all": {
- "message": "WARNING: This will permanently delete ALL your YouTube viewing history and playlists. This cannot be undone. Continue?",
- "description": "Warning for clearing all history and playlists."
- },
- "message_all_history_cleared": {
- "message": "All video and playlist history has been cleared successfully",
- "description": "Success message when all history and playlists are cleared"
- },
- "message_export_completed": {
- "message": "Export completed successfully",
- "description": "Success message when export is completed"
- },
- "message_failed_to_initialize": {
- "message": "Failed to initialize extension: $1",
- "description": "Error message when extension initialization fails, $1 is the error detail"
- },
- "message_error_loading_history": {
- "message": "Error loading history: $1",
- "description": "Error message when loading history fails, $1 is the error detail"
- },
- "message_no_playlists_found": {
- "message": "No playlists found.",
- "description": "Message when no playlists are found"
- },
- "message_error_loading_playlists": {
- "message": "Error loading playlists: $1",
- "description": "Error message when loading playlists fails, $1 is the error detail"
- },
- "message_playlist_removed": {
- "message": "Playlist removed",
- "description": "Success message when a playlist is removed"
- },
- "message_error_removing_playlist": {
- "message": "Error removing playlist: $1",
- "description": "Error message when removing a playlist fails, $1 is the error detail"
- },
- "message_auto_clean_updated": {
- "message": "Auto-clean period updated",
- "description": "Success message when auto-clean period is updated"
- },
- "message_pagination_count_updated": {
- "message": "Pagination count updated",
- "description": "Message when pagination count is updated"
- },
- "message_overlay_title_updated": {
- "message": "Overlay title updated",
- "description": "Message when overlay title is updated"
- },
- "message_overlay_color_updated": {
- "message": "Overlay color updated",
- "description": "Message when overlay color is updated"
- },
- "message_overlay_size_updated": {
- "message": "Overlay size updated",
- "description": "Message when overlay label size is updated"
- },
- "message_theme_preference_updated": {
- "message": "Theme preference updated",
- "description": "Message when theme preference is updated"
- },
- "message_debug_mode_enabled": {
- "message": "Debug mode enabled",
- "description": "Message when debug mode is enabled"
- },
- "message_debug_mode_disabled": {
- "message": "Debug mode disabled",
- "description": "Message when debug mode is disabled"
- },
- "message_sync_disabled": {
- "message": "Sync disabled - Click to enable",
- "description": "Tooltip for disabled sync status"
- },
- "message_sync_not_available": {
- "message": "Sync not available - Firefox Sync required",
- "description": "Tooltip for unavailable sync status"
- },
- "message_sync_initializing": {
- "message": "Initializing sync...",
- "description": "Tooltip for initializing sync status"
- },
- "message_sync_syncing": {
- "message": "Syncing data...",
- "description": "Tooltip for syncing data status"
- },
- "message_sync_ready": {
- "message": "Sync ready",
- "description": "Tooltip for sync ready status"
- },
- "message_sync_error": {
- "message": "Sync error - Click to retry",
- "description": "Tooltip for sync error status"
- },
- "message_sync_off": {
- "message": "Off",
- "description": "Label for sync off status"
- },
- "message_sync_na": {
- "message": "N/A",
- "description": "Label for sync not available status"
- },
- "message_sync_init": {
- "message": "Init",
- "description": "Label for sync initializing status"
- },
- "message_sync_on": {
- "message": "On",
- "description": "Label for sync on status"
- },
- "message_sync": {
- "message": "Sync",
- "description": "Label for sync status"
- },
- "message_syncing": {
- "message": "Syncing...",
- "description": "Label for syncing status"
- },
- "message_synced": {
- "message": "Synced",
- "description": "Label for synced status"
- },
- "message_sync_now": {
- "message": "Sync Now",
- "description": "Button label for sync now"
- },
- "message_full_sync": {
- "message": "Full Sync",
- "description": "Button label for full sync"
- },
- "message_syncing_status": {
- "message": "Syncing...",
- "description": "Status text for syncing"
- },
- "message_synced_status": {
- "message": "Synced",
- "description": "Status text for synced"
- },
- "message_never": {
- "message": "Never",
- "description": "Label for never synced"
- },
- "message_just_now": {
- "message": "Just now",
- "description": "Label for just now sync time"
- },
- "message_minutes_ago": {
- "message": "{MINUTES}m ago",
- "description": "Label for minutes ago sync time"
- },
- "message_hours_ago": {
- "message": "{HOURS}h ago",
- "description": "Label for hours ago sync time"
- },
- "message_days_ago": {
- "message": "{DAYS}d ago",
- "description": "Label for days ago sync time"
- },
- "message_firefox_sync_not_available": {
- "message": "Firefox Sync is not available. Please enable Firefox Sync in your browser settings.",
- "description": "Error message when Firefox Sync is not available"
- },
- "message_failed_to_enable_sync": {
- "message": "Failed to enable sync. Please check your Firefox Sync settings.",
- "description": "Error message when enabling sync fails"
- },
- "message_failed_to_disable_sync": {
- "message": "Failed to disable sync.",
- "description": "Error message when disabling sync fails"
- },
- "message_sync_currently_disabled": {
- "message": "Sync is currently disabled.",
- "description": "Error message when sync is currently disabled"
- },
- "message_full_sync_initiated": {
- "message": "Full sync initiated... This may take a moment.",
- "description": "Message when full sync is initiated"
- },
- "settings_convert_history": {
- "message": "Convert YouTube history",
- "description": "Button label to open the community converter tool"
- },
- "settings_convert_history_help": {
- "message": "Open the community tool to convert Google Takeout watch-history.json into a YT re:Watch import file.",
- "description": "Help text for the community converter tool link"
- },
- "message_unknown_error": {
- "message": "Unknown error",
- "description": "Fallback for unknown error details"
- }
-}
\ No newline at end of file
+ "message_no_active_tab": {
+ "message": "No active tab found.",
+ "description": "Error message when no active tab is found"
+ },
+ "message_video_removed": {
+ "message": "Video removed from history",
+ "description": "Success message when a video is removed from history"
+ },
+ "message_error_removing_video": {
+ "message": "Error removing video: $1",
+ "description": "Error message when removing a video fails, $1 is the error detail"
+ },
+ "message_history_cleared": {
+ "message": "History cleared successfully",
+ "description": "Success message when history is cleared"
+ },
+ "message_error_clearing_history": {
+ "message": "Error clearing history: $1",
+ "description": "Error message when clearing history fails, $1 is the error detail"
+ },
+ "message_export_success": {
+ "message": "History exported successfully: $1 videos, $2 playlists",
+ "description": "Success message when history is exported, $1 is video count, $2 is playlist count"
+ },
+ "message_error_exporting_history": {
+ "message": "Error exporting history: $1",
+ "description": "Error message when exporting history fails, $1 is the error detail"
+ },
+ "message_import_success": {
+ "message": "History $1 successfully: $2 videos, $3 playlists",
+ "description": "Success message when history is imported, $1 is mode, $2 is video count, $3 is playlist count"
+ },
+ "message_error_importing_history": {
+ "message": "Error importing history: $1",
+ "description": "Error message when importing history fails, $1 is the error detail"
+ },
+ "message_no_data_in_import": {
+ "message": "No data found in import file",
+ "description": "Error message when no data is found in import file"
+ },
+ "message_invalid_file_format": {
+ "message": "Invalid file format: $1",
+ "description": "Error message for invalid import file format, $1 is the detail"
+ },
+ "message_confirm_clear_history": {
+ "message": "Are you sure you want to clear all history? This cannot be undone.",
+ "description": "Confirmation for clearing all history."
+ },
+ "message_warning_clear_all": {
+ "message": "WARNING: This will permanently delete ALL your YouTube viewing history and playlists. This cannot be undone. Continue?",
+ "description": "Warning for clearing all history and playlists."
+ },
+ "message_all_history_cleared": {
+ "message": "All video and playlist history has been cleared successfully",
+ "description": "Success message when all history and playlists are cleared"
+ },
+ "message_export_completed": {
+ "message": "Export completed successfully",
+ "description": "Success message when export is completed"
+ },
+ "message_failed_to_initialize": {
+ "message": "Failed to initialize extension: $1",
+ "description": "Error message when extension initialization fails, $1 is the error detail"
+ },
+ "message_error_loading_history": {
+ "message": "Error loading history: $1",
+ "description": "Error message when loading history fails, $1 is the error detail"
+ },
+ "message_no_playlists_found": {
+ "message": "No playlists found.",
+ "description": "Message when no playlists are found"
+ },
+ "message_error_loading_playlists": {
+ "message": "Error loading playlists: $1",
+ "description": "Error message when loading playlists fails, $1 is the error detail"
+ },
+ "message_playlist_removed": {
+ "message": "Playlist removed",
+ "description": "Success message when a playlist is removed"
+ },
+ "message_error_removing_playlist": {
+ "message": "Error removing playlist: $1",
+ "description": "Error message when removing a playlist fails, $1 is the error detail"
+ },
+ "message_auto_clean_updated": {
+ "message": "Auto-clean period updated",
+ "description": "Success message when auto-clean period is updated"
+ },
+ "message_pagination_count_updated": {
+ "message": "Pagination count updated",
+ "description": "Message when pagination count is updated"
+ },
+ "message_overlay_title_updated": {
+ "message": "Overlay title updated",
+ "description": "Message when overlay title is updated"
+ },
+ "message_overlay_color_updated": {
+ "message": "Overlay color updated",
+ "description": "Message when overlay color is updated"
+ },
+ "message_overlay_size_updated": {
+ "message": "Overlay size updated",
+ "description": "Message when overlay label size is updated"
+ },
+ "message_theme_preference_updated": {
+ "message": "Theme preference updated",
+ "description": "Message when theme preference is updated"
+ },
+ "message_debug_mode_enabled": {
+ "message": "Debug mode enabled",
+ "description": "Message when debug mode is enabled"
+ },
+ "message_debug_mode_disabled": {
+ "message": "Debug mode disabled",
+ "description": "Message when debug mode is disabled"
+ },
+ "settings_convert_history": {
+ "message": "Convert YouTube history",
+ "description": "Button label to open the community converter tool"
+ },
+ "settings_convert_history_help": {
+ "message": "Open the community tool to convert Google Takeout watch-history.json into a YT re:Watch import file.",
+ "description": "Help text for the community converter tool link"
+ },
+ "message_unknown_error": {
+ "message": "Unknown error",
+ "description": "Fallback for unknown error details"
+ }
+}
diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json
index f19b54b..e801ed4 100644
--- a/src/_locales/en/messages.json
+++ b/src/_locales/en/messages.json
@@ -1,191 +1,166 @@
{
- "extensionName": {
- "message": "YouTube Local History",
- "description": "The name of the extension"
- },
- "popup_title": {
- "message": "YT re:Watch",
- "description": "Popup window title and main heading"
- },
- "theme_toggle": {
- "message": "Theme",
- "description": "Button label for toggling theme"
- },
- "theme_toggle_tooltip": {
- "message": "Toggle theme",
- "description": "Tooltip for theme toggle button"
- },
- "theme_light": {
- "message": "Light",
- "description": "Label for light theme option"
- },
- "theme_dark": {
- "message": "Dark",
- "description": "Label for dark theme option"
- },
- "theme_auto": {
- "message": "Auto",
- "description": "Label for auto/system theme option"
- },
- "theme_switch_to_dark": {
- "message": "Switch to dark theme",
- "description": "Tooltip for switching to dark theme"
- },
- "theme_switch_to_light": {
- "message": "Switch to light theme",
- "description": "Tooltip for switching to light theme"
- },
- "theme_system": {
- "message": "System Default",
- "description": "Label for system default theme option"
- },
- "theme_currently_following_system": {
- "message": "Currently following system theme - Click to override",
- "description": "Tooltip for auto theme option"
- },
- "clear_label": {
- "message": "Clear",
- "description": "Button label for clearing all history"
- },
- "clear_tooltip": {
- "message": "Clear all history",
- "description": "Tooltip for clear history button"
- },
- "close_label": {
- "message": "Close",
- "description": "Button label for closing the popup"
- },
- "sync_status": {
- "message": "Sync",
- "description": "Label for sync status indicator"
- },
- "sync_status_tooltip": {
- "message": "Sync status",
- "description": "Tooltip for sync status indicator"
- },
- "instructions_script_disabled_title": {
- "message": "Script Execution Disabled",
- "description": "Title for script execution disabled instructions"
- },
- "instructions_script_disabled_body": {
- "message": "If you are seeing this message, it means that script execution is disabled for this extension. Please follow these steps to enable it:",
- "description": "Body text for script execution disabled instructions"
- },
- "instructions_script_disabled_step1": {
- "message": "Go to Firefox settings: about:preferences#privacy",
- "description": "Step 1 for enabling script execution"
- },
- "instructions_script_disabled_step2": {
- "message": "Ensure \"Enhanced Tracking Protection\" is set to \"Standard\" or \"Custom\" and does not block scripts",
- "description": "Step 2 for enabling script execution"
- },
- "instructions_script_disabled_step3": {
- "message": "Check for any content blocking: click the shield icon in the address bar when the popup is open and ensure no scripts are blocked",
- "description": "Step 3 for enabling script execution"
- },
- "delete_label": {
- "message": "Delete",
- "description": "Button label for deleting an item (video, playlist, short)"
- },
- "pagination_page_info": {
- "message": "Page $1 of $2",
- "description": "Pagination info, e.g. Page 1 of 11"
- },
- "chart_skipped": {
- "message": "Skipped",
- "description": "Chart label: Skipped"
- },
- "chart_partial": {
- "message": "Partial",
- "description": "Chart label: Partial"
- },
- "chart_completed": {
- "message": "Completed",
- "description": "Chart label: Completed"
- },
- "chart_skipped_legend": {
- "message": "Skipped (<10%)",
- "description": "Chart legend: Skipped (<10%)"
- },
- "chart_partial_legend": {
- "message": "Partial (10-90%)",
- "description": "Chart legend: Partial (10-90%)"
- },
- "chart_completed_legend": {
- "message": "Completed (>=90%)",
- "description": "Chart legend: Completed (>=90%)"
- },
- "chart_minutes": {
- "message": "Minutes",
- "description": "Chart axis label: Minutes"
- },
- "chart_hour_of_day": {
- "message": "Hour of Day",
- "description": "Chart axis label: Hour of Day"
- },
- "message_pause_in_playlists_enabled": {
- "message": "Paused history in playlists enabled",
- "description": "Toast when global pause in playlists is enabled"
- },
- "message_pause_in_playlists_disabled": {
- "message": "Paused history in playlists disabled",
- "description": "Toast when global pause in playlists is disabled"
- },
- "date_today": {
- "message": "Today",
- "description": "Label for today's date"
- },
- "date_yesterday": {
- "message": "Yesterday",
- "description": "Label for yesterday's date"
- },
- "date_unknown": {
- "message": "Unknown",
- "description": "Label for unknown date"
- },
- "search_no_videos_found": {
- "message": "No videos found matching your search.",
- "description": "Message when no videos match the search"
- },
- "history_no_history_found": {
- "message": "No history found. Start watching some videos!",
- "description": "Message when no history is found"
- }
- ,
- "playlists_ignore_toggle_label": {
- "message": "Ignore",
- "description": "Label next to playlist ignore checkbox"
- },
- "playlists_ignore_toggle_tooltip": {
- "message": "Ignore videos in this playlist",
- "description": "Tooltip for playlist ignore checkbox"
- },
- "playlists_ignore_enabled": {
- "message": "Playlist will be ignored",
- "description": "Toast when ignore is enabled"
- },
- "playlists_ignore_disabled": {
- "message": "Playlist will be tracked",
- "description": "Toast when ignore is disabled"
- },
- "content_toggle_pause": {
- "message": "re:Watch — Click to pause history",
- "description": "Button text when tracking is active"
- },
- "content_toggle_paused": {
- "message": "re:Watch — History paused. Click to activate",
- "description": "Button text when tracking is paused"
- },
- "content_toggle_pause_title": {
- "message": "re:Watch — Click to pause history for this playlist",
- "description": "Tooltip when tracking is active"
- },
- "content_toggle_paused_title": {
- "message": "re:Watch — History is paused for this playlist. Click to activate tracking",
- "description": "Tooltip when tracking is paused"
- },
- "global_search_placeholder": {
- "message": "Search all content...",
- "description": "Placeholder text for the global search input"
- }
-}
\ No newline at end of file
+ "extensionName": {
+ "message": "YT re:Watch",
+ "description": "The name of the extension"
+ },
+ "popup_title": {
+ "message": "YT re:Watch",
+ "description": "Popup window title and main heading"
+ },
+ "theme_toggle": {
+ "message": "Theme",
+ "description": "Button label for toggling theme"
+ },
+ "theme_toggle_tooltip": {
+ "message": "Toggle theme",
+ "description": "Tooltip for theme toggle button"
+ },
+ "theme_light": {
+ "message": "Light",
+ "description": "Label for light theme option"
+ },
+ "theme_dark": {
+ "message": "Dark",
+ "description": "Label for dark theme option"
+ },
+ "theme_auto": {
+ "message": "Auto",
+ "description": "Label for auto/system theme option"
+ },
+ "theme_switch_to_dark": {
+ "message": "Switch to dark theme",
+ "description": "Tooltip for switching to dark theme"
+ },
+ "theme_switch_to_light": {
+ "message": "Switch to light theme",
+ "description": "Tooltip for switching to light theme"
+ },
+ "theme_system": {
+ "message": "System Default",
+ "description": "Label for system default theme option"
+ },
+ "theme_currently_following_system": {
+ "message": "Currently following system theme - Click to override",
+ "description": "Tooltip for auto theme option"
+ },
+ "clear_label": {
+ "message": "Clear",
+ "description": "Button label for clearing all history"
+ },
+ "clear_tooltip": {
+ "message": "Clear all history",
+ "description": "Tooltip for clear history button"
+ },
+ "close_label": {
+ "message": "Close",
+ "description": "Button label for closing the popup"
+ },
+ "instructions_script_disabled_title": {
+ "message": "Script Execution Disabled",
+ "description": "Title for script execution disabled instructions"
+ },
+ "instructions_script_disabled_body": {
+ "message": "If you are seeing this message, it means that script execution is disabled for this extension. Please follow these steps to enable it:",
+ "description": "Body text for script execution disabled instructions"
+ },
+ "instructions_script_disabled_step1": {
+ "message": "Go to Firefox settings: about:preferences#privacy",
+ "description": "Step 1 for enabling script execution"
+ },
+ "instructions_script_disabled_step2": {
+ "message": "Ensure \"Enhanced Tracking Protection\" is set to \"Standard\" or \"Custom\" and does not block scripts",
+ "description": "Step 2 for enabling script execution"
+ },
+ "instructions_script_disabled_step3": {
+ "message": "Check for any content blocking: click the shield icon in the address bar when the popup is open and ensure no scripts are blocked",
+ "description": "Step 3 for enabling script execution"
+ },
+ "delete_label": {
+ "message": "Delete",
+ "description": "Button label for deleting an item (video, playlist, short)"
+ },
+ "pagination_page_info": {
+ "message": "Page $1 of $2",
+ "description": "Pagination info, e.g. Page 1 of 11"
+ },
+ "chart_skipped": {
+ "message": "Skipped",
+ "description": "Chart label: Skipped"
+ },
+ "chart_partial": {
+ "message": "Partial",
+ "description": "Chart label: Partial"
+ },
+ "chart_completed": {
+ "message": "Completed",
+ "description": "Chart label: Completed"
+ },
+ "chart_skipped_legend": {
+ "message": "Skipped (<10%)",
+ "description": "Chart legend: Skipped (<10%)"
+ },
+ "chart_partial_legend": {
+ "message": "Partial (10-90%)",
+ "description": "Chart legend: Partial (10-90%)"
+ },
+ "chart_completed_legend": {
+ "message": "Completed (>=90%)",
+ "description": "Chart legend: Completed (>=90%)"
+ },
+ "chart_minutes": {
+ "message": "Minutes",
+ "description": "Chart axis label: Minutes"
+ },
+ "chart_hour_of_day": {
+ "message": "Hour of Day",
+ "description": "Chart axis label: Hour of Day"
+ },
+ "message_pause_in_playlists_enabled": {
+ "message": "Paused history in playlists enabled",
+ "description": "Toast when global pause in playlists is enabled"
+ },
+ "message_pause_in_playlists_disabled": {
+ "message": "Paused history in playlists disabled",
+ "description": "Toast when global pause in playlists is disabled"
+ },
+ "date_today": {
+ "message": "Today",
+ "description": "Label for today's date"
+ },
+ "date_yesterday": {
+ "message": "Yesterday",
+ "description": "Label for yesterday's date"
+ },
+ "date_unknown": {
+ "message": "Unknown",
+ "description": "Label for unknown date"
+ },
+ "search_no_videos_found": {
+ "message": "No videos found matching your search.",
+ "description": "Message when no videos match the search"
+ },
+ "history_no_history_found": {
+ "message": "No history found. Start watching some videos!",
+ "description": "Message when no history is found"
+ },
+ "playlists_ignore_toggle_label": {
+ "message": "Ignore",
+ "description": "Label next to playlist ignore checkbox"
+ },
+ "playlists_ignore_toggle_tooltip": {
+ "message": "Ignore videos in this playlist",
+ "description": "Tooltip for playlist ignore checkbox"
+ },
+ "playlists_ignore_enabled": {
+ "message": "Playlist will be ignored",
+ "description": "Toast when ignore is enabled"
+ },
+ "playlists_ignore_disabled": {
+ "message": "Playlist will be tracked",
+ "description": "Toast when ignore is disabled"
+ },
+ "global_search_placeholder": {
+ "message": "Search all content...",
+ "description": "Placeholder text for the global search input"
+ }
+}
diff --git a/src/_locales/en/settings.json b/src/_locales/en/settings.json
index 7d3e6d2..d46fa46 100644
--- a/src/_locales/en/settings.json
+++ b/src/_locales/en/settings.json
@@ -1,122 +1,190 @@
{
- "settings_interface_display": {
- "message": "Interface & Display",
- "description": "Settings section title for interface and display"
- },
- "settings_theme_label": {
- "message": "Theme",
- "description": "Label for theme selection in settings"
- },
- "settings_theme_help": {
- "message": "Choose your preferred theme",
- "description": "Help text for theme selection"
- },
- "settings_pagination_label": {
- "message": "Items per Page",
- "description": "Label for pagination count in settings"
- },
- "settings_pagination_help": {
- "message": "Number of items to show per page (1-100)",
- "description": "Help text for pagination count"
- },
- "settings_overlay_title_label": {
- "message": "Overlay Title",
- "description": "Label for overlay title in settings"
- },
- "settings_overlay_title_help": {
- "message": "Text to show in the overlay (max 12 characters)",
- "description": "Help text for overlay title"
- },
- "settings_overlay_color_label": {
- "message": "Overlay Color",
- "description": "Label for overlay color in settings"
- },
- "settings_overlay_color_help": {
- "message": "Color of the progress bar overlay",
- "description": "Help text for overlay color"
- },
- "settings_overlay_size_label": {
- "message": "Overlay Size",
- "description": "Label for overlay size in settings"
- },
- "settings_overlay_size_help": {
- "message": "Size of the overlay label and progress bar",
- "description": "Help text for overlay size"
- },
- "settings_data_sync": {
- "message": "Data & Sync",
- "description": "Settings section title for data and sync"
- },
- "settings_pause_in_playlists_label": {
- "message": "Pause history in playlists",
- "description": "Label for pause-in-playlists setting"
- },
- "settings_pause_in_playlists_help": {
- "message": "When playing from a playlist, don't save video progress",
- "description": "Help text for pause-in-playlists setting"
- },
- "settings_data_management_label": {
- "message": "Data Management",
- "description": "Label for data management section"
- },
- "settings_export": {
- "message": "Export",
- "description": "Button label for exporting history"
- },
- "settings_import": {
- "message": "Import",
- "description": "Button label for importing history"
- },
- "settings_export_help": {
- "message": "Export your history data (Import coming soon)",
- "description": "Help text for export/import"
- },
- "settings_auto_clean_label": {
- "message": "Auto-clean Period",
- "description": "Label for auto-clean period in settings"
- },
- "settings_auto_clean_help": {
- "message": "Automatically remove history older than selected period",
- "description": "Help text for auto-clean period"
- },
- "settings_sync_label": {
- "message": "Firefox Sync",
- "description": "Label for Firefox Sync option"
- },
- "settings_sync_help": {
- "message": "Sync your data across browser instances (Firefox only)",
- "description": "Help text for Firefox Sync"
- },
- "settings_last_sync_label": {
- "message": "Last Sync",
- "description": "Label for last sync time"
- },
- "settings_sync_now": {
- "message": "Sync Now",
- "description": "Button label for triggering sync"
- },
- "settings_full_sync": {
- "message": "Full Sync",
- "description": "Button label for triggering full sync"
- },
- "settings_full_sync_help": {
- "message": "Full Sync cleans old data and re-syncs everything",
- "description": "Help text for full sync"
- },
- "settings_debug_label": {
- "message": "Debug Mode",
- "description": "Label for debug mode option"
- },
- "settings_debug_help": {
- "message": "Enable debug logging (for troubleshooting)",
- "description": "Help text for debug mode"
- },
- "settings_version_label": {
- "message": "Extension Version",
- "description": "Label for extension version display"
- },
- "settings_save": {
- "message": "Save Settings",
- "description": "Button label for saving settings"
- }
-}
\ No newline at end of file
+ "settings_interface_display": {
+ "message": "Interface & Display",
+ "description": "Settings section title for interface and display"
+ },
+ "settings_data_label": {
+ "message": "Data",
+ "description": "Label for the data section in settings"
+ },
+ "settings_import_history": {
+ "message": "Import History",
+ "description": "Button label to import watch history"
+ },
+ "settings_import_history_help": {
+ "message": "Import History accepts the watch-history.html file from Google Takeout.",
+ "description": "Help text for the import history button"
+ },
+ "settings_import_subs": {
+ "message": "Import Subscriptions",
+ "description": "Button label to import subscriptions"
+ },
+ "settings_import_subs_help": {
+ "message": "Import Subscriptions accepts the subscriptions.csv file from Google Takeout.",
+ "description": "Help text for the import subscriptions button"
+ },
+ "settings_theme_label": {
+ "message": "Theme",
+ "description": "Label for theme selection in settings"
+ },
+ "settings_theme_help": {
+ "message": "Choose your preferred theme",
+ "description": "Help text for theme selection"
+ },
+ "settings_pagination_label": {
+ "message": "Items per Page",
+ "description": "Label for pagination count in settings"
+ },
+ "settings_pagination_help": {
+ "message": "Number of items to show per page (1-100)",
+ "description": "Help text for pagination count"
+ },
+ "settings_overlay_title_label": {
+ "message": "Viewed Label",
+ "description": "Label for overlay title in settings"
+ },
+ "settings_overlay_title_help": {
+ "message": "Text shown on viewed videos inside YT re:Watch",
+ "description": "Help text for overlay title"
+ },
+ "settings_overlay_color_label": {
+ "message": "Viewed Color",
+ "description": "Label for overlay color in settings"
+ },
+ "settings_overlay_color_help": {
+ "message": "Color used for viewed labels and progress bars inside YT re:Watch",
+ "description": "Help text for overlay color"
+ },
+ "settings_overlay_size_label": {
+ "message": "Viewed Size",
+ "description": "Label for overlay size in settings"
+ },
+ "settings_overlay_size_help": {
+ "message": "Size of viewed labels and progress bars inside YT re:Watch",
+ "description": "Help text for overlay size"
+ },
+ "settings_data_sync": {
+ "message": "Data",
+ "description": "Settings section title for data"
+ },
+ "settings_pause_in_playlists_label": {
+ "message": "Pause history in playlists",
+ "description": "Label for pause-in-playlists setting"
+ },
+ "settings_pause_in_playlists_help": {
+ "message": "When playing from a playlist, don't save video progress",
+ "description": "Help text for pause-in-playlists setting"
+ },
+ "settings_data_management_label": {
+ "message": "Data Management",
+ "description": "Label for data management section"
+ },
+ "settings_export": {
+ "message": "Export",
+ "description": "Button label for exporting history"
+ },
+ "settings_import": {
+ "message": "Import",
+ "description": "Button label for importing history"
+ },
+ "settings_export_help": {
+ "message": "Export or import your local history, subscriptions, playlists, settings, and caches",
+ "description": "Help text for export/import"
+ },
+ "settings_clear_help": {
+ "message": "Clear removes all saved video history (this cannot be undone)",
+ "description": "Help text for the Clear history button"
+ },
+ "settings_auto_clean_label": {
+ "message": "Auto-clean Period",
+ "description": "Label for auto-clean period in settings"
+ },
+ "settings_auto_clean_help": {
+ "message": "Automatically remove history older than selected period",
+ "description": "Help text for auto-clean period"
+ },
+ "settings_debug_label": {
+ "message": "Debug Mode",
+ "description": "Label for debug mode option"
+ },
+ "settings_debug_help": {
+ "message": "Enable debug logging (for troubleshooting)",
+ "description": "Help text for debug mode"
+ },
+ "settings_version_label": {
+ "message": "Extension Version",
+ "description": "Label for extension version display"
+ },
+ "settings_save": {
+ "message": "Save Settings",
+ "description": "Button label for saving settings"
+ },
+ "settings_local_feed_label": {
+ "message": "Local feed",
+ "description": "Label for the local subscriptions feed toggle"
+ },
+ "settings_local_feed_help": {
+ "message": "Use locally subscribed channels to build the YT re:Watch feed",
+ "description": "Help text for the local subscriptions feed toggle"
+ },
+ "settings_hide_recs_label": {
+ "message": "Hide recommendations & popups",
+ "description": "Label for the hide recommendations toggle"
+ },
+ "settings_hide_recs_help": {
+ "message": "Hide YouTube's home recommendations and \"history is off\" nag popups.",
+ "description": "Help text for the hide recommendations toggle"
+ },
+ "settings_hide_account_label": {
+ "message": "Hide login & account UI",
+ "description": "Label for the hide account UI toggle"
+ },
+ "settings_hide_account_help": {
+ "message": "Hide YouTube's sign-in buttons, account menu, and native Subscribe/Join buttons (your local Subscribe stays).",
+ "description": "Help text for the hide account UI toggle"
+ },
+ "settings_feed_refresh_label": {
+ "message": "Feed refresh interval (minutes)",
+ "description": "Label for the feed refresh interval input"
+ },
+ "settings_feed_refresh_help": {
+ "message": "How often the local feed checks subscribed channels for new uploads",
+ "description": "Help text for the feed refresh interval input"
+ },
+ "subscriptions_refresh": {
+ "message": "Refresh feed",
+ "description": "Button label to refresh the local subscriptions feed"
+ },
+ "subscriptions_open_feed": {
+ "message": "Open YT re:Watch",
+ "description": "Button label to open the dedicated feed page"
+ },
+ "subscriptions_clear_all": {
+ "message": "Clear all",
+ "description": "Button label to remove all local subscriptions"
+ },
+ "subscriptions_no_subs": {
+ "message": "No local subscriptions yet. Search in the extension and use Subscribe.",
+ "description": "Empty state for the subscriptions tab"
+ },
+ "subscriptions_unsubscribe": {
+ "message": "Unsubscribe",
+ "description": "Button label to remove a local subscription"
+ },
+ "subscriptions_refreshing": {
+ "message": "Refreshing…",
+ "description": "Refresh button label while a refresh is in progress"
+ },
+ "subscriptions_refresh_started": {
+ "message": "Refreshing feed in your YouTube tab…",
+ "description": "Message shown when a feed refresh starts"
+ },
+ "subscriptions_open_youtube": {
+ "message": "Open a YouTube tab to refresh the feed.",
+ "description": "Message shown when no YouTube tab is open to refresh the feed"
+ },
+ "message_settings_saved": {
+ "message": "Settings saved",
+ "description": "Generic confirmation that settings were saved"
+ }
+}
diff --git a/src/_locales/en/tabs.json b/src/_locales/en/tabs.json
index 91c8962..bf70c9e 100644
--- a/src/_locales/en/tabs.json
+++ b/src/_locales/en/tabs.json
@@ -11,6 +11,14 @@
"message": "Playlists",
"description": "Tab label for playlists"
},
+ "tab_subscriptions": {
+ "message": "Subs",
+ "description": "Tab label for local subscriptions"
+ },
+ "tab_watchlater": {
+ "message": "Later",
+ "description": "Tab label for local Watch Later list"
+ },
"tab_analytics": {
"message": "Analytics",
"description": "Tab label for analytics"
@@ -226,5 +234,17 @@
"playlists_unknown_title": {
"message": "Unknown Playlist",
"description": "Fallback title for playlists with no title"
+ },
+ "watchlater_empty": {
+ "message": "Nothing saved yet. On YouTube, right-click a video and choose \"Save to Watch Later (local)\".",
+ "description": "Empty state for the Watch Later tab"
+ },
+ "watchlater_clear_all": {
+ "message": "Clear all",
+ "description": "Button to remove all Watch Later items"
+ },
+ "watchlater_confirm_clear": {
+ "message": "Clear all Watch Later items?",
+ "description": "Confirmation prompt before clearing the Watch Later list"
}
}
\ No newline at end of file
diff --git a/src/_locales/es/feed.json b/src/_locales/es/feed.json
new file mode 100644
index 0000000..588f9e3
--- /dev/null
+++ b/src/_locales/es/feed.json
@@ -0,0 +1,506 @@
+{
+ "feed_menu": {
+ "message": "Men?",
+ "description": "Menu button label"
+ },
+ "feed_back_to_feed": {
+ "message": "Volver a tu feed",
+ "description": "Tooltip for returning to the feed"
+ },
+ "feed_search_youtube": {
+ "message": "Buscar en YouTube",
+ "description": "Search field placeholder"
+ },
+ "feed_refresh": {
+ "message": "Actualizar",
+ "description": "Refresh button label"
+ },
+ "feed_create": {
+ "message": "Crear",
+ "description": "Create button label"
+ },
+ "feed_save": {
+ "message": "Guardar",
+ "description": "Save button label"
+ },
+ "feed_clear_all": {
+ "message": "Borrar todo",
+ "description": "Clear all button label"
+ },
+ "feed_options": {
+ "message": "Opciones",
+ "description": "Options menu label"
+ },
+ "feed_unwatched_only": {
+ "message": "Solo no vistos",
+ "description": "Filter label for only unwatched videos"
+ },
+ "feed_hide_members": {
+ "message": "Ocultar videos de miembros",
+ "description": "Filter label for hiding members-only or members-first videos"
+ },
+ "feed_nav_home": {
+ "message": "Inicio",
+ "description": "Home navigation label"
+ },
+ "feed_nav_home_title": {
+ "message": "Personalizado localmente desde tu historial",
+ "description": "Home navigation tooltip"
+ },
+ "feed_nav_subscriptions": {
+ "message": "Suscripciones",
+ "description": "Subscriptions navigation label"
+ },
+ "feed_nav_subscriptions_title": {
+ "message": "?ltimos videos de tus canales suscritos",
+ "description": "Subscriptions navigation tooltip"
+ },
+ "feed_nav_shorts": {
+ "message": "Shorts",
+ "description": "Shorts navigation label"
+ },
+ "feed_nav_playlists": {
+ "message": "Listas",
+ "description": "Playlists navigation label"
+ },
+ "feed_nav_playlists_title": {
+ "message": "Ver listas guardadas",
+ "description": "Playlists navigation tooltip"
+ },
+ "feed_nav_history": {
+ "message": "Historial",
+ "description": "History navigation label"
+ },
+ "feed_nav_history_title": {
+ "message": "Ver tu historial local",
+ "description": "History navigation tooltip"
+ },
+ "feed_nav_channels": {
+ "message": "Canales",
+ "description": "Channels navigation label"
+ },
+ "feed_nav_channels_title": {
+ "message": "Administrar canales suscritos localmente",
+ "description": "Channels navigation tooltip"
+ },
+ "feed_nav_analytics": {
+ "message": "Anal?ticas",
+ "description": "Analytics navigation label"
+ },
+ "feed_nav_analytics_title": {
+ "message": "Ver tus anal?ticas de reproducci?n",
+ "description": "Analytics navigation tooltip"
+ },
+ "feed_nav_settings": {
+ "message": "Ajustes",
+ "description": "Settings navigation label"
+ },
+ "feed_nav_settings_title": {
+ "message": "Abrir ajustes de la extensi?n",
+ "description": "Settings navigation tooltip"
+ },
+ "feed_your_feed": {
+ "message": "Tu feed",
+ "description": "Main feed heading"
+ },
+ "feed_search_options": {
+ "message": "Opciones de b?squeda",
+ "description": "Search options aria label"
+ },
+ "feed_filters": {
+ "message": "Filtros",
+ "description": "Filters button label"
+ },
+ "feed_upload_date": {
+ "message": "Fecha de subida",
+ "description": "Upload date filter label"
+ },
+ "feed_today": {
+ "message": "Hoy",
+ "description": "Today filter label"
+ },
+ "feed_this_week": {
+ "message": "Esta semana",
+ "description": "This week filter label"
+ },
+ "feed_this_month": {
+ "message": "Este mes",
+ "description": "This month filter label"
+ },
+ "feed_this_year": {
+ "message": "Este a?o",
+ "description": "This year filter label"
+ },
+ "feed_duration": {
+ "message": "Duraci?n",
+ "description": "Duration filter label"
+ },
+ "feed_under_4_minutes": {
+ "message": "Menos de 4 minutos",
+ "description": "Under 4 minutes filter label"
+ },
+ "feed_4_to_20_minutes": {
+ "message": "4-20 minutos",
+ "description": "4 to 20 minutes filter label"
+ },
+ "feed_over_20_minutes": {
+ "message": "M?s de 20 minutos",
+ "description": "Over 20 minutes filter label"
+ },
+ "feed_watch_status": {
+ "message": "Estado de visualizaci?n",
+ "description": "Watch status filter label"
+ },
+ "feed_unwatched": {
+ "message": "No visto",
+ "description": "Unwatched filter label"
+ },
+ "feed_in_progress": {
+ "message": "En progreso",
+ "description": "In progress filter label"
+ },
+ "feed_watched": {
+ "message": "Visto",
+ "description": "Watched filter label"
+ },
+ "feed_sort_search_results": {
+ "message": "Ordenar resultados",
+ "description": "Sort search results aria label"
+ },
+ "feed_sort": {
+ "message": "Ordenar",
+ "description": "Sort filter label"
+ },
+ "feed_newest": {
+ "message": "M?s reciente",
+ "description": "Newest sort label"
+ },
+ "feed_oldest": {
+ "message": "M?s antiguo",
+ "description": "Oldest sort label"
+ },
+ "feed_search_results_for": {
+ "message": "Resultados para",
+ "description": "Search results heading prefix"
+ },
+ "feed_back_to_results": {
+ "message": "Volver a resultados",
+ "description": "Back to results button label"
+ },
+ "feed_no_channel_videos": {
+ "message": "A?n no se encontraron videos para este canal.",
+ "description": "Empty state for channel videos"
+ },
+ "feed_channels": {
+ "message": "Canales",
+ "description": "Channels label"
+ },
+ "feed_no_local_channels": {
+ "message": "A?n no hay canales suscritos localmente. Busca en la extensi?n y usa Suscribirse.",
+ "description": "Empty state for local channels"
+ },
+ "feed_playlists": {
+ "message": "Listas",
+ "description": "Playlists label"
+ },
+ "feed_no_saved_playlists": {
+ "message": "A?n no hay listas guardadas.",
+ "description": "Empty state for saved playlists"
+ },
+ "feed_history": {
+ "message": "Historial",
+ "description": "History label"
+ },
+ "feed_no_history_matches": {
+ "message": "Ning?n historial coincide con estos filtros.",
+ "description": "Empty state for filtered history"
+ },
+ "feed_load_more": {
+ "message": "Cargar m?s",
+ "description": "Load more button label"
+ },
+ "feed_analytics": {
+ "message": "Anal?ticas",
+ "description": "Analytics label"
+ },
+ "feed_analytics_subtitle": {
+ "message": "tus estad?sticas, guardadas localmente",
+ "description": "Analytics heading subtitle"
+ },
+ "feed_watch_activity_7_days": {
+ "message": "Actividad de reproducci?n (?ltimos 7 d?as)",
+ "description": "Watch activity chart title"
+ },
+ "feed_watch_time_by_hour": {
+ "message": "Tiempo visto por hora",
+ "description": "Watch time by hour chart title"
+ },
+ "feed_how_much_finish": {
+ "message": "Cu?nto terminas",
+ "description": "Completion analytics title"
+ },
+ "feed_top_channels": {
+ "message": "Canales principales",
+ "description": "Top channels analytics title"
+ },
+ "feed_continue_watching": {
+ "message": "Continuar viendo",
+ "description": "Continue watching analytics title"
+ },
+ "feed_settings": {
+ "message": "Ajustes",
+ "description": "Settings label"
+ },
+ "feed_settings_subtitle": {
+ "message": "Todo permanece local en este navegador.",
+ "description": "Settings subtitle"
+ },
+ "feed_settings_display": {
+ "message": "Pantalla",
+ "description": "Display settings section title"
+ },
+ "feed_settings_theme": {
+ "message": "Tema",
+ "description": "Theme setting label"
+ },
+ "feed_system_default": {
+ "message": "Predeterminado del sistema",
+ "description": "System default theme label"
+ },
+ "feed_dark": {
+ "message": "Oscuro",
+ "description": "Dark theme label"
+ },
+ "feed_light": {
+ "message": "Claro",
+ "description": "Light theme label"
+ },
+ "feed_accent_color": {
+ "message": "Color de acento",
+ "description": "Accent color setting label"
+ },
+ "feed_blue": {
+ "message": "Azul",
+ "description": "Blue color label"
+ },
+ "feed_red": {
+ "message": "Rojo",
+ "description": "Red color label"
+ },
+ "feed_green": {
+ "message": "Verde",
+ "description": "Green color label"
+ },
+ "feed_purple": {
+ "message": "Morado",
+ "description": "Purple color label"
+ },
+ "feed_orange": {
+ "message": "Naranja",
+ "description": "Orange color label"
+ },
+ "feed_default_tab": {
+ "message": "Pesta?a predeterminada",
+ "description": "Default tab setting label"
+ },
+ "feed_last_used_tab": {
+ "message": "?ltima pesta?a usada",
+ "description": "Last used tab option label"
+ },
+ "feed_history_and_feed": {
+ "message": "Historial y feed",
+ "description": "History and feed settings section title"
+ },
+ "feed_check_new_videos": {
+ "message": "Buscar videos nuevos",
+ "description": "Check for new videos setting label"
+ },
+ "feed_every_15_minutes": {
+ "message": "Cada 15 minutos",
+ "description": "Every 15 minutes interval label"
+ },
+ "feed_every_30_minutes": {
+ "message": "Cada 30 minutos",
+ "description": "Every 30 minutes interval label"
+ },
+ "feed_every_hour": {
+ "message": "Cada hora",
+ "description": "Every hour interval label"
+ },
+ "feed_every_3_hours": {
+ "message": "Cada 3 horas",
+ "description": "Every 3 hours interval label"
+ },
+ "feed_every_6_hours": {
+ "message": "Cada 6 horas",
+ "description": "Every 6 hours interval label"
+ },
+ "feed_every_12_hours": {
+ "message": "Cada 12 horas",
+ "description": "Every 12 hours interval label"
+ },
+ "feed_once_a_day": {
+ "message": "Una vez al d?a",
+ "description": "Once a day interval label"
+ },
+ "feed_keep_watch_history": {
+ "message": "Conservar historial",
+ "description": "Keep watch history setting label"
+ },
+ "feed_30_days": {
+ "message": "30 d?as",
+ "description": "30 days option label"
+ },
+ "feed_90_days": {
+ "message": "90 d?as",
+ "description": "90 days option label"
+ },
+ "feed_180_days": {
+ "message": "180 d?as",
+ "description": "180 days option label"
+ },
+ "feed_forever": {
+ "message": "Para siempre",
+ "description": "Forever option label"
+ },
+ "feed_data": {
+ "message": "Datos",
+ "description": "Data settings section title"
+ },
+ "feed_backup_restore": {
+ "message": "Copia y restauraci?n",
+ "description": "Backup and restore title"
+ },
+ "feed_backup_restore_help": {
+ "message": "Descarga o carga un archivo JSON con historial, suscripciones, listas y ajustes.",
+ "description": "Backup and restore help text"
+ },
+ "feed_backup": {
+ "message": "Copia",
+ "description": "Backup button label"
+ },
+ "feed_restore": {
+ "message": "Restaurar",
+ "description": "Restore button label"
+ },
+ "feed_import_from_youtube": {
+ "message": "Importar desde YouTube",
+ "description": "Import from YouTube title"
+ },
+ "feed_import_from_youtube_help": {
+ "message": "Usa watch-history.html para tu historial y subscriptions.csv para tus canales suscritos desde Google Takeout.",
+ "description": "Import from YouTube help text"
+ },
+ "feed_reset_all_data": {
+ "message": "Restablecer todos los datos",
+ "description": "Reset all data title"
+ },
+ "feed_reset_all_data_help": {
+ "message": "Eliminar todo, incluidas suscripciones, ajustes y copias.",
+ "description": "Reset all data help text"
+ },
+ "feed_reset_all": {
+ "message": "Restablecer todo",
+ "description": "Reset all button label"
+ },
+ "feed_add_to_playlist": {
+ "message": "A?adir a lista",
+ "description": "Add to playlist dialog title"
+ },
+ "feed_close": {
+ "message": "Cerrar",
+ "description": "Close button aria label"
+ },
+ "feed_create_new_playlist": {
+ "message": "Crear nueva lista",
+ "description": "Create new playlist placeholder"
+ },
+ "feed_video_options": {
+ "message": "Opciones del video",
+ "description": "feed video options"
+ },
+ "feed_more_from_channel": {
+ "message": "M?s de este canal",
+ "description": "feed more from channel"
+ },
+ "feed_less_from_channel": {
+ "message": "Menos de este canal",
+ "description": "feed less from channel"
+ },
+ "feed_hide_this_video": {
+ "message": "Ocultar este video",
+ "description": "feed hide this video"
+ },
+ "feed_unsubscribe": {
+ "message": "Cancelar suscripci?n",
+ "description": "feed unsubscribe"
+ },
+ "feed_not_subscribed": {
+ "message": "No suscrito",
+ "description": "feed not subscribed"
+ },
+ "feed_unsubscribed": {
+ "message": "Suscripci?n cancelada",
+ "description": "feed unsubscribed"
+ },
+ "feed_save_to_watch_later": {
+ "message": "Guardar en Ver m?s tarde",
+ "description": "feed save to watch later"
+ },
+ "feed_saved_to_watch_later": {
+ "message": "Guardado en Ver m?s tarde",
+ "description": "feed saved to watch later"
+ },
+ "feed_added_to_playlist": {
+ "message": "A?adido a la lista",
+ "description": "feed added to playlist"
+ },
+ "feed_set_as_playlist_cover": {
+ "message": "Establecer como portada",
+ "description": "feed set as playlist cover"
+ },
+ "feed_cover_updated": {
+ "message": "Portada actualizada",
+ "description": "feed cover updated"
+ },
+ "feed_remove_from_playlist": {
+ "message": "Quitar de esta lista",
+ "description": "feed remove from playlist"
+ },
+ "feed_this_channel": {
+ "message": "este canal",
+ "description": "feed this channel"
+ },
+ "feed_channel": {
+ "message": "canal",
+ "description": "feed channel"
+ },
+ "feed_recommend_more_from_status": {
+ "message": "Recomendaremos m?s de $1.",
+ "description": "feed recommend more from status"
+ },
+ "feed_recommend_more_status": {
+ "message": "Recomendaremos m?s",
+ "description": "feed recommend more status"
+ },
+ "feed_unsubscribed_from_status": {
+ "message": "Suscripci?n cancelada a $1.",
+ "description": "feed unsubscribed from status"
+ },
+ "feed_refreshing": {
+ "message": "Actualizando...",
+ "description": "feed refreshing"
+ },
+ "feed_updated": {
+ "message": "Feed actualizado.",
+ "description": "feed updated"
+ },
+ "feed_no_videos_subscribe_first": {
+ "message": "No se encontraron videos. Suscr?bete primero a canales.",
+ "description": "feed no videos subscribe first"
+ },
+ "feed_refresh_failed_status": {
+ "message": "Error al actualizar: $1. Si YouTube muestra una pantalla de consentimiento, abre youtube.com una vez en este navegador.",
+ "description": "feed refresh failed status"
+ }
+}
diff --git a/src/_locales/es/messages-group.json b/src/_locales/es/messages-group.json
index 2b255dd..d65b25b 100644
--- a/src/_locales/es/messages-group.json
+++ b/src/_locales/es/messages-group.json
@@ -47,10 +47,6 @@
"message": "¿Está seguro de que desea borrar todo el historial? Esto no se puede deshacer.",
"description": "Confirmation for clearing all history."
},
- "message_confirm_full_sync": {
- "message": "La sincronización completa limpiará los datos antiguos y volverá a sincronizar todo. Puede tardar. ¿Continuar?",
- "description": "Confirmation for full sync."
- },
"message_warning_clear_all": {
"message": "ADVERTENCIA: Esto eliminará todo su historial y listas de reproducción de YouTube. Esto no se puede deshacer. ¿Continuar?",
"description": "Warning for clearing all history and playlists."
@@ -119,114 +115,6 @@
"message": "Modo de depuración deshabilitado",
"description": "Message when debug mode is disabled"
},
- "message_sync_disabled": {
- "message": "Sincronización deshabilitada - Haga clic para habilitar",
- "description": "Tooltip for disabled sync status"
- },
- "message_sync_not_available": {
- "message": "Sincronización no disponible - Se requiere Firefox Sync",
- "description": "Tooltip for unavailable sync status"
- },
- "message_sync_initializing": {
- "message": "Inicializando sincronización...",
- "description": "Tooltip for initializing sync status"
- },
- "message_sync_syncing": {
- "message": "Sincronizando datos...",
- "description": "Tooltip for syncing data status"
- },
- "message_sync_ready": {
- "message": "Sincronización lista",
- "description": "Tooltip for sync ready status"
- },
- "message_sync_error": {
- "message": "Error de sincronización - Haga clic para reintentar",
- "description": "Tooltip for sync error status"
- },
- "message_sync_off": {
- "message": "Desactivado",
- "description": "Label for sync off status"
- },
- "message_sync_na": {
- "message": "N/A",
- "description": "Label for sync not available status"
- },
- "message_sync_init": {
- "message": "Init",
- "description": "Label for sync initializing status"
- },
- "message_sync_on": {
- "message": "Activado",
- "description": "Label for sync on status"
- },
- "message_sync": {
- "message": "Sincronización",
- "description": "Label for sync status"
- },
- "message_syncing": {
- "message": "Sincronizando...",
- "description": "Label for syncing status"
- },
- "message_synced": {
- "message": "Sincronizado",
- "description": "Label for synced status"
- },
- "message_sync_now": {
- "message": "Sincronizar ahora",
- "description": "Button label for sync now"
- },
- "message_full_sync": {
- "message": "Sincronización completa",
- "description": "Button label for full sync"
- },
- "message_syncing_status": {
- "message": "Sincronizando...",
- "description": "Status text for syncing"
- },
- "message_synced_status": {
- "message": "Sincronizado",
- "description": "Status text for synced"
- },
- "message_never": {
- "message": "Nunca",
- "description": "Label for never synced"
- },
- "message_just_now": {
- "message": "Justo ahora",
- "description": "Label for just now sync time"
- },
- "message_minutes_ago": {
- "message": "hace {MINUTES}m",
- "description": "Label for minutes ago sync time"
- },
- "message_hours_ago": {
- "message": "hace {HOURS}h",
- "description": "Label for hours ago sync time"
- },
- "message_days_ago": {
- "message": "hace {DAYS}d",
- "description": "Label for days ago sync time"
- },
- "message_firefox_sync_not_available": {
- "message": "Firefox Sync no está disponible. Por favor, active Firefox Sync en la configuración de su navegador.",
- "description": "Error message when Firefox Sync is not available"
- },
- "message_failed_to_enable_sync": {
- "message": "No se pudo habilitar la sincronización. Por favor, revise la configuración de Firefox Sync.",
- "description": "Error message when enabling sync fails"
- },
- "message_failed_to_disable_sync": {
- "message": "No se pudo deshabilitar la sincronización.",
- "description": "Error message when disabling sync fails"
- },
- "message_sync_currently_disabled": {
- "message": "La sincronización está actualmente deshabilitada.",
- "description": "Error message when sync is currently disabled"
- },
- "message_full_sync_initiated": {
- "message": "Sincronización completa iniciada... Esto puede tardar un momento.",
- "description": "Message when full sync is initiated"
- },
"settings_convert_history": {
"message": "Convertir historial de YouTube",
"description": "Button label to open the community converter tool"
@@ -239,4 +127,4 @@
"message": "Error desconocido",
"description": "Fallback for unknown error details"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json
index 712d9f6..18ee746 100644
--- a/src/_locales/es/messages.json
+++ b/src/_locales/es/messages.json
@@ -55,14 +55,6 @@
"message": "Cerrar",
"description": "Button label for closing the popup"
},
- "sync_status": {
- "message": "Sincronización",
- "description": "Label for sync status indicator"
- },
- "sync_status_tooltip": {
- "message": "Estado de sincronización",
- "description": "Tooltip for sync status indicator"
- },
"instructions_script_disabled_title": {
"message": "Ejecución de scripts deshabilitada",
"description": "Title for script execution disabled instructions"
@@ -167,24 +159,8 @@
"message": "La lista será rastreada",
"description": "Toast when ignore is disabled"
},
- "content_toggle_pause": {
- "message": "re:Watch — Clic para pausar el historial",
- "description": "Button text when tracking is active"
- },
- "content_toggle_paused": {
- "message": "re:Watch — Historial en pausa. Clic para activar",
- "description": "Button text when tracking is paused"
- },
- "content_toggle_pause_title": {
- "message": "re:Watch — Clic para pausar el historial para esta lista",
- "description": "Tooltip when tracking is active"
- },
- "content_toggle_paused_title": {
- "message": "re:Watch — El historial está en pausa para esta lista. Clic para activar",
- "description": "Tooltip when tracking is paused"
- },
"global_search_placeholder": {
"message": "Buscar en todo el contenido...",
"description": "Placeholder text for the global search input"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/es/settings.json b/src/_locales/es/settings.json
index 099d22c..1a3e90e 100644
--- a/src/_locales/es/settings.json
+++ b/src/_locales/es/settings.json
@@ -44,8 +44,8 @@
"description": "Help text for overlay size"
},
"settings_data_sync": {
- "message": "Datos y sincronización",
- "description": "Settings section title for data and sync"
+ "message": "Data",
+ "description": "Settings section title for data"
},
"settings_pause_in_playlists_label": {
"message": "Pausar historial en listas",
@@ -79,30 +79,6 @@
"message": "Elimina automáticamente el historial después del período seleccionado",
"description": "Help text for auto-clean period"
},
- "settings_sync_label": {
- "message": "Firefox Sync",
- "description": "Label for Firefox Sync option"
- },
- "settings_sync_help": {
- "message": "Sincroniza tus datos entre instancias del navegador (solo Firefox)",
- "description": "Help text for Firefox Sync"
- },
- "settings_last_sync_label": {
- "message": "Última sincronización",
- "description": "Label for last sync time"
- },
- "settings_sync_now": {
- "message": "Sincronizar ahora",
- "description": "Button label for triggering sync"
- },
- "settings_full_sync": {
- "message": "Sincronización completa",
- "description": "Button label for triggering full sync"
- },
- "settings_full_sync_help": {
- "message": "La sincronización completa limpia los datos antiguos y vuelve a sincronizar todo",
- "description": "Help text for full sync"
- },
"settings_debug_label": {
"message": "Modo de depuración",
"description": "Label for debug mode option"
@@ -118,5 +94,97 @@
"settings_save": {
"message": "Guardar configuración",
"description": "Button label for saving settings"
+ },
+ "settings_data_label": {
+ "message": "Data",
+ "description": "Label for the data section in settings"
+ },
+ "settings_import_history": {
+ "message": "Import History",
+ "description": "Button label to import watch history"
+ },
+ "settings_import_history_help": {
+ "message": "Import History accepts the watch-history.html file from Google Takeout.",
+ "description": "Help text for the import history button"
+ },
+ "settings_import_subs": {
+ "message": "Import Subscriptions",
+ "description": "Button label to import subscriptions"
+ },
+ "settings_import_subs_help": {
+ "message": "Import Subscriptions accepts the subscriptions.csv file from Google Takeout.",
+ "description": "Help text for the import subscriptions button"
+ },
+ "settings_clear_help": {
+ "message": "Clear removes all saved video history (this cannot be undone)",
+ "description": "Help text for the Clear history button"
+ },
+ "settings_local_feed_label": {
+ "message": "Local feed",
+ "description": "Label for the local subscriptions feed toggle"
+ },
+ "settings_local_feed_help": {
+ "message": "Use locally subscribed channels to build the YT re:Watch feed",
+ "description": "Help text for the local subscriptions feed toggle"
+ },
+ "settings_hide_recs_label": {
+ "message": "Hide recommendations & popups",
+ "description": "Label for the hide recommendations toggle"
+ },
+ "settings_hide_recs_help": {
+ "message": "Hide YouTube's home recommendations and \"history is off\" nag popups.",
+ "description": "Help text for the hide recommendations toggle"
+ },
+ "settings_hide_account_label": {
+ "message": "Hide login & account UI",
+ "description": "Label for the hide account UI toggle"
+ },
+ "settings_hide_account_help": {
+ "message": "Hide YouTube's sign-in buttons, account menu, and native Subscribe/Join buttons (your local Subscribe stays).",
+ "description": "Help text for the hide account UI toggle"
+ },
+ "settings_feed_refresh_label": {
+ "message": "Feed refresh interval (minutes)",
+ "description": "Label for the feed refresh interval input"
+ },
+ "settings_feed_refresh_help": {
+ "message": "How often the local feed checks subscribed channels for new uploads",
+ "description": "Help text for the feed refresh interval input"
+ },
+ "subscriptions_refresh": {
+ "message": "Refresh feed",
+ "description": "Button label to refresh the local subscriptions feed"
+ },
+ "subscriptions_open_feed": {
+ "message": "Open YT re:Watch",
+ "description": "Button label to open the dedicated feed page"
+ },
+ "subscriptions_clear_all": {
+ "message": "Clear all",
+ "description": "Button label to remove all local subscriptions"
+ },
+ "subscriptions_no_subs": {
+ "message": "No local subscriptions yet. Search in the extension and use Subscribe.",
+ "description": "Empty state for the subscriptions tab"
+ },
+ "subscriptions_unsubscribe": {
+ "message": "Unsubscribe",
+ "description": "Button label to remove a local subscription"
+ },
+ "subscriptions_refreshing": {
+ "message": "Refreshing…",
+ "description": "Refresh button label while a refresh is in progress"
+ },
+ "subscriptions_refresh_started": {
+ "message": "Refreshing feed in your YouTube tab…",
+ "description": "Message shown when a feed refresh starts"
+ },
+ "subscriptions_open_youtube": {
+ "message": "Open a YouTube tab to refresh the feed.",
+ "description": "Message shown when no YouTube tab is open to refresh the feed"
+ },
+ "message_settings_saved": {
+ "message": "Settings saved",
+ "description": "Generic confirmation that settings were saved"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/es/tabs.json b/src/_locales/es/tabs.json
index 6b3c914..6a606c3 100644
--- a/src/_locales/es/tabs.json
+++ b/src/_locales/es/tabs.json
@@ -226,5 +226,25 @@
"playlists_unknown_title": {
"message": "Lista desconocida",
"description": "Fallback title for playlists with no title"
+ },
+ "tab_subscriptions": {
+ "message": "Subs",
+ "description": "Tab label for local subscriptions"
+ },
+ "tab_watchlater": {
+ "message": "Later",
+ "description": "Tab label for local Watch Later list"
+ },
+ "watchlater_empty": {
+ "message": "Nothing saved yet. On YouTube, right-click a video and choose \"Save to Watch Later (local)\".",
+ "description": "Empty state for the Watch Later tab"
+ },
+ "watchlater_clear_all": {
+ "message": "Clear all",
+ "description": "Button to remove all Watch Later items"
+ },
+ "watchlater_confirm_clear": {
+ "message": "Clear all Watch Later items?",
+ "description": "Confirmation prompt before clearing the Watch Later list"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/fr/feed.json b/src/_locales/fr/feed.json
new file mode 100644
index 0000000..145fcba
--- /dev/null
+++ b/src/_locales/fr/feed.json
@@ -0,0 +1,506 @@
+{
+ "feed_menu": {
+ "message": "Menu",
+ "description": "Menu button label"
+ },
+ "feed_back_to_feed": {
+ "message": "Retour ? votre fil",
+ "description": "Tooltip for returning to the feed"
+ },
+ "feed_search_youtube": {
+ "message": "Rechercher sur YouTube",
+ "description": "Search field placeholder"
+ },
+ "feed_refresh": {
+ "message": "Actualiser",
+ "description": "Refresh button label"
+ },
+ "feed_create": {
+ "message": "Cr?er",
+ "description": "Create button label"
+ },
+ "feed_save": {
+ "message": "Enregistrer",
+ "description": "Save button label"
+ },
+ "feed_clear_all": {
+ "message": "Tout effacer",
+ "description": "Clear all button label"
+ },
+ "feed_options": {
+ "message": "Options",
+ "description": "Options menu label"
+ },
+ "feed_unwatched_only": {
+ "message": "Non vus uniquement",
+ "description": "Filter label for only unwatched videos"
+ },
+ "feed_hide_members": {
+ "message": "Masquer les videos membres",
+ "description": "Filter label for hiding members-only or members-first videos"
+ },
+ "feed_nav_home": {
+ "message": "Accueil",
+ "description": "Home navigation label"
+ },
+ "feed_nav_home_title": {
+ "message": "Personnalis? localement depuis votre historique",
+ "description": "Home navigation tooltip"
+ },
+ "feed_nav_subscriptions": {
+ "message": "Abonnements",
+ "description": "Subscriptions navigation label"
+ },
+ "feed_nav_subscriptions_title": {
+ "message": "Derni?res vid?os de vos cha?nes abonn?es",
+ "description": "Subscriptions navigation tooltip"
+ },
+ "feed_nav_shorts": {
+ "message": "Shorts",
+ "description": "Shorts navigation label"
+ },
+ "feed_nav_playlists": {
+ "message": "Playlists",
+ "description": "Playlists navigation label"
+ },
+ "feed_nav_playlists_title": {
+ "message": "Voir les playlists enregistr?es",
+ "description": "Playlists navigation tooltip"
+ },
+ "feed_nav_history": {
+ "message": "Historique",
+ "description": "History navigation label"
+ },
+ "feed_nav_history_title": {
+ "message": "Voir votre historique local",
+ "description": "History navigation tooltip"
+ },
+ "feed_nav_channels": {
+ "message": "Cha?nes",
+ "description": "Channels navigation label"
+ },
+ "feed_nav_channels_title": {
+ "message": "G?rer vos cha?nes abonn?es localement",
+ "description": "Channels navigation tooltip"
+ },
+ "feed_nav_analytics": {
+ "message": "Analyses",
+ "description": "Analytics navigation label"
+ },
+ "feed_nav_analytics_title": {
+ "message": "Voir vos analyses de visionnage",
+ "description": "Analytics navigation tooltip"
+ },
+ "feed_nav_settings": {
+ "message": "Param?tres",
+ "description": "Settings navigation label"
+ },
+ "feed_nav_settings_title": {
+ "message": "Ouvrir les param?tres de l?extension",
+ "description": "Settings navigation tooltip"
+ },
+ "feed_your_feed": {
+ "message": "Votre fil",
+ "description": "Main feed heading"
+ },
+ "feed_search_options": {
+ "message": "Options de recherche",
+ "description": "Search options aria label"
+ },
+ "feed_filters": {
+ "message": "Filtres",
+ "description": "Filters button label"
+ },
+ "feed_upload_date": {
+ "message": "Date de mise en ligne",
+ "description": "Upload date filter label"
+ },
+ "feed_today": {
+ "message": "Aujourd?hui",
+ "description": "Today filter label"
+ },
+ "feed_this_week": {
+ "message": "Cette semaine",
+ "description": "This week filter label"
+ },
+ "feed_this_month": {
+ "message": "Ce mois-ci",
+ "description": "This month filter label"
+ },
+ "feed_this_year": {
+ "message": "Cette ann?e",
+ "description": "This year filter label"
+ },
+ "feed_duration": {
+ "message": "Dur?e",
+ "description": "Duration filter label"
+ },
+ "feed_under_4_minutes": {
+ "message": "Moins de 4 minutes",
+ "description": "Under 4 minutes filter label"
+ },
+ "feed_4_to_20_minutes": {
+ "message": "4-20 minutes",
+ "description": "4 to 20 minutes filter label"
+ },
+ "feed_over_20_minutes": {
+ "message": "Plus de 20 minutes",
+ "description": "Over 20 minutes filter label"
+ },
+ "feed_watch_status": {
+ "message": "?tat de visionnage",
+ "description": "Watch status filter label"
+ },
+ "feed_unwatched": {
+ "message": "Non vu",
+ "description": "Unwatched filter label"
+ },
+ "feed_in_progress": {
+ "message": "En cours",
+ "description": "In progress filter label"
+ },
+ "feed_watched": {
+ "message": "Vu",
+ "description": "Watched filter label"
+ },
+ "feed_sort_search_results": {
+ "message": "Trier les r?sultats",
+ "description": "Sort search results aria label"
+ },
+ "feed_sort": {
+ "message": "Trier",
+ "description": "Sort filter label"
+ },
+ "feed_newest": {
+ "message": "Plus r?cent",
+ "description": "Newest sort label"
+ },
+ "feed_oldest": {
+ "message": "Plus ancien",
+ "description": "Oldest sort label"
+ },
+ "feed_search_results_for": {
+ "message": "R?sultats pour",
+ "description": "Search results heading prefix"
+ },
+ "feed_back_to_results": {
+ "message": "Retour aux r?sultats",
+ "description": "Back to results button label"
+ },
+ "feed_no_channel_videos": {
+ "message": "Aucune vid?o trouv?e pour cette cha?ne pour le moment.",
+ "description": "Empty state for channel videos"
+ },
+ "feed_channels": {
+ "message": "Cha?nes",
+ "description": "Channels label"
+ },
+ "feed_no_local_channels": {
+ "message": "Aucune cha?ne abonn?e localement. Recherchez dans l?extension et utilisez S?abonner.",
+ "description": "Empty state for local channels"
+ },
+ "feed_playlists": {
+ "message": "Playlists",
+ "description": "Playlists label"
+ },
+ "feed_no_saved_playlists": {
+ "message": "Aucune playlist enregistr?e.",
+ "description": "Empty state for saved playlists"
+ },
+ "feed_history": {
+ "message": "Historique",
+ "description": "History label"
+ },
+ "feed_no_history_matches": {
+ "message": "Aucun historique ne correspond ? ces filtres.",
+ "description": "Empty state for filtered history"
+ },
+ "feed_load_more": {
+ "message": "Charger plus",
+ "description": "Load more button label"
+ },
+ "feed_analytics": {
+ "message": "Analyses",
+ "description": "Analytics label"
+ },
+ "feed_analytics_subtitle": {
+ "message": "vos statistiques, conserv?es localement",
+ "description": "Analytics heading subtitle"
+ },
+ "feed_watch_activity_7_days": {
+ "message": "Activit? de visionnage (7 derniers jours)",
+ "description": "Watch activity chart title"
+ },
+ "feed_watch_time_by_hour": {
+ "message": "Temps de visionnage par heure",
+ "description": "Watch time by hour chart title"
+ },
+ "feed_how_much_finish": {
+ "message": "Ce que vous terminez",
+ "description": "Completion analytics title"
+ },
+ "feed_top_channels": {
+ "message": "Meilleures cha?nes",
+ "description": "Top channels analytics title"
+ },
+ "feed_continue_watching": {
+ "message": "Continuer ? regarder",
+ "description": "Continue watching analytics title"
+ },
+ "feed_settings": {
+ "message": "Param?tres",
+ "description": "Settings label"
+ },
+ "feed_settings_subtitle": {
+ "message": "Tout reste local dans ce navigateur.",
+ "description": "Settings subtitle"
+ },
+ "feed_settings_display": {
+ "message": "Affichage",
+ "description": "Display settings section title"
+ },
+ "feed_settings_theme": {
+ "message": "Th?me",
+ "description": "Theme setting label"
+ },
+ "feed_system_default": {
+ "message": "Par d?faut du syst?me",
+ "description": "System default theme label"
+ },
+ "feed_dark": {
+ "message": "Sombre",
+ "description": "Dark theme label"
+ },
+ "feed_light": {
+ "message": "Clair",
+ "description": "Light theme label"
+ },
+ "feed_accent_color": {
+ "message": "Couleur d?accent",
+ "description": "Accent color setting label"
+ },
+ "feed_blue": {
+ "message": "Bleu",
+ "description": "Blue color label"
+ },
+ "feed_red": {
+ "message": "Rouge",
+ "description": "Red color label"
+ },
+ "feed_green": {
+ "message": "Vert",
+ "description": "Green color label"
+ },
+ "feed_purple": {
+ "message": "Violet",
+ "description": "Purple color label"
+ },
+ "feed_orange": {
+ "message": "Orange",
+ "description": "Orange color label"
+ },
+ "feed_default_tab": {
+ "message": "Onglet par d?faut",
+ "description": "Default tab setting label"
+ },
+ "feed_last_used_tab": {
+ "message": "Dernier onglet utilis?",
+ "description": "Last used tab option label"
+ },
+ "feed_history_and_feed": {
+ "message": "Historique et fil",
+ "description": "History and feed settings section title"
+ },
+ "feed_check_new_videos": {
+ "message": "Rechercher de nouvelles vid?os",
+ "description": "Check for new videos setting label"
+ },
+ "feed_every_15_minutes": {
+ "message": "Toutes les 15 minutes",
+ "description": "Every 15 minutes interval label"
+ },
+ "feed_every_30_minutes": {
+ "message": "Toutes les 30 minutes",
+ "description": "Every 30 minutes interval label"
+ },
+ "feed_every_hour": {
+ "message": "Toutes les heures",
+ "description": "Every hour interval label"
+ },
+ "feed_every_3_hours": {
+ "message": "Toutes les 3 heures",
+ "description": "Every 3 hours interval label"
+ },
+ "feed_every_6_hours": {
+ "message": "Toutes les 6 heures",
+ "description": "Every 6 hours interval label"
+ },
+ "feed_every_12_hours": {
+ "message": "Toutes les 12 heures",
+ "description": "Every 12 hours interval label"
+ },
+ "feed_once_a_day": {
+ "message": "Une fois par jour",
+ "description": "Once a day interval label"
+ },
+ "feed_keep_watch_history": {
+ "message": "Conserver l?historique",
+ "description": "Keep watch history setting label"
+ },
+ "feed_30_days": {
+ "message": "30 jours",
+ "description": "30 days option label"
+ },
+ "feed_90_days": {
+ "message": "90 jours",
+ "description": "90 days option label"
+ },
+ "feed_180_days": {
+ "message": "180 jours",
+ "description": "180 days option label"
+ },
+ "feed_forever": {
+ "message": "Toujours",
+ "description": "Forever option label"
+ },
+ "feed_data": {
+ "message": "Donn?es",
+ "description": "Data settings section title"
+ },
+ "feed_backup_restore": {
+ "message": "Sauvegarde et restauration",
+ "description": "Backup and restore title"
+ },
+ "feed_backup_restore_help": {
+ "message": "T?l?chargez ou chargez un fichier JSON avec historique, abonnements, playlists et param?tres.",
+ "description": "Backup and restore help text"
+ },
+ "feed_backup": {
+ "message": "Sauvegarde",
+ "description": "Backup button label"
+ },
+ "feed_restore": {
+ "message": "Restaurer",
+ "description": "Restore button label"
+ },
+ "feed_import_from_youtube": {
+ "message": "Importer depuis YouTube",
+ "description": "Import from YouTube title"
+ },
+ "feed_import_from_youtube_help": {
+ "message": "Utilisez watch-history.html pour votre historique et subscriptions.csv pour vos cha?nes abonn?es depuis Google Takeout.",
+ "description": "Import from YouTube help text"
+ },
+ "feed_reset_all_data": {
+ "message": "R?initialiser toutes les donn?es",
+ "description": "Reset all data title"
+ },
+ "feed_reset_all_data_help": {
+ "message": "Tout supprimer, y compris abonnements, param?tres et sauvegardes.",
+ "description": "Reset all data help text"
+ },
+ "feed_reset_all": {
+ "message": "Tout r?initialiser",
+ "description": "Reset all button label"
+ },
+ "feed_add_to_playlist": {
+ "message": "Ajouter ? la playlist",
+ "description": "Add to playlist dialog title"
+ },
+ "feed_close": {
+ "message": "Fermer",
+ "description": "Close button aria label"
+ },
+ "feed_create_new_playlist": {
+ "message": "Cr?er une nouvelle playlist",
+ "description": "Create new playlist placeholder"
+ },
+ "feed_video_options": {
+ "message": "Options de la vid?o",
+ "description": "feed video options"
+ },
+ "feed_more_from_channel": {
+ "message": "Plus de cette cha?ne",
+ "description": "feed more from channel"
+ },
+ "feed_less_from_channel": {
+ "message": "Moins de cette cha?ne",
+ "description": "feed less from channel"
+ },
+ "feed_hide_this_video": {
+ "message": "Masquer cette vid?o",
+ "description": "feed hide this video"
+ },
+ "feed_unsubscribe": {
+ "message": "Se d?sabonner",
+ "description": "feed unsubscribe"
+ },
+ "feed_not_subscribed": {
+ "message": "Non abonn?",
+ "description": "feed not subscribed"
+ },
+ "feed_unsubscribed": {
+ "message": "D?sabonn?",
+ "description": "feed unsubscribed"
+ },
+ "feed_save_to_watch_later": {
+ "message": "Enregistrer dans ? regarder plus tard",
+ "description": "feed save to watch later"
+ },
+ "feed_saved_to_watch_later": {
+ "message": "Enregistr? dans ? regarder plus tard",
+ "description": "feed saved to watch later"
+ },
+ "feed_added_to_playlist": {
+ "message": "Ajout? ? la playlist",
+ "description": "feed added to playlist"
+ },
+ "feed_set_as_playlist_cover": {
+ "message": "D?finir comme couverture",
+ "description": "feed set as playlist cover"
+ },
+ "feed_cover_updated": {
+ "message": "Couverture mise ? jour",
+ "description": "feed cover updated"
+ },
+ "feed_remove_from_playlist": {
+ "message": "Retirer de cette playlist",
+ "description": "feed remove from playlist"
+ },
+ "feed_this_channel": {
+ "message": "cette cha?ne",
+ "description": "feed this channel"
+ },
+ "feed_channel": {
+ "message": "cha?ne",
+ "description": "feed channel"
+ },
+ "feed_recommend_more_from_status": {
+ "message": "Nous recommanderons plus de contenus de $1.",
+ "description": "feed recommend more from status"
+ },
+ "feed_recommend_more_status": {
+ "message": "Nous recommanderons plus",
+ "description": "feed recommend more status"
+ },
+ "feed_unsubscribed_from_status": {
+ "message": "D?sabonn? de $1.",
+ "description": "feed unsubscribed from status"
+ },
+ "feed_refreshing": {
+ "message": "Actualisation...",
+ "description": "feed refreshing"
+ },
+ "feed_updated": {
+ "message": "Fil mis ? jour.",
+ "description": "feed updated"
+ },
+ "feed_no_videos_subscribe_first": {
+ "message": "Aucune vid?o trouv?e. Abonnez-vous d?abord ? des cha?nes.",
+ "description": "feed no videos subscribe first"
+ },
+ "feed_refresh_failed_status": {
+ "message": "?chec de l?actualisation : $1. Si YouTube affiche un ?cran de consentement, ouvrez youtube.com une fois dans ce navigateur.",
+ "description": "feed refresh failed status"
+ }
+}
diff --git a/src/_locales/fr/messages-group.json b/src/_locales/fr/messages-group.json
index 5c45b50..885fb68 100644
--- a/src/_locales/fr/messages-group.json
+++ b/src/_locales/fr/messages-group.json
@@ -47,10 +47,6 @@
"message": "Êtes-vous sûr de vouloir effacer tout l'historique ? Cette action est irréversible.",
"description": "Confirmation message for clearing all history"
},
- "message_confirm_full_sync": {
- "message": "La synchronisation complète nettoiera les anciennes données et resynchronisera tout. Cela peut prendre un moment. Continuer ?",
- "description": "Confirmation message for full sync"
- },
"message_warning_clear_all": {
"message": "ATTENTION : Cela supprimera tout votre historique et vos playlists YouTube. Cette action est irréversible. Continuer ?",
"description": "Warning message for clearing all history and playlists"
@@ -119,114 +115,6 @@
"message": "Mode débogage désactivé",
"description": "Message when debug mode is disabled"
},
- "message_sync_disabled": {
- "message": "Synchronisation désactivée - Cliquez pour activer",
- "description": "Tooltip for disabled sync status"
- },
- "message_sync_not_available": {
- "message": "Synchronisation non disponible - Firefox Sync requis",
- "description": "Tooltip for unavailable sync status"
- },
- "message_sync_initializing": {
- "message": "Initialisation de la synchronisation...",
- "description": "Tooltip for initializing sync status"
- },
- "message_sync_syncing": {
- "message": "Synchronisation des données...",
- "description": "Tooltip for syncing data status"
- },
- "message_sync_ready": {
- "message": "Synchronisation prête",
- "description": "Tooltip for sync ready status"
- },
- "message_sync_error": {
- "message": "Erreur de synchronisation - Cliquez pour réessayer",
- "description": "Tooltip for sync error status"
- },
- "message_sync_off": {
- "message": "Désactivé",
- "description": "Label for sync off status"
- },
- "message_sync_na": {
- "message": "N/A",
- "description": "Label for sync not available status"
- },
- "message_sync_init": {
- "message": "Init",
- "description": "Label for sync initializing status"
- },
- "message_sync_on": {
- "message": "Activé",
- "description": "Label for sync on status"
- },
- "message_sync": {
- "message": "Synchronisation",
- "description": "Label for sync status"
- },
- "message_syncing": {
- "message": "Synchronisation...",
- "description": "Label for syncing status"
- },
- "message_synced": {
- "message": "Synchronisé",
- "description": "Label for synced status"
- },
- "message_sync_now": {
- "message": "Synchroniser maintenant",
- "description": "Button label for sync now"
- },
- "message_full_sync": {
- "message": "Synchronisation complète",
- "description": "Button label for full sync"
- },
- "message_syncing_status": {
- "message": "Synchronisation...",
- "description": "Status text for syncing"
- },
- "message_synced_status": {
- "message": "Synchronisé",
- "description": "Status text for synced"
- },
- "message_never": {
- "message": "Jamais",
- "description": "Label for never synced"
- },
- "message_just_now": {
- "message": "À l'instant",
- "description": "Label for just now sync time"
- },
- "message_minutes_ago": {
- "message": "il y a {MINUTES}m",
- "description": "Label for minutes ago sync time"
- },
- "message_hours_ago": {
- "message": "il y a {HOURS}h",
- "description": "Label for hours ago sync time"
- },
- "message_days_ago": {
- "message": "il y a {DAYS}j",
- "description": "Label for days ago sync time"
- },
- "message_firefox_sync_not_available": {
- "message": "Firefox Sync n'est pas disponible. Veuillez activer Firefox Sync dans les paramètres de votre navigateur.",
- "description": "Error message when Firefox Sync is not available"
- },
- "message_failed_to_enable_sync": {
- "message": "Échec de l'activation de la synchronisation. Veuillez vérifier vos paramètres Firefox Sync.",
- "description": "Error message when enabling sync fails"
- },
- "message_failed_to_disable_sync": {
- "message": "Échec de la désactivation de la synchronisation.",
- "description": "Error message when disabling sync fails"
- },
- "message_sync_currently_disabled": {
- "message": "La synchronisation est actuellement désactivée.",
- "description": "Error message when sync is currently disabled"
- },
- "message_full_sync_initiated": {
- "message": "Synchronisation complète lancée... Cela peut prendre un moment.",
- "description": "Message when full sync is initiated"
- },
"settings_convert_history": {
"message": "Convertir l’historique YouTube",
"description": "Button label to open the community converter tool"
@@ -239,4 +127,4 @@
"message": "Erreur inconnue",
"description": "Fallback for unknown error details"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json
index 244da6d..86fb270 100644
--- a/src/_locales/fr/messages.json
+++ b/src/_locales/fr/messages.json
@@ -55,14 +55,6 @@
"message": "Fermer",
"description": "Button label for closing the popup"
},
- "sync_status": {
- "message": "Sync",
- "description": "Label for sync status indicator"
- },
- "sync_status_tooltip": {
- "message": "État de la synchronisation",
- "description": "Tooltip for sync status indicator"
- },
"instructions_script_disabled_title": {
"message": "Exécution des scripts désactivée",
"description": "Title for script execution disabled instructions"
@@ -167,24 +159,8 @@
"message": "La playlist sera suivie",
"description": "Toast when ignore is disabled"
},
- "content_toggle_pause": {
- "message": "re:Watch — Cliquez pour mettre l'historique en pause",
- "description": "Button text when tracking is active"
- },
- "content_toggle_paused": {
- "message": "re:Watch — Historique en pause. Cliquez pour activer",
- "description": "Button text when tracking is paused"
- },
- "content_toggle_pause_title": {
- "message": "re:Watch — Cliquez pour mettre l'historique en pause pour cette playlist",
- "description": "Tooltip when tracking is active"
- },
- "content_toggle_paused_title": {
- "message": "re:Watch — L'historique est en pause pour cette playlist. Cliquez pour activer",
- "description": "Tooltip when tracking is paused"
- },
"global_search_placeholder": {
"message": "Rechercher dans tout le contenu...",
"description": "Placeholder text for the global search input"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/fr/settings.json b/src/_locales/fr/settings.json
index b9e7e13..05b47cb 100644
--- a/src/_locales/fr/settings.json
+++ b/src/_locales/fr/settings.json
@@ -44,8 +44,8 @@
"description": "Help text for overlay size"
},
"settings_data_sync": {
- "message": "Données & Synchronisation",
- "description": "Settings section title for data and sync"
+ "message": "Data",
+ "description": "Settings section title for data"
},
"settings_pause_in_playlists_label": {
"message": "Mettre l'historique en pause dans les playlists",
@@ -79,30 +79,6 @@
"message": "Supprimer automatiquement l'historique après la période sélectionnée",
"description": "Help text for auto-clean period"
},
- "settings_sync_label": {
- "message": "Firefox Sync",
- "description": "Label for Firefox Sync option"
- },
- "settings_sync_help": {
- "message": "Synchronisez vos données entre les instances du navigateur (Firefox uniquement)",
- "description": "Help text for Firefox Sync"
- },
- "settings_last_sync_label": {
- "message": "Dernière synchronisation",
- "description": "Label for last sync time"
- },
- "settings_sync_now": {
- "message": "Synchroniser maintenant",
- "description": "Button label for triggering sync"
- },
- "settings_full_sync": {
- "message": "Synchronisation complète",
- "description": "Button label for triggering full sync"
- },
- "settings_full_sync_help": {
- "message": "La synchronisation complète nettoie les anciennes données et resynchronise tout",
- "description": "Help text for full sync"
- },
"settings_debug_label": {
"message": "Mode débogage",
"description": "Label for debug mode option"
@@ -118,5 +94,97 @@
"settings_save": {
"message": "Enregistrer les paramètres",
"description": "Button label for saving settings"
+ },
+ "settings_data_label": {
+ "message": "Data",
+ "description": "Label for the data section in settings"
+ },
+ "settings_import_history": {
+ "message": "Import History",
+ "description": "Button label to import watch history"
+ },
+ "settings_import_history_help": {
+ "message": "Import History accepts the watch-history.html file from Google Takeout.",
+ "description": "Help text for the import history button"
+ },
+ "settings_import_subs": {
+ "message": "Import Subscriptions",
+ "description": "Button label to import subscriptions"
+ },
+ "settings_import_subs_help": {
+ "message": "Import Subscriptions accepts the subscriptions.csv file from Google Takeout.",
+ "description": "Help text for the import subscriptions button"
+ },
+ "settings_clear_help": {
+ "message": "Clear removes all saved video history (this cannot be undone)",
+ "description": "Help text for the Clear history button"
+ },
+ "settings_local_feed_label": {
+ "message": "Local feed",
+ "description": "Label for the local subscriptions feed toggle"
+ },
+ "settings_local_feed_help": {
+ "message": "Use locally subscribed channels to build the YT re:Watch feed",
+ "description": "Help text for the local subscriptions feed toggle"
+ },
+ "settings_hide_recs_label": {
+ "message": "Hide recommendations & popups",
+ "description": "Label for the hide recommendations toggle"
+ },
+ "settings_hide_recs_help": {
+ "message": "Hide YouTube's home recommendations and \"history is off\" nag popups.",
+ "description": "Help text for the hide recommendations toggle"
+ },
+ "settings_hide_account_label": {
+ "message": "Hide login & account UI",
+ "description": "Label for the hide account UI toggle"
+ },
+ "settings_hide_account_help": {
+ "message": "Hide YouTube's sign-in buttons, account menu, and native Subscribe/Join buttons (your local Subscribe stays).",
+ "description": "Help text for the hide account UI toggle"
+ },
+ "settings_feed_refresh_label": {
+ "message": "Feed refresh interval (minutes)",
+ "description": "Label for the feed refresh interval input"
+ },
+ "settings_feed_refresh_help": {
+ "message": "How often the local feed checks subscribed channels for new uploads",
+ "description": "Help text for the feed refresh interval input"
+ },
+ "subscriptions_refresh": {
+ "message": "Refresh feed",
+ "description": "Button label to refresh the local subscriptions feed"
+ },
+ "subscriptions_open_feed": {
+ "message": "Open YT re:Watch",
+ "description": "Button label to open the dedicated feed page"
+ },
+ "subscriptions_clear_all": {
+ "message": "Clear all",
+ "description": "Button label to remove all local subscriptions"
+ },
+ "subscriptions_no_subs": {
+ "message": "No local subscriptions yet. Search in the extension and use Subscribe.",
+ "description": "Empty state for the subscriptions tab"
+ },
+ "subscriptions_unsubscribe": {
+ "message": "Unsubscribe",
+ "description": "Button label to remove a local subscription"
+ },
+ "subscriptions_refreshing": {
+ "message": "Refreshing…",
+ "description": "Refresh button label while a refresh is in progress"
+ },
+ "subscriptions_refresh_started": {
+ "message": "Refreshing feed in your YouTube tab…",
+ "description": "Message shown when a feed refresh starts"
+ },
+ "subscriptions_open_youtube": {
+ "message": "Open a YouTube tab to refresh the feed.",
+ "description": "Message shown when no YouTube tab is open to refresh the feed"
+ },
+ "message_settings_saved": {
+ "message": "Settings saved",
+ "description": "Generic confirmation that settings were saved"
}
-}
\ No newline at end of file
+}
diff --git a/src/_locales/fr/tabs.json b/src/_locales/fr/tabs.json
index e4440ee..b4cab8f 100644
--- a/src/_locales/fr/tabs.json
+++ b/src/_locales/fr/tabs.json
@@ -226,5 +226,25 @@
"playlists_unknown_title": {
"message": "Playlist inconnue",
"description": "Fallback title for playlists with no title"
+ },
+ "tab_subscriptions": {
+ "message": "Subs",
+ "description": "Tab label for local subscriptions"
+ },
+ "tab_watchlater": {
+ "message": "Later",
+ "description": "Tab label for local Watch Later list"
+ },
+ "watchlater_empty": {
+ "message": "Nothing saved yet. On YouTube, right-click a video and choose \"Save to Watch Later (local)\".",
+ "description": "Empty state for the Watch Later tab"
+ },
+ "watchlater_clear_all": {
+ "message": "Clear all",
+ "description": "Button to remove all Watch Later items"
+ },
+ "watchlater_confirm_clear": {
+ "message": "Clear all Watch Later items?",
+ "description": "Confirmation prompt before clearing the Watch Later list"
}
-}
\ No newline at end of file
+}
diff --git a/src/background.js b/src/background.js
index 2f98f63..053e5cd 100644
--- a/src/background.js
+++ b/src/background.js
@@ -1,10 +1,9 @@
console.log('YouTube Video History Tracker background script running.');
-// Import required scripts for Chrome service worker
+// Load shared storage modules when this file runs as a Chrome MV3 service worker.
if (typeof importScripts === 'function') {
try {
- importScripts('indexeddb-storage.js', 'storage.js'); // sync-service.js removed - redundant with hybrid storage
- // Verify critical globals are available
+ importScripts('indexeddb-storage.js', 'storage.js');
if (typeof ytIndexedDBStorage === 'undefined') {
console.error('[Background] ytIndexedDBStorage not available after import');
}
@@ -39,6 +38,180 @@ const stateManager = {
}
};
+function sendContinuationThroughTab(tabId, token, config) {
+ return new Promise((resolve) => {
+ chrome.tabs.sendMessage(
+ tabId,
+ { type: 'fetchYouTubeSearchContinuationInTab', token, config },
+ (response) => {
+ if (chrome.runtime.lastError) {
+ resolve({ error: chrome.runtime.lastError.message });
+ return;
+ }
+ resolve(response || {});
+ }
+ );
+ });
+}
+
+function sendSearchPageThroughTab(tabId, query) {
+ return new Promise((resolve) => {
+ chrome.tabs.sendMessage(
+ tabId,
+ { type: 'fetchYouTubeSearchPageInTab', query },
+ (response) => {
+ if (chrome.runtime.lastError) {
+ resolve({ error: chrome.runtime.lastError.message });
+ return;
+ }
+ resolve(response || {});
+ }
+ );
+ });
+}
+
+async function createTemporaryYouTubeContext(url = 'https://www.youtube.com/') {
+ if (chrome.windows && typeof chrome.windows.create === 'function') {
+ try {
+ const win = await chrome.windows.create({
+ url,
+ focused: false,
+ type: 'popup',
+ width: 120,
+ height: 120,
+ left: -32000,
+ top: -32000
+ });
+ let tab = win && win.tabs && win.tabs[0];
+ if (!tab && win && win.id != null) {
+ const tabs = await chrome.tabs.query({ windowId: win.id });
+ tab = tabs && tabs[0];
+ }
+ if (tab && tab.id != null) {
+ return { tab, windowId: win.id || null };
+ }
+ if (win && win.id != null) {
+ try { await chrome.windows.remove(win.id); } catch (_) {}
+ }
+ } catch (_) {
+ // Some browsers do not allow offscreen extension-created windows.
+ }
+ }
+
+ const tab = await chrome.tabs.create({
+ url,
+ active: false
+ });
+ return { tab, windowId: null };
+}
+
+async function waitForTabReady(tabId, maxWaitMs = 7000) {
+ const startedAt = Date.now();
+ while (Date.now() - startedAt < maxWaitMs) {
+ try {
+ const tab = await chrome.tabs.get(tabId);
+ if (tab && tab.status === 'complete') return;
+ } catch (_) {
+ return;
+ }
+ await new Promise((resolve) => setTimeout(resolve, 200));
+ }
+}
+
+async function fetchSearchPageThroughYouTubeTab(query) {
+ const searchUrl = 'https://www.youtube.com/results?search_query=' + encodeURIComponent(query);
+ try {
+ const response = await fetch(searchUrl, { credentials: 'include' });
+ if (!response.ok) throw new Error(`YouTube returned ${response.status}`);
+ return { html: await response.text() };
+ } catch (error) {
+ // Fall through to a real youtube.com tab. Firefox can reject direct
+ // extension-page requests while allowing same-origin tab requests.
+ }
+
+ const tabs = await chrome.tabs.query({ url: ['*://www.youtube.com/*'] });
+ let temporaryTab = null;
+ let temporaryWindowId = null;
+ let lastError = 'Could not contact a YouTube tab';
+
+ async function tryTab(tab, attempts = 16) {
+ if (!tab || tab.id == null) return null;
+ for (let attempt = 0; attempt < attempts; attempt++) {
+ const response = await sendSearchPageThroughTab(tab.id, query);
+ if (response.html) return response;
+ lastError = response.error || lastError;
+ await new Promise((resolve) => setTimeout(resolve, 250));
+ }
+ return null;
+ }
+
+ try {
+ for (const tab of tabs) {
+ const response = await tryTab(tab, 8);
+ if (response) return response;
+ }
+
+ const temporary = await createTemporaryYouTubeContext(searchUrl);
+ temporaryTab = temporary.tab;
+ temporaryWindowId = temporary.windowId;
+ if (temporaryTab && temporaryTab.id != null) {
+ await waitForTabReady(temporaryTab.id);
+ }
+ const temporaryResponse = await tryTab(temporaryTab, 28);
+ if (temporaryResponse) return temporaryResponse;
+ return { error: lastError };
+ } finally {
+ if (temporaryWindowId != null && chrome.windows && typeof chrome.windows.remove === 'function') {
+ try { await chrome.windows.remove(temporaryWindowId); } catch (_) {}
+ } else if (temporaryTab && temporaryTab.id != null) {
+ try { await chrome.tabs.remove(temporaryTab.id); } catch (_) {}
+ }
+ }
+}
+
+async function fetchContinuationThroughYouTubeTab(token, config) {
+ const tabs = await chrome.tabs.query({ url: ['*://www.youtube.com/*'] });
+ let temporaryTab = null;
+ let temporaryWindowId = null;
+ let lastError = 'Could not contact a YouTube tab';
+
+ async function tryTab(tab, attempts = 16) {
+ if (!tab || tab.id == null) return null;
+ for (let attempt = 0; attempt < attempts; attempt++) {
+ const response = await sendContinuationThroughTab(tab.id, token, config);
+ if (response.data) return response;
+ lastError = response.error || lastError;
+ await new Promise((resolve) => setTimeout(resolve, 250));
+ }
+ return null;
+ }
+
+ try {
+ for (const tab of tabs) {
+ const response = await tryTab(tab, 8);
+ if (response) return response;
+ }
+
+ const temporary = await createTemporaryYouTubeContext();
+ temporaryTab = temporary.tab;
+ temporaryWindowId = temporary.windowId;
+ if (temporaryTab && temporaryTab.id != null) {
+ await waitForTabReady(temporaryTab.id);
+ }
+ const temporaryResponse = await tryTab(temporaryTab, 28);
+ if (temporaryResponse) {
+ return temporaryResponse;
+ }
+ return { error: lastError };
+ } finally {
+ if (temporaryWindowId != null && chrome.windows && typeof chrome.windows.remove === 'function') {
+ try { await chrome.windows.remove(temporaryWindowId); } catch (_) {}
+ } else if (temporaryTab && temporaryTab.id != null) {
+ try { await chrome.tabs.remove(temporaryTab.id); } catch (_) {}
+ }
+ }
+}
+
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
(async () => {
@@ -65,7 +238,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
});
await stateManager.set({ activePopupWindowId: newWindow.id });
}
-
+
if (message.type === 'videoUpdate') {
await stateManager.set({ lastVideoUpdate: message.data });
@@ -83,6 +256,111 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
return; // Return early because we're using sendResponse
}
+ if (message.type === 'getPlaylistMetadata') {
+ const playlistId = String(message.playlistId || '').trim();
+ if (!playlistId) {
+ sendResponse({ error: 'Missing playlist ID' });
+ return;
+ }
+ try {
+ const playlistUrl = `https://www.youtube.com/playlist?list=${encodeURIComponent(playlistId)}`;
+ const oembedUrl = 'https://www.youtube.com/oembed?format=json&url=' + encodeURIComponent(playlistUrl);
+ const response = await fetch(oembedUrl, { credentials: 'omit' });
+ if (!response.ok) throw new Error(`YouTube returned ${response.status}`);
+ const data = await response.json();
+ sendResponse({
+ title: data && data.title ? data.title : '',
+ thumbnail: data && data.thumbnail_url ? data.thumbnail_url : ''
+ });
+ } catch (error) {
+ sendResponse({ error: error && error.message ? error.message : String(error) });
+ }
+ return;
+ }
+
+ if (message.type === 'fetchYouTubeSearchPage') {
+ const query = String(message.query || '').trim();
+ if (!query) {
+ sendResponse({ error: 'Missing YouTube search query' });
+ return;
+ }
+ try {
+ sendResponse(await fetchSearchPageThroughYouTubeTab(query));
+ } catch (error) {
+ sendResponse({ error: error && error.message ? error.message : String(error) });
+ }
+ return;
+ }
+
+ if (message.type === 'fetchYouTubeSearchContinuation') {
+ const token = String(message.token || '');
+ const config = message.config || {};
+ if (!token || !config.clientVersion) {
+ sendResponse({ error: 'Missing YouTube search continuation data' });
+ return;
+ }
+ try {
+ const tabResponse = await fetchContinuationThroughYouTubeTab(token, config);
+ if (tabResponse.data) {
+ sendResponse(tabResponse);
+ return;
+ }
+
+ const key = config.apiKey ? `?key=${encodeURIComponent(config.apiKey)}` : '';
+ const headers = {
+ 'Content-Type': 'application/json',
+ 'X-YouTube-Client-Name': '1',
+ 'X-YouTube-Client-Version': String(config.clientVersion)
+ };
+ if (config.visitorData) headers['X-Goog-Visitor-Id'] = String(config.visitorData);
+ const context = {
+ client: {
+ clientName: 'WEB',
+ clientVersion: String(config.clientVersion),
+ visitorData: String(config.visitorData || ''),
+ hl: 'en',
+ gl: 'US'
+ }
+ };
+ if (config.clickTrackingParams) {
+ context.clickTracking = { clickTrackingParams: String(config.clickTrackingParams) };
+ }
+ const body = JSON.stringify({
+ context,
+ continuation: token
+ });
+ const endpoints = [
+ `https://www.youtube.com/youtubei/v1/search${key}`,
+ `https://youtubei.googleapis.com/youtubei/v1/search${key}`
+ ];
+ let lastError = null;
+ for (const endpoint of endpoints) {
+ try {
+ const response = await fetch(endpoint, {
+ method: 'POST',
+ credentials: endpoint.includes('www.youtube.com') ? 'include' : 'omit',
+ headers,
+ body
+ });
+ if (!response.ok) {
+ lastError = new Error(`YouTube returned ${response.status}`);
+ continue;
+ }
+ const data = await response.json();
+ sendResponse({ data });
+ return;
+ } catch (error) {
+ lastError = error;
+ }
+ }
+ throw lastError || new Error('YouTube search paging failed');
+ } catch (error) {
+ sendResponse({ error: error && error.message ? error.message : String(error) });
+ }
+ return;
+ }
+
+
// Handle content script storage RPC calls (ytStorageCall)
if (message.type === 'ytStorageCall') {
if (typeof ytStorage === 'undefined') {
@@ -150,3 +428,139 @@ chrome.windows.onRemoved.addListener(async (windowId) => {
}
});
+// ============================================================
+// Watch Later — right-click menu (no button injected into YouTube)
+// ============================================================
+const WATCH_LATER_MENU_ID = 'ytvht-add-watchlater';
+
+function createContextMenus() {
+ if (!chrome.contextMenus) return;
+ // removeAll first so reloading the extension doesn't throw "duplicate id".
+ chrome.contextMenus.removeAll(() => {
+ chrome.contextMenus.create({
+ id: WATCH_LATER_MENU_ID,
+ title: 'Save to Watch Later (local)',
+ contexts: ['link', 'page', 'video'],
+ documentUrlPatterns: ['*://*.youtube.com/*'],
+ targetUrlPatterns: ['*://*.youtube.com/*', '*://youtu.be/*']
+ });
+ });
+}
+
+// MV3 service workers are torn down and restarted; re-create the menu on both
+// install/update and browser startup so it's always present.
+chrome.runtime.onInstalled.addListener(createContextMenus);
+if (chrome.runtime.onStartup) chrome.runtime.onStartup.addListener(createContextMenus);
+
+// Extract a YouTube video id (and shorts flag) from any link/page URL.
+function parseYouTubeVideo(rawUrl) {
+ if (!rawUrl) return null;
+ try {
+ const u = new URL(rawUrl);
+ let videoId = u.searchParams.get('v');
+ let isShorts = false;
+ if (!videoId) {
+ const m = u.pathname.match(/\/shorts\/([\w-]{6,})/);
+ if (m) { videoId = m[1]; isShorts = true; }
+ }
+ if (!videoId && /(^|\.)youtu\.be$/.test(u.hostname)) {
+ const seg = u.pathname.split('/').filter(Boolean)[0];
+ if (seg) videoId = seg;
+ }
+ if (!videoId) return null;
+ videoId = videoId.slice(0, 20);
+ const url = isShorts
+ ? `https://www.youtube.com/shorts/${videoId}`
+ : `https://www.youtube.com/watch?v=${videoId}`;
+ return { videoId, url, isShorts };
+ } catch (_) {
+ return null;
+ }
+}
+
+// Injected into the page to read a video's title/channel from the DOM. Must be
+// fully self-contained (it runs in the tab, not here).
+function ytvhtExtractVideoMeta(videoId) {
+ const txt = (el) => (el && (el.textContent || '').trim()) || '';
+ const onWatch = new URLSearchParams(location.search).get('v') === videoId;
+ const onShorts = location.pathname.indexOf('/shorts/' + videoId) === 0;
+ if (onWatch || onShorts) {
+ const h1 = document.querySelector('h1.ytd-watch-metadata yt-formatted-string, h1.ytd-watch-metadata, yt-shorts-video-title-view-model h2 span');
+ const title = txt(h1) || document.title.replace(/ - YouTube( Shorts)?$/, '').trim();
+ const ch = document.querySelector('ytd-video-owner-renderer #channel-name a, #owner #channel-name a, ytd-channel-name a, #owner-name a');
+ return { title, channelName: txt(ch) };
+ }
+ const a = document.querySelector(
+ 'a#thumbnail[href*="' + videoId + '"], a[href*="watch?v=' + videoId + '"], a[href*="/shorts/' + videoId + '"]'
+ );
+ if (a) {
+ const box = a.closest('ytd-rich-item-renderer, ytd-video-renderer, ytd-grid-video-renderer, ytd-compact-video-renderer, ytd-playlist-video-renderer, ytd-playlist-panel-video-renderer, yt-lockup-view-model') || document;
+ const t = box.querySelector('#video-title, a#video-title-link, yt-formatted-string#video-title, .yt-lockup-metadata-view-model-wiz__title');
+ let title = txt(t) || a.getAttribute('title') || a.getAttribute('aria-label') || '';
+ const ch = box.querySelector('ytd-channel-name a, #channel-name a, .yt-content-metadata-view-model-wiz__metadata-text');
+ return { title: title.trim(), channelName: txt(ch) };
+ }
+ return { title: '', channelName: '' };
+}
+
+// Brief icon-badge feedback so the user knows the save worked (no extra
+// "notifications" permission needed).
+function flashBadge(tabId, text, color) {
+ if (!chrome.action || tabId == null) return;
+ try {
+ chrome.action.setBadgeBackgroundColor({ color: color || '#34a853', tabId });
+ chrome.action.setBadgeText({ text, tabId });
+ setTimeout(() => {
+ try { chrome.action.setBadgeText({ text: '', tabId }); } catch (_) {}
+ }, 2000);
+ } catch (_) { /* ignore */ }
+}
+
+async function handleAddWatchLater(info, tab) {
+ const src = info.linkUrl || info.pageUrl || (tab && tab.url) || '';
+ const parsed = parseYouTubeVideo(src);
+ if (!parsed) {
+ flashBadge(tab && tab.id, '?', '#ea4335');
+ return;
+ }
+
+ // Best-effort title/channel from the page; falls back to empty if blocked.
+ let meta = { title: '', channelName: '' };
+ try {
+ if (tab && tab.id != null && chrome.scripting) {
+ const results = await chrome.scripting.executeScript({
+ target: { tabId: tab.id },
+ func: ytvhtExtractVideoMeta,
+ args: [parsed.videoId]
+ });
+ if (results && results[0] && results[0].result) meta = results[0].result;
+ }
+ } catch (_) { /* ignore — save with what we have */ }
+
+ const record = {
+ videoId: parsed.videoId,
+ url: parsed.url,
+ isShorts: parsed.isShorts,
+ title: (meta.title || '').slice(0, 300),
+ channelName: (meta.channelName || '').slice(0, 200),
+ addedAt: Date.now()
+ };
+
+ try {
+ if (typeof ytStorage === 'undefined' || typeof ytStorage.setWatchLater !== 'function') {
+ throw new Error('ytStorage.setWatchLater unavailable');
+ }
+ await ytStorage.setWatchLater(parsed.videoId, record);
+ flashBadge(tab && tab.id, '+1', '#34a853');
+ } catch (e) {
+ console.error('[WatchLater] save failed:', e);
+ flashBadge(tab && tab.id, 'x', '#ea4335');
+ }
+}
+
+if (chrome.contextMenus && chrome.contextMenus.onClicked) {
+ chrome.contextMenus.onClicked.addListener((info, tab) => {
+ if (info.menuItemId !== WATCH_LATER_MENU_ID) return;
+ handleAddWatchLater(info, tab).catch((e) => console.error('[WatchLater]', e));
+ });
+}
diff --git a/src/content-css.js b/src/content-css.js
new file mode 100644
index 0000000..840de4d
--- /dev/null
+++ b/src/content-css.js
@@ -0,0 +1,260 @@
+(function() {
+ 'use strict';
+
+ function injectCSS() {
+ document.getElementById('ytvht-styles')?.remove();
+ const isExtensionFeed = !!document.getElementById('ytvht-home-feed');
+ document.documentElement.toggleAttribute('ytvht-extension-feed', isExtensionFeed);
+ document.documentElement.removeAttribute('ytvht-native-badge-only');
+
+ const style = document.createElement('style');
+ style.id = 'ytvht-styles';
+ style.textContent = `
+ .ytvht-viewed-label {
+ position: absolute !important;
+ top: 0 !important;
+ left: 0 !important;
+ padding: 8px 4px !important;
+ background-color: #4285f4 !important;
+ color: #fff !important;
+ font-size: 16px !important;
+ font-weight: bold !important;
+ z-index: 9999 !important;
+ border-radius: 0 0 4px 0 !important;
+ pointer-events: none !important;
+ }
+ .ytvht-progress-bar {
+ position: absolute !important;
+ bottom: 0 !important;
+ left: 0 !important;
+ height: 4px !important;
+ background-color: #4285f4 !important;
+ z-index: 2147483647 !important;
+ pointer-events: none !important;
+ }
+ .ytvht-progress-mask {
+ position: absolute !important;
+ bottom: 0 !important;
+ left: 0 !important;
+ width: 100% !important;
+ height: 6px !important;
+ background: rgba(0, 0, 0, 0.72) !important;
+ z-index: 2147483646 !important;
+ pointer-events: none !important;
+ }
+ /* Compact overlay for small extension-feed thumbnails.
+ Two-class selectors keep higher specificity than the dynamic
+ single-class rules in updateOverlayCSS, so size stays small here. */
+ .ytvht-viewed-label.ytvht-compact {
+ padding: 2px 4px !important;
+ font-size: 10px !important;
+ border-radius: 0 0 3px 0 !important;
+ }
+ .ytvht-progress-bar.ytvht-compact {
+ height: 3px !important;
+ }
+ .ytvht-progress-mask.ytvht-compact {
+ height: 5px !important;
+ }
+ .ytvht-native-overlay-target {
+ position: relative !important;
+ }
+ .ytvht-native-overlay-target::before {
+ content: attr(data-ytvht-label) !important;
+ position: absolute !important;
+ top: 0 !important;
+ left: 0 !important;
+ padding: var(--ytvht-label-padding, 8px 4px) !important;
+ background-color: var(--ytvht-overlay-color, #4285f4) !important;
+ color: #fff !important;
+ font-size: var(--ytvht-label-font-size, 16px) !important;
+ font-weight: bold !important;
+ z-index: 2147483647 !important;
+ border-radius: 0 0 4px 0 !important;
+ pointer-events: none !important;
+ }
+ .ytvht-native-overlay-target::after {
+ content: "" !important;
+ position: absolute !important;
+ bottom: 0 !important;
+ left: 0 !important;
+ width: var(--ytvht-progress-width, 100%) !important;
+ height: var(--ytvht-progress-height, 4px) !important;
+ background-color: var(--ytvht-overlay-color, #4285f4) !important;
+ z-index: 2147483647 !important;
+ pointer-events: none !important;
+ }
+ .ytvht-native-overlay-target.ytvht-native-overlay-no-progress::after {
+ content: none !important;
+ display: none !important;
+ }
+ .ytvht-native-progress-line {
+ position: absolute !important;
+ bottom: 0 !important;
+ left: 0 !important;
+ height: 4px !important;
+ background-color: #4285f4 !important;
+ z-index: 2147483647 !important;
+ pointer-events: none !important;
+ }
+ ytd-notification-renderer .ytvht-viewed-label,
+ ytd-notification-renderer .ytvht-progress-mask,
+ ytd-notification-renderer .ytvht-progress-bar,
+ ytd-notification-renderer .ytvht-native-progress-line,
+ ytd-notification-renderer .ytvht-native-overlay-target::before,
+ ytd-notification-renderer .ytvht-native-overlay-target::after,
+ ytd-multi-page-menu-renderer .ytvht-viewed-label,
+ ytd-multi-page-menu-renderer .ytvht-progress-mask,
+ ytd-multi-page-menu-renderer .ytvht-progress-bar,
+ ytd-multi-page-menu-renderer .ytvht-native-progress-line,
+ ytd-multi-page-menu-renderer .ytvht-native-overlay-target::before,
+ ytd-multi-page-menu-renderer .ytvht-native-overlay-target::after,
+ ytd-popup-container .ytvht-viewed-label,
+ ytd-popup-container .ytvht-progress-mask,
+ ytd-popup-container .ytvht-progress-bar,
+ ytd-popup-container .ytvht-native-progress-line,
+ ytd-popup-container .ytvht-native-overlay-target::before,
+ ytd-popup-container .ytvht-native-overlay-target::after {
+ content: none !important;
+ display: none !important;
+ }
+ html[ytvht-native-badge-only]:not([ytvht-extension-feed]) ytd-thumbnail-overlay-resume-playback-renderer,
+ html[ytvht-native-badge-only]:not([ytvht-extension-feed]) yt-thumbnail-overlay-resume-playback-renderer,
+ html[ytvht-native-badge-only]:not([ytvht-extension-feed]) yt-thumbnail-overlay-progress-bar-view-model,
+ html[ytvht-native-badge-only]:not([ytvht-extension-feed]) yt-thumbnail-overlay-progress-view-model,
+ html[ytvht-native-badge-only]:not([ytvht-extension-feed]) .ytThumbnailOverlayProgressBarHost,
+ html[ytvht-native-badge-only]:not([ytvht-extension-feed]) .ytThumbnailOverlayProgressBarProgress,
+ html[ytvht-native-badge-only]:not([ytvht-extension-feed]) [class*="ThumbnailOverlayProgress"],
+ html[ytvht-native-badge-only]:not([ytvht-extension-feed]) [class*="thumbnailOverlayProgress"],
+ html[ytvht-native-badge-only]:not([ytvht-extension-feed]) #progress:not(.ytp-progress-bar):not(.ytp-play-progress):not(.ytp-load-progress),
+ html[ytvht-native-badge-only]:not([ytvht-extension-feed]) tp-yt-paper-progress,
+ html[ytvht-native-badge-only]:not([ytvht-extension-feed]) yt-progress-bar {
+ display: none !important;
+ }
+ .ytvht-has-overlay ytd-thumbnail-overlay-resume-playback-renderer,
+ .ytvht-has-overlay yt-thumbnail-overlay-resume-playback-renderer,
+ .ytvht-has-overlay yt-thumbnail-overlay-progress-bar-view-model,
+ .ytvht-has-overlay yt-thumbnail-overlay-progress-view-model,
+ .ytvht-has-overlay .ytThumbnailOverlayProgressBarHost,
+ .ytvht-has-overlay .ytThumbnailOverlayProgressBarProgress,
+ .ytvht-has-overlay [class*="ThumbnailOverlayProgress"],
+ .ytvht-has-overlay [class*="thumbnailOverlayProgress"],
+ .ytvht-has-overlay #progress:not(.ytvht-progress-bar),
+ .ytvht-has-overlay tp-yt-paper-progress,
+ .ytvht-has-overlay yt-progress-bar {
+ display: none !important;
+ }
+ .ytvht-remove-button {
+ position: absolute !important;
+ bottom: 10px !important;
+ right: 10px !important;
+ width: 26px !important;
+ height: 26px !important;
+ line-height: 26px !important;
+ text-align: center !important;
+ font-size: 18px !important;
+ font-weight: 700 !important;
+ color: #fff !important;
+ background: #4285f4 !important;
+ border: none !important;
+ border-radius: 50% !important;
+ cursor: pointer !important;
+ z-index: 10000 !important;
+ pointer-events: auto !important;
+ opacity: 0 !important;
+ transition: opacity 0.15s ease-in-out !important;
+ user-select: none !important;
+ }
+ ytd-thumbnail:hover .ytvht-remove-button,
+ a#thumbnail:hover .ytvht-remove-button,
+ ytd-playlist-video-renderer:hover .ytvht-remove-button,
+ ytd-playlist-panel-video-renderer:hover .ytvht-remove-button,
+ yt-lockup-view-model:hover .ytvht-remove-button,
+ ytd-video-renderer:hover .ytvht-remove-button,
+ ytd-rich-item-renderer:hover .ytvht-remove-button,
+ ytd-grid-video-renderer:hover .ytvht-remove-button {
+ opacity: 0.95 !important;
+ }
+ .ytvht-info {
+ position: absolute !important;
+ top: -120px !important;
+ right: 0 !important;
+ background: var(--yt-spec-brand-background-primary, #0f0f0f) !important;
+ border: 1px solid var(--yt-spec-text-secondary, #aaa) !important;
+ border-radius: 8px !important;
+ padding: 12px !important;
+ width: 300px !important;
+ z-index: 9999 !important;
+ color: var(--yt-spec-text-primary, #fff) !important;
+ font-size: 14px !important;
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1) !important;
+ }
+ .ytvht-info-content {
+ display: flex !important;
+ align-items: start !important;
+ gap: 12px !important;
+ }
+ .ytvht-info-text {
+ flex-grow: 1 !important;
+ }
+ .ytvht-info-title {
+ font-weight: 500 !important;
+ margin-bottom: 8px !important;
+ color: #fff !important;
+ }
+ .ytvht-info-description {
+ color: #aaa !important;
+ line-height: 1.4 !important;
+ }
+ .ytvht-info-highlight {
+ color: #fff !important;
+ background: rgba(255,255,255,0.1) !important;
+ padding: 2px 6px !important;
+ border-radius: 4px !important;
+ }
+ .ytvht-close {
+ background: none !important;
+ border: none !important;
+ padding: 4px 8px !important;
+ cursor: pointer !important;
+ color: #aaa !important;
+ font-size: 20px !important;
+ opacity: 0.8 !important;
+ transition: opacity 0.2s !important;
+ }
+ .ytvht-close:hover {
+ opacity: 1 !important;
+ }
+ `;
+ document.head.appendChild(style);
+ }
+
+ function updateOverlayCSS(size, color) {
+ let styleElement = document.getElementById('ytvht-dynamic-styles');
+ if (!styleElement) {
+ styleElement = document.createElement('style');
+ styleElement.id = 'ytvht-dynamic-styles';
+ document.head.appendChild(styleElement);
+ }
+
+ styleElement.textContent = `
+ .ytvht-viewed-label {
+ padding: ${size.fontSize / 2}px 4px !important;
+ background-color: ${color} !important;
+ font-size: ${size.fontSize}px !important;
+ }
+ .ytvht-progress-bar {
+ height: ${size.bar}px !important;
+ background-color: ${color} !important;
+ }
+ .ytvht-remove-button {
+ background: ${color} !important;
+ }
+ `;
+ }
+
+ window.YTVHTContentCss = {
+ injectCSS,
+ updateOverlayCSS
+ };
+})();
diff --git a/src/content-import.js b/src/content-import.js
new file mode 100644
index 0000000..3f73b24
--- /dev/null
+++ b/src/content-import.js
@@ -0,0 +1,168 @@
+(function() {
+ 'use strict';
+
+ function createContentImportHelpers(dependencies) {
+ const log = dependencies.log;
+ const getStorage = dependencies.getStorage;
+
+ async function runImport(records, playlists, mergeMode) {
+ return getStorage().importRecords(records || [], playlists || [], !!mergeMode);
+ }
+
+ function maybeShowImportOverlayFromHash() {
+ if (window.location.hash === '#ytlh_import') {
+ showImportOverlay();
+ }
+ }
+
+ function showImportOverlay() {
+ if (document.getElementById('ytvhtImportOverlay')) {
+ return;
+ }
+ if (!document.body) {
+ return;
+ }
+
+ const overlay = document.createElement('div');
+ overlay.id = 'ytvhtImportOverlay';
+ overlay.style.position = 'fixed';
+ overlay.style.inset = '0';
+ overlay.style.background = 'rgba(0,0,0,0.4)';
+ overlay.style.zIndex = '999999';
+ overlay.style.display = 'flex';
+ overlay.style.alignItems = 'center';
+ overlay.style.justifyContent = 'center';
+
+ const modal = document.createElement('div');
+ modal.style.background = '#222';
+ modal.style.color = '#fff';
+ modal.style.padding = '20px';
+ modal.style.borderRadius = '8px';
+ modal.style.minWidth = '360px';
+ modal.style.maxWidth = '480px';
+ modal.style.boxShadow = '0 4px 16px rgba(0,0,0,0.5)';
+
+ modal.innerHTML = `
+