diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..ce0e08b
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,10 @@
+APP_NAME=Openscreen
+BUNDLE_ID=com.siddharthvaddem.openscreen
+
+APPLE_ID=
+TEAM_ID=
+SIGN_IDENTITY="Developer ID Application: Samir Patil ()"
+CSC_NAME="Samir Patil ()"
+
+NOTARY_PROFILE=OpenScreen-notary
+APPLE_APP_SPECIFIC_PASSWORD=
diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..3550a30
--- /dev/null
+++ b/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..cfee36d
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @siddharthvaddem
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 5638ffc..f42a92d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -3,6 +3,16 @@ name: Build Electron App
on:
workflow_dispatch:
+ inputs:
+ arch:
+ description: 'Architecture to build'
+ required: true
+ default: 'both'
+ type: choice
+ options:
+ - arm64
+ - x64
+ - both
jobs:
build-windows:
@@ -36,38 +46,180 @@ jobs:
build-macos:
runs-on: macos-latest
+ strategy:
+ matrix:
+ arch: ${{ github.event.inputs.arch == 'both' && fromJSON('["arm64", "x64"]') || fromJSON(format('["{0}"]', github.event.inputs.arch)) }}
+
steps:
+ # ─── Checkout ─────────────────────────────────────────────
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
+ # ─── Setup Node.js ────────────────────────────────────────
- name: Setup Node.js
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
- node-version: '22'
+ node-version: 22
+ cache: npm
+ # ─── Setup Python (needed by some native deps) ────────────
- name: Setup Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: '3.11'
+ # ─── Install Dependencies ─────────────────────────────────
- name: Install dependencies
run: npm ci
- - name: Install app dependencies
- run: npx electron-builder install-app-deps
-
- - name: Build macOS app
- run: npm run build:mac
+ # ─── Import Code Signing Certificate ──────────────────────
+ # This is the KEY step that makes CI signing work.
+ # We create a temporary keychain, import the .p12 cert into it,
+ # and set it as the default so codesign can find it.
+ - name: Import code signing certificate
env:
+ MAC_CERTIFICATE_P12: ${{ secrets.MAC_CERTIFICATE_P12 }}
+ MAC_CERTIFICATE_PASSWORD: ${{ secrets.MAC_CERTIFICATE_PASSWORD }}
+ run: |
+ # Create a temporary keychain
+ KEYCHAIN_PATH=$RUNNER_TEMP/build.keychain-db
+ KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
+
+ # Create and configure keychain
+ security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
+ security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
+ security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
+
+ # Decode and import certificate
+ echo "$MAC_CERTIFICATE_P12" | base64 --decode > $RUNNER_TEMP/certificate.p12
+ security import $RUNNER_TEMP/certificate.p12 \
+ -k "$KEYCHAIN_PATH" \
+ -P "$MAC_CERTIFICATE_PASSWORD" \
+ -T /usr/bin/codesign \
+ -T /usr/bin/security
+
+ # Allow codesign to access the keychain without UI prompt
+ security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
+
+ # Add to keychain search path (makes it the default)
+ security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"')
+
+ # Verify the identity is available
+ security find-identity -v -p codesigning "$KEYCHAIN_PATH"
+
+ # Clean up the .p12 file
+ rm -f $RUNNER_TEMP/certificate.p12
+
+ # ─── Build Vite + Electron ────────────────────────────────
+ - name: Build Vite + Electron
+ run: npx tsc && npx vite build
+
+ # ─── Package with electron-builder ────────────────────────
+ # electron-builder handles deep codesigning the .app bundle
+ # "notarize: false" in electron-builder.json5 prevents it from
+ # trying its own notarization flow
+ - name: Package .app bundle
+ run: npx electron-builder --mac --${{ matrix.arch }} --dir
+ env:
+ CSC_NAME: "Samir Patil (N26FZ4GW28)"
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- - name: Upload macOS build
+ # ─── Read version from package.json ───────────────────────
+ - name: Get version
+ id: version
+ run: echo "version=$(node -p 'require(\"./package.json\").version')" >> $GITHUB_OUTPUT
+
+ # ─── Locate the .app bundle ───────────────────────────────
+ - name: Find .app bundle
+ id: find_app
+ run: |
+ VERSION="${{ steps.version.outputs.version }}"
+ echo "=== Release directory contents ==="
+ ls -laR "release/${VERSION}/" || echo "release/${VERSION}/ not found"
+ echo "=== Searching for .app bundle ==="
+ APP_BUNDLE=$(find "release/${VERSION}" -maxdepth 4 -name "*.app" -type d | head -n1)
+ if [ -z "$APP_BUNDLE" ]; then
+ echo "::error::No .app bundle found in release/${VERSION}/"
+ exit 1
+ fi
+ echo "app_bundle=$APP_BUNDLE" >> $GITHUB_OUTPUT
+ echo "Found: $APP_BUNDLE"
+
+ # ─── Verify .app signature ────────────────────────────────
+ - name: Verify .app code signature
+ run: codesign --verify --deep --strict "${{ steps.find_app.outputs.app_bundle }}"
+
+ # ─── Create DMG ───────────────────────────────────────────
+ - name: Create DMG
+ id: dmg
+ run: |
+ VERSION="${{ steps.version.outputs.version }}"
+ ARCH="${{ matrix.arch }}"
+ DMG_NAME="Openscreen-Mac-${ARCH}-${VERSION}.dmg"
+ RELEASE_DIR="release/${VERSION}"
+ DMG_OUTPUT="${RELEASE_DIR}/${DMG_NAME}"
+ STAGING="${RELEASE_DIR}/dmg-staging"
+
+ mkdir -p "$STAGING"
+ cp -R "${{ steps.find_app.outputs.app_bundle }}" "$STAGING/"
+ ln -s /Applications "$STAGING/Applications"
+
+ hdiutil create \
+ -srcfolder "$STAGING" \
+ -volname "Openscreen" \
+ -fs HFS+ \
+ -fsargs "-c c=64,a=16,e=16" \
+ -format UDBZ \
+ "$DMG_OUTPUT"
+
+ rm -rf "$STAGING"
+
+ echo "dmg_path=$DMG_OUTPUT" >> $GITHUB_OUTPUT
+ echo "dmg_name=$DMG_NAME" >> $GITHUB_OUTPUT
+
+ # ─── Sign DMG ─────────────────────────────────────────────
+ - name: Sign DMG
+ run: |
+ codesign --force \
+ --sign "Developer ID Application: Samir Patil (N26FZ4GW28)" \
+ --timestamp \
+ "${{ steps.dmg.outputs.dmg_path }}"
+
+ # ─── Notarize DMG ────────────────────────────────────────
+ # On CI we can't use keychain profiles for notarytool, so we
+ # pass credentials directly via env vars / flags
+ - name: Notarize DMG
+ run: |
+ xcrun notarytool submit "${{ steps.dmg.outputs.dmg_path }}" \
+ --apple-id "${{ secrets.APPLE_ID }}" \
+ --team-id "${{ secrets.APPLE_TEAM_ID }}" \
+ --password "${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}" \
+ --wait
+ timeout-minutes: 15
+
+ # ─── Staple ───────────────────────────────────────────────
+ - name: Staple notarization ticket
+ run: xcrun stapler staple "${{ steps.dmg.outputs.dmg_path }}"
+
+ # ─── Validate ─────────────────────────────────────────────
+ - name: Validate stapled DMG
+ run: |
+ xcrun stapler validate "${{ steps.dmg.outputs.dmg_path }}"
+ spctl -a -vv -t install "${{ steps.dmg.outputs.dmg_path }}"
+
+ # ─── Upload Artifact ──────────────────────────────────────
+ - name: Upload notarized DMG
uses: actions/upload-artifact@v4
with:
- name: macos-installer
- path: release/**/*.dmg
+ name: openscreen-mac-${{ matrix.arch }}
+ path: ${{ steps.dmg.outputs.dmg_path }}
retention-days: 30
+ # ─── Cleanup Keychain ─────────────────────────────────────
+ - name: Cleanup keychain
+ if: always()
+ run: security delete-keychain $RUNNER_TEMP/build.keychain-db || true
+
build-linux:
runs-on: ubuntu-latest
steps:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b2b04db..4194797 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -31,6 +31,19 @@ jobs:
- run: npm ci
- run: npx tsc --noEmit
+ test:
+ name: Test
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: npm
+ - run: npm ci
+ - run: npm run test:browser:install
+ - run: npm run test:browser
+
build:
name: Build
runs-on: ubuntu-latest
diff --git a/.github/workflows/discord.yaml b/.github/workflows/discord.yaml
new file mode 100644
index 0000000..6da25d0
--- /dev/null
+++ b/.github/workflows/discord.yaml
@@ -0,0 +1,519 @@
+name: PR to Discord Forum
+
+on:
+ pull_request_target:
+ types: [opened, reopened, ready_for_review, converted_to_draft, synchronize, edited, labeled, unlabeled, closed]
+ pull_request_review:
+ types: [submitted]
+ issue_comment:
+ types: [created]
+ schedule:
+ - cron: "0 12 * * 1"
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ pull-requests: write
+ issues: read
+
+jobs:
+ notify:
+ if: github.event_name != 'schedule' && github.actor != 'github-actions[bot]'
+ concurrency:
+ group: discord-pr-sync-${{ github.repository }}-${{ github.event.pull_request.number || github.event.issue.number || github.run_id }}
+ cancel-in-progress: false
+ runs-on: ubuntu-latest
+ steps:
+ - name: Sync PR activity to Discord forum thread
+ id: sync
+ uses: actions/github-script@v7
+ env:
+ DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
+ DISCORD_PR_FORUM_WEBHOOK: ${{ secrets.DISCORD_PR_FORUM_WEBHOOK }}
+ DISCORD_WEBHOOK_USERNAME: ${{ secrets.DISCORD_WEBHOOK_USERNAME }}
+ DISCORD_WEBHOOK_AVATAR_URL: ${{ secrets.DISCORD_WEBHOOK_AVATAR_URL }}
+ DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }}
+ DISCORD_REVIEWER_ROLE_ID: ${{ secrets.DISCORD_REVIEWER_ROLE_ID }}
+ DISCORD_ALERT_WEBHOOK_URL: ${{ secrets.DISCORD_ALERT_WEBHOOK_URL }}
+ with:
+ script: |
+ const WEBHOOK_USERNAME = (process.env.DISCORD_WEBHOOK_USERNAME || "OpenScreen").trim();
+ const WEBHOOK_AVATAR = (process.env.DISCORD_WEBHOOK_AVATAR_URL || "").trim();
+
+ const THREAD_MARKER_REGEX = //i;
+ const webhookUrl = (process.env.DISCORD_WEBHOOK_URL || process.env.DISCORD_PR_FORUM_WEBHOOK || "").trim();
+ const botToken = (process.env.DISCORD_BOT_TOKEN || "").trim();
+ const reviewerRoleId = (process.env.DISCORD_REVIEWER_ROLE_ID || "").trim();
+ const alertWebhookUrl = (process.env.DISCORD_ALERT_WEBHOOK_URL || "").trim();
+
+ const TAGS = {
+ open: "1493976692967080096",
+ draft: "1493976782028935279",
+ ready: "1493976833626996756",
+ changes: "1493976909875515564",
+ approved: "1493976951038152764",
+ merged: "1493977049709281320",
+ closed: "1493977108102516786",
+ };
+
+ const labelTagMap = {
+ bug: "1493977562773458975",
+ enhancement: "1493977619216207993",
+ documentation: "1493978565153394830",
+ };
+
+ function cleanDescription(text, maxLen = 3500) {
+ if (!text) return "No description provided.";
+ const normalized = text
+ .replace(/\r\n/g, "\n")
+ .replace(/\n{3,}/g, "\n\n")
+ .trim();
+ if (normalized.length <= maxLen) return normalized;
+ return `${normalized.slice(0, maxLen - 1)}…`;
+ }
+
+ function trimThreadName(name) {
+ return name.length > 95 ? name.slice(0, 95) : name;
+ }
+
+ function extractThreadId(body) {
+ if (!body) return null;
+ const match = body.match(THREAD_MARKER_REGEX);
+ return match ? match[1] : null;
+ }
+
+ function upsertThreadMarker(body, threadId) {
+ const cleaned = (body || "").replace(THREAD_MARKER_REGEX, "").trim();
+ return `${cleaned}\n\n`.trim();
+ }
+
+ async function discordPost(payload, options = {}) {
+ const endpoint = new URL(webhookUrl);
+ endpoint.searchParams.set("wait", "true");
+ if (options.threadId) endpoint.searchParams.set("thread_id", String(options.threadId));
+
+ const response = await fetch(endpoint.toString(), {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ username: WEBHOOK_USERNAME,
+ avatar_url: WEBHOOK_AVATAR,
+ allowed_mentions: { parse: [] },
+ ...payload,
+ })
+ });
+
+ const contentType = (response.headers.get("content-type") || "").toLowerCase();
+ const text = await response.text();
+
+ if (!response.ok) {
+ throw new Error(`Discord API error ${response.status}: ${text}`);
+ }
+
+ if (!text) return {};
+ if (contentType.includes("application/json")) return JSON.parse(text);
+
+ // Some proxy/CDN edge responses may return HTML with 2xx; avoid crashing on JSON parse.
+ core.warning(`Discord webhook returned non-JSON response (content-type: ${contentType || "unknown"}).`);
+ return {};
+ }
+
+ async function patchDiscordThread(threadId, patchBody) {
+ if (!botToken || !threadId) return;
+ const response = await fetch(`https://discord.com/api/v10/channels/${threadId}`, {
+ method: "PATCH",
+ headers: {
+ "Authorization": `Bot ${botToken}`,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(patchBody),
+ });
+ if (!response.ok) {
+ const text = await response.text();
+ core.warning(`Discord thread patch failed (${response.status}): ${text}`);
+ }
+ }
+
+ function desiredStatusTag(prState) {
+ if (prState.merged && TAGS.merged) return TAGS.merged;
+ if (prState.closed && !prState.merged && TAGS.closed) return TAGS.closed;
+ if (prState.reviewState === "CHANGES_REQUESTED" && TAGS.changes) return TAGS.changes;
+ if (prState.reviewState === "APPROVED" && TAGS.approved) return TAGS.approved;
+ if (prState.draft && TAGS.draft) return TAGS.draft;
+ if (!prState.draft && TAGS.ready) return TAGS.ready;
+ return TAGS.open || null;
+ }
+
+ function tagIdsFromLabels(labels) {
+ const out = [];
+ for (const label of labels) {
+ const mapped = labelTagMap[label.toLowerCase()] || labelTagMap[label];
+ if (mapped) out.push(String(mapped));
+ }
+ return out;
+ }
+
+ async function getPullRequest() {
+ if (context.eventName === "pull_request_target" || context.eventName === "pull_request_review") {
+ return context.payload.pull_request || null;
+ }
+ if (context.eventName === "issue_comment") {
+ const issue = context.payload.issue;
+ if (!issue?.pull_request) return null;
+ const { data } = await github.rest.pulls.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: issue.number,
+ });
+ return data;
+ }
+ return null;
+ }
+
+ async function getReviewState(owner, repo, pullNumber) {
+ const { data } = await github.rest.pulls.listReviews({ owner, repo, pull_number: pullNumber, per_page: 100 });
+ let hasChanges = false;
+ let hasApproved = false;
+ for (const r of data) {
+ const s = (r.state || "").toUpperCase();
+ if (s === "CHANGES_REQUESTED") hasChanges = true;
+ if (s === "APPROVED") hasApproved = true;
+ }
+ if (hasChanges) return "CHANGES_REQUESTED";
+ if (hasApproved) return "APPROVED";
+ return "NONE";
+ }
+
+ async function sendFailureAlert(message) {
+ if (!alertWebhookUrl) return;
+ try {
+ await fetch(alertWebhookUrl, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ username: "OpenScreen",
+ avatar_url: WEBHOOK_AVATAR,
+ content: `⚠️ PR Discord sync failed\n${message}\nRun: ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
+ allowed_mentions: { parse: [] }
+ })
+ });
+ } catch {
+ core.warning("Failed to send failure alert webhook.");
+ }
+ }
+
+ try {
+ const pr = await getPullRequest();
+ if (!pr) {
+ core.info("No PR context found. Skipping.");
+ return;
+ }
+
+ if (!webhookUrl) {
+ const strictEvents = new Set(["pull_request_target", "workflow_dispatch"]);
+ const msg =
+ `Discord sync skipped: webhook secret unavailable for event '${context.eventName}'. ` +
+ "Set either DISCORD_WEBHOOK_URL or DISCORD_PR_FORUM_WEBHOOK in repository secrets.";
+ if (strictEvents.has(context.eventName)) {
+ core.setFailed(msg);
+ } else {
+ core.warning(msg);
+ }
+ return;
+ }
+
+ const action = context.payload.action || "";
+ const owner = context.repo.owner;
+ const repo = context.repo.repo;
+ const number = pr.number;
+ const title = pr.title;
+ const author = pr.user?.login || "unknown";
+ const url = pr.html_url;
+ const authorUrl = pr.user?.html_url || "";
+ const authorAvatar = pr.user?.avatar_url || "";
+ const base = pr.base?.ref || "";
+ const head = pr.head?.ref || "";
+ const repoFullName = pr.base?.repo?.full_name || `${owner}/${repo}`;
+ const labels = (pr.labels || []).map((l) => l.name);
+ const body = (pr.body || "").trim();
+ const reviewState = await getReviewState(owner, repo, number);
+
+ let threadId = extractThreadId(body);
+ const shouldCreateThread =
+ context.eventName === "pull_request_target" &&
+ ["opened", "reopened", "ready_for_review"].includes(action) &&
+ !threadId;
+
+ if (shouldCreateThread) {
+ const fields = [
+ { name: "PR", value: `[#${number}](${url})`, inline: true },
+ { name: "Author", value: `[${author}](${authorUrl || url})`, inline: true },
+ { name: "Status", value: pr.draft ? "Draft" : "Open", inline: true },
+ { name: "Branches", value: `\`${head}\` -> \`${base}\``, inline: true },
+ { name: "Changes", value: `+${pr.additions} / -${pr.deletions}`, inline: true },
+ { name: "Files Changed", value: String(pr.changed_files), inline: true }
+ ];
+
+ if (labels.length) {
+ fields.push({
+ name: "Labels",
+ value: labels.map((l) => `\`${l}\``).join(" "),
+ inline: false,
+ });
+ }
+
+ const statusTag = desiredStatusTag({ draft: pr.draft, reviewState, merged: false, closed: false });
+ const mappedLabelTags = tagIdsFromLabels(labels);
+ const appliedTags = [...new Set([statusTag, ...mappedLabelTags].filter(Boolean))];
+
+ const createPayload = {
+ content: action === "ready_for_review" ? "🔔 PR is now ready for review" : "🔔 New pull request opened",
+ thread_name: trimThreadName(`PR #${number} - ${title}`),
+ applied_tags: appliedTags,
+ embeds: [
+ {
+ title: `PR #${number}: ${title}`,
+ url,
+ description: cleanDescription(body),
+ color: pr.draft ? 15105570 : 1998671,
+ author: {
+ name: author,
+ url: authorUrl || undefined,
+ icon_url: authorAvatar || undefined,
+ },
+ fields,
+ footer: { text: repoFullName },
+ timestamp: new Date().toISOString(),
+ },
+ ],
+ };
+
+ const result = await discordPost(createPayload);
+ const createdThreadId = result.channel_id || null;
+ if (createdThreadId) {
+ const updatedBody = upsertThreadMarker(body, createdThreadId);
+ await github.rest.pulls.update({ owner, repo, pull_number: number, body: updatedBody });
+ core.info(`Created Discord thread ${createdThreadId} and stored mapping.`);
+ } else {
+ core.warning("Discord thread created but channel_id missing in response.");
+ }
+ return;
+ }
+
+ if (!threadId) {
+ core.info("No mapped Discord thread ID found; skipping update event.");
+ return;
+ }
+
+ if (context.eventName === "pull_request_target" && ["edited", "labeled", "unlabeled", "ready_for_review", "converted_to_draft"].includes(action)) {
+ const statusTag = desiredStatusTag({
+ draft: action === "converted_to_draft" ? true : pr.draft,
+ reviewState,
+ merged: false,
+ closed: false,
+ });
+ const mappedLabelTags = tagIdsFromLabels(labels);
+ const appliedTags = [...new Set([statusTag, ...mappedLabelTags].filter(Boolean))];
+ await patchDiscordThread(threadId, {
+ name: trimThreadName(`PR #${number} - ${title}`),
+ ...(appliedTags.length ? { applied_tags: appliedTags } : {}),
+ });
+ }
+
+ let updateMessage = null;
+ let updateEmbed = null;
+
+ if (context.eventName === "pull_request_target") {
+ if (action === "synchronize") {
+ const { data: commits } = await github.rest.pulls.listCommits({ owner, repo, pull_number: number, per_page: 5 });
+ const list = commits.map((c) => `- \`${c.sha.slice(0, 7)}\` ${c.commit.message.split("\n")[0]}`).join("\n") || "- No commit details";
+ updateMessage = `🧩 New commits pushed to PR #${number}`;
+ updateEmbed = {
+ title: `Commit Update • PR #${number}`,
+ url: `${url}/files`,
+ description: `${list}`,
+ color: 1998671,
+ footer: { text: repoFullName },
+ timestamp: new Date().toISOString(),
+ };
+ } else if (action === "edited") {
+ updateMessage = `✏️ PR #${number} details were edited`;
+ updateEmbed = {
+ title: `PR Updated • #${number}`,
+ url,
+ description: cleanDescription(body, 1200),
+ color: 1998671,
+ timestamp: new Date().toISOString(),
+ };
+ } else if (action === "closed") {
+ const isMerged = !!pr.merged;
+ const statusTag = desiredStatusTag({ draft: false, reviewState, merged: isMerged, closed: true });
+ const mappedLabelTags = tagIdsFromLabels(labels);
+ const appliedTags = [...new Set([statusTag, ...mappedLabelTags].filter(Boolean))];
+ await patchDiscordThread(threadId, {
+ ...(appliedTags.length ? { applied_tags: appliedTags } : {}),
+ ...(isMerged ? { archived: true, locked: true } : {}),
+ });
+
+ updateMessage = isMerged
+ ? `✅ PR #${number} was merged`
+ : `🛑 PR #${number} was closed without merge`;
+ updateEmbed = {
+ title: isMerged ? `Merged • PR #${number}` : `Closed • PR #${number}`,
+ url,
+ description: isMerged ? "This PR has been merged into the base branch." : "This PR was closed before merge.",
+ color: isMerged ? 5763719 : 15158332,
+ timestamp: new Date().toISOString(),
+ };
+ } else if (action === "ready_for_review") {
+ updateMessage = `🚀 PR #${number} moved from draft to ready for review`;
+ if (reviewerRoleId) updateMessage += ` <@&${reviewerRoleId}>`;
+ } else if (action === "converted_to_draft") {
+ updateMessage = `📝 PR #${number} converted to draft`;
+ }
+ } else if (context.eventName === "pull_request_review") {
+ const review = context.payload.review;
+ if (review) {
+ const state = (review.state || "commented").toUpperCase();
+ const reviewer = review.user?.login || "reviewer";
+ updateMessage = `🧪 Review ${state} by **${reviewer}** on PR #${number}`;
+ if (state === "CHANGES_REQUESTED" && reviewerRoleId) updateMessage += ` <@&${reviewerRoleId}>`;
+ updateEmbed = {
+ title: `Review ${state} • PR #${number}`,
+ url: review.html_url || url,
+ description: cleanDescription(review.body || "No review note.", 1000),
+ color: state === "APPROVED" ? 5763719 : state === "CHANGES_REQUESTED" ? 15158332 : 1998671,
+ timestamp: new Date().toISOString(),
+ };
+
+ if (state === "CHANGES_REQUESTED" || state === "APPROVED") {
+ const statusTag = desiredStatusTag({ draft: pr.draft, reviewState: state, merged: false, closed: false });
+ const mappedLabelTags = tagIdsFromLabels(labels);
+ const appliedTags = [...new Set([statusTag, ...mappedLabelTags].filter(Boolean))];
+ await patchDiscordThread(threadId, {
+ ...(appliedTags.length ? { applied_tags: appliedTags } : {}),
+ });
+ }
+ }
+ } else if (context.eventName === "issue_comment") {
+ const comment = context.payload.comment;
+ if (comment) {
+ const commenter = comment.user?.login || "user";
+ updateMessage = `💬 New comment by **${commenter}** on PR #${number}`;
+ updateEmbed = {
+ title: `New PR Comment • #${number}`,
+ url: comment.html_url || url,
+ description: cleanDescription(comment.body || "No comment body.", 1000),
+ color: 1998671,
+ timestamp: new Date().toISOString(),
+ };
+ }
+ }
+
+ if (!updateMessage && !updateEmbed) {
+ core.info("No Discord update message for this event/action. Skipping.");
+ return;
+ }
+
+ const payload = { content: updateMessage || "" };
+ if (updateEmbed) payload.embeds = [updateEmbed];
+ await discordPost(payload, { threadId });
+ core.info(`Posted update to Discord thread ${threadId}.`);
+ } catch (err) {
+ const msg = err && err.message ? err.message : String(err);
+ core.setFailed(msg);
+
+ const alertWebhook = process.env.DISCORD_ALERT_WEBHOOK_URL;
+ if (alertWebhook) {
+ try {
+ await fetch(alertWebhook, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ username: "OpenScreen",
+ avatar_url: WEBHOOK_AVATAR,
+ content: `⚠️ PR->Discord sync failed\n${msg}\nRun: ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
+ allowed_mentions: { parse: [] }
+ })
+ });
+ } catch {
+ core.warning("Failed to send alert webhook.");
+ }
+ }
+ }
+
+ weekly-contributor-leaderboard:
+ if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
+ runs-on: ubuntu-latest
+ steps:
+ - name: Post weekly contributor leaderboard
+ uses: actions/github-script@v7
+ env:
+ DISCORD_SPOTLIGHT_WEBHOOK_URL: ${{ secrets.DISCORD_SPOTLIGHT_WEBHOOK_URL }}
+ DISCORD_WEBHOOK_USERNAME: ${{ secrets.DISCORD_WEBHOOK_USERNAME }}
+ DISCORD_WEBHOOK_AVATAR_URL: ${{ secrets.DISCORD_WEBHOOK_AVATAR_URL }}
+ with:
+ script: |
+ const spotlightWebhook = (process.env.DISCORD_SPOTLIGHT_WEBHOOK_URL || "").trim();
+ const webhookUsername = (process.env.DISCORD_WEBHOOK_USERNAME || "OpenScreen").trim();
+ const webhookAvatar = (process.env.DISCORD_WEBHOOK_AVATAR_URL || "").trim();
+ if (!spotlightWebhook) {
+ core.info("DISCORD_SPOTLIGHT_WEBHOOK_URL missing. Skipping leaderboard post.");
+ return;
+ }
+
+ const since = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
+ const owner = context.repo.owner;
+ const repo = context.repo.repo;
+
+ const q = `repo:${owner}/${repo} is:pr is:merged merged:>=${since.substring(0, 10)}`;
+ const search = await github.rest.search.issuesAndPullRequests({
+ q,
+ per_page: 100,
+ });
+
+ const counter = new Map();
+ for (const item of search.data.items) {
+ const login = item.user?.login;
+ if (!login) continue;
+ counter.set(login, (counter.get(login) || 0) + 1);
+ }
+
+ const ranked = [...counter.entries()]
+ .sort((a, b) => b[1] - a[1])
+ .slice(0, 10);
+
+ const totalMerged = search.data.items.length;
+ const lines = ranked.length
+ ? ranked.map(([user, count], idx) => `${idx + 1}. **${user}** - ${count} merged PR(s)`).join("\n")
+ : "No merged PRs this week.";
+
+ const payload = {
+ username: webhookUsername,
+ ...(webhookAvatar ? { avatar_url: webhookAvatar } : {}),
+ embeds: [
+ {
+ title: "🌟 Weekly Contributor Leaderboard",
+ description: lines,
+ color: 1998671,
+ fields: [
+ { name: "Merged PRs (7d)", value: String(totalMerged), inline: true },
+ { name: "Repository", value: `${owner}/${repo}`, inline: true },
+ { name: "Period", value: "Last 7 days", inline: true }
+ ],
+ timestamp: new Date().toISOString()
+ }
+ ],
+ allowed_mentions: { parse: [] }
+ };
+
+ const res = await fetch(`${spotlightWebhook}?wait=true`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(payload)
+ });
+
+ if (!res.ok) {
+ const txt = await res.text();
+ core.setFailed(`Leaderboard post failed ${res.status}: ${txt}`);
+ }
diff --git a/.gitignore b/.gitignore
index 70cc387..771c4bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,9 +12,11 @@ dist
dist-electron
dist-ssr
*.local
+.env
# Editor directories and files
.vscode/*
+.zed/
!.vscode/extensions.json
.idea
.DS_Store
@@ -25,8 +27,19 @@ dist-ssr
*.sw?
release/**
*.kiro/
+.claude/
# npx electron-builder --mac --win
# Playwright
test-results
-playwright-report/
\ No newline at end of file
+playwright-report/
+
+# Vitest browser mode screenshots
+__screenshots__/
+
+# shell files
+/shell.sh
+# Nix
+result
+result-*
+.direnv/
diff --git a/README.md b/README.md
index b42355e..9ed0d1a 100644
--- a/README.md
+++ b/README.md
@@ -5,12 +5,15 @@
+
+
+
-
+
@@ -93,10 +96,25 @@ System audio capture relies on Electron's [desktopCapturer](https://www.electron
_I'm new to open source, idk what I'm doing lol. If something is wrong please raise an issue 🙏_
+## Documentation
+
+See the documentation here:
+[OpenScreen Docs](https://deepwiki.com/siddharthvaddem/openscreen)
+
## Contributing
Contributions are welcome! If you’d like to help out or see what’s currently being worked on, take a look at the open issues and the [project roadmap](https://github.com/users/siddharthvaddem/projects/3) to understand the current direction of the project and find ways to contribute.
+## Star History
+
+
+
+
+
+
+
+
+
## License
This project is licensed under the [MIT License](./LICENSE). By using this software, you agree that the authors are not liable for any issues, damages, or claims arising from its use.
diff --git a/biome.json b/biome.json
index c4c22f6..517be72 100644
--- a/biome.json
+++ b/biome.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://biomejs.dev/schemas/2.3.13/schema.json",
+ "$schema": "https://biomejs.dev/schemas/2.4.12/schema.json",
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
"files": { "ignoreUnknown": false, "includes": ["**", "!**/*.css"] },
"formatter": {
diff --git a/electron-builder.json5 b/electron-builder.json5
index 40fce0a..18498df 100644
--- a/electron-builder.json5
+++ b/electron-builder.json5
@@ -20,16 +20,18 @@
"!CONTRIBUTING.md",
"!LICENSE"
],
- "extraResources": [
- {
- "from": "public/wallpapers",
- "to": "assets/wallpapers"
- }
- ],
- "publish": [{"provider": "github"}],
-
- "mac": {
- "hardenedRuntime": false,
+ "extraResources": [
+ {
+ "from": "public/wallpapers",
+ "to": "assets/wallpapers"
+ }
+ ],
+
+ "mac": {
+ "notarize": false,
+ "hardenedRuntime": true,
+ "entitlements": "macos.entitlements",
+ "entitlementsInherit": "macos.entitlements",
"target": [
{
"target": "dmg",
@@ -38,13 +40,13 @@
],
"icon": "icons/icons/mac/icon.icns",
"artifactName": "${productName}-Mac-${arch}-${version}-Installer.${ext}",
- "extendInfo": {
- "NSAudioCaptureUsageDescription": "OpenScreen needs audio capture permission to record system audio.",
- "NSMicrophoneUsageDescription": "OpenScreen needs microphone access to record voice audio.",
- "NSCameraUsageDescription": "OpenScreen needs camera access to record webcam video.",
- "NSCameraUseContinuityCameraDeviceType": true,
- "com.apple.security.device.audio-input": true
- }
+ "extendInfo": {
+ "NSAudioCaptureUsageDescription": "OpenScreen needs audio capture permission to record system audio.",
+ "NSMicrophoneUsageDescription": "OpenScreen needs microphone access to record voice audio.",
+ "NSCameraUsageDescription": "OpenScreen needs camera access to record webcam video.",
+ "NSCameraUseContinuityCameraDeviceType": true,
+ "com.apple.security.device.audio-input": true
+ }
},
"linux": {
"target": [
@@ -54,14 +56,14 @@
"artifactName": "${productName}-Linux-${version}.${ext}",
"category": "AudioVideo"
},
- "win": {
- "target": [
- "nsis"
- ],
- "icon": "icons/icons/win/icon.ico"
- },
- "nsis": {
- "oneClick": false,
- "allowToChangeInstallationDirectory": true
- }
-}
+ "win": {
+ "target": [
+ "nsis"
+ ],
+ "icon": "icons/icons/win/icon.ico"
+ },
+ "nsis": {
+ "oneClick": false,
+ "allowToChangeInstallationDirectory": true
+ }
+}
diff --git a/electron/electron-env.d.ts b/electron/electron-env.d.ts
index 573aee8..e20cf7f 100644
--- a/electron/electron-env.d.ts
+++ b/electron/electron-env.d.ts
@@ -26,6 +26,8 @@ interface Window {
electronAPI: {
getSources: (opts: Electron.SourcesOptions) => Promise;
switchToEditor: () => Promise;
+ switchToHud: () => Promise;
+ startNewRecording: () => Promise<{ success: boolean; error?: string }>;
openSourceSelector: () => Promise;
selectSource: (source: ProcessedDesktopSource) => Promise;
getSelectedSource: () => Promise;
@@ -133,6 +135,10 @@ interface Window {
saveShortcuts: (shortcuts: unknown) => Promise<{ success: boolean; error?: string }>;
hudOverlayHide: () => void;
hudOverlayClose: () => void;
+ showCountdownOverlay: (value: number, runId: number) => Promise;
+ setCountdownOverlayValue: (value: number, runId: number) => Promise;
+ hideCountdownOverlay: (runId: number) => Promise;
+ onCountdownOverlayValue: (callback: (value: number | null) => void) => () => void;
setMicrophoneExpanded: (expanded: boolean) => void;
setHasUnsavedChanges: (hasChanges: boolean) => void;
onRequestSaveBeforeClose: (callback: () => Promise | boolean) => () => void;
diff --git a/electron/i18n.ts b/electron/i18n.ts
index b385008..2dfb4d3 100644
--- a/electron/i18n.ts
+++ b/electron/i18n.ts
@@ -5,10 +5,12 @@ import commonEn from "../src/i18n/locales/en/common.json";
import dialogsEn from "../src/i18n/locales/en/dialogs.json";
import commonEs from "../src/i18n/locales/es/common.json";
import dialogsEs from "../src/i18n/locales/es/dialogs.json";
+import commonFr from "../src/i18n/locales/fr/common.json";
+import dialogsFr from "../src/i18n/locales/fr/dialogs.json";
import commonZh from "../src/i18n/locales/zh-CN/common.json";
import dialogsZh from "../src/i18n/locales/zh-CN/dialogs.json";
-type Locale = "en" | "zh-CN" | "es";
+type Locale = "en" | "zh-CN" | "es" | "fr";
type Namespace = "common" | "dialogs";
type MessageMap = Record;
@@ -16,12 +18,13 @@ const messages: Record> = {
en: { common: commonEn, dialogs: dialogsEn },
"zh-CN": { common: commonZh, dialogs: dialogsZh },
es: { common: commonEs, dialogs: dialogsEs },
+ fr: { common: commonFr, dialogs: dialogsFr },
};
let currentLocale: Locale = "en";
export function setMainLocale(locale: string) {
- if (locale === "en" || locale === "zh-CN" || locale === "es") {
+ if (locale === "en" || locale === "zh-CN" || locale === "es" || locale === "fr") {
currentLocale = locale;
}
}
diff --git a/electron/ipc/handlers.ts b/electron/ipc/handlers.ts
index e43f53c..261d93f 100644
--- a/electron/ipc/handlers.ts
+++ b/electron/ipc/handlers.ts
@@ -352,10 +352,175 @@ function sampleCursorPoint() {
export function registerIpcHandlers(
createEditorWindow: () => void,
createSourceSelectorWindow: () => BrowserWindow,
+ createCountdownOverlayWindow: () => BrowserWindow,
getMainWindow: () => BrowserWindow | null,
getSourceSelectorWindow: () => BrowserWindow | null,
+ getCountdownOverlayWindow: () => BrowserWindow | null,
onRecordingStateChange?: (recording: boolean, sourceName: string) => void,
+ switchToHud?: () => void,
) {
+ const supportsWindowOpacity = process.platform !== "linux";
+ const countdownOverlayState = {
+ visible: false,
+ value: null as number | null,
+ activeRunId: null as number | null,
+ hideCommitId: 0,
+ hideCommitTimer: null as ReturnType | null,
+ };
+ const COUNTDOWN_OVERLAY_HIDE_DEBOUNCE_MS = 1200;
+
+ const clearCountdownOverlayHideCommit = () => {
+ if (countdownOverlayState.hideCommitTimer) {
+ clearTimeout(countdownOverlayState.hideCommitTimer);
+ countdownOverlayState.hideCommitTimer = null;
+ }
+ };
+
+ const commitCountdownOverlayHide = (win: BrowserWindow, hideCommitId: number) => {
+ if (win.isDestroyed()) {
+ return;
+ }
+
+ if (countdownOverlayState.visible || countdownOverlayState.hideCommitId !== hideCommitId) {
+ return;
+ }
+
+ win.hide();
+ if (supportsWindowOpacity) {
+ // Reset baseline opacity for the next show cycle.
+ win.setOpacity(1);
+ }
+ };
+
+ const flushCountdownOverlayState = (win: BrowserWindow) => {
+ if (win.isDestroyed()) {
+ return;
+ }
+
+ clearCountdownOverlayHideCommit();
+ win.webContents.send("countdown-overlay-value", countdownOverlayState.value);
+ if (!countdownOverlayState.visible) {
+ return;
+ }
+
+ if (win.isVisible()) {
+ if (supportsWindowOpacity) {
+ win.setOpacity(1);
+ }
+ return;
+ }
+
+ setTimeout(() => {
+ if (!win.isDestroyed() && countdownOverlayState.visible && !win.isVisible()) {
+ if (supportsWindowOpacity) {
+ win.setOpacity(0);
+ }
+ win.showInactive();
+
+ if (supportsWindowOpacity) {
+ setTimeout(() => {
+ if (!win.isDestroyed() && countdownOverlayState.visible && win.isVisible()) {
+ win.setOpacity(1);
+ }
+ }, 0);
+ }
+ }
+ }, 16);
+ };
+
+ ipcMain.handle("countdown-overlay-show", (_, value: number, runId: number) => {
+ countdownOverlayState.activeRunId = runId;
+ countdownOverlayState.visible = true;
+ countdownOverlayState.value = value;
+
+ const win = getCountdownOverlayWindow() ?? createCountdownOverlayWindow();
+ if (win.isDestroyed()) {
+ return;
+ }
+
+ if (win.webContents.isLoading()) {
+ win.webContents.once("did-finish-load", () => {
+ if (!win.isDestroyed()) {
+ flushCountdownOverlayState(win);
+ }
+ });
+ } else {
+ flushCountdownOverlayState(win);
+ }
+ });
+
+ ipcMain.handle("countdown-overlay-set-value", (_, value: number, runId: number) => {
+ if (countdownOverlayState.activeRunId !== runId || !countdownOverlayState.visible) {
+ return;
+ }
+
+ countdownOverlayState.value = value;
+
+ const win = getCountdownOverlayWindow();
+ if (!win || win.isDestroyed()) {
+ return;
+ }
+
+ if (win.webContents.isLoading()) {
+ return;
+ }
+
+ win.webContents.send("countdown-overlay-value", value);
+ });
+
+ ipcMain.handle("countdown-overlay-hide", (_, runId: number) => {
+ if (countdownOverlayState.activeRunId !== runId) {
+ return;
+ }
+
+ countdownOverlayState.visible = false;
+ countdownOverlayState.hideCommitId += 1;
+ const hideCommitId = countdownOverlayState.hideCommitId;
+ clearCountdownOverlayHideCommit();
+
+ const win = getCountdownOverlayWindow();
+ if (!win || win.isDestroyed()) {
+ countdownOverlayState.value = null;
+ return;
+ }
+
+ if (supportsWindowOpacity) {
+ // Hide visually immediately to avoid hide/show compositor flashes on rapid restart.
+ win.setOpacity(0);
+ }
+
+ countdownOverlayState.value = null;
+ if (!win.webContents.isLoading()) {
+ win.webContents.send("countdown-overlay-value", countdownOverlayState.value);
+ }
+
+ if (!supportsWindowOpacity) {
+ win.hide();
+ return;
+ }
+
+ countdownOverlayState.hideCommitTimer = setTimeout(() => {
+ countdownOverlayState.hideCommitTimer = null;
+ commitCountdownOverlayHide(win, hideCommitId);
+ }, COUNTDOWN_OVERLAY_HIDE_DEBOUNCE_MS);
+ });
+
+ ipcMain.handle("switch-to-hud", () => {
+ if (switchToHud) switchToHud();
+ });
+ ipcMain.handle("start-new-recording", () => {
+ try {
+ setCurrentRecordingSessionState(null);
+ if (switchToHud) {
+ switchToHud();
+ }
+ return { success: true };
+ } catch (error) {
+ console.error("Failed to start new recording:", error);
+ return { success: false, error: String(error) };
+ }
+ });
+
ipcMain.handle("get-sources", async (_, opts) => {
const sources = await desktopCapturer.getSources(opts);
return sources.map((source) => ({
@@ -473,7 +638,24 @@ export function registerIpcHandlers(
return { success: false, message: "No recorded video found" };
}
- const latestVideo = videoFiles.sort().reverse()[0];
+ // Sort by most recently modified to reliably get the latest recording.
+ // Lexicographic sort is unreliable (e.g. recording-9.webm > recording-10.webm).
+ let latestVideo: string | null = null;
+ let latestMtimeMs = 0;
+ for (const file of videoFiles) {
+ try {
+ const stat = await fs.stat(path.join(RECORDINGS_DIR, file));
+ if (stat.mtimeMs > latestMtimeMs) {
+ latestMtimeMs = stat.mtimeMs;
+ latestVideo = file;
+ }
+ } catch {
+ // Skip inaccessible files.
+ }
+ }
+ if (!latestVideo) {
+ return { success: false, message: "No recorded video found" };
+ }
const videoPath = path.join(RECORDINGS_DIR, latestVideo);
return { success: true, path: videoPath };
@@ -599,7 +781,19 @@ export function registerIpcHandlers(
ipcMain.handle("open-external-url", async (_, url: string) => {
try {
- await shell.openExternal(url);
+ const ALLOWED_SCHEMES = ["http:", "https:", "mailto:"];
+ let parsed: URL;
+ try {
+ parsed = new URL(url);
+ } catch {
+ return { success: false, error: "Invalid URL" };
+ }
+
+ if (!ALLOWED_SCHEMES.includes(parsed.protocol)) {
+ return { success: false, error: `Unsupported URL scheme: ${parsed.protocol}` };
+ }
+
+ await shell.openExternal(parsed.toString());
return { success: true };
} catch (error) {
console.error("Failed to open URL:", error);
@@ -622,6 +816,16 @@ export function registerIpcHandlers(
}
});
+ /**
+ * Handles saving an exported video file.
+ * Shows a save dialog, normalizes the file path for the current OS,
+ * ensures the directory exists, and writes the video data.
+ * @param _ - Unused event parameter.
+ * @param videoData - The exported video as an ArrayBuffer.
+ * @param fileName - Suggested filename for the save dialog.
+ * @returns Object with success status, optional file path, and error details.
+ */
+
ipcMain.handle("save-exported-video", async (_, videoData: ArrayBuffer, fileName: string) => {
try {
// Determine file type from extension
@@ -647,11 +851,18 @@ export function registerIpcHandlers(
};
}
- await fs.writeFile(result.filePath, Buffer.from(videoData));
+ // --- FIX: Normalize the path for Windows compatibility ---
+ const normalizedPath = path.normalize(result.filePath);
+
+ // Ensure the parent directory exists (Windows may fail if the folder is missing)
+ await fs.mkdir(path.dirname(normalizedPath), { recursive: true });
+ // --- END FIX ---
+
+ await fs.writeFile(normalizedPath, Buffer.from(videoData));
return {
success: true,
- path: result.filePath,
+ path: normalizedPath,
message: "Video exported successfully",
};
} catch (error) {
@@ -663,7 +874,6 @@ export function registerIpcHandlers(
};
}
});
-
ipcMain.handle("open-video-file-picker", async () => {
try {
const result = await dialog.showOpenDialog({
diff --git a/electron/main.ts b/electron/main.ts
index 7e19d46..ad0a33f 100644
--- a/electron/main.ts
+++ b/electron/main.ts
@@ -14,7 +14,12 @@ import {
} from "electron";
import { mainT, setMainLocale } from "./i18n";
import { registerIpcHandlers } from "./ipc/handlers";
-import { createEditorWindow, createHudOverlayWindow, createSourceSelectorWindow } from "./windows";
+import {
+ createCountdownOverlayWindow,
+ createEditorWindow,
+ createHudOverlayWindow,
+ createSourceSelectorWindow,
+} from "./windows";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -60,12 +65,15 @@ process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL
// Window references
let mainWindow: BrowserWindow | null = null;
let sourceSelectorWindow: BrowserWindow | null = null;
+let countdownOverlayWindow: BrowserWindow | null = null;
let tray: Tray | null = null;
let selectedSourceName = "";
+const isMac = process.platform === "darwin";
+const trayIconSize = isMac ? 16 : 24;
// Tray Icons
-const defaultTrayIcon = getTrayIcon("openscreen.png");
-const recordingTrayIcon = getTrayIcon("rec-button.png");
+const defaultTrayIcon = getTrayIcon("openscreen.png", trayIconSize);
+const recordingTrayIcon = getTrayIcon("rec-button.png", trayIconSize);
function createWindow() {
mainWindow = createHudOverlayWindow();
@@ -199,12 +207,12 @@ function createTray() {
});
}
-function getTrayIcon(filename: string) {
+function getTrayIcon(filename: string, size: number) {
return nativeImage
.createFromPath(path.join(process.env.VITE_PUBLIC || RENDERER_DIST, filename))
.resize({
- width: 24,
- height: 24,
+ width: size,
+ height: size,
quality: "best",
});
}
@@ -320,6 +328,18 @@ function createSourceSelectorWindowWrapper() {
return sourceSelectorWindow;
}
+function createCountdownOverlayWindowWrapper() {
+ if (countdownOverlayWindow && !countdownOverlayWindow.isDestroyed()) {
+ return countdownOverlayWindow;
+ }
+
+ countdownOverlayWindow = createCountdownOverlayWindow();
+ countdownOverlayWindow.on("closed", () => {
+ countdownOverlayWindow = null;
+ });
+ return countdownOverlayWindow;
+}
+
// On macOS, applications and their menu bar stay active until the user quits
// explicitly with Cmd + Q.
app.on("window-all-closed", () => {
@@ -329,8 +349,17 @@ app.on("window-all-closed", () => {
app.on("activate", () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
- if (BrowserWindow.getAllWindows().length === 0) {
- createWindow();
+ const hasVisibleWindow = BrowserWindow.getAllWindows().some((window) => {
+ if (window.isDestroyed() || !window.isVisible()) {
+ return false;
+ }
+
+ const url = window.webContents.getURL();
+ const isCountdownOverlayWindow = url.includes("windowType=countdown-overlay");
+ return !isCountdownOverlayWindow;
+ });
+ if (!hasVisibleWindow) {
+ showMainWindow();
}
});
@@ -371,11 +400,23 @@ app.whenReady().then(async () => {
// Ensure recordings directory exists
await ensureRecordingsDir();
+ function switchToHudWrapper() {
+ if (mainWindow) {
+ isForceClosing = true;
+ mainWindow.close();
+ isForceClosing = false;
+ mainWindow = null;
+ }
+ showMainWindow();
+ }
+
registerIpcHandlers(
createEditorWindowWrapper,
createSourceSelectorWindowWrapper,
+ createCountdownOverlayWindowWrapper,
() => mainWindow,
() => sourceSelectorWindow,
+ () => countdownOverlayWindow,
(recording: boolean, sourceName: string) => {
selectedSourceName = sourceName;
if (!tray) createTray();
@@ -384,6 +425,7 @@ app.whenReady().then(async () => {
showMainWindow();
}
},
+ switchToHudWrapper,
);
createWindow();
});
diff --git a/electron/preload.ts b/electron/preload.ts
index 8f1836b..6aa066f 100644
--- a/electron/preload.ts
+++ b/electron/preload.ts
@@ -18,6 +18,12 @@ contextBridge.exposeInMainWorld("electronAPI", {
switchToEditor: () => {
return ipcRenderer.invoke("switch-to-editor");
},
+ switchToHud: () => {
+ return ipcRenderer.invoke("switch-to-hud");
+ },
+ startNewRecording: () => {
+ return ipcRenderer.invoke("start-new-recording");
+ },
openSourceSelector: () => {
return ipcRenderer.invoke("open-source-selector");
},
@@ -124,6 +130,20 @@ contextBridge.exposeInMainWorld("electronAPI", {
setHasUnsavedChanges: (hasChanges: boolean) => {
ipcRenderer.send("set-has-unsaved-changes", hasChanges);
},
+ showCountdownOverlay: (value: number, runId: number) => {
+ return ipcRenderer.invoke("countdown-overlay-show", value, runId);
+ },
+ setCountdownOverlayValue: (value: number, runId: number) => {
+ return ipcRenderer.invoke("countdown-overlay-set-value", value, runId);
+ },
+ hideCountdownOverlay: (runId: number) => {
+ return ipcRenderer.invoke("countdown-overlay-hide", runId);
+ },
+ onCountdownOverlayValue: (callback: (value: number | null) => void) => {
+ const listener = (_event: unknown, value: number | null) => callback(value);
+ ipcRenderer.on("countdown-overlay-value", listener);
+ return () => ipcRenderer.removeListener("countdown-overlay-value", listener);
+ },
onRequestSaveBeforeClose: (callback: () => Promise | boolean) => {
const listener = async () => {
try {
diff --git a/electron/windows.ts b/electron/windows.ts
index fb9a655..daa6e5e 100644
--- a/electron/windows.ts
+++ b/electron/windows.ts
@@ -17,6 +17,11 @@ ipcMain.on("hud-overlay-hide", () => {
}
});
+/**
+ * Creates the always-on-top HUD overlay window centred at the bottom of the
+ * primary display. The window is frameless, transparent, and follows the user
+ * across macOS Spaces so it is never lost when switching virtual desktops.
+ */
export function createHudOverlayWindow(): BrowserWindow {
const primaryDisplay = screen.getPrimaryDisplay();
const { workArea } = primaryDisplay;
@@ -51,6 +56,12 @@ export function createHudOverlayWindow(): BrowserWindow {
},
});
+ // Follow the user across macOS Spaces (virtual desktops).
+ // Without this the HUD stays pinned to the Space it was first opened on.
+ if (process.platform === "darwin") {
+ win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
+ }
+
win.webContents.on("did-finish-load", () => {
win?.webContents.send("main-process-message", new Date().toLocaleString());
});
@@ -74,6 +85,10 @@ export function createHudOverlayWindow(): BrowserWindow {
return win;
}
+/**
+ * Creates the main editor window. Starts maximised with a hidden title bar on
+ * macOS. This window is not always-on-top and appears in the taskbar/dock.
+ */
export function createEditorWindow(): BrowserWindow {
const isMac = process.platform === "darwin";
@@ -120,6 +135,10 @@ export function createEditorWindow(): BrowserWindow {
return win;
}
+/**
+ * Creates the floating source-selector window used to pick a screen or window
+ * to record. Frameless, transparent, and follows the user across macOS Spaces.
+ */
export function createSourceSelectorWindow(): BrowserWindow {
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
@@ -142,6 +161,12 @@ export function createSourceSelectorWindow(): BrowserWindow {
},
});
+ // Follow the user across macOS Spaces so the selector appears on the
+ // active desktop regardless of where the HUD was originally opened.
+ if (process.platform === "darwin") {
+ win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
+ }
+
if (VITE_DEV_SERVER_URL) {
win.loadURL(VITE_DEV_SERVER_URL + "?windowType=source-selector");
} else {
@@ -152,3 +177,55 @@ export function createSourceSelectorWindow(): BrowserWindow {
return win;
}
+
+/**
+ * Creates a centered transparent countdown overlay window that sits above the
+ * HUD while recording pre-roll is running.
+ */
+export function createCountdownOverlayWindow(): BrowserWindow {
+ const { workArea } = screen.getPrimaryDisplay();
+ const overlayWidth = 420;
+ const overlayHeight = 260;
+
+ const win = new BrowserWindow({
+ width: overlayWidth,
+ height: overlayHeight,
+ minWidth: overlayWidth,
+ maxWidth: overlayWidth,
+ minHeight: overlayHeight,
+ maxHeight: overlayHeight,
+ x: Math.round(workArea.x + (workArea.width - overlayWidth) / 2),
+ y: Math.round(workArea.y + (workArea.height - overlayHeight) / 2),
+ frame: false,
+ resizable: false,
+ alwaysOnTop: true,
+ skipTaskbar: true,
+ focusable: false,
+ transparent: true,
+ backgroundColor: "#00000000",
+ hasShadow: false,
+ show: false,
+ webPreferences: {
+ preload: path.join(__dirname, "preload.mjs"),
+ nodeIntegration: false,
+ contextIsolation: true,
+ backgroundThrottling: false,
+ },
+ });
+
+ win.setIgnoreMouseEvents(true);
+
+ if (process.platform === "darwin") {
+ win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
+ }
+
+ if (VITE_DEV_SERVER_URL) {
+ win.loadURL(VITE_DEV_SERVER_URL + "?windowType=countdown-overlay");
+ } else {
+ win.loadFile(path.join(RENDERER_DIST, "index.html"), {
+ query: { windowType: "countdown-overlay" },
+ });
+ }
+
+ return win;
+}
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..77972fb
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,27 @@
+{
+ "nodes": {
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1775710090,
+ "narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "4c1018dae018162ec878d42fec712642d214fdfa",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "nixpkgs": "nixpkgs"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..7b2d328
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,122 @@
+{
+ description = "OpenScreen — desktop screen recorder with built-in editor";
+
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+ };
+
+ outputs =
+ { self, nixpkgs }:
+ let
+ systems = [
+ "x86_64-linux"
+ "aarch64-linux"
+ ];
+ forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
+ in
+ {
+ # -- Per-system outputs (packages, dev shells) --
+
+ packages = forAllSystems (pkgs: {
+ openscreen = pkgs.callPackage ./nix/package.nix { };
+ default = self.packages.${pkgs.stdenv.hostPlatform.system}.openscreen;
+ });
+
+ devShells = forAllSystems (
+ pkgs:
+ let
+ electron = pkgs.electron;
+
+ # Libraries Electron needs at runtime on Linux
+ runtimeLibs = with pkgs; [
+ # X11
+ libx11
+ libxcomposite
+ libxdamage
+ libxext
+ libxfixes
+ libxrandr
+ libxtst
+ libxcb
+ libxshmfence
+
+ # Wayland
+ wayland
+
+ # GTK / UI toolkit
+ gtk3
+ glib
+ pango
+ cairo
+ gdk-pixbuf
+ atk
+ at-spi2-atk
+ at-spi2-core
+
+ # Graphics
+ mesa
+ libGL
+ libdrm
+ vulkan-loader
+
+ # Networking / crypto (NSS for Chromium)
+ nss
+ nspr
+
+ # Audio
+ alsa-lib
+ pipewire
+ pulseaudio
+
+ # System
+ dbus
+ cups
+ expat
+ libnotify
+ libsecret
+ util-linux # libuuid
+ ];
+ in
+ {
+ default = pkgs.mkShell {
+ packages = with pkgs; [
+ nodejs_22
+ electron
+
+ # Native module compilation
+ python3
+ pkg-config
+ gcc
+
+ # Playwright browser tests
+ playwright-driver.browsers
+ ];
+
+ # Electron's prebuilt binary needs these at runtime
+ LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath runtimeLibs;
+
+ # Tell the npm `electron` package to use the Nix-provided binary
+ # instead of downloading its own. vite-plugin-electron respects this.
+ ELECTRON_OVERRIDE_DIST_PATH = "${electron}/libexec/electron";
+
+ # Playwright browser path for test:browser / test:e2e
+ PLAYWRIGHT_BROWSERS_PATH = "${pkgs.playwright-driver.browsers}";
+ PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "1";
+
+ shellHook = ''
+ echo "OpenScreen dev shell — node $(node --version), electron v$(electron --version 2>/dev/null | tr -d 'v')"
+ '';
+ };
+ }
+ );
+
+ # -- System-wide outputs (modules, overlay) --
+
+ overlays.default = final: _prev: {
+ openscreen = self.packages.${final.stdenv.hostPlatform.system}.openscreen;
+ };
+
+ nixosModules.default = import ./nix/module.nix self;
+ homeManagerModules.default = import ./nix/hm-module.nix self;
+ };
+}
diff --git a/icons/icons/mac/icon.icns b/icons/icons/mac/icon.icns
index 7d5a493..02de106 100644
Binary files a/icons/icons/mac/icon.icns and b/icons/icons/mac/icon.icns differ
diff --git a/macos.entitlements b/macos.entitlements
new file mode 100644
index 0000000..5c6ddcf
--- /dev/null
+++ b/macos.entitlements
@@ -0,0 +1,25 @@
+
+
+
+
+
+ com.apple.security.cs.allow-jit
+
+
+
+ com.apple.security.cs.allow-unsigned-executable-memory
+
+
+
+ com.apple.security.cs.disable-library-validation
+
+
+
+ com.apple.security.device.audio-input
+
+
+
+ com.apple.security.device.camera
+
+
+
diff --git a/nix/hm-module.nix b/nix/hm-module.nix
new file mode 100644
index 0000000..b04f827
--- /dev/null
+++ b/nix/hm-module.nix
@@ -0,0 +1,36 @@
+# Home Manager module for OpenScreen
+# Usage in flake-based Home Manager config:
+#
+# inputs.openscreen.url = "github:siddharthvaddem/openscreen";
+#
+# { inputs, ... }: {
+# imports = [ inputs.openscreen.homeManagerModules.default ];
+# programs.openscreen.enable = true;
+# }
+self:
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ cfg = config.programs.openscreen;
+in
+{
+ options.programs.openscreen = {
+ enable = lib.mkEnableOption "OpenScreen screen recorder";
+
+ package = lib.mkOption {
+ type = lib.types.package;
+ default = self.packages.${pkgs.stdenv.hostPlatform.system}.openscreen;
+ defaultText = lib.literalExpression "inputs.openscreen.packages.\${pkgs.stdenv.hostPlatform.system}.openscreen";
+ description = "The OpenScreen package to use.";
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ home.packages = [ cfg.package ];
+ };
+}
diff --git a/nix/module.nix b/nix/module.nix
new file mode 100644
index 0000000..3282d2d
--- /dev/null
+++ b/nix/module.nix
@@ -0,0 +1,42 @@
+# NixOS module for OpenScreen
+# Usage in flake-based NixOS config:
+#
+# inputs.openscreen.url = "github:siddharthvaddem/openscreen";
+#
+# { inputs, ... }: {
+# imports = [ inputs.openscreen.nixosModules.default ];
+# programs.openscreen.enable = true;
+# }
+self:
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+let
+ cfg = config.programs.openscreen;
+in
+{
+ options.programs.openscreen = {
+ enable = lib.mkEnableOption "OpenScreen screen recorder";
+
+ package = lib.mkOption {
+ type = lib.types.package;
+ default = self.packages.${pkgs.stdenv.hostPlatform.system}.openscreen;
+ defaultText = lib.literalExpression "inputs.openscreen.packages.\${pkgs.stdenv.hostPlatform.system}.openscreen";
+ description = "The OpenScreen package to use.";
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ environment.systemPackages = [ cfg.package ];
+
+ # Screen capture on Wayland requires xdg-desktop-portal.
+ # We enable the base portal; users should also enable a
+ # desktop-specific portal (e.g. xdg-desktop-portal-gtk,
+ # xdg-desktop-portal-hyprland) in their DE config.
+ xdg.portal.enable = lib.mkDefault true;
+ };
+}
diff --git a/nix/package.nix b/nix/package.nix
new file mode 100644
index 0000000..195043f
--- /dev/null
+++ b/nix/package.nix
@@ -0,0 +1,124 @@
+{
+ lib,
+ buildNpmPackage,
+ nodejs_22,
+ electron,
+ makeWrapper,
+ makeDesktopItem,
+ copyDesktopItems,
+}:
+
+buildNpmPackage {
+ nodejs = nodejs_22;
+ pname = "openscreen";
+ version = "1.3.0";
+
+ src =
+ let
+ fs = lib.fileset;
+ # gitTracked fails when source is already a store path (path: flake inputs).
+ # Detect this and fall back to cleanSource which handles both cases.
+ isStorePath = builtins.storeDir == builtins.substring 0 (builtins.stringLength builtins.storeDir) (toString ../.);
+ baseFiles = if isStorePath then fs.fromSource (lib.cleanSource ../.) else fs.gitTracked ../.;
+ in
+ fs.toSource {
+ root = ../.;
+ fileset = fs.difference baseFiles (
+ fs.unions [
+ ../nix
+ ../flake.nix
+ ../flake.lock
+ (fs.fileFilter (file: file.hasExt "md") ../.)
+ ]
+ );
+ };
+
+ npmDepsHash = "sha256-Pd6J9TuggA9vM4s/LjdoK4MoBEivSzAWc/G2+pFOM2U=";
+
+ env.ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
+
+ # electron-builder is not needed — we wrap system electron directly
+ npmFlags = [ "--ignore-scripts" ];
+ makeCacheWritable = true;
+
+ # vite-plugin-electron compiles electron/ sources into dist-electron/
+ # tsconfig has noEmit — tsc is type-check only
+ buildPhase = ''
+ runHook preBuild
+ npx vite build
+ runHook postBuild
+ '';
+
+ installPhase = ''
+ runHook preInstall
+
+ mkdir -p "$out/lib/openscreen"
+
+ # Renderer build output (index.html, JS chunks, copied public/ assets)
+ cp -r dist "$out/lib/openscreen/"
+
+ # Main process + preload (compiled by vite-plugin-electron)
+ cp -r dist-electron "$out/lib/openscreen/"
+
+ # Package manifest (electron reads "main" field to find entry point)
+ cp package.json "$out/lib/openscreen/"
+
+ # Strip devDependencies (electron, vitest, biome, playwright, etc.)
+ npm prune --omit=dev --no-save
+ cp -r node_modules "$out/lib/openscreen/"
+
+ # Asset resolution: when app.isPackaged is false, the main process resolves
+ # assets at /public/assets/. Mirror the electron-builder
+ # extraResources layout so wallpapers load correctly.
+ mkdir -p "$out/lib/openscreen/public/assets"
+ cp -r public/wallpapers "$out/lib/openscreen/public/assets/wallpapers"
+
+ # Wrap system electron with the app directory
+ mkdir -p "$out/bin"
+ makeWrapper "${electron}/bin/electron" "$out/bin/openscreen" \
+ --add-flags "$out/lib/openscreen" \
+ --set ELECTRON_IS_DEV 0
+
+ # Install icons to hicolor theme
+ for size in 16 24 32 48 64 128 256 512 1024; do
+ icon="icons/icons/png/''${size}x''${size}.png"
+ if [ -f "$icon" ]; then
+ install -Dm644 "$icon" \
+ "$out/share/icons/hicolor/''${size}x''${size}/apps/openscreen.png"
+ fi
+ done
+
+ runHook postInstall
+ '';
+
+ nativeBuildInputs = [
+ makeWrapper
+ copyDesktopItems
+ ];
+
+ desktopItems = [
+ (makeDesktopItem {
+ name = "openscreen";
+ desktopName = "OpenScreen";
+ genericName = "Screen Recorder";
+ exec = "openscreen %U";
+ icon = "openscreen";
+ comment = "Desktop screen recorder with built-in editor";
+ categories = [
+ "AudioVideo"
+ "Video"
+ "Recorder"
+ ];
+ startupWMClass = "Openscreen";
+ terminal = false;
+ })
+ ];
+
+ meta = {
+ description = "Desktop screen recorder with built-in editor";
+ homepage = "https://github.com/siddharthvaddem/openscreen";
+ license = lib.licenses.mit;
+ mainProgram = "openscreen";
+ platforms = lib.platforms.linux;
+ };
+}
diff --git a/package-lock.json b/package-lock.json
index fdbd6b9..4f854bc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,67 +16,67 @@
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slider": "^1.3.6",
- "@radix-ui/react-slot": "^1.2.3",
+ "@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"@types/gif.js": "^0.2.5",
- "@uiw/color-convert": "^2.9.2",
- "@uiw/react-color-block": "^2.9.2",
+ "@uiw/color-convert": "^2.10.1",
+ "@uiw/react-color-block": "^2.10.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
- "dnd-timeline": "^2.2.0",
- "emoji-picker-react": "^4.16.1",
+ "dnd-timeline": "^2.4.0",
+ "emoji-picker-react": "^4.18.0",
"fix-webm-duration": "^1.0.6",
"gif.js": "^0.2.0",
- "gsap": "^3.13.0",
+ "gsap": "^3.15.0",
"lucide-react": "^0.545.0",
- "mediabunny": "^1.25.1",
- "motion": "^12.23.24",
- "mp4box": "^2.2.0",
+ "mediabunny": "^1.40.1",
+ "motion": "^12.38.0",
+ "mp4box": "^2.3.0",
"pixi-filters": "^6.1.5",
- "pixi.js": "^8.14.0",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-icons": "^5.5.0",
+ "pixi.js": "^8.18.1",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-icons": "^5.6.0",
"react-resizable-panels": "^3.0.6",
- "react-rnd": "^10.5.2",
+ "react-rnd": "^10.5.3",
"sonner": "^2.0.7",
- "tailwind-merge": "^3.3.1",
+ "tailwind-merge": "^3.5.0",
"tailwindcss-animate": "^1.0.7",
"uuid": "^13.0.0",
"web-demuxer": "^4.0.0"
},
"devDependencies": {
- "@biomejs/biome": "^2.3.13",
- "@playwright/test": "^1.58.2",
+ "@biomejs/biome": "^2.4.12",
+ "@playwright/test": "^1.59.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
- "@types/node": "^25.0.3",
- "@types/react": "^18.2.64",
- "@types/react-dom": "^18.2.21",
- "@types/uuid": "^10.0.0",
- "@vitejs/plugin-react": "^4.2.1",
- "autoprefixer": "^10.4.21",
- "electron": "^39.2.7",
- "electron-builder": "^26.7.0",
- "electron-icon-builder": "^2.0.1",
- "electron-rebuild": "^3.2.9",
- "fast-check": "^4.5.2",
+ "@types/node": "^22.19.17",
+ "@types/react": "^18.3.28",
+ "@types/react-dom": "^18.3.7",
+ "@vitejs/plugin-react": "^5.2.0",
+ "@vitest/browser": "^4.1.4",
+ "@vitest/browser-playwright": "^4.1.4",
+ "autoprefixer": "^10.5.0",
+ "electron": "^41.2.1",
+ "electron-builder": "^26.8.1",
+ "esbuild": "^0.27.0",
+ "fast-check": "^4.7.0",
"husky": "^9.1.7",
- "jsdom": "^29.0.1",
- "lint-staged": "^16.3.2",
- "postcss": "^8.5.6",
- "tailwindcss": "^3.4.18",
- "terser": "^5.44.1",
- "typescript": "^5.2.2",
- "vite": "^5.1.6",
- "vite-plugin-electron": "^0.28.6",
- "vite-plugin-electron-renderer": "^0.14.5",
- "vitest": "^4.0.16"
+ "jsdom": "^29.0.2",
+ "lint-staged": "^16.4.0",
+ "postcss": "^8.5.10",
+ "tailwindcss": "^3.4.19",
+ "terser": "^5.46.1",
+ "typescript": "^5.9.3",
+ "vite": "^7.3.2",
+ "vite-plugin-electron": "^0.29.1",
+ "vite-plugin-electron-renderer": "^0.14.6",
+ "vitest": "^4.1.4"
},
"engines": {
"node": "22.22.1",
@@ -103,57 +103,47 @@
}
},
"node_modules/@asamuzakjp/css-color": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.0.1.tgz",
- "integrity": "sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==",
+ "version": "5.1.11",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz",
+ "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@csstools/css-calc": "^3.1.1",
- "@csstools/css-color-parser": "^4.0.2",
+ "@asamuzakjp/generational-cache": "^1.0.1",
+ "@csstools/css-calc": "^3.2.0",
+ "@csstools/css-color-parser": "^4.1.0",
"@csstools/css-parser-algorithms": "^4.0.0",
- "@csstools/css-tokenizer": "^4.0.0",
- "lru-cache": "^11.2.6"
+ "@csstools/css-tokenizer": "^4.0.0"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}
},
- "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {
- "version": "11.2.7",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
- "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": "20 || >=22"
- }
- },
"node_modules/@asamuzakjp/dom-selector": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.4.tgz",
- "integrity": "sha512-jXR6x4AcT3eIrS2fSNAwJpwirOkGcd+E7F7CP3zjdTqz9B/2huHOL8YJZBgekKwLML+u7qB/6P1LXQuMScsx0w==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz",
+ "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
+ "@asamuzakjp/generational-cache": "^1.0.1",
"@asamuzakjp/nwsapi": "^2.3.9",
"bidi-js": "^1.0.3",
"css-tree": "^3.2.1",
- "is-potential-custom-element-name": "^1.0.1",
- "lru-cache": "^11.2.7"
+ "is-potential-custom-element-name": "^1.0.1"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}
},
- "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": {
- "version": "11.2.7",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
- "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
+ "node_modules/@asamuzakjp/generational-cache": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz",
+ "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==",
"dev": true,
- "license": "BlueOak-1.0.0",
+ "license": "MIT",
"engines": {
- "node": "20 || >=22"
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}
},
"node_modules/@asamuzakjp/nwsapi": {
@@ -164,13 +154,13 @@
"license": "MIT"
},
"node_modules/@babel/code-frame": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
- "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "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.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
@@ -179,9 +169,9 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz",
- "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
+ "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -189,21 +179,21 @@
}
},
"node_modules/@babel/core": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz",
- "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
+ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.3",
- "@babel/helper-compilation-targets": "^7.27.2",
- "@babel/helper-module-transforms": "^7.28.3",
- "@babel/helpers": "^7.28.4",
- "@babel/parser": "^7.28.4",
- "@babel/template": "^7.27.2",
- "@babel/traverse": "^7.28.4",
- "@babel/types": "^7.28.4",
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helpers": "^7.28.6",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.29.0",
+ "@babel/types": "^7.29.0",
"@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
@@ -219,25 +209,15 @@
"url": "https://opencollective.com/babel"
}
},
- "node_modules/@babel/core/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/@babel/generator": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
- "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
+ "version": "7.29.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
+ "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.3",
- "@babel/types": "^7.28.2",
+ "@babel/parser": "^7.29.0",
+ "@babel/types": "^7.29.0",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -247,13 +227,13 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
- "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+ "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/compat-data": "^7.27.2",
+ "@babel/compat-data": "^7.28.6",
"@babel/helper-validator-option": "^7.27.1",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
@@ -263,16 +243,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-compilation-targets/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/@babel/helper-globals": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
@@ -284,29 +254,29 @@
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
- "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+ "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.27.1",
- "@babel/types": "^7.27.1"
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.28.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
- "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+ "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/helper-module-imports": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1",
- "@babel/traverse": "^7.28.3"
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
@@ -316,9 +286,9 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
- "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
+ "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
"dev": true,
"license": "MIT",
"engines": {
@@ -336,9 +306,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
- "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "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": {
@@ -356,27 +326,27 @@
}
},
"node_modules/@babel/helpers": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
- "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
+ "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.4"
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz",
- "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==",
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
+ "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.28.4"
+ "@babel/types": "^7.29.0"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -418,42 +388,42 @@
}
},
"node_modules/@babel/runtime": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
- "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+ "version": "7.29.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
+ "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": {
- "version": "7.27.2",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
- "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "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.27.1",
- "@babel/parser": "^7.27.2",
- "@babel/types": "^7.27.1"
+ "@babel/code-frame": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz",
- "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
+ "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.3",
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
"@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.28.4",
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.4",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0",
"debug": "^4.3.1"
},
"engines": {
@@ -461,23 +431,23 @@
}
},
"node_modules/@babel/types": {
- "version": "7.28.4",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz",
- "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==",
+ "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.27.1",
- "@babel/helper-validator-identifier": "^7.27.1"
+ "@babel/helper-validator-identifier": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@biomejs/biome": {
- "version": "2.3.13",
- "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.13.tgz",
- "integrity": "sha512-Fw7UsV0UAtWIBIm0M7g5CRerpu1eKyKAXIazzxhbXYUyMkwNrkX/KLkGI7b+uVDQ5cLUMfOC9vR60q9IDYDstA==",
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.12.tgz",
+ "integrity": "sha512-Rro7adQl3NLq/zJCIL98eElXKI8eEiBtoeu5TbXF/U3qbjuSc7Jb5rjUbeHHcquDWeSf3HnGP7XI5qGrlRk/pA==",
"dev": true,
"license": "MIT OR Apache-2.0",
"bin": {
@@ -491,20 +461,20 @@
"url": "https://opencollective.com/biome"
},
"optionalDependencies": {
- "@biomejs/cli-darwin-arm64": "2.3.13",
- "@biomejs/cli-darwin-x64": "2.3.13",
- "@biomejs/cli-linux-arm64": "2.3.13",
- "@biomejs/cli-linux-arm64-musl": "2.3.13",
- "@biomejs/cli-linux-x64": "2.3.13",
- "@biomejs/cli-linux-x64-musl": "2.3.13",
- "@biomejs/cli-win32-arm64": "2.3.13",
- "@biomejs/cli-win32-x64": "2.3.13"
+ "@biomejs/cli-darwin-arm64": "2.4.12",
+ "@biomejs/cli-darwin-x64": "2.4.12",
+ "@biomejs/cli-linux-arm64": "2.4.12",
+ "@biomejs/cli-linux-arm64-musl": "2.4.12",
+ "@biomejs/cli-linux-x64": "2.4.12",
+ "@biomejs/cli-linux-x64-musl": "2.4.12",
+ "@biomejs/cli-win32-arm64": "2.4.12",
+ "@biomejs/cli-win32-x64": "2.4.12"
}
},
"node_modules/@biomejs/cli-darwin-arm64": {
- "version": "2.3.13",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.13.tgz",
- "integrity": "sha512-0OCwP0/BoKzyJHnFdaTk/i7hIP9JHH9oJJq6hrSCPmJPo8JWcJhprK4gQlhFzrwdTBAW4Bjt/RmCf3ZZe59gwQ==",
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.12.tgz",
+ "integrity": "sha512-BnMU4Pc3ciEVteVpZ0BK33MLr7X57F5w1dwDLDn+/iy/yTrA4Q/N2yftidFtsA4vrDh0FMXDpacNV/Tl3fbmng==",
"cpu": [
"arm64"
],
@@ -519,9 +489,9 @@
}
},
"node_modules/@biomejs/cli-darwin-x64": {
- "version": "2.3.13",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.13.tgz",
- "integrity": "sha512-AGr8OoemT/ejynbIu56qeil2+F2WLkIjn2d8jGK1JkchxnMUhYOfnqc9sVzcRxpG9Ycvw4weQ5sprRvtb7Yhcw==",
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.12.tgz",
+ "integrity": "sha512-x9uJ0bI1rJsWICp3VH8w/5PnAVD3A7SqzDpbrfoUQX1QyWrK5jSU4fRLo/wSgGeplCivbxBRKmt5Xq4/nWvq8A==",
"cpu": [
"x64"
],
@@ -536,9 +506,9 @@
}
},
"node_modules/@biomejs/cli-linux-arm64": {
- "version": "2.3.13",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.13.tgz",
- "integrity": "sha512-xvOiFkrDNu607MPMBUQ6huHmBG1PZLOrqhtK6pXJW3GjfVqJg0Z/qpTdhXfcqWdSZHcT+Nct2fOgewZvytESkw==",
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.12.tgz",
+ "integrity": "sha512-tOwuCuZZtKi1jVzbk/5nXmIsziOB6yqN8c9r9QM0EJYPU6DpQWf11uBOSCfFKKM4H3d9ZoarvlgMfbcuD051Pw==",
"cpu": [
"arm64"
],
@@ -553,9 +523,9 @@
}
},
"node_modules/@biomejs/cli-linux-arm64-musl": {
- "version": "2.3.13",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.13.tgz",
- "integrity": "sha512-TUdDCSY+Eo/EHjhJz7P2GnWwfqet+lFxBZzGHldrvULr59AgahamLs/N85SC4+bdF86EhqDuuw9rYLvLFWWlXA==",
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.12.tgz",
+ "integrity": "sha512-FhfpkAAlKL6kwvcVap0Hgp4AhZmtd3YImg0kK1jd7C/aSoh4SfsB2f++yG1rU0lr8Y5MCFJrcSkmssiL9Xnnig==",
"cpu": [
"arm64"
],
@@ -570,9 +540,9 @@
}
},
"node_modules/@biomejs/cli-linux-x64": {
- "version": "2.3.13",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.13.tgz",
- "integrity": "sha512-s+YsZlgiXNq8XkgHs6xdvKDFOj/bwTEevqEY6rC2I3cBHbxXYU1LOZstH3Ffw9hE5tE1sqT7U23C00MzkXztMw==",
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.12.tgz",
+ "integrity": "sha512-8pFeAnLU9QdW9jCIslB/v82bI0lhBmz2ZAKc8pVMFPO0t0wAHsoEkrUQUbMkIorTRIjbqyNZHA3lEXavsPWYSw==",
"cpu": [
"x64"
],
@@ -587,9 +557,9 @@
}
},
"node_modules/@biomejs/cli-linux-x64-musl": {
- "version": "2.3.13",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.13.tgz",
- "integrity": "sha512-0bdwFVSbbM//Sds6OjtnmQGp4eUjOTt6kHvR/1P0ieR9GcTUAlPNvPC3DiavTqq302W34Ae2T6u5VVNGuQtGlQ==",
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.12.tgz",
+ "integrity": "sha512-dwTIgZrGutzhkQCuvHynCkyW6hJxUuyZqKKO0YNfaS2GUoRO+tOvxXZqZB6SkWAOdfZTzwaw8IEdUnIkHKHoew==",
"cpu": [
"x64"
],
@@ -604,9 +574,9 @@
}
},
"node_modules/@biomejs/cli-win32-arm64": {
- "version": "2.3.13",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.13.tgz",
- "integrity": "sha512-QweDxY89fq0VvrxME+wS/BXKmqMrOTZlN9SqQ79kQSIc3FrEwvW/PvUegQF6XIVaekncDykB5dzPqjbwSKs9DA==",
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.12.tgz",
+ "integrity": "sha512-B0DLnx0vA9ya/3v7XyCaP+/lCpnbWbMOfUFFve+xb5OxyYvdHaS55YsSddr228Y+JAFk58agCuZTsqNiw2a6ig==",
"cpu": [
"arm64"
],
@@ -621,9 +591,9 @@
}
},
"node_modules/@biomejs/cli-win32-x64": {
- "version": "2.3.13",
- "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.13.tgz",
- "integrity": "sha512-trDw2ogdM2lyav9WFQsdsfdVy1dvZALymRpgmWsvSez0BJzBjulhOT/t+wyKeh3pZWvwP3VMs1SoOKwO3wecMQ==",
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.12.tgz",
+ "integrity": "sha512-yMckRzTyZ83hkk8iDFWswqSdU8tvZxspJKnYNh7JZr/zhZNOlzH13k4ecboU6MurKExCe2HUkH75pGI/O2JwGA==",
"cpu": [
"x64"
],
@@ -637,6 +607,13 @@
"node": ">=14.21.3"
}
},
+ "node_modules/@blazediff/core": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@blazediff/core/-/core-1.9.1.tgz",
+ "integrity": "sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@bramus/specificity": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz",
@@ -671,9 +648,9 @@
}
},
"node_modules/@csstools/css-calc": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz",
- "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.0.tgz",
+ "integrity": "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==",
"dev": true,
"funding": [
{
@@ -695,9 +672,9 @@
}
},
"node_modules/@csstools/css-color-parser": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz",
- "integrity": "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.0.tgz",
+ "integrity": "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==",
"dev": true,
"funding": [
{
@@ -712,7 +689,7 @@
"license": "MIT",
"dependencies": {
"@csstools/color-helpers": "^6.0.2",
- "@csstools/css-calc": "^3.1.1"
+ "@csstools/css-calc": "^3.2.0"
},
"engines": {
"node": ">=20.19.0"
@@ -746,9 +723,9 @@
}
},
"node_modules/@csstools/css-syntax-patches-for-csstree": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.1.tgz",
- "integrity": "sha512-BvqN0AMWNAnLk9G8jnUT77D+mUbY/H2b3uDTvg2isJkHaOufUE2R3AOwxWo7VBQKT1lOdwdvorddo2B/lk64+w==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.3.tgz",
+ "integrity": "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==",
"dev": true,
"funding": [
{
@@ -865,10 +842,17 @@
"node": ">=10.12.0"
}
},
+ "node_modules/@electron/asar/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==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@electron/asar/node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -877,9 +861,9 @@
}
},
"node_modules/@electron/asar/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "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": {
@@ -921,9 +905,9 @@
}
},
"node_modules/@electron/fuses/node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -965,16 +949,6 @@
"global-agent": "^3.0.0"
}
},
- "node_modules/@electron/get/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/@electron/notarize": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz",
@@ -1007,9 +981,9 @@
}
},
"node_modules/@electron/notarize/node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1080,9 +1054,9 @@
}
},
"node_modules/@electron/osx-sign/node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1130,359 +1104,17 @@
"node": ">=22.12.0"
}
},
- "node_modules/@electron/rebuild/node_modules/@npmcli/fs": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz",
- "integrity": "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==",
+ "node_modules/@electron/rebuild/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
- "dependencies": {
- "semver": "^7.3.5"
- },
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/@electron/rebuild/node_modules/abbrev": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz",
- "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/@electron/rebuild/node_modules/cacache": {
- "version": "19.0.1",
- "resolved": "https://registry.npmjs.org/cacache/-/cacache-19.0.1.tgz",
- "integrity": "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@npmcli/fs": "^4.0.0",
- "fs-minipass": "^3.0.0",
- "glob": "^10.2.2",
- "lru-cache": "^10.0.1",
- "minipass": "^7.0.3",
- "minipass-collect": "^2.0.1",
- "minipass-flush": "^1.0.5",
- "minipass-pipeline": "^1.2.4",
- "p-map": "^7.0.2",
- "ssri": "^12.0.0",
- "tar": "^7.4.3",
- "unique-filename": "^4.0.0"
- },
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/@electron/rebuild/node_modules/chownr": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
- "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@electron/rebuild/node_modules/fs-minipass": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
- "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "minipass": "^7.0.3"
- },
- "engines": {
- "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
- }
- },
- "node_modules/@electron/rebuild/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",
- "dev": true,
- "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"
- },
"bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/@electron/rebuild/node_modules/isexe": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz",
- "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@electron/rebuild/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/@electron/rebuild/node_modules/make-fetch-happen": {
- "version": "14.0.3",
- "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz",
- "integrity": "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@npmcli/agent": "^3.0.0",
- "cacache": "^19.0.1",
- "http-cache-semantics": "^4.1.1",
- "minipass": "^7.0.2",
- "minipass-fetch": "^4.0.0",
- "minipass-flush": "^1.0.5",
- "minipass-pipeline": "^1.2.4",
- "negotiator": "^1.0.0",
- "proc-log": "^5.0.0",
- "promise-retry": "^2.0.1",
- "ssri": "^12.0.0"
+ "semver": "bin/semver.js"
},
"engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/@electron/rebuild/node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/@electron/rebuild/node_modules/minipass-collect": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz",
- "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "minipass": "^7.0.3"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/@electron/rebuild/node_modules/minipass-fetch": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-4.0.1.tgz",
- "integrity": "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "minipass": "^7.0.3",
- "minipass-sized": "^1.0.3",
- "minizlib": "^3.0.1"
- },
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- },
- "optionalDependencies": {
- "encoding": "^0.1.13"
- }
- },
- "node_modules/@electron/rebuild/node_modules/minizlib": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
- "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "minipass": "^7.1.2"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/@electron/rebuild/node_modules/negotiator": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
- "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/@electron/rebuild/node_modules/node-abi": {
- "version": "4.26.0",
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.26.0.tgz",
- "integrity": "sha512-8QwIZqikRvDIkXS2S93LjzhsSPJuIbfaMETWH+Bx8oOT9Sa9UsUtBFQlc3gBNd1+QINjaTloitXr1W3dQLi9Iw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "semver": "^7.6.3"
- },
- "engines": {
- "node": ">=22.12.0"
- }
- },
- "node_modules/@electron/rebuild/node_modules/node-api-version": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz",
- "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "semver": "^7.3.5"
- }
- },
- "node_modules/@electron/rebuild/node_modules/node-gyp": {
- "version": "11.5.0",
- "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.5.0.tgz",
- "integrity": "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "env-paths": "^2.2.0",
- "exponential-backoff": "^3.1.1",
- "graceful-fs": "^4.2.6",
- "make-fetch-happen": "^14.0.3",
- "nopt": "^8.0.0",
- "proc-log": "^5.0.0",
- "semver": "^7.3.5",
- "tar": "^7.4.3",
- "tinyglobby": "^0.2.12",
- "which": "^5.0.0"
- },
- "bin": {
- "node-gyp": "bin/node-gyp.js"
- },
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/@electron/rebuild/node_modules/nopt": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz",
- "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "abbrev": "^3.0.0"
- },
- "bin": {
- "nopt": "bin/nopt.js"
- },
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/@electron/rebuild/node_modules/p-map": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz",
- "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@electron/rebuild/node_modules/ssri": {
- "version": "12.0.0",
- "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz",
- "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "minipass": "^7.0.3"
- },
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/@electron/rebuild/node_modules/tar": {
- "version": "7.5.7",
- "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz",
- "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "@isaacs/fs-minipass": "^4.0.0",
- "chownr": "^3.0.0",
- "minipass": "^7.1.2",
- "minizlib": "^3.1.0",
- "yallist": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@electron/rebuild/node_modules/unique-filename": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz",
- "integrity": "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "unique-slug": "^5.0.0"
- },
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/@electron/rebuild/node_modules/unique-slug": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-5.0.0.tgz",
- "integrity": "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "imurmurhash": "^0.1.4"
- },
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/@electron/rebuild/node_modules/which": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
- "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^3.1.1"
- },
- "bin": {
- "node-which": "bin/which.js"
- },
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/@electron/rebuild/node_modules/yallist": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
- "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=18"
+ "node": ">=10"
}
},
"node_modules/@electron/universal": {
@@ -1504,10 +1136,27 @@
"node": ">=16.4"
}
},
+ "node_modules/@electron/universal/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@electron/universal/node_modules/brace-expansion": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
+ "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
"node_modules/@electron/universal/node_modules/fs-extra": {
- "version": "11.3.3",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz",
- "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==",
+ "version": "11.3.4",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz",
+ "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1520,9 +1169,9 @@
}
},
"node_modules/@electron/universal/node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1532,6 +1181,22 @@
"graceful-fs": "^4.1.6"
}
},
+ "node_modules/@electron/universal/node_modules/minimatch": {
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/@electron/universal/node_modules/universalify": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
@@ -1565,9 +1230,9 @@
}
},
"node_modules/@electron/windows-sign/node_modules/fs-extra": {
- "version": "11.3.3",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz",
- "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==",
+ "version": "11.3.4",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz",
+ "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -1582,9 +1247,9 @@
}
},
"node_modules/@electron/windows-sign/node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
"optional": true,
@@ -1609,9 +1274,9 @@
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
+ "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
"cpu": [
"ppc64"
],
@@ -1622,13 +1287,13 @@
"aix"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
+ "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
"cpu": [
"arm"
],
@@ -1639,13 +1304,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
+ "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
"cpu": [
"arm64"
],
@@ -1656,13 +1321,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz",
+ "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==",
"cpu": [
"x64"
],
@@ -1673,13 +1338,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz",
+ "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==",
"cpu": [
"arm64"
],
@@ -1690,13 +1355,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz",
+ "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==",
"cpu": [
"x64"
],
@@ -1707,13 +1372,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==",
"cpu": [
"arm64"
],
@@ -1724,13 +1389,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz",
+ "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==",
"cpu": [
"x64"
],
@@ -1741,13 +1406,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz",
+ "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==",
"cpu": [
"arm"
],
@@ -1758,13 +1423,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz",
+ "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==",
"cpu": [
"arm64"
],
@@ -1775,13 +1440,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz",
+ "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==",
"cpu": [
"ia32"
],
@@ -1792,13 +1457,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz",
+ "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==",
"cpu": [
"loong64"
],
@@ -1809,13 +1474,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz",
+ "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==",
"cpu": [
"mips64el"
],
@@ -1826,13 +1491,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz",
+ "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==",
"cpu": [
"ppc64"
],
@@ -1843,13 +1508,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz",
+ "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==",
"cpu": [
"riscv64"
],
@@ -1860,13 +1525,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz",
+ "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==",
"cpu": [
"s390x"
],
@@ -1877,13 +1542,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz",
+ "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==",
"cpu": [
"x64"
],
@@ -1894,13 +1559,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
- "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==",
"cpu": [
"arm64"
],
@@ -1915,9 +1580,9 @@
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==",
"cpu": [
"x64"
],
@@ -1928,13 +1593,13 @@
"netbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
- "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
"cpu": [
"arm64"
],
@@ -1949,9 +1614,9 @@
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==",
"cpu": [
"x64"
],
@@ -1962,13 +1627,13 @@
"openbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
- "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz",
+ "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==",
"cpu": [
"arm64"
],
@@ -1983,9 +1648,9 @@
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz",
+ "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==",
"cpu": [
"x64"
],
@@ -1996,13 +1661,13 @@
"sunos"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz",
+ "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==",
"cpu": [
"arm64"
],
@@ -2013,13 +1678,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz",
+ "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==",
"cpu": [
"ia32"
],
@@ -2030,13 +1695,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz",
+ "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==",
"cpu": [
"x64"
],
@@ -2047,7 +1712,7 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@exodus/bytes": {
@@ -2088,31 +1753,31 @@
}
},
"node_modules/@floating-ui/core": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
- "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
+ "version": "1.7.5",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz",
+ "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
"license": "MIT",
"dependencies": {
- "@floating-ui/utils": "^0.2.10"
+ "@floating-ui/utils": "^0.2.11"
}
},
"node_modules/@floating-ui/dom": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
- "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
+ "version": "1.7.6",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz",
+ "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
"license": "MIT",
"dependencies": {
- "@floating-ui/core": "^1.7.3",
- "@floating-ui/utils": "^0.2.10"
+ "@floating-ui/core": "^1.7.5",
+ "@floating-ui/utils": "^0.2.11"
}
},
"node_modules/@floating-ui/react-dom": {
- "version": "2.1.6",
- "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz",
- "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==",
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz",
+ "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==",
"license": "MIT",
"dependencies": {
- "@floating-ui/dom": "^1.7.4"
+ "@floating-ui/dom": "^1.7.6"
},
"peerDependencies": {
"react": ">=16.8.0",
@@ -2120,22 +1785,16 @@
}
},
"node_modules/@floating-ui/utils": {
- "version": "0.2.10",
- "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
- "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
- "license": "MIT"
- },
- "node_modules/@gar/promisify": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
- "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==",
- "dev": true,
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz",
+ "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
"license": "MIT"
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^5.1.2",
@@ -2149,22 +1808,11 @@
"node": ">=12"
}
},
- "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
- "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
"node_modules/@isaacs/cliui/node_modules/ansi-styles": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -2177,12 +1825,14 @@
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@isaacs/cliui/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==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"eastasianwidth": "^0.2.0",
@@ -2196,25 +1846,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
- "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- },
"node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^6.1.0",
@@ -2241,522 +1877,6 @@
"node": ">=18.0.0"
}
},
- "node_modules/@isaacs/fs-minipass/node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/@jimp/bmp": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.16.13.tgz",
- "integrity": "sha512-9edAxu7N2FX7vzkdl5Jo1BbACfycUtBQX+XBMcHA2bk62P8R0otgkHg798frgAk/WxQIzwxqOH6wMiCwrlAzdQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13",
- "bmp-js": "^0.1.0"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/core": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.16.13.tgz",
- "integrity": "sha512-qXpA1tzTnlkTku9yqtuRtS/wVntvE6f3m3GNxdTdtmc+O+Wcg9Xo2ABPMh7Nc0AHbMKzwvwgB2JnjZmlmJEObg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13",
- "any-base": "^1.1.0",
- "buffer": "^5.2.0",
- "exif-parser": "^0.1.12",
- "file-type": "^16.5.4",
- "load-bmfont": "^1.3.1",
- "mkdirp": "^0.5.1",
- "phin": "^2.9.1",
- "pixelmatch": "^4.0.2",
- "tinycolor2": "^1.4.1"
- }
- },
- "node_modules/@jimp/core/node_modules/mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.6"
- },
- "bin": {
- "mkdirp": "bin/cmd.js"
- }
- },
- "node_modules/@jimp/custom": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.16.13.tgz",
- "integrity": "sha512-LTATglVUPGkPf15zX1wTMlZ0+AU7cGEGF6ekVF1crA8eHUWsGjrYTB+Ht4E3HTrCok8weQG+K01rJndCp/l4XA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/core": "^0.16.13"
- }
- },
- "node_modules/@jimp/gif": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.16.13.tgz",
- "integrity": "sha512-yFAMZGv3o+YcjXilMWWwS/bv1iSqykFahFMSO169uVMtfQVfa90kt4/kDwrXNR6Q9i6VHpFiGZMlF2UnHClBvg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13",
- "gifwrap": "^0.9.2",
- "omggif": "^1.0.9"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/jpeg": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.16.13.tgz",
- "integrity": "sha512-BJHlDxzTlCqP2ThqP8J0eDrbBfod7npWCbJAcfkKqdQuFk0zBPaZ6KKaQKyKxmWJ87Z6ohANZoMKEbtvrwz1AA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13",
- "jpeg-js": "^0.4.2"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-blit": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.16.13.tgz",
- "integrity": "sha512-8Z1k96ZFxlhK2bgrY1JNWNwvaBeI/bciLM0yDOni2+aZwfIIiC7Y6PeWHTAvjHNjphz+XCt01WQmOYWCn0ML6g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-blur": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.16.13.tgz",
- "integrity": "sha512-PvLrfa8vkej3qinlebyhLpksJgCF5aiysDMSVhOZqwH5nQLLtDE9WYbnsofGw4r0VVpyw3H/ANCIzYTyCtP9Cg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-circle": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.16.13.tgz",
- "integrity": "sha512-RNave7EFgZrb5V5EpdvJGAEHMnDAJuwv05hKscNfIYxf0kR3KhViBTDy+MoTnMlIvaKFULfwIgaZWzyhuINMzA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-color": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.16.13.tgz",
- "integrity": "sha512-xW+9BtEvoIkkH/Wde9ql4nAFbYLkVINhpgAE7VcBUsuuB34WUbcBl/taOuUYQrPEFQJ4jfXiAJZ2H/rvKjCVnQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13",
- "tinycolor2": "^1.4.1"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-contain": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.16.13.tgz",
- "integrity": "sha512-QayTXw4tXMwU6q6acNTQrTTFTXpNRBe+MgTGMDU0lk+23PjlFCO/9sacflelG8lsp7vNHhAxFeHptDMAksEYzg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5",
- "@jimp/plugin-blit": ">=0.3.5",
- "@jimp/plugin-resize": ">=0.3.5",
- "@jimp/plugin-scale": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-cover": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.16.13.tgz",
- "integrity": "sha512-BSsP71GTNaqWRcvkbWuIVH+zK7b3TSNebbhDkFK0fVaUTzHuKMS/mgY4hDZIEVt7Rf5FjadAYtsujHN9w0iSYA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5",
- "@jimp/plugin-crop": ">=0.3.5",
- "@jimp/plugin-resize": ">=0.3.5",
- "@jimp/plugin-scale": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-crop": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.16.13.tgz",
- "integrity": "sha512-WEl2tPVYwzYL8OKme6Go2xqiWgKsgxlMwyHabdAU4tXaRwOCnOI7v4021gCcBb9zn/oWwguHuKHmK30Fw2Z/PA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-displace": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.16.13.tgz",
- "integrity": "sha512-qt9WKq8vWrcjySa9DyQ0x/RBMHQeiVjdVSY1SJsMjssPUf0pS74qorcuAkGi89biN3YoGUgPkpqECnAWnYwgGA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-dither": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.16.13.tgz",
- "integrity": "sha512-5/N3yJggbWQTlGZHQYJPmQXEwR52qaXjEzkp1yRBbtdaekXE3BG/suo0fqeoV/csf8ooI78sJzYmIrxNoWVtgQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-fisheye": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.16.13.tgz",
- "integrity": "sha512-2rZmTdFbT/cF9lEZIkXCYO0TsT114Q27AX5IAo0Sju6jVQbvIk1dFUTnwLDadTo8wkJlFzGqMQ24Cs8cHWOliA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-flip": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.16.13.tgz",
- "integrity": "sha512-EmcgAA74FTc5u7Z+hUO/sRjWwfPPLuOQP5O64x5g4j0T12Bd29IgsYZxoutZo/rb3579+JNa/3wsSEmyVv1EpA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5",
- "@jimp/plugin-rotate": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-gaussian": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.16.13.tgz",
- "integrity": "sha512-A1XKfGQD0iDdIiKqFYi8nZMv4dDVYdxbrmgR7y/CzUHhSYdcmoljLIIsZZM3Iks/Wa353W3vtvkWLuDbQbch1w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-invert": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.16.13.tgz",
- "integrity": "sha512-xFMrIn7czEZbdbMzZWuaZFnlLGJDVJ82y5vlsKsXRTG2kcxRsMPXvZRWHV57nSs1YFsNqXSbrC8B98n0E32njQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-mask": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.16.13.tgz",
- "integrity": "sha512-wLRYKVBXql2GAYgt6FkTnCfE+q5NomM7Dlh0oIPGAoMBWDyTx0eYutRK6PlUrRK2yMHuroAJCglICTbxqGzowQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-normalize": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.16.13.tgz",
- "integrity": "sha512-3tfad0n9soRna4IfW9NzQdQ2Z3ijkmo21DREHbE6CGcMIxOSvfRdSvf1qQPApxjTSo8LTU4MCi/fidx/NZ0GqQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-print": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.16.13.tgz",
- "integrity": "sha512-0m6i3p01PGRkGAK9r53hDYrkyMq+tlhLOIbsSTmZyh6HLshUKlTB7eXskF5OpVd5ZUHoltlNc6R+ggvKIzxRFw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13",
- "load-bmfont": "^1.4.0"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5",
- "@jimp/plugin-blit": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-resize": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.16.13.tgz",
- "integrity": "sha512-qoqtN8LDknm3fJm9nuPygJv30O3vGhSBD2TxrsCnhtOsxKAqVPJtFVdGd/qVuZ8nqQANQmTlfqTiK9mVWQ7MiQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-rotate": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.16.13.tgz",
- "integrity": "sha512-Ev+Jjmj1nHYw897z9C3R9dYsPv7S2/nxdgfFb/h8hOwK0Ovd1k/+yYS46A0uj/JCKK0pQk8wOslYBkPwdnLorw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5",
- "@jimp/plugin-blit": ">=0.3.5",
- "@jimp/plugin-crop": ">=0.3.5",
- "@jimp/plugin-resize": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-scale": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.16.13.tgz",
- "integrity": "sha512-05POQaEJVucjTiSGMoH68ZiELc7QqpIpuQlZ2JBbhCV+WCbPFUBcGSmE7w4Jd0E2GvCho/NoMODLwgcVGQA97A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5",
- "@jimp/plugin-resize": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-shadow": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.16.13.tgz",
- "integrity": "sha512-nmu5VSZ9hsB1JchTKhnnCY+paRBnwzSyK5fhkhtQHHoFD5ArBQ/5wU8y6tCr7k/GQhhGq1OrixsECeMjPoc8Zw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5",
- "@jimp/plugin-blur": ">=0.3.5",
- "@jimp/plugin-resize": ">=0.3.5"
- }
- },
- "node_modules/@jimp/plugin-threshold": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.16.13.tgz",
- "integrity": "sha512-+3zArBH0OE3Rhjm4HyAokMsZlIq5gpQec33CncyoSwxtRBM2WAhUVmCUKuBo+Lr/2/4ISoY4BWpHKhMLDix6cA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5",
- "@jimp/plugin-color": ">=0.8.0",
- "@jimp/plugin-resize": ">=0.8.0"
- }
- },
- "node_modules/@jimp/plugins": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.16.13.tgz",
- "integrity": "sha512-CJLdqODEhEVs4MgWCxpWL5l95sCBlkuSLz65cxEm56X5akIsn4LOlwnKoSEZioYcZUBvHhCheH67AyPTudfnQQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/plugin-blit": "^0.16.13",
- "@jimp/plugin-blur": "^0.16.13",
- "@jimp/plugin-circle": "^0.16.13",
- "@jimp/plugin-color": "^0.16.13",
- "@jimp/plugin-contain": "^0.16.13",
- "@jimp/plugin-cover": "^0.16.13",
- "@jimp/plugin-crop": "^0.16.13",
- "@jimp/plugin-displace": "^0.16.13",
- "@jimp/plugin-dither": "^0.16.13",
- "@jimp/plugin-fisheye": "^0.16.13",
- "@jimp/plugin-flip": "^0.16.13",
- "@jimp/plugin-gaussian": "^0.16.13",
- "@jimp/plugin-invert": "^0.16.13",
- "@jimp/plugin-mask": "^0.16.13",
- "@jimp/plugin-normalize": "^0.16.13",
- "@jimp/plugin-print": "^0.16.13",
- "@jimp/plugin-resize": "^0.16.13",
- "@jimp/plugin-rotate": "^0.16.13",
- "@jimp/plugin-scale": "^0.16.13",
- "@jimp/plugin-shadow": "^0.16.13",
- "@jimp/plugin-threshold": "^0.16.13",
- "timm": "^1.6.1"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/png": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.16.13.tgz",
- "integrity": "sha512-8cGqINvbWJf1G0Her9zbq9I80roEX0A+U45xFby3tDWfzn+Zz8XKDF1Nv9VUwVx0N3zpcG1RPs9hfheG4Cq2kg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/utils": "^0.16.13",
- "pngjs": "^3.3.3"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/png/node_modules/pngjs": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
- "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/@jimp/tiff": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.16.13.tgz",
- "integrity": "sha512-oJY8d9u95SwW00VPHuCNxPap6Q1+E/xM5QThb9Hu+P6EGuu6lIeLaNBMmFZyblwFbwrH+WBOZlvIzDhi4Dm/6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "utif": "^2.0.1"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/types": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.16.13.tgz",
- "integrity": "sha512-mC0yVNUobFDjoYLg4hoUwzMKgNlxynzwt3cDXzumGvRJ7Kb8qQGOWJQjQFo5OxmGExqzPphkirdbBF88RVLBCg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/bmp": "^0.16.13",
- "@jimp/gif": "^0.16.13",
- "@jimp/jpeg": "^0.16.13",
- "@jimp/png": "^0.16.13",
- "@jimp/tiff": "^0.16.13",
- "timm": "^1.6.1"
- },
- "peerDependencies": {
- "@jimp/custom": ">=0.3.5"
- }
- },
- "node_modules/@jimp/utils": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.16.13.tgz",
- "integrity": "sha512-VyCpkZzFTHXtKgVO35iKN0sYR10psGpV6SkcSeV4oF7eSYlR8Bl6aQLCzVeFjvESF7mxTmIiI3/XrMobVrtxDA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "regenerator-runtime": "^0.13.3"
- }
- },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -2870,9 +1990,9 @@
}
},
"node_modules/@malept/flatpak-bundler/node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2944,44 +2064,6 @@
"node": "^18.17.0 || >=20.5.0"
}
},
- "node_modules/@npmcli/agent/node_modules/agent-base": {
- "version": "7.1.4",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
- "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/@npmcli/agent/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": "MIT",
- "dependencies": {
- "agent-base": "^7.1.0",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/@npmcli/agent/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==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.2",
- "debug": "4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/@npmcli/agent/node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
@@ -2989,48 +2071,30 @@
"dev": true,
"license": "ISC"
},
- "node_modules/@npmcli/agent/node_modules/socks-proxy-agent": {
- "version": "8.0.5",
- "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
- "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.2",
- "debug": "^4.3.4",
- "socks": "^2.8.3"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/@npmcli/fs": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz",
- "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz",
+ "integrity": "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==",
"dev": true,
"license": "ISC",
"dependencies": {
- "@gar/promisify": "^1.1.3",
"semver": "^7.3.5"
},
"engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ "node": "^18.17.0 || >=20.5.0"
}
},
- "node_modules/@npmcli/move-file": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz",
- "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==",
- "deprecated": "This functionality has been moved to @npmcli/fs",
+ "node_modules/@npmcli/fs/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "mkdirp": "^1.0.4",
- "rimraf": "^3.0.2"
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
},
"engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ "node": ">=10"
}
},
"node_modules/@pixi/color": {
@@ -3159,31 +2223,11 @@
"url": "^0.11.0"
}
},
- "node_modules/@pixi/utils/node_modules/@types/earcut": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-2.1.4.tgz",
- "integrity": "sha512-qp3m9PPz4gULB9MhjGID7wpo3gJ4bTGXm7ltNDsmOvsPduTeHp8wSW9YckBj3mljeOh4F0m2z/0JKAALRKbmLQ==",
- "license": "MIT",
- "peer": true
- },
- "node_modules/@pixi/utils/node_modules/earcut": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz",
- "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==",
- "license": "ISC",
- "peer": true
- },
- "node_modules/@pixi/utils/node_modules/eventemitter3": {
- "version": "4.0.7",
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
- "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
- "license": "MIT",
- "peer": true
- },
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
"license": "MIT",
"optional": true,
"engines": {
@@ -3191,12 +2235,13 @@
}
},
"node_modules/@playwright/test": {
- "version": "1.58.2",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz",
- "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==",
+ "version": "1.59.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz",
+ "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "playwright": "1.58.2"
+ "playwright": "1.59.1"
},
"bin": {
"playwright": "cli.js"
@@ -3205,6 +2250,13 @@
"node": ">=18"
}
},
+ "node_modules/@polka/url": {
+ "version": "1.0.0-next.29",
+ "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
+ "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@radix-ui/number": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
@@ -3327,6 +2379,24 @@
}
}
},
+ "node_modules/@radix-ui/react-collection/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-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
@@ -3393,6 +2463,24 @@
}
}
},
+ "node_modules/@radix-ui/react-dialog/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-direction": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
@@ -3562,6 +2650,24 @@
}
}
},
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-popover": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz",
@@ -3599,6 +2705,24 @@
}
}
},
+ "node_modules/@radix-ui/react-popover/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-popper": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
@@ -3702,6 +2826,24 @@
}
}
},
+ "node_modules/@radix-ui/react-primitive/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-roving-focus": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
@@ -3776,6 +2918,24 @@
}
}
},
+ "node_modules/@radix-ui/react-select/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-slider": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.6.tgz",
@@ -3810,9 +2970,9 @@
}
},
"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==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
+ "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
@@ -3974,6 +3134,24 @@
}
}
},
+ "node_modules/@radix-ui/react-tooltip/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-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",
@@ -4140,16 +3318,16 @@
"license": "MIT"
},
"node_modules/@rolldown/pluginutils": {
- "version": "1.0.0-beta.27",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
- "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
+ "version": "1.0.0-rc.3",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz",
+ "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==",
"dev": true,
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz",
- "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz",
+ "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==",
"cpu": [
"arm"
],
@@ -4161,9 +3339,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz",
- "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz",
+ "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==",
"cpu": [
"arm64"
],
@@ -4175,9 +3353,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz",
- "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz",
+ "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==",
"cpu": [
"arm64"
],
@@ -4189,9 +3367,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz",
- "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz",
+ "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==",
"cpu": [
"x64"
],
@@ -4203,9 +3381,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz",
- "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz",
+ "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==",
"cpu": [
"arm64"
],
@@ -4217,9 +3395,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz",
- "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz",
+ "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==",
"cpu": [
"x64"
],
@@ -4231,9 +3409,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz",
- "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz",
+ "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==",
"cpu": [
"arm"
],
@@ -4245,9 +3423,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz",
- "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz",
+ "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==",
"cpu": [
"arm"
],
@@ -4259,9 +3437,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz",
- "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz",
+ "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==",
"cpu": [
"arm64"
],
@@ -4273,9 +3451,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz",
- "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz",
+ "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==",
"cpu": [
"arm64"
],
@@ -4287,9 +3465,23 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz",
- "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz",
+ "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz",
+ "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==",
"cpu": [
"loong64"
],
@@ -4301,9 +3493,23 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz",
- "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz",
+ "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz",
+ "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==",
"cpu": [
"ppc64"
],
@@ -4315,9 +3521,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz",
- "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz",
+ "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==",
"cpu": [
"riscv64"
],
@@ -4329,9 +3535,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz",
- "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz",
+ "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==",
"cpu": [
"riscv64"
],
@@ -4343,9 +3549,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz",
- "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz",
+ "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==",
"cpu": [
"s390x"
],
@@ -4357,9 +3563,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz",
- "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz",
+ "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==",
"cpu": [
"x64"
],
@@ -4371,9 +3577,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz",
- "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz",
+ "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==",
"cpu": [
"x64"
],
@@ -4384,10 +3590,24 @@
"linux"
]
},
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz",
+ "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
"node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz",
- "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz",
+ "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==",
"cpu": [
"arm64"
],
@@ -4399,9 +3619,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz",
- "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz",
+ "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==",
"cpu": [
"arm64"
],
@@ -4413,9 +3633,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz",
- "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz",
+ "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==",
"cpu": [
"ia32"
],
@@ -4427,9 +3647,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz",
- "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz",
+ "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==",
"cpu": [
"x64"
],
@@ -4441,9 +3661,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz",
- "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz",
+ "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==",
"cpu": [
"x64"
],
@@ -4577,23 +3797,6 @@
"@testing-library/dom": ">=7.21.4"
}
},
- "node_modules/@tokenizer/token": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
- "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@tootallnate/once": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
- "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10"
- }
- },
"node_modules/@types/aria-query": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
@@ -4675,12 +3878,13 @@
"version": "0.0.12",
"resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.12.tgz",
"integrity": "sha512-x2tZZYkSxXqWvTDgveSynfjq/T2HyiZHXb00j/+gy19yp70PHCizM48XFdjBCWH7eHBD0R5i/pw9yMBP/BH5uA==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"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==",
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
+ "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4704,16 +3908,17 @@
}
},
"node_modules/@types/dom-webcodecs": {
- "version": "0.1.17",
- "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.17.tgz",
- "integrity": "sha512-IwKW5uKL0Zrv5ccUJpjIlqf7ppk2v29l/ZLQxLlwHxljBfnDD9Gxm+hzMkGM0AOAL/21H0pp7cTUYLiiVUGchA==",
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.13.tgz",
+ "integrity": "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==",
"license": "MIT"
},
"node_modules/@types/earcut": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-3.0.0.tgz",
- "integrity": "sha512-k/9fOUGO39yd2sCjrbAJvGDEQvRwRnQIZlBz43roGwUZo5SHAmyVvSFyaVVZkicRVCaDXPKlbxrUcBuJoSWunQ==",
- "license": "MIT"
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-2.1.4.tgz",
+ "integrity": "sha512-qp3m9PPz4gULB9MhjGID7wpo3gJ4bTGXm7ltNDsmOvsPduTeHp8wSW9YckBj3mljeOh4F0m2z/0JKAALRKbmLQ==",
+ "license": "MIT",
+ "peer": true
},
"node_modules/@types/estree": {
"version": "1.0.8",
@@ -4754,9 +3959,9 @@
"license": "MIT"
},
"node_modules/@types/http-cache-semantics": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
- "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==",
"dev": true,
"license": "MIT"
},
@@ -4778,13 +3983,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "25.0.3",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
- "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
+ "version": "22.19.17",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz",
+ "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "undici-types": "~7.16.0"
+ "undici-types": "~6.21.0"
}
},
"node_modules/@types/plist": {
@@ -4807,14 +4012,14 @@
"license": "MIT"
},
"node_modules/@types/react": {
- "version": "18.3.26",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.26.tgz",
- "integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==",
+ "version": "18.3.28",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz",
+ "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
- "csstype": "^3.0.2"
+ "csstype": "^3.2.2"
}
},
"node_modules/@types/react-dom": {
@@ -4837,13 +4042,6 @@
"@types/node": "*"
}
},
- "node_modules/@types/uuid": {
- "version": "10.0.0",
- "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
- "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/verror": {
"version": "1.10.11",
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz",
@@ -4864,9 +4062,9 @@
}
},
"node_modules/@uiw/color-convert": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/@uiw/color-convert/-/color-convert-2.9.2.tgz",
- "integrity": "sha512-ibw9OS29S7GlL+vDwU3p5XG3vhR7XdzUecydpZbakUeg2Td6nfsnrCAX9sbLwQ73p0abO42v+V4qRaWq+7/BjQ==",
+ "version": "2.10.1",
+ "resolved": "https://registry.npmjs.org/@uiw/color-convert/-/color-convert-2.10.1.tgz",
+ "integrity": "sha512-/Z3YfBiX+SErRM59yQH88Id+Xy/k10nnkfTuqhX6RB2yYUcG57DoFqb6FudhiQ5fwzKvKf1k4xq9lfT1UTFUKQ==",
"license": "MIT",
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
@@ -4876,14 +4074,14 @@
}
},
"node_modules/@uiw/react-color-block": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/@uiw/react-color-block/-/react-color-block-2.9.2.tgz",
- "integrity": "sha512-0EIZTELA5pnxyMlBOFo3hrpy73db+Qeq6E+QptNfD/8izor8OvY1Uquj2VqD6gDz+iVHMELIoKxpaQ8sZR7NOg==",
+ "version": "2.10.1",
+ "resolved": "https://registry.npmjs.org/@uiw/react-color-block/-/react-color-block-2.10.1.tgz",
+ "integrity": "sha512-nGfhUGZhCbYH/gVvD12H9wZ/NBiToiSyrSdbracyIbA01tZInmkRNmfzTCkPq7yoXMa09I9G8pPISoyHre4TQg==",
"license": "MIT",
"dependencies": {
- "@uiw/color-convert": "2.9.2",
- "@uiw/react-color-editable-input": "2.9.2",
- "@uiw/react-color-swatch": "2.9.2"
+ "@uiw/color-convert": "2.10.1",
+ "@uiw/react-color-editable-input": "2.10.1",
+ "@uiw/react-color-swatch": "2.10.1"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
@@ -4895,9 +4093,9 @@
}
},
"node_modules/@uiw/react-color-editable-input": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input/-/react-color-editable-input-2.9.2.tgz",
- "integrity": "sha512-DY7pu12+LDRn6cxDMvsy1/quaPTxicAPz/kfODV7KBf8+Hq4rFWeJ4KS6m22IKIbQxrBQgmQG0WFJLaPeY7cPw==",
+ "version": "2.10.1",
+ "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input/-/react-color-editable-input-2.10.1.tgz",
+ "integrity": "sha512-jMim8eAw/5hz7gaZwBy3vM5wMxPMocOG+u1+wcKbqvavHaeg/wHq7Y29uRyFKj80s4FXvUKehXRQl0F68mA7jQ==",
"license": "MIT",
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
@@ -4909,12 +4107,12 @@
}
},
"node_modules/@uiw/react-color-swatch": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/@uiw/react-color-swatch/-/react-color-swatch-2.9.2.tgz",
- "integrity": "sha512-6zBy+E9NzZR672M2wPsbbNRqKy9Wi9jOuuxxyzov1CEZp+pPX7UwMlCX6RUhKdO0PzTSPCVQmbz5bplu5vsW0w==",
+ "version": "2.10.1",
+ "resolved": "https://registry.npmjs.org/@uiw/react-color-swatch/-/react-color-swatch-2.10.1.tgz",
+ "integrity": "sha512-DuGlaIszNcvtsY8BfW+RiUkEK1yVmnAamkzc/S5cQZwaAA5bCKhwwyaKPqh1/XWs7pR4pysjSNlMaeqaSOO34A==",
"license": "MIT",
"dependencies": {
- "@uiw/color-convert": "2.9.2"
+ "@uiw/color-convert": "2.10.1"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
@@ -4926,65 +4124,139 @@
}
},
"node_modules/@vitejs/plugin-react": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
- "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz",
+ "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/core": "^7.28.0",
+ "@babel/core": "^7.29.0",
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
- "@rolldown/pluginutils": "1.0.0-beta.27",
+ "@rolldown/pluginutils": "1.0.0-rc.3",
"@types/babel__core": "^7.20.5",
- "react-refresh": "^0.17.0"
+ "react-refresh": "^0.18.0"
},
"engines": {
- "node": "^14.18.0 || >=16.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
- "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/@vitest/browser": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.5.tgz",
+ "integrity": "sha512-iCDGI8c4yg+xmjUg2VsygdAUSIIB4x5Rht/P68OXy1hPELKXHDkzh87lkuTcdYmemRChDkEpB426MmDjzC0ziA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@blazediff/core": "1.9.1",
+ "@vitest/mocker": "4.1.5",
+ "@vitest/utils": "4.1.5",
+ "magic-string": "^0.30.21",
+ "pngjs": "^7.0.0",
+ "sirv": "^3.0.2",
+ "tinyrainbow": "^3.1.0",
+ "ws": "^8.19.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "vitest": "4.1.5"
+ }
+ },
+ "node_modules/@vitest/browser-playwright": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.5.tgz",
+ "integrity": "sha512-CWy0lBQJq97nionyJJdnaU4961IXTl43a7UCu5nHy51IoKxAt6PVIJLo+76rVl7KOOgcWHNkG4kbJu/pW7knvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/browser": "4.1.5",
+ "@vitest/mocker": "4.1.5",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "playwright": "*",
+ "vitest": "4.1.5"
+ },
+ "peerDependenciesMeta": {
+ "playwright": {
+ "optional": false
+ }
}
},
"node_modules/@vitest/expect": {
- "version": "4.0.16",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.16.tgz",
- "integrity": "sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA==",
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz",
+ "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@standard-schema/spec": "^1.0.0",
+ "@standard-schema/spec": "^1.1.0",
"@types/chai": "^5.2.2",
- "@vitest/spy": "4.0.16",
- "@vitest/utils": "4.0.16",
- "chai": "^6.2.1",
- "tinyrainbow": "^3.0.3"
+ "@vitest/spy": "4.1.5",
+ "@vitest/utils": "4.1.5",
+ "chai": "^6.2.2",
+ "tinyrainbow": "^3.1.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
- "node_modules/@vitest/pretty-format": {
- "version": "4.0.16",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.16.tgz",
- "integrity": "sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA==",
+ "node_modules/@vitest/mocker": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz",
+ "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "tinyrainbow": "^3.0.3"
+ "@vitest/spy": "4.1.5",
+ "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"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz",
+ "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^3.1.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/runner": {
- "version": "4.0.16",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.16.tgz",
- "integrity": "sha512-VWEDm5Wv9xEo80ctjORcTQRJ539EGPB3Pb9ApvVRAY1U/WkHXmmYISqU5E79uCwcW7xYUV38gwZD+RV755fu3Q==",
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz",
+ "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/utils": "4.0.16",
+ "@vitest/utils": "4.1.5",
"pathe": "^2.0.3"
},
"funding": {
@@ -4992,13 +4264,14 @@
}
},
"node_modules/@vitest/snapshot": {
- "version": "4.0.16",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.16.tgz",
- "integrity": "sha512-sf6NcrYhYBsSYefxnry+DR8n3UV4xWZwWxYbCJUt2YdvtqzSPR7VfGrY0zsv090DAbjFZsi7ZaMi1KnSRyK1XA==",
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz",
+ "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "4.0.16",
+ "@vitest/pretty-format": "4.1.5",
+ "@vitest/utils": "4.1.5",
"magic-string": "^0.30.21",
"pathe": "^2.0.3"
},
@@ -5007,9 +4280,9 @@
}
},
"node_modules/@vitest/spy": {
- "version": "4.0.16",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.16.tgz",
- "integrity": "sha512-4jIOWjKP0ZUaEmJm00E0cOBLU+5WE0BpeNr3XN6TEF05ltro6NJqHWxXD0kA8/Zc8Nh23AT8WQxwNG+WeROupw==",
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz",
+ "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==",
"dev": true,
"license": "MIT",
"funding": {
@@ -5017,29 +4290,30 @@
}
},
"node_modules/@vitest/utils": {
- "version": "4.0.16",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.16.tgz",
- "integrity": "sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA==",
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz",
+ "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/pretty-format": "4.0.16",
- "tinyrainbow": "^3.0.3"
+ "@vitest/pretty-format": "4.1.5",
+ "convert-source-map": "^2.0.0",
+ "tinyrainbow": "^3.1.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@webgpu/types": {
- "version": "0.1.66",
- "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.66.tgz",
- "integrity": "sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA==",
+ "version": "0.1.69",
+ "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz",
+ "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==",
"license": "BSD-3-Clause"
},
"node_modules/@xmldom/xmldom": {
- "version": "0.8.11",
- "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
- "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==",
+ "version": "0.8.13",
+ "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz",
+ "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
@@ -5053,29 +4327,19 @@
"license": "MIT"
},
"node_modules/abbrev": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz",
+ "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==",
"dev": true,
- "license": "ISC"
- },
- "node_modules/abort-controller": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
- "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "event-target-shim": "^5.0.0"
- },
+ "license": "ISC",
"engines": {
- "node": ">=6.5"
+ "node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
"bin": {
@@ -5086,49 +4350,19 @@
}
},
"node_modules/agent-base": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
- "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "debug": "4"
- },
"engines": {
- "node": ">= 6.0.0"
- }
- },
- "node_modules/agentkeepalive": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
- "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "humanize-ms": "^1.2.1"
- },
- "engines": {
- "node": ">= 8.0.0"
- }
- },
- "node_modules/aggregate-error": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
- "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "clean-stack": "^2.0.0",
- "indent-string": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
+ "node": ">= 14"
}
},
"node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
+ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5172,6 +4406,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -5181,6 +4416,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
@@ -5192,13 +4428,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/any-base": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz",
- "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@@ -5218,6 +4447,18 @@
"node": ">= 8"
}
},
+ "node_modules/anymatch/node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/app-builder-bin": {
"version": "5.0.0-alpha.12",
"resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-5.0.0-alpha.12.tgz",
@@ -5226,9 +4467,9 @@
"license": "MIT"
},
"node_modules/app-builder-lib": {
- "version": "26.7.0",
- "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.7.0.tgz",
- "integrity": "sha512-/UgCD8VrO79Wv8aBNpjMfsS1pIUfIPURoRn0Ik6tMe5avdZF+vQgl/juJgipcMmH3YS0BD573lCdCHyoi84USg==",
+ "version": "26.8.1",
+ "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-26.8.1.tgz",
+ "integrity": "sha512-p0Im/Dx5C4tmz8QEE1Yn4MkuPC8PrnlRneMhWJj7BBXQfNTJUshM/bp3lusdEsDbvvfJZpXWnYesgSLvwtM2Zw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5243,7 +4484,7 @@
"@malept/flatpak-bundler": "^0.4.0",
"@types/fs-extra": "9.0.13",
"async-exit-hook": "^2.0.1",
- "builder-util": "26.4.1",
+ "builder-util": "26.8.1",
"builder-util-runtime": "9.5.1",
"chromium-pickle-js": "^0.2.0",
"ci-info": "4.3.1",
@@ -5251,7 +4492,7 @@
"dotenv": "^16.4.5",
"dotenv-expand": "^11.0.6",
"ejs": "^3.1.8",
- "electron-publish": "26.6.0",
+ "electron-publish": "26.8.1",
"fs-extra": "^10.1.0",
"hosted-git-info": "^4.1.0",
"isbinaryfile": "^5.0.0",
@@ -5273,8 +4514,8 @@
"node": ">=14.0.0"
},
"peerDependencies": {
- "dmg-builder": "26.7.0",
- "electron-builder-squirrel-windows": "26.7.0"
+ "dmg-builder": "26.8.1",
+ "electron-builder-squirrel-windows": "26.8.1"
}
},
"node_modules/app-builder-lib/node_modules/@electron/get": {
@@ -5324,52 +4565,6 @@
"semver": "bin/semver.js"
}
},
- "node_modules/app-builder-lib/node_modules/@isaacs/cliui": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz",
- "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/app-builder-lib/node_modules/balanced-match": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.2.tgz",
- "integrity": "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "jackspeak": "^4.2.3"
- },
- "engines": {
- "node": "20 || >=22"
- }
- },
- "node_modules/app-builder-lib/node_modules/brace-expansion": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz",
- "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^4.0.2"
- },
- "engines": {
- "node": "20 || >=22"
- }
- },
- "node_modules/app-builder-lib/node_modules/chownr": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
- "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/app-builder-lib/node_modules/ci-info": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz",
@@ -5402,9 +4597,9 @@
}
},
"node_modules/app-builder-lib/node_modules/fs-extra/node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5424,144 +4619,17 @@
"node": ">= 10.0.0"
}
},
- "node_modules/app-builder-lib/node_modules/isexe": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz",
- "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==",
+ "node_modules/app-builder-lib/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/app-builder-lib/node_modules/jackspeak": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz",
- "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "@isaacs/cliui": "^9.0.0"
- },
- "engines": {
- "node": "20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/app-builder-lib/node_modules/jiti": {
- "version": "2.6.1",
- "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
- "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
- "dev": true,
- "license": "MIT",
+ "license": "ISC",
"bin": {
- "jiti": "lib/jiti-cli.mjs"
- }
- },
- "node_modules/app-builder-lib/node_modules/minimatch": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.0.tgz",
- "integrity": "sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "brace-expansion": "^5.0.2"
+ "semver": "bin/semver.js"
},
"engines": {
- "node": "20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/app-builder-lib/node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/app-builder-lib/node_modules/minizlib": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
- "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "minipass": "^7.1.2"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/app-builder-lib/node_modules/tar": {
- "version": "7.5.7",
- "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz",
- "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "@isaacs/fs-minipass": "^4.0.0",
- "chownr": "^3.0.0",
- "minipass": "^7.1.2",
- "minizlib": "^3.1.0",
- "yallist": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/app-builder-lib/node_modules/which": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
- "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^3.1.1"
- },
- "bin": {
- "node-which": "bin/which.js"
- },
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
- "node_modules/app-builder-lib/node_modules/yallist": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
- "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/aproba": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz",
- "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/are-we-there-yet": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
- "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
- "deprecated": "This package is no longer supported.",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "delegates": "^1.0.0",
- "readable-stream": "^3.6.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ "node": ">=10"
}
},
"node_modules/arg": {
@@ -5577,100 +4645,6 @@
"dev": true,
"license": "Python-2.0"
},
- "node_modules/args": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz",
- "integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "camelcase": "5.0.0",
- "chalk": "2.4.2",
- "leven": "2.1.0",
- "mri": "1.1.4"
- },
- "engines": {
- "node": ">= 6.0.0"
- }
- },
- "node_modules/args/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/args/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/args/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/args/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/args/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/args/node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/args/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/aria-hidden": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
@@ -5693,32 +4667,13 @@
"dequal": "^2.0.3"
}
},
- "node_modules/array-union": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
- "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/asn1": {
- "version": "0.2.6",
- "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
- "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "safer-buffer": "~2.1.0"
- }
- },
"node_modules/assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
"dev": true,
"license": "MIT",
+ "optional": true,
"engines": {
"node": ">=0.8"
}
@@ -5779,9 +4734,9 @@
}
},
"node_modules/autoprefixer": {
- "version": "10.4.21",
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
- "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz",
+ "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==",
"dev": true,
"funding": [
{
@@ -5799,10 +4754,9 @@
],
"license": "MIT",
"dependencies": {
- "browserslist": "^4.24.4",
- "caniuse-lite": "^1.0.30001702",
- "fraction.js": "^4.3.7",
- "normalize-range": "^0.1.2",
+ "browserslist": "^4.28.2",
+ "caniuse-lite": "^1.0.30001787",
+ "fraction.js": "^5.3.4",
"picocolors": "^1.1.1",
"postcss-value-parser": "^4.2.0"
},
@@ -5816,28 +4770,15 @@
"postcss": "^8.1.0"
}
},
- "node_modules/aws-sign2": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
- "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/aws4": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz",
- "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==",
- "dev": true,
- "license": "MIT"
- },
"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"
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
},
"node_modules/base64-js": {
"version": "1.5.1",
@@ -5861,23 +4802,16 @@
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
- "version": "2.8.15",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.15.tgz",
- "integrity": "sha512-qsJ8/X+UypqxHXN75M7dF88jNK37dLBRW7LeUzCPz+TNs37G8cfWy9nWzS+LS//g600zrt2le9KuXt0rWfDz5Q==",
+ "version": "2.10.20",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.20.tgz",
+ "integrity": "sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
- "baseline-browser-mapping": "dist/cli.js"
- }
- },
- "node_modules/bcrypt-pbkdf": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
- "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "tweetnacl": "^0.14.3"
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
}
},
"node_modules/bidi-js": {
@@ -5914,13 +4848,6 @@
"readable-stream": "^3.4.0"
}
},
- "node_modules/bmp-js": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
- "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/boolean": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
@@ -5931,12 +4858,16 @@
"optional": true
},
"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==",
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "balanced-match": "^1.0.0"
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
}
},
"node_modules/braces": {
@@ -5952,9 +4883,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.26.3",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz",
- "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==",
+ "version": "4.28.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
+ "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
"dev": true,
"funding": [
{
@@ -5972,11 +4903,11 @@
],
"license": "MIT",
"dependencies": {
- "baseline-browser-mapping": "^2.8.9",
- "caniuse-lite": "^1.0.30001746",
- "electron-to-chromium": "^1.5.227",
- "node-releases": "^2.0.21",
- "update-browserslist-db": "^1.1.3"
+ "baseline-browser-mapping": "^2.10.12",
+ "caniuse-lite": "^1.0.30001782",
+ "electron-to-chromium": "^1.5.328",
+ "node-releases": "^2.0.36",
+ "update-browserslist-db": "^1.2.3"
},
"bin": {
"browserslist": "cli.js"
@@ -6028,9 +4959,9 @@
"license": "MIT"
},
"node_modules/builder-util": {
- "version": "26.4.1",
- "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.4.1.tgz",
- "integrity": "sha512-FlgH43XZ50w3UtS1RVGDWOz8v9qMXPC7upMtKMtBEnYdt1OVoS61NYhKm/4x+cIaWqJTXua0+VVPI+fSPGXNIw==",
+ "version": "26.8.1",
+ "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.8.1.tgz",
+ "integrity": "sha512-pm1lTYbGyc90DHgCDO7eo8Rl4EqKLciayNbZqGziqnH9jrlKe8ZANGdityLZU+pJh16dfzjAx2xQq9McuIPEtw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6066,16 +4997,6 @@
"node": ">=12.0.0"
}
},
- "node_modules/builder-util/node_modules/agent-base": {
- "version": "7.1.4",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
- "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/builder-util/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
@@ -6091,38 +5012,10 @@
"node": ">=12"
}
},
- "node_modules/builder-util/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": "MIT",
- "dependencies": {
- "agent-base": "^7.1.0",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/builder-util/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==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.2",
- "debug": "4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/builder-util/node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6143,99 +5036,91 @@
}
},
"node_modules/cacache": {
- "version": "16.1.3",
- "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz",
- "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==",
+ "version": "19.0.1",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-19.0.1.tgz",
+ "integrity": "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==",
"dev": true,
"license": "ISC",
"dependencies": {
- "@npmcli/fs": "^2.1.0",
- "@npmcli/move-file": "^2.0.0",
- "chownr": "^2.0.0",
- "fs-minipass": "^2.1.0",
- "glob": "^8.0.1",
- "infer-owner": "^1.0.4",
- "lru-cache": "^7.7.1",
- "minipass": "^3.1.6",
- "minipass-collect": "^1.0.2",
+ "@npmcli/fs": "^4.0.0",
+ "fs-minipass": "^3.0.0",
+ "glob": "^10.2.2",
+ "lru-cache": "^10.0.1",
+ "minipass": "^7.0.3",
+ "minipass-collect": "^2.0.1",
"minipass-flush": "^1.0.5",
"minipass-pipeline": "^1.2.4",
- "mkdirp": "^1.0.4",
- "p-map": "^4.0.0",
- "promise-inflight": "^1.0.1",
- "rimraf": "^3.0.2",
- "ssri": "^9.0.0",
- "tar": "^6.1.11",
- "unique-filename": "^2.0.0"
+ "p-map": "^7.0.2",
+ "ssri": "^12.0.0",
+ "tar": "^7.4.3",
+ "unique-filename": "^4.0.0"
},
"engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/cacache/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cacache/node_modules/brace-expansion": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
+ "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
}
},
"node_modules/cacache/node_modules/glob": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
- "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
+ "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",
"dev": true,
"license": "ISC",
"dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^5.0.1",
- "once": "^1.3.0"
+ "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"
},
- "engines": {
- "node": ">=12"
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/cacache/node_modules/lru-cache": {
- "version": "7.18.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
- "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/cacache/node_modules/minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/cacache/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cacache/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "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/cacache/node_modules/minimatch": {
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/cacheable-lookup": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
@@ -6295,16 +5180,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/camelcase": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
- "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
@@ -6315,9 +5190,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001749",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001749.tgz",
- "integrity": "sha512-0rw2fJOmLfnzCRbkm8EyHL8SvI2Apu5UbnQuTsJ0ClgrH8hcwFooJ1s5R0EP8o8aVrFu8++ae29Kt9/gZAZp/Q==",
+ "version": "1.0.30001788",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz",
+ "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==",
"dev": true,
"funding": [
{
@@ -6335,27 +5210,10 @@
],
"license": "CC-BY-4.0"
},
- "node_modules/caseless": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
- "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
- "dev": true,
- "license": "Apache-2.0"
- },
- "node_modules/centra": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/centra/-/centra-2.7.0.tgz",
- "integrity": "sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "follow-redirects": "^1.15.6"
- }
- },
"node_modules/chai": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz",
- "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==",
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6416,13 +5274,13 @@
}
},
"node_modules/chownr": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
- "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
"dev": true,
- "license": "ISC",
+ "license": "BlueOak-1.0.0",
"engines": {
- "node": ">=10"
+ "node": ">=18"
}
},
"node_modules/chromium-pickle-js": {
@@ -6460,27 +5318,20 @@
"url": "https://polar.sh/cva"
}
},
- "node_modules/clean-stack": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
- "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/cli-cursor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
- "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "restore-cursor": "^3.1.0"
+ "restore-cursor": "^5.0.0"
},
"engines": {
- "node": ">=8"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/cli-spinners": {
@@ -6529,6 +5380,37 @@
"node": ">=12"
}
},
+ "node_modules/cliui/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
"node_modules/clone": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
@@ -6561,20 +5443,11 @@
"node": ">=6"
}
},
- "node_modules/code-point-at": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
- "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.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==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
@@ -6587,17 +5460,8 @@
"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-support": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
- "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
"dev": true,
- "license": "ISC",
- "bin": {
- "color-support": "bin.js"
- }
+ "license": "MIT"
},
"node_modules/colorette": {
"version": "2.0.20",
@@ -6646,62 +5510,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/concat-stream": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
- "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
- "dev": true,
- "engines": [
- "node >= 0.8"
- ],
- "license": "MIT",
- "dependencies": {
- "buffer-from": "^1.0.0",
- "inherits": "^2.0.3",
- "readable-stream": "^2.2.2",
- "typedarray": "^0.0.6"
- }
- },
- "node_modules/concat-stream/node_modules/readable-stream": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
- "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "node_modules/concat-stream/node_modules/safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/concat-stream/node_modules/string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "~5.1.0"
- }
- },
- "node_modules/console-control-strings": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
- "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
- "dev": true,
- "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",
@@ -6714,7 +5522,8 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "optional": true
},
"node_modules/crc": {
"version": "3.8.0",
@@ -6740,6 +5549,7 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
@@ -6750,6 +5560,29 @@
"node": ">= 8"
}
},
+ "node_modules/cross-spawn/node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/cross-spawn/node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/css-tree": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz",
@@ -6784,25 +5617,12 @@
}
},
"node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "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/dashdash": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
- "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "assert-plus": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
"node_modules/data-urls": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz",
@@ -6835,16 +5655,6 @@
}
}
},
- "node_modules/decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/decimal.js": {
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
@@ -6942,29 +5752,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/del": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz",
- "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "globby": "^11.0.1",
- "graceful-fs": "^4.2.4",
- "is-glob": "^4.0.1",
- "is-path-cwd": "^2.2.0",
- "is-path-inside": "^3.0.2",
- "p-map": "^4.0.0",
- "rimraf": "^3.0.2",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -6975,13 +5762,6 @@
"node": ">=0.4.0"
}
},
- "node_modules/delegates": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
- "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -7033,10 +5813,17 @@
"p-limit": "^3.1.0 "
}
},
+ "node_modules/dir-compare/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==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/dir-compare/node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7045,9 +5832,9 @@
}
},
"node_modules/dir-compare/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "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": {
@@ -7057,19 +5844,6 @@
"node": "*"
}
},
- "node_modules/dir-glob": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
- "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "path-type": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
@@ -7077,14 +5851,14 @@
"license": "MIT"
},
"node_modules/dmg-builder": {
- "version": "26.7.0",
- "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.7.0.tgz",
- "integrity": "sha512-uOOBA3f+kW3o4KpSoMQ6SNpdXU7WtxlJRb9vCZgOvqhTz4b3GjcoWKstdisizNZLsylhTMv8TLHFPFW0Uxsj/g==",
+ "version": "26.8.1",
+ "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-26.8.1.tgz",
+ "integrity": "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "app-builder-lib": "26.7.0",
- "builder-util": "26.4.1",
+ "app-builder-lib": "26.8.1",
+ "builder-util": "26.8.1",
"fs-extra": "^10.1.0",
"iconv-lite": "^0.6.2",
"js-yaml": "^4.1.0"
@@ -7109,9 +5883,9 @@
}
},
"node_modules/dmg-builder/node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7159,9 +5933,9 @@
}
},
"node_modules/dnd-timeline": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/dnd-timeline/-/dnd-timeline-2.2.0.tgz",
- "integrity": "sha512-bQ/2bm70eA7YeztgdxoSpdpTwPzj8VT2/wTlYrnFpqJ71et7EVJZR35XPZIVzBqSyKK9T/QyUzE26gYck9ldxg==",
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/dnd-timeline/-/dnd-timeline-2.4.0.tgz",
+ "integrity": "sha512-f71y55DjT2VDongtnqVd7S6XGmpUT1PkYlooFQZxLHfPVfKsyTtMHsfbXN98k2GkA5Z4h3ibqFjwBIfckE0aEg==",
"license": "MIT",
"dependencies": {
"@dnd-kit/core": "^6.1.0",
@@ -7177,12 +5951,6 @@
"license": "MIT",
"peer": true
},
- "node_modules/dom-walk": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
- "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==",
- "dev": true
- },
"node_modules/dotenv": {
"version": "16.6.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
@@ -7227,27 +5995,18 @@
}
},
"node_modules/earcut": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz",
- "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==",
- "license": "ISC"
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz",
+ "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==",
+ "license": "ISC",
+ "peer": true
},
"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/ecc-jsbn": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
- "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "jsbn": "~0.1.0",
- "safer-buffer": "^2.1.0"
- }
+ "license": "MIT"
},
"node_modules/ejs": {
"version": "3.1.10",
@@ -7266,15 +6025,15 @@
}
},
"node_modules/electron": {
- "version": "39.2.7",
- "resolved": "https://registry.npmjs.org/electron/-/electron-39.2.7.tgz",
- "integrity": "sha512-KU0uFS6LSTh4aOIC3miolcbizOFP7N1M46VTYVfqIgFiuA2ilfNaOHLDS9tCMvwwHRowAsvqBrh9NgMXcTOHCQ==",
+ "version": "41.2.1",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-41.2.1.tgz",
+ "integrity": "sha512-teeRThiYGTPKf/2yOW7zZA1bhb91KEQ4yLBPOg7GxpmnkLFLugKgQaAKOrCgdzwsXh/5mFIfmkm+4+wACJKwaA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@electron/get": "^2.0.0",
- "@types/node": "^22.7.7",
+ "@types/node": "^24.9.0",
"extract-zip": "^2.0.1"
},
"bin": {
@@ -7285,18 +6044,18 @@
}
},
"node_modules/electron-builder": {
- "version": "26.7.0",
- "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.7.0.tgz",
- "integrity": "sha512-LoXbCvSFxLesPneQ/fM7FB4OheIDA2tjqCdUkKlObV5ZKGhYgi5VHPHO/6UUOUodAlg7SrkPx7BZJPby+Vrtbg==",
+ "version": "26.8.1",
+ "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-26.8.1.tgz",
+ "integrity": "sha512-uWhx1r74NGpCagG0ULs/P9Nqv2nsoo+7eo4fLUOB8L8MdWltq9odW/uuLXMFCDGnPafknYLZgjNX0ZIFRzOQAw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "app-builder-lib": "26.7.0",
- "builder-util": "26.4.1",
+ "app-builder-lib": "26.8.1",
+ "builder-util": "26.8.1",
"builder-util-runtime": "9.5.1",
"chalk": "^4.1.2",
"ci-info": "^4.2.0",
- "dmg-builder": "26.7.0",
+ "dmg-builder": "26.8.1",
"fs-extra": "^10.1.0",
"lazy-val": "^1.0.5",
"simple-update-notifier": "2.0.0",
@@ -7311,15 +6070,15 @@
}
},
"node_modules/electron-builder-squirrel-windows": {
- "version": "26.7.0",
- "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.7.0.tgz",
- "integrity": "sha512-3EqkQK+q0kGshdPSKEPb2p5F75TENMKu6Fe5aTdeaPfdzFK4Yjp5L0d6S7K8iyvqIsGQ/ei4bnpyX9wt+kVCKQ==",
+ "version": "26.8.1",
+ "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.8.1.tgz",
+ "integrity": "sha512-o288fIdgPLHA76eDrFADHPoo7VyGkDCYbLV1GzndaMSAVBoZrGvM9m2IehdcVMzdAZJ2eV9bgyissQXHv5tGzA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
- "app-builder-lib": "26.7.0",
- "builder-util": "26.4.1",
+ "app-builder-lib": "26.8.1",
+ "builder-util": "26.8.1",
"electron-winstaller": "5.4.0"
}
},
@@ -7339,9 +6098,9 @@
}
},
"node_modules/electron-builder/node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7361,33 +6120,15 @@
"node": ">= 10.0.0"
}
},
- "node_modules/electron-icon-builder": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/electron-icon-builder/-/electron-icon-builder-2.0.1.tgz",
- "integrity": "sha512-rg9BxW2kJi3TXsMFFNXWXrwQEd5dzXmeD+w7Pj3k3z7aYRePLxE89qU4lvL/rK1X/NTY5KDn3+Dbgm1TU2dGXQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "args": "^5.0.1",
- "icon-gen": "^2.0.0",
- "jimp": "^0.16.1"
- },
- "bin": {
- "electron-icon-builder": "index.js"
- },
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/electron-publish": {
- "version": "26.6.0",
- "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.6.0.tgz",
- "integrity": "sha512-LsyHMMqbvJ2vsOvuWJ19OezgF2ANdCiHpIucDHNiLhuI+/F3eW98ouzWSRmXXi82ZOPZXC07jnIravY4YYwCLQ==",
+ "version": "26.8.1",
+ "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.8.1.tgz",
+ "integrity": "sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/fs-extra": "^9.0.11",
- "builder-util": "26.4.1",
+ "builder-util": "26.8.1",
"builder-util-runtime": "9.5.1",
"chalk": "^4.1.2",
"form-data": "^4.0.5",
@@ -7412,9 +6153,9 @@
}
},
"node_modules/electron-publish/node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7434,78 +6175,10 @@
"node": ">= 10.0.0"
}
},
- "node_modules/electron-rebuild": {
- "version": "3.2.9",
- "resolved": "https://registry.npmjs.org/electron-rebuild/-/electron-rebuild-3.2.9.tgz",
- "integrity": "sha512-FkEZNFViUem3P0RLYbZkUjC8LUFIK+wKq09GHoOITSJjfDAVQv964hwaNseTTWt58sITQX3/5fHNYcTefqaCWw==",
- "deprecated": "Please use @electron/rebuild moving forward. There is no API change, just a package name change",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@malept/cross-spawn-promise": "^2.0.0",
- "chalk": "^4.0.0",
- "debug": "^4.1.1",
- "detect-libc": "^2.0.1",
- "fs-extra": "^10.0.0",
- "got": "^11.7.0",
- "lzma-native": "^8.0.5",
- "node-abi": "^3.0.0",
- "node-api-version": "^0.1.4",
- "node-gyp": "^9.0.0",
- "ora": "^5.1.0",
- "semver": "^7.3.5",
- "tar": "^6.0.5",
- "yargs": "^17.0.1"
- },
- "bin": {
- "electron-rebuild": "lib/src/cli.js"
- },
- "engines": {
- "node": ">=12.13.0"
- }
- },
- "node_modules/electron-rebuild/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/electron-rebuild/node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/electron-rebuild/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/electron-to-chromium": {
- "version": "1.5.234",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.234.tgz",
- "integrity": "sha512-RXfEp2x+VRYn8jbKfQlRImzoJU01kyDvVPBmG39eU2iuRVhuS6vQNocB8J0/8GrIMLnPzgz4eW6WiRnJkTuNWg==",
+ "version": "1.5.340",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.340.tgz",
+ "integrity": "sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==",
"dev": true,
"license": "ISC"
},
@@ -7548,26 +6221,26 @@
}
},
"node_modules/electron/node_modules/@types/node": {
- "version": "22.19.3",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz",
- "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==",
+ "version": "24.12.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz",
+ "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "undici-types": "~6.21.0"
+ "undici-types": "~7.16.0"
}
},
"node_modules/electron/node_modules/undici-types": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
- "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true,
"license": "MIT"
},
"node_modules/emoji-picker-react": {
- "version": "4.16.1",
- "resolved": "https://registry.npmjs.org/emoji-picker-react/-/emoji-picker-react-4.16.1.tgz",
- "integrity": "sha512-MrPX0tOCfRL3uYI4of/2GRZ7S6qS7YlacKiF78uFH84/C62vcuHE2DZyv5b4ZJMk0e06es1jjB4e31Bb+YSM8w==",
+ "version": "4.18.0",
+ "resolved": "https://registry.npmjs.org/emoji-picker-react/-/emoji-picker-react-4.18.0.tgz",
+ "integrity": "sha512-vLTrLfApXAIciguGE57pXPWs9lPLBspbEpPMiUq03TIli2dHZBiB+aZ0R9/Wat0xmTfcd4AuEzQgSYxEZ8C88Q==",
"license": "MIT",
"dependencies": {
"flairup": "1.0.0"
@@ -7583,6 +6256,7 @@
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
"license": "MIT"
},
"node_modules/encoding": {
@@ -7607,13 +6281,13 @@
}
},
"node_modules/entities": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
- "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz",
+ "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
- "node": ">=0.12"
+ "node": ">=20.19.0"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
@@ -7649,16 +6323,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/error-ex": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
- "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-arrayish": "^0.2.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",
@@ -7678,9 +6342,9 @@
}
},
"node_modules/es-module-lexer": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
- "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+ "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"
},
@@ -7720,17 +6384,10 @@
"license": "MIT",
"optional": true
},
- "node_modules/es6-promise": {
- "version": "4.2.8",
- "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
- "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
- "dev": true,
- "license": "MIT"
- },
"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==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
+ "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -7738,32 +6395,35 @@
"esbuild": "bin/esbuild"
},
"engines": {
- "node": ">=12"
+ "node": ">=18"
},
"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"
+ "@esbuild/aix-ppc64": "0.27.7",
+ "@esbuild/android-arm": "0.27.7",
+ "@esbuild/android-arm64": "0.27.7",
+ "@esbuild/android-x64": "0.27.7",
+ "@esbuild/darwin-arm64": "0.27.7",
+ "@esbuild/darwin-x64": "0.27.7",
+ "@esbuild/freebsd-arm64": "0.27.7",
+ "@esbuild/freebsd-x64": "0.27.7",
+ "@esbuild/linux-arm": "0.27.7",
+ "@esbuild/linux-arm64": "0.27.7",
+ "@esbuild/linux-ia32": "0.27.7",
+ "@esbuild/linux-loong64": "0.27.7",
+ "@esbuild/linux-mips64el": "0.27.7",
+ "@esbuild/linux-ppc64": "0.27.7",
+ "@esbuild/linux-riscv64": "0.27.7",
+ "@esbuild/linux-s390x": "0.27.7",
+ "@esbuild/linux-x64": "0.27.7",
+ "@esbuild/netbsd-arm64": "0.27.7",
+ "@esbuild/netbsd-x64": "0.27.7",
+ "@esbuild/openbsd-arm64": "0.27.7",
+ "@esbuild/openbsd-x64": "0.27.7",
+ "@esbuild/openharmony-arm64": "0.27.7",
+ "@esbuild/sunos-x64": "0.27.7",
+ "@esbuild/win32-arm64": "0.27.7",
+ "@esbuild/win32-ia32": "0.27.7",
+ "@esbuild/win32-x64": "0.27.7"
}
},
"node_modules/escalade": {
@@ -7800,37 +6460,12 @@
"@types/estree": "^1.0.0"
}
},
- "node_modules/event-target-shim": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
- "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/eventemitter3": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
- "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
- "license": "MIT"
- },
- "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,
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
"license": "MIT",
- "engines": {
- "node": ">=0.8.x"
- }
- },
- "node_modules/exif-parser": {
- "version": "0.1.12",
- "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz",
- "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==",
- "dev": true
+ "peer": true
},
"node_modules/expect-type": {
"version": "1.3.0",
@@ -7849,13 +6484,6 @@
"dev": true,
"license": "Apache-2.0"
},
- "node_modules/extend": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
- "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/extract-zip": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
@@ -7889,9 +6517,9 @@
"optional": true
},
"node_modules/fast-check": {
- "version": "4.5.2",
- "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.5.2.tgz",
- "integrity": "sha512-tOzL01LMrDIWPLfvMiGUMH0AjqnOelHQPmgvYkW/aRO4Yaw+pBQqWmyebNzAEbKOigoCN8HkRWUZXFkjmiaXMQ==",
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.7.0.tgz",
+ "integrity": "sha512-NsZRtqvSSoCP0HbNjUD+r1JH8zqZalyp6gLY9e7OYs7NK9b6AHOs2baBFeBG7bVNsuoukh89x2Yg3rPsul8ziQ==",
"dev": true,
"funding": [
{
@@ -7905,7 +6533,7 @@
],
"license": "MIT",
"dependencies": {
- "pure-rand": "^7.0.0"
+ "pure-rand": "^8.0.0"
},
"engines": {
"node": ">=12.17.0"
@@ -7954,9 +6582,9 @@
"license": "MIT"
},
"node_modules/fastq": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
- "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
"license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
@@ -7972,48 +6600,54 @@
"pend": "~1.2.0"
}
},
- "node_modules/file-type": {
- "version": "16.5.4",
- "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
- "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "readable-web-to-node-stream": "^3.0.0",
- "strtok3": "^6.2.4",
- "token-types": "^4.1.1"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/file-type?sponsor=1"
- }
- },
- "node_modules/file-url": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/file-url/-/file-url-2.0.2.tgz",
- "integrity": "sha512-x3989K8a1jM6vulMigE8VngH7C5nci0Ks5d9kVjUXmNF28gmiZUNujk5HjwaS8dAzN2QmUfX56riJKgN00dNRw==",
- "dev": true,
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"license": "MIT",
"engines": {
- "node": ">=4"
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
}
},
"node_modules/filelist": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
- "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
+ "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"minimatch": "^5.0.1"
}
},
+ "node_modules/filelist/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/filelist/node_modules/brace-expansion": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
+ "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
"node_modules/filelist/node_modules/minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -8047,31 +6681,11 @@
"integrity": "sha512-IKlE+pNvL2R+kVL1kEhUYqRxVqeFnjiIvHWDMLFXNaqyUdFXQM2wte44EfMYJNHkW16X991t2Zg8apKkhv7OBA==",
"license": "MIT"
},
- "node_modules/follow-redirects": {
- "version": "1.15.11",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
- "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
- "dev": true,
- "funding": [
- {
- "type": "individual",
- "url": "https://github.com/sponsors/RubenVerborgh"
- }
- ],
- "license": "MIT",
- "engines": {
- "node": ">=4.0"
- },
- "peerDependenciesMeta": {
- "debug": {
- "optional": true
- }
- }
- },
"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==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"cross-spawn": "^7.0.6",
@@ -8084,14 +6698,17 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/forever-agent": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
- "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
+ "node_modules/foreground-child/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==",
"dev": true,
- "license": "Apache-2.0",
+ "license": "ISC",
"engines": {
- "node": "*"
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/form-data": {
@@ -8112,27 +6729,27 @@
}
},
"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==",
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "*"
},
"funding": {
- "type": "patreon",
+ "type": "github",
"url": "https://github.com/sponsors/rawify"
}
},
"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==",
+ "version": "12.38.0",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz",
+ "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==",
"license": "MIT",
"dependencies": {
- "motion-dom": "^12.23.23",
- "motion-utils": "^12.23.6",
+ "motion-dom": "^12.38.0",
+ "motion-utils": "^12.36.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
@@ -8168,38 +6785,18 @@
}
},
"node_modules/fs-minipass": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
- "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
+ "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==",
"dev": true,
"license": "ISC",
"dependencies": {
- "minipass": "^3.0.0"
+ "minipass": "^7.0.3"
},
"engines": {
- "node": ">= 8"
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
- "node_modules/fs-minipass/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/fs-minipass/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -8208,9 +6805,9 @@
"license": "ISC"
},
"node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -8230,34 +6827,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/gauge": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
- "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
- "deprecated": "This package is no longer supported.",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "aproba": "^1.0.3 || ^2.0.0",
- "color-support": "^1.1.3",
- "console-control-strings": "^1.1.0",
- "has-unicode": "^2.0.1",
- "signal-exit": "^3.0.7",
- "string-width": "^4.2.3",
- "strip-ansi": "^6.0.1",
- "wide-align": "^1.1.5"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
- }
- },
- "node_modules/gauge/node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -8353,16 +6922,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/getpass": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
- "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "assert-plus": "^1.0.0"
- }
- },
"node_modules/gif.js": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/gif.js/-/gif.js-0.2.0.tgz",
@@ -8378,22 +6937,11 @@
"js-binary-schema-parser": "^2.0.3"
}
},
- "node_modules/gifwrap": {
- "version": "0.9.4",
- "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.9.4.tgz",
- "integrity": "sha512-MDMwbhASQuVeD4JKd1fKgNgCRL3fGqMM4WaqpNhWO0JiMOAjbQdumbs4BbBZEy9/M00EHEjKN3HieVhCUlwjeQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "image-q": "^4.0.0",
- "omggif": "^1.0.10"
- }
- },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
+ "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",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -8423,10 +6971,17 @@
"node": ">=10.13.0"
}
},
+ "node_modules/glob/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==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/glob/node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8435,9 +6990,9 @@
}
},
"node_modules/glob/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "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": {
@@ -8447,17 +7002,6 @@
"node": "*"
}
},
- "node_modules/global": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
- "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "min-document": "^2.19.0",
- "process": "^0.11.10"
- }
- },
"node_modules/global-agent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
@@ -8477,6 +7021,20 @@
"node": ">=10.0"
}
},
+ "node_modules/global-agent/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/globalthis": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
@@ -8495,27 +7053,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/globby": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
- "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "array-union": "^2.1.0",
- "dir-glob": "^3.0.1",
- "fast-glob": "^3.2.9",
- "ignore": "^5.2.0",
- "merge2": "^1.4.1",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -8562,36 +7099,11 @@
"license": "ISC"
},
"node_modules/gsap": {
- "version": "3.13.0",
- "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.13.0.tgz",
- "integrity": "sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==",
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.15.0.tgz",
+ "integrity": "sha512-dMW4CWBTUK1AEEDeZc1g4xpPGIrSf9fJF960qbTZmN/QwZIWY5wgliS6JWl9/25fpTGJrMRtSjGtOmPnfjZB+A==",
"license": "Standard 'no charge' license: https://gsap.com/standard-license."
},
- "node_modules/har-schema": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
- "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/har-validator": {
- "version": "5.1.5",
- "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
- "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
- "deprecated": "this library is no longer supported",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ajv": "^6.12.3",
- "har-schema": "^2.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -8644,31 +7156,10 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/has-unicode": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
- "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/hasha": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz",
- "integrity": "sha512-jZ38TU/EBiGKrmyTNNZgnvCZHNowiRI4+w/I9noMlekHTZH3KyGgvJLmhSgykeAQ9j2SYPDosM0Bg3wHfzibAQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-stream": "^1.0.1",
- "pinkie-promise": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/hasown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
+ "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -8731,34 +7222,17 @@
"license": "BSD-2-Clause"
},
"node_modules/http-proxy-agent": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
- "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "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": "MIT",
"dependencies": {
- "@tootallnate/once": "2",
- "agent-base": "6",
- "debug": "4"
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
},
"engines": {
- "node": ">= 6"
- }
- },
- "node_modules/http-signature": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
- "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "assert-plus": "^1.0.0",
- "jsprim": "^1.2.2",
- "sshpk": "^1.7.0"
- },
- "engines": {
- "node": ">=0.8",
- "npm": ">=1.3.7"
+ "node": ">= 14"
}
},
"node_modules/http2-wrapper": {
@@ -8776,27 +7250,17 @@
}
},
"node_modules/https-proxy-agent": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
- "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "agent-base": "6",
+ "agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
- "node": ">= 6"
- }
- },
- "node_modules/humanize-ms": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
- "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.0.0"
+ "node": ">= 14"
}
},
"node_modules/husky": {
@@ -8815,47 +7279,6 @@
"url": "https://github.com/sponsors/typicode"
}
},
- "node_modules/icon-gen": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/icon-gen/-/icon-gen-2.1.0.tgz",
- "integrity": "sha512-rqIVvq9MJ8X7wnJW0NO8Eau/+5RWV7AH6L5vEt/U5Ajv5WefdDNDxGwJhGokyHuyBWeX7JqRMQ03tG0gAco4Eg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "commander": "^6.2.0",
- "del": "^6.0.0",
- "mkdirp": "^1.0.4",
- "pngjs": "^6.0.0",
- "svg2png": "4.1.1",
- "uuid": "^8.3.1"
- },
- "bin": {
- "icon-gen": "dist/bin/index.js"
- },
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/icon-gen/node_modules/commander": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
- "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/icon-gen/node_modules/uuid": {
- "version": "8.3.2",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
- "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "uuid": "dist/bin/uuid"
- }
- },
"node_modules/iconv-corefoundation": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
@@ -8908,33 +7331,6 @@
],
"license": "BSD-3-Clause"
},
- "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": "MIT",
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/image-q": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz",
- "integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/node": "16.9.1"
- }
- },
- "node_modules/image-q/node_modules/@types/node": {
- "version": "16.9.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
- "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@@ -8955,13 +7351,6 @@
"node": ">=8"
}
},
- "node_modules/infer-owner": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
- "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -8981,33 +7370,16 @@
"dev": true,
"license": "ISC"
},
- "node_modules/invert-kv": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
- "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/ip-address": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz",
- "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==",
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
+ "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 12"
}
},
- "node_modules/is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "dev": true,
- "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",
@@ -9048,18 +7420,12 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
- "node_modules/is-function": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz",
- "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -9082,13 +7448,6 @@
"node": ">=8"
}
},
- "node_modules/is-lambda": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
- "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -9098,26 +7457,6 @@
"node": ">=0.12.0"
}
},
- "node_modules/is-path-cwd": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
- "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/is-path-inside": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
- "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/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",
@@ -9125,23 +7464,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/is-stream": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-typedarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
- "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/is-unicode-supported": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
@@ -9155,20 +7477,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/is-utf8": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
- "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/isbinaryfile": {
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz",
@@ -9183,10 +7491,14 @@
}
},
"node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "license": "ISC"
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz",
+ "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
},
"node_modules/ismobilejs": {
"version": "1.1.1",
@@ -9194,17 +7506,11 @@
"integrity": "sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw==",
"license": "MIT"
},
- "node_modules/isstream": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
- "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
- "dev": true,
- "license": "MIT"
- },
"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==",
+ "dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
@@ -9234,36 +7540,16 @@
"node": ">=10"
}
},
- "node_modules/jimp": {
- "version": "0.16.13",
- "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.16.13.tgz",
- "integrity": "sha512-Bxz8q7V4rnCky9A0ktTNGA9SkNFVWRHodddI/DaAWZJzF7sVUlFYKQ60y9JGqrKpi48ECA/TnfMzzc5C70VByA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.7.2",
- "@jimp/custom": "^0.16.13",
- "@jimp/plugins": "^0.16.13",
- "@jimp/types": "^0.16.13",
- "regenerator-runtime": "^0.13.3"
- }
- },
"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==",
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+ "devOptional": true,
"license": "MIT",
"bin": {
- "jiti": "bin/jiti.js"
+ "jiti": "lib/jiti-cli.mjs"
}
},
- "node_modules/jpeg-js": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz",
- "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
- "dev": true,
- "license": "BSD-3-Clause"
- },
"node_modules/js-binary-schema-parser": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/js-binary-schema-parser/-/js-binary-schema-parser-2.0.3.tgz",
@@ -9289,22 +7575,15 @@
"js-yaml": "bin/js-yaml.js"
}
},
- "node_modules/jsbn": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
- "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/jsdom": {
- "version": "29.0.1",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.1.tgz",
- "integrity": "sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==",
+ "version": "29.0.2",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.2.tgz",
+ "integrity": "sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@asamuzakjp/css-color": "^5.0.1",
- "@asamuzakjp/dom-selector": "^7.0.3",
+ "@asamuzakjp/css-color": "^5.1.5",
+ "@asamuzakjp/dom-selector": "^7.0.6",
"@bramus/specificity": "^2.4.2",
"@csstools/css-syntax-patches-for-csstree": "^1.1.1",
"@exodus/bytes": "^1.15.0",
@@ -9338,28 +7617,15 @@
}
},
"node_modules/jsdom/node_modules/lru-cache": {
- "version": "11.2.7",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
- "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
+ "version": "11.3.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz",
+ "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
}
},
- "node_modules/jsdom/node_modules/tough-cookie": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz",
- "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "tldts": "^7.0.5"
- },
- "engines": {
- "node": ">=16"
- }
- },
"node_modules/jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -9380,13 +7646,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/json-schema": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
- "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
- "dev": true,
- "license": "(AFL-2.1 OR BSD-3-Clause)"
- },
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -9399,7 +7658,8 @@
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"dev": true,
- "license": "ISC"
+ "license": "ISC",
+ "optional": true
},
"node_modules/json5": {
"version": "2.2.3",
@@ -9424,54 +7684,6 @@
"graceful-fs": "^4.1.6"
}
},
- "node_modules/jsprim": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
- "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "assert-plus": "1.0.0",
- "extsprintf": "1.3.0",
- "json-schema": "0.4.0",
- "verror": "1.10.0"
- },
- "engines": {
- "node": ">=0.6.0"
- }
- },
- "node_modules/jsprim/node_modules/extsprintf": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
- "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
- "dev": true,
- "engines": [
- "node >=0.6.0"
- ],
- "license": "MIT"
- },
- "node_modules/jsprim/node_modules/verror": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
- "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
- "dev": true,
- "engines": [
- "node >=0.6.0"
- ],
- "license": "MIT",
- "dependencies": {
- "assert-plus": "^1.0.0",
- "core-util-is": "1.0.2",
- "extsprintf": "^1.2.0"
- }
- },
- "node_modules/kew": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz",
- "integrity": "sha512-IG6nm0+QtAMdXt9KvbgbGdvY50RSrw+U4sGZg+KlrSKPJEwVE5JVoI3d7RWfSMdBQneRheeAOj3lIjX5VL/9RQ==",
- "dev": true,
- "license": "Apache-2.0"
- },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -9482,16 +7694,6 @@
"json-buffer": "3.0.1"
}
},
- "node_modules/klaw": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
- "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==",
- "dev": true,
- "license": "MIT",
- "optionalDependencies": {
- "graceful-fs": "^4.1.9"
- }
- },
"node_modules/lazy-val": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
@@ -9499,29 +7701,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/lcid": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
- "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "invert-kv": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/leven": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
- "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
@@ -9541,17 +7720,17 @@
"license": "MIT"
},
"node_modules/lint-staged": {
- "version": "16.3.2",
- "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.3.2.tgz",
- "integrity": "sha512-xKqhC2AeXLwiAHXguxBjuChoTTWFC6Pees0SHPwOpwlvI3BH7ZADFPddAdN3pgo3aiKgPUx/bxE78JfUnxQnlg==",
+ "version": "16.4.0",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz",
+ "integrity": "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==",
"dev": true,
"license": "MIT",
"dependencies": {
"commander": "^14.0.3",
"listr2": "^9.0.5",
- "micromatch": "^4.0.8",
+ "picomatch": "^4.0.3",
"string-argv": "^0.3.2",
- "tinyexec": "^1.0.2",
+ "tinyexec": "^1.0.4",
"yaml": "^2.8.2"
},
"bin": {
@@ -9592,19 +7771,6 @@
"node": ">=20.0.0"
}
},
- "node_modules/listr2/node_modules/ansi-regex": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
- "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
"node_modules/listr2/node_modules/ansi-styles": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
@@ -9635,10 +7801,10 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/listr2/node_modules/emoji-regex": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
- "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "node_modules/listr2/node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
"dev": true,
"license": "MIT"
},
@@ -9692,133 +7858,10 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/listr2/node_modules/strip-ansi": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
- "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.2.2"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- },
- "node_modules/listr2/node_modules/wrap-ansi": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
- "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.2.1",
- "string-width": "^7.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
- "node_modules/listr2/node_modules/wrap-ansi/node_modules/string-width": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
- "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^10.3.0",
- "get-east-asian-width": "^1.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/load-bmfont": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.2.tgz",
- "integrity": "sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "buffer-equal": "0.0.1",
- "mime": "^1.3.4",
- "parse-bmfont-ascii": "^1.0.3",
- "parse-bmfont-binary": "^1.0.5",
- "parse-bmfont-xml": "^1.1.4",
- "phin": "^3.7.1",
- "xhr": "^2.0.1",
- "xtend": "^4.0.0"
- }
- },
- "node_modules/load-bmfont/node_modules/buffer-equal": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz",
- "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/load-bmfont/node_modules/mime": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
- "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "mime": "cli.js"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/load-bmfont/node_modules/phin": {
- "version": "3.7.1",
- "resolved": "https://registry.npmjs.org/phin/-/phin-3.7.1.tgz",
- "integrity": "sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==",
- "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "centra": "^2.7.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/load-json-file": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
- "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.1.2",
- "parse-json": "^2.2.0",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0",
- "strip-bom": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/lodash": {
- "version": "4.17.23",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
- "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"dev": true,
"license": "MIT"
},
@@ -9859,19 +7902,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/log-update/node_modules/ansi-regex": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
- "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
"node_modules/log-update/node_modules/ansi-styles": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
@@ -9885,29 +7915,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/log-update/node_modules/cli-cursor": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
- "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "restore-cursor": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/log-update/node_modules/emoji-regex": {
- "version": "10.6.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
- "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/log-update/node_modules/is-fullwidth-code-point": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
@@ -9924,39 +7931,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/log-update/node_modules/onetime": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
- "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mimic-function": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/log-update/node_modules/restore-cursor": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
- "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "onetime": "^7.0.0",
- "signal-exit": "^4.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/log-update/node_modules/slice-ansi": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
@@ -9974,58 +7948,6 @@
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
}
},
- "node_modules/log-update/node_modules/string-width": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
- "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^10.3.0",
- "get-east-asian-width": "^1.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/log-update/node_modules/strip-ansi": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
- "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^6.2.2"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
- }
- },
- "node_modules/log-update/node_modules/wrap-ansi": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
- "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^6.2.1",
- "string-width": "^7.0.0",
- "strip-ansi": "^7.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
- }
- },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -10078,32 +8000,6 @@
"lz-string": "bin/bin.js"
}
},
- "node_modules/lzma-native": {
- "version": "8.0.6",
- "resolved": "https://registry.npmjs.org/lzma-native/-/lzma-native-8.0.6.tgz",
- "integrity": "sha512-09xfg67mkL2Lz20PrrDeNYZxzeW7ADtpYFbwSQh9U8+76RIzx5QsJBMy8qikv3hbUPfpy6hqwxt6FcGK81g9AA==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "dependencies": {
- "node-addon-api": "^3.1.0",
- "node-gyp-build": "^4.2.1",
- "readable-stream": "^3.6.0"
- },
- "bin": {
- "lzmajs": "bin/lzmajs"
- },
- "engines": {
- "node": ">=10.0.0"
- }
- },
- "node_modules/lzma-native/node_modules/node-addon-api": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
- "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
@@ -10115,63 +8011,28 @@
}
},
"node_modules/make-fetch-happen": {
- "version": "10.2.1",
- "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz",
- "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==",
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz",
+ "integrity": "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==",
"dev": true,
"license": "ISC",
"dependencies": {
- "agentkeepalive": "^4.2.1",
- "cacache": "^16.1.0",
- "http-cache-semantics": "^4.1.0",
- "http-proxy-agent": "^5.0.0",
- "https-proxy-agent": "^5.0.0",
- "is-lambda": "^1.0.1",
- "lru-cache": "^7.7.1",
- "minipass": "^3.1.6",
- "minipass-collect": "^1.0.2",
- "minipass-fetch": "^2.0.3",
+ "@npmcli/agent": "^3.0.0",
+ "cacache": "^19.0.1",
+ "http-cache-semantics": "^4.1.1",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^4.0.0",
"minipass-flush": "^1.0.5",
"minipass-pipeline": "^1.2.4",
- "negotiator": "^0.6.3",
+ "negotiator": "^1.0.0",
+ "proc-log": "^5.0.0",
"promise-retry": "^2.0.1",
- "socks-proxy-agent": "^7.0.0",
- "ssri": "^9.0.0"
+ "ssri": "^12.0.0"
},
"engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ "node": "^18.17.0 || >=20.5.0"
}
},
- "node_modules/make-fetch-happen/node_modules/lru-cache": {
- "version": "7.18.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
- "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/make-fetch-happen/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/make-fetch-happen/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/matcher": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
@@ -10203,9 +8064,9 @@
"license": "CC0-1.0"
},
"node_modules/mediabunny": {
- "version": "1.25.1",
- "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.25.1.tgz",
- "integrity": "sha512-0Rrd47PMCVJbTPA7IJaXPCupV5/RZ/icgr+a0qExRJAr0n5vB4fsGSo+fdHIehG0CrddXtVRvNZwFtJz709yfA==",
+ "version": "1.40.1",
+ "resolved": "https://registry.npmjs.org/mediabunny/-/mediabunny-1.40.1.tgz",
+ "integrity": "sha512-HU/stGzAkdWaJIly6ypbUVgAUvT9kt39DIg0IaErR7/1fwtTmgUYs4i8uEPYcgcjPjbB9gtBmUXOLnXi6J2LDw==",
"license": "MPL-2.0",
"workspaces": [
"packages/*"
@@ -10219,12 +8080,6 @@
"url": "https://github.com/sponsors/Vanilagy"
}
},
- "node_modules/mediabunny/node_modules/@types/dom-webcodecs": {
- "version": "0.1.13",
- "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.13.tgz",
- "integrity": "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==",
- "license": "MIT"
- },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -10247,6 +8102,18 @@
"node": ">=8.6"
}
},
+ "node_modules/micromatch/node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/mime": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
@@ -10316,16 +8183,6 @@
"node": ">=4"
}
},
- "node_modules/min-document": {
- "version": "2.19.2",
- "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz",
- "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "dom-walk": "^0.1.0"
- }
- },
"node_modules/min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@@ -10337,15 +8194,16 @@
}
},
"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",
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
"dependencies": {
- "brace-expansion": "^2.0.1"
+ "brace-expansion": "^5.0.5"
},
"engines": {
- "node": ">=16 || 14 >=14.17"
+ "node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -10362,91 +8220,52 @@
}
},
"node_modules/minipass": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
- "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
- "license": "ISC",
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
"engines": {
- "node": ">=8"
+ "node": ">=16 || 14 >=14.17"
}
},
"node_modules/minipass-collect": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
- "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz",
+ "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==",
"dev": true,
"license": "ISC",
"dependencies": {
- "minipass": "^3.0.0"
+ "minipass": "^7.0.3"
},
"engines": {
- "node": ">= 8"
+ "node": ">=16 || 14 >=14.17"
}
},
- "node_modules/minipass-collect/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/minipass-collect/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/minipass-fetch": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz",
- "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-4.0.1.tgz",
+ "integrity": "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "minipass": "^3.1.6",
+ "minipass": "^7.0.3",
"minipass-sized": "^1.0.3",
- "minizlib": "^2.1.2"
+ "minizlib": "^3.0.1"
},
"engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ "node": "^18.17.0 || >=20.5.0"
},
"optionalDependencies": {
"encoding": "^0.1.13"
}
},
- "node_modules/minipass-fetch/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/minipass-fetch/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/minipass-flush": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
- "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz",
+ "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==",
"dev": true,
- "license": "ISC",
+ "license": "BlueOak-1.0.0",
"dependencies": {
"minipass": "^3.0.0"
},
@@ -10541,59 +8360,39 @@
"license": "ISC"
},
"node_modules/minizlib": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
- "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
+ "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "minipass": "^3.0.0",
- "yallist": "^4.0.0"
+ "minipass": "^7.1.2"
},
"engines": {
- "node": ">= 8"
+ "node": ">= 18"
}
},
- "node_modules/minizlib/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/minizlib/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/mkdirp": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
"bin": {
"mkdirp": "bin/cmd.js"
- },
- "engines": {
- "node": ">=10"
}
},
"node_modules/motion": {
- "version": "12.23.24",
- "resolved": "https://registry.npmjs.org/motion/-/motion-12.23.24.tgz",
- "integrity": "sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw==",
+ "version": "12.38.0",
+ "resolved": "https://registry.npmjs.org/motion/-/motion-12.38.0.tgz",
+ "integrity": "sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==",
"license": "MIT",
"dependencies": {
- "framer-motion": "^12.23.24",
+ "framer-motion": "^12.38.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
@@ -10614,37 +8413,37 @@
}
},
"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==",
+ "version": "12.38.0",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz",
+ "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==",
"license": "MIT",
"dependencies": {
- "motion-utils": "^12.23.6"
+ "motion-utils": "^12.36.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==",
+ "version": "12.36.0",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz",
+ "integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==",
"license": "MIT"
},
"node_modules/mp4box": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/mp4box/-/mp4box-2.2.0.tgz",
- "integrity": "sha512-tE+L7wdhSuwBKZGjUzj03Qzj4lWyOw8pHSPyLnvHTKx92NJGkJls0pcEusUHWEh5gWVBlhdu79STJh4Bubz9mQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/mp4box/-/mp4box-2.3.0.tgz",
+ "integrity": "sha512-nnABYbdh4UguEYyV+uRwQBi1tbb8kXka2Fx9yKzmDKAeh8gkvRKYxoK1XDd8GQIjSfN4rvsXrW1CBo4yRQJZDA==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=20.8.1"
}
},
- "node_modules/mri": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz",
- "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==",
+ "node_modules/mrmime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
+ "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=4"
+ "node": ">=10"
}
},
"node_modules/ms": {
@@ -10684,9 +8483,9 @@
}
},
"node_modules/negotiator": {
- "version": "0.6.4",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
- "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -10694,13 +8493,26 @@
}
},
"node_modules/node-abi": {
- "version": "3.78.0",
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.78.0.tgz",
- "integrity": "sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ==",
+ "version": "4.28.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.28.0.tgz",
+ "integrity": "sha512-Qfp5XZL1cJDOabOT8H5gnqMTmM4NjvYzHp4I/Kt/Sl76OVkOBBHRFlPspGV0hYvMoqQsypFjT/Yp7Km0beXW9g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "semver": "^7.3.5"
+ "semver": "^7.6.3"
+ },
+ "engines": {
+ "node": ">=22.12.0"
+ }
+ },
+ "node_modules/node-abi/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
@@ -10715,104 +8527,87 @@
"optional": true
},
"node_modules/node-api-version": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.1.4.tgz",
- "integrity": "sha512-KGXihXdUChwJAOHO53bv9/vXcLmdUsZ6jIptbvYvkpKfth+r7jw44JkVxQFA3kX5nQjzjmGu1uAu/xNNLNlI5g==",
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz",
+ "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"semver": "^7.3.5"
}
},
+ "node_modules/node-api-version/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/node-gyp": {
- "version": "9.4.1",
- "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz",
- "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==",
+ "version": "11.5.0",
+ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.5.0.tgz",
+ "integrity": "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"env-paths": "^2.2.0",
"exponential-backoff": "^3.1.1",
- "glob": "^7.1.4",
"graceful-fs": "^4.2.6",
- "make-fetch-happen": "^10.0.3",
- "nopt": "^6.0.0",
- "npmlog": "^6.0.0",
- "rimraf": "^3.0.2",
+ "make-fetch-happen": "^14.0.3",
+ "nopt": "^8.0.0",
+ "proc-log": "^5.0.0",
"semver": "^7.3.5",
- "tar": "^6.1.2",
- "which": "^2.0.2"
+ "tar": "^7.4.3",
+ "tinyglobby": "^0.2.12",
+ "which": "^5.0.0"
},
"bin": {
"node-gyp": "bin/node-gyp.js"
},
"engines": {
- "node": "^12.13 || ^14.13 || >=16"
+ "node": "^18.17.0 || >=20.5.0"
}
},
- "node_modules/node-gyp-build": {
- "version": "4.8.4",
- "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
- "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
+ "node_modules/node-gyp/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
- "license": "MIT",
+ "license": "ISC",
"bin": {
- "node-gyp-build": "bin.js",
- "node-gyp-build-optional": "optional.js",
- "node-gyp-build-test": "build-test.js"
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
}
},
"node_modules/node-releases": {
- "version": "2.0.23",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz",
- "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==",
+ "version": "2.0.37",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz",
+ "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==",
"dev": true,
"license": "MIT"
},
"node_modules/nopt": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz",
- "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==",
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz",
+ "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==",
"dev": true,
"license": "ISC",
"dependencies": {
- "abbrev": "^1.0.0"
+ "abbrev": "^3.0.0"
},
"bin": {
"nopt": "bin/nopt.js"
},
"engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
- }
- },
- "node_modules/normalize-package-data": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
- "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "hosted-git-info": "^2.1.4",
- "resolve": "^1.10.0",
- "semver": "2 || 3 || 4 || 5",
- "validate-npm-package-license": "^3.0.1"
- }
- },
- "node_modules/normalize-package-data/node_modules/hosted-git-info": {
- "version": "2.8.9",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
- "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/normalize-package-data/node_modules/semver": {
- "version": "5.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
- "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver"
+ "node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/normalize-path": {
@@ -10824,16 +8619,6 @@
"node": ">=0.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==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/normalize-url": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
@@ -10847,43 +8632,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/npmlog": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
- "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
- "deprecated": "This package is no longer supported.",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "are-we-there-yet": "^3.0.0",
- "console-control-strings": "^1.1.0",
- "gauge": "^4.0.3",
- "set-blocking": "^2.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
- }
- },
- "node_modules/number-is-nan": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
- "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/oauth-sign": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
- "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "*"
- }
- },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -10937,13 +8685,6 @@
],
"license": "MIT"
},
- "node_modules/omggif": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz",
- "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -10955,16 +8696,16 @@
}
},
"node_modules/onetime": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
- "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "mimic-fn": "^2.1.0"
+ "mimic-function": "^5.0.0"
},
"engines": {
- "node": ">=6"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -10994,17 +8735,60 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/os-locale": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
- "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==",
+ "node_modules/ora/node_modules/cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "lcid": "^1.0.0"
+ "restore-cursor": "^3.1.0"
},
"engines": {
- "node": ">=0.10.0"
+ "node": ">=8"
+ }
+ },
+ "node_modules/ora/node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ora/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
"node_modules/p-cancelable": {
@@ -11034,16 +8818,13 @@
}
},
"node_modules/p-map": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
- "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz",
+ "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "aggregate-error": "^3.0.0"
- },
"engines": {
- "node": ">=10"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -11053,60 +8834,9 @@
"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==",
+ "dev": true,
"license": "BlueOak-1.0.0"
},
- "node_modules/pako": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
- "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
- "dev": true,
- "license": "(MIT AND Zlib)"
- },
- "node_modules/parse-bmfont-ascii": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz",
- "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/parse-bmfont-binary": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz",
- "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/parse-bmfont-xml": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz",
- "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "xml-parse-from-string": "^1.0.0",
- "xml2js": "^0.5.0"
- }
- },
- "node_modules/parse-headers": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz",
- "integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/parse-json": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
- "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "error-ex": "^1.2.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/parse-svg-path": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz",
@@ -11114,13 +8844,13 @@
"license": "MIT"
},
"node_modules/parse5": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz",
- "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==",
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
+ "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "entities": "^6.0.0"
+ "entities": "^8.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
@@ -11140,6 +8870,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -11155,6 +8886,7 @@
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^10.2.0",
@@ -11171,17 +8903,8 @@
"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/path-type": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
- "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
+ "license": "ISC"
},
"node_modules/pathe": {
"version": "2.0.3",
@@ -11205,20 +8928,6 @@
"url": "https://github.com/sponsors/jet2jet"
}
},
- "node_modules/peek-readable": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz",
- "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/Borewit"
- }
- },
"node_modules/pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
@@ -11226,134 +8935,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/performance-now": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
- "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/phantomjs-prebuilt": {
- "version": "2.1.16",
- "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz",
- "integrity": "sha512-PIiRzBhW85xco2fuj41FmsyuYHKjKuXWmhjy3A/Y+CMpN/63TV+s9uzfVhsUwFe0G77xWtHBG8xmXf5BqEUEuQ==",
- "deprecated": "this package is now deprecated",
- "dev": true,
- "hasInstallScript": true,
- "license": "Apache-2.0",
- "dependencies": {
- "es6-promise": "^4.0.3",
- "extract-zip": "^1.6.5",
- "fs-extra": "^1.0.0",
- "hasha": "^2.2.0",
- "kew": "^0.7.0",
- "progress": "^1.1.8",
- "request": "^2.81.0",
- "request-progress": "^2.0.1",
- "which": "^1.2.10"
- },
- "bin": {
- "phantomjs": "bin/phantomjs"
- }
- },
- "node_modules/phantomjs-prebuilt/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/phantomjs-prebuilt/node_modules/extract-zip": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz",
- "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "concat-stream": "^1.6.2",
- "debug": "^2.6.9",
- "mkdirp": "^0.5.4",
- "yauzl": "^2.10.0"
- },
- "bin": {
- "extract-zip": "cli.js"
- }
- },
- "node_modules/phantomjs-prebuilt/node_modules/fs-extra": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz",
- "integrity": "sha512-VerQV6vEKuhDWD2HGOybV6v5I73syoc/cXAbKlgTC7M/oFVEtklWlp9QH2Ijw3IaWDOQcMkldSPa7zXy79Z/UQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.1.2",
- "jsonfile": "^2.1.0",
- "klaw": "^1.0.0"
- }
- },
- "node_modules/phantomjs-prebuilt/node_modules/jsonfile": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
- "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==",
- "dev": true,
- "license": "MIT",
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/phantomjs-prebuilt/node_modules/mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.6"
- },
- "bin": {
- "mkdirp": "bin/cmd.js"
- }
- },
- "node_modules/phantomjs-prebuilt/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/phantomjs-prebuilt/node_modules/progress": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
- "integrity": "sha512-UdA8mJ4weIkUBO224tIarHzuHs4HuYiJvsuGT7j/SPQiUJVjYvNDBIPa0hAorduOfjGohB/qHWRa/lrrWX/mXw==",
- "dev": true,
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/phantomjs-prebuilt/node_modules/which": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
- "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "which": "bin/which"
- }
- },
- "node_modules/phin": {
- "version": "2.9.3",
- "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
- "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==",
- "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
- "dev": true,
- "license": "MIT"
- },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -11361,12 +8942,12 @@
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"license": "MIT",
"engines": {
- "node": ">=8.6"
+ "node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
@@ -11381,29 +8962,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/pinkie": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
- "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/pinkie-promise": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
- "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "pinkie": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/pirates": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
@@ -11413,29 +8971,6 @@
"node": ">= 6"
}
},
- "node_modules/pixelmatch": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz",
- "integrity": "sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "pngjs": "^3.0.0"
- },
- "bin": {
- "pixelmatch": "bin/pixelmatch"
- }
- },
- "node_modules/pixelmatch/node_modules/pngjs": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
- "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4.0.0"
- }
- },
"node_modules/pixi-filters": {
"version": "6.1.5",
"resolved": "https://registry.npmjs.org/pixi-filters/-/pixi-filters-6.1.5.tgz",
@@ -11449,35 +8984,57 @@
}
},
"node_modules/pixi.js": {
- "version": "8.14.0",
- "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-8.14.0.tgz",
- "integrity": "sha512-ituDiEBb1Oqx56RYwTtC6MjPUhPfF/i15fpUv5oEqmzC/ce3SaSumulJcOjKG7+y0J0Ekl9Rl4XTxaUw+MVFZw==",
+ "version": "8.18.1",
+ "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-8.18.1.tgz",
+ "integrity": "sha512-6LUPWYgulZhp/w4kam2XHXB0QedISZIqrJbRdHLLQ3csn5a38uzKxAp6B5j6s89QFYaIJbg95kvgTRcbgpO1ow==",
"license": "MIT",
+ "workspaces": [
+ "examples",
+ "playground"
+ ],
"dependencies": {
"@pixi/colord": "^2.9.6",
- "@types/css-font-loading-module": "^0.0.12",
"@types/earcut": "^3.0.0",
- "@webgpu/types": "^0.1.40",
- "@xmldom/xmldom": "^0.8.10",
+ "@webgpu/types": "^0.1.69",
+ "@xmldom/xmldom": "^0.8.12",
"earcut": "^3.0.2",
"eventemitter3": "^5.0.1",
"gifuct-js": "^2.1.2",
"ismobilejs": "^1.1.1",
"parse-svg-path": "^0.1.2",
- "tiny-lru": "^11.4.5"
+ "tiny-lru": "^11.4.7"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/pixijs"
}
},
+ "node_modules/pixi.js/node_modules/@types/earcut": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-3.0.0.tgz",
+ "integrity": "sha512-k/9fOUGO39yd2sCjrbAJvGDEQvRwRnQIZlBz43roGwUZo5SHAmyVvSFyaVVZkicRVCaDXPKlbxrUcBuJoSWunQ==",
+ "license": "MIT"
+ },
+ "node_modules/pixi.js/node_modules/earcut": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz",
+ "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==",
+ "license": "ISC"
+ },
+ "node_modules/pixi.js/node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "license": "MIT"
+ },
"node_modules/playwright": {
- "version": "1.58.2",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
- "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
+ "version": "1.59.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
+ "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.58.2"
+ "playwright-core": "1.59.1"
},
"bin": {
"playwright": "cli.js"
@@ -11490,10 +9047,11 @@
}
},
"node_modules/playwright-core": {
- "version": "1.58.2",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
- "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
+ "version": "1.59.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
+ "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
"dev": true,
+ "license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
@@ -11501,20 +9059,6 @@
"node": ">=18"
}
},
- "node_modules/playwright/node_modules/fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
"node_modules/plist": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
@@ -11530,27 +9074,20 @@
"node": ">=10.4.0"
}
},
- "node_modules/pn": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
- "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/pngjs": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz",
- "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
+ "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=12.13.0"
+ "node": ">=14.19.0"
}
},
"node_modules/postcss": {
- "version": "8.5.6",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
- "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "version": "8.5.10",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
+ "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
"funding": [
{
"type": "opencollective",
@@ -11763,14 +9300,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/pretty-format/node_modules/react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true,
- "license": "MIT",
- "peer": true
- },
"node_modules/proc-log": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz",
@@ -11781,23 +9310,6 @@
"node": "^18.17.0 || >=20.5.0"
}
},
- "node_modules/process": {
- "version": "0.11.10",
- "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
- "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6.0"
- }
- },
- "node_modules/process-nextick-args": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
@@ -11808,13 +9320,6 @@
"node": ">=0.4.0"
}
},
- "node_modules/promise-inflight": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
- "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/promise-retry": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
@@ -11840,6 +9345,12 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/prop-types/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
"node_modules/proper-lockfile": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
@@ -11852,30 +9363,10 @@
"signal-exit": "^3.0.2"
}
},
- "node_modules/proper-lockfile/node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/psl": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
- "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "punycode": "^2.3.1"
- },
- "funding": {
- "url": "https://github.com/sponsors/lupomontero"
- }
- },
"node_modules/pump": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
- "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
+ "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11894,9 +9385,9 @@
}
},
"node_modules/pure-rand": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz",
- "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==",
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.0.tgz",
+ "integrity": "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A==",
"dev": true,
"funding": [
{
@@ -11911,9 +9402,9 @@
"license": "MIT"
},
"node_modules/qs": {
- "version": "6.14.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
- "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
+ "version": "6.15.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz",
+ "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==",
"license": "BSD-3-Clause",
"peer": true,
"dependencies": {
@@ -11995,12 +9486,12 @@
}
},
"node_modules/react-draggable": {
- "version": "4.4.6",
- "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz",
- "integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==",
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.5.0.tgz",
+ "integrity": "sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==",
"license": "MIT",
"dependencies": {
- "clsx": "^1.1.1",
+ "clsx": "^2.1.1",
"prop-types": "^15.8.1"
},
"peerDependencies": {
@@ -12008,34 +9499,27 @@
"react-dom": ">= 16.3.0"
}
},
- "node_modules/react-draggable/node_modules/clsx": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
- "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/react-icons": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz",
- "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==",
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.6.0.tgz",
+ "integrity": "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==",
"license": "MIT",
"peerDependencies": {
"react": "*"
}
},
"node_modules/react-is": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "license": "MIT"
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
},
"node_modules/react-refresh": {
- "version": "0.17.0",
- "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
- "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
+ "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -12043,9 +9527,9 @@
}
},
"node_modules/react-remove-scroll": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz",
- "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==",
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz",
+ "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==",
"license": "MIT",
"dependencies": {
"react-remove-scroll-bar": "^2.3.7",
@@ -12100,13 +9584,13 @@
}
},
"node_modules/react-rnd": {
- "version": "10.5.2",
- "resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.5.2.tgz",
- "integrity": "sha512-0Tm4x7k7pfHf2snewJA8x7Nwgt3LV+58MVEWOVsFjk51eYruFEa6Wy7BNdxt4/lH0wIRsu7Gm3KjSXY2w7YaNw==",
+ "version": "10.5.3",
+ "resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.5.3.tgz",
+ "integrity": "sha512-s/sIT3pGZnQ+57egijkTp9mizjIWrJz68Pq6yd+F/wniFY3IriML18dUXnQe/HP9uMiJ+9MAp44hljG99fZu6Q==",
"license": "MIT",
"dependencies": {
- "re-resizable": "6.11.2",
- "react-draggable": "4.4.6",
+ "re-resizable": "^6.11.2",
+ "react-draggable": "^4.5.0",
"tslib": "2.6.2"
},
"peerDependencies": {
@@ -12164,77 +9648,6 @@
"pify": "^2.3.0"
}
},
- "node_modules/read-pkg": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
- "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "load-json-file": "^1.0.0",
- "normalize-package-data": "^2.3.2",
- "path-type": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/read-pkg-up": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
- "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "find-up": "^1.0.0",
- "read-pkg": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/read-pkg-up/node_modules/find-up": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
- "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "path-exists": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/read-pkg-up/node_modules/path-exists": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
- "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "pinkie-promise": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/read-pkg/node_modules/path-type": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
- "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.1.2",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -12250,65 +9663,6 @@
"node": ">= 6"
}
},
- "node_modules/readable-web-to-node-stream": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz",
- "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "readable-stream": "^4.7.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/Borewit"
- }
- },
- "node_modules/readable-web-to-node-stream/node_modules/buffer": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
- "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "base64-js": "^1.3.1",
- "ieee754": "^1.2.1"
- }
- },
- "node_modules/readable-web-to-node-stream/node_modules/readable-stream": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
- "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "abort-controller": "^3.0.0",
- "buffer": "^6.0.3",
- "events": "^3.3.0",
- "process": "^0.11.10",
- "string_decoder": "^1.3.0"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- }
- },
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -12321,6 +9675,18 @@
"node": ">=8.10.0"
}
},
+ "node_modules/readdirp/node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/redent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
@@ -12335,92 +9701,6 @@
"node": ">=8"
}
},
- "node_modules/regenerator-runtime": {
- "version": "0.13.11",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
- "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/request": {
- "version": "2.88.2",
- "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
- "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
- "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "aws-sign2": "~0.7.0",
- "aws4": "^1.8.0",
- "caseless": "~0.12.0",
- "combined-stream": "~1.0.6",
- "extend": "~3.0.2",
- "forever-agent": "~0.6.1",
- "form-data": "~2.3.2",
- "har-validator": "~5.1.3",
- "http-signature": "~1.2.0",
- "is-typedarray": "~1.0.0",
- "isstream": "~0.1.2",
- "json-stringify-safe": "~5.0.1",
- "mime-types": "~2.1.19",
- "oauth-sign": "~0.9.0",
- "performance-now": "^2.1.0",
- "qs": "~6.5.2",
- "safe-buffer": "^5.1.2",
- "tough-cookie": "~2.5.0",
- "tunnel-agent": "^0.6.0",
- "uuid": "^3.3.2"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/request-progress": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz",
- "integrity": "sha512-dxdraeZVUNEn9AvLrxkgB2k6buTlym71dJk1fk4v8j3Ou3RKNm07BcgbHdj2lLgYGfqX71F+awb1MR+tWPFJzA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "throttleit": "^1.0.0"
- }
- },
- "node_modules/request/node_modules/form-data": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
- "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.6",
- "mime-types": "^2.1.12"
- },
- "engines": {
- "node": ">= 0.12"
- }
- },
- "node_modules/request/node_modules/qs": {
- "version": "6.5.3",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
- "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.6"
- }
- },
- "node_modules/request/node_modules/uuid": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
- "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
- "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
- "dev": true,
- "license": "MIT",
- "bin": {
- "uuid": "bin/uuid"
- }
- },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -12441,13 +9721,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/require-main-filename": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
- "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/resedit": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz",
@@ -12473,12 +9746,13 @@
"license": "MIT"
},
"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==",
+ "version": "1.22.12",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
+ "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
"license": "MIT",
"dependencies": {
- "is-core-module": "^2.16.0",
+ "es-errors": "^1.3.0",
+ "is-core-module": "^2.16.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
@@ -12513,25 +9787,34 @@
}
},
"node_modules/restore-cursor": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
- "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "onetime": "^5.1.0",
- "signal-exit": "^3.0.2"
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
},
"engines": {
- "node": ">=8"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/restore-cursor/node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
- "license": "ISC"
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
},
"node_modules/retry": {
"version": "0.12.0",
@@ -12561,20 +9844,18 @@
"license": "MIT"
},
"node_modules/rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
+ "peer": true,
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/roarr": {
@@ -12597,9 +9878,9 @@
}
},
"node_modules/rollup": {
- "version": "4.52.4",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz",
- "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==",
+ "version": "4.60.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz",
+ "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -12613,28 +9894,31 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.52.4",
- "@rollup/rollup-android-arm64": "4.52.4",
- "@rollup/rollup-darwin-arm64": "4.52.4",
- "@rollup/rollup-darwin-x64": "4.52.4",
- "@rollup/rollup-freebsd-arm64": "4.52.4",
- "@rollup/rollup-freebsd-x64": "4.52.4",
- "@rollup/rollup-linux-arm-gnueabihf": "4.52.4",
- "@rollup/rollup-linux-arm-musleabihf": "4.52.4",
- "@rollup/rollup-linux-arm64-gnu": "4.52.4",
- "@rollup/rollup-linux-arm64-musl": "4.52.4",
- "@rollup/rollup-linux-loong64-gnu": "4.52.4",
- "@rollup/rollup-linux-ppc64-gnu": "4.52.4",
- "@rollup/rollup-linux-riscv64-gnu": "4.52.4",
- "@rollup/rollup-linux-riscv64-musl": "4.52.4",
- "@rollup/rollup-linux-s390x-gnu": "4.52.4",
- "@rollup/rollup-linux-x64-gnu": "4.52.4",
- "@rollup/rollup-linux-x64-musl": "4.52.4",
- "@rollup/rollup-openharmony-arm64": "4.52.4",
- "@rollup/rollup-win32-arm64-msvc": "4.52.4",
- "@rollup/rollup-win32-ia32-msvc": "4.52.4",
- "@rollup/rollup-win32-x64-gnu": "4.52.4",
- "@rollup/rollup-win32-x64-msvc": "4.52.4",
+ "@rollup/rollup-android-arm-eabi": "4.60.2",
+ "@rollup/rollup-android-arm64": "4.60.2",
+ "@rollup/rollup-darwin-arm64": "4.60.2",
+ "@rollup/rollup-darwin-x64": "4.60.2",
+ "@rollup/rollup-freebsd-arm64": "4.60.2",
+ "@rollup/rollup-freebsd-x64": "4.60.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.60.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.60.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.60.2",
+ "@rollup/rollup-linux-arm64-musl": "4.60.2",
+ "@rollup/rollup-linux-loong64-gnu": "4.60.2",
+ "@rollup/rollup-linux-loong64-musl": "4.60.2",
+ "@rollup/rollup-linux-ppc64-gnu": "4.60.2",
+ "@rollup/rollup-linux-ppc64-musl": "4.60.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.60.2",
+ "@rollup/rollup-linux-riscv64-musl": "4.60.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.60.2",
+ "@rollup/rollup-linux-x64-gnu": "4.60.2",
+ "@rollup/rollup-linux-x64-musl": "4.60.2",
+ "@rollup/rollup-openbsd-x64": "4.60.2",
+ "@rollup/rollup-openharmony-arm64": "4.60.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.60.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.60.2",
+ "@rollup/rollup-win32-x64-gnu": "4.60.2",
+ "@rollup/rollup-win32-x64-msvc": "4.60.2",
"fsevents": "~2.3.2"
}
},
@@ -12690,9 +9974,9 @@
"license": "MIT"
},
"node_modules/sanitize-filename": {
- "version": "1.6.3",
- "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz",
- "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==",
+ "version": "1.6.4",
+ "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz",
+ "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==",
"dev": true,
"license": "WTFPL OR ISC",
"dependencies": {
@@ -12700,11 +9984,14 @@
}
},
"node_modules/sax": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
- "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
+ "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
"dev": true,
- "license": "ISC"
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=11.0.0"
+ }
},
"node_modules/saxes": {
"version": "6.0.0",
@@ -12729,16 +10016,13 @@
}
},
"node_modules/semver": {
- "version": "7.7.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
- "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "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"
- },
- "engines": {
- "node": ">=10"
}
},
"node_modules/semver-compare": {
@@ -12766,31 +10050,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/serialize-error/node_modules/type-fest": {
- "version": "0.13.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
- "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
- "dev": true,
- "license": "(MIT OR CC0-1.0)",
- "optional": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/set-blocking": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
- "dev": true,
- "license": "ISC"
- },
"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==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
@@ -12803,6 +10067,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -12829,14 +10094,14 @@
}
},
"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==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
"license": "MIT",
"peer": true,
"dependencies": {
"es-errors": "^1.3.0",
- "object-inspect": "^1.13.3"
+ "object-inspect": "^1.13.4"
},
"engines": {
"node": ">= 0.4"
@@ -12892,16 +10157,11 @@
"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"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true,
+ "license": "ISC"
},
"node_modules/simple-update-notifier": {
"version": "2.0.0",
@@ -12916,14 +10176,32 @@
"node": ">=10"
}
},
- "node_modules/slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "node_modules/simple-update-notifier/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sirv": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
+ "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "@polka/url": "^1.0.0-next.24",
+ "mrmime": "^2.0.0",
+ "totalist": "^3.0.0"
+ },
"engines": {
- "node": ">=8"
+ "node": ">=18"
}
},
"node_modules/slice-ansi": {
@@ -12969,18 +10247,18 @@
}
},
"node_modules/socks-proxy-agent": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz",
- "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==",
+ "version": "8.0.5",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
+ "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "agent-base": "^6.0.2",
- "debug": "^4.3.3",
- "socks": "^2.6.2"
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "socks": "^2.8.3"
},
"engines": {
- "node": ">= 10"
+ "node": ">= 14"
}
},
"node_modules/sonner": {
@@ -13023,42 +10301,6 @@
"source-map": "^0.6.0"
}
},
- "node_modules/spdx-correct": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
- "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "spdx-expression-parse": "^3.0.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-exceptions": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
- "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
- "dev": true,
- "license": "CC-BY-3.0"
- },
- "node_modules/spdx-expression-parse": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
- "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "spdx-exceptions": "^2.1.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-license-ids": {
- "version": "3.0.22",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz",
- "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==",
- "dev": true,
- "license": "CC0-1.0"
- },
"node_modules/sprintf-js": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
@@ -13067,65 +10309,19 @@
"license": "BSD-3-Clause",
"optional": true
},
- "node_modules/sshpk": {
- "version": "1.18.0",
- "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
- "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "asn1": "~0.2.3",
- "assert-plus": "^1.0.0",
- "bcrypt-pbkdf": "^1.0.0",
- "dashdash": "^1.12.0",
- "ecc-jsbn": "~0.1.1",
- "getpass": "^0.1.1",
- "jsbn": "~0.1.0",
- "safer-buffer": "^2.0.2",
- "tweetnacl": "~0.14.0"
- },
- "bin": {
- "sshpk-conv": "bin/sshpk-conv",
- "sshpk-sign": "bin/sshpk-sign",
- "sshpk-verify": "bin/sshpk-verify"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/ssri": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz",
- "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==",
+ "version": "12.0.0",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz",
+ "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==",
"dev": true,
"license": "ISC",
"dependencies": {
- "minipass": "^3.1.1"
+ "minipass": "^7.0.3"
},
"engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ "node": "^18.17.0 || >=20.5.0"
}
},
- "node_modules/ssri/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/ssri/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/stackback": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@@ -13144,9 +10340,9 @@
}
},
"node_modules/std-env": {
- "version": "3.10.0",
- "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
- "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
+ "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==",
"dev": true,
"license": "MIT"
},
@@ -13174,6 +10370,7 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
@@ -13189,6 +10386,7 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
@@ -13199,10 +10397,11 @@
"node": ">=8"
}
},
- "node_modules/strip-ansi": {
+ "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==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
@@ -13211,11 +10410,41 @@
"node": ">=8"
}
},
+ "node_modules/string-width/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
"node_modules/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==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
@@ -13224,17 +10453,17 @@
"node": ">=8"
}
},
- "node_modules/strip-bom": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
- "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==",
+ "node_modules/strip-ansi/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "is-utf8": "^0.2.0"
- },
"engines": {
- "node": ">=0.10.0"
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/strip-indent": {
@@ -13250,36 +10479,18 @@
"node": ">=8"
}
},
- "node_modules/strtok3": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
- "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@tokenizer/token": "^0.3.0",
- "peek-readable": "^4.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/Borewit"
- }
- },
"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==",
+ "version": "3.35.1",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz",
+ "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
"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",
+ "tinyglobby": "^0.2.11",
"ts-interface-checker": "^0.1.9"
},
"bin": {
@@ -13299,35 +10510,6 @@
"node": ">= 6"
}
},
- "node_modules/sucrase/node_modules/glob": {
- "version": "10.5.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
- "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
- "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"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/sucrase/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": ">=16 || 14 >=14.17"
- }
- },
"node_modules/sumchecker": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
@@ -13366,155 +10548,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/svg2png": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/svg2png/-/svg2png-4.1.1.tgz",
- "integrity": "sha512-9tOp9Ugjlunuf1ugqkhiYboTmTaTI7p48dz5ZjNA5NQJ5xS1NLTZZ1tF8vkJOIBb/ZwxGJsKZvRWqVpo4q9z9Q==",
- "dev": true,
- "license": "WTFPL",
- "dependencies": {
- "file-url": "^2.0.0",
- "phantomjs-prebuilt": "^2.1.14",
- "pn": "^1.0.0",
- "yargs": "^6.5.0"
- },
- "bin": {
- "svg2png": "bin/svg2png-cli.js"
- }
- },
- "node_modules/svg2png/node_modules/ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/svg2png/node_modules/camelcase": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
- "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/svg2png/node_modules/cliui": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
- "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1",
- "wrap-ansi": "^2.0.0"
- }
- },
- "node_modules/svg2png/node_modules/get-caller-file": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
- "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/svg2png/node_modules/is-fullwidth-code-point": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
- "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "number-is-nan": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/svg2png/node_modules/string-width": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
- "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "code-point-at": "^1.0.0",
- "is-fullwidth-code-point": "^1.0.0",
- "strip-ansi": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/svg2png/node_modules/strip-ansi": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
- "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-regex": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/svg2png/node_modules/wrap-ansi": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
- "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "string-width": "^1.0.1",
- "strip-ansi": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/svg2png/node_modules/y18n": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz",
- "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/svg2png/node_modules/yargs": {
- "version": "6.6.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz",
- "integrity": "sha512-6/QWTdisjnu5UHUzQGst+UOEuEVwIzFVGBjq3jMTFNs5WJQsH/X6nMURSaScIdF5txylr1Ao9bvbWiKi2yXbwA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "camelcase": "^3.0.0",
- "cliui": "^3.2.0",
- "decamelize": "^1.1.1",
- "get-caller-file": "^1.0.1",
- "os-locale": "^1.4.0",
- "read-pkg-up": "^1.0.1",
- "require-directory": "^2.1.1",
- "require-main-filename": "^1.0.1",
- "set-blocking": "^2.0.0",
- "string-width": "^1.0.2",
- "which-module": "^1.0.0",
- "y18n": "^3.2.1",
- "yargs-parser": "^4.2.0"
- }
- },
- "node_modules/svg2png/node_modules/yargs-parser": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz",
- "integrity": "sha512-+QQWqC2xeL0N5/TE+TY6OGEqyNRM+g2/r712PDNYgiCdXYCApXf1vzfmDSLBxfGRwV+moTq/V8FnMI24JCm2Yg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "camelcase": "^3.0.0"
- }
- },
"node_modules/symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
@@ -13523,9 +10556,9 @@
"license": "MIT"
},
"node_modules/tailwind-merge": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz",
- "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==",
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz",
+ "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==",
"license": "MIT",
"funding": {
"type": "github",
@@ -13533,9 +10566,9 @@
}
},
"node_modules/tailwindcss": {
- "version": "3.4.18",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz",
- "integrity": "sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==",
+ "version": "3.4.19",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
+ "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
"license": "MIT",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
@@ -13578,30 +10611,41 @@
"tailwindcss": ">=3.0.0 || insiders"
}
},
+ "node_modules/tailwindcss/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"
+ }
+ },
"node_modules/tar": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
- "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
+ "version": "7.5.13",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz",
+ "integrity": "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==",
"dev": true,
- "license": "ISC",
+ "license": "BlueOak-1.0.0",
"dependencies": {
- "chownr": "^2.0.0",
- "fs-minipass": "^2.0.0",
- "minipass": "^5.0.0",
- "minizlib": "^2.1.1",
- "mkdirp": "^1.0.3",
- "yallist": "^4.0.0"
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.1.0",
+ "yallist": "^5.0.0"
},
"engines": {
- "node": ">=10"
+ "node": ">=18"
}
},
"node_modules/tar/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
"dev": true,
- "license": "ISC"
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
},
"node_modules/temp": {
"version": "0.9.4",
@@ -13645,9 +10689,9 @@
}
},
"node_modules/temp-file/node_modules/jsonfile": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
- "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -13667,39 +10711,10 @@
"node": ">= 10.0.0"
}
},
- "node_modules/temp/node_modules/mkdirp": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
- "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "minimist": "^1.2.6"
- },
- "bin": {
- "mkdirp": "bin/cmd.js"
- }
- },
- "node_modules/temp/node_modules/rimraf": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
- "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
- "deprecated": "Rimraf versions prior to v4 are no longer supported",
- "dev": true,
- "license": "ISC",
- "peer": true,
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- }
- },
"node_modules/terser": {
- "version": "5.44.1",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz",
- "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
+ "version": "5.46.1",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz",
+ "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -13743,23 +10758,6 @@
"node": ">=0.8"
}
},
- "node_modules/throttleit": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz",
- "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/timm": {
- "version": "1.7.1",
- "resolved": "https://registry.npmjs.org/timm/-/timm-1.7.1.tgz",
- "integrity": "sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/tiny-async-pool": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz",
@@ -13781,9 +10779,9 @@
}
},
"node_modules/tiny-lru": {
- "version": "11.4.5",
- "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.4.5.tgz",
- "integrity": "sha512-hkcz3FjNJfKXjV4mjQ1OrXSLAehg8Hw+cEZclOVT+5c/cWQWImQ9wolzTjth+dmmDe++p3bme3fTxz6Q4Etsqw==",
+ "version": "11.4.7",
+ "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.4.7.tgz",
+ "integrity": "sha512-w/Te7uMUVeH0CR8vZIjr+XiN41V+30lkDdK+NRIDCUYKKuL9VcmaUEmaPISuwGhLlrTGh5yu18lENtR9axSxYw==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=12"
@@ -13796,17 +10794,10 @@
"dev": true,
"license": "MIT"
},
- "node_modules/tinycolor2": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
- "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/tinyexec": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
- "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz",
+ "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -13814,14 +10805,13 @@
}
},
"node_modules/tinyglobby": {
- "version": "0.2.15",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
- "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
- "dev": true,
+ "version": "0.2.16",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
+ "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
- "picomatch": "^4.0.3"
+ "picomatch": "^4.0.4"
},
"engines": {
"node": ">=12.0.0"
@@ -13830,41 +10820,10 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
- "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==",
- "dev": true,
- "license": "MIT",
- "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==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
"node_modules/tinyrainbow": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz",
- "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==",
+ "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": {
@@ -13872,22 +10831,22 @@
}
},
"node_modules/tldts": {
- "version": "7.0.27",
- "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz",
- "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==",
+ "version": "7.0.28",
+ "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.28.tgz",
+ "integrity": "sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "tldts-core": "^7.0.27"
+ "tldts-core": "^7.0.28"
},
"bin": {
"tldts": "bin/cli.js"
}
},
"node_modules/tldts-core": {
- "version": "7.0.27",
- "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz",
- "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==",
+ "version": "7.0.28",
+ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.28.tgz",
+ "integrity": "sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==",
"dev": true,
"license": "MIT"
},
@@ -13923,36 +10882,27 @@
"node": ">=8.0"
}
},
- "node_modules/token-types": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz",
- "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
+ "node_modules/totalist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
+ "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "@tokenizer/token": "^0.3.0",
- "ieee754": "^1.2.1"
- },
"engines": {
- "node": ">=10"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/Borewit"
+ "node": ">=6"
}
},
"node_modules/tough-cookie": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
- "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz",
+ "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
- "psl": "^1.1.28",
- "punycode": "^2.1.1"
+ "tldts": "^7.0.5"
},
"engines": {
- "node": ">=0.8"
+ "node": ">=16"
}
},
"node_modules/tr46": {
@@ -13990,33 +10940,20 @@
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
- "node_modules/tunnel-agent": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
- "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "node_modules/type-fest": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
+ "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
"dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "safe-buffer": "^5.0.1"
- },
+ "license": "(MIT OR CC0-1.0)",
+ "optional": true,
"engines": {
- "node": "*"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/tweetnacl": {
- "version": "0.14.5",
- "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
- "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
- "dev": true,
- "license": "Unlicense"
- },
- "node_modules/typedarray": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
- "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@@ -14032,9 +10969,9 @@
}
},
"node_modules/undici": {
- "version": "7.24.6",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.6.tgz",
- "integrity": "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==",
+ "version": "7.25.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz",
+ "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -14042,36 +10979,36 @@
}
},
"node_modules/undici-types": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
- "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/unique-filename": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz",
- "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz",
+ "integrity": "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==",
"dev": true,
"license": "ISC",
"dependencies": {
- "unique-slug": "^3.0.0"
+ "unique-slug": "^5.0.0"
},
"engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ "node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/unique-slug": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz",
- "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-5.0.0.tgz",
+ "integrity": "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==",
"dev": true,
"license": "ISC",
"dependencies": {
"imurmurhash": "^0.1.4"
},
"engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ "node": "^18.17.0 || >=20.5.0"
}
},
"node_modules/universalify": {
@@ -14085,9 +11022,9 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
- "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "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": [
{
@@ -14196,16 +11133,6 @@
"dev": true,
"license": "(WTFPL OR MIT)"
},
- "node_modules/utif": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz",
- "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "pako": "^1.0.5"
- }
- },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -14225,17 +11152,6 @@
"uuid": "dist-node/bin/uuid"
}
},
- "node_modules/validate-npm-package-license": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
- "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "spdx-correct": "^3.0.0",
- "spdx-expression-parse": "^3.0.0"
- }
- },
"node_modules/verror": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
@@ -14253,660 +11169,9 @@
}
},
"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"
- },
- "funding": {
- "url": "https://github.com/vitejs/vite?sponsor=1"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.3"
- },
- "peerDependencies": {
- "@types/node": "^18.0.0 || >=20.0.0",
- "less": "*",
- "lightningcss": "^1.21.0",
- "sass": "*",
- "sass-embedded": "*",
- "stylus": "*",
- "sugarss": "*",
- "terser": "^5.4.0"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- },
- "less": {
- "optional": true
- },
- "lightningcss": {
- "optional": true
- },
- "sass": {
- "optional": true
- },
- "sass-embedded": {
- "optional": true
- },
- "stylus": {
- "optional": true
- },
- "sugarss": {
- "optional": true
- },
- "terser": {
- "optional": true
- }
- }
- },
- "node_modules/vite-plugin-electron": {
- "version": "0.28.8",
- "resolved": "https://registry.npmjs.org/vite-plugin-electron/-/vite-plugin-electron-0.28.8.tgz",
- "integrity": "sha512-ir+B21oSGK9j23OEvt4EXyco9xDCaF6OGFe0V/8Zc0yL2+HMyQ6mmNQEIhXsEsZCSfIowBpwQBeHH4wVsfraeg==",
- "dev": true,
- "license": "MIT",
- "peerDependencies": {
- "vite-plugin-electron-renderer": "*"
- },
- "peerDependenciesMeta": {
- "vite-plugin-electron-renderer": {
- "optional": true
- }
- }
- },
- "node_modules/vite-plugin-electron-renderer": {
- "version": "0.14.6",
- "resolved": "https://registry.npmjs.org/vite-plugin-electron-renderer/-/vite-plugin-electron-renderer-0.14.6.tgz",
- "integrity": "sha512-oqkWFa7kQIkvHXG7+Mnl1RTroA4sP0yesKatmAy0gjZC4VwUqlvF9IvOpHd1fpLWsqYX/eZlVxlhULNtaQ78Jw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/vitest": {
- "version": "4.0.16",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.16.tgz",
- "integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/expect": "4.0.16",
- "@vitest/mocker": "4.0.16",
- "@vitest/pretty-format": "4.0.16",
- "@vitest/runner": "4.0.16",
- "@vitest/snapshot": "4.0.16",
- "@vitest/spy": "4.0.16",
- "@vitest/utils": "4.0.16",
- "es-module-lexer": "^1.7.0",
- "expect-type": "^1.2.2",
- "magic-string": "^0.30.21",
- "obug": "^2.1.1",
- "pathe": "^2.0.3",
- "picomatch": "^4.0.3",
- "std-env": "^3.10.0",
- "tinybench": "^2.9.0",
- "tinyexec": "^1.0.2",
- "tinyglobby": "^0.2.15",
- "tinyrainbow": "^3.0.3",
- "vite": "^6.0.0 || ^7.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": {
- "@edge-runtime/vm": "*",
- "@opentelemetry/api": "^1.9.0",
- "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
- "@vitest/browser-playwright": "4.0.16",
- "@vitest/browser-preview": "4.0.16",
- "@vitest/browser-webdriverio": "4.0.16",
- "@vitest/ui": "4.0.16",
- "happy-dom": "*",
- "jsdom": "*"
- },
- "peerDependenciesMeta": {
- "@edge-runtime/vm": {
- "optional": true
- },
- "@opentelemetry/api": {
- "optional": true
- },
- "@types/node": {
- "optional": true
- },
- "@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
- }
- }
- },
- "node_modules/vitest/node_modules/@esbuild/aix-ppc64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
- "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/android-arm": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
- "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/android-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
- "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/android-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
- "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/darwin-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
- "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/darwin-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
- "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
- "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/freebsd-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
- "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-arm": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
- "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
- "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-ia32": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
- "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-loong64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
- "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-mips64el": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
- "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-ppc64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
- "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-riscv64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
- "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-s390x": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
- "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
- "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/netbsd-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
- "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/openbsd-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
- "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/sunos-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
- "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/win32-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
- "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/win32-ia32": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
- "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/win32-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
- "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@vitest/mocker": {
- "version": "4.0.16",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.16.tgz",
- "integrity": "sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/spy": "4.0.16",
- "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-0"
- },
- "peerDependenciesMeta": {
- "msw": {
- "optional": true
- },
- "vite": {
- "optional": true
- }
- }
- },
- "node_modules/vitest/node_modules/esbuild": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
- "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.2",
- "@esbuild/android-arm": "0.27.2",
- "@esbuild/android-arm64": "0.27.2",
- "@esbuild/android-x64": "0.27.2",
- "@esbuild/darwin-arm64": "0.27.2",
- "@esbuild/darwin-x64": "0.27.2",
- "@esbuild/freebsd-arm64": "0.27.2",
- "@esbuild/freebsd-x64": "0.27.2",
- "@esbuild/linux-arm": "0.27.2",
- "@esbuild/linux-arm64": "0.27.2",
- "@esbuild/linux-ia32": "0.27.2",
- "@esbuild/linux-loong64": "0.27.2",
- "@esbuild/linux-mips64el": "0.27.2",
- "@esbuild/linux-ppc64": "0.27.2",
- "@esbuild/linux-riscv64": "0.27.2",
- "@esbuild/linux-s390x": "0.27.2",
- "@esbuild/linux-x64": "0.27.2",
- "@esbuild/netbsd-arm64": "0.27.2",
- "@esbuild/netbsd-x64": "0.27.2",
- "@esbuild/openbsd-arm64": "0.27.2",
- "@esbuild/openbsd-x64": "0.27.2",
- "@esbuild/openharmony-arm64": "0.27.2",
- "@esbuild/sunos-x64": "0.27.2",
- "@esbuild/win32-arm64": "0.27.2",
- "@esbuild/win32-ia32": "0.27.2",
- "@esbuild/win32-x64": "0.27.2"
- }
- },
- "node_modules/vitest/node_modules/fdir": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "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"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/vitest/node_modules/vite": {
- "version": "7.3.0",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz",
- "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz",
+ "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -14978,6 +11243,133 @@
}
}
},
+ "node_modules/vite-plugin-electron": {
+ "version": "0.29.1",
+ "resolved": "https://registry.npmjs.org/vite-plugin-electron/-/vite-plugin-electron-0.29.1.tgz",
+ "integrity": "sha512-AejNed5BgHFnuw8h5puTa61C6vdP4ydbsbo/uVjH1fTdHAlCDz1+o6pDQ/scQj1udDrGvH01+vTbzQh/vMnR9w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "vite-plugin-electron-renderer": "*"
+ },
+ "peerDependenciesMeta": {
+ "vite-plugin-electron-renderer": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-plugin-electron-renderer": {
+ "version": "0.14.6",
+ "resolved": "https://registry.npmjs.org/vite-plugin-electron-renderer/-/vite-plugin-electron-renderer-0.14.6.tgz",
+ "integrity": "sha512-oqkWFa7kQIkvHXG7+Mnl1RTroA4sP0yesKatmAy0gjZC4VwUqlvF9IvOpHd1fpLWsqYX/eZlVxlhULNtaQ78Jw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vite/node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/vitest": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz",
+ "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "4.1.5",
+ "@vitest/mocker": "4.1.5",
+ "@vitest/pretty-format": "4.1.5",
+ "@vitest/runner": "4.1.5",
+ "@vitest/snapshot": "4.1.5",
+ "@vitest/spy": "4.1.5",
+ "@vitest/utils": "4.1.5",
+ "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.1.0",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.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": {
+ "@edge-runtime/vm": "*",
+ "@opentelemetry/api": "^1.9.0",
+ "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
+ "@vitest/browser-playwright": "4.1.5",
+ "@vitest/browser-preview": "4.1.5",
+ "@vitest/browser-webdriverio": "4.1.5",
+ "@vitest/coverage-istanbul": "4.1.5",
+ "@vitest/coverage-v8": "4.1.5",
+ "@vitest/ui": "4.1.5",
+ "happy-dom": "*",
+ "jsdom": "*",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser-playwright": {
+ "optional": true
+ },
+ "@vitest/browser-preview": {
+ "optional": true
+ },
+ "@vitest/browser-webdriverio": {
+ "optional": true
+ },
+ "@vitest/coverage-istanbul": {
+ "optional": true
+ },
+ "@vitest/coverage-v8": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ },
+ "vite": {
+ "optional": false
+ }
+ }
+ },
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
@@ -15046,27 +11438,21 @@
}
},
"node_modules/which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
+ "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
+ "dev": true,
"license": "ISC",
"dependencies": {
- "isexe": "^2.0.0"
+ "isexe": "^3.1.1"
},
"bin": {
- "node-which": "bin/node-which"
+ "node-which": "bin/which.js"
},
"engines": {
- "node": ">= 8"
+ "node": "^18.17.0 || >=20.5.0"
}
},
- "node_modules/which-module": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
- "integrity": "sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==",
- "dev": true,
- "license": "ISC"
- },
"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",
@@ -15084,29 +11470,19 @@
"node": ">=8"
}
},
- "node_modules/wide-align": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
- "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "string-width": "^1.0.2 || 2 || 3 || 4"
- }
- },
"node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz",
+ "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==",
"dev": true,
"license": "MIT",
"dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
},
"engines": {
- "node": ">=10"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
@@ -15117,6 +11493,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
@@ -15130,6 +11507,57 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
+ "node_modules/wrap-ansi-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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/emoji-regex": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
+ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -15137,17 +11565,26 @@
"dev": true,
"license": "ISC"
},
- "node_modules/xhr": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz",
- "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==",
+ "node_modules/ws": {
+ "version": "8.20.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
+ "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "global": "~4.4.0",
- "is-function": "^1.0.1",
- "parse-headers": "^2.0.0",
- "xtend": "^4.0.0"
+ "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": {
@@ -15160,37 +11597,6 @@
"node": ">=18"
}
},
- "node_modules/xml-parse-from-string": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz",
- "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/xml2js": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
- "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "sax": ">=0.6.0",
- "xmlbuilder": "~11.0.0"
- },
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/xml2js/node_modules/xmlbuilder": {
- "version": "11.0.1",
- "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
- "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4.0"
- }
- },
"node_modules/xmlbuilder": {
"version": "15.1.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
@@ -15208,16 +11614,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/xtend": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
- "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.4"
- }
- },
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
@@ -15236,9 +11632,9 @@
"license": "ISC"
},
"node_modules/yaml": {
- "version": "2.8.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
- "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
+ "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
"devOptional": true,
"license": "ISC",
"bin": {
diff --git a/package.json b/package.json
index 8817372..dbd5862 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,8 @@
"test": "vitest --run",
"test:watch": "vitest",
"build-vite": "tsc && vite build",
+ "test:browser": "vitest --config vitest.browser.config.ts --run",
+ "test:browser:install": "playwright install --with-deps chromium-headless-shell",
"test:e2e": "playwright test",
"prepare": "husky"
},
@@ -38,67 +40,67 @@
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slider": "^1.3.6",
- "@radix-ui/react-slot": "^1.2.3",
+ "@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"@types/gif.js": "^0.2.5",
- "@uiw/color-convert": "^2.9.2",
- "@uiw/react-color-block": "^2.9.2",
+ "@uiw/color-convert": "^2.10.1",
+ "@uiw/react-color-block": "^2.10.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
- "dnd-timeline": "^2.2.0",
- "emoji-picker-react": "^4.16.1",
+ "dnd-timeline": "^2.4.0",
+ "emoji-picker-react": "^4.18.0",
"fix-webm-duration": "^1.0.6",
"gif.js": "^0.2.0",
- "gsap": "^3.13.0",
+ "gsap": "^3.15.0",
"lucide-react": "^0.545.0",
- "mediabunny": "^1.25.1",
- "motion": "^12.23.24",
- "mp4box": "^2.2.0",
+ "mediabunny": "^1.40.1",
+ "motion": "^12.38.0",
+ "mp4box": "^2.3.0",
"pixi-filters": "^6.1.5",
- "pixi.js": "^8.14.0",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-icons": "^5.5.0",
+ "pixi.js": "^8.18.1",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-icons": "^5.6.0",
"react-resizable-panels": "^3.0.6",
- "react-rnd": "^10.5.2",
+ "react-rnd": "^10.5.3",
"sonner": "^2.0.7",
- "tailwind-merge": "^3.3.1",
+ "tailwind-merge": "^3.5.0",
"tailwindcss-animate": "^1.0.7",
"uuid": "^13.0.0",
"web-demuxer": "^4.0.0"
},
"devDependencies": {
- "@biomejs/biome": "^2.3.13",
- "@playwright/test": "^1.58.2",
+ "@biomejs/biome": "^2.4.12",
+ "@playwright/test": "^1.59.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
- "@types/node": "^25.0.3",
- "@types/react": "^18.2.64",
- "@types/react-dom": "^18.2.21",
- "@types/uuid": "^10.0.0",
- "@vitejs/plugin-react": "^4.2.1",
- "autoprefixer": "^10.4.21",
- "electron": "^39.2.7",
- "electron-builder": "^26.7.0",
- "electron-icon-builder": "^2.0.1",
- "electron-rebuild": "^3.2.9",
- "fast-check": "^4.5.2",
+ "@types/node": "^22.19.17",
+ "@types/react": "^18.3.28",
+ "@types/react-dom": "^18.3.7",
+ "@vitejs/plugin-react": "^5.2.0",
+ "@vitest/browser": "^4.1.4",
+ "@vitest/browser-playwright": "^4.1.4",
+ "autoprefixer": "^10.5.0",
+ "electron": "^41.2.1",
+ "electron-builder": "^26.8.1",
+ "esbuild": "^0.27.0",
+ "fast-check": "^4.7.0",
"husky": "^9.1.7",
- "jsdom": "^29.0.1",
- "lint-staged": "^16.3.2",
- "postcss": "^8.5.6",
- "tailwindcss": "^3.4.18",
- "terser": "^5.44.1",
- "typescript": "^5.2.2",
- "vite": "^5.1.6",
- "vite-plugin-electron": "^0.28.6",
- "vite-plugin-electron-renderer": "^0.14.5",
- "vitest": "^4.0.16"
+ "jsdom": "^29.0.2",
+ "lint-staged": "^16.4.0",
+ "postcss": "^8.5.10",
+ "tailwindcss": "^3.4.19",
+ "terser": "^5.46.1",
+ "typescript": "^5.9.3",
+ "vite": "^7.3.2",
+ "vite-plugin-electron": "^0.29.1",
+ "vite-plugin-electron-renderer": "^0.14.6",
+ "vitest": "^4.1.4"
},
"main": "dist-electron/main.js",
"lint-staged": {
diff --git a/scripts/build_macos.sh b/scripts/build_macos.sh
new file mode 100755
index 0000000..bd35710
--- /dev/null
+++ b/scripts/build_macos.sh
@@ -0,0 +1,216 @@
+#!/bin/bash
+#
+# OpenScreen macOS Build Script
+# Produces: release//OpenScreen-Mac--.dmg
+#
+# Usage: chmod +x scripts/build_macos.sh && ./scripts/build_macos.sh
+#
+
+set -euo pipefail
+
+# ── Load .env ─────────────────────────────────────────────────────────
+PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+ENV_FILE="${PROJECT_ROOT}/.env"
+
+if [ -f "$ENV_FILE" ]; then
+ set -a
+ source "$ENV_FILE"
+ set +a
+else
+ echo "ERROR: .env file not found at ${ENV_FILE}"
+ echo "Create one with APP_NAME, SIGN_IDENTITY, NOTARY_PROFILE, etc."
+ exit 1
+fi
+
+# ── Config ────────────────────────────────────────────────────────────
+VERSION=$(node -p "require('${PROJECT_ROOT}/package.json').version")
+RELEASE_DIR="${PROJECT_ROOT}/release/${VERSION}"
+ENTITLEMENTS="${PROJECT_ROOT}/macos.entitlements"
+ARCHS=("arm64" "x64")
+
+# ── Colors ────────────────────────────────────────────────────────────
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+CYAN='\033[0;36m'
+NC='\033[0m' # No Color
+BOLD='\033[1m'
+
+print_step() { echo -e "\n${CYAN}${BOLD}▸ $1${NC}"; }
+print_ok() { echo -e "${GREEN}✓ $1${NC}"; }
+print_warn() { echo -e "${YELLOW}⚠ $1${NC}"; }
+print_err() { echo -e "${RED}✗ $1${NC}"; }
+
+# ── Preflight ─────────────────────────────────────────────────────────
+echo -e "\n${BOLD}╔══════════════════════════════════════════╗${NC}"
+echo -e "${BOLD}║ ${APP_NAME} macOS Build Script v${VERSION} ║${NC}"
+echo -e "${BOLD}╚══════════════════════════════════════════╝${NC}"
+
+print_step "Checking prerequisites..."
+
+if [[ "$(uname)" != "Darwin" ]]; then
+ print_err "This script must be run on macOS."
+ exit 1
+fi
+print_ok "Running on macOS ($(uname -m))"
+
+if ! command -v node &> /dev/null; then
+ print_err "Node.js not found. Please install Node.js first."
+ exit 1
+fi
+print_ok "Node.js found: $(node -v)"
+
+if ! command -v npm &> /dev/null; then
+ print_err "npm not found."
+ exit 1
+fi
+print_ok "npm found: $(npm -v)"
+
+# Check signing identity
+if ! security find-identity -v -p codesigning | grep -q "$SIGN_IDENTITY"; then
+ print_err "Signing identity not found: ${SIGN_IDENTITY}"
+ print_err "Run 'security find-identity -v -p codesigning' to see available identities."
+ exit 1
+fi
+print_ok "Signing identity found: ${SIGN_IDENTITY}"
+
+# Check notary profile
+if ! xcrun notarytool history --keychain-profile "$NOTARY_PROFILE" &> /dev/null; then
+ print_err "Notary profile '${NOTARY_PROFILE}' not found in keychain."
+ print_err "Run: xcrun notarytool store-credentials \"${NOTARY_PROFILE}\" --apple-id \"${APPLE_ID}\" --team-id \"${TEAM_ID}\""
+ exit 1
+fi
+print_ok "Notary profile found: ${NOTARY_PROFILE}"
+
+# Check entitlements
+if [ ! -f "$ENTITLEMENTS" ]; then
+ print_err "Entitlements file not found: ${ENTITLEMENTS}"
+ exit 1
+fi
+print_ok "Entitlements file found"
+
+# ── Clean ─────────────────────────────────────────────────────────────
+cd "$PROJECT_ROOT"
+
+print_step "Cleaning previous build artifacts..."
+rm -rf dist dist-electron "${RELEASE_DIR}"
+print_ok "Clean complete"
+
+# ── Install Dependencies ─────────────────────────────────────────────
+print_step "Installing dependencies..."
+npm ci
+print_ok "Dependencies installed"
+
+# ── Build Vite + Electron ────────────────────────────────────────────
+print_step "Building Vite + Electron... (this may take a minute)"
+npx tsc && npx vite build
+print_ok "Vite + Electron build complete"
+
+# ── Package, Sign, Notarize per Architecture ─────────────────────────
+for ARCH in "${ARCHS[@]}"; do
+ echo ""
+ echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+ echo -e "${BOLD} Building for: ${ARCH}${NC}"
+ echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
+
+ # ── Package with electron-builder ─────────────────────────────
+ print_step "[${ARCH}] Packaging with electron-builder..."
+
+ # Build .app only (--dir), electron-builder handles codesigning
+ # with hardenedRuntime + entitlements from electron-builder.json5
+ CSC_NAME="$CSC_NAME" npx electron-builder --mac --${ARCH} --dir
+
+ # Find the .app bundle
+ APP_BUNDLE=$(find "${RELEASE_DIR}" -maxdepth 2 -name "*.app" -type d | grep -i "${ARCH}\|mac" | head -n1)
+ if [ -z "$APP_BUNDLE" ]; then
+ # Fallback: find any .app in the output
+ APP_BUNDLE=$(find "${RELEASE_DIR}" -maxdepth 2 -name "*.app" -type d | head -n1)
+ fi
+
+ if [ -z "$APP_BUNDLE" ]; then
+ print_err "[${ARCH}] Could not find .app bundle in ${RELEASE_DIR}"
+ exit 1
+ fi
+ print_ok "[${ARCH}] App bundle: $(basename "$APP_BUNDLE")"
+
+ # ── Verify codesign on .app ───────────────────────────────────
+ print_step "[${ARCH}] Verifying .app code signature..."
+ codesign --verify --deep --strict "$APP_BUNDLE" 2>&1 || print_warn "[${ARCH}] Deep verify had warnings (may be expected pre-notarization)"
+ print_ok "[${ARCH}] .app signature verified"
+
+ # ── Create DMG ────────────────────────────────────────────────
+ DMG_NAME="${APP_NAME}-Mac-${ARCH}-${VERSION}.dmg"
+ DMG_OUTPUT="${RELEASE_DIR}/${DMG_NAME}"
+ DMG_STAGING="${RELEASE_DIR}/dmg-staging-${ARCH}"
+
+ print_step "[${ARCH}] Creating DMG..."
+
+ rm -f "$DMG_OUTPUT"
+ rm -rf "$DMG_STAGING"
+
+ # Stage: app + Applications shortcut for drag-to-install
+ mkdir -p "$DMG_STAGING"
+ cp -R "$APP_BUNDLE" "$DMG_STAGING/"
+ ln -s /Applications "$DMG_STAGING/Applications"
+
+ hdiutil create \
+ -srcfolder "$DMG_STAGING" \
+ -volname "${APP_NAME}" \
+ -fs HFS+ \
+ -fsargs "-c c=64,a=16,e=16" \
+ -format UDBZ \
+ "$DMG_OUTPUT"
+
+ print_ok "[${ARCH}] DMG created: ${DMG_NAME}"
+ rm -rf "$DMG_STAGING"
+
+ # ── Sign DMG ──────────────────────────────────────────────────
+ print_step "[${ARCH}] Signing DMG..."
+ codesign --force --sign "$SIGN_IDENTITY" --timestamp "$DMG_OUTPUT"
+ print_ok "[${ARCH}] DMG signed"
+
+ # ── Notarize DMG ──────────────────────────────────────────────
+ print_step "[${ARCH}] Notarizing DMG with Apple... (this may take several minutes)"
+ xcrun notarytool submit "$DMG_OUTPUT" \
+ --keychain-profile "$NOTARY_PROFILE" \
+ --wait
+ print_ok "[${ARCH}] DMG notarized"
+
+ # ── Staple ────────────────────────────────────────────────────
+ print_step "[${ARCH}] Stapling notarization ticket..."
+ xcrun stapler staple "$DMG_OUTPUT"
+ print_ok "[${ARCH}] Ticket stapled"
+
+ # ── Validate ──────────────────────────────────────────────────
+ print_step "[${ARCH}] Validating stapled DMG..."
+ xcrun stapler validate "$DMG_OUTPUT"
+ print_ok "[${ARCH}] Validation passed"
+
+done
+
+# ── Clean up unpacked dirs (keep only DMGs) ───────────────────────────
+print_step "Cleaning up intermediate directories..."
+find "${RELEASE_DIR}" -maxdepth 1 -type d ! -name "$(basename "$RELEASE_DIR")" -exec rm -rf {} + 2>/dev/null || true
+print_ok "Cleanup complete"
+
+# ── Done ──────────────────────────────────────────────────────────────
+echo ""
+echo -e "${GREEN}${BOLD}════════════════════════════════════════════${NC}"
+echo -e "${GREEN}${BOLD} Build & Notarization Complete!${NC}"
+echo -e "${GREEN}${BOLD}════════════════════════════════════════════${NC}"
+echo ""
+
+for ARCH in "${ARCHS[@]}"; do
+ DMG_NAME="${APP_NAME}-Mac-${ARCH}-${VERSION}.dmg"
+ DMG_PATH="${RELEASE_DIR}/${DMG_NAME}"
+ if [ -f "$DMG_PATH" ]; then
+ DMG_SIZE=$(du -h "$DMG_PATH" | cut -f1)
+ echo -e " 📦 ${BOLD}${ARCH}:${NC} ${DMG_PATH}"
+ echo -e " 📏 ${BOLD}Size:${NC} ${DMG_SIZE}"
+ echo ""
+ fi
+done
+
+echo -e " ${GREEN}All DMGs are fully signed, notarized, and stapled!${NC}"
+echo -e " ${GREEN}Ready for distribution outside the Mac App Store.${NC}"
+echo ""
diff --git a/scripts/i18n-check.mjs b/scripts/i18n-check.mjs
index 3fd0331..699ae9e 100644
--- a/scripts/i18n-check.mjs
+++ b/scripts/i18n-check.mjs
@@ -1,7 +1,7 @@
#!/usr/bin/env node
/**
* Validates that all locale translation files have identical key structures.
- * Compares zh-CN and es against the en baseline for every namespace.
+ * Compares all locale folders (except en) against the en baseline for every namespace.
*
* Usage: node scripts/i18n-check.mjs
*/
@@ -11,7 +11,6 @@ import path from "node:path";
const LOCALES_DIR = path.resolve("src/i18n/locales");
const BASE_LOCALE = "en";
-const COMPARE_LOCALES = ["zh-CN", "es"];
function getKeys(obj, prefix = "") {
const keys = [];
@@ -34,12 +33,19 @@ const namespaces = fs
.filter((f) => f.endsWith(".json"))
.map((f) => f.replace(".json", ""));
+const compareLocales = fs
+ .readdirSync(LOCALES_DIR, { withFileTypes: true })
+ .filter((entry) => entry.isDirectory())
+ .map((entry) => entry.name)
+ .filter((locale) => locale !== BASE_LOCALE)
+ .sort((a, b) => a.localeCompare(b));
+
for (const namespace of namespaces) {
const basePath = path.join(baseDir, `${namespace}.json`);
const baseData = JSON.parse(fs.readFileSync(basePath, "utf-8"));
const baseKeys = getKeys(baseData);
- for (const locale of COMPARE_LOCALES) {
+ for (const locale of compareLocales) {
const localePath = path.join(LOCALES_DIR, locale, `${namespace}.json`);
if (!fs.existsSync(localePath)) {
@@ -77,6 +83,6 @@ if (hasErrors) {
process.exit(1);
} else {
console.log(
- `i18n check PASSED — all ${COMPARE_LOCALES.length} locales match ${BASE_LOCALE} across ${namespaces.length} namespaces.`,
+ `i18n check PASSED — all ${compareLocales.length} locales match ${BASE_LOCALE} across ${namespaces.length} namespaces.`,
);
}
diff --git a/src/App.tsx b/src/App.tsx
index 9772ef8..4045b5d 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
+import { CountdownOverlay } from "./components/launch/CountdownOverlay.tsx";
import { LaunchWindow } from "./components/launch/LaunchWindow";
import { SourceSelector } from "./components/launch/SourceSelector";
import { Toaster } from "./components/ui/sonner";
@@ -9,18 +10,24 @@ import { ShortcutsProvider } from "./contexts/ShortcutsContext";
import { loadAllCustomFonts } from "./lib/customFonts";
export default function App() {
- const [windowType, setWindowType] = useState("");
+ const [windowType, setWindowType] = useState(
+ () => new URLSearchParams(window.location.search).get("windowType") || "",
+ );
useEffect(() => {
- const params = new URLSearchParams(window.location.search);
- const type = params.get("windowType") || "";
- setWindowType(type);
- if (type === "hud-overlay" || type === "source-selector") {
+ const type = new URLSearchParams(window.location.search).get("windowType") || "";
+ if (type !== windowType) {
+ setWindowType(type);
+ }
+
+ if (type === "hud-overlay" || type === "source-selector" || type === "countdown-overlay") {
document.body.style.background = "transparent";
document.documentElement.style.background = "transparent";
document.getElementById("root")?.style.setProperty("background", "transparent");
}
+ }, [windowType]);
+ useEffect(() => {
// Load custom fonts on app initialization
loadAllCustomFonts().catch((error) => {
console.error("Failed to load custom fonts:", error);
@@ -33,6 +40,8 @@ export default function App() {
return ;
case "source-selector":
return ;
+ case "countdown-overlay":
+ return ;
case "editor":
return (
diff --git a/src/components/launch/CountdownOverlay.tsx b/src/components/launch/CountdownOverlay.tsx
new file mode 100644
index 0000000..71d12c5
--- /dev/null
+++ b/src/components/launch/CountdownOverlay.tsx
@@ -0,0 +1,30 @@
+import { useEffect, useState } from "react";
+
+export function CountdownOverlay() {
+ const [value, setValue] = useState(null);
+
+ useEffect(() => {
+ const unsubscribe = window.electronAPI.onCountdownOverlayValue((nextValue) => {
+ setValue(nextValue);
+ });
+
+ return () => unsubscribe();
+ }, []);
+
+ if (value === null) {
+ return null;
+ }
+
+ return (
+
+ );
+}
diff --git a/src/components/launch/LaunchWindow.module.css b/src/components/launch/LaunchWindow.module.css
index ff68c3d..132fa0a 100644
--- a/src/components/launch/LaunchWindow.module.css
+++ b/src/components/launch/LaunchWindow.module.css
@@ -6,3 +6,78 @@
.electronNoDrag {
-webkit-app-region: no-drag;
}
+
+.languageMenuScroll {
+ max-height: 16rem;
+ overflow-y: auto;
+ overflow-x: hidden;
+ overscroll-behavior: contain;
+ touch-action: pan-y;
+ -webkit-overflow-scrolling: touch;
+}
+
+.languageMenuScroll::-webkit-scrollbar {
+ width: 8px;
+}
+
+.languageMenuScroll::-webkit-scrollbar-track {
+ background: rgba(255, 255, 255, 0.04);
+ border-radius: 999px;
+}
+
+.languageMenuScroll::-webkit-scrollbar-thumb {
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.35), rgba(255, 255, 255, 0.2));
+ border-radius: 999px;
+ border: 1px solid rgba(255, 255, 255, 0.15);
+}
+
+.languageMenuScroll::-webkit-scrollbar-thumb:hover {
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.3));
+}
+
+.languageMenuContainer {
+ position: relative;
+ z-index: 20;
+}
+
+.languageMenuPanel {
+ position: fixed;
+ right: 0;
+ top: 0;
+ width: 12rem;
+ padding: 0.375rem;
+ border-radius: 0.75rem;
+ border: 1px solid rgba(255, 255, 255, 0.14);
+ background: linear-gradient(160deg, rgba(28, 29, 42, 0.98), rgba(18, 19, 28, 0.98));
+ box-shadow: 0 20px 45px rgba(0, 0, 0, 0.55);
+ backdrop-filter: blur(14px);
+ pointer-events: auto;
+ box-sizing: border-box;
+}
+
+.languageMenuItem {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0.5rem 0.625rem;
+ border-radius: 0.5rem;
+ font-size: 11px;
+ color: rgba(255, 255, 255, 0.88);
+ background: transparent;
+ border: 0;
+ cursor: pointer;
+ transition: background-color 120ms ease, color 120ms ease;
+}
+
+.languageMenuItem:hover,
+.languageMenuItem:focus-visible {
+ background: rgba(255, 255, 255, 0.1);
+ color: #ffffff;
+ outline: none;
+}
+
+.languageMenuItemActive {
+ background: rgba(255, 255, 255, 0.12);
+ color: #ffffff;
+}
diff --git a/src/components/launch/LaunchWindow.tsx b/src/components/launch/LaunchWindow.tsx
index 249dd77..9b7d809 100644
--- a/src/components/launch/LaunchWindow.tsx
+++ b/src/components/launch/LaunchWindow.tsx
@@ -1,5 +1,6 @@
-import { ChevronDown, Languages } from "lucide-react";
-import { useEffect, useState } from "react";
+import { Check, ChevronDown, Languages } from "lucide-react";
+import { useEffect, useRef, useState } from "react";
+import { createPortal } from "react-dom";
import { BsPauseCircle, BsPlayCircle, BsRecordCircle } from "react-icons/bs";
import { FaRegStopCircle } from "react-icons/fa";
import { FaFolderOpen } from "react-icons/fa6";
@@ -18,9 +19,7 @@ import {
} from "react-icons/md";
import { RxDragHandleDots2 } from "react-icons/rx";
import { useI18n, useScopedT } from "@/contexts/I18nContext";
-import { type Locale, SUPPORTED_LOCALES } from "@/i18n/config";
-import { getLocaleName } from "@/i18n/loader";
-import { isMac as getIsMac } from "@/utils/platformUtils";
+import { getAvailableLocales, getLocaleName } from "@/i18n/loader";
import { useAudioLevelMeter } from "../../hooks/useAudioLevelMeter";
import { useCameraDevices } from "../../hooks/useCameraDevices";
import { useMicrophoneDevices } from "../../hooks/useMicrophoneDevices";
@@ -28,6 +27,7 @@ import { useScreenRecorder } from "../../hooks/useScreenRecorder";
import { requestCameraAccess } from "../../lib/requestCameraAccess";
import { formatTimePadded } from "../../utils/timeUtils";
import { AudioLevelMeter } from "../ui/audio-level-meter";
+import { Button } from "../ui/button";
import { Tooltip } from "../ui/tooltip";
import styles from "./LaunchWindow.module.css";
@@ -67,17 +67,26 @@ const hudGroupClasses =
const hudIconBtnClasses =
"flex items-center justify-center p-2 rounded-full transition-all duration-150 cursor-pointer text-white hover:bg-white/10 hover:scale-[1.08] active:scale-95";
+const hudAuxIconBtnClasses =
+ "flex items-center justify-center p-1.5 rounded-full transition-colors duration-150 text-white/55 hover:bg-white/10 disabled:opacity-30 disabled:cursor-not-allowed";
+
const windowBtnClasses =
"flex items-center justify-center p-2 rounded-full transition-all duration-150 cursor-pointer opacity-50 hover:opacity-90 hover:bg-white/[0.08]";
+const hudSidebarClasses = "ml-0.5 pl-1.5 border-l border-white/10 flex items-center gap-0.5";
+
export function LaunchWindow() {
const t = useScopedT("launch");
- const { locale, setLocale } = useI18n();
- const [isMac, setIsMac] = useState(false);
-
- useEffect(() => {
- getIsMac().then(setIsMac);
- }, []);
+ const availableLocales = getAvailableLocales();
+ const {
+ locale,
+ setLocale,
+ systemLocaleSuggestion,
+ acceptSystemLocaleSuggestion,
+ dismissSystemLocaleSuggestion,
+ resolveSystemLocaleSuggestion,
+ } = useI18n();
+ const suggestedLanguageName = systemLocaleSuggestion ? getLocaleName(systemLocaleSuggestion) : "";
const {
recording,
@@ -109,6 +118,18 @@ export function LaunchWindow() {
const [isWebcamHovered, setIsWebcamHovered] = useState(false);
const [isWebcamFocused, setIsWebcamFocused] = useState(false);
const webcamExpanded = isWebcamHovered || isWebcamFocused;
+ const [isLanguageMenuOpen, setIsLanguageMenuOpen] = useState(false);
+ const languageTriggerRef = useRef(null);
+ const languageMenuPanelRef = useRef(null);
+ const [languageMenuStyle, setLanguageMenuStyle] = useState<{
+ right: number;
+ top: number;
+ maxHeight: number;
+ }>({
+ right: 12,
+ top: 12,
+ maxHeight: 240,
+ });
const {
devices: micDevices,
@@ -162,6 +183,71 @@ export function LaunchWindow() {
});
}, []);
+ useEffect(() => {
+ if (!isLanguageMenuOpen) return;
+
+ const handlePointerDown = (event: PointerEvent) => {
+ const target = event.target as Node;
+ const clickedTrigger = languageTriggerRef.current?.contains(target);
+ const clickedMenu = languageMenuPanelRef.current?.contains(target);
+ if (!clickedTrigger && !clickedMenu) {
+ setIsLanguageMenuOpen(false);
+ }
+ };
+
+ const handleEscape = (event: KeyboardEvent) => {
+ if (event.key === "Escape") {
+ setIsLanguageMenuOpen(false);
+ }
+ };
+
+ window.addEventListener("pointerdown", handlePointerDown);
+ window.addEventListener("keydown", handleEscape);
+
+ return () => {
+ window.removeEventListener("pointerdown", handlePointerDown);
+ window.removeEventListener("keydown", handleEscape);
+ };
+ }, [isLanguageMenuOpen]);
+
+ useEffect(() => {
+ if (!isLanguageMenuOpen || !languageTriggerRef.current) return;
+
+ const updatePosition = () => {
+ if (!languageTriggerRef.current) return;
+ const rect = languageTriggerRef.current.getBoundingClientRect();
+ const gap = 8;
+ const viewportPadding = 8;
+ const availableHeight = Math.max(80, rect.top - viewportPadding - gap);
+ const top = Math.max(viewportPadding, rect.top - gap - availableHeight);
+
+ setLanguageMenuStyle({
+ right: Math.max(viewportPadding, window.innerWidth - rect.right),
+ top,
+ maxHeight: availableHeight,
+ });
+ };
+
+ updatePosition();
+ window.addEventListener("resize", updatePosition);
+ window.addEventListener("scroll", updatePosition, true);
+
+ return () => {
+ window.removeEventListener("resize", updatePosition);
+ window.removeEventListener("scroll", updatePosition, true);
+ };
+ }, [isLanguageMenuOpen]);
+
+ useEffect(() => {
+ if (!isLanguageMenuOpen || !languageMenuPanelRef.current) return;
+ const id = requestAnimationFrame(() => {
+ if (languageMenuPanelRef.current) {
+ languageMenuPanelRef.current.scrollTop = 0;
+ }
+ });
+ return () => cancelAnimationFrame(id);
+ }, [isLanguageMenuOpen]);
+
const [selectedSource, setSelectedSource] = useState("Screen");
const [hasSelectedSource, setHasSelectedSource] = useState(false);
@@ -228,25 +314,42 @@ export function LaunchWindow() {
};
return (
-
- {/* Language switcher — top-left, beside traffic lights */}
-
-
-
)}
- {/* Restart recording */}
- {recording && (
-
-
-
+ {!recording && (
+ <>
+ {/* Open video file */}
+
+
+
+
+ {/* Open project */}
+
+
+
+ >
)}
- {/* Cancel recording */}
- {recording && (
-
+ {/* Right sidebar controls */}
+
+
-
- )}
+
- {/* Open video file */}
-
-
-
+ {isLanguageMenuOpen
+ ? createPortal(
+
event.stopPropagation()}
+ >
+ {availableLocales.map((loc) => (
+
+ ))}
+
,
+ document.body,
+ )
+ : null}
- {/* Open project */}
-
-
-
-
- {/* Window controls */}
-
-
-
+ {/* Window controls */}
+
+
+
+
diff --git a/src/components/launch/SourceSelector.module.css b/src/components/launch/SourceSelector.module.css
index 51239ac..48d5507 100644
--- a/src/components/launch/SourceSelector.module.css
+++ b/src/components/launch/SourceSelector.module.css
@@ -2,15 +2,21 @@
background: linear-gradient(135deg, rgba(28, 28, 34, 0.92) 0%, rgba(18, 18, 22, 0.88) 100%);
backdrop-filter: blur(20px) saturate(160%);
-webkit-backdrop-filter: blur(20px) saturate(160%);
- border-radius: 14px;
- box-shadow:
- 0 4px 16px 0 rgba(0, 0, 0, 0.32),
- 0 1px 3px 0 rgba(0, 0, 0, 0.18) inset;
- border: 1px solid rgba(60, 60, 80, 0.18);
+ border-radius: 30px;
+ corner-shape: squircle;
+ /*
+ Removed box-shadow here because electron doesn't round corners of the shadow, thereby leaving a square border shadow conflicting with the rounded corners of the SourceSelector.
+ The result is easily visible when you place a white window just behind the SourceSelector
+ */
+ /* box-shadow:
+ 0 0px 16px 0 rgba(0, 0, 0, 0.32),
+ 0 1px 3px 0 rgba(0, 0, 0, 0.18) inset; */
+ border: 1.5px solid rgba(60, 60, 80, 0.3);
}
.sourceCard {
- border-radius: 12px;
+ corner-shape: squircle;
+ border-radius: 20px;
background: linear-gradient(120deg, rgba(38, 38, 48, 0.98) 0%, rgba(24, 24, 32, 0.96) 100%);
border: 1px solid rgba(60, 60, 80, 0.22);
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.18);
@@ -28,7 +34,7 @@
}
.selected {
- border: 2px solid #34b27b;
+ border: 1.5px solid #34b27b;
background: linear-gradient(120deg, rgba(52, 178, 123, 0.08) 0%, rgba(38, 38, 48, 0.98) 100%);
box-shadow:
0 0 12px rgba(52, 178, 123, 0.15),
@@ -70,30 +76,27 @@
}
/* scrollbar */
-.sourceGridScroll {
- scrollbar-width: thin;
- scrollbar-color: rgba(52, 178, 123, 0.5) rgba(40, 40, 50, 0.6);
-}
.sourceGridScroll::-webkit-scrollbar {
- width: 8px;
+ width: 3px;
}
.sourceGridScroll::-webkit-scrollbar-track {
- background: rgba(30, 30, 38, 0.5);
+ background: rgba(30, 30, 38, 0.3);
border-radius: 4px;
- margin: 4px 0;
}
.sourceGridScroll::-webkit-scrollbar-thumb {
- background: rgba(80, 80, 100, 0.6);
- border-radius: 4px;
+ background: rgba(52, 178, 123, 0.5);
+ border-radius: 10px;
}
.sourceGridScroll::-webkit-scrollbar-thumb:hover {
background: rgba(52, 178, 123, 0.6);
+ cursor: grab;
}
.sourceGridScroll::-webkit-scrollbar-thumb:active {
background: rgba(52, 178, 123, 0.8);
+ cursor: grabbing;
}
diff --git a/src/components/launch/SourceSelector.tsx b/src/components/launch/SourceSelector.tsx
index 5768c3a..a2aec55 100644
--- a/src/components/launch/SourceSelector.tsx
+++ b/src/components/launch/SourceSelector.tsx
@@ -65,7 +65,7 @@ export function SourceSelector() {
style={{ minHeight: "100vh" }}
>
-
+
{t("sourceSelector.loading")}
@@ -84,10 +84,10 @@ export function SourceSelector() {
{isSelected && (
-
+
@@ -111,16 +111,16 @@ export function SourceSelector() {
defaultValue={screenSources.length === 0 ? "windows" : "screens"}
className="flex-1 flex flex-col"
>
-
+
{t("sourceSelector.screens", { count: String(screenSources.length) })}
{t("sourceSelector.windows", { count: String(windowSources.length) })}
@@ -128,14 +128,14 @@ export function SourceSelector() {
{screenSources.map(renderSourceCard)}
{windowSources.map(renderSourceCard)}
@@ -143,18 +143,18 @@ export function SourceSelector() {
-
+
diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx
index 85336fd..22456b2 100644
--- a/src/components/ui/accordion.tsx
+++ b/src/components/ui/accordion.tsx
@@ -52,4 +52,4 @@ const AccordionContent = React.forwardRef<
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
-export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
+export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx
index 2935ed4..0480764 100644
--- a/src/components/ui/card.tsx
+++ b/src/components/ui/card.tsx
@@ -52,4 +52,4 @@ const CardFooter = React.forwardRef
,
- React.ComponentPropsWithoutRef
->(({ className, sideOffset = 4, ...props }, ref) => (
-
+ React.ComponentPropsWithoutRef & {
+ portalled?: boolean;
+ }
+>(({ className, sideOffset = 4, portalled = true, ...props }, ref) => {
+ const content = (
-
-));
+ );
+
+ if (!portalled) {
+ return content;
+ }
+
+ return {content};
+});
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
const DropdownMenuItem = React.forwardRef<
@@ -169,18 +177,18 @@ DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
export {
DropdownMenu,
- DropdownMenuTrigger,
- DropdownMenuContent,
- DropdownMenuItem,
DropdownMenuCheckboxItem,
- DropdownMenuRadioItem,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
DropdownMenuLabel,
+ DropdownMenuPortal,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
- DropdownMenuGroup,
- DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
- DropdownMenuRadioGroup,
+ DropdownMenuTrigger,
};
diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx
index 8d72390..9341c8d 100644
--- a/src/components/ui/popover.tsx
+++ b/src/components/ui/popover.tsx
@@ -57,4 +57,4 @@ function PopoverArrow({
);
}
-export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverArrow };
+export { Popover, PopoverAnchor, PopoverArrow, PopoverContent, PopoverTrigger };
diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx
index 53e21e6..d151d16 100644
--- a/src/components/ui/select.tsx
+++ b/src/components/ui/select.tsx
@@ -62,34 +62,50 @@ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayNam
const SelectContent = React.forwardRef<
React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, position = "popper", ...props }, ref) => (
-
-
-
- & {
+ showScrollButtons?: boolean;
+ viewportClassName?: string;
+ }
+>(
+ (
+ {
+ className,
+ children,
+ position = "popper",
+ showScrollButtons = true,
+ viewportClassName,
+ ...props
+ },
+ ref,
+ ) => (
+
+
- {children}
-
-
-
-
-));
+ {showScrollButtons ? : null}
+
+ {children}
+
+ {showScrollButtons ? : null}
+
+
+ ),
+);
SelectContent.displayName = SelectPrimitive.Content.displayName;
const SelectLabel = React.forwardRef<
@@ -141,13 +157,13 @@ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
export {
Select,
- SelectGroup,
- SelectValue,
- SelectTrigger,
SelectContent,
- SelectLabel,
+ SelectGroup,
SelectItem,
- SelectSeparator,
- SelectScrollUpButton,
+ SelectLabel,
SelectScrollDownButton,
+ SelectScrollUpButton,
+ SelectSeparator,
+ SelectTrigger,
+ SelectValue,
};
diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx
index dbee684..95e346a 100644
--- a/src/components/ui/tabs.tsx
+++ b/src/components/ui/tabs.tsx
@@ -50,4 +50,4 @@ const TabsContent = React.forwardRef<
));
TabsContent.displayName = TabsPrimitive.Content.displayName;
-export { Tabs, TabsList, TabsTrigger, TabsContent };
+export { Tabs, TabsContent, TabsList, TabsTrigger };
diff --git a/src/components/ui/tooltip.tsx b/src/components/ui/tooltip.tsx
index bd6687b..c5dfc12 100644
--- a/src/components/ui/tooltip.tsx
+++ b/src/components/ui/tooltip.tsx
@@ -67,4 +67,4 @@ function Tooltip({
);
}
-export { TooltipProvider, TooltipRoot, TooltipTrigger, TooltipContent, Tooltip };
+export { Tooltip, TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger };
diff --git a/src/components/video-editor/AnnotationOverlay.tsx b/src/components/video-editor/AnnotationOverlay.tsx
index 11548c7..f416c32 100644
--- a/src/components/video-editor/AnnotationOverlay.tsx
+++ b/src/components/video-editor/AnnotationOverlay.tsx
@@ -1,8 +1,40 @@
-import { useRef } from "react";
+import { type CSSProperties, type PointerEvent, useEffect, useRef, useState } from "react";
import { Rnd } from "react-rnd";
+import {
+ getBlurOverlayColor,
+ getMosaicGridOverlayColor,
+ getNormalizedMosaicBlockSize,
+} from "@/lib/blurEffects";
import { cn } from "@/lib/utils";
import { getArrowComponent } from "./ArrowSvgs";
-import type { AnnotationRegion } from "./types";
+import {
+ type AnnotationRegion,
+ type BlurData,
+ DEFAULT_BLUR_BLOCK_SIZE,
+ DEFAULT_BLUR_DATA,
+ DEFAULT_BLUR_INTENSITY,
+} from "./types";
+
+const FREEHAND_POINT_THRESHOLD = 1;
+type PreviewCanvasSource = {
+ width: number;
+ height: number;
+ clientWidth?: number;
+ clientHeight?: number;
+};
+
+function buildBlurPolygonClipPath(points: Array<{ x: number; y: number }>) {
+ if (points.length < 3) return undefined;
+ const polygon = points.map((point) => `${point.x}% ${point.y}%`).join(", ");
+ return `polygon(${polygon})`;
+}
+
+function buildBlurFreehandPath(points: Array<{ x: number; y: number }>, closed = true) {
+ if (closed ? points.length < 3 : points.length < 2) return null;
+ const [firstPoint, ...rest] = points;
+ const path = `M ${firstPoint.x} ${firstPoint.y} ${rest.map((point) => `L ${point.x} ${point.y}`).join(" ")}`;
+ return closed ? `${path} Z` : path;
+}
interface AnnotationOverlayProps {
annotation: AnnotationRegion;
@@ -11,9 +43,13 @@ interface AnnotationOverlayProps {
containerHeight: number;
onPositionChange: (id: string, position: { x: number; y: number }) => void;
onSizeChange: (id: string, size: { width: number; height: number }) => void;
+ onBlurDataChange?: (id: string, blurData: BlurData) => void;
+ onBlurDataCommit?: () => void;
onClick: (id: string) => void;
zIndex: number;
isSelectedBoost: boolean; // Boost z-index when selected for easy editing
+ previewSourceCanvas?: PreviewCanvasSource | null;
+ previewFrameVersion?: number;
}
export function AnnotationOverlay({
@@ -23,16 +59,130 @@ export function AnnotationOverlay({
containerHeight,
onPositionChange,
onSizeChange,
+ onBlurDataChange,
+ onBlurDataCommit,
onClick,
zIndex,
isSelectedBoost,
+ previewSourceCanvas,
+ previewFrameVersion,
}: AnnotationOverlayProps) {
- const x = (annotation.position.x / 100) * containerWidth;
- const y = (annotation.position.y / 100) * containerHeight;
- const width = (annotation.size.width / 100) * containerWidth;
- const height = (annotation.size.height / 100) * containerHeight;
-
+ const committedX = (annotation.position.x / 100) * containerWidth;
+ const committedY = (annotation.position.y / 100) * containerHeight;
+ const committedWidth = (annotation.size.width / 100) * containerWidth;
+ const committedHeight = (annotation.size.height / 100) * containerHeight;
+ const blurShape = annotation.type === "blur" ? (annotation.blurData?.shape ?? "rectangle") : null;
+ const isSelectedFreehandBlur = isSelected && blurShape === "freehand";
const isDraggingRef = useRef(false);
+ const isDrawingFreehandRef = useRef(false);
+ const freehandPointsRef = useRef>([]);
+ const [isFreehandDrawing, setIsFreehandDrawing] = useState(false);
+ const [draftFreehandPoints, setDraftFreehandPoints] = useState>(
+ [],
+ );
+ const [livePointerPoint, setLivePointerPoint] = useState<{ x: number; y: number } | null>(null);
+ const mosaicCanvasRef = useRef(null);
+ const blurType = annotation.type === "blur" ? (annotation.blurData?.type ?? "blur") : "blur";
+ const blurOverlayColor =
+ annotation.type === "blur" ? getBlurOverlayColor(annotation.blurData) : "";
+ const mosaicGridOverlayColor =
+ annotation.type === "blur" ? getMosaicGridOverlayColor(annotation.blurData) : "";
+ const [liveRect, setLiveRect] = useState({
+ x: committedX,
+ y: committedY,
+ width: committedWidth,
+ height: committedHeight,
+ });
+
+ useEffect(() => {
+ setLiveRect({
+ x: committedX,
+ y: committedY,
+ width: committedWidth,
+ height: committedHeight,
+ });
+ }, [committedHeight, committedWidth, committedX, committedY]);
+
+ const { x, y, width, height } = liveRect;
+
+ useEffect(() => {
+ if (annotation.type !== "blur" || blurType !== "mosaic") {
+ return;
+ }
+ void previewFrameVersion;
+
+ const canvas = mosaicCanvasRef.current;
+ const sourceCanvas = previewSourceCanvas;
+ if (!canvas || !sourceCanvas) {
+ return;
+ }
+
+ const sourceWidth = sourceCanvas.width;
+ const sourceHeight = sourceCanvas.height;
+ const sourceClientWidth = sourceCanvas.clientWidth || containerWidth || sourceWidth;
+ const sourceClientHeight = sourceCanvas.clientHeight || containerHeight || sourceHeight;
+ if (
+ sourceWidth <= 0 ||
+ sourceHeight <= 0 ||
+ sourceClientWidth <= 0 ||
+ sourceClientHeight <= 0
+ ) {
+ return;
+ }
+
+ const drawWidth = Math.max(1, Math.round(width));
+ const drawHeight = Math.max(1, Math.round(height));
+ if (drawWidth <= 0 || drawHeight <= 0) {
+ return;
+ }
+
+ canvas.width = drawWidth;
+ canvas.height = drawHeight;
+
+ const context = canvas.getContext("2d", { willReadFrequently: true });
+ if (!context) {
+ return;
+ }
+
+ const scaleX = sourceWidth / sourceClientWidth;
+ const scaleY = sourceHeight / sourceClientHeight;
+ const sourceX = Math.max(0, Math.floor(x * scaleX));
+ const sourceY = Math.max(0, Math.floor(y * scaleY));
+ const sourceSampleWidth = Math.max(1, Math.ceil(drawWidth * scaleX));
+ const sourceSampleHeight = Math.max(1, Math.ceil(drawHeight * scaleY));
+ const clampedSampleWidth = Math.max(1, Math.min(sourceSampleWidth, sourceWidth - sourceX));
+ const clampedSampleHeight = Math.max(1, Math.min(sourceSampleHeight, sourceHeight - sourceY));
+ const blockSize = getNormalizedMosaicBlockSize(annotation.blurData);
+ const downscaledWidth = Math.max(1, Math.round(drawWidth / blockSize));
+ const downscaledHeight = Math.max(1, Math.round(drawHeight / blockSize));
+ canvas.width = downscaledWidth;
+ canvas.height = downscaledHeight;
+
+ context.clearRect(0, 0, downscaledWidth, downscaledHeight);
+ context.imageSmoothingEnabled = true;
+ context.drawImage(
+ sourceCanvas as CanvasImageSource,
+ sourceX,
+ sourceY,
+ clampedSampleWidth,
+ clampedSampleHeight,
+ 0,
+ 0,
+ downscaledWidth,
+ downscaledHeight,
+ );
+ }, [
+ annotation,
+ blurType,
+ containerHeight,
+ containerWidth,
+ height,
+ previewFrameVersion,
+ previewSourceCanvas,
+ width,
+ x,
+ y,
+ ]);
const renderArrow = () => {
const direction = annotation.figureData?.arrowDirection || "right";
@@ -43,6 +193,95 @@ export function AnnotationOverlay({
return ;
};
+ const normalizePoint = (event: PointerEvent) => {
+ const rect = event.currentTarget.getBoundingClientRect();
+ const x = ((event.clientX - rect.left) / rect.width) * 100;
+ const y = ((event.clientY - rect.top) / rect.height) * 100;
+ return {
+ x: Math.max(0, Math.min(100, x)),
+ y: Math.max(0, Math.min(100, y)),
+ };
+ };
+
+ const appendFreehandPoint = (point: { x: number; y: number }) => {
+ const points = freehandPointsRef.current;
+ const lastPoint = points[points.length - 1];
+ if (!lastPoint) {
+ points.push(point);
+ return;
+ }
+ const dx = point.x - lastPoint.x;
+ const dy = point.y - lastPoint.y;
+ // Sample freehand points in annotation-space percent units to avoid overly dense paths.
+ if (Math.hypot(dx, dy) >= FREEHAND_POINT_THRESHOLD) {
+ points.push(point);
+ }
+ };
+
+ const handleFreehandPointerDown = (event: PointerEvent) => {
+ if (
+ !isSelected ||
+ annotation.type !== "blur" ||
+ annotation.blurData?.shape !== "freehand" ||
+ !onBlurDataChange
+ ) {
+ return;
+ }
+ event.preventDefault();
+ event.stopPropagation();
+ event.currentTarget.setPointerCapture(event.pointerId);
+ isDrawingFreehandRef.current = true;
+ setIsFreehandDrawing(true);
+ const point = normalizePoint(event);
+ freehandPointsRef.current = [point];
+ setDraftFreehandPoints([point]);
+ setLivePointerPoint(point);
+ };
+
+ const handleFreehandPointerMove = (event: PointerEvent) => {
+ if (!isDrawingFreehandRef.current) return;
+ event.preventDefault();
+ event.stopPropagation();
+ const point = normalizePoint(event);
+ setLivePointerPoint(point);
+ appendFreehandPoint(point);
+ setDraftFreehandPoints([...freehandPointsRef.current]);
+ };
+
+ const finishFreehandPointer = (event: PointerEvent) => {
+ if (!isDrawingFreehandRef.current || !onBlurDataChange) return;
+ isDrawingFreehandRef.current = false;
+ setIsFreehandDrawing(false);
+ try {
+ event.currentTarget.releasePointerCapture(event.pointerId);
+ } catch {
+ // no-op if already released
+ }
+ const points = [...freehandPointsRef.current];
+ if (livePointerPoint) {
+ const last = points[points.length - 1];
+ if (!last || Math.hypot(last.x - livePointerPoint.x, last.y - livePointerPoint.y) > 0.001) {
+ points.push(livePointerPoint);
+ }
+ }
+ if (points.length >= 3) {
+ const closedPoints = [...points];
+ const first = closedPoints[0];
+ const last = closedPoints[closedPoints.length - 1];
+ if (Math.hypot(last.x - first.x, last.y - first.y) > 0.001) {
+ closedPoints.push({ ...first });
+ }
+ onBlurDataChange(annotation.id, {
+ ...(annotation.blurData || { ...DEFAULT_BLUR_DATA, shape: "freehand" }),
+ shape: "freehand",
+ freehandPoints: closedPoints,
+ });
+ setDraftFreehandPoints(closedPoints);
+ onBlurDataCommit?.();
+ }
+ setLivePointerPoint(null);
+ };
+
const renderContent = () => {
switch (annotation.type) {
case "text":
@@ -113,6 +352,149 @@ export function AnnotationOverlay({
{renderArrow()}
);
+ case "blur": {
+ const shape = annotation.blurData?.shape ?? "rectangle";
+ const blurIntensity = Math.max(
+ 1,
+ Math.round(annotation.blurData?.intensity ?? DEFAULT_BLUR_INTENSITY),
+ );
+ const blockSize = Math.max(
+ 1,
+ Math.round(annotation.blurData?.blockSize ?? DEFAULT_BLUR_BLOCK_SIZE),
+ );
+ const activeFreehandPoints =
+ shape === "freehand"
+ ? isFreehandDrawing
+ ? draftFreehandPoints
+ : (annotation.blurData?.freehandPoints ?? [])
+ : [];
+ const drawingPoints =
+ isFreehandDrawing && livePointerPoint
+ ? (() => {
+ const last = activeFreehandPoints[activeFreehandPoints.length - 1];
+ if (!last) return [livePointerPoint];
+ const dx = livePointerPoint.x - last.x;
+ const dy = livePointerPoint.y - last.y;
+ return Math.hypot(dx, dy) > 0.01
+ ? [...activeFreehandPoints, livePointerPoint]
+ : activeFreehandPoints;
+ })()
+ : activeFreehandPoints;
+ const clipPath =
+ shape === "freehand" ? buildBlurPolygonClipPath(activeFreehandPoints) : undefined;
+ const freehandPath =
+ shape === "freehand"
+ ? buildBlurFreehandPath(
+ isFreehandDrawing ? drawingPoints : activeFreehandPoints,
+ !isFreehandDrawing,
+ )
+ : null;
+ const currentPointerPoint = isFreehandDrawing
+ ? livePointerPoint || drawingPoints[drawingPoints.length - 1] || null
+ : null;
+ const shapeBorderRadius = shape === "oval" ? "50%" : shape === "rectangle" ? "8px" : "0";
+ const shouldShowFreehandBlurFill =
+ shape !== "freehand" || (!!clipPath && !isFreehandDrawing);
+ const shapeMaskStyle: CSSProperties = {
+ borderRadius: shapeBorderRadius,
+ clipPath: isFreehandDrawing ? undefined : clipPath,
+ WebkitClipPath: isFreehandDrawing ? undefined : clipPath,
+ };
+ const isFreehandSelected = isSelectedFreehandBlur;
+ return (
+
+
+
+ {blurType === "mosaic" && shouldShowFreehandBlurFill && (
+
+ )}
+ {blurType === "mosaic" && shouldShowFreehandBlurFill && (
+
+ )}
+ {blurType === "mosaic" && (
+
+ )}
+ {isSelected && shape !== "freehand" && (
+
+ )}
+
+ {isSelected && shape === "freehand" && freehandPath && (
+
+ )}
+ {isFreehandSelected && (
+
+ )}
+
+ );
+ }
+
default:
return null;
}
@@ -125,7 +507,19 @@ export function AnnotationOverlay({
onDragStart={() => {
isDraggingRef.current = true;
}}
+ onDrag={(_e, d) => {
+ setLiveRect((prev) => ({
+ ...prev,
+ x: d.x,
+ y: d.y,
+ }));
+ }}
onDragStop={(_e, d) => {
+ setLiveRect((prev) => ({
+ ...prev,
+ x: d.x,
+ y: d.y,
+ }));
const xPercent = (d.x / containerWidth) * 100;
const yPercent = (d.y / containerHeight) * 100;
onPositionChange(annotation.id, { x: xPercent, y: yPercent });
@@ -135,7 +529,21 @@ export function AnnotationOverlay({
isDraggingRef.current = false;
}, 100);
}}
+ onResize={(_e, _direction, ref, _delta, position) => {
+ setLiveRect({
+ x: position.x,
+ y: position.y,
+ width: ref.offsetWidth,
+ height: ref.offsetHeight,
+ });
+ }}
onResizeStop={(_e, _direction, ref, _delta, position) => {
+ setLiveRect({
+ x: position.x,
+ y: position.y,
+ width: ref.offsetWidth,
+ height: ref.offsetHeight,
+ });
const xPercent = (position.x / containerWidth) * 100;
const yPercent = (position.y / containerHeight) * 100;
const widthPercent = (ref.offsetWidth / containerWidth) * 100;
@@ -149,18 +557,23 @@ export function AnnotationOverlay({
}}
bounds="parent"
className={cn(
- "cursor-move transition-all",
- isSelected && "ring-2 ring-[#34B27B] ring-offset-2 ring-offset-transparent",
+ "cursor-move",
+ isSelected &&
+ annotation.type !== "blur" &&
+ "ring-2 ring-[#34B27B] ring-offset-2 ring-offset-transparent",
)}
style={{
zIndex: isSelectedBoost ? zIndex + 1000 : zIndex, // Boost selected annotation to ensure it's on top
pointerEvents: isSelected ? "auto" : "none",
- border: isSelected ? "2px solid rgba(52, 178, 123, 0.8)" : "none",
- backgroundColor: isSelected ? "rgba(52, 178, 123, 0.1)" : "transparent",
- boxShadow: isSelected ? "0 0 0 1px rgba(52, 178, 123, 0.35)" : "none",
+ border:
+ isSelected && annotation.type !== "blur" ? "2px solid rgba(52, 178, 123, 0.8)" : "none",
+ backgroundColor:
+ isSelected && annotation.type !== "blur" ? "rgba(52, 178, 123, 0.1)" : "transparent",
+ boxShadow:
+ isSelected && annotation.type !== "blur" ? "0 0 0 1px rgba(52, 178, 123, 0.35)" : "none",
}}
- enableResizing={isSelected}
- disableDragging={!isSelected}
+ enableResizing={isSelected && !isSelectedFreehandBlur}
+ disableDragging={!isSelected || isSelectedFreehandBlur}
resizeHandleStyles={{
topLeft: {
width: "12px",
@@ -206,11 +619,13 @@ export function AnnotationOverlay({
>
{renderContent()}
diff --git a/src/components/video-editor/AnnotationSettingsPanel.tsx b/src/components/video-editor/AnnotationSettingsPanel.tsx
index b289392..4c26c88 100644
--- a/src/components/video-editor/AnnotationSettingsPanel.tsx
+++ b/src/components/video-editor/AnnotationSettingsPanel.tsx
@@ -5,6 +5,7 @@ import {
AlignRight,
Bold,
ChevronDown,
+ Copy,
Image as ImageIcon,
Info,
Italic,
@@ -32,7 +33,12 @@ import { type CustomFont, getCustomFonts } from "@/lib/customFonts";
import { cn } from "@/lib/utils";
import { AddCustomFontDialog } from "./AddCustomFontDialog";
import { getArrowComponent } from "./ArrowSvgs";
-import type { AnnotationRegion, AnnotationType, ArrowDirection, FigureData } from "./types";
+import {
+ type AnnotationRegion,
+ type AnnotationType,
+ type ArrowDirection,
+ type FigureData,
+} from "./types";
interface AnnotationSettingsPanelProps {
annotation: AnnotationRegion;
@@ -40,6 +46,7 @@ interface AnnotationSettingsPanelProps {
onTypeChange: (type: AnnotationType) => void;
onStyleChange: (style: Partial
) => void;
onFigureDataChange?: (figureData: FigureData) => void;
+ onDuplicate?: () => void;
onDelete: () => void;
}
@@ -62,6 +69,7 @@ export function AnnotationSettingsPanel({
onTypeChange,
onStyleChange,
onFigureDataChange,
+ onDuplicate,
onDelete,
}: AnnotationSettingsPanelProps) {
const t = useScopedT("settings");
@@ -597,15 +605,28 @@ export function AnnotationSettingsPanel({
-
+
+
+
+
+
diff --git a/src/components/video-editor/BlurSettingsPanel.tsx b/src/components/video-editor/BlurSettingsPanel.tsx
new file mode 100644
index 0000000..09bfe3a
--- /dev/null
+++ b/src/components/video-editor/BlurSettingsPanel.tsx
@@ -0,0 +1,247 @@
+import { Info, Trash2 } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import { Slider } from "@/components/ui/slider";
+import { useScopedT } from "@/contexts/I18nContext";
+import { getBlurOverlayColor } from "@/lib/blurEffects";
+import { cn } from "@/lib/utils";
+import {
+ type AnnotationRegion,
+ type BlurColor,
+ type BlurData,
+ type BlurShape,
+ DEFAULT_BLUR_BLOCK_SIZE,
+ DEFAULT_BLUR_DATA,
+ MAX_BLUR_BLOCK_SIZE,
+ MAX_BLUR_INTENSITY,
+ MIN_BLUR_BLOCK_SIZE,
+ MIN_BLUR_INTENSITY,
+} from "./types";
+
+interface BlurSettingsPanelProps {
+ blurRegion: AnnotationRegion;
+ onBlurDataChange: (blurData: BlurData) => void;
+ onBlurDataCommit?: () => void;
+ onDelete: () => void;
+}
+
+export function BlurSettingsPanel({
+ blurRegion,
+ onBlurDataChange,
+ onBlurDataCommit,
+ onDelete,
+}: BlurSettingsPanelProps) {
+ const t = useScopedT("settings");
+
+ const blurShapeOptions: Array<{ value: BlurShape; labelKey: string }> = [
+ { value: "rectangle", labelKey: "blurShapeRectangle" },
+ { value: "oval", labelKey: "blurShapeOval" },
+ ];
+ const blurColorOptions: Array<{ value: BlurColor; labelKey: string }> = [
+ { value: "white", labelKey: "blurColorWhite" },
+ { value: "black", labelKey: "blurColorBlack" },
+ ];
+
+ return (
+
+
+
+ {t("annotation.blurShape")}
+
+ {t("annotation.active")}
+
+
+
+
+ {blurShapeOptions.map((shape) => {
+ const activeShape = blurRegion.blurData?.shape || DEFAULT_BLUR_DATA.shape;
+ const isActive = activeShape === shape.value;
+ return (
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+
+ {blurColorOptions.map((option) => {
+ const activeColor = blurRegion.blurData?.color ?? DEFAULT_BLUR_DATA.color;
+ const isActive = activeColor === option.value;
+ return (
+
+ );
+ })}
+
+
+
+
+
+
+ {blurRegion.blurData?.type === "mosaic"
+ ? t("annotation.mosaicBlockSize")
+ : t("annotation.blurIntensity")}
+
+
+ {Math.round(
+ blurRegion.blurData?.type === "mosaic"
+ ? (blurRegion.blurData?.blockSize ?? DEFAULT_BLUR_BLOCK_SIZE)
+ : (blurRegion.blurData?.intensity ?? DEFAULT_BLUR_DATA.intensity),
+ )}
+ px
+
+
+
{
+ onBlurDataChange({
+ ...DEFAULT_BLUR_DATA,
+ ...blurRegion.blurData,
+ ...(blurRegion.blurData?.type === "mosaic"
+ ? { blockSize: values[0] }
+ : { intensity: values[0] }),
+ });
+ }}
+ onValueCommit={() => onBlurDataCommit?.()}
+ min={blurRegion.blurData?.type === "mosaic" ? MIN_BLUR_BLOCK_SIZE : MIN_BLUR_INTENSITY}
+ max={blurRegion.blurData?.type === "mosaic" ? MAX_BLUR_BLOCK_SIZE : MAX_BLUR_INTENSITY}
+ step={1}
+ className="w-full [&_[role=slider]]:bg-[#34B27B] [&_[role=slider]]:border-[#34B27B] [&_[role=slider]]:h-3 [&_[role=slider]]:w-3"
+ />
+
+
+
+
+
+
+
+ {t("annotation.shortcutsAndTips")}
+
+
+ - {t("annotation.tipMovePlayhead")}
+
+
+
+
+ );
+}
diff --git a/src/components/video-editor/SettingsPanel.tsx b/src/components/video-editor/SettingsPanel.tsx
index 7e556b8..4fb4193 100644
--- a/src/components/video-editor/SettingsPanel.tsx
+++ b/src/components/video-editor/SettingsPanel.tsx
@@ -42,20 +42,86 @@ import { cn } from "@/lib/utils";
import { type AspectRatio, isPortraitAspectRatio } from "@/utils/aspectRatioUtils";
import { getTestId } from "@/utils/getTestId";
import { AnnotationSettingsPanel } from "./AnnotationSettingsPanel";
+import { BlurSettingsPanel } from "./BlurSettingsPanel";
import { CropControl } from "./CropControl";
import { KeyboardShortcutsHelp } from "./KeyboardShortcutsHelp";
import type {
AnnotationRegion,
AnnotationType,
+ BlurData,
CropRegion,
FigureData,
PlaybackSpeed,
WebcamLayoutPreset,
WebcamMaskShape,
+ WebcamSizePreset,
ZoomDepth,
ZoomFocusMode,
} from "./types";
-import { SPEED_OPTIONS } from "./types";
+import { DEFAULT_WEBCAM_SIZE_PRESET, MAX_PLAYBACK_SPEED, SPEED_OPTIONS } from "./types";
+
+function CustomSpeedInput({
+ value,
+ onChange,
+ onError,
+}: {
+ value: number;
+ onChange: (val: number) => void;
+ onError: () => void;
+}) {
+ const isPreset = SPEED_OPTIONS.some((o) => o.speed === value);
+ const [draft, setDraft] = useState(isPreset ? "" : String(Math.round(value)));
+ const [isFocused, setIsFocused] = useState(false);
+
+ const prevValue = useRef(value);
+ if (!isFocused && prevValue.current !== value) {
+ prevValue.current = value;
+ setDraft(isPreset ? "" : String(Math.round(value)));
+ }
+
+ const handleChange = useCallback(
+ (e: React.ChangeEvent
) => {
+ const digits = e.target.value.replace(/\D/g, "");
+ if (digits === "") {
+ setDraft("");
+ return;
+ }
+ const num = Number(digits);
+ if (num > MAX_PLAYBACK_SPEED) {
+ onError();
+ return;
+ }
+ setDraft(digits);
+ if (num >= 1) onChange(num);
+ },
+ [onChange, onError],
+ );
+
+ const handleBlur = useCallback(() => {
+ setIsFocused(false);
+ if (!draft || Number(draft) < 1) {
+ setDraft(isPreset ? "" : String(Math.round(value)));
+ }
+ }, [draft, isPreset, value]);
+
+ return (
+
+ setIsFocused(true)}
+ onChange={handleChange}
+ onBlur={handleBlur}
+ onKeyDown={(e) => e.key === "Enter" && (e.target as HTMLInputElement).blur()}
+ className="w-12 bg-white/5 border border-white/10 rounded-md px-1 py-0.5 text-[11px] font-semibold text-[#d97706] text-center focus:outline-none focus:border-[#d97706]/40"
+ />
+ ×
+
+ );
+}
const WALLPAPER_COUNT = 18;
const WALLPAPER_RELATIVE = Array.from(
@@ -132,7 +198,11 @@ interface SettingsPanelProps {
onGifSizePresetChange?: (preset: GifSizePreset) => void;
gifOutputDimensions?: { width: number; height: number };
onExport?: () => void;
- unsavedExport?: { arrayBuffer: ArrayBuffer; fileName: string; format: string } | null;
+ unsavedExport?: {
+ arrayBuffer: ArrayBuffer;
+ fileName: string;
+ format: string;
+ } | null;
onSaveUnsavedExport?: () => void;
selectedAnnotationId?: string | null;
annotationRegions?: AnnotationRegion[];
@@ -140,7 +210,13 @@ interface SettingsPanelProps {
onAnnotationTypeChange?: (id: string, type: AnnotationType) => void;
onAnnotationStyleChange?: (id: string, style: Partial) => void;
onAnnotationFigureDataChange?: (id: string, figureData: FigureData) => void;
+ onAnnotationDuplicate?: (id: string) => void;
onAnnotationDelete?: (id: string) => void;
+ selectedBlurId?: string | null;
+ blurRegions?: AnnotationRegion[];
+ onBlurDataChange?: (id: string, blurData: BlurData) => void;
+ onBlurDataCommit?: () => void;
+ onBlurDelete?: (id: string) => void;
selectedSpeedId?: string | null;
selectedSpeedValue?: PlaybackSpeed | null;
onSpeedChange?: (speed: PlaybackSpeed) => void;
@@ -150,6 +226,12 @@ interface SettingsPanelProps {
onWebcamLayoutPresetChange?: (preset: WebcamLayoutPreset) => void;
webcamMaskShape?: import("./types").WebcamMaskShape;
onWebcamMaskShapeChange?: (shape: import("./types").WebcamMaskShape) => void;
+ selectedZoomInDuration?: number;
+ selectedZoomOutDuration?: number;
+ onZoomDurationChange?: (zoomIn: number, zoomOut: number) => void;
+ webcamSizePreset?: WebcamSizePreset;
+ onWebcamSizePresetChange?: (size: WebcamSizePreset) => void;
+ onWebcamSizePresetCommit?: () => void;
}
export default SettingsPanel;
@@ -163,6 +245,13 @@ const ZOOM_DEPTH_OPTIONS: Array<{ depth: ZoomDepth; label: string }> = [
{ depth: 6, label: "5×" },
];
+const ZOOM_SPEED_OPTIONS = [
+ { label: "Instant", zoomIn: 0, zoomOut: 0 },
+ { label: "Fast", zoomIn: 500, zoomOut: 350 },
+ { label: "Smooth", zoomIn: 1522, zoomOut: 1015 },
+ { label: "Lazy", zoomIn: 3000, zoomOut: 2000 },
+];
+
export function SettingsPanel({
selected,
onWallpaperChange,
@@ -213,7 +302,13 @@ export function SettingsPanel({
onAnnotationTypeChange,
onAnnotationStyleChange,
onAnnotationFigureDataChange,
+ onAnnotationDuplicate,
onAnnotationDelete,
+ selectedBlurId,
+ blurRegions = [],
+ onBlurDataChange,
+ onBlurDataCommit,
+ onBlurDelete,
selectedSpeedId,
selectedSpeedValue,
onSpeedChange,
@@ -223,6 +318,12 @@ export function SettingsPanel({
onWebcamLayoutPresetChange,
webcamMaskShape = "rectangle",
onWebcamMaskShapeChange,
+ selectedZoomInDuration,
+ selectedZoomOutDuration,
+ onZoomDurationChange,
+ webcamSizePreset = DEFAULT_WEBCAM_SIZE_PRESET,
+ onWebcamSizePresetChange,
+ onWebcamSizePresetCommit,
}: SettingsPanelProps) {
const t = useScopedT("settings");
const [wallpaperPaths, setWallpaperPaths] = useState([]);
@@ -268,6 +369,7 @@ export function SettingsPanel({
const cropSnapshotRef = useRef(null);
const [cropAspectLocked, setCropAspectLocked] = useState(false);
const [cropAspectRatio, setCropAspectRatio] = useState("");
+ const isPortraitCanvas = isPortraitAspectRatio(aspectRatio);
const videoWidth = videoElement?.videoWidth || 1920;
const videoHeight = videoElement?.videoHeight || 1080;
@@ -446,6 +548,9 @@ export function SettingsPanel({
const selectedAnnotation = selectedAnnotationId
? annotationRegions.find((a) => a.id === selectedAnnotationId)
: null;
+ const selectedBlur = selectedBlurId
+ ? blurRegions.find((region) => region.id === selectedBlurId)
+ : null;
// If an annotation is selected, show annotation settings instead
if (
@@ -466,11 +571,25 @@ export function SettingsPanel({
? (figureData) => onAnnotationFigureDataChange(selectedAnnotation.id, figureData)
: undefined
}
+ onDuplicate={
+ onAnnotationDuplicate ? () => onAnnotationDuplicate(selectedAnnotation.id) : undefined
+ }
onDelete={() => onAnnotationDelete(selectedAnnotation.id)}
/>
);
}
+ if (selectedBlur && onBlurDataChange && onBlurDelete) {
+ return (
+ onBlurDataChange(selectedBlur.id, blurData)}
+ onBlurDataCommit={onBlurDataCommit}
+ onDelete={() => onBlurDelete(selectedBlur.id)}
+ />
+ );
+ }
+
return (
@@ -547,6 +666,39 @@ export function SettingsPanel({
)}
)}
+
+ {zoomEnabled && (
+
+
+ {t("zoom.speed.title") || "Zoom Speed"}
+
+
+ {ZOOM_SPEED_OPTIONS.map((opt) => {
+ const isActive =
+ selectedZoomInDuration !== undefined &&
+ selectedZoomOutDuration !== undefined &&
+ Math.round(selectedZoomInDuration) === Math.round(opt.zoomIn) &&
+ Math.round(selectedZoomOutDuration) === Math.round(opt.zoomOut);
+ return (
+
+ );
+ })}
+
+
+ )}
{zoomEnabled && (
-
+
{SPEED_OPTIONS.map((option) => {
const isActive = selectedSpeedValue === option.speed;
return (
@@ -609,6 +761,29 @@ export function SettingsPanel({
);
})}
+
+
+
+ {t("speed.customPlaybackSpeed")}
+
+ {selectedSpeedId ? (
+
onSpeedChange?.(val)}
+ onError={() => toast.error(t("speed.maxSpeedError"))}
+ />
+ ) : (
+
+ )}
+
+
{!selectedSpeedId && (
{t("speed.selectRegion")}
)}
@@ -656,15 +831,17 @@ export function SettingsPanel({
- {WEBCAM_LAYOUT_PRESETS.filter(
- (preset) =>
- preset.value === "picture-in-picture" ||
- isPortraitAspectRatio(aspectRatio),
- ).map((preset) => (
+ {WEBCAM_LAYOUT_PRESETS.filter((preset) => {
+ if (preset.value === "picture-in-picture") return true;
+ if (preset.value === "vertical-stack") return isPortraitCanvas;
+ return !isPortraitCanvas;
+ }).map((preset) => (
{preset.value === "picture-in-picture"
? t("layout.pictureInPicture")
- : t("layout.verticalStack")}
+ : preset.value === "vertical-stack"
+ ? t("layout.verticalStack")
+ : t("layout.dualFrame")}
))}
@@ -751,6 +928,27 @@ export function SettingsPanel({
)}
+ {webcamLayoutPreset === "picture-in-picture" && (
+
+
+
+ {t("layout.webcamSize")}
+
+
+ {webcamSizePreset}%
+
+
+
onWebcamSizePresetChange?.(values[0])}
+ onValueCommit={() => onWebcamSizePresetCommit?.()}
+ min={10}
+ max={50}
+ step={1}
+ className="w-full"
+ />
+
+ )}
)}
@@ -879,7 +1077,7 @@ export function SettingsPanel({
-
+
{
setGradient(g);
onWallpaperChange(g);
diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx
index a27fbb9..6d21d13 100644
--- a/src/components/video-editor/VideoEditor.tsx
+++ b/src/components/video-editor/VideoEditor.tsx
@@ -1,13 +1,21 @@
import type { Span } from "dnd-timeline";
-import { FolderOpen, Languages, Save } from "lucide-react";
+import { FolderOpen, Languages, Save, Video } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
import { toast } from "sonner";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog";
import { useI18n, useScopedT } from "@/contexts/I18nContext";
import { useShortcuts } from "@/contexts/ShortcutsContext";
import { INITIAL_EDITOR_STATE, useEditorHistory } from "@/hooks/useEditorHistory";
-import { type Locale, SUPPORTED_LOCALES } from "@/i18n/config";
-import { getLocaleName } from "@/i18n/loader";
+import { type Locale } from "@/i18n/config";
+import { getAvailableLocales, getLocaleName } from "@/i18n/loader";
import {
calculateOutputDimensions,
type ExportFormat,
@@ -46,11 +54,13 @@ import { SettingsPanel } from "./SettingsPanel";
import TimelineEditor from "./timeline/TimelineEditor";
import {
type AnnotationRegion,
+ type BlurData,
type CursorTelemetryPoint,
clampFocusToDepth,
DEFAULT_ANNOTATION_POSITION,
DEFAULT_ANNOTATION_SIZE,
DEFAULT_ANNOTATION_STYLE,
+ DEFAULT_BLUR_DATA,
DEFAULT_FIGURE_DATA,
DEFAULT_PLAYBACK_SPEED,
DEFAULT_ZOOM_DEPTH,
@@ -64,6 +74,7 @@ import {
type ZoomRegion,
} from "./types";
import VideoPlayback, { VideoPlaybackRef } from "./VideoPlayback";
+import { TRANSITION_WINDOW_MS, ZOOM_IN_TRANSITION_WINDOW_MS } from "./videoPlayback/constants";
export default function VideoEditor() {
const {
@@ -90,6 +101,7 @@ export default function VideoEditor() {
aspectRatio,
webcamLayoutPreset,
webcamMaskShape,
+ webcamSizePreset,
webcamPosition,
} = editorState;
@@ -113,10 +125,12 @@ export default function VideoEditor() {
const [selectedTrimId, setSelectedTrimId] = useState(null);
const [selectedSpeedId, setSelectedSpeedId] = useState(null);
const [selectedAnnotationId, setSelectedAnnotationId] = useState(null);
+ const [selectedBlurId, setSelectedBlurId] = useState(null);
const [isExporting, setIsExporting] = useState(false);
const [exportProgress, setExportProgress] = useState(null);
const [exportError, setExportError] = useState(null);
const [showExportDialog, setShowExportDialog] = useState(false);
+ const [showNewRecordingDialog, setShowNewRecordingDialog] = useState(false);
const [exportQuality, setExportQuality] = useState("good");
const [exportFormat, setExportFormat] = useState("mp4");
const [gifFrameRate, setGifFrameRate] = useState(15);
@@ -141,12 +155,22 @@ export default function VideoEditor() {
const { shortcuts, isMac } = useShortcuts();
const t = useScopedT("editor");
const ts = useScopedT("settings");
+ const availableLocales = getAvailableLocales();
const { locale, setLocale } = useI18n();
const nextAnnotationIdRef = useRef(1);
const nextAnnotationZIndexRef = useRef(1);
const exporterRef = useRef(null);
+ const annotationOnlyRegions = useMemo(
+ () => annotationRegions.filter((region) => region.type !== "blur"),
+ [annotationRegions],
+ );
+ const blurRegions = useMemo(
+ () => annotationRegions.filter((region) => region.type === "blur"),
+ [annotationRegions],
+ );
+
const currentProjectMedia = useMemo(() => {
const screenVideoPath = videoSourcePath ?? (videoPath ? fromFileUrl(videoPath) : null);
if (!screenVideoPath) {
@@ -206,6 +230,7 @@ export default function VideoEditor() {
aspectRatio: normalizedEditor.aspectRatio,
webcamLayoutPreset: normalizedEditor.webcamLayoutPreset,
webcamMaskShape: normalizedEditor.webcamMaskShape,
+ webcamSizePreset: normalizedEditor.webcamSizePreset,
webcamPosition: normalizedEditor.webcamPosition,
});
setExportQuality(normalizedEditor.exportQuality);
@@ -218,6 +243,7 @@ export default function VideoEditor() {
setSelectedTrimId(null);
setSelectedSpeedId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
nextZoomIdRef.current = deriveNextId(
"zoom",
@@ -416,6 +442,7 @@ export default function VideoEditor() {
aspectRatio,
webcamLayoutPreset,
webcamMaskShape,
+ webcamSizePreset,
webcamPosition,
exportQuality,
exportFormat,
@@ -479,6 +506,7 @@ export default function VideoEditor() {
gifSizePreset,
videoPath,
t,
+ webcamSizePreset,
],
);
@@ -501,6 +529,16 @@ export default function VideoEditor() {
await saveProject(true);
}, [saveProject]);
+ const handleNewRecordingConfirm = useCallback(async () => {
+ const result = await window.electronAPI.startNewRecording();
+ if (result.success) {
+ setShowNewRecordingDialog(false);
+ } else {
+ console.error("Failed to start new recording:", result.error);
+ setError("Failed to start new recording: " + (result.error || "Unknown error"));
+ }
+ }, []);
+
const handleLoadProject = useCallback(async () => {
const result = await window.electronAPI.loadProjectFile();
@@ -602,7 +640,11 @@ export default function VideoEditor() {
const handleSelectZoom = useCallback((id: string | null) => {
setSelectedZoomId(id);
- if (id) setSelectedTrimId(null);
+ if (id) {
+ setSelectedTrimId(null);
+ setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
+ }
}, []);
const handleSelectTrim = useCallback((id: string | null) => {
@@ -610,6 +652,7 @@ export default function VideoEditor() {
if (id) {
setSelectedZoomId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
}
}, []);
@@ -618,6 +661,17 @@ export default function VideoEditor() {
if (id) {
setSelectedZoomId(null);
setSelectedTrimId(null);
+ setSelectedBlurId(null);
+ }
+ }, []);
+
+ const handleSelectBlur = useCallback((id: string | null) => {
+ setSelectedBlurId(id);
+ if (id) {
+ setSelectedZoomId(null);
+ setSelectedTrimId(null);
+ setSelectedAnnotationId(null);
+ setSelectedSpeedId(null);
}
}, []);
@@ -635,6 +689,7 @@ export default function VideoEditor() {
setSelectedZoomId(id);
setSelectedTrimId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
},
[pushState],
);
@@ -653,6 +708,7 @@ export default function VideoEditor() {
setSelectedZoomId(id);
setSelectedTrimId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
},
[pushState],
);
@@ -669,6 +725,7 @@ export default function VideoEditor() {
setSelectedTrimId(id);
setSelectedZoomId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
},
[pushState],
);
@@ -678,7 +735,11 @@ export default function VideoEditor() {
pushState((prev) => ({
zoomRegions: prev.zoomRegions.map((region) =>
region.id === id
- ? { ...region, startMs: Math.round(span.start), endMs: Math.round(span.end) }
+ ? {
+ ...region,
+ startMs: Math.round(span.start),
+ endMs: Math.round(span.end),
+ }
: region,
),
}));
@@ -691,7 +752,11 @@ export default function VideoEditor() {
pushState((prev) => ({
trimRegions: prev.trimRegions.map((region) =>
region.id === id
- ? { ...region, startMs: Math.round(span.start), endMs: Math.round(span.end) }
+ ? {
+ ...region,
+ startMs: Math.round(span.start),
+ endMs: Math.round(span.end),
+ }
: region,
),
}));
@@ -717,7 +782,11 @@ export default function VideoEditor() {
pushState((prev) => ({
zoomRegions: prev.zoomRegions.map((region) =>
region.id === selectedZoomId
- ? { ...region, depth, focus: clampFocusToDepth(region.focus, depth) }
+ ? {
+ ...region,
+ depth,
+ focus: clampFocusToDepth(region.focus, depth),
+ }
: region,
),
}));
@@ -739,7 +808,9 @@ export default function VideoEditor() {
const handleZoomDelete = useCallback(
(id: string) => {
- pushState((prev) => ({ zoomRegions: prev.zoomRegions.filter((r) => r.id !== id) }));
+ pushState((prev) => ({
+ zoomRegions: prev.zoomRegions.filter((r) => r.id !== id),
+ }));
if (selectedZoomId === id) {
setSelectedZoomId(null);
}
@@ -749,7 +820,9 @@ export default function VideoEditor() {
const handleTrimDelete = useCallback(
(id: string) => {
- pushState((prev) => ({ trimRegions: prev.trimRegions.filter((r) => r.id !== id) }));
+ pushState((prev) => ({
+ trimRegions: prev.trimRegions.filter((r) => r.id !== id),
+ }));
if (selectedTrimId === id) {
setSelectedTrimId(null);
}
@@ -763,6 +836,7 @@ export default function VideoEditor() {
setSelectedZoomId(null);
setSelectedTrimId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
}
}, []);
@@ -775,11 +849,14 @@ export default function VideoEditor() {
endMs: Math.round(span.end),
speed: DEFAULT_PLAYBACK_SPEED,
};
- pushState((prev) => ({ speedRegions: [...prev.speedRegions, newRegion] }));
+ pushState((prev) => ({
+ speedRegions: [...prev.speedRegions, newRegion],
+ }));
setSelectedSpeedId(id);
setSelectedZoomId(null);
setSelectedTrimId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
},
[pushState],
);
@@ -840,10 +917,54 @@ export default function VideoEditor() {
style: { ...DEFAULT_ANNOTATION_STYLE },
zIndex,
};
- pushState((prev) => ({ annotationRegions: [...prev.annotationRegions, newRegion] }));
+ pushState((prev) => ({
+ annotationRegions: [...prev.annotationRegions, newRegion],
+ }));
setSelectedAnnotationId(id);
setSelectedZoomId(null);
setSelectedTrimId(null);
+ setSelectedBlurId(null);
+ },
+ [pushState],
+ );
+
+ const handleBlurAdded = useCallback(
+ (span: Span) => {
+ const id = `annotation-${nextAnnotationIdRef.current++}`;
+ const zIndex = nextAnnotationZIndexRef.current++;
+ const newRegion: AnnotationRegion = {
+ id,
+ startMs: Math.round(span.start),
+ endMs: Math.round(span.end),
+ type: "blur",
+ content: "",
+ position: { ...DEFAULT_ANNOTATION_POSITION },
+ size: { ...DEFAULT_ANNOTATION_SIZE },
+ style: { ...DEFAULT_ANNOTATION_STYLE },
+ zIndex,
+ blurData: { ...DEFAULT_BLUR_DATA },
+ };
+ pushState((prev) => ({
+ annotationRegions: [...prev.annotationRegions, newRegion],
+ }));
+ setSelectedBlurId(id);
+ setSelectedAnnotationId(null);
+ setSelectedZoomId(null);
+ setSelectedTrimId(null);
+ setSelectedSpeedId(null);
+ },
+ [pushState],
+ );
+
+ const handleZoomDurationChange = useCallback(
+ (id: string, zoomIn: number, zoomOut: number) => {
+ pushState((prev) => ({
+ zoomRegions: prev.zoomRegions.map((region) =>
+ region.id === id
+ ? { ...region, zoomInDurationMs: zoomIn, zoomOutDurationMs: zoomOut }
+ : region,
+ ),
+ }));
},
[pushState],
);
@@ -853,7 +974,11 @@ export default function VideoEditor() {
pushState((prev) => ({
annotationRegions: prev.annotationRegions.map((region) =>
region.id === id
- ? { ...region, startMs: Math.round(span.start), endMs: Math.round(span.end) }
+ ? {
+ ...region,
+ startMs: Math.round(span.start),
+ endMs: Math.round(span.end),
+ }
: region,
),
}));
@@ -861,6 +986,33 @@ export default function VideoEditor() {
[pushState],
);
+ const handleAnnotationDuplicate = useCallback(
+ (id: string) => {
+ const duplicateId = `annotation-${nextAnnotationIdRef.current++}`;
+ const duplicateZIndex = nextAnnotationZIndexRef.current++;
+ pushState((prev) => {
+ const source = prev.annotationRegions.find((region) => region.id === id);
+ if (!source) return {};
+
+ const duplicate: AnnotationRegion = {
+ ...source,
+ id: duplicateId,
+ zIndex: duplicateZIndex,
+ position: { x: source.position.x + 4, y: source.position.y + 4 },
+ size: { ...source.size },
+ style: { ...source.style },
+ figureData: source.figureData ? { ...source.figureData } : undefined,
+ };
+
+ return { annotationRegions: [...prev.annotationRegions, duplicate] };
+ });
+ setSelectedAnnotationId(duplicateId);
+ setSelectedZoomId(null);
+ setSelectedTrimId(null);
+ },
+ [pushState],
+ );
+
const handleAnnotationDelete = useCallback(
(id: string) => {
pushState((prev) => ({
@@ -869,8 +1021,11 @@ export default function VideoEditor() {
if (selectedAnnotationId === id) {
setSelectedAnnotationId(null);
}
+ if (selectedBlurId === id) {
+ setSelectedBlurId(null);
+ }
},
- [selectedAnnotationId, pushState],
+ [selectedAnnotationId, selectedBlurId, pushState],
);
const handleAnnotationContentChange = useCallback(
@@ -905,12 +1060,26 @@ export default function VideoEditor() {
if (!region.figureData) {
updatedRegion.figureData = { ...DEFAULT_FIGURE_DATA };
}
+ } else if (type === "blur") {
+ updatedRegion.content = "";
+ if (!region.blurData) {
+ updatedRegion.blurData = { ...DEFAULT_BLUR_DATA };
+ }
}
return updatedRegion;
}),
}));
+
+ if (type === "blur" && selectedAnnotationId === id) {
+ setSelectedAnnotationId(null);
+ setSelectedBlurId(id);
+ setSelectedSpeedId(null);
+ } else if (type !== "blur" && selectedBlurId === id) {
+ setSelectedBlurId(null);
+ setSelectedAnnotationId(id);
+ }
},
- [pushState],
+ [pushState, selectedAnnotationId, selectedBlurId],
);
const handleAnnotationStyleChange = useCallback(
@@ -935,6 +1104,51 @@ export default function VideoEditor() {
[pushState],
);
+ const handleBlurDataPreviewChange = useCallback(
+ (id: string, blurData: BlurData) => {
+ updateState((prev) => ({
+ annotationRegions: prev.annotationRegions.map((region) =>
+ region.id === id
+ ? {
+ ...region,
+ blurData,
+ // Freehand drawing area is the full video surface.
+ ...(blurData.shape === "freehand"
+ ? {
+ position: { x: 0, y: 0 },
+ size: { width: 100, height: 100 },
+ }
+ : {}),
+ }
+ : region,
+ ),
+ }));
+ },
+ [updateState],
+ );
+
+ const handleBlurDataPanelChange = useCallback(
+ (id: string, blurData: BlurData) => {
+ pushState((prev) => ({
+ annotationRegions: prev.annotationRegions.map((region) =>
+ region.id === id
+ ? {
+ ...region,
+ blurData,
+ ...(blurData.shape === "freehand"
+ ? {
+ position: { x: 0, y: 0 },
+ size: { width: 100, height: 100 },
+ }
+ : {}),
+ }
+ : region,
+ ),
+ }));
+ },
+ [pushState],
+ );
+
const handleAnnotationPositionChange = useCallback(
(id: string, position: { x: number; y: number }) => {
pushState((prev) => ({
@@ -1048,11 +1262,14 @@ export default function VideoEditor() {
useEffect(() => {
if (
selectedAnnotationId &&
- !annotationRegions.some((region) => region.id === selectedAnnotationId)
+ !annotationOnlyRegions.some((region) => region.id === selectedAnnotationId)
) {
setSelectedAnnotationId(null);
}
- }, [selectedAnnotationId, annotationRegions]);
+ if (selectedBlurId && !blurRegions.some((region) => region.id === selectedBlurId)) {
+ setSelectedBlurId(null);
+ }
+ }, [selectedAnnotationId, selectedBlurId, annotationOnlyRegions, blurRegions]);
useEffect(() => {
if (selectedSpeedId && !speedRegions.some((region) => region.id === selectedSpeedId)) {
@@ -1174,6 +1391,7 @@ export default function VideoEditor() {
annotationRegions,
webcamLayoutPreset,
webcamMaskShape,
+ webcamSizePreset,
webcamPosition,
previewWidth,
previewHeight,
@@ -1307,6 +1525,7 @@ export default function VideoEditor() {
annotationRegions,
webcamLayoutPreset,
webcamMaskShape,
+ webcamSizePreset,
webcamPosition,
previewWidth,
previewHeight,
@@ -1377,6 +1596,7 @@ export default function VideoEditor() {
aspectRatio,
webcamLayoutPreset,
webcamMaskShape,
+ webcamSizePreset,
webcamPosition,
exportQuality,
handleExportSaved,
@@ -1482,6 +1702,34 @@ export default function VideoEditor() {
return (
+
+
- {SUPPORTED_LOCALES.map((loc) => (
+ {availableLocales.map((loc) => (
))}
+
@@ -1629,6 +1893,7 @@ export default function VideoEditor() {
onZoomAdded={handleZoomAdded}
onZoomSuggested={handleZoomSuggested}
onZoomSpanChange={handleZoomSpanChange}
+ onZoomDurationChange={handleZoomDurationChange}
onZoomDelete={handleZoomDelete}
selectedZoomId={selectedZoomId}
onSelectZoom={handleSelectZoom}
@@ -1644,18 +1909,25 @@ export default function VideoEditor() {
onSpeedDelete={handleSpeedDelete}
selectedSpeedId={selectedSpeedId}
onSelectSpeed={handleSelectSpeed}
- annotationRegions={annotationRegions}
+ annotationRegions={annotationOnlyRegions}
onAnnotationAdded={handleAnnotationAdded}
onAnnotationSpanChange={handleAnnotationSpanChange}
onAnnotationDelete={handleAnnotationDelete}
selectedAnnotationId={selectedAnnotationId}
onSelectAnnotation={handleSelectAnnotation}
+ blurRegions={blurRegions}
+ onBlurAdded={handleBlurAdded}
+ onBlurSpanChange={handleAnnotationSpanChange}
+ onBlurDelete={handleAnnotationDelete}
+ selectedBlurId={selectedBlurId}
+ onSelectBlur={handleSelectBlur}
aspectRatio={aspectRatio}
onAspectRatioChange={(ar) =>
pushState({
aspectRatio: ar,
webcamLayoutPreset:
- !isPortraitAspectRatio(ar) && webcamLayoutPreset === "vertical-stack"
+ (isPortraitAspectRatio(ar) && webcamLayoutPreset === "dual-frame") ||
+ (!isPortraitAspectRatio(ar) && webcamLayoutPreset === "vertical-stack")
? "picture-in-picture"
: webcamLayoutPreset,
})
@@ -1708,11 +1980,14 @@ export default function VideoEditor() {
onWebcamLayoutPresetChange={(preset) =>
pushState({
webcamLayoutPreset: preset,
- webcamPosition: preset === "vertical-stack" ? null : webcamPosition,
+ webcamPosition: preset === "picture-in-picture" ? webcamPosition : null,
})
}
webcamMaskShape={webcamMaskShape}
onWebcamMaskShapeChange={(shape) => pushState({ webcamMaskShape: shape })}
+ webcamSizePreset={webcamSizePreset}
+ onWebcamSizePresetChange={(v) => updateState({ webcamSizePreset: v })}
+ onWebcamSizePresetCommit={commitState}
videoElement={videoPlaybackRef.current?.video || null}
exportQuality={exportQuality}
onExportQualityChange={setExportQuality}
@@ -1739,12 +2014,18 @@ export default function VideoEditor() {
)}
onExport={handleOpenExportDialog}
selectedAnnotationId={selectedAnnotationId}
- annotationRegions={annotationRegions}
+ annotationRegions={annotationOnlyRegions}
onAnnotationContentChange={handleAnnotationContentChange}
onAnnotationTypeChange={handleAnnotationTypeChange}
onAnnotationStyleChange={handleAnnotationStyleChange}
onAnnotationFigureDataChange={handleAnnotationFigureDataChange}
+ onAnnotationDuplicate={handleAnnotationDuplicate}
onAnnotationDelete={handleAnnotationDelete}
+ selectedBlurId={selectedBlurId}
+ blurRegions={blurRegions}
+ onBlurDataChange={handleBlurDataPanelChange}
+ onBlurDataCommit={commitState}
+ onBlurDelete={handleAnnotationDelete}
selectedSpeedId={selectedSpeedId}
selectedSpeedValue={
selectedSpeedId
@@ -1755,6 +2036,21 @@ export default function VideoEditor() {
onSpeedDelete={handleSpeedDelete}
unsavedExport={unsavedExport}
onSaveUnsavedExport={handleSaveUnsavedExport}
+ selectedZoomInDuration={
+ selectedZoomId
+ ? (zoomRegions.find((z) => z.id === selectedZoomId)?.zoomInDurationMs ??
+ Math.round(ZOOM_IN_TRANSITION_WINDOW_MS))
+ : undefined
+ }
+ selectedZoomOutDuration={
+ selectedZoomId
+ ? (zoomRegions.find((z) => z.id === selectedZoomId)?.zoomOutDurationMs ??
+ Math.round(TRANSITION_WINDOW_MS))
+ : undefined
+ }
+ onZoomDurationChange={(zoomIn, zoomOut) =>
+ selectedZoomId && handleZoomDurationChange(selectedZoomId, zoomIn, zoomOut)
+ }
/>
diff --git a/src/components/video-editor/VideoPlayback.tsx b/src/components/video-editor/VideoPlayback.tsx
index b4d98a6..71e5a4f 100644
--- a/src/components/video-editor/VideoPlayback.tsx
+++ b/src/components/video-editor/VideoPlayback.tsx
@@ -24,6 +24,7 @@ import {
type Size,
type StyledRenderRect,
type WebcamLayoutPreset,
+ type WebcamSizePreset,
} from "@/lib/compositeLayout";
import { getCssClipPath } from "@/lib/webcamMaskShapes";
import {
@@ -34,6 +35,7 @@ import {
import { AnnotationOverlay } from "./AnnotationOverlay";
import {
type AnnotationRegion,
+ type BlurData,
type SpeedRegion,
type TrimRegion,
ZOOM_DEPTH_SCALES,
@@ -69,6 +71,7 @@ interface VideoPlaybackProps {
webcamVideoPath?: string;
webcamLayoutPreset: WebcamLayoutPreset;
webcamMaskShape?: import("./types").WebcamMaskShape;
+ webcamSizePreset?: WebcamSizePreset;
webcamPosition?: { cx: number; cy: number } | null;
onWebcamPositionChange?: (position: { cx: number; cy: number }) => void;
onWebcamPositionDragEnd?: () => void;
@@ -99,6 +102,13 @@ interface VideoPlaybackProps {
onSelectAnnotation?: (id: string | null) => void;
onAnnotationPositionChange?: (id: string, position: { x: number; y: number }) => void;
onAnnotationSizeChange?: (id: string, size: { width: number; height: number }) => void;
+ blurRegions?: AnnotationRegion[];
+ selectedBlurId?: string | null;
+ onSelectBlur?: (id: string | null) => void;
+ onBlurPositionChange?: (id: string, position: { x: number; y: number }) => void;
+ onBlurSizeChange?: (id: string, size: { width: number; height: number }) => void;
+ onBlurDataChange?: (id: string, blurData: BlurData) => void;
+ onBlurDataCommit?: () => void;
cursorTelemetry?: import("./types").CursorTelemetryPoint[];
}
@@ -119,6 +129,7 @@ const VideoPlayback = forwardRef(
webcamVideoPath,
webcamLayoutPreset,
webcamMaskShape,
+ webcamSizePreset,
webcamPosition,
onWebcamPositionChange,
onWebcamPositionDragEnd,
@@ -149,6 +160,13 @@ const VideoPlayback = forwardRef(
onSelectAnnotation,
onAnnotationPositionChange,
onAnnotationSizeChange,
+ blurRegions = [],
+ selectedBlurId,
+ onSelectBlur,
+ onBlurPositionChange,
+ onBlurSizeChange,
+ onBlurDataChange,
+ onBlurDataCommit,
cursorTelemetry = [],
},
ref,
@@ -163,11 +181,10 @@ const VideoPlayback = forwardRef(
const timeUpdateAnimationRef = useRef(null);
const [pixiReady, setPixiReady] = useState(false);
const [videoReady, setVideoReady] = useState(false);
+ const [overlaySize, setOverlaySize] = useState({ width: 800, height: 600 });
+ const [overlayElement, setOverlayElement] = useState(null);
const overlayRef = useRef(null);
- const [containerSize, setContainerSize] = useState({
- width: 800,
- height: 600,
- });
+
const focusIndicatorRef = useRef(null);
const [webcamLayout, setWebcamLayout] = useState(null);
const [webcamDimensions, setWebcamDimensions] = useState(null);
@@ -290,6 +307,7 @@ const VideoPlayback = forwardRef(
padding,
webcamDimensions,
webcamLayoutPreset,
+ webcamSizePreset,
webcamPosition,
webcamMaskShape,
});
@@ -321,6 +339,7 @@ const VideoPlayback = forwardRef(
padding,
webcamDimensions,
webcamLayoutPreset,
+ webcamSizePreset,
webcamPosition,
webcamMaskShape,
]);
@@ -329,6 +348,11 @@ const VideoPlayback = forwardRef(
layoutVideoContentRef.current = layoutVideoContent;
}, [layoutVideoContent]);
+ const setOverlayRefs = useCallback((node: HTMLDivElement | null) => {
+ overlayRef.current = node;
+ setOverlayElement(node);
+ }, []);
+
const selectedZoom = useMemo(() => {
if (!selectedZoomId) return null;
return zoomRegions.find((region) => region.id === selectedZoomId) ?? null;
@@ -345,7 +369,7 @@ const VideoPlayback = forwardRef(
if (!vid) return;
try {
allowPlaybackRef.current = true;
- await vid.play();
+ await vid.play().catch((err) => console.log("PLAY ERROR:", err));
} catch (error) {
allowPlaybackRef.current = false;
throw error;
@@ -519,83 +543,22 @@ const VideoPlayback = forwardRef(
useEffect(() => {
if (!pixiReady || !videoReady) return;
- const app = appRef.current;
- const cameraContainer = cameraContainerRef.current;
- const video = videoRef.current;
+ const el = overlayRef.current;
+ if (!el) return;
- if (!app || !cameraContainer || !video) return;
+ const observer = new ResizeObserver((entries) => {
+ const { width, height } = entries[0].contentRect;
- const tickerWasStarted = app.ticker?.started || false;
- if (tickerWasStarted && app.ticker) {
- app.ticker.stop();
- }
-
- const wasPlaying = !video.paused;
- if (wasPlaying) {
- video.pause();
- }
-
- animationStateRef.current = {
- scale: 1,
- focusX: DEFAULT_FOCUS.cx,
- focusY: DEFAULT_FOCUS.cy,
- progress: 0,
- x: 0,
- y: 0,
- appliedScale: 1,
- };
-
- // Reset motion blur state for clean transitions
- motionBlurStateRef.current = createMotionBlurState();
-
- if (blurFilterRef.current) {
- blurFilterRef.current.blur = 0;
- }
-
- requestAnimationFrame(() => {
- const container = cameraContainerRef.current;
- const videoStage = videoContainerRef.current;
- const sprite = videoSpriteRef.current;
- const currentApp = appRef.current;
- if (!container || !videoStage || !sprite || !currentApp) {
- return;
- }
-
- container.scale.set(1);
- container.position.set(0, 0);
- videoStage.scale.set(1);
- videoStage.position.set(0, 0);
- sprite.scale.set(1);
- sprite.position.set(0, 0);
-
- layoutVideoContent();
-
- applyZoomTransform({
- cameraContainer: container,
- blurFilter: blurFilterRef.current,
- stageSize: stageSizeRef.current,
- baseMask: baseMaskRef.current,
- zoomScale: 1,
- focusX: DEFAULT_FOCUS.cx,
- focusY: DEFAULT_FOCUS.cy,
- motionIntensity: 0,
- isPlaying: false,
- motionBlurAmount: motionBlurAmountRef.current,
- });
-
- requestAnimationFrame(() => {
- const finalApp = appRef.current;
- if (wasPlaying && video) {
- video.play().catch(() => {
- // Ignore autoplay restoration failures.
- });
- }
- if (tickerWasStarted && finalApp?.ticker) {
- finalApp.ticker.start();
- }
+ setOverlaySize({
+ width,
+ height,
});
});
- }, [pixiReady, videoReady, layoutVideoContent]);
+
+ observer.observe(el);
+
+ return () => observer.disconnect();
+ }, [pixiReady, videoReady]);
useEffect(() => {
if (!pixiReady || !videoReady) return;
@@ -622,7 +585,8 @@ const VideoPlayback = forwardRef(
}, [selectedZoom, pixiReady, videoReady, updateOverlayForRegion]);
useEffect(() => {
- const overlayEl = overlayRef.current;
+ if (!pixiReady || !videoReady) return;
+ const overlayEl = overlayElement;
if (!overlayEl) return;
if (!selectedZoom) {
overlayEl.style.cursor = "default";
@@ -631,7 +595,34 @@ const VideoPlayback = forwardRef(
}
overlayEl.style.cursor = isPlaying ? "not-allowed" : "grab";
overlayEl.style.pointerEvents = isPlaying ? "none" : "auto";
- }, [selectedZoom, isPlaying]);
+ }, [selectedZoom, isPlaying, pixiReady, videoReady, overlayElement]);
+
+ useEffect(() => {
+ const overlayEl = overlayElement;
+ if (!overlayEl) return;
+
+ const updateOverlaySize = () => {
+ const width = overlayEl.clientWidth || 800;
+ const height = overlayEl.clientHeight || 600;
+ setOverlaySize((prev) => {
+ if (prev.width === width && prev.height === height) return prev;
+ return { width, height };
+ });
+ };
+
+ updateOverlaySize();
+
+ if (typeof ResizeObserver !== "undefined") {
+ const observer = new ResizeObserver(() => {
+ updateOverlaySize();
+ });
+ observer.observe(overlayEl);
+ return () => observer.disconnect();
+ }
+
+ window.addEventListener("resize", updateOverlaySize);
+ return () => window.removeEventListener("resize", updateOverlaySize);
+ }, [overlayElement]);
useEffect(() => {
const container = containerRef.current;
@@ -864,22 +855,12 @@ const VideoPlayback = forwardRef(
};
const ticker = () => {
- const bm = baseMaskRef.current;
- const ss = stageSizeRef.current;
- const viewportRatio =
- bm.width > 0 && bm.height > 0
- ? {
- widthRatio: ss.width / bm.width,
- heightRatio: ss.height / bm.height,
- }
- : undefined;
const { region, strength, blendedScale, transition } = findDominantRegion(
zoomRegionsRef.current,
currentTimeRef.current,
{
connectZooms: true,
cursorTelemetry: cursorTelemetryRef.current,
- viewportRatio,
},
);
@@ -1135,21 +1116,6 @@ const VideoPlayback = forwardRef(
webcamVideo.currentTime = 0;
}, [webcamVideoPath]);
- useEffect(() => {
- if (!overlayRef.current) return;
-
- const observer = new ResizeObserver(() => {
- setContainerSize({
- width: overlayRef.current!.clientWidth,
- height: overlayRef.current!.clientHeight,
- });
- });
-
- observer.observe(overlayRef.current);
-
- return () => observer.disconnect();
- }, []);
-
useEffect(() => {
let mounted = true;
(async () => {
@@ -1301,9 +1267,9 @@ const VideoPlayback = forwardRef(
{/* Only render overlay after PIXI and video are fully initialized */}
{pixiReady && videoReady && (
(
style={{ display: "none", pointerEvents: "none" }}
/>
{(() => {
- const filtered = (annotationRegions || []).filter((annotation) => {
+ const filteredAnnotations = (annotationRegions || []).filter((annotation) => {
if (typeof annotation.startMs !== "number" || typeof annotation.endMs !== "number")
return false;
if (annotation.id === selectedAnnotationId) return true;
const timeMs = Math.round(currentTime * 1000);
- return timeMs >= annotation.startMs && timeMs <= annotation.endMs;
+ return timeMs >= annotation.startMs && timeMs < annotation.endMs;
});
- // Sort by z-index (lowest to highest) so higher z-index renders on top
- const sorted = [...filtered].sort((a, b) => a.zIndex - b.zIndex);
+ const filteredBlurRegions = (blurRegions || []).filter((blurRegion) => {
+ if (typeof blurRegion.startMs !== "number" || typeof blurRegion.endMs !== "number")
+ return false;
+
+ if (blurRegion.id === selectedBlurId) return true;
+
+ const timeMs = Math.round(currentTime * 1000);
+ return timeMs >= blurRegion.startMs && timeMs < blurRegion.endMs;
+ });
+
+ const sorted = [
+ ...filteredAnnotations.map((annotation) => ({
+ kind: "annotation" as const,
+ region: annotation,
+ })),
+ ...filteredBlurRegions.map((blurRegion) => ({
+ kind: "blur" as const,
+ region: blurRegion,
+ })),
+ ].sort((a, b) => a.region.zIndex - b.region.zIndex);
+ const previewSnapshotCanvas = (() => {
+ const app = appRef.current;
+ if (!app?.renderer?.extract) return null;
+ try {
+ return app.renderer.extract.canvas(app.stage);
+ } catch {
+ return null;
+ }
+ })();
// Handle click-through cycling: when clicking same annotation, cycle to next
const handleAnnotationClick = (clickedId: string) => {
if (!onSelectAnnotation) return;
// If clicking on already selected annotation and there are multiple overlapping
- if (clickedId === selectedAnnotationId && sorted.length > 1) {
+ if (clickedId === selectedAnnotationId && filteredAnnotations.length > 1) {
// Find current index and cycle to next
- const currentIndex = sorted.findIndex((a) => a.id === clickedId);
- const nextIndex = (currentIndex + 1) % sorted.length;
- onSelectAnnotation(sorted[nextIndex].id);
+ const currentIndex = filteredAnnotations.findIndex((a) => a.id === clickedId);
+ const nextIndex = (currentIndex + 1) % filteredAnnotations.length;
+ onSelectAnnotation(filteredAnnotations[nextIndex].id);
} else {
// First click or clicking different annotation
onSelectAnnotation(clickedId);
}
};
- return sorted.map((annotation) => {
- const containerWidth = containerSize.width;
- const containerHeight = containerSize.height;
- return (
-
onAnnotationPositionChange?.(id, position)}
- onSizeChange={(id, size) => onAnnotationSizeChange?.(id, size)}
- onClick={handleAnnotationClick}
- zIndex={annotation.zIndex}
- isSelectedBoost={annotation.id === selectedAnnotationId}
- />
- );
- });
+ const handleBlurClick = (clickedId: string) => {
+ if (!onSelectBlur) return;
+
+ if (clickedId === selectedBlurId && filteredBlurRegions.length > 1) {
+ const currentIndex = filteredBlurRegions.findIndex((a) => a.id === clickedId);
+ const nextIndex = (currentIndex + 1) % filteredBlurRegions.length;
+ onSelectBlur(filteredBlurRegions[nextIndex].id);
+ } else {
+ onSelectBlur(clickedId);
+ }
+ };
+
+ return sorted.map((item) => (
+ `${Math.round(p.x)}_${Math.round(p.y)}`).join("-")}`
+ : `${item.region.id}-${overlaySize.width}-${overlaySize.height}`
+ }
+ annotation={item.region}
+ isSelected={
+ item.kind === "blur"
+ ? item.region.id === selectedBlurId
+ : item.region.id === selectedAnnotationId
+ }
+ containerWidth={overlaySize.width}
+ containerHeight={overlaySize.height}
+ onPositionChange={(id, position) =>
+ item.kind === "blur"
+ ? onBlurPositionChange?.(id, position)
+ : onAnnotationPositionChange?.(id, position)
+ }
+ onSizeChange={(id, size) =>
+ item.kind === "blur"
+ ? onBlurSizeChange?.(id, size)
+ : onAnnotationSizeChange?.(id, size)
+ }
+ onBlurDataChange={
+ item.kind === "blur"
+ ? (id, blurData) => onBlurDataChange?.(id, blurData)
+ : undefined
+ }
+ onBlurDataCommit={item.kind === "blur" ? onBlurDataCommit : undefined}
+ onClick={item.kind === "blur" ? handleBlurClick : handleAnnotationClick}
+ zIndex={item.region.zIndex}
+ isSelectedBoost={
+ item.kind === "blur"
+ ? item.region.id === selectedBlurId
+ : item.region.id === selectedAnnotationId
+ }
+ previewSourceCanvas={previewSnapshotCanvas}
+ previewFrameVersion={Math.round(currentTime * 1000)}
+ />
+ ));
})()}
)}
diff --git a/src/components/video-editor/projectPersistence.test.ts b/src/components/video-editor/projectPersistence.test.ts
index fdf5f66..14dc240 100644
--- a/src/components/video-editor/projectPersistence.test.ts
+++ b/src/components/video-editor/projectPersistence.test.ts
@@ -44,6 +44,7 @@ describe("projectPersistence media compatibility", () => {
aspectRatio: "16:9",
webcamLayoutPreset: "picture-in-picture",
webcamMaskShape: "circle",
+ webcamPosition: null,
exportQuality: "good",
exportFormat: "mp4",
gifFrameRate: 15,
@@ -66,6 +67,99 @@ describe("projectPersistence media compatibility", () => {
normalizeProjectEditor({ webcamMaskShape: "not-a-real-shape" as never }).webcamMaskShape,
).toBe("rectangle");
});
+
+ it("normalizes blur region type and mosaic block size safely", () => {
+ const editor = normalizeProjectEditor({
+ annotationRegions: [
+ {
+ id: "annotation-1",
+ startMs: 0,
+ endMs: 500,
+ type: "blur",
+ content: "",
+ position: { x: 10, y: 10 },
+ size: { width: 20, height: 20 },
+ style: {
+ color: "#fff",
+ backgroundColor: "transparent",
+ fontSize: 32,
+ fontFamily: "Inter",
+ fontWeight: "bold",
+ fontStyle: "normal",
+ textDecoration: "none",
+ textAlign: "center",
+ },
+ zIndex: 1,
+ blurData: {
+ type: "mosaic",
+ shape: "rectangle",
+ color: "black",
+ intensity: 999,
+ blockSize: 999,
+ },
+ },
+ {
+ id: "annotation-2",
+ startMs: 0,
+ endMs: 500,
+ type: "blur",
+ content: "",
+ position: { x: 10, y: 10 },
+ size: { width: 20, height: 20 },
+ style: {
+ color: "#fff",
+ backgroundColor: "transparent",
+ fontSize: 32,
+ fontFamily: "Inter",
+ fontWeight: "bold",
+ fontStyle: "normal",
+ textDecoration: "none",
+ textAlign: "center",
+ },
+ zIndex: 2,
+ blurData: {
+ type: "invalid" as never,
+ shape: "rectangle",
+ color: "invalid" as never,
+ intensity: 10,
+ blockSize: 0,
+ },
+ },
+ ],
+ });
+
+ expect(editor.annotationRegions[0].blurData?.type).toBe("mosaic");
+ expect(editor.annotationRegions[0].blurData?.color).toBe("black");
+ expect(editor.annotationRegions[0].blurData?.intensity).toBe(40);
+ expect(editor.annotationRegions[0].blurData?.blockSize).toBe(48);
+ expect(editor.annotationRegions[1].blurData?.type).toBe("blur");
+ expect(editor.annotationRegions[1].blurData?.color).toBe("white");
+ expect(editor.annotationRegions[1].blurData?.blockSize).toBe(4);
+ });
+
+ it("accepts the dual frame webcam layout preset", () => {
+ expect(normalizeProjectEditor({ webcamLayoutPreset: "dual-frame" }).webcamLayoutPreset).toBe(
+ "dual-frame",
+ );
+ });
+
+ it("falls back from dual frame to picture in picture for portrait aspect ratios", () => {
+ expect(
+ normalizeProjectEditor({
+ aspectRatio: "9:16",
+ webcamLayoutPreset: "dual-frame",
+ }).webcamLayoutPreset,
+ ).toBe("picture-in-picture");
+ });
+
+ it("clears webcamPosition when the normalized preset is not picture in picture", () => {
+ expect(
+ normalizeProjectEditor({
+ webcamLayoutPreset: "dual-frame",
+ webcamPosition: { cx: 0.2, cy: 0.8 },
+ }).webcamPosition,
+ ).toBeNull();
+ });
});
it("creates stable snapshots for identical project state", () => {
diff --git a/src/components/video-editor/projectPersistence.ts b/src/components/video-editor/projectPersistence.ts
index cc8b600..c085e0d 100644
--- a/src/components/video-editor/projectPersistence.ts
+++ b/src/components/video-editor/projectPersistence.ts
@@ -1,29 +1,44 @@
+import { normalizeBlurColor, normalizeBlurType } from "@/lib/blurEffects";
import type { ExportFormat, ExportQuality, GifFrameRate, GifSizePreset } from "@/lib/exporter";
import type { ProjectMedia } from "@/lib/recordingSession";
import { normalizeProjectMedia } from "@/lib/recordingSession";
-import { ASPECT_RATIOS, type AspectRatio } from "@/utils/aspectRatioUtils";
+import { ASPECT_RATIOS, type AspectRatio, isPortraitAspectRatio } from "@/utils/aspectRatioUtils";
import {
type AnnotationRegion,
type CropRegion,
+ clampPlaybackSpeed,
DEFAULT_ANNOTATION_POSITION,
DEFAULT_ANNOTATION_SIZE,
DEFAULT_ANNOTATION_STYLE,
+ DEFAULT_BLUR_BLOCK_SIZE,
+ DEFAULT_BLUR_DATA,
+ DEFAULT_BLUR_FREEHAND_POINTS,
+ DEFAULT_BLUR_INTENSITY,
DEFAULT_CROP_REGION,
DEFAULT_FIGURE_DATA,
DEFAULT_PLAYBACK_SPEED,
DEFAULT_WEBCAM_LAYOUT_PRESET,
DEFAULT_WEBCAM_MASK_SHAPE,
DEFAULT_WEBCAM_POSITION,
+ DEFAULT_WEBCAM_SIZE_PRESET,
DEFAULT_ZOOM_DEPTH,
+ MAX_BLUR_BLOCK_SIZE,
+ MAX_BLUR_INTENSITY,
+ MAX_PLAYBACK_SPEED,
+ MIN_BLUR_BLOCK_SIZE,
+ MIN_BLUR_INTENSITY,
+ MIN_PLAYBACK_SPEED,
type SpeedRegion,
type TrimRegion,
type WebcamLayoutPreset,
type WebcamMaskShape,
type WebcamPosition,
+ type WebcamSizePreset,
type ZoomRegion,
} from "./types";
const WALLPAPER_COUNT = 18;
+const VALID_BLUR_SHAPES = new Set(["rectangle", "oval", "freehand"] as const);
export const WALLPAPER_PATHS = Array.from(
{ length: WALLPAPER_COUNT },
@@ -47,6 +62,7 @@ export interface ProjectEditorState {
aspectRatio: AspectRatio;
webcamLayoutPreset: WebcamLayoutPreset;
webcamMaskShape: WebcamMaskShape;
+ webcamSizePreset: WebcamSizePreset;
webcamPosition: WebcamPosition | null;
exportQuality: ExportQuality;
exportFormat: ExportFormat;
@@ -66,6 +82,26 @@ function isFiniteNumber(value: unknown): value is number {
return typeof value === "number" && Number.isFinite(value);
}
+function computeNormalizedWebcamLayoutPreset(
+ webcamLayoutPreset: Partial["webcamLayoutPreset"],
+ normalizedAspectRatio: AspectRatio,
+): WebcamLayoutPreset {
+ switch (webcamLayoutPreset) {
+ case "picture-in-picture":
+ return webcamLayoutPreset;
+ case "vertical-stack":
+ return isPortraitAspectRatio(normalizedAspectRatio)
+ ? webcamLayoutPreset
+ : DEFAULT_WEBCAM_LAYOUT_PRESET;
+ case "dual-frame":
+ return isPortraitAspectRatio(normalizedAspectRatio)
+ ? DEFAULT_WEBCAM_LAYOUT_PRESET
+ : webcamLayoutPreset;
+ default:
+ return DEFAULT_WEBCAM_LAYOUT_PRESET;
+ }
+}
+
function clamp(value: number, min: number, max: number) {
return Math.min(max, Math.max(min, value));
}
@@ -173,6 +209,26 @@ export function resolveProjectMedia(
export function normalizeProjectEditor(editor: Partial): ProjectEditorState {
const validAspectRatios = new Set(ASPECT_RATIOS);
+ const normalizedAspectRatio: AspectRatio = validAspectRatios.has(
+ editor.aspectRatio as AspectRatio,
+ )
+ ? (editor.aspectRatio as AspectRatio)
+ : "16:9";
+ const normalizedWebcamLayoutPreset = computeNormalizedWebcamLayoutPreset(
+ editor.webcamLayoutPreset,
+ normalizedAspectRatio,
+ );
+ const normalizedWebcamPosition: WebcamPosition | null =
+ normalizedWebcamLayoutPreset === "picture-in-picture" &&
+ editor.webcamPosition &&
+ typeof editor.webcamPosition === "object" &&
+ isFiniteNumber((editor.webcamPosition as WebcamPosition).cx) &&
+ isFiniteNumber((editor.webcamPosition as WebcamPosition).cy)
+ ? {
+ cx: clamp((editor.webcamPosition as WebcamPosition).cx, 0, 1),
+ cy: clamp((editor.webcamPosition as WebcamPosition).cy, 0, 1),
+ }
+ : DEFAULT_WEBCAM_POSITION;
const normalizedZoomRegions: ZoomRegion[] = Array.isArray(editor.zoomRegions)
? editor.zoomRegions
@@ -223,14 +279,10 @@ export function normalizeProjectEditor(editor: Partial): Pro
const endMs = Math.max(startMs + 1, rawEnd);
const speed =
- region.speed === 0.25 ||
- region.speed === 0.5 ||
- region.speed === 0.75 ||
- region.speed === 1.25 ||
- region.speed === 1.5 ||
- region.speed === 1.75 ||
- region.speed === 2
- ? region.speed
+ isFiniteNumber(region.speed) &&
+ region.speed >= MIN_PLAYBACK_SPEED &&
+ region.speed <= MAX_PLAYBACK_SPEED
+ ? clampPlaybackSpeed(region.speed)
: DEFAULT_PLAYBACK_SPEED;
return {
@@ -252,12 +304,22 @@ export function normalizeProjectEditor(editor: Partial): Pro
const rawEnd = isFiniteNumber(region.endMs) ? Math.round(region.endMs) : rawStart + 1000;
const startMs = Math.max(0, Math.min(rawStart, rawEnd));
const endMs = Math.max(startMs + 1, rawEnd);
+ const blurShape =
+ typeof region.blurData?.shape === "string" &&
+ VALID_BLUR_SHAPES.has(region.blurData.shape)
+ ? region.blurData.shape
+ : DEFAULT_BLUR_DATA.shape;
+ const blurType = normalizeBlurType(region.blurData?.type);
+ const blurColor = normalizeBlurColor(region.blurData?.color);
return {
id: region.id,
startMs,
endMs,
- type: region.type === "image" || region.type === "figure" ? region.type : "text",
+ type:
+ region.type === "image" || region.type === "figure" || region.type === "blur"
+ ? region.type
+ : "text",
content: typeof region.content === "string" ? region.content : "",
textContent: typeof region.textContent === "string" ? region.textContent : undefined,
imageContent: typeof region.imageContent === "string" ? region.imageContent : undefined,
@@ -304,6 +366,42 @@ export function normalizeProjectEditor(editor: Partial): Pro
...region.figureData,
}
: undefined,
+ blurData:
+ region.blurData && typeof region.blurData === "object"
+ ? {
+ ...DEFAULT_BLUR_DATA,
+ ...region.blurData,
+ type: blurType,
+ shape: blurShape,
+ color: blurColor,
+ intensity: isFiniteNumber(region.blurData.intensity)
+ ? clamp(region.blurData.intensity, MIN_BLUR_INTENSITY, MAX_BLUR_INTENSITY)
+ : DEFAULT_BLUR_INTENSITY,
+ blockSize: isFiniteNumber(region.blurData.blockSize)
+ ? clamp(region.blurData.blockSize, MIN_BLUR_BLOCK_SIZE, MAX_BLUR_BLOCK_SIZE)
+ : DEFAULT_BLUR_BLOCK_SIZE,
+ freehandPoints: Array.isArray(region.blurData.freehandPoints)
+ ? region.blurData.freehandPoints
+ .filter(
+ (
+ point,
+ ): point is {
+ x: number;
+ y: number;
+ } =>
+ Boolean(
+ point &&
+ isFiniteNumber((point as { x?: unknown }).x) &&
+ isFiniteNumber((point as { y?: unknown }).y),
+ ),
+ )
+ .map((point) => ({
+ x: clamp(point.x, 0, 100),
+ y: clamp(point.y, 0, 100),
+ }))
+ : DEFAULT_BLUR_FREEHAND_POINTS,
+ }
+ : undefined,
};
})
: [];
@@ -349,13 +447,8 @@ export function normalizeProjectEditor(editor: Partial): Pro
trimRegions: normalizedTrimRegions,
speedRegions: normalizedSpeedRegions,
annotationRegions: normalizedAnnotationRegions,
- aspectRatio:
- editor.aspectRatio && validAspectRatios.has(editor.aspectRatio) ? editor.aspectRatio : "16:9",
- webcamLayoutPreset:
- editor.webcamLayoutPreset === "vertical-stack" ||
- editor.webcamLayoutPreset === "picture-in-picture"
- ? editor.webcamLayoutPreset
- : DEFAULT_WEBCAM_LAYOUT_PRESET,
+ aspectRatio: normalizedAspectRatio,
+ webcamLayoutPreset: normalizedWebcamLayoutPreset,
webcamMaskShape:
editor.webcamMaskShape === "rectangle" ||
editor.webcamMaskShape === "circle" ||
@@ -363,16 +456,11 @@ export function normalizeProjectEditor(editor: Partial): Pro
editor.webcamMaskShape === "rounded"
? editor.webcamMaskShape
: DEFAULT_WEBCAM_MASK_SHAPE,
- webcamPosition:
- editor.webcamPosition &&
- typeof editor.webcamPosition === "object" &&
- isFiniteNumber((editor.webcamPosition as WebcamPosition).cx) &&
- isFiniteNumber((editor.webcamPosition as WebcamPosition).cy)
- ? {
- cx: clamp((editor.webcamPosition as WebcamPosition).cx, 0, 1),
- cy: clamp((editor.webcamPosition as WebcamPosition).cy, 0, 1),
- }
- : DEFAULT_WEBCAM_POSITION,
+ webcamSizePreset:
+ typeof editor.webcamSizePreset === "number" && isFiniteNumber(editor.webcamSizePreset)
+ ? Math.max(10, Math.min(50, editor.webcamSizePreset))
+ : DEFAULT_WEBCAM_SIZE_PRESET,
+ webcamPosition: normalizedWebcamPosition,
exportQuality:
editor.exportQuality === "medium" || editor.exportQuality === "source"
? editor.exportQuality
diff --git a/src/components/video-editor/timeline/Item.tsx b/src/components/video-editor/timeline/Item.tsx
index f265fe4..d89de94 100644
--- a/src/components/video-editor/timeline/Item.tsx
+++ b/src/components/video-editor/timeline/Item.tsx
@@ -1,8 +1,13 @@
import type { Span } from "dnd-timeline";
-import { useItem } from "dnd-timeline";
+import { useItem, useTimelineContext } from "dnd-timeline";
import { Gauge, MessageSquare, Scissors, ZoomIn } from "lucide-react";
import { useMemo } from "react";
import { cn } from "@/lib/utils";
+import {
+ DEFAULT_ZOOM_IN_MS,
+ DEFAULT_ZOOM_OUT_MS,
+ getDurations,
+} from "../videoPlayback/zoomRegionUtils";
import glassStyles from "./ItemGlass.module.css";
interface ItemProps {
@@ -13,8 +18,11 @@ interface ItemProps {
isSelected?: boolean;
onSelect?: () => void;
zoomDepth?: number;
+ zoomInDurationMs?: number;
+ zoomOutDurationMs?: number;
speedValue?: number;
- variant?: "zoom" | "trim" | "annotation" | "speed";
+ onZoomDurationChange?: (id: string, zoomIn: number, zoomOut: number) => void;
+ variant?: "zoom" | "trim" | "annotation" | "speed" | "blur";
}
// Map zoom depth to multiplier labels
@@ -44,10 +52,14 @@ export default function Item({
isSelected = false,
onSelect,
zoomDepth = 1,
+ zoomInDurationMs,
+ zoomOutDurationMs,
speedValue,
variant = "zoom",
children,
+ onZoomDurationChange,
}: ItemProps) {
+ const { pixelsToValue } = useTimelineContext();
const { setNodeRef, attributes, listeners, itemStyle, itemContentStyle } = useItem({
id,
span,
@@ -79,6 +91,16 @@ export default function Item({
const MIN_ITEM_PX = 6;
const safeItemStyle = { ...itemStyle, minWidth: MIN_ITEM_PX };
+ const { zoomIn, zoomOut } = useMemo(() => {
+ if (!isZoom) return { zoomIn: 0, zoomOut: 0 };
+ return getDurations({
+ startMs: span.start,
+ endMs: span.end,
+ zoomInDurationMs,
+ zoomOutDurationMs,
+ });
+ }, [isZoom, span.start, span.end, zoomInDurationMs, zoomOutDurationMs]);
+
return (
+ {isZoom && (
+ <>
+ {/* Transition In Marker */}
+
+ {/* Draggable handle for Transition In */}
+
{
+ e.stopPropagation();
+ e.preventDefault();
+ const target = e.currentTarget;
+ target.setPointerCapture(e.pointerId);
+
+ const startX = e.clientX;
+ const initialZoomIn = zoomInDurationMs ?? DEFAULT_ZOOM_IN_MS;
+ const initialZoomOut = zoomOutDurationMs ?? DEFAULT_ZOOM_OUT_MS;
+
+ const onPointerMove = (moveEvent: PointerEvent) => {
+ const deltaPx = moveEvent.clientX - startX;
+ const deltaMs = pixelsToValue(deltaPx);
+ const newDuration = Math.max(
+ 0,
+ Math.min(initialZoomIn + deltaMs, span.end - span.start - initialZoomOut),
+ );
+ onZoomDurationChange?.(id, newDuration, initialZoomOut);
+ };
+
+ const onPointerUp = () => {
+ target.releasePointerCapture(e.pointerId);
+ window.removeEventListener("pointermove", onPointerMove);
+ window.removeEventListener("pointerup", onPointerUp);
+ };
+
+ window.addEventListener("pointermove", onPointerMove);
+ window.addEventListener("pointerup", onPointerUp);
+ }}
+ />
+ {/* Transition Out Marker */}
+
+ {/* Draggable handle for Transition Out */}
+
{
+ e.stopPropagation();
+ e.preventDefault();
+ const target = e.currentTarget;
+ target.setPointerCapture(e.pointerId);
+
+ const startX = e.clientX;
+ const initialZoomIn = zoomInDurationMs ?? DEFAULT_ZOOM_IN_MS;
+ const initialZoomOut = zoomOutDurationMs ?? DEFAULT_ZOOM_OUT_MS;
+
+ const onPointerMove = (moveEvent: PointerEvent) => {
+ const deltaPx = startX - moveEvent.clientX; // Inverted because right-anchored
+ const deltaMs = pixelsToValue(deltaPx);
+ const newDuration = Math.max(
+ 0,
+ Math.min(initialZoomOut + deltaMs, span.end - span.start - initialZoomIn),
+ );
+ onZoomDurationChange?.(id, initialZoomIn, newDuration);
+ };
+
+ const onPointerUp = () => {
+ target.releasePointerCapture(e.pointerId);
+ window.removeEventListener("pointermove", onPointerMove);
+ window.removeEventListener("pointerup", onPointerUp);
+ };
+
+ window.addEventListener("pointermove", onPointerMove);
+ window.addEventListener("pointerup", onPointerUp);
+ }}
+ />
+ >
+ )}
void;
onZoomSuggested?: (span: Span, focus: ZoomFocus) => void;
onZoomSpanChange: (id: string, span: Span) => void;
+ onZoomDurationChange: (id: string, zoomIn: number, zoomOut: number) => void;
onZoomDelete: (id: string) => void;
selectedZoomId: string | null;
onSelectZoom: (id: string | null) => void;
@@ -73,6 +75,12 @@ interface TimelineEditorProps {
onAnnotationDelete?: (id: string) => void;
selectedAnnotationId?: string | null;
onSelectAnnotation?: (id: string | null) => void;
+ blurRegions?: AnnotationRegion[];
+ onBlurAdded?: (span: Span) => void;
+ onBlurSpanChange?: (id: string, span: Span) => void;
+ onBlurDelete?: (id: string) => void;
+ selectedBlurId?: string | null;
+ onSelectBlur?: (id: string | null) => void;
speedRegions?: SpeedRegion[];
onSpeedAdded?: (span: Span) => void;
onSpeedSpanChange?: (id: string, span: Span) => void;
@@ -96,7 +104,9 @@ interface TimelineRenderItem {
label: string;
zoomDepth?: number;
speedValue?: number;
- variant: "zoom" | "trim" | "annotation" | "speed";
+ zoomInDurationMs?: number;
+ zoomOutDurationMs?: number;
+ variant: "zoom" | "trim" | "annotation" | "speed" | "blur";
}
const SCALE_CANDIDATES = [
@@ -525,11 +535,14 @@ function Timeline({
onSelectZoom,
onSelectTrim,
onSelectAnnotation,
+ onSelectBlur,
onSelectSpeed,
selectedZoomId,
selectedTrimId,
selectedAnnotationId,
+ selectedBlurId,
selectedSpeedId,
+ onZoomDurationChange,
keyframes = [],
}: {
items: TimelineRenderItem[];
@@ -540,11 +553,14 @@ function Timeline({
onSelectZoom?: (id: string | null) => void;
onSelectTrim?: (id: string | null) => void;
onSelectAnnotation?: (id: string | null) => void;
+ onSelectBlur?: (id: string | null) => void;
onSelectSpeed?: (id: string | null) => void;
selectedZoomId: string | null;
selectedTrimId?: string | null;
selectedAnnotationId?: string | null;
+ selectedBlurId?: string | null;
selectedSpeedId?: string | null;
+ onZoomDurationChange: (id: string, zoomIn: number, zoomOut: number) => void;
keyframes?: { id: string; time: number }[];
}) {
const t = useScopedT("timeline");
@@ -568,6 +584,7 @@ function Timeline({
onSelectZoom?.(null);
onSelectTrim?.(null);
onSelectAnnotation?.(null);
+ onSelectBlur?.(null);
onSelectSpeed?.(null);
const rect = e.currentTarget.getBoundingClientRect();
@@ -586,6 +603,7 @@ function Timeline({
onSelectZoom,
onSelectTrim,
onSelectAnnotation,
+ onSelectBlur,
onSelectSpeed,
videoDurationMs,
sidebarWidth,
@@ -637,6 +655,7 @@ function Timeline({
const zoomItems = items.filter((item) => item.rowId === ZOOM_ROW_ID);
const trimItems = items.filter((item) => item.rowId === TRIM_ROW_ID);
const annotationItems = items.filter((item) => item.rowId === ANNOTATION_ROW_ID);
+ const blurItems = items.filter((item) => item.rowId === BLUR_ROW_ID);
const speedItems = items.filter((item) => item.rowId === SPEED_ROW_ID);
return (
@@ -668,6 +687,9 @@ function Timeline({
isSelected={item.id === selectedZoomId}
onSelect={() => onSelectZoom?.(item.id)}
zoomDepth={item.zoomDepth}
+ zoomInDurationMs={item.zoomInDurationMs}
+ zoomOutDurationMs={item.zoomOutDurationMs}
+ onZoomDurationChange={onZoomDurationChange}
variant="zoom"
>
{item.label}
@@ -711,6 +733,22 @@ function Timeline({
))}
+
+ {blurItems.map((item) => (
+ - onSelectBlur?.(item.id)}
+ variant={item.variant}
+ >
+ {item.label}
+
+ ))}
+
+
{speedItems.map((item) => (
- {
+ if (!selectedBlurId || !onBlurDelete || !onSelectBlur) return;
+ onBlurDelete(selectedBlurId);
+ onSelectBlur(null);
+ }, [selectedBlurId, onBlurDelete, onSelectBlur]);
+
const deleteSelectedSpeed = useCallback(() => {
if (!selectedSpeedId || !onSpeedDelete || !onSelectSpeed) return;
onSpeedDelete(selectedSpeedId);
@@ -908,9 +959,10 @@ export default function TimelineEditor({
const isZoomItem = zoomRegions.some((r) => r.id === excludeId);
const isTrimItem = trimRegions.some((r) => r.id === excludeId);
const isAnnotationItem = annotationRegions.some((r) => r.id === excludeId);
+ const isBlurItem = blurRegions.some((r) => r.id === excludeId);
const isSpeedItem = speedRegions.some((r) => r.id === excludeId);
- if (isAnnotationItem) {
+ if (isAnnotationItem || isBlurItem) {
return false;
}
@@ -937,7 +989,7 @@ export default function TimelineEditor({
return false;
},
- [zoomRegions, trimRegions, annotationRegions, speedRegions],
+ [zoomRegions, trimRegions, annotationRegions, blurRegions, speedRegions],
);
// At least 5% of the timeline or 1000ms, whichever is larger, so the region
@@ -1165,6 +1217,21 @@ export default function TimelineEditor({
onAnnotationAdded({ start: startPos, end: endPos });
}, [videoDuration, totalMs, currentTimeMs, onAnnotationAdded, defaultRegionDurationMs]);
+ const handleAddBlur = useCallback(() => {
+ if (!videoDuration || videoDuration === 0 || totalMs === 0 || !onBlurAdded) {
+ return;
+ }
+
+ const defaultDuration = Math.min(defaultRegionDurationMs, totalMs);
+ if (defaultDuration <= 0) {
+ return;
+ }
+
+ const startPos = Math.max(0, Math.min(currentTimeMs, totalMs));
+ const endPos = Math.min(startPos + defaultDuration, totalMs);
+ onBlurAdded({ start: startPos, end: endPos });
+ }, [videoDuration, totalMs, currentTimeMs, onBlurAdded, defaultRegionDurationMs]);
+
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) {
@@ -1183,6 +1250,9 @@ export default function TimelineEditor({
if (matchesShortcut(e, keyShortcuts.addAnnotation, isMac)) {
handleAddAnnotation();
}
+ if (matchesShortcut(e, keyShortcuts.addBlur, isMac)) {
+ handleAddBlur();
+ }
if (matchesShortcut(e, keyShortcuts.addSpeed, isMac)) {
handleAddSpeed();
}
@@ -1223,6 +1293,8 @@ export default function TimelineEditor({
deleteSelectedTrim();
} else if (selectedAnnotationId) {
deleteSelectedAnnotation();
+ } else if (selectedBlurId) {
+ deleteSelectedBlur();
} else if (selectedSpeedId) {
deleteSelectedSpeed();
}
@@ -1235,16 +1307,19 @@ export default function TimelineEditor({
handleAddZoom,
handleAddTrim,
handleAddAnnotation,
+ handleAddBlur,
handleAddSpeed,
deleteSelectedKeyframe,
deleteSelectedZoom,
deleteSelectedTrim,
deleteSelectedAnnotation,
+ deleteSelectedBlur,
deleteSelectedSpeed,
selectedKeyframeId,
selectedZoomId,
selectedTrimId,
selectedAnnotationId,
+ selectedBlurId,
selectedSpeedId,
annotationRegions,
currentTime,
@@ -1271,6 +1346,8 @@ export default function TimelineEditor({
span: { start: region.startMs, end: region.endMs },
label: t("labels.zoomItem", { index: String(index + 1) }),
zoomDepth: region.depth,
+ zoomInDurationMs: region.zoomInDurationMs,
+ zoomOutDurationMs: region.zoomOutDurationMs,
variant: "zoom",
}));
@@ -1304,6 +1381,14 @@ export default function TimelineEditor({
};
});
+ const blurs: TimelineRenderItem[] = blurRegions.map((region, index) => ({
+ id: region.id,
+ rowId: BLUR_ROW_ID,
+ span: { start: region.startMs, end: region.endMs },
+ label: t("labels.blurItem", { index: String(index + 1) }),
+ variant: "blur",
+ }));
+
const speeds: TimelineRenderItem[] = speedRegions.map((region, index) => ({
id: region.id,
rowId: SPEED_ROW_ID,
@@ -1313,8 +1398,8 @@ export default function TimelineEditor({
variant: "speed",
}));
- return [...zooms, ...trims, ...annotations, ...speeds];
- }, [zoomRegions, trimRegions, annotationRegions, speedRegions, t]);
+ return [...zooms, ...trims, ...annotations, ...blurs, ...speeds];
+ }, [zoomRegions, trimRegions, annotationRegions, blurRegions, speedRegions, t]);
// Flat list of all non-annotation region spans for neighbour-clamping during drag/resize
const allRegionSpans = useMemo(() => {
@@ -1335,6 +1420,8 @@ export default function TimelineEditor({
onSpeedSpanChange?.(id, span);
} else if (annotationRegions.some((r) => r.id === id)) {
onAnnotationSpanChange?.(id, span);
+ } else if (blurRegions.some((r) => r.id === id)) {
+ onBlurSpanChange?.(id, span);
}
},
[
@@ -1342,10 +1429,12 @@ export default function TimelineEditor({
trimRegions,
speedRegions,
annotationRegions,
+ blurRegions,
onZoomSpanChange,
onTrimSpanChange,
onSpeedSpanChange,
onAnnotationSpanChange,
+ onBlurSpanChange,
],
);
@@ -1403,6 +1492,25 @@ export default function TimelineEditor({
>
+
diff --git a/src/components/video-editor/types.ts b/src/components/video-editor/types.ts
index de06ba1..87e4331 100644
--- a/src/components/video-editor/types.ts
+++ b/src/components/video-editor/types.ts
@@ -3,6 +3,10 @@ import type { WebcamLayoutPreset } from "@/lib/compositeLayout";
export type ZoomDepth = 1 | 2 | 3 | 4 | 5 | 6;
export type ZoomFocusMode = "manual" | "auto";
export type { WebcamLayoutPreset };
+/** Webcam size as a percentage of the canvas reference dimension (10–50). */
+export type WebcamSizePreset = number;
+
+export const DEFAULT_WEBCAM_SIZE_PRESET: WebcamSizePreset = 25;
export const DEFAULT_WEBCAM_LAYOUT_PRESET: WebcamLayoutPreset = "picture-in-picture";
@@ -29,6 +33,8 @@ export interface ZoomRegion {
depth: ZoomDepth;
focus: ZoomFocus;
focusMode?: ZoomFocusMode;
+ zoomInDurationMs?: number;
+ zoomOutDurationMs?: number;
}
export interface CursorTelemetryPoint {
@@ -43,7 +49,7 @@ export interface TrimRegion {
endMs: number;
}
-export type AnnotationType = "text" | "image" | "figure";
+export type AnnotationType = "text" | "image" | "figure" | "blur";
export type ArrowDirection =
| "up"
@@ -61,6 +67,27 @@ export interface FigureData {
strokeWidth: number;
}
+export type BlurShape = "rectangle" | "oval" | "freehand";
+export type BlurType = "blur" | "mosaic";
+export type BlurColor = "white" | "black";
+
+export const MIN_BLUR_INTENSITY = 2;
+export const MAX_BLUR_INTENSITY = 40;
+export const DEFAULT_BLUR_INTENSITY = 12;
+export const MIN_BLUR_BLOCK_SIZE = 4;
+export const MAX_BLUR_BLOCK_SIZE = 48;
+export const DEFAULT_BLUR_BLOCK_SIZE = 12;
+
+export interface BlurData {
+ type: BlurType;
+ shape: BlurShape;
+ color: BlurColor;
+ intensity: number;
+ blockSize: number;
+ // Points are normalized (0-100) within the annotation bounds.
+ freehandPoints?: Array<{ x: number; y: number }>;
+}
+
export interface AnnotationPosition {
x: number;
y: number;
@@ -95,6 +122,7 @@ export interface AnnotationRegion {
style: AnnotationTextStyle;
zIndex: number;
figureData?: FigureData;
+ blurData?: BlurData;
}
export const DEFAULT_ANNOTATION_POSITION: AnnotationPosition = {
@@ -124,6 +152,27 @@ export const DEFAULT_FIGURE_DATA: FigureData = {
strokeWidth: 4,
};
+export const DEFAULT_BLUR_FREEHAND_POINTS: Array<{ x: number; y: number }> = [
+ { x: 10, y: 30 },
+ { x: 25, y: 10 },
+ { x: 55, y: 8 },
+ { x: 82, y: 20 },
+ { x: 90, y: 45 },
+ { x: 78, y: 72 },
+ { x: 52, y: 90 },
+ { x: 22, y: 84 },
+ { x: 8, y: 58 },
+];
+
+export const DEFAULT_BLUR_DATA: BlurData = {
+ type: "blur",
+ shape: "rectangle",
+ color: "white",
+ intensity: DEFAULT_BLUR_INTENSITY,
+ blockSize: DEFAULT_BLUR_BLOCK_SIZE,
+ freehandPoints: DEFAULT_BLUR_FREEHAND_POINTS,
+};
+
export interface CropRegion {
x: number;
y: number;
@@ -138,7 +187,16 @@ export const DEFAULT_CROP_REGION: CropRegion = {
height: 1,
};
-export type PlaybackSpeed = 0.25 | 0.5 | 0.75 | 1.25 | 1.5 | 1.75 | 2;
+export type PlaybackSpeed = number;
+
+export const MIN_PLAYBACK_SPEED = 0.1;
+// Anything above 16x causes the playhead to stall during preview
+// due to the video decoder not being able to keep up.
+export const MAX_PLAYBACK_SPEED = 16;
+
+export function clampPlaybackSpeed(speed: number): PlaybackSpeed {
+ return Math.round(Math.min(MAX_PLAYBACK_SPEED, Math.max(MIN_PLAYBACK_SPEED, speed)) * 100) / 100;
+}
export interface SpeedRegion {
id: string;
@@ -155,6 +213,9 @@ export const SPEED_OPTIONS: Array<{ speed: PlaybackSpeed; label: string }> = [
{ speed: 1.5, label: "1.5×" },
{ speed: 1.75, label: "1.75×" },
{ speed: 2, label: "2×" },
+ { speed: 3, label: "3×" },
+ { speed: 4, label: "4×" },
+ { speed: 5, label: "5×" },
];
export const DEFAULT_PLAYBACK_SPEED: PlaybackSpeed = 1.5;
diff --git a/src/components/video-editor/videoPlayback/layoutUtils.ts b/src/components/video-editor/videoPlayback/layoutUtils.ts
index 2444c39..4b713cf 100644
--- a/src/components/video-editor/videoPlayback/layoutUtils.ts
+++ b/src/components/video-editor/videoPlayback/layoutUtils.ts
@@ -5,6 +5,7 @@ import {
type Size,
type StyledRenderRect,
type WebcamLayoutPreset,
+ type WebcamSizePreset,
} from "@/lib/compositeLayout";
import type { CropRegion, WebcamMaskShape } from "../types";
@@ -20,6 +21,7 @@ interface LayoutParams {
padding?: number;
webcamDimensions?: Size | null;
webcamLayoutPreset?: WebcamLayoutPreset;
+ webcamSizePreset?: WebcamSizePreset;
webcamPosition?: { cx: number; cy: number } | null;
webcamMaskShape?: WebcamMaskShape;
}
@@ -47,6 +49,7 @@ export function layoutVideoContent(params: LayoutParams): LayoutResult | null {
padding = 0,
webcamDimensions,
webcamLayoutPreset,
+ webcamSizePreset,
webcamPosition,
webcamMaskShape,
} = params;
@@ -95,6 +98,7 @@ export function layoutVideoContent(params: LayoutParams): LayoutResult | null {
screenSize: { width: croppedVideoWidth, height: croppedVideoHeight },
webcamSize: webcamDimensions,
layoutPreset: webcamLayoutPreset,
+ webcamSizePreset,
webcamPosition,
webcamMaskShape,
});
@@ -136,7 +140,7 @@ export function layoutVideoContent(params: LayoutParams): LayoutResult | null {
screenRect.y,
screenRect.width,
screenRect.height,
- compositeLayout.screenCover ? 0 : borderRadius,
+ compositeLayout.screenBorderRadius ?? (compositeLayout.screenCover ? 0 : borderRadius),
);
maskGraphics.fill({ color: 0xffffff });
diff --git a/src/components/video-editor/videoPlayback/zoomRegionUtils.ts b/src/components/video-editor/videoPlayback/zoomRegionUtils.ts
index e5c16e1..88d08c1 100644
--- a/src/components/video-editor/videoPlayback/zoomRegionUtils.ts
+++ b/src/components/video-editor/videoPlayback/zoomRegionUtils.ts
@@ -7,7 +7,6 @@ import { clamp01, cubicBezier, easeOutScreenStudio } from "./mathUtils";
const CHAINED_ZOOM_PAN_GAP_MS = 1500;
const CONNECTED_ZOOM_PAN_DURATION_MS = 1000;
-const ZOOM_IN_OVERLAP_MS = 500;
type DominantRegionOptions = {
connectZooms?: boolean;
@@ -38,26 +37,49 @@ function easeConnectedPan(value: number) {
return cubicBezier(0.1, 0.0, 0.2, 1.0, value);
}
-export function computeRegionStrength(region: ZoomRegion, timeMs: number) {
- const zoomInEnd = region.startMs + ZOOM_IN_OVERLAP_MS;
- const leadInStart = zoomInEnd - ZOOM_IN_TRANSITION_WINDOW_MS;
- const leadOutEnd = region.endMs + TRANSITION_WINDOW_MS;
+export const DEFAULT_ZOOM_OUT_MS = TRANSITION_WINDOW_MS;
+export const DEFAULT_ZOOM_IN_MS = ZOOM_IN_TRANSITION_WINDOW_MS;
- if (timeMs < leadInStart || timeMs > leadOutEnd) {
+export function getDurations(region: {
+ startMs: number;
+ endMs: number;
+ zoomInDurationMs?: number;
+ zoomOutDurationMs?: number;
+}) {
+ let zoomIn = region.zoomInDurationMs ?? DEFAULT_ZOOM_IN_MS;
+ let zoomOut = region.zoomOutDurationMs ?? DEFAULT_ZOOM_OUT_MS;
+
+ const duration = region.endMs - region.startMs;
+ if (zoomIn + zoomOut > duration) {
+ const scale = duration / (zoomIn + zoomOut);
+ zoomIn *= scale;
+ zoomOut *= scale;
+ }
+
+ return { zoomIn, zoomOut };
+}
+
+export function computeRegionStrength(region: ZoomRegion, timeMs: number) {
+ const { zoomIn, zoomOut } = getDurations(region);
+
+ if (timeMs < region.startMs || timeMs > region.endMs) {
return 0;
}
- if (timeMs < zoomInEnd) {
- const progress = (timeMs - leadInStart) / ZOOM_IN_TRANSITION_WINDOW_MS;
+ // Zooming in
+ if (timeMs < region.startMs + zoomIn) {
+ const progress = Math.max(0, Math.min(1, (timeMs - region.startMs) / zoomIn));
return easeOutScreenStudio(progress);
}
- if (timeMs <= region.endMs) {
- return 1;
+ // Zooming out
+ if (timeMs > region.endMs - zoomOut) {
+ const progress = Math.max(0, Math.min(1, (region.endMs - timeMs) / zoomOut));
+ return easeOutScreenStudio(progress);
}
- const progress = clamp01((timeMs - region.endMs) / TRANSITION_WINDOW_MS);
- return 1 - easeOutScreenStudio(progress);
+ // Full zoom
+ return 1;
}
function getLinearFocus(start: ZoomFocus, end: ZoomFocus, amount: number): ZoomFocus {
diff --git a/src/components/video-editor/videoPlayback/zoomTransform.ts b/src/components/video-editor/videoPlayback/zoomTransform.ts
index 61ced66..800949f 100644
--- a/src/components/video-editor/videoPlayback/zoomTransform.ts
+++ b/src/components/video-editor/videoPlayback/zoomTransform.ts
@@ -90,8 +90,10 @@ export function computeZoomTransform({
}
const progress = Math.min(1, Math.max(0, zoomProgress));
- const focusStagePxX = baseMask.x + focusX * baseMask.width;
- const focusStagePxY = baseMask.y + focusY * baseMask.height;
+ // Focus coordinates are stage-normalized (0-1 of full canvas),
+ // so map directly to stage pixels, not through baseMask.
+ const focusStagePxX = focusX * stageSize.width;
+ const focusStagePxY = focusY * stageSize.height;
const stageCenterX = stageSize.width / 2;
const stageCenterY = stageSize.height / 2;
const scale = 1 + (zoomScale - 1) * progress;
@@ -128,8 +130,8 @@ export function computeFocusFromTransform({
const focusStagePxY = (stageCenterY - y) / zoomScale;
return {
- cx: (focusStagePxX - baseMask.x) / baseMask.width,
- cy: (focusStagePxY - baseMask.y) / baseMask.height,
+ cx: focusStagePxX / stageSize.width,
+ cy: focusStagePxY / stageSize.height,
};
}
diff --git a/src/contexts/I18nContext.tsx b/src/contexts/I18nContext.tsx
index 0b75212..1056749 100644
--- a/src/contexts/I18nContext.tsx
+++ b/src/contexts/I18nContext.tsx
@@ -5,16 +5,11 @@ import {
useContext,
useEffect,
useMemo,
+ useRef,
useState,
} from "react";
-import {
- DEFAULT_LOCALE,
- type I18nNamespace,
- LOCALE_STORAGE_KEY,
- type Locale,
- SUPPORTED_LOCALES,
-} from "@/i18n/config";
-import { translate } from "@/i18n/loader";
+import { DEFAULT_LOCALE, type I18nNamespace, LOCALE_STORAGE_KEY, type Locale } from "@/i18n/config";
+import { getAvailableLocales, translate } from "@/i18n/loader";
type TranslateVars = Record;
@@ -22,8 +17,14 @@ interface I18nContextValue {
locale: Locale;
setLocale: (locale: Locale) => void;
t: (qualifiedKey: string, vars?: TranslateVars) => string;
+ systemLocaleSuggestion: Locale | null;
+ acceptSystemLocaleSuggestion: () => void;
+ dismissSystemLocaleSuggestion: () => void;
+ resolveSystemLocaleSuggestion: () => void;
}
+const SYSTEM_LANGUAGE_PROMPT_SEEN_KEY = "openscreen-system-language-prompt-seen";
+
const I18nContext = createContext(null);
export function useI18n(): I18nContextValue {
@@ -41,7 +42,37 @@ export function useScopedT(namespace: I18nNamespace) {
}
function isSupportedLocale(value: string): value is Locale {
- return (SUPPORTED_LOCALES as readonly string[]).includes(value);
+ return getAvailableLocales().includes(value);
+}
+
+function getSupportedSystemLocale(): Locale | null {
+ if (typeof navigator === "undefined") return null;
+ const availableLocales = getAvailableLocales();
+
+ const candidates =
+ Array.isArray(navigator.languages) && navigator.languages.length > 0
+ ? navigator.languages
+ : [navigator.language];
+
+ for (const candidate of candidates) {
+ if (!candidate) continue;
+ if (isSupportedLocale(candidate)) return candidate;
+
+ const exactMatch = availableLocales.find(
+ (locale) => locale.toLowerCase() === candidate.toLowerCase(),
+ );
+ if (exactMatch) return exactMatch;
+
+ const baseLanguage = candidate.split("-")[0]?.toLowerCase();
+ if (!baseLanguage) continue;
+
+ if (baseLanguage === "zh" && availableLocales.includes("zh-CN")) return "zh-CN";
+
+ const baseMatch = availableLocales.find((locale) => locale.toLowerCase() === baseLanguage);
+ if (baseMatch) return baseMatch;
+ }
+
+ return null;
}
function getInitialLocale(): Locale {
@@ -56,6 +87,16 @@ function getInitialLocale(): Locale {
export function I18nProvider({ children }: { children: ReactNode }) {
const [locale, setLocaleState] = useState(getInitialLocale);
+ const [systemLocaleSuggestion, setSystemLocaleSuggestion] = useState(null);
+ const hasRunSystemLocaleCheckRef = useRef(false);
+
+ const markPromptAsHandled = useCallback(() => {
+ try {
+ localStorage.setItem(SYSTEM_LANGUAGE_PROMPT_SEEN_KEY, "1");
+ } catch {
+ // localStorage may be unavailable
+ }
+ }, []);
const setLocale = useCallback((newLocale: Locale) => {
setLocaleState(newLocale);
@@ -73,6 +114,48 @@ export function I18nProvider({ children }: { children: ReactNode }) {
document.documentElement.lang = locale;
}, [locale]);
+ useEffect(() => {
+ if (hasRunSystemLocaleCheckRef.current) return;
+ hasRunSystemLocaleCheckRef.current = true;
+
+ let hasStoredLocale = false;
+ let hasHandledSystemPrompt = false;
+ try {
+ const stored = localStorage.getItem(LOCALE_STORAGE_KEY);
+ hasStoredLocale = Boolean(stored && isSupportedLocale(stored));
+ hasHandledSystemPrompt = localStorage.getItem(SYSTEM_LANGUAGE_PROMPT_SEEN_KEY) === "1";
+ } catch {
+ // localStorage may be unavailable
+ }
+
+ if (hasStoredLocale || hasHandledSystemPrompt) return;
+
+ const detectedSystemLocale = getSupportedSystemLocale();
+ if (!detectedSystemLocale || detectedSystemLocale === DEFAULT_LOCALE) {
+ markPromptAsHandled();
+ return;
+ }
+
+ setSystemLocaleSuggestion(detectedSystemLocale);
+ }, [markPromptAsHandled]);
+
+ const acceptSystemLocaleSuggestion = useCallback(() => {
+ if (!systemLocaleSuggestion) return;
+ setLocale(systemLocaleSuggestion);
+ setSystemLocaleSuggestion(null);
+ markPromptAsHandled();
+ }, [markPromptAsHandled, setLocale, systemLocaleSuggestion]);
+
+ const dismissSystemLocaleSuggestion = useCallback(() => {
+ setSystemLocaleSuggestion(null);
+ markPromptAsHandled();
+ }, [markPromptAsHandled]);
+
+ const resolveSystemLocaleSuggestion = useCallback(() => {
+ setSystemLocaleSuggestion(null);
+ markPromptAsHandled();
+ }, [markPromptAsHandled]);
+
const t = useCallback(
(qualifiedKey: string, vars?: TranslateVars): string => {
const dotIndex = qualifiedKey.indexOf(".");
@@ -84,7 +167,26 @@ export function I18nProvider({ children }: { children: ReactNode }) {
[locale],
);
- const value = useMemo(() => ({ locale, setLocale, t }), [locale, setLocale, t]);
+ const value = useMemo(
+ () => ({
+ locale,
+ setLocale,
+ t,
+ systemLocaleSuggestion,
+ acceptSystemLocaleSuggestion,
+ dismissSystemLocaleSuggestion,
+ resolveSystemLocaleSuggestion,
+ }),
+ [
+ locale,
+ setLocale,
+ t,
+ systemLocaleSuggestion,
+ acceptSystemLocaleSuggestion,
+ dismissSystemLocaleSuggestion,
+ resolveSystemLocaleSuggestion,
+ ],
+ );
return {children};
}
diff --git a/src/hooks/useEditorHistory.ts b/src/hooks/useEditorHistory.ts
index d261c1f..cc19222 100644
--- a/src/hooks/useEditorHistory.ts
+++ b/src/hooks/useEditorHistory.ts
@@ -7,6 +7,7 @@ import type {
WebcamLayoutPreset,
WebcamMaskShape,
WebcamPosition,
+ WebcamSizePreset,
ZoomRegion,
} from "@/components/video-editor/types";
import {
@@ -14,6 +15,7 @@ import {
DEFAULT_WEBCAM_LAYOUT_PRESET,
DEFAULT_WEBCAM_MASK_SHAPE,
DEFAULT_WEBCAM_POSITION,
+ DEFAULT_WEBCAM_SIZE_PRESET,
} from "@/components/video-editor/types";
import type { AspectRatio } from "@/utils/aspectRatioUtils";
@@ -34,6 +36,7 @@ export interface EditorState {
aspectRatio: AspectRatio;
webcamLayoutPreset: WebcamLayoutPreset;
webcamMaskShape: WebcamMaskShape;
+ webcamSizePreset: WebcamSizePreset;
webcamPosition: WebcamPosition | null;
}
@@ -52,6 +55,7 @@ export const INITIAL_EDITOR_STATE: EditorState = {
aspectRatio: "16:9",
webcamLayoutPreset: DEFAULT_WEBCAM_LAYOUT_PRESET,
webcamMaskShape: DEFAULT_WEBCAM_MASK_SHAPE,
+ webcamSizePreset: DEFAULT_WEBCAM_SIZE_PRESET,
webcamPosition: DEFAULT_WEBCAM_POSITION,
};
diff --git a/src/hooks/useScreenRecorder.ts b/src/hooks/useScreenRecorder.ts
index 2b07e24..913386c 100644
--- a/src/hooks/useScreenRecorder.ts
+++ b/src/hooks/useScreenRecorder.ts
@@ -110,6 +110,10 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
const allowAutoFinalize = useRef(false);
const discardRecordingId = useRef(null);
const restarting = useRef(false);
+ const countdownRunId = useRef(0);
+ const [countdownActive, setCountdownActive] = useState(false);
+ const webcamReady = useRef(false);
+ const webcamAcquireId = useRef(0);
const getRecordingDurationMs = useCallback(() => {
const segmentDuration =
@@ -158,10 +162,6 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
microphoneStream.current.getTracks().forEach((track) => track.stop());
microphoneStream.current = null;
}
- if (webcamStream.current) {
- webcamStream.current.getTracks().forEach((track) => track.stop());
- webcamStream.current = null;
- }
if (mixingContext.current) {
mixingContext.current.close().catch(() => {
// Ignore close errors during recorder teardown.
@@ -194,6 +194,85 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
[t],
);
+ useEffect(() => {
+ if (!webcamEnabled) return;
+
+ let cancelled = false;
+ let acquiredStream: MediaStream | null = null;
+ const thisAcquireId = ++webcamAcquireId.current;
+ webcamReady.current = false;
+
+ const acquire = async () => {
+ try {
+ const stream = await navigator.mediaDevices.getUserMedia({
+ audio: false,
+ video: webcamDeviceId
+ ? {
+ deviceId: { exact: webcamDeviceId },
+ width: { ideal: WEBCAM_TARGET_WIDTH },
+ height: { ideal: WEBCAM_TARGET_HEIGHT },
+ frameRate: { ideal: WEBCAM_TARGET_FRAME_RATE, max: WEBCAM_TARGET_FRAME_RATE },
+ }
+ : {
+ width: { ideal: WEBCAM_TARGET_WIDTH },
+ height: { ideal: WEBCAM_TARGET_HEIGHT },
+ frameRate: { ideal: WEBCAM_TARGET_FRAME_RATE, max: WEBCAM_TARGET_FRAME_RATE },
+ },
+ });
+
+ if (cancelled || thisAcquireId !== webcamAcquireId.current) {
+ stream.getTracks().forEach((track) => {
+ track.onended = null;
+ track.stop();
+ });
+ return;
+ }
+
+ acquiredStream = stream;
+ stream.getVideoTracks().forEach((track) => {
+ track.onended = () => {
+ webcamStream.current = null;
+ if (!restarting.current) {
+ setWebcamEnabledState(false);
+ toast.error(t("recording.cameraDisconnected"));
+ }
+ };
+ });
+ webcamStream.current = stream;
+ webcamReady.current = true;
+ } catch (cameraError) {
+ if (!cancelled) {
+ console.warn("Failed to get webcam access:", cameraError);
+ setWebcamEnabledState(false);
+ const isDeviceError =
+ cameraError instanceof DOMException &&
+ [
+ "NotFoundError",
+ "DevicesNotFoundError",
+ "OverconstrainedError",
+ "NotReadableError",
+ ].includes(cameraError.name);
+ toast.error(t(isDeviceError ? "recording.cameraNotFound" : "recording.cameraBlocked"));
+ webcamReady.current = true;
+ }
+ }
+ };
+
+ void acquire();
+
+ return () => {
+ cancelled = true;
+ webcamReady.current = false;
+ if (acquiredStream) {
+ acquiredStream.getTracks().forEach((track) => {
+ track.onended = null;
+ track.stop();
+ });
+ webcamStream.current = null;
+ }
+ };
+ }, [webcamEnabled, webcamDeviceId, t]);
+
const finalizeRecording = useCallback(
(
activeScreenRecorder: RecorderHandle,
@@ -334,7 +413,10 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
}
return () => {
+ const activeRunId = countdownRunId.current;
if (cleanup) cleanup();
+ countdownRunId.current += 1;
+ void safeHideCountdownOverlay(activeRunId);
allowAutoFinalize.current = false;
restarting.current = false;
discardRecordingId.current = null;
@@ -365,7 +447,117 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
};
}, [teardownMedia]);
- const startRecording = async () => {
+ const safeShowCountdownOverlay = async (value: number, runId: number) => {
+ try {
+ await window.electronAPI.showCountdownOverlay(value, runId);
+ return true;
+ } catch (error) {
+ console.warn("Failed to show countdown overlay:", error);
+ return false;
+ }
+ };
+
+ const cancelCountdown = () => {
+ const activeRunId = countdownRunId.current;
+ countdownRunId.current += 1;
+ setCountdownActive(false);
+ void safeHideCountdownOverlay(activeRunId);
+ };
+
+ const safeSetCountdownOverlayValue = async (value: number, runId: number) => {
+ try {
+ await window.electronAPI.setCountdownOverlayValue(value, runId);
+ } catch (error) {
+ console.warn("Failed to update countdown overlay value:", error);
+ }
+ };
+
+ const safeHideCountdownOverlay = async (runId: number) => {
+ try {
+ await window.electronAPI.hideCountdownOverlay(runId);
+ } catch (error) {
+ console.warn("Failed to hide countdown overlay:", error);
+ }
+ };
+
+ const isCountdownRunActive = (runId?: number) =>
+ runId === undefined || countdownRunId.current === runId;
+
+ const startRecordCountdown = async () => {
+ if (countdownActive || recording) {
+ return;
+ }
+
+ const runId = countdownRunId.current + 1;
+ countdownRunId.current = runId;
+ setCountdownActive(true);
+
+ let selectedSource: ProcessedDesktopSource | null = null;
+ try {
+ selectedSource = await window.electronAPI.getSelectedSource();
+ } catch (error) {
+ console.warn("Failed to read selected source before countdown:", error);
+ }
+
+ if (!isCountdownRunActive(runId)) {
+ return;
+ }
+
+ if (!selectedSource) {
+ if (countdownRunId.current === runId) {
+ setCountdownActive(false);
+ }
+ alert(t("recording.selectSource"));
+ return;
+ }
+
+ let overlayHiddenBeforeStart = false;
+ try {
+ const values = [3, 2, 1];
+ const overlayShown = await safeShowCountdownOverlay(values[0], runId);
+
+ if (countdownRunId.current !== runId) {
+ return;
+ }
+
+ for (const value of values) {
+ if (countdownRunId.current !== runId) {
+ return;
+ }
+
+ if (overlayShown && value !== values[0]) {
+ await safeSetCountdownOverlayValue(value, runId);
+
+ if (countdownRunId.current !== runId) {
+ return;
+ }
+ }
+
+ await new Promise((resolve) => window.setTimeout(resolve, 1000));
+ }
+
+ if (countdownRunId.current !== runId) {
+ return;
+ }
+
+ setCountdownActive(false);
+ await safeHideCountdownOverlay(runId);
+ overlayHiddenBeforeStart = true;
+
+ if (countdownRunId.current !== runId) {
+ return;
+ }
+
+ await startRecording(runId);
+ } finally {
+ if (!overlayHiddenBeforeStart && countdownRunId.current === runId) {
+ setCountdownActive(false);
+ await safeHideCountdownOverlay(runId);
+ }
+ }
+ };
+
+ const startRecording = async (countdownRunToken?: number) => {
try {
const selectedSource = await window.electronAPI.getSelectedSource();
if (!selectedSource) {
@@ -373,6 +565,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
return;
}
+ if (!isCountdownRunActive(countdownRunToken)) {
+ teardownMedia();
+ return;
+ }
+
let screenMediaStream: MediaStream;
const videoConstraints = {
@@ -413,6 +610,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
}
screenStream.current = screenMediaStream;
+ if (!isCountdownRunActive(countdownRunToken)) {
+ teardownMedia();
+ return;
+ }
+
if (microphoneEnabled) {
try {
microphoneStream.current = await navigator.mediaDevices.getUserMedia({
@@ -437,32 +639,35 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
}
}
+ if (!isCountdownRunActive(countdownRunToken)) {
+ teardownMedia();
+ return;
+ }
+
if (webcamEnabled) {
- try {
- webcamStream.current = await navigator.mediaDevices.getUserMedia({
- audio: false,
- video: webcamDeviceId
- ? {
- deviceId: { exact: webcamDeviceId },
- width: { ideal: WEBCAM_TARGET_WIDTH },
- height: { ideal: WEBCAM_TARGET_HEIGHT },
- frameRate: { ideal: WEBCAM_TARGET_FRAME_RATE, max: WEBCAM_TARGET_FRAME_RATE },
- }
- : {
- width: { ideal: WEBCAM_TARGET_WIDTH },
- height: { ideal: WEBCAM_TARGET_HEIGHT },
- frameRate: { ideal: WEBCAM_TARGET_FRAME_RATE, max: WEBCAM_TARGET_FRAME_RATE },
- },
+ if (!webcamReady.current) {
+ await new Promise((resolve) => {
+ const interval = setInterval(() => {
+ if (webcamReady.current) {
+ clearInterval(interval);
+ resolve();
+ }
+ }, 50);
+ setTimeout(() => {
+ clearInterval(interval);
+ resolve();
+ }, 5000);
});
- } catch (cameraError) {
- console.warn("Failed to get webcam access:", cameraError);
- if (webcamStream.current) {
- webcamStream.current.getTracks().forEach((track) => track.stop());
- webcamStream.current = null;
- }
- setWebcamEnabledState(false);
- toast.error(t("recording.cameraDenied"));
}
+ if (!webcamStream.current) {
+ webcamAcquireId.current++;
+ setWebcamEnabledState(false);
+ }
+ }
+
+ if (!isCountdownRunActive(countdownRunToken)) {
+ teardownMedia();
+ return;
}
stream.current = new MediaStream();
@@ -505,6 +710,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
);
}
+ if (!isCountdownRunActive(countdownRunToken)) {
+ teardownMedia();
+ return;
+ }
+
let {
width = DEFAULT_WIDTH,
height = DEFAULT_HEIGHT,
@@ -524,6 +734,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
);
const hasAudio = stream.current.getAudioTracks().length > 0;
+ if (!isCountdownRunActive(countdownRunToken)) {
+ teardownMedia();
+ return;
+ }
+
screenRecorder.current = createRecorderHandle(stream.current, {
mimeType,
videoBitsPerSecond,
@@ -635,7 +850,17 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
};
const toggleRecording = () => {
- recording ? stopRecording.current() : startRecording();
+ if (recording) {
+ stopRecording.current();
+ return;
+ }
+
+ if (countdownActive) {
+ cancelCountdown();
+ return;
+ }
+
+ void startRecordCountdown();
};
const restartRecording = async () => {
@@ -649,7 +874,6 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
restarting.current = true;
discardRecordingId.current = activeRecordingId;
- allowAutoFinalize.current = false;
const stopPromises = [
new Promise((resolve) => {
@@ -700,13 +924,22 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
const cancelRecording = () => {
const activeScreenRecorder = screenRecorder.current;
- if (!activeScreenRecorder || activeScreenRecorder.recorder.state !== "recording") return;
+ if (
+ activeScreenRecorder?.recorder.state === "recording" ||
+ activeScreenRecorder?.recorder.state === "paused"
+ ) {
+ const activeRecordingId = recordingId.current;
+ discardRecordingId.current = activeRecordingId;
+ allowAutoFinalize.current = false;
- const activeRecordingId = recordingId.current;
- discardRecordingId.current = activeRecordingId;
- allowAutoFinalize.current = false;
+ stopRecording.current();
+ return;
+ }
- stopRecording.current();
+ if (countdownActive) {
+ cancelCountdown();
+ return;
+ }
};
return {
diff --git a/src/i18n/__tests__/tutorialHelpTranslations.test.ts b/src/i18n/__tests__/tutorialHelpTranslations.test.ts
new file mode 100644
index 0000000..fcfa9d3
--- /dev/null
+++ b/src/i18n/__tests__/tutorialHelpTranslations.test.ts
@@ -0,0 +1,59 @@
+import { describe, expect, it } from "vitest";
+import { type Locale, SUPPORTED_LOCALES } from "@/i18n/config";
+import enDialogs from "@/i18n/locales/en/dialogs.json";
+import esDialogs from "@/i18n/locales/es/dialogs.json";
+import frDialogs from "@/i18n/locales/fr/dialogs.json";
+import koKRDialogs from "@/i18n/locales/ko-KR/dialogs.json";
+import trDialogs from "@/i18n/locales/tr/dialogs.json";
+import zhCNDialogs from "@/i18n/locales/zh-CN/dialogs.json";
+
+const tutorialHelpKeys = [
+ "triggerLabel",
+ "title",
+ "description",
+ "explanationBefore",
+ "remove",
+ "explanationMiddle",
+ "covered",
+ "explanationAfter",
+ "visualExample",
+ "removed",
+ "kept",
+ "part1",
+ "part2",
+ "part3",
+ "finalVideo",
+ "step1Title",
+ "step1DescriptionBefore",
+ "step1DescriptionAfter",
+ "step2Title",
+ "step2Description",
+] as const;
+
+const keysThatMayBeEmpty = new Set<(typeof tutorialHelpKeys)[number]>(["step1DescriptionBefore"]);
+
+const dialogsByLocale = {
+ en: enDialogs,
+ "zh-CN": zhCNDialogs,
+ es: esDialogs,
+ fr: frDialogs,
+ tr: trDialogs,
+ "ko-KR": koKRDialogs,
+} satisfies Record }>;
+
+describe("TutorialHelp translations", () => {
+ it("defines every tutorial help key for each supported locale", () => {
+ for (const locale of SUPPORTED_LOCALES) {
+ const tutorial = dialogsByLocale[locale].tutorial;
+
+ for (const key of tutorialHelpKeys) {
+ const message = tutorial[key];
+ const label = `${locale} dialogs.tutorial.${key}`;
+ expect(message, label).toEqual(expect.any(String));
+ if (!keysThatMayBeEmpty.has(key)) {
+ expect((message as string).trim().length, label).toBeGreaterThan(0);
+ }
+ }
+ }
+ });
+});
diff --git a/src/i18n/config.ts b/src/i18n/config.ts
index a96c7ef..03761c8 100644
--- a/src/i18n/config.ts
+++ b/src/i18n/config.ts
@@ -1,5 +1,5 @@
export const DEFAULT_LOCALE = "en" as const;
-export const SUPPORTED_LOCALES = ["en", "zh-CN", "es"] as const;
+export const SUPPORTED_LOCALES = ["en", "zh-CN", "zh-TW", "es", "fr", "tr", "ko-KR"] as const;
export const I18N_NAMESPACES = [
"common",
"dialogs",
@@ -10,7 +10,7 @@ export const I18N_NAMESPACES = [
"timeline",
] as const;
-export type Locale = (typeof SUPPORTED_LOCALES)[number];
+export type Locale = string;
export type I18nNamespace = (typeof I18N_NAMESPACES)[number];
export const LOCALE_STORAGE_KEY = "openscreen-locale";
diff --git a/src/i18n/loader.ts b/src/i18n/loader.ts
index 4736db8..36d8eb6 100644
--- a/src/i18n/loader.ts
+++ b/src/i18n/loader.ts
@@ -1,6 +1,10 @@
-import { DEFAULT_LOCALE, type I18nNamespace, type Locale } from "./config";
+import { DEFAULT_LOCALE, I18N_NAMESPACES, type I18nNamespace, type Locale } from "./config";
type MessageMap = Record;
+type LocaleValidationError = {
+ locale: string;
+ missingNamespaces: I18nNamespace[];
+};
const modules = import.meta.glob("./locales/**/*.json", { eager: true }) as Record<
string,
@@ -18,6 +22,62 @@ for (const [path, mod] of Object.entries(modules)) {
messages[locale][namespace] = mod.default;
}
+const REQUIRED_NAMESPACES = new Set(I18N_NAMESPACES);
+
+const localeValidationErrors: LocaleValidationError[] = Object.keys(messages)
+ .map((locale) => {
+ const localeMessages = messages[locale] ?? {};
+ const missingNamespaces = I18N_NAMESPACES.filter((namespace) => !localeMessages[namespace]);
+ return {
+ locale,
+ missingNamespaces,
+ };
+ })
+ .filter((entry) => entry.missingNamespaces.length > 0);
+
+const invalidLocales = new Set(localeValidationErrors.map((entry) => entry.locale));
+
+const availableLocales = Object.keys(messages)
+ .filter((locale) => REQUIRED_NAMESPACES.size > 0 && hasRequiredNamespaces(messages[locale]))
+ .filter((locale) => !invalidLocales.has(locale))
+ .sort((a, b) => {
+ if (a === DEFAULT_LOCALE) return -1;
+ if (b === DEFAULT_LOCALE) return 1;
+ return a.localeCompare(b);
+ });
+
+if (localeValidationErrors.length > 0) {
+ console.error("[i18n] Incomplete locale folders were excluded:");
+ for (const entry of localeValidationErrors) {
+ console.error(
+ `[i18n] ${entry.locale}: missing ${entry.missingNamespaces.map((ns) => `${ns}.json`).join(", ")}`,
+ );
+ }
+}
+
+function hasRequiredNamespaces(localeMessages: Record | undefined): boolean {
+ if (!localeMessages) return false;
+ for (const namespace of REQUIRED_NAMESPACES) {
+ if (!localeMessages[namespace]) return false;
+ }
+ return true;
+}
+
+function isAvailableLocale(locale: string): locale is Locale {
+ return availableLocales.includes(locale);
+}
+
+export function getAvailableLocales(): Locale[] {
+ if (availableLocales.length === 0) {
+ return [DEFAULT_LOCALE];
+ }
+ return availableLocales;
+}
+
+export function getLocaleValidationErrors(): LocaleValidationError[] {
+ return localeValidationErrors;
+}
+
function getMessageValue(obj: unknown, dotPath: string): string | undefined {
const keys = dotPath.split(".");
let current: unknown = obj;
@@ -34,15 +94,18 @@ function interpolate(str: string, vars?: Record): strin
}
export function getMessages(locale: Locale, namespace: I18nNamespace): MessageMap {
- return messages[locale]?.[namespace] ?? {};
+ const resolvedLocale = isAvailableLocale(locale) ? locale : DEFAULT_LOCALE;
+ return messages[resolvedLocale]?.[namespace] ?? {};
}
export function getLocaleName(locale: Locale): string {
- return getMessageValue(messages[locale]?.common, "locale.name") ?? locale;
+ const resolvedLocale = isAvailableLocale(locale) ? locale : DEFAULT_LOCALE;
+ return getMessageValue(messages[resolvedLocale]?.common, "locale.name") ?? locale;
}
export function getLocaleShort(locale: Locale): string {
- return getMessageValue(messages[locale]?.common, "locale.short") ?? locale;
+ const resolvedLocale = isAvailableLocale(locale) ? locale : DEFAULT_LOCALE;
+ return getMessageValue(messages[resolvedLocale]?.common, "locale.short") ?? locale;
}
export function translate(
@@ -52,8 +115,10 @@ export function translate(
vars?: Record,
): string {
const value =
- getMessageValue(messages[locale]?.[namespace], key) ??
- getMessageValue(messages[DEFAULT_LOCALE]?.[namespace], key);
+ getMessageValue(
+ messages[isAvailableLocale(locale) ? locale : DEFAULT_LOCALE]?.[namespace],
+ key,
+ ) ?? getMessageValue(messages[DEFAULT_LOCALE]?.[namespace], key);
if (value == null) return `${namespace}.${key}`;
return interpolate(value, vars);
diff --git a/src/i18n/locales/en/dialogs.json b/src/i18n/locales/en/dialogs.json
index 66a33c2..a84b5fd 100644
--- a/src/i18n/locales/en/dialogs.json
+++ b/src/i18n/locales/en/dialogs.json
@@ -27,10 +27,11 @@
"triggerLabel": "How trimming works",
"title": "How Trimming Works",
"description": "Understanding how to cut out unwanted parts of your video.",
- "explanation": "The Trim tool works by defining the segments you want to",
- "explanationRemove": "remove",
- "explanationCovered": "covered",
- "explanationEnd": "by a red trim segment will be cut out when you export.",
+ "explanationBefore": "The Trim tool works by defining the segments you want to",
+ "remove": "remove",
+ "explanationMiddle": " — anything",
+ "covered": "covered",
+ "explanationAfter": "by a red trim segment will be cut out when you export.",
"visualExample": "Visual Example",
"removed": "REMOVED",
"kept": "Kept",
@@ -39,7 +40,9 @@
"part3": "Part 3",
"finalVideo": "Final Video",
"step1Title": "1. Add Trim",
- "step1Description": "Press T or click the scissors icon to mark a section for removal.",
+ "step1DescriptionBefore": "Press ",
+ "step1DescriptionAfter": " or click the scissors icon to mark a section for removal.",
+
"step2Title": "2. Adjust",
"step2Description": "Drag the edges of the red region to cover exactly what you want to cut out."
},
diff --git a/src/i18n/locales/en/editor.json b/src/i18n/locales/en/editor.json
index 6fdc310..a171b16 100644
--- a/src/i18n/locales/en/editor.json
+++ b/src/i18n/locales/en/editor.json
@@ -1,4 +1,10 @@
{
+ "newRecording": {
+ "title": "Return to Recorder",
+ "description": "Your current session has been saved.",
+ "cancel": "Cancel",
+ "confirm": "Confirm"
+ },
"errors": {
"noVideoLoaded": "No video loaded",
"videoNotReady": "Video not ready",
@@ -30,6 +36,8 @@
"systemAudioUnavailable": "System audio not available. Recording without system audio.",
"microphoneDenied": "Microphone access denied. Recording will continue without audio.",
"cameraDenied": "Camera access denied. Recording will continue without webcam.",
+ "cameraDisconnected": "Webcam disconnected.",
+ "cameraNotFound": "Camera not found.",
"permissionDenied": "Recording permission denied. Please allow screen recording."
}
}
diff --git a/src/i18n/locales/en/launch.json b/src/i18n/locales/en/launch.json
index cf111c4..e959a54 100644
--- a/src/i18n/locales/en/launch.json
+++ b/src/i18n/locales/en/launch.json
@@ -33,5 +33,11 @@
"recording": {
"selectSource": "Please select a source to record"
},
- "language": "Language"
+ "language": "Language",
+ "systemLanguagePrompt": {
+ "title": "Use your system language?",
+ "description": "We detected {{language}} as your system language. Do you want to switch OpenScreen to {{language}}?",
+ "switch": "Switch to {{language}}",
+ "keepDefault": "Keep current language"
+ }
}
diff --git a/src/i18n/locales/en/settings.json b/src/i18n/locales/en/settings.json
index 632a569..00e7c08 100644
--- a/src/i18n/locales/en/settings.json
+++ b/src/i18n/locales/en/settings.json
@@ -8,12 +8,21 @@
"manual": "Manual",
"auto": "Auto",
"autoDescription": "Camera follows the recorded cursor position"
+ },
+ "speed": {
+ "title": "Zoom Speed",
+ "instant": "Instant",
+ "fast": "Fast",
+ "smooth": "Smooth",
+ "lazy": "Lazy"
}
},
"speed": {
"playbackSpeed": "Playback Speed",
"selectRegion": "Select a speed region to adjust",
- "deleteRegion": "Delete Speed Region"
+ "deleteRegion": "Delete Speed Region",
+ "customPlaybackSpeed": "Custom Playback Speed",
+ "maxSpeedError": "Speed can't go higher than 16×"
},
"trim": {
"deleteRegion": "Delete Trim Region"
@@ -24,7 +33,9 @@
"selectPreset": "Select preset",
"pictureInPicture": "Picture in Picture",
"verticalStack": "Vertical Stack",
- "webcamShape": "Camera Shape"
+ "dualFrame": "Dual Frame",
+ "webcamShape": "Camera Shape",
+ "webcamSize": "Webcam Size"
},
"effects": {
"title": "Video Effects",
@@ -98,6 +109,7 @@
"typeText": "Text",
"typeImage": "Image",
"typeArrow": "Arrow",
+ "typeBlur": "Blur",
"textContent": "Text Content",
"textPlaceholder": "Enter your text...",
"fontStyle": "Font Style",
@@ -114,6 +126,18 @@
"arrowDirection": "Arrow Direction",
"strokeWidth": "Stroke Width: {{width}}px",
"arrowColor": "Arrow Color",
+ "blurType": "Blur Type",
+ "blurTypeBlur": "Blur",
+ "blurTypeMosaic": "Mosaic Blur",
+ "blurColor": "Blur Color",
+ "blurColorWhite": "White",
+ "blurColorBlack": "Black",
+ "blurShape": "Blur Shape",
+ "blurIntensity": "Blur Intensity",
+ "mosaicBlockSize": "Mosaic Block Size",
+ "blurShapeRectangle": "Rectangle",
+ "blurShapeOval": "Oval",
+ "blurShapeFreehand": "Freehand",
"deleteAnnotation": "Delete Annotation",
"shortcutsAndTips": "Shortcuts & Tips",
"tipMovePlayhead": "Move playhead to overlapping annotation section and select an item.",
diff --git a/src/i18n/locales/en/shortcuts.json b/src/i18n/locales/en/shortcuts.json
index 790302c..2c26aa4 100644
--- a/src/i18n/locales/en/shortcuts.json
+++ b/src/i18n/locales/en/shortcuts.json
@@ -18,6 +18,7 @@
"addTrim": "Add Trim",
"addSpeed": "Add Speed",
"addAnnotation": "Add Annotation",
+ "addBlur": "Add Blur",
"addKeyframe": "Add Keyframe",
"deleteSelected": "Delete Selected",
"playPause": "Play / Pause"
diff --git a/src/i18n/locales/en/timeline.json b/src/i18n/locales/en/timeline.json
index f748621..b4d5bd8 100644
--- a/src/i18n/locales/en/timeline.json
+++ b/src/i18n/locales/en/timeline.json
@@ -4,12 +4,14 @@
"suggestZooms": "Suggest Zooms from Cursor",
"addTrim": "Add Trim (T)",
"addAnnotation": "Add Annotation (A)",
+ "addBlur": "Add Blur (B)",
"addSpeed": "Add Speed (S)"
},
"hints": {
"pressZoom": "Press Z to add zoom",
"pressTrim": "Press T to add trim",
"pressAnnotation": "Press A to add annotation",
+ "pressBlur": "Press B to add blur region",
"pressSpeed": "Press S to add speed"
},
"labels": {
@@ -19,6 +21,7 @@
"trimItem": "Trim {{index}}",
"speedItem": "Speed {{index}}",
"annotationItem": "Annotation",
+ "blurItem": "Blur {{index}}",
"imageItem": "Image",
"emptyText": "Empty text"
},
diff --git a/src/i18n/locales/es/dialogs.json b/src/i18n/locales/es/dialogs.json
index acf2a04..f8a5e63 100644
--- a/src/i18n/locales/es/dialogs.json
+++ b/src/i18n/locales/es/dialogs.json
@@ -27,10 +27,11 @@
"triggerLabel": "Cómo funciona el recorte",
"title": "Cómo funciona el recorte",
"description": "Aprende a eliminar las partes no deseadas de tu video.",
- "explanation": "La herramienta de recorte funciona definiendo los segmentos que deseas",
- "explanationRemove": "eliminar",
- "explanationCovered": "cubierto",
- "explanationEnd": "por un segmento rojo de recorte será eliminado al exportar.",
+ "explanationBefore": "La herramienta de recorte funciona definiendo los segmentos que deseas",
+ "remove": "eliminar",
+ "explanationMiddle": " — cualquier parte",
+ "covered": "cubierta",
+ "explanationAfter": "por un segmento rojo será eliminada al exportar.",
"visualExample": "Ejemplo visual",
"removed": "ELIMINADO",
"kept": "Conservado",
@@ -39,7 +40,8 @@
"part3": "Parte 3",
"finalVideo": "Video final",
"step1Title": "1. Agregar recorte",
- "step1Description": "Presiona T o haz clic en el ícono de tijeras para marcar una sección a eliminar.",
+ "step1DescriptionBefore": "Presiona ",
+ "step1DescriptionAfter": " o haz clic en el ícono de tijeras para marcar una sección a eliminar.",
"step2Title": "2. Ajustar",
"step2Description": "Arrastra los bordes de la región roja para cubrir exactamente lo que deseas eliminar."
},
diff --git a/src/i18n/locales/es/editor.json b/src/i18n/locales/es/editor.json
index 99adc78..7956b75 100644
--- a/src/i18n/locales/es/editor.json
+++ b/src/i18n/locales/es/editor.json
@@ -30,6 +30,8 @@
"systemAudioUnavailable": "Audio del sistema no disponible. Grabando sin audio del sistema.",
"microphoneDenied": "Acceso al micrófono denegado. La grabación continuará sin audio.",
"cameraDenied": "Acceso a la cámara denegado. La grabación continuará sin cámara web.",
+ "cameraDisconnected": "Cámara web desconectada.",
+ "cameraNotFound": "Cámara no encontrada.",
"permissionDenied": "Permiso de grabación denegado. Por favor permite la grabación de pantalla."
}
}
diff --git a/src/i18n/locales/es/launch.json b/src/i18n/locales/es/launch.json
index f47bc81..68919aa 100644
--- a/src/i18n/locales/es/launch.json
+++ b/src/i18n/locales/es/launch.json
@@ -33,5 +33,11 @@
"recording": {
"selectSource": "Por favor selecciona una fuente para grabar"
},
- "language": "Idioma"
+ "language": "Idioma",
+ "systemLanguagePrompt": {
+ "title": "¿Usar el idioma del sistema?",
+ "description": "Detectamos {{language}} como idioma de tu sistema. ¿Quieres cambiar OpenScreen a {{language}}?",
+ "switch": "Cambiar a {{language}}",
+ "keepDefault": "Mantener idioma actual"
+ }
}
diff --git a/src/i18n/locales/es/settings.json b/src/i18n/locales/es/settings.json
index 586e840..92160bd 100644
--- a/src/i18n/locales/es/settings.json
+++ b/src/i18n/locales/es/settings.json
@@ -8,12 +8,21 @@
"manual": "Manual",
"auto": "Auto",
"autoDescription": "La cámara sigue la posición del cursor grabado"
+ },
+ "speed": {
+ "title": "Velocidad de zoom",
+ "instant": "Instantáneo",
+ "fast": "Rápido",
+ "smooth": "Suave",
+ "lazy": "Lento"
}
},
"speed": {
"playbackSpeed": "Velocidad de reproducción",
"selectRegion": "Selecciona una región de velocidad para ajustar",
- "deleteRegion": "Eliminar región de velocidad"
+ "deleteRegion": "Eliminar región de velocidad",
+ "customPlaybackSpeed": "Velocidad personalizada",
+ "maxSpeedError": "La velocidad no puede superar 16×"
},
"trim": {
"deleteRegion": "Eliminar región de recorte"
@@ -24,7 +33,9 @@
"selectPreset": "Seleccionar predefinido",
"pictureInPicture": "Imagen en imagen",
"verticalStack": "Apilado vertical",
- "webcamShape": "Forma de cámara"
+ "dualFrame": "Marco dual",
+ "webcamShape": "Forma de cámara",
+ "webcamSize": "Tamaño de cámara"
},
"effects": {
"title": "Efectos de video",
@@ -98,6 +109,7 @@
"typeText": "Texto",
"typeImage": "Imagen",
"typeArrow": "Flecha",
+ "typeBlur": "Desenfoque",
"textContent": "Contenido de texto",
"textPlaceholder": "Escribe tu texto...",
"fontStyle": "Estilo de fuente",
@@ -114,6 +126,18 @@
"arrowDirection": "Dirección de la flecha",
"strokeWidth": "Grosor del trazo: {{width}}px",
"arrowColor": "Color de la flecha",
+ "blurType": "Tipo de desenfoque",
+ "blurTypeBlur": "Desenfoque",
+ "blurTypeMosaic": "Desenfoque mosaico",
+ "blurColor": "Color del desenfoque",
+ "blurColorWhite": "Blanco",
+ "blurColorBlack": "Negro",
+ "blurShape": "Forma del desenfoque",
+ "blurIntensity": "Intensidad del desenfoque",
+ "mosaicBlockSize": "Tamano del bloque mosaico",
+ "blurShapeRectangle": "Rectángulo",
+ "blurShapeOval": "Óvalo",
+ "blurShapeFreehand": "Mano alzada",
"deleteAnnotation": "Eliminar anotación",
"shortcutsAndTips": "Atajos y consejos",
"tipMovePlayhead": "Mueve el cabezal de reproducción a la sección de anotación superpuesta y selecciona un elemento.",
diff --git a/src/i18n/locales/es/shortcuts.json b/src/i18n/locales/es/shortcuts.json
index a03420e..888ded0 100644
--- a/src/i18n/locales/es/shortcuts.json
+++ b/src/i18n/locales/es/shortcuts.json
@@ -18,6 +18,7 @@
"addTrim": "Agregar recorte",
"addSpeed": "Agregar velocidad",
"addAnnotation": "Agregar anotación",
+ "addBlur": "Agregar desenfoque",
"addKeyframe": "Agregar fotograma clave",
"deleteSelected": "Eliminar seleccionado",
"playPause": "Reproducir / Pausar"
diff --git a/src/i18n/locales/es/timeline.json b/src/i18n/locales/es/timeline.json
index 9f11bc9..12a83b0 100644
--- a/src/i18n/locales/es/timeline.json
+++ b/src/i18n/locales/es/timeline.json
@@ -4,13 +4,15 @@
"suggestZooms": "Sugerir zooms desde el cursor",
"addTrim": "Agregar recorte (T)",
"addAnnotation": "Agregar anotación (A)",
- "addSpeed": "Agregar velocidad (S)"
+ "addSpeed": "Agregar velocidad (S)",
+ "addBlur": "Agregar desenfoque (B)"
},
"hints": {
"pressZoom": "Presiona Z para agregar zoom",
"pressTrim": "Presiona T para agregar recorte",
"pressAnnotation": "Presiona A para agregar anotación",
- "pressSpeed": "Presiona S para agregar velocidad"
+ "pressSpeed": "Presiona S para agregar velocidad",
+ "pressBlur": "Presiona B para agregar una región de desenfoque"
},
"labels": {
"pan": "Desplazar",
@@ -20,7 +22,8 @@
"speedItem": "Velocidad {{index}}",
"annotationItem": "Anotación",
"imageItem": "Imagen",
- "emptyText": "Texto vacío"
+ "emptyText": "Texto vacío",
+ "blurItem": "Desenfoque {{index}}"
},
"emptyState": {
"noVideo": "No hay video cargado",
diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json
new file mode 100644
index 0000000..7eb7f83
--- /dev/null
+++ b/src/i18n/locales/fr/common.json
@@ -0,0 +1,29 @@
+{
+ "actions": {
+ "cancel": "Annuler",
+ "save": "Enregistrer",
+ "delete": "Supprimer",
+ "close": "Fermer",
+ "share": "Partager",
+ "done": "Terminer",
+ "open": "Ouvrir",
+ "upload": "Téléverser",
+ "export": "Exporter",
+ "file": "Fichier",
+ "edit": "Éditer",
+ "view": "Affichage",
+ "window": "Fenêtre",
+ "quit": "Quitter",
+ "stopRecording": "Arrêter l'enregistrement"
+ },
+ "playback": {
+ "play": "Lecture",
+ "pause": "Pause",
+ "fullscreen": "Plein écran",
+ "exitFullscreen": "Quitter le plein écran"
+ },
+ "locale": {
+ "name": "Français",
+ "short": "FR"
+ }
+}
diff --git a/src/i18n/locales/fr/dialogs.json b/src/i18n/locales/fr/dialogs.json
new file mode 100644
index 0000000..dbaae38
--- /dev/null
+++ b/src/i18n/locales/fr/dialogs.json
@@ -0,0 +1,70 @@
+{
+ "export": {
+ "complete": "Export terminé",
+ "yourFormatReady": "Votre {{format}} est prêt",
+ "showInFolder": "Afficher dans le dossier",
+ "finalizingVideo": "Finalisation de l'export vidéo...",
+ "compilingGifProgress": "Compilation du GIF... {{progress}}%",
+ "compilingGifWait": "Compilation du GIF... Cela peut prendre un moment",
+ "takeMoment": "Cela peut prendre un moment...",
+ "failed": "Export échoué",
+ "tryAgain": "Veuillez réessayer",
+ "finalizingVideoTitle": "Finalisation de la vidéo",
+ "compilingGif": "Compilation du GIF",
+ "exportingFormat": "Export de {{format}}",
+ "compiling": "Compilation en cours",
+ "renderingFrames": "Rendu des images",
+ "processing": "Traitement en cours...",
+ "finalizing": "Finalisation...",
+ "compilingStatus": "Compilation...",
+ "status": "Statut",
+ "format": "Format",
+ "frames": "Images",
+ "cancelExport": "Annuler l'export",
+ "savedSuccessfully": "{{format}} enregistré avec succès !"
+ },
+ "tutorial": {
+ "triggerLabel": "Comment fonctionne la coupe",
+ "title": "Comment fonctionne la coupe",
+ "description": "Comprendre comment supprimer les parties indésirables de votre vidéo.",
+ "explanationBefore": "L'outil Coupe fonctionne en définissant les segments que vous souhaitez",
+ "remove": "supprimer",
+ "explanationMiddle": " — tout ce qui est",
+ "covered": "couvert",
+ "explanationAfter": "par un segment de coupe rouge sera coupé lors de l'export.",
+ "visualExample": "Exemple visuel",
+ "removed": "SUPPRIMÉ",
+ "kept": "Conservé",
+ "part1": "Partie 1",
+ "part2": "Partie 2",
+ "part3": "Partie 3",
+ "finalVideo": "Vidéo finale",
+ "step1Title": "1. Ajouter une coupe",
+ "step1DescriptionBefore": "Appuyez sur ",
+ "step1DescriptionAfter": " ou cliquez sur l'icône ciseaux pour marquer une section à supprimer.",
+ "step2Title": "2. Ajuster",
+ "step2Description": "Faites glisser les bords de la région rouge pour couvrir exactement ce que vous souhaitez couper."
+ },
+ "unsavedChanges": {
+ "title": "Modifications non enregistrées",
+ "message": "Vous avez des modifications non enregistrées.",
+ "detail": "Voulez-vous enregistrer votre projet avant de fermer ?",
+ "saveAndClose": "Enregistrer et fermer",
+ "discardAndClose": "Ignorer et fermer",
+ "loadProject": "Charger un projet…",
+ "saveProject": "Enregistrer le projet…",
+ "saveProjectAs": "Enregistrer le projet sous…"
+ },
+ "fileDialogs": {
+ "saveGif": "Enregistrer le GIF exporté",
+ "saveVideo": "Enregistrer la vidéo exportée",
+ "selectVideo": "Sélectionner un fichier vidéo",
+ "saveProject": "Enregistrer le projet OpenScreen",
+ "openProject": "Ouvrir un projet OpenScreen",
+ "gifImage": "Image GIF",
+ "mp4Video": "Vidéo MP4",
+ "videoFiles": "Fichiers vidéo",
+ "openscreenProject": "Projet OpenScreen",
+ "allFiles": "Tous les fichiers"
+ }
+}
diff --git a/src/i18n/locales/fr/editor.json b/src/i18n/locales/fr/editor.json
new file mode 100644
index 0000000..03596bd
--- /dev/null
+++ b/src/i18n/locales/fr/editor.json
@@ -0,0 +1,43 @@
+{
+ "newRecording": {
+ "title": "Retour à l'enregistreur",
+ "description": "Votre session actuelle a été enregistrée.",
+ "cancel": "Annuler",
+ "confirm": "Confirmer"
+ },
+ "errors": {
+ "noVideoLoaded": "Aucune vidéo chargée",
+ "videoNotReady": "Vidéo non prête",
+ "unableToDetermineSourcePath": "Impossible de déterminer le chemin de la vidéo source",
+ "failedToSaveGif": "Échec de l'enregistrement du GIF",
+ "gifExportFailed": "L'export du GIF a échoué",
+ "failedToSaveVideo": "Échec de l'enregistrement de la vidéo",
+ "exportFailed": "L'export a échoué",
+ "exportFailedWithError": "L'export a échoué : {{error}}",
+ "failedToSaveExport": "Échec de l'enregistrement de l'export",
+ "failedToSaveExportedVideo": "Échec de l'enregistrement de la vidéo exportée",
+ "failedToRevealInFolder": "Erreur lors de l'affichage dans le dossier : {{error}}"
+ },
+ "export": {
+ "canceled": "Export annulé",
+ "exportedSuccessfully": "{{format}} exporté avec succès"
+ },
+ "project": {
+ "saveCanceled": "Enregistrement du projet annulé",
+ "failedToSave": "Échec de l'enregistrement du projet",
+ "savedTo": "Projet enregistré dans {{path}}",
+ "failedToLoad": "Échec du chargement du projet",
+ "invalidFormat": "Format de fichier projet invalide",
+ "loadedFrom": "Projet chargé depuis {{path}}"
+ },
+ "recording": {
+ "failedCameraAccess": "Échec de la demande d'accès à la caméra.",
+ "cameraBlocked": "L'accès à la caméra est bloqué. Activez-le dans les paramètres système pour utiliser la webcam.",
+ "systemAudioUnavailable": "Audio système non disponible. Enregistrement sans audio système.",
+ "microphoneDenied": "Accès au microphone refusé. L'enregistrement continuera sans audio.",
+ "cameraDenied": "Accès à la caméra refusé. L'enregistrement continuera sans webcam.",
+ "cameraDisconnected": "Webcam déconnectée.",
+ "cameraNotFound": "Caméra introuvable.",
+ "permissionDenied": "Permission d'enregistrement refusée. Veuillez autoriser l'enregistrement d'écran."
+ }
+}
diff --git a/src/i18n/locales/fr/launch.json b/src/i18n/locales/fr/launch.json
new file mode 100644
index 0000000..55521cb
--- /dev/null
+++ b/src/i18n/locales/fr/launch.json
@@ -0,0 +1,43 @@
+{
+ "tooltips": {
+ "hideHUD": "Masquer le HUD",
+ "closeApp": "Fermer l'application",
+ "restartRecording": "Redémarrer l'enregistrement",
+ "cancelRecording": "Annuler l'enregistrement",
+ "pauseRecording": "Mettre en pause l'enregistrement",
+ "resumeRecording": "Reprendre l'enregistrement",
+ "openVideoFile": "Ouvrir un fichier vidéo",
+ "openProject": "Ouvrir un projet"
+ },
+ "audio": {
+ "enableSystemAudio": "Activer l'audio système",
+ "disableSystemAudio": "Désactiver l'audio système",
+ "enableMicrophone": "Activer le microphone",
+ "disableMicrophone": "Désactiver le microphone",
+ "defaultMicrophone": "Microphone par défaut"
+ },
+ "webcam": {
+ "enableWebcam": "Activer la webcam",
+ "disableWebcam": "Désactiver la webcam",
+ "defaultCamera": "Caméra par défaut",
+ "searching": "Recherche en cours...",
+ "noneFound": "Aucune caméra trouvée",
+ "unavailable": "Caméra non disponible"
+ },
+ "sourceSelector": {
+ "loading": "Chargement des sources...",
+ "screens": "Écrans ({{count}})",
+ "windows": "Fenêtres ({{count}})",
+ "defaultSourceName": "Écran"
+ },
+ "recording": {
+ "selectSource": "Veuillez sélectionner une source à enregistrer"
+ },
+ "language": "Langue",
+ "systemLanguagePrompt": {
+ "title": "Utiliser la langue de votre système ?",
+ "description": "Nous avons détecté {{language}} comme langue système. Voulez-vous passer OpenScreen en {{language}} ?",
+ "switch": "Passer en {{language}}",
+ "keepDefault": "Conserver la langue actuelle"
+ }
+}
diff --git a/src/i18n/locales/fr/settings.json b/src/i18n/locales/fr/settings.json
new file mode 100644
index 0000000..0dff11f
--- /dev/null
+++ b/src/i18n/locales/fr/settings.json
@@ -0,0 +1,183 @@
+{
+ "zoom": {
+ "level": "Niveau de zoom",
+ "selectRegion": "Sélectionnez une région de zoom à ajuster",
+ "deleteZoom": "Supprimer le zoom",
+ "focusMode": {
+ "title": "Mode focus",
+ "manual": "Manuel",
+ "auto": "Auto",
+ "autoDescription": "La caméra suit la position du curseur enregistré"
+ },
+ "speed": {
+ "title": "Vitesse du zoom",
+ "instant": "Instantané",
+ "fast": "Rapide",
+ "smooth": "Fluide",
+ "lazy": "Lent"
+ }
+ },
+ "speed": {
+ "playbackSpeed": "Vitesse de lecture",
+ "selectRegion": "Sélectionnez une région de vitesse à ajuster",
+ "deleteRegion": "Supprimer la région de vitesse",
+ "customPlaybackSpeed": "Vitesse de lecture personnalisée",
+ "maxSpeedError": "La vitesse ne peut pas dépasser 16×"
+ },
+ "trim": {
+ "deleteRegion": "Supprimer la région de coupe"
+ },
+ "layout": {
+ "title": "Mise en page",
+ "preset": "Préréglage",
+ "selectPreset": "Choisir un préréglage",
+ "pictureInPicture": "Incrustation d'image",
+ "verticalStack": "Empilement vertical",
+ "dualFrame": "Double cadre",
+ "webcamShape": "Forme de la caméra",
+ "webcamSize": "Taille de la caméra"
+ },
+ "effects": {
+ "title": "Effets vidéo",
+ "blurBg": "Flou arrière-plan",
+ "motionBlur": "Flou de mouvement",
+ "off": "désactivé",
+ "shadow": "Ombre",
+ "roundness": "Arrondi",
+ "padding": "Marge"
+ },
+ "background": {
+ "title": "Arrière-plan",
+ "image": "Image",
+ "color": "Couleur",
+ "gradient": "Dégradé",
+ "uploadCustom": "Téléverser une image",
+ "gradientLabel": "Dégradé {{index}}"
+ },
+ "crop": {
+ "title": "Recadrage",
+ "cropVideo": "Recadrer la vidéo",
+ "dragInstruction": "Faites glisser chaque côté pour ajuster la zone de recadrage",
+ "ratio": "Ratio",
+ "free": "Libre",
+ "done": "Terminer",
+ "lockAspectRatio": "Verrouiller le ratio",
+ "unlockAspectRatio": "Déverrouiller le ratio"
+ },
+ "exportFormat": {
+ "mp4": "MP4",
+ "gif": "GIF",
+ "mp4Video": "Vidéo MP4",
+ "mp4Description": "Fichier vidéo haute qualité",
+ "gifAnimation": "Animation GIF",
+ "gifDescription": "Image animée pour le partage"
+ },
+ "exportQuality": {
+ "title": "Qualité d'export",
+ "low": "Faible",
+ "medium": "Moyenne",
+ "high": "Haute"
+ },
+ "gifSettings": {
+ "frameRate": "Fréquence d'images GIF",
+ "size": "Taille du GIF",
+ "loop": "GIF en boucle"
+ },
+ "project": {
+ "save": "Enregistrer le projet",
+ "load": "Charger un projet"
+ },
+ "export": {
+ "videoButton": "Exporter la vidéo",
+ "gifButton": "Exporter le GIF",
+ "chooseSaveLocation": "Choisir l'emplacement d'enregistrement"
+ },
+ "links": {
+ "reportBug": "Signaler un bug",
+ "starOnGithub": "Étoile sur GitHub"
+ },
+ "imageUpload": {
+ "invalidFileType": "Type de fichier invalide",
+ "jpgOnly": "Veuillez téléverser un fichier image JPG ou JPEG.",
+ "uploadSuccess": "Image personnalisée téléversée avec succès !",
+ "failedToUpload": "Échec du téléversement de l'image",
+ "errorReading": "Une erreur s'est produite lors de la lecture du fichier."
+ },
+ "annotation": {
+ "title": "Paramètres d'annotation",
+ "active": "Actif",
+ "typeText": "Texte",
+ "typeImage": "Image",
+ "typeArrow": "Flèche",
+ "typeBlur": "Flou",
+ "textContent": "Contenu du texte",
+ "textPlaceholder": "Saisissez votre texte...",
+ "fontStyle": "Style de police",
+ "selectStyle": "Choisir un style",
+ "size": "Taille",
+ "customFonts": "Polices personnalisées",
+ "textColor": "Couleur du texte",
+ "background": "Arrière-plan",
+ "none": "Aucun",
+ "color": "Couleur",
+ "clearBackground": "Supprimer l'arrière-plan",
+ "uploadImage": "Téléverser une image",
+ "supportedFormats": "Formats supportés : JPG, PNG, GIF, WebP",
+ "arrowDirection": "Direction de la flèche",
+ "strokeWidth": "Épaisseur du trait : {{width}}px",
+ "arrowColor": "Couleur de la flèche",
+ "blurType": "Type de flou",
+ "blurTypeBlur": "Flou",
+ "blurTypeMosaic": "Flou mosaique",
+ "blurColor": "Couleur du flou",
+ "blurColorWhite": "Blanc",
+ "blurColorBlack": "Noir",
+ "blurShape": "Forme du flou",
+ "blurIntensity": "Intensité du flou",
+ "mosaicBlockSize": "Taille des blocs de mosaique",
+ "blurShapeRectangle": "Rectangle",
+ "blurShapeOval": "Ovale",
+ "blurShapeFreehand": "Main levée",
+ "deleteAnnotation": "Supprimer l'annotation",
+ "shortcutsAndTips": "Raccourcis & Astuces",
+ "tipMovePlayhead": "Déplacez la tête de lecture sur la section d'annotation et sélectionnez un élément.",
+ "tipTabCycle": "Utilisez Tab pour cycler entre les éléments superposés.",
+ "tipShiftTabCycle": "Utilisez Shift+Tab pour cycler en sens inverse.",
+ "invalidImageType": "Type de fichier invalide",
+ "imageFormatsOnly": "Veuillez téléverser un fichier image JPG, PNG, GIF ou WebP.",
+ "imageUploadSuccess": "Image téléversée avec succès !",
+ "failedImageUpload": "Échec du téléversement de l'image"
+ },
+ "fontStyles": {
+ "classic": "Classique",
+ "editor": "Éditeur",
+ "strong": "Gras",
+ "typewriter": "Machine à écrire",
+ "deco": "Déco",
+ "simple": "Simple",
+ "modern": "Moderne",
+ "clean": "Épuré"
+ },
+ "customFont": {
+ "dialogTitle": "Ajouter une police Google",
+ "urlLabel": "URL d'import Google Fonts",
+ "urlPlaceholder": "https://fonts.googleapis.com/css2?family=Roboto&display=swap",
+ "urlHelp": "Obtenez-la depuis Google Fonts : Sélectionnez une police → Cliquez sur « Obtenir la police » → Copiez l'URL @import",
+ "nameLabel": "Nom d'affichage",
+ "namePlaceholder": "Ma police personnalisée",
+ "nameHelp": "C'est ainsi que la police apparaîtra dans le sélecteur de polices",
+ "addButton": "Ajouter la police",
+ "addingButton": "Ajout en cours...",
+ "errorEmptyUrl": "Veuillez saisir une URL d'import Google Fonts",
+ "errorInvalidUrl": "Veuillez saisir une URL Google Fonts valide",
+ "errorEmptyName": "Veuillez saisir un nom de police",
+ "errorExtractFailed": "Impossible d'extraire la famille de polices depuis l'URL",
+ "successMessage": "Police « {{fontName}} » ajoutée avec succès",
+ "failedToAdd": "Échec de l'ajout de la police",
+ "errorTimeout": "La police a mis trop de temps à charger. Vérifiez l'URL et réessayez.",
+ "errorLoadFailed": "La police n'a pas pu être chargée. Vérifiez que l'URL Google Fonts est correcte."
+ },
+ "language": {
+ "title": "Langue"
+ }
+}
diff --git a/src/i18n/locales/fr/shortcuts.json b/src/i18n/locales/fr/shortcuts.json
new file mode 100644
index 0000000..a0ce83f
--- /dev/null
+++ b/src/i18n/locales/fr/shortcuts.json
@@ -0,0 +1,37 @@
+{
+ "title": "Raccourcis clavier",
+ "customize": "Personnaliser",
+ "configurable": "Configurable",
+ "fixed": "Fixe",
+ "pressKey": "Appuyez sur une touche…",
+ "clickToChange": "Cliquez pour modifier",
+ "pressEscToCancel": "Appuyez sur Échap pour annuler",
+ "helpText": "Cliquez sur un raccourci puis appuyez sur la nouvelle combinaison de touches. Appuyez sur Échap pour annuler.",
+ "resetToDefaults": "Réinitialiser les valeurs par défaut",
+ "alreadyUsedBy": "Déjà utilisé par {{action}}",
+ "swap": "Échanger",
+ "reservedShortcut": "Ce raccourci est réservé pour « {{label}} » et ne peut pas être réassigné.",
+ "savedToast": "Raccourcis clavier enregistrés",
+ "resetToast": "Réinitialisé aux raccourcis par défaut — cliquez sur Enregistrer pour appliquer",
+ "actions": {
+ "addZoom": "Ajouter un zoom",
+ "addTrim": "Ajouter une coupe",
+ "addSpeed": "Ajouter une vitesse",
+ "addAnnotation": "Ajouter une annotation",
+ "addBlur": "Ajouter un flou",
+ "addKeyframe": "Ajouter une image-clé",
+ "deleteSelected": "Supprimer la sélection",
+ "playPause": "Lecture / Pause"
+ },
+ "fixedActions": {
+ "undo": "Annuler",
+ "redo": "Rétablir",
+ "cycleAnnotationsForward": "Parcourir les annotations en avant",
+ "cycleAnnotationsBackward": "Parcourir les annotations en arrière",
+ "deleteSelectedAlt": "Supprimer la sélection (alt)",
+ "panTimeline": "Panoramique de la timeline",
+ "zoomTimeline": "Zoom de la timeline",
+ "frameBack": "Image précédente",
+ "frameForward": "Image suivante"
+ }
+}
diff --git a/src/i18n/locales/fr/timeline.json b/src/i18n/locales/fr/timeline.json
new file mode 100644
index 0000000..5985ea6
--- /dev/null
+++ b/src/i18n/locales/fr/timeline.json
@@ -0,0 +1,53 @@
+{
+ "buttons": {
+ "addZoom": "Ajouter un zoom (Z)",
+ "suggestZooms": "Suggérer des zooms depuis le curseur",
+ "addTrim": "Ajouter une coupe (T)",
+ "addAnnotation": "Ajouter une annotation (A)",
+ "addSpeed": "Ajouter une vitesse (S)",
+ "addBlur": "Ajouter un flou (B)"
+ },
+ "hints": {
+ "pressZoom": "Appuyez sur Z pour ajouter un zoom",
+ "pressTrim": "Appuyez sur T pour ajouter une coupe",
+ "pressAnnotation": "Appuyez sur A pour ajouter une annotation",
+ "pressSpeed": "Appuyez sur S pour ajouter une vitesse",
+ "pressBlur": "Appuyez sur B pour ajouter une zone de flou"
+ },
+ "labels": {
+ "pan": "Panoramique",
+ "zoom": "Zoom",
+ "zoomItem": "Zoom {{index}}",
+ "trimItem": "Coupe {{index}}",
+ "speedItem": "Vitesse {{index}}",
+ "annotationItem": "Annotation",
+ "imageItem": "Image",
+ "emptyText": "Texte vide",
+ "blurItem": "Flou {{index}}"
+ },
+ "emptyState": {
+ "noVideo": "Aucune vidéo chargée",
+ "dragAndDrop": "Glissez-déposez une vidéo pour commencer à éditer"
+ },
+ "errors": {
+ "cannotPlaceZoom": "Impossible de placer le zoom ici",
+ "zoomExistsAtLocation": "Un zoom existe déjà à cet emplacement ou l\u0027espace disponible est insuffisant.",
+ "zoomSuggestionUnavailable": "Gestionnaire de suggestions de zoom non disponible",
+ "noCursorTelemetry": "Aucune télémétrie de curseur disponible",
+ "noCursorTelemetryDescription": "Enregistrez d\u0027abord un screencast pour générer des suggestions basées sur le curseur.",
+ "noUsableTelemetry": "Aucune télémétrie de curseur utilisable",
+ "noUsableTelemetryDescription": "L\u0027enregistrement ne contient pas suffisamment de données de mouvement du curseur.",
+ "noDwellMoments": "Aucun moment de pause du curseur trouvé",
+ "noDwellMomentsDescription": "Essayez un enregistrement avec des pauses plus lentes du curseur sur les actions importantes.",
+ "noAutoZoomSlots": "Aucun emplacement de zoom automatique disponible",
+ "noAutoZoomSlotsDescription": "Les points de pause détectés chevauchent des régions de zoom existantes.",
+ "cannotPlaceTrim": "Impossible de placer la coupe ici",
+ "trimExistsAtLocation": "Une coupe existe déjà à cet emplacement ou l\u0027espace disponible est insuffisant.",
+ "cannotPlaceSpeed": "Impossible de placer la vitesse ici",
+ "speedExistsAtLocation": "Une région de vitesse existe déjà à cet emplacement ou l\u0027espace disponible est insuffisant."
+ },
+ "success": {
+ "addedZoomSuggestions": "{{count}} suggestion de zoom basée sur le curseur ajoutée",
+ "addedZoomSuggestionsPlural": "{{count}} suggestions de zoom basées sur le curseur ajoutées"
+ }
+}
diff --git a/src/i18n/locales/ko-KR/common.json b/src/i18n/locales/ko-KR/common.json
new file mode 100644
index 0000000..b83cb44
--- /dev/null
+++ b/src/i18n/locales/ko-KR/common.json
@@ -0,0 +1,29 @@
+{
+ "actions": {
+ "cancel": "취소",
+ "save": "저장",
+ "delete": "삭제",
+ "close": "닫기",
+ "share": "공유",
+ "done": "완료",
+ "open": "열기",
+ "upload": "업로드",
+ "export": "내보내기",
+ "file": "파일",
+ "edit": "편집",
+ "view": "보기",
+ "window": "창",
+ "quit": "종료",
+ "stopRecording": "녹화 중지"
+ },
+ "playback": {
+ "play": "재생",
+ "pause": "일시정지",
+ "fullscreen": "전체화면",
+ "exitFullscreen": "전체화면 종료"
+ },
+ "locale": {
+ "name": "한국어",
+ "short": "KO"
+ }
+}
diff --git a/src/i18n/locales/ko-KR/dialogs.json b/src/i18n/locales/ko-KR/dialogs.json
new file mode 100644
index 0000000..3093cdf
--- /dev/null
+++ b/src/i18n/locales/ko-KR/dialogs.json
@@ -0,0 +1,70 @@
+{
+ "export": {
+ "complete": "내보내기 완료",
+ "yourFormatReady": "{{format}} 파일이 준비되었습니다",
+ "showInFolder": "폴더에서 보기",
+ "finalizingVideo": "비디오 내보내기 마무리 중...",
+ "compilingGifProgress": "GIF 생성 중... {{progress}}%",
+ "compilingGifWait": "GIF 생성 중... 잠시 시간이 걸릴 수 있습니다",
+ "takeMoment": "잠시 기다려 주세요...",
+ "failed": "내보내기 실패",
+ "tryAgain": "다시 시도해 주세요",
+ "finalizingVideoTitle": "비디오 마무리 중",
+ "compilingGif": "GIF 생성 중",
+ "exportingFormat": "{{format}} 내보내는 중",
+ "compiling": "생성 중...",
+ "renderingFrames": "프레임 렌더링 중",
+ "processing": "처리 중...",
+ "finalizing": "마무리 중...",
+ "compilingStatus": "생성 중...",
+ "status": "상태",
+ "format": "형식",
+ "frames": "프레임",
+ "cancelExport": "내보내기 취소",
+ "savedSuccessfully": "{{format}} 저장이 완료되었습니다!"
+ },
+ "tutorial": {
+ "triggerLabel": "트리밍 사용법",
+ "title": "트리밍 사용법",
+ "description": "비디오에서 불필요한 부분을 잘라내는 방법을 알아보세요.",
+ "explanationBefore": "트림 도구는 제거할 구간을",
+ "remove": "지정",
+ "explanationMiddle": "하는 방식으로 동작합니다 —",
+ "covered": "빨간 트림 구간으로 덮인",
+ "explanationAfter": "부분은 내보낼 때 잘려나갑니다.",
+ "visualExample": "화면 예시",
+ "removed": "제거됨",
+ "kept": "유지됨",
+ "part1": "파트 1",
+ "part2": "파트 2",
+ "part3": "파트 3",
+ "finalVideo": "최종 비디오",
+ "step1Title": "1. 트림 추가",
+ "step1DescriptionBefore": "",
+ "step1DescriptionAfter": "키를 누르거나 가위 아이콘을 클릭해 제거할 구간을 표시하세요.",
+ "step2Title": "2. 조정",
+ "step2Description": "빨간 구간의 가장자리를 드래그해 잘라낼 범위를 설정하세요."
+ },
+ "unsavedChanges": {
+ "title": "저장되지 않은 변경 사항",
+ "message": "저장되지 않은 변경 사항이 있습니다.",
+ "detail": "닫기 전에 프로젝트를 저장하시겠습니까?",
+ "saveAndClose": "저장 후 닫기",
+ "discardAndClose": "저장하지 않고 닫기",
+ "loadProject": "프로젝트 불러오기...",
+ "saveProject": "프로젝트 저장...",
+ "saveProjectAs": "다른 이름으로 프로젝트 저장..."
+ },
+ "fileDialogs": {
+ "saveGif": "내보낸 GIF 저장",
+ "saveVideo": "내보낸 비디오 저장",
+ "selectVideo": "비디오 파일 선택",
+ "saveProject": "OpenScreen 프로젝트 저장",
+ "openProject": "OpenScreen 프로젝트 열기",
+ "gifImage": "GIF 이미지",
+ "mp4Video": "MP4 비디오",
+ "videoFiles": "비디오 파일",
+ "openscreenProject": "OpenScreen 프로젝트",
+ "allFiles": "모든 파일"
+ }
+}
diff --git a/src/i18n/locales/ko-KR/editor.json b/src/i18n/locales/ko-KR/editor.json
new file mode 100644
index 0000000..4db7d1f
--- /dev/null
+++ b/src/i18n/locales/ko-KR/editor.json
@@ -0,0 +1,41 @@
+{
+ "newRecording": {
+ "title": "녹화로 돌아가기",
+ "description": "현재 세션이 저장되었습니다.",
+ "cancel": "취소",
+ "confirm": "확인"
+ },
+ "errors": {
+ "noVideoLoaded": "불러온 비디오가 없습니다",
+ "videoNotReady": "비디오가 준비되지 않았습니다",
+ "unableToDetermineSourcePath": "소스 비디오 경로를 확인할 수 없습니다",
+ "failedToSaveGif": "GIF 저장에 실패했습니다",
+ "gifExportFailed": "GIF 내보내기에 실패했습니다",
+ "failedToSaveVideo": "비디오 저장에 실패했습니다",
+ "exportFailed": "내보내기에 실패했습니다",
+ "exportFailedWithError": "내보내기 실패: {{error}}",
+ "failedToSaveExport": "내보낸 파일 저장에 실패했습니다",
+ "failedToSaveExportedVideo": "내보낸 비디오 저장에 실패했습니다",
+ "failedToRevealInFolder": "폴더에서 파일 표시 오류: {{error}}"
+ },
+ "export": {
+ "canceled": "내보내기가 취소되었습니다",
+ "exportedSuccessfully": "{{format}} 내보내기가 완료되었습니다"
+ },
+ "project": {
+ "saveCanceled": "프로젝트 저장이 취소되었습니다",
+ "failedToSave": "프로젝트 저장에 실패했습니다",
+ "savedTo": "프로젝트가 {{path}}에 저장되었습니다",
+ "failedToLoad": "프로젝트 불러오기에 실패했습니다",
+ "invalidFormat": "유효하지 않은 프로젝트 파일 형식입니다",
+ "loadedFrom": "{{path}}에서 프로젝트를 불러왔습니다"
+ },
+ "recording": {
+ "failedCameraAccess": "카메라 접근 권한 요청에 실패했습니다.",
+ "cameraBlocked": "카메라 접근이 차단되어 있습니다. 시스템 설정에서 권한을 허용해 주세요.",
+ "systemAudioUnavailable": "시스템 오디오를 사용할 수 없습니다. 시스템 오디오 없이 녹화합니다.",
+ "microphoneDenied": "마이크 접근이 거부되었습니다. 오디오 없이 녹화를 계속합니다.",
+ "cameraDenied": "카메라 접근이 거부되었습니다. 웹캠 없이 녹화를 계속합니다.",
+ "permissionDenied": "녹화 권한이 거부되었습니다. 화면 녹화를 허용해 주세요."
+ }
+}
diff --git a/src/i18n/locales/ko-KR/launch.json b/src/i18n/locales/ko-KR/launch.json
new file mode 100644
index 0000000..1cc695e
--- /dev/null
+++ b/src/i18n/locales/ko-KR/launch.json
@@ -0,0 +1,37 @@
+{
+ "tooltips": {
+ "hideHUD": "HUD 숨기기",
+ "closeApp": "앱 닫기",
+ "restartRecording": "녹화 다시 시작",
+ "cancelRecording": "녹화 취소",
+ "pauseRecording": "녹화 일시정지",
+ "resumeRecording": "녹화 재개",
+ "openVideoFile": "비디오 파일 열기",
+ "openProject": "프로젝트 열기"
+ },
+ "audio": {
+ "enableSystemAudio": "시스템 오디오 활성화",
+ "disableSystemAudio": "시스템 오디오 비활성화",
+ "enableMicrophone": "마이크 활성화",
+ "disableMicrophone": "마이크 비활성화",
+ "defaultMicrophone": "기본 마이크"
+ },
+ "webcam": {
+ "enableWebcam": "웹캠 활성화",
+ "disableWebcam": "웹캠 비활성화",
+ "defaultCamera": "기본 카메라",
+ "searching": "검색 중...",
+ "noneFound": "카메라를 찾을 수 없음",
+ "unavailable": "카메라를 사용할 수 없음"
+ },
+ "sourceSelector": {
+ "loading": "소스 불러오는 중...",
+ "screens": "화면 ({{count}}개)",
+ "windows": "창 ({{count}}개)",
+ "defaultSourceName": "화면"
+ },
+ "recording": {
+ "selectSource": "녹화할 소스를 선택해 주세요"
+ },
+ "language": "언어"
+}
diff --git a/src/i18n/locales/ko-KR/settings.json b/src/i18n/locales/ko-KR/settings.json
new file mode 100644
index 0000000..cd9f734
--- /dev/null
+++ b/src/i18n/locales/ko-KR/settings.json
@@ -0,0 +1,162 @@
+{
+ "zoom": {
+ "level": "줌 레벨",
+ "selectRegion": "조정할 줌 구간을 선택하세요",
+ "deleteZoom": "줌 삭제",
+ "focusMode": {
+ "title": "포커스 모드",
+ "manual": "수동",
+ "auto": "자동",
+ "autoDescription": "녹화된 커서 위치를 따라 카메라가 이동합니다"
+ }
+ },
+ "speed": {
+ "playbackSpeed": "재생 속도",
+ "selectRegion": "조정할 속도 구간을 선택하세요",
+ "deleteRegion": "속도 구간 삭제",
+ "customPlaybackSpeed": "재생 속도 직접 입력",
+ "maxSpeedError": "속도는 16×를 초과할 수 없습니다"
+ },
+ "trim": {
+ "deleteRegion": "트림 구간 삭제"
+ },
+ "layout": {
+ "title": "레이아웃",
+ "preset": "프리셋",
+ "selectPreset": "프리셋 선택",
+ "pictureInPicture": "화면 속 화면",
+ "verticalStack": "세로 배치",
+ "webcamShape": "카메라 모양",
+ "webcamSize": "웹캠 크기"
+ },
+ "effects": {
+ "title": "비디오 효과",
+ "blurBg": "배경 흐림",
+ "motionBlur": "모션 블러",
+ "off": "끄기",
+ "shadow": "그림자",
+ "roundness": "모서리 둥글기",
+ "padding": "여백"
+ },
+ "background": {
+ "title": "배경",
+ "image": "이미지",
+ "color": "색상",
+ "gradient": "그라디언트",
+ "uploadCustom": "직접 업로드",
+ "gradientLabel": "그라디언트 {{index}}"
+ },
+ "crop": {
+ "title": "자르기",
+ "cropVideo": "비디오 자르기",
+ "dragInstruction": "각 면을 드래그해 자르기 영역을 조정하세요",
+ "ratio": "비율",
+ "free": "자유",
+ "done": "완료",
+ "lockAspectRatio": "화면 비율 고정",
+ "unlockAspectRatio": "화면 비율 해제"
+ },
+ "exportFormat": {
+ "mp4": "MP4",
+ "gif": "GIF",
+ "mp4Video": "MP4 비디오",
+ "mp4Description": "고화질 비디오 파일",
+ "gifAnimation": "GIF 애니메이션",
+ "gifDescription": "공유용 애니메이션 이미지"
+ },
+ "exportQuality": {
+ "title": "내보내기 품질",
+ "low": "낮음",
+ "medium": "보통",
+ "high": "높음"
+ },
+ "gifSettings": {
+ "frameRate": "GIF 프레임 속도",
+ "size": "GIF 크기",
+ "loop": "GIF 반복"
+ },
+ "project": {
+ "save": "프로젝트 저장",
+ "load": "프로젝트 불러오기"
+ },
+ "export": {
+ "videoButton": "비디오 내보내기",
+ "gifButton": "GIF 내보내기",
+ "chooseSaveLocation": "저장 위치 선택"
+ },
+ "links": {
+ "reportBug": "버그 신고",
+ "starOnGithub": "GitHub에 Star 남기기"
+ },
+ "imageUpload": {
+ "invalidFileType": "지원하지 않는 파일 형식입니다",
+ "jpgOnly": "JPG 또는 JPEG 이미지 파일을 업로드해 주세요.",
+ "uploadSuccess": "커스텀 이미지가 성공적으로 업로드되었습니다!",
+ "failedToUpload": "이미지 업로드에 실패했습니다",
+ "errorReading": "파일을 읽는 중 오류가 발생했습니다."
+ },
+ "annotation": {
+ "title": "주석 설정",
+ "active": "활성",
+ "typeText": "텍스트",
+ "typeImage": "이미지",
+ "typeArrow": "화살표",
+ "textContent": "텍스트 내용",
+ "textPlaceholder": "텍스트를 입력하세요...",
+ "fontStyle": "폰트 스타일",
+ "selectStyle": "스타일 선택",
+ "size": "크기",
+ "customFonts": "커스텀 폰트",
+ "textColor": "텍스트 색상",
+ "background": "배경",
+ "none": "없음",
+ "color": "색상",
+ "clearBackground": "배경 지우기",
+ "uploadImage": "이미지 업로드",
+ "supportedFormats": "지원 형식: JPG, PNG, GIF, WebP",
+ "arrowDirection": "화살표 방향",
+ "strokeWidth": "선 두께: {{width}}px",
+ "arrowColor": "화살표 색상",
+ "deleteAnnotation": "주석 삭제",
+ "shortcutsAndTips": "단축키 및 팁",
+ "tipMovePlayhead": "재생 헤드를 주석 구간으로 옮겨 항목을 선택하세요.",
+ "tipTabCycle": "Tab 키로 겹치는 항목을 순환할 수 있습니다.",
+ "tipShiftTabCycle": "Shift+Tab으로 역방향 순환할 수 있습니다.",
+ "invalidImageType": "지원하지 않는 파일 형식입니다",
+ "imageFormatsOnly": "JPG, PNG, GIF 또는 WebP 이미지 파일을 업로드해 주세요.",
+ "imageUploadSuccess": "이미지가 성공적으로 업로드되었습니다!",
+ "failedImageUpload": "이미지 업로드에 실패했습니다"
+ },
+ "fontStyles": {
+ "classic": "클래식",
+ "editor": "에디터",
+ "strong": "강조",
+ "typewriter": "타자기",
+ "deco": "데코",
+ "simple": "심플",
+ "modern": "모던",
+ "clean": "클린"
+ },
+ "customFont": {
+ "dialogTitle": "Google 폰트 추가",
+ "urlLabel": "Google Fonts 가져오기 URL",
+ "urlPlaceholder": "https://fonts.googleapis.com/css2?family=Roboto&display=swap",
+ "urlHelp": "Google Fonts에서 폰트 선택 → \"폰트 가져오기\" 클릭 → @import URL 복사",
+ "nameLabel": "표시 이름",
+ "namePlaceholder": "내 커스텀 폰트",
+ "nameHelp": "폰트 선택기에서 표시될 이름입니다",
+ "addButton": "폰트 추가",
+ "addingButton": "추가 중...",
+ "errorEmptyUrl": "Google Fonts 가져오기 URL을 입력해 주세요",
+ "errorInvalidUrl": "유효한 Google Fonts URL을 입력해 주세요",
+ "errorEmptyName": "폰트 이름을 입력해 주세요",
+ "errorExtractFailed": "URL에서 폰트 패밀리를 추출할 수 없습니다",
+ "successMessage": "\"{{fontName}}\" 폰트가 성공적으로 추가되었습니다",
+ "failedToAdd": "폰트 추가에 실패했습니다",
+ "errorTimeout": "폰트 로딩 시간이 초과되었습니다. URL을 확인하고 다시 시도해 주세요.",
+ "errorLoadFailed": "폰트를 불러올 수 없습니다. Google Fonts URL이 올바른지 확인해 주세요."
+ },
+ "language": {
+ "title": "언어"
+ }
+}
diff --git a/src/i18n/locales/ko-KR/shortcuts.json b/src/i18n/locales/ko-KR/shortcuts.json
new file mode 100644
index 0000000..1760d8f
--- /dev/null
+++ b/src/i18n/locales/ko-KR/shortcuts.json
@@ -0,0 +1,36 @@
+{
+ "title": "키보드 단축키",
+ "customize": "사용자 지정",
+ "configurable": "변경 가능",
+ "fixed": "고정",
+ "pressKey": "키를 누르세요...",
+ "clickToChange": "클릭해서 변경",
+ "pressEscToCancel": "Esc를 눌러 취소",
+ "helpText": "단축키를 클릭한 후 새 키 조합을 누르세요. 취소하려면 Esc를 누르세요.",
+ "resetToDefaults": "기본값으로 초기화",
+ "alreadyUsedBy": "이미 {{action}}에서 사용 중입니다",
+ "swap": "교체",
+ "reservedShortcut": "이 단축키는 \"{{label}}\"에 예약되어 있어 변경할 수 없습니다.",
+ "savedToast": "키보드 단축키가 저장되었습니다",
+ "resetToast": "기본 단축키로 초기화되었습니다 — 저장을 클릭해 적용하세요",
+ "actions": {
+ "addZoom": "줌 추가",
+ "addTrim": "트림 추가",
+ "addSpeed": "속도 추가",
+ "addAnnotation": "주석 추가",
+ "addKeyframe": "키프레임 추가",
+ "deleteSelected": "선택 항목 삭제",
+ "playPause": "재생 / 일시정지"
+ },
+ "fixedActions": {
+ "undo": "실행 취소",
+ "redo": "다시 실행",
+ "cycleAnnotationsForward": "주석 앞으로 순환",
+ "cycleAnnotationsBackward": "주석 뒤로 순환",
+ "deleteSelectedAlt": "선택 항목 삭제 (대체)",
+ "panTimeline": "타임라인 이동",
+ "zoomTimeline": "타임라인 확대/축소",
+ "frameBack": "이전 프레임",
+ "frameForward": "다음 프레임"
+ }
+}
diff --git a/src/i18n/locales/ko-KR/timeline.json b/src/i18n/locales/ko-KR/timeline.json
new file mode 100644
index 0000000..167c26f
--- /dev/null
+++ b/src/i18n/locales/ko-KR/timeline.json
@@ -0,0 +1,50 @@
+{
+ "buttons": {
+ "addZoom": "줌 추가 (Z)",
+ "suggestZooms": "커서 기반 줌 제안",
+ "addTrim": "트림 추가 (T)",
+ "addAnnotation": "주석 추가 (A)",
+ "addSpeed": "속도 추가 (S)"
+ },
+ "hints": {
+ "pressZoom": "Z를 눌러 줌 추가",
+ "pressTrim": "T를 눌러 트림 추가",
+ "pressAnnotation": "A를 눌러 주석 추가",
+ "pressSpeed": "S를 눌러 속도 추가"
+ },
+ "labels": {
+ "pan": "이동",
+ "zoom": "줌",
+ "zoomItem": "줌 {{index}}",
+ "trimItem": "트림 {{index}}",
+ "speedItem": "속도 {{index}}",
+ "annotationItem": "주석",
+ "imageItem": "이미지",
+ "emptyText": "빈 텍스트"
+ },
+ "emptyState": {
+ "noVideo": "불러온 비디오 없음",
+ "dragAndDrop": "비디오를 드래그 앤 드롭해서 편집을 시작하세요"
+ },
+ "errors": {
+ "cannotPlaceZoom": "이 위치에 줌을 추가할 수 없습니다",
+ "zoomExistsAtLocation": "이 위치에 이미 줌이 있거나 공간이 부족합니다.",
+ "zoomSuggestionUnavailable": "줌 제안 기능을 사용할 수 없습니다",
+ "noCursorTelemetry": "커서 데이터가 없습니다",
+ "noCursorTelemetryDescription": "커서 기반 제안을 생성하려면 먼저 화면을 녹화해 주세요.",
+ "noUsableTelemetry": "사용 가능한 커서 데이터가 없습니다",
+ "noUsableTelemetryDescription": "녹화에 충분한 커서 이동 데이터가 포함되어 있지 않습니다.",
+ "noDwellMoments": "명확한 커서 정지 구간을 찾을 수 없습니다",
+ "noDwellMomentsDescription": "중요한 동작에서 커서를 천천히 멈추며 녹화해 보세요.",
+ "noAutoZoomSlots": "자동 줌 슬롯이 없습니다",
+ "noAutoZoomSlotsDescription": "감지된 정지 지점이 기존 줌 구간과 겹칩니다.",
+ "cannotPlaceTrim": "이 위치에 트림을 추가할 수 없습니다",
+ "trimExistsAtLocation": "이 위치에 이미 트림이 있거나 공간이 부족합니다.",
+ "cannotPlaceSpeed": "이 위치에 속도를 추가할 수 없습니다",
+ "speedExistsAtLocation": "이 위치에 이미 속도 구간이 있거나 공간이 부족합니다."
+ },
+ "success": {
+ "addedZoomSuggestions": "커서 기반 줌 제안 {{count}}개가 추가되었습니다",
+ "addedZoomSuggestionsPlural": "커서 기반 줌 제안 {{count}}개가 추가되었습니다"
+ }
+}
diff --git a/src/i18n/locales/tr/common.json b/src/i18n/locales/tr/common.json
new file mode 100644
index 0000000..3ec132c
--- /dev/null
+++ b/src/i18n/locales/tr/common.json
@@ -0,0 +1,29 @@
+{
+ "actions": {
+ "cancel": "İptal",
+ "save": "Kaydet",
+ "delete": "Sil",
+ "close": "Kapat",
+ "share": "Paylaş",
+ "done": "Tamam",
+ "open": "Aç",
+ "upload": "Yükle",
+ "export": "Dışa Aktar",
+ "file": "Dosya",
+ "edit": "Düzenle",
+ "view": "Görünüm",
+ "window": "Pencere",
+ "quit": "Çıkış",
+ "stopRecording": "Kaydı Durdur"
+ },
+ "playback": {
+ "play": "Oynat",
+ "pause": "Duraklat",
+ "fullscreen": "Tam Ekran",
+ "exitFullscreen": "Tam Ekrandan Çık"
+ },
+ "locale": {
+ "name": "Türkçe",
+ "short": "TR"
+ }
+}
diff --git a/src/i18n/locales/tr/dialogs.json b/src/i18n/locales/tr/dialogs.json
new file mode 100644
index 0000000..9fab50d
--- /dev/null
+++ b/src/i18n/locales/tr/dialogs.json
@@ -0,0 +1,70 @@
+{
+ "export": {
+ "complete": "Dışa Aktarım Tamamlandı",
+ "yourFormatReady": "{{format}} dosyanız hazır",
+ "showInFolder": "Klasörde Göster",
+ "finalizingVideo": "Video dışa aktarımı sonlandırılıyor...",
+ "compilingGifProgress": "GIF derleniyor... %{{progress}}",
+ "compilingGifWait": "GIF derleniyor... Bu biraz zaman alabilir",
+ "takeMoment": "Bu biraz zaman alabilir...",
+ "failed": "Dışa Aktarım Başarısız",
+ "tryAgain": "Lütfen tekrar deneyin",
+ "finalizingVideoTitle": "Video Sonlandırılıyor",
+ "compilingGif": "GIF Derleniyor",
+ "exportingFormat": "{{format}} Dışa Aktarılıyor",
+ "compiling": "Derleniyor",
+ "renderingFrames": "Kareler İşleniyor",
+ "processing": "İşleniyor...",
+ "finalizing": "Sonlandırılıyor...",
+ "compilingStatus": "Derleniyor...",
+ "status": "Durum",
+ "format": "Biçim",
+ "frames": "Kareler",
+ "cancelExport": "Dışa Aktarımı İptal Et",
+ "savedSuccessfully": "{{format}} başarıyla kaydedildi!"
+ },
+ "tutorial": {
+ "triggerLabel": "Kırpma nasıl çalışır",
+ "title": "Kırpma Nasıl Çalışır",
+ "description": "Videonuzun istenmeyen bölümlerini nasıl keseceğinizi anlayın.",
+ "explanationBefore": "Kırpma aracı, istediğiniz bölümleri",
+ "remove": "kaldırmak",
+ "explanationMiddle": " için kullanılır; kırmızı kırpma bölgesiyle",
+ "covered": "kaplanan",
+ "explanationAfter": "her şey dışa aktarımda kesilecektir.",
+ "visualExample": "Görsel Örnek",
+ "removed": "KALDIRILDI",
+ "kept": "Korundu",
+ "part1": "Bölüm 1",
+ "part2": "Bölüm 2",
+ "part3": "Bölüm 3",
+ "finalVideo": "Son Video",
+ "step1Title": "1. Kırpma Ekle",
+ "step1DescriptionBefore": "Kaldırılacak bölümü işaretlemek için ",
+ "step1DescriptionAfter": " tuşuna basın veya makas simgesine tıklayın.",
+ "step2Title": "2. Ayarla",
+ "step2Description": "Kesmek istediğiniz kısmı tam olarak kaplamak için kırmızı bölgenin kenarlarını sürükleyin."
+ },
+ "unsavedChanges": {
+ "title": "Kaydedilmemiş Değişiklikler",
+ "message": "Kaydedilmemiş değişiklikleriniz var.",
+ "detail": "Kapatmadan önce projenizi kaydetmek ister misiniz?",
+ "saveAndClose": "Kaydet ve Kapat",
+ "discardAndClose": "Kaydetmeden Kapat",
+ "loadProject": "Proje Yükle…",
+ "saveProject": "Proje Kaydet…",
+ "saveProjectAs": "Farklı Kaydet…"
+ },
+ "fileDialogs": {
+ "saveGif": "Dışa Aktarılan GIF'i Kaydet",
+ "saveVideo": "Dışa Aktarılan Videoyu Kaydet",
+ "selectVideo": "Video Dosyası Seç",
+ "saveProject": "OpenScreen Projesini Kaydet",
+ "openProject": "OpenScreen Projesini Aç",
+ "gifImage": "GIF Görüntüsü",
+ "mp4Video": "MP4 Video",
+ "videoFiles": "Video Dosyaları",
+ "openscreenProject": "OpenScreen Projesi",
+ "allFiles": "Tüm Dosyalar"
+ }
+}
diff --git a/src/i18n/locales/tr/editor.json b/src/i18n/locales/tr/editor.json
new file mode 100644
index 0000000..dfa4cb1
--- /dev/null
+++ b/src/i18n/locales/tr/editor.json
@@ -0,0 +1,35 @@
+{
+ "errors": {
+ "noVideoLoaded": "Video yüklenmedi",
+ "videoNotReady": "Video hazır değil",
+ "unableToDetermineSourcePath": "Kaynak video yolu belirlenemiyor",
+ "failedToSaveGif": "GIF kaydedilemedi",
+ "gifExportFailed": "GIF dışa aktarımı başarısız oldu",
+ "failedToSaveVideo": "Video kaydedilemedi",
+ "exportFailed": "Dışa aktarım başarısız oldu",
+ "exportFailedWithError": "Dışa aktarım başarısız: {{error}}",
+ "failedToSaveExport": "Dışa aktarım kaydedilemedi",
+ "failedToSaveExportedVideo": "Dışa aktarılan video kaydedilemedi",
+ "failedToRevealInFolder": "Klasörde gösterme hatası: {{error}}"
+ },
+ "export": {
+ "canceled": "Dışa aktarım iptal edildi",
+ "exportedSuccessfully": "{{format}} başarıyla dışa aktarıldı"
+ },
+ "project": {
+ "saveCanceled": "Proje kaydetme iptal edildi",
+ "failedToSave": "Proje kaydedilemedi",
+ "savedTo": "Proje şuraya kaydedildi: {{path}}",
+ "failedToLoad": "Proje yüklenemedi",
+ "invalidFormat": "Geçersiz proje dosyası biçimi",
+ "loadedFrom": "Proje şuradan yüklendi: {{path}}"
+ },
+ "recording": {
+ "failedCameraAccess": "Kamera erişimi istenemedi.",
+ "cameraBlocked": "Kamera erişimi engellendi. Kamerayı kullanmak için sistem ayarlarından izin verin.",
+ "systemAudioUnavailable": "Sistem sesi kullanılamıyor. Sistem sesi olmadan kaydediliyor.",
+ "microphoneDenied": "Mikrofon erişimi reddedildi. Kayıt ses olmadan devam edecek.",
+ "cameraDenied": "Kamera erişimi reddedildi. Kayıt kamera olmadan devam edecek.",
+ "permissionDenied": "Kayıt izni reddedildi. Lütfen ekran kaydına izin verin."
+ }
+}
diff --git a/src/i18n/locales/tr/launch.json b/src/i18n/locales/tr/launch.json
new file mode 100644
index 0000000..f48d99d
--- /dev/null
+++ b/src/i18n/locales/tr/launch.json
@@ -0,0 +1,48 @@
+{
+ "tooltips": {
+ "hideHUD": "Kontrol panelini gizle",
+ "closeApp": "Uygulamayı kapat",
+ "restartRecording": "Kaydı yeniden başlat",
+ "cancelRecording": "Kaydı iptal et",
+ "pauseRecording": "Kaydı duraklat",
+ "resumeRecording": "Kayda devam et",
+ "openVideoFile": "Video dosyası aç",
+ "openProject": "Proje aç"
+ },
+ "audio": {
+ "enableSystemAudio": "Sistem sesini etkinleştir",
+ "disableSystemAudio": "Sistem sesini devre dışı bırak",
+ "enableMicrophone": "Mikrofonu etkinleştir",
+ "disableMicrophone": "Mikrofonu devre dışı bırak",
+ "defaultMicrophone": "Varsayılan Mikrofon",
+ "enableNoiseReduction": "Gürültü azaltmayı etkinleştir (yapay zeka destekli)",
+ "disableNoiseReduction": "Gürültü azaltmayı devre dışı bırak",
+ "noiseReduction": "Gürültü azaltma",
+ "clickToCycle": "Seviye değiştirmek için tıklayın",
+ "nrLevel": {
+ "light": "Hafif",
+ "moderate": "Orta",
+ "aggressive": "Güçlü"
+ },
+ "noiseReductionPrompt": "Daha net ses için yapay zeka destekli gürültü azaltmayı etkinleştirmek ister misiniz?",
+ "enableNoiseReductionShort": "Etkinleştir"
+ },
+ "webcam": {
+ "enableWebcam": "Kamerayı etkinleştir",
+ "disableWebcam": "Kamerayı devre dışı bırak",
+ "defaultCamera": "Varsayılan Kamera",
+ "searching": "Aranıyor...",
+ "noneFound": "Kamera bulunamadı",
+ "unavailable": "Kamera kullanılamıyor"
+ },
+ "sourceSelector": {
+ "loading": "Kaynaklar yükleniyor...",
+ "screens": "Ekranlar ({{count}})",
+ "windows": "Pencereler ({{count}})",
+ "defaultSourceName": "Ekran"
+ },
+ "recording": {
+ "selectSource": "Lütfen kayıt için bir kaynak seçin"
+ },
+ "language": "Dil"
+}
diff --git a/src/i18n/locales/tr/settings.json b/src/i18n/locales/tr/settings.json
new file mode 100644
index 0000000..936f75c
--- /dev/null
+++ b/src/i18n/locales/tr/settings.json
@@ -0,0 +1,176 @@
+{
+ "zoom": {
+ "level": "Yakınlaştırma Seviyesi",
+ "selectRegion": "Ayarlamak için bir yakınlaştırma bölgesi seçin",
+ "deleteZoom": "Yakınlaştırmayı Sil",
+ "focusMode": {
+ "title": "Odak Modu",
+ "manual": "Manuel",
+ "auto": "Otomatik",
+ "autoDescription": "Kamera kaydedilen imleç konumunu takip eder"
+ }
+ },
+ "speed": {
+ "playbackSpeed": "Oynatma Hızı",
+ "selectRegion": "Ayarlamak için bir hız bölgesi seçin",
+ "deleteRegion": "Hız Bölgesini Sil"
+ },
+ "trim": {
+ "deleteRegion": "Kırpma Bölgesini Sil"
+ },
+ "layout": {
+ "title": "Düzen",
+ "preset": "Ön Ayar",
+ "selectPreset": "Ön ayar seçin",
+ "pictureInPicture": "Resim İçinde Resim",
+ "verticalStack": "Dikey Yığın",
+ "webcamShape": "Kamera Şekli"
+ },
+ "effects": {
+ "title": "Video Efektleri",
+ "blurBg": "Arka Planı Bulanıklaştır",
+ "motionBlur": "Hareket Bulanıklığı",
+ "off": "kapalı",
+ "shadow": "Gölge",
+ "roundness": "Yuvarlaklık",
+ "padding": "Dolgu"
+ },
+ "background": {
+ "title": "Arka Plan",
+ "image": "Görüntü",
+ "color": "Renk",
+ "gradient": "Gradyan",
+ "uploadCustom": "Özel Yükle",
+ "gradientLabel": "Gradyan {{index}}"
+ },
+ "crop": {
+ "title": "Kırpma",
+ "cropVideo": "Videoyu Kırp",
+ "dragInstruction": "Kırpma alanını ayarlamak için her kenarı sürükleyin",
+ "ratio": "Oran",
+ "free": "Serbest",
+ "done": "Tamam",
+ "lockAspectRatio": "En boy oranını kilitle",
+ "unlockAspectRatio": "En boy oranının kilidini aç"
+ },
+ "exportFormat": {
+ "mp4": "MP4",
+ "gif": "GIF",
+ "mp4Video": "MP4 Video",
+ "mp4Description": "Yüksek kaliteli video dosyası",
+ "gifAnimation": "GIF Animasyon",
+ "gifDescription": "Paylaşım için hareketli görüntü"
+ },
+ "exportQuality": {
+ "title": "Dışa Aktarım Kalitesi",
+ "low": "Düşük",
+ "medium": "Orta",
+ "high": "Yüksek"
+ },
+ "gifSettings": {
+ "frameRate": "GIF Kare Hızı",
+ "size": "GIF Boyutu",
+ "loop": "GIF Döngüsü"
+ },
+ "project": {
+ "save": "Projeyi Kaydet",
+ "load": "Proje Yükle"
+ },
+ "export": {
+ "videoButton": "Videoyu Dışa Aktar",
+ "gifButton": "GIF Olarak Dışa Aktar",
+ "chooseSaveLocation": "Kayıt Konumu Seç"
+ },
+ "links": {
+ "reportBug": "Hata Bildir",
+ "starOnGithub": "GitHub'da Yıldızla"
+ },
+ "imageUpload": {
+ "invalidFileType": "Geçersiz dosya türü",
+ "jpgOnly": "Lütfen bir JPG veya JPEG görüntü dosyası yükleyin.",
+ "uploadSuccess": "Özel görüntü başarıyla yüklendi!",
+ "failedToUpload": "Görüntü yüklenemedi",
+ "errorReading": "Dosya okunurken bir hata oluştu."
+ },
+ "annotation": {
+ "title": "Açıklama Ayarları",
+ "active": "Aktif",
+ "typeText": "Metin",
+ "typeImage": "Görüntü",
+ "typeArrow": "Ok",
+ "typeBlur": "Bulanık",
+ "textContent": "Metin İçeriği",
+ "textPlaceholder": "Metninizi girin...",
+ "fontStyle": "Yazı Tipi Stili",
+ "selectStyle": "Stil seçin",
+ "size": "Boyut",
+ "customFonts": "Özel Yazı Tipleri",
+ "textColor": "Metin Rengi",
+ "background": "Arka Plan",
+ "none": "Yok",
+ "color": "Renk",
+ "clearBackground": "Arka Planı Temizle",
+ "uploadImage": "Görüntü Yükle",
+ "supportedFormats": "Desteklenen biçimler: JPG, PNG, GIF, WebP",
+ "arrowDirection": "Ok Yönü",
+ "strokeWidth": "Çizgi Kalınlığı: {{width}}px",
+ "arrowColor": "Ok Rengi",
+ "blurShape": "Bulanık Şekli",
+ "blurIntensity": "Bulanıklık Yoğunluğu",
+ "blurShapeRectangle": "Dikdörtgen",
+ "blurShapeOval": "Oval",
+ "blurShapeFreehand": "Serbest",
+ "deleteAnnotation": "Açıklamayı Sil",
+ "shortcutsAndTips": "Kısayollar ve İpuçları",
+ "tipMovePlayhead": "Oynatma imlecini çakışan açıklama bölümüne taşıyın ve bir öğe seçin.",
+ "tipTabCycle": "Çakışan öğeler arasında geçiş yapmak için Tab tuşunu kullanın.",
+ "tipShiftTabCycle": "Geriye doğru geçiş yapmak için Shift+Tab kullanın.",
+ "invalidImageType": "Geçersiz dosya türü",
+ "imageFormatsOnly": "Lütfen bir JPG, PNG, GIF veya WebP görüntü dosyası yükleyin.",
+ "imageUploadSuccess": "Görüntü başarıyla yüklendi!",
+ "failedImageUpload": "Görüntü yüklenemedi"
+ },
+ "fontStyles": {
+ "classic": "Klasik",
+ "editor": "Editör",
+ "strong": "Kalın",
+ "typewriter": "Daktilo",
+ "deco": "Dekoratif",
+ "simple": "Sade",
+ "modern": "Modern",
+ "clean": "Temiz"
+ },
+ "customFont": {
+ "dialogTitle": "Google Yazı Tipi Ekle",
+ "urlLabel": "Google Fonts İçe Aktarım URL'si",
+ "urlPlaceholder": "https://fonts.googleapis.com/css2?family=Roboto&display=swap",
+ "urlHelp": "Google Fonts'tan alabilirsiniz: Bir yazı tipi seçin → \"Get font\"a tıklayın → @import URL'sini kopyalayın",
+ "nameLabel": "Görünen Ad",
+ "namePlaceholder": "Özel Yazı Tipim",
+ "nameHelp": "Yazı tipinin seçicide nasıl görüneceğini belirler",
+ "addButton": "Yazı Tipi Ekle",
+ "addingButton": "Ekleniyor...",
+ "errorEmptyUrl": "Lütfen bir Google Fonts içe aktarım URL'si girin",
+ "errorInvalidUrl": "Lütfen geçerli bir Google Fonts URL'si girin",
+ "errorEmptyName": "Lütfen bir yazı tipi adı girin",
+ "errorExtractFailed": "URL'den yazı tipi ailesi çıkarılamadı",
+ "successMessage": "\"{{fontName}}\" yazı tipi başarıyla eklendi",
+ "failedToAdd": "Yazı tipi eklenemedi",
+ "errorTimeout": "Yazı tipinin yüklenmesi çok uzun sürdü. Lütfen URL'yi kontrol edip tekrar deneyin.",
+ "errorLoadFailed": "Yazı tipi yüklenemedi. Lütfen Google Fonts URL'sinin doğruluğunu kontrol edin."
+ },
+ "language": {
+ "title": "Dil"
+ },
+ "audio": {
+ "title": "Ses",
+ "noiseReduction": "Gürültü Azaltma",
+ "level": "Seviye",
+ "nrLevel": {
+ "light": "Hafif",
+ "moderate": "Orta",
+ "aggressive": "Güçlü"
+ },
+ "nrDescription": "Yapay zeka destekli gürültü azaltma arka plan gürültüsünü temizler. Daha yüksek seviyeler daha agresiftir ancak ses kalitesini etkileyebilir."
+ }
+}
diff --git a/src/i18n/locales/tr/shortcuts.json b/src/i18n/locales/tr/shortcuts.json
new file mode 100644
index 0000000..69b28ed
--- /dev/null
+++ b/src/i18n/locales/tr/shortcuts.json
@@ -0,0 +1,37 @@
+{
+ "title": "Klavye Kısayolları",
+ "customize": "Özelleştir",
+ "configurable": "Yapılandırılabilir",
+ "fixed": "Sabit",
+ "pressKey": "Bir tuşa basın…",
+ "clickToChange": "Değiştirmek için tıklayın",
+ "pressEscToCancel": "İptal etmek için Esc tuşuna basın",
+ "helpText": "Bir kısayola tıklayın, ardından yeni tuş kombinasyonuna basın. İptal etmek için Esc tuşuna basın.",
+ "resetToDefaults": "Varsayılanlara sıfırla",
+ "alreadyUsedBy": "\"{{action}}\" tarafından zaten kullanılıyor",
+ "swap": "Değiştir",
+ "reservedShortcut": "Bu kısayol \"{{label}}\" için ayrılmıştır ve yeniden atanamaz.",
+ "savedToast": "Klavye kısayolları kaydedildi",
+ "resetToast": "Varsayılan kısayollara sıfırlandı — uygulamak için Kaydet'e tıklayın",
+ "actions": {
+ "addZoom": "Yakınlaştırma Ekle",
+ "addTrim": "Kırpma Ekle",
+ "addSpeed": "Hız Ekle",
+ "addAnnotation": "Açıklama Ekle",
+ "addBlur": "Bulanik Ekle",
+ "addKeyframe": "Anahtar Kare Ekle",
+ "deleteSelected": "Seçileni Sil",
+ "playPause": "Oynat / Duraklat"
+ },
+ "fixedActions": {
+ "undo": "Geri Al",
+ "redo": "Yinele",
+ "cycleAnnotationsForward": "Açıklamalar Arasında İleri Geç",
+ "cycleAnnotationsBackward": "Açıklamalar Arasında Geri Geç",
+ "deleteSelectedAlt": "Seçileni Sil (alternatif)",
+ "panTimeline": "Zaman Çizelgesini Kaydır",
+ "zoomTimeline": "Zaman Çizelgesini Yakınlaştır",
+ "frameBack": "Önceki Kare",
+ "frameForward": "Sonraki Kare"
+ }
+}
diff --git a/src/i18n/locales/tr/timeline.json b/src/i18n/locales/tr/timeline.json
new file mode 100644
index 0000000..294640b
--- /dev/null
+++ b/src/i18n/locales/tr/timeline.json
@@ -0,0 +1,53 @@
+{
+ "buttons": {
+ "addZoom": "Yakınlaştırma Ekle (Z)",
+ "suggestZooms": "İmleçten Yakınlaştırma Öner",
+ "addTrim": "Kırpma Ekle (T)",
+ "addAnnotation": "Açıklama Ekle (A)",
+ "addSpeed": "Hız Ekle (S)",
+ "addBlur": "Bulanık ekle (B)"
+ },
+ "hints": {
+ "pressZoom": "Yakınlaştırma eklemek için Z tuşuna basın",
+ "pressTrim": "Kırpma eklemek için T tuşuna basın",
+ "pressAnnotation": "Açıklama eklemek için A tuşuna basın",
+ "pressSpeed": "Hız eklemek için S tuşuna basın",
+ "pressBlur": "Bulanık bölge eklemek için B tuşuna basın"
+ },
+ "labels": {
+ "pan": "Kaydır",
+ "zoom": "Yakınlaştır",
+ "zoomItem": "Yakınlaştırma {{index}}",
+ "trimItem": "Kırpma {{index}}",
+ "speedItem": "Hız {{index}}",
+ "annotationItem": "Açıklama",
+ "imageItem": "Görüntü",
+ "emptyText": "Boş metin",
+ "blurItem": "Bulanık {{index}}"
+ },
+ "emptyState": {
+ "noVideo": "Video Yüklenmedi",
+ "dragAndDrop": "Düzenlemeye başlamak için bir video sürükleyip bırakın"
+ },
+ "errors": {
+ "cannotPlaceZoom": "Buraya yakınlaştırma yerleştirilemiyor",
+ "zoomExistsAtLocation": "Bu konumda zaten bir yakınlaştırma var veya yeterli alan yok.",
+ "zoomSuggestionUnavailable": "Yakınlaştırma öneri işleyicisi kullanılamıyor",
+ "noCursorTelemetry": "İmleç telemetrisi mevcut değil",
+ "noCursorTelemetryDescription": "İmleç tabanlı öneriler oluşturmak için önce bir ekran kaydı yapın.",
+ "noUsableTelemetry": "Kullanılabilir imleç telemetrisi yok",
+ "noUsableTelemetryDescription": "Kayıt yeterli imleç hareketi verisi içermiyor.",
+ "noDwellMoments": "Belirgin imleç bekleme anları bulunamadı",
+ "noDwellMomentsDescription": "Önemli işlemlerde daha yavaş imleç duraklamaları olan bir kayıt deneyin.",
+ "noAutoZoomSlots": "Otomatik yakınlaştırma alanı yok",
+ "noAutoZoomSlotsDescription": "Algılanan bekleme noktaları mevcut yakınlaştırma bölgeleriyle çakışıyor.",
+ "cannotPlaceTrim": "Buraya kırpma yerleştirilemiyor",
+ "trimExistsAtLocation": "Bu konumda zaten bir kırpma var veya yeterli alan yok.",
+ "cannotPlaceSpeed": "Buraya hız yerleştirilemiyor",
+ "speedExistsAtLocation": "Bu konumda zaten bir hız bölgesi var veya yeterli alan yok."
+ },
+ "success": {
+ "addedZoomSuggestions": "{{count}} imleç tabanlı yakınlaştırma önerisi eklendi",
+ "addedZoomSuggestionsPlural": "{{count}} imleç tabanlı yakınlaştırma önerisi eklendi"
+ }
+}
diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json
index 9a3cc1c..d8bff69 100644
--- a/src/i18n/locales/zh-CN/common.json
+++ b/src/i18n/locales/zh-CN/common.json
@@ -23,7 +23,7 @@
"exitFullscreen": "退出全屏"
},
"locale": {
- "name": "中文",
- "short": "中文"
+ "name": "简体中文",
+ "short": "简中"
}
}
diff --git a/src/i18n/locales/zh-CN/dialogs.json b/src/i18n/locales/zh-CN/dialogs.json
index 3f181bc..0385b36 100644
--- a/src/i18n/locales/zh-CN/dialogs.json
+++ b/src/i18n/locales/zh-CN/dialogs.json
@@ -27,10 +27,11 @@
"triggerLabel": "剪辑功能说明",
"title": "剪辑功能说明",
"description": "了解如何剪掉视频中不需要的部分。",
- "explanation": "剪辑工具通过定义您要",
- "explanationRemove": "移除",
- "explanationCovered": "覆盖",
- "explanationEnd": "的片段来工作。被红色剪辑区域覆盖的部分将在导出时被剪掉。",
+ "explanationBefore": "剪辑工具通过定义您要",
+ "remove": "移除",
+ "explanationMiddle": "——任何被",
+ "covered": "覆盖",
+ "explanationAfter": "的红色剪辑区域部分将在导出时被剪掉。",
"visualExample": "示例演示",
"removed": "已移除",
"kept": "保留",
@@ -39,7 +40,8 @@
"part3": "第 3 部分",
"finalVideo": "最终视频",
"step1Title": "1. 添加剪辑",
- "step1Description": "按 T 或点击剪刀图标来标记要移除的片段。",
+ "step1DescriptionBefore": "按",
+ "step1DescriptionAfter": "键或点击剪刀图标来标记要移除的片段。",
"step2Title": "2. 调整",
"step2Description": "拖动红色区域的边缘,精确覆盖您要剪掉的部分。"
},
diff --git a/src/i18n/locales/zh-CN/editor.json b/src/i18n/locales/zh-CN/editor.json
index 5d27bef..1980354 100644
--- a/src/i18n/locales/zh-CN/editor.json
+++ b/src/i18n/locales/zh-CN/editor.json
@@ -1,4 +1,10 @@
{
+ "newRecording": {
+ "title": "返回录屏",
+ "description": "当前会话已保存。",
+ "cancel": "取消",
+ "confirm": "确认"
+ },
"errors": {
"noVideoLoaded": "未加载视频",
"videoNotReady": "视频未就绪",
@@ -30,6 +36,8 @@
"systemAudioUnavailable": "系统音频不可用。将在无系统音频的情况下录制。",
"microphoneDenied": "麦克风权限被拒绝。录制将继续,但不包含音频。",
"cameraDenied": "摄像头权限被拒绝。录制将继续,但不包含摄像头画面。",
+ "cameraDisconnected": "摄像头已断开连接。",
+ "cameraNotFound": "未找到摄像头。",
"permissionDenied": "录屏权限被拒绝。请允许屏幕录制。"
}
}
diff --git a/src/i18n/locales/zh-CN/launch.json b/src/i18n/locales/zh-CN/launch.json
index 6b63df1..a5c2a9d 100644
--- a/src/i18n/locales/zh-CN/launch.json
+++ b/src/i18n/locales/zh-CN/launch.json
@@ -33,5 +33,11 @@
"recording": {
"selectSource": "请选择要录制的源"
},
- "language": "语言"
+ "language": "语言",
+ "systemLanguagePrompt": {
+ "title": "使用系统语言吗?",
+ "description": "我们检测到你的系统语言是{{language}}。是否将 OpenScreen 切换为{{language}}?",
+ "switch": "切换到{{language}}",
+ "keepDefault": "保持当前语言"
+ }
}
diff --git a/src/i18n/locales/zh-CN/settings.json b/src/i18n/locales/zh-CN/settings.json
index ab0d41b..10a8ecd 100644
--- a/src/i18n/locales/zh-CN/settings.json
+++ b/src/i18n/locales/zh-CN/settings.json
@@ -8,12 +8,21 @@
"manual": "手动",
"auto": "自动",
"autoDescription": "摄像头跟随录制时的光标位置"
+ },
+ "speed": {
+ "title": "缩放速度",
+ "instant": "即时",
+ "fast": "快速",
+ "smooth": "平滑",
+ "lazy": "缓慢"
}
},
"speed": {
"playbackSpeed": "播放速度",
"selectRegion": "选择要调整的速度区域",
- "deleteRegion": "删除速度区域"
+ "deleteRegion": "删除速度区域",
+ "customPlaybackSpeed": "自定义播放速度",
+ "maxSpeedError": "速度不能超过 16×"
},
"trim": {
"deleteRegion": "删除剪辑区域"
@@ -24,7 +33,9 @@
"selectPreset": "选择预设",
"pictureInPicture": "画中画",
"verticalStack": "垂直堆叠",
- "webcamShape": "摄像头形状"
+ "dualFrame": "双画框",
+ "webcamShape": "摄像头形状",
+ "webcamSize": "摄像头大小"
},
"effects": {
"title": "视频效果",
@@ -98,6 +109,7 @@
"typeText": "文本",
"typeImage": "图片",
"typeArrow": "箭头",
+ "typeBlur": "模糊",
"textContent": "文本内容",
"textPlaceholder": "输入您的文本...",
"fontStyle": "字体样式",
@@ -114,6 +126,11 @@
"arrowDirection": "箭头方向",
"strokeWidth": "描边宽度:{{width}}px",
"arrowColor": "箭头颜色",
+ "blurShape": "模糊形状",
+ "blurIntensity": "模糊强度",
+ "blurShapeRectangle": "矩形",
+ "blurShapeOval": "椭圆",
+ "blurShapeFreehand": "自由手绘",
"deleteAnnotation": "删除标注",
"shortcutsAndTips": "快捷键与提示",
"tipMovePlayhead": "将播放头移动到重叠的标注区域并选择一个项目。",
diff --git a/src/i18n/locales/zh-CN/shortcuts.json b/src/i18n/locales/zh-CN/shortcuts.json
index e2faa2f..3328239 100644
--- a/src/i18n/locales/zh-CN/shortcuts.json
+++ b/src/i18n/locales/zh-CN/shortcuts.json
@@ -18,6 +18,7 @@
"addTrim": "添加剪辑",
"addSpeed": "添加速度",
"addAnnotation": "添加标注",
+ "addBlur": "添加模糊",
"addKeyframe": "添加关键帧",
"deleteSelected": "删除所选",
"playPause": "播放 / 暂停"
diff --git a/src/i18n/locales/zh-CN/timeline.json b/src/i18n/locales/zh-CN/timeline.json
index d712751..7841dcb 100644
--- a/src/i18n/locales/zh-CN/timeline.json
+++ b/src/i18n/locales/zh-CN/timeline.json
@@ -4,13 +4,15 @@
"suggestZooms": "根据光标建议缩放",
"addTrim": "添加剪辑 (T)",
"addAnnotation": "添加标注 (A)",
- "addSpeed": "添加速度 (S)"
+ "addSpeed": "添加速度 (S)",
+ "addBlur": "添加模糊 (B)"
},
"hints": {
"pressZoom": "按 Z 添加缩放",
"pressTrim": "按 T 添加剪辑",
"pressAnnotation": "按 A 添加标注",
- "pressSpeed": "按 S 添加速度"
+ "pressSpeed": "按 S 添加速度",
+ "pressBlur": "按 B 添加模糊区域"
},
"labels": {
"pan": "平移",
@@ -20,7 +22,8 @@
"speedItem": "速度 {{index}}",
"annotationItem": "标注",
"imageItem": "图片",
- "emptyText": "空文本"
+ "emptyText": "空文本",
+ "blurItem": "模糊 {{index}}"
},
"emptyState": {
"noVideo": "未加载视频",
diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json
new file mode 100644
index 0000000..971d9ab
--- /dev/null
+++ b/src/i18n/locales/zh-TW/common.json
@@ -0,0 +1,29 @@
+{
+ "actions": {
+ "cancel": "取消",
+ "save": "儲存",
+ "delete": "刪除",
+ "close": "關閉",
+ "share": "分享",
+ "done": "完成",
+ "open": "開啟",
+ "upload": "上傳",
+ "export": "匯出",
+ "file": "檔案",
+ "edit": "編輯",
+ "view": "檢視",
+ "window": "視窗",
+ "quit": "退出",
+ "stopRecording": "停止錄製"
+ },
+ "playback": {
+ "play": "播放",
+ "pause": "暫停",
+ "fullscreen": "全螢幕",
+ "exitFullscreen": "退出全螢幕"
+ },
+ "locale": {
+ "name": "繁體中文",
+ "short": "繁中"
+ }
+}
diff --git a/src/i18n/locales/zh-TW/dialogs.json b/src/i18n/locales/zh-TW/dialogs.json
new file mode 100644
index 0000000..b582aba
--- /dev/null
+++ b/src/i18n/locales/zh-TW/dialogs.json
@@ -0,0 +1,70 @@
+{
+ "export": {
+ "complete": "匯出完成",
+ "yourFormatReady": "您的 {{format}} 已準備就緒",
+ "showInFolder": "在資料夾中顯示",
+ "finalizingVideo": "正在完成影片匯出...",
+ "compilingGifProgress": "正在編譯 GIF... {{progress}}%",
+ "compilingGifWait": "正在編譯 GIF... 這可能需要一些時間",
+ "takeMoment": "這可能需要一點時間...",
+ "failed": "匯出失敗",
+ "tryAgain": "請重試",
+ "finalizingVideoTitle": "正在完成影片",
+ "compilingGif": "正在編譯 GIF",
+ "exportingFormat": "正在匯出 {{format}}",
+ "compiling": "編譯中",
+ "renderingFrames": "渲染影格",
+ "processing": "處理中...",
+ "finalizing": "正在完成...",
+ "compilingStatus": "編譯中...",
+ "status": "狀態",
+ "format": "格式",
+ "frames": "影格",
+ "cancelExport": "取消匯出",
+ "savedSuccessfully": "{{format}} 儲存成功!"
+ },
+ "tutorial": {
+ "triggerLabel": "剪輯功能說明",
+ "title": "剪輯功能說明",
+ "description": "了解如何剪掉影片中不需要的部分。",
+ "explanationBefore": "剪輯工具透過定義您要",
+ "remove": "移除",
+ "explanationMiddle": "——任何被",
+ "covered": "覆蓋",
+ "explanationAfter": "的紅色剪輯區域部分將在匯出時被剪掉。",
+ "visualExample": "示例演示",
+ "removed": "已移除",
+ "kept": "保留",
+ "part1": "第 1 部分",
+ "part2": "第 2 部分",
+ "part3": "第 3 部分",
+ "finalVideo": "最終影片",
+ "step1Title": "1. 添加剪輯",
+ "step1DescriptionBefore": "按",
+ "step1DescriptionAfter": "鍵或點擊剪刀圖示來標記要移除的片段。",
+ "step2Title": "2. 調整",
+ "step2Description": "拖動紅色區域的邊緣,精確覆蓋您要剪掉的部分。"
+ },
+ "unsavedChanges": {
+ "title": "未儲存的變更",
+ "message": "您有未儲存的變更。",
+ "detail": "是否在關閉前儲存專案?",
+ "saveAndClose": "儲存並關閉",
+ "discardAndClose": "捨棄並關閉",
+ "loadProject": "載入專案…",
+ "saveProject": "儲存專案…",
+ "saveProjectAs": "專案另存新檔…"
+ },
+ "fileDialogs": {
+ "saveGif": "儲存匯出的 GIF",
+ "saveVideo": "儲存匯出的影片",
+ "selectVideo": "選擇影片檔案",
+ "saveProject": "儲存 OpenScreen 專案",
+ "openProject": "開啟 OpenScreen 專案",
+ "gifImage": "GIF 圖片",
+ "mp4Video": "MP4 影片",
+ "videoFiles": "影片檔案",
+ "openscreenProject": "OpenScreen 專案",
+ "allFiles": "所有檔案"
+ }
+}
diff --git a/src/i18n/locales/zh-TW/editor.json b/src/i18n/locales/zh-TW/editor.json
new file mode 100644
index 0000000..73a3f4e
--- /dev/null
+++ b/src/i18n/locales/zh-TW/editor.json
@@ -0,0 +1,41 @@
+{
+ "newRecording": {
+ "title": "返回錄影",
+ "description": "目前工作階段已儲存。",
+ "cancel": "取消",
+ "confirm": "確認"
+ },
+ "errors": {
+ "noVideoLoaded": "未載入影片",
+ "videoNotReady": "影片未就緒",
+ "unableToDetermineSourcePath": "無法確定來源影片路徑",
+ "failedToSaveGif": "儲存 GIF 失敗",
+ "gifExportFailed": "GIF 匯出失敗",
+ "failedToSaveVideo": "儲存影片失敗",
+ "exportFailed": "匯出失敗",
+ "exportFailedWithError": "匯出失敗:{{error}}",
+ "failedToSaveExport": "儲存匯出檔案失敗",
+ "failedToSaveExportedVideo": "儲存匯出的影片失敗",
+ "failedToRevealInFolder": "在資料夾中顯示時出錯:{{error}}"
+ },
+ "export": {
+ "canceled": "匯出已取消",
+ "exportedSuccessfully": "{{format}} 匯出成功"
+ },
+ "project": {
+ "saveCanceled": "專案儲存已取消",
+ "failedToSave": "儲存專案失敗",
+ "savedTo": "專案已儲存至 {{path}}",
+ "failedToLoad": "載入專案失敗",
+ "invalidFormat": "無效的專案檔案格式",
+ "loadedFrom": "專案已從 {{path}} 載入"
+ },
+ "recording": {
+ "failedCameraAccess": "請求攝影機權限失敗。",
+ "cameraBlocked": "攝影機權限已被封鎖。請在系統設定中啟用以使用攝影機。",
+ "systemAudioUnavailable": "系統音訊不可用。將在無系統音訊的情況下錄製。",
+ "microphoneDenied": "麥克風權限被拒絕。錄製將繼續,但不包含音訊。",
+ "cameraDenied": "攝影機權限被拒絕。錄製將繼續,但不包含攝影機畫面。",
+ "permissionDenied": "錄影權限被拒絕。請允許螢幕錄製。"
+ }
+}
diff --git a/src/i18n/locales/zh-TW/launch.json b/src/i18n/locales/zh-TW/launch.json
new file mode 100644
index 0000000..e8b723f
--- /dev/null
+++ b/src/i18n/locales/zh-TW/launch.json
@@ -0,0 +1,37 @@
+{
+ "tooltips": {
+ "hideHUD": "隱藏控制面板",
+ "closeApp": "關閉應用程式",
+ "restartRecording": "重新開始錄製",
+ "cancelRecording": "取消錄製",
+ "pauseRecording": "暫停錄製",
+ "resumeRecording": "繼續錄製",
+ "openVideoFile": "開啟影片檔案",
+ "openProject": "開啟專案"
+ },
+ "audio": {
+ "enableSystemAudio": "啟用系統音訊",
+ "disableSystemAudio": "停用系統音訊",
+ "enableMicrophone": "啟用麥克風",
+ "disableMicrophone": "停用麥克風",
+ "defaultMicrophone": "預設麥克風"
+ },
+ "webcam": {
+ "enableWebcam": "啟用攝影機",
+ "disableWebcam": "停用攝影機",
+ "defaultCamera": "預設攝影機",
+ "searching": "正在搜尋...",
+ "noneFound": "未找到攝影機",
+ "unavailable": "攝影機不可用"
+ },
+ "sourceSelector": {
+ "loading": "正在載入來源...",
+ "screens": "螢幕 ({{count}})",
+ "windows": "視窗 ({{count}})",
+ "defaultSourceName": "螢幕"
+ },
+ "recording": {
+ "selectSource": "請選擇要錄製的來源"
+ },
+ "language": "語言"
+}
diff --git a/src/i18n/locales/zh-TW/settings.json b/src/i18n/locales/zh-TW/settings.json
new file mode 100644
index 0000000..6344a99
--- /dev/null
+++ b/src/i18n/locales/zh-TW/settings.json
@@ -0,0 +1,176 @@
+{
+ "zoom": {
+ "level": "縮放級別",
+ "selectRegion": "選擇要調整的縮放區域",
+ "deleteZoom": "刪除縮放",
+ "focusMode": {
+ "title": "對焦模式",
+ "manual": "手動",
+ "auto": "自動",
+ "autoDescription": "攝影機跟隨錄製時的游標位置"
+ },
+ "speed": {
+ "title": "縮放速度",
+ "instant": "即時",
+ "fast": "快速",
+ "smooth": "平滑",
+ "lazy": "緩慢"
+ }
+ },
+ "speed": {
+ "playbackSpeed": "播放速度",
+ "selectRegion": "選擇要調整的速度區域",
+ "deleteRegion": "刪除速度區域",
+ "customPlaybackSpeed": "自訂播放速度",
+ "maxSpeedError": "速度不能超過 16×"
+ },
+ "trim": {
+ "deleteRegion": "刪除剪輯區域"
+ },
+ "layout": {
+ "title": "版面配置",
+ "preset": "預設",
+ "selectPreset": "選擇預設",
+ "pictureInPicture": "子母畫面",
+ "verticalStack": "垂直堆疊",
+ "dualFrame": "雙畫框",
+ "webcamShape": "攝影機形狀",
+ "webcamSize": "攝影機大小"
+ },
+ "effects": {
+ "title": "影片效果",
+ "blurBg": "模糊背景",
+ "motionBlur": "動態模糊",
+ "off": "關",
+ "shadow": "陰影",
+ "roundness": "圓角",
+ "padding": "內邊距"
+ },
+ "background": {
+ "title": "背景",
+ "image": "圖片",
+ "color": "顏色",
+ "gradient": "漸層",
+ "uploadCustom": "上傳自訂",
+ "gradientLabel": "漸層 {{index}}"
+ },
+ "crop": {
+ "title": "裁剪",
+ "cropVideo": "裁剪影片",
+ "dragInstruction": "拖動每一側來調整裁剪區域",
+ "ratio": "比例",
+ "free": "自由",
+ "done": "完成",
+ "lockAspectRatio": "鎖定長寬比",
+ "unlockAspectRatio": "解鎖長寬比"
+ },
+ "exportFormat": {
+ "mp4": "MP4",
+ "gif": "GIF",
+ "mp4Video": "MP4 影片",
+ "mp4Description": "高品質影片檔案",
+ "gifAnimation": "GIF 動畫",
+ "gifDescription": "可分享的動態圖片"
+ },
+ "exportQuality": {
+ "title": "匯出品質",
+ "low": "低",
+ "medium": "中",
+ "high": "高"
+ },
+ "gifSettings": {
+ "frameRate": "GIF 影格率",
+ "size": "GIF 尺寸",
+ "loop": "循環 GIF"
+ },
+ "project": {
+ "save": "儲存專案",
+ "load": "載入專案"
+ },
+ "export": {
+ "videoButton": "匯出影片",
+ "gifButton": "匯出 GIF",
+ "chooseSaveLocation": "選擇儲存位置"
+ },
+ "links": {
+ "reportBug": "回報錯誤",
+ "starOnGithub": "在 GitHub 上加星"
+ },
+ "imageUpload": {
+ "invalidFileType": "無效的檔案類型",
+ "jpgOnly": "請上傳 JPG 或 JPEG 格式的圖片檔案。",
+ "uploadSuccess": "自訂圖片上傳成功!",
+ "failedToUpload": "上傳圖片失敗",
+ "errorReading": "讀取檔案時出錯。"
+ },
+ "annotation": {
+ "title": "標註設定",
+ "active": "啟用",
+ "typeText": "文字",
+ "typeImage": "圖片",
+ "typeArrow": "箭頭",
+ "typeBlur": "模糊",
+ "textContent": "文字內容",
+ "textPlaceholder": "輸入您的文字...",
+ "fontStyle": "字體樣式",
+ "selectStyle": "選擇樣式",
+ "size": "大小",
+ "customFonts": "自訂字體",
+ "textColor": "文字顏色",
+ "background": "背景",
+ "none": "無",
+ "color": "顏色",
+ "clearBackground": "清除背景",
+ "uploadImage": "上傳圖片",
+ "supportedFormats": "支援的格式:JPG、PNG、GIF、WebP",
+ "arrowDirection": "箭頭方向",
+ "strokeWidth": "描邊寬度:{{width}}px",
+ "arrowColor": "箭頭顏色",
+ "blurShape": "模糊形狀",
+ "blurIntensity": "模糊強度",
+ "blurShapeRectangle": "矩形",
+ "blurShapeOval": "橢圓",
+ "blurShapeFreehand": "自由手繪",
+ "deleteAnnotation": "刪除標註",
+ "shortcutsAndTips": "快捷鍵與提示",
+ "tipMovePlayhead": "將播放頭移動到重疊的標註區域並選擇一個項目。",
+ "tipTabCycle": "使用 Tab 鍵在重疊項目之間循環切換。",
+ "tipShiftTabCycle": "使用 Shift+Tab 反向循環切換。",
+ "invalidImageType": "無效的檔案類型",
+ "imageFormatsOnly": "請上傳 JPG、PNG、GIF 或 WebP 格式的圖片檔案。",
+ "imageUploadSuccess": "圖片上傳成功!",
+ "failedImageUpload": "上傳圖片失敗"
+ },
+ "fontStyles": {
+ "classic": "經典",
+ "editor": "編輯器",
+ "strong": "粗體",
+ "typewriter": "打字機",
+ "deco": "裝飾",
+ "simple": "簡約",
+ "modern": "現代",
+ "clean": "簡潔"
+ },
+ "customFont": {
+ "dialogTitle": "新增 Google 字體",
+ "urlLabel": "Google Fonts 匯入 URL",
+ "urlPlaceholder": "https://fonts.googleapis.com/css2?family=Roboto&display=swap",
+ "urlHelp": "從 Google Fonts 取得:選擇字體 → 點擊 \"Get font\" → 複製 @import URL",
+ "nameLabel": "顯示名稱",
+ "namePlaceholder": "我的自訂字體",
+ "nameHelp": "這是字體在字體選擇器中顯示的名稱",
+ "addButton": "新增字體",
+ "addingButton": "新增中...",
+ "errorEmptyUrl": "請輸入 Google Fonts 匯入 URL",
+ "errorInvalidUrl": "請輸入有效的 Google Fonts URL",
+ "errorEmptyName": "請輸入字體名稱",
+ "errorExtractFailed": "無法從 URL 中提取字體系列",
+ "successMessage": "字體 \"{{fontName}}\" 新增成功",
+ "failedToAdd": "新增字體失敗",
+ "errorTimeout": "字體載入時間過長。請檢查 URL 並重試。",
+ "errorLoadFailed": "無法載入該字體。請確認 Google Fonts URL 是否正確。"
+ },
+ "language": {
+ "title": "語言"
+ }
+}
diff --git a/src/i18n/locales/zh-TW/shortcuts.json b/src/i18n/locales/zh-TW/shortcuts.json
new file mode 100644
index 0000000..54c0cfc
--- /dev/null
+++ b/src/i18n/locales/zh-TW/shortcuts.json
@@ -0,0 +1,37 @@
+{
+ "title": "鍵盤快捷鍵",
+ "customize": "自訂",
+ "configurable": "可設定",
+ "fixed": "固定",
+ "pressKey": "請按下按鍵…",
+ "clickToChange": "點擊以變更",
+ "pressEscToCancel": "按 Esc 取消",
+ "helpText": "點擊一個快捷鍵,然後按下新的組合鍵。按 Esc 取消。",
+ "resetToDefaults": "還原預設設定",
+ "alreadyUsedBy": "已被 \"{{action}}\" 使用",
+ "swap": "交換",
+ "reservedShortcut": "此快捷鍵已保留給 \"{{label}}\",無法重新指定。",
+ "savedToast": "鍵盤快捷鍵已儲存",
+ "resetToast": "已還原預設快捷鍵 — 點擊儲存以套用",
+ "actions": {
+ "addZoom": "新增縮放",
+ "addTrim": "新增剪輯",
+ "addSpeed": "新增速度",
+ "addAnnotation": "新增標註",
+ "addBlur": "新增模糊",
+ "addKeyframe": "新增關鍵影格",
+ "deleteSelected": "刪除所選",
+ "playPause": "播放 / 暫停"
+ },
+ "fixedActions": {
+ "undo": "復原",
+ "redo": "重做",
+ "cycleAnnotationsForward": "向前切換標註",
+ "cycleAnnotationsBackward": "向後切換標註",
+ "deleteSelectedAlt": "刪除所選(替代)",
+ "panTimeline": "平移時間軸",
+ "zoomTimeline": "縮放時間軸",
+ "frameBack": "上一影格",
+ "frameForward": "下一影格"
+ }
+}
diff --git a/src/i18n/locales/zh-TW/timeline.json b/src/i18n/locales/zh-TW/timeline.json
new file mode 100644
index 0000000..52457d6
--- /dev/null
+++ b/src/i18n/locales/zh-TW/timeline.json
@@ -0,0 +1,53 @@
+{
+ "buttons": {
+ "addZoom": "新增縮放 (Z)",
+ "suggestZooms": "根據游標建議縮放",
+ "addTrim": "新增剪輯 (T)",
+ "addAnnotation": "新增標註 (A)",
+ "addSpeed": "新增速度 (S)",
+ "addBlur": "新增模糊 (B)"
+ },
+ "hints": {
+ "pressZoom": "按 Z 新增縮放",
+ "pressTrim": "按 T 新增剪輯",
+ "pressAnnotation": "按 A 新增標註",
+ "pressSpeed": "按 S 新增速度",
+ "pressBlur": "按 B 新增模糊區域"
+ },
+ "labels": {
+ "pan": "平移",
+ "zoom": "縮放",
+ "zoomItem": "縮放 {{index}}",
+ "trimItem": "剪輯 {{index}}",
+ "speedItem": "速度 {{index}}",
+ "annotationItem": "標註",
+ "imageItem": "圖片",
+ "emptyText": "空文字",
+ "blurItem": "模糊 {{index}}"
+ },
+ "emptyState": {
+ "noVideo": "未載入影片",
+ "dragAndDrop": "拖放影片以開始編輯"
+ },
+ "errors": {
+ "cannotPlaceZoom": "無法在此處放置縮放",
+ "zoomExistsAtLocation": "此位置已存在縮放或沒有足夠的空間。",
+ "zoomSuggestionUnavailable": "縮放建議處理器不可用",
+ "noCursorTelemetry": "無可用的游標遙測資料",
+ "noCursorTelemetryDescription": "請先錄製一段螢幕錄影以產生基於游標的建議。",
+ "noUsableTelemetry": "無可用的游標遙測資料",
+ "noUsableTelemetryDescription": "錄製內容沒有包含足夠的游標移動資料。",
+ "noDwellMoments": "未找到明確的游標停留時刻",
+ "noDwellMomentsDescription": "請嘗試在重要操作上進行較慢游標停留的錄製。",
+ "noAutoZoomSlots": "無可用的自動縮放位置",
+ "noAutoZoomSlotsDescription": "偵測到的停留點與現有縮放區域重疊。",
+ "cannotPlaceTrim": "無法在此處放置剪輯",
+ "trimExistsAtLocation": "此位置已存在剪輯或沒有足夠的空間。",
+ "cannotPlaceSpeed": "無法在此處放置速度",
+ "speedExistsAtLocation": "此位置已存在速度區域或沒有足夠的空間。"
+ },
+ "success": {
+ "addedZoomSuggestions": "已新增 {{count}} 個基於游標的縮放建議",
+ "addedZoomSuggestionsPlural": "已新增 {{count}} 個基於游標的縮放建議"
+ }
+}
diff --git a/src/index.css b/src/index.css
index 74f5669..694f8bd 100644
--- a/src/index.css
+++ b/src/index.css
@@ -88,6 +88,10 @@
display: none; /* Chrome, Safari, Opera */
}
+ .squircle {
+ corner-shape: squircle;
+ }
+
/* Smooth playback scrubber */
input[type="range"] {
-webkit-appearance: none;
diff --git a/src/lib/blurEffects.test.ts b/src/lib/blurEffects.test.ts
new file mode 100644
index 0000000..4797e69
--- /dev/null
+++ b/src/lib/blurEffects.test.ts
@@ -0,0 +1,80 @@
+import { describe, expect, it } from "vitest";
+import { applyMosaicToImageData, getBlurOverlayColor, normalizeBlurColor } from "./blurEffects";
+
+function createTestImageData(width: number, height: number) {
+ const data = new Uint8ClampedArray(width * height * 4);
+
+ for (let y = 0; y < height; y++) {
+ for (let x = 0; x < width; x++) {
+ const offset = (y * width + x) * 4;
+ data[offset] = x * 20 + y;
+ data[offset + 1] = y * 20 + x;
+ data[offset + 2] = (x + y) * 10;
+ data[offset + 3] = 255;
+ }
+ }
+
+ return {
+ data,
+ width,
+ height,
+ } as ImageData;
+}
+
+describe("applyMosaicToImageData", () => {
+ it("collapses each block to a single representative color", () => {
+ const imageData = createTestImageData(4, 4);
+ const original = new Uint8ClampedArray(imageData.data);
+
+ applyMosaicToImageData(imageData, 2);
+
+ const topLeft = Array.from(imageData.data.slice(0, 4));
+ const topRightOffset = (1 * 4 + 1) * 4;
+ const topRight = Array.from(imageData.data.slice(topRightOffset, topRightOffset + 4));
+ expect(topLeft).toEqual(topRight);
+
+ expect(Array.from(original.slice(0, 4))).not.toEqual(topLeft);
+ });
+
+ it("reduces unique pixel colors, making the transform information-lossy", () => {
+ const imageData = createTestImageData(8, 8);
+ const before = new Set();
+ const after = new Set();
+
+ for (let i = 0; i < imageData.data.length; i += 4) {
+ before.add(
+ `${imageData.data[i]}-${imageData.data[i + 1]}-${imageData.data[i + 2]}-${imageData.data[i + 3]}`,
+ );
+ }
+
+ applyMosaicToImageData(imageData, 4);
+
+ for (let i = 0; i < imageData.data.length; i += 4) {
+ after.add(
+ `${imageData.data[i]}-${imageData.data[i + 1]}-${imageData.data[i + 2]}-${imageData.data[i + 3]}`,
+ );
+ }
+
+ expect(after.size).toBeLessThan(before.size);
+ expect(after.size).toBe(4);
+ });
+});
+
+describe("blur color helpers", () => {
+ it("normalizes invalid blur colors to white", () => {
+ expect(normalizeBlurColor("black")).toBe("black");
+ expect(normalizeBlurColor("invalid")).toBe("white");
+ });
+
+ it("returns a dark overlay when black blur color is selected", () => {
+ expect(
+ getBlurOverlayColor({
+ type: "blur",
+ shape: "rectangle",
+ color: "black",
+ intensity: 12,
+ blockSize: 12,
+ }),
+ ).toBe("rgba(0, 0, 0, 0.18)");
+ });
+});
diff --git a/src/lib/blurEffects.ts b/src/lib/blurEffects.ts
new file mode 100644
index 0000000..6933924
--- /dev/null
+++ b/src/lib/blurEffects.ts
@@ -0,0 +1,113 @@
+import {
+ type BlurColor,
+ type BlurData,
+ type BlurType,
+ DEFAULT_BLUR_BLOCK_SIZE,
+ DEFAULT_BLUR_INTENSITY,
+ MAX_BLUR_BLOCK_SIZE,
+ MAX_BLUR_INTENSITY,
+ MIN_BLUR_BLOCK_SIZE,
+ MIN_BLUR_INTENSITY,
+} from "@/components/video-editor/types";
+
+function clamp(value: number, min: number, max: number) {
+ if (!Number.isFinite(value)) return min;
+ return Math.min(max, Math.max(min, value));
+}
+
+export function normalizeBlurType(value: unknown): BlurType {
+ return value === "mosaic" ? "mosaic" : "blur";
+}
+
+export function normalizeBlurColor(value: unknown): BlurColor {
+ return value === "black" ? "black" : "white";
+}
+
+export function getNormalizedBlurIntensity(blurData?: BlurData | null): number {
+ return clamp(
+ blurData?.intensity ?? DEFAULT_BLUR_INTENSITY,
+ MIN_BLUR_INTENSITY,
+ MAX_BLUR_INTENSITY,
+ );
+}
+
+export function getNormalizedMosaicBlockSize(blurData?: BlurData | null, scaleFactor = 1): number {
+ const rawBlockSize = clamp(
+ blurData?.blockSize ?? DEFAULT_BLUR_BLOCK_SIZE,
+ MIN_BLUR_BLOCK_SIZE,
+ MAX_BLUR_BLOCK_SIZE,
+ );
+ return Math.max(1, Math.round(rawBlockSize * Math.max(scaleFactor, 0.01)));
+}
+
+export function getBlurOverlayColor(blurData?: BlurData | null): string {
+ const blurColor = normalizeBlurColor(blurData?.color);
+ const blurType = normalizeBlurType(blurData?.type);
+
+ if (blurColor === "black") {
+ return blurType === "mosaic" ? "rgba(0, 0, 0, 0.72)" : "rgba(0, 0, 0, 0.56)";
+ }
+
+ return blurType === "mosaic" ? "rgba(255, 255, 255, 0.06)" : "rgba(255, 255, 255, 0.02)";
+}
+
+export function getMosaicGridOverlayColor(blurData?: BlurData | null): string {
+ return normalizeBlurColor(blurData?.color) === "black"
+ ? "rgba(255,255,255,0.05)"
+ : "rgba(255,255,255,0.04)";
+}
+
+export function applyMosaicToImageData(imageData: ImageData, blockSize: number): ImageData {
+ const width = imageData.width;
+ const height = imageData.height;
+ const data = imageData.data;
+ const normalizedBlockSize = Math.max(1, Math.floor(blockSize));
+
+ if (width <= 0 || height <= 0 || normalizedBlockSize <= 1) {
+ return imageData;
+ }
+
+ for (let blockY = 0; blockY < height; blockY += normalizedBlockSize) {
+ for (let blockX = 0; blockX < width; blockX += normalizedBlockSize) {
+ const blockWidth = Math.min(normalizedBlockSize, width - blockX);
+ const blockHeight = Math.min(normalizedBlockSize, height - blockY);
+ const pixelCount = blockWidth * blockHeight;
+
+ if (pixelCount <= 0) {
+ continue;
+ }
+
+ let redTotal = 0;
+ let greenTotal = 0;
+ let blueTotal = 0;
+ let alphaTotal = 0;
+
+ for (let y = blockY; y < blockY + blockHeight; y++) {
+ for (let x = blockX; x < blockX + blockWidth; x++) {
+ const offset = (y * width + x) * 4;
+ redTotal += data[offset];
+ greenTotal += data[offset + 1];
+ blueTotal += data[offset + 2];
+ alphaTotal += data[offset + 3];
+ }
+ }
+
+ const averageRed = Math.round(redTotal / pixelCount);
+ const averageGreen = Math.round(greenTotal / pixelCount);
+ const averageBlue = Math.round(blueTotal / pixelCount);
+ const averageAlpha = Math.round(alphaTotal / pixelCount);
+
+ for (let y = blockY; y < blockY + blockHeight; y++) {
+ for (let x = blockX; x < blockX + blockWidth; x++) {
+ const offset = (y * width + x) * 4;
+ data[offset] = averageRed;
+ data[offset + 1] = averageGreen;
+ data[offset + 2] = averageBlue;
+ data[offset + 3] = averageAlpha;
+ }
+ }
+ }
+ }
+
+ return imageData;
+}
diff --git a/src/lib/compositeLayout.test.ts b/src/lib/compositeLayout.test.ts
index 93ce2c5..596eb75 100644
--- a/src/lib/compositeLayout.test.ts
+++ b/src/lib/compositeLayout.test.ts
@@ -24,16 +24,111 @@ describe("computeCompositeLayout", () => {
webcamSize: { width: 1920, height: 1080 },
});
+ const refDim = Math.sqrt(1280 * 720);
+ const defaultFraction = 25 / 100; // DEFAULT_WEBCAM_SIZE_PRESET = 25
expect(layout).not.toBeNull();
expect(layout!.webcamRect).not.toBeNull();
- expect(layout!.webcamRect!.width).toBeLessThanOrEqual(Math.round(1280 * 0.18) + 1);
- expect(layout!.webcamRect!.height).toBeLessThanOrEqual(Math.round(720 * 0.18) + 1);
+ expect(layout!.webcamRect!.width).toBeLessThanOrEqual(Math.round(refDim * defaultFraction) + 1);
+ expect(layout!.webcamRect!.height).toBeLessThanOrEqual(
+ Math.round(refDim * defaultFraction) + 1,
+ );
expect(
Math.abs(layout!.webcamRect!.width * 1080 - layout!.webcamRect!.height * 1920),
).toBeLessThanOrEqual(1920);
});
- it("uses cover-style full-width stacking in vertical stack mode", () => {
+ it("produces consistent webcam size across landscape and portrait aspect ratios", () => {
+ const webcamSize = { width: 1280, height: 720 };
+ const landscape = computeCompositeLayout({
+ canvasSize: { width: 1920, height: 1080 },
+ screenSize: { width: 1920, height: 1080 },
+ webcamSize,
+ webcamSizePreset: 50,
+ });
+ const portrait = computeCompositeLayout({
+ canvasSize: { width: 1080, height: 1920 },
+ screenSize: { width: 1080, height: 1920 },
+ webcamSize,
+ webcamSizePreset: 50,
+ });
+
+ expect(landscape).not.toBeNull();
+ expect(portrait).not.toBeNull();
+ // Same total pixel count — webcam area should be comparable
+ const landscapeArea = landscape!.webcamRect!.width * landscape!.webcamRect!.height;
+ const portraitArea = portrait!.webcamRect!.width * portrait!.webcamRect!.height;
+ expect(landscapeArea).toBe(portraitArea);
+ });
+
+ it("scales the webcam proportionally as webcamSizePreset increases", () => {
+ const canvasSize = { width: 1920, height: 1080 };
+ const screenSize = { width: 1920, height: 1080 };
+ const webcamSize = { width: 1280, height: 720 };
+
+ const small = computeCompositeLayout({
+ canvasSize,
+ screenSize,
+ webcamSize,
+ webcamSizePreset: 10,
+ });
+ const medium = computeCompositeLayout({
+ canvasSize,
+ screenSize,
+ webcamSize,
+ webcamSizePreset: 25,
+ });
+ const large = computeCompositeLayout({
+ canvasSize,
+ screenSize,
+ webcamSize,
+ webcamSizePreset: 50,
+ });
+
+ expect(small!.webcamRect!.width).toBeLessThan(medium!.webcamRect!.width);
+ expect(medium!.webcamRect!.width).toBeLessThan(large!.webcamRect!.width);
+ expect(small!.webcamRect!.height).toBeLessThan(medium!.webcamRect!.height);
+ expect(medium!.webcamRect!.height).toBeLessThan(large!.webcamRect!.height);
+ });
+
+ it("clamps webcamSizePreset to the valid range (10–50)", () => {
+ const canvasSize = { width: 1920, height: 1080 };
+ const screenSize = { width: 1920, height: 1080 };
+ const webcamSize = { width: 1280, height: 720 };
+
+ const atMin = computeCompositeLayout({
+ canvasSize,
+ screenSize,
+ webcamSize,
+ webcamSizePreset: 10,
+ });
+ const belowMin = computeCompositeLayout({
+ canvasSize,
+ screenSize,
+ webcamSize,
+ webcamSizePreset: 1,
+ });
+ const atMax = computeCompositeLayout({
+ canvasSize,
+ screenSize,
+ webcamSize,
+ webcamSizePreset: 50,
+ });
+ const aboveMax = computeCompositeLayout({
+ canvasSize,
+ screenSize,
+ webcamSize,
+ webcamSizePreset: 100,
+ });
+
+ // Values below 10 should clamp to 10
+ expect(belowMin!.webcamRect!.width).toBe(atMin!.webcamRect!.width);
+ expect(belowMin!.webcamRect!.height).toBe(atMin!.webcamRect!.height);
+ // Values above 50 should clamp to 50
+ expect(aboveMax!.webcamRect!.width).toBe(atMax!.webcamRect!.width);
+ expect(aboveMax!.webcamRect!.height).toBe(atMax!.webcamRect!.height);
+ });
+
+ it("centers the combined screen and webcam stack in vertical stack mode", () => {
const layout = computeCompositeLayout({
canvasSize: { width: 1920, height: 1080 },
maxContentSize: { width: 1536, height: 864 },
@@ -43,23 +138,19 @@ describe("computeCompositeLayout", () => {
});
expect(layout).not.toBeNull();
- expect(layout?.screenRect).toEqual({
- x: 0,
- y: 0,
- width: 1920,
- height: 0,
- });
- expect(layout?.webcamRect).toEqual({
- x: 0,
- y: 0,
- width: 1920,
- height: 1080,
- borderRadius: 0,
- });
- expect(layout?.screenCover).toBe(true);
+ // Webcam is full-width at the bottom
+ expect(layout!.webcamRect).not.toBeNull();
+ expect(layout!.webcamRect!.x).toBe(0);
+ expect(layout!.webcamRect!.width).toBe(1920);
+ expect(layout!.webcamRect!.borderRadius).toBe(0);
+ // Screen fills remaining space at the top (cover mode)
+ expect(layout!.screenRect.x).toBe(0);
+ expect(layout!.screenRect.y).toBe(0);
+ expect(layout!.screenRect.width).toBe(1920);
+ expect(layout!.screenCover).toBe(true);
});
- it("fills the canvas with the screen when vertical stack has no webcam", () => {
+ it("keeps the screen full-canvas and omits the webcam when dimensions are unavailable in stack mode", () => {
const layout = computeCompositeLayout({
canvasSize: { width: 1920, height: 1080 },
maxContentSize: { width: 1536, height: 864 },
@@ -78,6 +169,29 @@ describe("computeCompositeLayout", () => {
expect(layout?.screenCover).toBe(true);
});
+ it("uses a 2:1 split layout in dual frame mode", () => {
+ const layout = computeCompositeLayout({
+ canvasSize: { width: 1920, height: 1080 },
+ maxContentSize: { width: 1536, height: 864 },
+ screenSize: { width: 1920, height: 1080 },
+ webcamSize: { width: 1280, height: 720 },
+ layoutPreset: "dual-frame",
+ });
+
+ expect(layout).not.toBeNull();
+ expect(layout?.webcamRect).not.toBeNull();
+ expect(layout?.screenRect.y).toBe(108);
+ expect(layout?.screenRect.height).toBe(864);
+ expect(layout?.screenBorderRadius).toBe(layout?.webcamRect?.borderRadius);
+ expect(layout?.webcamRect?.y).toBe(108);
+ expect(layout?.webcamRect?.height).toBe(864);
+ expect(layout?.webcamRect?.x).toBeGreaterThan(layout?.screenRect.x ?? 0);
+ expect(
+ Math.abs((layout?.screenRect.width ?? 0) - 2 * (layout?.webcamRect?.width ?? 0)),
+ ).toBeLessThanOrEqual(1);
+ expect(layout?.screenCover).toBe(true);
+ });
+
it("forces circular and square masks to use square dimensions", () => {
const circularLayout = computeCompositeLayout({
canvasSize: { width: 1920, height: 1080 },
diff --git a/src/lib/compositeLayout.ts b/src/lib/compositeLayout.ts
index a3c84e8..e6db733 100644
--- a/src/lib/compositeLayout.ts
+++ b/src/lib/compositeLayout.ts
@@ -15,7 +15,9 @@ export interface Size {
height: number;
}
-export type WebcamLayoutPreset = "picture-in-picture" | "vertical-stack";
+export type WebcamLayoutPreset = "picture-in-picture" | "vertical-stack" | "dual-frame";
+/** Webcam size as a percentage of the canvas reference dimension (10–50). */
+export type WebcamSizePreset = number;
export interface WebcamLayoutShadow {
color: string;
@@ -32,7 +34,6 @@ interface BorderRadiusRule {
interface OverlayTransform {
type: "overlay";
- maxStageFraction: number;
marginFraction: number;
minMargin: number;
minSize: number;
@@ -43,9 +44,17 @@ interface StackTransform {
gap: number;
}
+interface SplitTransform {
+ type: "split";
+ gapFraction: number;
+ minGap: number;
+ screenUnits: number;
+ webcamUnits: number;
+}
+
export interface WebcamLayoutPresetDefinition {
label: string;
- transform: OverlayTransform | StackTransform;
+ transform: OverlayTransform | StackTransform | SplitTransform;
borderRadius: BorderRadiusRule;
shadow: WebcamLayoutShadow | null;
}
@@ -53,11 +62,18 @@ export interface WebcamLayoutPresetDefinition {
export interface WebcamCompositeLayout {
screenRect: RenderRect;
webcamRect: StyledRenderRect | null;
+ screenBorderRadius?: number;
/** When true, the video should be scaled to cover screenRect (cropping overflow). */
screenCover?: boolean;
}
-const MAX_STAGE_FRACTION = 0.18;
+/** Convert a webcam size percentage (10–50) to a fraction of the reference dimension. */
+function webcamSizeToFraction(percent: number): number {
+ const safe = Number.isFinite(percent) ? percent : 25;
+ const clamped = Math.max(10, Math.min(50, safe));
+ return clamped / 100;
+}
+
const MARGIN_FRACTION = 0.02;
const MAX_BORDER_RADIUS = 24;
const WEBCAM_LAYOUT_PRESET_MAP: Record = {
@@ -65,7 +81,6 @@ const WEBCAM_LAYOUT_PRESET_MAP: Record {
+ if (buffer) {
+ tokens.push(...buffer.split(/(\s+)/).filter((s) => s.length > 0));
+ buffer = "";
+ }
+ };
+ for (const ch of chars) {
+ if (CJK_CHAR.test(ch)) {
+ flushBuffer();
+ tokens.push(ch);
+ } else {
+ buffer += ch;
+ }
+ }
+ flushBuffer();
+ return tokens;
+}
// SVG path data for each arrow direction
const ARROW_PATHS: Record = {
@@ -96,6 +139,101 @@ function renderArrow(
ctx.restore();
}
+function drawBlurPath(
+ ctx: CanvasRenderingContext2D,
+ annotation: AnnotationRegion,
+ x: number,
+ y: number,
+ width: number,
+ height: number,
+) {
+ const shape = annotation.blurData?.shape || "rectangle";
+ if (shape === "rectangle") {
+ ctx.beginPath();
+ ctx.rect(x, y, width, height);
+ return;
+ }
+
+ if (shape === "oval") {
+ ctx.beginPath();
+ ctx.ellipse(x + width / 2, y + height / 2, width / 2, height / 2, 0, 0, Math.PI * 2);
+ return;
+ }
+
+ const points = annotation.blurData?.freehandPoints;
+ if (shape === "freehand" && points && points.length >= 3) {
+ ctx.beginPath();
+ ctx.moveTo(x + (points[0].x / 100) * width, y + (points[0].y / 100) * height);
+ for (let i = 1; i < points.length; i++) {
+ ctx.lineTo(x + (points[i].x / 100) * width, y + (points[i].y / 100) * height);
+ }
+ ctx.closePath();
+ return;
+ }
+
+ ctx.beginPath();
+ ctx.rect(x, y, width, height);
+}
+
+function renderBlur(
+ ctx: CanvasRenderingContext2D,
+ annotation: AnnotationRegion,
+ x: number,
+ y: number,
+ width: number,
+ height: number,
+ scaleFactor: number,
+) {
+ const canvas = ctx.canvas;
+ const blurType = normalizeBlurType(annotation.blurData?.type);
+
+ const blurRadius = Math.max(
+ 1,
+ Math.round(getNormalizedBlurIntensity(annotation.blurData) * scaleFactor),
+ );
+ const samplePadding =
+ blurType === "mosaic"
+ ? Math.max(0, Math.ceil(getNormalizedMosaicBlockSize(annotation.blurData, scaleFactor)))
+ : Math.max(2, Math.ceil(blurRadius * 2));
+ const sx = Math.max(0, Math.floor(x) - samplePadding);
+ const sy = Math.max(0, Math.floor(y) - samplePadding);
+ const ex = Math.min(canvas.width, Math.ceil(x + width) + samplePadding);
+ const ey = Math.min(canvas.height, Math.ceil(y + height) + samplePadding);
+ const sw = Math.max(0, ex - sx);
+ const sh = Math.max(0, ey - sy);
+ if (sw <= 0 || sh <= 0) return;
+
+ if (!blurScratchCanvas || !blurScratchCtx) {
+ blurScratchCanvas = document.createElement("canvas");
+ blurScratchCtx = blurScratchCanvas.getContext("2d");
+ }
+ if (!blurScratchCanvas || !blurScratchCtx) return;
+
+ blurScratchCanvas.width = sw;
+ blurScratchCanvas.height = sh;
+ blurScratchCtx.clearRect(0, 0, sw, sh);
+ blurScratchCtx.drawImage(canvas, sx, sy, sw, sh, 0, 0, sw, sh);
+
+ if (blurType === "mosaic") {
+ const imageData = blurScratchCtx.getImageData(0, 0, sw, sh);
+ applyMosaicToImageData(
+ imageData,
+ getNormalizedMosaicBlockSize(annotation.blurData, scaleFactor),
+ );
+ blurScratchCtx.putImageData(imageData, 0, 0);
+ }
+
+ ctx.save();
+ drawBlurPath(ctx, annotation, x, y, width, height);
+ ctx.clip();
+ ctx.filter = blurType === "mosaic" ? "none" : `blur(${blurRadius}px)`;
+ ctx.drawImage(blurScratchCanvas, sx, sy);
+ ctx.filter = "none";
+ ctx.fillStyle = getBlurOverlayColor(annotation.blurData);
+ ctx.fillRect(sx, sy, sw, sh);
+ ctx.restore();
+}
+
function renderText(
ctx: CanvasRenderingContext2D,
annotation: AnnotationRegion,
@@ -144,13 +282,13 @@ function renderText(
lines.push("");
continue;
}
- const words = rawLine.split(/(\s+)/);
+ const tokens = tokenizeForWrap(rawLine);
let current = "";
- for (const word of words) {
- const test = current + word;
+ for (const token of tokens) {
+ const test = current + token;
if (current && ctx.measureText(test).width > availableWidth) {
lines.push(current);
- current = word.trimStart();
+ current = token.trimStart();
} else {
current = test;
}
@@ -268,7 +406,7 @@ export async function renderAnnotations(
): Promise {
// Filter active annotations at current time
const activeAnnotations = annotations.filter(
- (ann) => currentTimeMs >= ann.startMs && currentTimeMs <= ann.endMs,
+ (ann) => currentTimeMs >= ann.startMs && currentTimeMs < ann.endMs,
);
// Sort by z-index (lower first, so higher z-index draws on top)
@@ -304,6 +442,10 @@ export async function renderAnnotations(
);
}
break;
+
+ case "blur":
+ renderBlur(ctx, annotation, x, y, width, height, scaleFactor);
+ break;
}
}
}
diff --git a/src/lib/exporter/audioEncoder.ts b/src/lib/exporter/audioEncoder.ts
index 490eed2..2391d08 100644
--- a/src/lib/exporter/audioEncoder.ts
+++ b/src/lib/exporter/audioEncoder.ts
@@ -5,6 +5,7 @@ import type { VideoMuxer } from "./muxer";
const AUDIO_BITRATE = 128_000;
const DECODE_BACKPRESSURE_LIMIT = 20;
const MIN_SPEED_REGION_DELTA_MS = 0.0001;
+const SEEK_TIMEOUT_MS = 5_000;
export class AudioProcessor {
private cancelled = false;
@@ -18,9 +19,9 @@ export class AudioProcessor {
demuxer: WebDemuxer,
muxer: VideoMuxer,
videoUrl: string,
- trimRegions?: TrimRegion[],
- speedRegions?: SpeedRegion[],
- readEndSec?: number,
+ trimRegions: TrimRegion[] | undefined,
+ speedRegions: SpeedRegion[] | undefined,
+ validatedDurationSec: number,
): Promise {
const sortedTrims = trimRegions ? [...trimRegions].sort((a, b) => a.startMs - b.startMs) : [];
const sortedSpeedRegions = speedRegions
@@ -35,14 +36,19 @@ export class AudioProcessor {
videoUrl,
sortedTrims,
sortedSpeedRegions,
+ validatedDurationSec,
);
- if (!this.cancelled) {
+ if (!this.cancelled && renderedAudioBlob.size > 0) {
await this.muxRenderedAudioBlob(renderedAudioBlob, muxer);
return;
}
+ return;
}
// No speed edits: keep the original demux/decode/encode path with trim timestamp remap.
+ // The +0.5s buffer mirrors streamingDecoder.decodeAll's read window so the trim-only
+ // and speed-aware paths agree on how far to read past the validated duration boundary.
+ const readEndSec = validatedDurationSec + 0.5;
await this.processTrimOnlyAudio(demuxer, muxer, sortedTrims, readEndSec);
}
@@ -55,7 +61,7 @@ export class AudioProcessor {
): Promise {
let audioConfig: AudioDecoderConfig;
try {
- audioConfig = (await demuxer.getDecoderConfig("audio")) as AudioDecoderConfig;
+ audioConfig = await demuxer.getDecoderConfig("audio");
} catch {
console.warn("[AudioProcessor] No audio track found, skipping");
return;
@@ -80,11 +86,10 @@ export class AudioProcessor {
typeof readEndSec === "number" && Number.isFinite(readEndSec)
? Math.max(0, readEndSec)
: undefined;
- const audioStream = (
+ const audioStream =
safeReadEndSec !== undefined
? demuxer.read("audio", 0, safeReadEndSec)
- : demuxer.read("audio")
- ) as ReadableStream;
+ : demuxer.read("audio");
const reader = audioStream.getReader();
try {
@@ -187,6 +192,7 @@ export class AudioProcessor {
videoUrl: string,
trimRegions: TrimRegion[],
speedRegions: SpeedRegion[],
+ validatedDurationSec: number,
): Promise {
const media = document.createElement("audio");
media.src = videoUrl;
@@ -211,15 +217,44 @@ export class AudioProcessor {
const destinationNode = audioContext.createMediaStreamDestination();
sourceNode.connect(destinationNode);
- const { recorder, recordedBlobPromise } = this.startAudioRecording(destinationNode.stream);
let rafId: number | null = null;
+ let recorder: MediaRecorder | null = null;
+ let recordedBlobPromise: Promise | null = null;
try {
if (audioContext.state === "suspended") {
await audioContext.resume();
}
- await this.seekTo(media, 0);
+ // Skip past any initial trim region(s) before recording starts to avoid
+ // capturing trimmed audio during the first rAF frames of playback.
+ // Loops to handle back-to-back or overlapping trims at t=0.
+ const effectiveEnd = validatedDurationSec;
+ let startPosition = 0;
+ for (let i = 0; i <= trimRegions.length; i++) {
+ const activeTrim = this.findActiveTrimRegion(startPosition * 1000, trimRegions);
+ if (!activeTrim) break;
+ startPosition = activeTrim.endMs / 1000;
+ if (startPosition >= effectiveEnd) break;
+ }
+
+ if (startPosition >= effectiveEnd) {
+ // All content is trimmed — return silent blob
+ return new Blob([], { type: "audio/webm" });
+ }
+
+ await this.seekTo(media, startPosition);
+
+ // Set initial playback rate for the starting position
+ const initialSpeedRegion = this.findActiveSpeedRegion(startPosition * 1000, speedRegions);
+ if (initialSpeedRegion) {
+ media.playbackRate = initialSpeedRegion.speed;
+ }
+
+ // Start recording only AFTER seeking past trims
+ const recording = this.startAudioRecording(destinationNode.stream);
+ recorder = recording.recorder;
+ recordedBlobPromise = recording.recordedBlobPromise;
await media.play();
await new Promise((resolve, reject) => {
@@ -249,24 +284,66 @@ export class AudioProcessor {
return;
}
+ // Stop playback at validated duration — browser's media.duration
+ // may be inflated from bad container metadata.
+ if (media.currentTime >= validatedDurationSec) {
+ media.pause();
+ cleanup();
+ resolve();
+ return;
+ }
+
const currentTimeMs = media.currentTime * 1000;
const activeTrimRegion = this.findActiveTrimRegion(currentTimeMs, trimRegions);
if (activeTrimRegion && !media.paused && !media.ended) {
const skipToTime = activeTrimRegion.endMs / 1000;
- if (skipToTime >= media.duration) {
+ if (skipToTime >= media.duration || skipToTime >= validatedDurationSec) {
media.pause();
cleanup();
resolve();
return;
}
+ // Pause recording during trim seek to prevent capturing
+ // silence/noise as the audio element seeks.
+ media.pause();
+ if (recorder?.state === "recording") recorder.pause();
+ const onSeeked = () => {
+ clearTimeout(seekTimer);
+ if (this.cancelled) {
+ cleanup();
+ resolve();
+ return;
+ }
+ if (recorder?.state === "paused") recorder.resume();
+ media
+ .play()
+ .then(() => {
+ if (!this.cancelled) rafId = requestAnimationFrame(tick);
+ })
+ .catch((err) => {
+ cleanup();
+ reject(
+ new Error(
+ `Failed to resume playback after trim seek: ${err instanceof Error ? err.message : String(err)}`,
+ ),
+ );
+ });
+ };
+ const seekTimer = window.setTimeout(() => {
+ media.removeEventListener("seeked", onSeeked);
+ cleanup();
+ reject(new Error("Audio seek timed out while skipping trim region"));
+ }, SEEK_TIMEOUT_MS);
+ media.addEventListener("seeked", onSeeked, { once: true });
media.currentTime = skipToTime;
- } else {
- const activeSpeedRegion = this.findActiveSpeedRegion(currentTimeMs, speedRegions);
- const playbackRate = activeSpeedRegion ? activeSpeedRegion.speed : 1;
- if (Math.abs(media.playbackRate - playbackRate) > 0.0001) {
- media.playbackRate = playbackRate;
- }
+ return;
+ }
+
+ const activeSpeedRegion = this.findActiveSpeedRegion(currentTimeMs, speedRegions);
+ const playbackRate = activeSpeedRegion ? activeSpeedRegion.speed : 1;
+ if (Math.abs(media.playbackRate - playbackRate) > 0.0001) {
+ media.playbackRate = playbackRate;
}
if (!media.paused && !media.ended) {
@@ -286,7 +363,7 @@ export class AudioProcessor {
cancelAnimationFrame(rafId);
}
media.pause();
- if (recorder.state !== "inactive") {
+ if (recorder && recorder.state !== "inactive") {
recorder.stop();
}
destinationNode.stream.getTracks().forEach((track) => track.stop());
@@ -297,6 +374,12 @@ export class AudioProcessor {
media.load();
}
+ if (!recordedBlobPromise) {
+ // Invariant: either an early return above fires, or startAudioRecording ran and
+ // populated recordedBlobPromise before the playback Promise resolved. Reaching
+ // here means that contract was broken — fail loud instead of returning silence.
+ throw new Error("Audio recorder finished without assigning recordedBlobPromise");
+ }
const recordedBlob = await recordedBlobPromise;
if (this.cancelled) {
throw new Error("Export cancelled");
@@ -314,8 +397,8 @@ export class AudioProcessor {
try {
await demuxer.load(file);
- const audioConfig = (await demuxer.getDecoderConfig("audio")) as AudioDecoderConfig;
- const reader = (demuxer.read("audio") as ReadableStream).getReader();
+ const audioConfig = await demuxer.getDecoderConfig("audio");
+ const reader = demuxer.read("audio").getReader();
let isFirstChunk = true;
try {
@@ -459,7 +542,10 @@ export class AudioProcessor {
}
private cloneWithTimestamp(src: AudioData, newTimestamp: number): AudioData {
- const isPlanar = src.format?.includes("planar") ?? false;
+ if (!src.format) {
+ throw new Error("AudioData format is required for cloning");
+ }
+ const isPlanar = src.format.includes("planar");
const numPlanes = isPlanar ? src.numberOfChannels : 1;
let totalSize = 0;
@@ -476,7 +562,7 @@ export class AudioProcessor {
}
return new AudioData({
- format: src.format!,
+ format: src.format,
sampleRate: src.sampleRate,
numberOfFrames: src.numberOfFrames,
numberOfChannels: src.numberOfChannels,
diff --git a/src/lib/exporter/frameRenderer.ts b/src/lib/exporter/frameRenderer.ts
index be0e8e3..f1815c1 100644
--- a/src/lib/exporter/frameRenderer.ts
+++ b/src/lib/exporter/frameRenderer.ts
@@ -13,6 +13,7 @@ import type {
CropRegion,
SpeedRegion,
WebcamLayoutPreset,
+ WebcamSizePreset,
ZoomDepth,
ZoomRegion,
} from "@/components/video-editor/types";
@@ -70,12 +71,14 @@ interface FrameRenderConfig {
webcamSize?: Size | null;
webcamLayoutPreset?: WebcamLayoutPreset;
webcamMaskShape?: import("@/components/video-editor/types").WebcamMaskShape;
+ webcamSizePreset?: WebcamSizePreset;
webcamPosition?: { cx: number; cy: number } | null;
annotationRegions?: AnnotationRegion[];
speedRegions?: SpeedRegion[];
previewWidth?: number;
previewHeight?: number;
cursorTelemetry?: import("@/components/video-editor/types").CursorTelemetryPoint[];
+ platform: string;
}
interface AnimationState {
@@ -112,6 +115,8 @@ export class FrameRenderer {
private shadowCtx: CanvasRenderingContext2D | null = null;
private compositeCanvas: HTMLCanvasElement | null = null;
private compositeCtx: CanvasRenderingContext2D | null = null;
+ private rasterCanvas: HTMLCanvasElement | null = null;
+ private rasterCtx: CanvasRenderingContext2D | null = null;
private config: FrameRenderConfig;
private animationState: AnimationState;
private layoutCache: LayoutCache | null = null;
@@ -120,9 +125,11 @@ export class FrameRenderer {
private smoothedAutoFocus: { cx: number; cy: number } | null = null;
private prevAnimationTimeMs: number | null = null;
private prevTargetProgress = 0;
+ private isLinux = false;
constructor(config: FrameRenderConfig) {
this.config = config;
+ this.isLinux = config.platform === "linux";
this.animationState = {
scale: 1,
focusX: DEFAULT_FOCUS.cx,
@@ -183,14 +190,24 @@ export class FrameRenderer {
this.compositeCanvas = document.createElement("canvas");
this.compositeCanvas.width = this.config.width;
this.compositeCanvas.height = this.config.height;
+
+ // On Linux, getImageData() is called frequently causing frequent CPU readback
this.compositeCtx = this.compositeCanvas.getContext("2d", {
- willReadFrequently: false,
+ willReadFrequently: this.isLinux,
});
if (!this.compositeCtx) {
throw new Error("Failed to get 2D context for composite canvas");
}
+ this.rasterCanvas = document.createElement("canvas");
+ this.rasterCanvas.width = this.config.width;
+ this.rasterCanvas.height = this.config.height;
+ this.rasterCtx = this.rasterCanvas.getContext("2d");
+ if (!this.rasterCtx) {
+ throw new Error("Failed to get 2D context for raster canvas");
+ }
+
// Setup shadow canvas if needed
if (this.config.showShadow) {
this.shadowCanvas = document.createElement("canvas");
@@ -453,6 +470,7 @@ export class FrameRenderer {
screenSize: { width: croppedVideoWidth, height: croppedVideoHeight },
webcamSize: webcamFrame ? this.config.webcamSize : null,
layoutPreset: this.config.webcamLayoutPreset,
+ webcamSizePreset: this.config.webcamSizePreset,
webcamPosition: this.config.webcamPosition,
webcamMaskShape: this.config.webcamMaskShape,
});
@@ -494,7 +512,12 @@ export class FrameRenderer {
const previewWidth = this.config.previewWidth ?? this.config.width;
const previewHeight = this.config.previewHeight ?? this.config.height;
const canvasScaleFactor = Math.min(width / previewWidth, height / previewHeight);
- const scaledBorderRadius = compositeLayout.screenCover ? 0 : borderRadius * canvasScaleFactor;
+ const scaledBorderRadius =
+ compositeLayout.screenBorderRadius != null
+ ? compositeLayout.screenBorderRadius
+ : compositeLayout.screenCover
+ ? 0
+ : borderRadius * canvasScaleFactor;
this.maskGraphics.clear();
this.maskGraphics.roundRect(0, 0, screenRect.width, screenRect.height, scaledBorderRadius);
@@ -522,16 +545,10 @@ export class FrameRenderer {
private updateAnimationState(timeMs: number): number {
if (!this.cameraContainer || !this.layoutCache) return 0;
- const bmEx = this.layoutCache.maskRect;
- const ssEx = this.layoutCache.stageSize;
- const viewportRatio =
- bmEx.width > 0 && bmEx.height > 0
- ? { widthRatio: ssEx.width / bmEx.width, heightRatio: ssEx.height / bmEx.height }
- : undefined;
const { region, strength, blendedScale, transition } = findDominantRegion(
this.config.zoomRegions,
timeMs,
- { connectZooms: true, cursorTelemetry: this.config.cursorTelemetry, viewportRatio },
+ { connectZooms: true, cursorTelemetry: this.config.cursorTelemetry },
);
const defaultFocus = DEFAULT_FOCUS;
@@ -675,10 +692,49 @@ export class FrameRenderer {
);
}
+ // On Linux/Wayland the implicit GPU→2D texture-sharing path
+ // used by drawImage(webglCanvas) can fail silently (EGL/Ozone),
+ // producing green/empty frames. Explicit gl.readPixels always
+ // copies from GPU to CPU memory, bypassing that path.
+ private readbackVideoCanvas(): HTMLCanvasElement {
+ const glCanvas = this.app!.canvas as HTMLCanvasElement;
+ const gl =
+ (glCanvas.getContext("webgl2") as WebGL2RenderingContext | null) ??
+ (glCanvas.getContext("webgl") as WebGLRenderingContext | null);
+
+ if (!gl || !this.rasterCanvas || !this.rasterCtx) {
+ return glCanvas;
+ }
+
+ const w = glCanvas.width;
+ const h = glCanvas.height;
+ const buf = new Uint8Array(w * h * 4);
+ gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, buf);
+
+ // readPixels returns rows bottom-to-top; flip vertically
+ const rowSize = w * 4;
+ const temp = new Uint8Array(rowSize);
+ for (let top = 0, bot = h - 1; top < bot; top++, bot--) {
+ const tOff = top * rowSize;
+ const bOff = bot * rowSize;
+ temp.set(buf.subarray(tOff, tOff + rowSize));
+ buf.copyWithin(tOff, bOff, bOff + rowSize);
+ buf.set(temp, bOff);
+ }
+
+ const imageData = new ImageData(new Uint8ClampedArray(buf.buffer), w, h);
+ this.rasterCtx.putImageData(imageData, 0, 0);
+
+ return this.rasterCanvas;
+ }
+
private compositeWithShadows(webcamFrame?: VideoFrame | null): void {
if (!this.compositeCanvas || !this.compositeCtx || !this.app) return;
- const videoCanvas = this.app.canvas as HTMLCanvasElement;
+ const videoCanvas = this.isLinux
+ ? this.readbackVideoCanvas()
+ : (this.app.canvas as HTMLCanvasElement);
+
const ctx = this.compositeCtx;
const w = this.compositeCanvas.width;
const h = this.compositeCanvas.height;
@@ -735,6 +791,22 @@ export class FrameRenderer {
if (webcamFrame && webcamRect) {
const preset = getWebcamLayoutPresetDefinition(this.config.webcamLayoutPreset);
const shape = webcamRect.maskShape ?? this.config.webcamMaskShape ?? "rectangle";
+ const sourceWidth =
+ ("displayWidth" in webcamFrame && webcamFrame.displayWidth > 0
+ ? webcamFrame.displayWidth
+ : webcamFrame.codedWidth) || webcamRect.width;
+ const sourceHeight =
+ ("displayHeight" in webcamFrame && webcamFrame.displayHeight > 0
+ ? webcamFrame.displayHeight
+ : webcamFrame.codedHeight) || webcamRect.height;
+ const sourceAspect = sourceWidth / sourceHeight;
+ const targetAspect = webcamRect.width / webcamRect.height;
+ const sourceCropWidth =
+ sourceAspect > targetAspect ? Math.round(sourceHeight * targetAspect) : sourceWidth;
+ const sourceCropHeight =
+ sourceAspect > targetAspect ? sourceHeight : Math.round(sourceWidth / targetAspect);
+ const sourceCropX = Math.max(0, Math.round((sourceWidth - sourceCropWidth) / 2));
+ const sourceCropY = Math.max(0, Math.round((sourceHeight - sourceCropHeight) / 2));
ctx.save();
drawCanvasClipPath(
ctx,
@@ -756,6 +828,10 @@ export class FrameRenderer {
ctx.clip();
ctx.drawImage(
webcamFrame as unknown as CanvasImageSource,
+ sourceCropX,
+ sourceCropY,
+ sourceCropWidth,
+ sourceCropHeight,
webcamRect.x,
webcamRect.y,
webcamRect.width,
@@ -795,5 +871,7 @@ export class FrameRenderer {
this.shadowCtx = null;
this.compositeCanvas = null;
this.compositeCtx = null;
+ this.rasterCanvas = null;
+ this.rasterCtx = null;
}
}
diff --git a/src/lib/exporter/gifExporter.browser.test.ts b/src/lib/exporter/gifExporter.browser.test.ts
new file mode 100644
index 0000000..db9b144
--- /dev/null
+++ b/src/lib/exporter/gifExporter.browser.test.ts
@@ -0,0 +1,43 @@
+import { describe, expect, it } from "vitest";
+import sampleVideoUrl from "../../../tests/fixtures/sample.webm?url";
+import { GifExporter } from "./gifExporter";
+import type { ExportProgress } from "./types";
+
+describe("GifExporter (real browser)", () => {
+ it("exports a valid GIF blob from a real video", async () => {
+ const progressEvents: ExportProgress[] = [];
+
+ const exporter = new GifExporter({
+ videoUrl: sampleVideoUrl,
+ width: 320,
+ height: 180,
+ frameRate: 15,
+ loop: true,
+ sizePreset: "medium",
+ wallpaper: "#1a1a2e",
+ zoomRegions: [],
+ showShadow: false,
+ shadowIntensity: 0,
+ showBlur: false,
+ cropRegion: { x: 0, y: 0, width: 1, height: 1 },
+ onProgress: (p) => progressEvents.push(p),
+ });
+
+ const result = await exporter.export();
+
+ expect(result.success, result.error).toBe(true);
+ expect(result.blob).toBeInstanceOf(Blob);
+
+ const buf = await result.blob!.arrayBuffer();
+ const header = new TextDecoder().decode(new Uint8Array(buf, 0, 6));
+ expect(header).toMatch(/^GIF8[79]a/);
+
+ expect(result.blob!.size).toBeGreaterThan(1024);
+
+ expect(progressEvents.length).toBeGreaterThan(0);
+
+ const finalizing = progressEvents.filter((p) => p.phase === "finalizing");
+ expect(finalizing.length).toBeGreaterThan(0);
+ expect(finalizing.at(-1)!.percentage).toBe(100);
+ });
+});
diff --git a/src/lib/exporter/gifExporter.ts b/src/lib/exporter/gifExporter.ts
index d059264..46ac6a0 100644
--- a/src/lib/exporter/gifExporter.ts
+++ b/src/lib/exporter/gifExporter.ts
@@ -5,8 +5,10 @@ import type {
SpeedRegion,
TrimRegion,
WebcamLayoutPreset,
+ WebcamSizePreset,
ZoomRegion,
} from "@/components/video-editor/types";
+import { getPlatform } from "@/utils/platformUtils";
import { AsyncVideoFrameQueue } from "./asyncVideoFrameQueue";
import { FrameRenderer } from "./frameRenderer";
import { StreamingVideoDecoder } from "./streamingDecoder";
@@ -42,6 +44,7 @@ interface GifExporterConfig {
cropRegion: CropRegion;
webcamLayoutPreset?: WebcamLayoutPreset;
webcamMaskShape?: import("@/components/video-editor/types").WebcamMaskShape;
+ webcamSizePreset?: WebcamSizePreset;
webcamPosition?: { cx: number; cy: number } | null;
annotationRegions?: AnnotationRegion[];
previewWidth?: number;
@@ -113,7 +116,10 @@ export class GifExporter {
async export(): Promise {
let webcamFrameQueue: AsyncVideoFrameQueue | null = null;
+
try {
+ const platform = await getPlatform();
+
this.cleanup();
this.cancelled = false;
@@ -144,12 +150,14 @@ export class GifExporter {
webcamSize: webcamInfo ? { width: webcamInfo.width, height: webcamInfo.height } : null,
webcamLayoutPreset: this.config.webcamLayoutPreset,
webcamMaskShape: this.config.webcamMaskShape,
+ webcamSizePreset: this.config.webcamSizePreset,
webcamPosition: this.config.webcamPosition,
annotationRegions: this.config.annotationRegions,
speedRegions: this.config.speedRegions,
previewWidth: this.config.previewWidth,
previewHeight: this.config.previewHeight,
cursorTelemetry: this.config.cursorTelemetry,
+ platform,
});
await this.renderer.initialize();
@@ -171,11 +179,11 @@ export class GifExporter {
});
// Calculate effective duration and frame count (excluding trim regions)
- const effectiveDuration = this.streamingDecoder.getEffectiveDuration(
+ const { effectiveDuration, totalFrames } = this.streamingDecoder.getExportMetrics(
+ this.config.frameRate,
this.config.trimRegions,
this.config.speedRegions,
);
- const totalFrames = Math.ceil(effectiveDuration * this.config.frameRate);
// Calculate frame delay in milliseconds (gif.js uses ms)
const frameDelay = Math.round(1000 / this.config.frameRate);
diff --git a/src/lib/exporter/streamingDecoder.test.ts b/src/lib/exporter/streamingDecoder.test.ts
index 1969c84..55b9123 100644
--- a/src/lib/exporter/streamingDecoder.test.ts
+++ b/src/lib/exporter/streamingDecoder.test.ts
@@ -1,5 +1,44 @@
import { describe, expect, it } from "vitest";
-import { shouldFailDecodeEndedEarly } from "./streamingDecoder";
+import { shouldFailDecodeEndedEarly, validateDuration } from "./streamingDecoder";
+
+describe("validateDuration", () => {
+ it("returns scanned duration when container reports Infinity", () => {
+ expect(validateDuration(Infinity, 15.3)).toBe(15.3);
+ });
+
+ it("returns scanned duration when container reports 0", () => {
+ expect(validateDuration(0, 15.3)).toBe(15.3);
+ });
+
+ it("returns scanned duration when container reports NaN", () => {
+ expect(validateDuration(NaN, 15.3)).toBe(15.3);
+ });
+
+ it("returns scanned duration when container is inflated beyond threshold", () => {
+ expect(validateDuration(42, 15.3)).toBe(15.3);
+ });
+
+ it("returns container duration when values are close", () => {
+ expect(validateDuration(15.5, 15.3)).toBe(15.5);
+ });
+
+ it("returns container duration when scanned is slightly higher", () => {
+ // container < scanned (scanned overshoot from last frame duration)
+ expect(validateDuration(15.0, 15.3)).toBe(15.0);
+ });
+
+ it("returns scanned duration when container under-reports beyond threshold", () => {
+ expect(validateDuration(10, 15.3)).toBe(15.3);
+ });
+
+ it("returns container duration when scanned is zero (corrupted/empty file)", () => {
+ expect(validateDuration(10, 0)).toBe(10);
+ });
+
+ it("returns 0 when both container is NaN and scanned is zero", () => {
+ expect(validateDuration(NaN, 0)).toBe(0);
+ });
+});
describe("shouldFailDecodeEndedEarly", () => {
it("does not fail once every segment has been satisfied", () => {
diff --git a/src/lib/exporter/streamingDecoder.ts b/src/lib/exporter/streamingDecoder.ts
index ccb510b..24b9844 100644
--- a/src/lib/exporter/streamingDecoder.ts
+++ b/src/lib/exporter/streamingDecoder.ts
@@ -2,6 +2,52 @@ import { WebDemuxer } from "web-demuxer";
import type { SpeedRegion, TrimRegion } from "@/components/video-editor/types";
const SOURCE_LOAD_TIMEOUT_MS = 60_000;
+const EPSILON_SEC = 0.001;
+/**
+ * Build a full WebCodecs-compatible AV1 codec string from the AV1CodecConfigurationRecord.
+ * web-demuxer may return a bare "av01" when the WASM-side parser fails to read
+ * the extradata (e.g. raw OBU sequence header from WebM instead of ISOBMFF av1C box).
+ * This function parses the record if present, otherwise returns a safe default.
+ *
+ * @see https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox-section
+ */
+function buildAV1CodecString(description?: BufferSource): string {
+ const fallback = "av01.0.01M.08";
+
+ if (!description) return fallback;
+
+ const bytes =
+ description instanceof ArrayBuffer
+ ? new Uint8Array(description)
+ : new Uint8Array(description.buffer, description.byteOffset, description.byteLength);
+
+ // AV1CodecConfigurationRecord layout (4+ bytes):
+ // Byte 0: marker (1) | version (7)
+ // Byte 1: seq_profile (3) | seq_level_idx_0 (5)
+ // Byte 2: seq_tier_0 (1) | high_bitdepth (1) | twelve_bit (1) | ...
+ // The spec says version should be 1, but Chrome/Electron's MediaRecorder
+ // may write version 127 (0xFF first byte). We accept any version as long
+ // as the marker bit is set and the record is long enough.
+ if (bytes.length < 4) return fallback;
+ if (!(bytes[0] & 0x80)) return fallback; // marker bit must be 1
+
+ // Byte 1: seq_profile (3) | seq_level_idx_0 (5)
+ const profile = (bytes[1] >> 5) & 0x07;
+ const level = bytes[1] & 0x1f;
+
+ // Byte 2: seq_tier_0 (1) | high_bitdepth (1) | twelve_bit (1) | monochrome (1) | ...
+ const tier = (bytes[2] >> 7) & 0x01;
+ const highBitdepth = (bytes[2] >> 6) & 0x01;
+ const twelveBit = (bytes[2] >> 5) & 0x01;
+ let bitdepth = 8;
+ if (highBitdepth) bitdepth = twelveBit ? 12 : 10;
+
+ const tierChar = tier ? "H" : "M";
+ const levelStr = level.toString().padStart(2, "0");
+ const bitdepthStr = bitdepth.toString().padStart(2, "0");
+
+ return `av01.${profile}.${levelStr}${tierChar}.${bitdepthStr}`;
+}
export interface DecodedVideoInfo {
width: number;
@@ -24,6 +70,37 @@ type EarlyDecodeEndCheck = {
const EARLY_DECODE_END_THRESHOLD_SEC = 1;
const METADATA_TAIL_TOLERANCE_SEC = 1.5;
const STREAM_DURATION_MATCH_TOLERANCE_SEC = 0.25;
+const DURATION_DIVERGENCE_THRESHOLD_SEC = 1.5;
+// Fallback upper bound for the packet scan when no reliable duration hint is
+// available. Explicit end is required (some containers are truncated without
+// one), but the hint-derived bound would cap the scan prematurely when
+// container/stream duration are missing or corrupt.
+const SCAN_UNBOUNDED_FALLBACK_SEC = 24 * 60 * 60;
+
+/**
+ * Validate container duration against actual packet timestamps.
+ *
+ * Chrome/Electron's MediaRecorder writes WebM containers with unreliable
+ * Duration fields (often Infinity, 0, or inflated) — especially on Linux.
+ * This function picks the most trustworthy duration value.
+ *
+ * @param containerDuration Duration from the container-level metadata
+ * @param scannedDuration Duration derived from actual packet timestamps (ground truth)
+ */
+export function validateDuration(containerDuration: number, scannedDuration: number): number {
+ if (scannedDuration <= 0) {
+ // Zero scanned duration means corrupted/empty file — fall back to container
+ // (downstream shouldFailDecodeEndedEarly will catch truly empty files)
+ return Number.isFinite(containerDuration) ? Math.max(containerDuration, 0) : 0;
+ }
+ if (!Number.isFinite(containerDuration) || containerDuration <= 0) {
+ return scannedDuration;
+ }
+ if (Math.abs(containerDuration - scannedDuration) > DURATION_DIVERGENCE_THRESHOLD_SEC) {
+ return scannedDuration;
+ }
+ return containerDuration;
+}
export function shouldFailDecodeEndedEarly({
cancelled,
@@ -155,10 +232,43 @@ export class StreamingVideoDecoder {
const audioStream = mediaInfo.streams.find((s) => s.codec_type_string === "audio");
+ // Scan video packets to find the true content boundary.
+ // MediaRecorder (especially on Linux) writes unreliable container durations.
+ // Packet timestamps are ground truth — no decode needed, just timestamp reads.
+ // Pass explicit range because some containers are truncated without one.
+ // Sanitize because mediaInfo.duration can be NaN/Infinity (Chromium Linux bug),
+ // which would propagate into demuxer.read() as an invalid endpoint.
+ const containerDurationSec = Number.isFinite(mediaInfo.duration) ? mediaInfo.duration : 0;
+ const streamDurationSec =
+ typeof videoStream?.duration === "number" && Number.isFinite(videoStream.duration)
+ ? videoStream.duration
+ : 0;
+ const hintedDurationSec = Math.max(containerDurationSec, streamDurationSec, 0);
+ const scanEndSec =
+ hintedDurationSec > 0 ? hintedDurationSec + 0.5 : SCAN_UNBOUNDED_FALLBACK_SEC;
+ let maxPacketEndUs = 0;
+ const scanReader = this.demuxer.read("video", 0, scanEndSec).getReader();
+ try {
+ while (true) {
+ const { done, value } = await scanReader.read();
+ if (done || !value) break;
+ const endUs = value.timestamp + (value.duration ?? 0);
+ if (endUs > maxPacketEndUs) maxPacketEndUs = endUs;
+ }
+ } finally {
+ try {
+ await scanReader.cancel();
+ } catch {
+ /* already closed */
+ }
+ }
+ const scannedDuration = maxPacketEndUs / 1_000_000;
+ const validatedDuration = validateDuration(mediaInfo.duration, scannedDuration);
+
this.metadata = {
width: videoStream?.width || 1920,
height: videoStream?.height || 1080,
- duration: mediaInfo.duration,
+ duration: validatedDuration,
streamDuration:
typeof videoStream?.duration === "number" && Number.isFinite(videoStream.duration)
? videoStream.duration
@@ -171,7 +281,15 @@ export class StreamingVideoDecoder {
return this.metadata;
}
-
+ /**
+ * Decodes all video frames from the loaded source and invokes a callback for each.
+ * Handles trimming and speed adjustments, and resamples to the target frame rate.
+ * On Windows, early decode termination is tolerated to work around driver quirks.
+ * @param targetFrameRate - Desired output frame rate.
+ * @param trimRegions - Array of time regions to keep (others discarded).
+ * @param speedRegions - Array of speed adjustments for specific time ranges.
+ * @param onFrame - Async callback receiving each decoded VideoFrame.
+ */
async decodeAll(
targetFrameRate: number,
trimRegions: TrimRegion[] | undefined,
@@ -183,17 +301,30 @@ export class StreamingVideoDecoder {
}
const decoderConfig = await this.demuxer.getDecoderConfig("video");
- const codec = this.metadata.codec.toLowerCase();
+
+ // web-demuxer may return a bare "av01" for AV1 in WebM containers when the
+ // extradata isn't in the expected ISOBMFF format. WebCodecs requires the
+ // full parametrized form (e.g. "av01.0.05M.08").
+ if (/^av01$/i.test(decoderConfig.codec)) {
+ decoderConfig.codec = buildAV1CodecString(
+ decoderConfig.description as BufferSource | undefined,
+ );
+ }
+
+ const codec = decoderConfig.codec.toLowerCase();
const shouldPreferSoftwareDecode = codec.includes("av01") || codec.includes("av1");
const segments = this.splitBySpeed(
this.computeSegments(this.metadata.duration, trimRegions),
speedRegions,
);
+ const requiredEndSec = segments[segments.length - 1]?.endSec ?? 0;
+
const segmentOutputFrameCounts = segments.map((segment) =>
- Math.ceil(((segment.endSec - segment.startSec) / segment.speed) * targetFrameRate),
+ Math.ceil(
+ ((segment.endSec - segment.startSec - EPSILON_SEC) / segment.speed) * targetFrameRate,
+ ),
);
const frameDurationUs = 1_000_000 / targetFrameRate;
- const epsilonSec = 0.001;
// Async frame queue — decoder pushes, consumer pulls
const pendingFrames: VideoFrame[] = [];
@@ -248,7 +379,7 @@ export class StreamingVideoDecoder {
// One forward stream through the whole file.
// Pass explicit range because some containers are truncated when no end is provided.
- const readEndSec = Math.max(this.metadata.duration, this.metadata.streamDuration ?? 0) + 0.5;
+ const readEndSec = this.metadata.duration + 0.5;
const reader = this.demuxer.read("video", 0, readEndSec).getReader();
// Feed chunks to decoder in background with backpressure
@@ -304,7 +435,7 @@ export class StreamingVideoDecoder {
const sourceTimeSec =
segment.startSec + (segmentFrameIndex / targetFrameRate) * segment.speed;
- if (sourceTimeSec >= segment.endSec - epsilonSec) return false;
+ if (sourceTimeSec >= segment.endSec - EPSILON_SEC) return false;
const clone = new VideoFrame(heldFrame, { timestamp: heldFrame.timestamp });
await onFrame(clone, exportFrameIndex * frameDurationUs, sourceTimeSec * 1000);
@@ -323,7 +454,7 @@ export class StreamingVideoDecoder {
// Finalize completed segments before handling this frame.
while (
segmentIdx < segments.length &&
- frameTimeSec >= segments[segmentIdx].endSec - epsilonSec
+ frameTimeSec >= segments[segmentIdx].endSec - EPSILON_SEC
) {
const segment = segments[segmentIdx];
while (!this.cancelled && (await emitHeldFrameForTarget(segment))) {
@@ -335,7 +466,7 @@ export class StreamingVideoDecoder {
if (
heldFrame &&
segmentIdx < segments.length &&
- heldFrameSec < segments[segmentIdx].startSec - epsilonSec
+ heldFrameSec < segments[segmentIdx].startSec - EPSILON_SEC
) {
heldFrame.close();
heldFrame = null;
@@ -350,7 +481,7 @@ export class StreamingVideoDecoder {
const currentSegment = segments[segmentIdx];
// Before current segment (trimmed region or pre-roll).
- if (frameTimeSec < currentSegment.startSec - epsilonSec) {
+ if (frameTimeSec < currentSegment.startSec - EPSILON_SEC) {
frame.close();
continue;
}
@@ -371,7 +502,7 @@ export class StreamingVideoDecoder {
const sourceTimeSec =
currentSegment.startSec + (segmentFrameIndex / targetFrameRate) * currentSegment.speed;
- if (sourceTimeSec >= currentSegment.endSec - epsilonSec) {
+ if (sourceTimeSec >= currentSegment.endSec - EPSILON_SEC) {
break;
}
if (sourceTimeSec > handoffBoundarySec) {
@@ -393,7 +524,7 @@ export class StreamingVideoDecoder {
if (heldFrame && segmentIdx < segments.length) {
while (!this.cancelled && segmentIdx < segments.length) {
const segment = segments[segmentIdx];
- if (heldFrameSec < segment.startSec - epsilonSec) {
+ if (heldFrameSec < segment.startSec - EPSILON_SEC) {
break;
}
@@ -405,7 +536,7 @@ export class StreamingVideoDecoder {
segmentFrameIndex = 0;
if (
segmentIdx < segments.length &&
- heldFrameSec < segments[segmentIdx].startSec - epsilonSec
+ heldFrameSec < segments[segmentIdx].startSec - EPSILON_SEC
) {
break;
}
@@ -435,7 +566,8 @@ export class StreamingVideoDecoder {
}
this.decoder = null;
- const requiredEndSec = segments.length > 0 ? segments[segments.length - 1].endSec : 0;
+ const isWindows = typeof navigator !== "undefined" && /Windows/.test(navigator.userAgent);
+
if (
shouldFailDecodeEndedEarly({
cancelled: this.cancelled,
@@ -446,9 +578,22 @@ export class StreamingVideoDecoder {
) {
const decodedAtLabel =
lastDecodedFrameSec === null ? "no decoded frame" : `${lastDecodedFrameSec.toFixed(3)}s`;
- throw new Error(
- `Video decode ended early at ${decodedAtLabel} (needed ${requiredEndSec.toFixed(3)}s).`,
- );
+ const decodeGapSec =
+ lastDecodedFrameSec === null ? Infinity : requiredEndSec - lastDecodedFrameSec;
+
+ // On Windows, tolerate a small decode gap: up to 10% of required duration, capped at 3 seconds.
+ const maxToleratedGap = Math.min(3.0, requiredEndSec * 0.1);
+
+ if (isWindows && lastDecodedFrameSec !== null && decodeGapSec <= maxToleratedGap) {
+ console.warn(
+ `[StreamingVideoDecoder] Decode ended early on Windows with a gap of ${decodeGapSec.toFixed(2)}s ` +
+ `(max tolerated: ${maxToleratedGap.toFixed(2)}s) – proceeding anyway.`,
+ );
+ } else {
+ throw new Error(
+ `Video decode ended early at ${decodedAtLabel} (needed ${requiredEndSec.toFixed(3)}s).`,
+ );
+ }
}
}
@@ -480,11 +625,24 @@ export class StreamingVideoDecoder {
return segments;
}
- getEffectiveDuration(trimRegions?: TrimRegion[], speedRegions?: SpeedRegion[]): number {
+ getExportMetrics(
+ targetFrameRate: number,
+ trimRegions?: TrimRegion[],
+ speedRegions?: SpeedRegion[],
+ ): { effectiveDuration: number; totalFrames: number } {
if (!this.metadata) throw new Error("Must call loadMetadata() first");
const trimSegments = this.computeSegments(this.metadata.duration, trimRegions);
- const speedSegments = this.splitBySpeed(trimSegments, speedRegions);
- return speedSegments.reduce((sum, seg) => sum + (seg.endSec - seg.startSec) / seg.speed, 0);
+ const segments = this.splitBySpeed(trimSegments, speedRegions);
+ return {
+ effectiveDuration: segments.reduce(
+ (sum, seg) => sum + (seg.endSec - seg.startSec) / seg.speed,
+ 0,
+ ),
+ totalFrames: segments.reduce((sum, seg) => {
+ const segDur = seg.endSec - seg.startSec - EPSILON_SEC;
+ return sum + Math.max(0, Math.ceil((segDur / seg.speed) * targetFrameRate));
+ }, 0),
+ };
}
private splitBySpeed(
diff --git a/src/lib/exporter/videoExporter.browser.test.ts b/src/lib/exporter/videoExporter.browser.test.ts
new file mode 100644
index 0000000..ec2b0f6
--- /dev/null
+++ b/src/lib/exporter/videoExporter.browser.test.ts
@@ -0,0 +1,43 @@
+import { describe, expect, it } from "vitest";
+import sampleVideoUrl from "../../../tests/fixtures/sample.webm?url";
+import type { ExportProgress } from "./types";
+import { VideoExporter } from "./videoExporter";
+
+describe("VideoExporter (real browser)", () => {
+ it("exports a valid MP4 blob from a real video", async () => {
+ const progressEvents: ExportProgress[] = [];
+
+ const exporter = new VideoExporter({
+ videoUrl: sampleVideoUrl,
+ width: 320,
+ height: 180,
+ frameRate: 15,
+ bitrate: 1_000_000,
+ wallpaper: "#1a1a2e",
+ zoomRegions: [],
+ showShadow: false,
+ shadowIntensity: 0,
+ showBlur: false,
+ cropRegion: { x: 0, y: 0, width: 1, height: 1 },
+ onProgress: (p) => progressEvents.push(p),
+ });
+
+ const result = await exporter.export();
+
+ expect(result.success, result.error).toBe(true);
+ expect(result.blob).toBeInstanceOf(Blob);
+
+ const buf = await result.blob!.arrayBuffer();
+ const bytes = new Uint8Array(buf);
+ const ftyp = new TextDecoder().decode(bytes.slice(4, 8));
+ expect(ftyp).toBe("ftyp");
+
+ expect(result.blob!.size).toBeGreaterThan(1024);
+
+ expect(progressEvents.length).toBeGreaterThan(0);
+
+ const finalizing = progressEvents.filter((p) => p.phase === "finalizing");
+ expect(finalizing.length).toBeGreaterThan(0);
+ expect(finalizing.at(-1)!.percentage).toBe(100);
+ });
+});
diff --git a/src/lib/exporter/videoExporter.ts b/src/lib/exporter/videoExporter.ts
index 761186f..44c1b88 100644
--- a/src/lib/exporter/videoExporter.ts
+++ b/src/lib/exporter/videoExporter.ts
@@ -4,8 +4,10 @@ import type {
SpeedRegion,
TrimRegion,
WebcamLayoutPreset,
+ WebcamSizePreset,
ZoomRegion,
} from "@/components/video-editor/types";
+import { getPlatform } from "@/utils/platformUtils";
import { AsyncVideoFrameQueue } from "./asyncVideoFrameQueue";
import { AudioProcessor } from "./audioEncoder";
import { FrameRenderer } from "./frameRenderer";
@@ -33,6 +35,7 @@ interface VideoExporterConfig extends ExportConfig {
cropRegion: CropRegion;
webcamLayoutPreset?: WebcamLayoutPreset;
webcamMaskShape?: import("@/components/video-editor/types").WebcamMaskShape;
+ webcamSizePreset?: WebcamSizePreset;
webcamPosition?: { cx: number; cy: number } | null;
annotationRegions?: AnnotationRegion[];
previewWidth?: number;
@@ -110,6 +113,8 @@ export class VideoExporter {
this.fatalEncoderError = null;
try {
+ const platform = await getPlatform();
+
const streamingDecoder = new StreamingVideoDecoder();
this.streamingDecoder = streamingDecoder;
const videoInfo = await streamingDecoder.loadMetadata(this.config.videoUrl);
@@ -137,12 +142,14 @@ export class VideoExporter {
webcamSize: webcamInfo ? { width: webcamInfo.width, height: webcamInfo.height } : null,
webcamLayoutPreset: this.config.webcamLayoutPreset,
webcamMaskShape: this.config.webcamMaskShape,
+ webcamSizePreset: this.config.webcamSizePreset,
webcamPosition: this.config.webcamPosition,
annotationRegions: this.config.annotationRegions,
speedRegions: this.config.speedRegions,
previewWidth: this.config.previewWidth,
previewHeight: this.config.previewHeight,
cursorTelemetry: this.config.cursorTelemetry,
+ platform,
});
this.renderer = renderer;
await renderer.initialize();
@@ -154,17 +161,11 @@ export class VideoExporter {
this.muxer = muxer;
await muxer.initialize();
- const effectiveDuration = streamingDecoder.getEffectiveDuration(
+ const { totalFrames } = streamingDecoder.getExportMetrics(
+ this.config.frameRate,
this.config.trimRegions,
this.config.speedRegions,
);
- const totalFrames = Math.ceil(effectiveDuration * this.config.frameRate);
- const readEndSec = Math.max(videoInfo.duration, videoInfo.streamDuration ?? 0) + 0.5;
-
- console.log("[VideoExporter] Original duration:", videoInfo.duration, "s");
- console.log("[VideoExporter] Effective duration:", effectiveDuration, "s");
- console.log("[VideoExporter] Total frames to export:", totalFrames);
- console.log("[VideoExporter] Using streaming decode (web-demuxer + VideoDecoder)");
const frameDuration = 1_000_000 / this.config.frameRate;
let frameIndex = 0;
@@ -234,25 +235,29 @@ export class VideoExporter {
const canvas = renderer.getCanvas();
- // Read raw pixels from the canvas instead of passing
- // the canvas directly to VideoFrame. On some Linux
- // systems the GPU shared-image path (EGL/Ozone) fails
- // silently, producing empty frames.
- const canvasCtx = canvas.getContext("2d")!;
- const imageData = canvasCtx.getImageData(0, 0, canvas.width, canvas.height);
- const exportFrame = new VideoFrame(imageData.data.buffer, {
- format: "RGBA",
- codedWidth: canvas.width,
- codedHeight: canvas.height,
- timestamp,
- duration: frameDuration,
- colorSpace: {
- primaries: "bt709",
- transfer: "iec61966-2-1",
- matrix: "rgb",
- fullRange: true,
- },
- });
+ let exportFrame: VideoFrame;
+
+ // On some Linux systems the GPU shared-image path (EGL/Ozone) fails
+ // silently, producing empty frames, so we force a CPU readback instead.
+ if (platform === "linux") {
+ const canvasCtx = canvas.getContext("2d")!;
+ const imageData = canvasCtx.getImageData(0, 0, canvas.width, canvas.height);
+ exportFrame = new VideoFrame(imageData.data.buffer, {
+ format: "RGBA",
+ codedWidth: canvas.width,
+ codedHeight: canvas.height,
+ timestamp,
+ duration: frameDuration,
+ colorSpace: {
+ primaries: "bt709",
+ transfer: "iec61966-2-1",
+ matrix: "rgb",
+ fullRange: true,
+ },
+ });
+ } else {
+ exportFrame = new VideoFrame(canvas, { timestamp, duration: frameDuration });
+ }
while (
this.encoder &&
@@ -343,7 +348,7 @@ export class VideoExporter {
this.config.videoUrl,
this.config.trimRegions,
this.config.speedRegions,
- readEndSec,
+ videoInfo.duration,
);
}
}
@@ -419,7 +424,7 @@ export class VideoExporter {
})();
this.muxingPromises.push(muxingPromise);
- this.encodeQueue--;
+ this.encodeQueue = Math.max(0, this.encodeQueue - 1);
},
error: (error) => {
console.error("[VideoExporter] Encoder error:", error);
diff --git a/src/lib/requestCameraAccess.ts b/src/lib/requestCameraAccess.ts
index 2494224..cfcd4d1 100644
--- a/src/lib/requestCameraAccess.ts
+++ b/src/lib/requestCameraAccess.ts
@@ -17,9 +17,7 @@ export async function requestCameraAccess(): Promise {
if (window.electronAPI?.requestCameraAccess) {
try {
const electronResult = await window.electronAPI.requestCameraAccess();
- if (!electronResult.success || !electronResult.granted) {
- return electronResult;
- }
+ return electronResult;
} catch (error) {
return {
success: false,
diff --git a/src/lib/shortcuts.ts b/src/lib/shortcuts.ts
index 00b96b6..96592a4 100644
--- a/src/lib/shortcuts.ts
+++ b/src/lib/shortcuts.ts
@@ -3,6 +3,7 @@ export const SHORTCUT_ACTIONS = [
"addTrim",
"addSpeed",
"addAnnotation",
+ "addBlur",
"addKeyframe",
"deleteSelected",
"playPause",
@@ -108,6 +109,7 @@ export const DEFAULT_SHORTCUTS: ShortcutsConfig = {
addTrim: { key: "t" },
addSpeed: { key: "s" },
addAnnotation: { key: "a" },
+ addBlur: { key: "b" },
addKeyframe: { key: "f" },
deleteSelected: { key: "d", ctrl: true },
playPause: { key: " " },
@@ -118,6 +120,7 @@ export const SHORTCUT_LABELS: Record = {
addTrim: "Add Trim",
addSpeed: "Add Speed",
addAnnotation: "Add Annotation",
+ addBlur: "Add Blur",
addKeyframe: "Add Keyframe",
deleteSelected: "Delete Selected",
playPause: "Play / Pause",
@@ -125,9 +128,10 @@ export const SHORTCUT_LABELS: Record = {
export function matchesShortcut(
e: KeyboardEvent,
- binding: ShortcutBinding,
+ binding: ShortcutBinding | undefined,
isMacPlatform: boolean,
): boolean {
+ if (!binding) return false;
if (e.key.toLowerCase() !== binding.key.toLowerCase()) return false;
const primaryMod = isMacPlatform ? e.metaKey : e.ctrlKey;
diff --git a/src/main.tsx b/src/main.tsx
index 670e2b6..365bdc7 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -4,6 +4,17 @@ import App from "./App.tsx";
import { I18nProvider } from "./contexts/I18nContext";
import "./index.css";
+const windowType = new URLSearchParams(window.location.search).get("windowType") || "";
+if (
+ windowType === "hud-overlay" ||
+ windowType === "source-selector" ||
+ windowType === "countdown-overlay"
+) {
+ document.body.style.background = "transparent";
+ document.documentElement.style.background = "transparent";
+ document.getElementById("root")?.style.setProperty("background", "transparent");
+}
+
ReactDOM.createRoot(document.getElementById("root")!).render(
diff --git a/src/utils/platformUtils.ts b/src/utils/platformUtils.ts
index 37d5a6d..2fb57e1 100644
--- a/src/utils/platformUtils.ts
+++ b/src/utils/platformUtils.ts
@@ -3,7 +3,7 @@ let cachedPlatform: string | null = null;
/**
* Gets the current platform from Electron
*/
-const getPlatform = async (): Promise => {
+export const getPlatform = async (): Promise => {
if (cachedPlatform) return cachedPlatform;
try {
@@ -14,9 +14,14 @@ const getPlatform = async (): Promise => {
console.warn("Failed to get platform from Electron, falling back to navigator:", error);
// Fallback for development/testing
let fallbackPlatform = "win32";
- if (typeof navigator !== "undefined" && /Mac|iPhone|iPad|iPod/.test(navigator.platform)) {
- fallbackPlatform = "darwin";
+ if (typeof navigator !== "undefined") {
+ if (/Mac|iPhone|iPad|iPod/.test(navigator.platform)) {
+ fallbackPlatform = "darwin";
+ } else if (/Linux/.test(navigator.platform)) {
+ fallbackPlatform = "linux";
+ }
}
+
cachedPlatform = fallbackPlatform;
return fallbackPlatform;
}
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
index 4e668f3..d76ee15 100644
--- a/src/vite-env.d.ts
+++ b/src/vite-env.d.ts
@@ -19,6 +19,8 @@ interface Window {
electronAPI: {
getSources: (opts: Electron.SourcesOptions) => Promise;
switchToEditor: () => Promise;
+ switchToHud: () => Promise;
+ startNewRecording: () => Promise<{ success: boolean; error?: string }>;
openSourceSelector: () => Promise;
selectSource: (source: ProcessedDesktopSource) => Promise;
getSelectedSource: () => Promise;
diff --git a/vite.config.ts b/vite.config.ts
index 9a44766..725f24e 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -47,10 +47,16 @@ export default defineConfig({
},
rollupOptions: {
output: {
- manualChunks: {
- pixi: ["pixi.js"],
- "react-vendor": ["react", "react-dom"],
- "video-processing": ["mediabunny", "mp4box", "@fix-webm-duration/fix"],
+ manualChunks(id) {
+ if (id.includes("pixi.js") || id.includes("pixi-filters") || id.includes("@pixi/"))
+ return "pixi";
+ if (id.includes("react-dom") || id.includes("/react/")) return "react-vendor";
+ if (
+ id.includes("mediabunny") ||
+ id.includes("mp4box") ||
+ id.includes("fix-webm-duration")
+ )
+ return "video-processing";
},
},
},
diff --git a/vitest.browser.config.ts b/vitest.browser.config.ts
new file mode 100644
index 0000000..ba5cc42
--- /dev/null
+++ b/vitest.browser.config.ts
@@ -0,0 +1,28 @@
+import path from "node:path";
+import { playwright } from "@vitest/browser-playwright";
+import { defineConfig } from "vitest/config";
+
+export default defineConfig({
+ test: {
+ include: ["src/**/*.browser.test.{ts,tsx}"],
+ browser: {
+ enabled: true,
+ provider: playwright({
+ launch: {
+ // Software WebGL so Pixi.js works in headless CI without a GPU.
+ args: ["--enable-unsafe-swiftshader", "--use-gl=swiftshader"],
+ },
+ }),
+ headless: true,
+ instances: [{ browser: "chromium" }],
+ },
+ testTimeout: 120_000,
+ hookTimeout: 30_000,
+ },
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "src"),
+ },
+ },
+ assetsInclude: ["**/*.webm"],
+});