mirror of
https://github.com/mue/mue.git
synced 2026-06-13 12:08:42 +02:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5d8bfec0e |
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"enabledPlugins": {
|
||||
"typescript-lsp@claude-plugins-official": true
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(perl -i -pe:*)",
|
||||
"Bash(bun run:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
153
.github/.copilot-instructions.md
vendored
153
.github/.copilot-instructions.md
vendored
@@ -1,153 +0,0 @@
|
||||
# Mue Project Guidelines for GitHub Copilot
|
||||
|
||||
## Project Overview
|
||||
|
||||
Mue is a fast, open-source browser extension that provides a customizable new tab page for Chrome, Firefox, Edge, and Safari. Built with React and Vite, it emphasizes privacy, performance, and extensibility.
|
||||
|
||||
## Technical Stack
|
||||
|
||||
- **Framework**: React 19 with hooks and functional components
|
||||
- **Build Tool**: Vite with SWC
|
||||
- **Package Manager**: Bun >= 1.3.0 (ALWAYS use Bun, never npm or yarn)
|
||||
- **Styling**: SCSS with component-scoped styles
|
||||
- **Target**: Browser extensions (Manifest V3)
|
||||
- **Testing**: Manual testing across Chrome, Firefox, and Safari
|
||||
|
||||
## Code Style & Quality
|
||||
|
||||
- Follow ESLint configuration in `eslint.config.js`
|
||||
- Run `bun run lint:fix` before committing
|
||||
- Run `bun run pretty` to format code with Prettier
|
||||
- Use conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, etc.
|
||||
- All commits must pass commitlint validation
|
||||
|
||||
## File Organization
|
||||
|
||||
```
|
||||
src/
|
||||
├── components/ # Reusable UI components (Elements, Form, Layout)
|
||||
├── features/ # Feature-specific code (background, weather, etc.)
|
||||
├── utils/ # Utility functions and helpers
|
||||
├── contexts/ # React Context providers
|
||||
├── hooks/ # Custom React hooks
|
||||
├── i18n/ # Internationalization files
|
||||
└── scss/ # Global styles, variables, mixins
|
||||
```
|
||||
|
||||
## Critical Rules
|
||||
|
||||
### 1. Translation Files (IMPORTANT!)
|
||||
|
||||
- **`en_GB.json` is the base translation file**
|
||||
- Never edit other locale files directly
|
||||
- Workflow: Edit `en_GB.json` → Run `bun run translations`
|
||||
- This ensures consistent formatting across all 30+ locales
|
||||
- Translation files are in `src/i18n/locales/`
|
||||
|
||||
### 2. Branch Strategy
|
||||
|
||||
```
|
||||
dev (active development)
|
||||
↓
|
||||
beta (release candidates)
|
||||
↓
|
||||
main (production/stable)
|
||||
```
|
||||
|
||||
- All PRs target the `dev` branch
|
||||
- Never commit directly to `beta` or `main`
|
||||
- See `CONTRIBUTING.md` for full workflow
|
||||
|
||||
### 3. Package Manager
|
||||
|
||||
- **ALWAYS use Bun**, never npm or yarn
|
||||
- Commands: `bun install`, `bun run <script>`
|
||||
- Project requires Bun >= 1.3.0
|
||||
|
||||
### 4. Multi-Browser Support
|
||||
|
||||
- Test changes in Chrome, Firefox, and Safari when possible
|
||||
- Browser-specific builds are in `build/chrome/`, `build/firefox/`, etc.
|
||||
- Manifest files: `manifest/chrome.json`, `manifest/firefox.json`
|
||||
|
||||
### 5. State Management
|
||||
|
||||
- Use `useLocalStorageState` hook for persistent settings
|
||||
- Use React Context for shared state (see `src/contexts/`)
|
||||
- Store user data in localStorage or IndexedDB (via `customBackgroundDB.js`)
|
||||
|
||||
### 6. Styling
|
||||
|
||||
- SCSS files in `src/scss/`
|
||||
- Use existing variables from `_variables.scss`
|
||||
- Use mixins from `_mixins.scss` (especially for responsive design)
|
||||
- Mobile styles in `_mobile.scss`
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
bun run dev # Start dev server
|
||||
bun run dev:host # Dev server accessible on network
|
||||
bun run build # Build for production (all browsers)
|
||||
bun run lint # Run linters
|
||||
bun run lint:fix # Auto-fix linting issues
|
||||
bun run pretty # Format code
|
||||
bun run translations # Update translation files
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Component Structure
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react';
|
||||
import './ComponentName.scss';
|
||||
|
||||
export default function ComponentName({ prop1, prop2 }) {
|
||||
const [state, setState] = useState();
|
||||
|
||||
return <div className="component-name">{/* JSX content */}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### Using localStorage
|
||||
|
||||
```jsx
|
||||
import { useLocalStorageState } from 'utils/useLocalStorageState';
|
||||
|
||||
const [value, setValue] = useLocalStorageState('settingKey', defaultValue);
|
||||
```
|
||||
|
||||
### Using Translations
|
||||
|
||||
```jsx
|
||||
import { useContext } from 'react';
|
||||
import { TranslationContext } from 'contexts';
|
||||
|
||||
const { t } = useContext(TranslationContext);
|
||||
const text = t('translation.key');
|
||||
```
|
||||
|
||||
## Things to Avoid
|
||||
|
||||
- ❌ Don't use npm or yarn (use Bun)
|
||||
- ❌ Don't edit locale files other than `en_GB.json`
|
||||
- ❌ Don't commit directly to `beta` or `main` branches
|
||||
- ❌ Don't introduce new dependencies without discussion
|
||||
- ❌ Don't skip linting/formatting before commits
|
||||
- ❌ Don't use class components (use functional components only)
|
||||
|
||||
## When Making Changes
|
||||
|
||||
1. Check which files need updates (component, styles, utils)
|
||||
2. Follow existing patterns in similar components
|
||||
3. Update translations in `en_GB.json` if adding new text
|
||||
4. Run `bun run lint:fix` and `bun run pretty`
|
||||
5. Test in at least Chrome and Firefox
|
||||
6. Write conventional commit message
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- See `CONTRIBUTING.md` for full contribution guidelines
|
||||
- See `docs/RELEASE_PROCESS.md` for release procedures
|
||||
- Check existing components in `src/components/` for patterns
|
||||
13
.github/dependabot.yml
vendored
13
.github/dependabot.yml
vendored
@@ -1,12 +1 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
target-branch: "dev"
|
||||
labels:
|
||||
- "dependencies"
|
||||
commit-message:
|
||||
prefix: "chore"
|
||||
include: "scope"
|
||||
|
||||
|
||||
3
.github/workflows/automerge.yml
vendored
3
.github/workflows/automerge.yml
vendored
@@ -8,11 +8,12 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: '1.3.1'
|
||||
bun-version: latest
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
- name: Lint
|
||||
run: bun run lint
|
||||
continue-on-error: true
|
||||
- name: Build
|
||||
run: bun run build
|
||||
automerge:
|
||||
|
||||
30
.github/workflows/beta-release.yml
vendored
30
.github/workflows/beta-release.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
branches:
|
||||
- beta
|
||||
tags:
|
||||
- "v*-beta.*"
|
||||
- 'v*-beta.*'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: "1.3.1"
|
||||
bun-version: '1.3.1'
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
run: |
|
||||
# Get the latest beta or production tag
|
||||
PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
||||
|
||||
|
||||
if [ -z "$PREVIOUS_TAG" ]; then
|
||||
echo "No previous tag found, using all commits"
|
||||
COMMITS=$(git log --pretty=format:"- %s (%h)" HEAD)
|
||||
@@ -53,13 +53,13 @@ jobs:
|
||||
echo "Generating changelog from $PREVIOUS_TAG to HEAD"
|
||||
COMMITS=$(git log --pretty=format:"- %s (%h)" ${PREVIOUS_TAG}..HEAD)
|
||||
fi
|
||||
|
||||
|
||||
# Create changelog with categorization
|
||||
FEATURES=$(echo "$COMMITS" | grep -i "^- feat" || echo "")
|
||||
FIXES=$(echo "$COMMITS" | grep -i "^- fix" || echo "")
|
||||
CHORES=$(echo "$COMMITS" | grep -i "^- chore\|^- docs\|^- style\|^- refactor" || echo "")
|
||||
OTHER=$(echo "$COMMITS" | grep -v -i "^- feat\|^- fix\|^- chore\|^- docs\|^- style\|^- refactor" || echo "")
|
||||
|
||||
|
||||
{
|
||||
echo "changelog<<EOF"
|
||||
if [ -n "$FEATURES" ]; then
|
||||
@@ -98,35 +98,35 @@ jobs:
|
||||
- name: Create or Update GitHub Pre-Release
|
||||
run: |
|
||||
RELEASE_NOTES=$(cat <<EOF
|
||||
## 🧪 Mue v${{ steps.version.outputs.version }}
|
||||
|
||||
## 🧪 Mue Beta v${{ steps.version.outputs.version }}
|
||||
|
||||
**⚠️ This is a beta release for testing purposes only.**
|
||||
|
||||
|
||||
### Testing Instructions
|
||||
1. Download the appropriate ZIP file below
|
||||
2. For Chrome: Load as unpacked extension or install from [unlisted link](https://chromewebstore.google.com/detail/mue/bngmbednanpcfochchhgbkookpiaiaid) (dev team only)
|
||||
3. For Firefox: Install via about:debugging → Load Temporary Add-on
|
||||
4. Report issues at https://github.com/mue/mue/issues
|
||||
|
||||
|
||||
${{ steps.changelog.outputs.changelog }}
|
||||
|
||||
|
||||
### Installation Files
|
||||
- **Chrome/Edge**: \`chrome-${{ steps.version.outputs.version }}.zip\`
|
||||
- **Firefox**: \`firefox-${{ steps.version.outputs.version }}.zip\`
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
**🔗 Demo**: [demo.muetab.com](https://demo.muetab.com)
|
||||
**📱 Beta Branch Demo**: [mue-git-beta-mue.vercel.app](https://mue-git-beta-mue.vercel.app)
|
||||
EOF
|
||||
)
|
||||
|
||||
|
||||
if [ "${{ steps.check_release.outputs.exists }}" = "true" ]; then
|
||||
echo "Updating existing release..."
|
||||
gh release edit "v${{ steps.version.outputs.version }}" \
|
||||
--notes "$RELEASE_NOTES" \
|
||||
--prerelease
|
||||
|
||||
|
||||
# Upload new files (will replace if they exist)
|
||||
gh release upload "v${{ steps.version.outputs.version }}" \
|
||||
"build/chrome-${{ steps.version.outputs.version }}.zip" \
|
||||
@@ -137,7 +137,7 @@ jobs:
|
||||
gh release create "v${{ steps.version.outputs.version }}" \
|
||||
"build/chrome-${{ steps.version.outputs.version }}.zip" \
|
||||
"build/firefox-${{ steps.version.outputs.version }}.zip" \
|
||||
--title "v${{ steps.version.outputs.version }}" \
|
||||
--title "Beta v${{ steps.version.outputs.version }}" \
|
||||
--notes "$RELEASE_NOTES" \
|
||||
--prerelease
|
||||
fi
|
||||
|
||||
32
.github/workflows/hotfix-release.yml
vendored
32
.github/workflows/hotfix-release.yml
vendored
@@ -42,21 +42,6 @@ jobs:
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Generate changelog
|
||||
id: changelog
|
||||
run: |
|
||||
COMMITS=$(git log --pretty=format:"- %s (%h)" origin/main..${{ github.event.inputs.branch_name }})
|
||||
|
||||
{
|
||||
echo "changelog<<EOF"
|
||||
echo "### 🚨 Hotfix"
|
||||
echo "${{ github.event.inputs.description }}"
|
||||
echo ""
|
||||
echo "### Changes"
|
||||
echo "$COMMITS"
|
||||
echo "EOF"
|
||||
} >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Calculate hotfix version (auto-patch bump)
|
||||
id: version
|
||||
run: |
|
||||
@@ -116,6 +101,23 @@ jobs:
|
||||
git push origin main
|
||||
git push origin "v${{ steps.version.outputs.new_version }}"
|
||||
|
||||
- name: Generate changelog
|
||||
id: changelog
|
||||
run: |
|
||||
# Get commits from hotfix branch
|
||||
git checkout ${{ github.event.inputs.branch_name }}
|
||||
COMMITS=$(git log --pretty=format:"- %s (%h)" origin/main..${{ github.event.inputs.branch_name }})
|
||||
|
||||
{
|
||||
echo "changelog<<EOF"
|
||||
echo "### 🚨 Hotfix"
|
||||
echo "${{ github.event.inputs.description }}"
|
||||
echo ""
|
||||
echo "### Changes"
|
||||
echo "$COMMITS"
|
||||
echo "EOF"
|
||||
} >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create GitHub Release
|
||||
run: |
|
||||
git checkout main
|
||||
|
||||
21
.github/workflows/production-release.yml
vendored
21
.github/workflows/production-release.yml
vendored
@@ -162,25 +162,6 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Back-merge main to beta
|
||||
run: |
|
||||
git fetch origin beta
|
||||
git checkout beta
|
||||
git merge --no-ff main -m "chore: back-merge production release v${{ steps.version.outputs.version }} from main"
|
||||
git push origin beta
|
||||
|
||||
- name: Back-merge main to dev
|
||||
run: |
|
||||
git fetch origin dev
|
||||
git checkout dev
|
||||
git merge --no-ff main -m "chore: back-merge production release v${{ steps.version.outputs.version }} from main"
|
||||
git push origin dev
|
||||
|
||||
- name: Output success summary
|
||||
run: |
|
||||
echo "## 🚀 Production Release Published!" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -203,4 +184,4 @@ jobs:
|
||||
echo "- [ ] Update [muetab.com/blog/changelog](https://muetab.com/blog/changelog)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [ ] Announce release on Discord/social media" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [ ] Monitor issue tracker for bug reports" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [x] Back-merged \`main\` to \`beta\` and \`dev\` (automated)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- [ ] Merge \`main\` back to \`beta\` and \`dev\` to sync version" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
57
.github/workflows/submit-beta.yml
vendored
57
.github/workflows/submit-beta.yml
vendored
@@ -1,57 +0,0 @@
|
||||
name: Submit to Browser Stores (Beta)
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [prereleased]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Pre-release tag to re-submit (e.g. v7.6.1-beta.3)"
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
submit-beta:
|
||||
runs-on: ubuntu-latest
|
||||
environment: beta
|
||||
|
||||
steps:
|
||||
- name: Resolve tag and version
|
||||
id: tag
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
TAG="${{ github.event.inputs.tag }}"
|
||||
else
|
||||
TAG="${{ github.event.release.tag_name }}"
|
||||
fi
|
||||
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||
echo "version=${TAG#v}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Download release ZIPs
|
||||
run: |
|
||||
mkdir -p dist
|
||||
gh release download "${{ steps.tag.outputs.tag }}" \
|
||||
--pattern "chrome-*.zip" \
|
||||
--dir ./dist
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
- name: Verify ZIP
|
||||
run: |
|
||||
ls -la dist/
|
||||
test -n "$(ls dist/chrome-*.zip 2>/dev/null)" || \
|
||||
{ echo "chrome ZIP not found in dist/"; exit 1; }
|
||||
|
||||
- name: Submit to Chrome Web Store (Trusted Testers)
|
||||
uses: PlasmoHQ/bpp@v3
|
||||
with:
|
||||
keys: ${{ secrets.SUBMIT_KEYS_BETA }}
|
||||
chrome-zip: dist/chrome-${{ steps.tag.outputs.version }}.zip
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## Beta Store Submission: ${{ steps.tag.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Chrome Web Store (trustedTesters)" >> $GITHUB_STEP_SUMMARY
|
||||
56
.github/workflows/submit.yml
vendored
56
.github/workflows/submit.yml
vendored
@@ -1,57 +1,27 @@
|
||||
name: Submit to Browser Stores
|
||||
name: Submit
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Release tag to re-submit (e.g. v7.6.1)"
|
||||
description: "Release tag to submit, i.e 6.0.5"
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
submit:
|
||||
runs-on: ubuntu-latest
|
||||
environment: production
|
||||
|
||||
steps:
|
||||
- name: Resolve tag and version
|
||||
id: tag
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
TAG="${{ github.event.inputs.tag }}"
|
||||
else
|
||||
TAG="${{ github.event.release.tag_name }}"
|
||||
fi
|
||||
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||
echo "version=${TAG#v}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Download release ZIPs
|
||||
run: |
|
||||
mkdir -p dist
|
||||
gh release download "${{ steps.tag.outputs.tag }}" \
|
||||
--pattern "chrome-*.zip" \
|
||||
--dir ./dist
|
||||
- name: Setup Chrome
|
||||
uses: browser-actions/setup-chrome@latest
|
||||
with:
|
||||
chrome-version: latest
|
||||
- name: Download Github Release Assets
|
||||
uses: PlasmoHQ/download-release-asset@v1.0.0
|
||||
with:
|
||||
tag: ${{ github.event.inputs.tag }}
|
||||
- name: Browser Plugin Publish
|
||||
uses: PlasmoHQ/bpp@v2
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
- name: Verify ZIP
|
||||
run: |
|
||||
ls -la dist/
|
||||
test -f "dist/chrome-${{ steps.tag.outputs.version }}.zip" || \
|
||||
{ echo "chrome ZIP not found"; exit 1; }
|
||||
|
||||
- name: Submit to Chrome Web Store
|
||||
uses: PlasmoHQ/bpp@v3
|
||||
PUPPETEER_EXECUTABLE_PATH: /opt/hostedtoolcache/chromium/latest/x64/chrome
|
||||
with:
|
||||
keys: ${{ secrets.SUBMIT_KEYS }}
|
||||
chrome-zip: dist/chrome-${{ steps.tag.outputs.version }}.zip
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## Store Submission: ${{ steps.tag.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Chrome Web Store (listed)" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
149
.github/workflows/version-bump.yml
vendored
149
.github/workflows/version-bump.yml
vendored
@@ -1,18 +1,31 @@
|
||||
name: Version Bump
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
labels:
|
||||
- 'release'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
bump_type:
|
||||
description: 'Version bump type'
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- patch # 7.5.0 -> 7.5.1 (bug fixes)
|
||||
- minor # 7.5.0 -> 7.6.0 (new features)
|
||||
- major # 7.5.0 -> 8.0.0 (breaking changes)
|
||||
pre_release:
|
||||
description: 'Pre-release label (leave empty for stable release)'
|
||||
required: false
|
||||
type: choice
|
||||
options:
|
||||
- ''
|
||||
- beta
|
||||
- rc
|
||||
- alpha
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
bump-version:
|
||||
if: github.event.pull_request.merged == true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -31,99 +44,99 @@ jobs:
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Extract bump info from PR
|
||||
id: bump_info
|
||||
run: |
|
||||
BUMP_TYPE=$(echo "${{ github.event.pull_request.body }}" | grep -oP "bump: \K(major|minor|patch)" || echo "patch")
|
||||
IS_RELEASE=$(echo "${{ github.event.pull_request.body }}" | grep -q "is_release: true" && echo "true" || echo "false")
|
||||
|
||||
echo "bump_type=$BUMP_TYPE" >> $GITHUB_OUTPUT
|
||||
echo "is_release=$IS_RELEASE" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Calculate new version
|
||||
id: version
|
||||
run: |
|
||||
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
||||
echo "Current version: $CURRENT_VERSION"
|
||||
|
||||
# Remove any pre-release suffix for base version
|
||||
BASE_VERSION=$(echo $CURRENT_VERSION | sed 's/-.*$//')
|
||||
IFS='.' read -r -a VERSION_PARTS <<< "$BASE_VERSION"
|
||||
|
||||
|
||||
MAJOR="${VERSION_PARTS[0]}"
|
||||
MINOR="${VERSION_PARTS[1]}"
|
||||
PATCH="${VERSION_PARTS[2]}"
|
||||
|
||||
case "${{ steps.bump_info.outputs.bump_type }}" in
|
||||
major)
|
||||
MAJOR=$((MAJOR + 1))
|
||||
MINOR=0
|
||||
PATCH=0
|
||||
;;
|
||||
minor)
|
||||
MINOR=$((MINOR + 1))
|
||||
PATCH=0
|
||||
;;
|
||||
patch)
|
||||
PATCH=$((PATCH + 1))
|
||||
;;
|
||||
esac
|
||||
|
||||
NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
|
||||
|
||||
# Add pre-release suffix if NOT a production release AND NOT on main branch
|
||||
if [ "${{ steps.bump_info.outputs.is_release }}" != "true" ] && [ "${{ github.ref_name }}" != "main" ]; then
|
||||
BETA_COUNT=$(git tag -l "v${NEW_VERSION}-beta.*" | wc -l)
|
||||
BETA_NUM=$((BETA_COUNT + 1))
|
||||
NEW_VERSION="${NEW_VERSION}-beta.${BETA_NUM}"
|
||||
|
||||
# If requesting a pre-release and current is already a pre-release, keep base version
|
||||
# Otherwise, bump the version based on type
|
||||
if [ -n "${{ github.event.inputs.pre_release }}" ] && [ "$IS_PRERELEASE" = "true" ]; then
|
||||
# Keep existing base version for iterative betas (7.6.0-beta.1 -> 7.6.0-beta.2)
|
||||
NEW_VERSION="$BASE_VERSION"
|
||||
else
|
||||
# Bump version based on type
|
||||
case "${{ github.event.inputs.bump_type }}" in
|
||||
major)
|
||||
MAJOR=$((MAJOR + 1))
|
||||
MINOR=0
|
||||
PATCH=0
|
||||
;;
|
||||
minor)
|
||||
MINOR=$((MINOR + 1))
|
||||
PATCH=0
|
||||
;;
|
||||
patch)
|
||||
PATCH=$((PATCH + 1))
|
||||
;;
|
||||
esac
|
||||
|
||||
NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
|
||||
fi
|
||||
|
||||
# Browser extension manifests require clean semver — strip pre-release suffix
|
||||
STABLE_VERSION=$(echo "$NEW_VERSION" | sed 's/-.*$//')
|
||||
|
||||
|
||||
# Add pre-release label if specified
|
||||
if [ -n "${{ github.event.inputs.pre_release }}" ]; then
|
||||
# Get beta number by counting existing beta tags for this version
|
||||
BETA_COUNT=$(git tag -l "v${NEW_VERSION}-${{ github.event.inputs.pre_release }}.*" | wc -l)
|
||||
BETA_NUM=$((BETA_COUNT + 1))
|
||||
NEW_VERSION="${NEW_VERSION}-${{ github.event.inputs.pre_release }}.${BETA_NUM}"
|
||||
fi
|
||||
|
||||
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "stable_version=$STABLE_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "New version will be: $NEW_VERSION"
|
||||
|
||||
- name: Update package.json
|
||||
run: bun x json -I -f package.json -e "this.version='${{ steps.version.outputs.new_version }}'"
|
||||
run: |
|
||||
bun x json -I -f package.json -e "this.version='${{ steps.version.outputs.new_version }}'"
|
||||
|
||||
- name: Update Chrome manifest
|
||||
run: bun x json -I -f manifest/chrome.json -e "this.version='${{ steps.version.outputs.stable_version }}'"
|
||||
run: |
|
||||
VERSION_WITHOUT_PRERELEASE=$(echo "${{ steps.version.outputs.new_version }}" | sed 's/-.*$//')
|
||||
bun x json -I -f manifest/chrome.json -e "this.version='${VERSION_WITHOUT_PRERELEASE}'"
|
||||
|
||||
- name: Update Firefox manifest
|
||||
run: bun x json -I -f manifest/firefox.json -e "this.version='${{ steps.version.outputs.stable_version }}'"
|
||||
run: |
|
||||
VERSION_WITHOUT_PRERELEASE=$(echo "${{ steps.version.outputs.new_version }}" | sed 's/-.*$//')
|
||||
bun x json -I -f manifest/firefox.json -e "this.version='${VERSION_WITHOUT_PRERELEASE}'"
|
||||
|
||||
- name: Update Safari manifest
|
||||
run: bun x json -I -f safari/Mue\ Extension/Resources/manifest.json -e "this.version='${{ steps.version.outputs.stable_version }}'"
|
||||
run: |
|
||||
VERSION_WITHOUT_PRERELEASE=$(echo "${{ steps.version.outputs.new_version }}" | sed 's/-.*$//')
|
||||
bun x json -I -f safari/Mue\ Extension/Resources/manifest.json -e "this.version='${VERSION_WITHOUT_PRERELEASE}'"
|
||||
|
||||
- name: Update Safari Xcode project
|
||||
run: sed -i "s/MARKETING_VERSION = [^;]*/MARKETING_VERSION = ${{ steps.version.outputs.stable_version }}/g" safari/Mue.xcodeproj/project.pbxproj
|
||||
run: |
|
||||
VERSION_WITHOUT_PRERELEASE=$(echo "${{ steps.version.outputs.new_version }}" | sed 's/-.*$//')
|
||||
sed -i "s/MARKETING_VERSION = [^;]*/MARKETING_VERSION = ${VERSION_WITHOUT_PRERELEASE}/g" safari/Mue.xcodeproj/project.pbxproj
|
||||
|
||||
- name: Update constants.js
|
||||
run: sed -i "s/export const VERSION = '[^']*'/export const VERSION = '${{ steps.version.outputs.new_version }}'/" src/config/constants.js
|
||||
run: |
|
||||
sed -i "s/export const VERSION = '[^']*'/export const VERSION = '${{ steps.version.outputs.new_version }}'/" src/config/constants.js
|
||||
|
||||
- name: Commit and tag
|
||||
- name: Commit version bump
|
||||
run: |
|
||||
git add package.json manifest/chrome.json manifest/firefox.json safari/Mue\ Extension/Resources/manifest.json safari/Mue.xcodeproj/project.pbxproj src/config/constants.js
|
||||
git commit -m "chore: bump version to ${{ steps.version.outputs.new_version }}"
|
||||
git tag -a "v${{ steps.version.outputs.new_version }}" -m "Release v${{ steps.version.outputs.new_version }}"
|
||||
|
||||
- name: Push changes
|
||||
run: |
|
||||
git push origin ${{ github.ref_name }}
|
||||
git push origin "v${{ steps.version.outputs.new_version }}"
|
||||
|
||||
- name: Auto back-merge (main only)
|
||||
if: github.ref_name == 'main'
|
||||
run: |
|
||||
git fetch origin
|
||||
git checkout beta
|
||||
git merge --no-ff origin/main -m "chore: back-merge main into beta"
|
||||
git push origin beta
|
||||
|
||||
git checkout dev
|
||||
git merge --no-ff origin/beta -m "chore: back-merge beta into dev"
|
||||
git push origin dev
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "✅ Version bumped to ${{ steps.version.outputs.new_version }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "📦 Tag: v${{ steps.version.outputs.new_version }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "🔀 Branch: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "📦 Tag created: v${{ steps.version.outputs.new_version }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### Files updated:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- package.json" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -132,9 +145,3 @@ jobs:
|
||||
echo "- safari/Mue Extension/Resources/manifest.json" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- safari/Mue.xcodeproj/project.pbxproj" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- src/config/constants.js" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "${{ github.ref_name }}" = "main" ]; then
|
||||
echo "### 🔄 Auto back-merge:" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- main → beta ✅" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- beta → dev ✅" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,7 +5,6 @@ node_modules/
|
||||
build/
|
||||
.idea/
|
||||
dist/
|
||||
.claude/worktrees/
|
||||
|
||||
# Safari Extension Build Files
|
||||
safari/Mue Extension/Resources/*.html
|
||||
@@ -30,7 +29,6 @@ safari/DerivedData/
|
||||
safari/build/
|
||||
|
||||
# Files
|
||||
unused-translations.txt
|
||||
package-lock.json
|
||||
.stylelintcache
|
||||
yarn-error.log
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
#!/bin/sh
|
||||
bunx --bun commitlint --edit "$1"
|
||||
bunx --bun commitlint --edit $1
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"setup": [],
|
||||
"teardown": [],
|
||||
"run": [
|
||||
"bun run dev"
|
||||
]
|
||||
}
|
||||
121
CLAUDE.md
121
CLAUDE.md
@@ -1,121 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## What is Mue?
|
||||
|
||||
Mue is a browser extension (Chrome, Firefox, Safari) that replaces the new tab page with a customizable dashboard featuring widgets like clock, weather, quotes, greetings, quick links, and backgrounds. Built with React 19, Vite 7, and Manifest V3.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
bun install # Install dependencies (always use bun, never npm/yarn)
|
||||
bun run dev # Dev server with HMR (opens browser automatically)
|
||||
bun run dev:host # Dev server exposed on network
|
||||
bun run build # Production build for all browsers (Chrome, Firefox, Safari)
|
||||
bun run lint # Run ESLint + Stylelint
|
||||
bun run lint:fix # Auto-fix lint issues
|
||||
bun run pretty # Format with Prettier
|
||||
bun run translations # Sync all locale files from en_GB.json base
|
||||
bun run translations:percentages # Update translation completion stats
|
||||
bun run translations:unused # Find unused translation keys
|
||||
```
|
||||
|
||||
Build outputs: `build/chrome/`, `build/firefox/`, `safari/Mue Extension/Resources/`. Vite's `prepareBuilds` plugin (in `vite.config.mjs`) copies dist + manifests + icons into each browser folder and creates versioned zips.
|
||||
|
||||
E2E tests use Cypress:
|
||||
|
||||
```bash
|
||||
bun run test:e2e # Open Cypress UI
|
||||
bun run test:e2e:headless # Run headlessly (CI)
|
||||
```
|
||||
|
||||
Tests live in `cypress/e2e/`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Bootstrap Flow
|
||||
|
||||
1. `src/index.jsx` - Initializes i18n from localStorage language, sets up Sentry, runs data migrations, exposes global `window.t()`, renders `<RouterProvider router={router} />`
|
||||
2. `src/router/` - Hash-based router (`createHashRouter`) required for extension compatibility (no physical file routing). `ModalRouter.jsx` and `RouterBridge.jsx` handle modal deep-linking.
|
||||
3. `src/App.jsx` - `useAppSetup()` checks first-run state, calls `loadSettings()` (which applies theme, fonts, custom CSS to the DOM), listens for EventBus `'refresh'` events. Renders: `<TranslationProvider>` → `<Background>` + `<CustomWidgets>` + `<Widgets>` + `<Modals>`
|
||||
|
||||
### State Management (no Redux/Zustand)
|
||||
|
||||
All persistent state lives in **localStorage**. Components read from localStorage on mount and re-read when they receive EventBus refresh events. The only React Context is `TranslationContext` for i18n.
|
||||
|
||||
- `localStorage.getItem('key') === 'true'` is the standard boolean check pattern
|
||||
- Settings changes write to localStorage, then emit `EventBus.emit('refresh', 'category')` to notify widgets
|
||||
- `src/utils/settings/load.js` applies localStorage settings to the DOM (theme classes, injected style elements for custom fonts/CSS)
|
||||
|
||||
### EventBus (`src/utils/eventbus.js`)
|
||||
|
||||
Static class wrapping DOM CustomEvents. Primary communication mechanism between settings UI and widgets.
|
||||
|
||||
Key events:
|
||||
|
||||
- `'refresh'` with payload: `'quote'`, `'greeting'`, `'background'`, `'widgets'`, `'clock'`, `'other'` - triggers widget reload
|
||||
- `'languageChange'` with `{language: 'en_GB'}` - switches locale
|
||||
- `'modal'` with `'openMainModal'` - opens settings modal
|
||||
|
||||
Pattern: register in `useEffect`, clean up with `EventBus.off()` on unmount.
|
||||
|
||||
### Feature Organization (`src/features/`)
|
||||
|
||||
Each feature (background, time, quote, greeting, weather, search, quicklinks, message, navbar, stats, marketplace, welcome, helpers, modals) follows this structure:
|
||||
|
||||
```
|
||||
feature/
|
||||
├── index.jsx # Main component
|
||||
├── options/index.jsx # Settings panel UI
|
||||
├── hooks/ # Feature-specific hooks (useQuoteState, useQuoteLoader, etc.)
|
||||
├── components/ # Subcomponents
|
||||
├── api/ # Data fetching/processing
|
||||
└── scss/ # Styles
|
||||
```
|
||||
|
||||
The `misc` feature is special - it contains the modal system (`modals/Modals.jsx`), widget layout (`CustomWidgets.jsx`, `views/Widgets.jsx`), and the settings view (`views/Settings.jsx`).
|
||||
|
||||
### Modal & Settings System
|
||||
|
||||
`Modals.jsx` orchestrates four modals (main, welcome, update, apps). The main modal has three tabs: Settings, Discover (marketplace), Library. Deep-linking via URL hash: `#settings/appearance/fonts`, `#discover/quote_packs`.
|
||||
|
||||
### Global Variables (`src/config/variables.js`)
|
||||
|
||||
Singleton object holding `language` (i18n instance), `languagecode`, `stats`, and `constants`. Mutated during initialization. `variables.getMessage()` is dynamically set by TranslationContext.
|
||||
|
||||
### Hooks
|
||||
|
||||
- `useFrequencyInterval()` - configurable update intervals with visibility-aware pause/resume
|
||||
- `useCachedFetch()` - fetch with localStorage caching and TTL
|
||||
- `useT()` / `useTranslation()` - access translation function from context
|
||||
|
||||
### Path Aliases (configured in `vite.config.mjs`)
|
||||
|
||||
Use these instead of relative imports: `@/`, `components/`, `contexts/`, `hooks/`, `assets/`, `config/`, `features/`, `lib/`, `scss/`, `translations/`, `utils/`, `i18n/`
|
||||
|
||||
## Code Rules
|
||||
|
||||
### Do not add comments
|
||||
|
||||
Keep code self-explanatory. Use descriptive names instead of comments.
|
||||
|
||||
### Do not use emojis in code strings
|
||||
|
||||
No emojis in console logs, placeholders, or code strings. Exception: user-facing toast notifications may include emojis.
|
||||
|
||||
### All user-visible strings must use i18n
|
||||
|
||||
Never hardcode user-facing text. Use `const t = useT(); t('widgets.greeting.morning')` or `getMessage('key')`. Console logs don't need i18n.
|
||||
|
||||
### Translation workflow
|
||||
Edit `src/i18n/locales/en_GB.json` first (it's the base file), then run `bun run translations` to sync all other locales.
|
||||
|
||||
### Naming conventions
|
||||
Concise names without redundant prefixes. `const [open, setOpen] = useState(false)` not `const [isOpen, setIsOpen]`. Exception: `is` prefix is fine for boolean-returning functions (`isValid()`), `has` prefix for boolean properties.
|
||||
|
||||
### Branch strategy
|
||||
Three branches: `dev` (all PRs target here) → `beta` → `main`. Hotfix branches (`hotfix/*`) branch from `main`.
|
||||
|
||||
### Conventional commits
|
||||
Enforced by commitlint: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, `test:`, `style:`, `perf:`. Scopes encouraged: `feat(weather): add hourly forecast`.
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM oven/bun:1.1.42
|
||||
FROM oven/bun:latest
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -8,4 +8,4 @@ RUN bun install
|
||||
|
||||
EXPOSE 5173
|
||||
|
||||
CMD ["bun", "run", "dev:host"]
|
||||
CMD ["bun", "run", "dev:host"]
|
||||
45
GEMINI.md
45
GEMINI.md
@@ -1,45 +0,0 @@
|
||||
# Mue Engineering Standards (GEMINI.md)
|
||||
|
||||
This file contains project-specific instructions and guidelines for Mue.
|
||||
|
||||
## Stack & Commands
|
||||
|
||||
- **Package Manager:** Always use `bun`. Never use `npm` or `yarn`.
|
||||
- **Framework:** React 19, Vite 7, Manifest V3.
|
||||
- **Core Commands:**
|
||||
- `bun install`: Install dependencies.
|
||||
- `bun run dev`: Start dev server.
|
||||
- `bun run build`: Production build for Chrome, Firefox, Safari.
|
||||
- `bun run lint`: Run ESLint and Stylelint.
|
||||
- `bun run lint:fix`: Auto-fix lint issues.
|
||||
- `bun run pretty`: Format with Prettier.
|
||||
- `bun run translations`: Sync locale files from `en_GB.json`.
|
||||
- `bun run translations:percentages`: Update translation stats.
|
||||
- `bun run translations:unused`: Find unused translation keys.
|
||||
- `bun run test:e2e:headless`: Run Cypress tests headlessly.
|
||||
|
||||
## Architecture & Patterns
|
||||
|
||||
- **State Management:** Use **localStorage** for persistent state.
|
||||
- **Communication:** Use `src/utils/eventbus.js` for inter-widget/UI communication.
|
||||
- Register in `useEffect`, cleanup with `EventBus.off()`.
|
||||
- **I18n:** All user-visible strings MUST use `src/i18n/locales/en_GB.json` as the base.
|
||||
- Use `useT()` hook or `getMessage()` helper.
|
||||
- **Routing:** Hash-based router (`createHashRouter`) in `src/router/`.
|
||||
- **Path Aliases:** Use `@/`, `components/`, `features/`, etc. (see `vite.config.mjs`).
|
||||
|
||||
## Coding Rules
|
||||
|
||||
- **No Comments:** Keep code self-explanatory through descriptive naming.
|
||||
- **No Emojis:** Do not use emojis in code strings or logs (except user-facing toasts).
|
||||
- **Naming:** Concise names. Use `const [open, setOpen] = useState(false)`, not `isOpen`.
|
||||
- **Commits:** Conventional commits (`feat:`, `fix:`, etc.) are enforced.
|
||||
- **Translations:** Edit `en_GB.json` first, then run `bun run translations`.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
- `src/features/`: Feature-based organization (background, time, quote, etc.).
|
||||
- `src/components/`: Shared UI elements and layouts.
|
||||
- `src/utils/`: Shared utilities and helpers.
|
||||
- `manifest/`: Extension manifest templates.
|
||||
- `safari/`: Safari-specific project files.
|
||||
362
bun.lock
362
bun.lock
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 0,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "mue",
|
||||
@@ -17,14 +16,12 @@
|
||||
"blurhash": "^2.0.5",
|
||||
"fast-blurhash": "^1.1.4",
|
||||
"image-conversion": "^2.1.1",
|
||||
"maplibre-gl": "^5.24.0",
|
||||
"react": "^19.2.3",
|
||||
"react-best-gradient-color-picker": "^3.0.14",
|
||||
"react-clock": "6.0.0",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-modal": "3.16.3",
|
||||
"react-router": "^7.13.0",
|
||||
"react-toastify": "11.0.5",
|
||||
"use-debounce": "^10.1.0",
|
||||
},
|
||||
@@ -35,7 +32,6 @@
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@vitejs/plugin-react-swc": "^4.2.2",
|
||||
"adm-zip": "0.5.16",
|
||||
"cypress": "^15.10.0",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
@@ -135,10 +131,6 @@
|
||||
|
||||
"@csstools/selector-specificity": ["@csstools/selector-specificity@6.0.0", "", { "peerDependencies": { "postcss-selector-parser": "^7.1.1" } }, "sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA=="],
|
||||
|
||||
"@cypress/request": ["@cypress/request@3.0.10", "", { "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", "caseless": "~0.12.0", "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~4.0.4", "http-signature": "~1.4.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", "qs": "~6.14.1", "safe-buffer": "^5.1.2", "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" } }, "sha512-hauBrOdvu08vOsagkZ/Aju5XuiZx6ldsLfByg1htFeldhex+PeMrYauANzFsMJeAA0+dyPLbDoX2OYuvVoLDkQ=="],
|
||||
|
||||
"@cypress/xvfb": ["@cypress/xvfb@1.2.4", "", { "dependencies": { "debug": "^3.1.0", "lodash.once": "^4.1.1" } }, "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q=="],
|
||||
|
||||
"@dnd-kit/accessibility": ["@dnd-kit/accessibility@3.1.1", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw=="],
|
||||
|
||||
"@dnd-kit/core": ["@dnd-kit/core@6.3.1", "", { "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ=="],
|
||||
@@ -257,26 +249,6 @@
|
||||
|
||||
"@keyv/serialize": ["@keyv/serialize@1.1.1", "", {}, "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA=="],
|
||||
|
||||
"@mapbox/jsonlint-lines-primitives": ["@mapbox/jsonlint-lines-primitives@2.0.2", "", {}, "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ=="],
|
||||
|
||||
"@mapbox/point-geometry": ["@mapbox/point-geometry@1.1.0", "", {}, "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ=="],
|
||||
|
||||
"@mapbox/tiny-sdf": ["@mapbox/tiny-sdf@2.1.0", "", {}, "sha512-uFJhNh36BR4OCuWIEiWaEix9CA2WzT6CAIcqVjWYpnx8+QDtS+oC4QehRrx5cX4mgWs37MmKnwUejeHxVymzNg=="],
|
||||
|
||||
"@mapbox/unitbezier": ["@mapbox/unitbezier@0.0.1", "", {}, "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw=="],
|
||||
|
||||
"@mapbox/vector-tile": ["@mapbox/vector-tile@2.0.4", "", { "dependencies": { "@mapbox/point-geometry": "~1.1.0", "@types/geojson": "^7946.0.16", "pbf": "^4.0.1" } }, "sha512-AkOLcbgGTdXScosBWwmmD7cDlvOjkg/DetGva26pIRiZPdeJYjYKarIlb4uxVzi6bwHO6EWH82eZ5Nuv4T5DUg=="],
|
||||
|
||||
"@mapbox/whoots-js": ["@mapbox/whoots-js@3.1.0", "", {}, "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q=="],
|
||||
|
||||
"@maplibre/geojson-vt": ["@maplibre/geojson-vt@6.1.0", "", { "dependencies": { "kdbush": "^4.0.2" } }, "sha512-2eIY4gZxeKIVOZVNkAMb+5NgXhgsMQpOveTQAvnp53LYqHGJZDidk7Ew0Tged9PThidpbS+NFTh0g4zivhPDzQ=="],
|
||||
|
||||
"@maplibre/maplibre-gl-style-spec": ["@maplibre/maplibre-gl-style-spec@24.8.3", "", { "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", "@mapbox/unitbezier": "^0.0.1", "json-stringify-pretty-compact": "^4.0.0", "minimist": "^1.2.8", "quickselect": "^3.0.0", "rw": "^1.3.3", "tinyqueue": "^3.0.0" }, "bin": { "gl-style-migrate": "dist/gl-style-migrate.mjs", "gl-style-validate": "dist/gl-style-validate.mjs", "gl-style-format": "dist/gl-style-format.mjs" } }, "sha512-e5u6GZXA0t6HnBh63YLefSLy62W1QloF4qVLAo1ae88oH/8+PNK98i7/aVf/XWc4wD9D2lTgW2LaApYLPtM1sA=="],
|
||||
|
||||
"@maplibre/mlt": ["@maplibre/mlt@1.1.9", "", { "dependencies": { "@mapbox/point-geometry": "^1.1.0" } }, "sha512-g/tD8EYJB97udq33ipuJ9a4Q7fcbZnTEnUrgnEc/tLMmEL+zaCbR+X5fkDBO2dgpaAMsLH179qE3UXg2N0Nc/g=="],
|
||||
|
||||
"@maplibre/vt-pbf": ["@maplibre/vt-pbf@4.3.0", "", { "dependencies": { "@mapbox/point-geometry": "^1.1.0", "@mapbox/vector-tile": "^2.0.4", "@maplibre/geojson-vt": "^5.0.4", "@types/geojson": "^7946.0.16", "@types/supercluster": "^7.1.3", "pbf": "^4.0.1", "supercluster": "^8.0.1" } }, "sha512-jIvp8F5hQCcreqOOpEt42TJMUlsrEcpf/kI1T2v85YrQRV6PPXUcEXUg5karKtH6oh47XJZ4kHu56pUkOuqA7w=="],
|
||||
|
||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||
|
||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||
@@ -409,22 +381,10 @@
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="],
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
|
||||
"@types/node": ["@types/node@10.17.60", "", {}, "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="],
|
||||
|
||||
"@types/sinonjs__fake-timers": ["@types/sinonjs__fake-timers@8.1.1", "", {}, "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g=="],
|
||||
|
||||
"@types/sizzle": ["@types/sizzle@2.3.10", "", {}, "sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww=="],
|
||||
|
||||
"@types/supercluster": ["@types/supercluster@7.1.3", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA=="],
|
||||
|
||||
"@types/tmp": ["@types/tmp@0.2.6", "", {}, "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA=="],
|
||||
|
||||
"@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
|
||||
|
||||
"@vitejs/plugin-react-swc": ["@vitejs/plugin-react-swc@4.2.2", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-beta.47", "@swc/core": "^1.13.5" }, "peerDependencies": { "vite": "^4 || ^5 || ^6 || ^7" } }, "sha512-x+rE6tsxq/gxrEJN3Nv3dIV60lFflPj94c90b+NNo6n1QV1QQUTLoL0MpaOVasUZ0zqVBn7ead1B5ecx1JAGfA=="],
|
||||
|
||||
"@wojtekmaj/date-utils": ["@wojtekmaj/date-utils@2.0.2", "", {}, "sha512-Do66mSlSNifFFuo3l9gNKfRMSFi26CRuQMsDJuuKO/ekrDWuTTtE4ZQxoFCUOG+NgxnpSeBq/k5TY8ZseEzLpA=="],
|
||||
@@ -437,20 +397,12 @@
|
||||
|
||||
"adm-zip": ["adm-zip@0.5.16", "", {}, "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ=="],
|
||||
|
||||
"aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
|
||||
|
||||
"ajv": ["ajv@6.12.6", "", { "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" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||
|
||||
"ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="],
|
||||
|
||||
"ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
"ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"arch": ["arch@2.2.0", "", {}, "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
|
||||
@@ -471,26 +423,14 @@
|
||||
|
||||
"arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="],
|
||||
|
||||
"asn1": ["asn1@0.2.6", "", { "dependencies": { "safer-buffer": "~2.1.0" } }, "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ=="],
|
||||
|
||||
"assert-plus": ["assert-plus@1.0.0", "", {}, "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="],
|
||||
|
||||
"ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="],
|
||||
|
||||
"astral-regex": ["astral-regex@2.0.0", "", {}, "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="],
|
||||
|
||||
"async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
|
||||
|
||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
|
||||
"at-least-node": ["at-least-node@1.0.0", "", {}, "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="],
|
||||
|
||||
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
||||
|
||||
"aws-sign2": ["aws-sign2@0.7.0", "", {}, "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA=="],
|
||||
|
||||
"aws4": ["aws4@1.13.2", "", {}, "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw=="],
|
||||
|
||||
"axe-core": ["axe-core@4.11.1", "", {}, "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A=="],
|
||||
|
||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||
@@ -499,16 +439,8 @@
|
||||
|
||||
"base64-arraybuffer": ["base64-arraybuffer@1.0.2", "", {}, "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="],
|
||||
|
||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.9.18", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA=="],
|
||||
|
||||
"bcrypt-pbkdf": ["bcrypt-pbkdf@1.0.2", "", { "dependencies": { "tweetnacl": "^0.14.3" } }, "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w=="],
|
||||
|
||||
"blob-util": ["blob-util@2.0.2", "", {}, "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ=="],
|
||||
|
||||
"bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="],
|
||||
|
||||
"blurhash": ["blurhash@2.0.5", "", {}, "sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
||||
@@ -517,14 +449,8 @@
|
||||
|
||||
"browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
|
||||
|
||||
"buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
||||
|
||||
"buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="],
|
||||
|
||||
"cacheable": ["cacheable@2.3.2", "", { "dependencies": { "@cacheable/memory": "^2.0.7", "@cacheable/utils": "^2.3.3", "hookified": "^1.15.0", "keyv": "^5.5.5", "qified": "^0.6.0" } }, "sha512-w+ZuRNmex9c1TR9RcsxbfTKCjSL0rh1WA5SABbrWprIHeNBdmyQLSYonlDy9gpD+63XT8DgZ/wNh1Smvc9WnJA=="],
|
||||
|
||||
"cachedir": ["cachedir@2.4.0", "", {}, "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ=="],
|
||||
|
||||
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
@@ -535,22 +461,10 @@
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001766", "", {}, "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA=="],
|
||||
|
||||
"caseless": ["caseless@0.12.0", "", {}, "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="],
|
||||
|
||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||
|
||||
"ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="],
|
||||
|
||||
"clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="],
|
||||
|
||||
"cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="],
|
||||
|
||||
"cli-table3": ["cli-table3@0.6.1", "", { "dependencies": { "string-width": "^4.2.0" }, "optionalDependencies": { "colors": "1.4.0" } }, "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA=="],
|
||||
|
||||
"cli-truncate": ["cli-truncate@2.1.0", "", { "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" } }, "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg=="],
|
||||
|
||||
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||
|
||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||
@@ -561,16 +475,6 @@
|
||||
|
||||
"colord": ["colord@2.9.3", "", {}, "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="],
|
||||
|
||||
"colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
|
||||
|
||||
"colors": ["colors@1.4.0", "", {}, "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="],
|
||||
|
||||
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||
|
||||
"commander": ["commander@6.2.1", "", {}, "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="],
|
||||
|
||||
"common-tags": ["common-tags@1.8.2", "", {}, "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA=="],
|
||||
|
||||
"compare-func": ["compare-func@2.0.0", "", { "dependencies": { "array-ify": "^1.0.0", "dot-prop": "^5.1.0" } }, "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA=="],
|
||||
|
||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||
@@ -583,10 +487,6 @@
|
||||
|
||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||
|
||||
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
|
||||
|
||||
"core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
|
||||
|
||||
"cosmiconfig": ["cosmiconfig@9.0.0", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg=="],
|
||||
|
||||
"cosmiconfig-typescript-loader": ["cosmiconfig-typescript-loader@6.2.0", "", { "dependencies": { "jiti": "^2.6.1" }, "peerDependencies": { "@types/node": "*", "cosmiconfig": ">=9", "typescript": ">=5" } }, "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ=="],
|
||||
@@ -601,22 +501,16 @@
|
||||
|
||||
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
||||
|
||||
"cypress": ["cypress@15.10.0", "", { "dependencies": { "@cypress/request": "^3.0.10", "@cypress/xvfb": "^1.2.4", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "@types/tmp": "^0.2.3", "arch": "^2.2.0", "blob-util": "^2.0.2", "bluebird": "^3.7.2", "buffer": "^5.7.1", "cachedir": "^2.3.0", "chalk": "^4.1.0", "ci-info": "^4.1.0", "cli-cursor": "^3.1.0", "cli-table3": "0.6.1", "commander": "^6.2.1", "common-tags": "^1.8.0", "dayjs": "^1.10.4", "debug": "^4.3.4", "enquirer": "^2.3.6", "eventemitter2": "6.4.7", "execa": "4.1.0", "executable": "^4.1.1", "extract-zip": "2.0.1", "figures": "^3.2.0", "fs-extra": "^9.1.0", "hasha": "5.2.2", "is-installed-globally": "~0.4.0", "listr2": "^3.8.3", "lodash": "^4.17.23", "log-symbols": "^4.0.0", "minimist": "^1.2.8", "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", "process": "^0.11.10", "proxy-from-env": "1.0.0", "request-progress": "^3.0.0", "supports-color": "^8.1.1", "systeminformation": "^5.27.14", "tmp": "~0.2.4", "tree-kill": "1.2.2", "untildify": "^4.0.0", "yauzl": "^2.10.0" }, "bin": { "cypress": "bin/cypress" } }, "sha512-OtUh7OMrfEjKoXydlAD1CfG2BvKxIqgWGY4/RMjrqQ3BKGBo5JFKoYNH+Tpcj4xKxWH4XK0Xri+9y8WkxhYbqQ=="],
|
||||
|
||||
"damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="],
|
||||
|
||||
"dargs": ["dargs@8.1.0", "", {}, "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw=="],
|
||||
|
||||
"dashdash": ["dashdash@1.14.1", "", { "dependencies": { "assert-plus": "^1.0.0" } }, "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g=="],
|
||||
|
||||
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
|
||||
|
||||
"data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
|
||||
|
||||
"data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="],
|
||||
|
||||
"dayjs": ["dayjs@1.11.19", "", {}, "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
||||
@@ -625,8 +519,6 @@
|
||||
|
||||
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
||||
|
||||
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
|
||||
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
||||
@@ -635,18 +527,10 @@
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"earcut": ["earcut@3.0.2", "", {}, "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ=="],
|
||||
|
||||
"ecc-jsbn": ["ecc-jsbn@0.1.2", "", { "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" } }, "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.279", "", {}, "sha512-0bblUU5UNdOt5G7XqGiJtpZMONma6WAfq9vsFmtn9x1+joAObr6x1chfqyxFSDCAFwFhCQDrqeAr6MYdpwJ9Hg=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||
|
||||
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
|
||||
|
||||
"enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="],
|
||||
|
||||
"env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
|
||||
|
||||
"error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="],
|
||||
@@ -697,20 +581,8 @@
|
||||
|
||||
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
||||
|
||||
"eventemitter2": ["eventemitter2@6.4.7", "", {}, "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg=="],
|
||||
|
||||
"execa": ["execa@4.1.0", "", { "dependencies": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", "human-signals": "^1.1.1", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.0", "onetime": "^5.1.0", "signal-exit": "^3.0.2", "strip-final-newline": "^2.0.0" } }, "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA=="],
|
||||
|
||||
"executable": ["executable@4.1.1", "", { "dependencies": { "pify": "^2.2.0" } }, "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg=="],
|
||||
|
||||
"exenv": ["exenv@1.2.2", "", {}, "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw=="],
|
||||
|
||||
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
||||
|
||||
"extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="],
|
||||
|
||||
"extsprintf": ["extsprintf@1.3.0", "", {}, "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="],
|
||||
|
||||
"fast-blurhash": ["fast-blurhash@1.1.4", "", {}, "sha512-xeH121M027hgWHHhHWYYjUmMKl8vCH3PPkXk439ixsP8Bvb/r3UFqg12oMSToD/aSAw8EE6XiTdfZ6M5jaLfzg=="],
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
@@ -727,12 +599,8 @@
|
||||
|
||||
"fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
|
||||
|
||||
"fd-slicer": ["fd-slicer@1.1.0", "", { "dependencies": { "pend": "~1.2.0" } }, "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g=="],
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"figures": ["figures@3.2.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg=="],
|
||||
|
||||
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
@@ -745,12 +613,6 @@
|
||||
|
||||
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
||||
|
||||
"forever-agent": ["forever-agent@0.6.1", "", {}, "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw=="],
|
||||
|
||||
"form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
|
||||
|
||||
"fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
@@ -771,24 +633,16 @@
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
|
||||
"get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
|
||||
|
||||
"get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="],
|
||||
|
||||
"get-user-locale": ["get-user-locale@3.0.0", "", { "dependencies": { "memoize": "^10.0.0" } }, "sha512-iJfHSmdYV39UUBw7Jq6GJzeJxUr4U+S03qdhVuDsR9gCEnfbqLy9gYDJFBJQL1riqolFUKQvx36mEkp2iGgJ3g=="],
|
||||
|
||||
"getpass": ["getpass@0.1.7", "", { "dependencies": { "assert-plus": "^1.0.0" } }, "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng=="],
|
||||
|
||||
"git-raw-commits": ["git-raw-commits@4.0.0", "", { "dependencies": { "dargs": "^8.0.0", "meow": "^12.0.1", "split2": "^4.0.0" }, "bin": { "git-raw-commits": "cli.mjs" } }, "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ=="],
|
||||
|
||||
"gl-matrix": ["gl-matrix@3.4.4", "", {}, "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ=="],
|
||||
|
||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||
|
||||
"global-directory": ["global-directory@4.0.1", "", { "dependencies": { "ini": "4.1.1" } }, "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q=="],
|
||||
|
||||
"global-dirs": ["global-dirs@3.0.1", "", { "dependencies": { "ini": "2.0.0" } }, "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA=="],
|
||||
|
||||
"global-modules": ["global-modules@2.0.0", "", { "dependencies": { "global-prefix": "^3.0.0" } }, "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A=="],
|
||||
|
||||
"global-prefix": ["global-prefix@3.0.0", "", { "dependencies": { "ini": "^1.3.5", "kind-of": "^6.0.2", "which": "^1.3.1" } }, "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg=="],
|
||||
@@ -803,11 +657,9 @@
|
||||
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
|
||||
|
||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
"has-flag": ["has-flag@5.0.1", "", {}, "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA=="],
|
||||
|
||||
"has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
|
||||
|
||||
@@ -817,8 +669,6 @@
|
||||
|
||||
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||
|
||||
"hasha": ["hasha@5.2.2", "", { "dependencies": { "is-stream": "^2.0.0", "type-fest": "^0.8.0" } }, "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ=="],
|
||||
|
||||
"hashery": ["hashery@1.4.0", "", { "dependencies": { "hookified": "^1.14.0" } }, "sha512-Wn2i1In6XFxl8Az55kkgnFRiAlIAushzh26PTjL2AKtQcEfXrcLa7Hn5QOWGZEf3LU057P9TwwZjFyxfS1VuvQ=="],
|
||||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
@@ -833,14 +683,8 @@
|
||||
|
||||
"html2canvas": ["html2canvas@1.4.1", "", { "dependencies": { "css-line-break": "^2.1.0", "text-segmentation": "^1.0.3" } }, "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA=="],
|
||||
|
||||
"http-signature": ["http-signature@1.4.0", "", { "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^2.0.2", "sshpk": "^1.18.0" } }, "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg=="],
|
||||
|
||||
"human-signals": ["human-signals@1.1.1", "", {}, "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="],
|
||||
|
||||
"husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="],
|
||||
|
||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||
|
||||
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||
|
||||
"image-conversion": ["image-conversion@2.1.1", "", {}, "sha512-hnMOmP7q2jxA+52FZ+wHNhg3fdFRlgfngsQH2JQHEQkafY7tj/8F15e6Rv/RxDegc872jvyaRHwMbkTZK1Cjbg=="],
|
||||
@@ -853,9 +697,7 @@
|
||||
|
||||
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
||||
|
||||
"indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
|
||||
|
||||
"ini": ["ini@2.0.0", "", {}, "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA=="],
|
||||
"ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
|
||||
|
||||
"internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
|
||||
|
||||
@@ -887,8 +729,6 @@
|
||||
|
||||
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
||||
|
||||
"is-installed-globally": ["is-installed-globally@0.4.0", "", { "dependencies": { "global-dirs": "^3.0.0", "is-path-inside": "^3.0.2" } }, "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ=="],
|
||||
|
||||
"is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
|
||||
|
||||
"is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="],
|
||||
@@ -899,7 +739,7 @@
|
||||
|
||||
"is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="],
|
||||
|
||||
"is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="],
|
||||
"is-path-inside": ["is-path-inside@4.0.0", "", {}, "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA=="],
|
||||
|
||||
"is-plain-object": ["is-plain-object@5.0.0", "", {}, "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="],
|
||||
|
||||
@@ -909,8 +749,6 @@
|
||||
|
||||
"is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="],
|
||||
|
||||
"is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
||||
|
||||
"is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="],
|
||||
|
||||
"is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="],
|
||||
@@ -919,10 +757,6 @@
|
||||
|
||||
"is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="],
|
||||
|
||||
"is-typedarray": ["is-typedarray@1.0.0", "", {}, "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="],
|
||||
|
||||
"is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="],
|
||||
|
||||
"is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="],
|
||||
|
||||
"is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="],
|
||||
@@ -933,8 +767,6 @@
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"isstream": ["isstream@0.1.2", "", {}, "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="],
|
||||
|
||||
"iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="],
|
||||
|
||||
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
||||
@@ -943,36 +775,22 @@
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||
|
||||
"jsbn": ["jsbn@0.1.1", "", {}, "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="],
|
||||
|
||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
|
||||
|
||||
"json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="],
|
||||
|
||||
"json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
|
||||
|
||||
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
||||
|
||||
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
||||
|
||||
"json-stringify-pretty-compact": ["json-stringify-pretty-compact@4.0.0", "", {}, "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q=="],
|
||||
|
||||
"json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="],
|
||||
|
||||
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
|
||||
"jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="],
|
||||
|
||||
"jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="],
|
||||
|
||||
"jsprim": ["jsprim@2.0.2", "", { "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.4.0", "verror": "1.10.0" } }, "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ=="],
|
||||
|
||||
"jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="],
|
||||
|
||||
"kdbush": ["kdbush@4.0.2", "", {}, "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA=="],
|
||||
|
||||
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||
|
||||
"kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
|
||||
@@ -987,12 +805,8 @@
|
||||
|
||||
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
|
||||
|
||||
"listr2": ["listr2@3.14.0", "", { "dependencies": { "cli-truncate": "^2.1.0", "colorette": "^2.0.16", "log-update": "^4.0.0", "p-map": "^4.0.0", "rfdc": "^1.3.0", "rxjs": "^7.5.1", "through": "^2.3.8", "wrap-ansi": "^7.0.0" }, "peerDependencies": { "enquirer": ">= 2.3.0 < 3" }, "optionalPeers": ["enquirer"] }, "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g=="],
|
||||
|
||||
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||
|
||||
"lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="],
|
||||
|
||||
"lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="],
|
||||
|
||||
"lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="],
|
||||
@@ -1003,8 +817,6 @@
|
||||
|
||||
"lodash.mergewith": ["lodash.mergewith@4.6.2", "", {}, "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ=="],
|
||||
|
||||
"lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="],
|
||||
|
||||
"lodash.snakecase": ["lodash.snakecase@4.1.1", "", {}, "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="],
|
||||
|
||||
"lodash.startcase": ["lodash.startcase@4.4.0", "", {}, "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg=="],
|
||||
@@ -1017,16 +829,10 @@
|
||||
|
||||
"lodash.upperfirst": ["lodash.upperfirst@4.3.1", "", {}, "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg=="],
|
||||
|
||||
"log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="],
|
||||
|
||||
"log-update": ["log-update@4.0.0", "", { "dependencies": { "ansi-escapes": "^4.3.0", "cli-cursor": "^3.1.0", "slice-ansi": "^4.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg=="],
|
||||
|
||||
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
||||
|
||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"maplibre-gl": ["maplibre-gl@5.24.0", "", { "dependencies": { "@mapbox/jsonlint-lines-primitives": "^2.0.2", "@mapbox/point-geometry": "^1.1.0", "@mapbox/tiny-sdf": "^2.1.0", "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^2.0.4", "@mapbox/whoots-js": "^3.1.0", "@maplibre/geojson-vt": "^6.1.0", "@maplibre/maplibre-gl-style-spec": "^24.8.1", "@maplibre/mlt": "^1.1.8", "@maplibre/vt-pbf": "^4.3.0", "@types/geojson": "^7946.0.16", "earcut": "^3.0.2", "gl-matrix": "^3.4.4", "kdbush": "^4.0.2", "murmurhash-js": "^1.0.0", "pbf": "^4.0.1", "potpack": "^2.1.0", "quickselect": "^3.0.0", "tinyqueue": "^3.0.0" } }, "sha512-ALyFxgtd5R+65UqZ/++lOqwWcC0SNho9c27fYSyLmG7AfnAul2o46F05aDJGPbFU57wos9dgcIySHs0Xe6ia3A=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"mathml-tag-names": ["mathml-tag-names@4.0.0", "", {}, "sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ=="],
|
||||
@@ -1037,18 +843,10 @@
|
||||
|
||||
"meow": ["meow@14.0.0", "", {}, "sha512-JhC3R1f6dbspVtmF3vKjAWz1EVIvwFrGGPLSdU6rK79xBwHWTuHoLnRX/t1/zHS1Ch1Y2UtIrih7DAHuH9JFJA=="],
|
||||
|
||||
"merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="],
|
||||
|
||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||
|
||||
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||
|
||||
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
|
||||
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
|
||||
|
||||
"mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
|
||||
|
||||
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||
@@ -1057,8 +855,6 @@
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"murmurhash-js": ["murmurhash-js@1.0.0", "", {}, "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
||||
@@ -1069,8 +865,6 @@
|
||||
|
||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||
|
||||
"npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
|
||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||
@@ -1085,22 +879,14 @@
|
||||
|
||||
"object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="],
|
||||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
|
||||
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
|
||||
|
||||
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
||||
|
||||
"ospath": ["ospath@1.2.2", "", {}, "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA=="],
|
||||
|
||||
"own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
|
||||
|
||||
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
||||
|
||||
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
||||
|
||||
"p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="],
|
||||
|
||||
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
||||
|
||||
"parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
|
||||
@@ -1111,18 +897,10 @@
|
||||
|
||||
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
||||
|
||||
"pbf": ["pbf@4.0.1", "", { "dependencies": { "resolve-protobuf-schema": "^2.1.0" }, "bin": { "pbf": "bin/pbf" } }, "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA=="],
|
||||
|
||||
"pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="],
|
||||
|
||||
"performance-now": ["performance-now@2.1.0", "", {}, "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
|
||||
|
||||
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
|
||||
|
||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||
@@ -1139,36 +917,20 @@
|
||||
|
||||
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
||||
|
||||
"potpack": ["potpack@2.1.0", "", {}, "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ=="],
|
||||
|
||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||
|
||||
"prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
|
||||
|
||||
"pretty-bytes": ["pretty-bytes@5.6.0", "", {}, "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg=="],
|
||||
|
||||
"process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="],
|
||||
|
||||
"progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="],
|
||||
|
||||
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||
|
||||
"protocol-buffers-schema": ["protocol-buffers-schema@3.6.1", "", {}, "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ=="],
|
||||
|
||||
"proxy-from-env": ["proxy-from-env@1.0.0", "", {}, "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A=="],
|
||||
|
||||
"pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="],
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"qified": ["qified@0.6.0", "", { "dependencies": { "hookified": "^1.14.0" } }, "sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA=="],
|
||||
|
||||
"qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="],
|
||||
|
||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||
|
||||
"quickselect": ["quickselect@3.0.0", "", {}, "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g=="],
|
||||
|
||||
"rd": ["rd@2.0.1", "", { "dependencies": { "@types/node": "^10.3.6" } }, "sha512-/XdKU4UazUZTXFmI0dpABt8jSXPWcEyaGdk340KdHnsEOdkTctlX23aAK7ChQDn39YGNlAJr1M5uvaKt4QnpNw=="],
|
||||
|
||||
"react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
|
||||
@@ -1187,8 +949,6 @@
|
||||
|
||||
"react-modal": ["react-modal@3.16.3", "", { "dependencies": { "exenv": "^1.2.0", "prop-types": "^15.7.2", "react-lifecycles-compat": "^3.0.0", "warning": "^4.0.3" }, "peerDependencies": { "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19", "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19" } }, "sha512-yCYRJB5YkeQDQlTt17WGAgFJ7jr2QYcWa1SHqZ3PluDmnKJ/7+tVU+E6uKyZ0nODaeEj+xCpK4LcSnKXLMC0Nw=="],
|
||||
|
||||
"react-router": ["react-router@7.13.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw=="],
|
||||
|
||||
"react-toastify": ["react-toastify@11.0.5", "", { "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { "react": "^18 || ^19", "react-dom": "^18 || ^19" } }, "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA=="],
|
||||
|
||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
@@ -1197,8 +957,6 @@
|
||||
|
||||
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
|
||||
|
||||
"request-progress": ["request-progress@3.0.0", "", { "dependencies": { "throttleit": "^1.0.0" } }, "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg=="],
|
||||
|
||||
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
||||
|
||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||
@@ -1207,40 +965,24 @@
|
||||
|
||||
"resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="],
|
||||
|
||||
"resolve-protobuf-schema": ["resolve-protobuf-schema@2.1.0", "", { "dependencies": { "protocol-buffers-schema": "^3.3.1" } }, "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ=="],
|
||||
|
||||
"restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="],
|
||||
|
||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||
|
||||
"rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
|
||||
|
||||
"rollup": ["rollup@4.57.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.0", "@rollup/rollup-android-arm64": "4.57.0", "@rollup/rollup-darwin-arm64": "4.57.0", "@rollup/rollup-darwin-x64": "4.57.0", "@rollup/rollup-freebsd-arm64": "4.57.0", "@rollup/rollup-freebsd-x64": "4.57.0", "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", "@rollup/rollup-linux-arm-musleabihf": "4.57.0", "@rollup/rollup-linux-arm64-gnu": "4.57.0", "@rollup/rollup-linux-arm64-musl": "4.57.0", "@rollup/rollup-linux-loong64-gnu": "4.57.0", "@rollup/rollup-linux-loong64-musl": "4.57.0", "@rollup/rollup-linux-ppc64-gnu": "4.57.0", "@rollup/rollup-linux-ppc64-musl": "4.57.0", "@rollup/rollup-linux-riscv64-gnu": "4.57.0", "@rollup/rollup-linux-riscv64-musl": "4.57.0", "@rollup/rollup-linux-s390x-gnu": "4.57.0", "@rollup/rollup-linux-x64-gnu": "4.57.0", "@rollup/rollup-linux-x64-musl": "4.57.0", "@rollup/rollup-openbsd-x64": "4.57.0", "@rollup/rollup-openharmony-arm64": "4.57.0", "@rollup/rollup-win32-arm64-msvc": "4.57.0", "@rollup/rollup-win32-ia32-msvc": "4.57.0", "@rollup/rollup-win32-x64-gnu": "4.57.0", "@rollup/rollup-win32-x64-msvc": "4.57.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA=="],
|
||||
|
||||
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||
|
||||
"rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="],
|
||||
|
||||
"rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
|
||||
|
||||
"safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="],
|
||||
|
||||
"safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"sass": ["sass@1.97.3", "", { "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "optionalDependencies": { "@parcel/watcher": "^2.4.1" }, "bin": { "sass": "sass.js" } }, "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg=="],
|
||||
|
||||
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||
|
||||
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="],
|
||||
|
||||
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
|
||||
|
||||
"set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
|
||||
@@ -1259,7 +1001,7 @@
|
||||
|
||||
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||
|
||||
"signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
||||
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"slash": ["slash@5.1.0", "", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="],
|
||||
|
||||
@@ -1269,8 +1011,6 @@
|
||||
|
||||
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
||||
|
||||
"sshpk": ["sshpk@1.18.0", "", { "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "bcrypt-pbkdf": "^1.0.0", "dashdash": "^1.12.0", "ecc-jsbn": "~0.1.1", "getpass": "^0.1.1", "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" }, "bin": { "sshpk-conv": "bin/sshpk-conv", "sshpk-sign": "bin/sshpk-sign", "sshpk-verify": "bin/sshpk-verify" } }, "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ=="],
|
||||
|
||||
"stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
|
||||
|
||||
"string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="],
|
||||
@@ -1287,9 +1027,7 @@
|
||||
|
||||
"string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
|
||||
"strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
||||
|
||||
@@ -1305,9 +1043,7 @@
|
||||
|
||||
"stylelint-scss": ["stylelint-scss@7.0.0", "", { "dependencies": { "css-tree": "^3.0.1", "is-plain-object": "^5.0.0", "known-css-properties": "^0.37.0", "mdn-data": "^2.25.0", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.6", "postcss-selector-parser": "^7.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "stylelint": "^16.8.2 || ^17.0.0" } }, "sha512-H88kCC+6Vtzj76NsC8rv6x/LW8slBzIbyeSjsKVlS+4qaEJoDrcJR4L+8JdrR2ORdTscrBzYWiiT2jq6leYR1Q=="],
|
||||
|
||||
"supercluster": ["supercluster@8.0.1", "", { "dependencies": { "kdbush": "^4.0.2" } }, "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ=="],
|
||||
|
||||
"supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
|
||||
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
|
||||
"supports-hyperlinks": ["supports-hyperlinks@4.4.0", "", { "dependencies": { "has-flag": "^5.0.1", "supports-color": "^10.2.2" } }, "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg=="],
|
||||
|
||||
@@ -1315,16 +1051,12 @@
|
||||
|
||||
"svg-tags": ["svg-tags@1.0.0", "", {}, "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA=="],
|
||||
|
||||
"systeminformation": ["systeminformation@5.30.7", "", { "os": "!aix", "bin": { "systeminformation": "lib/cli.js" } }, "sha512-33B/cftpaWdpvH+Ho9U1b08ss8GQuLxrWHelbJT1yw4M48Taj8W3ezcPuaLoIHZz5V6tVHuQPr5BprEfnBLBMw=="],
|
||||
|
||||
"table": ["table@6.9.0", "", { "dependencies": { "ajv": "^8.0.1", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", "string-width": "^4.2.3", "strip-ansi": "^6.0.1" } }, "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A=="],
|
||||
|
||||
"text-extensions": ["text-extensions@2.4.0", "", {}, "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g=="],
|
||||
|
||||
"text-segmentation": ["text-segmentation@1.0.3", "", { "dependencies": { "utrie": "^1.0.2" } }, "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw=="],
|
||||
|
||||
"throttleit": ["throttleit@1.0.1", "", {}, "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ=="],
|
||||
|
||||
"through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="],
|
||||
|
||||
"tinycolor2": ["tinycolor2@1.4.2", "", {}, "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA=="],
|
||||
@@ -1333,30 +1065,12 @@
|
||||
|
||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
|
||||
"tinyqueue": ["tinyqueue@3.0.0", "", {}, "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g=="],
|
||||
|
||||
"tldts": ["tldts@6.1.86", "", { "dependencies": { "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ=="],
|
||||
|
||||
"tldts-core": ["tldts-core@6.1.86", "", {}, "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="],
|
||||
|
||||
"tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="],
|
||||
|
||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||
|
||||
"tough-cookie": ["tough-cookie@5.1.2", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A=="],
|
||||
|
||||
"tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
|
||||
|
||||
"tweetnacl": ["tweetnacl@0.14.5", "", {}, "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="],
|
||||
|
||||
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
||||
|
||||
"type-fest": ["type-fest@0.8.1", "", {}, "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="],
|
||||
|
||||
"typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
|
||||
|
||||
"typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="],
|
||||
@@ -1373,10 +1087,6 @@
|
||||
|
||||
"unicorn-magic": ["unicorn-magic@0.4.0", "", {}, "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw=="],
|
||||
|
||||
"universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
|
||||
|
||||
"untildify": ["untildify@4.0.0", "", {}, "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw=="],
|
||||
|
||||
"update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
@@ -1387,10 +1097,6 @@
|
||||
|
||||
"utrie": ["utrie@1.0.2", "", { "dependencies": { "base64-arraybuffer": "^1.0.2" } }, "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw=="],
|
||||
|
||||
"uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
|
||||
|
||||
"verror": ["verror@1.10.0", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw=="],
|
||||
|
||||
"vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
|
||||
|
||||
"vite-plugin-progress": ["vite-plugin-progress@0.0.7", "", { "dependencies": { "picocolors": "^1.0.0", "progress": "^2.0.3", "rd": "^2.0.1" }, "peerDependencies": { "vite": ">2.0.0-0" } }, "sha512-zyvKdcc/X+6hnw3J1HVV1TKrlFKC4Rh8GnDnWG/2qhRXjqytTcM++xZ+SAPnoDsSyWl8O93ymK0wZRgHAoglEQ=="],
|
||||
@@ -1411,8 +1117,6 @@
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
|
||||
"write-file-atomic": ["write-file-atomic@7.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg=="],
|
||||
|
||||
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||
@@ -1423,8 +1127,6 @@
|
||||
|
||||
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
||||
|
||||
"yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="],
|
||||
|
||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
|
||||
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
|
||||
@@ -1447,67 +1149,43 @@
|
||||
|
||||
"@commitlint/types/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||
|
||||
"@cypress/xvfb/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
||||
|
||||
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||
|
||||
"@keyv/bigmap/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="],
|
||||
|
||||
"@maplibre/vt-pbf/@maplibre/geojson-vt": ["@maplibre/geojson-vt@5.0.4", "", {}, "sha512-KGg9sma45S+stfH9vPCJk1J0lSDLWZgCT9Y8u8qWZJyjFlP8MNP1WGTxIMYJZjDvVT3PDn05kN1C95Sut1HpgQ=="],
|
||||
|
||||
"@types/conventional-commits-parser/@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
|
||||
|
||||
"@types/yauzl/@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
|
||||
|
||||
"ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
|
||||
|
||||
"brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"cacheable/keyv": ["keyv@5.6.0", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw=="],
|
||||
|
||||
"chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
|
||||
"cli-table3/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"cli-truncate/slice-ansi": ["slice-ansi@3.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ=="],
|
||||
|
||||
"cli-truncate/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"conventional-commits-parser/meow": ["meow@12.1.1", "", {}, "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw=="],
|
||||
|
||||
"css-tree/mdn-data": ["mdn-data@2.12.2", "", {}, "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA=="],
|
||||
|
||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"figures/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
|
||||
|
||||
"git-raw-commits/meow": ["meow@12.1.1", "", {}, "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw=="],
|
||||
|
||||
"global-directory/ini": ["ini@4.1.1", "", {}, "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g=="],
|
||||
|
||||
"global-prefix/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
|
||||
|
||||
"global-prefix/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "./bin/which" } }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="],
|
||||
|
||||
"globby/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
||||
|
||||
"globby/is-path-inside": ["is-path-inside@4.0.0", "", {}, "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA=="],
|
||||
|
||||
"import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||
|
||||
"log-update/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"string-width/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||
|
||||
"stylelint/file-entry-cache": ["file-entry-cache@11.1.2", "", { "dependencies": { "flat-cache": "^6.1.20" } }, "sha512-N2WFfK12gmrK1c1GXOqiAJ1tc5YE+R53zvQ+t5P8S5XhnmKYVB5eZEiLNZKDSmoG8wqqbF9EXYBBW/nef19log=="],
|
||||
|
||||
"stylelint/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
||||
|
||||
"supports-hyperlinks/has-flag": ["has-flag@5.0.1", "", {}, "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA=="],
|
||||
"supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"supports-hyperlinks/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
|
||||
|
||||
@@ -1515,9 +1193,11 @@
|
||||
|
||||
"table/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"table/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"wrap-ansi/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"write-file-atomic/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
"wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
@@ -1529,15 +1209,9 @@
|
||||
|
||||
"@commitlint/top-level/find-up/unicorn-magic": ["unicorn-magic@0.1.0", "", {}, "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ=="],
|
||||
|
||||
"cli-table3/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"cli-truncate/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"log-update/wrap-ansi/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
"cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"stylelint/file-entry-cache/flat-cache": ["flat-cache@6.1.20", "", { "dependencies": { "cacheable": "^2.3.2", "flatted": "^3.3.3", "hookified": "^1.15.0" } }, "sha512-AhHYqwvN62NVLp4lObVXGVluiABTHapoB57EyegZVmazN+hhGhLTn3uZbOofoTw4DSDvVCadzzyChXhOAvy8uQ=="],
|
||||
|
||||
@@ -1545,13 +1219,19 @@
|
||||
|
||||
"table/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"table/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"wrap-ansi/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"@commitlint/top-level/find-up/locate-path/p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="],
|
||||
|
||||
"log-update/wrap-ansi/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
"yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"@commitlint/top-level/find-up/locate-path/p-locate/p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="],
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { defineConfig } from "cypress";
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
baseUrl: "http://localhost:5173",
|
||||
viewportWidth: 1920,
|
||||
viewportHeight: 1080,
|
||||
setupNodeEvents(on, config) {
|
||||
// implement node event listeners here
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,131 +0,0 @@
|
||||
describe('Basic Features', () => {
|
||||
beforeEach(() => {
|
||||
// Bypass onboarding and enable features
|
||||
cy.visit('/', {
|
||||
onBeforeLoad: (win) => {
|
||||
win.localStorage.clear();
|
||||
win.localStorage.setItem('firstRun', 'true');
|
||||
win.localStorage.setItem('stats', 'true');
|
||||
win.localStorage.setItem('navbarHover', 'false');
|
||||
win.localStorage.setItem('notesEnabled', 'true');
|
||||
win.localStorage.setItem('searchBar', 'true');
|
||||
win.localStorage.setItem('voiceSearch', 'false');
|
||||
win.localStorage.setItem('language', 'en_GB');
|
||||
win.localStorage.setItem('theme', 'dark');
|
||||
win.localStorage.setItem('widgetStyle', 'new');
|
||||
win.localStorage.setItem('showWelcome', 'false');
|
||||
win.localStorage.setItem('view', 'true'); // Enable Maximise button
|
||||
win.localStorage.setItem('background', 'true'); // Enable Background for Maximise button
|
||||
win.localStorage.setItem('photoInformation', 'true'); // Enable Photo Information
|
||||
win.localStorage.setItem('backgroundType', 'api'); // Required for backgroundLoader
|
||||
|
||||
win.localStorage.setItem('quoteType', 'custom');
|
||||
win.localStorage.setItem('customQuote', 'Test Quote');
|
||||
win.localStorage.setItem('customQuoteAuthor', 'Test Author');
|
||||
|
||||
// Seed other widgets
|
||||
win.localStorage.setItem('greeting', 'true');
|
||||
win.localStorage.setItem('time', 'true');
|
||||
win.localStorage.setItem('date', 'true');
|
||||
|
||||
// Seed Quick Links with data so it renders with height
|
||||
win.localStorage.setItem('quicklinks', JSON.stringify([
|
||||
{ name: "Test Link", url: "https://example.com", key: "1" }
|
||||
]));
|
||||
win.localStorage.setItem('quicklinksenabled', 'true');
|
||||
|
||||
win.localStorage.setItem('message', 'true');
|
||||
win.localStorage.setItem('messages', '["Test Message"]');
|
||||
|
||||
win.localStorage.setItem('order', JSON.stringify(["greeting", "time", "quicklinks", "quote", "date", "message"]));
|
||||
}
|
||||
});
|
||||
|
||||
// Mock Background API
|
||||
cy.intercept('GET', '**/images/random*', { fixture: 'background.json' }).as('getBackground');
|
||||
});
|
||||
|
||||
it('should perform search', () => {
|
||||
const searchText = 'Cypress Test Search';
|
||||
cy.get('#searchtext').should('exist');
|
||||
cy.get('#searchtext').type(searchText);
|
||||
cy.get('#searchtext').should('have.value', searchText);
|
||||
});
|
||||
|
||||
it('should use Notes feature', () => {
|
||||
cy.get('.notes .navbarButton').click();
|
||||
const noteText = 'This is a test note';
|
||||
cy.get('.notesContainer textarea').should('be.visible');
|
||||
cy.get('.notesContainer textarea').clear().type(noteText);
|
||||
cy.reload();
|
||||
cy.get('.notes .navbarButton').click();
|
||||
cy.get('.notesContainer textarea').should('have.value', noteText);
|
||||
});
|
||||
|
||||
it('should maximise and unmaximise widgets', () => {
|
||||
cy.get('#widgets').should('not.have.css', 'display', 'none');
|
||||
cy.get('button[aria-label="Maximise"]').click();
|
||||
cy.get('#widgets').should('have.css', 'display', 'none');
|
||||
cy.get('button[aria-label="Maximise"]').click();
|
||||
cy.get('#widgets').should('not.have.css', 'display', 'none');
|
||||
});
|
||||
|
||||
it('should display photo information and background actions', () => {
|
||||
cy.wait('@getBackground');
|
||||
cy.get('.photoInformation', { timeout: 10000 }).should('exist').and('be.visible');
|
||||
cy.get('.photoInformation').trigger('mouseover');
|
||||
|
||||
// Check primary content
|
||||
cy.get('.photoInformation .primary-content').should('exist');
|
||||
|
||||
// Check Action Buttons
|
||||
cy.get('.photoInformation .buttons').should('exist');
|
||||
cy.get('.photoInformation .buttons svg').should('have.length.at.least', 3);
|
||||
|
||||
// Test Favourite Interaction
|
||||
cy.get('.photoInformation .buttons > .tooltip').eq(1).click({ force: true });
|
||||
});
|
||||
|
||||
it('should display quote link', () => {
|
||||
// Reload with offlineMode to ensure we get a guaranteed offline quote
|
||||
cy.visit('/', {
|
||||
onBeforeLoad: (win) => {
|
||||
win.localStorage.clear();
|
||||
win.localStorage.setItem('firstRun', 'true');
|
||||
win.localStorage.setItem('showWelcome', 'false');
|
||||
win.localStorage.setItem('widgetStyle', 'new');
|
||||
win.localStorage.setItem('offlineMode', 'true');
|
||||
win.localStorage.setItem('order', JSON.stringify(["quote"]));
|
||||
}
|
||||
});
|
||||
|
||||
cy.get('.quotediv').should('exist');
|
||||
cy.get('.quotediv .quote').should('not.be.empty');
|
||||
cy.get('.quotediv a').should('exist').and('have.attr', 'href').and('include', 'wikipedia');
|
||||
});
|
||||
|
||||
it('should have a refresh button', () => {
|
||||
cy.get('button[aria-label="Refresh"]').should('exist').and('be.visible');
|
||||
});
|
||||
|
||||
it('should display other dashboard widgets', () => {
|
||||
// Verify Greeting
|
||||
cy.get('.greeting').should('exist').and('be.visible');
|
||||
|
||||
// Verify Time (Clock)
|
||||
// Checks for either generic clock class or clock container
|
||||
cy.get('.clock-container').should('exist').and('be.visible');
|
||||
|
||||
// Verify Date
|
||||
cy.get('.date').should('exist').and('be.visible');
|
||||
|
||||
// Verify Quick Links
|
||||
// Checks for the container
|
||||
cy.get('.quicklinkscontainer').should('exist').and('be.visible');
|
||||
// Ensure it has content (the link we seeded)
|
||||
cy.get('.quicklinkscontainer a').should('have.length.at.least', 1);
|
||||
|
||||
// Verify Message
|
||||
cy.get('.message').should('exist').and('be.visible').and('contain', 'Test Message');
|
||||
});
|
||||
});
|
||||
@@ -1,48 +0,0 @@
|
||||
describe('Welcome Modal Flow', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('should complete the full onboarding flow', () => {
|
||||
// Step 1: Intro
|
||||
cy.contains('Welcome to Mue Tab', { timeout: 10000 }).should('be.visible');
|
||||
cy.contains('Next').click();
|
||||
|
||||
// Step 2: Language
|
||||
cy.contains('Choose your language').should('be.visible');
|
||||
cy.contains('Next').click();
|
||||
|
||||
// Step 3: Import Settings
|
||||
cy.get('.upload').should('be.visible');
|
||||
cy.contains('Next').click();
|
||||
|
||||
// Step 4: Theme
|
||||
cy.contains('Select a theme').should('be.visible');
|
||||
cy.contains('Dark').click();
|
||||
cy.get('body').should('have.class', 'dark');
|
||||
|
||||
cy.contains('Light').click();
|
||||
cy.get('body').should('not.have.class', 'dark');
|
||||
|
||||
cy.contains('Auto').click();
|
||||
cy.contains('Dark').click();
|
||||
cy.contains('Next').click();
|
||||
|
||||
// Step 5: Style
|
||||
cy.contains('Choose a style').should('be.visible');
|
||||
cy.contains('Modern').click();
|
||||
cy.contains('Next').click();
|
||||
|
||||
// Step 6: Privacy
|
||||
cy.contains('Privacy Options').should('be.visible');
|
||||
cy.contains('Next').click();
|
||||
|
||||
// Step 7: Final
|
||||
cy.contains('Final step').should('be.visible');
|
||||
cy.contains('Finish').click();
|
||||
|
||||
// Verify Dashboard
|
||||
cy.get('.greeting').should('be.visible');
|
||||
});
|
||||
});
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"file": "https://images.unsplash.com/photo-1472214103451-9374bd1c798e",
|
||||
"location": {
|
||||
"name": "Loch Lomond, United Kingdom",
|
||||
"latitude": 56.2466465,
|
||||
"longitude": -4.7208886
|
||||
},
|
||||
"photographer": "Adam Jang",
|
||||
"photographer_page": "https://unsplash.com/@adamjang",
|
||||
"camera": "Canon, EOS 70D",
|
||||
"views": 1500000,
|
||||
"downloads": 10000,
|
||||
"likes": 500,
|
||||
"category": "nature",
|
||||
"colour": "#262626",
|
||||
"blur_hash": "L25#t70000?b00?b?bof00D%?bof",
|
||||
"pun": "test_unique_id"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
[
|
||||
{
|
||||
"quote": "The only way to do great work is to love what you do.",
|
||||
"author": "Steve Jobs",
|
||||
"category": "inspirational",
|
||||
"language": "en",
|
||||
"authorlink": "https://en.wikipedia.org/wiki/Steve_Jobs"
|
||||
}
|
||||
]
|
||||
@@ -1,24 +0,0 @@
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
@@ -1,19 +0,0 @@
|
||||
// This example e2e.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
@@ -5,6 +5,7 @@ import jsxA11y from 'eslint-plugin-jsx-a11y';
|
||||
import prettier from 'eslint-config-prettier';
|
||||
|
||||
export default [
|
||||
// Ignore patterns
|
||||
{
|
||||
ignores: [
|
||||
'**/node_modules/**',
|
||||
@@ -17,6 +18,7 @@ export default [
|
||||
],
|
||||
},
|
||||
|
||||
// Base config for all JS/JSX files
|
||||
{
|
||||
files: ['**/*.{js,jsx,mjs}'],
|
||||
languageOptions: {
|
||||
@@ -24,6 +26,7 @@ export default [
|
||||
sourceType: 'module',
|
||||
parserOptions: { ecmaFeatures: { jsx: true } },
|
||||
globals: {
|
||||
// Browser globals
|
||||
window: 'readonly',
|
||||
document: 'readonly',
|
||||
navigator: 'readonly',
|
||||
@@ -45,6 +48,7 @@ export default [
|
||||
AbortController: 'readonly',
|
||||
btoa: 'readonly',
|
||||
atob: 'readonly',
|
||||
// Node globals for scripts
|
||||
process: 'readonly',
|
||||
__dirname: 'readonly',
|
||||
__filename: 'readonly',
|
||||
@@ -60,17 +64,21 @@ export default [
|
||||
...react.configs['jsx-runtime'].rules,
|
||||
...reactHooks.configs.recommended.rules,
|
||||
|
||||
'react/prop-types': 'off',
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'react/jsx-uses-react': 'off',
|
||||
// React specific rules
|
||||
'react/prop-types': 'off', // Using PropTypes is optional
|
||||
'react/react-in-jsx-scope': 'off', // Not needed with React 17+
|
||||
'react/jsx-uses-react': 'off', // Not needed with React 17+
|
||||
|
||||
// General rules
|
||||
'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
||||
'no-console': ['warn', { allow: ['warn', 'error'] }],
|
||||
|
||||
// Modern JS
|
||||
'prefer-const': 'warn',
|
||||
'no-var': 'error',
|
||||
},
|
||||
},
|
||||
|
||||
// Prettier config (must be last to override other formatting rules)
|
||||
prettier,
|
||||
];
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
"@/*": ["./*"],
|
||||
"i18n/*": ["./i18n/*"],
|
||||
"components/*": ["./components/*"],
|
||||
"hooks/*": ["./hooks/*"],
|
||||
"assets/*": ["./assets/*"],
|
||||
"config/*": ["./config/*"],
|
||||
"features/*": ["./features/*"],
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"name": {
|
||||
"message": "Mue"
|
||||
},
|
||||
"description": {
|
||||
"message": "Fast, open and free-to-use new tab page for modern browsers."
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
"default_locale": "en",
|
||||
"name": "__MSG_name__",
|
||||
"description": "__MSG_description__",
|
||||
"version": "7.6.1",
|
||||
"version": "7.6.0",
|
||||
"homepage_url": "https://muetab.com",
|
||||
"permissions": ["search", "bookmarks"],
|
||||
"permissions": ["search"],
|
||||
"action": {
|
||||
"default_icon": "icons/128x128.png"
|
||||
},
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
"manifest_version": 3,
|
||||
"name": "Mue",
|
||||
"description": "Fast, open and free-to-use new tab page for modern browsers.",
|
||||
"version": "7.6.1",
|
||||
"version": "7.6.0",
|
||||
"homepage_url": "https://muetab.com",
|
||||
"permissions": ["bookmarks"],
|
||||
"action": {
|
||||
"default_icon": "icons/128x128.png"
|
||||
},
|
||||
|
||||
10
package.json
10
package.json
@@ -9,7 +9,7 @@
|
||||
"homepage": "https://muetab.com",
|
||||
"bugs": "https://github.com/mue/mue/issues/new?assignees=&labels=bug&template=bug-report.md&title=%5BBUG%5D",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "7.6.1",
|
||||
"version": "7.6.0",
|
||||
"type": "module",
|
||||
"packageManager": "bun@1.3.1",
|
||||
"engines": {
|
||||
@@ -29,14 +29,12 @@
|
||||
"blurhash": "^2.0.5",
|
||||
"fast-blurhash": "^1.1.4",
|
||||
"image-conversion": "^2.1.1",
|
||||
"maplibre-gl": "^5.24.0",
|
||||
"react": "^19.2.3",
|
||||
"react-best-gradient-color-picker": "^3.0.14",
|
||||
"react-clock": "6.0.0",
|
||||
"react-dom": "^19.2.3",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-modal": "3.16.3",
|
||||
"react-router": "^7.13.0",
|
||||
"react-toastify": "11.0.5",
|
||||
"use-debounce": "^10.1.0"
|
||||
},
|
||||
@@ -47,7 +45,6 @@
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@vitejs/plugin-react-swc": "^4.2.2",
|
||||
"adm-zip": "0.5.16",
|
||||
"cypress": "^15.10.0",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
@@ -65,11 +62,8 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev:host": "vite --host",
|
||||
"test:e2e": "cypress open",
|
||||
"test:e2e:headless": "cypress run",
|
||||
"translations": "cd scripts && node updatetranslations.cjs",
|
||||
"translations:percentages": "node scripts/updateTranslationPercentages.cjs",
|
||||
"translations:unused": "node scripts/findUnusedTranslations.cjs",
|
||||
"build": "vite build",
|
||||
"pretty": "prettier --write \"./**/*.{js,jsx,json,scss,css}\"",
|
||||
"lint": "eslint \"./src/**/*.{js,jsx}\" && stylelint \"./src/**/*.{scss,css}\"",
|
||||
@@ -77,4 +71,4 @@
|
||||
"postinstall": "husky",
|
||||
"prepare": "husky"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"default_locale": "en",
|
||||
"name": "__MSG_name__",
|
||||
"description": "__MSG_description__",
|
||||
"version": "7.6.1",
|
||||
"version": "7.6.0",
|
||||
"homepage_url": "https://muetab.com",
|
||||
"permissions": ["search"],
|
||||
"chrome_url_overrides": {
|
||||
|
||||
@@ -27,12 +27,7 @@ class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling {
|
||||
message = request?.userInfo?["message"]
|
||||
}
|
||||
|
||||
os_log(
|
||||
.default,
|
||||
"Received message from browser.runtime.sendNativeMessage: %@ (profile: %@)",
|
||||
String(describing: message),
|
||||
profile?.uuidString ?? "none"
|
||||
)
|
||||
os_log(.default, "Received message from browser.runtime.sendNativeMessage: %@ (profile: %@)", String(describing: message), profile?.uuidString ?? "none")
|
||||
|
||||
let response = NSExtensionItem()
|
||||
if #available(iOS 15.0, macOS 11.0, *) {
|
||||
|
||||
@@ -255,7 +255,7 @@
|
||||
"@executable_path/../../../../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MARKETING_VERSION = 7.6.1;
|
||||
MARKETING_VERSION = 7.6.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
SafariServices,
|
||||
@@ -289,7 +289,7 @@
|
||||
"@executable_path/../../../../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MARKETING_VERSION = 7.6.1;
|
||||
MARKETING_VERSION = 7.6.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
SafariServices,
|
||||
@@ -445,7 +445,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 7.6.1;
|
||||
MARKETING_VERSION = 7.6.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
SafariServices,
|
||||
@@ -486,7 +486,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 7.6.1;
|
||||
MARKETING_VERSION = 7.6.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"-framework",
|
||||
SafariServices,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"colors": [
|
||||
"colors" : [
|
||||
{
|
||||
"idiom": "universal"
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,68 @@
|
||||
{
|
||||
"images": [
|
||||
"images" : [
|
||||
{
|
||||
"filename": "icon_16x16.png",
|
||||
"idiom": "mac",
|
||||
"scale": "1x",
|
||||
"size": "16x16"
|
||||
"filename" : "icon_16x16.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename": "icon_16x16@2x.png",
|
||||
"idiom": "mac",
|
||||
"scale": "2x",
|
||||
"size": "16x16"
|
||||
"filename" : "icon_16x16@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename": "icon_32x32.png",
|
||||
"idiom": "mac",
|
||||
"scale": "1x",
|
||||
"size": "32x32"
|
||||
"filename" : "icon_32x32.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename": "icon_32x32@2x.png",
|
||||
"idiom": "mac",
|
||||
"scale": "2x",
|
||||
"size": "32x32"
|
||||
"filename" : "icon_32x32@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename": "icon_128x128.png",
|
||||
"idiom": "mac",
|
||||
"scale": "1x",
|
||||
"size": "128x128"
|
||||
"filename" : "icon_128x128.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename": "icon_128x128@2x.png",
|
||||
"idiom": "mac",
|
||||
"scale": "2x",
|
||||
"size": "128x128"
|
||||
"filename" : "icon_128x128@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename": "icon_256x256.png",
|
||||
"idiom": "mac",
|
||||
"scale": "1x",
|
||||
"size": "256x256"
|
||||
"filename" : "icon_256x256.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename": "icon_256x256@2x.png",
|
||||
"idiom": "mac",
|
||||
"scale": "2x",
|
||||
"size": "256x256"
|
||||
"filename" : "icon_256x256@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename": "icon_512x512.png",
|
||||
"idiom": "mac",
|
||||
"scale": "1x",
|
||||
"size": "512x512"
|
||||
"filename" : "icon_512x512.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename": "icon_512x512@2x.png",
|
||||
"idiom": "mac",
|
||||
"scale": "2x",
|
||||
"size": "512x512"
|
||||
"filename" : "icon_512x512@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"images": [
|
||||
"images" : [
|
||||
{
|
||||
"idiom": "universal",
|
||||
"scale": "1x"
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom": "universal",
|
||||
"scale": "2x"
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom": "universal",
|
||||
"scale": "3x"
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
function show(enabled, useSettingsInsteadOfPreferences) {
|
||||
if (useSettingsInsteadOfPreferences) {
|
||||
document.getElementsByClassName('state-on')[0].innerText =
|
||||
'Extension is enabled and ready to use!';
|
||||
document.getElementsByClassName('state-off')[0].innerText =
|
||||
'Extension is disabled. Enable it in Safari Settings.';
|
||||
document.getElementsByClassName('state-unknown')[0].innerText =
|
||||
'Enable Mue in Safari Settings to get started.';
|
||||
document.getElementsByClassName('open-preferences')[0].innerText = 'Open Safari Settings';
|
||||
}
|
||||
if (useSettingsInsteadOfPreferences) {
|
||||
document.getElementsByClassName('state-on')[0].innerText = "Extension is enabled and ready to use!";
|
||||
document.getElementsByClassName('state-off')[0].innerText = "Extension is disabled. Enable it in Safari Settings.";
|
||||
document.getElementsByClassName('state-unknown')[0].innerText = "Enable Mue in Safari Settings to get started.";
|
||||
document.getElementsByClassName('open-preferences')[0].innerText = "Open Safari Settings";
|
||||
}
|
||||
|
||||
if (typeof enabled === 'boolean') {
|
||||
document.body.classList.toggle(`state-on`, enabled);
|
||||
document.body.classList.toggle(`state-off`, !enabled);
|
||||
} else {
|
||||
document.body.classList.remove(`state-on`);
|
||||
document.body.classList.remove(`state-off`);
|
||||
}
|
||||
if (typeof enabled === "boolean") {
|
||||
document.body.classList.toggle(`state-on`, enabled);
|
||||
document.body.classList.toggle(`state-off`, !enabled);
|
||||
} else {
|
||||
document.body.classList.remove(`state-on`);
|
||||
document.body.classList.remove(`state-off`);
|
||||
}
|
||||
}
|
||||
|
||||
function openPreferences() {
|
||||
webkit.messageHandlers.controller.postMessage('open-preferences');
|
||||
webkit.messageHandlers.controller.postMessage("open-preferences");
|
||||
}
|
||||
|
||||
document.querySelector('button.open-preferences').addEventListener('click', openPreferences);
|
||||
document.querySelector("button.open-preferences").addEventListener("click", openPreferences);
|
||||
|
||||
@@ -1,133 +1,124 @@
|
||||
* {
|
||||
-webkit-user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
cursor: default;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
-webkit-user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
cursor: default;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--spacing: 20px;
|
||||
--text-color: #fff;
|
||||
--shadow: 0 4px 20px rgb(0 0 0 / 30%);
|
||||
--background-color: #0a0a0a;
|
||||
--spacing: 20px;
|
||||
--text-color: #ffffff;
|
||||
--shadow: 0 4px 20px rgb(0 0 0 / 30%);
|
||||
--background-color: #0A0A0A;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
font-family:
|
||||
'Lexend Deca',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
sans-serif;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
font-family: 'Lexend Deca', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
}
|
||||
|
||||
.gradient-bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--background-color);
|
||||
z-index: -1;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--background-color);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--spacing) * 0.5);
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
color: var(--text-color);
|
||||
padding: var(--spacing);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--spacing) * 0.5);
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
color: var(--text-color);
|
||||
padding: var(--spacing);
|
||||
}
|
||||
|
||||
img {
|
||||
filter: drop-shadow(var(--shadow));
|
||||
margin-bottom: 5px;
|
||||
filter: drop-shadow(var(--shadow));
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 3em;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||
letter-spacing: -0.5px;
|
||||
font-size: 3em;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.2em;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
opacity: 0.95;
|
||||
text-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
|
||||
font-size: 1.2em;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
opacity: 0.95;
|
||||
text-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.status-container {
|
||||
margin: 10px 0;
|
||||
min-height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 10px 0;
|
||||
min-height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.status-container p {
|
||||
font-size: 1.1em;
|
||||
margin: 0;
|
||||
padding: 12px 24px;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--shadow);
|
||||
font-size: 1.1em;
|
||||
margin: 0;
|
||||
padding: 12px 24px;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
body:not(.state-on, .state-off) :is(.state-on, .state-off) {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.state-on :is(.state-off, .state-unknown) {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.state-off :is(.state-on, .state-unknown) {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
padding: 12px 28px;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: var(--shadow);
|
||||
margin-top: 5px;
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
padding: 12px 28px;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: var(--shadow);
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #f0f0f0;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 25px rgb(0 0 0 / 40%);
|
||||
background: #f0f0f0;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 25px rgb(0 0 0 / 40%);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: var(--shadow);
|
||||
transform: translateY(0);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
@@ -22,15 +22,11 @@ class ViewController: NSViewController, WKNavigationDelegate, WKScriptMessageHan
|
||||
|
||||
self.webView.configuration.userContentController.add(self, name: "controller")
|
||||
|
||||
let mainUrl = Bundle.main.url(forResource: "Main", withExtension: "html")!
|
||||
let resourceUrl = Bundle.main.resourceURL!
|
||||
self.webView.loadFileURL(mainUrl, allowingReadAccessTo: resourceUrl)
|
||||
self.webView.loadFileURL(Bundle.main.url(forResource: "Main", withExtension: "html")!, allowingReadAccessTo: Bundle.main.resourceURL!)
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
SFSafariExtensionManager.getStateOfSafariExtension(
|
||||
withIdentifier: extensionBundleIdentifier
|
||||
) { (state, error) in
|
||||
SFSafariExtensionManager.getStateOfSafariExtension(withIdentifier: extensionBundleIdentifier) { (state, error) in
|
||||
guard let state = state, error == nil else {
|
||||
// Insert code to inform the user that something went wrong.
|
||||
return
|
||||
@@ -47,11 +43,11 @@ class ViewController: NSViewController, WKNavigationDelegate, WKScriptMessageHan
|
||||
}
|
||||
|
||||
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||
guard let messageBody = message.body as? String, messageBody == "open-preferences" else {
|
||||
return
|
||||
if (message.body as! String != "open-preferences") {
|
||||
return;
|
||||
}
|
||||
|
||||
SFSafariApplication.showPreferencesForExtension(withIdentifier: extensionBundleIdentifier) { _ in
|
||||
SFSafariApplication.showPreferencesForExtension(withIdentifier: extensionBundleIdentifier) { error in
|
||||
DispatchQueue.main.async {
|
||||
NSApplication.shared.terminate(nil)
|
||||
}
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const LOCALE_FILE = path.join(__dirname, '../src/i18n/locales/en_GB.json');
|
||||
const ACHIEVEMENTS_FILE = path.join(__dirname, '../src/i18n/locales/achievements/en_GB.json');
|
||||
const SEARCH_DIR = path.join(__dirname, '../src');
|
||||
const FILE_EXTENSIONS = ['.js', '.jsx', '.ts', '.tsx', '.json'];
|
||||
|
||||
/**
|
||||
* Flatten nested JSON object into dot-notation keys
|
||||
* @param {Object} obj - The object to flatten
|
||||
* @param {String} prefix - The prefix for nested keys
|
||||
* @returns {Array} Array of flattened keys
|
||||
*/
|
||||
function flattenKeys(obj, prefix = '') {
|
||||
const keys = [];
|
||||
|
||||
for (const key in obj) {
|
||||
const fullKey = prefix ? `${prefix}.${key}` : key;
|
||||
|
||||
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
|
||||
keys.push(...flattenKeys(obj[key], fullKey));
|
||||
} else {
|
||||
keys.push(fullKey);
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
|
||||
function getAllFiles(dir, fileList = []) {
|
||||
const files = fs.readdirSync(dir);
|
||||
|
||||
files.forEach(file => {
|
||||
const filePath = path.join(dir, file);
|
||||
const stat = fs.statSync(filePath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
if (!file.startsWith('.') && file !== 'node_modules' && file !== 'dist' && file !== 'build') {
|
||||
getAllFiles(filePath, fileList);
|
||||
}
|
||||
} else {
|
||||
const ext = path.extname(file);
|
||||
if (FILE_EXTENSIONS.includes(ext)) {
|
||||
fileList.push(filePath);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return fileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search files for usage of a translation key
|
||||
* Handles both direct usage and dynamic template literal construction
|
||||
* @param {String} key - The translation key to search for
|
||||
* @param {Array} files - Array of file paths to search
|
||||
* @param {Map} fileContentsCache - Cache of file contents
|
||||
* @returns {Boolean} True if the key is found in any file
|
||||
*/
|
||||
function isKeyUsed(key, files, fileContentsCache) {
|
||||
const keySegments = key.split('.');
|
||||
const patterns = [];
|
||||
|
||||
const escapedFullKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
patterns.push(new RegExp(`['"\`]${escapedFullKey}['"\`]`, 'g'));
|
||||
|
||||
if (keySegments.length >= 2) {
|
||||
const lastTwo = keySegments.slice(-2).map(s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('\\.');
|
||||
patterns.push(new RegExp(`['"\`]${lastTwo}['"\`]`, 'g'));
|
||||
}
|
||||
|
||||
if (keySegments.length >= 3) {
|
||||
const lastThree = keySegments.slice(-3).map(s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('\\.');
|
||||
patterns.push(new RegExp(`['"\`]${lastThree}['"\`]`, 'g'));
|
||||
}
|
||||
|
||||
if (keySegments.length >= 3) {
|
||||
const finalSegment = keySegments[keySegments.length - 1].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
patterns.push(new RegExp(`\\.${finalSegment}['"\`]`, 'g'));
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
let content = fileContentsCache.get(file);
|
||||
if (content === undefined) {
|
||||
try {
|
||||
content = fs.readFileSync(file, 'utf-8');
|
||||
fileContentsCache.set(file, content);
|
||||
} catch {
|
||||
fileContentsCache.set(file, '');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (const pattern of patterns) {
|
||||
if (pattern.test(content)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function main() {
|
||||
console.log('Finding unused translation keys...\n');
|
||||
|
||||
let translationKeys = [];
|
||||
|
||||
try {
|
||||
const localeContent = fs.readFileSync(LOCALE_FILE, 'utf-8');
|
||||
const localeData = JSON.parse(localeContent);
|
||||
translationKeys = flattenKeys(localeData);
|
||||
console.log(`Found ${translationKeys.length} translation keys in en_GB.json`);
|
||||
} catch (error) {
|
||||
console.error(`Error reading locale file: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
if (fs.existsSync(ACHIEVEMENTS_FILE)) {
|
||||
const achievementsContent = fs.readFileSync(ACHIEVEMENTS_FILE, 'utf-8');
|
||||
const achievementsData = JSON.parse(achievementsContent);
|
||||
const achievementKeys = flattenKeys(achievementsData, 'achievements');
|
||||
translationKeys.push(...achievementKeys);
|
||||
console.log(`Found ${achievementKeys.length} achievement keys in achievements/en_GB.json`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Warning: Could not read achievements file: ${error.message}`);
|
||||
}
|
||||
|
||||
console.log(`\nTotal keys to check: ${translationKeys.length}`);
|
||||
|
||||
console.log('Scanning source files...');
|
||||
const files = getAllFiles(SEARCH_DIR);
|
||||
console.log(`Found ${files.length} files to search\n`);
|
||||
|
||||
const fileContentsCache = new Map();
|
||||
|
||||
const unusedKeys = [];
|
||||
const usedKeys = [];
|
||||
|
||||
console.log('Searching for key usage (including template literals)...');
|
||||
|
||||
let processed = 0;
|
||||
const totalKeys = translationKeys.length;
|
||||
const startTime = Date.now();
|
||||
|
||||
for (const key of translationKeys) {
|
||||
processed++;
|
||||
|
||||
if (processed % 10 === 0 || processed === totalKeys) {
|
||||
const percent = Math.round(processed / totalKeys * 100);
|
||||
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
||||
process.stdout.write(`\r Progress: ${processed}/${totalKeys} (${percent}%) - ${elapsed}s elapsed`);
|
||||
}
|
||||
|
||||
if (isKeyUsed(key, files, fileContentsCache)) {
|
||||
usedKeys.push(key);
|
||||
} else {
|
||||
unusedKeys.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
const totalTime = ((Date.now() - startTime) / 1000).toFixed(1);
|
||||
console.log(`\r Progress: ${totalKeys}/${totalKeys} (100%) - ${totalTime}s total \n`);
|
||||
|
||||
console.log('\n' + '='.repeat(70));
|
||||
console.log('RESULTS');
|
||||
console.log('='.repeat(70) + '\n');
|
||||
|
||||
console.log(`Used keys: ${usedKeys.length}`);
|
||||
console.log(`Unused keys: ${unusedKeys.length}`);
|
||||
console.log(`Usage rate: ${((usedKeys.length / totalKeys) * 100).toFixed(2)}%\n`);
|
||||
|
||||
if (unusedKeys.length > 0) {
|
||||
console.log('Unused translation keys:\n');
|
||||
|
||||
const grouped = {};
|
||||
unusedKeys.forEach(key => {
|
||||
const topLevel = key.split('.')[0];
|
||||
if (!grouped[topLevel]) {
|
||||
grouped[topLevel] = [];
|
||||
}
|
||||
grouped[topLevel].push(key);
|
||||
});
|
||||
|
||||
Object.keys(grouped).sort().forEach(category => {
|
||||
console.log(`\n ${category}:`);
|
||||
grouped[category].sort().forEach(key => {
|
||||
console.log(` - ${key}`);
|
||||
});
|
||||
});
|
||||
|
||||
const outputFile = path.join(__dirname, '../unused-translations.txt');
|
||||
const outputContent = [
|
||||
'# Unused Translation Keys',
|
||||
`# Generated: ${new Date().toISOString()}`,
|
||||
`# Total unused: ${unusedKeys.length}`,
|
||||
`# Note: This script checks for full keys and partial keys (last 2-3 segments)`,
|
||||
`# to catch dynamic template literal usage like \`\${PREFIX}.key\``,
|
||||
'',
|
||||
...unusedKeys.sort()
|
||||
].join('\n');
|
||||
|
||||
fs.writeFileSync(outputFile, outputContent, 'utf-8');
|
||||
console.log(`\nFull list saved to: ${path.relative(process.cwd(), outputFile)}`);
|
||||
} else {
|
||||
console.log('No unused translation keys found!');
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(70) + '\n');
|
||||
|
||||
if (unusedKeys.length > 0) {
|
||||
console.log('Tip: You can safely remove these keys from your translation files to reduce bundle size.');
|
||||
console.log('Note: Some keys might be used dynamically - review before removing!');
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -2,6 +2,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
|
||||
// Language code mappings between Weblate and Mue
|
||||
const CODE_MAPPINGS = {
|
||||
de: 'de_DE',
|
||||
id: 'id_ID',
|
||||
@@ -68,7 +69,7 @@ async function updateTranslationPercentages() {
|
||||
fs.writeFileSync(outputPath, JSON.stringify(percentages, null, 2));
|
||||
fs.appendFileSync(outputPath, '\n');
|
||||
|
||||
console.log(`Translation percentages updated successfully!`);
|
||||
console.log(`✓ Translation percentages updated successfully!`);
|
||||
console.log(` Total languages: ${Object.keys(percentages).length}`);
|
||||
console.log(` Output: ${outputPath}`);
|
||||
|
||||
|
||||
@@ -16,12 +16,13 @@ const compareAndRemoveKeys = (json1, json2) => {
|
||||
|
||||
const localesDir = path.join(__dirname, '../src/i18n/locales');
|
||||
const achievementsDir = path.join(localesDir, 'achievements');
|
||||
const manifestLocalesDir = path.join(__dirname, '../manifest/_locales');
|
||||
|
||||
// Check if the locales directory exists, if not, create it
|
||||
if (!fs.existsSync(localesDir)) {
|
||||
fs.mkdirSync(localesDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Check if the achievements directory exists, if not, create it
|
||||
if (!fs.existsSync(achievementsDir)) {
|
||||
fs.mkdirSync(achievementsDir, { recursive: true });
|
||||
}
|
||||
@@ -72,6 +73,7 @@ fs.readdirSync(achievementsDir).forEach((file) => {
|
||||
const locales = fs.readdirSync(localesDir);
|
||||
locales.forEach((locale) => {
|
||||
if (!fs.existsSync(path.join(achievementsDir, locale))) {
|
||||
// ignore directories
|
||||
if (fs.lstatSync(path.join(localesDir, locale)).isDirectory()) {
|
||||
return;
|
||||
}
|
||||
@@ -81,30 +83,3 @@ fs.readdirSync(achievementsDir).forEach((file) => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log('Syncing manifest/_locales with src/i18n/locales...');
|
||||
const enManifestPath = path.join(manifestLocalesDir, 'en', 'messages.json');
|
||||
|
||||
if (!fs.existsSync(enManifestPath)) {
|
||||
console.error(`English manifest file does not exist at '${enManifestPath}'`);
|
||||
} else {
|
||||
const enManifest = fs.readFileSync(enManifestPath, 'utf8');
|
||||
|
||||
const localeFiles = fs.readdirSync(localesDir).filter((file) => {
|
||||
return !fs.lstatSync(path.join(localesDir, file)).isDirectory() && file.endsWith('.json');
|
||||
});
|
||||
|
||||
localeFiles.forEach((localeFile) => {
|
||||
const localeCode = localeFile.replace('.json', '');
|
||||
const manifestLocalePath = path.join(manifestLocalesDir, localeCode);
|
||||
const manifestMessagesPath = path.join(manifestLocalePath, 'messages.json');
|
||||
|
||||
if (!fs.existsSync(manifestLocalePath)) {
|
||||
console.log(`Creating missing locale: ${localeCode}`);
|
||||
fs.mkdirSync(manifestLocalePath, { recursive: true });
|
||||
fs.writeFileSync(manifestMessagesPath, enManifest);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Manifest locales sync complete!');
|
||||
}
|
||||
|
||||
19
src/App.jsx
19
src/App.jsx
@@ -1,21 +1,15 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Outlet } from 'react-router';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import Background from 'features/background/Background';
|
||||
import Widgets from 'features/misc/views/Widgets';
|
||||
import Modals from 'features/misc/modals/Modals';
|
||||
import CustomWidgets from 'features/misc/CustomWidgets';
|
||||
import { loadSettings, moveSettings } from 'utils/settings';
|
||||
import EventBus from 'utils/eventbus';
|
||||
import variables from 'config/variables';
|
||||
import { TranslationProvider } from 'contexts/TranslationContext';
|
||||
import { registerAllHandlers } from 'utils/marketplace/registerHandlers';
|
||||
import { installDefaultPacks } from 'utils/marketplace/installDefaultPacks';
|
||||
|
||||
const useAppSetup = () => {
|
||||
useEffect(() => {
|
||||
registerAllHandlers();
|
||||
|
||||
const firstRun = localStorage.getItem('firstRun');
|
||||
const stats = localStorage.getItem('stats');
|
||||
|
||||
@@ -26,10 +20,8 @@ const useAppSetup = () => {
|
||||
|
||||
loadSettings();
|
||||
|
||||
installDefaultPacks();
|
||||
|
||||
const refreshHandler = (data) => {
|
||||
if (data === 'other' || data === 'greeting' || data === 'clock' || data === 'quote') {
|
||||
if (data === 'other') {
|
||||
loadSettings(true);
|
||||
}
|
||||
};
|
||||
@@ -63,15 +55,11 @@ const App = () => {
|
||||
|
||||
useAppSetup();
|
||||
|
||||
const languagecode = variables.languagecode || localStorage.getItem('language') || 'en_GB';
|
||||
const languagecode = localStorage.getItem('language') || 'en_GB';
|
||||
|
||||
return (
|
||||
<TranslationProvider
|
||||
initialLanguage={languagecode}
|
||||
initialTranslations={variables.language?.messages || {}}
|
||||
>
|
||||
<TranslationProvider initialLanguage={languagecode}>
|
||||
{showBackground && <Background />}
|
||||
<CustomWidgets />
|
||||
<ToastContainer
|
||||
position="top-center"
|
||||
autoClose={toastDisplayTime}
|
||||
@@ -83,7 +71,6 @@ const App = () => {
|
||||
<Widgets />
|
||||
<Modals />
|
||||
</div>
|
||||
<Outlet />
|
||||
</TranslationProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { captureException } from '@sentry/react';
|
||||
import variables from 'config/variables';
|
||||
|
||||
class ErrorBoundary extends PureComponent {
|
||||
constructor(props) {
|
||||
@@ -27,35 +26,21 @@ class ErrorBoundary extends PureComponent {
|
||||
|
||||
render() {
|
||||
if (this.state.error) {
|
||||
const title = variables.getMessage
|
||||
? variables.getMessage('error_boundary.title')
|
||||
: 'An error occurred';
|
||||
const message = variables.getMessage
|
||||
? variables.getMessage('error_boundary.message')
|
||||
: 'Something went wrong. Please try refreshing the page.';
|
||||
const reportButton = variables.getMessage
|
||||
? variables.getMessage('error_boundary.report_button')
|
||||
: 'Report Error';
|
||||
const sentSuccessfully = variables.getMessage
|
||||
? variables.getMessage('error_boundary.sent_successfully')
|
||||
: 'Report sent successfully';
|
||||
const supportDiscord = variables.getMessage
|
||||
? variables.getMessage('error_boundary.support_discord')
|
||||
: 'Get Support on Discord';
|
||||
|
||||
return (
|
||||
<div className="criticalError">
|
||||
<div className="criticalError-message">
|
||||
<h1>{title}</h1>
|
||||
<p>{message}</p>
|
||||
<h1>A critical error has occurred</h1>
|
||||
<p>
|
||||
The new tab page could not be loaded. Please uninstall the extension and try again.
|
||||
</p>
|
||||
<div className="criticalError-actions">
|
||||
{this.state.showReport ? (
|
||||
<button onClick={() => this.reportError()}>{reportButton}</button>
|
||||
<button onClick={() => this.reportError()}>Report Issue</button>
|
||||
) : (
|
||||
<p>{sentSuccessfully}</p>
|
||||
<p>Sent Successfully</p>
|
||||
)}
|
||||
<a href="https://discord.gg/zv8C9F8" target="_blank" rel="noreferrer">
|
||||
{supportDiscord}
|
||||
Support Discord
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,229 +1,72 @@
|
||||
import { useT } from 'contexts';
|
||||
import variables from 'config/variables';
|
||||
|
||||
import { useState, memo, useEffect } from 'react';
|
||||
import { useState, memo } from 'react';
|
||||
import { MdAddLink, MdClose } from 'react-icons/md';
|
||||
import { Tooltip } from 'components/Elements';
|
||||
import { Button } from 'components/Elements';
|
||||
import { Dropdown, Text } from 'components/Form/Settings';
|
||||
import { IconService } from 'utils/quicklinks';
|
||||
|
||||
import './AddModal.scss';
|
||||
|
||||
function AddModal({ urlError, iconError, addLink, closeModal, edit, editData, editLink }) {
|
||||
const t = useT();
|
||||
const [name, setName] = useState(edit ? editData.name : '');
|
||||
const [url, setUrl] = useState(edit ? editData.url : '');
|
||||
const [icon, setIcon] = useState(edit ? editData.icon : '');
|
||||
const [iconType, setIconType] = useState(edit && editData.iconType ? editData.iconType : 'auto');
|
||||
const [iconData, setIconData] = useState(edit && editData.iconData ? editData.iconData : null);
|
||||
const [iconPreview, setIconPreview] = useState(null);
|
||||
const [uploadError, setUploadError] = useState('');
|
||||
const [suggestedName, setSuggestedName] = useState('');
|
||||
const [resetKey, setResetKey] = useState(Date.now());
|
||||
|
||||
useEffect(() => {
|
||||
if (!edit) {
|
||||
localStorage.removeItem('quicklink_modal_name');
|
||||
localStorage.removeItem('quicklink_modal_url');
|
||||
localStorage.removeItem('quicklink_modal_iconType');
|
||||
localStorage.removeItem('quicklink_modal_icon_url');
|
||||
localStorage.removeItem('quicklink_modal_emoji');
|
||||
|
||||
setName('');
|
||||
setUrl('');
|
||||
setIcon('');
|
||||
setIconType('auto');
|
||||
setIconData(null);
|
||||
setIconPreview(null);
|
||||
setUploadError('');
|
||||
setSuggestedName('');
|
||||
|
||||
setResetKey(Date.now());
|
||||
}
|
||||
}, [edit]);
|
||||
|
||||
useEffect(() => {
|
||||
if (name || !url) {
|
||||
setSuggestedName('');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let urlToTest = url;
|
||||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||||
urlToTest = 'https://' + url;
|
||||
}
|
||||
|
||||
const domain = new URL(urlToTest).hostname;
|
||||
if (domain) {
|
||||
const parts = domain.split('.');
|
||||
let name = parts[0];
|
||||
if (parts.length > 2 && parts[parts.length - 2] === 'co') {
|
||||
name = parts[parts.length - 3];
|
||||
}
|
||||
setSuggestedName(name);
|
||||
}
|
||||
} catch (e) {
|
||||
setSuggestedName('');
|
||||
}
|
||||
}, [url, name]);
|
||||
|
||||
const handleIconUpload = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
try {
|
||||
const dataUrl = await IconService.uploadCustomIcon(file);
|
||||
setIconData(dataUrl);
|
||||
setIconPreview(dataUrl);
|
||||
setUploadError('');
|
||||
} catch (e) {
|
||||
setUploadError(e.message);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
const finalName = name || suggestedName || '';
|
||||
|
||||
if (edit) {
|
||||
editLink(editData, finalName, url, icon, iconType, iconData);
|
||||
} else {
|
||||
addLink(finalName, url, icon, iconType, iconData);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
handleSubmit();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="addLinkModal" onKeyDown={handleKeyDown}>
|
||||
<div className="addLinkModal">
|
||||
<div className="shareHeader">
|
||||
<span className="title">
|
||||
{edit ? t('widgets.quicklinks.edit') : t('widgets.quicklinks.new')}
|
||||
{edit
|
||||
? variables.getMessage('widgets.quicklinks.edit')
|
||||
: variables.getMessage('widgets.quicklinks.new')}
|
||||
</span>
|
||||
<Tooltip title={t('modals.welcome.buttons.close')}>
|
||||
<Tooltip title={variables.getMessage('modals.welcome.buttons.close')}>
|
||||
<div className="close" onClick={() => closeModal()}>
|
||||
<MdClose />
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="quicklinkModalTextbox">
|
||||
<div className="addLinkModal-row">
|
||||
<div className="addLinkModal-field">
|
||||
<div className="addLinkModal-labelRow">
|
||||
<label className="addLinkModal-label">{t('widgets.quicklinks.name')}</label>
|
||||
{suggestedName && !name && (
|
||||
<span className="addLinkModal-suggestedText">
|
||||
{t('widgets.quicklinks.suggested', { name: suggestedName })}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Text
|
||||
key={`name-${resetKey}`}
|
||||
name="quicklink_modal_name"
|
||||
noSetting={true}
|
||||
onChange={(value) => setName(value)}
|
||||
placeholder={suggestedName || t('widgets.quicklinks.name_placeholder')}
|
||||
/>
|
||||
</div>
|
||||
<div className="addLinkModal-field">
|
||||
<label className="addLinkModal-label">
|
||||
{t('widgets.quicklinks.url')}
|
||||
<span className="addLinkModal-required">*</span>
|
||||
</label>
|
||||
<Text
|
||||
key={`url-${resetKey}`}
|
||||
name="quicklink_modal_url"
|
||||
noSetting={true}
|
||||
onChange={(value) => {
|
||||
let finalValue = value;
|
||||
if (value && !value.startsWith('http://') && !value.startsWith('https://')) {
|
||||
finalValue = 'https://' + value;
|
||||
}
|
||||
setUrl(finalValue);
|
||||
}}
|
||||
placeholder={t('widgets.quicklinks.url_placeholder')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="addLinkModal-dropdownWrapper">
|
||||
<Dropdown
|
||||
label={t('widgets.quicklinks.icon_type_label')}
|
||||
name="quicklink_modal_iconType"
|
||||
noSetting={true}
|
||||
onChange={(value) => setIconType(value)}
|
||||
items={[
|
||||
{ value: 'auto', text: t('widgets.quicklinks.icon_type_auto') },
|
||||
{ value: 'custom_url', text: t('widgets.quicklinks.icon_type_custom_url') },
|
||||
{ value: 'custom_upload', text: t('widgets.quicklinks.icon_type_upload') },
|
||||
{ value: 'emoji', text: t('widgets.quicklinks.icon_type_emoji') },
|
||||
{ value: 'letter', text: t('widgets.quicklinks.icon_type_letter') },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{iconType === 'custom_url' && (
|
||||
<div className="text-field" style={{ gridColumn: 'span 2' }}>
|
||||
<Text
|
||||
key={`icon-url-${resetKey}`}
|
||||
name="quicklink_modal_icon_url"
|
||||
noSetting={true}
|
||||
onChange={(value) => setIcon(value)}
|
||||
placeholder={t('widgets.quicklinks.icon_url_placeholder')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{iconType === 'custom_upload' && (
|
||||
<div className="text-field" style={{ gridColumn: 'span 2' }}>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleIconUpload}
|
||||
style={{ padding: '10px 0' }}
|
||||
/>
|
||||
{iconPreview && (
|
||||
<img
|
||||
src={iconPreview}
|
||||
alt={t('common.alt_text.preview')}
|
||||
style={{ width: '40px', height: '40px', marginTop: '8px', borderRadius: '4px' }}
|
||||
/>
|
||||
)}
|
||||
{uploadError && (
|
||||
<span className="dropdown-error" style={{ display: 'block', marginTop: '4px' }}>
|
||||
{uploadError}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{iconType === 'emoji' && (
|
||||
<div className="text-field" style={{ gridColumn: 'span 2' }}>
|
||||
<Text
|
||||
key={`emoji-${resetKey}`}
|
||||
name="quicklink_modal_emoji"
|
||||
noSetting={true}
|
||||
onChange={(value) => setIcon(value)}
|
||||
placeholder=""
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<input
|
||||
type="text"
|
||||
className="text-field-input"
|
||||
placeholder={variables.getMessage('widgets.quicklinks.name')}
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value.replace(/(\r\n|\n|\r)/gm, ''))}
|
||||
style={{ gridColumn: 'span 2' }}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
className="text-field-input"
|
||||
placeholder={variables.getMessage('widgets.quicklinks.url')}
|
||||
value={url}
|
||||
onChange={(e) => setUrl(e.target.value.replace(/(\r\n|\n|\r)/gm, ''))}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
className="text-field-input"
|
||||
placeholder={variables.getMessage('widgets.quicklinks.icon')}
|
||||
value={icon}
|
||||
onChange={(e) => setIcon(e.target.value.replace(/(\r\n|\n|\r)/gm, ''))}
|
||||
/>
|
||||
</div>
|
||||
<div className="addFooter">
|
||||
<span className="dropdown-error">
|
||||
{iconError} {urlError}
|
||||
</span>
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={handleSubmit}
|
||||
icon={<MdAddLink />}
|
||||
label={
|
||||
edit ? t('modals.main.settings.sections.quicklinks.edit') : t('widgets.quicklinks.add')
|
||||
}
|
||||
/>
|
||||
{edit ? (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => editLink(editData, name, url, icon)}
|
||||
icon={<MdAddLink />}
|
||||
label={variables.getMessage('modals.main.settings.sections.quicklinks.edit')}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => addLink(name, url, icon)}
|
||||
icon={<MdAddLink />}
|
||||
label={variables.getMessage('widgets.quicklinks.add')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
@use 'scss/variables' as *;
|
||||
@use 'scss/mixins' as *;
|
||||
|
||||
.addLinkModal {
|
||||
width: 600px;
|
||||
max-width: 90vw;
|
||||
|
||||
.addLinkModal-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.addLinkModal-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-right: 35px;
|
||||
|
||||
.text-field-container {
|
||||
width: 100%;
|
||||
|
||||
.text-field {
|
||||
width: 100%;
|
||||
|
||||
.text-field-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.addLinkModal-labelRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.addLinkModal-label {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
}
|
||||
}
|
||||
|
||||
.addLinkModal-suggestedText {
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
text-transform: none;
|
||||
letter-spacing: 0;
|
||||
white-space: nowrap;
|
||||
|
||||
@include themed {
|
||||
color: t($link);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.addLinkModal-required {
|
||||
@include themed {
|
||||
color: t($link);
|
||||
}
|
||||
}
|
||||
|
||||
.addLinkModal-dropdownWrapper {
|
||||
grid-column: span 2;
|
||||
|
||||
.dropdown {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,20 +3,7 @@ import Tooltip from 'components/Elements/Tooltip/Tooltip';
|
||||
|
||||
const Button = forwardRef(
|
||||
(
|
||||
{
|
||||
icon,
|
||||
label,
|
||||
type,
|
||||
iconPlacement,
|
||||
onClick,
|
||||
active,
|
||||
disabled,
|
||||
tooltipTitle,
|
||||
tooltipKey,
|
||||
href,
|
||||
style,
|
||||
badge,
|
||||
},
|
||||
{ icon, label, type, iconPlacement, onClick, active, disabled, tooltipTitle, tooltipKey, href, style },
|
||||
ref,
|
||||
) => {
|
||||
let className;
|
||||
@@ -59,24 +46,16 @@ const Button = forwardRef(
|
||||
<button className={className} onClick={onClick} ref={ref} disabled={disabled} style={style}>
|
||||
{icon}
|
||||
{label}
|
||||
{badge !== undefined && badge !== null && <span className="btn-badge">{badge}</span>}
|
||||
</button>
|
||||
);
|
||||
|
||||
const linkButton = (
|
||||
<a
|
||||
className={className}
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
disabled={disabled}
|
||||
href={href}
|
||||
style={style}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
<a className={className} onClick={onClick} ref={ref} disabled={disabled} href={href} style={style}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{icon}
|
||||
{label}
|
||||
{badge && <span className="btn-badge">{badge}</span>}
|
||||
</a>
|
||||
);
|
||||
|
||||
@@ -94,7 +73,6 @@ const Button = forwardRef(
|
||||
>
|
||||
{icon}
|
||||
{label}
|
||||
{badge && <span className="btn-badge">{badge}</span>}
|
||||
</a>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { Suspense, lazy, useState, memo, useEffect, useRef, useMemo } from 'react';
|
||||
import { useLocation, useNavigate, useParams } from 'react-router';
|
||||
import { useT } from 'contexts';
|
||||
import { Suspense, lazy, useState, memo, useEffect } from 'react';
|
||||
import variables from 'config/variables';
|
||||
|
||||
import './scss/index.scss';
|
||||
import ModalLoader from './components/ModalLoader';
|
||||
import ModalTopBar from './components/ModalTopBar';
|
||||
import { TAB_TYPES } from './constants/tabConfig';
|
||||
import { updateHash, parseDeepLink } from 'utils/deepLinking';
|
||||
import { useRouterBridge } from '../../../router/RouterBridge';
|
||||
|
||||
const Settings = lazy(() => import('../../../features/misc/views/Settings'));
|
||||
const Library = lazy(() => import('../../../features/misc/views/Library'));
|
||||
const Discover = lazy(() => import('../../../features/misc/views/Discover'));
|
||||
|
||||
// Map tab types to their corresponding components
|
||||
const TAB_COMPONENTS = {
|
||||
[TAB_TYPES.SETTINGS]: Settings,
|
||||
[TAB_TYPES.LIBRARY]: Library,
|
||||
@@ -20,224 +19,185 @@ const TAB_COMPONENTS = {
|
||||
};
|
||||
|
||||
function MainModal({ modalClose, deepLinkData }) {
|
||||
const t = useT();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const params = useParams();
|
||||
const { deepLinkData: routerDeepLinkData } = useRouterBridge();
|
||||
|
||||
// Use router-based deepLinkData if available, fallback to prop
|
||||
// Memoize to prevent infinite loops in useEffect
|
||||
const effectiveDeepLinkData = useMemo(
|
||||
() => routerDeepLinkData || deepLinkData,
|
||||
[location.pathname, deepLinkData],
|
||||
);
|
||||
|
||||
// Derive currentTab from router location instead of state
|
||||
const currentTab = effectiveDeepLinkData?.tab || TAB_TYPES.SETTINGS;
|
||||
|
||||
// Initialize with deep link tab if provided, otherwise default to settings
|
||||
const initialTab = deepLinkData?.tab || TAB_TYPES.SETTINGS;
|
||||
const [currentTab, setCurrentTab] = useState(initialTab);
|
||||
const [currentSection, setCurrentSection] = useState('');
|
||||
const [currentSectionName, setCurrentSectionName] = useState('');
|
||||
const [currentSubSection, setCurrentSubSection] = useState(
|
||||
effectiveDeepLinkData?.subSection || null,
|
||||
);
|
||||
const [currentSubSection, setCurrentSubSection] = useState(deepLinkData?.subSection || null);
|
||||
const [productView, setProductView] = useState(null);
|
||||
const [resetDiscoverToAll, setResetDiscoverToAll] = useState(false);
|
||||
const [navigationTrigger, setNavigationTrigger] = useState(null);
|
||||
const [iframeBreadcrumbs, setIframeBreadcrumbs] = useState([]);
|
||||
|
||||
const historyRef = useRef([]);
|
||||
const historyIndexRef = useRef(-1);
|
||||
const [canGoBack, setCanGoBack] = useState(false);
|
||||
const [canGoForward, setCanGoForward] = useState(false);
|
||||
|
||||
const updateNavButtons = () => {
|
||||
setCanGoBack(historyIndexRef.current > 0);
|
||||
setCanGoForward(historyIndexRef.current < historyRef.current.length - 1);
|
||||
};
|
||||
|
||||
// Clear product view when changing tabs
|
||||
useEffect(() => {
|
||||
setProductView(null);
|
||||
}, [currentTab]);
|
||||
|
||||
// Handle deep link updates (when modal opens via EventBus with new deep link)
|
||||
useEffect(() => {
|
||||
if (effectiveDeepLinkData) {
|
||||
if (effectiveDeepLinkData.tab === TAB_TYPES.SETTINGS && effectiveDeepLinkData.section) {
|
||||
if (deepLinkData) {
|
||||
// Update tab if different
|
||||
if (deepLinkData.tab && deepLinkData.tab !== currentTab) {
|
||||
setCurrentTab(deepLinkData.tab);
|
||||
}
|
||||
|
||||
// Handle settings section navigation with subsection
|
||||
if (deepLinkData.tab === TAB_TYPES.SETTINGS && deepLinkData.section) {
|
||||
setNavigationTrigger({
|
||||
type: 'settings-section',
|
||||
data: effectiveDeepLinkData.section,
|
||||
data: deepLinkData.section,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
if (effectiveDeepLinkData.subSection) {
|
||||
setCurrentSubSection(effectiveDeepLinkData.subSection);
|
||||
if (historyIndexRef.current >= 0) {
|
||||
historyRef.current[historyIndexRef.current] = {
|
||||
...historyRef.current[historyIndexRef.current],
|
||||
subSection: effectiveDeepLinkData.subSection,
|
||||
};
|
||||
}
|
||||
// Set sub-section if present
|
||||
if (deepLinkData.subSection) {
|
||||
setCurrentSubSection(deepLinkData.subSection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [effectiveDeepLinkData]);
|
||||
}, [deepLinkData]);
|
||||
|
||||
// Clear hash when modal closes
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
// When modal unmounts, clear the hash
|
||||
if (window.location.hash) {
|
||||
window.history.replaceState(null, null, window.location.pathname);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
// React to router location changes
|
||||
useEffect(() => {
|
||||
if (effectiveDeepLinkData) {
|
||||
if (effectiveDeepLinkData.tab === TAB_TYPES.SETTINGS && effectiveDeepLinkData.section) {
|
||||
setNavigationTrigger({
|
||||
type: 'settings-section',
|
||||
data: effectiveDeepLinkData.section,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
setCurrentSubSection(effectiveDeepLinkData.subSection || null);
|
||||
return;
|
||||
}
|
||||
// Listen for browser back/forward navigation via popstate
|
||||
const handlePopState = () => {
|
||||
const linkData = window.location.hash ? parseDeepLink(window.location.hash) : null;
|
||||
|
||||
if (
|
||||
effectiveDeepLinkData.itemId &&
|
||||
effectiveDeepLinkData.collection &&
|
||||
effectiveDeepLinkData.fromCollection
|
||||
) {
|
||||
setNavigationTrigger({
|
||||
type: 'collection',
|
||||
data: effectiveDeepLinkData.collection,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
setTimeout(() => {
|
||||
if (linkData) {
|
||||
// Update tab if different
|
||||
if (linkData.tab && linkData.tab !== currentTab) {
|
||||
setCurrentTab(linkData.tab);
|
||||
}
|
||||
|
||||
// Handle settings section navigation
|
||||
if (linkData.tab === TAB_TYPES.SETTINGS && linkData.section) {
|
||||
setNavigationTrigger({
|
||||
type: 'settings-section',
|
||||
data: linkData.section,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
// Set sub-section if present in hash
|
||||
setCurrentSubSection(linkData.subSection || null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle product and collection navigation
|
||||
if (linkData.itemId && linkData.collection && linkData.fromCollection) {
|
||||
// Product viewed from within a collection
|
||||
// First set collection state, then navigate to product
|
||||
setNavigationTrigger({
|
||||
type: 'collection',
|
||||
data: linkData.collection,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
// Small delay to ensure collection state is set before navigating to product
|
||||
setTimeout(() => {
|
||||
setNavigationTrigger({
|
||||
type: 'product',
|
||||
data: {
|
||||
id: linkData.itemId,
|
||||
type: linkData.category,
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}, 100);
|
||||
} else if (linkData.itemId) {
|
||||
// Product navigation (standalone)
|
||||
setNavigationTrigger({
|
||||
type: 'product',
|
||||
data: {
|
||||
id: effectiveDeepLinkData.itemId,
|
||||
type: effectiveDeepLinkData.category,
|
||||
id: linkData.itemId,
|
||||
type: linkData.category,
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}, 100);
|
||||
} else if (effectiveDeepLinkData.itemId) {
|
||||
setNavigationTrigger({
|
||||
type: 'product',
|
||||
data: {
|
||||
id: effectiveDeepLinkData.itemId,
|
||||
type: effectiveDeepLinkData.category,
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
} else if (effectiveDeepLinkData.collection) {
|
||||
setNavigationTrigger({
|
||||
type: 'collection',
|
||||
data: effectiveDeepLinkData.collection,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
} else {
|
||||
setProductView(null);
|
||||
setNavigationTrigger({
|
||||
type: 'main',
|
||||
data: { clearCollection: true },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
} else if (linkData.collection) {
|
||||
// Collection page navigation
|
||||
setNavigationTrigger({
|
||||
type: 'collection',
|
||||
data: linkData.collection,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
} else {
|
||||
// Back to main view (clear collection state)
|
||||
setProductView(null);
|
||||
setNavigationTrigger({
|
||||
type: 'main',
|
||||
data: { clearCollection: true },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [effectiveDeepLinkData, currentTab]);
|
||||
};
|
||||
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
return () => window.removeEventListener('popstate', handlePopState);
|
||||
}, [currentTab]);
|
||||
|
||||
const handleChangeTab = (newTab) => {
|
||||
historyRef.current = [];
|
||||
historyIndexRef.current = -1;
|
||||
updateNavButtons();
|
||||
setCurrentTab(newTab);
|
||||
// Update URL hash when tab changes
|
||||
if (newTab === TAB_TYPES.DISCOVER) {
|
||||
const section = effectiveDeepLinkData?.category || effectiveDeepLinkData?.section || 'all';
|
||||
const itemId = effectiveDeepLinkData?.itemId ? `/${effectiveDeepLinkData.itemId}` : '';
|
||||
navigate(`/${newTab}/${section}${itemId}`);
|
||||
updateHash(`#${newTab}/all`);
|
||||
} else if (newTab === TAB_TYPES.LIBRARY) {
|
||||
navigate(`/${newTab}/added`);
|
||||
updateHash(`#${newTab}/added`);
|
||||
} else {
|
||||
navigate(`/${newTab}`);
|
||||
updateHash(`#${newTab}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSectionChange = (section, sectionName) => {
|
||||
setCurrentSection(section);
|
||||
setCurrentSectionName(sectionName);
|
||||
// Only reset subsection if we're actually changing to a different section
|
||||
// Don't reset on initial section set (when currentSectionName is empty)
|
||||
if (currentSectionName !== '' && currentSectionName !== sectionName) {
|
||||
setCurrentSubSection(null);
|
||||
}
|
||||
const entry = {
|
||||
section,
|
||||
sectionName,
|
||||
subSection:
|
||||
currentSectionName === '' || currentSectionName === sectionName ? currentSubSection : null,
|
||||
};
|
||||
const current = historyRef.current[historyIndexRef.current];
|
||||
if (
|
||||
!current ||
|
||||
current.sectionName !== sectionName ||
|
||||
current.subSection !== entry.subSection
|
||||
) {
|
||||
historyRef.current = historyRef.current.slice(0, historyIndexRef.current + 1).concat(entry);
|
||||
historyIndexRef.current = historyRef.current.length - 1;
|
||||
updateNavButtons();
|
||||
}
|
||||
// Clear sub-section when changing sections
|
||||
setCurrentSubSection(null);
|
||||
// Update URL hash when section changes
|
||||
if (currentTab === TAB_TYPES.DISCOVER) {
|
||||
// Don't navigate away if we're viewing a specific item
|
||||
if (effectiveDeepLinkData?.itemId) {
|
||||
return;
|
||||
}
|
||||
// For Discover tab, update with the section type
|
||||
const sectionMap = {
|
||||
[t('modals.main.marketplace.all')]: 'all',
|
||||
[t('modals.main.marketplace.photo_packs')]: 'photo_packs',
|
||||
[t('modals.main.marketplace.quote_packs')]: 'quote_packs',
|
||||
[t('modals.main.marketplace.preset_settings')]: 'preset_settings',
|
||||
[t('modals.main.marketplace.collections')]: 'collections',
|
||||
[variables.getMessage('modals.main.marketplace.all')]: 'all',
|
||||
[variables.getMessage('modals.main.marketplace.photo_packs')]: 'photo_packs',
|
||||
[variables.getMessage('modals.main.marketplace.quote_packs')]: 'quote_packs',
|
||||
[variables.getMessage('modals.main.marketplace.preset_settings')]: 'preset_settings',
|
||||
[variables.getMessage('modals.main.marketplace.collections')]: 'collections',
|
||||
};
|
||||
const sectionKey = sectionMap[section];
|
||||
if (sectionKey) {
|
||||
navigate(`/${currentTab}/${sectionKey}`);
|
||||
updateHash(`#${currentTab}/${sectionKey}`);
|
||||
}
|
||||
} else if (currentTab === TAB_TYPES.SETTINGS && sectionName) {
|
||||
// Include subsection in hash if it exists and we're not changing sections
|
||||
const path =
|
||||
currentSubSection && (currentSectionName === '' || currentSectionName === sectionName)
|
||||
? `/${currentTab}/${sectionName}/${currentSubSection}`
|
||||
: `/${currentTab}/${sectionName}`;
|
||||
navigate(path, { replace: true });
|
||||
// For Settings tab, update with the section name
|
||||
updateHash(`#${currentTab}/${sectionName}`, false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubSectionChange = (subSection, sectionName) => {
|
||||
setCurrentSubSection(subSection);
|
||||
const effectiveSectionName = sectionName || currentSectionName;
|
||||
const entry = { section: currentSection, sectionName: effectiveSectionName, subSection };
|
||||
const current = historyRef.current[historyIndexRef.current];
|
||||
if (
|
||||
!current ||
|
||||
current.sectionName !== effectiveSectionName ||
|
||||
current.subSection !== subSection
|
||||
) {
|
||||
historyRef.current = historyRef.current.slice(0, historyIndexRef.current + 1).concat(entry);
|
||||
historyIndexRef.current = historyRef.current.length - 1;
|
||||
updateNavButtons();
|
||||
}
|
||||
// Update URL hash when sub-section changes
|
||||
if (currentTab === TAB_TYPES.SETTINGS && sectionName) {
|
||||
if (subSection) {
|
||||
navigate(`/${currentTab}/${sectionName}/${subSection}`);
|
||||
updateHash(`#${currentTab}/${sectionName}/${subSection}`);
|
||||
} else {
|
||||
navigate(`/${currentTab}/${sectionName}`);
|
||||
// Going back to section, remove sub-section from hash
|
||||
updateHash(`#${currentTab}/${sectionName}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleProductView = (product) => {
|
||||
setProductView(product);
|
||||
// URL hash is already updated by child components (Browse.jsx)
|
||||
// Browser history automatically tracks hash changes
|
||||
};
|
||||
|
||||
const handleResetDiscoverToAll = () => {
|
||||
@@ -245,39 +205,21 @@ function MainModal({ modalClose, deepLinkData }) {
|
||||
setTimeout(() => setResetDiscoverToAll(false), 100);
|
||||
};
|
||||
|
||||
const restoreHistoryEntry = (entry) => {
|
||||
setCurrentSubSection(entry.subSection);
|
||||
if (entry.sectionName !== currentSectionName) {
|
||||
setCurrentSection(entry.section);
|
||||
setCurrentSectionName(entry.sectionName);
|
||||
setNavigationTrigger({
|
||||
type: 'settings-section',
|
||||
data: entry.sectionName,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
if (currentTab === TAB_TYPES.SETTINGS) {
|
||||
const hash = entry.subSection
|
||||
? `#${currentTab}/${entry.sectionName}/${entry.subSection}`
|
||||
: `#${currentTab}/${entry.sectionName}`;
|
||||
window.history.replaceState(null, null, hash);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
if (historyIndexRef.current <= 0) return;
|
||||
historyIndexRef.current -= 1;
|
||||
updateNavButtons();
|
||||
restoreHistoryEntry(historyRef.current[historyIndexRef.current]);
|
||||
// Clear iframe breadcrumbs when navigating back
|
||||
setIframeBreadcrumbs([]);
|
||||
window.history.back();
|
||||
};
|
||||
|
||||
const handleForward = () => {
|
||||
if (historyIndexRef.current >= historyRef.current.length - 1) return;
|
||||
historyIndexRef.current += 1;
|
||||
updateNavButtons();
|
||||
restoreHistoryEntry(historyRef.current[historyIndexRef.current]);
|
||||
window.history.forward();
|
||||
};
|
||||
|
||||
// Browser manages history state, so we always show buttons enabled
|
||||
// Browser will handle whether there's actually history to go back/forward
|
||||
const canGoBack = true;
|
||||
const canGoForward = true;
|
||||
|
||||
const TabComponent = TAB_COMPONENTS[currentTab] || Settings;
|
||||
|
||||
return (
|
||||
@@ -302,7 +244,7 @@ function MainModal({ modalClose, deepLinkData }) {
|
||||
<TabComponent
|
||||
key={currentTab}
|
||||
changeTab={handleChangeTab}
|
||||
deepLinkData={effectiveDeepLinkData}
|
||||
deepLinkData={deepLinkData}
|
||||
currentTab={currentTab}
|
||||
onSectionChange={handleSectionChange}
|
||||
onSubSectionChange={handleSubSectionChange}
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
import { memo } from 'react';
|
||||
import { memo, useState, useEffect } from 'react';
|
||||
import { useT } from 'contexts/TranslationContext';
|
||||
import { Tooltip } from 'components/Elements';
|
||||
import { getIconComponent, DIVIDER_LABELS } from '../constants/tabConfig';
|
||||
|
||||
function Tab({ label, currentTab, onClick, navbarTab, isCollapsed }) {
|
||||
function Tab({ label, currentTab, onClick, navbarTab }) {
|
||||
const t = useT();
|
||||
const isExperimental = localStorage.getItem('experimental') !== 'false';
|
||||
const [isExperimental, setIsExperimental] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
setIsExperimental(localStorage.getItem('experimental') !== 'false');
|
||||
}, []);
|
||||
|
||||
// Get the icon component for this label (label is already translated)
|
||||
const IconComponent = getIconComponent(label, { getMessage: t });
|
||||
|
||||
// Determine if this label should have a divider after it
|
||||
const hasDivider = DIVIDER_LABELS.some((key) => t(key) === label);
|
||||
|
||||
// Build className
|
||||
const baseClass = navbarTab ? 'navbar-item' : 'tab-list-item';
|
||||
const activeClass = navbarTab ? 'navbar-item-active' : 'tab-list-active';
|
||||
const className = `${baseClass}${currentTab === label ? ` ${activeClass}` : ''}`;
|
||||
|
||||
// Hide experimental tab if experimental mode is disabled
|
||||
const isExperimentalTab = label === t('modals.main.settings.sections.experimental.title');
|
||||
if (isExperimentalTab && !isExperimental) {
|
||||
return <hr />;
|
||||
@@ -22,18 +29,10 @@ function Tab({ label, currentTab, onClick, navbarTab, isCollapsed }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
{isCollapsed ? (
|
||||
<Tooltip title={label} placement="right">
|
||||
<button className={className} onClick={() => onClick(label)}>
|
||||
{IconComponent && <IconComponent />}
|
||||
</button>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<button className={className} onClick={() => onClick(label)}>
|
||||
{IconComponent && <IconComponent />} <span>{label}</span>
|
||||
</button>
|
||||
)}
|
||||
{!isCollapsed && hasDivider && <hr />}
|
||||
<button className={className} onClick={() => onClick(label)}>
|
||||
{IconComponent && <IconComponent />} <span>{label}</span>
|
||||
</button>
|
||||
{hasDivider && <hr />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { useState, useEffect, useLayoutEffect, useRef } from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { useT } from 'contexts/TranslationContext';
|
||||
import variables from 'config/variables';
|
||||
import Tab from './Tab';
|
||||
import ReminderInfo from '../components/ReminderInfo';
|
||||
import SidebarToggle from '../components/SidebarToggle';
|
||||
import ErrorBoundary from '../../../../features/misc/modals/ErrorBoundary';
|
||||
import { TAB_TYPES } from '../constants/tabConfig';
|
||||
import { SearchInput } from 'components/Form/Settings';
|
||||
import EventBus from 'utils/eventbus';
|
||||
|
||||
const Tabs = ({
|
||||
children,
|
||||
@@ -21,6 +18,7 @@ const Tabs = ({
|
||||
}) => {
|
||||
const t = useT();
|
||||
|
||||
// Find initial section from deep link if available
|
||||
const getInitialSection = () => {
|
||||
if (deepLinkData?.section && sections) {
|
||||
const section = sections.find((s) => s.name === deepLinkData.section);
|
||||
@@ -31,15 +29,6 @@ const Tabs = ({
|
||||
};
|
||||
}
|
||||
}
|
||||
if (deepLinkData?.category && sections) {
|
||||
const section = sections.find((s) => s.name === deepLinkData.category);
|
||||
if (section) {
|
||||
return {
|
||||
label: t(section.label),
|
||||
name: section.name,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
label: children[0]?.props.label,
|
||||
name: children[0]?.props.name,
|
||||
@@ -47,80 +36,70 @@ const Tabs = ({
|
||||
};
|
||||
|
||||
const initial = getInitialSection();
|
||||
const [currentTab, setCurrentTab] = useState(initial.label);
|
||||
const [currentName, setCurrentName] = useState(initial.name);
|
||||
const [showReminder, setShowReminder] = useState(localStorage.getItem('showReminder') === 'true');
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(
|
||||
localStorage.getItem('sidebarCollapsed') === 'true',
|
||||
);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const contentRef = useRef(null);
|
||||
|
||||
const currentTab = (() => {
|
||||
if (sections && currentName) {
|
||||
const section = sections.find((s) => s.name === currentName);
|
||||
if (section) {
|
||||
return t(section.label);
|
||||
}
|
||||
}
|
||||
const child = children.find((c) => c.props.name === currentName);
|
||||
return child?.props.label || children[0]?.props.label;
|
||||
})();
|
||||
|
||||
const handleTabClick = (tab, name) => {
|
||||
if (name !== currentName) {
|
||||
variables.stats.postEvent('tab', `Opened ${name}`);
|
||||
}
|
||||
|
||||
setCurrentTab(tab);
|
||||
setCurrentName(name);
|
||||
|
||||
// Scroll content to top when changing tabs
|
||||
if (contentRef.current) {
|
||||
contentRef.current.scrollTop = 0;
|
||||
}
|
||||
|
||||
// Notify parent of section change with both label and name
|
||||
if (onSectionChange) {
|
||||
onSectionChange(tab, name);
|
||||
}
|
||||
};
|
||||
|
||||
// Notify parent of initial section on mount
|
||||
useEffect(() => {
|
||||
if (onSectionChange && currentTab) {
|
||||
onSectionChange(currentTab, currentName);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// React to deep link changes (e.g., when navigating to a suggested pack from settings)
|
||||
// Update labels when language changes
|
||||
useEffect(() => {
|
||||
if (deepLinkData && sections) {
|
||||
const targetSection = deepLinkData.section || deepLinkData.category;
|
||||
if (targetSection) {
|
||||
const section = sections.find((s) => s.name === targetSection);
|
||||
if (section && section.name !== currentName) {
|
||||
setCurrentName(section.name);
|
||||
if (contentRef.current) {
|
||||
contentRef.current.scrollTop = 0;
|
||||
}
|
||||
}
|
||||
if (sections && currentName) {
|
||||
const section = sections.find((s) => s.name === currentName);
|
||||
if (section) {
|
||||
const newLabel = t(section.label);
|
||||
setCurrentTab(newLabel);
|
||||
}
|
||||
}
|
||||
}, [deepLinkData, sections, currentName]);
|
||||
}, [t, sections, currentName]);
|
||||
|
||||
// useLayoutEffect is appropriate here for synchronous state updates before paint
|
||||
useLayoutEffect(() => {
|
||||
// Handle navigation trigger for settings sections (popstate)
|
||||
useEffect(() => {
|
||||
if (navigationTrigger?.type === 'settings-section' && sections) {
|
||||
const section = sections.find((s) => s.name === navigationTrigger.data);
|
||||
if (section) {
|
||||
const label = t(section.label);
|
||||
setCurrentTab(label);
|
||||
setCurrentName(section.name);
|
||||
// Scroll content to top when navigating via browser history
|
||||
if (contentRef.current) {
|
||||
contentRef.current.scrollTop = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [navigationTrigger, sections]);
|
||||
}, [navigationTrigger, sections, t]);
|
||||
|
||||
// useLayoutEffect is appropriate here for synchronous state updates before paint
|
||||
useLayoutEffect(() => {
|
||||
// Reset to first tab when requested
|
||||
useEffect(() => {
|
||||
if (resetToFirst) {
|
||||
setCurrentTab(children[0]?.props.label);
|
||||
setCurrentName(children[0]?.props.name);
|
||||
// Scroll content to top when resetting to first tab
|
||||
if (contentRef.current) {
|
||||
contentRef.current.scrollTop = 0;
|
||||
}
|
||||
@@ -130,91 +109,40 @@ const Tabs = ({
|
||||
}
|
||||
}, [resetToFirst]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleShowReminder = () => {
|
||||
localStorage.setItem('showReminder', 'true');
|
||||
setShowReminder(true);
|
||||
};
|
||||
|
||||
EventBus.on('showReminder', handleShowReminder);
|
||||
return () => EventBus.off('showReminder', handleShowReminder);
|
||||
}, []);
|
||||
|
||||
const handleHideReminder = () => {
|
||||
localStorage.setItem('showReminder', 'false');
|
||||
setShowReminder(false);
|
||||
};
|
||||
|
||||
const handleToggleSidebar = () => {
|
||||
const newState = !sidebarCollapsed;
|
||||
setSidebarCollapsed(newState);
|
||||
localStorage.setItem('sidebarCollapsed', newState.toString());
|
||||
};
|
||||
|
||||
// Show sidebar for Settings and Discover tabs
|
||||
const showSidebar = activeTab === TAB_TYPES.SETTINGS || activeTab === TAB_TYPES.DISCOVER;
|
||||
|
||||
const filteredChildren = children.filter((tab) => {
|
||||
if (!searchQuery.trim()) return true;
|
||||
return tab.props.label.toLowerCase().includes(searchQuery.toLowerCase());
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyPress = (e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'b') {
|
||||
e.preventDefault();
|
||||
if (showSidebar) {
|
||||
setSidebarCollapsed((prev) => !prev);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyPress);
|
||||
return () => window.removeEventListener('keydown', handleKeyPress);
|
||||
}, [showSidebar]);
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', width: '100%', height: '100%', overflow: 'hidden' }}>
|
||||
{showSidebar ? (
|
||||
<div className={`modalSidebar ${sidebarCollapsed ? 'collapsed' : 'expanded'}`}>
|
||||
<div className="sidebarHeader">
|
||||
<SidebarToggle isCollapsed={sidebarCollapsed} onToggle={handleToggleSidebar} />
|
||||
{!sidebarCollapsed && activeTab === TAB_TYPES.SETTINGS && (
|
||||
<SearchInput
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder={t('widgets.search')}
|
||||
fullWidth
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{filteredChildren.map((tab, index) => (
|
||||
<div className="modalSidebar">
|
||||
{children.map((tab, index) => (
|
||||
<Tab
|
||||
key={index}
|
||||
currentTab={currentTab}
|
||||
label={tab.props.label}
|
||||
onClick={(nextTab) => handleTabClick(nextTab, tab.props.name)}
|
||||
navbarTab={navbar}
|
||||
isCollapsed={sidebarCollapsed}
|
||||
/>
|
||||
))}
|
||||
{searchQuery.trim() && filteredChildren.length === 0 && (
|
||||
<div className="sidebarEmptyState">{t('widgets.weather.not_found')}</div>
|
||||
)}
|
||||
<ReminderInfo isVisible={showReminder} onHide={handleHideReminder} />
|
||||
</div>
|
||||
) : null}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', flex: 1, minWidth: 0 }}>
|
||||
<ReminderInfo isVisible={showReminder} onHide={handleHideReminder} />
|
||||
<div className="modalTabContent" ref={contentRef}>
|
||||
{children.map((tab, index) => {
|
||||
if (tab.props.label !== currentTab) {
|
||||
return null;
|
||||
}
|
||||
<div className="modalTabContent" ref={contentRef}>
|
||||
{children.map((tab, index) => {
|
||||
if (tab.props.label !== currentTab) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorBoundary key={`error-boundary-${index}`}>{tab.props.children}</ErrorBoundary>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
return (
|
||||
<ErrorBoundary key={`error-boundary-${index}`}>{tab.props.children}</ErrorBoundary>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useT } from 'contexts';
|
||||
import variables from 'config/variables';
|
||||
import SidebarSkeleton from './SidebarSkeleton';
|
||||
|
||||
const ModalLoader = ({ currentTab }) => (
|
||||
@@ -11,7 +11,7 @@ const ModalLoader = ({ currentTab }) => (
|
||||
<div className="emptyMessage">
|
||||
<div className="loaderHolder">
|
||||
<div id="loader"></div>
|
||||
<span className="subtitle">{t('modals.main.loading')}</span>
|
||||
<span className="subtitle">{variables.getMessage('modals.main.loading')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
20
src/components/Elements/MainModal/components/ModalNavbar.jsx
Normal file
20
src/components/Elements/MainModal/components/ModalNavbar.jsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import variables from 'config/variables';
|
||||
import { Button } from 'components/Elements';
|
||||
import { NAVBAR_BUTTONS } from '../constants/tabConfig';
|
||||
|
||||
const ModalNavbar = ({ currentTab, onChangeTab }) => (
|
||||
<div className="modalNavbar">
|
||||
{NAVBAR_BUTTONS.map(({ tab, icon: Icon, messageKey }) => (
|
||||
<Button
|
||||
key={tab}
|
||||
type="navigation"
|
||||
onClick={() => onChangeTab(tab)}
|
||||
icon={<Icon />}
|
||||
label={variables.getMessage(messageKey)}
|
||||
active={currentTab === tab}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default ModalNavbar;
|
||||
@@ -1,33 +1,21 @@
|
||||
import { useT } from 'contexts/TranslationContext';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { MdClose, MdChevronRight, MdArrowBack, MdArrowForward } from 'react-icons/md';
|
||||
import { Tooltip, Button } from 'components/Elements';
|
||||
import { NAVBAR_BUTTONS, TAB_TYPES } from '../constants/tabConfig';
|
||||
import { NAVBAR_BUTTONS } from '../constants/tabConfig';
|
||||
import mueAboutIcon from 'assets/icons/mue_about.png';
|
||||
|
||||
// Map marketplace types to translation keys
|
||||
const MARKETPLACE_TYPE_TO_KEY = {
|
||||
photo_packs: 'modals.main.marketplace.photo_packs',
|
||||
'photo packs': 'modals.main.marketplace.photo_packs',
|
||||
photos: 'modals.main.marketplace.photo_packs',
|
||||
quote_packs: 'modals.main.marketplace.quote_packs',
|
||||
'quote packs': 'modals.main.marketplace.quote_packs',
|
||||
quotes: 'modals.main.marketplace.quote_packs',
|
||||
preset_settings: 'modals.main.marketplace.preset_settings',
|
||||
'preset settings': 'modals.main.marketplace.preset_settings',
|
||||
settings: 'modals.main.marketplace.preset_settings',
|
||||
collections: 'modals.main.marketplace.collections',
|
||||
all: 'modals.main.marketplace.all',
|
||||
};
|
||||
|
||||
const BREADCRUMB_LABEL_TO_CATEGORY = {
|
||||
'photo packs': 'photo_packs',
|
||||
'quote packs': 'quote_packs',
|
||||
'preset settings': 'preset_settings',
|
||||
collections: 'collections',
|
||||
marketplace: 'all',
|
||||
};
|
||||
|
||||
function ModalTopBar({
|
||||
currentTab,
|
||||
currentSection,
|
||||
@@ -36,7 +24,6 @@ function ModalTopBar({
|
||||
productView,
|
||||
iframeBreadcrumbs,
|
||||
onTabChange,
|
||||
onSectionChange,
|
||||
onSubSectionChange,
|
||||
onClose,
|
||||
onBack,
|
||||
@@ -45,48 +32,21 @@ function ModalTopBar({
|
||||
canGoForward,
|
||||
}) {
|
||||
const t = useT();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [installedCount, setInstalledCount] = useState(() => {
|
||||
try {
|
||||
const installed = JSON.parse(localStorage.getItem('installed')) || [];
|
||||
return installed.length;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const updateCount = () => {
|
||||
try {
|
||||
const installed = JSON.parse(localStorage.getItem('installed')) || [];
|
||||
setInstalledCount(installed.length);
|
||||
} catch (e) {
|
||||
setInstalledCount(0);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('storage', updateCount);
|
||||
|
||||
window.addEventListener('installedAddonsChanged', updateCount);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('storage', updateCount);
|
||||
window.removeEventListener('installedAddonsChanged', updateCount);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Get the current tab label
|
||||
const currentTabButton = NAVBAR_BUTTONS.find(({ tab }) => tab === currentTab);
|
||||
const currentTabLabel = currentTabButton ? t(currentTabButton.messageKey) : '';
|
||||
|
||||
// Utility function to get translated sub-section label
|
||||
const getSubSectionLabel = (subSection, sectionName) => {
|
||||
if (!subSection || !sectionName) {
|
||||
return subSection;
|
||||
}
|
||||
if (!subSection || !sectionName) return subSection;
|
||||
|
||||
// Use the same translation pattern as the section components
|
||||
const translationKey = `modals.main.settings.sections.${sectionName}.${subSection}.title`;
|
||||
const translated = t(translationKey);
|
||||
|
||||
// If translation key is returned as-is or empty, it means translation doesn't exist
|
||||
// Fall back to capitalized sub-section name
|
||||
if (!translated || translated === translationKey) {
|
||||
return subSection.charAt(0).toUpperCase() + subSection.slice(1);
|
||||
}
|
||||
@@ -94,78 +54,53 @@ function ModalTopBar({
|
||||
return translated;
|
||||
};
|
||||
|
||||
// Determine breadcrumb path with click handlers
|
||||
const breadcrumbPath = [];
|
||||
|
||||
if (currentTabLabel) {
|
||||
breadcrumbPath.push({
|
||||
label: currentTabLabel,
|
||||
onClick:
|
||||
(iframeBreadcrumbs && iframeBreadcrumbs.length > 0) || productView
|
||||
? () => {
|
||||
navigate('/discover/all');
|
||||
}
|
||||
: null,
|
||||
onClick: productView ? productView.onBackToAll : null, // Clickable if viewing a product
|
||||
});
|
||||
|
||||
// Check if we have iframe breadcrumbs (from Discover iframe)
|
||||
// If so, only use the last item (the item name) and keep our section
|
||||
if (iframeBreadcrumbs && iframeBreadcrumbs.length > 0) {
|
||||
const relevantCrumbs = iframeBreadcrumbs.slice(1);
|
||||
|
||||
relevantCrumbs.forEach((crumb, index) => {
|
||||
const isLast = index === relevantCrumbs.length - 1;
|
||||
|
||||
const lowerLabel = crumb.label.toLowerCase();
|
||||
const translationKey = MARKETPLACE_TYPE_TO_KEY[lowerLabel];
|
||||
const displayLabel = translationKey ? t(translationKey) : crumb.label;
|
||||
|
||||
const categoryKey = BREADCRUMB_LABEL_TO_CATEGORY[lowerLabel];
|
||||
// Get the last breadcrumb item (the item name)
|
||||
const lastCrumb = iframeBreadcrumbs[iframeBreadcrumbs.length - 1];
|
||||
|
||||
// Add current section if available and different from the last crumb
|
||||
if (currentSection && currentSection !== lastCrumb.label) {
|
||||
breadcrumbPath.push({
|
||||
label: displayLabel,
|
||||
onClick:
|
||||
crumb.clickable && !isLast && crumb.href
|
||||
? () => {
|
||||
const href = crumb.href;
|
||||
|
||||
if (href.includes('type=')) {
|
||||
const urlParams = new URLSearchParams(href.split('?')[1]);
|
||||
const typeParam = urlParams.get('type');
|
||||
if (typeParam) {
|
||||
navigate(`/discover/${typeParam}`);
|
||||
}
|
||||
} else if (href.includes('/collections')) {
|
||||
navigate('/discover/collections');
|
||||
} else if (href.includes('/collection/')) {
|
||||
const collectionId = href.split('/collection/')[1]?.split('?')[0];
|
||||
if (collectionId) {
|
||||
navigate(`/discover/collection/${collectionId}`);
|
||||
}
|
||||
} else if (categoryKey) {
|
||||
navigate(`/discover/${categoryKey}`);
|
||||
} else if (href === '/marketplace' || href === '/marketplace/') {
|
||||
navigate('/discover/all');
|
||||
} else {
|
||||
const stepsBack = relevantCrumbs.length - index - 1;
|
||||
for (let i = 0; i < stepsBack; i++) {
|
||||
window.history.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
: null,
|
||||
label: currentSection,
|
||||
onClick: () => onBack(), // Clickable to go back
|
||||
});
|
||||
}
|
||||
|
||||
// Add the item name from iframe
|
||||
breadcrumbPath.push({
|
||||
label: lastCrumb.label,
|
||||
onClick: null, // Current item - not clickable
|
||||
});
|
||||
} else if (productView) {
|
||||
// If viewing a collection page itself (not a product within it)
|
||||
if (productView.isCollection) {
|
||||
// Show: Discover > Collection Name
|
||||
breadcrumbPath.push({
|
||||
label: productView.collectionTitle || productView.name,
|
||||
onClick: null,
|
||||
onClick: null, // Current page - not clickable
|
||||
});
|
||||
} else {
|
||||
// Viewing a product
|
||||
// Show: Discover > Collection/Category > Product
|
||||
if (productView.fromCollection && productView.collectionTitle) {
|
||||
// If from a collection, show collection name
|
||||
breadcrumbPath.push({
|
||||
label: productView.collectionTitle,
|
||||
onClick: productView.onBack || null,
|
||||
});
|
||||
} else {
|
||||
// Otherwise show category
|
||||
const categoryKey = MARKETPLACE_TYPE_TO_KEY[productView.type];
|
||||
if (categoryKey) {
|
||||
breadcrumbPath.push({
|
||||
@@ -174,23 +109,24 @@ function ModalTopBar({
|
||||
});
|
||||
}
|
||||
}
|
||||
// Add product name as final breadcrumb
|
||||
breadcrumbPath.push({
|
||||
label: productView.name,
|
||||
onClick: null,
|
||||
onClick: null, // Current item - not clickable
|
||||
});
|
||||
}
|
||||
} else if (currentSection) {
|
||||
// Show: Tab > Section or Tab > Section > Sub-Section
|
||||
breadcrumbPath.push({
|
||||
label: currentSection,
|
||||
onClick: currentSubSection
|
||||
? () => onSubSectionChange(null, currentSectionName)
|
||||
: null,
|
||||
onClick: currentSubSection ? () => onSubSectionChange(null) : null, // Clickable if sub-section is active
|
||||
});
|
||||
|
||||
// Add sub-section if present
|
||||
if (currentSubSection) {
|
||||
breadcrumbPath.push({
|
||||
label: getSubSectionLabel(currentSubSection, currentSectionName),
|
||||
onClick: null,
|
||||
onClick: null, // Current sub-section - not clickable
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -200,30 +136,35 @@ function ModalTopBar({
|
||||
<div className="modalTopBar">
|
||||
<div className="topBarLeft">
|
||||
<div className="navigationButtons">
|
||||
<Tooltip title={t('common.navigation.back')} key="backTooltip">
|
||||
<Tooltip title="Back" key="backTooltip">
|
||||
<button
|
||||
className="navButton"
|
||||
onClick={onBack}
|
||||
disabled={!canGoBack}
|
||||
aria-label={t('common.navigation.navigate_back')}
|
||||
aria-label="Navigate back"
|
||||
>
|
||||
<MdArrowBack />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip title={t('common.navigation.forward')} key="forwardTooltip">
|
||||
<Tooltip title="Forward" key="forwardTooltip">
|
||||
<button
|
||||
className="navButton"
|
||||
onClick={onForward}
|
||||
disabled={!canGoForward}
|
||||
aria-label={t('common.navigation.navigate_forward')}
|
||||
aria-label="Navigate forward"
|
||||
>
|
||||
<MdArrowForward />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<img src={mueAboutIcon} alt="Mue" className="topBarLogo" draggable={false} />
|
||||
<img
|
||||
src={mueAboutIcon}
|
||||
alt="Mue"
|
||||
className="topBarLogo"
|
||||
draggable={false}
|
||||
/>
|
||||
{breadcrumbPath.length > 0 && (
|
||||
<nav className="breadcrumbs" aria-label={t('common.navigation.breadcrumb_navigation')}>
|
||||
<nav className="breadcrumbs" aria-label="Breadcrumb navigation">
|
||||
{breadcrumbPath.map((item, index) => {
|
||||
const isLast = index === breadcrumbPath.length - 1;
|
||||
const isClickable = item.onClick !== null;
|
||||
@@ -242,7 +183,7 @@ function ModalTopBar({
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={t('common.navigation.navigate_to', { item: item.label })}
|
||||
aria-label={`Navigate to ${item.label}`}
|
||||
>
|
||||
{item.label}
|
||||
</span>
|
||||
@@ -254,9 +195,7 @@ function ModalTopBar({
|
||||
{item.label}
|
||||
</span>
|
||||
)}
|
||||
{!isLast && (
|
||||
<MdChevronRight className="breadcrumb-separator" aria-hidden="true" />
|
||||
)}
|
||||
{!isLast && <MdChevronRight className="breadcrumb-separator" aria-hidden="true" />}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
@@ -265,22 +204,16 @@ function ModalTopBar({
|
||||
</div>
|
||||
<div className="topBarRight">
|
||||
<div className="topBarNavigation">
|
||||
{NAVBAR_BUTTONS.map(({ tab, icon: Icon, messageKey }) => {
|
||||
const badgeValue =
|
||||
tab === TAB_TYPES.LIBRARY && installedCount > 0 ? installedCount : undefined;
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={tab}
|
||||
type="navigation"
|
||||
onClick={() => onTabChange(tab)}
|
||||
active={currentTab === tab}
|
||||
icon={<Icon />}
|
||||
label={t(messageKey)}
|
||||
badge={badgeValue}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{NAVBAR_BUTTONS.map(({ tab, icon: Icon, messageKey }) => (
|
||||
<Button
|
||||
key={tab}
|
||||
type="navigation"
|
||||
onClick={() => onTabChange(tab)}
|
||||
active={currentTab === tab}
|
||||
icon={<Icon />}
|
||||
label={t(messageKey)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<Tooltip title={t('modals.welcome.buttons.close')} key="closeTooltip">
|
||||
<span className="closeModal" onClick={onClose}>
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
import { useT } from 'contexts';
|
||||
import variables from 'config/variables';
|
||||
import { MdRefresh, MdClose } from 'react-icons/md';
|
||||
|
||||
const ReminderInfo = ({ isVisible, onHide }) => {
|
||||
const t = useT();
|
||||
if (!isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="reminder-info">
|
||||
<span className="title">{t('modals.main.settings.reminder.title')}</span>
|
||||
<span className="subtitle">{t('modals.main.settings.reminder.message')}</span>
|
||||
<div className="shareHeader">
|
||||
<span className="title">{variables.getMessage('modals.main.settings.reminder.title')}</span>
|
||||
<span className="closeModal" onClick={onHide}>
|
||||
<MdClose />
|
||||
</span>
|
||||
</div>
|
||||
<span className="subtitle">
|
||||
{variables.getMessage('modals.main.settings.reminder.message')}
|
||||
</span>
|
||||
<button onClick={() => window.location.reload()}>
|
||||
<MdRefresh />
|
||||
{t('modals.main.error_boundary.refresh')}
|
||||
{variables.getMessage('modals.main.error_boundary.refresh')}
|
||||
</button>
|
||||
<span className="closeModal" onClick={onHide}>
|
||||
<MdClose />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,41 +1,34 @@
|
||||
import { TAB_TYPES } from '../constants/tabConfig';
|
||||
|
||||
// Tab-specific configurations with exact divider positions
|
||||
const TAB_CONFIGS = {
|
||||
[TAB_TYPES.SETTINGS]: {
|
||||
itemCount: 16,
|
||||
dividerPositions: [10, 12],
|
||||
textWidths: [80, 100, 70, 90, 85, 75, 80, 95, 90, 75, 85, 90, 85, 80, 70, 95],
|
||||
showSearch: true,
|
||||
itemCount: 16, // Excluding experimental
|
||||
dividerPositions: [10, 12], // After Weather, Language
|
||||
textWidths: [80, 100, 70, 90, 85, 75, 80, 95, 90, 75, 85, 90, 85, 80, 70, 95], // Fixed widths in pixels
|
||||
},
|
||||
[TAB_TYPES.DISCOVER]: {
|
||||
itemCount: 5,
|
||||
dividerPositions: [0],
|
||||
textWidths: [60, 95, 95, 110, 90],
|
||||
showSearch: false,
|
||||
dividerPositions: [0], // After "All"
|
||||
textWidths: [60, 95, 95, 110, 90], // Fixed widths
|
||||
},
|
||||
[TAB_TYPES.LIBRARY]: {
|
||||
itemCount: 0,
|
||||
itemCount: 0, // Library doesn't show sidebar
|
||||
dividerPositions: [],
|
||||
textWidths: [],
|
||||
showSearch: false,
|
||||
},
|
||||
};
|
||||
|
||||
const SidebarSkeleton = ({ currentTab = TAB_TYPES.SETTINGS }) => {
|
||||
const config = TAB_CONFIGS[currentTab] || TAB_CONFIGS[TAB_TYPES.SETTINGS];
|
||||
|
||||
// Library tab doesn't show sidebar
|
||||
if (config.itemCount === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sidebarSkeleton">
|
||||
{/* Header with toggle button and optional search */}
|
||||
<div className="skeletonHeader">
|
||||
<div className="skeletonToggle pulse" />
|
||||
{config.showSearch && <div className="skeletonSearch pulse" />}
|
||||
</div>
|
||||
|
||||
{Array.from({ length: config.itemCount }).map((_, index) => {
|
||||
const hasDivider = config.dividerPositions.includes(index);
|
||||
const textWidth = config.textWidths[index] || 80;
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { FiSidebar } from 'react-icons/fi';
|
||||
import { Tooltip } from 'components/Elements';
|
||||
import { useT } from 'contexts/TranslationContext';
|
||||
|
||||
function SidebarToggle({ isCollapsed, onToggle }) {
|
||||
const t = useT();
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
title={isCollapsed ? t('modals.main.sidebar.expand') : t('modals.main.sidebar.collapse')}
|
||||
placement="right"
|
||||
>
|
||||
<button
|
||||
className="sidebarToggleButton"
|
||||
onClick={onToggle}
|
||||
aria-label={isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}
|
||||
aria-expanded={!isCollapsed}
|
||||
>
|
||||
<FiSidebar />
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export default SidebarToggle;
|
||||
@@ -1,2 +1,3 @@
|
||||
export { default as ModalLoader } from './ModalLoader';
|
||||
export { default as ModalNavbar } from './ModalNavbar';
|
||||
export { default as ReminderInfo } from './ReminderInfo';
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import variables from 'config/variables';
|
||||
import {
|
||||
MdSettings,
|
||||
MdWidgets,
|
||||
MdShoppingBasket,
|
||||
MdTune,
|
||||
MdBookmarks,
|
||||
MdExplore,
|
||||
@@ -26,12 +28,14 @@ import {
|
||||
MdCollectionsBookmark,
|
||||
} from 'react-icons/md';
|
||||
|
||||
// Tab type constants
|
||||
export const TAB_TYPES = {
|
||||
SETTINGS: 'settings',
|
||||
LIBRARY: 'library',
|
||||
DISCOVER: 'discover',
|
||||
};
|
||||
|
||||
// Icon component mapping - using component references instead of elements
|
||||
export const ICON_COMPONENTS = {
|
||||
SETTINGS: MdTune,
|
||||
LIBRARY: MdBookmarks,
|
||||
@@ -59,8 +63,9 @@ export const ICON_COMPONENTS = {
|
||||
COLLECTIONS: MdCollectionsBookmark,
|
||||
};
|
||||
|
||||
// Message keys for icon mapping
|
||||
export const MESSAGE_KEYS = {
|
||||
OVERVIEW: 'modals.main.settings.sections.order.title',
|
||||
OVERVIEW: 'modals.main.marketplace.product.overview',
|
||||
SETTINGS: 'modals.main.navbar.settings',
|
||||
LIBRARY: 'modals.main.navbar.library',
|
||||
DISCOVER: 'modals.main.navbar.discover',
|
||||
@@ -90,6 +95,8 @@ export const MESSAGE_KEYS = {
|
||||
COLLECTIONS: 'modals.main.marketplace.collections',
|
||||
};
|
||||
|
||||
// Helper to get icon component by translated label
|
||||
// This function builds a map at runtime using variables.getMessage
|
||||
export const getIconComponent = (label, variables) => {
|
||||
const iconMap = {
|
||||
[variables.getMessage(MESSAGE_KEYS.OVERVIEW)]: ICON_COMPONENTS.OVERVIEW,
|
||||
@@ -125,6 +132,7 @@ export const getIconComponent = (label, variables) => {
|
||||
return iconMap[label];
|
||||
};
|
||||
|
||||
// Navbar configuration
|
||||
export const NAVBAR_BUTTONS = [
|
||||
{
|
||||
tab: TAB_TYPES.SETTINGS,
|
||||
@@ -143,6 +151,7 @@ export const NAVBAR_BUTTONS = [
|
||||
},
|
||||
];
|
||||
|
||||
// Labels that should have dividers after them
|
||||
export const DIVIDER_LABELS = [
|
||||
'modals.main.settings.sections.weather.title',
|
||||
'modals.main.settings.sections.language.title',
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
@use 'modules/scrollbars' as *;
|
||||
@use 'settings/main' as *;
|
||||
@use 'marketplace/main' as *;
|
||||
|
||||
/* Fixed: Added sass:map module */
|
||||
// Fixed: Added sass:map module
|
||||
|
||||
.Overlay {
|
||||
position: fixed;
|
||||
@@ -20,16 +19,6 @@
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
|
||||
&.ReactModal__Overlay--after-open {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.ReactModal__Overlay--before-close {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.Modal {
|
||||
@@ -38,15 +27,14 @@
|
||||
}
|
||||
|
||||
box-shadow: 0 0 20px rgb(0 0 0 / 30%);
|
||||
opacity: 0;
|
||||
opacity: 1;
|
||||
z-index: -2;
|
||||
transition-timing-function: ease-in;
|
||||
border-radius: map.get($modal, 'border-radius');
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
transform: scale(0.95);
|
||||
transition: opacity 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94),
|
||||
transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
transform: scale(0);
|
||||
transition: all 0.3s cubic-bezier(0.47, 1.64, 0.41, 0.8);
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
@@ -60,7 +48,7 @@
|
||||
.closePositioning {
|
||||
position: absolute;
|
||||
top: 3rem;
|
||||
inset-inline-end: 3rem;
|
||||
right: 3rem;
|
||||
}
|
||||
|
||||
.closeModal {
|
||||
@@ -102,13 +90,14 @@
|
||||
|
||||
.ReactModal__Content--before-close {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
#modal {
|
||||
height: 80vh;
|
||||
width: clamp(60vw, 1400px, 90vw);
|
||||
backdrop-filter: blur(16px) saturate(180%);
|
||||
|
||||
-webkit-backdrop-filter: blur(16px) saturate(180%);
|
||||
backdrop-filter: blur(16px) saturate(180%);
|
||||
overflow: hidden;
|
||||
border-radius: 12px;
|
||||
@@ -280,7 +269,7 @@ h5 {
|
||||
@include themed {
|
||||
background: t($modal-sidebar);
|
||||
border-radius: t($borderRadius);
|
||||
box-shadow: 0 0 0 1px t($modal-border);
|
||||
box-shadow: 0 0 0 1px t($modal-sidebarActive);
|
||||
|
||||
&:hover {
|
||||
background: t($modal-sidebarActive);
|
||||
@@ -305,7 +294,6 @@ h5 {
|
||||
|
||||
padding: 15px;
|
||||
border-radius: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,51 +312,27 @@ h5 {
|
||||
|
||||
.reminder-info {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 10px 16px;
|
||||
flex-shrink: 0;
|
||||
flex-flow: column;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
padding: 15px;
|
||||
gap: 15px;
|
||||
|
||||
@include themed {
|
||||
background-color: t($modal-secondaryColour);
|
||||
border-bottom: 1px solid t($modal-sidebarActive);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
opacity: 0.7;
|
||||
flex: 1;
|
||||
border-radius: t($borderRadius);
|
||||
border: 1px solid t($modal-sidebarActive);
|
||||
}
|
||||
|
||||
button {
|
||||
@include basicIconButton(8px 14px, 0.875rem, modal);
|
||||
@include basicIconButton(5px, 5px, modal);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
justify-content: center;
|
||||
gap: 15px;
|
||||
|
||||
svg {
|
||||
margin: 0 !important;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.closeModal {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
opacity: 0.6;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// this file is too long
|
||||
@use 'modules/lightbox' as *;
|
||||
@use 'scss/variables' as *;
|
||||
|
||||
.items {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1.5rem;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 280px));
|
||||
grid-gap: 1.5rem;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 30px;
|
||||
|
||||
@@ -23,7 +24,7 @@
|
||||
background-size: cover;
|
||||
padding: 1.5rem;
|
||||
will-change: transform;
|
||||
transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0); // Force GPU acceleration
|
||||
|
||||
@include themed {
|
||||
background-color: t($modal-secondaryColour);
|
||||
@@ -45,10 +46,6 @@
|
||||
border-radius: 12px;
|
||||
transition: 0.5s;
|
||||
|
||||
@include themed {
|
||||
background-color: t($modal-sidebarActive);
|
||||
}
|
||||
|
||||
&.item-icon-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -69,7 +66,6 @@
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
|
||||
.card-title {
|
||||
font-size: 18px;
|
||||
@@ -114,17 +110,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.item-disabled {
|
||||
opacity: 0.5;
|
||||
|
||||
.card-title,
|
||||
.card-subtitle {
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-uninstall-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -133,7 +118,7 @@
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background-color: rgb(0 0 0 / 50%);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
@@ -143,7 +128,7 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgb(220 50 50 / 90%);
|
||||
background-color: rgba(220, 50, 50, 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,8 +142,8 @@
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgb(255 255 255 / 40%);
|
||||
box-shadow: 0 2px 6px rgb(0 0 0 / 30%);
|
||||
border: 2px solid rgba(255, 255, 255, 0.4);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
will-change: transform;
|
||||
@@ -176,7 +161,7 @@
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
background-color: rgb(100 100 100 / 90%);
|
||||
background-color: rgba(100, 100, 100, 0.9);
|
||||
cursor: help;
|
||||
|
||||
svg {
|
||||
@@ -199,13 +184,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.item-card-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 1.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.itemPage {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
@@ -229,7 +207,7 @@
|
||||
table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
overflow-wrap: break-word !important;
|
||||
word-wrap: break-word !important;
|
||||
font-size: 16px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
@@ -241,7 +219,7 @@
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
background-color: rgb(100 100 100 / 90%);
|
||||
background-color: rgba(100, 100, 100, 0.9);
|
||||
cursor: help;
|
||||
|
||||
svg {
|
||||
@@ -274,7 +252,7 @@
|
||||
.emptyMessage {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
gap: 5px;
|
||||
grid-gap: 5px;
|
||||
padding: 50px;
|
||||
|
||||
.title,
|
||||
@@ -305,7 +283,7 @@
|
||||
flex-flow: column;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
img {
|
||||
@@ -348,6 +326,7 @@ p.author {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.filter {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
@@ -395,7 +374,7 @@ p.author {
|
||||
margin-bottom: 15px;
|
||||
|
||||
.tooltip {
|
||||
margin-inline-end: 25px;
|
||||
margin-right: 25px;
|
||||
}
|
||||
|
||||
.mainTitle {
|
||||
@@ -427,7 +406,7 @@ p.author {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
@include themed {
|
||||
@@ -437,7 +416,7 @@ p.author {
|
||||
|
||||
&:hover {
|
||||
@include themed {
|
||||
background-color: rgb(255 255 255 / 15%);
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,105 +434,8 @@ p.author {
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid rgb(255 255 255 / 50%);
|
||||
outline: 2px solid rgba(255, 255, 255, 0.5);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.view-toggle-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
|
||||
.view-toggle-btn {
|
||||
all: unset;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
user-select: none;
|
||||
user-select: none;
|
||||
|
||||
@include themed {
|
||||
background-color: t($modal-sidebarActive);
|
||||
color: t($subColor);
|
||||
}
|
||||
|
||||
svg {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@include themed {
|
||||
background-color: rgb(255 255 255 / 15%);
|
||||
color: t($color);
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
@include themed {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@include themed {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid rgb(255 255 255 / 50%);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.items-list {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
grid-template-columns: unset !important;
|
||||
gap: 12px !important;
|
||||
|
||||
.item {
|
||||
flex-direction: row !important;
|
||||
align-items: center !important;
|
||||
padding: 1rem 1.5rem !important;
|
||||
gap: 20px !important;
|
||||
|
||||
&:hover {
|
||||
transform: translate3d(5px, 0, 0) !important;
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-details {
|
||||
flex: 1;
|
||||
flex-direction: row !important;
|
||||
align-items: center !important;
|
||||
justify-content: space-between;
|
||||
gap: 15px;
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.card-subtitle {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.card-chips {
|
||||
margin-top: 0 !important;
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
@use 'scss/variables' as *;
|
||||
|
||||
.btn-default {
|
||||
@include modal-button(standard);
|
||||
|
||||
padding: 0 20px;
|
||||
font-weight: 500;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.updateCheck {
|
||||
flex-flow: row !important;
|
||||
}
|
||||
@@ -22,11 +11,6 @@
|
||||
margin-top: 0;
|
||||
float: none !important;
|
||||
padding: 0 20px;
|
||||
font-weight: 500;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@@ -36,11 +20,6 @@
|
||||
margin-top: 0;
|
||||
float: none !important;
|
||||
padding: 0 20px;
|
||||
font-weight: 500;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-navigation {
|
||||
@@ -48,9 +27,7 @@
|
||||
|
||||
padding: 10px 20px;
|
||||
border-radius: 12px !important;
|
||||
transition:
|
||||
background 0.2s ease,
|
||||
transform 0.1s ease;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
|
||||
@include themed {
|
||||
@@ -71,22 +48,18 @@
|
||||
|
||||
&:hover {
|
||||
@include themed {
|
||||
background: rgb(0 0 0 / 4%) !important;
|
||||
background: rgba(0, 0, 0, 0.04) !important;
|
||||
}
|
||||
|
||||
.light & {
|
||||
background: rgb(0 0 0 / 4%) !important;
|
||||
background: rgba(0, 0, 0, 0.04) !important;
|
||||
}
|
||||
|
||||
.dark & {
|
||||
background: rgb(255 255 255 / 6%) !important;
|
||||
background: rgba(255, 255, 255, 0.06) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
span,
|
||||
svg {
|
||||
font-size: 1.1em !important;
|
||||
@@ -99,51 +72,6 @@
|
||||
color: t($color);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-badge {
|
||||
margin-inline-start: 3px;
|
||||
padding: 5px 7px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.75em !important;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 18px;
|
||||
|
||||
@include themed {
|
||||
background-color: t($modal-sidebarActive);
|
||||
color: t($color);
|
||||
}
|
||||
|
||||
.light & {
|
||||
background-color: rgb(0 0 0 / 8%);
|
||||
color: rgb(0 0 0 / 80%);
|
||||
}
|
||||
|
||||
.dark & {
|
||||
background-color: rgb(255 255 255 / 12%);
|
||||
color: rgb(255 255 255 / 90%);
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-navigation-active .btn-badge {
|
||||
@include themed {
|
||||
background-color: rgb(0 0 0 / 8%);
|
||||
color: rgb(0 0 0 / 80%);
|
||||
}
|
||||
|
||||
.light & {
|
||||
background-color: rgb(0 0 0 / 10%);
|
||||
color: rgb(0 0 0 / 85%);
|
||||
}
|
||||
|
||||
.dark & {
|
||||
background-color: rgb(255 255 255 / 15%);
|
||||
color: rgb(255 255 255 / 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* safari fix */
|
||||
@@ -155,17 +83,17 @@
|
||||
|
||||
.btn-navigation-active {
|
||||
@include themed {
|
||||
background: rgb(0 0 0 / 6%) !important;
|
||||
background: rgba(0, 0, 0, 0.06) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.light & {
|
||||
background: rgb(0 0 0 / 6%) !important;
|
||||
background: rgba(0, 0, 0, 0.06) !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.dark & {
|
||||
background: rgb(255 255 255 / 10%) !important;
|
||||
background: rgba(255, 255, 255, 0.1) !important;
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
@@ -184,11 +112,7 @@
|
||||
flex-flow: row;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
transition:
|
||||
background 0.2s ease,
|
||||
transform 0.1s ease,
|
||||
border 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
transition: 0.5s;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
@@ -201,15 +125,13 @@
|
||||
@include themed {
|
||||
background: t($modal-sidebarActive);
|
||||
box-shadow: 0 0 0 1px t($color);
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
&:focus {
|
||||
@include themed {
|
||||
background: t($modal-sidebarActive);
|
||||
box-shadow: 0 0 0 2px t($color);
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 1px t($color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +139,6 @@
|
||||
@include themed {
|
||||
background: t($modal-sidebarActive);
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,11 +162,6 @@ a.btn-collection {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
border-radius: 8px !important;
|
||||
padding: 0 !important;
|
||||
|
||||
@include modal-button(standard);
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +1,74 @@
|
||||
@use 'scss/variables' as *;
|
||||
|
||||
.modalTabContent {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
width: 100% !important;
|
||||
|
||||
/* button {
|
||||
@include modal-button(standard);
|
||||
} */
|
||||
|
||||
padding: 1rem 2rem 5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 2rem;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-background);
|
||||
border-radius: t($borderRadius);
|
||||
}
|
||||
|
||||
@extend %tabText;
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
background: rgb(196 196 196 / 74%);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.settingsRow {
|
||||
padding: 1rem 2rem 5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
transition: 0.4s ease-in-out;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
background: t($modal-background);
|
||||
|
||||
@include themed {
|
||||
border-bottom: 1px solid t($modal-border);
|
||||
margin: 0;
|
||||
border-radius: t($borderRadius);
|
||||
|
||||
@extend %tabText;
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
background: rgb(196 196 196 / 74%);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&.settingsNoBorder {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
.settingsRow {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
max-width: 50%;
|
||||
gap: 5px;
|
||||
}
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
transition: 0.4s ease-in-out;
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: flex-end;
|
||||
width: 300px;
|
||||
gap: 10px;
|
||||
/* border-top: 1px solid #ccc; */
|
||||
border-bottom: 1px solid #676767;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
|
||||
button {
|
||||
margin-top: 10px;
|
||||
&.settingsNoBorder {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.link {
|
||||
margin-top: 10px;
|
||||
&:last-child {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
flex-flow: column;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.action {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: flex-end;
|
||||
width: 300px;
|
||||
|
||||
button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.link {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +77,7 @@
|
||||
.resetDataButtonsLayout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
grid-gap: 20px;
|
||||
|
||||
:nth-child(1) {
|
||||
grid-column: span 2;
|
||||
@@ -251,13 +246,7 @@ table {
|
||||
}
|
||||
|
||||
.messageAction {
|
||||
[dir='ltr'] & {
|
||||
float: right;
|
||||
}
|
||||
|
||||
[dir='rtl'] & {
|
||||
float: left;
|
||||
}
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +270,7 @@ table {
|
||||
|
||||
hr {
|
||||
height: 100%;
|
||||
margin-inline-end: 5px;
|
||||
margin-right: 5px;
|
||||
|
||||
@include themed {
|
||||
border-color: t($modal-secondaryColour);
|
||||
@@ -296,7 +285,8 @@ table {
|
||||
transition: 0.5s;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
padding-inline: 10px 5px;
|
||||
padding-left: 10px;
|
||||
padding-right: 5px;
|
||||
|
||||
input[type='tel'],
|
||||
input[type='number'] {
|
||||
|
||||
@@ -4,87 +4,46 @@
|
||||
@include themed {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0.75rem 0.5rem;
|
||||
// padding: 1rem 1.5rem 4rem 1.5rem;
|
||||
padding: 0.5rem 0 0 0.5rem;
|
||||
background: t($modal-sidebar);
|
||||
border-start-start-radius: 12px;
|
||||
border-end-start-radius: 12px;
|
||||
border-start-end-radius: 0;
|
||||
border-end-end-radius: 0;
|
||||
overflow: hidden auto;
|
||||
border-radius: 12px 0 0 12px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
height: 100%;
|
||||
min-width: 250px;
|
||||
flex-shrink: 0;
|
||||
transition: min-width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
.sidebarHeader {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.search-input-container {
|
||||
flex: 1;
|
||||
width: auto;
|
||||
|
||||
.search-input-field {
|
||||
height: 38px;
|
||||
font-size: 14px;
|
||||
padding-inline: 42px 14px;
|
||||
padding-block: 0;
|
||||
border-radius: 10px;
|
||||
|
||||
&::placeholder {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.search-input-icon {
|
||||
inset-inline-start: 16px;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
width: 20px;
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
color: t($subColor);
|
||||
font-size: 20px;
|
||||
transition: color 0.2s ease;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
background: t($modal-sidebarActive);
|
||||
margin: 0.5rem 0.75rem;
|
||||
background: #ccc;
|
||||
margin: 0 1.75rem;
|
||||
border: none;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
button:not(.sidebarToggleButton) {
|
||||
button {
|
||||
color: t($color);
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
border-radius: 10px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin: 0.15rem 0.25rem;
|
||||
padding: 0.65rem 0.75rem;
|
||||
transition:
|
||||
background 0.2s ease,
|
||||
transform 0.1s ease;
|
||||
margin: 0.2rem;
|
||||
padding: 0.5rem;
|
||||
transition: 0.5s;
|
||||
outline: none;
|
||||
border: none;
|
||||
background: none;
|
||||
width: calc(100% - 0.5rem);
|
||||
text-align: start;
|
||||
position: relative;
|
||||
min-width: calc(100% - 1.2em);
|
||||
text-align: left;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 1rem;
|
||||
@@ -92,145 +51,21 @@
|
||||
|
||||
&:hover {
|
||||
background: t($modal-sidebarActive);
|
||||
|
||||
svg {
|
||||
color: t($color);
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: t($modal-sidebarActive);
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 0 0 0.5px t($color);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
&:focus {
|
||||
background: t($modal-sidebarActive);
|
||||
box-shadow: 0 0 0 2px t($color);
|
||||
box-shadow: 0 0 0 0.5px t($color);
|
||||
}
|
||||
|
||||
span {
|
||||
white-space: nowrap;
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
transition:
|
||||
opacity 0.25s ease,
|
||||
max-width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebarEmptyState {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: t($color);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.tab-list-active {
|
||||
background: t($modal-sidebarActive);
|
||||
position: relative;
|
||||
|
||||
svg {
|
||||
color: t($color);
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset-inline-start: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 3px;
|
||||
height: 60%;
|
||||
background: t($color);
|
||||
|
||||
[dir='ltr'] & {
|
||||
border-radius: 0 2px 2px 0;
|
||||
}
|
||||
|
||||
[dir='rtl'] & {
|
||||
border-radius: 2px 0 0 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
min-width: 64px;
|
||||
padding: 0.75rem 0.25rem;
|
||||
|
||||
.sidebarHeader {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
button:not(.sidebarToggleButton) {
|
||||
justify-content: center;
|
||||
padding: 0.65rem;
|
||||
gap: 0;
|
||||
|
||||
span {
|
||||
opacity: 0;
|
||||
max-width: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-list-active::before {
|
||||
inset-inline-start: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 60%;
|
||||
height: 3px;
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
border-radius: 2px 2px 0 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
opacity: 0;
|
||||
margin: 0.25rem 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebarToggleButton {
|
||||
@include themed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
color: t($subColor);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
outline: none;
|
||||
flex-shrink: 0;
|
||||
|
||||
svg {
|
||||
font-size: 18px;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: t($modal-sidebarActive);
|
||||
color: t($color);
|
||||
|
||||
svg {
|
||||
color: t($color);
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: t($modal-sidebarActive);
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
background: t($modal-sidebarActive);
|
||||
box-shadow: 0 0 0 2px t($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,39 +75,10 @@
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
// Sidebar skeleton loader
|
||||
.sidebarSkeleton {
|
||||
padding: 0.5rem 0.2rem;
|
||||
|
||||
.skeletonHeader {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.skeletonToggle {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
flex-shrink: 0;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-sidebarActive);
|
||||
}
|
||||
}
|
||||
|
||||
.skeletonSearch {
|
||||
flex: 1;
|
||||
height: 38px;
|
||||
border-radius: 10px;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-sidebarActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.skeletonItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -284,7 +90,8 @@
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
border-radius: 6px;
|
||||
margin-inline: 20px;
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
flex-shrink: 0;
|
||||
|
||||
@include themed {
|
||||
|
||||
@@ -7,11 +7,10 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1.5rem;
|
||||
|
||||
padding: 1.5rem 1.5rem;
|
||||
// width: 100%;
|
||||
|
||||
backdrop-filter: blur(16px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(16px) saturate(180%);
|
||||
backdrop-filter: blur(16px) saturate(180%);
|
||||
|
||||
@include themed {
|
||||
@@ -75,7 +74,6 @@
|
||||
.breadcrumb-segment {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
// gap: 0.5rem;
|
||||
}
|
||||
|
||||
@@ -83,7 +81,6 @@
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
}
|
||||
|
||||
white-space: nowrap;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
@@ -101,7 +98,6 @@
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
}
|
||||
|
||||
opacity: 0.5;
|
||||
font-size: 1.2rem;
|
||||
margin: 0 0.25rem;
|
||||
@@ -111,7 +107,6 @@
|
||||
@include themed {
|
||||
color: t($color);
|
||||
}
|
||||
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,48 @@
|
||||
@use 'modules/tabs/stats' as *;
|
||||
|
||||
input {
|
||||
/* colour picker */
|
||||
&[type='color'] {
|
||||
border-radius: 100%;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
border: none;
|
||||
outline: none;
|
||||
appearance: none;
|
||||
vertical-align: middle;
|
||||
background: none;
|
||||
|
||||
@include themed {
|
||||
border: t($modal-sidebarActive) 1px solid;
|
||||
}
|
||||
|
||||
&::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&::-webkit-color-swatch {
|
||||
border: none;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* firefox fixes for colour picker (using "," didn't work) */
|
||||
&[type='color']::-moz-color-swatch {
|
||||
border-radius: 100%;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
border: none;
|
||||
outline: none;
|
||||
appearance: none;
|
||||
vertical-align: middle;
|
||||
background: none;
|
||||
|
||||
&::-moz-color-swatch {
|
||||
border: none;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* date picker */
|
||||
&[type='date'] {
|
||||
width: 260px;
|
||||
@@ -53,7 +95,7 @@ h4 {
|
||||
position: sticky;
|
||||
top: -20px;
|
||||
z-index: 90;
|
||||
padding: 25px 0 15px;
|
||||
padding: 25px 0 15px 0;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
justify-content: space-between;
|
||||
@@ -169,7 +211,7 @@ h4 {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin-inline-start: 5px;
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
font-size: 14px;
|
||||
@@ -188,7 +230,7 @@ h4 {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin-inline-start: 5px;
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
font-size: 14px;
|
||||
@@ -231,7 +273,7 @@ h4 {
|
||||
transition: 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
/* Warning banner (used in Search settings and potentially others) */
|
||||
// Warning banner (used in Search settings and potentially others)
|
||||
.itemWarning {
|
||||
padding: 10px 20px;
|
||||
display: flex;
|
||||
|
||||
@@ -18,7 +18,7 @@ legend {
|
||||
}
|
||||
|
||||
.MuiFormControlLabel-labelPlacementStart {
|
||||
margin-inline-start: 0 !important;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
.MuiSwitch-colorPrimary.Mui-checked + .MuiSwitch-track {
|
||||
@@ -152,10 +152,11 @@ legend,
|
||||
.settingsRow {
|
||||
.MuiFormControlLabel-root {
|
||||
flex-direction: row-reverse;
|
||||
margin-inline: 0;
|
||||
margin-right: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.MuiFormControlLabel-root {
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
padding: 20px;
|
||||
gap: 20px;
|
||||
grid-gap: 20px;
|
||||
|
||||
@include themed {
|
||||
div {
|
||||
@@ -95,16 +95,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced custom images grid
|
||||
.images-grid {
|
||||
display: grid;
|
||||
padding: 1px;
|
||||
|
||||
// Show all checkboxes when in selection mode (any image selected)
|
||||
&.selection-mode {
|
||||
.image-checkbox {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: 20px;
|
||||
|
||||
@@ -125,8 +126,7 @@
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
|
||||
// box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
|
||||
|
||||
.image-nav-buttons {
|
||||
opacity: 1;
|
||||
@@ -156,11 +156,11 @@
|
||||
appearance: none;
|
||||
border: 2px solid #fff;
|
||||
border-radius: 4px;
|
||||
background: rgb(0 0 0 / 60%);
|
||||
backdrop-filter: blur(4px);
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
-webkit-backdrop-filter: blur(4px);
|
||||
backdrop-filter: blur(4px);
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 30%);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
position: relative;
|
||||
|
||||
&:checked {
|
||||
@@ -187,6 +187,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Keep checkbox visible when checked
|
||||
&:has(input:checked) {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -243,8 +244,8 @@
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: rgb(0 0 0 / 60%);
|
||||
backdrop-filter: blur(8px);
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
backdrop-filter: blur(8px);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
@@ -258,7 +259,7 @@
|
||||
}
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: rgb(0 0 0 / 80%);
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
@@ -313,7 +314,7 @@
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: rgb(255 71 87 / 90%);
|
||||
background: rgba(255, 71, 87, 0.9);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -322,7 +323,7 @@
|
||||
opacity: 0;
|
||||
transition: all 0.2s;
|
||||
z-index: 11;
|
||||
box-shadow: 0 2px 8px rgb(0 0 0 / 30%);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
|
||||
svg {
|
||||
font-size: 20px;
|
||||
@@ -334,6 +335,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Show delete button when card is hovered or has checkbox visible
|
||||
&:hover .delete-button,
|
||||
.image-checkbox:has(input:checked) ~ * .delete-button {
|
||||
opacity: 1;
|
||||
@@ -342,6 +344,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Storage quota display
|
||||
.storage-quota {
|
||||
padding: 15px 20px;
|
||||
margin-top: 10px;
|
||||
@@ -410,6 +413,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Folder tagging modal styles
|
||||
.taggingModalContent {
|
||||
padding: 20px;
|
||||
|
||||
@@ -447,7 +451,7 @@
|
||||
|
||||
&:focus {
|
||||
border-color: #ff5c25;
|
||||
box-shadow: 0 0 0 3px rgb(255 92 37 / 10%);
|
||||
box-shadow: 0 0 0 3px rgba(255, 92, 37, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -501,7 +505,7 @@
|
||||
.overviewGrid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 30px;
|
||||
grid-gap: 30px;
|
||||
}
|
||||
|
||||
.tabPreview {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
.statGrid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 10px;
|
||||
grid-gap: 10px;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
@@ -43,7 +43,7 @@
|
||||
.achievementsGrid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 10px;
|
||||
grid-gap: 10px;
|
||||
}
|
||||
|
||||
.achievement {
|
||||
@@ -134,6 +134,7 @@
|
||||
button {
|
||||
margin-bottom: 15px;
|
||||
flex-flow: row !important;
|
||||
padding-inline: 20px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { memo } from 'react';
|
||||
import { useT } from 'contexts';
|
||||
import variables from 'config/variables';
|
||||
import { MdClose, MdRestartAlt } from 'react-icons/md';
|
||||
import { setDefaultSettings } from 'utils/settings';
|
||||
import { Tooltip, Button } from 'components/Elements';
|
||||
|
||||
function ResetModal({ modalClose }) {
|
||||
const t = useT();
|
||||
const reset = () => {
|
||||
variables.stats.postEvent('setting', 'Reset');
|
||||
setDefaultSettings('reset');
|
||||
@@ -17,32 +15,34 @@ function ResetModal({ modalClose }) {
|
||||
<div className="smallModal">
|
||||
<div className="shareHeader">
|
||||
<span className="title">
|
||||
{t('modals.main.settings.sections.advanced.reset_modal.title')}
|
||||
{variables.getMessage('modals.main.settings.sections.advanced.reset_modal.title')}
|
||||
</span>
|
||||
<Tooltip title={t('modals.main.settings.sections.advanced.reset_modal.cancel')}>
|
||||
<Tooltip
|
||||
title={variables.getMessage('modals.main.settings.sections.advanced.reset_modal.cancel')}
|
||||
>
|
||||
<div className="close" onClick={modalClose}>
|
||||
<MdClose />
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<span className="title">
|
||||
{t('modals.main.settings.sections.advanced.reset_modal.question')}
|
||||
{variables.getMessage('modals.main.settings.sections.advanced.reset_modal.question')}
|
||||
</span>
|
||||
<span className="subtitle">
|
||||
{t('modals.main.settings.sections.advanced.reset_modal.information')}
|
||||
{variables.getMessage('modals.main.settings.sections.advanced.reset_modal.information')}
|
||||
</span>
|
||||
<div className="resetFooter">
|
||||
<Button
|
||||
type="secondary"
|
||||
onClick={modalClose}
|
||||
icon={<MdClose />}
|
||||
label={t('modals.main.settings.sections.advanced.reset_modal.cancel')}
|
||||
label={variables.getMessage('modals.main.settings.sections.advanced.reset_modal.cancel')}
|
||||
/>
|
||||
<Button
|
||||
type="settings"
|
||||
onClick={() => reset()}
|
||||
icon={<MdRestartAlt />}
|
||||
label={t('modals.main.settings.buttons.reset')}
|
||||
label={variables.getMessage('modals.main.settings.buttons.reset')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { memo } from 'react';
|
||||
import { useT } from 'contexts';
|
||||
import variables from 'config/variables';
|
||||
import { MdClose, MdEmail, MdContentCopy } from 'react-icons/md';
|
||||
import { FaFacebookF } from 'react-icons/fa';
|
||||
import { AiFillWechat } from 'react-icons/ai';
|
||||
@@ -12,34 +12,37 @@ import { Button } from 'components/Elements';
|
||||
import './sharemodal.scss';
|
||||
|
||||
function ShareModal({ modalClose, data }) {
|
||||
const t = useT();
|
||||
if (data.startsWith('https://cdn.')) {
|
||||
data = {
|
||||
url: data,
|
||||
name: t('modals.share.item_type.image'),
|
||||
name: 'this image',
|
||||
};
|
||||
} else if (data.startsWith('"')) {
|
||||
data = {
|
||||
url: data,
|
||||
name: t('modals.share.item_type.quote'),
|
||||
name: 'this quote',
|
||||
};
|
||||
} else {
|
||||
data = {
|
||||
url: data,
|
||||
name: t('modals.share.item_type.marketplace_item'),
|
||||
name: 'this marketplace item',
|
||||
};
|
||||
}
|
||||
|
||||
const copyLink = () => {
|
||||
navigator.clipboard.writeText(data.url);
|
||||
toast(data.startsWith('"') ? t('toasts.quote') : t('toasts.link_copied'));
|
||||
toast(
|
||||
data.startsWith('"')
|
||||
? variables.getMessage('toasts.quote')
|
||||
: variables.getMessage('toasts.link_copied'),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="smallModal">
|
||||
<div className="shareHeader">
|
||||
<span className="title">{t('widgets.quote.share')}</span>
|
||||
<Tooltip title={t('modals.welcome.buttons.close')}>
|
||||
<span className="title">{variables.getMessage('widgets.quote.share')}</span>
|
||||
<Tooltip title={variables.getMessage('modals.welcome.buttons.close')}>
|
||||
<div className="close" onClick={modalClose}>
|
||||
<MdClose />
|
||||
</div>
|
||||
@@ -50,13 +53,13 @@ function ShareModal({ modalClose, data }) {
|
||||
onClick={() =>
|
||||
window
|
||||
.open(
|
||||
`https://x.com/intent/tweet?text=${t('modals.share.twitter_message', { name: data.name })}: ${data.url}`,
|
||||
`https://x.com/intent/tweet?text=Check out ${data.name} on @getmue: ${data.url}`,
|
||||
'_blank',
|
||||
)
|
||||
.focus()
|
||||
}
|
||||
icon={<SiX />}
|
||||
tooltipTitle={t('modals.share.social.twitter')}
|
||||
tooltipTitle="X (Twitter)"
|
||||
type="icon"
|
||||
/>
|
||||
<Button
|
||||
@@ -66,20 +69,23 @@ function ShareModal({ modalClose, data }) {
|
||||
.focus()
|
||||
}
|
||||
icon={<FaFacebookF />}
|
||||
tooltipTitle={t('modals.share.social.facebook')}
|
||||
tooltipTitle="Facebook"
|
||||
type="icon"
|
||||
/>
|
||||
<Button
|
||||
onClick={() =>
|
||||
window
|
||||
.open(
|
||||
`mailto:email@example.com?subject=${encodeURIComponent(t('modals.share.email_subject'))}&body=${encodeURIComponent(t('modals.share.email_body', { name: data.name, url: data.url }))}`,
|
||||
'mailto:email@example.com?subject=Check%20out%20this%20%on%20%Mue!&body=' +
|
||||
data.name +
|
||||
'on Mue: ' +
|
||||
data.url,
|
||||
'_blank',
|
||||
)
|
||||
.focus()
|
||||
}
|
||||
icon={<MdEmail />}
|
||||
tooltipTitle={t('modals.share.social.email')}
|
||||
tooltipTitle="Email"
|
||||
type="icon"
|
||||
/>
|
||||
<Button
|
||||
@@ -92,7 +98,7 @@ function ShareModal({ modalClose, data }) {
|
||||
.focus()
|
||||
}
|
||||
icon={<AiFillWechat />}
|
||||
tooltipTitle={t('modals.share.social.wechat')}
|
||||
tooltipTitle="WeChat"
|
||||
type="icon"
|
||||
/>
|
||||
<Button
|
||||
@@ -102,7 +108,7 @@ function ShareModal({ modalClose, data }) {
|
||||
.focus()
|
||||
}
|
||||
icon={<SiTencentqq />}
|
||||
tooltipTitle={t('modals.share.social.qq')}
|
||||
tooltipTitle="Tencent QQ"
|
||||
type="icon"
|
||||
/>
|
||||
</div>
|
||||
@@ -111,7 +117,7 @@ function ShareModal({ modalClose, data }) {
|
||||
<Button
|
||||
onClick={() => copyLink()}
|
||||
icon={<MdContentCopy />}
|
||||
tooltipTitle={t('modals.share.copy_link')}
|
||||
tooltipTitle={variables.getMessage('modals.share.copy_link')}
|
||||
type="icon"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { IconService } from 'utils/quicklinks';
|
||||
import './smarticon.scss';
|
||||
|
||||
export const SmartIcon = ({ item, size = 32, fallbackChain, className = '' }) => {
|
||||
const [currentIconIndex, setCurrentIconIndex] = useState(0);
|
||||
const [iconUrls, setIconUrls] = useState([]);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
const [showPlaceholder, setShowPlaceholder] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const urls = IconService.getIconUrl(item, fallbackChain);
|
||||
setIconUrls(Array.isArray(urls) ? urls : [urls]);
|
||||
setCurrentIconIndex(0);
|
||||
setHasError(false);
|
||||
setShowPlaceholder(false);
|
||||
}, [item, fallbackChain]);
|
||||
|
||||
const handleImageError = () => {
|
||||
if (currentIconIndex < iconUrls.length - 1) {
|
||||
setCurrentIconIndex((prev) => prev + 1);
|
||||
setHasError(false);
|
||||
} else {
|
||||
setHasError(true);
|
||||
setShowPlaceholder(true);
|
||||
}
|
||||
};
|
||||
|
||||
if (item.iconType === 'emoji' && item.icon) {
|
||||
return (
|
||||
<div className={`smart-icon emoji ${className}`} style={{ fontSize: size }}>
|
||||
{item.icon}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (item.iconType === 'letter' || showPlaceholder) {
|
||||
const avatar = IconService.generateLetterAvatar(item.name);
|
||||
return (
|
||||
<div
|
||||
className={`smart-icon letter-avatar ${className}`}
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
backgroundColor: avatar.backgroundColor,
|
||||
fontSize: size * 0.5,
|
||||
}}
|
||||
>
|
||||
{avatar.letter}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const currentUrl = iconUrls[currentIconIndex];
|
||||
if (!currentUrl) {
|
||||
const avatar = IconService.generateLetterAvatar(item.name);
|
||||
return (
|
||||
<div
|
||||
className={`smart-icon letter-avatar ${className}`}
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
backgroundColor: avatar.backgroundColor,
|
||||
fontSize: size * 0.5,
|
||||
}}
|
||||
>
|
||||
{avatar.letter}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<img
|
||||
src={currentUrl}
|
||||
alt={item.name}
|
||||
className={`smart-icon ${className}`}
|
||||
style={{ width: size, height: size }}
|
||||
onError={handleImageError}
|
||||
draggable={false}
|
||||
loading="lazy"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export { SmartIcon } from './SmartIcon';
|
||||
@@ -1,26 +0,0 @@
|
||||
.smart-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
flex-shrink: 0;
|
||||
|
||||
&.emoji {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&.letter-avatar {
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
text-transform: uppercase;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
img {
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useState, memo, useRef, useId } from 'react';
|
||||
import { useState, memo, useRef } from 'react';
|
||||
import { useFloating, flip, offset, shift } from '@floating-ui/react-dom';
|
||||
import './tooltip.scss';
|
||||
|
||||
function Tooltip({ children, title, style, placement, subtitle }) {
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
const [closing, setClosing] = useState(false);
|
||||
const [isClosing, setIsClosing] = useState(false);
|
||||
const [reference, setReference] = useState(null);
|
||||
const tooltipId = useId();
|
||||
const tooltipId = useRef(`tooltip-${Math.random()}`);
|
||||
const closeTimeout = useRef(null);
|
||||
|
||||
const {
|
||||
@@ -23,23 +23,23 @@ function Tooltip({ children, title, style, placement, subtitle }) {
|
||||
},
|
||||
});
|
||||
|
||||
const { setFloating } = refs;
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
// Clear any pending close timeout if mouse re-enters during exit
|
||||
if (closeTimeout.current) {
|
||||
clearTimeout(closeTimeout.current);
|
||||
closeTimeout.current = null;
|
||||
}
|
||||
setClosing(false);
|
||||
setIsClosing(false);
|
||||
setShowTooltip(true);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setClosing(true);
|
||||
setIsClosing(true);
|
||||
// Wait for exit animation to complete before unmounting
|
||||
closeTimeout.current = setTimeout(() => {
|
||||
setShowTooltip(false);
|
||||
setClosing(false);
|
||||
}, 200);
|
||||
setIsClosing(false);
|
||||
}, 200); // Match exit animation duration
|
||||
};
|
||||
|
||||
const handleFocus = () => {
|
||||
@@ -47,25 +47,22 @@ function Tooltip({ children, title, style, placement, subtitle }) {
|
||||
clearTimeout(closeTimeout.current);
|
||||
closeTimeout.current = null;
|
||||
}
|
||||
setClosing(false);
|
||||
setIsClosing(false);
|
||||
setShowTooltip(true);
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
setClosing(true);
|
||||
setIsClosing(true);
|
||||
closeTimeout.current = setTimeout(() => {
|
||||
setShowTooltip(false);
|
||||
setClosing(false);
|
||||
setIsClosing(false);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
// Determine the data-status attribute value
|
||||
const getStatus = () => {
|
||||
if (!showTooltip && !closing) {
|
||||
return 'initial';
|
||||
}
|
||||
if (closing) {
|
||||
return 'close';
|
||||
}
|
||||
if (!showTooltip && !isClosing) return 'initial';
|
||||
if (isClosing) return 'close';
|
||||
return 'open';
|
||||
};
|
||||
|
||||
@@ -79,13 +76,13 @@ function Tooltip({ children, title, style, placement, subtitle }) {
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
ref={setReference}
|
||||
aria-describedby={tooltipId}
|
||||
aria-describedby={tooltipId.current}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
{(showTooltip || closing) && (
|
||||
{(showTooltip || isClosing) && (
|
||||
<span
|
||||
ref={setFloating}
|
||||
ref={refs.setFloating}
|
||||
style={{
|
||||
position: strategy,
|
||||
top: y ?? '',
|
||||
|
||||
@@ -5,54 +5,6 @@
|
||||
display: grid;
|
||||
}
|
||||
|
||||
@keyframes floatingFromTop {
|
||||
0% {
|
||||
transform: translateY(5px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes floatingFromBottom {
|
||||
0% {
|
||||
transform: translateY(-5px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes floatingFromLeft {
|
||||
0% {
|
||||
transform: translateX(5px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes floatingFromRight {
|
||||
0% {
|
||||
transform: translateX(-5px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes floating {
|
||||
0% {
|
||||
transform: translate(0, -5px);
|
||||
@@ -85,36 +37,21 @@
|
||||
opacity 0.2s ease-out,
|
||||
transform 0.2s ease-out;
|
||||
|
||||
/* Initial state (not yet shown) */
|
||||
// Initial state (not yet shown)
|
||||
&[data-status='initial'] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Open state - entrance animation */
|
||||
// Open state - entrance animation
|
||||
&[data-status='open'] {
|
||||
opacity: 1;
|
||||
transform: translate(0, 0);
|
||||
animation-name: floating;
|
||||
animation-duration: 0.3s;
|
||||
animation-timing-function: ease-in;
|
||||
|
||||
&[data-placement^='top'] {
|
||||
animation-name: floatingFromTop;
|
||||
}
|
||||
|
||||
&[data-placement^='bottom'] {
|
||||
animation-name: floatingFromBottom;
|
||||
}
|
||||
|
||||
&[data-placement^='left'] {
|
||||
animation-name: floatingFromLeft;
|
||||
}
|
||||
|
||||
&[data-placement^='right'] {
|
||||
animation-name: floatingFromRight;
|
||||
}
|
||||
}
|
||||
|
||||
/* Closing state - exit animation */
|
||||
// Closing state - exit animation
|
||||
&[data-status='close'] {
|
||||
opacity: 0;
|
||||
|
||||
|
||||
@@ -18,28 +18,27 @@ const Checkbox = memo((props) => {
|
||||
props.onChange(value);
|
||||
}
|
||||
|
||||
variables.stats.postEvent('setting', `${props.name} ${value ? 'enabled' : 'disabled'}`);
|
||||
variables.stats.postEvent(
|
||||
'setting',
|
||||
`${props.name} ${value ? 'enabled' : 'disabled'}`,
|
||||
);
|
||||
|
||||
if (props.element) {
|
||||
if (!document.querySelector(props.element)) {
|
||||
localStorage.setItem('showReminder', 'true');
|
||||
EventBus.emit('showReminder');
|
||||
return;
|
||||
document.querySelector('.reminder-info').style.display = 'flex';
|
||||
return localStorage.setItem('showReminder', true);
|
||||
}
|
||||
}
|
||||
|
||||
EventBus.emit('refresh', props.category);
|
||||
}, [checked, props]);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e) => {
|
||||
if ((e.key === ' ' || e.key === 'Enter') && !props.disabled) {
|
||||
e.preventDefault();
|
||||
handleChange();
|
||||
}
|
||||
},
|
||||
[handleChange, props.disabled],
|
||||
);
|
||||
const handleKeyDown = useCallback((e) => {
|
||||
if ((e.key === ' ' || e.key === 'Enter') && !props.disabled) {
|
||||
e.preventDefault();
|
||||
handleChange();
|
||||
}
|
||||
}, [handleChange, props.disabled]);
|
||||
|
||||
return (
|
||||
<div className={`checkbox-wrapper ${props.disabled ? 'disabled' : ''}`}>
|
||||
@@ -54,7 +53,9 @@ const Checkbox = memo((props) => {
|
||||
aria-label={props.text}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
<div className={`checkbox-box ${checked ? 'checked' : ''}`}>{checked && <MdCheck />}</div>
|
||||
<div className={`checkbox-box ${checked ? 'checked' : ''}`}>
|
||||
{checked && <MdCheck />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
flex: 1;
|
||||
transition: color 0.2s ease;
|
||||
pointer-events: none;
|
||||
font-size: 1rem;
|
||||
|
||||
@include themed {
|
||||
color: t($color);
|
||||
@@ -75,7 +74,7 @@
|
||||
transform: scale(0.95);
|
||||
|
||||
@include themed {
|
||||
box-shadow: 0 0 0 4px rgb(255 92 37 / 10%);
|
||||
box-shadow: 0 0 0 4px rgba(255, 92, 37, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +90,7 @@
|
||||
}
|
||||
|
||||
svg {
|
||||
font-size: 1.125rem;
|
||||
font-size: 18px;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +1,28 @@
|
||||
import { useState, memo, useRef, useEffect, useCallback } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { MdExpandMore, MdClose, MdCheck } from 'react-icons/md';
|
||||
import { useState, memo, useRef, useEffect } from 'react';
|
||||
import { MdExpandMore, MdClose } from 'react-icons/md';
|
||||
|
||||
import './ChipSelect.scss';
|
||||
|
||||
function ChipSelect({ label, options, onChange, name }) {
|
||||
const storageKey = name || 'apiCategories';
|
||||
let start = (localStorage.getItem(storageKey) || '').split(',');
|
||||
function ChipSelect({ label, options, onChange }) {
|
||||
let start = (localStorage.getItem('apiCategories') || '').split(',');
|
||||
if (start[0] === '') {
|
||||
start = [];
|
||||
}
|
||||
|
||||
const [optionsSelected, setOptionsSelected] = useState(start);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [closing, setClosing] = useState(false);
|
||||
const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0, width: 0 });
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const containerRef = useRef(null);
|
||||
const controlRef = useRef(null);
|
||||
const menuRef = useRef(null);
|
||||
|
||||
const closeDropdown = useCallback(() => {
|
||||
setClosing(true);
|
||||
setTimeout(() => {
|
||||
setOpen(false);
|
||||
setClosing(false);
|
||||
}, 200);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event) => {
|
||||
// Ignore clicks on color inputs to prevent closing when native color picker opens
|
||||
if (event.target.type === 'color') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Also ignore clicks on color input labels and wrappers
|
||||
const target = event.target;
|
||||
if (target.tagName === 'LABEL' && target.htmlFor) {
|
||||
const associatedInput = document.getElementById(target.htmlFor) || document.querySelector(`input[name="${target.htmlFor}"]`);
|
||||
if (associatedInput && associatedInput.type === 'color') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if clicking within a color input wrapper
|
||||
const colorInputWrapper = target.closest('.colour-picker');
|
||||
if (colorInputWrapper && colorInputWrapper.querySelector('input[type="color"]')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
containerRef.current &&
|
||||
!containerRef.current.contains(event.target) &&
|
||||
menuRef.current &&
|
||||
!menuRef.current.contains(event.target)
|
||||
) {
|
||||
closeDropdown();
|
||||
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, [closeDropdown]);
|
||||
|
||||
const calculatePosition = useCallback(() => {
|
||||
if (controlRef.current) {
|
||||
const rect = controlRef.current.getBoundingClientRect();
|
||||
const gap = 4;
|
||||
const viewportHeight = window.innerHeight;
|
||||
|
||||
const estimatedMenuHeight = Math.min(options.length * 44, 250);
|
||||
|
||||
const spaceBelow = viewportHeight - rect.bottom - gap;
|
||||
const spaceAbove = rect.top - gap;
|
||||
|
||||
const shouldFlipUp = spaceBelow < estimatedMenuHeight && spaceAbove > spaceBelow;
|
||||
|
||||
return {
|
||||
top: shouldFlipUp ? rect.top - gap : rect.bottom + gap,
|
||||
left: rect.left,
|
||||
width: rect.width,
|
||||
maxHeight: shouldFlipUp ? Math.min(250, spaceAbove) : Math.min(250, spaceBelow),
|
||||
flipped: shouldFlipUp,
|
||||
};
|
||||
}
|
||||
return { top: 0, left: 0, width: 0, maxHeight: 250, flipped: false };
|
||||
}, [options]);
|
||||
|
||||
const openDropdown = useCallback(() => {
|
||||
const position = calculatePosition();
|
||||
setMenuPosition(position);
|
||||
setOpen(true);
|
||||
}, [calculatePosition]);
|
||||
}, []);
|
||||
|
||||
const handleToggle = (optionName) => {
|
||||
let newSelected;
|
||||
@@ -102,8 +33,7 @@ function ChipSelect({ label, options, onChange, name }) {
|
||||
}
|
||||
|
||||
setOptionsSelected(newSelected);
|
||||
const storageKey = name || 'apiCategories';
|
||||
localStorage.setItem(storageKey, newSelected.join(','));
|
||||
localStorage.setItem('apiCategories', newSelected.join(','));
|
||||
|
||||
if (onChange) {
|
||||
onChange(newSelected);
|
||||
@@ -118,17 +48,7 @@ function ChipSelect({ label, options, onChange, name }) {
|
||||
return (
|
||||
<div className="chipSelect" ref={containerRef}>
|
||||
{label && <label className="chipSelect-label">{label}</label>}
|
||||
<div
|
||||
ref={controlRef}
|
||||
className="chipSelect-control"
|
||||
onClick={() => {
|
||||
if (open) {
|
||||
closeDropdown();
|
||||
} else {
|
||||
openDropdown();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="chipSelect-control" onClick={() => setIsOpen(!isOpen)}>
|
||||
<div className="chipSelect-value">
|
||||
{optionsSelected.length === 0 ? (
|
||||
<span className="chipSelect-placeholder">Select options...</span>
|
||||
@@ -149,40 +69,27 @@ function ChipSelect({ label, options, onChange, name }) {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<MdExpandMore className={`chipSelect-arrow ${open ? 'open' : ''}`} />
|
||||
<MdExpandMore className={`chipSelect-arrow ${isOpen ? 'open' : ''}`} />
|
||||
</div>
|
||||
{(open || closing) &&
|
||||
createPortal(
|
||||
<div
|
||||
ref={menuRef}
|
||||
className={`chipSelect-dropdown ${closing ? 'closing' : ''} ${menuPosition.flipped ? 'flipped' : ''}`}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: `${menuPosition.top}px`,
|
||||
left: `${menuPosition.left}px`,
|
||||
width: `${menuPosition.width}px`,
|
||||
maxHeight: menuPosition.maxHeight ? `${menuPosition.maxHeight}px` : '250px',
|
||||
transform: menuPosition.flipped ? 'translateY(-100%)' : 'none',
|
||||
}}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<div
|
||||
key={option.name}
|
||||
className={`chipSelect-option ${optionsSelected.includes(option.name) ? 'selected' : ''}`}
|
||||
onClick={() => handleToggle(option.name)}
|
||||
>
|
||||
<div className="chipSelect-option-checkbox">
|
||||
{optionsSelected.includes(option.name) && <MdCheck />}
|
||||
</div>
|
||||
<span className="chipSelect-option-label">
|
||||
{option.name.charAt(0).toUpperCase() + option.name.slice(1)}
|
||||
{option.count && ` (${option.count})`}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>,
|
||||
document.body,
|
||||
)}
|
||||
{isOpen && (
|
||||
<div className="chipSelect-dropdown">
|
||||
{options.map((option) => (
|
||||
<div
|
||||
key={option.name}
|
||||
className={`chipSelect-option ${optionsSelected.includes(option.name) ? 'selected' : ''}`}
|
||||
onClick={() => handleToggle(option.name)}
|
||||
>
|
||||
<span className="chipSelect-option-checkbox">
|
||||
{optionsSelected.includes(option.name) && '✓'}
|
||||
</span>
|
||||
<span className="chipSelect-option-label">
|
||||
{option.name.charAt(0).toUpperCase() + option.name.slice(1)}
|
||||
{option.count && ` (${option.count})`}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,64 +1,14 @@
|
||||
@use 'scss/variables' as *;
|
||||
@use 'scss/mixins' as *;
|
||||
|
||||
@include keyframes(chipSelectSlideIn) {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@include keyframes(chipSelectSlideOut) {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
@include keyframes(chipSelectSlideInUp) {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-100%) translateY(10px);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
@include keyframes(chipSelectSlideOutUp) {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateY(-100%) translateY(10px);
|
||||
}
|
||||
}
|
||||
|
||||
.chipSelect {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
margin-top: 10px;
|
||||
gap: 8px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
.chipSelect-label {
|
||||
font-size: 0.75rem;
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
@@ -73,10 +23,9 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-height: 56px;
|
||||
padding: 0 16px;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
outline: none;
|
||||
transition: 0.2s ease;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-sidebar);
|
||||
@@ -93,7 +42,6 @@
|
||||
.chipSelect-value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.chipSelect-placeholder {
|
||||
@@ -111,11 +59,10 @@
|
||||
.chipSelect-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 10px;
|
||||
font-size: 0.8125rem;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
font-size: 13px;
|
||||
text-transform: capitalize;
|
||||
transition: all 0.15s ease;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-sidebarActive);
|
||||
@@ -127,34 +74,32 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
margin-left: 2px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
transition: all 0.15s ease;
|
||||
transition: 0.2s ease;
|
||||
|
||||
@include themed {
|
||||
color: t($subColor);
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: t($color);
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
font-size: 0.75rem;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chipSelect-arrow {
|
||||
flex-shrink: 0;
|
||||
font-size: 1.5rem;
|
||||
font-size: 24px;
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
@include themed {
|
||||
@@ -165,108 +110,69 @@
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chipSelect-dropdown {
|
||||
max-height: 250px;
|
||||
overflow-y: auto;
|
||||
z-index: 9999;
|
||||
@include animation(chipSelectSlideIn 0.2s ease-out);
|
||||
will-change: transform, opacity;
|
||||
.chipSelect-dropdown {
|
||||
position: absolute;
|
||||
top: calc(100% + 4px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-height: 250px;
|
||||
overflow-y: auto;
|
||||
z-index: 100;
|
||||
|
||||
@include themed {
|
||||
background: t($modal-background);
|
||||
border: 1px solid t($modal-sidebarActive);
|
||||
border-radius: t($borderRadius);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
&.flipped {
|
||||
@include animation(chipSelectSlideInUp 0.2s ease-out);
|
||||
|
||||
&.closing {
|
||||
@include animation(chipSelectSlideOutUp 0.2s ease-out forwards);
|
||||
}
|
||||
}
|
||||
|
||||
&.closing:not(.flipped) {
|
||||
@include animation(chipSelectSlideOut 0.2s ease-out forwards);
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
@include themed {
|
||||
background: t($modal-sidebar);
|
||||
background: t($modal-background);
|
||||
border: 1px solid t($modal-sidebarActive);
|
||||
border-radius: t($borderRadius);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
@include themed {
|
||||
background: t($modal-sidebarActive);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@include themed {
|
||||
background: t($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chipSelect-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
outline: none;
|
||||
|
||||
@include themed {
|
||||
color: t($color);
|
||||
|
||||
&:hover {
|
||||
background: t($modal-sidebarActive);
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: t($modal-sidebar);
|
||||
}
|
||||
}
|
||||
|
||||
.chipSelect-option-checkbox {
|
||||
flex-shrink: 0;
|
||||
.chipSelect-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
gap: 10px;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease;
|
||||
|
||||
@include themed {
|
||||
border: 2px solid t($modal-sidebarActive);
|
||||
color: t($color);
|
||||
|
||||
&:hover {
|
||||
background: t($modal-sidebarActive);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: t($modal-sidebar);
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
.chipSelect-option-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
|
||||
&.selected .chipSelect-option-checkbox {
|
||||
@include themed {
|
||||
background: t($link);
|
||||
border-color: t($link);
|
||||
color: white;
|
||||
@include themed {
|
||||
border: 2px solid t($modal-sidebarActive);
|
||||
border-radius: 4px;
|
||||
color: t($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chipSelect-option-label {
|
||||
flex: 1;
|
||||
&.selected .chipSelect-option-checkbox {
|
||||
@include themed {
|
||||
background: t($modal-sidebarActive);
|
||||
border-color: t($color);
|
||||
}
|
||||
}
|
||||
|
||||
.chipSelect-option-label {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user