diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 0000000..1d16081 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,49 @@ +name: Deploy Docs + +on: + push: + branches: [main] + paths: + - 'docs/**' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - name: Setup Pages + uses: actions/configure-pages@v4 + - name: Install dependencies + run: npm ci + - name: Build with VitePress + run: npm run docs:build + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/.vitepress/dist + + deploy: + needs: build + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/versioning.yaml b/.github/workflows/versioning.yaml index e0a61a8..1e18da2 100644 --- a/.github/workflows/versioning.yaml +++ b/.github/workflows/versioning.yaml @@ -1,4 +1,4 @@ -name: 버전 관리 +name: 버전 관리 및 릴리즈 on: pull_request: @@ -11,169 +11,115 @@ permissions: pull-requests: read jobs: - bump-version: + release: runs-on: ubuntu-latest - if: github.event.pull_request.merged == true # PR이 병합된 경우에만 실행 + if: github.event.pull_request.merged == true steps: - # 1단계: 리포지토리 체크아웃 - name: 리포지토리 체크아웃 - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: - fetch-depth: 0 # 모든 히스토리와 태그를 가져옴 + fetch-depth: 0 - # 2단계: Node.js 설정 - name: Node.js 설정 - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: '18' # Node.js 18 이상으로 설정 + node-version: '20' - # 3단계: 의존성 설치 - name: 의존성 설치 run: npm install - # 4단계: PR 번호 가져오기 - - name: PR 번호 가져오기 - id: get_pr_number - uses: actions/github-script@v6 + - name: PR 라벨에서 버전 범프 결정 + id: version + uses: actions/github-script@v7 with: script: | - const pr = context.payload.pull_request; - if (!pr) { - throw new Error('해당 커밋과 연관된 PR을 찾을 수 없습니다.'); + const labels = context.payload.pull_request.labels.map(l => l.name); + let bump = 'patch'; + if (labels.includes('🔖 major')) bump = 'major'; + else if (labels.includes('🔖 minor')) bump = 'minor'; + + // 최신 태그에서 현재 버전 추출 + const { execSync } = require('child_process'); + let currentVersion; + try { + const latestTag = execSync('git describe --tags --abbrev=0', { encoding: 'utf-8' }).trim(); + currentVersion = latestTag.replace(/^v/, ''); + } catch { + currentVersion = '0.0.0'; } - return pr.number; - # 5단계: PR 라벨 가져오기 (콤마로 구분된 문자열로 반환) - - name: PR 라벨 가져오기 - id: get_labels - uses: actions/github-script@v6 - with: - script: | - const prNumber = ${{ steps.get_pr_number.outputs.result }}; - const pr = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: prNumber, - }); - const labels = pr.data.labels.map(label => label.name).join(','); - return labels; - - # 6단계: PR 라벨 출력하기 - - name: PR 라벨 출력하기 - run: | - echo "PR 라벨: ${{ steps.get_labels.outputs.result }}" + const [major, minor, patch] = currentVersion.split('.').map(Number); + let newVersion; + if (bump === 'major') newVersion = `${major + 1}.0.0`; + else if (bump === 'minor') newVersion = `${major}.${minor + 1}.0`; + else newVersion = `${major}.${minor}.${patch + 1}`; - # 7단계: 파일 존재 확인 및 목록 출력 - - name: List files to verify manifest.config.ts - run: | - echo "현재 작업 디렉토리: $(pwd)" - ls -al - find . -name "manifest.config.ts" + core.setOutput('new_version', newVersion); + core.setOutput('bump', bump); - # 8단계: 다음 버전 결정 - - name: 다음 버전 결정 - id: version - run: | - set -euo pipefail - IFS=$'\n\t' - - # 최신 태그 가져오기 - LATEST_TAG=$(git describe --tags $(git rev-list --tags --max-count=1) 2>/dev/null || echo "v0.0.0") - echo "최신 태그: $LATEST_TAG" - - # 현재 버전 추출 - CURRENT_VERSION=${LATEST_TAG#v} - echo "현재 버전: $CURRENT_VERSION" - - # 버전 분할 - IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION" - - # PR 라벨 가져오기 (콤마로 구분된 문자열) - LABELS=$(echo "${{ steps.get_labels.outputs.result }}" | tr -d '"') - echo "PR 라벨: $LABELS" - - # 콤마를 기준으로 배열로 변환 - IFS=',' read -r -a LABEL_ARRAY <<< "$LABELS" - - # 라벨에 따라 버전 증가 (우선순위: major > minor > patch) - NEW_VERSION="" - for label in "${LABEL_ARRAY[@]}"; do - label=$(echo "$label" | xargs) # 공백 제거 - if [[ "$label" == "🔖 major" ]]; then - NEW_MAJOR=$((MAJOR + 1)) - NEW_VERSION="$NEW_MAJOR.0.0" - break - elif [[ "$label" == "🔖 minor" ]]; then - NEW_MINOR=$((MINOR + 1)) - NEW_VERSION="$MAJOR.$NEW_MINOR.0" - break - elif [[ "$label" == "🔖 patch" ]]; then - NEW_PATCH=$((PATCH + 1)) - NEW_VERSION="$MAJOR.$MINOR.$NEW_PATCH" - break - fi - done - - # 라벨이 없거나 인식할 수 없는 경우 패치 버전 증가 - if [[ -z "$NEW_VERSION" ]]; then - echo "라벨이 지정되지 않았거나 인식할 수 없습니다. 기본적으로 패치 버전을 증가시킵니다." - NEW_PATCH=$((PATCH + 1)) - NEW_VERSION="$MAJOR.$MINOR.$NEW_PATCH" - fi - - echo "새 버전: $NEW_VERSION" - - # 출력 설정 - echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT - - # 9단계: package.json 및 manifest.config.ts 버전 업데이트 - - name: package.json 및 manifest.config.ts 버전 업데이트 - run: | - NEW_VERSION=${{ steps.version.outputs.new_version }} - echo "새 버전: $NEW_VERSION" - - # package.json 업데이트 - echo "package.json을 버전 $NEW_VERSION으로 업데이트합니다." - npm version $NEW_VERSION --no-git-tag-version - - # manifest.config.ts 업데이트 - FILE_PATH="manifest.config.ts" - if [ -f "$FILE_PATH" ]; then - echo "manifest.config.ts를 버전 $NEW_VERSION으로 업데이트합니다." - sed -i "s/\(version: '\)[0-9]\+\.[0-9]\+\.[0-9]\+'/\1$NEW_VERSION'/" "$FILE_PATH" - echo "🔄 업데이트된 manifest.config.ts 내용:" - cat "$FILE_PATH" - else - echo "❌ manifest.config.ts 파일을 찾을 수 없습니다!" - exit 1 - fi - - # 10단계: 변경 사항 커밋 및 푸시 - - name: 변경 사항 커밋 및 푸시 + - name: package.json 버전 업데이트 + run: npm version ${{ steps.version.outputs.new_version }} --no-git-tag-version + + - name: 프로덕션 빌드 + run: npm run build + + - name: dist 압축 + run: cd dist && zip -r ../dotbugi-v${{ steps.version.outputs.new_version }}.zip . + + - name: README 배지 버전 업데이트 + run: sed -i "s/Chrome_Web_Store-v[0-9]\+\.[0-9]\+\.[0-9]\+/Chrome_Web_Store-v${{ steps.version.outputs.new_version }}/" README.md + + - name: 변경사항 커밋 및 태그 run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git add package.json manifest.config.ts - git commit -m "🔖 chore: bump version to v${{ steps.version.outputs.new_version }}" - git push origin main + git add package.json package-lock.json README.md + git commit -m "chore: bump version to v${{ steps.version.outputs.new_version }}" + git tag v${{ steps.version.outputs.new_version }} + git push origin main --follow-tags + + - name: 릴리즈 노트 생성 + id: changelog + uses: actions/github-script@v7 + with: + script: | + const { execSync } = require('child_process'); + + // 이전 태그 찾기 + let prevTag; + try { + prevTag = execSync('git describe --tags --abbrev=0 HEAD~1', { encoding: 'utf-8' }).trim(); + } catch { + prevTag = execSync('git rev-list --max-parents=0 HEAD', { encoding: 'utf-8' }).trim(); + } - # 11단계: 새 태그 생성 및 푸시 - - name: 새 태그 생성 및 푸시 - run: | - NEW_VERSION=${{ steps.version.outputs.new_version }} - git tag -a v$NEW_VERSION -m "Release v$NEW_VERSION" - git push origin v$NEW_VERSION - - # 12단계: 릴리스 생성 - - name: 릴리스 생성 - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + const newVersion = '${{ steps.version.outputs.new_version }}'; + + // PR에 포함된 커밋 가져오기 + const commits = execSync( + `git log ${prevTag}..HEAD --pretty=format:"- %s (%h)" --no-merges`, + { encoding: 'utf-8' } + ).trim(); + + const pr = context.payload.pull_request; + const body = [ + `## What's Changed`, + ``, + `**PR:** #${pr.number} ${pr.title}`, + ``, + `### Commits`, + commits, + ``, + `**Full Changelog:** https://github.com/${context.repo.owner}/${context.repo.repo}/compare/${prevTag}...v${newVersion}`, + ].join('\n'); + + core.setOutput('body', body); + + - name: GitHub 릴리즈 생성 + uses: softprops/action-gh-release@v2 with: tag_name: v${{ steps.version.outputs.new_version }} - release_name: Release v${{ steps.version.outputs.new_version }} - body: 'Release v${{ steps.version.outputs.new_version }}' - draft: false - prerelease: false - generate_release_notes: true + name: v${{ steps.version.outputs.new_version }} + body: ${{ steps.changelog.outputs.body }} + files: dotbugi-v${{ steps.version.outputs.new_version }}.zip diff --git a/.gitignore b/.gitignore index 3d53d11..1b3be4b 100755 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ dist/ # 환경 설정 파일 (환경에 따라 사용) .env +.env.* .env.local .env.development.local .env.test.local @@ -46,4 +47,9 @@ temp/ dist *.md -!README.md \ No newline at end of file +!README.md +!docs/**/*.md + +# VitePress +docs/.vitepress/dist/ +docs/.vitepress/cache/ \ No newline at end of file diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts new file mode 100644 index 0000000..96aa955 --- /dev/null +++ b/docs/.vitepress/config.ts @@ -0,0 +1,100 @@ +import { defineConfig, type DefaultTheme } from 'vitepress'; + +function guideSidebar(basic: string, advanced: string, calendar: string, faq: string, label: string, prefix = ''): DefaultTheme.SidebarItem[] { + return [{ text: label, items: [ + { text: basic, link: `${prefix}/guide/basic` }, + { text: advanced, link: `${prefix}/guide/advanced` }, + { text: calendar, link: `${prefix}/guide/calendar` }, + { text: faq, link: `${prefix}/guide/faq` }, + ]}]; +} + +function updatesSidebar(label: string, changelog: string, prefix = ''): DefaultTheme.SidebarItem[] { + return [{ text: label, items: [{ text: changelog, link: `${prefix}/updates/changelog` }] }]; +} + +export default defineConfig({ + title: '돋부기', + base: '/dotbugi/', + description: 'dotbugi 사용 가이드 및 업데이트 내역', + + head: [ + ['link', { rel: 'icon', href: '/dotbugi/favicon.png', type: 'image/png' }], + ['link', { rel: 'stylesheet', href: 'https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard-jp.min.css' }], + ], + + locales: { + root: { + label: '한국어', + lang: 'ko-KR', + themeConfig: { + nav: [ + { text: '가이드', link: '/guide/basic' }, + { text: '업데이트', link: '/updates/changelog' }, + { text: '설치', link: 'https://chromewebstore.google.com/detail/hsu-%EB%8F%8B%EB%B6%80%EA%B8%B0-%F0%9F%94%8E/fbhdnbombekihdhjcfiimiibfmikghch' }, + ], + sidebar: { + '/guide/': guideSidebar('간단 사용 설명서', '고급 사용 설명서', 'Google 캘린더 연동', 'FAQ', '가이드'), + '/updates/': updatesSidebar('업데이트', '업데이트 로그'), + }, + outline: { label: '목차' }, + docFooter: { prev: '이전', next: '다음' }, + }, + }, + en: { + label: 'English', + lang: 'en-US', + themeConfig: { + nav: [ + { text: 'Guide', link: '/en/guide/basic' }, + { text: 'Updates', link: '/en/updates/changelog' }, + { text: 'Install', link: 'https://chromewebstore.google.com/detail/hsu-%EB%8F%8B%EB%B6%80%EA%B8%B0-%F0%9F%94%8E/fbhdnbombekihdhjcfiimiibfmikghch' }, + ], + sidebar: { + '/en/guide/': guideSidebar('Quick Start', 'Advanced Guide', 'Google Calendar', 'FAQ', 'Guide', '/en'), + '/en/updates/': updatesSidebar('Updates', 'Changelog', '/en'), + }, + outline: { label: 'On this page' }, + docFooter: { prev: 'Previous', next: 'Next' }, + }, + }, + ja: { + label: '日本語', + lang: 'ja-JP', + themeConfig: { + nav: [ + { text: 'ガイド', link: '/ja/guide/basic' }, + { text: '更新履歴', link: '/ja/updates/changelog' }, + { text: 'インストール', link: 'https://chromewebstore.google.com/detail/hsu-%EB%8F%8B%EB%B6%80%EA%B8%B0-%F0%9F%94%8E/fbhdnbombekihdhjcfiimiibfmikghch' }, + ], + sidebar: { + '/ja/guide/': guideSidebar('簡単ガイド', '詳細ガイド', 'Google カレンダー連携', 'よくある質問', 'ガイド', '/ja'), + '/ja/updates/': updatesSidebar('更新履歴', '更新ログ', '/ja'), + }, + outline: { label: '目次' }, + docFooter: { prev: '前へ', next: '次へ' }, + }, + }, + zh: { + label: '中文', + lang: 'zh-CN', + themeConfig: { + nav: [ + { text: '指南', link: '/zh/guide/basic' }, + { text: '更新日志', link: '/zh/updates/changelog' }, + { text: '安装', link: 'https://chromewebstore.google.com/detail/hsu-%EB%8F%8B%EB%B6%80%EA%B8%B0-%F0%9F%94%8E/fbhdnbombekihdhjcfiimiibfmikghch' }, + ], + sidebar: { + '/zh/guide/': guideSidebar('快速入门', '高级指南', 'Google 日历', '常见问题', '指南', '/zh'), + '/zh/updates/': updatesSidebar('更新日志', '更新记录', '/zh'), + }, + outline: { label: '目录' }, + docFooter: { prev: '上一页', next: '下一页' }, + }, + }, + }, + + themeConfig: { + socialLinks: [{ icon: 'github', link: 'https://github.com/hs-shell/dotbugi' }], + }, +}); diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css new file mode 100644 index 0000000..029899f --- /dev/null +++ b/docs/.vitepress/theme/custom.css @@ -0,0 +1,4 @@ +:root { + --vp-font-family-base: 'Pretendard JP', -apple-system, BlinkMacSystemFont, system-ui, sans-serif; + --vp-font-family-mono: 'JetBrains Mono', 'Fira Code', monospace; +} diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts new file mode 100644 index 0000000..149273e --- /dev/null +++ b/docs/.vitepress/theme/index.ts @@ -0,0 +1,4 @@ +import DefaultTheme from 'vitepress/theme'; +import './custom.css'; + +export default DefaultTheme; diff --git a/docs/en/guide/advanced.md b/docs/en/guide/advanced.md new file mode 100644 index 0000000..20d2440 --- /dev/null +++ b/docs/en/guide/advanced.md @@ -0,0 +1,135 @@ +# Advanced User Guide + +## Course Tracking Settings + +A tracking toggle button is displayed on each course card on the LMS main page. + +- **Blue check**: Tracking enabled — tasks will appear on the dashboard. +- **Transparent**: Tracking disabled — hidden from the dashboard. + +By default, all regular courses are tracked, and extracurricular courses are excluded. + +| Before Tracking | After Adding Tracking | +| :------------------------------------------: | :---------------------------------------------: | +| ![Before Tracking](/images/track-before.png) | ![After Tracking](/images/track-after.png) | + +## Search and Filter + +You can search by course name or title using the search bar at the top. + +Filter options: + +- **By course**: Show only specific courses +- **By status**: Filter completed/incomplete items + +When a filter is applied, a "Filter applied" indicator appears. + +Filter + +## Hiding Tasks + +You can hide items that you have already checked or no longer need. + +- **Right-click** a card → Select "Hide this task" +- Right-click a course group header to hide all items for that course at once. + +Hidden items can be viewed and restored in the "Hidden Tasks" section of the Settings tab. + +| Hide | Unhide | +| :-----------------------: | :---------------------------: | +| ![Hide](/images/hide.png) | ![Unhide](/images/unhide.png) | + +## Course Page Status Badges + +Status badges are displayed next to activities (lectures/assignments/quizzes) on each course page. + +### Lectures (VOD) + + + + + + + + + + + + +
BadgeStatusMeaning
AttendedAttendedWatched for the required attendance time or more
Watching · In 11 daysWatchingPartially watched (progress visualized)
Watching · In 23 hoursWatching (urgent)Partially watched + less than 24 hours until deadline
Not Watched · In 6 daysNot WatchedNot yet watched
Not Watched · In 23 hoursNot Watched (urgent)Less than 24 hours until deadline
AbsentAbsentPast due
HiddenHiddenItem has been hidden
+ +::: info Watching Badge +The watching badge calculates and displays progress based on the study time data from the progress page. +::: + +### Assignments / Quizzes + +
+
+ +**Assignments** + + + + + + + + + + +
BadgeMeaning
SubmittedAssignment submitted
Not Submitted · In 6 daysMore than 1 day until deadline
Not Submitted · In 23 hoursLess than 24 hours until deadline
Not Submitted (Closed)Past due
HiddenItem has been hidden
+ +
+
+ +**Quizzes** + + + + + + + + + + +
BadgeMeaning
TakenQuiz completed
Not Taken · In 6 daysMore than 1 day until deadline
Not Taken · In 23 hoursLess than 24 hours until deadline
Not Taken (Closed)Past due
HiddenItem has been hidden
+ +
+
+ +![Course Page](/images/page.png) + +## Auto-Play Player + +![Player](/images/player.png) + +::: info Smart Playback +- Lectures already marked as attended are automatically skipped. +- If there is a previous watch position, playback resumes from that point. +::: + +### Estimated End Time + +During playback, the estimated end time for the remaining lectures is displayed on the button. + +### Changing Playback Order + +You can change the playback order by dragging and dropping lectures in the sidebar list. + +### Page Leave Warning + +A warning is displayed when you try to leave the page during auto-playback. + +## Refresh + +Refresh + +You can refresh the data using the refresh button in the dropdown menu. A red dot appears when 5 minutes have passed since the last refresh. Data is automatically refreshed after 1 hour. + +## Contact + +Settings + +You can contact us via email or KakaoTalk open chat at the bottom of the Settings tab. diff --git a/docs/en/guide/basic.md b/docs/en/guide/basic.md new file mode 100644 index 0000000..57c4cd6 --- /dev/null +++ b/docs/en/guide/basic.md @@ -0,0 +1,78 @@ +# Basic User Guide + +## Installation + +1. Search for **HSU Dotbugi** on the Chrome Web Store. +2. Click the **Add to Chrome** button. +3. After installation, navigate to the university LMS (learn.hansung.ac.kr). + +![Chrome Web Store](/images/web-store.png) + +## First Launch + +Once installed, a Dotbugi button will appear at the bottom-right of the LMS page. Click it to open the dashboard. + +![Dashboard](/images/onboard.png) + +## Dashboard + +The dashboard consists of 3 tabs: **Online Lectures**, **Assignments**, and **Quizzes**. + +Each card displays the course name, title, and deadline, with colors that change based on status. + +
+
+ +**Online Lectures** + + + + + + + + + +
BadgeMeaning
AttendedWatching completed
Watching · In 11 daysPartially watched
Not Watched · In 6 daysNot watched
AbsentPast due
+ +
+
+ +**Assignments / Quizzes** + + + + + + + + + +
BadgeMeaning
SubmittedSubmitted/taken
Not Submitted · In 6 daysMore than 1 day until deadline
Not Taken · In 23 hoursLess than 24 hours remaining
Not Submitted (Closed)Past due
+ +
+
+ +Clicking a card navigates directly to the corresponding lecture/assignment/quiz page. + +Data is automatically refreshed **every hour**, and also refreshes immediately when you return to the tab after more than an hour away. To refresh manually, click **⋮ Menu → Refresh** in the top-right corner. A red dot on the ⋮ button indicates the data is stale. + +![Dashboard](/images/tasks.png) + +## Auto-Play Player + +The sidebar that appears on the left side of the course page lets you automatically play unwatched lectures in sequence. + +1. Click the Dotbugi trigger on the left side of the course page. +2. Press the **Start Playing** button to begin auto-playing from the first unwatched lecture. +3. When all lectures are finished, it will be marked as "Completed". + +![Dashboard](/images/player.png) + +## Change Language + +You can choose from Korean, English, Chinese, or Japanese in the Settings tab. + +| | | +| -------------------------------- | --------------------------------- | +| ![Dashboard](/images/setting.png) | ![Dashboard](/images/language.png) | diff --git a/docs/en/guide/calendar.md b/docs/en/guide/calendar.md new file mode 100644 index 0000000..ec48c33 --- /dev/null +++ b/docs/en/guide/calendar.md @@ -0,0 +1,39 @@ +# Google Calendar Integration + +## Connect + +1. Open the **Settings** tab in the dashboard. +2. Click the **Login** button under "Google Calendar Integration". +3. Log in with your Google account to complete the integration. + +Settings + +## Sync + +After connecting, press the **Sync Tasks** button in the dropdown menu to add tasks to Google Calendar. + +Sync + +Sync result notifications: + +- "N events have been added" — New items were added +- "Already up to date" — No items to add +- "Failed to add N events" — Some items failed + +## Calendar Color Coding + +| Color | Item | +| ------------ | ---------------- | +| Lavender | Online Lectures | +| Blue | Assignments | +| Orange | Quizzes | + +## Login Expiration + +A notification is displayed when your Google login expires. Click the "Log in again" button to reconnect. + +Expired + +## Disconnect + +Click the **Logout** button in the Settings tab to disconnect the integration. diff --git a/docs/en/guide/faq.md b/docs/en/guide/faq.md new file mode 100644 index 0000000..42d38bd --- /dev/null +++ b/docs/en/guide/faq.md @@ -0,0 +1,55 @@ +# Frequently Asked Questions + +## Installation & Setup + +::: details The Dotbugi button doesn't appear after installation +Make sure you are logged in to the LMS (learn.hansung.ac.kr). Dotbugi only works on LMS pages. You may need to refresh the page after installation. +::: + +::: details Can I use it on other browsers? +Currently supported on Chrome and Chromium-based browsers (Edge, Brave, Arc, etc.). Firefox, Safari, and others are not supported. +::: + +## Dashboard + +::: details Data isn't updating in real time +Data is automatically refreshed every hour. To refresh immediately, click **⋮ Menu → Refresh**. +::: + +::: details A specific course isn't showing on the dashboard +The tracking toggle for that course may be turned off. On the LMS main page, click the button at the top-right of the course card to enable tracking. +::: + +::: details Can I track community (non-credit) courses? +Yes. Enable the tracking toggle for community courses on the LMS main page. They are disabled by default. +::: + +## Auto Player + +::: details Auto playback won't start +The player only appears on course pages (course/view.php). Click the Dotbugi icon in the left sidebar, then press "Start Watching". +::: + +::: details Does it replay already attended lectures? +No. Already attended lectures are automatically skipped. Partially watched lectures resume from where you left off. +::: + +## Google Calendar + +::: details Why does it request Google permissions? +Write access to Google Calendar API is needed to add events. Dotbugi does not access any other data. +::: + +::: details Events don't appear in my calendar after syncing +Check the "Other calendars" list in Google Calendar. The Dotbugi calendar may be hidden. +::: + +## Other + +::: details Is my personal data sent to external servers? +No. Dotbugi stores all data in the browser's local storage and does not transmit data to external servers. Communication with Google API only occurs during calendar sync. +::: + +::: details Badge colors don't match the actual attendance status +Try **⋮ Menu → Refresh** to fetch the latest data. There may be a delay in LMS data reflection. +::: diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 0000000..deed178 --- /dev/null +++ b/docs/en/index.md @@ -0,0 +1,147 @@ +--- +layout: home + +hero: + name: Dotbugi + tagline: Make your LMS more convenient + image: + src: /favicon.png + alt: Dotbugi + actions: + - theme: brand + text: User Guide + link: /en/guide/basic + - theme: alt + text: FAQ + link: /en/guide/faq + - theme: alt + text: Update Log + link: /en/updates/changelog +--- + +
+ +
📖
+
+ Basic User Guide + Learn the basics from installation to the dashboard and auto-play. +
+
+ +
⚙️
+
+ Advanced User Guide + Explore advanced features like filters, badges, and hiding tasks. +
+
+ +
📅
+
+ Google Calendar Integration + Automatically add assignment and quiz deadlines to your calendar. +
+
+
+ +
+
+

Task Dashboard

+

View online lectures, assignments, and quizzes in one screen with color-coded deadline statuses.

+
+
+

Auto Continuous Playback

+

Automatically play unwatched lectures in sequence to complete your coursework quickly.

+
+
+

Google Calendar Integration

+

Automatically register assignment and quiz deadlines to Google Calendar so you never miss a due date.

+
+
+

Course Page Badges

+

Instantly check the attendance/submission status of each activity with badges on the course page.

+
+
+

Multi-language Support

+

Supports 4 languages: Korean, English, Chinese, and Japanese.

+
+
+ + diff --git a/docs/en/updates/changelog.md b/docs/en/updates/changelog.md new file mode 100644 index 0000000..f134fc6 --- /dev/null +++ b/docs/en/updates/changelog.md @@ -0,0 +1,9 @@ +# Update Log + +## v4.0.5 + +- Course page status badges and current week highlight feature +- Quiz submission tracking and task hiding feature +- Per-course tracking selection and UX improvements +- Popover settings tab and calendar integration improvements +- Multi-language (i18n) support and LMS parsing localization diff --git a/docs/guide/advanced.md b/docs/guide/advanced.md new file mode 100644 index 0000000..639f6c1 --- /dev/null +++ b/docs/guide/advanced.md @@ -0,0 +1,135 @@ +# 고급 사용 설명서 + +## 과목 트래킹 설정 + +LMS 메인 페이지의 각 과목 카드에 트래킹 토글 버튼이 표시됩니다. + +- **파란색 체크**: 트래킹 중 — 대시보드에 할 일이 표시됩니다. +- **반투명**: 트래킹 해제 — 대시보드에서 숨겨집니다. + +기본적으로 모든 정규 과목은 트래킹되며, 비교과 과목은 제외됩니다. + +| 트래킹 전 | 트래킹 추가 | +| :------------------------------------: | :-----------------------------------: | +| ![트래킹 전](/images/track-before.png) | ![트래킹 후](/images/track-after.png) | + +## 검색 및 필터 + +상단 검색창에서 과목명이나 제목으로 검색할 수 있습니다. + +필터 옵션: + +- **과목별**: 특정 과목만 표시 +- **상태별**: 완료/미완료 항목 필터링 + +필터가 적용되면 "필터 설정됨" 표시가 나타납니다. + +필터 + +## 태스크 숨기기 + +이미 확인했거나 불필요한 항목은 숨길 수 있습니다. + +- 카드를 **우클릭** → "이 태스크 숨기기" 선택 +- 과목 그룹 헤더를 우클릭하면 해당 과목의 모든 항목을 한번에 숨길 수 있습니다. + +숨긴 항목은 설정 탭의 "숨긴 태스크"에서 확인하고 복원할 수 있습니다. + +| 숨기기 | 숨기기 해제 | +| :-------------------------: | :--------------------------------: | +| ![숨기기](/images/hide.png) | ![숨기기 해제](/images/unhide.png) | + +## 강의 페이지 상태 배지 + +각 강의의 코스 페이지에서 활동(강의/과제/퀴즈) 옆에 상태 배지가 표시됩니다. + +### 강의 (VOD) + + + + + + + + + + + + +
배지상태의미
출석출석출석인정 요구시간 이상 시청 완료
시청중 · 11일 후시청중일부 시청함 (진행률 시각화)
시청중 · 23시간 후시청중 (임박)일부 시청 + 마감까지 24시간 이내
미시청 · 6일 후미시청아직 시청하지 않음
미시청 · 23시간 후미시청 (임박)마감까지 24시간 이내
결석결석기한이 지남
숨김숨김숨김 처리된 항목
+ +::: info 시청중 배지 +시청중 배지는 진도 페이지의 학습시간 데이터를 기반으로 진행률을 계산하여 표시합니다. +::: + +### 과제 / 퀴즈 + +
+
+ +**과제** + + + + + + + + + + +
배지의미
제출완료과제 제출 완료
미제출 · 6일 후마감까지 1일 이상
미제출 · 23시간 후마감까지 24시간 이내
미제출 (마감)기한이 지남
숨김숨김 처리된 항목
+ +
+
+ +**퀴즈** + + + + + + + + + + +
배지의미
응시완료퀴즈 응시 완료
미응시 · 6일 후마감까지 1일 이상
미응시 · 23시간 후마감까지 24시간 이내
미응시 (마감)기한이 지남
숨김숨김 처리된 항목
+ +
+
+ +![강의 페이지](/images/page.png) + +## 자동 재생 플레이어 + +![플레이어](/images/player.png) + +::: info 스마트 재생 +- 이미 출석 처리된 강의는 자동으로 건너뜁니다. +- 이전에 시청한 위치가 있으면 해당 지점부터 이어서 재생합니다. +::: + +### 예상 종료 시간 + +재생 중 버튼에 남은 강의의 예상 종료 시간이 표시됩니다. + +### 재생 순서 변경 + +사이드바의 강의 목록에서 드래그 앤 드롭으로 재생 순서를 바꿀 수 있습니다. + +### 페이지 이탈 경고 + +자동 재생 중 페이지를 떠나려 하면 경고가 표시됩니다. + +## 새로고침 + +새로고침 + +드롭다운 메뉴의 새로고침 버튼으로 데이터를 갱신할 수 있습니다. 마지막 갱신 후 5분이 지나면 빨간 점이 표시됩니다. 1시간이 지나면 자동으로 데이터가 갱신됩니다. + +## 문의 + +새로고침 + +설정 탭 하단에서 이메일 또는 카카오톡 오픈채팅으로 문의할 수 있습니다. diff --git a/docs/guide/basic.md b/docs/guide/basic.md new file mode 100644 index 0000000..948e288 --- /dev/null +++ b/docs/guide/basic.md @@ -0,0 +1,78 @@ +# 간단 사용 설명서 + +## 설치 + +1. Chrome 웹스토어에서 **HSU 돋부기**를 검색합니다. +2. **Chrome에 추가** 버튼을 클릭합니다. +3. 설치 완료 후, 학교 LMS(learn.hansung.ac.kr)에 접속합니다. + +![크롬 웹스토어](/images/web-store.png) + +## 첫 실행 + +설치가 완료되면 LMS 페이지 우측 하단에 돋부기 버튼이 나타납니다. 클릭하면 대시보드가 열립니다. + +![대시보드](/images/onboard.png) + +## 대시보드 + +대시보드는 **온라인 강의**, **과제**, **퀴즈** 3개 탭으로 구성됩니다. + +각 카드에는 과목명, 제목, 마감일이 표시되며 상태에 따라 색상이 달라집니다. + +
+
+ +**온라인 강의** + + + + + + + + + +
배지의미
출석시청 완료
시청중 · 11일 후일부 시청함
미시청 · 6일 후미시청
결석기한 지남
+ +
+
+ +**과제 / 퀴즈** + + + + + + + + + +
배지의미
제출완료제출/응시 완료
미제출 · 6일 후마감 1일 이상
미응시 · 23시간 후24시간 이내
미제출 (마감)기한 지남
+ +
+
+ +카드를 클릭하면 해당 강의/과제/퀴즈 페이지로 바로 이동합니다. + +데이터는 **1시간마다** 자동으로 갱신되며, 브라우저 탭을 벗어났다가 돌아왔을 때도 1시간이 지났으면 즉시 갱신됩니다. 바로 갱신하고 싶다면 우측 상단 **⋮ 메뉴 → 새로고침**을 클릭하세요. ⋮ 버튼에 빨간 점이 보이면 데이터가 오래되었다는 뜻입니다. + +![대시보드](/images/tasks.png) + +## 자동 재생 플레이어 + +강의 페이지 좌측에 나타나는 사이드바에서 미수강 강의를 자동으로 연속 재생할 수 있습니다. + +1. 강의 페이지에서 좌측의 돋부기 트리거를 클릭합니다. +2. **수강 시작** 버튼을 누르면 첫 번째 미수강 강의부터 자동 재생됩니다. +3. 모든 강의가 끝나면 "수강 완료"로 표시됩니다. + +![대시보드](/images/player.png) + +## 언어 변경 + +설정 탭에서 한국어, English, 中文, 日本語 중 선택할 수 있습니다. + +| | | +| -------------------------------- | --------------------------------- | +| ![대시보드](/images/setting.png) | ![대시보드](/images/language.png) | diff --git a/docs/guide/calendar.md b/docs/guide/calendar.md new file mode 100644 index 0000000..780a059 --- /dev/null +++ b/docs/guide/calendar.md @@ -0,0 +1,39 @@ +# Google 캘린더 연동 + +## 연결하기 + +1. 대시보드의 **설정** 탭을 엽니다. +2. "Google Calendar 연동"의 **로그인** 버튼을 클릭합니다. +3. Google 계정으로 로그인하면 연동이 완료됩니다. + +새로고침 + +## 동기화 + +연동 후 드롭다운 메뉴의 **태스크 동기화** 버튼을 누르면 할 일이 Google 캘린더에 추가됩니다. + +동기화 + +동기화 결과 알림: + +- "N개 일정이 추가되었어요" — 새 항목이 추가됨 +- "이미 최신 상태예요" — 추가할 항목 없음 +- "N개 일정 추가에 실패했어요" — 일부 실패 + +## 캘린더 색상 구분 + +| 색상 | 항목 | +| --------- | ----------- | +| 💜 라벤더 | 온라인 강의 | +| 💙 파랑 | 과제 | +| 🧡 주황 | 퀴즈 | + +## 로그인 만료 + +Google 로그인이 만료되면 알림이 표시됩니다. "다시 로그인" 버튼을 눌러 재연결하세요. + +만료 + +## 연동 해제 + +설정 탭에서 **로그아웃** 버튼을 클릭하면 연동이 해제됩니다. diff --git a/docs/guide/faq.md b/docs/guide/faq.md new file mode 100644 index 0000000..38d7f5f --- /dev/null +++ b/docs/guide/faq.md @@ -0,0 +1,55 @@ +# 자주 묻는 질문 + +## 설치 및 실행 + +::: details 설치 후 돋부기 버튼이 안 보여요 +LMS(learn.hansung.ac.kr)에 로그인한 상태인지 확인하세요. 돋부기는 LMS 페이지에서만 동작합니다. 설치 직후에는 페이지를 새로고침해야 할 수 있습니다. +::: + +::: details 다른 브라우저에서도 사용할 수 있나요? +현재 Chrome 및 Chrome 기반 브라우저(Edge, Brave, Arc 등)에서 사용할 수 있습니다. Firefox, Safari 등은 지원하지 않습니다. +::: + +## 대시보드 + +::: details 데이터가 실시간으로 반영되지 않아요 +데이터는 1시간마다 자동 갱신됩니다. 바로 반영하고 싶다면 **⋮ 메뉴 → 새로고침**을 클릭하세요. +::: + +::: details 특정 과목이 대시보드에 안 나와요 +LMS 메인 페이지에서 해당 과목의 트래킹 토글이 꺼져 있을 수 있습니다. 과목 카드의 우측 상단 버튼을 클릭하여 트래킹을 활성화하세요. +::: + +::: details 비교과(커뮤니티) 과목도 트래킹할 수 있나요? +네, LMS 메인 페이지에서 비교과 과목의 트래킹 토글을 켜면 대시보드에 표시됩니다. 기본값은 꺼져 있습니다. +::: + +## 자동 재생 플레이어 + +::: details 자동 재생이 시작되지 않아요 +강의 페이지(course/view.php)에서만 플레이어가 나타납니다. 좌측 사이드바의 돋부기 아이콘을 클릭한 후 "수강 시작" 버튼을 눌러주세요. +::: + +::: details 이미 출석한 강의도 재생되나요? +아니요, 이미 출석 처리된 강의는 자동으로 건너뜁니다. 이전에 일부 시청한 강의는 시청한 지점부터 이어서 재생됩니다. +::: + +## Google 캘린더 + +::: details Google 캘린더 연동 시 권한을 왜 요청하나요? +캘린더에 일정을 추가하기 위해 Google Calendar API의 이벤트 쓰기 권한이 필요합니다. 돋부기는 일정 추가 외에 다른 데이터에 접근하지 않습니다. +::: + +::: details 동기화했는데 캘린더에 일정이 안 보여요 +Google 캘린더 앱이나 웹에서 "다른 캘린더" 목록을 확인하세요. 돋부기 캘린더가 숨김 처리되어 있을 수 있습니다. +::: + +## 기타 + +::: details 개인정보가 외부로 전송되나요? +아니요. 돋부기는 모든 데이터를 브라우저 로컬 스토리지에 저장하며, 외부 서버로 전송하지 않습니다. Google 캘린더 연동 시에만 Google API와 통신합니다. +::: + +::: details 배지 색상이 실제 출석 상태와 다르게 표시돼요 +**⋮ 메뉴 → 새로고침**으로 최신 데이터를 가져와 보세요. LMS의 데이터 반영에 시간차가 있을 수 있습니다. +::: diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..9756068 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,162 @@ +--- +layout: home + +head: + - - script + - {} + - | + if (!sessionStorage.getItem('lang-redirected')) { + const lang = navigator.language?.toLowerCase() || ''; + const base = '/dotbugi/'; + const map = { en: 'en/', ja: 'ja/', zh: 'zh/' }; + const match = Object.entries(map).find(([k]) => lang.startsWith(k)); + if (match) { + sessionStorage.setItem('lang-redirected', '1'); + window.location.replace(base + match[1]); + } + } + +hero: + name: 돋부기 + tagline: 학교 LMS를 더 편리하게 + image: + src: /favicon.png + alt: 돋부기 + actions: + - theme: brand + text: 사용 설명서 + link: /guide/basic + - theme: alt + text: FAQ + link: /guide/faq + - theme: alt + text: 업데이트 로그 + link: /updates/changelog +--- + +
+ +
📖
+
+ 간단 사용 설명서 + 설치부터 대시보드, 자동 재생까지 기본 사용법을 알아봅니다. +
+
+ +
⚙️
+
+ 고급 사용 설명서 + 필터, 배지, 숨기기 등 고급 기능을 활용해 보세요. +
+
+ +
📅
+
+ Google 캘린더 연동 + 과제·퀴즈 마감일을 캘린더에 자동 등록합니다. +
+
+
+ +
+
+

할 일 대시보드

+

온라인 강의, 과제, 퀴즈를 한 화면에 모아 마감 상태를 색상으로 구분합니다.

+
+
+

자동 연속 재생

+

미수강 강의를 자동으로 연속 재생하여 수강을 빠르게 완료할 수 있습니다.

+
+
+

Google 캘린더 연동

+

과제·퀴즈 마감일을 Google 캘린더에 자동 등록하여 일정을 놓치지 않습니다.

+
+
+

강의 페이지 배지

+

강의 페이지에서 각 활동의 출석/제출 상태를 배지로 한눈에 확인할 수 있습니다.

+
+
+

다국어 지원

+

한국어, English, 中文, 日本語 4개 언어를 지원합니다.

+
+
+ + diff --git a/docs/ja/guide/advanced.md b/docs/ja/guide/advanced.md new file mode 100644 index 0000000..6a1c157 --- /dev/null +++ b/docs/ja/guide/advanced.md @@ -0,0 +1,135 @@ +# 上級ガイド + +## 科目トラッキング設定 + +LMSメインページの各科目カードにトラッキングトグルボタンが表示されます。 + +- **青いチェック**: トラッキング中 — ダッシュボードにタスクが表示されます。 +- **半透明**: トラッキング解除 — ダッシュボードから非表示になります。 + +デフォルトではすべての正規科目がトラッキングされ、課外科目は除外されます。 + +| トラッキング前 | トラッキング追加 | +| :------------------------------------: | :-----------------------------------: | +| ![トラッキング前](/images/track-before.png) | ![トラッキング後](/images/track-after.png) | + +## 検索とフィルター + +上部の検索バーで科目名やタイトルで検索できます。 + +フィルターオプション: + +- **科目別**: 特定の科目のみ表示 +- **状態別**: 完了/未完了の項目をフィルタリング + +フィルターが適用されると「フィルター設定済み」の表示が出ます。 + +フィルター + +## タスクの非表示 + +確認済みまたは不要な項目は非表示にできます。 + +- カードを**右クリック** → 「このタスクを非表示」を選択 +- 科目グループヘッダーを右クリックすると、その科目のすべての項目を一括で非表示にできます。 + +非表示にした項目は設定タブの「非表示のタスク」で確認・復元できます。 + +| 非表示 | 非表示解除 | +| :-------------------------: | :--------------------------------: | +| ![非表示](/images/hide.png) | ![非表示解除](/images/unhide.png) | + +## 講義ページのステータスバッジ + +各講義のコースページでアクティビティ(講義/課題/クイズ)の横にステータスバッジが表示されます。 + +### 講義 (VOD) + + + + + + + + + + + + +
バッジ状態意味
出席出席出席認定の必要時間以上を視聴完了
視聴中 · 11日後視聴中一部視聴済み(進捗率を可視化)
視聴中 · 23時間後視聴中(間近)一部視聴 + 締切まで24時間以内
未視聴 · 6日後未視聴まだ視聴していない
未視聴 · 23時間後未視聴(間近)締切まで24時間以内
欠席欠席期限切れ
非表示非表示非表示にされた項目
+ +::: info 視聴中バッジ +視聴中バッジは進捗ページの学習時間データに基づいて進捗率を計算・表示します。 +::: + +### 課題 / クイズ + +
+
+ +**課題** + + + + + + + + + + +
バッジ意味
提出済み課題提出完了
未提出 · 6日後締切まで1日以上
未提出 · 23時間後締切まで24時間以内
未提出 (締切)期限切れ
非表示非表示にされた項目
+ +
+
+ +**クイズ** + + + + + + + + + + +
バッジ意味
受験済みクイズ受験完了
未受験 · 6日後締切まで1日以上
未受験 · 23時間後締切まで24時間以内
未受験 (締切)期限切れ
非表示非表示にされた項目
+ +
+
+ +![講義ページ](/images/page.png) + +## 自動再生プレーヤー + +![プレーヤー](/images/player.png) + +::: info スマート再生 +- すでに出席処理された講義は自動的にスキップされます。 +- 以前視聴した位置がある場合、その地点から再生を再開します。 +::: + +### 予想終了時間 + +再生中にボタンに残りの講義の予想終了時間が表示されます。 + +### 再生順序の変更 + +サイドバーの講義リストでドラッグ&ドロップにより再生順序を変更できます。 + +### ページ離脱の警告 + +自動再生中にページを離れようとすると警告が表示されます。 + +## 更新 + +更新 + +ドロップダウンメニューの更新ボタンでデータを更新できます。最後の更新から5分が経過すると赤い点が表示されます。1時間が経過すると自動的にデータが更新されます。 + +## お問い合わせ + +更新 + +設定タブの下部からメールまたはKakaoTalkオープンチャットでお問い合わせいただけます。 diff --git a/docs/ja/guide/basic.md b/docs/ja/guide/basic.md new file mode 100644 index 0000000..9251c4a --- /dev/null +++ b/docs/ja/guide/basic.md @@ -0,0 +1,78 @@ +# 基本ガイド + +## インストール + +1. Chromeウェブストアで**HSU ドットブギ**を検索します。 +2. **Chromeに追加**ボタンをクリックします。 +3. インストール完了後、大学LMS(learn.hansung.ac.kr)にアクセスします。 + +![Chromeウェブストア](/images/web-store.png) + +## 初回起動 + +インストールが完了すると、LMSページの右下にドットブギボタンが表示されます。クリックするとダッシュボードが開きます。 + +![ダッシュボード](/images/onboard.png) + +## ダッシュボード + +ダッシュボードは**オンライン講義**、**課題**、**クイズ**の3つのタブで構成されています。 + +各カードには科目名、タイトル、締切日が表示され、状態に応じて色が変わります。 + +
+
+ +**オンライン講義** + + + + + + + + + +
バッジ意味
出席視聴完了
視聴中 · 11日後一部視聴済み
未視聴 · 6日後未視聴
欠席期限切れ
+ +
+
+ +**課題 / クイズ** + + + + + + + + + +
バッジ意味
提出済み提出/受験完了
未提出 · 6日後締切まで1日以上
未受験 · 23時間後24時間以内
未提出 (締切)期限切れ
+ +
+
+ +カードをクリックすると、該当の講義/課題/クイズページに直接移動します。 + +データは**1時間ごと**に自動更新され、ブラウザタブを離れて戻った際も1時間が経過していれば即座に更新されます。すぐに更新したい場合は、右上の **⋮ メニュー → 更新** をクリックしてください。⋮ ボタンに赤い点が表示されている場合、データが古くなっていることを示しています。 + +![ダッシュボード](/images/tasks.png) + +## 自動再生プレーヤー + +講義ページの左側に表示されるサイドバーから、未視聴の講義を自動で連続再生できます。 + +1. 講義ページで左側のドットブギトリガーをクリックします。 +2. **受講開始**ボタンを押すと、最初の未視聴講義から自動再生されます。 +3. すべての講義が終わると「受講完了」と表示されます。 + +![ダッシュボード](/images/player.png) + +## 言語変更 + +設定タブで한국어、English、中文、日本語から選択できます。 + +| | | +| -------------------------------- | --------------------------------- | +| ![ダッシュボード](/images/setting.png) | ![ダッシュボード](/images/language.png) | diff --git a/docs/ja/guide/calendar.md b/docs/ja/guide/calendar.md new file mode 100644 index 0000000..3082e4f --- /dev/null +++ b/docs/ja/guide/calendar.md @@ -0,0 +1,39 @@ +# Googleカレンダー連携 + +## 連携する + +1. ダッシュボードの**設定**タブを開きます。 +2. 「Google Calendar連携」の**ログイン**ボタンをクリックします。 +3. Googleアカウントでログインすると連携が完了します。 + +更新 + +## 同期 + +連携後、ドロップダウンメニューの**タスク同期**ボタンを押すと、タスクがGoogleカレンダーに追加されます。 + +同期 + +同期結果の通知: + +- 「N件の予定が追加されました」 — 新しい項目が追加された +- 「すでに最新の状態です」 — 追加する項目なし +- 「N件の予定の追加に失敗しました」 — 一部失敗 + +## カレンダーの色分け + +| 色 | 項目 | +| --------- | ------------- | +| 💜 ラベンダー | オンライン講義 | +| 💙 ブルー | 課題 | +| 🧡 オレンジ | クイズ | + +## ログインの有効期限切れ + +Googleログインの有効期限が切れると通知が表示されます。「再ログイン」ボタンを押して再接続してください。 + +有効期限切れ + +## 連携解除 + +設定タブで**ログアウト**ボタンをクリックすると連携が解除されます。 diff --git a/docs/ja/guide/faq.md b/docs/ja/guide/faq.md new file mode 100644 index 0000000..a2d1679 --- /dev/null +++ b/docs/ja/guide/faq.md @@ -0,0 +1,55 @@ +# よくある質問 + +## インストールと起動 + +::: details インストール後にドットブギボタンが表示されません +LMS(learn.hansung.ac.kr)にログインしているか確認してください。ドットブギはLMSページでのみ動作します。インストール直後はページの更新が必要な場合があります。 +::: + +::: details 他のブラウザでも使えますか? +現在、ChromeおよびChromiumベースのブラウザ(Edge、Brave、Arcなど)に対応しています。Firefox、Safariなどには対応していません。 +::: + +## ダッシュボード + +::: details データがリアルタイムで反映されません +データは1時間ごとに自動更新されます。すぐに反映したい場合は **⋮ メニュー → 更新** をクリックしてください。 +::: + +::: details 特定の科目がダッシュボードに表示されません +LMSメインページでその科目のトラッキングがオフになっている可能性があります。科目カードの右上のボタンをクリックしてトラッキングを有効にしてください。 +::: + +::: details コミュニティ(非教科)科目もトラッキングできますか? +はい、LMSメインページでコミュニティ科目のトラッキングをオンにするとダッシュボードに表示されます。デフォルトではオフになっています。 +::: + +## 自動再生プレイヤー + +::: details 自動再生が始まりません +プレイヤーは講義ページ(course/view.php)でのみ表示されます。左サイドバーのドットブギアイコンをクリックし、「受講開始」ボタンを押してください。 +::: + +::: details すでに出席した講義も再生されますか? +いいえ、すでに出席処理された講義は自動的にスキップされます。一部視聴した講義は、視聴した位置から再開されます。 +::: + +## Googleカレンダー + +::: details Google権限を要求する理由は? +カレンダーにイベントを追加するためにGoogle Calendar APIの書き込み権限が必要です。ドットブギは予定の追加以外のデータにはアクセスしません。 +::: + +::: details 同期してもカレンダーに予定が表示されません +Googleカレンダーの「他のカレンダー」リストを確認してください。ドットブギのカレンダーが非表示になっている可能性があります。 +::: + +## その他 + +::: details 個人情報は外部に送信されますか? +いいえ。ドットブギはすべてのデータをブラウザのローカルストレージに保存し、外部サーバーには送信しません。Googleカレンダー連携時のみGoogle APIと通信します。 +::: + +::: details バッジの色が実際の出席状況と異なります +**⋮ メニュー → 更新** で最新データを取得してみてください。LMSのデータ反映にタイムラグがある場合があります。 +::: diff --git a/docs/ja/index.md b/docs/ja/index.md new file mode 100644 index 0000000..e2a17ad --- /dev/null +++ b/docs/ja/index.md @@ -0,0 +1,147 @@ +--- +layout: home + +hero: + name: ドットブギ + tagline: 大学LMSをもっと便利に + image: + src: /favicon.png + alt: ドットブギ + actions: + - theme: brand + text: 使い方ガイド + link: /ja/guide/basic + - theme: alt + text: よくある質問 + link: /ja/guide/faq + - theme: alt + text: アップデートログ + link: /ja/updates/changelog +--- + +
+ +
📖
+
+ 基本ガイド + インストールからダッシュボード、自動再生まで基本的な使い方を紹介します。 +
+
+ +
⚙️
+
+ 上級ガイド + フィルター、バッジ、非表示などの高度な機能を活用しましょう。 +
+
+ +
📅
+
+ Googleカレンダー連携 + 課題・クイズの締切をカレンダーに自動登録します。 +
+
+
+ +
+
+

タスクダッシュボード

+

オンライン講義、課題、クイズを一画面にまとめ、締切状況を色で区別します。

+
+
+

自動連続再生

+

未視聴の講義を自動で連続再生し、受講を素早く完了できます。

+
+
+

Googleカレンダー連携

+

課題・クイズの締切をGoogleカレンダーに自動登録し、予定を見逃しません。

+
+
+

講義ページバッジ

+

講義ページで各アクティビティの出席・提出状況をバッジで一目で確認できます。

+
+
+

多言語対応

+

한국어、English、中文、日本語の4言語に対応しています。

+
+
+ + diff --git a/docs/ja/updates/changelog.md b/docs/ja/updates/changelog.md new file mode 100644 index 0000000..6796e38 --- /dev/null +++ b/docs/ja/updates/changelog.md @@ -0,0 +1,9 @@ +# アップデートログ + +## v4.0.5 + +- 講義ページのステータスバッジおよび現在の週ハイライト機能 +- クイズ提出トラッキングおよびタスク非表示機能 +- 科目別トラッキング選択およびUX改善 +- ポップオーバー設定タブおよびカレンダー連携の改善 +- 多言語(i18n)対応およびLMSパーシングの多言語対応 diff --git a/docs/public/favicon.png b/docs/public/favicon.png new file mode 100644 index 0000000..5ce5084 Binary files /dev/null and b/docs/public/favicon.png differ diff --git a/docs/public/images/dashboard.png b/docs/public/images/dashboard.png new file mode 100644 index 0000000..5bc8caa Binary files /dev/null and b/docs/public/images/dashboard.png differ diff --git a/docs/public/images/expired.png b/docs/public/images/expired.png new file mode 100644 index 0000000..c6e3cdb Binary files /dev/null and b/docs/public/images/expired.png differ diff --git a/docs/public/images/filter.png b/docs/public/images/filter.png new file mode 100644 index 0000000..94b0ec4 Binary files /dev/null and b/docs/public/images/filter.png differ diff --git a/docs/public/images/hide.png b/docs/public/images/hide.png new file mode 100644 index 0000000..9c448dc Binary files /dev/null and b/docs/public/images/hide.png differ diff --git a/docs/public/images/language.png b/docs/public/images/language.png new file mode 100644 index 0000000..f0f6552 Binary files /dev/null and b/docs/public/images/language.png differ diff --git a/docs/public/images/onboard.png b/docs/public/images/onboard.png new file mode 100644 index 0000000..848bedd Binary files /dev/null and b/docs/public/images/onboard.png differ diff --git a/docs/public/images/page.png b/docs/public/images/page.png new file mode 100644 index 0000000..2b04d67 Binary files /dev/null and b/docs/public/images/page.png differ diff --git a/docs/public/images/player.png b/docs/public/images/player.png new file mode 100644 index 0000000..e40cb7d Binary files /dev/null and b/docs/public/images/player.png differ diff --git a/docs/public/images/refresh.png b/docs/public/images/refresh.png new file mode 100644 index 0000000..319c532 Binary files /dev/null and b/docs/public/images/refresh.png differ diff --git a/docs/public/images/setting-tab.png b/docs/public/images/setting-tab.png new file mode 100644 index 0000000..85e0828 Binary files /dev/null and b/docs/public/images/setting-tab.png differ diff --git a/docs/public/images/setting.png b/docs/public/images/setting.png new file mode 100644 index 0000000..a14cff7 Binary files /dev/null and b/docs/public/images/setting.png differ diff --git a/docs/public/images/sync.png b/docs/public/images/sync.png new file mode 100644 index 0000000..298a97d Binary files /dev/null and b/docs/public/images/sync.png differ diff --git a/docs/public/images/tasks.png b/docs/public/images/tasks.png new file mode 100644 index 0000000..908d869 Binary files /dev/null and b/docs/public/images/tasks.png differ diff --git a/docs/public/images/track-after.png b/docs/public/images/track-after.png new file mode 100644 index 0000000..7b75b79 Binary files /dev/null and b/docs/public/images/track-after.png differ diff --git a/docs/public/images/track-before.png b/docs/public/images/track-before.png new file mode 100644 index 0000000..38e5ef0 Binary files /dev/null and b/docs/public/images/track-before.png differ diff --git a/docs/public/images/unhide.png b/docs/public/images/unhide.png new file mode 100644 index 0000000..c8223fb Binary files /dev/null and b/docs/public/images/unhide.png differ diff --git a/docs/public/images/web-store.png b/docs/public/images/web-store.png new file mode 100644 index 0000000..3558b96 Binary files /dev/null and b/docs/public/images/web-store.png differ diff --git a/docs/updates/changelog.md b/docs/updates/changelog.md new file mode 100644 index 0000000..4866184 --- /dev/null +++ b/docs/updates/changelog.md @@ -0,0 +1,9 @@ +# 업데이트 로그 + +## v4.0.5 + +- 강의 페이지 상태 배지 및 현재 주차 하이라이트 기능 +- 퀴즈 제출 트래킹 및 태스크 숨김 기능 +- 과목별 트래킹 선택 및 UX 개선 +- 팝오버 설정 탭 및 캘린더 연동 개선 +- 다국어(i18n) 지원 및 LMS 파싱 다국어 대응 diff --git a/docs/zh/guide/advanced.md b/docs/zh/guide/advanced.md new file mode 100644 index 0000000..0999452 --- /dev/null +++ b/docs/zh/guide/advanced.md @@ -0,0 +1,135 @@ +# 高级使用说明 + +## 科目跟踪设置 + +LMS 主页面的每个科目卡片上会显示跟踪切换按钮。 + +- **蓝色勾选**: 跟踪中 — 待办事项将显示在仪表盘中。 +- **半透明**: 取消跟踪 — 从仪表盘中隐藏。 + +默认情况下,所有正规科目都会被跟踪,非学分科目除外。 + +| 跟踪前 | 添加跟踪 | +| :------------------------------------: | :-----------------------------------: | +| ![跟踪前](/images/track-before.png) | ![跟踪后](/images/track-after.png) | + +## 搜索和筛选 + +可以在顶部搜索栏中按科目名称或标题进行搜索。 + +筛选选项: + +- **按科目**: 仅显示特定科目 +- **按状态**: 筛选已完成/未完成项目 + +应用筛选后会显示"已设置筛选"提示。 + +筛选 + +## 隐藏任务 + +已确认或不需要的项目可以隐藏。 + +- **右键点击** 卡片 → 选择"隐藏此任务" +- 右键点击科目分组标题可以一次性隐藏该科目的所有项目。 + +隐藏的项目可以在设置标签页的"已隐藏任务"中查看和恢复。 + +| 隐藏 | 取消隐藏 | +| :-------------------------: | :--------------------------------: | +| ![隐藏](/images/hide.png) | ![取消隐藏](/images/unhide.png) | + +## 课程页面状态徽章 + +在每门课程的页面中,活动(课程/作业/测验)旁边会显示状态徽章。 + +### 课程 (VOD) + + + + + + + + + + + + +
徽章状态含义
出勤出勤已观看达到出勤要求时长
观看中 · 11天后观看中部分观看(进度可视化)
观看中 · 23小时后观看中(临近)部分观看 + 距截止不到24小时
未观看 · 6天后未观看尚未观看
未观看 · 23小时后未观看(临近)距截止不到24小时
缺勤缺勤已过期
已隐藏已隐藏已隐藏的项目
+ +::: info 观看中徽章 +观看中徽章基于进度页面的学习时间数据计算并显示进度。 +::: + +### 作业 / 测验 + +
+
+ +**作业** + + + + + + + + + + +
徽章含义
已提交作业提交完成
未提交 · 6天后距截止1天以上
未提交 · 23小时后距截止不到24小时
未提交 (截止)已过期
已隐藏已隐藏的项目
+ +
+
+ +**测验** + + + + + + + + + + +
徽章含义
已参加测验参加完成
未参加 · 6天后距截止1天以上
未参加 · 23小时后距截止不到24小时
未参加 (截止)已过期
已隐藏已隐藏的项目
+ +
+
+ +![课程页面](/images/page.png) + +## 自动播放器 + +![播放器](/images/player.png) + +::: info 智能播放 +- 已出勤的课程会自动跳过。 +- 如果有之前观看的位置,将从该位置继续播放。 +::: + +### 预计结束时间 + +播放中按钮上会显示剩余课程的预计结束时间。 + +### 更改播放顺序 + +可以在侧边栏的课程列表中通过拖放来更改播放顺序。 + +### 离开页面警告 + +自动播放中尝试离开页面时会显示警告。 + +## 刷新 + +刷新 + +可以通过下拉菜单中的刷新按钮更新数据。最后更新超过5分钟后会显示红点。超过1小时后数据将自动更新。 + +## 联系我们 + +刷新 + +可以在设置标签页底部通过电子邮件或 KakaoTalk 开放聊天进行咨询。 diff --git a/docs/zh/guide/basic.md b/docs/zh/guide/basic.md new file mode 100644 index 0000000..c4ff37e --- /dev/null +++ b/docs/zh/guide/basic.md @@ -0,0 +1,78 @@ +# 基础使用说明 + +## 安装 + +1. 在 Chrome 网上应用店搜索 **HSU 돋부기**。 +2. 点击 **添加至 Chrome** 按钮。 +3. 安装完成后,访问学校 LMS (learn.hansung.ac.kr)。 + +![Chrome 网上应用店](/images/web-store.png) + +## 首次运行 + +安装完成后,LMS 页面右下角会出现돋부기按钮。点击即可打开仪表盘。 + +![仪表盘](/images/onboard.png) + +## 仪表盘 + +仪表盘由 **在线课程**、**作业**、**测验** 3个标签页组成。 + +每张卡片显示科目名称、标题和截止日期,并根据状态以不同颜色区分。 + +
+
+ +**在线课程** + + + + + + + + + +
徽章含义
出勤已观看完成
观看中 · 11天后部分观看
未观看 · 6天后未观看
缺勤已过期
+ +
+
+ +**作业 / 测验** + + + + + + + + + +
徽章含义
已提交提交/参加完成
未提交 · 6天后距截止1天以上
未参加 · 23小时后24小时以内
未提交 (截止)已过期
+ +
+
+ +点击卡片即可直接跳转到对应的课程/作业/测验页面。 + +数据每 **1小时** 自动更新,离开浏览器标签页后返回时如果超过1小时也会立即更新。如需立即刷新,请点击右上角 **⋮ 菜单 → 刷新**。如果 ⋮ 按钮上出现红点,说明数据已过期。 + +![仪表盘](/images/tasks.png) + +## 自动播放器 + +在课程页面左侧出现的侧边栏中,可以自动连续播放未观看的课程。 + +1. 在课程页面点击左侧的돋부기触发按钮。 +2. 点击 **开始学习** 按钮,将从第一个未观看的课程开始自动播放。 +3. 所有课程播放完毕后,将显示"学习完成"。 + +![仪表盘](/images/player.png) + +## 语言切换 + +在设置标签页中可以选择한국어、English、中文、日本語。 + +| | | +| -------------------------------- | --------------------------------- | +| ![仪表盘](/images/setting.png) | ![仪表盘](/images/language.png) | diff --git a/docs/zh/guide/calendar.md b/docs/zh/guide/calendar.md new file mode 100644 index 0000000..7d46c7e --- /dev/null +++ b/docs/zh/guide/calendar.md @@ -0,0 +1,39 @@ +# Google 日历同步 + +## 连接 + +1. 打开仪表盘的 **设置** 标签页。 +2. 点击"Google Calendar 同步"的 **登录** 按钮。 +3. 使用 Google 账号登录后即完成同步连接。 + +刷新 + +## 同步 + +连接后,点击下拉菜单中的 **同步任务** 按钮,待办事项将被添加到 Google 日历中。 + +同步 + +同步结果通知: + +- "已添加 N 个日程" — 添加了新项目 +- "已是最新状态" — 没有需要添加的项目 +- "N 个日程添加失败" — 部分失败 + +## 日历颜色区分 + +| 颜色 | 项目 | +| --------- | ----------- | +| 💜 薰衣草 | 在线课程 | +| 💙 蓝色 | 作业 | +| 🧡 橙色 | 测验 | + +## 登录过期 + +Google 登录过期时会显示通知。请点击"重新登录"按钮进行重新连接。 + +过期 + +## 取消同步 + +在设置标签页中点击 **登出** 按钮即可取消同步连接。 diff --git a/docs/zh/guide/faq.md b/docs/zh/guide/faq.md new file mode 100644 index 0000000..16d3f75 --- /dev/null +++ b/docs/zh/guide/faq.md @@ -0,0 +1,55 @@ +# 常见问题 + +## 安装与启动 + +::: details 安装后看不到돋부기按钮 +请确认是否已登录LMS(learn.hansung.ac.kr)。돋부기仅在LMS页面上运行。安装后可能需要刷新页面。 +::: + +::: details 可以在其他浏览器上使用吗? +目前支持Chrome及基于Chromium的浏览器(Edge、Brave、Arc等)。不支持Firefox、Safari等。 +::: + +## 仪表盘 + +::: details 数据没有实时更新 +数据每小时自动刷新一次。如需立即刷新,请点击 **⋮ 菜单 → 刷新**。 +::: + +::: details 特定课程没有显示在仪表盘上 +该课程的追踪开关可能已关闭。请在LMS主页的课程卡片右上角点击按钮启用追踪。 +::: + +::: details 可以追踪社区(非学分)课程吗? +可以。在LMS主页开启社区课程的追踪开关即可在仪表盘中显示。默认为关闭状态。 +::: + +## 自动播放器 + +::: details 自动播放无法开始 +播放器仅在课程页面(course/view.php)上显示。请点击左侧栏的돋부기图标,然后点击"开始学习"按钮。 +::: + +::: details 已出勤的课程也会播放吗? +不会。已标记出勤的课程会自动跳过。之前部分观看的课程会从上次观看的位置继续播放。 +::: + +## Google日历 + +::: details 为什么需要Google权限? +需要Google Calendar API的写入权限来添加日程。돋부기不会访问其他任何数据。 +::: + +::: details 同步后日历中看不到日程 +请检查Google日历中的"其他日历"列表。돋부기的日历可能被隐藏了。 +::: + +## 其他 + +::: details 个人信息会发送到外部服务器吗? +不会。돋부기将所有数据存储在浏览器本地存储中,不会向外部服务器传输数据。仅在Google日历同步时与Google API通信。 +::: + +::: details 徽章颜色与实际出勤状态不一致 +请尝试 **⋮ 菜单 → 刷新** 获取最新数据。LMS数据可能存在延迟。 +::: diff --git a/docs/zh/index.md b/docs/zh/index.md new file mode 100644 index 0000000..e79792b --- /dev/null +++ b/docs/zh/index.md @@ -0,0 +1,147 @@ +--- +layout: home + +hero: + name: 돋부기 + tagline: 让大学LMS更便捷 + image: + src: /favicon.png + alt: 돋부기 + actions: + - theme: brand + text: 使用说明 + link: /zh/guide/basic + - theme: alt + text: 常见问题 + link: /zh/guide/faq + - theme: alt + text: 更新日志 + link: /zh/updates/changelog +--- + +
+ +
📖
+
+ 基础使用说明 + 从安装到仪表盘、自动播放,了解基本使用方法。 +
+
+ +
⚙️
+
+ 高级使用说明 + 使用筛选、徽章、隐藏等高级功能。 +
+
+ +
📅
+
+ Google 日历同步 + 将作业和测验截止日期自动添加到日历中。 +
+
+
+ +
+
+

待办仪表盘

+

将在线课程、作业、测验汇集在一个界面,通过颜色区分截止状态。

+
+
+

自动连续播放

+

自动连续播放未观看的课程,快速完成学习。

+
+
+

Google 日历同步

+

将作业和测验截止日期自动添加到 Google 日历,不再错过任何日程。

+
+
+

课程页面徽章

+

在课程页面上以徽章形式一目了然地查看各活动的出勤/提交状态。

+
+
+

多语言支持

+

支持한국어、English、中文、日本語四种语言。

+
+
+ + diff --git a/docs/zh/updates/changelog.md b/docs/zh/updates/changelog.md new file mode 100644 index 0000000..6ce1192 --- /dev/null +++ b/docs/zh/updates/changelog.md @@ -0,0 +1,9 @@ +# 更新日志 + +## v4.0.5 + +- 课程页面状态徽章及当前周次高亮功能 +- 测验提交跟踪及任务隐藏功能 +- 按科目跟踪选择及用户体验改进 +- 弹出窗口设置标签页及日历同步改进 +- 多语言 (i18n) 支持及 LMS 解析多语言适配 diff --git a/manifest.config.ts b/manifest.config.ts index 9346380..32a52e5 100644 --- a/manifest.config.ts +++ b/manifest.config.ts @@ -1,10 +1,18 @@ import { ManifestV3Export } from '@crxjs/vite-plugin'; +import packageJson from './package.json' with { type: 'json' }; -const manifest = { +const PROD_CLIENT_ID = '804067218183-3pev3tppten6i94lrfvmk729hmbdejqb.apps.googleusercontent.com'; +const DEV_CLIENT_ID = '981860765955-t69elhj8osi7vdp84m5bf8b9hdib44kr.apps.googleusercontent.com'; + +export function createManifest(mode?: string): ManifestV3Export { + const clientId = mode === 'mock' ? DEV_CLIENT_ID : PROD_CLIENT_ID; + + return { manifest_version: 3, - name: 'HSU 돋부기 🔎', - version: '4.0.5', - description: '한성대학교 LMS 강의, 과제, 퀴즈를 한 눈에!', + name: '__MSG_extName__', + version: packageJson.version, + description: '__MSG_extDescription__', + default_locale: 'ko', action: {}, icons: { '16': 'images/icon/icon-16.png', @@ -18,7 +26,7 @@ const manifest = { content_scripts: [ { matches: ['https://learn.hansung.ac.kr/**'], - js: ['src/content/index.tsx'], + js: ['src/popover/index.tsx'], }, ], web_accessible_resources: [ @@ -27,13 +35,11 @@ const manifest = { matches: ['*://*/*'], }, ], - options_page: 'option.html', - permissions: ['storage', 'notifications', 'alarms', 'identity'], + permissions: ['storage', 'identity'], host_permissions: ['https://*/*', 'http://*/*'], oauth2: { - client_id: '804067218183-3pev3tppten6i94lrfvmk729hmbdejqb.apps.googleusercontent.com', + client_id: clientId, scopes: ['https://www.googleapis.com/auth/calendar.events'], }, -} as ManifestV3Export; - -export default manifest; + }; +} diff --git a/option.html b/option.html deleted file mode 100644 index 6b73a4b..0000000 --- a/option.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - 돋부기 - - -
-
- - - diff --git a/package-lock.json b/package-lock.json index 34d37af..138423f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dotbugi", - "version": "3.1.19", + "version": "4.0.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dotbugi", - "version": "3.1.19", + "version": "4.0.5", "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/modifiers": "^9.0.0", @@ -16,7 +16,9 @@ "@heroui/theme": "^2.4.6", "@radix-ui/react-avatar": "^1.1.2", "@radix-ui/react-checkbox": "^1.1.3", + "@radix-ui/react-context-menu": "^2.2.16", "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-popover": "^1.1.4", "@radix-ui/react-progress": "^1.1.1", @@ -27,20 +29,19 @@ "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-toast": "^1.2.4", "@radix-ui/react-tooltip": "^1.1.7", - "@types/gapi.client.calendar": "^3.0.12", - "axios": "^1.12.0", + "@types/gapi.client.calendar-v3": "^0.0.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "framer-motion": "^12.0.6", - "glob": "^11.0.1", "googleapis": "^160.0.0", + "i18next": "^25.8.18", "lucide-react": "^0.471.2", "motion": "^12.23.24", - "node-fetch": "^3.3.2", "react": "^18.3.1", "react-colorful": "^5.6.1", "react-dom": "^18.3.1", + "react-i18next": "^16.5.8", "react-router-dom": "^7.9.1", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", @@ -49,6 +50,7 @@ "devDependencies": { "@crxjs/vite-plugin": "^2.0.0-beta.30", "@eslint/js": "^9.17.0", + "@testing-library/jest-dom": "^6.9.1", "@types/chrome": "^0.0.296", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", @@ -58,6 +60,7 @@ "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.16", "globals": "^15.14.0", + "jsdom": "^25.0.1", "postcss": "^8.5.1", "prettier": "^3.4.2", "tailwindcss": "^3.4.17", @@ -65,7 +68,274 @@ "typescript": "~5.6.2", "typescript-eslint": "^8.18.2", "vite": "^6.3.6", - "vite-plugin-pages": "^0.32.4" + "vite-plugin-pages": "^0.32.4", + "vitepress": "^1.6.4", + "vitest": "^4.1.0" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@algolia/abtesting": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.15.2.tgz", + "integrity": "sha512-rF7vRVE61E0QORw8e2NNdnttcl3jmFMWS9B4hhdga12COe+lMa26bQLfcBn/Nbp9/AF/8gXdaRCPsVns3CnjsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.49.2", + "@algolia/requester-browser-xhr": "5.49.2", + "@algolia/requester-fetch": "5.49.2", + "@algolia/requester-node-http": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.49.2", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.49.2.tgz", + "integrity": "sha512-XyvKCm0RRmovMI/ChaAVjTwpZhXdbgt3iZofK914HeEHLqD1MUFFVLz7M0+Ou7F56UkHXwRbpHwb9xBDNopprQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.49.2", + "@algolia/requester-browser-xhr": "5.49.2", + "@algolia/requester-fetch": "5.49.2", + "@algolia/requester-node-http": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.49.2", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.49.2.tgz", + "integrity": "sha512-jq/3qvtmj3NijZlhq7A1B0Cl41GfaBpjJxcwukGsYds6aMSCWrEAJ9pUqw/C9B3hAmILYKl7Ljz3N9SFvekD3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.49.2", + "@algolia/requester-browser-xhr": "5.49.2", + "@algolia/requester-fetch": "5.49.2", + "@algolia/requester-node-http": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.49.2", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.49.2.tgz", + "integrity": "sha512-bn0biLequn3epobCfjUqCxlIlurLr4RHu7RaE4trgN+RDcUq6HCVC3/yqq1hwbNYpVtulnTOJzcaxYlSr1fnuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.49.2", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.49.2.tgz", + "integrity": "sha512-z14wfFs1T3eeYbCArC8pvntAWsPo9f6hnUGoj8IoRUJTwgJiiySECkm8bmmV47/x0oGHfsVn3kBdjMX0yq0sNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.49.2", + "@algolia/requester-browser-xhr": "5.49.2", + "@algolia/requester-fetch": "5.49.2", + "@algolia/requester-node-http": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.49.2", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.49.2.tgz", + "integrity": "sha512-GpRf7yuuAX93+Qt0JGEJZwgtL0MFdjFO9n7dn8s2pA9mTjzl0Sc5+uTk1VPbIAuf7xhCP9Mve+URGb6J+EYxgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.49.2", + "@algolia/requester-browser-xhr": "5.49.2", + "@algolia/requester-fetch": "5.49.2", + "@algolia/requester-node-http": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.49.2", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.49.2.tgz", + "integrity": "sha512-HZwApmNkp0DiAjZcLYdQLddcG4Agb88OkojiAHGgcm5DVXobT5uSZ9lmyrbw/tmQBJwgu2CNw4zTyXoIB7YbPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.49.2", + "@algolia/requester-browser-xhr": "5.49.2", + "@algolia/requester-fetch": "5.49.2", + "@algolia/requester-node-http": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.49.2", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.49.2.tgz", + "integrity": "sha512-y1IOpG6OSmTpGg/CT0YBb/EAhR2nsC18QWp9Jy8HO9iGySpcwaTvs5kHa17daP3BMTwWyaX9/1tDTDQshZzXdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.49.2", + "@algolia/requester-browser-xhr": "5.49.2", + "@algolia/requester-fetch": "5.49.2", + "@algolia/requester-node-http": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.49.2", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.49.2.tgz", + "integrity": "sha512-YYJRjaZ2bqk923HxE4um7j/Cm3/xoSkF2HC2ZweOF8cXL3sqnlndSUYmCaxHFjNPWLaSHk2IfssX6J/tdKTULw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.49.2", + "@algolia/requester-browser-xhr": "5.49.2", + "@algolia/requester-fetch": "5.49.2", + "@algolia/requester-node-http": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.49.2", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.49.2.tgz", + "integrity": "sha512-9WgH+Dha39EQQyGKCHlGYnxW/7W19DIrEbCEbnzwAMpGAv1yTWCHMPXHxYa+LcL3eCp2V/5idD1zHNlIKmHRHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.49.2", + "@algolia/requester-browser-xhr": "5.49.2", + "@algolia/requester-fetch": "5.49.2", + "@algolia/requester-node-http": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.49.2", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.49.2.tgz", + "integrity": "sha512-K7Gp5u+JtVYgaVpBxF5rGiM+Ia8SsMdcAJMTDV93rwh00DKNllC19o1g+PwrDjDvyXNrnTEbofzbTs2GLfFyKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.49.2", + "@algolia/requester-browser-xhr": "5.49.2", + "@algolia/requester-fetch": "5.49.2", + "@algolia/requester-node-http": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.49.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.49.2.tgz", + "integrity": "sha512-3UhYCcWX6fbtN8ABcxZlhaQEwXFh3CsFtARyyadQShHMPe3mJV9Wel4FpJTa+seugRkbezFz0tt6aPTZSYTBuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.49.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.49.2.tgz", + "integrity": "sha512-G94VKSGbsr+WjsDDOBe5QDQ82QYgxvpxRGJfCHZBnYKYsy/jv9qGIDb93biza+LJWizQBUtDj7bZzp3QZyzhPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.49.2", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.49.2.tgz", + "integrity": "sha512-UuihBGHafG/ENsrcTGAn5rsOffrCIRuHMOsD85fZGLEY92ate+BMTUqxz60dv5zerh8ZumN4bRm8eW2z9L11jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@alloc/quick-lru": { @@ -94,16 +364,37 @@ "node": ">=6.0.0" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" @@ -227,9 +518,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -237,9 +528,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -257,27 +548,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", - "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.5" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -318,16 +609,25 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -363,23 +663,23 @@ } }, "node_modules/@babel/types": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", - "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@crxjs/vite-plugin": { - "version": "2.0.0-beta.30", - "resolved": "https://registry.npmjs.org/@crxjs/vite-plugin/-/vite-plugin-2.0.0-beta.30.tgz", - "integrity": "sha512-skRcaJAbDrcfKPRqgBtyeBAk19KrRqtA8lO3ZiwJgnpRPX8EICbv0iR6Jb8E3V+knXCrYTc4O5Im+r+n43f14A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@crxjs/vite-plugin/-/vite-plugin-2.3.0.tgz", + "integrity": "sha512-+0CNVGS4bB30OoaF1vUsHVwWU1Lm7MxI0XWY9Fd/Ob+ZVTZgEFNqJ1ZC69IVwQsoYhY0sMQLvpLWiFIuDz8htg==", "dev": true, "license": "MIT", "dependencies": { @@ -394,7 +694,8 @@ "fs-extra": "^10.0.1", "jsesc": "^3.0.2", "magic-string": "^0.30.12", - "picocolors": "^1.0.0", + "pathe": "^2.0.1", + "picocolors": "^1.1.1", "react-refresh": "^0.13.0", "rollup": "2.79.2", "rxjs": "7.5.7" @@ -425,9 +726,9 @@ } }, "node_modules/@crxjs/vite-plugin/node_modules/rollup": { - "version": "2.79.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", - "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", + "version": "2.80.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz", + "integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==", "dev": true, "license": "MIT", "bin": { @@ -440,6 +741,121 @@ "fsevents": "~2.3.2" } }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@dnd-kit/accessibility": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", @@ -507,31 +923,80 @@ "react": ">=16.8.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", - "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", - "cpu": [ - "ppc64" - ], + "node_modules/@docsearch/css": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", + "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@docsearch/js": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", + "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@docsearch/react": "3.8.2", + "preact": "^10.0.0" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", - "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", - "cpu": [ + "node_modules/@docsearch/react": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", + "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.2", + "algoliasearch": "^5.14.2" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -548,7 +1013,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -565,7 +1029,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -582,7 +1045,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -599,7 +1061,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -616,7 +1077,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -633,7 +1093,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -650,7 +1109,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -667,7 +1125,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -684,7 +1141,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -701,7 +1157,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -718,7 +1173,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -735,7 +1189,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -752,7 +1205,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -769,7 +1221,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -786,7 +1237,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -803,7 +1253,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -820,7 +1269,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -837,7 +1285,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -854,7 +1301,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -871,7 +1317,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -888,7 +1333,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -905,7 +1349,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -922,7 +1365,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -933,9 +1375,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -975,24 +1417,61 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", - "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.5", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", - "minimatch": "^3.1.2" + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1003,20 +1482,20 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { @@ -1026,6 +1505,17 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -1039,20 +1529,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/js": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", - "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", - "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1060,13 +1566,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -1348,9 +1854,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1361,6 +1867,23 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.74", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.74.tgz", + "integrity": "sha512-yqaohfY6jnYjTVpuTkaBQHrWbdUrQyWXhau0r/0EZiNWYXPX/P8WWwl1DoLH5CbvDjjcWQw5J0zADhgCUklOqA==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, "node_modules/@internationalized/date": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.6.0.tgz", @@ -1451,7 +1974,7 @@ "version": "0.3.6", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true, "dependencies": { @@ -1460,9 +1983,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -1687,6 +2210,160 @@ } } }, + "node_modules/@radix-ui/react-context-menu": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.16.tgz", + "integrity": "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context-menu/node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-context-menu/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context-menu/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context-menu/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context-menu/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context-menu/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context-menu/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context-menu/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-dialog": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz", @@ -1882,54 +2559,61 @@ } } }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", - "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz", - "integrity": "sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==", + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0" - }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", - "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.0" - }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -1940,50 +2624,31 @@ } } }, - "node_modules/@radix-ui/react-label": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.1.tgz", - "integrity": "sha512-UUw5E4e/2+4kFMH7+YxORXGWggtY6sM8WIwh5RZchhLuUg2H1hc98Py+pr8HMz6rdaYrK2t296ZEjYLOCO5uUw==", + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.1" + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-popover": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.4.tgz", - "integrity": "sha512-aUACAkXx8LaFymDma+HQVji7WhvEhpFJ7+qPz17Nf4lLZqtreGOFRiNQWQmhzp7kEWg9cOyyQJpdIMUMPc/CPw==", + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.3", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.1", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.1", - "@radix-ui/react-portal": "1.1.3", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-slot": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "^2.6.1" + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2000,117 +2665,82 @@ } } }, - "node_modules/@radix-ui/react-popper": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz", - "integrity": "sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==", + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "license": "MIT", "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-rect": "1.1.0", - "@radix-ui/react-use-size": "1.1.0", - "@radix-ui/rect": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz", - "integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==", + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", - "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", - "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", + "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.1.1" - }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-progress": { + "node_modules/@radix-ui/react-focus-scope": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.1.tgz", - "integrity": "sha512-6diOawA84f/eMxFHcWut0aE1C2kyE9dOyCTQOMRR2C/qPiXz/X0SaiA/RLbapQaXUCmy0/hLMf9meSccD1N0pA==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz", + "integrity": "sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==", "license": "MIT", "dependencies": { - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.1" + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -2127,52 +2757,31 @@ } } }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.1.tgz", - "integrity": "sha512-QE1RoxPGJ/Nm8Qmk0PxP8ojmoaS67i0s7hVssS7KuI2FQoc/uzVlZsqKfQvxPE6D8hICCPHJ4D88zNhT3OOmkw==", + "node_modules/@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-collection": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0" + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.2.tgz", - "integrity": "sha512-EFI1N/S3YxZEW/lJ/H1jY3njlvTd8tBmgKEn4GHi51+aMm94i6NmAJstsm5cu3yJwYqYc93gpCPm21FeAbFk6g==", + "node_modules/@radix-ui/react-label": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.1.tgz", + "integrity": "sha512-UUw5E4e/2+4kFMH7+YxORXGWggtY6sM8WIwh5RZchhLuUg2H1hc98Py+pr8HMz6rdaYrK2t296ZEjYLOCO5uUw==", "license": "MIT", "dependencies": { - "@radix-ui/number": "1.1.0", - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/react-primitive": "2.0.1" }, "peerDependencies": { "@types/react": "*", @@ -2189,33 +2798,30 @@ } } }, - "node_modules/@radix-ui/react-select": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.5.tgz", - "integrity": "sha512-eVV7N8jBXAXnyrc+PsOF89O9AfVgGnbLxUtBb0clJ8y8ENMWLARGMI/1/SBRLz7u4HqxLgN71BJ17eono3wcjA==", - "license": "MIT", - "dependencies": { - "@radix-ui/number": "1.1.0", - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-collection": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.4", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.1", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.1", - "@radix-ui/react-portal": "1.1.3", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-slot": "1.1.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.1", + "node_modules/@radix-ui/react-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.2" + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", @@ -2232,17 +2838,19 @@ } } }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.4.tgz", - "integrity": "sha512-XDUI0IVYVSwjMXxM6P4Dfti7AH+Y4oS/TB+sglZ/EXc7cqLwGAmp1NlMrcUjj7ks6R5WTZuWKv44FBbLpwU3sA==", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2259,13 +2867,16 @@ } } }, - "node_modules/@radix-ui/react-separator": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.1.tgz", - "integrity": "sha512-RRiNRSrD8iUiXriq/Y5n4/3iE8HzqgLHsusUSg5jVpU2+3tqcUFPJXHDymwEypunc2sWxDUS3UC+rkZRlHedsw==", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.1" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2282,14 +2893,11 @@ } } }, - "node_modules/@radix-ui/react-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2300,24 +2908,51 @@ } } }, - "node_modules/@radix-ui/react-tabs": { + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-context": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.2.tgz", - "integrity": "sha512-9u/tQJMcC2aGq7KXpGivMm1mgq7oRJKXphDwdypPd/j21j/2znamPU8WkXgnhUaTrSFNIt8XhOyCAupg8/GbwQ==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-roving-focus": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.1.0" - }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, @@ -2330,24 +2965,30 @@ } } }, - "node_modules/@radix-ui/react-toast": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.4.tgz", - "integrity": "sha512-Sch9idFJHJTMH9YNpxxESqABcAFweJG4tKv+0zo0m5XBvUSL8FM5xKcJLFLXononpePs8IclyX1KieL5SDUNgA==", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-collection": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.3", - "@radix-ui/react-portal": "1.1.3", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.1" + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2364,24 +3005,40 @@ } } }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.7.tgz", - "integrity": "sha512-ss0s80BC0+g0+Zc53MvilcnTYSOi4mSuFWBPYPuTOFGjx+pUU+ZrmamMNwS56t8MTFlniA5ocjd4jYm/CdhbOg==", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.4", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.1", - "@radix-ui/react-portal": "1.1.3", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-slot": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.1" + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2398,17 +3055,14 @@ } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.4.tgz", - "integrity": "sha512-XDUI0IVYVSwjMXxM6P4Dfti7AH+Y4oS/TB+sglZ/EXc7cqLwGAmp1NlMrcUjj7ks6R5WTZuWKv44FBbLpwU3sA==", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2425,62 +3079,92 @@ } } }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", - "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", - "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.0" + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", - "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.0" + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", - "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2491,10 +3175,10 @@ } } }, - "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", - "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2506,13 +3190,14 @@ } } }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", - "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "license": "MIT", "dependencies": { - "@radix-ui/rect": "1.1.0" + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2524,13 +3209,13 @@ } } }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", - "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.0" + "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2542,1696 +3227,4101 @@ } } }, - "node_modules/@radix-ui/react-visually-hidden": { + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-use-layout-effect": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.1.tgz", - "integrity": "sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.0.1" - }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/rect": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", - "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", - "license": "MIT" - }, - "node_modules/@react-aria/focus": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.19.0.tgz", - "integrity": "sha512-hPF9EXoUQeQl1Y21/rbV2H4FdUR2v+4/I0/vB+8U3bT1CJ+1AFj1hc/rqx2DqEwDlEwOHN+E4+mRahQmlybq0A==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", "dependencies": { - "@react-aria/interactions": "^3.22.5", - "@react-aria/utils": "^3.26.0", - "@react-types/shared": "^3.26.0", - "@swc/helpers": "^0.5.0", - "clsx": "^2.0.0" + "@radix-ui/rect": "1.1.1" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-aria/i18n": { - "version": "3.12.4", - "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.4.tgz", - "integrity": "sha512-j9+UL3q0Ls8MhXV9gtnKlyozq4aM95YywXqnmJtzT1rYeBx7w28hooqrWkCYLfqr4OIryv1KUnPiCSLwC2OC7w==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", "dependencies": { - "@internationalized/date": "^3.6.0", - "@internationalized/message": "^3.1.6", - "@internationalized/number": "^3.6.0", - "@internationalized/string": "^3.2.5", - "@react-aria/ssr": "^3.9.7", - "@react-aria/utils": "^3.26.0", - "@react-types/shared": "^3.26.0", - "@swc/helpers": "^0.5.0" + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-aria/interactions": { - "version": "3.22.5", - "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.22.5.tgz", - "integrity": "sha512-kMwiAD9E0TQp+XNnOs13yVJghiy8ET8L0cbkeuTgNI96sOAp/63EJ1FSrDf17iD8sdjt41LafwX/dKXW9nCcLQ==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.4.tgz", + "integrity": "sha512-aUACAkXx8LaFymDma+HQVji7WhvEhpFJ7+qPz17Nf4lLZqtreGOFRiNQWQmhzp7kEWg9cOyyQJpdIMUMPc/CPw==", + "license": "MIT", "dependencies": { - "@react-aria/ssr": "^3.9.7", - "@react-aria/utils": "^3.26.0", - "@react-types/shared": "^3.26.0", - "@swc/helpers": "^0.5.0" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.3", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.1", + "@radix-ui/react-portal": "1.1.3", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-slot": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "^2.6.1" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/overlays": { - "version": "3.24.0", - "resolved": "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.24.0.tgz", - "integrity": "sha512-0kAXBsMNTc/a3M07tK9Cdt/ea8CxTAEJ223g8YgqImlmoBBYAL7dl5G01IOj67TM64uWPTmZrOklBchHWgEm3A==", - "license": "Apache-2.0", - "dependencies": { - "@react-aria/focus": "^3.19.0", - "@react-aria/i18n": "^3.12.4", - "@react-aria/interactions": "^3.22.5", - "@react-aria/ssr": "^3.9.7", - "@react-aria/utils": "^3.26.0", - "@react-aria/visually-hidden": "^3.8.18", - "@react-stately/overlays": "^3.6.12", - "@react-types/button": "^3.10.1", - "@react-types/overlays": "^3.8.11", - "@react-types/shared": "^3.26.0", - "@swc/helpers": "^0.5.0" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-aria/ssr": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.7.tgz", - "integrity": "sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-popper": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz", + "integrity": "sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==", + "license": "MIT", "dependencies": { - "@swc/helpers": "^0.5.0" - }, - "engines": { - "node": ">= 12" + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-aria/switch": { - "version": "3.6.10", - "resolved": "https://registry.npmjs.org/@react-aria/switch/-/switch-3.6.10.tgz", - "integrity": "sha512-FtaI9WaEP1tAmra1sYlAkYXg9x75P5UtgY8pSbe9+1WRyWbuE1QZT+RNCTi3IU4fZ7iJQmXH6+VaMyzPlSUagw==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-portal": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz", + "integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==", + "license": "MIT", "dependencies": { - "@react-aria/toggle": "^3.10.10", - "@react-stately/toggle": "^3.8.0", - "@react-types/shared": "^3.26.0", - "@react-types/switch": "^3.5.7", - "@swc/helpers": "^0.5.0" + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-aria/switch/node_modules/@react-aria/toggle": { - "version": "3.10.11", - "resolved": "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.10.11.tgz", - "integrity": "sha512-J3jO3KJiUbaYVDEpeXSBwqcyKxpi9OreiHRGiaxb6VwB+FWCj7Gb2WKajByXNyfs8jc6kX9VUFaXa7jze60oEQ==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "license": "MIT", "dependencies": { - "@react-aria/focus": "^3.19.1", - "@react-aria/interactions": "^3.23.0", - "@react-aria/utils": "^3.27.0", - "@react-stately/toggle": "^3.8.1", - "@react-types/checkbox": "^3.9.1", - "@react-types/shared": "^3.27.0", - "@swc/helpers": "^0.5.0" + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-aria/switch/node_modules/@react-aria/toggle/node_modules/@react-aria/focus": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.19.1.tgz", - "integrity": "sha512-bix9Bu1Ue7RPcYmjwcjhB14BMu2qzfJ3tMQLqDc9pweJA66nOw8DThy3IfVr8Z7j2PHktOLf9kcbiZpydKHqzg==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "license": "MIT", "dependencies": { - "@react-aria/interactions": "^3.23.0", - "@react-aria/utils": "^3.27.0", - "@react-types/shared": "^3.27.0", - "@swc/helpers": "^0.5.0", - "clsx": "^2.0.0" + "@radix-ui/react-slot": "1.1.1" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-aria/switch/node_modules/@react-aria/toggle/node_modules/@react-aria/interactions": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.23.0.tgz", - "integrity": "sha512-0qR1atBIWrb7FzQ+Tmr3s8uH5mQdyRH78n0krYaG8tng9+u1JlSi8DGRSaC9ezKyNB84m7vHT207xnHXGeJ3Fg==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-progress": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.1.tgz", + "integrity": "sha512-6diOawA84f/eMxFHcWut0aE1C2kyE9dOyCTQOMRR2C/qPiXz/X0SaiA/RLbapQaXUCmy0/hLMf9meSccD1N0pA==", + "license": "MIT", "dependencies": { - "@react-aria/ssr": "^3.9.7", - "@react-aria/utils": "^3.27.0", - "@react-types/shared": "^3.27.0", - "@swc/helpers": "^0.5.0" + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.1" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-aria/switch/node_modules/@react-aria/toggle/node_modules/@react-aria/utils": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz", - "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.1.tgz", + "integrity": "sha512-QE1RoxPGJ/Nm8Qmk0PxP8ojmoaS67i0s7hVssS7KuI2FQoc/uzVlZsqKfQvxPE6D8hICCPHJ4D88zNhT3OOmkw==", + "license": "MIT", "dependencies": { - "@react-aria/ssr": "^3.9.7", - "@react-stately/utils": "^3.10.5", - "@react-types/shared": "^3.27.0", - "@swc/helpers": "^0.5.0", - "clsx": "^2.0.0" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", - "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-aria/switch/node_modules/@react-stately/toggle": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.8.1.tgz", - "integrity": "sha512-MVpe79ghVQiwLmVzIPhF/O/UJAUc9B+ZSylVTyJiEPi0cwhbkKGQv9thOF0ebkkRkace5lojASqUAYtSTZHQJA==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.2.tgz", + "integrity": "sha512-EFI1N/S3YxZEW/lJ/H1jY3njlvTd8tBmgKEn4GHi51+aMm94i6NmAJstsm5cu3yJwYqYc93gpCPm21FeAbFk6g==", + "license": "MIT", "dependencies": { - "@react-stately/utils": "^3.10.5", - "@react-types/checkbox": "^3.9.1", - "@react-types/shared": "^3.27.0", - "@swc/helpers": "^0.5.0" + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-aria/switch/node_modules/@react-types/shared": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz", - "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==", - "license": "Apache-2.0", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-aria/utils": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.26.0.tgz", - "integrity": "sha512-LkZouGSjjQ0rEqo4XJosS4L3YC/zzQkfRM3KoqK6fUOmUJ9t0jQ09WjiF+uOoG9u+p30AVg3TrZRUWmoTS+koQ==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-select": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.5.tgz", + "integrity": "sha512-eVV7N8jBXAXnyrc+PsOF89O9AfVgGnbLxUtBb0clJ8y8ENMWLARGMI/1/SBRLz7u4HqxLgN71BJ17eono3wcjA==", + "license": "MIT", "dependencies": { - "@react-aria/ssr": "^3.9.7", - "@react-stately/utils": "^3.10.5", - "@react-types/shared": "^3.26.0", - "@swc/helpers": "^0.5.0", - "clsx": "^2.0.0" + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.4", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.1", + "@radix-ui/react-portal": "1.1.3", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-slot": "1.1.1", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.2" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-aria/visually-hidden": { - "version": "3.8.18", - "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.18.tgz", - "integrity": "sha512-l/0igp+uub/salP35SsNWq5mGmg3G5F5QMS1gDZ8p28n7CgjvzyiGhJbbca7Oxvaw1HRFzVl9ev+89I7moNnFQ==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.4.tgz", + "integrity": "sha512-XDUI0IVYVSwjMXxM6P4Dfti7AH+Y4oS/TB+sglZ/EXc7cqLwGAmp1NlMrcUjj7ks6R5WTZuWKv44FBbLpwU3sA==", + "license": "MIT", "dependencies": { - "@react-aria/interactions": "^3.22.5", - "@react-aria/utils": "^3.26.0", - "@react-types/shared": "^3.26.0", - "@swc/helpers": "^0.5.0" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-stately/overlays": { - "version": "3.6.13", - "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.13.tgz", - "integrity": "sha512-WsU85Gf/b+HbWsnnYw7P/Ila3wD+C37Uk/WbU4/fHgJ26IEOWsPE6wlul8j54NZ1PnLNhV9Fn+Kffi+PaJMQXQ==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-separator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.1.tgz", + "integrity": "sha512-RRiNRSrD8iUiXriq/Y5n4/3iE8HzqgLHsusUSg5jVpU2+3tqcUFPJXHDymwEypunc2sWxDUS3UC+rkZRlHedsw==", + "license": "MIT", "dependencies": { - "@react-stately/utils": "^3.10.5", - "@react-types/overlays": "^3.8.12", - "@swc/helpers": "^0.5.0" + "@radix-ui/react-primitive": "2.0.1" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-stately/toggle": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.8.0.tgz", - "integrity": "sha512-pyt/k/J8BwE/2g6LL6Z6sMSWRx9HEJB83Sm/MtovXnI66sxJ2EfQ1OaXB7Su5PEL9OMdoQF6Mb+N1RcW3zAoPw==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", + "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "license": "MIT", "dependencies": { - "@react-stately/utils": "^3.10.5", - "@react-types/checkbox": "^3.9.0", - "@react-types/shared": "^3.26.0", - "@swc/helpers": "^0.5.0" + "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-stately/utils": { - "version": "3.10.5", - "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.5.tgz", - "integrity": "sha512-iMQSGcpaecghDIh3mZEpZfoFH3ExBwTtuBEcvZ2XnGzCgQjeYXcMdIUwAfVQLXFTdHUHGF6Gu6/dFrYsCzySBQ==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.2.tgz", + "integrity": "sha512-9u/tQJMcC2aGq7KXpGivMm1mgq7oRJKXphDwdypPd/j21j/2znamPU8WkXgnhUaTrSFNIt8XhOyCAupg8/GbwQ==", + "license": "MIT", "dependencies": { - "@swc/helpers": "^0.5.0" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-roving-focus": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-types/button": { - "version": "3.10.2", - "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.10.2.tgz", - "integrity": "sha512-h8SB/BLoCgoBulCpyzaoZ+miKXrolK9XC48+n1dKJXT8g4gImrficurDW6+PRTQWaRai0Q0A6bu8UibZOU4syg==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-toast": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.4.tgz", + "integrity": "sha512-Sch9idFJHJTMH9YNpxxESqABcAFweJG4tKv+0zo0m5XBvUSL8FM5xKcJLFLXononpePs8IclyX1KieL5SDUNgA==", + "license": "MIT", "dependencies": { - "@react-types/shared": "^3.27.0" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.3", + "@radix-ui/react-portal": "1.1.3", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.1" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-types/button/node_modules/@react-types/shared": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz", - "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==", - "license": "Apache-2.0", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-types/calendar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@react-types/calendar/-/calendar-3.6.0.tgz", - "integrity": "sha512-BtFh4BFwvsYlsaSqUOVxlqXZSlJ6u4aozgO3PwHykhpemwidlzNwm9qDZhcMWPioNF/w2cU/6EqhvEKUHDnFZg==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-tooltip": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.7.tgz", + "integrity": "sha512-ss0s80BC0+g0+Zc53MvilcnTYSOi4mSuFWBPYPuTOFGjx+pUU+ZrmamMNwS56t8MTFlniA5ocjd4jYm/CdhbOg==", + "license": "MIT", "dependencies": { - "@internationalized/date": "^3.7.0", - "@react-types/shared": "^3.27.0" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.4", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.1", + "@radix-ui/react-portal": "1.1.3", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-slot": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.1" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" - } - }, - "node_modules/@react-types/calendar/node_modules/@internationalized/date": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.7.0.tgz", - "integrity": "sha512-VJ5WS3fcVx0bejE/YHfbDKR/yawZgKqn/if+oEeLqNwBtPzVB06olkfcnojTmEMX+gTpH+FlQ69SHNitJ8/erQ==", - "license": "Apache-2.0", + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.4.tgz", + "integrity": "sha512-XDUI0IVYVSwjMXxM6P4Dfti7AH+Y4oS/TB+sglZ/EXc7cqLwGAmp1NlMrcUjj7ks6R5WTZuWKv44FBbLpwU3sA==", + "license": "MIT", "dependencies": { - "@swc/helpers": "^0.5.0" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@react-types/calendar/node_modules/@react-types/shared": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz", - "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "license": "MIT", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-types/checkbox": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.9.1.tgz", - "integrity": "sha512-0x/KQcipfNM9Nvy6UMwYG25roRLvsiqf0J3woTYylNNWzF+72XT0iI5FdJkE3w2wfa0obmSoeq4WcbFREQrH/A==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "license": "MIT", "dependencies": { - "@react-types/shared": "^3.27.0" + "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-types/checkbox/node_modules/@react-types/shared": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz", - "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-types/datepicker": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/@react-types/datepicker/-/datepicker-3.9.0.tgz", - "integrity": "sha512-dbKL5Qsm2MQwOTtVQdOcKrrphcXAqDD80WLlSQrBLg+waDuuQ7H+TrvOT0thLKloNBlFUGnZZfXGRHINpih/0g==", - "license": "Apache-2.0", - "dependencies": { - "@internationalized/date": "^3.6.0", - "@react-types/calendar": "^3.5.0", - "@react-types/overlays": "^3.8.11", - "@react-types/shared": "^3.26.0" - }, + "node_modules/@radix-ui/react-use-effect-event/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-types/overlays": { - "version": "3.8.12", - "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.8.12.tgz", - "integrity": "sha512-ZvR1t0YV7/6j+6OD8VozKYjvsXT92+C/2LOIKozy7YUNS5KI4MkXbRZzJvkuRECVZOmx8JXKTUzhghWJM/3QuQ==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", + "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", + "license": "MIT", "dependencies": { - "@react-types/shared": "^3.27.0" + "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-types/overlays/node_modules/@react-types/shared": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz", - "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "license": "MIT", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-types/shared": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.26.0.tgz", - "integrity": "sha512-6FuPqvhmjjlpEDLTiYx29IJCbCNWPlsyO+ZUmCUXzhUv2ttShOXfw8CmeHWHftT/b2KweAWuzqSlfeXPR76jpw==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "license": "MIT", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-types/switch": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/@react-types/switch/-/switch-3.5.8.tgz", - "integrity": "sha512-sL7jmh8llF8BxzY4HXkSU4bwU8YU6gx45P85D0AdYXgRHxU9Cp7BQPOMF4pJoQ8TTej05MymY5q7xvJVmxUTAQ==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "license": "MIT", "dependencies": { - "@react-types/shared": "^3.27.0" + "@radix-ui/rect": "1.1.0" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@react-types/switch/node_modules/@react-types/shared": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz", - "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==", - "license": "Apache-2.0", + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@rollup/pluginutils": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", - "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", - "dev": true, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.1.tgz", + "integrity": "sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg==", "license": "MIT", "dependencies": { - "estree-walker": "^2.0.1", - "picomatch": "^2.2.2" + "@radix-ui/react-primitive": "2.0.1" }, - "engines": { - "node": ">= 8.0.0" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.2.tgz", - "integrity": "sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "node_modules/@radix-ui/rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", + "license": "MIT" }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.2.tgz", - "integrity": "sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==", + "node_modules/@react-aria/focus": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.19.0.tgz", + "integrity": "sha512-hPF9EXoUQeQl1Y21/rbV2H4FdUR2v+4/I0/vB+8U3bT1CJ+1AFj1hc/rqx2DqEwDlEwOHN+E4+mRahQmlybq0A==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/interactions": "^3.22.5", + "@react-aria/utils": "^3.26.0", + "@react-types/shared": "^3.26.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/i18n": { + "version": "3.12.4", + "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.4.tgz", + "integrity": "sha512-j9+UL3q0Ls8MhXV9gtnKlyozq4aM95YywXqnmJtzT1rYeBx7w28hooqrWkCYLfqr4OIryv1KUnPiCSLwC2OC7w==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.6.0", + "@internationalized/message": "^3.1.6", + "@internationalized/number": "^3.6.0", + "@internationalized/string": "^3.2.5", + "@react-aria/ssr": "^3.9.7", + "@react-aria/utils": "^3.26.0", + "@react-types/shared": "^3.26.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.22.5", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.22.5.tgz", + "integrity": "sha512-kMwiAD9E0TQp+XNnOs13yVJghiy8ET8L0cbkeuTgNI96sOAp/63EJ1FSrDf17iD8sdjt41LafwX/dKXW9nCcLQ==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.7", + "@react-aria/utils": "^3.26.0", + "@react-types/shared": "^3.26.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/overlays": { + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.24.0.tgz", + "integrity": "sha512-0kAXBsMNTc/a3M07tK9Cdt/ea8CxTAEJ223g8YgqImlmoBBYAL7dl5G01IOj67TM64uWPTmZrOklBchHWgEm3A==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/focus": "^3.19.0", + "@react-aria/i18n": "^3.12.4", + "@react-aria/interactions": "^3.22.5", + "@react-aria/ssr": "^3.9.7", + "@react-aria/utils": "^3.26.0", + "@react-aria/visually-hidden": "^3.8.18", + "@react-stately/overlays": "^3.6.12", + "@react-types/button": "^3.10.1", + "@react-types/overlays": "^3.8.11", + "@react-types/shared": "^3.26.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.7.tgz", + "integrity": "sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/switch": { + "version": "3.6.10", + "resolved": "https://registry.npmjs.org/@react-aria/switch/-/switch-3.6.10.tgz", + "integrity": "sha512-FtaI9WaEP1tAmra1sYlAkYXg9x75P5UtgY8pSbe9+1WRyWbuE1QZT+RNCTi3IU4fZ7iJQmXH6+VaMyzPlSUagw==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/toggle": "^3.10.10", + "@react-stately/toggle": "^3.8.0", + "@react-types/shared": "^3.26.0", + "@react-types/switch": "^3.5.7", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/switch/node_modules/@react-aria/toggle": { + "version": "3.10.11", + "resolved": "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.10.11.tgz", + "integrity": "sha512-J3jO3KJiUbaYVDEpeXSBwqcyKxpi9OreiHRGiaxb6VwB+FWCj7Gb2WKajByXNyfs8jc6kX9VUFaXa7jze60oEQ==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/focus": "^3.19.1", + "@react-aria/interactions": "^3.23.0", + "@react-aria/utils": "^3.27.0", + "@react-stately/toggle": "^3.8.1", + "@react-types/checkbox": "^3.9.1", + "@react-types/shared": "^3.27.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/switch/node_modules/@react-aria/toggle/node_modules/@react-aria/focus": { + "version": "3.19.1", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.19.1.tgz", + "integrity": "sha512-bix9Bu1Ue7RPcYmjwcjhB14BMu2qzfJ3tMQLqDc9pweJA66nOw8DThy3IfVr8Z7j2PHktOLf9kcbiZpydKHqzg==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/interactions": "^3.23.0", + "@react-aria/utils": "^3.27.0", + "@react-types/shared": "^3.27.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/switch/node_modules/@react-aria/toggle/node_modules/@react-aria/interactions": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.23.0.tgz", + "integrity": "sha512-0qR1atBIWrb7FzQ+Tmr3s8uH5mQdyRH78n0krYaG8tng9+u1JlSi8DGRSaC9ezKyNB84m7vHT207xnHXGeJ3Fg==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.7", + "@react-aria/utils": "^3.27.0", + "@react-types/shared": "^3.27.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/switch/node_modules/@react-aria/toggle/node_modules/@react-aria/utils": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz", + "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.7", + "@react-stately/utils": "^3.10.5", + "@react-types/shared": "^3.27.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/switch/node_modules/@react-stately/toggle": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.8.1.tgz", + "integrity": "sha512-MVpe79ghVQiwLmVzIPhF/O/UJAUc9B+ZSylVTyJiEPi0cwhbkKGQv9thOF0ebkkRkace5lojASqUAYtSTZHQJA==", + "license": "Apache-2.0", + "dependencies": { + "@react-stately/utils": "^3.10.5", + "@react-types/checkbox": "^3.9.1", + "@react-types/shared": "^3.27.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/switch/node_modules/@react-types/shared": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz", + "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/utils": { + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.26.0.tgz", + "integrity": "sha512-LkZouGSjjQ0rEqo4XJosS4L3YC/zzQkfRM3KoqK6fUOmUJ9t0jQ09WjiF+uOoG9u+p30AVg3TrZRUWmoTS+koQ==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.7", + "@react-stately/utils": "^3.10.5", + "@react-types/shared": "^3.26.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/visually-hidden": { + "version": "3.8.18", + "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.18.tgz", + "integrity": "sha512-l/0igp+uub/salP35SsNWq5mGmg3G5F5QMS1gDZ8p28n7CgjvzyiGhJbbca7Oxvaw1HRFzVl9ev+89I7moNnFQ==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/interactions": "^3.22.5", + "@react-aria/utils": "^3.26.0", + "@react-types/shared": "^3.26.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/overlays": { + "version": "3.6.13", + "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.13.tgz", + "integrity": "sha512-WsU85Gf/b+HbWsnnYw7P/Ila3wD+C37Uk/WbU4/fHgJ26IEOWsPE6wlul8j54NZ1PnLNhV9Fn+Kffi+PaJMQXQ==", + "license": "Apache-2.0", + "dependencies": { + "@react-stately/utils": "^3.10.5", + "@react-types/overlays": "^3.8.12", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/toggle": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.8.0.tgz", + "integrity": "sha512-pyt/k/J8BwE/2g6LL6Z6sMSWRx9HEJB83Sm/MtovXnI66sxJ2EfQ1OaXB7Su5PEL9OMdoQF6Mb+N1RcW3zAoPw==", + "license": "Apache-2.0", + "dependencies": { + "@react-stately/utils": "^3.10.5", + "@react-types/checkbox": "^3.9.0", + "@react-types/shared": "^3.26.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/utils": { + "version": "3.10.5", + "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.5.tgz", + "integrity": "sha512-iMQSGcpaecghDIh3mZEpZfoFH3ExBwTtuBEcvZ2XnGzCgQjeYXcMdIUwAfVQLXFTdHUHGF6Gu6/dFrYsCzySBQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/button": { + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.10.2.tgz", + "integrity": "sha512-h8SB/BLoCgoBulCpyzaoZ+miKXrolK9XC48+n1dKJXT8g4gImrficurDW6+PRTQWaRai0Q0A6bu8UibZOU4syg==", + "license": "Apache-2.0", + "dependencies": { + "@react-types/shared": "^3.27.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/button/node_modules/@react-types/shared": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz", + "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/calendar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@react-types/calendar/-/calendar-3.6.0.tgz", + "integrity": "sha512-BtFh4BFwvsYlsaSqUOVxlqXZSlJ6u4aozgO3PwHykhpemwidlzNwm9qDZhcMWPioNF/w2cU/6EqhvEKUHDnFZg==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.7.0", + "@react-types/shared": "^3.27.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/calendar/node_modules/@internationalized/date": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.7.0.tgz", + "integrity": "sha512-VJ5WS3fcVx0bejE/YHfbDKR/yawZgKqn/if+oEeLqNwBtPzVB06olkfcnojTmEMX+gTpH+FlQ69SHNitJ8/erQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@react-types/calendar/node_modules/@react-types/shared": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz", + "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/checkbox": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.9.1.tgz", + "integrity": "sha512-0x/KQcipfNM9Nvy6UMwYG25roRLvsiqf0J3woTYylNNWzF+72XT0iI5FdJkE3w2wfa0obmSoeq4WcbFREQrH/A==", + "license": "Apache-2.0", + "dependencies": { + "@react-types/shared": "^3.27.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/checkbox/node_modules/@react-types/shared": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz", + "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/datepicker": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@react-types/datepicker/-/datepicker-3.9.0.tgz", + "integrity": "sha512-dbKL5Qsm2MQwOTtVQdOcKrrphcXAqDD80WLlSQrBLg+waDuuQ7H+TrvOT0thLKloNBlFUGnZZfXGRHINpih/0g==", + "license": "Apache-2.0", + "dependencies": { + "@internationalized/date": "^3.6.0", + "@react-types/calendar": "^3.5.0", + "@react-types/overlays": "^3.8.11", + "@react-types/shared": "^3.26.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/overlays": { + "version": "3.8.12", + "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.8.12.tgz", + "integrity": "sha512-ZvR1t0YV7/6j+6OD8VozKYjvsXT92+C/2LOIKozy7YUNS5KI4MkXbRZzJvkuRECVZOmx8JXKTUzhghWJM/3QuQ==", + "license": "Apache-2.0", + "dependencies": { + "@react-types/shared": "^3.27.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/overlays/node_modules/@react-types/shared": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz", + "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/shared": { + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.26.0.tgz", + "integrity": "sha512-6FuPqvhmjjlpEDLTiYx29IJCbCNWPlsyO+ZUmCUXzhUv2ttShOXfw8CmeHWHftT/b2KweAWuzqSlfeXPR76jpw==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/switch": { + "version": "3.5.8", + "resolved": "https://registry.npmjs.org/@react-types/switch/-/switch-3.5.8.tgz", + "integrity": "sha512-sL7jmh8llF8BxzY4HXkSU4bwU8YU6gx45P85D0AdYXgRHxU9Cp7BQPOMF4pJoQ8TTej05MymY5q7xvJVmxUTAQ==", + "license": "Apache-2.0", + "dependencies": { + "@react-types/shared": "^3.27.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/switch/node_modules/@react-types/shared": { + "version": "3.27.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz", + "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", "cpu": [ "arm64" ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", + "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.4" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", + "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^3.1.0" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", + "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", + "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", + "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/transformers": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz", + "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/types": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", + "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/chrome": { + "version": "0.0.296", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.296.tgz", + "integrity": "sha512-EWVcJlIXi2PzKfnzOXkIfySMFkdOQ6DiKkhbuHeuvTxOcVYeUBXbfiExSJ5EDS4M0gAChO/15tDHqMWP47muYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/filesystem": "*", + "@types/har-format": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/filesystem": { + "version": "0.0.36", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz", + "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/filewriter": "*" + } + }, + "node_modules/@types/filewriter": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz", + "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/gapi.client": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/gapi.client/-/gapi.client-1.0.8.tgz", + "integrity": "sha512-qJQUmmumbYym3Amax0S8CVzuSngcXsC1fJdwRS2zeW5lM63zXkw4wJFP+bG0jzgi0R6EsJKoHnGNVTDbOyG1ng==", + "license": "MIT" + }, + "node_modules/@types/gapi.client.calendar-v3": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/gapi.client.calendar-v3/-/gapi.client.calendar-v3-0.0.4.tgz", + "integrity": "sha512-EwXzPhk98ef+esL1R951e3HFeMQSP6hSBv+YBbaAJoOLLRANV2JY5KHOImBSkk7HKt1PiarQGx5BD8+4eHeeIQ==", + "license": "MIT", + "dependencies": { + "@maxim_mazurok/gapi.client.calendar-v3": "latest" + } + }, + "node_modules/@types/gapi.client.discovery-v1": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/gapi.client.discovery-v1/-/gapi.client.discovery-v1-0.0.4.tgz", + "integrity": "sha512-uevhRumNE65F5mf2gABLaReOmbFSXONuzFZjNR3dYv6BmkHg+wciubHrfBAsp3554zNo3Dcg6dUAlwMqQfpwjQ==", + "license": "MIT", + "dependencies": { + "@maxim_mazurok/gapi.client.discovery-v1": "latest" + } + }, + "node_modules/@types/har-format": { + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz", + "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", + "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", + "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", + "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", + "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/type-utils": "8.20.0", + "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", + "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", + "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", + "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.20.0", + "@typescript-eslint/utils": "8.20.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", + "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", + "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/visitor-keys": "8.20.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", + "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.20.0", + "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/typescript-estree": "8.20.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", + "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.20.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", + "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.0", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.0.tgz", + "integrity": "sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.0", + "@vitest/utils": "4.1.0", + "chai": "^6.2.2", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.0.tgz", + "integrity": "sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.0", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/mocker/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.0.tgz", + "integrity": "sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.0.tgz", + "integrity": "sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.0", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.0.tgz", + "integrity": "sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.0", + "@vitest/utils": "4.1.0", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.0.tgz", + "integrity": "sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.0.tgz", + "integrity": "sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.0", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.30.tgz", + "integrity": "sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@vue/shared": "3.5.30", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-core/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.30.tgz", + "integrity": "sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.30", + "@vue/shared": "3.5.30" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz", + "integrity": "sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@vue/compiler-core": "3.5.30", + "@vue/compiler-dom": "3.5.30", + "@vue/compiler-ssr": "3.5.30", + "@vue/shared": "3.5.30", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.8", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.30.tgz", + "integrity": "sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.30", + "@vue/shared": "3.5.30" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.9" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.9", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.30.tgz", + "integrity": "sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.30" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.30.tgz", + "integrity": "sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.30", + "@vue/shared": "3.5.30" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.30.tgz", + "integrity": "sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.30", + "@vue/runtime-core": "3.5.30", + "@vue/shared": "3.5.30", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.30.tgz", + "integrity": "sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.30", + "@vue/shared": "3.5.30" + }, + "peerDependencies": { + "vue": "3.5.30" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.30.tgz", + "integrity": "sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/integrations": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", + "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vueuse/core": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "funding": { + "url": "https://github.com/sponsors/antfu" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.2.tgz", - "integrity": "sha512-OZuTVTpj3CDSIxmPgGH8en/XtirV5nfljHZ3wrNwvgkT5DQLhIKAeuFSiwtbMto6oVexV0k1F1zqURPKf5rI1Q==", - "cpu": [ - "arm64" - ], + "node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.2.tgz", - "integrity": "sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==", - "cpu": [ - "x64" - ], + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.2.tgz", - "integrity": "sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==", - "cpu": [ - "arm64" - ], + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webcomponents/custom-elements": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.6.0.tgz", + "integrity": "sha512-CqTpxOlUCPWRNUPZDxT5v2NnHXA4oox612iUGnmTUGQFhZ1Gkj8kirtl/2wcF6MqX7+PqqicZzOCBKKfIn0dww==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true, + "license": "Apache-2.0", + "peer": true + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "devOptional": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.2.tgz", - "integrity": "sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==", - "cpu": [ - "x64" - ], + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "peer": true, + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.2.tgz", - "integrity": "sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==", - "cpu": [ - "arm" - ], + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.2.tgz", - "integrity": "sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==", - "cpu": [ - "arm" - ], + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.2.tgz", - "integrity": "sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">= 14" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.2.tgz", - "integrity": "sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ==", - "cpu": [ - "arm64" - ], + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.50.2.tgz", - "integrity": "sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw==", - "cpu": [ - "loong64" - ], + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.2.tgz", - "integrity": "sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag==", - "cpu": [ - "ppc64" - ], + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.2.tgz", - "integrity": "sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ==", - "cpu": [ - "riscv64" - ], + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "peer": true }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.2.tgz", - "integrity": "sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw==", - "cpu": [ - "riscv64" - ], + "node_modules/algoliasearch": { + "version": "5.49.2", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.49.2.tgz", + "integrity": "sha512-1K0wtDaRONwfhL4h8bbJ9qTjmY6rhGgRvvagXkMBsAOMNr+3Q2SffHECh9DIuNVrMA1JwA0zCwhyepgBZVakng==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@algolia/abtesting": "1.15.2", + "@algolia/client-abtesting": "5.49.2", + "@algolia/client-analytics": "5.49.2", + "@algolia/client-common": "5.49.2", + "@algolia/client-insights": "5.49.2", + "@algolia/client-personalization": "5.49.2", + "@algolia/client-query-suggestions": "5.49.2", + "@algolia/client-search": "5.49.2", + "@algolia/ingestion": "1.49.2", + "@algolia/monitoring": "1.49.2", + "@algolia/recommend": "5.49.2", + "@algolia/requester-browser-xhr": "5.49.2", + "@algolia/requester-fetch": "5.49.2", + "@algolia/requester-node-http": "5.49.2" + }, + "engines": { + "node": ">= 14.0.0" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.2.tgz", - "integrity": "sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w==", - "cpu": [ - "s390x" - ], - "dev": true, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.2.tgz", - "integrity": "sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==", - "cpu": [ - "x64" - ], + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.2.tgz", - "integrity": "sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw==", - "cpu": [ - "x64" - ], + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.2.tgz", - "integrity": "sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==", - "cpu": [ - "arm64" - ], + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] + "engines": { + "node": ">=12" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.2.tgz", - "integrity": "sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA==", - "cpu": [ - "arm64" - ], + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "MIT" }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.2.tgz", - "integrity": "sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA==", - "cpu": [ - "ia32" - ], + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.2.tgz", - "integrity": "sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA==", - "cpu": [ - "x64" + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "MIT" }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "node_modules/baseline-browser-mapping": { + "version": "2.10.8", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", + "integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==", + "dev": true, "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "engines": { + "node": "*" } }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", "dependencies": { - "@babel/types": "^7.20.7" + "balanced-match": "^1.0.0" } }, - "node_modules/@types/chrome": { - "version": "0.0.296", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.296.tgz", - "integrity": "sha512-EWVcJlIXi2PzKfnzOXkIfySMFkdOQ6DiKkhbuHeuvTxOcVYeUBXbfiExSJ5EDS4M0gAChO/15tDHqMWP47muYw==", - "dev": true, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "license": "MIT", "dependencies": { - "@types/filesystem": "*", - "@types/har-format": "*" + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "@types/ms": "*" + "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" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "devOptional": true, "license": "MIT", - "peer": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } + "peer": true }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", - "peer": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@types/filesystem": { - "version": "0.0.36", - "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz", - "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==", - "dev": true, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { - "@types/filewriter": "*" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@types/filewriter": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz", - "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "license": "MIT" - }, - "node_modules/@types/gapi.client": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/gapi.client/-/gapi.client-1.0.8.tgz", - "integrity": "sha512-qJQUmmumbYym3Amax0S8CVzuSngcXsC1fJdwRS2zeW5lM63zXkw4wJFP+bG0jzgi0R6EsJKoHnGNVTDbOyG1ng==", - "license": "MIT" - }, - "node_modules/@types/gapi.client.calendar": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@types/gapi.client.calendar/-/gapi.client.calendar-3.0.12.tgz", - "integrity": "sha512-3H4hubmAuOkYHGSoVkxYAz6zg2g5LhQ1Y0/GLDIt1AkebKZgGiK+5JNFq4Nn2qDkjFh8pMY3TLIC/J5ueMd6YA==", - "deprecated": "use @types/gapi.client.calendar-v3 instead; see https://github.com/Maxim-Mazurok/google-api-typings-generator/issues/652 for details", "license": "MIT", - "dependencies": { - "@maxim_mazurok/gapi.client.calendar-v3": "latest" + "engines": { + "node": ">=6" } }, - "node_modules/@types/gapi.client.discovery-v1": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@types/gapi.client.discovery-v1/-/gapi.client.discovery-v1-0.0.4.tgz", - "integrity": "sha512-uevhRumNE65F5mf2gABLaReOmbFSXONuzFZjNR3dYv6BmkHg+wciubHrfBAsp3554zNo3Dcg6dUAlwMqQfpwjQ==", + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", "license": "MIT", - "dependencies": { - "@maxim_mazurok/gapi.client.discovery-v1": "latest" + "engines": { + "node": ">= 6" } }, - "node_modules/@types/har-format": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz", - "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==", + "node_modules/caniuse-lite": { + "version": "1.0.30001779", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001779.tgz", + "integrity": "sha512-U5og2PN7V4DMgF50YPNtnZJGWVLFjjsN3zb6uMT5VGYIewieDj1upwfuVNXf4Kor+89c3iCRJnSzMD5LmTvsfA==", "dev": true, - "license": "MIT" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", "dev": true, - "license": "MIT" + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, - "node_modules/@types/node": { - "version": "22.10.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", - "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "undici-types": "~6.20.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.3.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", - "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", - "devOptional": true, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/@types/react-dom": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", - "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", - "devOptional": true, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, "license": "MIT", - "peerDependencies": { - "@types/react": "^18.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", - "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", + "node_modules/cheerio": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/type-utils": "8.20.0", - "@typescript-eslint/utils": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.0" + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18.17" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", - "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/typescript-estree": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", - "debug": "^4.3.4" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 8.10.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://paulmillr.com/funding/" }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", - "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", - "dev": true, - "license": "MIT", + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", "dependencies": { - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0" + "is-glob": "^4.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">= 6" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", - "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.20.0", - "@typescript-eslint/utils": "8.20.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.0.0" - }, + "peer": true, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.0" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "url": "https://polar.sh/cva" } }, - "node_modules/@typescript-eslint/types": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", - "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", - "dev": true, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=6" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", - "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", - "dev": true, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.0" + "color-convert": "^2.0.1", + "color-string": "^1.9.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" + "engines": { + "node": ">=7.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/color2k": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", + "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.8" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", - "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/typescript-estree": "8.20.0" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "url": "https://opencollective.com/express" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", - "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", - "eslint-visitor-keys": "^4.2.0" + "is-what": "^5.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/mesqueeb" } }, - "node_modules/@vitejs/plugin-react": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", - "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", - "dev": true, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { - "@babel/core": "^7.26.0", - "@babel/plugin-transform-react-jsx-self": "^7.25.9", - "@babel/plugin-transform-react-jsx-source": "^7.25.9", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + "node": ">= 8" } }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, - "license": "MIT", - "peer": true, + "license": "BSD-2-Clause", "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true, - "license": "MIT", - "peer": true + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", "dev": true, + "license": "MIT" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "license": "MIT", - "peer": true + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "license": "MIT", - "peer": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" + "engines": { + "node": ">= 12" } }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "dev": true, - "license": "Apache-2.0", - "peer": true, + "license": "MIT", "dependencies": { - "@xtuc/long": "4.2.2" + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", "license": "MIT", - "peer": true + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dev": true, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", - "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" + "engines": { + "node": ">=0.4.0" } }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" + "engines": { + "node": ">=6" } }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/@webcomponents/custom-elements": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.6.0.tgz", - "integrity": "sha512-CqTpxOlUCPWRNUPZDxT5v2NnHXA4oox612iUGnmTUGQFhZ1Gkj8kirtl/2wcF6MqX7+PqqicZzOCBKKfIn0dww==", + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", "dev": true, - "license": "BSD-3-Clause" + "license": "MIT" }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true, - "license": "Apache-2.0", - "peer": true + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" }, "engines": { - "node": ">=0.4.0" + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { - "acorn": "^8.11.0" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": ">=0.4.0" + "node": ">= 0.4" } }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "license": "MIT", - "engines": { - "node": ">= 14" + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/electron-to-chromium": { + "version": "1.5.313", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz", + "integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" } }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "node_modules/enhanced-resolve": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", + "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "engines": { + "node": ">=10.13.0" } }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", - "peer": true + "engines": { + "node": ">= 0.4" + } }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", - "peer": true, - "peerDependencies": { - "ajv": "^6.9.1" + "engines": { + "node": ">= 0.4" } }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "node_modules/es-module-lexer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "es-errors": "^1.3.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">= 0.4" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 0.4" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "node_modules/esbuild": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "devOptional": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=6" } }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "Python-2.0" - }, - "node_modules/aria-hidden": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", - "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/autoprefixer": { - "version": "10.4.20", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", - "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", - "postcss-value-parser": "^4.2.0" + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" }, "bin": { - "autoprefixer": "bin/autoprefixer" + "eslint": "bin/eslint.js" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" }, "peerDependencies": { - "postcss": "^8.1.0" + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/axios": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.0.tgz", - "integrity": "sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg==", + "node_modules/eslint-plugin-react-hooks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", + "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", + "dev": true, "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bignumber.js": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", - "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.18.tgz", + "integrity": "sha512-IRGEoFn3OKalm3hjfolEWGqoF/jPqeEYFp+C8B0WMzwGwBMvlRDQd06kghDhF0C61uJ6WfSDhEZE/sAQjduKgw==", + "dev": true, "license": "MIT", - "engines": { - "node": "*" + "peerDependencies": { + "eslint": ">=8.40" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "ISC" + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "node_modules/brace-expansion": { + "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", @@ -4242,220 +7332,182 @@ "concat-map": "0.0.1" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", "dependencies": { - "fill-range": "^7.1.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", "bin": { - "browserslist": "cli.js" + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": ">=4" } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "node_modules/esprima-extract-comments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/esprima-extract-comments/-/esprima-extract-comments-1.1.0.tgz", + "integrity": "sha512-sBQUnvJwpeE9QnPrxh7dpI/dp67erYG4WXEAreAMoelPRpMR7NWb4YtwRPn9b+H1uLQKl/qS8WYmyaljTpjIsw==", "dev": true, "license": "MIT", - "peer": true + "dependencies": { + "esprima": "^4.0.0" + }, + "engines": { + "node": ">=4" + } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" + "estraverse": "^5.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10" } }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "estraverse": "^5.2.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4.0" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "engines": { - "node": ">=6" + "node": ">=4.0" } }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "license": "MIT", + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001743", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", - "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.x" + } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=12.0.0" } }, - "node_modules/cheerio": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", - "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/extract-comments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/extract-comments/-/extract-comments-1.1.0.tgz", + "integrity": "sha512-dzbZV2AdSSVW/4E7Ti5hZdHWbA+Z80RJsJhr5uiL10oyjl/gy7/o+HI1HwK4/WSZhlq4SNKU3oUzXlM13Qx02Q==", "dev": true, "license": "MIT", "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "encoding-sniffer": "^0.2.0", - "htmlparser2": "^9.1.0", - "parse5": "^7.1.2", - "parse5-htmlparser2-tree-adapter": "^7.0.0", - "parse5-parser-stream": "^7.1.2", - "undici": "^6.19.5", - "whatwg-mimetype": "^4.0.0" + "esprima-extract-comments": "^1.1.0", + "parse-code-context": "^1.0.0" }, "engines": { - "node": ">=18.17" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + "node": ">=6" } }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } + "license": "MIT" }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" }, "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": ">=8.6.0" } }, - "node_modules/chokidar/node_modules/glob-parent": { + "node_modules/fast-glob/node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", @@ -4467,1544 +7519,1651 @@ "node": ">= 6" } }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.0" - } + "license": "MIT" }, - "node_modules/class-variance-authority": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", - "license": "Apache-2.0", - "dependencies": { - "clsx": "^2.1.1" - }, - "funding": { - "url": "https://polar.sh/cva" - } + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/fastq": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" } }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], "license": "MIT", "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" }, "engines": { - "node": ">=12.5.0" + "node": "^12.20 || >= 14.13" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=16.0.0" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "license": "MIT", "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/color2k": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", - "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "node_modules/focus-trap": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.8.0.tgz", + "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", "license": "MIT", + "dependencies": { + "tabbable": "^6.4.0" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, "engines": { - "node": ">=18" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" }, "engines": { - "node": ">= 8" + "node": ">= 6" } }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" + "fetch-blob": "^3.1.2" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "engines": { + "node": ">=12.20.0" } }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "engines": { - "node": ">= 6" + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/fb55" + "type": "patreon", + "url": "https://github.com/sponsors/rawify" } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "node_modules/framer-motion": { + "version": "12.23.24", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz", + "integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==", "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" + "dependencies": { + "motion-dom": "^12.23.23", + "motion-utils": "^12.23.6", + "tslib": "^2.4.0" }, - "engines": { - "node": ">=4" + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } } }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, - "license": "MIT" + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 12" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/date-fns": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", - "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", + "node_modules/gaxios": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.1.tgz", + "integrity": "sha512-Odju3uBUJyVCkW64nLD4wKLhbh93bh6vIg/ZIXkWiLPBrdgtc65+tls/qml+un3pr6JqYVFDZbbmLDQT68rTOQ==", + "license": "Apache-2.0", "dependencies": { - "ms": "^2.1.3" + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=18" } }, - "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", - "license": "MIT" + "node_modules/gcp-metadata": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz", + "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "license": "Apache-2.0" - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "engines": { + "node": ">= 0.4" } }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", "dependencies": { - "domelementtype": "^2.3.0" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "node": ">=10.13.0" } }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true, "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" + "peer": true + }, + "node_modules/globals": { + "version": "15.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", + "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" }, "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "license": "MIT" + }, + "node_modules/google-auth-library": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.3.0.tgz", + "integrity": "sha512-ylSE3RlCRZfZB56PFJSfUCuiuPq83Fx8hqu1KPWGK8FVdSaxlp/qkeMMX/DT/18xkwXIHvXEXkZsljRwfrdEfQ==", + "license": "Apache-2.0", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.0.0", + "gcp-metadata": "^7.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" + "node_modules/google-logging-utils": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.1.tgz", + "integrity": "sha512-rcX58I7nqpu4mbKztFeOAObbomBbHU2oIb/d3tJfF3dizGSApqtSwYJigGCooHdnMyQBIw8BrWyK96w3YXgr6A==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "node_modules/googleapis": { + "version": "160.0.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-160.0.0.tgz", + "integrity": "sha512-lAGFQVSeYvWKCxeCQKo20xWFFDgnWuJYbLn92IgLrT4UTYVOGdrZ9XTqgWJf316isE9KdfuDY5X8Tu4ZrXSFig==", "license": "Apache-2.0", "dependencies": { - "safe-buffer": "^5.0.1" + "google-auth-library": "^10.2.0", + "googleapis-common": "^8.0.0" + }, + "engines": { + "node": ">=18" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.82", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.82.tgz", - "integrity": "sha512-Zq16uk1hfQhyGx5GpwPAYDwddJuSGhtRhgOA2mCxANYaDT79nAeGnaXogMGng4KqLaJUVnOnuL0+TDop9nLOiA==", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" + "node_modules/googleapis-common": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-8.0.0.tgz", + "integrity": "sha512-66if47It7y+Sab3HMkwEXx1kCq9qUC9px8ZXoj1CMrmLmUw81GpbnsNlXnlyZyGbGPGcj+tDD9XsZ23m7GLaJQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "gaxios": "^7.0.0-rc.4", + "google-auth-library": "^10.1.0", + "qs": "^6.7.0", + "url-template": "^2.0.8" + }, + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/encoding-sniffer": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", - "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", - "dev": true, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", - "dependencies": { - "iconv-lite": "^0.6.3", - "whatwg-encoding": "^3.1.1" + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/enhanced-resolve": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", - "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, + "license": "MIT" + }, + "node_modules/gtoken": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "gaxios": "^7.0.0", + "jws": "^4.0.0" }, "engines": { - "node": ">=10.13.0" + "node": ">=18" } }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "license": "MIT", - "peer": true - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", "dependencies": { - "es-errors": "^1.3.0" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" } }, - "node_modules/esbuild": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", - "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", - "devOptional": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.0", - "@esbuild/android-arm": "0.25.0", - "@esbuild/android-arm64": "0.25.0", - "@esbuild/android-x64": "0.25.0", - "@esbuild/darwin-arm64": "0.25.0", - "@esbuild/darwin-x64": "0.25.0", - "@esbuild/freebsd-arm64": "0.25.0", - "@esbuild/freebsd-x64": "0.25.0", - "@esbuild/linux-arm": "0.25.0", - "@esbuild/linux-arm64": "0.25.0", - "@esbuild/linux-ia32": "0.25.0", - "@esbuild/linux-loong64": "0.25.0", - "@esbuild/linux-mips64el": "0.25.0", - "@esbuild/linux-ppc64": "0.25.0", - "@esbuild/linux-riscv64": "0.25.0", - "@esbuild/linux-s390x": "0.25.0", - "@esbuild/linux-x64": "0.25.0", - "@esbuild/netbsd-arm64": "0.25.0", - "@esbuild/netbsd-x64": "0.25.0", - "@esbuild/openbsd-arm64": "0.25.0", - "@esbuild/openbsd-x64": "0.25.0", - "@esbuild/sunos-x64": "0.25.0", - "@esbuild/win32-arm64": "0.25.0", - "@esbuild/win32-ia32": "0.25.0", - "@esbuild/win32-x64": "0.25.0" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", "dev": true, "license": "MIT", - "engines": { - "node": ">=10" + "dependencies": { + "@types/hast": "^3.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/eslint": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", - "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.10.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.18.0", - "@eslint/plugin-kit": "^0.2.5", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" + "whatwg-encoding": "^3.1.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "node": ">=18" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", - "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", - "dev": true, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + "dependencies": { + "void-elements": "3.1.0" } }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.18.tgz", - "integrity": "sha512-IRGEoFn3OKalm3hjfolEWGqoF/jPqeEYFp+C8B0WMzwGwBMvlRDQd06kghDhF0C61uJ6WfSDhEZE/sAQjduKgw==", + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", "dev": true, "license": "MIT", - "peerDependencies": { - "eslint": ">=8.40" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", "dev": true, - "license": "BSD-2-Clause", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" } }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": ">= 14" } }, - "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", "dependencies": { - "acorn": "^8.14.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "agent-base": "^7.1.2", + "debug": "4" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">= 14" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "node_modules/i18next": { + "version": "25.8.18", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.18.tgz", + "integrity": "sha512-lzY5X83BiL5AP77+9DydbrqkQHFN9hUzWGjqjLpPcp5ZOzuu1aSoKaU3xbBLSjWx9dAzW431y+d+aogxOZaKRA==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6" }, - "engines": { - "node": ">=4" + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/esprima-extract-comments": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/esprima-extract-comments/-/esprima-extract-comments-1.1.0.tgz", - "integrity": "sha512-sBQUnvJwpeE9QnPrxh7dpI/dp67erYG4WXEAreAMoelPRpMR7NWb4YtwRPn9b+H1uLQKl/qS8WYmyaljTpjIsw==", + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", "dependencies": { - "esprima": "^4.0.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">= 4" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "estraverse": "^5.2.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=4.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">=0.8.19" } }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.8.x" + "node_modules/intl-messageformat": { + "version": "10.7.14", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.14.tgz", + "integrity": "sha512-mMGnE4E1otdEutV5vLUdCxRJygHB5ozUBxsPB5qhitewssrS/qGruq9bmvIRkkGsNeK5ZWLfYRld18UHGTIifQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.2", + "@formatjs/fast-memoize": "2.2.6", + "@formatjs/icu-messageformat-parser": "2.11.0", + "tslib": "2" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "license": "MIT" }, - "node_modules/extract-comments": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/extract-comments/-/extract-comments-1.1.0.tgz", - "integrity": "sha512-dzbZV2AdSSVW/4E7Ti5hZdHWbA+Z80RJsJhr5uiL10oyjl/gy7/o+HI1HwK4/WSZhlq4SNKU3oUzXlM13Qx02Q==", - "dev": true, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "license": "MIT", "dependencies": { - "esprima-extract-comments": "^1.1.0", - "parse-code-context": "^1.0.0" + "binary-extensions": "^2.0.0" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" + "hasown": "^2.0.2" }, "engines": { - "node": ">=8.6.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz", - "integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause", - "peer": true - }, - "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "license": "MIT", "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" + "is-extglob": "^2.1.1" }, "engines": { - "node": "^12.20 || >= 14.13" + "node": ">=0.10.0" } }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", "dev": true, "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, "engines": { - "node": ">=16.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "to-regex-range": "^5.0.1" + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { - "node": ">=8" + "node": ">= 10.13.0" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "has-flag": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "license": "BSD-3-Clause", + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", "bin": { - "flat": "cli.js" + "jiti": "bin/jiti.js" } }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=16" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", "dev": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], "license": "MIT", + "dependencies": { + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, "engines": { - "node": ">=4.0" + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" }, "peerDependenciesMeta": { - "debug": { + "canvas": { "optional": true } } }, - "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=6" } }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", "license": "MIT", "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" + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" }, "engines": { - "node": ">= 6" + "node": ">=6" } }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, "license": "MIT", "dependencies": { - "fetch-blob": "^3.1.2" + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=12.20.0" + "node": ">= 0.8.0" } }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "dev": true, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "license": "MIT", "engines": { - "node": "*" + "node": ">=14" }, "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" + "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/framer-motion": { - "version": "12.23.24", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz", - "integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==", + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/loader-runner": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", + "dev": true, "license": "MIT", - "dependencies": { - "motion-dom": "^12.23.23", - "motion-utils": "^12.23.6", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "@emotion/is-prop-valid": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" + "peer": true, + "engines": { + "node": ">=6.11.5" }, - "peerDependenciesMeta": { - "@emotion/is-prop-valid": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/local-pkg": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" }, "engines": { - "node": ">=12" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "p-locate": "^5.0.0" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" } }, - "node_modules/gaxios": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.1.tgz", - "integrity": "sha512-Odju3uBUJyVCkW64nLD4wKLhbh93bh6vIg/ZIXkWiLPBrdgtc65+tls/qml+un3pr6JqYVFDZbbmLDQT68rTOQ==", - "license": "Apache-2.0", + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "node-fetch": "^3.3.2" - }, - "engines": { - "node": ">=18" + "yallist": "^3.0.2" } }, - "node_modules/gcp-metadata": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz", - "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==", - "license": "Apache-2.0", + "node_modules/lucide-react": { + "version": "0.471.2", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.471.2.tgz", + "integrity": "sha512-A8fDycQxGeaSOTaI7Bm4fg8LBXO7Qr9ORAX47bDRvugCsjLIliugQO0PkKFoeAD57LIQwlWKd3NIQ3J7hYp84g==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", "dependencies": { - "gaxios": "^7.0.0", - "google-logging-utils": "^1.0.0", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=18" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", "dev": true, + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">= 0.4" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=6" - } + "peer": true }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, "engines": { - "node": ">= 0.4" + "node": ">= 8" } }, - "node_modules/glob": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", - "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", - "license": "ISC", + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", "dev": true, - "license": "BSD-2-Clause", - "peer": true + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "node_modules/glob/node_modules/brace-expansion": { + "node_modules/micromark-util-types": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "node_modules/glob/node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "license": "ISC", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8.6" } }, - "node_modules/globals": { - "version": "15.14.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", - "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "license": "MIT" - }, - "node_modules/google-auth-library": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.3.0.tgz", - "integrity": "sha512-ylSE3RlCRZfZB56PFJSfUCuiuPq83Fx8hqu1KPWGK8FVdSaxlp/qkeMMX/DT/18xkwXIHvXEXkZsljRwfrdEfQ==", - "license": "Apache-2.0", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", "dependencies": { - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "gaxios": "^7.0.0", - "gcp-metadata": "^7.0.0", - "google-logging-utils": "^1.0.0", - "gtoken": "^8.0.0", - "jws": "^4.0.0" + "mime-db": "1.52.0" }, "engines": { - "node": ">=18" + "node": ">= 0.6" } }, - "node_modules/google-logging-utils": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.1.tgz", - "integrity": "sha512-rcX58I7nqpu4mbKztFeOAObbomBbHU2oIb/d3tJfF3dizGSApqtSwYJigGCooHdnMyQBIw8BrWyK96w3YXgr6A==", - "license": "Apache-2.0", + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=4" } }, - "node_modules/googleapis": { - "version": "160.0.0", - "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-160.0.0.tgz", - "integrity": "sha512-lAGFQVSeYvWKCxeCQKo20xWFFDgnWuJYbLn92IgLrT4UTYVOGdrZ9XTqgWJf316isE9KdfuDY5X8Tu4ZrXSFig==", - "license": "Apache-2.0", + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", "dependencies": { - "google-auth-library": "^10.2.0", - "googleapis-common": "^8.0.0" + "brace-expansion": "^2.0.2" }, "engines": { - "node": ">=18" - } - }, - "node_modules/googleapis-common": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-8.0.0.tgz", - "integrity": "sha512-66if47It7y+Sab3HMkwEXx1kCq9qUC9px8ZXoj1CMrmLmUw81GpbnsNlXnlyZyGbGPGcj+tDD9XsZ23m7GLaJQ==", - "license": "Apache-2.0", - "dependencies": { - "extend": "^3.0.2", - "gaxios": "^7.0.0-rc.4", - "google-auth-library": "^10.1.0", - "qs": "^6.7.0", - "url-template": "^2.0.8" + "node": ">=16 || 14 >=14.17" }, - "engines": { - "node": ">=18.0.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "node_modules/minisearch": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", + "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "dev": true, "license": "MIT" }, - "node_modules/gtoken": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", - "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "node_modules/mlly": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", + "dev": true, "license": "MIT", "dependencies": { - "gaxios": "^7.0.0", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=18" + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "node_modules/motion": { + "version": "12.23.24", + "resolved": "https://registry.npmjs.org/motion/-/motion-12.23.24.tgz", + "integrity": "sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw==", "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "framer-motion": "^12.23.24", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "node_modules/motion-dom": { + "version": "12.23.23", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz", + "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==", "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "motion-utils": "^12.23.6" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "license": "MIT", "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" + "bin": { + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">= 0.4" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", { "type": "github", - "url": "https://github.com/sponsors/fb55" + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" } ], "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" + "engines": { + "node": ">=10.5.0" } }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "license": "MIT", "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" }, "engines": { - "node": ">= 14" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4" + "node": ">=0.10.0" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" + "boolbase": "^1.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/nwsapi": { + "version": "2.2.23", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", + "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/intl-messageformat": { - "version": "10.7.14", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.14.tgz", - "integrity": "sha512-mMGnE4E1otdEutV5vLUdCxRJygHB5ozUBxsPB5qhitewssrS/qGruq9bmvIRkkGsNeK5ZWLfYRld18UHGTIifQ==", - "license": "BSD-3-Clause", - "dependencies": { - "@formatjs/ecma402-abstract": "2.3.2", - "@formatjs/fast-memoize": "2.2.6", - "@formatjs/icu-messageformat-parser": "2.11.0", - "tslib": "2" + "node": ">=0.10.0" } }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, "engines": { "node": ">= 0.4" }, @@ -6012,644 +9171,736 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extglob": { + "node_modules/obug": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", + "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, "license": "MIT", + "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" + }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, "engines": { - "node": ">=0.12.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" }, - "node_modules/jackspeak": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", - "license": "BlueOak-1.0.0", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "callsites": "^3.0.0" }, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=6" + } + }, + "node_modules/parse-code-context": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-code-context/-/parse-code-context-1.0.0.tgz", + "integrity": "sha512-OZQaqKaQnR21iqhlnPfVisFjBWjhnMl5J9MgbP8xC+EwoVqbXrq78lp+9Zb3ahmLzrIX5Us/qbvBnaS3hkH6OA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "entities": "^4.5.0" }, - "engines": { - "node": ">= 10.13.0" + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" + "domhandler": "^5.0.3", + "parse5": "^7.0.0" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "parse5": "^7.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0" + "engines": { + "node": ">=8" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "license": "MIT" }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", "dev": true, "license": "MIT" }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" + "engines": { + "node": ">=8.6" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/jwa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "license": "MIT", - "dependencies": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" + "engines": { + "node": ">= 6" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", "dev": true, "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "license": "MIT", - "peer": true, "engines": { - "node": ">=6.11.5" + "node": "^10 || ^12 || >=14" } }, - "node_modules/local-pkg": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", - "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", - "dev": true, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", "license": "MIT", "dependencies": { - "mlly": "^1.7.3", - "pkg-types": "^1.2.1" + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" }, "engines": { - "node": ">=14" + "node": ">=14.0.0" }, - "funding": { - "url": "https://github.com/sponsors/antfu" + "peerDependencies": { + "postcss": "^8.0.0" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "camelcase-css": "^2.0.1" }, "engines": { - "node": ">=10" + "node": "^12 || ^14 || >= 16" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" } }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lucide-react": { - "version": "0.471.2", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.471.2.tgz", - "integrity": "sha512-A8fDycQxGeaSOTaI7Bm4fg8LBXO7Qr9ORAX47bDRvugCsjLIliugQO0PkKFoeAD57LIQwlWKd3NIQ3J7hYp84g==", - "license": "ISC", "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, "engines": { - "node": ">= 0.4" + "node": ">=4" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/preact": { + "version": "10.29.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.0.tgz", + "integrity": "sha512-wSAGyk2bYR1c7t3SZ3jHcM6xy0lcBcDel6lODcs9ME6Th++Dx2KU+6D3HD8wMMKGA8Wpw7OMd3/4RGzYRpzwRg==", "dev": true, "license": "MIT", - "peer": true + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, "license": "MIT", "engines": { - "node": ">= 8" + "node": ">= 0.8.0" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "dev": true, "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=8.6" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.6" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, "engines": { - "node": ">= 0.6" + "node": ">=6" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", "dependencies": { - "brace-expansion": "^1.1.7" + "side-channel": "^1.1.0" }, "engines": { - "node": "*" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=0.10.0" } }, - "node_modules/mlly": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", - "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", - "dev": true, + "node_modules/react-colorful": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", + "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", "dependencies": { - "acorn": "^8.14.0", - "pathe": "^2.0.1", - "pkg-types": "^1.3.0", - "ufo": "^1.5.4" + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" } }, - "node_modules/motion": { - "version": "12.23.24", - "resolved": "https://registry.npmjs.org/motion/-/motion-12.23.24.tgz", - "integrity": "sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw==", + "node_modules/react-i18next": { + "version": "16.5.8", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.8.tgz", + "integrity": "sha512-2ABeHHlakxVY+LSirD+OiERxFL6+zip0PaHo979bgwzeHg27Sqc82xxXWIrSFmfWX0ZkrvXMHwhsi/NGUf5VQg==", "license": "MIT", "dependencies": { - "framer-motion": "^12.23.24", - "tslib": "^2.4.0" + "@babel/runtime": "^7.28.4", + "html-parse-stringify": "^3.0.1", + "use-sync-external-store": "^1.6.0" }, "peerDependencies": { - "@emotion/is-prop-valid": "*", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" + "i18next": ">= 25.6.2", + "react": ">= 16.8.0", + "typescript": "^5" }, "peerDependenciesMeta": { - "@emotion/is-prop-valid": { + "react-dom": { "optional": true }, - "react": { + "react-native": { "optional": true }, - "react-dom": { + "typescript": { "optional": true } } }, - "node_modules/motion-dom": { - "version": "12.23.23", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz", - "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==", + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, "license": "MIT", - "dependencies": { - "motion-utils": "^12.23.6" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/motion-utils": { - "version": "12.23.6", - "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", - "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "node_modules/react-remove-scroll": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", + "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", "license": "MIT", "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, + "node_modules/react-router": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.1.tgz", + "integrity": "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==", "license": "MIT", - "peer": true - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true } - ], + } + }, + "node_modules/react-router-dom": { + "version": "7.13.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.1.tgz", + "integrity": "sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==", "license": "MIT", + "dependencies": { + "react-router": "7.13.1" + }, "engines": { - "node": ">=10.5.0" + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" } }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", "license": "MIT", "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=10" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.10.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" + "regex-utilities": "^2.3.0" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "regex-utilities": "^2.3.0" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, "license": "MIT", + "peer": true, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, "engines": { "node": ">= 0.4" }, @@ -6657,658 +9908,715 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", - "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" - }, "engines": { - "node": ">= 0.8.0" + "node": ">=4" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "devOptional": true, "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=10" + "node": ">=18.0.0", + "npm": ">=8.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "fsevents": "~2.3.2" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "queue-microtask": "^1.2.2" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" + "node_modules/rxjs": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", "dependencies": { - "callsites": "^3.0.0" + "xmlchars": "^2.2.0" }, "engines": { - "node": ">=6" + "node": ">=v12.22.7" } }, - "node_modules/parse-code-context": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-code-context/-/parse-code-context-1.0.0.tgz", - "integrity": "sha512-OZQaqKaQnR21iqhlnPfVisFjBWjhnMl5J9MgbP8xC+EwoVqbXrq78lp+9Zb3ahmLzrIX5Us/qbvBnaS3hkH6OA==", - "dev": true, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "loose-envify": "^1.1.0" } }, - "node_modules/parse5": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", - "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "entities": "^4.5.0" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" }, "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", - "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "domhandler": "^5.0.3", - "parse5": "^7.0.0" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/parse5-parser-stream": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", - "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "parse5": "^7.0.0" + "fast-deep-equal": "^3.1.3" }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "peerDependencies": { + "ajv": "^8.8.2" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - } + "peer": true }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/search-insights": { + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "peer": true }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", - "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", - "engines": { - "node": "20 || >=22" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/pathe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", - "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", - "engines": { - "node": ">=8.6" + "dependencies": { + "shebang-regex": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "node_modules/shiki": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", + "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", "dev": true, "license": "MIT", "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" + "@shikijs/core": "2.5.0", + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/langs": "2.5.0", + "@shikijs/themes": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" } }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">=14.0.0" + "node": ">= 0.4" }, - "peerDependencies": { - "postcss": "^8.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { - "camelcase-css": "^2.0.1" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { - "node": "^12 || ^14 || >= 16" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" + "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" }, "engines": { - "node": ">= 14" + "node": ">= 0.4" }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" + "node": ">= 8" } }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "devOptional": true, + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "engines": { - "node": ">= 0.8.0" + "node": ">=0.10.0" } }, - "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", + "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=14" + "node": ">=12" }, "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", - "peer": true, "dependencies": { - "safe-buffer": "^5.1.0" + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0" + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/react-colorful": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", - "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "ansi-regex": "^5.0.1" }, - "peerDependencies": { - "react": "^18.3.1" + "engines": { + "node": ">=8" } }, - "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", - "dev": true, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/react-remove-scroll": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", - "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, "license": "MIT", "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.3", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.3" + "min-indent": "^1.0.0" }, "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "node": ">=8" } }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, "license": "MIT", - "dependencies": { - "react-style-singleton": "^2.2.2", - "tslib": "^2.0.0" - }, "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "node": ">=8" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-router": { - "version": "7.9.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.1.tgz", - "integrity": "sha512-pfAByjcTpX55mqSDGwGnY9vDCpxqBLASg0BMNAuMmpSGESo/TaOUG6BllhAtAkCGx8Rnohik/XtaqiYUJtgW2g==", + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", "license": "MIT", "dependencies": { - "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0" + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" }, "engines": { - "node": ">=20.0.0" + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "bin": { + "glob": "dist/esm/bin.mjs" }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/react-router-dom": { - "version": "7.9.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.1.tgz", - "integrity": "sha512-U9WBQssBE9B1vmRjo9qTM7YRzfZ3lUxESIZnsf4VjR/lXYz9MHjvOxHzr/aUm4efpktbVOrF09rL/y4VHa8RMw==", - "license": "MIT", + "node_modules/sucrase/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", "dependencies": { - "react-router": "7.9.1" + "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=20.0.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/react-style-singleton": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "license": "MIT", + "node_modules/sucrase/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/sucrase/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", "dependencies": { - "get-nonce": "^1.0.0", - "tslib": "^2.0.0" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + "node": ">=16 || 14 >=14.18" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "dev": true, "license": "MIT", "dependencies": { - "picomatch": "^2.2.1" + "copy-anything": "^4" }, "engines": { - "node": ">=8.10.0" + "node": ">=16" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", - "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, "engines": { "node": ">= 0.4" }, @@ -7316,147 +10624,148 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", "dev": true, + "license": "MIT" + }, + "node_modules/tailwind-merge": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", "license": "MIT", - "engines": { - "node": ">=4" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/tailwind-variants": { + "version": "0.1.20", + "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-0.1.20.tgz", + "integrity": "sha512-AMh7x313t/V+eTySKB0Dal08RHY7ggYK0MSn/ad8wKWOrDUIzyiWNayRUm2PIJ4VRkvRnfNuyRuKbLV3EN+ewQ==", "license": "MIT", + "dependencies": { + "tailwind-merge": "^1.14.0" + }, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">=16.x", + "pnpm": ">=7.x" + }, + "peerDependencies": { + "tailwindcss": "*" } }, - "node_modules/rollup": { - "version": "4.50.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.2.tgz", - "integrity": "sha512-BgLRGy7tNS9H66aIMASq1qSYbAAJV6Z6WR4QYTvj5FgF15rZ/ympT1uixHXwzbZUBDbkvqUI1KR0fH1FhMaQ9w==", - "devOptional": true, + "node_modules/tailwind-variants/node_modules/tailwind-merge": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz", + "integrity": "sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" }, "bin": { - "rollup": "dist/bin/rollup" + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.50.2", - "@rollup/rollup-android-arm64": "4.50.2", - "@rollup/rollup-darwin-arm64": "4.50.2", - "@rollup/rollup-darwin-x64": "4.50.2", - "@rollup/rollup-freebsd-arm64": "4.50.2", - "@rollup/rollup-freebsd-x64": "4.50.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.50.2", - "@rollup/rollup-linux-arm-musleabihf": "4.50.2", - "@rollup/rollup-linux-arm64-gnu": "4.50.2", - "@rollup/rollup-linux-arm64-musl": "4.50.2", - "@rollup/rollup-linux-loong64-gnu": "4.50.2", - "@rollup/rollup-linux-ppc64-gnu": "4.50.2", - "@rollup/rollup-linux-riscv64-gnu": "4.50.2", - "@rollup/rollup-linux-riscv64-musl": "4.50.2", - "@rollup/rollup-linux-s390x-gnu": "4.50.2", - "@rollup/rollup-linux-x64-gnu": "4.50.2", - "@rollup/rollup-linux-x64-musl": "4.50.2", - "@rollup/rollup-openharmony-arm64": "4.50.2", - "@rollup/rollup-win32-arm64-msvc": "4.50.2", - "@rollup/rollup-win32-ia32-msvc": "4.50.2", - "@rollup/rollup-win32-x64-msvc": "4.50.2", - "fsevents": "~2.3.2" + "node": ">=14.0.0" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" } }, - "node_modules/rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", + "node_modules/terser": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", + "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", + "devOptional": true, + "license": "BSD-2-Clause", + "peer": true, "dependencies": { - "loose-envify": "^1.1.0" + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" } }, - "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "node_modules/terser-webpack-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", + "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -7464,1005 +10773,1216 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } } }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "devOptional": true, + "license": "MIT", + "peer": true }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "peer": true, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", "dependencies": { - "randombytes": "^2.1.0" + "any-promise": "^1.0.0" } }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "thenify": ">= 3.1.0 < 4" }, "engines": { - "node": ">=8" + "node": ">=0.8" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "devOptional": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">= 0.4" + "node": ">=12.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "devOptional": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "devOptional": true, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=16" } }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, "license": "MIT", "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" + "punycode": "^2.3.1" }, "engines": { - "node": ">= 0.4" - }, + "node": ">=18" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", + "node_modules/ts-api-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", + "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=18.12" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "typescript": ">=4.8.4" } }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, + "node_modules/ts-loader": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", + "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", + "dev": true, "license": "MIT", "dependencies": { - "is-arrayish": "^0.3.1" + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" } }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "node_modules/ts-loader/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "license": "BSD-3-Clause", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">= 8" + "node": ">=10" } }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", + "node_modules/tsconfck": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.4.tgz", + "integrity": "sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==", + "license": "MIT", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, "engines": { - "node": ">=0.10.0" + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "peer": true, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, "engines": { - "node": ">=0.10.0" + "node": ">=14.17" } }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/typescript-eslint": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.20.0.tgz", + "integrity": "sha512-Kxz2QRFsgbWj6Xcftlw3Dd154b3cEPFqQC+qMZrMypSijPd4UanKKvoKDrJ4o8AIfZFKAF+7sMaEIR8mTElozA==", + "dev": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "@typescript-eslint/eslint-plugin": "8.20.0", + "@typescript-eslint/parser": "8.20.0", + "@typescript-eslint/utils": "8.20.0" }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", + "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18.17" } }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "devOptional": true, + "license": "MIT", + "peer": true }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { + "node_modules/unist-util-is": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "@types/unist": "^3.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" + "@types/unist": "^3.0.0" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "@types/unist": "^3.0.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/sucrase/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "update-browserslist-db": "cli.js" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/sucrase/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", + "license": "BSD" + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "tslib": "^2.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=10" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/sucrase/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">=10" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/tailwind-merge": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", - "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dev": true, "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/tailwind-variants": { - "version": "0.1.20", - "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-0.1.20.tgz", - "integrity": "sha512-AMh7x313t/V+eTySKB0Dal08RHY7ggYK0MSn/ad8wKWOrDUIzyiWNayRUm2PIJ4VRkvRnfNuyRuKbLV3EN+ewQ==", + "node_modules/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "devOptional": true, "license": "MIT", "dependencies": { - "tailwind-merge": "^1.14.0" + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" }, "engines": { - "node": ">=16.x", - "pnpm": ">=7.x" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, - "peerDependencies": { - "tailwindcss": "*" - } - }, - "node_modules/tailwind-variants/node_modules/tailwind-merge": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz", - "integrity": "sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==", - "license": "MIT", "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } } }, - "node_modules/tailwindcss": { - "version": "3.4.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", - "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "node_modules/vite-plugin-pages": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/vite-plugin-pages/-/vite-plugin-pages-0.32.4.tgz", + "integrity": "sha512-OM8CNb8mAzyYR8ASRC0+2LXVB8ecR/5JHc5RpxbWtF+CmhjhmIELs0iV5y8qvU48soZbk+NsFOYlhoIcjw3+ew==", + "dev": true, "license": "MIT", "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", + "@types/debug": "^4.1.12", + "debug": "^4.3.7", + "dequal": "^2.0.3", + "extract-comments": "^1.1.0", "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.6", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", + "json5": "^2.2.3", + "local-pkg": "^0.5.1", "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" + "yaml": "^2.6.1" }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" + "peerDependencies": { + "@vue/compiler-sfc": "^2.7.0 || ^3.0.0", + "vite": "^2.0.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, - "engines": { - "node": ">=14.0.0" + "peerDependenciesMeta": { + "@solidjs/router": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "react-router": { + "optional": true + }, + "vue-router": { + "optional": true + } } }, - "node_modules/tailwindcss-animate": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", - "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "node_modules/vite-tsconfig-paths": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", + "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "globrex": "^0.1.2", + "tsconfck": "^3.0.3" + }, "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders" + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } } }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "devOptional": true, "license": "MIT", "engines": { - "node": ">=6" - } - }, - "node_modules/terser": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", - "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" + "node": ">=12.0.0" }, - "bin": { - "terser": "bin/terser" + "peerDependencies": { + "picomatch": "^3 || ^4" }, - "engines": { - "node": ">=10" + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.11", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", - "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", - "dev": true, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "devOptional": true, "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, "engines": { - "node": ">= 10.13.0" + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitepress": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", + "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/css": "3.8.2", + "@docsearch/js": "3.8.2", + "@iconify-json/simple-icons": "^1.2.21", + "@shikijs/core": "^2.1.0", + "@shikijs/transformers": "^2.1.0", + "@shikijs/types": "^2.1.0", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^5.2.1", + "@vue/devtools-api": "^7.7.0", + "@vue/shared": "^3.5.13", + "@vueuse/core": "^12.4.0", + "@vueuse/integrations": "^12.4.0", + "focus-trap": "^7.6.4", + "mark.js": "8.11.1", + "minisearch": "^7.1.1", + "shiki": "^2.1.0", + "vite": "^5.4.14", + "vue": "^3.5.13" + }, + "bin": { + "vitepress": "bin/vitepress.js" }, "peerDependencies": { - "webpack": "^5.1.0" + "markdown-it-mathjax3": "^4", + "postcss": "^8" }, "peerDependenciesMeta": { - "@swc/core": { + "markdown-it-mathjax3": { "optional": true }, - "esbuild": { - "optional": true - }, - "uglify-js": { + "postcss": { "optional": true } } }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "node_modules/vitepress/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" } }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "node_modules/vitepress/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "node_modules/vitepress/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=12" } }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "node_modules/vitepress/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "peer": true - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "node_modules/vitepress/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=0.8" + "node": ">=12" } }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "devOptional": true, + "node_modules/vitepress/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "node": ">=12" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "devOptional": true, + "node_modules/vitepress/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "node": ">=12" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "devOptional": true, + "node_modules/vitepress/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/vitepress/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8.0" + "node": ">=12" } }, - "node_modules/ts-api-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", - "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", + "node_modules/vitepress/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" + "node": ">=12" } }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, - "node_modules/ts-loader": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", - "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", + "node_modules/vitepress/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4", - "source-map": "^0.7.4" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "typescript": "*", - "webpack": "^5.0.0" + "node": ">=12" } }, - "node_modules/ts-loader/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "node_modules/vitepress/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/tsconfck": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.4.tgz", - "integrity": "sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==", + "node_modules/vitepress/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=12" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/vitepress/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.8.0" + "node": ">=12" } }, - "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, + "node_modules/vitepress/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=14.17" + "node": ">=12" } }, - "node_modules/typescript-eslint": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.20.0.tgz", - "integrity": "sha512-Kxz2QRFsgbWj6Xcftlw3Dd154b3cEPFqQC+qMZrMypSijPd4UanKKvoKDrJ4o8AIfZFKAF+7sMaEIR8mTElozA==", + "node_modules/vitepress/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.20.0", - "@typescript-eslint/parser": "8.20.0", - "@typescript-eslint/utils": "8.20.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "node": ">=12" } }, - "node_modules/ufo": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", - "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "node_modules/vitepress/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/undici": { - "version": "6.21.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", - "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", + "node_modules/vitepress/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=18.17" + "node": ">=12" } }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "node_modules/vitepress/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "peer": true + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/vitepress/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">= 10.0.0" + "node": ">=12" } }, - "node_modules/update-browserslist-db": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } + "node_modules/vitepress/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" ], + "dev": true, "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/vitepress/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, - "node_modules/url-template": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", - "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", - "license": "BSD" - }, - "node_modules/use-callback-ref": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "node_modules/vitepress/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "node": ">=12" } }, - "node_modules/use-sidecar": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "node_modules/vitepress/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + "node": ">=12" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/vite": { - "version": "6.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", - "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitepress/node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -8471,25 +11991,19 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", + "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" + "terser": "^5.4.0" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, - "jiti": { - "optional": true - }, "less": { "optional": true }, @@ -8510,105 +12024,162 @@ }, "terser": { "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true } } }, - "node_modules/vite-plugin-pages": { - "version": "0.32.4", - "resolved": "https://registry.npmjs.org/vite-plugin-pages/-/vite-plugin-pages-0.32.4.tgz", - "integrity": "sha512-OM8CNb8mAzyYR8ASRC0+2LXVB8ecR/5JHc5RpxbWtF+CmhjhmIELs0iV5y8qvU48soZbk+NsFOYlhoIcjw3+ew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/debug": "^4.1.12", - "debug": "^4.3.7", - "dequal": "^2.0.3", - "extract-comments": "^1.1.0", - "fast-glob": "^3.3.2", - "json5": "^2.2.3", - "local-pkg": "^0.5.1", - "picocolors": "^1.1.1", - "yaml": "^2.6.1" + "node_modules/vitest": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.0.tgz", + "integrity": "sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.0", + "@vitest/mocker": "4.1.0", + "@vitest/pretty-format": "4.1.0", + "@vitest/runner": "4.1.0", + "@vitest/snapshot": "4.1.0", + "@vitest/spy": "4.1.0", + "@vitest/utils": "4.1.0", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vue/compiler-sfc": "^2.7.0 || ^3.0.0", - "vite": "^2.0.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.0", + "@vitest/browser-preview": "4.1.0", + "@vitest/browser-webdriverio": "4.1.0", + "@vitest/ui": "4.1.0", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" }, "peerDependenciesMeta": { - "@solidjs/router": { + "@edge-runtime/vm": { "optional": true }, - "@vue/compiler-sfc": { + "@opentelemetry/api": { "optional": true }, - "react-router": { + "@types/node": { "optional": true }, - "vue-router": { + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { "optional": true + }, + "vite": { + "optional": false } } }, - "node_modules/vite-tsconfig-paths": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", - "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", + "node_modules/vitest/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" - }, - "peerDependencies": { - "vite": "*" + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/vite/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "devOptional": true, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", "license": "MIT", "engines": { - "node": ">=12.0.0" + "node": ">=0.10.0" + } + }, + "node_modules/vue": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.30.tgz", + "integrity": "sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.30", + "@vue/compiler-sfc": "3.5.30", + "@vue/runtime-dom": "3.5.30", + "@vue/server-renderer": "3.5.30", + "@vue/shared": "3.5.30" }, "peerDependencies": { - "picomatch": "^3 || ^4" + "typescript": "*" }, "peerDependenciesMeta": { - "picomatch": { + "typescript": { "optional": true } } }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "devOptional": true, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "xml-name-validator": "^5.0.0" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">=18" } }, "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, "license": "MIT", "peer": true, @@ -8629,37 +12200,49 @@ "node": ">= 8" } }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, "node_modules/webpack": { - "version": "5.97.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", - "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", + "version": "5.105.4", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz", + "integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", + "acorn": "^8.16.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", + "enhanced-resolve": "^5.20.0", + "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", + "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.17", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.4" }, "bin": { "webpack": "bin/webpack.js" @@ -8678,9 +12261,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", + "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", "dev": true, "license": "MIT", "peer": true, @@ -8737,6 +12320,20 @@ "node": ">=18" } }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8752,6 +12349,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -8850,6 +12464,45 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -8881,6 +12534,17 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index ecc67e6..dcf48f4 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,13 @@ "dev": "vite", "build": "tsc -b && vite build", "build:mock": "tsc -b && vite build --mode mock", + "test": "vitest run", + "test:watch": "vitest", "lint": "eslint .", - "preview": "vite preview" + "preview": "vite preview", + "docs:dev": "vitepress dev docs", + "docs:build": "vitepress build docs", + "docs:preview": "vitepress preview docs" }, "dependencies": { "@dnd-kit/core": "^6.3.1", @@ -19,7 +24,9 @@ "@heroui/theme": "^2.4.6", "@radix-ui/react-avatar": "^1.1.2", "@radix-ui/react-checkbox": "^1.1.3", + "@radix-ui/react-context-menu": "^2.2.16", "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-popover": "^1.1.4", "@radix-ui/react-progress": "^1.1.1", @@ -30,20 +37,19 @@ "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-toast": "^1.2.4", "@radix-ui/react-tooltip": "^1.1.7", - "@types/gapi.client.calendar": "^3.0.12", - "axios": "^1.12.0", + "@types/gapi.client.calendar-v3": "^0.0.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "framer-motion": "^12.0.6", - "glob": "^11.0.1", "googleapis": "^160.0.0", + "i18next": "^25.8.18", "lucide-react": "^0.471.2", "motion": "^12.23.24", - "node-fetch": "^3.3.2", "react": "^18.3.1", "react-colorful": "^5.6.1", "react-dom": "^18.3.1", + "react-i18next": "^16.5.8", "react-router-dom": "^7.9.1", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", @@ -52,6 +58,7 @@ "devDependencies": { "@crxjs/vite-plugin": "^2.0.0-beta.30", "@eslint/js": "^9.17.0", + "@testing-library/jest-dom": "^6.9.1", "@types/chrome": "^0.0.296", "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", @@ -61,6 +68,7 @@ "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.16", "globals": "^15.14.0", + "jsdom": "^25.0.1", "postcss": "^8.5.1", "prettier": "^3.4.2", "tailwindcss": "^3.4.17", @@ -68,6 +76,13 @@ "typescript": "~5.6.2", "typescript-eslint": "^8.18.2", "vite": "^6.3.6", - "vite-plugin-pages": "^0.32.4" + "vite-plugin-pages": "^0.32.4", + "vitepress": "^1.6.4", + "vitest": "^4.1.0" + }, + "overrides": { + "@crxjs/vite-plugin": { + "rollup": "^2.80.0" + } } } diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json new file mode 100644 index 0000000..55600fd --- /dev/null +++ b/public/_locales/en/messages.json @@ -0,0 +1,8 @@ +{ + "extName": { + "message": "HSU Dotbugi 🔎" + }, + "extDescription": { + "message": "View all Hansung University LMS lectures, assignments, and quizzes at a glance!" + } +} diff --git a/public/_locales/ja/messages.json b/public/_locales/ja/messages.json new file mode 100644 index 0000000..3855e43 --- /dev/null +++ b/public/_locales/ja/messages.json @@ -0,0 +1,8 @@ +{ + "extName": { + "message": "HSU Dotbugi 🔎" + }, + "extDescription": { + "message": "漢城大学LMSの講義、課題、クイズを一目で!" + } +} diff --git a/public/_locales/ko/messages.json b/public/_locales/ko/messages.json new file mode 100644 index 0000000..bf1c2c2 --- /dev/null +++ b/public/_locales/ko/messages.json @@ -0,0 +1,8 @@ +{ + "extName": { + "message": "HSU 돋부기 🔎" + }, + "extDescription": { + "message": "한성대학교 LMS 강의, 과제, 퀴즈를 한 눈에!" + } +} diff --git a/public/_locales/zh/messages.json b/public/_locales/zh/messages.json new file mode 100644 index 0000000..e4673d1 --- /dev/null +++ b/public/_locales/zh/messages.json @@ -0,0 +1,8 @@ +{ + "extName": { + "message": "HSU Dotbugi 🔎" + }, + "extDescription": { + "message": "一目了然查看汉城大学LMS课程、作业和测验!" + } +} diff --git a/src/__tests__/attendance.test.ts b/src/__tests__/attendance.test.ts new file mode 100644 index 0000000..dccadf3 --- /dev/null +++ b/src/__tests__/attendance.test.ts @@ -0,0 +1,84 @@ +import { describe, it, expect } from 'vitest'; +import { isAttended, isAbsent } from '@/lib/attendance'; + +describe('isAttended', () => { + it('소문자 "o"는 출석', () => { + expect(isAttended('o')).toBe(true); + }); + + it('대문자 "O"는 출석', () => { + expect(isAttended('O')).toBe(true); + }); + + it('공백 포함 " o "는 출석', () => { + expect(isAttended(' o ')).toBe(true); + }); + + it('"x"는 미출석', () => { + expect(isAttended('x')).toBe(false); + }); + + it('"X"는 미출석', () => { + expect(isAttended('X')).toBe(false); + }); + + it('빈 문자열은 미출석', () => { + expect(isAttended('')).toBe(false); + }); + + it('"출석"같은 한글은 미출석으로 판단', () => { + expect(isAttended('출석')).toBe(false); + }); + + it('"O출석" 같은 혼합 문자열은 미출석', () => { + expect(isAttended('O출석')).toBe(false); + }); + + it('공백만 있는 문자열은 미출석 (trim 후 빈 문자열)', () => { + expect(isAttended(' ')).toBe(false); + }); + + it('탭 문자 포함된 "\\to\\t"는 미출석 (trim 후 "o"가 아님)', () => { + // '\to\t' → trim → 'o' 가 됨 + expect(isAttended('\to\t')).toBe(true); + }); + + it('"0" (숫자 영)은 미출석 ("o"가 아님)', () => { + expect(isAttended('0')).toBe(false); + }); +}); + +describe('isAbsent', () => { + it('"X"는 결석', () => { + expect(isAbsent('X')).toBe(true); + }); + + it('"x"는 결석', () => { + expect(isAbsent('x')).toBe(true); + }); + + it('"X (결석)"처럼 X로 시작하면 결석', () => { + expect(isAbsent('X (결석)')).toBe(true); + }); + + it('"o"는 결석 아님', () => { + expect(isAbsent('o')).toBe(false); + }); + + it('빈 문자열은 결석 아님', () => { + expect(isAbsent('')).toBe(false); + }); + + it('"결석"같은 한글은 결석 아님 (X로 시작하지 않으므로)', () => { + expect(isAbsent('결석')).toBe(false); + }); + + it('"xX" 소문자 x로 시작하면 결석', () => { + expect(isAbsent('xX')).toBe(true); + }); + + it('" X" 공백으로 시작하면 결석 아님 (toUpperCase 후 startsWith)', () => { + // ' X'.toUpperCase() = ' X', startsWith('X') = false + expect(isAbsent(' X')).toBe(false); + }); +}); diff --git a/src/__tests__/badge.test.ts b/src/__tests__/badge.test.ts new file mode 100644 index 0000000..1f229cb --- /dev/null +++ b/src/__tests__/badge.test.ts @@ -0,0 +1,65 @@ +import { describe, it, expect } from 'vitest'; + +// badge.ts 내부 함수들을 테스트하기 위해 모듈을 동적으로 import +// parseTimeToSeconds, normalizeUrl은 export되지 않으므로 같은 로직을 테스트 + +describe('parseTimeToSeconds (logic)', () => { + function parseTimeToSeconds(time: string): number { + const parts = time.split(':').map(Number); + if (parts.length === 3) return parts[0] * 3600 + parts[1] * 60 + parts[2]; + if (parts.length === 2) return parts[0] * 60 + parts[1]; + return 0; + } + + it('HH:MM:SS 형식을 초로 변환', () => { + expect(parseTimeToSeconds('01:30:00')).toBe(5400); + expect(parseTimeToSeconds('00:05:30')).toBe(330); + expect(parseTimeToSeconds('02:00:00')).toBe(7200); + }); + + it('MM:SS 형식을 초로 변환', () => { + expect(parseTimeToSeconds('05:30')).toBe(330); + expect(parseTimeToSeconds('00:00')).toBe(0); + expect(parseTimeToSeconds('90:00')).toBe(5400); + }); + + it('잘못된 형식은 0 반환', () => { + expect(parseTimeToSeconds('')).toBe(0); + expect(parseTimeToSeconds('abc')).toBe(0); + expect(parseTimeToSeconds('30')).toBe(0); + }); + + it('경계값 처리', () => { + expect(parseTimeToSeconds('00:00:00')).toBe(0); + expect(parseTimeToSeconds('23:59:59')).toBe(86399); + expect(parseTimeToSeconds('00:00:01')).toBe(1); + }); +}); + +describe('normalizeUrl (logic)', () => { + const BASE_LINK = 'https://learn.hansung.ac.kr'; + + function normalizeUrl(url: string): string { + if (url.startsWith('http')) return url; + return BASE_LINK + (url.startsWith('/') ? '' : '/') + url; + } + + it('http로 시작하는 URL은 그대로 반환', () => { + expect(normalizeUrl('https://learn.hansung.ac.kr/mod/vod/view.php?id=1')).toBe( + 'https://learn.hansung.ac.kr/mod/vod/view.php?id=1', + ); + expect(normalizeUrl('http://example.com')).toBe('http://example.com'); + }); + + it('/로 시작하는 상대 경로에 BASE_LINK 추가', () => { + expect(normalizeUrl('/mod/vod/view.php?id=1')).toBe( + 'https://learn.hansung.ac.kr/mod/vod/view.php?id=1', + ); + }); + + it('/가 없는 상대 경로에 BASE_LINK/ 추가', () => { + expect(normalizeUrl('mod/vod/view.php?id=1')).toBe( + 'https://learn.hansung.ac.kr/mod/vod/view.php?id=1', + ); + }); +}); diff --git a/src/__tests__/cache.test.ts b/src/__tests__/cache.test.ts new file mode 100644 index 0000000..235e06d --- /dev/null +++ b/src/__tests__/cache.test.ts @@ -0,0 +1,56 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { + getLastRequestTime, + setLastRequestTime, + clearLastRequestTime, + CACHE_TTL_MS, + CACHE_TTL_MINUTES, + REFRESH_INTERVAL_MS, +} from '@/lib/cache'; + +describe('cache', () => { + beforeEach(() => { + localStorage.clear(); + }); + + it('초기 상태에서 null 반환', () => { + expect(getLastRequestTime()).toBeNull(); + }); + + it('setLastRequestTime 후 getLastRequestTime 반환', () => { + setLastRequestTime(1000); + expect(getLastRequestTime()).toBe(1000); + }); + + it('clearLastRequestTime 후 null 반환', () => { + setLastRequestTime(1000); + clearLastRequestTime(); + expect(getLastRequestTime()).toBeNull(); + }); + + it('큰 타임스탬프도 정확히 저장/반환', () => { + const ts = Date.now(); + setLastRequestTime(ts); + expect(getLastRequestTime()).toBe(ts); + }); + + it('덮어쓰기가 동작', () => { + setLastRequestTime(100); + setLastRequestTime(200); + expect(getLastRequestTime()).toBe(200); + }); +}); + +describe('cache constants', () => { + it('REFRESH_INTERVAL_MS = 60초', () => { + expect(REFRESH_INTERVAL_MS).toBe(60_000); + }); + + it('CACHE_TTL_MINUTES = 60분', () => { + expect(CACHE_TTL_MINUTES).toBe(60); + }); + + it('CACHE_TTL_MS = 60분 * 60초 * 1000', () => { + expect(CACHE_TTL_MS).toBe(CACHE_TTL_MINUTES * REFRESH_INTERVAL_MS); + }); +}); diff --git a/src/__tests__/calendarUtils.test.ts b/src/__tests__/calendarUtils.test.ts new file mode 100644 index 0000000..43dcd7e --- /dev/null +++ b/src/__tests__/calendarUtils.test.ts @@ -0,0 +1,93 @@ +import { describe, it, expect } from 'vitest'; +import { CalendarEvent } from '@/lib/transformCalendarEvents'; + +type GoogleCalendarEvent = { + summary: string; + colorId?: string; + start: { dateTime: string; timeZone: string }; + end: { dateTime: string; timeZone: string }; +}; + +const EVENT_COLOR_MAP: Record = { + vod: '1', + assign: '9', + quiz: '6', +}; + +function convertCalendarEventsToGoogleEvents(events: CalendarEvent[]): GoogleCalendarEvent[] { + return events + .filter((event) => event.start !== null && event.end !== null) + .map((event) => ({ + summary: `${event.title} - ${event.subject}`, + colorId: EVENT_COLOR_MAP[event.type], + start: { + dateTime: event.start!.toISOString(), + timeZone: 'Asia/Seoul', + }, + end: { + dateTime: event.end!.toISOString(), + timeZone: 'Asia/Seoul', + }, + })); +} + +describe('convertCalendarEventsToGoogleEvents', () => { + it('CalendarEvent를 GoogleCalendarEvent로 변환', () => { + const events: CalendarEvent[] = [ + { + id: 'assign-1', + title: '1주차 과제', + subject: '프로그래밍', + type: 'assign', + start: new Date('2026-03-18T00:00:00'), + end: new Date('2026-03-24T23:59:59'), + }, + ]; + + const result = convertCalendarEventsToGoogleEvents(events); + expect(result).toHaveLength(1); + expect(result[0].summary).toBe('1주차 과제 - 프로그래밍'); + expect(result[0].colorId).toBe('9'); + expect(result[0].start.timeZone).toBe('Asia/Seoul'); + expect(result[0].end.timeZone).toBe('Asia/Seoul'); + }); + + it('type에 따른 colorId 매핑', () => { + const events: CalendarEvent[] = [ + { id: '1', title: 'VOD', subject: '과목', type: 'vod', start: new Date(), end: new Date() }, + { id: '2', title: '과제', subject: '과목', type: 'assign', start: new Date(), end: new Date() }, + { id: '3', title: '퀴즈', subject: '과목', type: 'quiz', start: new Date(), end: new Date() }, + ]; + + const result = convertCalendarEventsToGoogleEvents(events); + expect(result[0].colorId).toBe('1'); + expect(result[1].colorId).toBe('9'); + expect(result[2].colorId).toBe('6'); + }); + + it('start 또는 end가 null인 이벤트 필터링', () => { + const events: CalendarEvent[] = [ + { id: '1', title: 'A', subject: '과목', type: 'vod', start: null, end: new Date() }, + { id: '2', title: 'B', subject: '과목', type: 'vod', start: new Date(), end: null }, + { id: '3', title: 'C', subject: '과목', type: 'vod', start: null, end: null }, + { id: '4', title: 'D', subject: '과목', type: 'vod', start: new Date(), end: new Date() }, + ]; + + const result = convertCalendarEventsToGoogleEvents(events); + expect(result).toHaveLength(1); + expect(result[0].summary).toBe('D - 과목'); + }); + + it('빈 배열 입력 시 빈 배열 반환', () => { + expect(convertCalendarEventsToGoogleEvents([])).toEqual([]); + }); + + it('summary 형식이 "title - subject"', () => { + const events: CalendarEvent[] = [ + { id: 'q1', title: '중간고사', subject: '자료구조', type: 'quiz', start: new Date(), end: new Date() }, + ]; + + const result = convertCalendarEventsToGoogleEvents(events); + expect(result[0].summary).toBe('중간고사 - 자료구조'); + }); +}); diff --git a/src/__tests__/currentWeek.test.ts b/src/__tests__/currentWeek.test.ts new file mode 100644 index 0000000..9223dda --- /dev/null +++ b/src/__tests__/currentWeek.test.ts @@ -0,0 +1,156 @@ +import { describe, it, expect } from 'vitest'; + +const MONTH_NAMES: Record = { + january: 1, february: 2, march: 3, april: 4, may: 5, june: 6, + july: 7, august: 8, september: 9, october: 10, november: 11, december: 12, +}; + +function parseLocalizedDate(str: string, year: number): Date | null { + let m: RegExpMatchArray | null; + + m = str.match(/(\d{1,2})월\s*(\d{1,2})일/) ?? str.match(/(\d{1,2})月(\d{1,2})日/); + if (m) return new Date(year, parseInt(m[1]) - 1, parseInt(m[2])); + + m = str.match(/^(\d{1,2})\/(\d{1,2})$/); + if (m) return new Date(year, parseInt(m[1]) - 1, parseInt(m[2])); + + m = str.match(/(\d{1,2})\s+([A-Za-z]+)/); + if (m && MONTH_NAMES[m[2].toLowerCase()]) { + return new Date(year, MONTH_NAMES[m[2].toLowerCase()] - 1, parseInt(m[1])); + } + + return null; +} + +function parseSectionDateRange(text: string, year: number): { start: Date; end: Date } | null { + const match = text.match(/\[([^\]]+)\]/); + if (!match) return null; + + const [startStr, endStr] = match[1].split(/\s*-\s*/); + if (!startStr || !endStr) return null; + + const start = parseLocalizedDate(startStr.trim(), year); + const end = parseLocalizedDate(endStr.trim(), year); + if (!start || !end) return null; + + end.setHours(23, 59, 59); + return { start, end }; +} + +describe('parseLocalizedDate', () => { + const year = 2026; + + describe('한국어 (월일)', () => { + it('3월18일', () => { + const result = parseLocalizedDate('3월18일', year); + expect(result).toEqual(new Date(2026, 2, 18)); + }); + + it('12월 1일 (공백 포함)', () => { + const result = parseLocalizedDate('12월 1일', year); + expect(result).toEqual(new Date(2026, 11, 1)); + }); + }); + + describe('중국어 (月日)', () => { + it('03月04日', () => { + const result = parseLocalizedDate('03月04日', year); + expect(result).toEqual(new Date(2026, 2, 4)); + }); + + it('12月31日', () => { + const result = parseLocalizedDate('12月31日', year); + expect(result).toEqual(new Date(2026, 11, 31)); + }); + }); + + describe('일본어 (MM/DD)', () => { + it('03/04', () => { + const result = parseLocalizedDate('03/04', year); + expect(result).toEqual(new Date(2026, 2, 4)); + }); + + it('1/1', () => { + const result = parseLocalizedDate('1/1', year); + expect(result).toEqual(new Date(2026, 0, 1)); + }); + }); + + describe('영어 (DD Month)', () => { + it('04 March', () => { + const result = parseLocalizedDate('04 March', year); + expect(result).toEqual(new Date(2026, 2, 4)); + }); + + it('25 December', () => { + const result = parseLocalizedDate('25 December', year); + expect(result).toEqual(new Date(2026, 11, 25)); + }); + + it('대소문자 무시', () => { + const result = parseLocalizedDate('1 january', year); + expect(result).toEqual(new Date(2026, 0, 1)); + }); + }); + + describe('잘못된 형식', () => { + it('빈 문자열', () => { + expect(parseLocalizedDate('', year)).toBeNull(); + }); + + it('인식 불가 형식', () => { + expect(parseLocalizedDate('2026-03-18', year)).toBeNull(); + }); + + it('잘못된 영어 월 이름', () => { + expect(parseLocalizedDate('04 Smarch', year)).toBeNull(); + }); + }); +}); + +describe('parseSectionDateRange', () => { + const year = 2026; + + it('한국어 범위 파싱', () => { + const result = parseSectionDateRange('1주차 [3월18일 - 3월24일]', year); + expect(result).not.toBeNull(); + expect(result!.start).toEqual(new Date(2026, 2, 18)); + expect(result!.end.getMonth()).toBe(2); + expect(result!.end.getDate()).toBe(24); + expect(result!.end.getHours()).toBe(23); + }); + + it('영어 범위 파싱', () => { + const result = parseSectionDateRange('Week 1 [18 March - 24 March]', year); + expect(result).not.toBeNull(); + expect(result!.start).toEqual(new Date(2026, 2, 18)); + expect(result!.end.getDate()).toBe(24); + }); + + it('일본어 범위 파싱', () => { + const result = parseSectionDateRange('第1週 [03/18 - 03/24]', year); + expect(result).not.toBeNull(); + expect(result!.start).toEqual(new Date(2026, 2, 18)); + }); + + it('중국어 범위 파싱', () => { + const result = parseSectionDateRange('第1周 [03月18日 - 03月24日]', year); + expect(result).not.toBeNull(); + expect(result!.start).toEqual(new Date(2026, 2, 18)); + }); + + it('대괄호 없으면 null', () => { + expect(parseSectionDateRange('1주차 3월18일', year)).toBeNull(); + }); + + it('하이픈 없으면 null', () => { + expect(parseSectionDateRange('[3월18일]', year)).toBeNull(); + }); + + it('end 날짜가 23:59:59로 설정됨', () => { + const result = parseSectionDateRange('[3월18일 - 3월24일]', year); + expect(result!.end.getHours()).toBe(23); + expect(result!.end.getMinutes()).toBe(59); + expect(result!.end.getSeconds()).toBe(59); + }); +}); diff --git a/src/__tests__/dateUtils.test.ts b/src/__tests__/dateUtils.test.ts new file mode 100644 index 0000000..9b729c0 --- /dev/null +++ b/src/__tests__/dateUtils.test.ts @@ -0,0 +1,342 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { + parseDate, + isCurrentDateInRange, + isCurrentDateByDate, + isWithinSevenDays, + extractEndDate, + calculateDueDate, + calculateRemainingTime, + timeAgo, +} from '@/lib/dateUtils'; + +describe('parseDate', () => { + it('하이픈 구분 날짜를 파싱', () => { + const date = parseDate('2024-03-15 09:00'); + expect(date).toBeInstanceOf(Date); + expect(date.getFullYear()).toBe(2024); + expect(date.getMonth()).toBe(2); // 0-indexed + expect(date.getDate()).toBe(15); + }); + + it('하이픈을 슬래시로 변환 (Safari 호환)', () => { + const date = parseDate('2024-01-01'); + expect(date.getFullYear()).toBe(2024); + }); + + it('시간 포함 문자열도 파싱', () => { + const date = parseDate('2024-12-31 23:59'); + expect(date.getMonth()).toBe(11); + expect(date.getDate()).toBe(31); + }); + + it('하이픈이 없는 문자열은 그대로 Date 생성자로 전달', () => { + const date = parseDate('2024/03/15'); + expect(date.getFullYear()).toBe(2024); + }); + + it('초까지 포함된 날짜 문자열', () => { + const date = parseDate('2024-06-15 09:30:45'); + expect(date.getHours()).toBe(9); + expect(date.getMinutes()).toBe(30); + }); +}); + +describe('isCurrentDateInRange', () => { + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date('2024-06-15 12:00:00')); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it('현재 날짜가 범위 내이면 true', () => { + expect(isCurrentDateInRange('2024-06-01 00:00 ~ 2024-06-30 23:59')).toBe(true); + }); + + it('현재 날짜가 범위 전이면 false', () => { + expect(isCurrentDateInRange('2024-07-01 00:00 ~ 2024-07-30 23:59')).toBe(false); + }); + + it('현재 날짜가 범위 후이면 false', () => { + expect(isCurrentDateInRange('2024-01-01 00:00 ~ 2024-01-31 23:59')).toBe(false); + }); + + it('null이면 false', () => { + expect(isCurrentDateInRange(null)).toBe(false); + }); + + it('빈 문자열이면 false', () => { + expect(isCurrentDateInRange('')).toBe(false); + }); + + it('" ~ "가 없으면 false', () => { + expect(isCurrentDateInRange('2024-06-01')).toBe(false); + }); + + it('" ~ " 뒤가 비어있으면 false', () => { + expect(isCurrentDateInRange('2024-06-01 ~ ')).toBe(false); + }); + + it('시작일과 같은 날이면 true', () => { + expect(isCurrentDateInRange('2024-06-15 00:00 ~ 2024-06-30 23:59')).toBe(true); + }); + + it('종료일과 같은 시간이면 true (<=)', () => { + vi.setSystemTime(new Date('2024-06-30 23:59:00')); + expect(isCurrentDateInRange('2024-06-01 00:00 ~ 2024-06-30 23:59')).toBe(true); + }); + + it('"~"만 있고 공백이 없어도 분리됨 (includes " ~ " 실패)', () => { + expect(isCurrentDateInRange('2024-06-01~2024-06-30')).toBe(false); + }); + + it('시작과 종료가 동일한 시간이면 그 정확한 시점에 true', () => { + vi.setSystemTime(new Date('2024-06-15 10:00:00')); + expect(isCurrentDateInRange('2024-06-15 10:00 ~ 2024-06-15 10:00')).toBe(true); + }); +}); + +describe('isCurrentDateByDate', () => { + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date('2024-06-15 12:00:00')); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it('미래 날짜이면 true', () => { + expect(isCurrentDateByDate('2024-12-31')).toBe(true); + }); + + it('과거 날짜이면 false', () => { + expect(isCurrentDateByDate('2024-01-01')).toBe(false); + }); + + it('null이면 false', () => { + expect(isCurrentDateByDate(null)).toBe(false); + }); + + it('빈 문자열이면 false', () => { + expect(isCurrentDateByDate('')).toBe(false); + }); + + it('길이 1 이하 문자열이면 false', () => { + expect(isCurrentDateByDate('X')).toBe(false); + }); +}); + +describe('isWithinSevenDays', () => { + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date('2024-06-15 12:00:00')); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it('3일 후는 7일 이내', () => { + expect(isWithinSevenDays('2024-06-18 12:00:00')).toBe(true); + }); + + it('정확히 7일 후는 7일 이내', () => { + expect(isWithinSevenDays('2024-06-22 12:00:00')).toBe(true); + }); + + it('8일 후는 7일 초과', () => { + expect(isWithinSevenDays('2024-06-23 12:00:01')).toBe(false); + }); + + it('과거 날짜는 false', () => { + expect(isWithinSevenDays('2024-06-10 12:00:00')).toBe(false); + }); + + it('오늘은 true (diff = 0)', () => { + expect(isWithinSevenDays('2024-06-15 12:00:00')).toBe(true); + }); + + it('7일 + 1초 후는 false', () => { + // 정확히 7일 = 604800000ms, 1초 추가 + const target = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000 + 1000); + expect(isWithinSevenDays(target.toISOString())).toBe(false); + }); + + it('1초 후는 true', () => { + const target = new Date(Date.now() + 1000); + expect(isWithinSevenDays(target.toISOString())).toBe(true); + }); +}); + +describe('extractEndDate', () => { + it('" ~ "로 분리된 범위에서 끝 날짜 추출', () => { + expect(extractEndDate('2024-01-01 ~ 2024-06-30')).toBe('2024-06-30'); + }); + + it('null이면 null', () => { + expect(extractEndDate(null)).toBeNull(); + }); + + it('" ~ "가 없으면 undefined → null', () => { + expect(extractEndDate('2024-06-30')).toBeNull(); + }); + + it('빈 문자열이면 null (split 결과 [1]이 undefined)', () => { + expect(extractEndDate('')).toBeNull(); + }); +}); + +describe('calculateDueDate', () => { + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date('2024-06-15 12:00:00')); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it('null이면 noInfo 상태', () => { + const result = calculateDueDate(null); + expect(result.status).toBe('noInfo'); + }); + + it('유효하지 않은 날짜면 invalid 상태', () => { + const result = calculateDueDate('invalid-date'); + expect(result.status).toBe('invalid'); + }); + + it('이미 지난 날짜면 expired 상태', () => { + const result = calculateDueDate('2024-06-14 12:00:00'); + expect(result.status).toBe('expired'); + }); + + it('1일 이상 남으면 daysLeft 상태', () => { + const result = calculateDueDate('2024-06-20 12:00:00'); + expect(result.status).toBe('daysLeft'); + }); + + it('1일 미만(시간 단위) 남으면 urgent 상태', () => { + const result = calculateDueDate('2024-06-15 18:00:00'); + expect(result.status).toBe('urgent'); + }); + + it('몇 분만 남으면 urgent + 분 단위 메시지', () => { + const result = calculateDueDate('2024-06-15 12:30:00'); + expect(result.status).toBe('urgent'); + expect(result.message).toContain('date.minutesLater'); + }); + + it('정확히 현재 시간이면 expired', () => { + const result = calculateDueDate('2024-06-15 12:00:00'); + expect(result.status).toBe('expired'); + }); + + it('정확히 24시간 후면 daysLeft (days=1)', () => { + const result = calculateDueDate('2024-06-16 12:00:00'); + expect(result.status).toBe('daysLeft'); + expect(result.message).toContain('days:1'); + }); + + it('23시간 59분 후면 urgent (hours 단위)', () => { + const result = calculateDueDate('2024-06-16 11:59:00'); + expect(result.status).toBe('urgent'); + expect(result.message).toContain('date.hoursLater'); + }); + + it('1초 후면 urgent + 0분 → hours가 0이므로 minutesLater', () => { + const result = calculateDueDate('2024-06-15 12:00:01'); + expect(result.status).toBe('urgent'); + expect(result.message).toContain('date.minutesLater'); + }); + + it('borderColor가 상태에 따라 다름', () => { + expect(calculateDueDate(null).borderColor).toBe('border-amber-500'); + expect(calculateDueDate('2024-06-14').borderColor).toBe('border-red-950'); + expect(calculateDueDate('2024-06-20').borderColor).toBe('border-amber-500'); + expect(calculateDueDate('2024-06-15 18:00:00').borderColor).toBe('border-red-700'); + }); +}); + +describe('calculateRemainingTime', () => { + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date('2024-06-15 12:00:00')); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it('null이면 noInfo', () => { + expect(calculateRemainingTime(null)).toContain('date.noInfo'); + }); + + it('과거 시간이면 expired', () => { + expect(calculateRemainingTime('2024-01-01 00:00:00')).toContain('date.expired'); + }); + + it('1일 이상 남으면 days/hours/minutes 포함', () => { + const result = calculateRemainingTime('2024-06-20 15:30:00'); + expect(result).toContain('date.remaining'); + }); + + it('1일 미만이면 hours/minutes만', () => { + const result = calculateRemainingTime('2024-06-15 18:30:00'); + expect(result).toContain('date.remainingHoursMinutes'); + }); + + it('1시간 미만이면 minutes만', () => { + const result = calculateRemainingTime('2024-06-15 12:45:00'); + expect(result).toContain('date.remainingMinutes'); + }); +}); + +describe('timeAgo', () => { + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date('2024-06-15 12:00:00')); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it('방금 전 (0분 차이)', () => { + expect(timeAgo(Date.now())).toContain('date.justNow'); + }); + + it('분 단위', () => { + const fiveMinAgo = Date.now() - 5 * 60 * 1000; + expect(timeAgo(fiveMinAgo)).toContain('date.minutesAgo'); + }); + + it('시간 단위', () => { + const twoHoursAgo = Date.now() - 2 * 60 * 60 * 1000; + expect(timeAgo(twoHoursAgo)).toContain('date.hoursAgo'); + }); + + it('일 단위', () => { + const threeDaysAgo = Date.now() - 3 * 24 * 60 * 60 * 1000; + expect(timeAgo(threeDaysAgo)).toContain('date.daysAgo'); + }); + + it('59초 전은 justNow (분 단위 미만)', () => { + const fiftyNineSecAgo = Date.now() - 59 * 1000; + expect(timeAgo(fiftyNineSecAgo)).toContain('date.justNow'); + }); + + it('정확히 60초 전은 minutesAgo', () => { + const oneMinAgo = Date.now() - 60 * 1000; + expect(timeAgo(oneMinAgo)).toContain('date.minutesAgo'); + }); + + it('정확히 1시간 전은 hoursAgo', () => { + const oneHourAgo = Date.now() - 60 * 60 * 1000; + expect(timeAgo(oneHourAgo)).toContain('date.hoursAgo'); + }); +}); diff --git a/src/__tests__/deduplicateInto.test.ts b/src/__tests__/deduplicateInto.test.ts new file mode 100644 index 0000000..6a20a71 --- /dev/null +++ b/src/__tests__/deduplicateInto.test.ts @@ -0,0 +1,78 @@ +import { describe, it, expect } from 'vitest'; +import { deduplicateInto } from '@/lib/deduplicateInto'; + +describe('deduplicateInto', () => { + it('빈 source는 target에 영향 없음', () => { + const target = [{ id: '1' }]; + const seen = new Set(); + deduplicateInto(target, [], seen, (item) => item.id); + expect(target).toHaveLength(1); + }); + + it('중복 없는 source는 전부 추가', () => { + const target: { id: string }[] = []; + const seen = new Set(); + deduplicateInto(target, [{ id: '1' }, { id: '2' }], seen, (item) => item.id); + expect(target).toHaveLength(2); + }); + + it('source 내부 중복은 첫 번째만 추가', () => { + const target: { id: string }[] = []; + const seen = new Set(); + deduplicateInto(target, [{ id: '1' }, { id: '1' }], seen, (item) => item.id); + expect(target).toHaveLength(1); + }); + + it('target에 이미 있는 키는 추가하지 않음 (seen으로 판단)', () => { + const target = [{ id: '1' }]; + const seen = new Set(['1']); + deduplicateInto(target, [{ id: '1' }, { id: '2' }], seen, (item) => item.id); + expect(target).toHaveLength(2); + expect(target[1].id).toBe('2'); + }); + + it('seen Set이 호출 후 업데이트됨', () => { + const target: { id: string }[] = []; + const seen = new Set(); + deduplicateInto(target, [{ id: 'a' }, { id: 'b' }], seen, (item) => item.id); + expect(seen.has('a')).toBe(true); + expect(seen.has('b')).toBe(true); + }); + + it('여러 번 호출 시 seen이 누적되어 중복 방지', () => { + const target: { id: string }[] = []; + const seen = new Set(); + deduplicateInto(target, [{ id: '1' }], seen, (item) => item.id); + deduplicateInto(target, [{ id: '1' }, { id: '2' }], seen, (item) => item.id); + expect(target).toHaveLength(2); + expect(target.map((t) => t.id)).toEqual(['1', '2']); + }); + + it('커스텀 getKey 함수 사용', () => { + type Item = { courseId: string; week: number }; + const target: Item[] = []; + const seen = new Set(); + const items: Item[] = [ + { courseId: 'C1', week: 1 }, + { courseId: 'C1', week: 1 }, // 중복 + { courseId: 'C1', week: 2 }, + ]; + deduplicateInto(target, items, seen, (item) => `${item.courseId}-${item.week}`); + expect(target).toHaveLength(2); + }); + + it('빈 키 문자열도 정상 동작', () => { + const target: { id: string }[] = []; + const seen = new Set(); + deduplicateInto(target, [{ id: '' }, { id: '' }], seen, (item) => item.id); + expect(target).toHaveLength(1); + }); + + it('target과 source가 같은 배열 참조여도 동작 (무한루프 아님 - for..of snapshot)', () => { + const arr = [{ id: '1' }]; + const seen = new Set(); + // source의 for..of는 호출 시점의 길이 기준이므로 안전 + deduplicateInto(arr, [{ id: '2' }], seen, (item) => item.id); + expect(arr).toHaveLength(2); + }); +}); diff --git a/src/__tests__/fetchHtml.test.ts b/src/__tests__/fetchHtml.test.ts new file mode 100644 index 0000000..a2c71a5 --- /dev/null +++ b/src/__tests__/fetchHtml.test.ts @@ -0,0 +1,75 @@ +import { describe, it, expect } from 'vitest'; +import { getText, getHref } from '@/lib/fetchHtml'; + +function el(html: string): Element { + const div = document.createElement('div'); + div.innerHTML = html; + return div; +} + +describe('getText', () => { + it('셀렉터로 텍스트 추출', () => { + const parent = el('Hello'); + expect(getText(parent, '.name')).toBe('Hello'); + }); + + it('텍스트 앞뒤 공백 trim', () => { + const parent = el(' Hello '); + expect(getText(parent, '.name')).toBe('Hello'); + }); + + it('셀렉터가 매칭 안 되면 null', () => { + const parent = el('Hello'); + expect(getText(parent, '.missing')).toBeNull(); + }); + + it('빈 텍스트 요소는 falsy이므로 null', () => { + const parent = el(''); + expect(getText(parent, '.name')).toBeNull(); + }); + + it('공백만 있는 텍스트는 trim 후 빈 문자열 → null', () => { + const parent = el(' '); + expect(getText(parent, '.name')).toBeNull(); + }); + + it('중첩 요소의 텍스트도 포함', () => { + const parent = el('
Bold text
'); + expect(getText(parent, '.wrap')).toBe('Bold text'); + }); + + it('여러 매칭 시 첫 번째 요소만 반환', () => { + const parent = el('FirstSecond'); + expect(getText(parent, '.item')).toBe('First'); + }); +}); + +describe('getHref', () => { + it('a 태그의 href 추출', () => { + const parent = el('Link'); + const result = getHref(parent, '.link'); + expect(result).toContain('example.com'); + }); + + it('셀렉터가 매칭 안 되면 null', () => { + const parent = el('No link'); + expect(getHref(parent, 'a')).toBeNull(); + }); + + it('href가 없는 a 태그는 빈 문자열 → falsy → null', () => { + const parent = el('No href'); + expect(getHref(parent, '.link')).toBeNull(); + }); + + it('a가 아닌 요소에 href가 없으면 null', () => { + const parent = el(''); + expect(getHref(parent, '.link')).toBeNull(); + }); + + it('상대 경로도 추출 (jsdom이 절대 경로로 변환할 수 있음)', () => { + const parent = el('Link'); + const result = getHref(parent, '.link'); + expect(result).toBeTruthy(); + expect(result).toContain('/path/to/page'); + }); +}); diff --git a/src/__tests__/filterData.test.ts b/src/__tests__/filterData.test.ts new file mode 100644 index 0000000..d108c6e --- /dev/null +++ b/src/__tests__/filterData.test.ts @@ -0,0 +1,376 @@ +import { describe, it, expect } from 'vitest'; +import { filterVods, filterAssigns, filterQuizzes } from '@/lib/filterData'; +import { Vod, Assign, Quiz, Filters } from '@/types'; + +const makeVod = (overrides: Partial = {}): Vod => ({ + courseId: 'C1', + courseTitle: '프로그래밍', + prof: '김교수', + week: 1, + subject: '1주차', + title: '강의1', + url: '/v1', + range: '2024-01-01 ~ 2024-01-07', + length: '30:00', + isAttendance: 'O', + weeklyAttendance: 'O', + ...overrides, +}); + +const makeAssign = (overrides: Partial = {}): Assign => ({ + courseId: 'C1', + courseTitle: '프로그래밍', + prof: '김교수', + subject: '1주차', + title: '과제1', + url: '/a1', + isSubmit: false, + dueDate: '2024-06-30', + ...overrides, +}); + +const makeQuiz = (overrides: Partial = {}): Quiz => ({ + courseId: 'C1', + courseTitle: '프로그래밍', + prof: '김교수', + subject: '1주차', + title: '퀴즈1', + url: '/q1', + isSubmit: false, + dueDate: '2024-06-30', + ...overrides, +}); + +const emptyFilters: Filters = { courseTitles: [] }; + +describe('filterVods', () => { + it('필터 없이 전체 반환', () => { + const vods = [makeVod(), makeVod({ title: '강의2' })]; + const result = filterVods(vods, emptyFilters, '', 'range'); + expect(result).toHaveLength(2); + }); + + it('과목 필터로 특정 과목만 반환', () => { + const vods = [ + makeVod({ courseTitle: '수학' }), + makeVod({ courseTitle: '영어' }), + ]; + const filters: Filters = { courseTitles: ['수학'] }; + const result = filterVods(vods, filters, '', 'range'); + expect(result).toHaveLength(1); + expect(result[0].courseTitle).toBe('수학'); + }); + + it('검색어로 제목 필터링', () => { + const vods = [ + makeVod({ title: '파이썬 기초' }), + makeVod({ title: '자바 고급' }), + ]; + const result = filterVods(vods, emptyFilters, '파이썬', 'range'); + expect(result).toHaveLength(1); + }); + + it('검색어로 교수명 필터링', () => { + const vods = [ + makeVod({ prof: '김교수' }), + makeVod({ prof: '이교수' }), + ]; + const result = filterVods(vods, emptyFilters, '이교수', 'range'); + expect(result).toHaveLength(1); + }); + + it('검색어 대소문자 무시', () => { + const vods = [makeVod({ title: 'Python Basics' })]; + const result = filterVods(vods, emptyFilters, 'python', 'range'); + expect(result).toHaveLength(1); + }); + + it('출석 상태 필터 - 출석만', () => { + const vods = [ + makeVod({ isAttendance: 'O' }), + makeVod({ isAttendance: 'X', title: '강의2' }), + ]; + // i18n mock: attendance.attended + const filters: Filters = { courseTitles: [], attendanceStatuses: ['attendance.attended'] }; + const result = filterVods(vods, filters, '', 'range'); + expect(result).toHaveLength(1); + expect(result[0].isAttendance).toBe('O'); + }); + + it('정렬: 미출석이 먼저 (isAttendance 기준)', () => { + const vods = [ + makeVod({ isAttendance: 'O', title: '출석' }), + makeVod({ isAttendance: 'X', title: '미출석' }), + ]; + const result = filterVods(vods, emptyFilters, '', 'range'); + // isAttended('O') = true → 출석이 앞으로? 아니, 코드에서 attendanceA ? -1 : 1 + // 실제로는 출석이 먼저 정렬됨 (출석 완료한 것을 상단) + // 아... 코드를 다시 보면: attendanceA ? -1 : 1 → 출석이면 -1 → 출석이 먼저 + expect(result[0].isAttendance).toBe('O'); + }); + + it('정렬: title 기준', () => { + const vods = [ + makeVod({ isAttendance: 'X', title: 'B강의' }), + makeVod({ isAttendance: 'X', title: 'A강의' }), + ]; + const result = filterVods(vods, emptyFilters, '', 'title'); + expect(result[0].title).toBe('A강의'); + expect(result[1].title).toBe('B강의'); + }); + + it('정렬: range 기준, null은 뒤로', () => { + const vods = [ + makeVod({ isAttendance: 'X', range: null, title: 'A' }), + makeVod({ isAttendance: 'X', range: '2024-01-01 ~ 2024-01-07', title: 'B' }), + ]; + const result = filterVods(vods, emptyFilters, '', 'range'); + expect(result[0].range).not.toBeNull(); + expect(result[1].range).toBeNull(); + }); + + it('빈 배열이면 빈 배열 반환', () => { + expect(filterVods([], emptyFilters, '', 'range')).toEqual([]); + }); + + it('출석 상태 필터가 빈 배열이면 전체 반환', () => { + const vods = [makeVod({ isAttendance: 'O' }), makeVod({ isAttendance: 'X', title: '강의2' })]; + const filters: Filters = { courseTitles: [], attendanceStatuses: [] }; + const result = filterVods(vods, filters, '', 'range'); + expect(result).toHaveLength(2); + }); + + it('정렬: 양쪽 모두 range null이면 순서 유지 (0 반환)', () => { + const vods = [ + makeVod({ isAttendance: 'X', range: null, title: 'B' }), + makeVod({ isAttendance: 'X', range: null, title: 'A' }), + ]; + const result = filterVods(vods, emptyFilters, '', 'range'); + // 둘 다 null이면 0 반환 → 원래 순서 유지 + expect(result[0].title).toBe('B'); + }); + + it('정렬: 출석 상태가 같으면 range로 정렬', () => { + const vods = [ + makeVod({ isAttendance: 'O', range: '2024-02-01 ~ 2024-02-07', title: 'B' }), + makeVod({ isAttendance: 'O', range: '2024-01-01 ~ 2024-01-07', title: 'A' }), + ]; + const result = filterVods(vods, emptyFilters, '', 'range'); + expect(result[0].title).toBe('A'); + }); + + it('검색어로 과목명 필터링', () => { + const vods = [ + makeVod({ courseTitle: '컴퓨터구조' }), + makeVod({ courseTitle: '운영체제', title: '강의2' }), + ]; + const result = filterVods(vods, emptyFilters, '컴퓨터', 'range'); + expect(result).toHaveLength(1); + expect(result[0].courseTitle).toBe('컴퓨터구조'); + }); + + it('과목 필터 + 검색어 동시 적용', () => { + const vods = [ + makeVod({ courseTitle: '수학', title: '미분' }), + makeVod({ courseTitle: '수학', title: '적분' }), + makeVod({ courseTitle: '영어', title: '미분' }), + ]; + const filters: Filters = { courseTitles: ['수학'] }; + const result = filterVods(vods, filters, '적분', 'range'); + expect(result).toHaveLength(1); + expect(result[0].title).toBe('적분'); + }); +}); + +describe('filterAssigns', () => { + it('필터 없이 전체 반환', () => { + const assigns = [makeAssign(), makeAssign({ title: '과제2' })]; + const result = filterAssigns(assigns, emptyFilters, '', 'dueDate'); + expect(result).toHaveLength(2); + }); + + it('과목 필터', () => { + const assigns = [ + makeAssign({ courseTitle: '수학' }), + makeAssign({ courseTitle: '영어' }), + ]; + const filters: Filters = { courseTitles: ['수학'] }; + const result = filterAssigns(assigns, filters, '', 'dueDate'); + expect(result).toHaveLength(1); + }); + + it('제출 상태 필터 - 미제출만', () => { + const assigns = [ + makeAssign({ isSubmit: false }), + makeAssign({ isSubmit: true, title: '과제2' }), + ]; + const filters: Filters = { courseTitles: [], submitStatuses: [false] }; + const result = filterAssigns(assigns, filters, '', 'dueDate'); + expect(result).toHaveLength(1); + expect(result[0].isSubmit).toBe(false); + }); + + it('정렬: 미제출이 먼저', () => { + const assigns = [ + makeAssign({ isSubmit: true, title: '제출됨' }), + makeAssign({ isSubmit: false, title: '미제출' }), + ]; + const result = filterAssigns(assigns, emptyFilters, '', 'dueDate'); + expect(result[0].isSubmit).toBe(false); + }); + + it('정렬: dueDate null은 맨 뒤', () => { + const assigns = [ + makeAssign({ isSubmit: false, dueDate: null, title: 'A' }), + makeAssign({ isSubmit: false, dueDate: '2024-06-15', title: 'B' }), + ]; + const result = filterAssigns(assigns, emptyFilters, '', 'dueDate'); + expect(result[0].dueDate).toBe('2024-06-15'); + expect(result[1].dueDate).toBeNull(); + }); + + it('정렬: 이른 마감이 먼저', () => { + const assigns = [ + makeAssign({ isSubmit: false, dueDate: '2024-07-01', title: 'B' }), + makeAssign({ isSubmit: false, dueDate: '2024-06-15', title: 'A' }), + ]; + const result = filterAssigns(assigns, emptyFilters, '', 'dueDate'); + expect(result[0].title).toBe('A'); + }); + + it('정렬: title 기준', () => { + const assigns = [ + makeAssign({ isSubmit: false, title: 'B과제' }), + makeAssign({ isSubmit: false, title: 'A과제' }), + ]; + const result = filterAssigns(assigns, emptyFilters, '', 'title'); + expect(result[0].title).toBe('A과제'); + }); + + it('검색어로 과목명 필터링', () => { + const assigns = [ + makeAssign({ courseTitle: '데이터베이스' }), + makeAssign({ courseTitle: '네트워크' }), + ]; + const result = filterAssigns(assigns, emptyFilters, '데이터', 'dueDate'); + expect(result).toHaveLength(1); + }); + + it('정렬: dueDate가 같으면 순서 유지', () => { + const assigns = [ + makeAssign({ isSubmit: false, dueDate: '2024-06-30', title: 'B' }), + makeAssign({ isSubmit: false, dueDate: '2024-06-30', title: 'A' }), + ]; + const result = filterAssigns(assigns, emptyFilters, '', 'dueDate'); + // 같은 dueDate → 0 반환 → 원래 순서 + expect(result[0].title).toBe('B'); + }); + + it('정렬: 양쪽 모두 dueDate null이면 순서 유지', () => { + const assigns = [ + makeAssign({ isSubmit: false, dueDate: null, title: 'B' }), + makeAssign({ isSubmit: false, dueDate: null, title: 'A' }), + ]; + const result = filterAssigns(assigns, emptyFilters, '', 'dueDate'); + expect(result[0].title).toBe('B'); + }); + + it('submitStatuses 필터가 빈 배열이면 전체 반환', () => { + const assigns = [ + makeAssign({ isSubmit: true, title: 'A' }), + makeAssign({ isSubmit: false, title: 'B' }), + ]; + const filters: Filters = { courseTitles: [], submitStatuses: [] }; + const result = filterAssigns(assigns, filters, '', 'dueDate'); + expect(result).toHaveLength(2); + }); + + it('제출/미제출 모두 포함하는 submitStatuses 필터', () => { + const assigns = [ + makeAssign({ isSubmit: true, title: 'A' }), + makeAssign({ isSubmit: false, title: 'B' }), + ]; + const filters: Filters = { courseTitles: [], submitStatuses: [true, false] }; + const result = filterAssigns(assigns, filters, '', 'dueDate'); + expect(result).toHaveLength(2); + }); +}); + +describe('filterQuizzes', () => { + it('필터 없이 전체 반환', () => { + const quizzes = [makeQuiz(), makeQuiz({ title: '퀴즈2' })]; + const result = filterQuizzes(quizzes, emptyFilters, '', 'dueDate'); + expect(result).toHaveLength(2); + }); + + it('제출 상태 필터', () => { + const quizzes = [ + makeQuiz({ isSubmit: false }), + makeQuiz({ isSubmit: true, title: '퀴즈2' }), + ]; + const filters: Filters = { courseTitles: [], submitStatuses: [true] }; + const result = filterQuizzes(quizzes, filters, '', 'dueDate'); + expect(result).toHaveLength(1); + expect(result[0].isSubmit).toBe(true); + }); + + it('정렬: 미제출 우선', () => { + const quizzes = [ + makeQuiz({ isSubmit: true, title: '제출됨' }), + makeQuiz({ isSubmit: false, title: '미제출' }), + ]; + const result = filterQuizzes(quizzes, emptyFilters, '', 'dueDate'); + expect(result[0].isSubmit).toBe(false); + }); + + it('정렬: dueDate null은 맨 뒤', () => { + const quizzes = [ + makeQuiz({ isSubmit: false, dueDate: null, title: 'A' }), + makeQuiz({ isSubmit: false, dueDate: '2024-06-15', title: 'B' }), + ]; + const result = filterQuizzes(quizzes, emptyFilters, '', 'dueDate'); + expect(result[0].dueDate).toBe('2024-06-15'); + }); + + it('검색어와 과목 필터 동시 적용', () => { + const quizzes = [ + makeQuiz({ courseTitle: '수학', title: '기말 퀴즈' }), + makeQuiz({ courseTitle: '수학', title: '중간 퀴즈' }), + makeQuiz({ courseTitle: '영어', title: '기말 퀴즈' }), + ]; + const filters: Filters = { courseTitles: ['수학'] }; + const result = filterQuizzes(quizzes, filters, '기말', 'dueDate'); + expect(result).toHaveLength(1); + expect(result[0].title).toBe('기말 퀴즈'); + expect(result[0].courseTitle).toBe('수학'); + }); + + it('정렬: 이른 마감이 먼저 (퀴즈)', () => { + const quizzes = [ + makeQuiz({ isSubmit: false, dueDate: '2024-07-15', title: 'B' }), + makeQuiz({ isSubmit: false, dueDate: '2024-06-15', title: 'A' }), + ]; + const result = filterQuizzes(quizzes, emptyFilters, '', 'dueDate'); + expect(result[0].title).toBe('A'); + }); + + it('정렬: title 기준 (퀴즈)', () => { + const quizzes = [ + makeQuiz({ isSubmit: false, title: 'B퀴즈' }), + makeQuiz({ isSubmit: false, title: 'A퀴즈' }), + ]; + const result = filterQuizzes(quizzes, emptyFilters, '', 'title'); + expect(result[0].title).toBe('A퀴즈'); + }); + + it('여러 과목 필터 동시 적용', () => { + const quizzes = [ + makeQuiz({ courseTitle: '수학', title: '퀴즈1' }), + makeQuiz({ courseTitle: '영어', title: '퀴즈2' }), + makeQuiz({ courseTitle: '물리', title: '퀴즈3' }), + ]; + const filters: Filters = { courseTitles: ['수학', '영어'] }; + const result = filterQuizzes(quizzes, filters, '', 'dueDate'); + expect(result).toHaveLength(2); + }); +}); diff --git a/src/__tests__/generateKey.test.ts b/src/__tests__/generateKey.test.ts new file mode 100644 index 0000000..b4aee94 --- /dev/null +++ b/src/__tests__/generateKey.test.ts @@ -0,0 +1,60 @@ +import { describe, it, expect } from 'vitest'; +import { makeVodKey, makeItemKey, makeVodGroupKey } from '@/lib/generateKey'; + +describe('makeVodKey', () => { + it('courseId-title-week 형식의 키 생성', () => { + expect(makeVodKey('123', '강의1', 3)).toBe('123-강의1-3'); + }); + + it('빈 문자열도 키 생성', () => { + expect(makeVodKey('', '', 0)).toBe('--0'); + }); + + it('특수문자 포함', () => { + expect(makeVodKey('c-1', 'A/B', 1)).toBe('c-1-A/B-1'); + }); +}); + +describe('makeItemKey', () => { + it('courseId-title-dueDate 형식의 키 생성', () => { + expect(makeItemKey('123', '과제1', '2024-06-15')).toBe('123-과제1-2024-06-15'); + }); + + it('빈 값 처리', () => { + expect(makeItemKey('', '', '')).toBe('--'); + }); +}); + +describe('makeVodGroupKey', () => { + it('courseId-subject-range 형식의 키 생성', () => { + expect(makeVodGroupKey('123', '1주차', '2024-01-01 ~ 2024-01-07')) + .toBe('123-1주차-2024-01-01 ~ 2024-01-07'); + }); + + it('range가 null이면 null 포함', () => { + expect(makeVodGroupKey('123', '1주차', null)).toBe('123-1주차-null'); + }); + + it('같은 courseId+subject이지만 다른 range면 다른 키', () => { + const key1 = makeVodGroupKey('123', '1주차', '2024-01-01 ~ 2024-01-07'); + const key2 = makeVodGroupKey('123', '1주차', '2024-01-08 ~ 2024-01-14'); + expect(key1).not.toBe(key2); + }); + + it('한글/특수문자 포함 키 생성', () => { + expect(makeVodGroupKey('C-1', '1주차 (보충)', '2024-01-01 ~ 2024-01-07')) + .toBe('C-1-1주차 (보충)-2024-01-01 ~ 2024-01-07'); + }); +}); + +describe('키 충돌 검증', () => { + it('makeVodKey - 서로 다른 입력은 다른 키 생성', () => { + expect(makeVodKey('C1', '강의1', 1)).not.toBe(makeVodKey('C1', '강의1', 2)); + expect(makeVodKey('C1', '강의1', 1)).not.toBe(makeVodKey('C2', '강의1', 1)); + expect(makeVodKey('C1', '강의1', 1)).not.toBe(makeVodKey('C1', '강의2', 1)); + }); + + it('makeItemKey - 서로 다른 입력은 다른 키 생성', () => { + expect(makeItemKey('C1', '과제', '2024-01')).not.toBe(makeItemKey('C1', '과제', '2024-02')); + }); +}); diff --git a/src/__tests__/lmsKeywords.test.ts b/src/__tests__/lmsKeywords.test.ts new file mode 100644 index 0000000..0fd7517 --- /dev/null +++ b/src/__tests__/lmsKeywords.test.ts @@ -0,0 +1,97 @@ +import { describe, it, expect } from 'vitest'; +import { normalizeLmsDate, normalizeLmsRange, NOT_SUBMITTED, BULK_APPROVED } from '@/lib/lmsKeywords'; + +describe('normalizeLmsDate', () => { + it('null 반환', () => { + expect(normalizeLmsDate(null)).toBeNull(); + }); + + it('빈 문자열은 falsy이므로 null 반환', () => { + expect(normalizeLmsDate('')).toBeNull(); + }); + + it('이미 ISO-like 형식(ko/en)이면 그대로 반환', () => { + expect(normalizeLmsDate('2024-03-15 09:00')).toBe('2024-03-15 09:00'); + }); + + it('ISO 형식에 초까지 포함되어도 그대로 반환', () => { + expect(normalizeLmsDate('2024-03-15 09:00:00')).toBe('2024-03-15 09:00:00'); + }); + + it('일본어 형식 파싱 (요일 포함)', () => { + expect(normalizeLmsDate('2023年 04月 24日(月曜日) 10:50')).toBe('2023-04-24 10:50'); + }); + + it('중국어 형식 파싱 (星期 포함)', () => { + expect(normalizeLmsDate('2023年04月24日 星期一 10:50')).toBe('2023-04-24 10:50'); + }); + + it('일본어 형식 - 공백 없는 경우', () => { + expect(normalizeLmsDate('2023年04月24日 10:50')).toBe('2023-04-24 10:50'); + }); + + it('앞뒤 공백은 trim', () => { + expect(normalizeLmsDate(' 2024-03-15 09:00 ')).toBe('2024-03-15 09:00'); + }); + + it('매칭 안 되는 형식은 원본 반환', () => { + expect(normalizeLmsDate('March 15, 2024')).toBe('March 15, 2024'); + }); +}); + +describe('normalizeLmsRange', () => { + it('null 반환', () => { + expect(normalizeLmsRange(null)).toBeNull(); + }); + + it('~ 없는 단일 날짜는 normalizeLmsDate로 처리', () => { + expect(normalizeLmsRange('2023年04月24日 10:50')).toBe('2023-04-24 10:50'); + }); + + it('ko/en range 정규화', () => { + expect(normalizeLmsRange('2024-03-15 09:00 ~ 2024-03-22 23:59')) + .toBe('2024-03-15 09:00 ~ 2024-03-22 23:59'); + }); + + it('ja range 정규화', () => { + expect(normalizeLmsRange('2023年 04月 24日(月曜日) 10:50 ~ 2023年 05月 01日(月曜日) 23:59')) + .toBe('2023-04-24 10:50 ~ 2023-05-01 23:59'); + }); + + it('zh range 정규화', () => { + expect(normalizeLmsRange('2023年04月24日 星期一 10:50 ~ 2023年05月01日 星期一 23:59')) + .toBe('2023-04-24 10:50 ~ 2023-05-01 23:59'); + }); + + it('빈 문자열은 falsy이므로 null 반환', () => { + expect(normalizeLmsRange('')).toBeNull(); + }); +}); + +describe('NOT_SUBMITTED', () => { + it('한국어 포함', () => { + expect(NOT_SUBMITTED).toContain('미제출'); + }); + + it('영어 포함', () => { + expect(NOT_SUBMITTED).toContain('Not submitted'); + }); + + it('일본어 포함', () => { + expect(NOT_SUBMITTED).toContain('提出なし'); + }); + + it('중국어 포함', () => { + expect(NOT_SUBMITTED).toContain('没有作业'); + }); +}); + +describe('BULK_APPROVED', () => { + it('한국어 포함', () => { + expect(BULK_APPROVED).toContain('일괄출석인정'); + }); + + it('영어 포함', () => { + expect(BULK_APPROVED).toContain('Batch attendance'); + }); +}); diff --git a/src/__tests__/parseCourses.test.ts b/src/__tests__/parseCourses.test.ts new file mode 100644 index 0000000..dfa80c8 --- /dev/null +++ b/src/__tests__/parseCourses.test.ts @@ -0,0 +1,238 @@ +import { describe, it, expect } from 'vitest'; +import { parseCoursesFromDOM } from '@/lib/parseCourses'; + +function setupDOM(html: string) { + document.body.innerHTML = html; +} + +describe('parseCoursesFromDOM', () => { + it('정상적인 과목 리스트 파싱', () => { + setupDOM(` + + `); + const courses = parseCoursesFromDOM(); + expect(courses).toHaveLength(1); + expect(courses[0].courseId).toBe('12345'); + expect(courses[0].courseTitle).toBe('프로그래밍 기초'); + expect(courses[0].prof).toBe('김교수'); + }); + + it('course_link가 없는 li는 무시', () => { + setupDOM(` +
    +
  • 빈 항목
  • +
+ `); + expect(parseCoursesFromDOM()).toEqual([]); + }); + + it('빈 DOM이면 빈 배열', () => { + setupDOM(''); + expect(parseCoursesFromDOM()).toEqual([]); + }); + + it('커뮤니티(비교과) 과목 식별', () => { + setupDOM(` + + `); + const courses = parseCoursesFromDOM(); + expect(courses[0].isCommunity).toBe(true); + }); + + it('일반 과목은 isCommunity가 false', () => { + setupDOM(` + + `); + const courses = parseCoursesFromDOM(); + expect(courses[0].isCommunity).toBe(false); + }); + + it('제목에서 "new" 텍스트 제거', () => { + setupDOM(` + + `); + const courses = parseCoursesFromDOM(); + expect(courses[0].courseTitle).toBe('자료구조'); + }); + + it('제목에서 대괄호 제거', () => { + setupDOM(` + + `); + const courses = parseCoursesFromDOM(); + expect(courses[0].courseTitle).toBe(' 알고리즘'); + }); + + it('courseId가 없는 URL은 무시', () => { + setupDOM(` + + `); + expect(parseCoursesFromDOM()).toEqual([]); + }); + + it('교수명이 없는 항목은 무시', () => { + setupDOM(` + + `); + expect(parseCoursesFromDOM()).toEqual([]); + }); + + it('여러 과목 동시 파싱', () => { + setupDOM(` + + `); + const courses = parseCoursesFromDOM(); + expect(courses).toHaveLength(2); + }); + + it('h1 태그에서도 제목 파싱', () => { + setupDOM(` + + `); + const courses = parseCoursesFromDOM(); + expect(courses).toHaveLength(1); + expect(courses[0].courseTitle).toBe('컴파일러'); + }); + + it('h2 태그에서도 제목 파싱', () => { + setupDOM(` + + `); + const courses = parseCoursesFromDOM(); + expect(courses).toHaveLength(1); + expect(courses[0].courseTitle).toBe('네트워크'); + }); + + it('"New" 대문자도 제거 (case insensitive)', () => { + setupDOM(` + + `); + const courses = parseCoursesFromDOM(); + expect(courses[0].courseTitle).toBe('운영체제'); + }); + + it('제목이 빈 문자열이면 무시 (courseTitle falsy)', () => { + setupDOM(` + + `); + expect(parseCoursesFromDOM()).toEqual([]); + }); + + it('유효한 항목과 무효한 항목 혼합 시 유효한 것만 반환', () => { + setupDOM(` + + `); + const courses = parseCoursesFromDOM(); + expect(courses).toHaveLength(1); + expect(courses[0].courseTitle).toBe('유효과목'); + }); +}); diff --git a/src/__tests__/setup.ts b/src/__tests__/setup.ts new file mode 100644 index 0000000..0b4f49a --- /dev/null +++ b/src/__tests__/setup.ts @@ -0,0 +1,15 @@ +import { vi } from 'vitest'; + +// Mock i18n - 키를 그대로 반환하되, interpolation 값이 있으면 포함 +vi.mock('@/i18n', () => ({ + default: { + t: (key: string, options?: Record) => { + const opts = { ...options }; + delete opts.ns; + const params = Object.entries(opts) + .map(([k, v]) => `${k}:${v}`) + .join(','); + return params ? `${key}{${params}}` : key; + }, + }, +})); diff --git a/src/__tests__/storage.test.ts b/src/__tests__/storage.test.ts new file mode 100644 index 0000000..bb0690f --- /dev/null +++ b/src/__tests__/storage.test.ts @@ -0,0 +1,99 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { saveDataToStorage, loadDataFromStorage, loadAndTransform } from '@/lib/storage'; + +// chrome.storage.local mock +const mockStorage: Record = {}; + +beforeEach(() => { + Object.keys(mockStorage).forEach((key) => delete mockStorage[key]); + + vi.stubGlobal('chrome', { + storage: { + local: { + set: vi.fn((items: Record, cb?: () => void) => { + Object.assign(mockStorage, items); + cb?.(); + }), + get: vi.fn((keys: string[], cb: (result: Record) => void) => { + const result: Record = {}; + keys.forEach((key) => { + if (mockStorage[key] !== undefined) result[key] = mockStorage[key]; + }); + cb(result); + }), + }, + }, + }); +}); + +describe('saveDataToStorage', () => { + it('데이터를 chrome.storage.local에 저장', () => { + saveDataToStorage('testKey', { value: 42 }); + expect(chrome.storage.local.set).toHaveBeenCalledWith({ testKey: { value: 42 } }, expect.any(Function)); + expect(mockStorage['testKey']).toEqual({ value: 42 }); + }); + + it('문자열 데이터 저장', () => { + saveDataToStorage('str', 'hello'); + expect(mockStorage['str']).toBe('hello'); + }); + + it('배열 데이터 저장', () => { + saveDataToStorage('arr', [1, 2, 3]); + expect(mockStorage['arr']).toEqual([1, 2, 3]); + }); +}); + +describe('loadDataFromStorage', () => { + it('저장된 데이터를 콜백으로 반환', () => { + mockStorage['myKey'] = { name: 'test' }; + const callback = vi.fn(); + + loadDataFromStorage('myKey', callback); + expect(callback).toHaveBeenCalledWith({ name: 'test' }); + }); + + it('데이터가 없으면 null 반환', () => { + const callback = vi.fn(); + + loadDataFromStorage('nonExistent', callback); + expect(callback).toHaveBeenCalledWith(null); + }); +}); + +describe('loadAndTransform', () => { + it('JSON 문자열을 파싱하고 변환 함수 적용', () => { + mockStorage['items'] = JSON.stringify([1, 2, 3]); + const callback = vi.fn(); + + loadAndTransform('items', (data) => data.reduce((a, b) => a + b, 0), callback); + expect(callback).toHaveBeenCalledWith(6); + }); + + it('이미 파싱된 데이터에 변환 함수 적용', () => { + mockStorage['items'] = [10, 20]; + const callback = vi.fn(); + + loadAndTransform('items', (data) => data.length, callback); + expect(callback).toHaveBeenCalledWith(2); + }); + + it('데이터가 없으면 콜백 호출 안 함', () => { + const callback = vi.fn(); + + loadAndTransform('missing', (data) => data, callback); + expect(callback).not.toHaveBeenCalled(); + }); + + it('잘못된 JSON이면 콜백 호출 안 함', () => { + mockStorage['bad'] = '{invalid json'; + const callback = vi.fn(); + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + + loadAndTransform('bad', (data) => data, callback); + expect(callback).not.toHaveBeenCalled(); + expect(consoleSpy).toHaveBeenCalled(); + + consoleSpy.mockRestore(); + }); +}); diff --git a/src/__tests__/stringUtils.test.ts b/src/__tests__/stringUtils.test.ts new file mode 100644 index 0000000..7110c26 --- /dev/null +++ b/src/__tests__/stringUtils.test.ts @@ -0,0 +1,66 @@ +import { describe, it, expect } from 'vitest'; +import { removeSquareBrackets, formatDateString } from '@/lib/stringUtils'; + +describe('removeSquareBrackets', () => { + it('대괄호와 내용을 제거', () => { + expect(removeSquareBrackets('[A반] 프로그래밍')).toBe(' 프로그래밍'); + }); + + it('여러 대괄호를 모두 제거', () => { + expect(removeSquareBrackets('[A반] [2학기] 프로그래밍')).toBe(' 프로그래밍'); + }); + + it('대괄호가 없으면 원본 반환', () => { + expect(removeSquareBrackets('프로그래밍')).toBe('프로그래밍'); + }); + + it('빈 문자열 처리', () => { + expect(removeSquareBrackets('')).toBe(''); + }); + + it('빈 대괄호 제거', () => { + expect(removeSquareBrackets('[] 과목')).toBe(' 과목'); + }); + + it('중첩 대괄호: [^\\]]*는 ]를 포함하지 않으므로 [[내부] 매칭 → "]" 남음', () => { + // 정규식 /\[[^\]]*\]/g: [[내부] 가 첫 매치 → 제거 → "]" 남음 + expect(removeSquareBrackets('[[내부]]')).toBe(']'); + }); + + it('닫히지 않은 대괄호는 유지', () => { + expect(removeSquareBrackets('[미완성')).toBe('[미완성'); + }); + + it('숫자가 포함된 대괄호 제거', () => { + expect(removeSquareBrackets('[001] 과제')).toBe(' 과제'); + }); + + it('특수문자가 포함된 대괄호 제거', () => { + expect(removeSquareBrackets('[A-1반] 수업')).toBe(' 수업'); + }); +}); + +describe('formatDateString', () => { + it('전체 날짜 범위를 축약 형식으로 변환', () => { + expect(formatDateString('2024-03-15 09:00:00 ~ 2024-03-22 23:59:00')) + .toBe('24.03.15 09:00 ~ 24.03.22 23:59'); + }); + + it('null이면 i18n 키 반환', () => { + const result = formatDateString(null); + expect(result).toBe('date.noDeadline'); + }); + + it('매칭되지 않는 형식은 원본 그대로 반환', () => { + expect(formatDateString('마감 없음')).toBe('마감 없음'); + }); + + it('빈 문자열은 falsy이므로 noDeadline 반환', () => { + expect(formatDateString('')).toBe('date.noDeadline'); + }); + + it('초 부분이 없는 형식은 원본 반환', () => { + expect(formatDateString('2024-03-15 09:00 ~ 2024-03-22 23:59')) + .toBe('2024-03-15 09:00 ~ 2024-03-22 23:59'); + }); +}); diff --git a/src/__tests__/summarizeCourseData.test.ts b/src/__tests__/summarizeCourseData.test.ts new file mode 100644 index 0000000..67ee53a --- /dev/null +++ b/src/__tests__/summarizeCourseData.test.ts @@ -0,0 +1,116 @@ +import { describe, it, expect } from 'vitest'; +import { summarizeVods } from '@/lib/summarizeCourseData'; +import { Vod } from '@/types'; + +const makeVod = (overrides: Partial = {}): Vod => ({ + courseId: 'C1', + courseTitle: '프로그래밍', + prof: '김교수', + week: 1, + subject: '1주차', + title: '강의1', + url: '/v1', + range: '2024-01-01 ~ 2024-01-07', + length: '30:00', + isAttendance: 'O', + weeklyAttendance: 'O', + ...overrides, +}); + +describe('summarizeVods', () => { + it('빈 배열이면 done=0, total=0', () => { + expect(summarizeVods([])).toEqual({ done: 0, total: 0 }); + }); + + it('같은 그룹의 VOD들은 하나로 카운트', () => { + const vods = [ + makeVod({ title: '파트1' }), + makeVod({ title: '파트2' }), + ]; // 같은 courseId+subject+range → 1그룹 + const result = summarizeVods(vods); + expect(result.total).toBe(1); + }); + + it('다른 그룹은 별도 카운트', () => { + const vods = [ + makeVod({ subject: '1주차' }), + makeVod({ subject: '2주차' }), + ]; + const result = summarizeVods(vods); + expect(result.total).toBe(2); + }); + + it('weeklyAttendance가 O이면 done 카운트', () => { + const vods = [ + makeVod({ subject: '1주차', weeklyAttendance: 'O' }), + makeVod({ subject: '2주차', weeklyAttendance: 'X' }), + ]; + const result = summarizeVods(vods); + expect(result.done).toBe(1); + expect(result.total).toBe(2); + }); + + it('그룹의 첫 번째 항목의 weeklyAttendance로 판단', () => { + const vods = [ + makeVod({ title: '파트1', weeklyAttendance: 'O' }), + makeVod({ title: '파트2', weeklyAttendance: 'X' }), + ]; // 같은 그룹 → 첫 번째(O) 기준 + const result = summarizeVods(vods); + expect(result.done).toBe(1); + }); + + it('모두 출석이면 done === total', () => { + const vods = [ + makeVod({ subject: '1주차', weeklyAttendance: 'O' }), + makeVod({ subject: '2주차', weeklyAttendance: 'O' }), + ]; + const result = summarizeVods(vods); + expect(result.done).toBe(result.total); + }); + + it('모두 미출석이면 done === 0', () => { + const vods = [ + makeVod({ subject: '1주차', weeklyAttendance: 'X' }), + makeVod({ subject: '2주차', weeklyAttendance: 'X' }), + ]; + const result = summarizeVods(vods); + expect(result.done).toBe(0); + }); + + it('다른 courseId인 같은 subject+range는 별도 그룹', () => { + const vods = [ + makeVod({ courseId: 'C1', subject: '1주차' }), + makeVod({ courseId: 'C2', subject: '1주차' }), + ]; + const result = summarizeVods(vods); + expect(result.total).toBe(2); + }); + + it('같은 courseId+subject이지만 다른 range는 별도 그룹', () => { + const vods = [ + makeVod({ range: '2024-01-01 ~ 2024-01-07' }), + makeVod({ range: '2024-01-08 ~ 2024-01-14' }), + ]; + const result = summarizeVods(vods); + expect(result.total).toBe(2); + }); + + it('weeklyAttendance가 "o" (소문자)이면 isAttended → done 카운트', () => { + const vods = [makeVod({ subject: '1주차', weeklyAttendance: 'o' })]; + const result = summarizeVods(vods); + expect(result.done).toBe(1); + }); + + it('weeklyAttendance가 빈 문자열이면 미출석', () => { + const vods = [makeVod({ subject: '1주차', weeklyAttendance: '' })]; + const result = summarizeVods(vods); + expect(result.done).toBe(0); + }); + + it('단일 VOD 단일 그룹', () => { + const vods = [makeVod()]; + const result = summarizeVods(vods); + expect(result.total).toBe(1); + expect(result.done).toBe(1); + }); +}); diff --git a/src/__tests__/transformCalendarEvents.test.ts b/src/__tests__/transformCalendarEvents.test.ts new file mode 100644 index 0000000..302b72b --- /dev/null +++ b/src/__tests__/transformCalendarEvents.test.ts @@ -0,0 +1,143 @@ +import { describe, it, expect } from 'vitest'; +import { vodGroupsToEvents, dueDateItemToEvent } from '@/lib/transformCalendarEvents'; +import { Vod } from '@/types'; + +const makeVod = (overrides: Partial = {}): Vod => ({ + courseId: 'C1', + courseTitle: '[A반] 프로그래밍', + prof: '김교수', + week: 1, + subject: '[1주차] 개론', + title: '강의1', + url: '/v1', + range: '2024-01-01 09:00 ~ 2024-01-07 23:59', + length: '30:00', + isAttendance: 'O', + weeklyAttendance: 'O', + ...overrides, +}); + +describe('vodGroupsToEvents', () => { + it('같은 courseId+subject+range는 하나의 이벤트로 그룹핑', () => { + const vods = [ + makeVod({ title: '파트1' }), + makeVod({ title: '파트2' }), + ]; + const events = vodGroupsToEvents(vods); + expect(events).toHaveLength(1); + expect(events[0].type).toBe('vod'); + }); + + it('다른 subject면 별도 이벤트', () => { + const vods = [ + makeVod({ subject: '1주차' }), + makeVod({ subject: '2주차' }), + ]; + const events = vodGroupsToEvents(vods); + expect(events).toHaveLength(2); + }); + + it('range에서 시작/종료 날짜 파싱', () => { + const events = vodGroupsToEvents([makeVod()]); + expect(events[0].start).toBeInstanceOf(Date); + expect(events[0].end).toBeInstanceOf(Date); + }); + + it('range가 null이면 start/end도 null', () => { + const events = vodGroupsToEvents([makeVod({ range: null })]); + expect(events[0].start).toBeNull(); + expect(events[0].end).toBeNull(); + }); + + it('대괄호가 title/subject에서 제거됨', () => { + const events = vodGroupsToEvents([makeVod()]); + expect(events[0].title).not.toContain('['); + expect(events[0].subject).not.toContain('['); + }); + + it('빈 배열이면 빈 이벤트 배열', () => { + expect(vodGroupsToEvents([])).toEqual([]); + }); + + it('다른 courseId면 같은 subject+range여도 별도 이벤트', () => { + const vods = [ + makeVod({ courseId: 'C1' }), + makeVod({ courseId: 'C2' }), + ]; + const events = vodGroupsToEvents(vods); + expect(events).toHaveLength(2); + }); + + it('같은 courseId+subject이지만 다른 range면 별도 이벤트', () => { + const vods = [ + makeVod({ range: '2024-01-01 09:00 ~ 2024-01-07 23:59' }), + makeVod({ range: '2024-01-08 09:00 ~ 2024-01-14 23:59' }), + ]; + const events = vodGroupsToEvents(vods); + expect(events).toHaveLength(2); + }); + + it('그룹의 첫 번째 VOD 기준으로 title/subject 설정', () => { + const vods = [ + makeVod({ courseTitle: '[A반] 수학', subject: '[1주차] 개론', title: '파트1' }), + makeVod({ courseTitle: '[B반] 수학', subject: '[2주차] 심화', title: '파트2' }), + ]; + // 같은 courseId+subject+range이면 하나로 묶이므로, 다른 subject면 별도 + const events = vodGroupsToEvents(vods); + expect(events).toHaveLength(2); + }); +}); + +describe('dueDateItemToEvent', () => { + it('assign 타입 이벤트 생성', () => { + const item = { courseId: 'C1', courseTitle: '[A반] 수학', title: '과제1', dueDate: '2024-06-30' }; + const event = dueDateItemToEvent(item, 'assign'); + expect(event.type).toBe('assign'); + expect(event.start).toBeInstanceOf(Date); + expect(event.title).toBe(' 수학'); + }); + + it('quiz 타입 이벤트 생성', () => { + const item = { courseId: 'C1', courseTitle: '영어', title: '퀴즈1', dueDate: '2024-06-30' }; + const event = dueDateItemToEvent(item, 'quiz'); + expect(event.type).toBe('quiz'); + }); + + it('dueDate가 null이면 start/end null', () => { + const item = { courseId: 'C1', courseTitle: '수학', title: '과제1', dueDate: null }; + const event = dueDateItemToEvent(item, 'assign'); + expect(event.start).toBeNull(); + expect(event.end).toBeNull(); + }); + + it('id는 courseId+title+dueDate 조합', () => { + const item = { courseId: 'C1', courseTitle: '수학', title: '과제1', dueDate: '2024-06-30' }; + const event = dueDateItemToEvent(item, 'assign'); + expect(event.id).toBe('C1과제12024-06-30'); + }); + + it('dueDate가 startOfDay로 정규화됨', () => { + const item = { courseId: 'C1', courseTitle: '수학', title: '과제1', dueDate: '2024-06-30 15:30:00' }; + const event = dueDateItemToEvent(item, 'assign'); + expect(event.start!.getHours()).toBe(0); + expect(event.start!.getMinutes()).toBe(0); + }); + + it('start와 end가 동일한 Date 값', () => { + const item = { courseId: 'C1', courseTitle: '수학', title: '과제1', dueDate: '2024-06-30' }; + const event = dueDateItemToEvent(item, 'assign'); + expect(event.start!.getTime()).toBe(event.end!.getTime()); + }); + + it('subject는 item의 title에서 대괄호 제거한 값', () => { + const item = { courseId: 'C1', courseTitle: '수학', title: '[3주차] 리포트', dueDate: '2024-06-30' }; + const event = dueDateItemToEvent(item, 'assign'); + expect(event.subject).toBe(' 리포트'); + }); + + it('dueDate null일 때 id에 null 문자열 포함', () => { + const item = { courseId: 'C1', courseTitle: '수학', title: '과제1', dueDate: null }; + const event = dueDateItemToEvent(item, 'assign'); + expect(event.id).toBe('C1과제1null'); + }); +}); diff --git a/src/__tests__/transformCourseData.test.ts b/src/__tests__/transformCourseData.test.ts new file mode 100644 index 0000000..bc64f6a --- /dev/null +++ b/src/__tests__/transformCourseData.test.ts @@ -0,0 +1,154 @@ +import { describe, it, expect } from 'vitest'; +import { mergeVodWithAttendance, mergeDueDateItems } from '@/lib/transformCourseData'; +import { CourseBase } from '@/types'; + +const baseCourse: CourseBase = { + courseId: 'C1', + courseTitle: '프로그래밍', + prof: '김교수', +}; + +describe('mergeVodWithAttendance', () => { + it('title+week가 일치하는 항목만 병합', () => { + const vodList = [ + { week: 1, subject: '1주차', title: '강의1', url: '/v1', range: '2024-01-01 ~ 2024-01-07', length: '30:00' }, + { week: 2, subject: '2주차', title: '강의2', url: '/v2', range: null, length: '45:00' }, + ]; + const attendanceList = [ + { title: '강의1', isAttendance: 'O', weeklyAttendance: 'O', week: 1 }, + // week 2의 출석 데이터 없음 + ]; + + const result = mergeVodWithAttendance(baseCourse, vodList, attendanceList); + expect(result).toHaveLength(1); + expect(result[0].title).toBe('강의1'); + expect(result[0].isAttendance).toBe('O'); + expect(result[0].courseId).toBe('C1'); + }); + + it('빈 vodList이면 빈 배열 반환', () => { + const result = mergeVodWithAttendance(baseCourse, [], []); + expect(result).toEqual([]); + }); + + it('빈 attendanceList이면 모든 VOD 누락', () => { + const vodList = [ + { week: 1, subject: '1주차', title: '강의1', url: '/v1', range: null, length: '30:00' }, + ]; + const result = mergeVodWithAttendance(baseCourse, vodList, []); + expect(result).toEqual([]); + }); + + it('같은 title이지만 다른 week이면 매칭 안 됨', () => { + const vodList = [ + { week: 1, subject: '1주차', title: '강의1', url: '/v1', range: null, length: '30:00' }, + ]; + const attendanceList = [ + { title: '강의1', isAttendance: 'O', weeklyAttendance: 'O', week: 2 }, + ]; + const result = mergeVodWithAttendance(baseCourse, vodList, attendanceList); + expect(result).toEqual([]); + }); + + it('같은 week이지만 다른 title이면 매칭 안 됨', () => { + const vodList = [ + { week: 1, subject: '1주차', title: '강의A', url: '/v1', range: null, length: '30:00' }, + ]; + const attendanceList = [ + { title: '강의B', isAttendance: 'O', weeklyAttendance: 'O', week: 1 }, + ]; + const result = mergeVodWithAttendance(baseCourse, vodList, attendanceList); + expect(result).toEqual([]); + }); + + it('동일 title+week 출석이 여러 개면 마지막이 사용됨 (Map 덮어쓰기)', () => { + const vodList = [ + { week: 1, subject: '1주차', title: '강의1', url: '/v1', range: null, length: '30:00' }, + ]; + const attendanceList = [ + { title: '강의1', isAttendance: 'X', weeklyAttendance: 'X', week: 1 }, + { title: '강의1', isAttendance: 'O', weeklyAttendance: 'O', week: 1 }, + ]; + const result = mergeVodWithAttendance(baseCourse, vodList, attendanceList); + expect(result[0].isAttendance).toBe('O'); + }); + + it('course 정보가 결과에 포함됨', () => { + const vodList = [ + { week: 1, subject: '1주차', title: '강의1', url: '/v1', range: null, length: '30:00' }, + ]; + const attendanceList = [ + { title: '강의1', isAttendance: 'O', weeklyAttendance: 'O', week: 1 }, + ]; + const result = mergeVodWithAttendance(baseCourse, vodList, attendanceList); + expect(result[0].courseTitle).toBe('프로그래밍'); + expect(result[0].prof).toBe('김교수'); + }); +}); + +describe('mergeDueDateItems', () => { + it('course 정보를 각 항목에 병합', () => { + const items = [ + { title: '과제1', dueDate: '2024-06-30' }, + { title: '과제2', dueDate: null }, + ]; + const result = mergeDueDateItems(baseCourse, items); + expect(result).toHaveLength(2); + expect(result[0].courseId).toBe('C1'); + expect(result[0].title).toBe('과제1'); + expect(result[1].dueDate).toBeNull(); + }); + + it('빈 배열이면 빈 배열 반환', () => { + expect(mergeDueDateItems(baseCourse, [])).toEqual([]); + }); + + it('추가 필드가 있어도 보존됨', () => { + const items = [{ title: '과제1', dueDate: '2024-06-30', extra: 'value' }]; + const result = mergeDueDateItems(baseCourse, items); + expect(result[0].extra).toBe('value'); + }); + + it('item의 필드가 course 필드와 겹치면 item이 우선', () => { + // T에 courseTitle이 있으면 spread 순서에 의해 item 값이 우선 + const items = [{ title: '과제1', dueDate: '2024-06-30', courseTitle: '재정의됨' } as { title: string; dueDate: string; courseTitle: string }]; + const result = mergeDueDateItems(baseCourse, items); + expect(result[0].courseTitle).toBe('재정의됨'); + }); +}); + +describe('mergeVodWithAttendance - 추가 edge cases', () => { + it('대량 데이터 매칭', () => { + const vodList = Array.from({ length: 100 }, (_, i) => ({ + week: i, subject: `${i}주차`, title: `강의${i}`, url: `/v${i}`, range: null, length: '30:00', + })); + const attendanceList = Array.from({ length: 100 }, (_, i) => ({ + title: `강의${i}`, isAttendance: i % 2 === 0 ? 'O' : 'X', weeklyAttendance: 'O', week: i, + })); + const result = mergeVodWithAttendance(baseCourse, vodList, attendanceList); + expect(result).toHaveLength(100); + }); + + it('title에 특수문자가 포함된 경우 정확한 매칭', () => { + const vodList = [ + { week: 1, subject: '1주차', title: '강의-1(A)', url: '/v1', range: null, length: '30:00' }, + ]; + const attendanceList = [ + { title: '강의-1(A)', isAttendance: 'O', weeklyAttendance: 'O', week: 1 }, + ]; + const result = mergeVodWithAttendance(baseCourse, vodList, attendanceList); + expect(result).toHaveLength(1); + }); + + it('isCommunity 필드가 course에서 전파됨', () => { + const communityCourse: CourseBase = { ...baseCourse, isCommunity: true }; + const vodList = [ + { week: 1, subject: '1주차', title: '강의1', url: '/v1', range: null, length: '30:00' }, + ]; + const attendanceList = [ + { title: '강의1', isAttendance: 'O', weeklyAttendance: 'O', week: 1 }, + ]; + const result = mergeVodWithAttendance(communityCourse, vodList, attendanceList); + expect(result[0].isCommunity).toBe(true); + }); +}); diff --git a/src/assets/gmail.png b/src/assets/gmail.png new file mode 100644 index 0000000..1648f51 Binary files /dev/null and b/src/assets/gmail.png differ diff --git a/src/background.ts b/src/background.ts index 13084f1..9b6fe48 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,93 +1,24 @@ -interface AlarmDetails { - title: string; - message: string; -} - -const alarmsMap: { [alarmId: string]: AlarmDetails } = {}; - -function parseDateTime(dateTimeStr: string): Date | null { - const parts = dateTimeStr.split(' '); - if (parts.length !== 2) { - return null; - } - - const [datePart, timePart] = parts; - const dateComponents = datePart.split('-'); - const timeComponents = timePart.split(':'); - - if (dateComponents.length !== 3 || (timeComponents.length !== 2 && timeComponents.length !== 3)) { - return null; - } - - const year = parseInt(dateComponents[0], 10); - const month = parseInt(dateComponents[1], 10) - 1; - const day = parseInt(dateComponents[2], 10); - const hour = parseInt(timeComponents[0], 10); - const minute = parseInt(timeComponents[1], 10); - const second = timeComponents.length === 3 ? parseInt(timeComponents[2], 10) : 0; - - return new Date(year, month, day, hour, minute, second); -} - -function schedulePreEventAlarm(alarmId: string, dateTimeStr: string, title: string, message: string): void { - const eventDate = parseDateTime(dateTimeStr); - if (!eventDate) { - console.error('Invalid date time string:', dateTimeStr); - return; - } - - const alarmTime = eventDate.getTime() - 24 * 60 * 60 * 1000; - const now = Date.now(); - - if (alarmTime <= now) { - console.warn('Alarm time is in the past. Cannot schedule alarm.'); - return; - } - - alarmsMap[alarmId] = { title, message }; - - chrome.alarms.create(alarmId, { when: alarmTime }); - console.log(`Alarm [${alarmId}] scheduled for ${new Date(alarmTime).toString()}`); -} - -function cancelAlarm(alarmId: string): void { - chrome.alarms.clear(alarmId, (wasCleared: boolean) => { - if (wasCleared) { - delete alarmsMap[alarmId]; - } - }); -} - -/** - * 알람이 발생했을 때 해당 알람의 정보를 기반으로 알림(notification)을 띄우는 리스너 - */ -chrome.alarms.onAlarm.addListener((alarm) => { - const alarmId = alarm.name; - const details = alarmsMap[alarmId]; - if (details) { - chrome.notifications.create(`${alarmId}-notification`, { - type: 'basic', - iconUrl: 'images/icon/icon-128.png', - title: details.title, - message: details.message, - priority: 2, +chrome.runtime.onMessage.addListener((message, _, sendResponse) => { + if (message.action === 'getAuthToken') { + chrome.identity.getAuthToken({ interactive: message.interactive ?? true }, (token) => { + if (chrome.runtime.lastError || !token) { + sendResponse({ token: null, error: chrome.runtime.lastError?.message }); + } else { + sendResponse({ token }); + } }); - delete alarmsMap[alarmId]; + return true; + } else if (message.action === 'removeCachedAuthToken') { + chrome.identity.removeCachedAuthToken({ token: message.token }, () => { + sendResponse({ success: true }); + }); + return true; } }); -/** - * 메시지를 통해 알람 예약 및 취소 요청을 처리하는 리스너 - */ -chrome.runtime.onMessage.addListener((message, _, sendResponse) => { - if (message.action === 'scheduleAlarm') { - const { alarmId, dateTime, title, message: alarmMessage } = message; - schedulePreEventAlarm(alarmId, dateTime, title, alarmMessage); - sendResponse({ status: 'scheduled', alarmId }); - } else if (message.action === 'cancelAlarm') { - const { alarmId } = message; - cancelAlarm(alarmId); - sendResponse({ status: 'cancelled', alarmId }); +chrome.runtime.onInstalled.addListener((details) => { + if (details.reason === 'install') { + chrome.tabs.create({ url: 'https://hs-shell.github.io/dotbugi/' }); } }); diff --git a/src/components/ui/context-menu.tsx b/src/components/ui/context-menu.tsx new file mode 100644 index 0000000..ba43c94 --- /dev/null +++ b/src/components/ui/context-menu.tsx @@ -0,0 +1,202 @@ +import * as React from "react" +import * as ContextMenuPrimitive from "@radix-ui/react-context-menu" +import { Check, ChevronRight, Circle } from "lucide-react" + +import { cn } from "@/lib/utils" +import { useShadowRoot } from "@/popover/lib/ShadowRootContext" + +const ContextMenu = ContextMenuPrimitive.Root + +const ContextMenuTrigger = ContextMenuPrimitive.Trigger + +const ContextMenuGroup = ContextMenuPrimitive.Group + +const ContextMenuPortal = ContextMenuPrimitive.Portal + +const ContextMenuSub = ContextMenuPrimitive.Sub + +const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup + +const ContextMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName + +const ContextMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName + +const ContextMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const shadowRoot = useShadowRoot(); + return ( + + + + ) +}) +ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName + +const ContextMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName + +const ContextMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +ContextMenuCheckboxItem.displayName = + ContextMenuPrimitive.CheckboxItem.displayName + +const ContextMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName + +const ContextMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName + +const ContextMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName + +const ContextMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +ContextMenuShortcut.displayName = "ContextMenuShortcut" + +export { + ContextMenu, + ContextMenuTrigger, + ContextMenuContent, + ContextMenuItem, + ContextMenuCheckboxItem, + ContextMenuRadioItem, + ContextMenuLabel, + ContextMenuSeparator, + ContextMenuShortcut, + ContextMenuGroup, + ContextMenuPortal, + ContextMenuSub, + ContextMenuSubContent, + ContextMenuSubTrigger, + ContextMenuRadioGroup, +} diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx new file mode 100644 index 0000000..682aea0 --- /dev/null +++ b/src/components/ui/dropdown-menu.tsx @@ -0,0 +1,203 @@ +import * as React from "react" +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" +import { Check, ChevronRight, Circle } from "lucide-react" + +import { cn } from "@/lib/utils" +import { useShadowRoot } from "@/popover/lib/ShadowRootContext" + +const DropdownMenu = DropdownMenuPrimitive.Root + +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger + +const DropdownMenuGroup = DropdownMenuPrimitive.Group + +const DropdownMenuPortal = DropdownMenuPrimitive.Portal + +const DropdownMenuSub = DropdownMenuPrimitive.Sub + +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup + +const DropdownMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +DropdownMenuSubTrigger.displayName = + DropdownMenuPrimitive.SubTrigger.displayName + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSubContent.displayName = + DropdownMenuPrimitive.SubContent.displayName + +const DropdownMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => { + const shadowRoot = useShadowRoot(); + return ( + + + + ) +}) +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName + +const DropdownMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + svg]:size-4 [&>svg]:shrink-0", + inset && "pl-8", + className + )} + {...props} + /> +)) +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuCheckboxItem.displayName = + DropdownMenuPrimitive.CheckboxItem.displayName + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName + +const DropdownMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +DropdownMenuShortcut.displayName = "DropdownMenuShortcut" + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, +} diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx index 6c0c2db..c26903f 100644 --- a/src/components/ui/popover.tsx +++ b/src/components/ui/popover.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as PopoverPrimitive from '@radix-ui/react-popover'; import { cn } from '@/lib/utils'; -import { useShadowRoot } from '@/lib/ShadowRootContext'; +import { useShadowRoot } from '@/popover/lib/ShadowRootContext'; const Popover = PopoverPrimitive.Root; diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx index a84242c..cd37473 100644 --- a/src/components/ui/select.tsx +++ b/src/components/ui/select.tsx @@ -1,14 +1,15 @@ -import * as React from "react" -import * as SelectPrimitive from "@radix-ui/react-select" -import { Check, ChevronDown, ChevronUp } from "lucide-react" +import * as React from 'react'; +import * as SelectPrimitive from '@radix-ui/react-select'; +import { Check, ChevronDown, ChevronUp } from 'lucide-react'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; +import { useShadowRoot } from '@/popover/lib/ShadowRootContext'; -const Select = SelectPrimitive.Root +const Select = SelectPrimitive.Root; -const SelectGroup = SelectPrimitive.Group +const SelectGroup = SelectPrimitive.Group; -const SelectValue = SelectPrimitive.Value +const SelectValue = SelectPrimitive.Value; const SelectTrigger = React.forwardRef< React.ElementRef, @@ -17,7 +18,7 @@ const SelectTrigger = React.forwardRef< span]:line-clamp-1", + 'flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1', className )} {...props} @@ -27,8 +28,8 @@ const SelectTrigger = React.forwardRef< -)) -SelectTrigger.displayName = SelectPrimitive.Trigger.displayName +)); +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; const SelectScrollUpButton = React.forwardRef< React.ElementRef, @@ -36,16 +37,13 @@ const SelectScrollUpButton = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName +)); +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; const SelectScrollDownButton = React.forwardRef< React.ElementRef, @@ -53,61 +51,56 @@ const SelectScrollDownButton = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -SelectScrollDownButton.displayName = - SelectPrimitive.ScrollDownButton.displayName +)); +SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName; const SelectContent = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, children, position = "popper", ...props }, ref) => ( - - - - (({ className, children, position = 'popper', ...props }, ref) => { + const shadowRoot = useShadowRoot(); + return ( + + - {children} - - - - -)) -SelectContent.displayName = SelectPrimitive.Content.displayName + + + {children} + + + + + ); +}); +SelectContent.displayName = SelectPrimitive.Content.displayName; const SelectLabel = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -SelectLabel.displayName = SelectPrimitive.Label.displayName + +)); +SelectLabel.displayName = SelectPrimitive.Label.displayName; const SelectItem = React.forwardRef< React.ElementRef, @@ -116,32 +109,28 @@ const SelectItem = React.forwardRef< - + - + {children} -)) -SelectItem.displayName = SelectPrimitive.Item.displayName +)); +SelectItem.displayName = SelectPrimitive.Item.displayName; const SelectSeparator = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)) -SelectSeparator.displayName = SelectPrimitive.Separator.displayName + +)); +SelectSeparator.displayName = SelectPrimitive.Separator.displayName; export { Select, @@ -154,4 +143,4 @@ export { SelectSeparator, SelectScrollUpButton, SelectScrollDownButton, -} +}; diff --git a/src/constants/constant.ts b/src/constants/links.ts similarity index 79% rename from src/constants/constant.ts rename to src/constants/links.ts index 53601fd..7164f33 100644 --- a/src/constants/constant.ts +++ b/src/constants/links.ts @@ -1,7 +1,6 @@ export const BASE_LINK = 'https://learn.hansung.ac.kr' as const; export const getVodPageLink = (courseId: string) => { return BASE_LINK + `/report/ubcompletion/user_progress_a.php?id=${courseId}`; - return BASE_LINK + `/course/view.php?id=${courseId}`; }; export const getAssignPageLink = (courseId: string) => { return BASE_LINK + `/mod/assign/index.php?id=${courseId}`; @@ -12,3 +11,6 @@ export const getQuizPageLink = (courseId: string) => { export const getIndexPageLink = (courseId: string) => { return BASE_LINK + `/course/view.php?id=${courseId}`; }; +export const getVodProgressPageLink = (courseId: string) => { + return BASE_LINK + `/report/ubcompletion/user_progress.php?id=${courseId}`; +}; diff --git a/src/content/App.tsx b/src/content/App.tsx deleted file mode 100644 index 97371a9..0000000 --- a/src/content/App.tsx +++ /dev/null @@ -1,349 +0,0 @@ -import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover'; -import { useEffect, useMemo, useState } from 'react'; -import icon from '@/assets/icon.png'; -import exit from '@/assets/exit.png'; -import { Assign, CourseBase, Filters, Quiz, TAB_TYPE, Vod } from './types'; -import { ListFilter, OctagonAlert, RefreshCw, Search } from 'lucide-react'; -import filter from '@/assets/filter.svg'; -import PopoverFooter from './components/PopoverFooter'; -import { Spinner } from '@/components/ui/spinner'; -import { useGetCourses } from '@/hooks/useGetCourse'; -import Video from './components/Video'; -import Assignment from './components/Assignment'; -import QuizTab from './components/QuizTab'; -import { Button } from '@/components/ui/button'; -import FilterBadge from './components/FilterBadge'; - -import FilterPanel from './components/FilterPanel'; -import { filterVods, filterAssigns, filterQuizes } from '@/lib/filterData'; -import PendingDialogWithBeforeUnload from './components/PendingDialog'; -import StickyPopoverTrigger from './StickyPopoverTrigger'; -import { useCourseData } from '@/hooks/useCourseData'; - -const attendanceOptions = ['출석', '결석']; -const submitOptions = [ - { label: '제출완료', value: true }, - { label: '제출필요', value: false }, -]; - -export default function App() { - const { courses } = useGetCourses(); - const typeCourses: CourseBase[] = courses; - - // 데이터 관련 상태를 useCourseData 커스텀 훅으로 관리 - const { vods, assigns, quizes, isPending, remainingTime, isError, updateData, setIsPending } = - useCourseData(typeCourses); - - // activeTab의 타입을 TAB_TYPE으로 지정 - const [activeTab, setActiveTab] = useState(TAB_TYPE.VIDEO); - const [isOpen, setIsOpen] = useState(false); - const [searchTerm, setSearchTerm] = useState(''); - const [vodSortBy] = useState('isAttendance'); - const [assignSortBy] = useState('isSubmit'); - const [quizSortBy] = useState('dueDate'); - const [isFilterOpen, setIsFilterOpen] = useState(false); - - // 필터 상태 관리 - Record을 사용하여 TAB_TYPE을 키로 지정 - const [filters, setFilters] = useState>({ - VIDEO: { courseTitles: [], attendanceStatuses: [] }, - ASSIGN: { courseTitles: [], submitStatuses: [] }, - QUIZ: { courseTitles: [] }, - }); - - useEffect(() => { - setSearchTerm(''); - }, [activeTab]); - - useEffect(() => { - setSearchTerm(''); - setIsFilterOpen(false); - }, [activeTab]); - - // 필터 옵션 추출 - const courseTitlesMap = useMemo( - () => ({ - VIDEO: Array.from(new Set(vods.map((vod) => vod.courseTitle))), - ASSIGN: Array.from(new Set(assigns.map((assign) => assign.courseTitle))), - QUIZ: Array.from(new Set(quizes.map((quiz) => quiz.courseTitle))), - }), - [vods, assigns, quizes] - ); - - // 필터 적용 - const filteredVods = useMemo(() => { - return filterVods(vods, filters[activeTab], searchTerm, vodSortBy); - }, [vods, filters, activeTab, searchTerm, vodSortBy]); - - const filteredAssigns = useMemo(() => { - return filterAssigns(assigns, filters[activeTab], searchTerm, assignSortBy); - }, [assigns, filters, activeTab, searchTerm, assignSortBy]); - - const filteredQuizes = useMemo(() => { - return filterQuizes(quizes, filters[activeTab], searchTerm, quizSortBy); - }, [quizes, filters, activeTab, searchTerm, quizSortBy]); - - // Vods용 필터 핸들러 - const handleAttendanceFilterChange = (status: string) => { - setFilters((prev) => { - const current = prev[activeTab].attendanceStatuses || []; - const updated = current.includes(status) ? current.filter((s) => s !== status) : [...current, status]; - return { - ...prev, - [activeTab]: { - ...prev[activeTab], - attendanceStatuses: updated, - }, - }; - }); - }; - - // Assigns용 필터 핸들러 - const handleSubmitFilterChange = (isSubmit: boolean) => { - setFilters((prev) => { - const current = prev[activeTab].submitStatuses || []; - const updated = current.includes(isSubmit) ? current.filter((s) => s !== isSubmit) : [...current, isSubmit]; - return { - ...prev, - [activeTab]: { - ...prev[activeTab], - submitStatuses: updated, - }, - }; - }); - }; - - // CourseTitle 필터 핸들러 - const handleCourseTitleChange = (courseTitle: string) => { - setFilters((prev) => { - const current = prev[activeTab].courseTitles; - const updated = current.includes(courseTitle) - ? current.filter((title) => title !== courseTitle) - : [...current, courseTitle]; - return { - ...prev, - [activeTab]: { - ...prev[activeTab], - courseTitles: updated, - }, - }; - }); - }; - - const isFilterSet = useMemo(() => { - const currentFilters = filters[activeTab]; - const { courseTitles, attendanceStatuses, submitStatuses } = currentFilters; - return ( - (courseTitles && courseTitles.length > 0) || - (attendanceStatuses && attendanceStatuses.length > 0) || - (submitStatuses && submitStatuses.length > 0) - ); - }, [filters, activeTab]); - - const clearFilters = () => { - setFilters((prev) => ({ - ...prev, - [activeTab]: { - courseTitles: [], - ...(activeTab === 'VIDEO' ? { attendanceStatuses: [] } : {}), - ...(activeTab === 'ASSIGN' ? { submitStatuses: [] } : {}), - }, - })); - }; - - return ( - <> - {}} /> - - - - {isOpen ? ( - { - setIsOpen(!isOpen); - e.preventDefault(); - }} - draggable={false} - className="rounded-2xl w-32 h-32 shadow-2xl shadow-zinc-900 cursor-pointer" - alt="Close" - /> - ) : ( - { - setIsOpen(!isOpen); - e.preventDefault(); - }} - draggable={false} - className="rounded-2xl w-32 h-32 shadow-2xl shadow-zinc-900 cursor-pointer" - alt="Open" - /> - )} - - - -
-
-
- {activeTab === 'VIDEO' - ? '온라인 강의 목록' - : activeTab === 'ASSIGN' - ? '과제 목록' - : activeTab === 'QUIZ' - ? '퀴즈 목록' - : '오류'} -
-
- = 30 - ? 'text-amber-500 font-semibold' - : 'text-zinc-400' - : Math.floor(remainingTime / 60) >= 1 - ? 'text-amber-500 font-semibold' - : 'text-zinc-400' - }`} - > - {remainingTime < 60 - ? `${Math.round(remainingTime)}분 전` - : `${Math.floor(remainingTime / 60)}시간 전`} - - -
-
-
- - setSearchTerm(e.target.value)} - autoFocus={true} - className="bg-zinc-50 rounded-xl border border-zinc-300 w-full text-lg h-12 pl-12 pr-4 placeholder-gray-400 font-medium py-0 outline-none focus:ring-0 focus:border-zinc-300 focus:bg-slate-50 transition-all duration-200" - /> -
-
-
- {filters[activeTab].courseTitles.map((title) => ( - handleCourseTitleChange(title)} /> - ))} - {activeTab === 'VIDEO' && - filters[activeTab].attendanceStatuses && - filters[activeTab].attendanceStatuses.map((status) => ( - handleAttendanceFilterChange(status)} - /> - ))} - {activeTab === 'ASSIGN' && - filters[activeTab].submitStatuses && - filters[activeTab].submitStatuses.map((status) => ( - handleSubmitFilterChange(status)} - /> - ))} -
- - {/* 고정된 필터 아이콘 영역 */} -
- - - - - - - - - - -
-
-
-
- {isPending ? ( -
- -
- ) : ( - <> - {isError ? ( -
- -

오류가 발생했습니다.

-

{ - location.reload(); - }} - className="py-4 text-xl font-medium underline text-zinc-500 hover:text-zinc-950 hover:cursor-pointer transition-all duration-200" - > - 페이지 새로고침 -

-
- ) : ( - <> - {activeTab === 'VIDEO' &&
- -
-
- - ); -} diff --git a/src/content/components/Assignment.tsx b/src/content/components/Assignment.tsx deleted file mode 100644 index 99a6589..0000000 --- a/src/content/components/Assignment.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { calculateDueDate, calculateRemainingTime } from '@/lib/utils'; -import { Card, CardFooter, CardHeader } from '@/components/ui/card'; -import { BadgeCheck, Clock, Siren, TriangleAlert } from 'lucide-react'; -import { Tooltip } from '@radix-ui/react-tooltip'; -import { TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; -import thung from '@/assets/thung.png'; -import { Assign } from '../types'; - -interface Props { - courseData: Assign[]; -} -export default function Assignment({ courseData }: Props) { - if (!courseData || courseData.length === 0) { - return ( -
- -
- 과제가 없습니다 -
-
- ); - } - - return ( -
- {courseData.map((course, index) => { - if (!course) return null; - - const isDueDateSame = true; - const timeDifference = calculateDueDate(course.dueDate!); - - return ( - window.open(`${course.url}`, '_blank')} - key={`${course.title}-${index}`} - className={`cursor-pointer w-full rounded-2xl shadow-md bg-white overflow-hidden border-0 border-l-4 ${course.isSubmit ? 'border-green-500' : timeDifference.borderColor} hover:bg-zinc-100 transition-all duration-200`} - > - -
-
{course.courseTitle}
-
{course.title}
-
-
- - - -
- - - {isDueDateSame ? timeDifference.message : '확인 필요'} - -
-
- - {calculateRemainingTime(course.dueDate)} - -
-
- {course.isSubmit ? ( - - ) : timeDifference.textColor.includes('red') ? ( - - ) : ( - - )} -
{course.isSubmit ? '제출 완료' : '제출 필요'}
-
-
-
- ); - })} -
- ); -} diff --git a/src/content/components/PendingDialog.tsx b/src/content/components/PendingDialog.tsx deleted file mode 100644 index 4a2f43f..0000000 --- a/src/content/components/PendingDialog.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { useState, useEffect } from 'react'; -import ReactDOM from 'react-dom'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Loader2 } from 'lucide-react'; -import styles from '@/styles/shadow.css?inline'; -import { createShadowRoot } from '@/lib/createShadowRoot'; - -interface PendingDialogProps { - isPending: boolean; - onClose?: () => void; -} - -export default function PendingDialog({ isPending, onClose }: PendingDialogProps) { - const [open, setOpen] = useState(false); - const [cancelEnabled, setCancelEnabled] = useState(false); - const [modalContainer, setModalContainer] = useState(null); - const [hostElement, setHostElement] = useState(null); - - useEffect(() => { - let host = document.getElementById('shadow-modal-host') as HTMLElement | null; - if (!host) { - host = document.createElement('div'); - host.id = 'shadow-modal-host'; - host.style.zIndex = '9999'; - host.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'; - document.body.prepend(host); - } - setHostElement(host); - const newShadowRoot = createShadowRoot( - host, - [ - styles, - ` - :host { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - display: flex; - align-items: center; - justify-content: center; - z-index: 9999; - } - `, - ], - 'modal-container' - ); - setModalContainer(newShadowRoot); - }, []); - - // open 상태에 따라 host의 display 업데이트 - useEffect(() => { - if (hostElement) { - hostElement.style.display = open ? 'flex' : 'none'; - } - }, [open, hostElement]); - - // isPending 상태에 따라 모달 열림/닫힘 관리 - useEffect(() => { - if (isPending) { - setOpen(true); - setCancelEnabled(false); - } else { - setOpen(false); - setCancelEnabled(false); - } - }, [isPending]); - - // 모달이 열리면 스크롤을 막고, 닫히면 복구 - useEffect(() => { - if (open) { - document.body.style.overflow = 'hidden'; - } else { - document.body.style.overflow = ''; - } - }, [open]); - - // 모달이 열릴 때 15초 후에 취소 버튼 활성화 - useEffect(() => { - let timer: NodeJS.Timeout; - if (open) { - timer = setTimeout(() => { - setCancelEnabled(true); - }, 15000); - } - return () => { - clearTimeout(timer); - }; - }, [open]); - - const handleClose = () => { - // 취소 버튼이 활성화된 경우에만 닫힘 동작 실행 - if (cancelEnabled) { - setOpen(false); - if (onClose) { - onClose(); - } - } - }; - - const modalContent = ( - - - 요청 진행 중 -

요청이 진행중입니다. 잠시만 기다려주세요.

-
- -
- -

완료되면 자동으로 창이 닫힙니다.

-
- -
-
- ); - - if (!modalContainer) return null; - return ReactDOM.createPortal(modalContent, modalContainer); -} diff --git a/src/content/components/PopoverFooter.tsx b/src/content/components/PopoverFooter.tsx deleted file mode 100644 index 5ee1908..0000000 --- a/src/content/components/PopoverFooter.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { NotebookText, VideoIcon, Zap } from 'lucide-react'; -import { TAB_TYPE } from '../types'; - -interface Prop { - activeTab: string; - setActiveTab: (tabType: TAB_TYPE) => void; -} - -export default function PopoverFooter({ activeTab, setActiveTab }: Prop) { - return ( -
-
setActiveTab(TAB_TYPE.VIDEO)} - > - - 강의 -
-
setActiveTab(TAB_TYPE.ASSIGN)} - > - - 과제 -
-
setActiveTab(TAB_TYPE.QUIZ)} - > - - 퀴즈 -
-
- ); -} diff --git a/src/content/components/PopoverHeader.tsx b/src/content/components/PopoverHeader.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/content/components/QuizTab.tsx b/src/content/components/QuizTab.tsx deleted file mode 100644 index a420bb3..0000000 --- a/src/content/components/QuizTab.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { calculateDueDate, calculateRemainingTime } from '@/lib/utils'; -import { Quiz } from '../types'; -import { Card, CardFooter, CardHeader } from '@/components/ui/card'; -import { Clock, Siren, TriangleAlert } from 'lucide-react'; -import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; -import thung from '@/assets/thung.png'; - -interface Props { - courseData: Quiz[]; -} - -export default function QuizTab({ courseData }: Props) { - if (!courseData || courseData.length === 0) { - return ( -
- -
- 퀴즈가 없습니다 -
-
- ); - } - - return ( -
- {courseData.map((course, index) => { - if (!course) return null; - - const isDueDateSame = true; - const timeDifference = calculateDueDate(course.dueDate!); - - return ( - window.open(`${course.url}`, '_blank')} - key={`${course.title}-${index}`} - className={`cursor-pointer w-full rounded-2xl shadow-md bg-white overflow-hidden border-0 border-l-4 ${timeDifference.borderColor} hover:bg-zinc-100 transition-all duration-200`} - > - -
-
{course.courseTitle}
-
{course.title}
-
-
- - - -
- - - {isDueDateSame ? timeDifference.message : '확인 필요'} - -
-
- - {calculateRemainingTime(course.dueDate)} - -
-
-
- {timeDifference.textColor.includes('red') ? ( - - ) : ( - - )} -
-
{'직접 확인'}
-
-
-
- ); - })} -
- ); -} diff --git a/src/content/components/Video.tsx b/src/content/components/Video.tsx deleted file mode 100644 index 685e4bf..0000000 --- a/src/content/components/Video.tsx +++ /dev/null @@ -1,222 +0,0 @@ -import { useState } from 'react'; -import { Card, CardContent, CardFooter, CardHeader } from '@/components/ui/card'; -import { Vod } from '../types'; -import { - calculateRemainingTimeByRange, - calculateTimeDifference, - formatDateString, - isCurrentDateInRange, -} from '@/lib/utils'; -import { BadgeCheck, ChevronDown, ChevronUp, Clock, Siren, TriangleAlert } from 'lucide-react'; - -import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; - -import thung from '@/assets/thung.png'; - -interface Props { - courseData: Vod[]; -} - -export default function Video({ courseData }: Props) { - const [expandedCards, setExpandedCards] = useState<{ [key: string]: boolean }>({}); - - const toggleCard = (courseId: string) => { - setExpandedCards((prev) => ({ ...prev, [courseId]: !prev[courseId] })); - }; - - const groupedData = courseData.reduce( - (acc, item) => { - const key = `${item.courseId}-${item.subject}-${item.range}`; - if (!acc[key]) { - acc[key] = []; - } - acc[key].push(item); - return acc; - }, - {} as Record - ); - - if (!courseData || courseData.length === 0) { - return ( -
- -
- 강의가 없습니다 -
-
- ); - } - - const sortedVodGroups = Object.values(groupedData).sort((groupA, groupB) => { - const firstA = groupA[0]; - const firstB = groupB[0]; - - const isAX = firstA.weeklyAttendance.toUpperCase().startsWith('X'); - const isBX = firstB.weeklyAttendance.toUpperCase().startsWith('X'); - - // X가 있는 항목을 먼저 정렬 - if (isAX && !isBX) return -1; - if (!isAX && isBX) return 1; - - const rangeA = firstA.range; - const rangeB = firstB.range; - const isRangeANull = rangeA === null; - const isRangeBNull = rangeB === null; - - // isCurrentDateInRange가 true인 항목을 먼저 정렬 (X와 O 모두) - const isCurrentDateInRangeA = isCurrentDateInRange(firstA.range); - const isCurrentDateInRangeB = isCurrentDateInRange(firstB.range); - - if (isAX) { - // X일 때는 isCurrentDateInRange가 true인 항목을 먼저 배치, 그 다음 null - if (isCurrentDateInRangeA && !isCurrentDateInRangeB) return -1; - if (!isCurrentDateInRangeA && isCurrentDateInRangeB) return 1; - if (isRangeANull && !isRangeBNull) return 1; - if (!isRangeANull && isRangeBNull) return -1; - } else { - // O일 때는 isCurrentDateInRange가 true인 항목을 먼저 배치, 그 다음 null, 그 다음 시간순 정렬 - if (isCurrentDateInRangeA && !isCurrentDateInRangeB) return -1; - if (!isCurrentDateInRangeA && isCurrentDateInRangeB) return 1; - if (isRangeANull && !isRangeBNull) return 1; - if (!isRangeANull && isRangeBNull) return -1; - - // rangeStart 날짜 기준으로 시간순으로 정렬 - if (!isRangeANull && !isRangeBNull) { - const rangeStartA = rangeA.split(' ~ ')[0]; - const rangeStartB = rangeB.split(' ~ ')[0]; - const dateA = new Date(rangeStartA); - const dateB = new Date(rangeStartB); - - if (dateA < dateB) return -1; - if (dateA > dateB) return 1; - } - } - - // courseTitle로 기본 정렬 - if (firstA.courseTitle < firstB.courseTitle) return -1; - if (firstA.courseTitle > firstB.courseTitle) return 1; - - return 0; - }); - - return ( -
- {sortedVodGroups.map((vods, index) => { - if (!vods || vods.length === 0) return null; - - const sortedVods = vods.slice().sort((a, b) => { - const isAX = a.isAttendance.toUpperCase().startsWith('X'); - const isBX = b.isAttendance.toUpperCase().startsWith('X'); - if (isAX && !isBX) return -1; - if (!isAX && isBX) return 1; - - if (a.range && b.range) { - const rangeStartA = a.range.split(' ~ ')[0]; - const rangeStartB = b.range.split(' ~ ')[0]; - const dateA = new Date(rangeStartA); - const dateB = new Date(rangeStartB); - if (dateA < dateB) return -1; - if (dateA > dateB) return 1; - } - - if (a.courseTitle < b.courseTitle) return -1; - if (a.courseTitle > b.courseTitle) return 1; - - return 0; - }); - - const item = vods[0]; - const isDueDateSame = true; - const timeDifference = calculateTimeDifference(item.range); - const isExpanded = expandedCards[`${item.title}-${index}`] || false; - - return ( - - toggleCard(`${item.title}-${index}`)} - > -
-
{item.courseTitle}
-
{item.subject}
-
- {isExpanded ? : } -
- {isExpanded && ( - - {sortedVods.map((vod, vodIndex) => { - return ( -
window.open(`${vod.url.replace('view', 'viewer')}`, '_blank', 'VodContentWindow')} - > -
- {vod.title} -
-
- {formatDateString(vod.range)},{' '} - - {vod.length} - -
-
- ); - })} -
- )} - - - -
- - - {isDueDateSame ? timeDifference.message : '확인 필요'} - -
-
- - {calculateRemainingTimeByRange(vods[0].range)} - -
-
-
- {item.weeklyAttendance.toLocaleLowerCase().trim() === 'o' ? ( - - ) : timeDifference.message.includes('시간') ? ( - - ) : ( - - )} -
-
- {item.weeklyAttendance.toLocaleLowerCase().trim() === 'o' ? '출석' : '결석'} -
-
-
-
- ); - })} -
- ); -} diff --git a/src/hooks/useCalendarEvents.ts b/src/hooks/useCalendarEvents.ts index cc5f1ac..60ad11f 100644 --- a/src/hooks/useCalendarEvents.ts +++ b/src/hooks/useCalendarEvents.ts @@ -1,100 +1,30 @@ import { useState, useEffect } from 'react'; -import { startOfDay } from 'date-fns'; -import { loadDataFromStorage } from '@/lib/storage'; -import { removeSquareBrackets } from '@/lib/utils'; -import { Vod, Assign, Quiz } from '@/content/types'; +import { loadAndTransform } from '@/lib/storage'; +import { Vod, Assign, Quiz } from '@/types'; +import { CalendarEvent, vodGroupsToEvents, dueDateItemToEvent } from '@/lib/transformCalendarEvents'; -export type CalendarEvent = { - id: string; - type: 'vod' | 'assign' | 'quiz'; - title: string; - subject: string; - start: Date | null; - end: Date | null; -}; +export type { CalendarEvent }; function useCalendarEvents() { const [events, setEvents] = useState([]); - const loadEvents = (storageKey: string, transform: (data: T[]) => CalendarEvent[]) => { - loadDataFromStorage(storageKey, (data: string | null) => { - if (!data) return; - - let parsedData: T[]; - if (typeof data === 'string') { - try { - parsedData = JSON.parse(data); - } catch (error) { - console.error(`JSON 파싱 에러 (${storageKey}):`, error); - return; - } - } else { - parsedData = data; - } - - const eventsData = transform(parsedData); - setEvents((prev) => [...prev, ...eventsData]); - }); - }; - useEffect(() => { - loadEvents('vod', (vods) => { - const groupedData = vods.reduce( - (acc, item) => { - const key = `${item.courseId}-${item.subject}-${item.range}`; - if (!acc[key]) { - acc[key] = []; - } - acc[key].push(item); - return acc; - }, - {} as Record - ); + const appendEvents = (newEvents: CalendarEvent[]) => { + setEvents((prev) => [...prev, ...newEvents]); + }; - return Object.entries(groupedData).map(([key, vodItems]) => { - const range = vodItems[0].range; - const [start, end] = range ? range.split(' ~ ') : [null, null]; - return { - id: key, - type: 'vod', - start: start ? new Date(start.replace(/-/g, '/')) : null, - end: end ? new Date(end.replace(/-/g, '/')) : null, - title: removeSquareBrackets(vodItems[0].courseTitle), - subject: removeSquareBrackets(vodItems[0].subject), - }; - }); - }); + loadAndTransform('vod', vodGroupsToEvents, appendEvents); - // assign 데이터 로딩 및 변환 - loadEvents('assign', (assigns) => - assigns.map((assign) => { - const dueDate = assign.dueDate; - const normalizedDate = dueDate ? startOfDay(new Date(dueDate)) : null; - return { - id: assign.courseId + assign.title + assign.dueDate, - type: 'assign', - start: normalizedDate, - end: normalizedDate, - title: removeSquareBrackets(assign.courseTitle), - subject: removeSquareBrackets(assign.title), - }; - }) + loadAndTransform( + 'assign', + (assigns) => assigns.map((a) => dueDateItemToEvent(a, 'assign')), + appendEvents, ); - // quiz 데이터 로딩 및 변환 - loadEvents('quiz', (quizzes) => - quizzes.map((quiz) => { - const dueDate = quiz.dueDate; - const normalizedDate = dueDate ? startOfDay(new Date(dueDate)) : null; - return { - id: quiz.courseId + quiz.title + quiz.dueDate, - type: 'quiz', - start: normalizedDate, - end: normalizedDate, - title: removeSquareBrackets(quiz.courseTitle), - subject: removeSquareBrackets(quiz.title), - }; - }) + loadAndTransform( + 'quiz', + (quizzes) => quizzes.map((q) => dueDateItemToEvent(q, 'quiz')), + appendEvents, ); }, []); diff --git a/src/hooks/useCardData.ts b/src/hooks/useCardData.ts index 53cf9db..693dc22 100644 --- a/src/hooks/useCardData.ts +++ b/src/hooks/useCardData.ts @@ -1,114 +1,36 @@ import { useState, useEffect } from 'react'; -import { loadDataFromStorage } from '@/lib/storage'; -import { Vod, Assign, Quiz } from '@/content/types'; +import { loadAndTransform } from '@/lib/storage'; +import { Vod, Assign, Quiz } from '@/types'; +import { CardData, summarizeVods } from '@/lib/summarizeCourseData'; -export type CardData = { - type: 'vod' | 'assign' | 'quiz'; - done: number; - total: number; -}; +export type { CardData }; -function useCardData() { - const [vodSummary, setVodSummary] = useState([]); - const [assignSummary, setAssignSummary] = useState([]); - const [quizSummary, setQuizSummary] = useState([]); - - const loadEvents = ( - storageKey: string, - transform: (data: T[]) => CardData[], - setter: (data: CardData[]) => void - ) => { - loadDataFromStorage(storageKey, (data: string | null) => { - if (!data) return; +type Summaries = Record<'vod' | 'assign' | 'quiz', CardData>; - let parsedData: T[]; - if (typeof data === 'string') { - try { - parsedData = JSON.parse(data); - } catch (error) { - console.error(`JSON 파싱 에러 (${storageKey}):`, error); - return; - } - } else { - parsedData = data; - } +const EMPTY: CardData = { done: 0, total: 0 }; - const eventsData = transform(parsedData); - setter(eventsData); - }); +function useCardData() { + const [summaries, setSummaries] = useState({ vod: EMPTY, assign: EMPTY, quiz: EMPTY }); + const updateKey = (key: keyof Summaries) => (data: CardData) => { + setSummaries((prev) => ({ ...prev, [key]: data })); }; useEffect(() => { - loadEvents( - 'vod', - (vods) => { - const groupedData = vods.reduce( - (acc, item) => { - const key = `${item.courseId}-${item.subject}-${item.range}`; - if (!acc[key]) { - acc[key] = []; - } - acc[key].push(item); - return acc; - }, - {} as Record - ); - - let done = 0; - Object.values(groupedData).forEach((vodItems) => { - if (vodItems[0].weeklyAttendance.toLowerCase() === 'o') { - done += 1; - } - }); - - return [ - { - type: 'vod', - done, - total: Object.keys(groupedData).length, - }, - ]; - }, - setVodSummary - ); - - loadEvents( - 'assign', - (assigns) => { - const total = assigns.length; - let done = 0; - assigns.forEach((assign) => { - if (assign.isSubmit) done += 1; - }); - return [ - { - type: 'assign', - done, - total, - }, - ]; - }, - setAssignSummary - ); - - loadEvents( - 'quiz', - (quizzes) => { - const total = quizzes.length; - const done = 0; - return [ - { - type: 'quiz', - done, - total, - }, - ]; - }, - setQuizSummary - ); + loadAndTransform('vod', summarizeVods, updateKey('vod')); + + loadAndTransform('assign', (assigns) => ({ + done: assigns.filter((a) => a.isSubmit).length, + total: assigns.length, + }), updateKey('assign')); + + // QuizData에 완료 여부 필드가 없어 done은 항상 0 + loadAndTransform('quiz', (quizzes) => ({ + done: 0, + total: quizzes.length, + }), updateKey('quiz')); }, []); - return { vodSummary, assignSummary, quizSummary }; + return summaries; } export default useCardData; diff --git a/src/hooks/useCourseData.tsx b/src/hooks/useCourseData.tsx index 546cf64..0932043 100644 --- a/src/hooks/useCourseData.tsx +++ b/src/hooks/useCourseData.tsx @@ -1,196 +1,295 @@ -import { useState, useEffect, useCallback } from 'react'; -import { Vod, Assign, Quiz, CourseBase } from '@/content/types'; +import { useState, useEffect, useCallback, useRef } from 'react'; +import { Vod, Assign, Quiz, CourseBase } from '@/types'; import { loadDataFromStorage, saveDataToStorage } from '@/lib/storage'; -import { requestData } from '@/lib/fetchCourseData'; +import { scrapeCourseData } from '@/lib/fetchCourseData'; import { isCurrentDateByDate, isCurrentDateInRange } from '@/lib/utils'; -import { makeAssignKey, makeQuizKey, makeVodKey } from '@/utils/generate-key'; +import { makeItemKey, makeVodKey } from '@/lib/generateKey'; +import { mergeVodWithAttendance, mergeDueDateItems } from '@/lib/transformCourseData'; +import { deduplicateInto } from '@/lib/deduplicateInto'; +import { loadMockData } from '@/mocks/loadMockData'; +import { + REFRESH_INTERVAL_MS, + CACHE_TTL_MINUTES, + CACHE_TTL_MS, + getLastRequestTime, + setLastRequestTime, + clearLastRequestTime, +} from '@/lib/cache'; + +const skipDateFilter = !!import.meta.env.VITE_MOCK_SKIP_DATE_FILTER; + +function filterVodsForDisplay(vods: Vod[]): Vod[] { + return skipDateFilter ? vods : vods.filter((v) => isCurrentDateInRange(v.range)); +} + +function filterItemsForDisplay(items: T[]): T[] { + return skipDateFilter ? items : items.filter((item) => item.dueDate === null || isCurrentDateByDate(item.dueDate)); +} export function useCourseData(courses: CourseBase[]) { const [vods, setVods] = useState([]); const [assigns, setAssigns] = useState([]); - const [quizes, setQuizes] = useState([]); + const [quizzes, setQuizzes] = useState([]); + const quizzesRef = useRef(quizzes); + quizzesRef.current = quizzes; const [refreshTime, setRefreshTime] = useState(null); - const [isPending, setIsPending] = useState(false); + const [isPending, setIsPending] = useState(false); const [remainingTime, setRemainingTime] = useState(0); const [isError, setIsError] = useState(false); + const [pendingCourseIds, setPendingCourseIds] = useState>(new Set()); + + const useMockData = import.meta.env.VITE_MOCK && !import.meta.env.VITE_MOCK_COURSES; + + const applyMock = useCallback(async () => { + const mock = await loadMockData(); + setVods(mock.vods); + setAssigns(mock.assigns); + setQuizzes(mock.quizzes); + }, []); - // dev 모드: mock 데이터 로드 useEffect(() => { - if (!import.meta.env.VITE_MOCK) return; + if (!useMockData) return; let cancelled = false; - import('@/mocks/mockData').then(({ mockVods, mockAssigns, mockQuizes }) => { + applyMock().then(() => { if (cancelled) return; - setVods(mockVods); - setAssigns(mockAssigns); - setQuizes(mockQuizes); setRefreshTime(new Date().toLocaleTimeString()); setRemainingTime(5); }); return () => { cancelled = true; }; - }, []); + }, [applyMock, useMockData]); - // dev 모드에서는 실제 fetch를 하지 않음 - const updateData = useCallback(async () => { - if (import.meta.env.VITE_MOCK) { - const { mockVods, mockAssigns, mockQuizes } = await import('@/mocks/mockData'); - setVods(mockVods); - setAssigns(mockAssigns); - setQuizes(mockQuizes); + const refreshCourseData = useCallback(async () => { + if (useMockData) { + await applyMock(); setRefreshTime(new Date().toLocaleTimeString()); setRemainingTime(0); + setIsPending(false); return; } + try { setIsError(false); setIsPending(true); - const currentTime = new Date().getTime(); + const fetchedAt = Date.now(); - const tempVods: Vod[] = []; - const tempAssigns: Assign[] = []; - const tempQuizes: Quiz[] = []; + const vods: Vod[] = []; + const assigns: Assign[] = []; + const quizzes: Quiz[] = []; + const seenVods = new Set(); + const seenAssigns = new Set(); + const seenQuizzes = new Set(); - const vodSet = new Set(tempVods.map((v) => makeVodKey(v.courseId, v.title, v.week))); - const assignSet = new Set(tempAssigns.map((a) => makeAssignKey(a.courseId, a.title, a.dueDate ? a.dueDate : ''))); - const quizSet = new Set(tempQuizes.map((q) => makeQuizKey(q.courseId, q.title, q.dueDate ? q.dueDate : ''))); + // 현재 상태의 퀴즈 데이터를 courseId별로 그룹핑 (마감된 퀴즈 캐시용) + const cachedQuizMap = new Map(); + for (const q of quizzesRef.current) { + const arr = cachedQuizMap.get(q.courseId) ?? []; + arr.push(q); + cachedQuizMap.set(q.courseId, arr); + } await Promise.all( courses.map(async (course) => { - const result = await requestData(course.courseId); - - result.vodDataArray.forEach((vodData) => { - result.vodAttendanceArray.forEach((vodAttendanceData) => { - const vodKey = makeVodKey(course.courseId, vodData.title, vodData.week); - if ( - vodAttendanceData.title === vodData.title && - vodAttendanceData.week === vodData.week && - isCurrentDateInRange(vodData.range) - ) { - if (!vodSet.has(vodKey)) { - vodSet.add(vodKey); - tempVods.push({ - courseId: course.courseId, - prof: course.prof, - courseTitle: course.courseTitle, - week: vodAttendanceData.week, - title: vodData.title, - isAttendance: vodAttendanceData.isAttendance, - weeklyAttendance: vodAttendanceData.weeklyAttendance, - length: vodData.length, - range: vodData.range, - subject: vodData.subject, - url: vodData.url, - }); - } - } - }); - }); - - result.assignDataArray.forEach((assignData) => { - const assignKey = makeAssignKey( - course.courseId, - assignData.title, - assignData.dueDate ? assignData.dueDate : '' - ); - if (!assignSet.has(assignKey) && isCurrentDateByDate(assignData.dueDate)) { - console.info(assignKey); - assignSet.add(assignKey); - tempAssigns.push({ - courseId: course.courseId, - prof: course.prof, - courseTitle: course.courseTitle, - subject: assignData.subject, - title: assignData.title, - dueDate: assignData.dueDate, - isSubmit: assignData.isSubmit, - url: assignData.url, - }); - } - }); - - result.quizDataArray.forEach((quizData) => { - const quizKey = makeQuizKey(course.courseId, quizData.title, quizData.dueDate ? quizData.dueDate : ''); - if (!quizSet.has(quizKey) && isCurrentDateByDate(quizData.dueDate)) { - console.info(quizKey); - quizSet.add(quizKey); - tempQuizes.push({ - courseId: course.courseId, - prof: course.prof, - courseTitle: course.courseTitle, - subject: quizData.subject, - title: quizData.title, - dueDate: quizData.dueDate, - url: quizData.url, - }); - } - }); + const scraped = await scrapeCourseData(course.courseId, cachedQuizMap.get(course.courseId), course.isCommunity); + + deduplicateInto( + vods, + mergeVodWithAttendance(course, scraped.vodDataArray, scraped.vodAttendanceArray), + seenVods, + (v) => makeVodKey(v.courseId, v.title, v.week), + ); + deduplicateInto( + assigns, + mergeDueDateItems(course, scraped.assignDataArray), + seenAssigns, + (a) => makeItemKey(a.courseId, a.title, a.dueDate ?? ''), + ); + deduplicateInto( + quizzes, + mergeDueDateItems(course, scraped.quizDataArray), + seenQuizzes, + (q) => makeItemKey(q.courseId, q.title, q.dueDate ?? ''), + ); }) ); - setVods(tempVods); - setAssigns(tempAssigns); - setQuizes(tempQuizes); + // Storage: 전체 데이터 저장 (강의 페이지 등에서 사용) + saveDataToStorage('vod', vods); + saveDataToStorage('assign', assigns); + saveDataToStorage('quiz', quizzes); - saveDataToStorage('vod', tempVods); - saveDataToStorage('assign', tempAssigns); - saveDataToStorage('quiz', tempQuizes); + // State: 대시보드 표시용 날짜 필터링 적용 + setVods(filterVodsForDisplay(vods)); + setAssigns(filterItemsForDisplay(assigns)); + setQuizzes(filterItemsForDisplay(quizzes)); - setRefreshTime(new Date(currentTime).toLocaleTimeString()); + setRefreshTime(new Date(fetchedAt).toLocaleTimeString()); setRemainingTime(0); - localStorage.setItem('lastRequestTime', currentTime.toString()); - saveDataToStorage('lastRequestTime', currentTime.toString()); - - setIsPending(false); + setLastRequestTime(fetchedAt); + saveDataToStorage('lastRequestTime', fetchedAt.toString()); } catch (error) { console.warn(error); - localStorage.removeItem('lastRequestTime'); + clearLastRequestTime(); setIsError(true); + } finally { setIsPending(false); } - }, [courses]); + }, [courses, applyMock, useMockData]); + + const addCourseData = useCallback(async (course: CourseBase) => { + setPendingCourseIds((prev) => new Set(prev).add(course.courseId)); + try { + const scraped = await scrapeCourseData(course.courseId, undefined, course.isCommunity); + + const newVods = mergeVodWithAttendance(course, scraped.vodDataArray, scraped.vodAttendanceArray); + const newAssigns = mergeDueDateItems(course, scraped.assignDataArray); + const newQuizzes = mergeDueDateItems(course, scraped.quizDataArray); + + // Storage: 전체 데이터 저장 + loadDataFromStorage('vod', (stored) => { + const all = [...(stored ?? [])]; + const seen = new Set(all.map((v) => makeVodKey(v.courseId, v.title, v.week))); + deduplicateInto(all, newVods, seen, (v) => makeVodKey(v.courseId, v.title, v.week)); + saveDataToStorage('vod', all); + }); + loadDataFromStorage('assign', (stored) => { + const all = [...(stored ?? [])]; + const seen = new Set(all.map((a) => makeItemKey(a.courseId, a.title, a.dueDate ?? ''))); + deduplicateInto(all, newAssigns, seen, (a) => makeItemKey(a.courseId, a.title, a.dueDate ?? '')); + saveDataToStorage('assign', all); + }); + loadDataFromStorage('quiz', (stored) => { + const all = [...(stored ?? [])]; + const seen = new Set(all.map((q) => makeItemKey(q.courseId, q.title, q.dueDate ?? ''))); + deduplicateInto(all, newQuizzes, seen, (q) => makeItemKey(q.courseId, q.title, q.dueDate ?? '')); + saveDataToStorage('quiz', all); + }); + + // State: 대시보드 표시용 필터링된 데이터만 + const filteredNewVods = filterVodsForDisplay(newVods); + const filteredNewAssigns = filterItemsForDisplay(newAssigns); + const filteredNewQuizzes = filterItemsForDisplay(newQuizzes); + + setVods((prev) => { + const seen = new Set(prev.map((v) => makeVodKey(v.courseId, v.title, v.week))); + const merged = [...prev]; + deduplicateInto(merged, filteredNewVods, seen, (v) => makeVodKey(v.courseId, v.title, v.week)); + return merged; + }); + + setAssigns((prev) => { + const seen = new Set(prev.map((a) => makeItemKey(a.courseId, a.title, a.dueDate ?? ''))); + const merged = [...prev]; + deduplicateInto(merged, filteredNewAssigns, seen, (a) => makeItemKey(a.courseId, a.title, a.dueDate ?? '')); + return merged; + }); + + setQuizzes((prev) => { + const seen = new Set(prev.map((q) => makeItemKey(q.courseId, q.title, q.dueDate ?? ''))); + const merged = [...prev]; + deduplicateInto(merged, filteredNewQuizzes, seen, (q) => makeItemKey(q.courseId, q.title, q.dueDate ?? '')); + return merged; + }); + } catch (error) { + console.warn('[Dotbugi] 강의 추가 데이터 fetch 실패:', error); + } finally { + setPendingCourseIds((prev) => { + const next = new Set(prev); + next.delete(course.courseId); + return next; + }); + } + }, []); + + const removeCourseData = useCallback((courseId: string) => { + // Storage: 전체 데이터에서 해당 강의 제거 + loadDataFromStorage('vod', (stored) => { + if (stored) saveDataToStorage('vod', stored.filter((v) => v.courseId !== courseId)); + }); + loadDataFromStorage('assign', (stored) => { + if (stored) saveDataToStorage('assign', stored.filter((a) => a.courseId !== courseId)); + }); + loadDataFromStorage('quiz', (stored) => { + if (stored) saveDataToStorage('quiz', stored.filter((q) => q.courseId !== courseId)); + }); + + // State: 대시보드에서 제거 + setVods((prev) => prev.filter((v) => v.courseId !== courseId)); + setAssigns((prev) => prev.filter((a) => a.courseId !== courseId)); + setQuizzes((prev) => prev.filter((q) => q.courseId !== courseId)); + }, []); + // 캐시 TTL 기반 자동 갱신 타이머 useEffect(() => { - let timer: ReturnType; - if (remainingTime >= 1440) { - updateData(); - } else { - timer = setTimeout(() => { - setRemainingTime((prev) => prev + 1); - }, 60 * 1000); + if (remainingTime >= CACHE_TTL_MINUTES) { + refreshCourseData(); + return; } - return () => { - clearTimeout(timer); + const timer = setTimeout(() => setRemainingTime((prev) => prev + 1), REFRESH_INTERVAL_MS); + return () => clearTimeout(timer); + }, [remainingTime, refreshCourseData]); + + // 탭이 다시 보일 때 캐시 만료 확인 후 즉시 갱신 + useEffect(() => { + const handleVisibility = () => { + if (document.visibilityState !== 'visible') return; + const lastRequest = getLastRequestTime(); + if (!lastRequest) return; + const elapsed = Date.now() - lastRequest; + if (elapsed >= CACHE_TTL_MS) { + refreshCourseData(); + } else { + setRemainingTime(elapsed / REFRESH_INTERVAL_MS); + } }; - }, [remainingTime, updateData]); + document.addEventListener('visibilitychange', handleVisibility); + return () => document.removeEventListener('visibilitychange', handleVisibility); + }, [refreshCourseData]); + // 초기 로드: 캐시 확인 후 fetch 또는 캐시 사용 (최초 1회만 실행) + const initialLoadDone = useRef(false); useEffect(() => { if (!courses || courses.length === 0) return; + if (initialLoadDone.current) return; + initialLoadDone.current = true; + + const lastRequest = getLastRequestTime(); + const now = Date.now(); - const lastRequestTime = localStorage.getItem('lastRequestTime'); - const currentTime = new Date().getTime(); - const oneDay = 60 * 60 * 1000 * 24; + if (lastRequest) { + setRefreshTime(new Date(lastRequest).toLocaleTimeString()); + } - if (lastRequestTime) setRefreshTime(new Date(parseInt(lastRequestTime, 10)).toLocaleTimeString()); - if (!lastRequestTime || currentTime - parseInt(lastRequestTime, 10) >= oneDay) { + if (!lastRequest || now - lastRequest >= CACHE_TTL_MS) { setIsPending(true); - updateData(); + refreshCourseData(); } else { - const minutes = (currentTime - parseInt(lastRequestTime, 10)) / (60 * 1000); - setRemainingTime(minutes); - loadDataFromStorage('vod', (data) => { - if (!data) return; - setVods((data as Vod[]).filter((vod) => isCurrentDateInRange(vod.range))); + setRemainingTime((now - lastRequest) / REFRESH_INTERVAL_MS); + loadDataFromStorage('vod', (data) => { + if (data) setVods(filterVodsForDisplay(data)); }); - loadDataFromStorage('assign', (data) => { - if (!data) return; - setAssigns((data as Assign[]).filter((assign) => isCurrentDateByDate(assign.dueDate))); + loadDataFromStorage('assign', (data) => { + if (data) setAssigns(filterItemsForDisplay(data)); }); - loadDataFromStorage('quiz', (data) => { - if (!data) return; - setQuizes((data as Quiz[]).filter((quiz) => isCurrentDateByDate(quiz.dueDate))); + loadDataFromStorage('quiz', (data) => { + if (data) setQuizzes(filterItemsForDisplay(data)); }); } - }, [courses, updateData]); - return { vods, assigns, quizes, isPending, remainingTime, refreshTime, isError, updateData, setIsPending }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [courses]); + + return { + vods, + assigns, + quizzes, + isPending, + remainingTime, + refreshTime, + isError, + refreshCourseData, + addCourseData, + removeCourseData, + pendingCourseIds, + }; } diff --git a/src/hooks/useDashboardFilters.ts b/src/hooks/useDashboardFilters.ts new file mode 100644 index 0000000..7680280 --- /dev/null +++ b/src/hooks/useDashboardFilters.ts @@ -0,0 +1,101 @@ +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { Assign, Filters, Quiz, TAB_TYPE, Vod } from '@/types'; +import { filterVods, filterAssigns, filterQuizzes } from '@/lib/filterData'; + +interface UseDashboardFiltersParams { + vods: Vod[]; + assigns: Assign[]; + quizzes: Quiz[]; + activeTab: TAB_TYPE; +} + +const INITIAL_FILTERS: Record = { + VIDEO: { courseTitles: [], attendanceStatuses: [] }, + ASSIGN: { courseTitles: [], submitStatuses: [] }, + QUIZ: { courseTitles: [], submitStatuses: [] }, + SETTING: { courseTitles: [] }, +}; + +function uniqueTitles(items: { courseTitle: string }[]): string[] { + return [...new Set(items.map((item) => item.courseTitle))]; +} + +export function useDashboardFilters({ vods, assigns, quizzes, activeTab }: UseDashboardFiltersParams) { + const [searchTerm, setSearchTerm] = useState(''); + const [isFilterOpen, setIsFilterOpen] = useState(false); + const [filters, setFilters] = useState(INITIAL_FILTERS); + + useEffect(() => { + setSearchTerm(''); + setIsFilterOpen(false); + }, [activeTab]); + + const courseTitlesMap = useMemo( + () => ({ + VIDEO: uniqueTitles(vods), + ASSIGN: uniqueTitles(assigns), + QUIZ: uniqueTitles(quizzes), + SETTING: [], + }), + [vods, assigns, quizzes] + ); + + const filteredVods = useMemo( + () => filterVods(vods, filters[activeTab], searchTerm, 'isAttendance'), + [vods, filters, activeTab, searchTerm] + ); + + const filteredAssigns = useMemo( + () => filterAssigns(assigns, filters[activeTab], searchTerm, 'isSubmit'), + [assigns, filters, activeTab, searchTerm] + ); + + const filteredQuizzes = useMemo( + () => filterQuizzes(quizzes, filters[activeTab], searchTerm, 'dueDate'), + [quizzes, filters, activeTab, searchTerm] + ); + + const handleFilterChange = useCallback( + (field: K, value: Filters[K] extends (infer T)[] | undefined ? T : never) => { + setFilters((prev) => { + const current = (prev[activeTab][field] as (typeof value)[]) || []; + const updated = current.includes(value) ? current.filter((v) => v !== value) : [...current, value]; + return { ...prev, [activeTab]: { ...prev[activeTab], [field]: updated } }; + }); + }, + [activeTab] + ); + + const handleCourseTitleChange = useCallback( + (title: string) => handleFilterChange('courseTitles', title), + [handleFilterChange] + ); + const handleAttendanceFilterChange = useCallback( + (status: string) => handleFilterChange('attendanceStatuses', status), + [handleFilterChange] + ); + const handleSubmitFilterChange = useCallback( + (isSubmit: boolean) => handleFilterChange('submitStatuses', isSubmit), + [handleFilterChange] + ); + + const clearFilters = useCallback(() => { + setFilters((prev) => ({ ...prev, [activeTab]: { ...INITIAL_FILTERS[activeTab] } })); + }, [activeTab]); + + return { + searchTerm, + setSearchTerm, + isFilterOpen, + setIsFilterOpen, + filters, + courseTitlesMap, + filteredVods, + filteredAssigns, + filteredQuizzes, + handleCourseTitleChange, + handleAttendanceFilterChange, + handleSubmitFilterChange, + clearFilters, + }; +} diff --git a/src/hooks/useGetCourse.ts b/src/hooks/useGetCourse.ts deleted file mode 100644 index 392be39..0000000 --- a/src/hooks/useGetCourse.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { CourseBase } from '@/content/types'; -import { saveDataToStorage } from '@/lib/storage'; -import { removeSquareBrackets } from '@/lib/utils'; -import { useState, useEffect } from 'react'; - -export interface UseCourseResult { - courses: CourseBase[]; -} - -export const useGetCourses = (): UseCourseResult => { - const [courses, setCourses] = useState([]); - useEffect(() => { - if (import.meta.env.VITE_MOCK) { - import('@/mocks/mockData').then(({ mockCourses }) => { - setCourses(mockCourses); - saveDataToStorage('courses', JSON.stringify(mockCourses)); - console.info('[Dotbugi] DEV 모드: mock 강의 목록 사용'); - }); - return; - } - if (!document) return; - const courseData = Array.from(document.querySelectorAll('.course_box')); - const data = courseData - .map((div) => { - const label = div.querySelector('.course_link .course-name .course-label')?.textContent?.trim() || null; - if (!label || label === '커뮤니티') return null; - const a = div.querySelector('a'); - const url = new URL((a as HTMLAnchorElement).href); - const urlParams = new URLSearchParams(url.search); - const courseId = urlParams.get('id') || ''; - const titleSection = div.querySelector('.course_link .course-name .course-title'); - const prof = titleSection?.querySelector('p')?.textContent?.trim() || ''; - let courseTitle = titleSection?.querySelector('h1, h2, h3')?.textContent?.replace(/new/i, '').trim() || ''; - courseTitle = removeSquareBrackets(courseTitle); - return { courseId, courseTitle, prof }; - }) - .filter( - (item): item is { courseId: string; courseTitle: string; prof: string } => - item !== null && item.courseId !== '' && item.courseTitle !== '' && item.prof !== '' - ); - - setCourses(data); - saveDataToStorage('courses', JSON.stringify(data)); - console.info('[Dotbugi] 강의 목록:', data); - }, []); - - return { courses }; -}; diff --git a/src/hooks/useGetCourses.ts b/src/hooks/useGetCourses.ts new file mode 100644 index 0000000..4a60ccf --- /dev/null +++ b/src/hooks/useGetCourses.ts @@ -0,0 +1,86 @@ +import { CourseBase } from '@/types'; +import { saveDataToStorage, loadDataFromStorage } from '@/lib/storage'; +import { parseCoursesFromDOM } from '@/lib/parseCourses'; +import { useState, useEffect, useCallback, useMemo } from 'react'; + +function getMockCourses(): CourseBase[] | null { + const raw = import.meta.env.VITE_MOCK_COURSES; + if (!raw) return null; + try { + const parsed = JSON.parse(raw); + if (Array.isArray(parsed) && parsed.length > 0) return parsed; + } catch { + console.error('[Dotbugi] VITE_MOCK_COURSES 파싱 실패'); + } + return null; +} + +export const useGetCourses = () => { + const [allCourses, setAllCourses] = useState([]); + const [trackedCourseIds, setTrackedCourseIdsState] = useState(null); + + const setTrackedCourseIds = useCallback((ids: string[]) => { + setTrackedCourseIdsState(ids); + saveDataToStorage('trackedCourseIds', ids); + }, []); + + useEffect(() => { + const initCourses = (courses: CourseBase[]) => { + setAllCourses(courses); + saveDataToStorage('courses', JSON.stringify(courses)); + + loadDataFromStorage('trackedCourseIds', (savedIds) => { + if (savedIds && savedIds.length > 0) { + setTrackedCourseIdsState(savedIds); + } else { + // 최초 사용: 비교과(커뮤니티) 제외하고 전부 트래킹 + const defaultIds = courses + .filter((c) => !c.isCommunity) + .map((c) => c.courseId); + setTrackedCourseIdsState(defaultIds); + saveDataToStorage('trackedCourseIds', defaultIds); + } + }); + }; + + if (import.meta.env.VITE_MOCK) { + const envCourses = getMockCourses(); + if (envCourses) { + // MOCK_COURSES 모드: chrome.storage 없이 전부 트래킹 + setAllCourses(envCourses); + const defaultIds = envCourses.map((c) => c.courseId); + setTrackedCourseIdsState(defaultIds); + return; + } + import('@/mocks/mockData').then(({ mockCourses }) => { + initCourses(mockCourses); + }); + return; + } + + const parsed = parseCoursesFromDOM(); + initCourses(parsed); + }, []); + + // DOM 토글에서 trackedCourseIds가 변경되면 React 상태 동기화 + useEffect(() => { + const listener = (changes: { [key: string]: chrome.storage.StorageChange }) => { + if (changes.trackedCourseIds) { + const newIds = changes.trackedCourseIds.newValue as string[] | undefined; + if (newIds) { + setTrackedCourseIdsState(newIds); + } + } + }; + chrome.storage.onChanged.addListener(listener); + return () => chrome.storage.onChanged.removeListener(listener); + }, []); + + const trackedCourses = useMemo(() => { + if (!trackedCourseIds) return []; + const idSet = new Set(trackedCourseIds); + return allCourses.filter((c) => idSet.has(c.courseId)); + }, [allCourses, trackedCourseIds]); + + return { allCourses, trackedCourses, trackedCourseIds: trackedCourseIds ?? [], setTrackedCourseIds }; +}; diff --git a/src/hooks/useHiddenTasks.ts b/src/hooks/useHiddenTasks.ts new file mode 100644 index 0000000..3642442 --- /dev/null +++ b/src/hooks/useHiddenTasks.ts @@ -0,0 +1,49 @@ +import { useState, useEffect, useCallback } from 'react'; +import { saveDataToStorage, loadDataFromStorage } from '@/lib/storage'; + +const STORAGE_KEY = 'hiddenTaskUrls'; + +export function useHiddenTasks() { + const [hiddenUrls, setHiddenUrls] = useState>(new Set()); + + useEffect(() => { + loadDataFromStorage(STORAGE_KEY, (data) => { + if (data) setHiddenUrls(new Set(data)); + }); + }, []); + + const persist = useCallback((urls: Set) => { + saveDataToStorage(STORAGE_KEY, Array.from(urls)); + }, []); + + const hideTask = useCallback((url: string) => { + setHiddenUrls((prev) => { + const next = new Set(prev); + next.add(url); + persist(next); + return next; + }); + }, [persist]); + + const hideTasks = useCallback((urls: string[]) => { + setHiddenUrls((prev) => { + const next = new Set(prev); + for (const url of urls) next.add(url); + persist(next); + return next; + }); + }, [persist]); + + const unhideTask = useCallback((url: string) => { + setHiddenUrls((prev) => { + const next = new Set(prev); + next.delete(url); + persist(next); + return next; + }); + }, [persist]); + + const isHidden = useCallback((url: string) => hiddenUrls.has(url), [hiddenUrls]); + + return { hiddenUrls, hideTask, hideTasks, unhideTask, isHidden }; +} diff --git a/src/hooks/useRemainingTime.ts b/src/hooks/useRemainingTime.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/i18n/LanguageSwitcher.tsx b/src/i18n/LanguageSwitcher.tsx new file mode 100644 index 0000000..14ca450 --- /dev/null +++ b/src/i18n/LanguageSwitcher.tsx @@ -0,0 +1,25 @@ +import { useTranslation } from 'react-i18next'; +import { SUPPORTED_LANGUAGES, changeLanguage, type LanguageCode } from '@/i18n'; +import { Globe } from 'lucide-react'; + +export default function LanguageSwitcher() { + const { i18n, t } = useTranslation('common'); + + return ( +
+ + +
+ ); +} diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 0000000..957306a --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,67 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; + +import koCommon from './locales/ko/common.json'; +import koPopover from './locales/ko/popover.json'; +import koPlayer from './locales/ko/player.json'; + +import enCommon from './locales/en/common.json'; +import enPopover from './locales/en/popover.json'; +import enPlayer from './locales/en/player.json'; + +import jaCommon from './locales/ja/common.json'; +import jaPopover from './locales/ja/popover.json'; +import jaPlayer from './locales/ja/player.json'; + +import zhCommon from './locales/zh/common.json'; +import zhPopover from './locales/zh/popover.json'; +import zhPlayer from './locales/zh/player.json'; + +export const SUPPORTED_LANGUAGES = [ + { code: 'ko', label: '한국어' }, + { code: 'en', label: 'English' }, + { code: 'ja', label: '日本語' }, + { code: 'zh', label: '中文' }, +] as const; + +export type LanguageCode = (typeof SUPPORTED_LANGUAGES)[number]['code']; + +function detectLanguage(): LanguageCode { + const navLang = navigator.language.split('-')[0]; + const supported = SUPPORTED_LANGUAGES.map((l) => l.code) as readonly string[]; + return supported.includes(navLang) ? (navLang as LanguageCode) : 'ko'; +} + +const resources = { + ko: { common: koCommon, popover: koPopover, player: koPlayer }, + en: { common: enCommon, popover: enPopover, player: enPlayer }, + ja: { common: jaCommon, popover: jaPopover, player: jaPlayer }, + zh: { common: zhCommon, popover: zhPopover, player: zhPlayer }, +}; + +i18n.use(initReactI18next).init({ + resources, + lng: detectLanguage(), + fallbackLng: 'ko', + defaultNS: 'common', + ns: ['common', 'popover', 'player'], + interpolation: { escapeValue: false }, +}); + +// chrome.storage에서 저장된 언어 설정을 불러와서 적용 +if (typeof chrome !== 'undefined' && chrome.storage?.local) { + chrome.storage.local.get('language', (result) => { + if (result.language && i18n.language !== result.language) { + i18n.changeLanguage(result.language); + } + }); +} + +export function changeLanguage(lng: LanguageCode) { + i18n.changeLanguage(lng); + if (typeof chrome !== 'undefined' && chrome.storage?.local) { + chrome.storage.local.set({ language: lng }); + } +} + +export default i18n; diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json new file mode 100644 index 0000000..8bb05ba --- /dev/null +++ b/src/i18n/locales/en/common.json @@ -0,0 +1,132 @@ +{ + "vod": "Lectures", + "assign": "Assignments", + "quiz": "Quizzes", + "setting": "Setting", + "dashboard": "Dashboard", + "close": "Close", + "clearAll": "Clear All", + "search": "Search", + "cancel": "Cancel", + "notFound": "404 Not Found", + "attendance": { + "attended": "Attended", + "absent": "Absent" + }, + "submit": { + "done": "Submitted", + "needed": "Not Submitted", + "doneSpaced": "Submitted", + "neededSpaced": "Not Submitted" + }, + "status": { + "notStarted": "Not Started", + "completed": "Completed", + "inProgress": "In Progress", + "checkManually": "Check Manually", + "checkManuallyNoSpace": "Check Manually" + }, + "date": { + "noDeadline": "No Deadline", + "noInfo": "No Info", + "expired": "Expired", + "daysLater": "In {{days}} days", + "hoursLater": "In {{hours}} hours", + "minutesLater": "In {{minutes}} min", + "remaining": "{{days}}d {{hours}}h {{minutes}}m left", + "remainingHoursMinutes": "{{hours}}h {{minutes}}m left", + "remainingMinutes": "{{minutes}}m left", + "justNow": "Just now", + "daysAgo": "{{days}}d {{hours}}h {{minutes}}m ago", + "hoursAgo": "{{hours}}h {{minutes}}m ago", + "minutesAgo": "{{minutes}}m ago", + "ago": "ago", + "minutesAgoShort": "{{minutes}}m ago", + "hoursAgoShort": "{{hours}}h ago", + "monthYear": "MMMM yyyy", + "weekdays": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] + }, + "count": "{{count}}", + "countZero": "0", + "percentComplete": "{{percent}}% Done", + "error": { + "occurred": "An error occurred.", + "refreshPage": "Refresh Page" + }, + "noRecords": "No records found.", + "empty": { + "vod": "No lectures", + "assign": "No assignments", + "quiz": "No quizzes" + }, + "progress": "Progress", + "attendancePeriod": "Attendance Period", + "filterSet": "Filter Applied", + "language": "Language", + "contact": "Contact Us", + "calendar": { + "bannerTitle": "Google Calendar Sync", + "bannerDesc": "Manage your lectures, assignments, and quizzes on Google Calendar with Dotbugi", + "login": "Log In", + "sync": "Sync Tasks", + "syncShort": "Sync", + "logout": "Log Out", + "syncing": "Syncing...", + "syncSuccess": "{{count}} events added", + "syncNoNew": "Already up to date", + "syncFailed": "Failed to add {{failed}} events", + "syncPartial": "{{added}} added, {{failed}} failed", + "tokenExpired": "Google login has expired", + "tokenExpiredAction": "Log in again" + }, + "refresh": "Refresh", + "refreshing": "Refreshing...", + "refreshSuccess": "Refresh complete", + "bubble": { + "tasks": "{{count}} left to go!", + "allDone": "All done!", + "updatedAgo": "As of {{time}}" + }, + "courses": { + "title": "Manage Courses", + "desc": "Select which courses to track", + "community": "Community" + }, + "hide": { + "task": "Hide this task", + "title": "Hidden Tasks", + "empty": "No hidden tasks", + "unhide": "Unhide" + }, + "github": { + "likeProject": "Like this project?", + "starOnGithub": "Star us on GitHub!" + }, + "courseStatus": { + "vod": { + "completed": "Attended", + "inProgress": "Watching", + "absent": "Absent", + "pending": "Not Watched" + }, + "assign": { + "completed": "Submitted", + "late": "Late Submission", + "absent": "Not Submitted (Closed)", + "pending": "Not Submitted" + }, + "quiz": { + "completed": "Taken", + "absent": "Not Taken (Closed)", + "pending": "Not Taken" + }, + "hidden": "Hidden", + "currentWeek": "This Week", + "goToCurrentWeek": "Go to this week", + "bar": { + "loading": "Loading info...", + "success": "Info loaded", + "error": "Failed to load info" + } + } +} diff --git a/src/i18n/locales/en/player.json b/src/i18n/locales/en/player.json new file mode 100644 index 0000000..6264d64 --- /dev/null +++ b/src/i18n/locales/en/player.json @@ -0,0 +1,13 @@ +{ + "noVideoInfo": "No video information", + "startPrompt": "Press the Start button to begin auto-play", + "stopWatching": "Stop", + "expectedEnd": "Done by {{time}}", + "doneWatching": "Completed", + "startWatching": "Start", + "lectureList": "Lecture List", + "noVideos": "No videos to watch", + "refreshPrompt": "Click Dotbugi to refresh lectures", + "appIcon": "Dotbugi Icon", + "appName": "Dotbugi 🔎" +} diff --git a/src/i18n/locales/en/popover.json b/src/i18n/locales/en/popover.json new file mode 100644 index 0000000..b0b40e5 --- /dev/null +++ b/src/i18n/locales/en/popover.json @@ -0,0 +1,13 @@ +{ + "header": { + "vodList": "Online Lectures", + "assignList": "Assignments", + "quizList": "Quizzes", + "setting": "Setting" + }, + "pending": { + "title": "Request in Progress", + "message": "Please wait while the request is being processed.", + "autoClose": "This window will close automatically when done." + } +} diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json new file mode 100644 index 0000000..6010c23 --- /dev/null +++ b/src/i18n/locales/ja/common.json @@ -0,0 +1,132 @@ +{ + "vod": "講義", + "assign": "課題", + "quiz": "クイズ", + "setting": "設定", + "dashboard": "ダッシュボード", + "close": "閉じる", + "clearAll": "すべてクリア", + "search": "検索", + "cancel": "キャンセル", + "notFound": "404 Not Found", + "attendance": { + "attended": "出席", + "absent": "欠席" + }, + "submit": { + "done": "提出済み", + "needed": "未提出", + "doneSpaced": "提出済み", + "neededSpaced": "未提出" + }, + "status": { + "notStarted": "未学習", + "completed": "学習完了", + "inProgress": "学習中", + "checkManually": "直接確認", + "checkManuallyNoSpace": "直接確認" + }, + "date": { + "noDeadline": "期限なし", + "noInfo": "情報なし", + "expired": "締切", + "daysLater": "{{days}}日後", + "hoursLater": "{{hours}}時間後", + "minutesLater": "{{minutes}}分後", + "remaining": "{{days}}日{{hours}}時間{{minutes}}分 残り", + "remainingHoursMinutes": "{{hours}}時間{{minutes}}分 残り", + "remainingMinutes": "{{minutes}}分 残り", + "justNow": "たった今", + "daysAgo": "{{days}}日{{hours}}時間{{minutes}}分前", + "hoursAgo": "{{hours}}時間{{minutes}}分前", + "minutesAgo": "{{minutes}}分前", + "ago": "前", + "minutesAgoShort": "{{minutes}}分前", + "hoursAgoShort": "{{hours}}時間前", + "monthYear": "yyyy年 MM月", + "weekdays": ["日", "月", "火", "水", "木", "金", "土"] + }, + "count": "{{count}} 件", + "countZero": "0 件", + "percentComplete": "{{percent}}% 完了", + "error": { + "occurred": "エラーが発生しました。", + "refreshPage": "ページを更新" + }, + "noRecords": "記録がありません。", + "empty": { + "vod": "講義がありません", + "assign": "課題がありません", + "quiz": "クイズがありません" + }, + "progress": "進捗率", + "attendancePeriod": "出席認定期間", + "filterSet": "フィルター適用中", + "language": "言語", + "contact": "お問い合わせ", + "calendar": { + "bannerTitle": "Google Calendar 連携", + "bannerDesc": "Dotbugiで講義・課題・クイズの日程をGoogle Calendarで管理しましょう", + "login": "ログイン", + "sync": "タスク同期", + "syncShort": "同期", + "logout": "ログアウト", + "syncing": "同期中...", + "syncSuccess": "{{count}}件の予定を追加しました", + "syncNoNew": "すでに最新です", + "syncFailed": "{{failed}}件の追加に失敗しました", + "syncPartial": "{{added}}件追加、{{failed}}件失敗", + "tokenExpired": "Googleログインの期限が切れました", + "tokenExpiredAction": "再ログイン" + }, + "refresh": "更新", + "refreshing": "更新中...", + "refreshSuccess": "更新完了", + "bubble": { + "tasks": "あと{{count}}件あるよ!", + "allDone": "全部完了!", + "updatedAgo": "{{time}} 時点" + }, + "courses": { + "title": "受講科目管理", + "desc": "追跡する科目を選択してください", + "community": "コミュニティ" + }, + "hide": { + "task": "このタスクを非表示", + "title": "非表示のタスク", + "empty": "非表示のタスクはありません", + "unhide": "再表示" + }, + "github": { + "likeProject": "このプロジェクトが気に入りましたか?", + "starOnGithub": "GitHubでスターをお願いします!" + }, + "courseStatus": { + "vod": { + "completed": "出席", + "inProgress": "視聴中", + "absent": "欠席", + "pending": "未視聴" + }, + "assign": { + "completed": "提出済み", + "late": "遅れた提出", + "absent": "未提出 (締切)", + "pending": "未提出" + }, + "quiz": { + "completed": "受験済み", + "absent": "未受験 (締切)", + "pending": "未受験" + }, + "hidden": "非表示", + "currentWeek": "今週", + "goToCurrentWeek": "今週へ移動", + "bar": { + "loading": "情報を読み込み中...", + "success": "情報の読み込み完了", + "error": "情報の読み込みに失敗しました" + } + } +} diff --git a/src/i18n/locales/ja/player.json b/src/i18n/locales/ja/player.json new file mode 100644 index 0000000..b1a46e4 --- /dev/null +++ b/src/i18n/locales/ja/player.json @@ -0,0 +1,13 @@ +{ + "noVideoInfo": "映像情報がありません", + "startPrompt": "自動受講を開始するには受講開始ボタンを押してください", + "stopWatching": "受講終了", + "expectedEnd": "{{time}} 完了予定", + "doneWatching": "受講完了", + "startWatching": "受講開始", + "lectureList": "講義リスト", + "noVideos": "受講する映像がありません", + "refreshPrompt": "Dotbugiをクリックして講義を更新してください", + "appIcon": "Dotbugiアイコン", + "appName": "Dotbugi 🔎" +} diff --git a/src/i18n/locales/ja/popover.json b/src/i18n/locales/ja/popover.json new file mode 100644 index 0000000..b925ddd --- /dev/null +++ b/src/i18n/locales/ja/popover.json @@ -0,0 +1,13 @@ +{ + "header": { + "vodList": "オンライン講義一覧", + "assignList": "課題一覧", + "quizList": "クイズ一覧", + "setting": "設定" + }, + "pending": { + "title": "リクエスト処理中", + "message": "リクエストを処理しています。しばらくお待ちください。", + "autoClose": "完了すると自動的にウィンドウが閉じます。" + } +} diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json new file mode 100644 index 0000000..7efde54 --- /dev/null +++ b/src/i18n/locales/ko/common.json @@ -0,0 +1,132 @@ +{ + "vod": "강의", + "assign": "과제", + "quiz": "퀴즈", + "setting": "설정", + "dashboard": "대시보드", + "close": "닫기", + "clearAll": "모두 지우기", + "search": "검색", + "cancel": "취소", + "notFound": "404 Not Found", + "attendance": { + "attended": "출석", + "absent": "결석" + }, + "submit": { + "done": "제출완료", + "needed": "제출필요", + "doneSpaced": "제출 완료", + "neededSpaced": "제출 필요" + }, + "status": { + "notStarted": "학습전", + "completed": "학습완료", + "inProgress": "학습중", + "checkManually": "직접 확인", + "checkManuallyNoSpace": "직접확인" + }, + "date": { + "noDeadline": "기한없음", + "noInfo": "정보없음", + "expired": "마감", + "daysLater": "{{days}}일 후", + "hoursLater": "{{hours}}시간 후", + "minutesLater": "{{minutes}}분 후", + "remaining": "{{days}}일 {{hours}}시간 {{minutes}}분 남음", + "remainingHoursMinutes": "{{hours}}시간 {{minutes}}분 남음", + "remainingMinutes": "{{minutes}}분 남음", + "justNow": "지금 막", + "daysAgo": "{{days}}일 {{hours}}시간 {{minutes}}분 전", + "hoursAgo": "{{hours}}시간 {{minutes}}분 전", + "minutesAgo": "{{minutes}}분 전", + "ago": "전", + "minutesAgoShort": "{{minutes}}분 전", + "hoursAgoShort": "{{hours}}시간 전", + "monthYear": "yyyy년 MM월", + "weekdays": ["일", "월", "화", "수", "목", "금", "토"] + }, + "count": "{{count}} 개", + "countZero": "0 개", + "percentComplete": "{{percent}}% 완료", + "error": { + "occurred": "오류가 발생했습니다.", + "refreshPage": "페이지 새로고침" + }, + "noRecords": "기록이 존재하지 않습니다.", + "empty": { + "vod": "강의가 없습니다", + "assign": "과제가 없습니다", + "quiz": "퀴즈가 없습니다" + }, + "progress": "진도율", + "attendancePeriod": "출석인정기간", + "filterSet": "필터 설정됨", + "language": "언어", + "contact": "문의하기", + "calendar": { + "bannerTitle": "Google Calendar 연동", + "bannerDesc": "돋부기를 통해 강의, 과제, 퀴즈 일정을 Google Calendar로 관리하세요", + "login": "로그인", + "sync": "태스크 동기화", + "syncShort": "동기화", + "logout": "로그아웃", + "syncing": "동기화 중...", + "syncSuccess": "{{count}}개 일정이 추가되었어요", + "syncNoNew": "이미 최신 상태예요", + "syncFailed": "{{failed}}개 일정 추가에 실패했어요", + "syncPartial": "{{added}}개 추가, {{failed}}개 실패", + "tokenExpired": "Google 로그인이 만료되었어요", + "tokenExpiredAction": "다시 로그인" + }, + "refresh": "새로고침", + "refreshing": "새로고침 중...", + "refreshSuccess": "새로고침 완료", + "bubble": { + "tasks": "아직 {{count}}개 남았어요!", + "allDone": "모두 완료했어요!", + "updatedAgo": "{{time}} 기준" + }, + "courses": { + "title": "수강 과목 관리", + "desc": "추적할 과목을 선택하세요", + "community": "비교과" + }, + "hide": { + "task": "이 태스크 숨기기", + "title": "숨긴 태스크", + "empty": "숨긴 태스크가 없습니다", + "unhide": "숨김 해제" + }, + "github": { + "likeProject": "이 프로젝트가 마음에 드시나요?", + "starOnGithub": "GitHub에서 스타를 눌러주세요!" + }, + "courseStatus": { + "vod": { + "completed": "출석", + "inProgress": "시청중", + "absent": "결석", + "pending": "미시청" + }, + "assign": { + "completed": "제출완료", + "late": "늦은 제출", + "absent": "미제출 (마감)", + "pending": "미제출" + }, + "quiz": { + "completed": "응시완료", + "absent": "미응시 (마감)", + "pending": "미응시" + }, + "hidden": "숨김", + "currentWeek": "현재 주차", + "goToCurrentWeek": "현재 주차로 이동", + "bar": { + "loading": "정보 불러오는 중...", + "success": "정보 불러오기 완료", + "error": "정보를 불러오지 못했습니다" + } + } +} diff --git a/src/i18n/locales/ko/player.json b/src/i18n/locales/ko/player.json new file mode 100644 index 0000000..a2783b5 --- /dev/null +++ b/src/i18n/locales/ko/player.json @@ -0,0 +1,13 @@ +{ + "noVideoInfo": "영상 정보가 없습니다", + "startPrompt": "자동수강을 시작하려면 수강시작 버튼을 눌러주세요", + "stopWatching": "수강 종료", + "expectedEnd": "{{time}} 완료 예정", + "doneWatching": "수강 완료", + "startWatching": "수강 시작", + "lectureList": "강의 목록", + "noVideos": "수강할 영상이 없습니다", + "refreshPrompt": "이클래스 돋부기를 눌러 강의를 새로고침 해주세요", + "appIcon": "돋부기 아이콘", + "appName": "돋부기 🔎" +} diff --git a/src/i18n/locales/ko/popover.json b/src/i18n/locales/ko/popover.json new file mode 100644 index 0000000..5339495 --- /dev/null +++ b/src/i18n/locales/ko/popover.json @@ -0,0 +1,13 @@ +{ + "header": { + "vodList": "온라인 강의 목록", + "assignList": "과제 목록", + "quizList": "퀴즈 목록", + "setting": "설정" + }, + "pending": { + "title": "요청 진행 중", + "message": "요청이 진행중입니다. 잠시만 기다려주세요.", + "autoClose": "완료되면 자동으로 창이 닫힙니다." + } +} diff --git a/src/i18n/locales/zh/common.json b/src/i18n/locales/zh/common.json new file mode 100644 index 0000000..f6eee61 --- /dev/null +++ b/src/i18n/locales/zh/common.json @@ -0,0 +1,133 @@ +{ + "vod": "课程", + "assign": "作业", + "quiz": "测验", + + "setting": "设置", + "dashboard": "仪表盘", + "close": "关闭", + "clearAll": "全部清除", + "search": "搜索", + "cancel": "取消", + "notFound": "404 Not Found", + "attendance": { + "attended": "出勤", + "absent": "缺勤" + }, + "submit": { + "done": "已提交", + "needed": "未提交", + "doneSpaced": "已提交", + "neededSpaced": "未提交" + }, + "status": { + "notStarted": "未学习", + "completed": "学习完成", + "inProgress": "学习中", + "checkManually": "手动确认", + "checkManuallyNoSpace": "手动确认" + }, + "date": { + "noDeadline": "无截止日", + "noInfo": "无信息", + "expired": "已截止", + "daysLater": "{{days}}天后", + "hoursLater": "{{hours}}小时后", + "minutesLater": "{{minutes}}分钟后", + "remaining": "剩余{{days}}天{{hours}}小时{{minutes}}分钟", + "remainingHoursMinutes": "剩余{{hours}}小时{{minutes}}分钟", + "remainingMinutes": "剩余{{minutes}}分钟", + "justNow": "刚刚", + "daysAgo": "{{days}}天{{hours}}小时{{minutes}}分钟前", + "hoursAgo": "{{hours}}小时{{minutes}}分钟前", + "minutesAgo": "{{minutes}}分钟前", + "ago": "前", + "minutesAgoShort": "{{minutes}}分钟前", + "hoursAgoShort": "{{hours}}小时前", + "monthYear": "yyyy年 MM月", + "weekdays": ["日", "一", "二", "三", "四", "五", "六"] + }, + "count": "{{count}} 个", + "countZero": "0 个", + "percentComplete": "{{percent}}% 完成", + "error": { + "occurred": "发生了错误。", + "refreshPage": "刷新页面" + }, + "noRecords": "没有记录。", + "empty": { + "vod": "没有课程", + "assign": "没有作业", + "quiz": "没有测验" + }, + "progress": "进度", + "attendancePeriod": "出勤认定期间", + "filterSet": "已设置筛选", + "language": "语言", + "contact": "联系我们", + "calendar": { + "bannerTitle": "Google Calendar 同步", + "bannerDesc": "通过Dotbugi在Google Calendar上管理课程、作业和测验日程", + "login": "登录", + "sync": "同步任务", + "syncShort": "同步", + "logout": "退出登录", + "syncing": "同步中...", + "syncSuccess": "已添加{{count}}个日程", + "syncNoNew": "已是最新状态", + "syncFailed": "{{failed}}个日程添加失败", + "syncPartial": "已添加{{added}}个,{{failed}}个失败", + "tokenExpired": "Google登录已过期", + "tokenExpiredAction": "重新登录" + }, + "refresh": "刷新", + "refreshing": "刷新中...", + "refreshSuccess": "刷新完成", + "bubble": { + "tasks": "还剩{{count}}个哦!", + "allDone": "全部完成!", + "updatedAgo": "{{time}}基准" + }, + "courses": { + "title": "课程管理", + "desc": "选择要追踪的课程", + "community": "社区" + }, + "hide": { + "task": "隐藏此任务", + "title": "已隐藏的任务", + "empty": "没有隐藏的任务", + "unhide": "取消隐藏" + }, + "github": { + "likeProject": "喜欢这个项目吗?", + "starOnGithub": "在GitHub上给我们加星!" + }, + "courseStatus": { + "vod": { + "completed": "出勤", + "inProgress": "观看中", + "absent": "缺勤", + "pending": "未观看" + }, + "assign": { + "completed": "已提交", + "late": "逾期提交", + "absent": "未提交 (截止)", + "pending": "未提交" + }, + "quiz": { + "completed": "已参加", + "absent": "未参加 (截止)", + "pending": "未参加" + }, + "hidden": "已隐藏", + "currentWeek": "本周", + "goToCurrentWeek": "跳转到本周", + "bar": { + "loading": "正在加载信息...", + "success": "信息加载完成", + "error": "信息加载失败" + } + } +} diff --git a/src/i18n/locales/zh/player.json b/src/i18n/locales/zh/player.json new file mode 100644 index 0000000..3289a26 --- /dev/null +++ b/src/i18n/locales/zh/player.json @@ -0,0 +1,13 @@ +{ + "noVideoInfo": "没有视频信息", + "startPrompt": "请按开始按钮开始自动播放", + "stopWatching": "停止学习", + "expectedEnd": "预计{{time}}完成", + "doneWatching": "学习完成", + "startWatching": "开始学习", + "lectureList": "课程列表", + "noVideos": "没有可观看的视频", + "refreshPrompt": "点击Dotbugi刷新课程", + "appIcon": "Dotbugi图标", + "appName": "Dotbugi 🔎" +} diff --git a/src/i18n/locales/zh/popover.json b/src/i18n/locales/zh/popover.json new file mode 100644 index 0000000..6f63c7a --- /dev/null +++ b/src/i18n/locales/zh/popover.json @@ -0,0 +1,13 @@ +{ + "header": { + "vodList": "在线课程列表", + "assignList": "作业列表", + "quizList": "测验列表", + "setting": "设置" + }, + "pending": { + "title": "请求处理中", + "message": "正在处理请求,请稍候。", + "autoClose": "完成后窗口将自动关闭。" + } +} diff --git a/src/lib/attendance.ts b/src/lib/attendance.ts new file mode 100644 index 0000000..789f4bb --- /dev/null +++ b/src/lib/attendance.ts @@ -0,0 +1,7 @@ +export function isAttended(value: string) { + return value.toLowerCase().trim() === 'o'; +} + +export function isAbsent(value: string) { + return value.toUpperCase().startsWith('X'); +} diff --git a/src/lib/cache.ts b/src/lib/cache.ts new file mode 100644 index 0000000..74f5233 --- /dev/null +++ b/src/lib/cache.ts @@ -0,0 +1,18 @@ +export const REFRESH_INTERVAL_MS = 60 * 1000; +export const CACHE_TTL_MINUTES = 60; // 1시간 +export const CACHE_TTL_MS = CACHE_TTL_MINUTES * REFRESH_INTERVAL_MS; + +const CACHE_KEY = 'lastRequestTime'; + +export function getLastRequestTime(): number | null { + const raw = localStorage.getItem(CACHE_KEY); + return raw ? parseInt(raw, 10) : null; +} + +export function setLastRequestTime(time: number): void { + localStorage.setItem(CACHE_KEY, time.toString()); +} + +export function clearLastRequestTime(): void { + localStorage.removeItem(CACHE_KEY); +} diff --git a/src/lib/calendarUtils.ts b/src/lib/calendarUtils.ts index b363825..2a3f20e 100644 --- a/src/lib/calendarUtils.ts +++ b/src/lib/calendarUtils.ts @@ -1,8 +1,9 @@ -import { CalendarEvent } from '@/hooks/useCalendarEvents'; +import { CalendarEvent } from '@/lib/transformCalendarEvents'; export type GoogleCalendarEvent = { summary: string; description?: string; + colorId?: string; start: { dateTime: string; timeZone: string; @@ -13,79 +14,190 @@ export type GoogleCalendarEvent = { }; }; -export const getOAuthToken = async (): Promise => { - return new Promise((resolve) => { - chrome.identity.getAuthToken({ interactive: false }, (cachedToken) => { - if (chrome.runtime.lastError || !cachedToken) { - console.error('자동 로그인 실패:', chrome.runtime.lastError?.message); - resolve(null); - } else { - resolve(cachedToken); - } - }); - }); +export type CalendarSyncResult = { + added: number; + failed: number; + tokenExpired: boolean; +}; + +const EVENT_COLOR_MAP: Record = { + vod: '1', // 라벤더 + assign: '9', // 블루베리 (파랑) + quiz: '6', // 귤 (주황) +}; + +export const getOAuthToken = async (interactive = false): Promise => { + try { + const response = await chrome.runtime.sendMessage({ action: 'getAuthToken', interactive }); + if (response?.token) { + return response.token; + } + console.error('OAuth 토큰 획득 실패:', response?.error); + } catch (e) { + console.error('OAuth 메시지 전송 실패:', e); + } + return null; +}; + +export const removeCachedAuthToken = async (token: string): Promise => { + try { + await chrome.runtime.sendMessage({ action: 'removeCachedAuthToken', token }); + } catch (e) { + console.error('토큰 제거 메시지 전송 실패:', e); + } }; /** * 캘린더 API에 이벤트를 추가합니다. - * @param event 캘린더에 추가할 이벤트 객체 - * @param token OAuth 토큰 + * 401 반환 시 tokenExpired를 true로 반환합니다. */ -export async function addCalendarEvent(event: GoogleCalendarEvent, token: string): Promise { +export async function addCalendarEvent( + event: GoogleCalendarEvent, + token: string, +): Promise<{ ok: boolean; tokenExpired: boolean }> { try { - fetch('https://www.googleapis.com/calendar/v3/calendars/primary/events', { + const response = await fetch('https://www.googleapis.com/calendar/v3/calendars/primary/events', { method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify(event), - }) - .then((response) => response.json()) - .then(() => {}) - .catch((error) => console.error('이벤트 추가 실패:', error)); + }); + if (response.status === 401) { + return { ok: false, tokenExpired: true }; + } + if (!response.ok) { + const errorBody = await response.json(); + console.error('이벤트 추가 실패:', response.status, errorBody); + return { ok: false, tokenExpired: false }; + } + return { ok: true, tokenExpired: false }; } catch (error) { console.error('Error adding calendar event:', error); + return { ok: false, tokenExpired: false }; + } +} + +const BATCH_CONCURRENCY = 2; +const BATCH_DELAY_MS = 500; + +async function runBatch( + items: T[], + fn: (item: T) => Promise, + concurrency: number, +): Promise { + const results: R[] = new Array(items.length); + + for (let i = 0; i < items.length; i += concurrency) { + const chunk = items.slice(i, i + concurrency); + const chunkResults = await Promise.all(chunk.map((item) => fn(item))); + chunkResults.forEach((r, j) => { results[i + j] = r; }); + if (i + concurrency < items.length) { + await new Promise((resolve) => setTimeout(resolve, BATCH_DELAY_MS)); + } + } + + return results; +} + +/** + * 이벤트를 동시 5건씩 전송하고 실패한 건을 1회 재시도합니다. + */ +export async function addCalendarEventsBatch( + events: GoogleCalendarEvent[], + token: string, +): Promise { + const results = await runBatch(events, (event) => addCalendarEvent(event, token), BATCH_CONCURRENCY); + + let added = 0; + const failed: GoogleCalendarEvent[] = []; + let tokenExpired = false; + + results.forEach((result, i) => { + if (result.tokenExpired) { + tokenExpired = true; + } else if (result.ok) { + added++; + } else { + failed.push(events[i]); + } + }); + + if (tokenExpired) { + return { added, failed: events.length - added, tokenExpired: true }; } + + // 실패한 건 1회 재시도 + if (failed.length > 0) { + const retryResults = await runBatch(failed, (event) => addCalendarEvent(event, token), BATCH_CONCURRENCY); + let retryFailed = 0; + retryResults.forEach((result) => { + if (result.ok) { + added++; + } else if (result.tokenExpired) { + tokenExpired = true; + retryFailed++; + } else { + retryFailed++; + } + }); + return { added, failed: retryFailed, tokenExpired }; + } + + return { added, failed: 0, tokenExpired: false }; } /** * 구글 캘린더 API를 사용해 현재 이벤트 목록을 가져옵니다. - * @param token OAuth 토큰 - * @returns CalendarEvent 배열 */ -export async function getCalendarEvents(token: string): Promise { +export async function getCalendarEvents( + token: string, +): Promise<{ events: GoogleCalendarEvent[]; tokenExpired: boolean }> { try { const pastDate = new Date(); - pastDate.setMonth(pastDate.getMonth() - 3); - const response = await fetch( - `https://www.googleapis.com/calendar/v3/calendars/primary/events?timeMin=${pastDate.toISOString()}&orderBy=startTime&singleEvents=true`, - { - headers: { - Authorization: `Bearer ${token}`, - }, + pastDate.setMonth(pastDate.getMonth() - 4); + + let allEvents: GoogleCalendarEvent[] = []; + let pageToken: string | undefined; + + do { + const url = new URL('https://www.googleapis.com/calendar/v3/calendars/primary/events'); + url.searchParams.set('timeMin', pastDate.toISOString()); + url.searchParams.set('orderBy', 'startTime'); + url.searchParams.set('singleEvents', 'true'); + url.searchParams.set('maxResults', '2500'); + if (pageToken) url.searchParams.set('pageToken', pageToken); + + const response = await fetch(url.toString(), { + headers: { Authorization: `Bearer ${token}` }, + }); + + if (response.status === 401) { + return { events: [], tokenExpired: true }; } - ); - const data = await response.json(); - return data.items || []; + + const data = await response.json(); + allEvents = allEvents.concat(data.items || []); + pageToken = data.nextPageToken; + } while (pageToken); + + return { events: allEvents, tokenExpired: false }; } catch (error) { console.error('캘린더 이벤트 가져오기 실패:', error); - return []; + return { events: [], tokenExpired: false }; } } /** * CalendarEvent 배열을 받아 GoogleCalendarEvent 배열로 변환합니다. - * 여기서는 title과 subject를 summary로 조합하는 예시입니다. - * @param events CalendarEvent 배열 - * @returns GoogleCalendarEvent 배열 */ export function convertCalendarEventsToGoogleEvents(events: CalendarEvent[]): GoogleCalendarEvent[] { return events .filter((event) => event.start !== null && event.end !== null) .map((event) => ({ - summary: `${event.title}`, - description: `${event.subject}`, + summary: `${event.title} - ${event.subject}`, + colorId: EVENT_COLOR_MAP[event.type], start: { dateTime: event.start!.toISOString(), timeZone: 'Asia/Seoul', diff --git a/src/lib/courseStatus/badge.ts b/src/lib/courseStatus/badge.ts new file mode 100644 index 0000000..500b5ba --- /dev/null +++ b/src/lib/courseStatus/badge.ts @@ -0,0 +1,184 @@ +import { Vod, Assign, Quiz } from '@/types'; +import { isAttended } from '@/lib/attendance'; +import { isCurrentDateByDate, parseDate, calculateDueDate, extractEndDate } from '@/lib/dateUtils'; +import { BASE_LINK } from '@/constants/links'; +import i18n from '@/i18n'; + +// ── Types ─────────────────────────────────────────────────────────── + +type StatusType = 'completed' | 'inProgress' | 'late' | 'absent' | 'urgent' | 'pending' | 'hidden'; + +interface StatusInfo { + label: string; + type: StatusType; + progress?: number; // 0~100, inProgress 전용 +} + +// ── Constants ─────────────────────────────────────────────────────── + +const BADGE_ATTR = 'data-dotbugi-status'; + +const STATUS_COLORS: Record = { + completed: { bg: '#dcfce7', text: '#166534', border: '#86efac' }, + inProgress: { bg: '#dbeafe', text: '#1e40af', border: '#93c5fd' }, + late: { bg: '#450a0a', text: '#fecaca', border: '#7f1d1d' }, + absent: { bg: '#450a0a', text: '#fecaca', border: '#7f1d1d' }, + urgent: { bg: '#fee2e2', text: '#b91c1c', border: '#fca5a5' }, + pending: { bg: '#fef3c7', text: '#92400e', border: '#fcd34d' }, + hidden: { bg: '#f3f4f6', text: '#6b7280', border: '#d1d5db' }, +}; + +const t = (key: string) => i18n.t(key, { ns: 'common' }); + +// ── Helpers ───────────────────────────────────────────────────────── + +function parseTimeToSeconds(time: string): number { + const parts = time.split(':').map(Number); + if (parts.length === 3) return parts[0] * 3600 + parts[1] * 60 + parts[2]; + if (parts.length === 2) return parts[0] * 60 + parts[1]; + return 0; +} + +// ── Status determination ──────────────────────────────────────────── + +function getVodStatus(vod: Vod): StatusInfo { + if (isAttended(vod.isAttendance)) { + return { label: t('courseStatus.vod.completed'), type: 'completed' }; + } + + const rangeEnd = vod.range?.split(' ~ ')[1]; + if (rangeEnd && new Date() > parseDate(rangeEnd)) { + return { label: t('courseStatus.vod.absent'), type: 'absent' }; + } + + // 수강중: watchedTime > 0 이고 아직 출석 미완료 + if (vod.watchedTime && vod.requiredTime) { + const watchedSec = parseTimeToSeconds(vod.watchedTime); + const requiredSec = parseTimeToSeconds(vod.requiredTime); + if (watchedSec > 0 && requiredSec > 0) { + const progress = Math.min(100, Math.round((watchedSec / requiredSec) * 100)); + const endDate = extractEndDate(vod.range); + const remaining = endDate ? calculateDueDate(endDate) : null; + const suffix = + remaining && remaining.status !== 'expired' && remaining.status !== 'noInfo' + ? ` · ${remaining.message}` + : ''; + const isUrgent = remaining?.status === 'urgent'; + return { + label: `${t('courseStatus.vod.inProgress')}${suffix}`, + type: isUrgent ? 'urgent' : 'inProgress', + progress, + }; + } + } + + const endDate = extractEndDate(vod.range); + const remaining = endDate ? calculateDueDate(endDate) : null; + const label = + remaining && remaining.status !== 'expired' && remaining.status !== 'noInfo' + ? `${t('courseStatus.vod.pending')} · ${remaining.message}` + : t('courseStatus.vod.pending'); + const type = remaining?.status === 'urgent' ? 'urgent' : 'pending'; + return { label, type }; +} + +function getDueDateStatus(isSubmit: boolean, dueDate: string | null, labelKey: 'assign' | 'quiz'): StatusInfo { + if (isSubmit) { + return { label: t(`courseStatus.${labelKey}.completed`), type: 'completed' }; + } + if (dueDate && !isCurrentDateByDate(dueDate)) { + return { label: t(`courseStatus.${labelKey}.absent`), type: 'absent' }; + } + const remaining = dueDate ? calculateDueDate(dueDate) : null; + const label = + remaining && remaining.status !== 'expired' && remaining.status !== 'noInfo' + ? `${t(`courseStatus.${labelKey}.pending`)} · ${remaining.message}` + : t(`courseStatus.${labelKey}.pending`); + const type = remaining?.status === 'urgent' ? 'urgent' : 'pending'; + return { label, type }; +} + +// ── Badge creation ────────────────────────────────────────────────── + +function createBadge({ label, type, progress }: StatusInfo): HTMLElement { + const { bg, text, border } = STATUS_COLORS[type]; + const badge = document.createElement('span'); + badge.setAttribute(BADGE_ATTR, ''); + badge.textContent = label; + + let background = bg; + if (progress !== undefined) { + // 프로그레스 시각화: 왼쪽에서 오른쪽으로 채움색 적용 + const fillColor = type === 'urgent' ? '#fca5a5' : '#93c5fd'; + background = `linear-gradient(to right, ${fillColor} 0%, ${fillColor} ${progress}%, ${bg} ${progress}%, ${bg} 100%)`; + } + + badge.style.cssText = ` + display:inline-flex;align-items:center;padding:2px 8px;margin-left:8px; + border-radius:9999px;font-size:11px;font-weight:600;line-height:1.4; + background:${background};color:${text};border:1px solid ${border}; + white-space:nowrap;vertical-align:middle; + `; + return badge; +} + +function normalizeUrl(url: string): string { + if (url.startsWith('http')) return url; + return BASE_LINK + (url.startsWith('/') ? '' : '/') + url; +} + +// ── Badge injection ───────────────────────────────────────────────── + +function injectBadgesForSelector( + selector: string, + dataMap: Map, + getStatus: (item: T) => StatusInfo, + hiddenUrls: Set, + hiddenStatus: StatusInfo, +) { + document.querySelectorAll(selector).forEach((activity) => { + const link = activity.querySelector('a') as HTMLAnchorElement | null; + const nameEl = activity.querySelector('.instancename'); + if (!link || !nameEl) return; + + const url = normalizeUrl(link.href); + + if (hiddenUrls.has(url)) { + nameEl.appendChild(createBadge(hiddenStatus)); + return; + } + + const item = dataMap.get(url); + if (item) nameEl.appendChild(createBadge(getStatus(item))); + }); +} + +export function injectBadgesIntoDOM(vods: Vod[], assigns: Assign[], quizzes: Quiz[], hiddenUrls: Set) { + document.querySelectorAll(`[${BADGE_ATTR}]`).forEach((el) => el.remove()); + + const hidden: StatusInfo = { label: t('courseStatus.hidden'), type: 'hidden' }; + + injectBadgesForSelector( + 'li.modtype_vod:not(.dimmed) .activityinstance', + new Map(vods.map((v) => [v.url, v])), + getVodStatus, + hiddenUrls, + hidden, + ); + + injectBadgesForSelector( + 'li.modtype_assign:not(.dimmed) .activityinstance', + new Map(assigns.map((a) => [a.url, a])), + (a) => getDueDateStatus(a.isSubmit, a.dueDate, 'assign'), + hiddenUrls, + hidden, + ); + + injectBadgesForSelector( + 'li.modtype_quiz:not(.dimmed) .activityinstance', + new Map(quizzes.map((q) => [q.url, q])), + (q) => getDueDateStatus(q.isSubmit, q.dueDate, 'quiz'), + hiddenUrls, + hidden, + ); +} diff --git a/src/lib/courseStatus/currentWeek.ts b/src/lib/courseStatus/currentWeek.ts new file mode 100644 index 0000000..2129039 --- /dev/null +++ b/src/lib/courseStatus/currentWeek.ts @@ -0,0 +1,128 @@ +import i18n from '@/i18n'; + +const CURRENT_WEEK_ATTR = 'data-dotbugi-current-week'; +const SCROLL_BTN_ID = 'dotbugi-scroll-current-week'; + +const MONTH_NAMES: Record = { + january: 1, february: 2, march: 3, april: 4, may: 5, june: 6, + july: 7, august: 8, september: 9, october: 10, november: 11, december: 12, +}; + +const t = (key: string) => i18n.t(key, { ns: 'common' }); + +// ── Date parsing ──────────────────────────────────────────────────── + +function inferYearFromSection(section: Element): number | null { + const m = section.querySelector('.text-ubstrap')?.textContent?.trim()?.match(/(\d{4})-/); + return m ? parseInt(m[1]) : null; +} + +function inferYear(sections: NodeListOf): number { + for (const s of sections) { + const y = inferYearFromSection(s); + if (y) return y; + } + return new Date().getFullYear(); +} + +function parseLocalizedDate(str: string, year: number): Date | null { + let m: RegExpMatchArray | null; + + // Korean: 3월18일 / Chinese: 03月04日 + m = str.match(/(\d{1,2})월\s*(\d{1,2})일/) ?? str.match(/(\d{1,2})月(\d{1,2})日/); + if (m) return new Date(year, parseInt(m[1]) - 1, parseInt(m[2])); + + // Japanese: 03/04 + m = str.match(/^(\d{1,2})\/(\d{1,2})$/); + if (m) return new Date(year, parseInt(m[1]) - 1, parseInt(m[2])); + + // English: 04 March + m = str.match(/(\d{1,2})\s+([A-Za-z]+)/); + if (m && MONTH_NAMES[m[2].toLowerCase()]) { + return new Date(year, MONTH_NAMES[m[2].toLowerCase()] - 1, parseInt(m[1])); + } + + return null; +} + +function parseSectionDateRange(text: string, year: number): { start: Date; end: Date } | null { + const match = text.match(/\[([^\]]+)\]/); + if (!match) return null; + + const [startStr, endStr] = match[1].split(/\s*-\s*/); + if (!startStr || !endStr) return null; + + const start = parseLocalizedDate(startStr.trim(), year); + const end = parseLocalizedDate(endStr.trim(), year); + if (!start || !end) return null; + + end.setHours(23, 59, 59); + return { start, end }; +} + +// ── Highlight & scroll button ─────────────────────────────────────── + +export function highlightCurrentWeek() { + const now = new Date(); + const sections = document.querySelectorAll('li[id^="section-"]'); + const fallbackYear = inferYear(sections); + let currentWeekTarget: Element | null = null; + + for (const section of sections) { + if (section.querySelector(`[${CURRENT_WEEK_ATTR}]`)) continue; + + const year = inferYearFromSection(section) ?? fallbackYear; + const name = section.querySelector('.sectionname')?.textContent?.trim(); + if (!name) continue; + + const range = parseSectionDateRange(name, year); + if (!range || now < range.start || now > range.end) continue; + + currentWeekTarget = section; + + const content = section.querySelector('.content') as HTMLElement | null; + if (!content) continue; + + content.style.borderLeft = '4px solid #3b82f6'; + content.style.paddingLeft = '12px'; + content.style.borderRadius = '4px'; + + const marker = document.createElement('span'); + marker.setAttribute(CURRENT_WEEK_ATTR, ''); + marker.textContent = t('courseStatus.currentWeek'); + marker.style.cssText = ` + display:inline-flex;align-items:center;padding:2px 8px;margin-left:8px; + border-radius:9999px;font-size:11px;font-weight:600;line-height:1.4; + background:#eff6ff;color:#1d4ed8;border:1px solid #bfdbfe; + white-space:nowrap;vertical-align:middle; + `; + + section.querySelector('.sectionname span')?.appendChild(marker); + } + + if (currentWeekTarget && !document.getElementById(SCROLL_BTN_ID)) { + const target = currentWeekTarget; + const btn = document.createElement('button'); + btn.id = SCROLL_BTN_ID; + btn.textContent = t('courseStatus.goToCurrentWeek'); + btn.style.cssText = ` + position:fixed;bottom:24px;left:50%;transform:translateX(-50%);z-index:9999; + display:inline-flex;align-items:center;gap:6px;padding:10px 18px; + border-radius:24px;font-size:13px;font-weight:600;color:#fff; + background:#3b82f6;border:none;cursor:pointer;transition:all 0.3s; + box-shadow:0 4px 12px rgba(59,130,246,0.4); + `; + btn.addEventListener('mouseenter', () => { + btn.style.background = '#2563eb'; + btn.style.boxShadow = '0 6px 16px rgba(59,130,246,0.5)'; + }); + btn.addEventListener('mouseleave', () => { + btn.style.background = '#3b82f6'; + btn.style.boxShadow = '0 4px 12px rgba(59,130,246,0.4)'; + }); + btn.addEventListener('click', () => { + target.scrollIntoView({ behavior: 'smooth', block: 'center' }); + }); + document.body.appendChild(btn); + } +} diff --git a/src/lib/courseStatus/index.ts b/src/lib/courseStatus/index.ts new file mode 100644 index 0000000..f5f3847 --- /dev/null +++ b/src/lib/courseStatus/index.ts @@ -0,0 +1,95 @@ +import { CourseBase, Quiz } from '@/types'; +import { scrapeCourseData } from '@/lib/fetchCourseData'; +import { mergeVodWithAttendance, mergeDueDateItems } from '@/lib/transformCourseData'; + +import { + load, + loadCourseData, + loadAllData, + saveCourseData, + cleanupExpiredTempCourses, + registerTempCourse, + isFetchCacheValid, + setFetchCache, +} from './storage'; +import { injectBadgesIntoDOM } from './badge'; +import { highlightCurrentWeek } from './currentWeek'; +import { showStatusBar } from './statusBar'; + +// Course info from DOM + +function parseCourseInfoFromDOM(courseId: string): CourseBase { + const prof = document.querySelector('.course-header .media-heading')?.textContent?.trim() ?? ''; + const courseTitle = + document.querySelector('.coursename h1 a')?.textContent?.trim() ?? + document.querySelector('.page-header-headings h1')?.textContent?.trim() ?? + ''; + return { courseId, courseTitle, prof }; +} + +// Fetch & inject + +async function fetchAndInject(courseId: string, course: CourseBase, isTracked: boolean, isCommunity: boolean) { + showStatusBar('loading'); + + const [hiddenUrls, cachedQuizzes] = await Promise.all([ + load('hiddenTaskUrls'), + load('quiz').then((all) => (all ?? []).filter((q) => q.courseId === courseId)), + ]); + + try { + const scraped = await scrapeCourseData(courseId, cachedQuizzes, isCommunity); + const newVods = mergeVodWithAttendance(course, scraped.vodDataArray, scraped.vodAttendanceArray); + const newAssigns = mergeDueDateItems(course, scraped.assignDataArray); + const newQuizzes = mergeDueDateItems(course, scraped.quizDataArray); + + const all = await loadAllData(); + saveCourseData(courseId, newVods, newAssigns, newQuizzes, all); + + if (!isTracked) await registerTempCourse(courseId); + + injectBadgesIntoDOM(newVods, newAssigns, newQuizzes, new Set(hiddenUrls ?? [])); + setFetchCache(courseId); + showStatusBar('success'); + } catch (error) { + console.error('[Dotbugi] 강의 데이터 로드 오류:', error); + showStatusBar('error'); + } +} + +// Main entry point + +export async function injectCourseStatus() { + const match = window.location.href.match(/\/course\/view\.php\?id=(\d+)/); + if (!match) return; + + const courseId = match[1]; + + highlightCurrentWeek(); + await cleanupExpiredTempCourses(); + + const [trackedIds, communityIds] = await Promise.all([ + load('trackedCourseIds'), + load('communityIds'), + ]); + const isTracked = trackedIds?.includes(courseId) ?? false; + const isCommunity = communityIds?.includes(courseId) ?? false; + + // 뒤로가기/앞으로가기 → 캐시 사용 + if (isFetchCacheValid(courseId)) { + const data = await loadCourseData(courseId); + injectBadgesIntoDOM(data.vods, data.assigns, data.quizzes, data.hiddenUrls); + return; + } + + // 트래킹 강의 → 저장된 데이터로 빠른 초기 렌더링 + if (isTracked) { + const data = await loadCourseData(courseId); + if (data.vods.length || data.assigns.length || data.quizzes.length) { + injectBadgesIntoDOM(data.vods, data.assigns, data.quizzes, data.hiddenUrls); + } + } + + // 최신 데이터 fetch + fetchAndInject(courseId, parseCourseInfoFromDOM(courseId), isTracked, isCommunity); +} diff --git a/src/lib/courseStatus/statusBar.ts b/src/lib/courseStatus/statusBar.ts new file mode 100644 index 0000000..785701c --- /dev/null +++ b/src/lib/courseStatus/statusBar.ts @@ -0,0 +1,49 @@ +import i18n from '@/i18n'; + +const STATUS_BAR_ID = 'dotbugi-status-bar'; +const STATUS_BAR_HEIGHT = 36; + +const t = (key: string) => i18n.t(key, { ns: 'common' }); + +const STATUS_BAR_STYLES: Record = { + loading: { bg: '#eff6ff', text: '#1d4ed8' }, + success: { bg: '#f0fdf4', text: '#166534' }, + error: { bg: '#fef2f2', text: '#991b1b' }, +}; + +export function showStatusBar(state: 'loading' | 'success' | 'error') { + let bar = document.getElementById(STATUS_BAR_ID); + if (!bar) { + bar = document.createElement('div'); + bar.id = STATUS_BAR_ID; + bar.style.cssText = ` + position:fixed;top:0;left:0;right:0;z-index:99999; + height:0;opacity:0;overflow:hidden; + display:flex;align-items:center;justify-content:center; + gap:8px;font-size:13px;font-weight:500; + transition:height 0.3s ease,opacity 0.3s ease; + `; + document.body.appendChild(bar); + } + + const { bg, text } = STATUS_BAR_STYLES[state]; + bar.style.background = bg; + bar.style.color = text; + bar.style.height = `${STATUS_BAR_HEIGHT}px`; + bar.style.opacity = '1'; + bar.textContent = t(`courseStatus.bar.${state}`); + + const navbar = document.querySelector('.navbar-fixed-top'); + if (navbar) { + navbar.style.transition = 'top 0.3s ease'; + navbar.style.top = `${STATUS_BAR_HEIGHT}px`; + } + + if (state !== 'loading') { + setTimeout(() => { + bar!.style.height = '0'; + bar!.style.opacity = '0'; + if (navbar) navbar.style.top = '0'; + }, 2000); + } +} diff --git a/src/lib/courseStatus/storage.ts b/src/lib/courseStatus/storage.ts new file mode 100644 index 0000000..94ccccc --- /dev/null +++ b/src/lib/courseStatus/storage.ts @@ -0,0 +1,107 @@ +import { Vod, Assign, Quiz } from '@/types'; +import { loadDataFromStorage, saveDataToStorage } from '@/lib/storage'; + +const TEMP_COURSES_KEY = 'tempCourseFetchTimes'; +const TEMP_COURSE_TTL_MS = 60 * 1000; +const FETCH_CACHE_TTL_MS = 2 * 60 * 1000; + +type TempCourseFetchTimes = Record; + +// ── Generic loader ────────────────────────────────────────────────── + +export function load(key: string): Promise { + return new Promise((resolve) => loadDataFromStorage(key, resolve)); +} + +// ── Course data ───────────────────────────────────────────────────── + +export async function loadCourseData(courseId: string) { + const [vods, assigns, quizzes, hidden] = await Promise.all([ + load('vod'), + load('assign'), + load('quiz'), + load('hiddenTaskUrls'), + ]); + + const byCourse = (items: T[] | null) => + (items ?? []).filter((item) => item.courseId === courseId); + + return { + vods: byCourse(vods), + assigns: byCourse(assigns), + quizzes: byCourse(quizzes), + hiddenUrls: new Set(hidden ?? []), + }; +} + +export async function loadAllData() { + const [vods, assigns, quizzes] = await Promise.all([ + load('vod'), + load('assign'), + load('quiz'), + ]); + return { vods: vods ?? [], assigns: assigns ?? [], quizzes: quizzes ?? [] }; +} + +export function saveCourseData( + courseId: string, + newVods: Vod[], + newAssigns: Assign[], + newQuizzes: Quiz[], + all: { vods: Vod[]; assigns: Assign[]; quizzes: Quiz[] }, +) { + const exclude = (items: T[]) => + items.filter((item) => item.courseId !== courseId); + + saveDataToStorage('vod', [...exclude(all.vods), ...newVods]); + saveDataToStorage('assign', [...exclude(all.assigns), ...newAssigns]); + saveDataToStorage('quiz', [...exclude(all.quizzes), ...newQuizzes]); +} + +// ── Temp course cleanup ───────────────────────────────────────────── + +export async function cleanupExpiredTempCourses() { + const times = (await load(TEMP_COURSES_KEY)) ?? {}; + const now = Date.now(); + const expired = Object.entries(times) + .filter(([, at]) => now - at >= TEMP_COURSE_TTL_MS) + .map(([id]) => id); + + if (expired.length === 0) return; + + const expiredSet = new Set(expired); + const all = await loadAllData(); + + const notExpired = (items: T[]) => + items.filter((item) => !expiredSet.has(item.courseId)); + + saveDataToStorage('vod', notExpired(all.vods)); + saveDataToStorage('assign', notExpired(all.assigns)); + saveDataToStorage('quiz', notExpired(all.quizzes)); + + for (const id of expired) delete times[id]; + saveDataToStorage(TEMP_COURSES_KEY, times); +} + +export async function registerTempCourse(courseId: string) { + const times = (await load(TEMP_COURSES_KEY)) ?? {}; + times[courseId] = Date.now(); + saveDataToStorage(TEMP_COURSES_KEY, times); +} + +// ── Fetch cache (sessionStorage) ──────────────────────────────────── + +function isBackForwardNavigation(): boolean { + const [entry] = performance.getEntriesByType('navigation') as PerformanceNavigationTiming[]; + return entry?.type === 'back_forward'; +} + +export function isFetchCacheValid(courseId: string): boolean { + if (!isBackForwardNavigation()) return false; + const raw = sessionStorage.getItem(`dotbugi_fetch_${courseId}`); + return !!raw && Date.now() - parseInt(raw, 10) < FETCH_CACHE_TTL_MS; +} + +export function setFetchCache(courseId: string) { + sessionStorage.setItem(`dotbugi_fetch_${courseId}`, Date.now().toString()); +} diff --git a/src/lib/dateUtils.ts b/src/lib/dateUtils.ts new file mode 100644 index 0000000..02d8753 --- /dev/null +++ b/src/lib/dateUtils.ts @@ -0,0 +1,121 @@ +import i18n from '@/i18n'; +import { TimeDifferenceResult } from '@/types'; + +const MS_PER_MINUTE = 1000 * 60; +const MS_PER_HOUR = MS_PER_MINUTE * 60; +const MS_PER_DAY = MS_PER_HOUR * 24; + +const t = (key: string, options?: Record) => i18n.t(key, { ns: 'common', ...options }); + +/** 하이픈 구분 날짜 문자열을 Date로 변환 (Safari 호환) */ +export function parseDate(str: string): Date { + return new Date(str.replace(/-/g, '/')); +} + +export function isCurrentDateInRange(dateRange: string | null) { + if (!dateRange || !dateRange.includes(' ~ ')) return false; + + const [startStr, endStr] = dateRange.split(' ~ '); + if (!startStr || !endStr) return false; + + const startDate = parseDate(startStr); + const endDate = parseDate(endStr); + const now = new Date(); + + return now >= startDate && now <= endDate; +} + +export function isCurrentDateByDate(date: string | null) { + if (!date || date.length <= 1) return false; + return new Date() <= new Date(date); +} + +export function isWithinSevenDays(date: string) { + const diffDays = (new Date(date).getTime() - Date.now()) / MS_PER_DAY; + return diffDays <= 7 && diffDays >= 0; +} + +export function extractEndDate(range: string | null): string | null { + if (!range) return null; + return range.split(' ~ ')[1] ?? null; +} + +export function calculateDueDate(dueDate: string | null): TimeDifferenceResult { + if (!dueDate) { + return { + message: t('date.noInfo'), + status: 'noInfo', + borderColor: 'border-amber-500', + borderLeftColor: 'border-l-amber-500', + textColor: 'text-amber-500', + }; + } + + const now = new Date(); + const endDate = new Date(dueDate); + + if (isNaN(endDate.getTime())) { + return { message: 'Invalid date format', status: 'invalid', borderColor: 'gray', borderLeftColor: 'gray', textColor: 'black' }; + } + + if (now >= endDate) { + return { + message: t('date.expired'), + status: 'expired', + borderColor: 'border-red-950', + borderLeftColor: 'border-l-red-950', + textColor: 'text-red-950', + }; + } + + const timeDiff = endDate.getTime() - now.getTime(); + const days = Math.floor(timeDiff / MS_PER_DAY); + + if (days >= 1) { + return { + message: t('date.daysLater', { days }), + status: 'daysLeft', + borderColor: 'border-amber-500', + borderLeftColor: 'border-l-amber-500', + textColor: 'text-amber-500', + }; + } + + const hours = Math.floor(timeDiff / MS_PER_HOUR); + const minutes = Math.floor(timeDiff / MS_PER_MINUTE); + return { + message: hours !== 0 ? t('date.hoursLater', { hours }) : t('date.minutesLater', { minutes }), + status: 'urgent', + borderColor: 'border-red-700', + borderLeftColor: 'border-l-red-700', + textColor: 'text-red-700', + }; +} + +export function calculateRemainingTime(endTime: string | null) { + if (!endTime) return t('date.noInfo'); + + const timeDiff = new Date(endTime).getTime() - Date.now(); + const daysLeft = Math.floor(timeDiff / MS_PER_DAY); + const hoursLeft = Math.floor((timeDiff % MS_PER_DAY) / MS_PER_HOUR); + const minutesLeft = Math.floor((timeDiff % MS_PER_HOUR) / MS_PER_MINUTE); + + if (daysLeft < 0 || hoursLeft < 0 || minutesLeft < 0) return t('date.expired'); + + if (daysLeft > 0) return t('date.remaining', { days: daysLeft, hours: hoursLeft, minutes: minutesLeft }); + if (hoursLeft > 0) return t('date.remainingHoursMinutes', { hours: hoursLeft, minutes: minutesLeft }); + return t('date.remainingMinutes', { minutes: minutesLeft }); +} + +export function timeAgo(givenTimestamp: number) { + const diffMs = Date.now() - givenTimestamp; + + const days = Math.floor(diffMs / MS_PER_DAY); + const hours = Math.floor((diffMs % MS_PER_DAY) / MS_PER_HOUR); + const minutes = Math.floor((diffMs % MS_PER_HOUR) / MS_PER_MINUTE); + + if (days === 0 && hours === 0 && minutes === 0) return t('date.justNow'); + if (days > 0) return t('date.daysAgo', { days, hours, minutes }); + if (hours > 0) return t('date.hoursAgo', { hours, minutes }); + return t('date.minutesAgo', { minutes }); +} diff --git a/src/lib/deduplicateInto.ts b/src/lib/deduplicateInto.ts new file mode 100644 index 0000000..8678e4a --- /dev/null +++ b/src/lib/deduplicateInto.ts @@ -0,0 +1,17 @@ +/** + * 키 생성 함수 기반으로 중복 제거하면서 배열에 추가 + */ +export function deduplicateInto( + target: T[], + source: T[], + seen: Set, + getKey: (item: T) => string, +): void { + for (const item of source) { + const key = getKey(item); + if (!seen.has(key)) { + seen.add(key); + target.push(item); + } + } +} diff --git a/src/lib/fetchAssign.ts b/src/lib/fetchAssign.ts index d3f902a..7909501 100644 --- a/src/lib/fetchAssign.ts +++ b/src/lib/fetchAssign.ts @@ -1,55 +1,39 @@ -export const fetchAssign = async (link: string) => { - try { - const response = await fetch(link, { - method: 'GET', - credentials: 'include', - }); +import { fetchHtml, getText, getHref } from './fetchHtml'; - if (!response.ok) { - throw new Error('Network response was not ok'); - } +// 과제 테이블 컬럼 (generaltable) +// [주(c0)] [과제(c1)] [종료 일시(c2)] [제출(c3)] [성적(c4)] +const COL = { + WEEK: '.cell.c0', + TITLE_LINK: '.cell.c1 a', + DUE_DATE: '.cell.c2', + SUBMIT_STATUS: '.cell.c3', +} as const; - const html = await response.text(); - const parser = new DOMParser(); - const doc = parser.parseFromString(html, 'text/html'); +import { NOT_SUBMITTED, normalizeLmsDate } from './lmsKeywords'; - const headerMap: Record = {}; - const headers = Array.from(doc.querySelectorAll('table.generaltable thead tr th')); - headers.forEach((header) => { - const text = header.textContent?.trim(); - const className = header.className.match(/c\d+/)?.[0]; - if (text && className) { - if (text.includes('주제')) headerMap['subject'] = '.cell.' + className; - else if (text.includes('과제')) { - headerMap['title'] = '.cell.' + className; - headerMap['url'] = headerMap['title'] + ' a'; - } else if (text.includes('종료 일시')) headerMap['dueDate'] = '.cell.' + className; - else if (text.includes('제출')) headerMap['isSubmit'] = '.cell.' + className; - } - }); +export const fetchAssign = async (link: string) => { + try { + const doc = await fetchHtml(link); + const rows = doc.querySelectorAll('table.generaltable tbody tr'); - let subject: string; - const rows = Array.from(doc.querySelectorAll('table.generaltable tbody tr')); - const assignments = rows - .map((row) => { - const title = - row.querySelector(headerMap.title)?.textContent?.trim() || - row.querySelector(headerMap.assign)?.textContent?.trim() || - null; - const sbj = row.querySelector(headerMap.subject)?.textContent?.trim() || ''; - const url = (row.querySelector(headerMap.url) as HTMLAnchorElement)?.href || null; - const dueDate = row.querySelector(headerMap.dueDate)?.textContent?.trim() || null; - const isSubmit = row.querySelector(headerMap.isSubmit)?.textContent?.trim() === '미제출' ? false : true; + let lastWeekLabel = ''; - if (sbj.length !== 0) subject = sbj; - if (!title || !url || !dueDate) return null; - return { subject, title, url, dueDate, isSubmit }; - }) - .filter((assign) => assign !== null); + return Array.from(rows).flatMap((row) => { + const weekLabel = getText(row, COL.WEEK); + if (weekLabel) lastWeekLabel = weekLabel; - return assignments; + const title = getText(row, COL.TITLE_LINK); + const url = getHref(row, COL.TITLE_LINK); + const rawDueDate = getText(row, COL.DUE_DATE)?.trim(); + if (!title || !url) return []; + const dueDate = rawDueDate && rawDueDate !== '-' ? normalizeLmsDate(rawDueDate) ?? null : null; + + const submitText = getText(row, COL.SUBMIT_STATUS) ?? ''; + const isSubmit = !NOT_SUBMITTED.some((keyword) => submitText.includes(keyword)); + return { subject: lastWeekLabel, title, url, dueDate, isSubmit }; + }); } catch (error) { - console.error('Fetch error:', error); + console.error('[Dotbugi] 과제 조회 오류:', error); throw error; } }; diff --git a/src/lib/fetchCourseData.ts b/src/lib/fetchCourseData.ts index aa1e1b8..34490d7 100644 --- a/src/lib/fetchCourseData.ts +++ b/src/lib/fetchCourseData.ts @@ -1,27 +1,72 @@ +import { VodAttendanceData } from '@/types'; import { fetchVodAttendance } from './fetchVodAttendance'; -import { fetchIndexPage } from './fetchIndexPage'; -import { getAssignPageLink, getIndexPageLink, getQuizPageLink, getVodPageLink } from '@/constants/constant'; +import { fetchVodProgress } from './fetchVodProgress'; +import { fetchVodList } from './fetchVodList'; +import { getAssignPageLink, getVodProgressPageLink, getIndexPageLink, getQuizPageLink, getVodPageLink } from '@/constants/links'; import { fetchAssign } from './fetchAssign'; import { fetchQuiz } from './fetchQuiz'; +import { Quiz } from '@/types'; -export const requestData = async (id: string) => { - const VOD_LINK = getVodPageLink(id); - const INDEX_LINK = getIndexPageLink(id); - const ASSIGN_LINK = getAssignPageLink(id); - const QUIZ_LINK = getQuizPageLink(id); +/** + * 일반 강좌: user_progress_a.php 출석 데이터에 user_progress.php 시간 데이터를 병합 + * (일괄출석인정 등 기존 출석 판정 유지 + 시청중 배지용 시간 데이터 추가) + */ +function mergeTimeData( + attendance: VodAttendanceData[], + progress: VodAttendanceData[], +): VodAttendanceData[] { + const progressByKey = new Map(); + for (const p of progress) { + progressByKey.set(`${p.title}-${p.week}`, p); + } + + return attendance.map((att) => { + const prog = progressByKey.get(`${att.title}-${att.week}`); + if (!prog) return att; + return { + ...att, + requiredTime: prog.requiredTime, + watchedTime: prog.watchedTime, + }; + }); +} + +export const scrapeCourseData = async ( + courseId: string, + cachedQuizzes?: Quiz[], + isCommunity?: boolean, +) => { + // 캐시된 퀴즈의 제출 상태 맵 생성 (url → isSubmit) + const cachedSubmitMap = cachedQuizzes + ? new Map(cachedQuizzes.map((q) => [q.url, q.isSubmit])) + : undefined; try { - const [vodAttendanceArray, vodDataArray, assignDataArray, quizDataArray] = await Promise.all([ - fetchVodAttendance(VOD_LINK), - fetchIndexPage(INDEX_LINK), - fetchAssign(ASSIGN_LINK), - fetchQuiz(QUIZ_LINK), + // 커뮤니티: user_progress.php만 사용 + // 일반: user_progress_a.php(출석) + user_progress.php(시간 데이터) 병합 + const progressLink = getVodProgressPageLink(courseId); + + const [vodAttendanceRaw, vodProgress, vodList, assignList, quizList] = await Promise.all([ + isCommunity ? Promise.resolve(null) : fetchVodAttendance(getVodPageLink(courseId)), + fetchVodProgress(progressLink), + fetchVodList(getIndexPageLink(courseId)), + fetchAssign(getAssignPageLink(courseId)), + fetchQuiz(getQuizPageLink(courseId), cachedSubmitMap), ]); - console.info('[Dotbugi]', vodAttendanceArray, vodDataArray, assignDataArray, quizDataArray); - return { vodAttendanceArray, vodDataArray, assignDataArray, quizDataArray }; + const vodAttendance = isCommunity || !vodAttendanceRaw + ? vodProgress + : mergeTimeData(vodAttendanceRaw, vodProgress); + + console.info('[Dotbugi]', vodAttendance, vodList, assignList, quizList); + return { + vodAttendanceArray: vodAttendance, + vodDataArray: vodList, + assignDataArray: assignList, + quizDataArray: quizList, + }; } catch (error) { - console.error('Error while fetching data:', error); + console.error('[Dotbugi] 강의 데이터 스크래핑 오류:', error); throw error; } }; diff --git a/src/lib/fetchHtml.ts b/src/lib/fetchHtml.ts new file mode 100644 index 0000000..c4e7558 --- /dev/null +++ b/src/lib/fetchHtml.ts @@ -0,0 +1,26 @@ +/** + * LMS 페이지를 fetch하고 DOM으로 파싱하여 반환 + */ +export async function fetchHtml(link: string): Promise { + const response = await fetch(link, { + method: 'GET', + credentials: 'include', + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${link}`); + } + + const html = await response.text(); + return new DOMParser().parseFromString(html, 'text/html'); +} + +/** 셀렉터로 요소의 텍스트 추출. 없으면 null */ +export function getText(parent: Element, selector: string): string | null { + return parent.querySelector(selector)?.textContent?.trim() || null; +} + +/** 셀렉터로 a 태그의 href 추출. 없으면 null */ +export function getHref(parent: Element, selector: string): string | null { + return parent.querySelector(selector)?.href || null; +} diff --git a/src/lib/fetchIndexPage.ts b/src/lib/fetchIndexPage.ts deleted file mode 100644 index 63a039b..0000000 --- a/src/lib/fetchIndexPage.ts +++ /dev/null @@ -1,49 +0,0 @@ -export const fetchIndexPage = async (link: string) => { - try { - const response = await fetch(link, { - method: 'GET', - credentials: 'include', - }); - - if (!response.ok) { - throw new Error('Network response was not ok'); - } - - const html = await response.text(); - const parser = new DOMParser(); - const doc = parser.parseFromString(html, 'text/html'); - - const weeks = Array.from(doc.querySelectorAll('#region-main > div > div > div.total_sections > div > ul > li')); - - const vods = weeks - .map((week, index) => { - const subject = week.querySelector('.content .sectionname')?.textContent?.trim() || index + '주차'; - const contents = Array.from(week.querySelectorAll('.content .vod .activityinstance')); - const vodsWeekly = contents - .filter((item) => !item.closest('.dimmed')) - .map((item) => { - const week = index + 1; - const instancename = item.querySelector('.instancename'); - instancename?.querySelector('.accesshide')?.remove(); - const title = instancename?.textContent?.trim() || null; - const url = item.querySelector('a')?.getAttribute('href') || null; - const range = item.querySelector('.text-ubstrap')?.textContent?.trim() || ''; - const length = item.querySelector('.text-info')?.textContent?.replace(',', '').trim() || ''; - - if (!title || !url || !range) return null; - return { week, subject, title, url, range, length }; - }) - .filter((item) => item !== null); - - if (!vodsWeekly || vodsWeekly.length === 0) return null; - return vodsWeekly; - }) - .filter((week) => week !== null) - .flat(); - - return vods; - } catch (error) { - console.error('Fetch error:', error); - throw error; - } -}; diff --git a/src/lib/fetchQuiz.ts b/src/lib/fetchQuiz.ts index e5107ce..65feaea 100644 --- a/src/lib/fetchQuiz.ts +++ b/src/lib/fetchQuiz.ts @@ -1,58 +1,96 @@ -export const fetchQuiz = async (link: string) => { +import { fetchHtml, getText } from './fetchHtml'; +import { BASE_LINK } from '@/constants/links'; +import { normalizeLmsDate } from './lmsKeywords'; +import { isCurrentDateByDate } from './dateUtils'; + +// 퀴즈 테이블 컬럼 (generaltable) +// [주(c0)] [제목(c1)] [종료 일시(c2)] [성적(c3)] +const COL = { + WEEK: '.cell.c0', + TITLE_LINK: '.cell.c1 a', + DUE_DATE: '.cell.c2', +} as const; + +/** + * 상대/절대 경로를 퀴즈 모듈 절대 URL로 변환 + * view.php?id=123 → https://learn.hansung.ac.kr/mod/quiz/view.php?id=123 + */ +function toQuizUrl(rawHref: string): string { + if (rawHref.startsWith('http')) return rawHref; + return `${BASE_LINK}/mod/quiz/${rawHref}`; +} + +/** + * 퀴즈 상세 페이지에서 제출 여부를 확인 + * quizattemptsummary 테이블에 행이 있으면 제출된 것으로 판단 + */ +async function fetchQuizSubmitStatus(quizUrl: string): Promise { try { - const response = await fetch(link, { - method: 'GET', - credentials: 'include', - }); - - if (!response.ok) { - throw new Error('Network response was not ok'); - } - - const html = await response.text(); - const parser = new DOMParser(); - const doc = parser.parseFromString(html, 'text/html'); - - const headerMap: Record = {}; - const headers = Array.from(doc.querySelectorAll('table.generaltable thead tr th')); - headers.forEach((header) => { - const text = header.textContent?.trim(); - const className = header.className.match(/c\d+/)?.[0]; - if (text && className) { - if (text.includes('주제')) headerMap['subject'] = '.cell.' + className; - else if (text.includes('제목')) { - headerMap['title'] = '.cell.' + className; - headerMap['url'] = headerMap['title'] + ' a'; - } else if (text.includes('종료 일시')) headerMap['dueDate'] = '.cell.' + className; - } - }); - - let subject: string; - const rows = Array.from(doc.querySelectorAll('table.generaltable tbody tr')); - const quizzes = rows - .map((row) => { - const sbj = row.querySelector(headerMap.subject)?.textContent?.trim() || ''; - const title = row.querySelector(headerMap.title)?.textContent?.trim() || null; - let url = (row.querySelector(headerMap.url) as HTMLAnchorElement)?.href || null; - const dueDate = row.querySelector(headerMap.dueDate)?.textContent?.trim() || null; - - if (sbj.length !== 0) subject = sbj; - - if (url) { - const index = url.indexOf('view'); - url = url.slice(0, index) + 'mod/quiz/' + url.slice(index); - } + const doc = await fetchHtml(quizUrl); + const rows = doc.querySelectorAll('table.quizattemptsummary tbody tr'); + return rows.length > 0; + } catch { + return false; + } +} + +type QuizItem = { + title: string; + subject: string; + url: string; + dueDate: string | null; + isSubmit: boolean; +}; + +/** + * 퀴즈 목록 + 제출 여부 조회 + * @param cachedSubmitMap 이전에 저장된 퀴즈별 제출 상태 (key: url) + * 마감된 퀴즈는 상세 페이지를 다시 fetch하지 않고 캐시된 값을 사용 + */ +export const fetchQuiz = async ( + link: string, + cachedSubmitMap?: Map, +) => { + try { + const doc = await fetchHtml(link); + const rows = doc.querySelectorAll('table.generaltable tbody tr'); + + let lastWeekLabel = ''; + + const quizItems: Omit[] = Array.from(rows) + .flatMap((row) => { + const weekLabel = getText(row, COL.WEEK); + if (weekLabel) lastWeekLabel = weekLabel; + + const titleLink = row.querySelector(COL.TITLE_LINK); + const rawDueDate = getText(row, COL.DUE_DATE)?.trim(); + if (!titleLink) return []; + const dueDate = rawDueDate && rawDueDate !== '-' ? normalizeLmsDate(rawDueDate) ?? null : null; - if (title && url && dueDate) { - return { title, subject, url, dueDate }; + const title = titleLink.textContent?.trim(); + const rawHref = titleLink.getAttribute('href'); + if (!title || !rawHref) return []; + + return { title, subject: lastWeekLabel, url: toQuizUrl(rawHref), dueDate }; + }); + + // 마감 전 퀴즈만 상세 페이지 fetch, 마감된 퀴즈는 캐시 사용 + const results: QuizItem[] = await Promise.all( + quizItems.map(async (item) => { + const isExpired = item.dueDate ? !isCurrentDateByDate(item.dueDate) : false; + + if (isExpired && cachedSubmitMap?.has(item.url)) { + return { ...item, isSubmit: cachedSubmitMap.get(item.url)! }; } - return null; - }) - .filter((quiz) => quiz !== null); - return quizzes; + const isSubmit = await fetchQuizSubmitStatus(item.url); + return { ...item, isSubmit }; + }), + ); + + return results; } catch (error) { - console.error('Fetch error:', error); + console.error('[Dotbugi] 퀴즈 조회 오류:', error); throw error; } }; diff --git a/src/lib/fetchVodAttendance.ts b/src/lib/fetchVodAttendance.ts index 601bdbb..2f45c0b 100644 --- a/src/lib/fetchVodAttendance.ts +++ b/src/lib/fetchVodAttendance.ts @@ -1,129 +1,114 @@ -import { VodAttendanceData } from '@/content/types'; +import { VodAttendanceData } from '@/types'; +import { fetchHtml } from './fetchHtml'; + +// 출석부 테이블 컬럼 인덱스 (user_progress_table) +// [주차(0)] [강의 자료(1)] [출석인정 요구시간(2)] [총 학습시간(3)] [출석(4)] [주차 출석(5)] +const COL = { + WEEK: 0, + TITLE: 1, + ATTENDANCE: 4, + WEEKLY_ATTENDANCE: 5, +} as const; + +import { BULK_APPROVED } from './lmsKeywords'; /** - * 온라인 출석부 정보 가져오기 - * - * @param link 강의 링크 - * @returns [{온라인 강의 제목, 출석 여부, 주차정보}...] + * rowspan/colspan이 있는 테이블 행을 평탄화된 셀 값 배열로 변환 */ -export const fetchVodAttendance = async (link: string) => { - try { - const response = await fetch(link, { - method: 'GET', - credentials: 'include', - }); - - if (!response.ok) { - throw new Error('Network response was not ok'); +function flattenRow( + row: Element, + pendingSpans: (number | undefined)[], + spanValues: (string | null)[], +): (string | null)[] { + const cells = Array.from(row.querySelectorAll('td')); + const rowData: (string | null)[] = []; + let col = 0; + + const consumePendingSpans = () => { + while (pendingSpans[col] && pendingSpans[col]! > 0) { + rowData.push(spanValues[col] || null); + pendingSpans[col]! -= 1; + col++; } + }; - const html = await response.text(); - const parser = new DOMParser(); - const doc = parser.parseFromString(html, 'text/html'); + consumePendingSpans(); - const headerMap: Record = {}; + for (const cell of cells) { + consumePendingSpans(); - const headers = Array.from(doc.querySelectorAll('.user_progress_table > thead > tr > th')); + const rowspan = parseInt(cell.getAttribute('rowspan') || '1', 10); + const colspan = parseInt(cell.getAttribute('colspan') || '1', 10); + const content = cell.textContent?.trim() || ''; - headers.forEach((header, index) => { - const text = header.textContent?.trim() || ''; - if (text === '강의 자료') headerMap['title'] = index; - else if (text === '출석') headerMap['isAttendance'] = index; - else if (text === '주차 출석') headerMap['weeklyAttendance'] = index; - }); + for (let i = 0; i < colspan; i++) { + rowData.push(content); + if (rowspan > 1) { + pendingSpans[col] = rowspan - 1; + spanValues[col] = content; + } else { + spanValues[col] = null; + } + col++; + } + } + + while (col < pendingSpans.length) { + if (pendingSpans[col] && pendingSpans[col]! > 0) { + rowData.push(spanValues[col] || null); + pendingSpans[col]! -= 1; + } else { + rowData.push(null); + } + col++; + } - const rows = Array.from(doc.querySelectorAll('.user_progress_table > tbody > tr')); + return rowData; +} - const spanTable: (number | undefined)[] = []; - const cellValues: (string | null)[] = []; - const vods: VodAttendanceData[] = []; - let idx = 0; +/** + * 온라인 출석부 정보 가져오기 + */ +export const fetchVodAttendance = async (link: string) => { + try { + const doc = await fetchHtml(link); + const rows = doc.querySelectorAll('.user_progress_table > tbody > tr'); + + const pendingSpans: (number | undefined)[] = []; + const spanValues: (string | null)[] = []; + const results: VodAttendanceData[] = []; + let currentWeek = 0; let lastWeeklyAttendance = ''; + rows.forEach((row) => { try { - const cells = Array.from(row.querySelectorAll('td')); - let colIndex = 0; - const rowData: (string | null)[] = []; - - while (spanTable[colIndex] && spanTable[colIndex]! > 0) { - rowData.push(cellValues[colIndex] || null); - spanTable[colIndex]! -= 1; - colIndex++; - } + const cells = flattenRow(row, pendingSpans, spanValues); - cells.forEach((cell) => { - while (spanTable[colIndex] && spanTable[colIndex]! > 0) { - rowData.push(cellValues[colIndex] || null); - spanTable[colIndex]! -= 1; - colIndex++; - } - - const rowspan = parseInt(cell.getAttribute('rowspan') || '1', 10); - const colspan = parseInt(cell.getAttribute('colspan') || '1', 10); - const cellContent = cell.textContent?.trim() || ''; - - for (let i = 0; i < colspan; i++) { - rowData.push(cellContent); - - if (rowspan > 1) { - spanTable[colIndex] = rowspan - 1; - cellValues[colIndex] = cellContent; - } else { - cellValues[colIndex] = null; - } - colIndex++; - } - }); - - while (colIndex < spanTable.length) { - if (spanTable[colIndex] && spanTable[colIndex]! > 0) { - rowData.push(cellValues[colIndex] || null); - spanTable[colIndex]! -= 1; - } else { - rowData.push(null); - } - colIndex++; - } + const title = cells[COL.TITLE] || ''; + const isAttendance = cells[COL.ATTENDANCE] || ''; - let weeklyAttendance = - headerMap['weeklyAttendance'] !== undefined ? rowData[headerMap['weeklyAttendance']] || '' : ''; + let weeklyAttendance = cells[COL.WEEKLY_ATTENDANCE] || ''; if (weeklyAttendance) { lastWeeklyAttendance = weeklyAttendance; } else { weeklyAttendance = lastWeeklyAttendance; } + if (BULK_APPROVED.some((keyword) => weeklyAttendance.includes(keyword))) weeklyAttendance = 'o'; - if (weeklyAttendance.includes('일괄출석인정')) weeklyAttendance = 'o'; - - const title = headerMap['title'] !== undefined ? rowData[headerMap['title']] || '' : ''; - const isAttendance = headerMap['isAttendance'] !== undefined ? rowData[headerMap['isAttendance']] || '' : ''; + const parsedWeek = parseInt(cells[COL.WEEK] || ''); + if (!isNaN(parsedWeek)) currentWeek = parsedWeek; - let weekStr = rowData[0] || ''; - if (weekStr !== '' && !isNaN(parseInt(weekStr))) { - idx = parseInt(weekStr); - } else { - weekStr = idx.toString(); - } + if (!title || !isAttendance) return; - if (!title || !isAttendance) { - return; - } - const week = parseInt(weekStr); - - vods.push({ - title, - isAttendance, - weeklyAttendance, - week, - }); + results.push({ title, isAttendance, weeklyAttendance, week: currentWeek }); } catch (error) { - console.error(`[Dotbugi] 영상 강의 조회 오류: ${link} ${row}`, error); + console.error(`[Dotbugi] 출석부 행 파싱 오류: ${link}`, error); } }); - return vods; + return results; } catch (error) { - console.error('Fetch error:', error); + console.error('[Dotbugi] 출석부 조회 오류:', error); throw error; } }; diff --git a/src/lib/fetchVodList.ts b/src/lib/fetchVodList.ts new file mode 100644 index 0000000..1176f7b --- /dev/null +++ b/src/lib/fetchVodList.ts @@ -0,0 +1,42 @@ +import { fetchHtml, getText, getHref } from './fetchHtml'; +import { normalizeLmsRange } from './lmsKeywords'; + +function parseWeekNumber(section: Element): number { + const match = section.id?.match(/section-(\d+)/); + return match ? parseInt(match[1], 10) : 0; +} + +function parseVodTitle(activity: Element): string | undefined { + const nameEl = activity.querySelector('.instancename'); + if (!nameEl) return; + const clone = nameEl.cloneNode(true) as Element; + clone.querySelector('.accesshide')?.remove(); + return clone.textContent?.trim(); +} + +export const fetchVodList = async (link: string) => { + try { + const doc = await fetchHtml(link); + const sections = doc.querySelectorAll('li[id^="section-"]'); + + return Array.from(sections).flatMap((section) => { + const week = parseWeekNumber(section); + const subject = getText(section, '.sectionname') || `${week}`; + const vodActivities = section.querySelectorAll('li.modtype_vod:not(.dimmed) .activityinstance'); + + return Array.from(vodActivities).flatMap((activity) => { + const title = parseVodTitle(activity); + const url = getHref(activity, 'a'); + const rawRange = getText(activity, '.text-ubstrap'); + if (!title || !url || !rawRange) return []; + const range = normalizeLmsRange(rawRange)!; + + const length = getText(activity, '.text-info')?.replace(',', '') ?? ''; + return { week, subject, title, url, range, length }; + }); + }); + } catch (error) { + console.error('[Dotbugi] VOD 목록 조회 오류:', error); + throw error; + } +}; diff --git a/src/lib/fetchVodProgress.ts b/src/lib/fetchVodProgress.ts new file mode 100644 index 0000000..4d08d6a --- /dev/null +++ b/src/lib/fetchVodProgress.ts @@ -0,0 +1,117 @@ +import { VodAttendanceData } from '@/types'; +import { fetchHtml } from './fetchHtml'; + +function parseTimeToSeconds(time: string): number { + const parts = time.split(':').map(Number); + if (parts.length === 3) return parts[0] * 3600 + parts[1] * 60 + parts[2]; + if (parts.length === 2) return parts[0] * 60 + parts[1]; + return 0; +} + +/** + * VOD 학습 진도 정보 가져오기 (user_progress.php) + * + * `.user_progress` 테이블에서 출석인정 요구시간과 총 학습시간을 추출. + * - 일반 강좌: 시청중 배지용 시간 데이터 제공 (출석 판정은 user_progress_a.php 사용) + * - 커뮤니티 강좌: 출석 판정 + 시간 데이터 모두 담당 (user_progress_a.php 접근 불가) + */ +export const fetchVodProgress = async (link: string) => { + try { + const doc = await fetchHtml(link); + const rows = doc.querySelectorAll('.user_progress > tbody > tr'); + + const results: VodAttendanceData[] = []; + let currentWeek = 0; + + rows.forEach((row) => { + try { + const cells = Array.from(row.querySelectorAll('td')); + if (cells.length === 0) return; + + // 주차 파싱: sectiontitle이 있는 셀 (rowspan이 있을 수 있음) + const sectionTitle = row.querySelector('.sectiontitle'); + if (sectionTitle) { + const parsedWeek = parseInt(sectionTitle.textContent?.trim() || '', 10); + if (!isNaN(parsedWeek)) currentWeek = parsedWeek; + } + + // VOD 아이콘이 있는 행만 처리 (빈 주차 건너뛰기) + const titleCell = cells.find((cell) => cell.querySelector('img[src*="vod"]')); + if (!titleCell) return; + + // 제목 추출: 이미지 태그 뒤의 텍스트 + const title = titleCell.textContent?.trim() || ''; + if (!title) return; + + // 시간 데이터 추출: 출석인정 요구시간, 총 학습시간 + const timeCells = cells.filter((cell) => { + const text = cell.textContent?.trim() || ''; + return /^\d{1,2}:\d{2}(:\d{2})?$/.test(text); + }); + + // 상세보기 버튼에서 추가 정보 추출 + const trackBtn = row.querySelector('.track_detail'); + + let requiredTime = ''; + let watchedTime = ''; + + if (timeCells.length >= 1) { + requiredTime = timeCells[0].textContent?.trim() || ''; + } + + // 총 학습시간은 상세보기 버튼이 있는 셀에서 추출 (시간 텍스트 + 버튼이 공존) + if (trackBtn) { + const parentCell = trackBtn.closest('td'); + if (parentCell) { + const textNodes = Array.from(parentCell.childNodes) + .filter((node) => node.nodeType === 3) + .map((node) => node.textContent?.trim() || '') + .filter(Boolean); + const timeText = textNodes.find((t) => /^\d{1,2}:\d{2}(:\d{2})?$/.test(t)); + if (timeText) watchedTime = timeText; + } + } else if (timeCells.length >= 2) { + watchedTime = timeCells[1].textContent?.trim() || ''; + } + + // 출석 판단: 총 학습시간 >= 출석인정 요구시간 + const isAttendance = + requiredTime && watchedTime && parseTimeToSeconds(watchedTime) >= parseTimeToSeconds(requiredTime) + ? 'o' + : 'x'; + + results.push({ + title, + isAttendance, + weeklyAttendance: '', // 후처리에서 계산 + week: currentWeek, + requiredTime, + watchedTime, + }); + } catch (error) { + console.error(`[Dotbugi] 진도 행 파싱 오류: ${link}`, error); + } + }); + + // 주차별 출석 계산: 해당 주차의 모든 VOD가 출석이면 'o' + const weekGroups = new Map(); + for (const item of results) { + const group = weekGroups.get(item.week) ?? []; + group.push(item); + weekGroups.set(item.week, group); + } + + for (const [, group] of weekGroups) { + const allAttended = group.every((item) => item.isAttendance === 'o'); + const weeklyAttendance = allAttended ? 'o' : 'x'; + for (const item of group) { + item.weeklyAttendance = weeklyAttendance; + } + } + + return results; + } catch (error) { + console.error('[Dotbugi] VOD 진도 조회 오류:', error); + throw error; + } +}; diff --git a/src/lib/filterData.tsx b/src/lib/filterData.tsx index 0c789e3..962797b 100644 --- a/src/lib/filterData.tsx +++ b/src/lib/filterData.tsx @@ -1,34 +1,39 @@ -import { Vod, Assign, Quiz, Filters } from '@/content/types'; +import { Vod, Assign, Quiz, CourseBase, Filters } from '@/types'; +import { isAttended } from './utils'; +import i18n from '@/i18n'; + +function matchesBase(item: CourseBase & { title: string }, courseTitles: string[], term: string): boolean { + if (courseTitles.length > 0 && !courseTitles.includes(item.courseTitle)) return false; + if ( + term && + !item.courseTitle.toLowerCase().includes(term) && + !item.title.toLowerCase().includes(term) && + !item.prof.toLowerCase().includes(term) + ) + return false; + return true; +} // 필터 적용 for VODs export function filterVods(vods: Vod[], filters: Filters, searchTerm: string, sortBy: keyof Vod): Vod[] { - let data = vods; - const { courseTitles, attendanceStatuses } = filters; + const term = searchTerm.toLowerCase(); - if (courseTitles.length > 0) { - data = data.filter((vod) => courseTitles.includes(vod.courseTitle)); - } - - if (attendanceStatuses && attendanceStatuses.length > 0) { - data = data.filter((vod) => { - const status = vod.isAttendance.toLowerCase().trim() === 'o' ? '출석' : '결석'; - return attendanceStatuses.includes(status); - }); - } + const attendedLabel = i18n.t('attendance.attended', { ns: 'common' }); + const absentLabel = i18n.t('attendance.absent', { ns: 'common' }); - if (searchTerm !== '') { - data = data.filter( - (item) => - item.courseTitle.toLowerCase().includes(searchTerm.toLowerCase()) || - item.title.toLowerCase().includes(searchTerm.toLowerCase()) || - item.prof.toLowerCase().includes(searchTerm.toLowerCase()) - ); - } + const data = vods.filter((vod) => { + if (!matchesBase(vod, courseTitles, term)) return false; + if (attendanceStatuses && attendanceStatuses.length > 0) { + const status = isAttended(vod.isAttendance) ? attendedLabel : absentLabel; + if (!attendanceStatuses.includes(status)) return false; + } + return true; + }); return data.sort((a, b) => { - const attendanceA = a.isAttendance.toLowerCase().trim() === 'o'; - const attendanceB = b.isAttendance.toLowerCase().trim() === 'o'; + const attendanceA = isAttended(a.isAttendance); + const attendanceB = isAttended(b.isAttendance); if (attendanceA !== attendanceB) { return attendanceA ? -1 : 1; @@ -48,26 +53,14 @@ export function filterVods(vods: Vod[], filters: Filters, searchTerm: string, so // 필터 적용 for Assigns export function filterAssigns(assigns: Assign[], filters: Filters, searchTerm: string, sortBy: keyof Assign): Assign[] { - let data = assigns; - const { courseTitles, submitStatuses } = filters; + const term = searchTerm.toLowerCase(); - if (courseTitles.length > 0) { - data = data.filter((assign) => courseTitles.includes(assign.courseTitle)); - } - - if (submitStatuses && submitStatuses.length > 0) { - data = data.filter((assign) => submitStatuses.includes(assign.isSubmit)); - } - - if (searchTerm !== '') { - data = data.filter( - (item) => - item.courseTitle.toLowerCase().includes(searchTerm.toLowerCase()) || - item.title.toLowerCase().includes(searchTerm.toLowerCase()) || - item.prof.toLowerCase().includes(searchTerm.toLowerCase()) - ); - } + const data = assigns.filter((assign) => { + if (!matchesBase(assign, courseTitles, term)) return false; + if (submitStatuses && submitStatuses.length > 0 && !submitStatuses.includes(assign.isSubmit)) return false; + return true; + }); return data.sort((a, b) => { // 미제출 우선 배치 @@ -87,34 +80,31 @@ export function filterAssigns(assigns: Assign[], filters: Filters, searchTerm: s }); } -// 필터 적용 for Quizes -export function filterQuizes(quizes: Quiz[], filters: Filters, searchTerm: string, sortBy: keyof Quiz): Quiz[] { - let data = quizes; - - const { courseTitles } = filters; - - if (courseTitles.length > 0) { - data = data.filter((quiz) => courseTitles.includes(quiz.courseTitle)); - } +// 필터 적용 for Quizzes +export function filterQuizzes(quizzes: Quiz[], filters: Filters, searchTerm: string, sortBy: keyof Quiz): Quiz[] { + const { courseTitles, submitStatuses } = filters; + const term = searchTerm.toLowerCase(); - if (searchTerm !== '') { - data = data.filter( - (item) => - item.courseTitle.toLowerCase().includes(searchTerm.toLowerCase()) || - item.title.toLowerCase().includes(searchTerm.toLowerCase()) || - item.prof.toLowerCase().includes(searchTerm.toLowerCase()) - ); - } + const data = quizzes.filter((quiz) => { + if (!matchesBase(quiz, courseTitles, term)) return false; + if (submitStatuses && submitStatuses.length > 0 && !submitStatuses.includes(quiz.isSubmit)) return false; + return true; + }); return data.sort((a, b) => { + // 미제출 우선 배치 + if (!a.isSubmit && b.isSubmit) return -1; + if (a.isSubmit && !b.isSubmit) return 1; + switch (sortBy) { case 'title': return a.title.localeCompare(b.title); - default: - if (a.dueDate === null && b.dueDate !== null) return 1; - if (a.dueDate !== null && b.dueDate === null) return -1; - if (a.dueDate === null && b.dueDate === null) return 0; - return (a.dueDate ?? '').localeCompare(b.dueDate ?? ''); + default: { + const dateA = a.dueDate === null ? Number.MAX_SAFE_INTEGER : new Date(a.dueDate).getTime(); + const dateB = b.dueDate === null ? Number.MAX_SAFE_INTEGER : new Date(b.dueDate).getTime(); + if (dateA !== dateB) return dateA - dateB; + return 0; + } } }); } diff --git a/src/lib/generateKey.ts b/src/lib/generateKey.ts new file mode 100644 index 0000000..e10b26e --- /dev/null +++ b/src/lib/generateKey.ts @@ -0,0 +1,4 @@ +export const makeVodKey = (courseId: string, title: string, week: number) => `${courseId}-${title}-${week}`; +export const makeItemKey = (courseId: string, title: string, dueDate: string) => `${courseId}-${title}-${dueDate}`; +export const makeVodGroupKey = (courseId: string, subject: string, range: string | null) => + `${courseId}-${subject}-${range}`; diff --git a/src/lib/injectCourseToggles.ts b/src/lib/injectCourseToggles.ts new file mode 100644 index 0000000..1b4a065 --- /dev/null +++ b/src/lib/injectCourseToggles.ts @@ -0,0 +1,107 @@ +import { loadDataFromStorage, saveDataToStorage } from '@/lib/storage'; +import { parseCoursesFromDOM } from '@/lib/parseCourses'; + +const TOGGLE_ATTR = 'data-dotbugi-toggle'; + +export function injectCourseToggles() { + const allCourses = parseCoursesFromDOM(); + if (allCourses.length === 0) return; + + // 커뮤니티 강좌 ID 저장 (강의 페이지 배지 주입 시 참조) + const communityIds = allCourses.filter((c) => c.isCommunity).map((c) => c.courseId); + saveDataToStorage('communityIds', communityIds); + + loadDataFromStorage('trackedCourseIds', (savedIds) => { + const trackedIds = savedIds ?? allCourses.filter((c) => !c.isCommunity).map((c) => c.courseId); + + if (!savedIds) { + saveDataToStorage('trackedCourseIds', trackedIds); + } + + const trackedSet = new Set(trackedIds); + const listItems = document.querySelectorAll('.my-course-lists > li'); + + listItems.forEach((li) => { + if (li.querySelector(`[${TOGGLE_ATTR}]`)) return; + + const link = li.querySelector('a.course_link') as HTMLAnchorElement | null; + if (!link) return; + + const courseId = new URL(link.href).searchParams.get('id'); + if (!courseId) return; + + const isTracked = trackedSet.has(courseId); + const toggle = createToggleElement(isTracked); + toggle.setAttribute(TOGGLE_ATTR, courseId); + + toggle.addEventListener('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + handleToggle(toggle, courseId); + }); + + const courseBox = li.querySelector('.course_box'); + if (courseBox) { + (courseBox as HTMLElement).style.position = 'relative'; + courseBox.appendChild(toggle); + } + }); + }); +} + +function createToggleElement(isTracked: boolean): HTMLElement { + const btn = document.createElement('div'); + btn.style.cssText = ` + position: absolute; + top: 8px; + right: 8px; + width: 28px; + height: 28px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + z-index: 10; + transition: all 0.2s ease; + background: ${isTracked ? '#3b82f6' : 'rgba(0, 0, 0, 0.4)'}; + border: 2px solid ${isTracked ? '#3b82f6' : 'rgba(255, 255, 255, 0.6)'}; + box-shadow: 0 2px 4px rgba(0,0,0,0.2); + `; + + btn.innerHTML = getCheckSvg(isTracked); + btn.title = isTracked ? 'Tracking' : 'Not tracking'; + return btn; +} + +function getCheckSvg(isTracked: boolean): string { + if (isTracked) { + return ``; + } + return ``; +} + +function updateToggleVisual(toggle: HTMLElement, isTracked: boolean) { + toggle.style.background = isTracked ? '#3b82f6' : 'rgba(0, 0, 0, 0.4)'; + toggle.style.borderColor = isTracked ? '#3b82f6' : 'rgba(255, 255, 255, 0.6)'; + toggle.innerHTML = getCheckSvg(isTracked); + toggle.title = isTracked ? 'Tracking' : 'Not tracking'; +} + +function handleToggle(toggle: HTMLElement, courseId: string) { + loadDataFromStorage('trackedCourseIds', (currentIds) => { + if (!currentIds) return; + + const isCurrentlyTracked = currentIds.includes(courseId); + let newIds: string[]; + + if (isCurrentlyTracked) { + newIds = currentIds.filter((id) => id !== courseId); + } else { + newIds = [...currentIds, courseId]; + } + + saveDataToStorage('trackedCourseIds', newIds); + updateToggleVisual(toggle, !isCurrentlyTracked); + }); +} diff --git a/src/lib/lmsKeywords.ts b/src/lib/lmsKeywords.ts new file mode 100644 index 0000000..9d9a575 --- /dev/null +++ b/src/lib/lmsKeywords.ts @@ -0,0 +1,61 @@ +/** + * LMS HTML 파싱에 사용되는 키워드 목록 + * + * UI 언어 설정과 무관하게, LMS 자체 언어 설정에 따라 HTML에 표시되는 문자열이 달라집니다. + * 각 키워드 배열에 LMS가 지원하는 모든 언어의 문자열을 추가하면 어떤 언어 설정이든 파싱이 동작합니다. + * + * 새 언어를 추가하려면 배열에 해당 언어의 문자열을 추가하면 됩니다. + */ + +/** 과제 미제출 상태 텍스트 */ +export const NOT_SUBMITTED = [ + '미제출', // ko + 'Not submitted', // en + '提出なし', // 'ja', + '没有作业', // 'zh', +]; + +/** 일괄출석인정 텍스트 (주차 출석 컬럼에 포함되면 출석 처리) */ +export const BULK_APPROVED = [ + '일괄출석인정', // ko + 'Batch attendance', // en + // 'ja', + // 'zh', +]; + +/** + * LMS 날짜 문자열을 JS Date가 파싱 가능한 형식으로 정규화 + * + * 지원 형식: + * ko/en: "2023-04-24 10:50" + * zh: "2023年04月24日 星期一 10:50" + * ja: "2023年 04月 24日(月曜日) 10:50" + * + * 결과: "2023-04-24 10:50" (통일) + */ +export function normalizeLmsDate(raw: string | null): string | null { + if (!raw) return null; + const str = raw.trim(); + + // 이미 ISO-like 형식이면 그대로 반환 (ko/en) + if (/^\d{4}-\d{2}-\d{2}/.test(str)) return str; + + // ja/zh: "2023年04月24日..." 또는 "2023年 04月 24日..." + const match = str.match(/(\d{4})年\s*(\d{2})月\s*(\d{2})日.*?(\d{2}:\d{2})/); + if (match) { + return `${match[1]}-${match[2]}-${match[3]} ${match[4]}`; + } + + return str; +} + +/** + * LMS VOD range 문자열 정규화 ("시작 ~ 종료" 형태) + * 각 날짜를 개별적으로 정규화하여 반환 + */ +export function normalizeLmsRange(raw: string | null): string | null { + if (!raw) return null; + if (!raw.includes('~')) return normalizeLmsDate(raw); + const parts = raw.split('~').map((s) => normalizeLmsDate(s.trim())); + return parts.join(' ~ '); +} diff --git a/src/lib/parseCourses.ts b/src/lib/parseCourses.ts new file mode 100644 index 0000000..8f2ced0 --- /dev/null +++ b/src/lib/parseCourses.ts @@ -0,0 +1,26 @@ +import { CourseBase } from '@/types'; +import { removeSquareBrackets } from '@/lib/utils'; + +// course_label_ec = 커뮤니티(비교과), 클래스 기반 필터링으로 다국어 대응 +const COMMUNITY_CLASS = 'course_label_ec'; + +function parseCourseFromElement(li: Element): CourseBase | null { + const link = li.querySelector('a.course_link') as HTMLAnchorElement | null; + if (!link) return null; + + const courseId = new URL(link.href).searchParams.get('id'); + const titleEl = link.querySelector('.course-title'); + const prof = titleEl?.querySelector('p')?.textContent?.trim(); + const rawTitle = titleEl?.querySelector('h1, h2, h3')?.textContent?.replace(/new/i, '').trim(); + const courseTitle = rawTitle ? removeSquareBrackets(rawTitle) : ''; + + if (!courseId || !courseTitle || !prof) return null; + + const isCommunity = li.classList.contains(COMMUNITY_CLASS); + return { courseId, courseTitle, prof, isCommunity }; +} + +export function parseCoursesFromDOM(): CourseBase[] { + return Array.from(document.querySelectorAll('.my-course-lists > li')) + .flatMap((li) => parseCourseFromElement(li) ?? []); +} diff --git a/src/lib/storage.ts b/src/lib/storage.ts index 595f1ba..99a36bf 100644 --- a/src/lib/storage.ts +++ b/src/lib/storage.ts @@ -21,3 +21,30 @@ export function loadDataFromStorage(key: string, callback: (data: T | null) = } }); } + +/** + * storage에서 데이터를 로드하고 JSON 파싱 후 변환 함수를 적용하는 유틸 + */ +export function loadAndTransform( + storageKey: string, + transform: (data: T[]) => R, + callback: (result: R) => void +): void { + loadDataFromStorage(storageKey, (data: string | null) => { + if (!data) return; + + let parsedData: T[]; + if (typeof data === 'string') { + try { + parsedData = JSON.parse(data); + } catch (error) { + console.error(`JSON 파싱 에러 (${storageKey}):`, error); + return; + } + } else { + parsedData = data; + } + + callback(transform(parsedData)); + }); +} diff --git a/src/lib/stringUtils.ts b/src/lib/stringUtils.ts new file mode 100644 index 0000000..5dff7a5 --- /dev/null +++ b/src/lib/stringUtils.ts @@ -0,0 +1,16 @@ +import i18n from '@/i18n'; + +export function removeSquareBrackets(str: string) { + return str.replace(/\[[^\]]*\]/g, ''); +} + +export function formatDateString(input: string | null) { + if (!input) return i18n.t('date.noDeadline', { ns: 'common' }); + const regex = /(\d{4})-(\d{2})-(\d{2}) (\d{2}:\d{2}):\d{2} ~ (\d{4})-(\d{2})-(\d{2}) (\d{2}:\d{2}):\d{2}/; + + return input.replace( + regex, + (_, y1, m1, d1, t1, y2, m2, d2, t2) => + `${y1.slice(2)}.${m1}.${d1} ${t1} ~ ${y2.slice(2)}.${m2}.${d2} ${t2}` + ); +} diff --git a/src/lib/summarizeCourseData.ts b/src/lib/summarizeCourseData.ts new file mode 100644 index 0000000..de59461 --- /dev/null +++ b/src/lib/summarizeCourseData.ts @@ -0,0 +1,19 @@ +import { Vod } from '@/types'; +import { makeVodGroupKey } from '@/lib/generateKey'; +import { isAttended } from '@/lib/utils'; + +export type CardData = { + done: number; + total: number; +}; + +export function summarizeVods(vods: Vod[]): CardData { + const groups: Record = {}; + for (const vod of vods) { + const key = makeVodGroupKey(vod.courseId, vod.subject, vod.range); + (groups[key] ??= []).push(vod); + } + const groupList = Object.values(groups); + const done = groupList.filter((g) => isAttended(g[0].weeklyAttendance)).length; + return { done, total: groupList.length }; +} diff --git a/src/lib/transformCalendarEvents.ts b/src/lib/transformCalendarEvents.ts new file mode 100644 index 0000000..5f43205 --- /dev/null +++ b/src/lib/transformCalendarEvents.ts @@ -0,0 +1,50 @@ +import { startOfDay } from 'date-fns'; +import { Vod } from '@/types'; +import { removeSquareBrackets } from '@/lib/utils'; +import { parseDate } from '@/lib/dateUtils'; +import { makeVodGroupKey } from '@/lib/generateKey'; + +export type CalendarEvent = { + id: string; + type: 'vod' | 'assign' | 'quiz'; + title: string; + subject: string; + start: Date | null; + end: Date | null; +}; + +export function vodGroupsToEvents(vods: Vod[]): CalendarEvent[] { + const grouped: Record = {}; + for (const vod of vods) { + const key = makeVodGroupKey(vod.courseId, vod.subject, vod.range); + (grouped[key] ??= []).push(vod); + } + + return Object.entries(grouped).map(([key, items]) => { + const range = items[0].range; + const [startStr, endStr] = range ? range.split(' ~ ') : [null, null]; + return { + id: key, + type: 'vod' as const, + start: startStr ? parseDate(startStr) : null, + end: endStr ? parseDate(endStr) : null, + title: removeSquareBrackets(items[0].courseTitle), + subject: removeSquareBrackets(items[0].subject), + }; + }); +} + +export function dueDateItemToEvent( + item: { courseId: string; courseTitle: string; title: string; dueDate: string | null }, + type: 'assign' | 'quiz', +): CalendarEvent { + const normalizedDate = item.dueDate ? startOfDay(new Date(item.dueDate)) : null; + return { + id: item.courseId + item.title + item.dueDate, + type, + start: normalizedDate, + end: normalizedDate, + title: removeSquareBrackets(item.courseTitle), + subject: removeSquareBrackets(item.title), + }; +} diff --git a/src/lib/transformCourseData.ts b/src/lib/transformCourseData.ts new file mode 100644 index 0000000..d25cbe9 --- /dev/null +++ b/src/lib/transformCourseData.ts @@ -0,0 +1,41 @@ +import { Vod, CourseBase } from '@/types'; + +/** + * VOD 데이터와 출석 데이터를 title+week 기준으로 조인 (날짜 필터 없이 전체 반환) + */ +export function mergeVodWithAttendance( + course: CourseBase, + vodList: { week: number; subject: string; title: string; url: string; range: string | null; length: string }[], + attendanceList: { title: string; isAttendance: string; weeklyAttendance: string; week: number; requiredTime?: string; watchedTime?: string }[] +): Vod[] { + const attendanceByKey = new Map(); + for (const att of attendanceList) { + attendanceByKey.set(`${att.title}-${att.week}`, att); + } + + const results: Vod[] = []; + for (const vod of vodList) { + const attendance = attendanceByKey.get(`${vod.title}-${vod.week}`); + if (!attendance) continue; + results.push({ + ...course, + ...vod, + isAttendance: attendance.isAttendance, + weeklyAttendance: attendance.weeklyAttendance, + requiredTime: attendance.requiredTime, + watchedTime: attendance.watchedTime, + }); + } + return results; +} + +/** + * 마감일 기반 항목(과제/퀴즈)에 course 정보를 병합 (날짜 필터 없이 전체 반환) + */ +export function mergeDueDateItems( + course: CourseBase, + items: T[], +): (CourseBase & T)[] { + return items.map((item) => ({ ...course, ...item })); +} + diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 0f2c101..4172fb0 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,4 +1,3 @@ -import { TimeDifferenceResult } from '@/content/types'; import { clsx, type ClassValue } from 'clsx'; import { twMerge } from 'tailwind-merge'; @@ -6,199 +5,15 @@ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } -export function isCurrentDateInRange(dateRange: string | null) { - if (!dateRange || !dateRange.includes(' ~ ')) { - return false; - } - - const [startStr, endStr] = dateRange.split(' ~ '); - - if (!startStr || !endStr) { - return false; - } - - const startDate = new Date(startStr.replace(/-/g, '/')); - const endDate = new Date(endStr.replace(/-/g, '/')); - - const currentDate = new Date(); - - return currentDate >= startDate && currentDate <= endDate; -} - -export function isCurrentDateByDate(date: string | null) { - if (!date || date.length <= 1) return false; - const endDate = new Date(date); - const currentDate = new Date(); - return currentDate <= endDate; -} - -export function isWithinSevenDays(date: string) { - const dueDate = new Date(date); // 문자열을 Date 객체로 변환 - const now = new Date(); // 현재 날짜 - const diffTime = dueDate.getTime() - now.getTime(); // 두 날짜의 차이 (밀리초) - const diffDays = diffTime / (1000 * 60 * 60 * 24); // 차이를 일(day)로 변환 - return diffDays <= 7 && diffDays >= 0; -} - -export const calculateTimeDifference = (timeRange: string | null): TimeDifferenceResult => { - if (!timeRange) { - return { - message: `정보없음`, - borderColor: 'border-amber-500', - borderLeftColor: 'border-l-amber-500', - textColor: 'text-amber-500', - }; - } - - const now = new Date(); - const [startString, endString] = timeRange.split(' ~ '); - const startDate = new Date(startString); - const endDate = new Date(endString); - - if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { - return { message: 'Invalid date format', borderColor: 'gray', borderLeftColor: 'gray', textColor: 'black' }; - } - - if (now < endDate) { - const timeDiff = endDate.getTime() - now.getTime(); - const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24)); - - if (days >= 1) { - return { - message: `${days}일 후`, - borderColor: 'border-amber-500', - borderLeftColor: 'border-l-amber-500', - textColor: 'text-amber-500', - }; - } else { - const hours = Math.floor(timeDiff / (1000 * 60 * 60)); - const minutes = Math.floor(timeDiff / (1000 * 60)); - return { - message: `${hours !== 0 ? `${hours}시간 후` : `${minutes}분 후`}`, - borderColor: 'border-red-700', - borderLeftColor: 'border-l-red-700', - textColor: 'text-red-700', - }; - } - } else { - return { - message: '마감', - borderColor: 'border-red-950', - borderLeftColor: 'border-l-red-950', - textColor: 'text-red-950', - }; - } -}; - -export const calculateDueDate = (dueDate: string | null): TimeDifferenceResult => { - if (!dueDate) { - return { - message: `정보없음`, - borderColor: 'border-amber-500', - borderLeftColor: 'border-l-amber-500', - textColor: 'text-amber-500', - }; - } - - const now = new Date(); - const endDate = new Date(dueDate); - - if (isNaN(endDate.getTime())) { - return { message: 'Invalid date format', borderColor: 'gray', borderLeftColor: 'gray', textColor: 'black' }; - } - - if (now < endDate) { - const timeDiff = endDate.getTime() - now.getTime(); - const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24)); - - if (days >= 1) { - return { - message: `${days}일 후`, - borderColor: 'border-amber-500', - borderLeftColor: 'border-l-amber-500', - textColor: 'text-amber-500', - }; - } else { - const hours = Math.floor(timeDiff / (1000 * 60 * 60)); - const minutes = Math.floor(timeDiff / (1000 * 60)); - return { - message: `${hours !== 0 ? `${hours}시간 후` : `${minutes}분 후`}`, - borderColor: 'border-red-700', - borderLeftColor: 'border-l-red-700', - textColor: 'text-red-700', - }; - } - } else { - return { - message: '마감', - borderColor: 'border-red-950', - borderLeftColor: 'border-l-red-950', - textColor: 'text-red-950', - }; - } -}; - -export const formatDateString = (input: string | null) => { - if (!input) return '기한없음'; - const regex = /(\d{4})-(\d{2})-(\d{2}) (\d{2}:\d{2}):\d{2} ~ (\d{4})-(\d{2})-(\d{2}) (\d{2}:\d{2}):\d{2}/; - - const formatted = input.replace( - regex, - (_, year1, month1, day1, time1, year2, month2, day2, time2) => - `${year1.slice(2)}.${month1}.${day1} ${time1} ~ ${year2.slice(2)}.${month2}.${day2} ${time2}` - ); - - return formatted; -}; - -export const calculateRemainingTimeByRange = (range: string | null) => { - if (!range) return '정보없음'; - const [, endDateStr] = range.split(' ~ '); - const endDate = new Date(endDateStr); - - const now = new Date(); - - const timeDiff = endDate.getTime() - now.getTime(); - const daysLeft = Math.floor(timeDiff / (1000 * 60 * 60 * 24)); - const hoursLeft = Math.floor((timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); - const minutesLeft = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60)); - - if (daysLeft < 0 || hoursLeft < 0 || minutesLeft < 0) return `마감`; - return `${daysLeft === 0 ? '' : daysLeft + '일'} ${hoursLeft === 0 ? '' : hoursLeft + '시간'} ${minutesLeft}분 남음`; -}; - -export const calculateRemainingTime = (endTime: string | null) => { - if (!endTime) return '정보없음'; - const endDate = new Date(endTime); - - const now = new Date(); - - const timeDiff = endDate.getTime() - now.getTime(); - const daysLeft = Math.floor(timeDiff / (1000 * 60 * 60 * 24)); - const hoursLeft = Math.floor((timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); - const minutesLeft = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60)); - - if (daysLeft < 0 || hoursLeft < 0 || minutesLeft < 0) return `마감`; - return `${daysLeft === 0 ? '' : daysLeft + '일'} ${hoursLeft === 0 ? '' : hoursLeft + '시간'} ${minutesLeft}분 남음`; -}; - -export const removeSquareBrackets = (str: string) => { - return str.replace(/\[[^\]]*\]/g, ''); -}; - -export const TimeAgo = (givenTimestamp: number) => { - const now = Date.now(); - const diffMs = now - givenTimestamp; - - // 각 단위별 밀리초 - const msPerMinute = 1000 * 60; - const msPerHour = msPerMinute * 60; - const msPerDay = msPerHour * 24; - - // 차이를 일, 시간, 분 단위로 계산 - const days = Math.floor(diffMs / msPerDay); - const hours = Math.floor((diffMs % msPerDay) / msPerHour); - const minutes = Math.floor((diffMs % msPerHour) / msPerMinute); - if (days === 0 && hours === 0 && minutes === 0) return '지금 막'; - return `${days !== 0 ? days + '일 ' : ''}${hours !== 0 ? hours + '시간 ' : ''}${minutes !== 0 ? minutes + '분 전' : '전'}`; -}; +// 하위 호환: 기존 import 경로 유지 +export { isAttended, isAbsent } from './attendance'; +export { + isCurrentDateInRange, + isCurrentDateByDate, + isWithinSevenDays, + extractEndDate, + calculateDueDate, + calculateRemainingTime, + timeAgo as TimeAgo, +} from './dateUtils'; +export { removeSquareBrackets, formatDateString } from './stringUtils'; diff --git a/src/mocks/loadMockData.ts b/src/mocks/loadMockData.ts new file mode 100644 index 0000000..a9a0880 --- /dev/null +++ b/src/mocks/loadMockData.ts @@ -0,0 +1,13 @@ +import { Vod, Assign, Quiz } from '@/types'; + +export type MockData = { + vods: Vod[]; + assigns: Assign[]; + quizzes: Quiz[]; +}; + +export async function loadMockData(): Promise { + const { mockVods, mockAssigns, mockQuizes } = await import('./mockData'); + return { vods: mockVods, assigns: mockAssigns, quizzes: mockQuizes }; +} + diff --git a/src/mocks/mockData.ts b/src/mocks/mockData.ts index 16253f3..7d442af 100644 --- a/src/mocks/mockData.ts +++ b/src/mocks/mockData.ts @@ -1,4 +1,4 @@ -import { CourseBase, Vod, Assign, Quiz } from '@/content/types'; +import { CourseBase, Vod, Assign, Quiz } from '@/types'; function offsetDate(days: number, hours = 0): string { const d = new Date(); @@ -20,34 +20,34 @@ export const mockCourses: CourseBase[] = [ ]; export const mockVods: Vod[] = [ - // 출석 완료 + 기간 내 { courseId: '1001', courseTitle: '자료구조', prof: '김교수', week: 7, subject: '7주차', - title: '이진 탐색 트리', - url: '#', - range: makeRange(-3, 5), - length: '45:00', - isAttendance: 'O', - weeklyAttendance: '1/1', + title: '힙과 우선순위 큐', + url: '#mock-vod-2', + range: makeRange(-5, 0), + length: '38:00', + isAttendance: 'X', + weeklyAttendance: '0/1', }, - // 결석 + 마감 임박 (오늘 안에 끝남) + // 출석 완료 + 기간 내 { courseId: '1001', courseTitle: '자료구조', prof: '김교수', week: 7, subject: '7주차', - title: '힙과 우선순위 큐', - url: '#', - range: makeRange(-5, 0, ), - length: '38:00', - isAttendance: 'X', - weeklyAttendance: '0/1', + title: '이진 탐색 트리', + url: '#mock-vod-1', + range: makeRange(-3, 5), + length: '45:00', + isAttendance: 'O', + weeklyAttendance: '1/1', }, + // 결석 + 마감 임박 (오늘 안에 끝남) // 결석 + 기간 여유 { courseId: '1002', @@ -56,7 +56,7 @@ export const mockVods: Vod[] = [ week: 6, subject: '6주차', title: '프로세스 동기화', - url: '#', + url: '#mock-vod-3', range: makeRange(-2, 7), length: '50:00', isAttendance: 'X', @@ -70,7 +70,7 @@ export const mockVods: Vod[] = [ week: 6, subject: '6주차', title: '데드락', - url: '#', + url: '#mock-vod-4', range: makeRange(-2, 7), length: '42:00', isAttendance: 'O', @@ -84,7 +84,7 @@ export const mockVods: Vod[] = [ week: 5, subject: '5주차', title: 'TCP/IP 프로토콜', - url: '#', + url: '#mock-vod-5', range: makeRange(-6, 0), length: '55:00', isAttendance: 'X', @@ -97,7 +97,7 @@ export const mockVods: Vod[] = [ week: 5, subject: '5주차', title: 'UDP와 소켓 프로그래밍', - url: '#', + url: '#mock-vod-6', range: makeRange(-6, 0), length: '35:00', isAttendance: 'O', @@ -111,7 +111,7 @@ export const mockVods: Vod[] = [ week: 4, subject: '4주차', title: '정규화 이론', - url: '#', + url: '#mock-vod-7', range: makeRange(-1, 14), length: '60:00', isAttendance: 'O', @@ -120,6 +120,28 @@ export const mockVods: Vod[] = [ ]; export const mockAssigns: Assign[] = [ + // 오늘 마감 (캘린더 동기화 + 중복 테스트용) + { + courseId: '1001', + courseTitle: '자료구조', + prof: '김교수', + subject: '7주차', + title: '오늘 마감 과제 A', + url: '#mock-assign-1', + isSubmit: false, + dueDate: offsetDate(0), + }, + // 중복 테스트: 동일 제목/날짜 + { + courseId: '1001', + courseTitle: '자료구조', + prof: '김교수', + subject: '7주차', + title: '오늘 마감 과제 A', + url: '#mock-assign-2', + isSubmit: false, + dueDate: offsetDate(0), + }, // 미제출 + 마감 임박 (내일) { courseId: '1001', @@ -127,7 +149,7 @@ export const mockAssigns: Assign[] = [ prof: '김교수', subject: '7주차', title: '이진 트리 구현 과제', - url: '#', + url: '#mock-assign-3', isSubmit: false, dueDate: offsetDate(1), }, @@ -138,7 +160,7 @@ export const mockAssigns: Assign[] = [ prof: '이교수', subject: '6주차', title: '프로세스 스케줄링 시뮬레이션', - url: '#', + url: '#mock-assign-4', isSubmit: false, dueDate: offsetDate(3), }, @@ -149,7 +171,7 @@ export const mockAssigns: Assign[] = [ prof: '김교수', subject: '6주차', title: '스택/큐 구현 과제', - url: '#', + url: '#mock-assign-5', isSubmit: true, dueDate: offsetDate(5), }, @@ -160,7 +182,7 @@ export const mockAssigns: Assign[] = [ prof: '박교수', subject: '5주차', title: '소켓 프로그래밍 실습', - url: '#', + url: '#mock-assign-6', isSubmit: false, dueDate: offsetDate(7), }, @@ -171,7 +193,7 @@ export const mockAssigns: Assign[] = [ prof: '최교수', subject: '4주차', title: 'SQL 쿼리 작성', - url: '#', + url: '#mock-assign-7', isSubmit: true, dueDate: offsetDate(-1), }, @@ -182,7 +204,7 @@ export const mockAssigns: Assign[] = [ prof: '최교수', subject: '5주차', title: 'ERD 설계 과제', - url: '#', + url: '#mock-assign-8', isSubmit: false, dueDate: offsetDate(0, 3), }, @@ -193,7 +215,7 @@ export const mockAssigns: Assign[] = [ prof: '이교수', subject: '5주차', title: '메모리 관리 보고서', - url: '#', + url: '#mock-assign-9', isSubmit: false, dueDate: offsetDate(-2), }, @@ -204,13 +226,27 @@ export const mockAssigns: Assign[] = [ prof: '박교수', subject: '6주차', title: '네트워크 분석 레포트', - url: '#', + url: '#mock-assign-10', isSubmit: false, dueDate: null, }, ]; +// Player 테스트용 mock VOD (실제 LMS VOD ID) +export const mockPlayerVodIds = ['1086348', '1088220', '1130884']; + export const mockQuizes: Quiz[] = [ + // 오늘 마감 (캘린더 동기화 테스트용) + { + courseId: '1002', + courseTitle: '운영체제', + prof: '이교수', + subject: '6주차', + title: '오늘 마감 퀴즈', + url: '#mock-quiz-1', + dueDate: offsetDate(0), + isSubmit: true, + }, // 마감 임박 (내일) { courseId: '1001', @@ -218,8 +254,9 @@ export const mockQuizes: Quiz[] = [ prof: '김교수', subject: '7주차', title: '트리 구조 퀴즈', - url: '#', + url: '#mock-quiz-2', dueDate: offsetDate(1), + isSubmit: false, }, // 마감 여유 (5일) { @@ -228,8 +265,9 @@ export const mockQuizes: Quiz[] = [ prof: '이교수', subject: '6주차', title: '프로세스 관리 퀴즈', - url: '#', + url: '#mock-quiz-3', dueDate: offsetDate(5), + isSubmit: false, }, // 마감 몇 시간 뒤 (긴급) { @@ -238,8 +276,9 @@ export const mockQuizes: Quiz[] = [ prof: '박교수', subject: '5주차', title: 'OSI 모델 퀴즈', - url: '#', + url: '#mock-quiz-4', dueDate: offsetDate(0, 5), + isSubmit: true, }, // 마감 이미 지남 (새로고침 전 마감) { @@ -248,8 +287,9 @@ export const mockQuizes: Quiz[] = [ prof: '최교수', subject: '4주차', title: 'SQL 기초 퀴즈', - url: '#', + url: '#mock-quiz-5', dueDate: offsetDate(-1), + isSubmit: true, }, // 마감 2주 뒤 { @@ -258,8 +298,9 @@ export const mockQuizes: Quiz[] = [ prof: '최교수', subject: '5주차', title: '정규화 퀴즈', - url: '#', + url: '#mock-quiz-6', dueDate: offsetDate(14), + isSubmit: false, }, // dueDate null 케이스 { @@ -268,7 +309,8 @@ export const mockQuizes: Quiz[] = [ prof: '김교수', subject: '8주차', title: '그래프 탐색 퀴즈', - url: '#', + url: '#mock-quiz-7', dueDate: null, + isSubmit: false, }, ]; diff --git a/src/option/App.tsx b/src/option/App.tsx deleted file mode 100644 index ebb41cc..0000000 --- a/src/option/App.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { Routes, Route, useLocation } from 'react-router-dom'; -import { AnimatePresence, motion } from 'framer-motion'; -import Sidebar from './Sidebar'; -import { Toaster } from '@/components/ui/toaster'; - -import VodPage from 'src/pages/VodPage'; -import AssignmentPage from 'src/pages/AssignmentPage'; -import DashboardPage from '@/pages/DashboardPage'; -import QuizPage from '@/pages/QuizPage'; -import Header from './Header'; -import Labo from './Labo'; -import ColorSetting from './ColorSetting'; - -const pageVariants = { - initial: { - opacity: 0, - x: '-5%', - }, - in: { - opacity: 1, - x: 0, - }, - out: { - opacity: 0, - x: '5%', - }, -}; - -const pageTransition = { - type: 'tween', - ease: 'anticipate', - duration: 0.5, -}; - -const AnimatedRoutes = () => { - const location = useLocation(); - return ( - - -
- - } /> - } /> - } /> - } /> - } /> - } /> - 404 Not Found
} /> - - - - ); -}; - -export default function App() { - return ( - <> -
- -
-
-
- -
-
-
-
- - - ); -} diff --git a/src/option/ColorSetting.tsx b/src/option/ColorSetting.tsx deleted file mode 100644 index 5fdab51..0000000 --- a/src/option/ColorSetting.tsx +++ /dev/null @@ -1,429 +0,0 @@ -import { useEffect, useState } from 'react'; -import { Save, RotateCcw } from 'lucide-react'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; -import { Input } from '@/components/ui/input'; -import { toast } from '@/hooks/use-toast'; -import { loadDataFromStorage, saveDataToStorage } from '@/lib/storage'; -import type { CourseBase } from '@/content/types'; -import { HexColorPicker } from 'react-colorful'; - -// Update the CourseColorSetting type to include opacity -export type CourseColorSetting = { - courseId: string; - color: string; - colorType: 'solid' | 'gradient'; - gradient?: string; - opacity?: number; // Add opacity property -}; - -// Recommended color presets -const colorPresets = [ - '#e2e2e2', // Light Gray - '#ff75c3', // Pink - '#ffa647', // Orange - '#ffe83f', // Yellow - '#9fff5b', // Lime - '#70e2ff', // Cyan - '#cd93ff', // Purple - '#09203f', // Dark Blue -]; - -// Gradient presets -const gradientPresets = [ - 'linear-gradient(to right, #ff75c3, #ffa647)', - 'linear-gradient(to right, #ffa647, #ffe83f)', - 'linear-gradient(to right, #ffe83f, #9fff5b)', - 'linear-gradient(to right, #9fff5b, #70e2ff)', - 'linear-gradient(to right, #70e2ff, #cd93ff)', - 'linear-gradient(to right, #cd93ff, #ff75c3)', - 'linear-gradient(to right, #AC32E4, #4801FF)', - 'linear-gradient(to right, #09203f, #537895)', -]; - -export default function ColorSetting() { - const [courses, setCourses] = useState([]); - const [courseColors, setCourseColors] = useState([]); - const [originalColors, setOriginalColors] = useState>({}); - - useEffect(() => { - loadDataFromStorage('courses', (data: string | null) => { - if (data) { - const loadedCourses: CourseBase[] = JSON.parse(data); - setCourses(loadedCourses); - } - }); - }, []); - - useEffect(() => { - loadDataFromStorage('courseColors', (data: string | null) => { - if (data) { - const loadedColors: CourseColorSetting[] = JSON.parse(data); - - const updatedColors = loadedColors.map((color) => ({ - ...color, - colorType: color.colorType || 'solid', - })); - - setCourseColors(updatedColors); - - const originals: Record = {}; - updatedColors.forEach((item) => { - originals[item.courseId] = item.color; - }); - setOriginalColors(originals); - } - }); - }, []); - - useEffect(() => { - if (courses.length > 0 && courseColors.length === 0) { - const defaultColors = courses.map((course) => ({ - courseId: course.courseId, - color: '#6366f1', - colorType: 'solid' as const, - })); - setCourseColors(defaultColors); - - const originals: Record = {}; - defaultColors.forEach((item) => { - originals[item.courseId] = item.color; - }); - setOriginalColors(originals); - } - }, [courses, courseColors.length]); - - const handleColorChange = ( - courseId: string, - newColor: string, - colorType: 'solid' | 'gradient' = 'solid', - gradientValue?: string, - opacity?: number - ) => { - setCourseColors((prev) => - prev.map((item) => { - if (item.courseId === courseId) { - return { - ...item, - color: newColor, - colorType, - gradient: gradientValue, - opacity: opacity !== undefined ? opacity : item.opacity || 1, // Preserve or set default opacity - }; - } - return item; - }) - ); - }; - const resetToOriginal = (courseId: string) => { - if (originalColors[courseId]) { - handleColorChange(courseId, originalColors[courseId], 'solid'); - } - }; - - // Save color settings - const handleSave = () => { - saveDataToStorage('courseColors', JSON.stringify(courseColors)); - - const originals: Record = {}; - courseColors.forEach((item) => { - originals[item.courseId] = item.color; - }); - setOriginalColors(originals); - - toast({ - title: '색상 설정 저장 완료', - description: '강의 색상 설정이 저장되었습니다.', - }); - }; - - const getCourseColorSetting = (courseId: string) => { - return ( - courseColors.find((item) => item.courseId === courseId) || { - courseId, - color: '#6366f1', - colorType: 'solid' as const, - } - ); - }; - - const getOriginalColor = (courseId: string) => { - return originalColors[courseId] || '#6366f1'; - }; - - return ( -
-
-

강의 색상 설정

-

각 강의에 표시될 색상을 설정합니다.

-
- -
- {courses.map((course) => ( - resetToOriginal(course.courseId)} - /> - ))} -
- -
- -
-
- ); -} - -interface CourseCardProps { - course: CourseBase; - colorSetting: CourseColorSetting; - originalColor: string; - onColorChange: ( - courseId: string, - color: string, - colorType: 'solid' | 'gradient', - gradient?: string, - opacity?: number - ) => void; - onReset: () => void; -} - -function CourseCard({ course, colorSetting, originalColor, onColorChange, onReset }: CourseCardProps) { - const hasChanged = colorSetting.color !== originalColor; - const [customColor, setCustomColor] = useState(colorSetting.color); - const [customGradient, setCustomGradient] = useState( - colorSetting.gradient || 'linear-gradient(to right, #ff75c3, #ffa647)' - ); - const [gradientStartColor, setGradientStartColor] = useState('#ff75c3'); - const [gradientEndColor, setGradientEndColor] = useState('#ffa647'); - const [opacity, setOpacity] = useState(colorSetting.opacity || 1); - - useEffect(() => { - setCustomColor(colorSetting.color); - setOpacity(colorSetting.opacity || 1); - if (colorSetting.gradient) { - setCustomGradient(colorSetting.gradient); - - const gradientMatch = colorSetting.gradient.match( - /linear-gradient\(to right, (#[0-9a-fA-F]{3,6}), (#[0-9a-fA-F]{3,6})\)/ - ); - if (gradientMatch) { - setGradientStartColor(gradientMatch[1]); - setGradientEndColor(gradientMatch[2]); - } - } - }, [colorSetting]); - - const getBackgroundStyle = () => { - const opacityValue = opacity !== undefined ? opacity : 1; - - switch (colorSetting.colorType) { - case 'gradient': - if (colorSetting.gradient) { - const gradientMatch = colorSetting.gradient.match( - /linear-gradient\(to right, (#[0-9a-fA-F]{3,6}), (#[0-9a-fA-F]{3,6})\)/ - ); - - if (gradientMatch) { - const startColor = gradientMatch[1]; - const endColor = gradientMatch[2]; - const startRgba = hexToRgba(startColor, opacityValue); - const endRgba = hexToRgba(endColor, opacityValue); - return { background: `linear-gradient(to right, ${startRgba}, ${endRgba})` }; - } - return { background: colorSetting.gradient }; - } - return { background: customGradient }; - - case 'solid': - default: - return { backgroundColor: hexToRgba(colorSetting.color, opacityValue) }; - } - }; - - const hexToRgba = (hex: string, opacity: number) => { - hex = hex.replace('#', ''); - - let r, g, b; - if (hex.length === 3) { - r = Number.parseInt(hex.charAt(0) + hex.charAt(0), 16); - g = Number.parseInt(hex.charAt(1) + hex.charAt(1), 16); - b = Number.parseInt(hex.charAt(2) + hex.charAt(2), 16); - } else { - r = Number.parseInt(hex.substring(0, 2), 16); - g = Number.parseInt(hex.substring(2, 4), 16); - b = Number.parseInt(hex.substring(4, 6), 16); - } - - return `rgba(${r}, ${g}, ${b}, ${opacity})`; - }; - - const handleOpacityChange = (value: number) => { - setOpacity(value); - if (colorSetting.colorType === 'solid') { - onColorChange(course.courseId, customColor, 'solid', undefined, value); - } else { - onColorChange(course.courseId, customGradient, 'gradient', customGradient, value); - } - }; - - const handleCustomColorChange = (value: string) => { - setCustomColor(value); - onColorChange(course.courseId, value, 'solid', undefined, opacity); - }; - - const handleGradientColorChange = (startColor: string, endColor: string) => { - const newGradient = `linear-gradient(to right, ${startColor}, ${endColor})`; - setGradientStartColor(startColor); - setGradientEndColor(endColor); - setCustomGradient(newGradient); - onColorChange(course.courseId, newGradient, 'gradient', newGradient, opacity); - }; - - const opacitySlider = ( -
-
- - {Math.round(opacity * 100)}% -
- handleOpacityChange(Number.parseFloat(e.target.value))} - className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700" - /> -
- ); - - return ( - - - {course.courseTitle} - {course.prof} - - -
- - - - - - - - - Solid - - - Gradient - - - - -
- {colorPresets.map((color) => ( -
onColorChange(course.courseId, color, 'solid', undefined, opacity)} - /> - ))} -
- -
- handleCustomColorChange(color)} - className="w-full h-[120px]" - /> -
- -
- handleCustomColorChange(e.target.value)} - className="h-8" - /> -
- - {opacitySlider} - - - -
- {gradientPresets.map((gradient, index) => ( -
onColorChange(course.courseId, gradient, 'gradient', gradient, opacity)} - /> - ))} -
- -
-
-

시작 색상

- handleGradientColorChange(color, gradientEndColor)} - className="w-full h-[120px]" - /> -
-
-

끝 색상

- handleGradientColorChange(gradientStartColor, color)} - className="w-full h-[120px]" - /> -
-
- -
- - {opacitySlider} - - - - -
- 선택된 스타일 -
- - {colorSetting.colorType === 'solid' - ? `${colorSetting.color} (${Math.round((colorSetting.opacity || 1) * 100)}%)` - : `Gradient (${Math.round((colorSetting.opacity || 1) * 100)}%)`} - -
-
- {hasChanged && ( - - )} -
- - - ); -} diff --git a/src/option/GithubStarBanner.tsx b/src/option/GithubStarBanner.tsx deleted file mode 100644 index 298b63b..0000000 --- a/src/option/GithubStarBanner.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import type React from 'react'; -import { useState } from 'react'; -import { Github } from 'lucide-react'; -import { motion } from 'framer-motion'; - -const GitHubStarBanner: React.FC = () => { - const [isHovered, setIsHovered] = useState(false); - - const handleClick = () => { - window.open('https://github.com/hs-shell/dotbugi', '_blank'); - }; - - return ( -
setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - className="mt-4 mb-2 mx-1 py-4 px-2 bg-gradient-to-r from-slate-100 to-slate-200 rounded-lg border border-slate-200 shadow-sm cursor-pointer hover:shadow-md transition-all duration-300 group" - > -
-
- -
- - - - - - -
-
-

이 프로젝트가 마음에 드시나요?

-

GitHub에서 스타를 눌러주세요!

-
-
- ); -}; - -export default GitHubStarBanner; diff --git a/src/option/Header.tsx b/src/option/Header.tsx deleted file mode 100644 index c507aae..0000000 --- a/src/option/Header.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { loadDataFromStorage } from '@/lib/storage'; -import { TimeAgo } from '@/lib/utils'; -import { History } from 'lucide-react'; -import { useEffect, useState } from 'react'; -export default function Header({ location }: { location: string }) { - const [lastRequest, setLastRequest] = useState(''); - useEffect(() => { - loadDataFromStorage('lastRequestTime', (data: string | null) => { - if (!data) setLastRequest('기록이 존재하지 않습니다.'); - else setLastRequest(TimeAgo(parseInt(data))); - }); - }, []); - - let title = '안녕하세요'; - - switch (location) { - case '/vod': - title = '동영상 강의 목록'; - window.document.title = '돋부기 | 강의 목록'; - break; - case '/assignment': - title = '과제 목록'; - window.document.title = '돋부기 | 과제 목록'; - break; - case '/quiz': - title = '퀴즈 목록'; - window.document.title = '돋부기 | 퀴즈 목록'; - break; - default: - title = '대시보드 🚀'; - window.document.title = '돋부기 | 대시보드'; - break; - } - return ( -
-

{title}

-
- - {lastRequest} -
-
- ); -} diff --git a/src/option/Labo.tsx b/src/option/Labo.tsx deleted file mode 100644 index e856125..0000000 --- a/src/option/Labo.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { Button } from '@/components/ui/button'; -import { calendar_v3 } from 'googleapis'; - -type CalendarEvent = calendar_v3.Schema$Event; - -const Labo: React.FC = () => { - const [token, setToken] = useState(null); - const [events, setEvents] = useState([]); - - const fetchCalendarEvents = (token: string) => { - fetch( - 'https://www.googleapis.com/calendar/v3/calendars/primary/events?timeMin=' + - new Date().toISOString() + - '&orderBy=startTime&singleEvents=true', - { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ) - .then((response) => response.json()) - .then((data) => setEvents(data.items || [])) - .catch((error) => console.error('캘린더 이벤트 가져오기 실패:', error)); - }; - - useEffect(() => { - chrome.identity.getAuthToken({ interactive: false }, (cachedToken) => { - if (chrome.runtime.lastError || !cachedToken) { - console.error('자동 로그인 실패:', chrome.runtime.lastError?.message); - } else { - setToken(cachedToken); - fetchCalendarEvents(cachedToken); - } - }); - }, []); - - const handleLogin = () => { - chrome.identity.getAuthToken({ interactive: true }, (newToken) => { - if (chrome.runtime.lastError || !newToken) { - console.error(chrome.runtime.lastError); - return; - } - setToken(newToken); - fetchCalendarEvents(newToken); - }); - }; - - const handleLogout = () => { - if (!token) return; - - chrome.identity.removeCachedAuthToken({ token }, () => { - fetch(`https://oauth2.googleapis.com/revoke?token=${token}`, { - method: 'POST', - headers: { - 'Content-type': 'application/x-www-form-urlencoded', - }, - }) - .then((response) => { - if (response.ok) { - setToken(null); - setEvents([]); - } else { - console.error('토큰 폐기 요청에 실패했습니다.'); - } - }) - .catch((error) => { - console.error('에러 발생:', error); - }); - }); - }; - - const addCalendarEvent = () => { - if (!token) return; - - const event = { - summary: '테스트 이벤트 🎉', - description: '이것은 Google Calendar API를 사용한 이벤트 생성입니다.', - start: { - dateTime: new Date().toISOString(), - timeZone: 'Asia/Seoul', - }, - end: { - dateTime: new Date(new Date().getTime() + 60 * 60 * 1000).toISOString(), - timeZone: 'Asia/Seoul', - }, - }; - - fetch('https://www.googleapis.com/calendar/v3/calendars/primary/events', { - method: 'POST', - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify(event), - }) - .then((response) => response.json()) - .then(() => { - alert('새로운 이벤트가 추가되었습니다!'); - fetchCalendarEvents(token); - }) - .catch((error) => console.error('이벤트 추가 실패:', error)); - }; - - return ( -
-

Google Calendar 연동

- {token ? ( -
- - -

내 캘린더 일정

-
    - {events.length > 0 ? ( - events.map((event, index) => ( -
  • - {event.summary} -

    - {event.start?.dateTime?.replace('T', ' ').substring(0, 16)} ~{' '} - {event.end?.dateTime?.replace('T', ' ').substring(0, 16)} -

    -
  • - )) - ) : ( -

    일정이 없습니다.

    - )} -
-
- ) : ( - - )} -
- ); -}; - -export default Labo; diff --git a/src/option/Sidebar.tsx b/src/option/Sidebar.tsx deleted file mode 100644 index 811751c..0000000 --- a/src/option/Sidebar.tsx +++ /dev/null @@ -1,97 +0,0 @@ -'use client'; - -import { Calendar, LayoutDashboard, NotebookText, Palette, Video, Zap } from 'lucide-react'; -import type React from 'react'; -import { Link, useLocation } from 'react-router-dom'; -import icon from '@/assets/icon.png'; -import GitHubStarBanner from './GithubStarBanner'; - -const Sidebar: React.FC = () => { - return ( - - ); -}; - -interface SidebarItemProps { - to: string; - icon: React.ReactNode; - label: string; -} - -const SidebarItem: React.FC = ({ to, icon, label }) => { - const location = useLocation(); - const isActive = location.pathname === to; // 현재 경로와 to가 일치하는지 확인 - - return ( -
  • - - {icon} - {label} - -
  • - ); -}; - -export default Sidebar; diff --git a/src/option/SummaryCard.tsx b/src/option/SummaryCard.tsx deleted file mode 100644 index 2162a84..0000000 --- a/src/option/SummaryCard.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { Card, CardContent, CardHeader } from '@/components/ui/card'; -import { Progress } from '@/components/ui/progress'; -import useCardData from '@/hooks/useCardData'; -import { Zap, Video, NotebookTextIcon } from 'lucide-react'; -import { ReactNode } from 'react'; -import clsx from 'clsx'; -import { Link } from 'react-router-dom'; - -interface CardItemProps { - title: string; - icon: ReactNode; - data: { done: number; total: number }[]; - color: string; - link: string; -} - -const colorMap: Record = { - blue: 'bg-blue-500', - violet: 'bg-violet-500', - amber: 'bg-amber-500', - red: 'bg-red-500', - green: 'bg-green-500', -}; - -export default function SummaryCard() { - const { vodSummary, assignSummary, quizSummary } = useCardData(); - - const CardItem = ({ title, icon, data, color, link }: CardItemProps) => { - const done = data.length > 0 ? data[0].done : 0; - const total = data.length > 0 ? data[0].total : 1; - const percentage = Math.round((done / total) * 100); - const bgColorClass = colorMap[color] || 'bg-gray-500'; // 기본값 설정 - - return ( - - - -

    {title}

    - {icon} -
    - - {title.includes('퀴즈') ? ( -
    {data.length > 0 ? `${total} 개` : '0 개'}
    - ) : ( -
    {data.length > 0 ? `${done} / ${total}` : '0 개'}
    - )} - {title.includes('퀴즈') ? ( -
    - ) : ( - - )} -

    - {title.includes('퀴즈') ? '직접 확인' : isNaN(percentage) ? 0 + '% 완료' : percentage + '% 완료'} -

    - - - - ); - }; - - return ( -
    - } - data={vodSummary} - color="blue" - link={'vod'} - /> - } - data={assignSummary} - color="violet" - link={'assignment'} - /> - } - data={quizSummary} - color="amber" - link={'quiz'} - /> -
    - ); -} diff --git a/src/option/calendar.tsx b/src/option/calendar.tsx deleted file mode 100644 index 02513b2..0000000 --- a/src/option/calendar.tsx +++ /dev/null @@ -1,586 +0,0 @@ -import { useState, useMemo, useEffect } from 'react'; -import { Card } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; -import { cn } from '@/lib/utils'; -import { - format, - addMonths, - subMonths, - startOfMonth, - startOfWeek, - isSameMonth, - isToday, - isBefore, - addDays, - isSunday, -} from 'date-fns'; -import { ChevronLeft, ChevronRight, NotebookText, Zap, ListFilter, CalendarArrowUp } from 'lucide-react'; -import useCalendarEvents, { CalendarEvent } from '@/hooks/useCalendarEvents'; -import filter from '@/assets/filter.svg'; -import { Label } from '@/components/ui/label'; -import { - getOAuthToken, - addCalendarEvent, - getCalendarEvents, - convertCalendarEventsToGoogleEvents, - GoogleCalendarEvent, -} from '@/lib/calendarUtils'; -import { toast } from '@/hooks/use-toast'; -import { loadDataFromStorage } from '@/lib/storage'; -import GoogleCalendar from '@/assets/calendar.png'; -function isSameDate(d1: Date, d2: Date) { - return d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth() && d1.getDate() === d2.getDate(); -} - -function isInEventRange(day: Date, event: CalendarEvent) { - if (!event.start || !event.end) return false; - return day >= event.start && day <= event.end; -} - -function isSingleDayEvent(event: CalendarEvent) { - if (!event.start || !event.end) return false; - return isSameDate(event.start, event.end); -} - -function getRangePosition(day: Date, event: CalendarEvent): 'single' | 'start' | 'middle' | 'end' | 'after' | null { - if (!event.start || !event.end) return null; - - if (!isInEventRange(day, event)) return null; - if (isSingleDayEvent(event)) return 'single'; - if (isSameDate(day, addDays(event.start, 1))) return 'after'; - if (isSameDate(day, event.start)) return 'start'; - if (isSameDate(day, event.end)) return 'end'; - return 'middle'; -} - -function eventsOverlap(a: CalendarEvent, b: CalendarEvent) { - if (a.start === null || a.end === null || b.start === null || b.end === null) { - return false; - } - return a.start <= b.end && b.start <= a.end; -} -// 헬퍼: hex 색상을 rgba 문자열로 변환 (투명도 적용) -function hexToRgba(hex: string, opacity: number): string { - hex = hex.replace('#', ''); - const r = parseInt(hex.substring(0, 2), 16); - const g = parseInt(hex.substring(2, 4), 16); - const b = parseInt(hex.substring(4, 6), 16); - return `rgba(${r}, ${g}, ${b}, ${opacity})`; -} - -interface CalendarEventWithRow extends CalendarEvent { - row: number; -} - -const colorClasses = [ - 'bg-rose-100 text-rose-800', - 'bg-amber-100 text-amber-800', - 'bg-emerald-100 text-emerald-800', - 'bg-sky-100 text-sky-800', - 'bg-violet-100 text-violet-800', - 'bg-fuchsia-100 text-fuchsia-800', - 'bg-lime-100 text-lime-800', - 'bg-cyan-100 text-cyan-800', -]; - -export function Calendar() { - const events = useCalendarEvents(); - const [currentMonth, setCurrentMonth] = useState(new Date()); - const [selectedDate, setSelectedDate] = useState(null); - - // 필터 state 관리 - const [selectedFilters, setSelectedFilters] = useState([]); - const [isFilterOpen, setIsFilterOpen] = useState(false); - - const typeFilters = [ - { value: 'vod', label: '동영상 강의' }, - { value: 'assign', label: '과제' }, - { value: 'quiz', label: '퀴즈' }, - ]; - - const titleFilters = Array.from(new Set(events.map((event) => event.title))).map((title) => ({ - value: title, - label: title, - })); - - const filterOptions = [...typeFilters, ...titleFilters]; - - const toggleFilter = (filter: string) => { - setSelectedFilters((prev) => (prev.includes(filter) ? prev.filter((f) => f !== filter) : [...prev, filter])); - }; - - const clearFilters = () => { - setSelectedFilters([]); - }; - - const isFilterSet = selectedFilters.length > 0; - - const firstDayOfMonth = startOfMonth(currentMonth); - const startDate = startOfWeek(firstDayOfMonth, { weekStartsOn: 0 }); - const dayList = useMemo(() => { - return Array.from({ length: 42 }, (_, i) => addDays(startDate, i)); - }, [startDate]); - - const handlePrevMonth = () => { - setCurrentMonth((prev) => subMonths(prev, 1)); - }; - - const handleNextMonth = () => { - setCurrentMonth((prev) => addMonths(prev, 1)); - }; - - const handleSelectDate = (date: Date) => { - if (isSameMonth(date, currentMonth)) { - setSelectedDate(date); - } else { - if (isBefore(date, firstDayOfMonth)) { - handlePrevMonth(); - } else { - handleNextMonth(); - } - setSelectedDate(date); - } - }; - - const eventsWithRow = useMemo(() => { - const sorted = [...events].sort((a, b) => { - const aTime = a.start ? a.start.getTime() : Number.MIN_SAFE_INTEGER; - const bTime = b.start ? b.start.getTime() : Number.MIN_SAFE_INTEGER; - return aTime - bTime; - }); - - const assigned: CalendarEventWithRow[] = []; - for (const event of sorted) { - let row = 0; - while (assigned.some((e) => e.row === row && eventsOverlap(e, event))) { - row++; - } - assigned.push({ ...event, row }); - } - return assigned; - }, [events]); - - const subjectList = useMemo(() => { - return Array.from(new Set(events.map((event) => event.title))); - }, [events]); - - const subjectColorMap = useMemo(() => { - const map: { [key: string]: string } = {}; - subjectList.forEach((subject, index) => { - map[subject] = colorClasses[index % colorClasses.length]; - }); - return map; - }, [subjectList]); - - // 저장소에서 강의 색상 데이터 불러오기 (vod 데이터에 적용) - const [courseColors, setCourseColors] = useState<{ - [courseId: string]: { color: string; colorType: string; gradient?: string; opacity: number }; - } | null>(null); - - useEffect(() => { - loadDataFromStorage('courseColors', (data: string | null) => { - if (data) { - try { - const parsedData = JSON.parse(data); - const map = Array.isArray(parsedData) - ? parsedData.reduce( - (acc, cur) => { - acc[cur.courseId] = cur; - return acc; - }, - {} as { [courseId: string]: { color: string; colorType: string; gradient?: string; opacity: number } } - ) - : {}; - setCourseColors(map); - } catch (error) { - console.error('courseColors 파싱 에러:', error); - } - } - }); - }, []); - - const renderEvents = (day: Date, isCurrent: boolean) => { - const weekStart = startOfWeek(day, { weekStartsOn: 0 }); - const weekEnd = addDays(weekStart, 6); - - const weekEvents = eventsWithRow.filter( - (event) => event.end !== null && event.start !== null && event.end >= weekStart && event.start <= weekEnd - ); - - const weekStack: { [eventId: string]: number } = {}; - weekEvents - .sort((a, b) => { - const aTime = a.start ? a.start.getTime() : Number.MIN_SAFE_INTEGER; - const bTime = b.start ? b.start.getTime() : Number.MIN_SAFE_INTEGER; - return aTime - bTime; - }) - .forEach((event) => { - let row = 0; - while ( - Object.entries(weekStack).some(([id, assignedRow]) => { - if (id === event.id) return false; - const assignedEvent = weekEvents.find((e) => e.id === id); - if (!assignedEvent) return false; - const eventStartInWeek = event.start ? (event.start < weekStart ? weekStart : event.start) : weekStart; - const eventEndInWeek = event.end ? (event.end > weekEnd ? weekEnd : event.end) : weekEnd; - const assignedStartInWeek = assignedEvent.start - ? assignedEvent.start < weekStart - ? weekStart - : assignedEvent.start - : weekStart; - const assignedEndInWeek = assignedEvent.end - ? assignedEvent.end > weekEnd - ? weekEnd - : assignedEvent.end - : weekEnd; - - return ( - assignedRow === row && eventStartInWeek <= assignedEndInWeek && assignedStartInWeek <= eventEndInWeek - ); - }) - ) { - row++; - } - weekStack[event.id] = row; - }); - - const typeFilterValues = typeFilters.map((f) => f.value); - const selectedTypeFilters = selectedFilters.filter((f) => typeFilterValues.includes(f)); - const selectedTitleFilters = selectedFilters.filter((f) => !typeFilterValues.includes(f)); - - const eventsOfTheDay = eventsWithRow - .filter((event) => { - if (!isInEventRange(day, event)) return false; - if (selectedTypeFilters.length > 0 && selectedTitleFilters.length > 0) { - return selectedTypeFilters.includes(event.type) && selectedTitleFilters.includes(event.title); - } else if (selectedTypeFilters.length > 0) { - return selectedTypeFilters.includes(event.type); - } else if (selectedTitleFilters.length > 0) { - return selectedTitleFilters.includes(event.title); - } - return true; - }) - .map((event) => ({ ...event, row: weekStack[event.id] ?? 0 })); - - const maxRow = eventsOfTheDay.length > 0 ? Math.max(...eventsOfTheDay.map((e) => e.row)) : -1; - const numRows = maxRow + 1; - - return ( -
    - {Array.from({ length: numRows }, (_, rowIndex) => { - const event = eventsOfTheDay.find((e) => e.row === rowIndex); - if (event) { - const rangePosition = getRangePosition(day, event); - if (!rangePosition) return
    ; - - const eventId = event.id.split('-')[0]; - const isVodCustom = event.type === 'vod' && courseColors && eventId && courseColors[eventId]; - let customStyle = {}; - - if (isVodCustom) { - const courseData = courseColors?.[eventId]; - - const eventStart = event.start ?? new Date(0); - const eventEnd = event.end ?? new Date(0); - - const totalDays = Math.floor((eventEnd.getTime() - eventStart.getTime()) / (1000 * 3600 * 24)) + 1; - - if (courseData?.colorType === 'gradient' && courseData.gradient && totalDays > 1) { - const dayIndex = Math.floor((day.getTime() - eventStart.getTime()) / (1000 * 3600 * 24)); - - const regex = /linear-gradient\(to right, (#[0-9a-fA-F]+), (#[0-9a-fA-F]+)\)/; - const match = courseData.gradient.match(regex); - - if (match) { - const rgba1 = hexToRgba(match[1], courseData.opacity); - const rgba2 = hexToRgba(match[2], courseData.opacity); - customStyle = { - backgroundImage: `linear-gradient(to right, ${rgba1}, ${rgba2})`, - backgroundSize: `${totalDays * 100}% 100%`, - backgroundPosition: `${-(dayIndex * 100)}% 0`, - }; - } else { - customStyle = { backgroundImage: courseData.gradient, opacity: courseData.opacity }; - } - } else { - // 단색(hex)인 경우: null 값 방지 후 rgba 변환 - customStyle = { background: hexToRgba(courseData?.color ?? '#000000', courseData?.opacity ?? 1) }; - } - } - - if (rangePosition === 'single') { - return ( -
    - - {event.type === 'assign' ? ( - - ) : event.type === 'quiz' ? ( - - ) : null} - - - {event.title} - {event.subject} - -
    - ); - } - - const isStart = rangePosition === 'start'; - const isEnd = rangePosition === 'end'; - const showTitle = isStart; - - const additionalClasses = cn(isStart && 'rounded-l-sm ml-1', isEnd && 'rounded-r-sm mr-1'); - - const defaultClass = isVodCustom - ? '' - : isCurrent - ? `${subjectColorMap[event.title]} text-zinc-700` - : 'bg-zinc-100 text-zinc-300'; - - return ( -
    - {showTitle && ( - - {event.title} - {event.subject} - - )} -
    - ); - } else { - return
    ; - } - })} -
    - ); - }; - - const handleRedirectToCalendar = async () => { - const token = await getOAuthToken(); - if (!token) { - toast({ - title: '동기화 실패 🚨', - description: '구글 캘린더 연동 상태를 확인해주세요', - variant: 'destructive', - }); - return; - } - - window.open('https://calendar.google.com/calendar', '_blank'); - }; - const handleCalendarSync = async () => { - const token = await getOAuthToken(); - if (!token) { - toast({ - title: '동기화 실패 🚨', - description: '구글 캘린더 연동 상태를 확인해주세요', - variant: 'destructive', - }); - return; - } - - try { - const existingEvents: GoogleCalendarEvent[] = await getCalendarEvents(token); - const newEventsData: GoogleCalendarEvent[] = convertCalendarEventsToGoogleEvents(events); - - const normalizeEvent = (event: { - summary: string; - description?: string; - start: { dateTime: string }; - end: { dateTime: string }; - }) => ({ - summary: event.summary.trim().toLowerCase(), - description: (event.description || '').trim().toLowerCase(), - startTime: new Date(event.start.dateTime).getTime(), - endTime: new Date(event.end.dateTime).getTime(), - }); - - const uniqueNewEvents = newEventsData.filter((newEvent) => { - const normNew = normalizeEvent(newEvent); - return !existingEvents.some((existingEvent) => { - const normExisting = normalizeEvent(existingEvent); - return ( - normExisting.summary === normNew.summary && - normExisting.description === normNew.description && - normExisting.startTime === normNew.startTime && - normExisting.endTime === normNew.endTime - ); - }); - }); - - if (uniqueNewEvents.length === 0) { - toast({ - title: '캘린더가 최신 상태입니다 🤩', - description: '이미 최신 정보로 동기화되었습니다.', - variant: 'default', - }); - } else { - for (const event of uniqueNewEvents) { - await addCalendarEvent(event, token); - } - toast({ - title: '동기화 성공 🚀', - description: `${uniqueNewEvents.length}개의 이벤트가 추가되었습니다.`, - variant: 'default', - }); - } - } catch (error) { - toast({ - title: '동기화 오류 🚨', - description: '이벤트 동기화 중 오류가 발생했습니다.', - variant: 'destructive', - }); - console.error(error); - } - }; - - return ( - -
    -
    - -
    -
    - -
    {format(currentMonth, 'yyyy년 MM월')}
    - -
    - -
    -
    - -
    -
    - - - - - -
    - {filterOptions.map((option) => ( -
    -
    - toggleFilter(option.value)} - className="shadow-md rounded-sm peer h-5 w-5 cursor-pointer appearance-none border border-zinc-800 bg-white checked:border-primary checked:bg-primary focus:outline-none focus:ring-primary focus:ring-offset-0" - /> - - - -
    - -
    - ))} -
    - - -
    -
    -
    -
    -
    - - {/* 요일 헤더 */} -
    - {['일', '월', '화', '수', '목', '금', '토'].map((dayName) => ( -
    - {dayName} -
    - ))} -
    - - {/* 달력 그리드 */} -
    - {dayList.map((dayItem) => { - const isCurrent = isSameMonth(dayItem, currentMonth); - const isTodayDate = isToday(dayItem); - const sunday = isSunday(dayItem); - const isSelected = selectedDate && isSameDate(selectedDate, dayItem); - return ( -
    handleSelectDate(dayItem)} - className={cn( - 'relative cursor-pointer w-full min-h-[120px] py-2 rounded-lg', - isCurrent ? 'bg-white' : 'text-gray-300', - isSelected && 'bg-zinc-50', - 'hover:bg-zinc-50 transition-all duration-300' - )} - > -
    -
    - {format(dayItem, 'd')} -
    - {renderEvents(dayItem, isCurrent)} -
    - ); - })} -
    - - ); -} diff --git a/src/option/components/AssignCard.tsx b/src/option/components/AssignCard.tsx deleted file mode 100644 index a20acec..0000000 --- a/src/option/components/AssignCard.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { useEffect, useState } from 'react'; -import { Card, CardContent } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { Assign } from '@/content/types'; - -import { calculateDueDate, calculateRemainingTime } from '@/lib/utils'; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; - -interface TaskStatusCardProps { - assign: Assign; -} - -const AssignCard: React.FC = ({ assign }) => { - const [showRemainingTime, setShowRemainingTime] = useState(false); - - useEffect(() => { - let timer: NodeJS.Timeout; - if (showRemainingTime) { - timer = setTimeout(() => { - setShowRemainingTime(false); - }, 5000); - } - return () => clearTimeout(timer); - }, [showRemainingTime]); - - if (!assign) { - return <>; - } - const timeDifference = calculateDueDate(assign.dueDate); - - return ( - { - window.open(assign.url, '_blank'); - }} - > - -
    -
    -

    {assign.courseTitle}

    -
    -
    {assign.title}
    -
    - -
    - - {assign.isSubmit ? '제출완료' : '제출필요'} - - - - - - {assign.dueDate} - - - - {calculateRemainingTime(assign.dueDate)} - - - -
    -
    -
    - ); -}; - -export default AssignCard; diff --git a/src/option/components/AssignContent.tsx b/src/option/components/AssignContent.tsx deleted file mode 100644 index 2c741c0..0000000 --- a/src/option/components/AssignContent.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { Card, CardContent } from '@/components/ui/card'; -import { useEffect, useState } from 'react'; -import { Assign } from '@/content/types'; -import { loadDataFromStorage } from '@/lib/storage'; -import AssignCard from './AssignCard'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import thung from '@/assets/thung.png'; - -export function AssignContent() { - const [assignArray, setAssignArray] = useState([]); - - useEffect(() => { - loadDataFromStorage('assign', (data: string | null) => { - if (!data) return; - - let parsedData: Assign[]; - if (typeof data === 'string') { - try { - parsedData = JSON.parse(data); - } catch (error) { - console.error('JSON 파싱 에러:', error); - return; - } - } else { - parsedData = data as Assign[]; - } - - const sortedAssignArray = parsedData.sort((a, b) => { - // 미제출 우선 배치 - if (!a.isSubmit && b.isSubmit) return -1; - if (a.isSubmit && !b.isSubmit) return 1; - - // 마감 빠른 순 (null은 맨 뒤) - const dateA = a.dueDate === null ? Number.MAX_SAFE_INTEGER : new Date(a.dueDate).getTime(); - const dateB = b.dueDate === null ? Number.MAX_SAFE_INTEGER : new Date(b.dueDate).getTime(); - if (dateA !== dateB) return dateA - dateB; - - return a.courseTitle.localeCompare(b.courseTitle); - }); - - setAssignArray(sortedAssignArray); - }); - }, []); - - return ( -
    - - {assignArray.length === 0 ? ( -
    - -
    - ) : ( - - -
    - {assignArray.map((assign) => { - const key = `${assign.courseId}-${assign.title}-${assign.dueDate}`; - return ; - })} -
    -
    -
    - )} -
    -
    - ); -} diff --git a/src/option/components/CourseDetailModal.tsx b/src/option/components/CourseDetailModal.tsx deleted file mode 100644 index fdcb39a..0000000 --- a/src/option/components/CourseDetailModal.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import ReactDOM from 'react-dom'; -import { useEffect, useState } from 'react'; -import { Button } from '@/components/ui/button'; -import type { Vod } from '@/content/types'; -import { Card, CardContent } from '@/components/ui/card'; -import { BadgeCheck, Siren, TriangleAlert, Video, X } from 'lucide-react'; -import { calculateRemainingTimeByRange, calculateTimeDifference, formatDateString } from '@/lib/utils'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import type React from 'react'; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; -import { Badge } from '@/components/ui/badge'; - -interface ModalProps { - vodList: Vod[]; - onClose: () => void; -} - -const CourseDetailModal: React.FC = ({ vodList, onClose }: ModalProps) => { - const [isVisible, setIsVisible] = useState(false); - const [showRemainingTime, setShowRemainingTime] = useState(false); - - useEffect(() => { - setIsVisible(true); - return () => { - setIsVisible(false); - }; - }, []); - - useEffect(() => { - let timer: NodeJS.Timeout; - if (showRemainingTime) { - timer = setTimeout(() => { - setShowRemainingTime(false); - }, 5000); - } - return () => clearTimeout(timer); - }, [showRemainingTime]); - - const handleClose = () => { - setIsVisible(false); - setTimeout(() => { - onClose(); - }, 300); - }; - - const timeDifference = calculateTimeDifference(vodList[0].range); - - const modalContent = ( -
    -
    e.stopPropagation()} - > -
    - -
    -

    {vodList[0].courseTitle}

    -

    {vodList[0].subject}

    -
    - - -
    - {vodList.map((vod, index) => { - const isAttendance = vod.isAttendance.toLowerCase().trim() === 'o'; - return ( - { - window.open(`${vod.url.replace('view', 'viewer')}`, '_blank', 'VodContentWindow'); - }} - > - - - - ); - })} -
    -
    -
    - - - - - 출석인정기간 : {formatDateString(vodList[0].range)} - - - - {calculateRemainingTimeByRange(vodList[0].range)} - - - -
    -
    -
    -
    - ); - - const modalRoot = document.getElementById('shadow-modal-root'); - if (!modalRoot) { - console.error('Modal root element not found.'); - return null; - } - - return ReactDOM.createPortal(modalContent, modalRoot); -}; - -export default CourseDetailModal; diff --git a/src/option/components/CourseHideModal.tsx b/src/option/components/CourseHideModal.tsx deleted file mode 100644 index 8ca515a..0000000 --- a/src/option/components/CourseHideModal.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import ReactDOM from 'react-dom'; -import { useEffect, useState } from 'react'; -import type { Vod } from '@/content/types'; -import type React from 'react'; -import { Button } from '@/components/ui/button'; - -interface ModalProps { - vod: Vod; - onClose: () => void; -} - -const CourseHideModal: React.FC = ({ vod, onClose }) => { - const [isVisible, setIsVisible] = useState(false); - - useEffect(() => { - setIsVisible(true); - return () => { - setIsVisible(false); - }; - }, []); - - const handleClose = () => { - setIsVisible(false); - setTimeout(() => { - onClose(); - }, 300); - }; - - const handleHideCourse = () => { - handleClose(); - }; - - const modalContent = ( -
    -
    e.stopPropagation()} // 모달 클릭 시 닫히지 않도록 방지 - > -

    해당 강좌를 숨기시겠습니까?

    -

    - {vod.courseTitle} - {vod.subject} -

    - -
    - - -
    -
    -
    - ); - - const modalRoot = document.getElementById('shadow-modal-root'); - if (!modalRoot) { - console.error('Modal root element not found.'); - return null; - } - - return ReactDOM.createPortal(modalContent, modalRoot); -}; - -export default CourseHideModal; diff --git a/src/option/components/CustomTabs.tsx b/src/option/components/CustomTabs.tsx deleted file mode 100644 index a95aa29..0000000 --- a/src/option/components/CustomTabs.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { cn } from '@/lib/utils'; - -interface CustomTabsProps { - tabs: string[]; - activeTab: string; - onTabChange: (tab: string) => void; -} - -export function CustomTabs({ tabs, activeTab, onTabChange }: CustomTabsProps) { - return ( -
    - {tabs.map((tab) => ( - - ))} -
    - ); -} diff --git a/src/option/components/QuizCard.tsx b/src/option/components/QuizCard.tsx deleted file mode 100644 index 74d08c7..0000000 --- a/src/option/components/QuizCard.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { useEffect, useState } from 'react'; -import { Card, CardContent } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { Quiz } from '@/content/types'; - -import { calculateDueDate, calculateRemainingTime } from '@/lib/utils'; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; - -interface TaskStatusCardProps { - quiz: Quiz; -} - -const QuizCard: React.FC = ({ quiz }) => { - const [showRemainingTime, setShowRemainingTime] = useState(false); - - useEffect(() => { - let timer: NodeJS.Timeout; - if (showRemainingTime) { - timer = setTimeout(() => { - setShowRemainingTime(false); - }, 5000); - } - return () => clearTimeout(timer); - }, [showRemainingTime]); - - if (!quiz) { - return <>; - } - - const timeDifference = calculateDueDate(quiz.dueDate); - - return ( - { - window.open(quiz.url, '_blank'); - }} - > - -
    -
    -

    {quiz.courseTitle}

    -
    -
    {quiz.title}
    -
    - -
    - - 직접확인 - - - - - - {quiz.dueDate} - - - - {calculateRemainingTime(quiz.dueDate)} - - - -
    -
    -
    - ); -}; - -export default QuizCard; diff --git a/src/option/components/QuizContent.tsx b/src/option/components/QuizContent.tsx deleted file mode 100644 index 7724c9d..0000000 --- a/src/option/components/QuizContent.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { Card, CardContent } from '@/components/ui/card'; -import { useEffect, useState } from 'react'; -import { Quiz } from '@/content/types'; -import { loadDataFromStorage } from '@/lib/storage'; -import QuizCard from './QuizCard'; -import { ScrollArea } from '@radix-ui/react-scroll-area'; -import thung from '@/assets/thung.png'; - -export function QuizContent() { - const [quizArray, setQuizArray] = useState([]); - - useEffect(() => { - loadDataFromStorage('quiz', (data: string | null) => { - if (!data) return; - - let parsedData: Quiz[]; - if (typeof data === 'string') { - try { - parsedData = JSON.parse(data); - } catch (error) { - console.error('JSON 파싱 에러:', error); - return; - } - } else { - parsedData = data as Quiz[]; - } - - const sortedQuizArray = parsedData.sort((a, b) => { - // 마감 빠른 순 (null은 맨 뒤) - const dateA = a.dueDate === null ? Number.MAX_SAFE_INTEGER : new Date(a.dueDate).getTime(); - const dateB = b.dueDate === null ? Number.MAX_SAFE_INTEGER : new Date(b.dueDate).getTime(); - if (dateA !== dateB) return dateA - dateB; - - return a.courseTitle.localeCompare(b.courseTitle); - }); - - setQuizArray(sortedQuizArray); - }); - }, []); - - return ( -
    - - {quizArray.length === 0 ? ( -
    - -
    - ) : ( - - -
    - {quizArray.map((quiz) => { - const key = `${quiz.courseId}-${quiz.title}-${quiz.dueDate}`; - return ; - })} -
    -
    -
    - )} -
    -
    - ); -} diff --git a/src/option/components/Sidebar.tsx b/src/option/components/Sidebar.tsx deleted file mode 100644 index ad90465..0000000 --- a/src/option/components/Sidebar.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { SidebarHeader } from './SidebarHeader'; -import { TYPES } from '@/content/types'; -import { data } from './data'; - -export function Sidebar({ activeTab, setActiveTab }: { activeTab: TYPES; setActiveTab: (type: TYPES) => void }) { - return ( - - ); -} diff --git a/src/option/components/SidebarHeader.tsx b/src/option/components/SidebarHeader.tsx deleted file mode 100644 index dbd4f7a..0000000 --- a/src/option/components/SidebarHeader.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import icon from '@/assets/icon.png'; - -export function SidebarHeader() { - return ( -
    - -
    - ); -} diff --git a/src/option/components/VodCard.tsx b/src/option/components/VodCard.tsx deleted file mode 100644 index 4484b07..0000000 --- a/src/option/components/VodCard.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { useState } from 'react'; -import { Card, CardContent } from '@/components/ui/card'; -import { Progress } from '@/components/ui/progress'; -import { Badge } from '@/components/ui/badge'; -import { Vod } from '@/content/types'; - -import { calculateRemainingTimeByRange, formatDateString, removeSquareBrackets } from '@/lib/utils'; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; -import CourseDetailModal from './CourseDetailModal'; - -interface TaskStatusCardProps { - vodList: Vod[]; -} - -const VodCard: React.FC = ({ vodList }) => { - const [isVisible, setIsVisible] = useState(false); - - if (vodList.length === 0) return <>; - - let value = 0; - vodList.forEach((vod) => { - if (vod.isAttendance.toLowerCase() === 'o') value += 1; - }); - const total = (value * 100) / vodList.length; - - - return ( - <> - {isVisible && ( - { - setIsVisible(false); - }} - /> - )} - { - setIsVisible(true); - }} - > - -
    -
    -

    - {removeSquareBrackets(vodList[0].courseTitle)} -

    -
    -
    {vodList[0].subject}
    -
    - -
    - - {total === 0 ? '학습전' : vodList[0].weeklyAttendance.toLowerCase() === 'o' ? '학습완료' : '학습중'} - - - - - - {formatDateString(vodList[0].range)} - - - - {calculateRemainingTimeByRange(vodList[0].range)} - - - -
    -
    -
    - 진도율 - {Math.round(total)}% -
    - -
    -
    -
    - - ); -}; - -export default VodCard; diff --git a/src/option/components/VodContent.tsx b/src/option/components/VodContent.tsx deleted file mode 100644 index 9871b6d..0000000 --- a/src/option/components/VodContent.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { Card, CardContent } from '@/components/ui/card'; -import { useEffect, useState } from 'react'; -import { Vod } from '@/content/types'; -import { loadDataFromStorage } from '@/lib/storage'; -import VodCard from './VodCard'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import thung from '@/assets/thung.png'; -import { isCurrentDateInRange } from '@/lib/utils'; - -export function VodContent() { - const [vodArray, setVodArray] = useState([]); - useEffect(() => { - loadDataFromStorage('vod', (data: string | null) => { - if (!data) return; - - let parsedData: Vod[]; - if (typeof data === 'string') { - try { - parsedData = JSON.parse(data); - } catch (error) { - console.error('JSON 파싱 에러:', error); - return; - } - } else { - parsedData = data as Vod[]; - } - - const groupedData = parsedData.reduce( - (acc, item) => { - const key = `${item.courseId}-${item.subject}-${item.range}`; - if (!acc[key]) { - acc[key] = []; - } - acc[key].push(item); - return acc; - }, - {} as Record - ); - - const sortedVodGroups = Object.values(groupedData).sort((groupA, groupB) => { - const firstA = groupA[0]; - const firstB = groupB[0]; - - const isAX = firstA.weeklyAttendance.toUpperCase().startsWith('X'); - const isBX = firstB.weeklyAttendance.toUpperCase().startsWith('X'); - - // X가 있는 항목을 먼저 정렬 - if (isAX && !isBX) return -1; - if (!isAX && isBX) return 1; - - const rangeA = firstA.range; - const rangeB = firstB.range; - const isRangeANull = rangeA === null; - const isRangeBNull = rangeB === null; - - // isCurrentDateInRange가 true인 항목을 먼저 정렬 (X와 O 모두) - const isCurrentDateInRangeA = isCurrentDateInRange(firstA.range); - const isCurrentDateInRangeB = isCurrentDateInRange(firstB.range); - - if (isAX) { - // X일 때는 isCurrentDateInRange가 true인 항목을 먼저 배치, 그 다음 null - if (isCurrentDateInRangeA && !isCurrentDateInRangeB) return -1; - if (!isCurrentDateInRangeA && isCurrentDateInRangeB) return 1; - if (isRangeANull && !isRangeBNull) return 1; - if (!isRangeANull && isRangeBNull) return -1; - } else { - // O일 때는 isCurrentDateInRange가 true인 항목을 먼저 배치, 그 다음 null, 그 다음 시간순 정렬 - if (isCurrentDateInRangeA && !isCurrentDateInRangeB) return -1; - if (!isCurrentDateInRangeA && isCurrentDateInRangeB) return 1; - if (isRangeANull && !isRangeBNull) return 1; - if (!isRangeANull && isRangeBNull) return -1; - - // rangeStart 날짜 기준으로 시간순으로 정렬 - if (!isRangeANull && !isRangeBNull) { - const rangeStartA = rangeA.split(' ~ ')[0]; - const rangeStartB = rangeB.split(' ~ ')[0]; - const dateA = new Date(rangeStartA); - const dateB = new Date(rangeStartB); - - if (dateA < dateB) return -1; - if (dateA > dateB) return 1; - } - } - - // courseTitle로 기본 정렬 - if (firstA.courseTitle < firstB.courseTitle) return -1; - if (firstA.courseTitle > firstB.courseTitle) return 1; - - return 0; - }); - - setVodArray(sortedVodGroups); - }); - }, []); - - return ( -
    - - {vodArray.length === 0 ? ( -
    - -
    - ) : ( - - -
    - {vodArray.map((vodGroup, index) => { - return ; - })} -
    -
    -
    - )} -
    -
    - ); -} diff --git a/src/option/components/data.tsx b/src/option/components/data.tsx deleted file mode 100644 index 58efcbe..0000000 --- a/src/option/components/data.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { TYPES } from '@/content/types'; -import { NotebookText, Video, Zap } from 'lucide-react'; - -export const data = [ - { - title: '강의', - icon: