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/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 9393ef6..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,6 +27,7 @@ dist-ssr
*.sw?
release/**
*.kiro/
+.claude/
# npx electron-builder --mac --win
# Playwright
@@ -32,4 +35,11 @@ test-results
playwright-report/
# Vitest browser mode screenshots
-__screenshots__/
\ No newline at end of file
+__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..ca053ef 100644
--- a/electron-builder.json5
+++ b/electron-builder.json5
@@ -20,16 +20,20 @@
"!CONTRIBUTING.md",
"!LICENSE"
],
- "extraResources": [
- {
- "from": "public/wallpapers",
- "to": "assets/wallpapers"
- }
- ],
- "publish": [{"provider": "github"}],
-
- "mac": {
- "hardenedRuntime": false,
+ // Asset layout contract: "wallpapers/" under resourcesPath must align with
+ // assetBaseDir in electron/preload.ts (packaged branch).
+ "extraResources": [
+ {
+ "from": "public/wallpapers",
+ "to": "wallpapers"
+ }
+ ],
+
+ "mac": {
+ "notarize": false,
+ "hardenedRuntime": true,
+ "entitlements": "macos.entitlements",
+ "entitlementsInherit": "macos.entitlements",
"target": [
{
"target": "dmg",
@@ -38,13 +42,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 +58,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 b2a3720..85d8294 100644
--- a/electron/electron-env.d.ts
+++ b/electron/electron-env.d.ts
@@ -37,7 +37,7 @@ interface Window {
status: string;
error?: string;
}>;
- getAssetBasePath: () => Promise;
+ assetBaseUrl: string;
storeRecordedVideo: (
videoData: ArrayBuffer,
fileName: string,
@@ -63,7 +63,8 @@ interface Window {
message?: string;
error?: string;
}>;
- setRecordingState: (recording: boolean) => Promise;
+ setRecordingState: (recording: boolean, recordingId?: number) => Promise;
+ discardCursorTelemetry: (recordingId: number) => Promise;
getCursorTelemetry: (videoPath?: string) => Promise<{
success: boolean;
samples: CursorTelemetryPoint[];
@@ -135,6 +136,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 2dfb4d3..4222741 100644
--- a/electron/i18n.ts
+++ b/electron/i18n.ts
@@ -7,24 +7,45 @@ 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 commonJa from "../src/i18n/locales/ja-JP/common.json";
+import dialogsJa from "../src/i18n/locales/ja-JP/dialogs.json";
+import commonKo from "../src/i18n/locales/ko-KR/common.json";
+import dialogsKo from "../src/i18n/locales/ko-KR/dialogs.json";
+import commonTr from "../src/i18n/locales/tr/common.json";
+import dialogsTr from "../src/i18n/locales/tr/dialogs.json";
import commonZh from "../src/i18n/locales/zh-CN/common.json";
import dialogsZh from "../src/i18n/locales/zh-CN/dialogs.json";
+import commonZhTw from "../src/i18n/locales/zh-TW/common.json";
+import dialogsZhTw from "../src/i18n/locales/zh-TW/dialogs.json";
-type Locale = "en" | "zh-CN" | "es" | "fr";
+type Locale = "en" | "zh-CN" | "zh-TW" | "es" | "fr" | "ja-JP" | "ko-KR" | "tr";
type Namespace = "common" | "dialogs";
type MessageMap = Record;
const messages: Record> = {
en: { common: commonEn, dialogs: dialogsEn },
"zh-CN": { common: commonZh, dialogs: dialogsZh },
+ "zh-TW": { common: commonZhTw, dialogs: dialogsZhTw },
es: { common: commonEs, dialogs: dialogsEs },
fr: { common: commonFr, dialogs: dialogsFr },
+ "ja-JP": { common: commonJa, dialogs: dialogsJa },
+ "ko-KR": { common: commonKo, dialogs: dialogsKo },
+ tr: { common: commonTr, dialogs: dialogsTr },
};
let currentLocale: Locale = "en";
export function setMainLocale(locale: string) {
- if (locale === "en" || locale === "zh-CN" || locale === "es" || locale === "fr") {
+ if (
+ locale === "en" ||
+ locale === "zh-CN" ||
+ locale === "zh-TW" ||
+ locale === "es" ||
+ locale === "fr" ||
+ locale === "ja-JP" ||
+ locale === "ko-KR" ||
+ locale === "tr"
+ ) {
currentLocale = locale;
}
}
diff --git a/electron/ipc/handlers.ts b/electron/ipc/handlers.ts
index 4cb4875..95ed797 100644
--- a/electron/ipc/handlers.ts
+++ b/electron/ipc/handlers.ts
@@ -1,6 +1,6 @@
import fs from "node:fs/promises";
import path from "node:path";
-import { fileURLToPath, pathToFileURL } from "node:url";
+import { fileURLToPath } from "node:url";
import {
app,
BrowserWindow,
@@ -11,6 +11,10 @@ import {
shell,
systemPreferences,
} from "electron";
+import {
+ type CursorTelemetryPoint,
+ createCursorTelemetryBuffer,
+} from "../../src/lib/cursorTelemetryBuffer";
import {
normalizeProjectMedia,
normalizeRecordingSession,
@@ -275,14 +279,23 @@ async function storeRecordedSessionFiles(payload: StoreRecordedSessionInput) {
currentProjectPath = null;
const telemetryPath = `${screenVideoPath}.cursor.json`;
- if (pendingCursorSamples.length > 0) {
- await fs.writeFile(
- telemetryPath,
- JSON.stringify({ version: CURSOR_TELEMETRY_VERSION, samples: pendingCursorSamples }, null, 2),
- "utf-8",
- );
+ const pendingBatch = cursorTelemetryBuffer.takeNextBatch();
+ if (pendingBatch && pendingBatch.samples.length > 0) {
+ try {
+ await fs.writeFile(
+ telemetryPath,
+ JSON.stringify(
+ { version: CURSOR_TELEMETRY_VERSION, samples: pendingBatch.samples },
+ null,
+ 2,
+ ),
+ "utf-8",
+ );
+ } catch (err) {
+ cursorTelemetryBuffer.prependBatch(pendingBatch);
+ throw err;
+ }
}
- pendingCursorSamples = [];
const sessionManifestPath = path.join(
RECORDINGS_DIR,
@@ -302,16 +315,11 @@ const CURSOR_TELEMETRY_VERSION = 1;
const CURSOR_SAMPLE_INTERVAL_MS = 100;
const MAX_CURSOR_SAMPLES = 60 * 60 * 10; // 1 hour @ 10Hz
-interface CursorTelemetryPoint {
- timeMs: number;
- cx: number;
- cy: number;
-}
-
let cursorCaptureInterval: NodeJS.Timeout | null = null;
let cursorCaptureStartTimeMs = 0;
-let activeCursorSamples: CursorTelemetryPoint[] = [];
-let pendingCursorSamples: CursorTelemetryPoint[] = [];
+const cursorTelemetryBuffer = createCursorTelemetryBuffer({
+ maxActiveSamples: MAX_CURSOR_SAMPLES,
+});
function clamp(value: number, min: number, max: number) {
return Math.min(max, Math.max(min, value));
@@ -338,29 +346,173 @@ function sampleCursorPoint() {
const cx = clamp((cursor.x - bounds.x) / width, 0, 1);
const cy = clamp((cursor.y - bounds.y) / height, 0, 1);
- activeCursorSamples.push({
+ cursorTelemetryBuffer.push({
timeMs: Math.max(0, Date.now() - cursorCaptureStartTimeMs),
cx,
cy,
});
-
- if (activeCursorSamples.length > MAX_CURSOR_SAMPLES) {
- activeCursorSamples.shift();
- }
}
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", async () => {
+ ipcMain.handle("start-new-recording", () => {
try {
setCurrentRecordingSessionState(null);
if (switchToHud) {
@@ -490,7 +642,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 };
@@ -531,18 +700,21 @@ export function registerIpcHandlers(
}
});
- ipcMain.handle("set-recording-state", (_, recording: boolean) => {
+ ipcMain.handle("set-recording-state", (_, recording: boolean, recordingId?: number) => {
if (recording) {
stopCursorCapture();
- activeCursorSamples = [];
- pendingCursorSamples = [];
+ // The renderer is the source of truth for the recording id (it
+ // uses the same id as the saved fileName). Fall back to a
+ // timestamp only if the renderer didn't supply one, so the
+ // buffer always has a stable key per session.
+ const id = typeof recordingId === "number" ? recordingId : Date.now();
+ cursorTelemetryBuffer.startSession(id);
cursorCaptureStartTimeMs = Date.now();
sampleCursorPoint();
cursorCaptureInterval = setInterval(sampleCursorPoint, CURSOR_SAMPLE_INTERVAL_MS);
} else {
stopCursorCapture();
- pendingCursorSamples = [...activeCursorSamples];
- activeCursorSamples = [];
+ cursorTelemetryBuffer.endSession();
}
const source = selectedSource || { name: "Screen" };
@@ -551,6 +723,10 @@ export function registerIpcHandlers(
}
});
+ ipcMain.handle("discard-cursor-telemetry", (_, recordingId: number) => {
+ cursorTelemetryBuffer.discardBatch(recordingId);
+ });
+
ipcMain.handle("get-cursor-telemetry", async (_, videoPath?: string) => {
const targetVideoPath = normalizeVideoSourcePath(
videoPath ?? currentRecordingSession?.screenVideoPath,
@@ -616,7 +792,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);
@@ -624,20 +812,15 @@ export function registerIpcHandlers(
}
});
- // Return base path for assets so renderer can resolve file:// paths in production
- ipcMain.handle("get-asset-base-path", () => {
- try {
- if (app.isPackaged) {
- const assetPath = path.join(process.resourcesPath, "assets");
- return pathToFileURL(`${assetPath}${path.sep}`).toString();
- }
- const assetPath = path.join(app.getAppPath(), "public", "assets");
- return pathToFileURL(`${assetPath}${path.sep}`).toString();
- } catch (err) {
- console.error("Failed to resolve asset base path:", err);
- return null;
- }
- });
+ /**
+ * 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 {
@@ -664,11 +847,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) {
@@ -680,7 +870,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 0f06f9e..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();
}
});
@@ -384,8 +413,10 @@ app.whenReady().then(async () => {
registerIpcHandlers(
createEditorWindowWrapper,
createSourceSelectorWindowWrapper,
+ createCountdownOverlayWindowWrapper,
() => mainWindow,
() => sourceSelectorWindow,
+ () => countdownOverlayWindow,
(recording: boolean, sourceName: string) => {
selectedSourceName = sourceName;
if (!tray) createTray();
diff --git a/electron/preload.ts b/electron/preload.ts
index eeca25c..46e16f0 100644
--- a/electron/preload.ts
+++ b/electron/preload.ts
@@ -1,17 +1,21 @@
import { contextBridge, ipcRenderer } from "electron";
import type { RecordingSession, StoreRecordedSessionInput } from "../src/lib/recordingSession";
+// Asset base URL is passed from the main process via webPreferences.additionalArguments
+// (see windows.ts). Sandboxed preloads cannot import node:path / node:url, so we
+// can't compute it here.
+const ASSET_BASE_URL_ARG_PREFIX = "--asset-base-url=";
+const assetBaseUrlArg = process.argv.find((arg) => arg.startsWith(ASSET_BASE_URL_ARG_PREFIX));
+const assetBaseUrl = assetBaseUrlArg ? assetBaseUrlArg.slice(ASSET_BASE_URL_ARG_PREFIX.length) : "";
+
contextBridge.exposeInMainWorld("electronAPI", {
+ assetBaseUrl,
hudOverlayHide: () => {
ipcRenderer.send("hud-overlay-hide");
},
hudOverlayClose: () => {
ipcRenderer.send("hud-overlay-close");
},
- getAssetBasePath: async () => {
- // ask main process for the correct base path (production vs dev)
- return await ipcRenderer.invoke("get-asset-base-path");
- },
getSources: async (opts: Electron.SourcesOptions) => {
return await ipcRenderer.invoke("get-sources", opts);
},
@@ -47,12 +51,15 @@ contextBridge.exposeInMainWorld("electronAPI", {
getRecordedVideoPath: () => {
return ipcRenderer.invoke("get-recorded-video-path");
},
- setRecordingState: (recording: boolean) => {
- return ipcRenderer.invoke("set-recording-state", recording);
+ setRecordingState: (recording: boolean, recordingId?: number) => {
+ return ipcRenderer.invoke("set-recording-state", recording, recordingId);
},
getCursorTelemetry: (videoPath?: string) => {
return ipcRenderer.invoke("get-cursor-telemetry", videoPath);
},
+ discardCursorTelemetry: (recordingId: number) => {
+ return ipcRenderer.invoke("discard-cursor-telemetry", recordingId);
+ },
onStopRecordingFromTray: (callback: () => void) => {
const listener = () => callback();
ipcRenderer.on("stop-recording-from-tray", listener);
@@ -130,6 +137,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..f94009a 100644
--- a/electron/windows.ts
+++ b/electron/windows.ts
@@ -1,5 +1,5 @@
import path from "node:path";
-import { fileURLToPath } from "node:url";
+import { fileURLToPath, pathToFileURL } from "node:url";
import { BrowserWindow, ipcMain, screen } from "electron";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -9,6 +9,13 @@ const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"];
const RENDERER_DIST = path.join(APP_ROOT, "dist");
const HEADLESS = process.env["HEADLESS"] === "true";
+// Asset base URL for renderer (wallpapers, etc.). Packaged: extraResources copies
+// public/wallpapers -> resources/wallpapers. Unpackaged: /public/.
+const ASSET_BASE_DIR = process.defaultApp
+ ? path.join(__dirname, "..", "public")
+ : process.resourcesPath;
+const ASSET_BASE_URL_ARG = `--asset-base-url=${pathToFileURL(`${ASSET_BASE_DIR}${path.sep}`).toString()}`;
+
let hudOverlayWindow: BrowserWindow | null = null;
ipcMain.on("hud-overlay-hide", () => {
@@ -17,6 +24,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;
@@ -45,12 +57,19 @@ export function createHudOverlayWindow(): BrowserWindow {
show: !HEADLESS,
webPreferences: {
preload: path.join(__dirname, "preload.mjs"),
+ additionalArguments: [ASSET_BASE_URL_ARG],
nodeIntegration: false,
contextIsolation: true,
backgroundThrottling: false,
},
});
+ // 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 +93,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";
@@ -95,6 +118,7 @@ export function createEditorWindow(): BrowserWindow {
show: !HEADLESS,
webPreferences: {
preload: path.join(__dirname, "preload.mjs"),
+ additionalArguments: [ASSET_BASE_URL_ARG],
nodeIntegration: false,
contextIsolation: true,
webSecurity: false,
@@ -120,6 +144,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;
@@ -137,11 +165,18 @@ export function createSourceSelectorWindow(): BrowserWindow {
backgroundColor: "#00000000",
webPreferences: {
preload: path.join(__dirname, "preload.mjs"),
+ additionalArguments: [ASSET_BASE_URL_ARG],
nodeIntegration: false,
contextIsolation: true,
},
});
+ // 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 +187,56 @@ 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"),
+ additionalArguments: [ASSET_BASE_URL_ARG],
+ 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..13a8658
--- /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/. Place wallpapers at that root to match the
+ # packaged layout (electron-builder extraResources -> resources/wallpapers).
+ mkdir -p "$out/lib/openscreen/public"
+ cp -r public/wallpapers "$out/lib/openscreen/public/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 d4b1d13..a449101 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,70 +16,68 @@
"@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",
"@uiw/react-color-colorful": "^2.9.2",
"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",
+ "@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",
- "@vitest/browser": "^4.0.16",
- "@vitest/browser-playwright": "^4.0.16",
- "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",
@@ -106,57 +104,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": {
@@ -167,13 +155,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"
},
@@ -182,9 +170,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": {
@@ -192,21 +180,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",
@@ -222,25 +210,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"
@@ -250,13 +228,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",
@@ -266,16 +244,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",
@@ -287,29 +255,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"
@@ -319,9 +287,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": {
@@ -339,9 +307,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": {
@@ -359,27 +327,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"
@@ -421,42 +389,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": {
@@ -464,23 +432,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": {
@@ -494,20 +462,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"
],
@@ -522,9 +490,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"
],
@@ -539,9 +507,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"
],
@@ -556,9 +524,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"
],
@@ -573,9 +541,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"
],
@@ -590,9 +558,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"
],
@@ -607,9 +575,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"
],
@@ -624,9 +592,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"
],
@@ -640,6 +608,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",
@@ -674,9 +649,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": [
{
@@ -698,9 +673,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": [
{
@@ -715,7 +690,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"
@@ -749,9 +724,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": [
{
@@ -868,10 +843,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": {
@@ -880,9 +862,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": {
@@ -924,9 +906,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": {
@@ -968,16 +950,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",
@@ -1010,9 +982,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": {
@@ -1083,9 +1055,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": {
@@ -1133,359 +1105,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": {
@@ -1507,10 +1137,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": {
@@ -1523,9 +1170,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": {
@@ -1535,6 +1182,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",
@@ -1568,9 +1231,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,
@@ -1585,9 +1248,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,
@@ -1612,9 +1275,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"
],
@@ -1625,13 +1288,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"
],
@@ -1642,13 +1305,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"
],
@@ -1659,13 +1322,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"
],
@@ -1676,13 +1339,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"
],
@@ -1693,13 +1356,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"
],
@@ -1710,13 +1373,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"
],
@@ -1727,13 +1390,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"
],
@@ -1744,13 +1407,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"
],
@@ -1761,13 +1424,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"
],
@@ -1778,13 +1441,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"
],
@@ -1795,13 +1458,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"
],
@@ -1812,13 +1475,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"
],
@@ -1829,13 +1492,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"
],
@@ -1846,13 +1509,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"
],
@@ -1863,13 +1526,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"
],
@@ -1880,13 +1543,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"
],
@@ -1897,17 +1560,18 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
- "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "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"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"netbsd"
@@ -1917,9 +1581,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"
],
@@ -1930,17 +1594,18 @@
"netbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
- "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
"cpu": [
"arm64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"openbsd"
@@ -1950,9 +1615,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"
],
@@ -1963,17 +1628,18 @@
"openbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
- "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "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"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"openharmony"
@@ -1983,9 +1649,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 +1662,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 +1679,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 +1696,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 +1713,7 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@exodus/bytes": {
@@ -2088,31 +1754,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 +1786,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 +1809,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 +1826,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 +1847,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 +1878,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 +1991,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 +2065,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 +2072,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 +2224,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": {
@@ -3195,6 +2240,7 @@
"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.59.1"
},
@@ -3209,7 +2255,8 @@
"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
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@radix-ui/number": {
"version": "1.1.1",
@@ -3333,6 +2380,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",
@@ -3399,6 +2464,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",
@@ -3568,6 +2651,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",
@@ -3605,6 +2706,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",
@@ -3708,6 +2827,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",
@@ -3782,6 +2919,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",
@@ -3816,9 +2971,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"
@@ -3980,6 +3135,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",
@@ -4146,16 +3319,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"
],
@@ -4167,9 +3340,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"
],
@@ -4181,9 +3354,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"
],
@@ -4195,9 +3368,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"
],
@@ -4209,9 +3382,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"
],
@@ -4223,9 +3396,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"
],
@@ -4237,9 +3410,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"
],
@@ -4251,9 +3424,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"
],
@@ -4265,9 +3438,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"
],
@@ -4279,9 +3452,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"
],
@@ -4293,9 +3466,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"
],
@@ -4307,9 +3494,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"
],
@@ -4321,9 +3522,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"
],
@@ -4335,9 +3536,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"
],
@@ -4349,9 +3550,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"
],
@@ -4363,9 +3564,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"
],
@@ -4377,9 +3578,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"
],
@@ -4390,10 +3591,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"
],
@@ -4405,9 +3620,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"
],
@@ -4419,9 +3634,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"
],
@@ -4433,9 +3648,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"
],
@@ -4447,9 +3662,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"
],
@@ -4477,7 +3692,8 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@szmarczak/http-timer": {
"version": "4.0.6",
@@ -4582,23 +3798,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",
@@ -4670,6 +3869,7 @@
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
"integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@types/deep-eql": "*",
"assertion-error": "^2.0.1"
@@ -4679,12 +3879,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": {
@@ -4695,7 +3896,8 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
"integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@types/dom-mediacapture-transform": {
"version": "0.1.11",
@@ -4707,16 +3909,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",
@@ -4757,9 +3960,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"
},
@@ -4781,13 +3984,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": {
@@ -4810,14 +4013,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": {
@@ -4840,13 +4043,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",
@@ -4867,9 +4063,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"
@@ -4909,14 +4105,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"
@@ -4960,9 +4156,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"
@@ -5034,12 +4230,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"
@@ -5065,64 +4261,66 @@
}
},
"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.0.16",
- "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.0.16.tgz",
- "integrity": "sha512-t4toy8X/YTnjYEPoY0pbDBg3EvDPg1elCDrfc+VupPHwoN/5/FNQ8Z+xBYIaEnOE2vVEyKwqYBzZ9h9rJtZVcg==",
+ "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": {
- "@vitest/mocker": "4.0.16",
- "@vitest/utils": "4.0.16",
+ "@blazediff/core": "1.9.1",
+ "@vitest/mocker": "4.1.5",
+ "@vitest/utils": "4.1.5",
"magic-string": "^0.30.21",
- "pixelmatch": "7.1.0",
"pngjs": "^7.0.0",
"sirv": "^3.0.2",
- "tinyrainbow": "^3.0.3",
- "ws": "^8.18.3"
+ "tinyrainbow": "^3.1.0",
+ "ws": "^8.19.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
- "vitest": "4.0.16"
+ "vitest": "4.1.5"
}
},
"node_modules/@vitest/browser-playwright": {
- "version": "4.0.16",
- "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.0.16.tgz",
- "integrity": "sha512-I2Fy/ANdphi1yI46d15o0M1M4M0UJrUiVKkH5oKeRZZCdPg0fw/cfTKZzv9Ge9eobtJYp4BGblMzXdXH0vcl5g==",
+ "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.0.16",
- "@vitest/mocker": "4.0.16",
- "tinyrainbow": "^3.0.3"
+ "@vitest/browser": "4.1.5",
+ "@vitest/mocker": "4.1.5",
+ "tinyrainbow": "^3.1.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"playwright": "*",
- "vitest": "4.0.16"
+ "vitest": "4.1.5"
},
"peerDependenciesMeta": {
"playwright": {
@@ -5130,1201 +4328,72 @@
}
}
},
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
- "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "aix"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/android-arm": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
- "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/android-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
- "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/android-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
- "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
- "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/darwin-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
- "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
- "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
- "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/linux-arm": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
- "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/linux-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
- "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/linux-ia32": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
- "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/linux-loong64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
- "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
- "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
- "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
- "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/linux-s390x": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
- "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/linux-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
- "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
- "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "netbsd"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
- "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "openbsd"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/sunos-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
- "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "sunos"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/win32-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
- "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/win32-ia32": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
- "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/@esbuild/win32-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
- "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser-playwright/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,
- "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/browser-playwright/node_modules/esbuild": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
- "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "peer": true,
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.12",
- "@esbuild/android-arm": "0.25.12",
- "@esbuild/android-arm64": "0.25.12",
- "@esbuild/android-x64": "0.25.12",
- "@esbuild/darwin-arm64": "0.25.12",
- "@esbuild/darwin-x64": "0.25.12",
- "@esbuild/freebsd-arm64": "0.25.12",
- "@esbuild/freebsd-x64": "0.25.12",
- "@esbuild/linux-arm": "0.25.12",
- "@esbuild/linux-arm64": "0.25.12",
- "@esbuild/linux-ia32": "0.25.12",
- "@esbuild/linux-loong64": "0.25.12",
- "@esbuild/linux-mips64el": "0.25.12",
- "@esbuild/linux-ppc64": "0.25.12",
- "@esbuild/linux-riscv64": "0.25.12",
- "@esbuild/linux-s390x": "0.25.12",
- "@esbuild/linux-x64": "0.25.12",
- "@esbuild/netbsd-arm64": "0.25.12",
- "@esbuild/netbsd-x64": "0.25.12",
- "@esbuild/openbsd-arm64": "0.25.12",
- "@esbuild/openbsd-x64": "0.25.12",
- "@esbuild/openharmony-arm64": "0.25.12",
- "@esbuild/sunos-x64": "0.25.12",
- "@esbuild/win32-arm64": "0.25.12",
- "@esbuild/win32-ia32": "0.25.12",
- "@esbuild/win32-x64": "0.25.12"
- }
- },
- "node_modules/@vitest/browser-playwright/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,
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/picomatch": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
- "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
- "dev": true,
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/@vitest/browser-playwright/node_modules/vite": {
- "version": "6.4.2",
- "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz",
- "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==",
- "dev": true,
- "optional": true,
- "peer": true,
- "dependencies": {
- "esbuild": "^0.25.0",
- "fdir": "^6.4.4",
- "picomatch": "^4.0.2",
- "postcss": "^8.5.3",
- "rollup": "^4.34.9",
- "tinyglobby": "^0.2.13"
- },
- "bin": {
- "vite": "bin/vite.js"
- },
- "engines": {
- "node": "^18.0.0 || ^20.0.0 || >=22.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 || >=22.0.0",
- "jiti": ">=1.21.0",
- "less": "*",
- "lightningcss": "^1.21.0",
- "sass": "*",
- "sass-embedded": "*",
- "stylus": "*",
- "sugarss": "*",
- "terser": "^5.16.0",
- "tsx": "^4.8.1",
- "yaml": "^2.4.2"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- },
- "jiti": {
- "optional": true
- },
- "less": {
- "optional": true
- },
- "lightningcss": {
- "optional": true
- },
- "sass": {
- "optional": true
- },
- "sass-embedded": {
- "optional": true
- },
- "stylus": {
- "optional": true
- },
- "sugarss": {
- "optional": true
- },
- "terser": {
- "optional": true
- },
- "tsx": {
- "optional": true
- },
- "yaml": {
- "optional": true
- }
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
- "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "aix"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/android-arm": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
- "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/android-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
- "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/android-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
- "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
- "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/darwin-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
- "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
- "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
- "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/linux-arm": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
- "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/linux-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
- "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/linux-ia32": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
- "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/linux-loong64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
- "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
- "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
- "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
- "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/linux-s390x": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
- "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/linux-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
- "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
- "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "netbsd"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
- "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "openbsd"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/sunos-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
- "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "sunos"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/win32-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
- "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/win32-ia32": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
- "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/node_modules/@esbuild/win32-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
- "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@vitest/browser/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,
- "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/browser/node_modules/esbuild": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
- "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "peer": true,
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.12",
- "@esbuild/android-arm": "0.25.12",
- "@esbuild/android-arm64": "0.25.12",
- "@esbuild/android-x64": "0.25.12",
- "@esbuild/darwin-arm64": "0.25.12",
- "@esbuild/darwin-x64": "0.25.12",
- "@esbuild/freebsd-arm64": "0.25.12",
- "@esbuild/freebsd-x64": "0.25.12",
- "@esbuild/linux-arm": "0.25.12",
- "@esbuild/linux-arm64": "0.25.12",
- "@esbuild/linux-ia32": "0.25.12",
- "@esbuild/linux-loong64": "0.25.12",
- "@esbuild/linux-mips64el": "0.25.12",
- "@esbuild/linux-ppc64": "0.25.12",
- "@esbuild/linux-riscv64": "0.25.12",
- "@esbuild/linux-s390x": "0.25.12",
- "@esbuild/linux-x64": "0.25.12",
- "@esbuild/netbsd-arm64": "0.25.12",
- "@esbuild/netbsd-x64": "0.25.12",
- "@esbuild/openbsd-arm64": "0.25.12",
- "@esbuild/openbsd-x64": "0.25.12",
- "@esbuild/openharmony-arm64": "0.25.12",
- "@esbuild/sunos-x64": "0.25.12",
- "@esbuild/win32-arm64": "0.25.12",
- "@esbuild/win32-ia32": "0.25.12",
- "@esbuild/win32-x64": "0.25.12"
- }
- },
- "node_modules/@vitest/browser/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,
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/@vitest/browser/node_modules/picomatch": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
- "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
- "dev": true,
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/@vitest/browser/node_modules/pixelmatch": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-7.1.0.tgz",
- "integrity": "sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==",
- "dev": true,
- "dependencies": {
- "pngjs": "^7.0.0"
- },
- "bin": {
- "pixelmatch": "bin/pixelmatch"
- }
- },
- "node_modules/@vitest/browser/node_modules/pngjs": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
- "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==",
- "dev": true,
- "engines": {
- "node": ">=14.19.0"
- }
- },
- "node_modules/@vitest/browser/node_modules/vite": {
- "version": "6.4.2",
- "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz",
- "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==",
- "dev": true,
- "optional": true,
- "peer": true,
- "dependencies": {
- "esbuild": "^0.25.0",
- "fdir": "^6.4.4",
- "picomatch": "^4.0.2",
- "postcss": "^8.5.3",
- "rollup": "^4.34.9",
- "tinyglobby": "^0.2.13"
- },
- "bin": {
- "vite": "bin/vite.js"
- },
- "engines": {
- "node": "^18.0.0 || ^20.0.0 || >=22.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 || >=22.0.0",
- "jiti": ">=1.21.0",
- "less": "*",
- "lightningcss": "^1.21.0",
- "sass": "*",
- "sass-embedded": "*",
- "stylus": "*",
- "sugarss": "*",
- "terser": "^5.16.0",
- "tsx": "^4.8.1",
- "yaml": "^2.4.2"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- },
- "jiti": {
- "optional": true
- },
- "less": {
- "optional": true
- },
- "lightningcss": {
- "optional": true
- },
- "sass": {
- "optional": true
- },
- "sass-embedded": {
- "optional": true
- },
- "stylus": {
- "optional": true
- },
- "sugarss": {
- "optional": true
- },
- "terser": {
- "optional": true
- },
- "tsx": {
- "optional": true
- },
- "yaml": {
- "optional": true
- }
- }
- },
"node_modules/@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/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": {
+ "@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.0.16",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.16.tgz",
- "integrity": "sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA==",
+ "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.0.3"
+ "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": {
@@ -6332,12 +4401,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"
},
@@ -6346,37 +4417,40 @@
}
},
"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": {
"url": "https://opencollective.com/vitest"
}
},
"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"
@@ -6390,29 +4464,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": {
@@ -6423,49 +4487,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": {
@@ -6509,6 +4543,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"
@@ -6518,6 +4553,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"
@@ -6529,13 +4565,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",
@@ -6555,6 +4584,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",
@@ -6563,9 +4604,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": {
@@ -6580,7 +4621,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",
@@ -6588,7 +4629,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",
@@ -6610,8 +4651,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": {
@@ -6661,52 +4702,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",
@@ -6739,9 +4734,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": {
@@ -6761,144 +4756,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": {
@@ -6914,100 +4782,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",
@@ -7030,32 +4804,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"
}
@@ -7065,6 +4820,7 @@
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=12"
}
@@ -7115,9 +4871,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": [
{
@@ -7135,10 +4891,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"
},
@@ -7152,28 +4907,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",
@@ -7197,23 +4939,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": {
@@ -7250,13 +4985,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",
@@ -7267,12 +4995,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": {
@@ -7288,9 +5020,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": [
{
@@ -7308,11 +5040,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"
@@ -7364,9 +5096,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": {
@@ -7402,16 +5134,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",
@@ -7427,38 +5149,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": {
@@ -7479,99 +5173,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",
@@ -7631,16 +5317,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",
@@ -7651,9 +5327,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": [
{
@@ -7671,28 +5347,12 @@
],
"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.2",
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
"integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=18"
}
@@ -7751,13 +5411,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": {
@@ -7795,27 +5455,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": {
@@ -7864,6 +5517,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",
@@ -7896,20 +5580,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"
@@ -7922,17 +5597,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",
@@ -7981,62 +5647,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",
@@ -8049,7 +5659,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",
@@ -8075,6 +5686,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",
@@ -8085,6 +5697,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",
@@ -8119,25 +5754,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",
@@ -8170,16 +5792,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",
@@ -8277,29 +5889,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",
@@ -8310,13 +5899,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",
@@ -8368,10 +5950,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": {
@@ -8380,9 +5969,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": {
@@ -8392,19 +5981,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",
@@ -8412,14 +5988,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"
@@ -8444,9 +6020,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": {
@@ -8494,9 +6070,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",
@@ -8512,12 +6088,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",
@@ -8562,27 +6132,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",
@@ -8601,15 +6162,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": {
@@ -8620,18 +6181,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",
@@ -8646,15 +6207,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"
}
},
@@ -8674,9 +6235,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": {
@@ -8696,33 +6257,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",
@@ -8747,9 +6290,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": {
@@ -8769,78 +6312,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"
},
@@ -8883,26 +6358,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"
@@ -8918,6 +6393,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": {
@@ -8942,13 +6418,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"
@@ -8984,16 +6460,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",
@@ -9013,10 +6479,11 @@
}
},
"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==",
- "dev": true
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
+ "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
+ "dev": true,
+ "license": "MIT"
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
@@ -9054,17 +6521,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",
@@ -9072,32 +6532,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": {
@@ -9129,41 +6592,17 @@
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0"
}
},
- "node_modules/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",
@@ -9182,13 +6621,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",
@@ -9222,9 +6654,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": [
{
@@ -9238,7 +6670,7 @@
],
"license": "MIT",
"dependencies": {
- "pure-rand": "^7.0.0"
+ "pure-rand": "^8.0.0"
},
"engines": {
"node": ">=12.17.0"
@@ -9287,9 +6719,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"
@@ -9305,48 +6737,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": {
@@ -9380,31 +6818,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",
@@ -9417,14 +6835,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": {
@@ -9445,27 +6866,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": {
@@ -9501,38 +6922,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",
@@ -9541,9 +6942,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,
@@ -9563,34 +6964,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",
@@ -9686,16 +7059,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",
@@ -9711,22 +7074,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": {
@@ -9756,10 +7108,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": {
@@ -9768,9 +7127,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": {
@@ -9780,17 +7139,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",
@@ -9810,6 +7158,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",
@@ -9828,27 +7190,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",
@@ -9895,36 +7236,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",
@@ -9977,31 +7293,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"
@@ -10064,34 +7359,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": {
@@ -10109,27 +7387,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": {
@@ -10148,47 +7416,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",
@@ -10241,33 +7468,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",
@@ -10288,13 +7488,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",
@@ -10314,33 +7507,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",
@@ -10381,18 +7557,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",
@@ -10415,13 +7585,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",
@@ -10431,26 +7594,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",
@@ -10458,23 +7601,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",
@@ -10488,20 +7614,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",
@@ -10516,10 +7628,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",
@@ -10527,17 +7643,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"
@@ -10567,36 +7677,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",
@@ -10622,22 +7712,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",
@@ -10671,28 +7754,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",
@@ -10713,13 +7783,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",
@@ -10732,7 +7795,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",
@@ -10757,54 +7821,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",
@@ -10815,16 +7831,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",
@@ -10832,291 +7838,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/lightningcss": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
- "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
- "dev": true,
- "optional": true,
- "peer": true,
- "dependencies": {
- "detect-libc": "^2.0.3"
- },
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- },
- "optionalDependencies": {
- "lightningcss-android-arm64": "1.32.0",
- "lightningcss-darwin-arm64": "1.32.0",
- "lightningcss-darwin-x64": "1.32.0",
- "lightningcss-freebsd-x64": "1.32.0",
- "lightningcss-linux-arm-gnueabihf": "1.32.0",
- "lightningcss-linux-arm64-gnu": "1.32.0",
- "lightningcss-linux-arm64-musl": "1.32.0",
- "lightningcss-linux-x64-gnu": "1.32.0",
- "lightningcss-linux-x64-musl": "1.32.0",
- "lightningcss-win32-arm64-msvc": "1.32.0",
- "lightningcss-win32-x64-msvc": "1.32.0"
- }
- },
- "node_modules/lightningcss-android-arm64": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
- "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true,
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-darwin-arm64": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
- "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true,
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-darwin-x64": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
- "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true,
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-freebsd-x64": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
- "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "peer": true,
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-arm-gnueabihf": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
- "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-arm64-gnu": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
- "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-arm64-musl": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
- "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-x64-gnu": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
- "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-linux-x64-musl": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
- "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-win32-arm64-msvc": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
- "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
- "node_modules/lightningcss-win32-x64-msvc": {
- "version": "1.32.0",
- "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
- "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
- "engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/parcel"
- }
- },
"node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
@@ -11136,17 +7857,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": {
@@ -11187,19 +7908,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",
@@ -11230,10 +7938,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"
},
@@ -11287,133 +7995,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"
},
@@ -11454,19 +8039,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",
@@ -11480,29 +8052,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",
@@ -11519,39 +8068,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",
@@ -11569,58 +8085,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",
@@ -11673,32 +8137,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",
@@ -11710,63 +8148,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",
@@ -11798,9 +8201,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/*"
@@ -11814,12 +8217,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",
@@ -11842,6 +8239,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",
@@ -11911,16 +8320,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",
@@ -11932,15 +8331,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"
@@ -11957,91 +8357,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"
},
@@ -12136,59 +8497,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": {
@@ -12209,44 +8550,35 @@
}
},
"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==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
"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": ">=10"
}
@@ -12288,9 +8620,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": {
@@ -12298,13 +8630,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"
@@ -12319,104 +8664,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": {
@@ -12428,16 +8756,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",
@@ -12451,43 +8769,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",
@@ -12541,13 +8822,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",
@@ -12559,16 +8833,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"
@@ -12598,17 +8872,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": {
@@ -12638,16 +8955,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"
@@ -12657,60 +8971,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",
@@ -12718,13 +8981,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"
@@ -12744,6 +9007,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"
@@ -12759,6 +9023,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",
@@ -12775,23 +9040,15 @@
"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",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/pe-library": {
"version": "0.4.1",
@@ -12808,20 +9065,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",
@@ -12829,134 +9072,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",
@@ -12964,12 +9079,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"
@@ -12984,29 +9099,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",
@@ -13016,29 +9108,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",
@@ -13052,33 +9121,55 @@
}
},
"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.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.59.1"
},
@@ -13097,6 +9188,7 @@
"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"
},
@@ -13104,20 +9196,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",
@@ -13133,27 +9211,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.8",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
- "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
+ "version": "8.5.10",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
+ "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
"funding": [
{
"type": "opencollective",
@@ -13168,6 +9239,7 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -13365,14 +9437,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",
@@ -13383,23 +9447,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",
@@ -13410,13 +9457,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",
@@ -13442,6 +9482,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",
@@ -13454,30 +9500,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": {
@@ -13496,9 +9522,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": [
{
@@ -13513,9 +9539,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": {
@@ -13597,12 +9623,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": {
@@ -13610,34 +9636,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": {
@@ -13645,9 +9664,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",
@@ -13702,13 +9721,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": {
@@ -13766,77 +9785,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",
@@ -13852,65 +9800,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",
@@ -13923,6 +9812,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",
@@ -13937,92 +9838,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",
@@ -14043,13 +9858,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",
@@ -14075,12 +9883,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"
},
@@ -14115,25 +9924,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",
@@ -14163,20 +9981,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": {
@@ -14199,9 +10015,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": {
@@ -14215,28 +10031,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"
}
},
@@ -14292,9 +10111,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": {
@@ -14302,11 +10121,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",
@@ -14331,16 +10153,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": {
@@ -14368,31 +10187,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"
@@ -14405,6 +10204,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"
@@ -14431,14 +10231,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"
@@ -14494,16 +10294,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",
@@ -14518,11 +10313,25 @@
"node": ">=10"
}
},
+ "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",
@@ -14532,16 +10341,6 @@
"node": ">=18"
}
},
- "node_modules/slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/slice-ansi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
@@ -14585,18 +10384,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": {
@@ -14639,42 +10438,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",
@@ -14683,65 +10446,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",
@@ -14760,10 +10477,11 @@
}
},
"node_modules/std-env": {
- "version": "3.10.0",
- "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
- "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
- "dev": true
+ "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"
},
"node_modules/string_decoder": {
"version": "1.3.0",
@@ -14789,6 +10507,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",
@@ -14804,6 +10523,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",
@@ -14814,10 +10534,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"
@@ -14826,11 +10547,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"
@@ -14839,17 +10590,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": {
@@ -14865,36 +10616,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": {
@@ -14914,35 +10647,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",
@@ -14981,155 +10685,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",
@@ -15138,9 +10693,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",
@@ -15148,9 +10703,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",
@@ -15193,30 +10748,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",
@@ -15260,9 +10826,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": {
@@ -15282,39 +10848,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": {
@@ -15358,23 +10895,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",
@@ -15396,9 +10916,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"
@@ -15411,17 +10931,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": {
@@ -15429,14 +10942,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"
@@ -15445,63 +10957,33 @@
"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.1.0",
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
"integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/tldts": {
- "version": "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"
},
@@ -15537,45 +11019,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==",
- "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_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",
"engines": {
"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": {
@@ -15613,33 +11077,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",
@@ -15655,9 +11106,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": {
@@ -15665,36 +11116,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": {
@@ -15708,9 +11159,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": [
{
@@ -15819,16 +11270,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",
@@ -15848,17 +11289,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",
@@ -15876,646 +11306,24 @@
}
},
"node_modules/vite": {
- "version": "5.4.21",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
- "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz",
+ "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==",
"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,
- "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",
+ "esbuild": "^0.27.0",
+ "fdir": "^6.5.0",
"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.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
- "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/android-arm": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
- "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/android-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
- "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/android-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
- "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
- "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/darwin-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
- "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
- "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
- "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-arm": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
- "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
- "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-ia32": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
- "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-loong64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
- "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
- "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
- "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
- "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-s390x": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
- "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/linux-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
- "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
- "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
- "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/sunos-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
- "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/win32-arm64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
- "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/win32-ia32": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
- "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/vitest/node_modules/@esbuild/win32-x64": {
- "version": "0.25.12",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
- "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "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,
- "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.25.12",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
- "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
- "dev": true,
- "hasInstallScript": true,
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.12",
- "@esbuild/android-arm": "0.25.12",
- "@esbuild/android-arm64": "0.25.12",
- "@esbuild/android-x64": "0.25.12",
- "@esbuild/darwin-arm64": "0.25.12",
- "@esbuild/darwin-x64": "0.25.12",
- "@esbuild/freebsd-arm64": "0.25.12",
- "@esbuild/freebsd-x64": "0.25.12",
- "@esbuild/linux-arm": "0.25.12",
- "@esbuild/linux-arm64": "0.25.12",
- "@esbuild/linux-ia32": "0.25.12",
- "@esbuild/linux-loong64": "0.25.12",
- "@esbuild/linux-mips64el": "0.25.12",
- "@esbuild/linux-ppc64": "0.25.12",
- "@esbuild/linux-riscv64": "0.25.12",
- "@esbuild/linux-s390x": "0.25.12",
- "@esbuild/linux-x64": "0.25.12",
- "@esbuild/netbsd-arm64": "0.25.12",
- "@esbuild/netbsd-x64": "0.25.12",
- "@esbuild/openbsd-arm64": "0.25.12",
- "@esbuild/openbsd-x64": "0.25.12",
- "@esbuild/openharmony-arm64": "0.25.12",
- "@esbuild/sunos-x64": "0.25.12",
- "@esbuild/win32-arm64": "0.25.12",
- "@esbuild/win32-ia32": "0.25.12",
- "@esbuild/win32-x64": "0.25.12"
- }
- },
- "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,
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/vitest/node_modules/picomatch": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
- "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
- "dev": true,
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/vitest/node_modules/vite": {
- "version": "6.4.2",
- "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz",
- "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==",
- "dev": true,
- "dependencies": {
- "esbuild": "^0.25.0",
- "fdir": "^6.4.4",
- "picomatch": "^4.0.2",
- "postcss": "^8.5.3",
- "rollup": "^4.34.9",
- "tinyglobby": "^0.2.13"
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
- "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
@@ -16524,14 +11332,14 @@
"fsevents": "~2.3.3"
},
"peerDependencies": {
- "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "@types/node": "^20.19.0 || >=22.12.0",
"jiti": ">=1.21.0",
- "less": "*",
+ "less": "^4.0.0",
"lightningcss": "^1.21.0",
- "sass": "*",
- "sass-embedded": "*",
- "stylus": "*",
- "sugarss": "*",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
@@ -16572,6 +11380,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",
@@ -16640,27 +11575,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",
@@ -16678,29 +11607,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"
@@ -16711,6 +11630,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",
@@ -16724,6 +11644,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",
@@ -16736,6 +11707,7 @@
"resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
"integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10.0.0"
},
@@ -16752,19 +11724,6 @@
}
}
},
- "node_modules/xhr": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz",
- "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "global": "~4.4.0",
- "is-function": "^1.0.1",
- "parse-headers": "^2.0.0",
- "xtend": "^4.0.0"
- }
- },
"node_modules/xml-name-validator": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
@@ -16775,37 +11734,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",
@@ -16823,16 +11751,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",
@@ -16851,9 +11769,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 1c4c471..102e97c 100644
--- a/package.json
+++ b/package.json
@@ -40,70 +40,68 @@
"@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",
"@uiw/react-color-colorful": "^2.9.2",
"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",
+ "@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",
- "@vitest/browser": "^4.0.16",
- "@vitest/browser-playwright": "^4.0.16",
- "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 c320946..b37af68 100644
--- a/scripts/i18n-check.mjs
+++ b/scripts/i18n-check.mjs
@@ -1,17 +1,15 @@
#!/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
*/
-
import fs from "node:fs";
import path from "node:path";
const LOCALES_DIR = path.resolve("src/i18n/locales");
const BASE_LOCALE = "en";
-const COMPARE_LOCALES = ["zh-CN", "es", "tr"];
function getKeys(obj, prefix = "") {
const keys = [];
@@ -34,12 +32,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 +82,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 eb6a9be..3f8064e 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,
@@ -33,7 +34,12 @@ import { cn } from "@/lib/utils";
import ColorPicker from "../ui/color-picker";
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;
@@ -41,6 +47,7 @@ interface AnnotationSettingsPanelProps {
onTypeChange: (type: AnnotationType) => void;
onStyleChange: (style: Partial
) => void;
onFigureDataChange?: (figureData: FigureData) => void;
+ onDuplicate?: () => void;
onDelete: () => void;
}
@@ -63,6 +70,7 @@ export function AnnotationSettingsPanel({
onTypeChange,
onStyleChange,
onFigureDataChange,
+ onDuplicate,
onDelete,
}: AnnotationSettingsPanelProps) {
const t = useScopedT("settings");
@@ -593,15 +601,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 c9bfb6c..82e106c 100644
--- a/src/components/video-editor/SettingsPanel.tsx
+++ b/src/components/video-editor/SettingsPanel.tsx
@@ -13,7 +13,7 @@ import {
Upload,
X,
} from "lucide-react";
-import { useCallback, useEffect, useRef, useState } from "react";
+import { useCallback, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import {
Accordion,
@@ -33,20 +33,22 @@ import { Slider } from "@/components/ui/slider";
import { Switch } from "@/components/ui/switch";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useScopedT } from "@/contexts/I18nContext";
-import { getAssetPath } from "@/lib/assetPath";
import { WEBCAM_LAYOUT_PRESETS } from "@/lib/compositeLayout";
import type { ExportFormat, ExportQuality, GifFrameRate, GifSizePreset } from "@/lib/exporter";
import { GIF_FRAME_RATES, GIF_SIZE_PRESETS } from "@/lib/exporter";
import { cn } from "@/lib/utils";
+import { resolveImageWallpaperUrl, WALLPAPER_PATHS } from "@/lib/wallpaper";
import { type AspectRatio, isPortraitAspectRatio } from "@/utils/aspectRatioUtils";
import { getTestId } from "@/utils/getTestId";
import ColorPicker from "../ui/color-picker";
import { AnnotationSettingsPanel } from "./AnnotationSettingsPanel";
+import { BlurSettingsPanel } from "./BlurSettingsPanel";
import { CropControl } from "./CropControl";
import { KeyboardShortcutsHelp } from "./KeyboardShortcutsHelp";
import type {
AnnotationRegion,
AnnotationType,
+ BlurData,
CropRegion,
FigureData,
PlaybackSpeed,
@@ -121,11 +123,6 @@ function CustomSpeedInput({
);
}
-const WALLPAPER_COUNT = 18;
-const WALLPAPER_RELATIVE = Array.from(
- { length: WALLPAPER_COUNT },
- (_, i) => `wallpapers/wallpaper${i + 1}.jpg`,
-);
const GRADIENTS = [
"linear-gradient( 111.6deg, rgba(114,167,232,1) 9.4%, rgba(253,129,82,1) 43.9%, rgba(253,129,82,1) 54.8%, rgba(249,202,86,1) 86.3% )",
"linear-gradient(120deg, #d4fc79 0%, #96e6a1 100%)",
@@ -208,7 +205,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;
@@ -284,7 +287,13 @@ export function SettingsPanel({
onAnnotationTypeChange,
onAnnotationStyleChange,
onAnnotationFigureDataChange,
+ onAnnotationDuplicate,
onAnnotationDelete,
+ selectedBlurId,
+ blurRegions = [],
+ onBlurDataChange,
+ onBlurDataCommit,
+ onBlurDelete,
selectedSpeedId,
selectedSpeedValue,
onSpeedChange,
@@ -299,24 +308,12 @@ export function SettingsPanel({
onWebcamSizePresetCommit,
}: SettingsPanelProps) {
const t = useScopedT("settings");
- const [wallpaperPaths, setWallpaperPaths] = useState([]);
+ // Resolved URLs are for DOM rendering only (backgroundImage). The canonical
+ // `/wallpapers/wallpaperN.jpg` form in WALLPAPER_PATHS is what gets persisted
+ // on click — never the machine-specific file:// URL.
+ const wallpaperPreviewUrls = useMemo(() => WALLPAPER_PATHS.map(resolveImageWallpaperUrl), []);
const [customImages, setCustomImages] = useState([]);
const fileInputRef = useRef(null);
-
- useEffect(() => {
- let mounted = true;
- (async () => {
- try {
- const resolved = await Promise.all(WALLPAPER_RELATIVE.map((p) => getAssetPath(p)));
- if (mounted) setWallpaperPaths(resolved);
- } catch (_err) {
- if (mounted) setWallpaperPaths(WALLPAPER_RELATIVE.map((p) => `/${p}`));
- }
- })();
- return () => {
- mounted = false;
- };
- }, []);
const colorPalette = [
"#FF0000",
"#FFD700",
@@ -342,6 +339,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;
@@ -498,7 +496,7 @@ export function SettingsPanel({
setCustomImages((prev) => prev.filter((img) => img !== imageUrl));
// If the removed image was selected, clear selection
if (selected === imageUrl) {
- onWallpaperChange(wallpaperPaths[0] || WALLPAPER_RELATIVE[0]);
+ onWallpaperChange(WALLPAPER_PATHS[0]);
}
};
@@ -520,6 +518,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 (
@@ -540,11 +541,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 (
@@ -753,15 +768,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")}
))}
@@ -1066,26 +1083,12 @@ export function SettingsPanel({
);
})}
- {(wallpaperPaths.length > 0
- ? wallpaperPaths
- : WALLPAPER_RELATIVE.map((p) => `/${p}`)
- ).map((path) => {
- const isSelected = (() => {
- if (!selected) return false;
- if (selected === path) return true;
- try {
- const clean = (s: string) =>
- s.replace(/^file:\/\//, "").replace(/^\//, "");
- if (clean(selected).endsWith(clean(path))) return true;
- if (clean(path).endsWith(clean(selected))) return true;
- } catch {
- // Best-effort comparison; fallback to strict match.
- }
- return false;
- })();
+ {WALLPAPER_PATHS.map((canonicalPath, i) => {
+ const previewUrl = wallpaperPreviewUrls[i] ?? canonicalPath;
+ const isSelected = selected === canonicalPath;
return (
onWallpaperChange(path)}
+ onClick={() => onWallpaperChange(canonicalPath)}
role="button"
/>
);
diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx
index 88c3aae..7adc558 100644
--- a/src/components/video-editor/VideoEditor.tsx
+++ b/src/components/video-editor/VideoEditor.tsx
@@ -14,8 +14,8 @@ import {
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,
@@ -32,6 +32,7 @@ import { computeFrameStepTime } from "@/lib/frameStep";
import type { ProjectMedia } from "@/lib/recordingSession";
import { matchesShortcut } from "@/lib/shortcuts";
import { loadUserPreferences, saveUserPreferences } from "@/lib/userPreferences";
+import { BackgroundLoadError } from "@/lib/wallpaper";
import {
getAspectRatioValue,
getNativeAspectRatioValue,
@@ -54,11 +55,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,
@@ -122,6 +125,7 @@ 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);
@@ -149,14 +153,24 @@ export default function VideoEditor() {
const nextSpeedIdRef = useRef(1);
const { shortcuts, isMac } = useShortcuts();
+ const { locale, setLocale, t: rawT } = useI18n();
const t = useScopedT("editor");
const ts = useScopedT("settings");
- const { locale, setLocale } = useI18n();
+ const availableLocales = getAvailableLocales();
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) {
@@ -229,6 +243,7 @@ export default function VideoEditor() {
setSelectedTrimId(null);
setSelectedSpeedId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
nextZoomIdRef.current = deriveNextId(
"zoom",
@@ -307,7 +322,6 @@ export default function VideoEditor() {
aspectRatio,
webcamLayoutPreset,
webcamMaskShape,
- webcamSizePreset,
webcamPosition,
exportQuality,
exportFormat,
@@ -347,7 +361,10 @@ export default function VideoEditor() {
setLastSavedSnapshot(
createProjectSnapshot(
webcamSourcePath
- ? { screenVideoPath: sourcePath, webcamVideoPath: webcamSourcePath }
+ ? {
+ screenVideoPath: sourcePath,
+ webcamVideoPath: webcamSourcePath,
+ }
: { screenVideoPath: sourcePath },
INITIAL_EDITOR_STATE,
),
@@ -484,7 +501,6 @@ export default function VideoEditor() {
aspectRatio,
webcamLayoutPreset,
webcamMaskShape,
- webcamSizePreset,
webcamPosition,
exportQuality,
exportFormat,
@@ -493,6 +509,7 @@ export default function VideoEditor() {
gifSizePreset,
videoPath,
t,
+ webcamSizePreset,
],
);
@@ -533,18 +550,18 @@ export default function VideoEditor() {
}
if (!result.success) {
- toast.error(result.message || "Failed to load project");
+ toast.error(result.message || t("project.failedToLoad"));
return;
}
const restored = await applyLoadedProject(result.project, result.path ?? null);
if (!restored) {
- toast.error("Invalid project file format");
+ toast.error(t("project.invalidFormat"));
return;
}
- toast.success(`Project loaded from ${result.path}`);
- }, [applyLoadedProject]);
+ toast.success(t("project.loadedFrom", { path: result.path ?? "" }));
+ }, [applyLoadedProject, t]);
useEffect(() => {
const removeLoadListener = window.electronAPI.onMenuLoadProject(handleLoadProject);
@@ -626,7 +643,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) => {
@@ -634,6 +655,7 @@ export default function VideoEditor() {
if (id) {
setSelectedZoomId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
}
}, []);
@@ -642,6 +664,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);
}
}, []);
@@ -659,6 +692,7 @@ export default function VideoEditor() {
setSelectedZoomId(id);
setSelectedTrimId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
},
[pushState],
);
@@ -677,6 +711,7 @@ export default function VideoEditor() {
setSelectedZoomId(id);
setSelectedTrimId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
},
[pushState],
);
@@ -693,6 +728,7 @@ export default function VideoEditor() {
setSelectedTrimId(id);
setSelectedZoomId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
},
[pushState],
);
@@ -803,6 +839,7 @@ export default function VideoEditor() {
setSelectedZoomId(null);
setSelectedTrimId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
}
}, []);
@@ -822,6 +859,7 @@ export default function VideoEditor() {
setSelectedZoomId(null);
setSelectedTrimId(null);
setSelectedAnnotationId(null);
+ setSelectedBlurId(null);
},
[pushState],
);
@@ -888,6 +926,35 @@ export default function VideoEditor() {
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],
);
@@ -909,6 +976,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) => ({
@@ -917,8 +1011,11 @@ export default function VideoEditor() {
if (selectedAnnotationId === id) {
setSelectedAnnotationId(null);
}
+ if (selectedBlurId === id) {
+ setSelectedBlurId(null);
+ }
},
- [selectedAnnotationId, pushState],
+ [selectedAnnotationId, selectedBlurId, pushState],
);
const handleAnnotationContentChange = useCallback(
@@ -953,12 +1050,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(
@@ -983,6 +1094,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) => ({
@@ -1096,11 +1252,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)) {
@@ -1126,17 +1285,22 @@ export default function VideoEditor() {
const handleExportSaved = useCallback(
(formatLabel: "GIF" | "Video", filePath: string) => {
setExportedFilePath(filePath);
- toast.success(`${formatLabel} exported successfully`, {
- description: filePath,
- action: {
- label: "Show in Folder",
- onClick: () => {
- void handleShowExportedFile(filePath);
+ toast.success(
+ t("export.exportedSuccessfully", {
+ format: formatLabel,
+ }),
+ {
+ description: filePath,
+ action: {
+ label: rawT("common.actions.showInFolder"),
+ onClick: () => {
+ void handleShowExportedFile(filePath);
+ },
},
},
- });
+ );
},
- [handleShowExportedFile],
+ [handleShowExportedFile, t, rawT],
);
const handleSaveUnsavedExport = useCallback(async () => {
@@ -1240,6 +1404,12 @@ export default function VideoEditor() {
const timestamp = Date.now();
const fileName = `export-${timestamp}.gif`;
+ if (result.warnings) {
+ for (const warning of result.warnings) {
+ toast.warning(warning);
+ }
+ }
+
const saveResult = await window.electronAPI.saveExportedVideo(arrayBuffer, fileName);
if (saveResult.canceled) {
@@ -1374,6 +1544,12 @@ export default function VideoEditor() {
const timestamp = Date.now();
const fileName = `export-${timestamp}.mp4`;
+ if (result.warnings) {
+ for (const warning of result.warnings) {
+ toast.warning(warning);
+ }
+ }
+
const saveResult = await window.electronAPI.saveExportedVideo(arrayBuffer, fileName);
if (saveResult.canceled) {
@@ -1397,9 +1573,15 @@ export default function VideoEditor() {
}
} catch (error) {
console.error("Export error:", error);
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
- setExportError(errorMessage);
- toast.error(`Export failed: ${errorMessage}`);
+ if (error instanceof BackgroundLoadError) {
+ const message = t("errors.exportBackgroundLoadFailed", { url: error.displayUrl });
+ setExportError(message);
+ toast.error(message);
+ } else {
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
+ setExportError(errorMessage);
+ toast.error(t("errors.exportFailedWithError", { error: errorMessage }));
+ }
} finally {
setIsExporting(false);
exporterRef.current = null;
@@ -1432,6 +1614,7 @@ export default function VideoEditor() {
exportQuality,
handleExportSaved,
cursorTelemetry,
+ t,
],
);
@@ -1510,7 +1693,7 @@ export default function VideoEditor() {
if (loading) {
return (
-
Loading video...
+
{t("loadingVideo")}
);
}
@@ -1524,7 +1707,7 @@ export default function VideoEditor() {
onClick={handleLoadProject}
className="px-3 py-1.5 rounded-md bg-[#34B27B] text-white text-sm hover:bg-[#34B27B]/90"
>
- Load Project File
+ {ts("project.load")}
@@ -1579,7 +1762,7 @@ export default function VideoEditor() {
className="bg-transparent text-[11px] font-medium outline-none cursor-pointer appearance-none pr-1"
style={{ color: "inherit" }}
>
- {SUPPORTED_LOCALES.map((loc) => (
+ {availableLocales.map((loc) => (
@@ -1675,11 +1858,18 @@ export default function VideoEditor() {
cropRegion={cropRegion}
trimRegions={trimRegions}
speedRegions={speedRegions}
- annotationRegions={annotationRegions}
+ annotationRegions={annotationOnlyRegions}
selectedAnnotationId={selectedAnnotationId}
onSelectAnnotation={handleSelectAnnotation}
onAnnotationPositionChange={handleAnnotationPositionChange}
onAnnotationSizeChange={handleAnnotationSizeChange}
+ blurRegions={blurRegions}
+ selectedBlurId={selectedBlurId}
+ onSelectBlur={handleSelectBlur}
+ onBlurPositionChange={handleAnnotationPositionChange}
+ onBlurSizeChange={handleAnnotationSizeChange}
+ onBlurDataChange={handleBlurDataPreviewChange}
+ onBlurDataCommit={commitState}
cursorTelemetry={cursorTelemetry}
/>
@@ -1732,18 +1922,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,
})
@@ -1796,7 +1993,7 @@ export default function VideoEditor() {
onWebcamLayoutPresetChange={(preset) =>
pushState({
webcamLayoutPreset: preset,
- webcamPosition: preset === "vertical-stack" ? null : webcamPosition,
+ webcamPosition: preset === "picture-in-picture" ? webcamPosition : null,
})
}
webcamMaskShape={webcamMaskShape}
@@ -1830,12 +2027,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
diff --git a/src/components/video-editor/VideoPlayback.tsx b/src/components/video-editor/VideoPlayback.tsx
index 08c1c25..35e0077 100644
--- a/src/components/video-editor/VideoPlayback.tsx
+++ b/src/components/video-editor/VideoPlayback.tsx
@@ -18,7 +18,6 @@ import {
useRef,
useState,
} from "react";
-import { getAssetPath } from "@/lib/assetPath";
import {
getWebcamLayoutCssBoxShadow,
type Size,
@@ -26,6 +25,7 @@ import {
type WebcamLayoutPreset,
type WebcamSizePreset,
} from "@/lib/compositeLayout";
+import { classifyWallpaper, DEFAULT_WALLPAPER, resolveImageWallpaperUrl } from "@/lib/wallpaper";
import { getCssClipPath } from "@/lib/webcamMaskShapes";
import {
type AspectRatio,
@@ -35,6 +35,7 @@ import {
import { AnnotationOverlay } from "./AnnotationOverlay";
import {
type AnnotationRegion,
+ type BlurData,
type SpeedRegion,
type TrimRegion,
ZOOM_DEPTH_SCALES,
@@ -101,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[];
}
@@ -152,6 +160,13 @@ const VideoPlayback = forwardRef(
onSelectAnnotation,
onAnnotationPositionChange,
onAnnotationSizeChange,
+ blurRegions = [],
+ selectedBlurId,
+ onSelectBlur,
+ onBlurPositionChange,
+ onBlurSizeChange,
+ onBlurDataChange,
+ onBlurDataCommit,
cursorTelemetry = [],
},
ref,
@@ -166,7 +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 focusIndicatorRef = useRef(null);
const [webcamLayout, setWebcamLayout] = useState(null);
const [webcamDimensions, setWebcamDimensions] = useState(null);
@@ -330,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;
@@ -346,7 +369,10 @@ const VideoPlayback = forwardRef(
if (!vid) return;
try {
allowPlaybackRef.current = true;
- await vid.play();
+ await vid.play().catch((err) => {
+ console.log("PLAY ERROR:", err);
+ throw err;
+ });
} catch (error) {
allowPlaybackRef.current = false;
throw error;
@@ -519,84 +545,24 @@ const VideoPlayback = forwardRef(
useEffect(() => {
if (!pixiReady || !videoReady) return;
+ const el = overlayRef.current;
+ if (!el) return;
- const app = appRef.current;
- const cameraContainer = cameraContainerRef.current;
- const video = videoRef.current;
+ // Seed immediately so overlays never start at 800×600
+ setOverlaySize({ width: el.clientWidth, height: el.clientHeight });
- if (!app || !cameraContainer || !video) return;
-
- 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();
- }
+ const observer = new ResizeObserver((entries) => {
+ if (!entries[0]) return;
+ const { width, height } = entries[0].contentRect;
+ setOverlaySize((prev) => {
+ if (prev.width === width && prev.height === height) return prev;
+ return { width, height };
});
});
- }, [pixiReady, videoReady, layoutVideoContent]);
+
+ observer.observe(el);
+ return () => observer.disconnect();
+ }, [pixiReady, videoReady]);
useEffect(() => {
if (!pixiReady || !videoReady) return;
@@ -623,7 +589,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";
@@ -632,7 +599,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;
@@ -759,7 +753,6 @@ const VideoPlayback = forwardRef(
blurFilter.resolution = app.renderer.resolution;
blurFilter.blur = 0;
const motionBlurFilter = new MotionBlurFilter([0, 0], 5, 0);
- videoContainer.filters = [blurFilter, motionBlurFilter];
blurFilterRef.current = blurFilter;
motionBlurFilterRef.current = motionBlurFilter;
@@ -807,7 +800,7 @@ const VideoPlayback = forwardRef(
videoContainer.mask = null;
maskGraphicsRef.current = null;
if (blurFilterRef.current) {
- videoContainer.filters = [];
+ videoContainer.filters = null;
blurFilterRef.current.destroy();
blurFilterRef.current = null;
}
@@ -864,23 +857,14 @@ const VideoPlayback = forwardRef(
state.appliedScale = appliedTransform.scale;
};
+ let lastMotionBlurActive: boolean | null = null;
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,
},
);
@@ -1031,6 +1015,23 @@ const VideoPlayback = forwardRef(
motionIntensity,
motionVector,
);
+
+ const isMotionBlurActive = (motionBlurAmountRef.current || 0) > 0 && isPlayingRef.current;
+
+ if (isMotionBlurActive !== lastMotionBlurActive && videoContainerRef.current) {
+ if (isMotionBlurActive) {
+ if (blurFilterRef.current && motionBlurFilterRef.current) {
+ videoContainerRef.current.filters = [
+ blurFilterRef.current,
+ motionBlurFilterRef.current,
+ ];
+ lastMotionBlurActive = true;
+ }
+ } else {
+ videoContainerRef.current.filters = null;
+ lastMotionBlurActive = false;
+ }
+ }
};
app.ticker.add(ticker);
@@ -1068,7 +1069,17 @@ const VideoPlayback = forwardRef(
videoReadyRafRef.current = requestAnimationFrame(waitForRenderableFrame);
};
- const [resolvedWallpaper, setResolvedWallpaper] = useState(null);
+ const resolvedWallpaper = useMemo(() => {
+ const source = wallpaper || DEFAULT_WALLPAPER;
+ const classified = classifyWallpaper(source);
+ if (classified.kind !== "image") return classified.value;
+ try {
+ return resolveImageWallpaperUrl(classified.path);
+ } catch (err) {
+ console.warn("[VideoPlayback] wallpaper resolve failed:", err);
+ return null;
+ }
+ }, [wallpaper]);
const webcamCssBoxShadow = useMemo(
() => getWebcamLayoutCssBoxShadow(webcamLayoutPreset),
[webcamLayoutPreset],
@@ -1136,58 +1147,6 @@ const VideoPlayback = forwardRef(
webcamVideo.currentTime = 0;
}, [webcamVideoPath]);
- useEffect(() => {
- let mounted = true;
- (async () => {
- try {
- if (!wallpaper) {
- const def = await getAssetPath("wallpapers/wallpaper1.jpg");
- if (mounted) setResolvedWallpaper(def);
- return;
- }
-
- if (
- wallpaper.startsWith("#") ||
- wallpaper.startsWith("linear-gradient") ||
- wallpaper.startsWith("radial-gradient")
- ) {
- if (mounted) setResolvedWallpaper(wallpaper);
- return;
- }
-
- // If it's a data URL (custom uploaded image), use as-is
- if (wallpaper.startsWith("data:")) {
- if (mounted) setResolvedWallpaper(wallpaper);
- return;
- }
-
- // If it's an absolute web/http or file path, use as-is
- if (
- wallpaper.startsWith("http") ||
- wallpaper.startsWith("file://") ||
- wallpaper.startsWith("/")
- ) {
- // If it's an absolute server path (starts with '/'), resolve via getAssetPath as well
- if (wallpaper.startsWith("/")) {
- const rel = wallpaper.replace(/^\//, "");
- const p = await getAssetPath(rel);
- if (mounted) setResolvedWallpaper(p);
- return;
- }
- if (mounted) setResolvedWallpaper(wallpaper);
- return;
- }
- const p = await getAssetPath(wallpaper.replace(/^\//, ""));
- if (mounted) setResolvedWallpaper(p);
- } catch (_err) {
- if (mounted) setResolvedWallpaper(wallpaper || "/wallpapers/wallpaper1.jpg");
- }
- })();
- return () => {
- mounted = false;
- };
- }, [wallpaper]);
-
useEffect(() => {
return () => {
if (videoReadyRafRef.current) {
@@ -1287,9 +1246,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 =
+ filteredBlurRegions.length > 0
+ ? (() => {
+ const app = appRef.current;
+ if (!app?.renderer?.extract) return null;
+ try {
+ return app.renderer.extract.canvas(app.stage);
+ } catch {
+ return null;
+ }
+ })()
+ : 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 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) => (
onAnnotationPositionChange?.(id, position)}
- onSizeChange={(id, size) => onAnnotationSizeChange?.(id, size)}
- onClick={handleAnnotationClick}
- zIndex={annotation.zIndex}
- isSelectedBoost={annotation.id === selectedAnnotationId}
+ key={
+ item.kind === "blur"
+ ? `${item.region.id}-${overlaySize.width}-${overlaySize.height}-${item.region.blurData?.type ?? "blur"}-${item.region.blurData?.shape ?? "rectangle"}-${item.region.blurData?.color ?? "white"}-${Math.round(item.region.blurData?.blockSize ?? 0)}-${Math.round(item.region.blurData?.intensity ?? 0)}-${(item.region.blurData?.freehandPoints ?? []).map((p) => `${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..8a17b9e 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", () => {
@@ -103,3 +197,62 @@ it("detects unsaved changes from differing snapshots", () => {
expect(hasProjectUnsavedChanges("same", "same")).toBe(false);
expect(hasProjectUnsavedChanges("current", "baseline")).toBe(true);
});
+
+describe("wallpaper legacy normalization", () => {
+ it("rewrites pre-fix packaged paths (resources/assets/wallpapers/…)", () => {
+ const normalized = normalizeProjectEditor({
+ wallpaper: "file:///opt/Openscreen/resources/assets/wallpapers/wallpaper5.jpg",
+ });
+ expect(normalized.wallpaper).toBe("/wallpapers/wallpaper5.jpg");
+ });
+
+ it("rewrites new packaged layout (resources/wallpapers/…)", () => {
+ const normalized = normalizeProjectEditor({
+ wallpaper: "file:///opt/Openscreen/resources/wallpapers/wallpaper3.jpg",
+ });
+ expect(normalized.wallpaper).toBe("/wallpapers/wallpaper3.jpg");
+ });
+
+ it("rewrites unpackaged dev layout (public/wallpapers/…)", () => {
+ const normalized = normalizeProjectEditor({
+ wallpaper: "file:///home/user/project/public/wallpapers/wallpaper1.jpg",
+ });
+ expect(normalized.wallpaper).toBe("/wallpapers/wallpaper1.jpg");
+ });
+
+ it("rewrites Windows-style file URLs with drive letter", () => {
+ const normalized = normalizeProjectEditor({
+ wallpaper: "file:///C:/Users/me/openscreen/resources/wallpapers/wallpaper2.jpg",
+ });
+ expect(normalized.wallpaper).toBe("/wallpapers/wallpaper2.jpg");
+ });
+
+ it("leaves canonical relative paths untouched", () => {
+ const normalized = normalizeProjectEditor({ wallpaper: "/wallpapers/wallpaper2.jpg" });
+ expect(normalized.wallpaper).toBe("/wallpapers/wallpaper2.jpg");
+ });
+
+ it("leaves data URIs untouched", () => {
+ const dataUri = "data:image/png;base64,AAA";
+ expect(normalizeProjectEditor({ wallpaper: dataUri }).wallpaper).toBe(dataUri);
+ });
+
+ it("leaves colors and gradients untouched", () => {
+ expect(normalizeProjectEditor({ wallpaper: "#1a1a2e" }).wallpaper).toBe("#1a1a2e");
+ expect(
+ normalizeProjectEditor({ wallpaper: "linear-gradient(90deg, red, blue)" }).wallpaper,
+ ).toBe("linear-gradient(90deg, red, blue)");
+ });
+
+ it("does NOT rewrite user files outside the known install layout", () => {
+ const userPath = "file:///home/user/Pictures/wallpapers/wallpaper1.jpg";
+ expect(normalizeProjectEditor({ wallpaper: userPath }).wallpaper).toBe(userPath);
+ });
+
+ it("falls back to default for bundled paths outside WALLPAPER_PATHS", () => {
+ const normalized = normalizeProjectEditor({
+ wallpaper: "file:///opt/Openscreen/resources/wallpapers/wallpaper99.jpg",
+ });
+ expect(normalized.wallpaper).toBe("/wallpapers/wallpaper1.jpg");
+ });
+});
diff --git a/src/components/video-editor/projectPersistence.ts b/src/components/video-editor/projectPersistence.ts
index 45513d4..7259c1e 100644
--- a/src/components/video-editor/projectPersistence.ts
+++ b/src/components/video-editor/projectPersistence.ts
@@ -1,7 +1,9 @@
+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 { DEFAULT_WALLPAPER, WALLPAPER_PATHS } from "@/lib/wallpaper";
+import { ASPECT_RATIOS, type AspectRatio, isPortraitAspectRatio } from "@/utils/aspectRatioUtils";
import {
type AnnotationRegion,
type CropRegion,
@@ -9,6 +11,10 @@ import {
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,
@@ -17,7 +23,11 @@ import {
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,
@@ -28,12 +38,23 @@ import {
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 },
- (_, i) => `/wallpapers/wallpaper${i + 1}.jpg`,
-);
+// Pre-fix projects could persist resolved file:// URLs (machine-specific) for
+// bundled wallpapers. Rewrite only paths that match a known install layout
+// (resources/[assets/]wallpapers for packaged, public/wallpapers for dev) so
+// a legitimate user file that happens to live in a folder named "wallpapers"
+// elsewhere is never silently replaced.
+const LEGACY_FILE_WALLPAPER_RE =
+ /^file:\/\/.*?\/(?:resources\/(?:assets\/)?|public\/)wallpapers\/(wallpaper\d+\.jpg)$/i;
+const CANONICAL_WALLPAPERS = new Set(WALLPAPER_PATHS);
+
+function normalizeWallpaperValue(value: string): string {
+ const match = LEGACY_FILE_WALLPAPER_RE.exec(value);
+ if (!match) return value;
+ const canonical = `/wallpapers/${match[1]}`;
+ return CANONICAL_WALLPAPERS.has(canonical) ? canonical : DEFAULT_WALLPAPER;
+}
export const PROJECT_VERSION = 2;
@@ -72,6 +93,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));
}
@@ -179,6 +220,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
@@ -254,12 +315,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,
@@ -306,6 +377,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,
};
})
: [];
@@ -329,7 +436,10 @@ export function normalizeProjectEditor(editor: Partial): Pro
const cropHeight = clamp(rawCropHeight, 0.01, 1 - cropY);
return {
- wallpaper: typeof editor.wallpaper === "string" ? editor.wallpaper : WALLPAPER_PATHS[0],
+ wallpaper:
+ typeof editor.wallpaper === "string"
+ ? normalizeWallpaperValue(editor.wallpaper)
+ : DEFAULT_WALLPAPER,
shadowIntensity: typeof editor.shadowIntensity === "number" ? editor.shadowIntensity : 0,
showBlur: typeof editor.showBlur === "boolean" ? editor.showBlur : false,
motionBlurAmount: isFiniteNumber(editor.motionBlurAmount)
@@ -351,13 +461,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" ||
@@ -369,16 +474,7 @@ export function normalizeProjectEditor(editor: Partial): Pro
typeof editor.webcamSizePreset === "number" && isFiniteNumber(editor.webcamSizePreset)
? Math.max(10, Math.min(50, editor.webcamSizePreset))
: DEFAULT_WEBCAM_SIZE_PRESET,
- 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,
+ 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..d2b80c7 100644
--- a/src/components/video-editor/timeline/Item.tsx
+++ b/src/components/video-editor/timeline/Item.tsx
@@ -1,7 +1,8 @@
import type { Span } from "dnd-timeline";
import { useItem } from "dnd-timeline";
-import { Gauge, MessageSquare, Scissors, ZoomIn } from "lucide-react";
+import { Gauge, MessageSquare, MousePointer2, Scissors, ZoomIn } from "lucide-react";
import { useMemo } from "react";
+import { useScopedT } from "@/contexts/I18nContext";
import { cn } from "@/lib/utils";
import glassStyles from "./ItemGlass.module.css";
@@ -14,7 +15,8 @@ interface ItemProps {
onSelect?: () => void;
zoomDepth?: number;
speedValue?: number;
- variant?: "zoom" | "trim" | "annotation" | "speed";
+ isAutoFocus?: boolean;
+ variant?: "zoom" | "trim" | "annotation" | "speed" | "blur";
}
// Map zoom depth to multiplier labels
@@ -45,9 +47,11 @@ export default function Item({
onSelect,
zoomDepth = 1,
speedValue,
+ isAutoFocus = false,
variant = "zoom",
children,
}: ItemProps) {
+ const t = useScopedT("timeline");
const { setNodeRef, attributes, listeners, itemStyle, itemContentStyle } = useItem({
id,
span,
@@ -132,19 +136,25 @@ export default function Item({
{ZOOM_LABELS[zoomDepth] || `${zoomDepth}×`}
+ {isAutoFocus && (
+
+ )}
>
) : isTrim ? (
<>
- Trim
+ {t("labels.trim")}
>
) : isSpeed ? (
<>
- {speedValue !== undefined ? `${speedValue}×` : "Speed"}
+ {speedValue !== undefined ? `${speedValue}×` : t("labels.speed")}
>
) : (
diff --git a/src/components/video-editor/timeline/TimelineEditor.tsx b/src/components/video-editor/timeline/TimelineEditor.tsx
index b64aad0..6fe3474 100644
--- a/src/components/video-editor/timeline/TimelineEditor.tsx
+++ b/src/components/video-editor/timeline/TimelineEditor.tsx
@@ -44,6 +44,7 @@ import { detectZoomDwellCandidates, normalizeCursorTelemetry } from "./zoomSugge
const ZOOM_ROW_ID = "row-zoom";
const TRIM_ROW_ID = "row-trim";
const ANNOTATION_ROW_ID = "row-annotation";
+const BLUR_ROW_ID = "row-blur";
const SPEED_ROW_ID = "row-speed";
const FALLBACK_RANGE_MS = 1000;
const TARGET_MARKER_COUNT = 12;
@@ -73,6 +74,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 +103,8 @@ interface TimelineRenderItem {
label: string;
zoomDepth?: number;
speedValue?: number;
- variant: "zoom" | "trim" | "annotation" | "speed";
+ isAutoFocus?: boolean;
+ variant: "zoom" | "trim" | "annotation" | "speed" | "blur";
}
const SCALE_CANDIDATES = [
@@ -525,10 +533,12 @@ function Timeline({
onSelectZoom,
onSelectTrim,
onSelectAnnotation,
+ onSelectBlur,
onSelectSpeed,
selectedZoomId,
selectedTrimId,
selectedAnnotationId,
+ selectedBlurId,
selectedSpeedId,
keyframes = [],
}: {
@@ -540,10 +550,12 @@ 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;
keyframes?: { id: string; time: number }[];
}) {
@@ -568,6 +580,7 @@ function Timeline({
onSelectZoom?.(null);
onSelectTrim?.(null);
onSelectAnnotation?.(null);
+ onSelectBlur?.(null);
onSelectSpeed?.(null);
const rect = e.currentTarget.getBoundingClientRect();
@@ -586,6 +599,7 @@ function Timeline({
onSelectZoom,
onSelectTrim,
onSelectAnnotation,
+ onSelectBlur,
onSelectSpeed,
videoDurationMs,
sidebarWidth,
@@ -637,6 +651,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 +683,7 @@ function Timeline({
isSelected={item.id === selectedZoomId}
onSelect={() => onSelectZoom?.(item.id)}
zoomDepth={item.zoomDepth}
+ isAutoFocus={item.isAutoFocus}
variant="zoom"
>
{item.label}
@@ -711,6 +727,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 +952,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 +982,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 +1210,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 +1243,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 +1286,8 @@ export default function TimelineEditor({
deleteSelectedTrim();
} else if (selectedAnnotationId) {
deleteSelectedAnnotation();
+ } else if (selectedBlurId) {
+ deleteSelectedBlur();
} else if (selectedSpeedId) {
deleteSelectedSpeed();
}
@@ -1235,16 +1300,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 +1339,7 @@ export default function TimelineEditor({
span: { start: region.startMs, end: region.endMs },
label: t("labels.zoomItem", { index: String(index + 1) }),
zoomDepth: region.depth,
+ isAutoFocus: region.focusMode === "auto",
variant: "zoom",
}));
@@ -1304,6 +1373,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 +1390,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 +1412,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 +1421,12 @@ export default function TimelineEditor({
trimRegions,
speedRegions,
annotationRegions,
+ blurRegions,
onZoomSpanChange,
onTrimSpanChange,
onSpeedSpanChange,
onAnnotationSpanChange,
+ onBlurSpanChange,
],
);
@@ -1403,6 +1484,25 @@ export default function TimelineEditor({
>
+
diff --git a/src/components/video-editor/types.ts b/src/components/video-editor/types.ts
index e8e649f..046f428 100644
--- a/src/components/video-editor/types.ts
+++ b/src/components/video-editor/types.ts
@@ -47,7 +47,7 @@ export interface TrimRegion {
endMs: number;
}
-export type AnnotationType = "text" | "image" | "figure";
+export type AnnotationType = "text" | "image" | "figure" | "blur";
export type ArrowDirection =
| "up"
@@ -65,6 +65,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;
@@ -99,6 +120,7 @@ export interface AnnotationRegion {
style: AnnotationTextStyle;
zIndex: number;
figureData?: FigureData;
+ blurData?: BlurData;
}
export const DEFAULT_ANNOTATION_POSITION: AnnotationPosition = {
@@ -128,6 +150,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;
diff --git a/src/components/video-editor/videoPlayback/layoutUtils.ts b/src/components/video-editor/videoPlayback/layoutUtils.ts
index 5059ccb..4b713cf 100644
--- a/src/components/video-editor/videoPlayback/layoutUtils.ts
+++ b/src/components/video-editor/videoPlayback/layoutUtils.ts
@@ -140,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/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 cc19222..bd410da 100644
--- a/src/hooks/useEditorHistory.ts
+++ b/src/hooks/useEditorHistory.ts
@@ -17,6 +17,7 @@ import {
DEFAULT_WEBCAM_POSITION,
DEFAULT_WEBCAM_SIZE_PRESET,
} from "@/components/video-editor/types";
+import { DEFAULT_WALLPAPER } from "@/lib/wallpaper";
import type { AspectRatio } from "@/utils/aspectRatioUtils";
// Undoable state — selection IDs are intentionally excluded (undoing a
@@ -46,7 +47,7 @@ export const INITIAL_EDITOR_STATE: EditorState = {
speedRegions: [],
annotationRegions: [],
cropRegion: DEFAULT_CROP_REGION,
- wallpaper: "/wallpapers/wallpaper1.jpg",
+ wallpaper: DEFAULT_WALLPAPER,
shadowIntensity: 0,
showBlur: false,
motionBlurAmount: 0,
diff --git a/src/hooks/useScreenRecorder.ts b/src/hooks/useScreenRecorder.ts
index 2b07e24..dc9758f 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 =
@@ -118,11 +122,15 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
}, []);
const selectMimeType = () => {
+ // H.264 first: hardware-accelerated on all modern devices, gives sharp
+ // real-time output. AV1/VP9 are great for distribution but too
+ // CPU-intensive for live 60 fps capture — they produce blurry frames
+ // when the software encoder can't keep up.
const preferred = [
- "video/webm;codecs=av1",
"video/webm;codecs=h264",
- "video/webm;codecs=vp9",
"video/webm;codecs=vp8",
+ "video/webm;codecs=vp9",
+ "video/webm;codecs=av1",
"video/webm",
];
@@ -158,10 +166,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 +198,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,
@@ -225,6 +308,7 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
try {
const screenBlob = await activeScreenRecorder.recordedBlobPromise;
if (discardRecordingId.current === activeRecordingId) {
+ window.electronAPI?.discardCursorTelemetry(activeRecordingId);
return;
}
if (screenBlob.size === 0) {
@@ -334,7 +418,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 +452,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 +570,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
return;
}
+ if (!isCountdownRunActive(countdownRunToken)) {
+ teardownMedia();
+ return;
+ }
+
let screenMediaStream: MediaStream;
const videoConstraints = {
@@ -413,6 +615,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
}
screenStream.current = screenMediaStream;
+ if (!isCountdownRunActive(countdownRunToken)) {
+ teardownMedia();
+ return;
+ }
+
if (microphoneEnabled) {
try {
microphoneStream.current = await navigator.mediaDevices.getUserMedia({
@@ -437,32 +644,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 +715,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
);
}
+ if (!isCountdownRunActive(countdownRunToken)) {
+ teardownMedia();
+ return;
+ }
+
let {
width = DEFAULT_WIDTH,
height = DEFAULT_HEIGHT,
@@ -524,6 +739,11 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
);
const hasAudio = stream.current.getAudioTracks().length > 0;
+ if (!isCountdownRunActive(countdownRunToken)) {
+ teardownMedia();
+ return;
+ }
+
screenRecorder.current = createRecorderHandle(stream.current, {
mimeType,
videoBitsPerSecond,
@@ -553,7 +773,7 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
setRecording(true);
setPaused(false);
setElapsedSeconds(0);
- window.electronAPI?.setRecordingState(true);
+ window.electronAPI?.setRecordingState(true, recordingId.current);
const activeScreenRecorder = screenRecorder.current;
const activeWebcamRecorder = webcamRecorder.current;
@@ -635,7 +855,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 +879,6 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
restarting.current = true;
discardRecordingId.current = activeRecordingId;
- allowAutoFinalize.current = false;
const stopPromises = [
new Promise((resolve) => {
@@ -700,13 +929,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 636d727..788a315 100644
--- a/src/i18n/config.ts
+++ b/src/i18n/config.ts
@@ -1,5 +1,14 @@
export const DEFAULT_LOCALE = "en" as const;
-export const SUPPORTED_LOCALES = ["en", "zh-CN", "es", , "fr", "tr"] as const;
+export const SUPPORTED_LOCALES = [
+ "en",
+ "zh-CN",
+ "zh-TW",
+ "es",
+ "fr",
+ "tr",
+ "ko-KR",
+ "ja-JP",
+] as const;
export const I18N_NAMESPACES = [
"common",
"dialogs",
@@ -10,7 +19,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/common.json b/src/i18n/locales/en/common.json
index fdc65e6..cdefe84 100644
--- a/src/i18n/locales/en/common.json
+++ b/src/i18n/locales/en/common.json
@@ -9,6 +9,7 @@
"open": "Open",
"upload": "Upload",
"export": "Export",
+ "showInFolder": "Show in Folder",
"file": "File",
"edit": "Edit",
"view": "View",
diff --git a/src/i18n/locales/en/editor.json b/src/i18n/locales/en/editor.json
index ea2ceaa..13e2e13 100644
--- a/src/i18n/locales/en/editor.json
+++ b/src/i18n/locales/en/editor.json
@@ -5,6 +5,7 @@
"cancel": "Cancel",
"confirm": "Confirm"
},
+ "loadingVideo": "Loading video...",
"errors": {
"noVideoLoaded": "No video loaded",
"videoNotReady": "Video not ready",
@@ -14,6 +15,7 @@
"failedToSaveVideo": "Failed to save video",
"exportFailed": "Export failed",
"exportFailedWithError": "Export failed: {{error}}",
+ "exportBackgroundLoadFailed": "Export failed: could not load background image ({{url}})",
"failedToSaveExport": "Failed to save export",
"failedToSaveExportedVideo": "Failed to save exported video",
"failedToRevealInFolder": "Error revealing in folder: {{error}}"
@@ -36,6 +38,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 a64a332..9b85c2b 100644
--- a/src/i18n/locales/en/settings.json
+++ b/src/i18n/locales/en/settings.json
@@ -26,6 +26,7 @@
"selectPreset": "Select preset",
"pictureInPicture": "Picture in Picture",
"verticalStack": "Vertical Stack",
+ "dualFrame": "Dual Frame",
"webcamShape": "Camera Shape",
"webcamSize": "Webcam Size"
},
@@ -103,6 +104,7 @@
"typeText": "Text",
"typeImage": "Image",
"typeArrow": "Arrow",
+ "typeBlur": "Blur",
"textContent": "Text Content",
"textPlaceholder": "Enter your text...",
"fontStyle": "Font Style",
@@ -121,6 +123,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..389184b 100644
--- a/src/i18n/locales/en/timeline.json
+++ b/src/i18n/locales/en/timeline.json
@@ -4,21 +4,26 @@
"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": {
"pan": "Pan",
"zoom": "Zoom",
+ "trim": "Trim",
+ "speed": "Speed",
"zoomItem": "Zoom {{index}}",
"trimItem": "Trim {{index}}",
"speedItem": "Speed {{index}}",
"annotationItem": "Annotation",
+ "blurItem": "Blur {{index}}",
"imageItem": "Image",
"emptyText": "Empty text"
},
diff --git a/src/i18n/locales/es/common.json b/src/i18n/locales/es/common.json
index a6f61e7..13cccb0 100644
--- a/src/i18n/locales/es/common.json
+++ b/src/i18n/locales/es/common.json
@@ -9,6 +9,7 @@
"open": "Abrir",
"upload": "Subir",
"export": "Exportar",
+ "showInFolder": "Mostrar en carpeta",
"file": "Archivo",
"edit": "Editar",
"view": "Vista",
diff --git a/src/i18n/locales/es/editor.json b/src/i18n/locales/es/editor.json
index 99adc78..c71368a 100644
--- a/src/i18n/locales/es/editor.json
+++ b/src/i18n/locales/es/editor.json
@@ -8,6 +8,7 @@
"failedToSaveVideo": "Error al guardar el video",
"exportFailed": "La exportación falló",
"exportFailedWithError": "La exportación falló: {{error}}",
+ "exportBackgroundLoadFailed": "La exportación falló: no se pudo cargar la imagen de fondo ({{url}})",
"failedToSaveExport": "Error al guardar la exportación",
"failedToSaveExportedVideo": "Error al guardar el video exportado",
"failedToRevealInFolder": "Error al mostrar en la carpeta: {{error}}"
@@ -30,6 +31,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 aecdf69..423b158 100644
--- a/src/i18n/locales/es/settings.json
+++ b/src/i18n/locales/es/settings.json
@@ -26,6 +26,7 @@
"selectPreset": "Seleccionar predefinido",
"pictureInPicture": "Imagen en imagen",
"verticalStack": "Apilado vertical",
+ "dualFrame": "Marco dual",
"webcamShape": "Forma de cámara",
"webcamSize": "Tamaño de cámara"
},
@@ -103,6 +104,7 @@
"typeText": "Texto",
"typeImage": "Imagen",
"typeArrow": "Flecha",
+ "typeBlur": "Desenfoque",
"textContent": "Contenido de texto",
"textPlaceholder": "Escribe tu texto...",
"fontStyle": "Estilo de fuente",
@@ -121,6 +123,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..1a1abe1 100644
--- a/src/i18n/locales/es/timeline.json
+++ b/src/i18n/locales/es/timeline.json
@@ -4,23 +4,28 @@
"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",
"zoom": "Zoom",
+ "trim": "Recortar",
+ "speed": "Velocidad",
"zoomItem": "Zoom {{index}}",
"trimItem": "Recorte {{index}}",
"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
index 7eb7f83..5d09214 100644
--- a/src/i18n/locales/fr/common.json
+++ b/src/i18n/locales/fr/common.json
@@ -9,6 +9,7 @@
"open": "Ouvrir",
"upload": "Téléverser",
"export": "Exporter",
+ "showInFolder": "Afficher dans le dossier",
"file": "Fichier",
"edit": "Éditer",
"view": "Affichage",
diff --git a/src/i18n/locales/fr/dialogs.json b/src/i18n/locales/fr/dialogs.json
index b4056a5..dbaae38 100644
--- a/src/i18n/locales/fr/dialogs.json
+++ b/src/i18n/locales/fr/dialogs.json
@@ -27,10 +27,11 @@
"triggerLabel": "Comment fonctionne la coupe",
"title": "Comment fonctionne la coupe",
"description": "Comprendre comment supprimer les parties indésirables de votre vidéo.",
- "explanation": "L'outil Coupe fonctionne en définissant les segments que vous souhaitez",
- "explanationRemove": "supprimer",
- "explanationCovered": "couvert",
- "explanationEnd": "par un segment de coupe rouge sera coupé lors de l'export.",
+ "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é",
@@ -39,7 +40,8 @@
"part3": "Partie 3",
"finalVideo": "Vidéo finale",
"step1Title": "1. Ajouter une coupe",
- "step1Description": "Appuyez sur T ou cliquez sur l'icône ciseaux pour marquer une section à supprimer.",
+ "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."
},
diff --git a/src/i18n/locales/fr/editor.json b/src/i18n/locales/fr/editor.json
index 779bcd7..ae48fdf 100644
--- a/src/i18n/locales/fr/editor.json
+++ b/src/i18n/locales/fr/editor.json
@@ -1,4 +1,10 @@
{
+ "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",
@@ -8,6 +14,7 @@
"failedToSaveVideo": "Échec de l'enregistrement de la vidéo",
"exportFailed": "L'export a échoué",
"exportFailedWithError": "L'export a échoué : {{error}}",
+ "exportBackgroundLoadFailed": "L'export a échoué : impossible de charger l'image d'arrière-plan ({{url}})",
"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}}"
@@ -30,6 +37,8 @@
"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
index f4bfb27..55521cb 100644
--- a/src/i18n/locales/fr/launch.json
+++ b/src/i18n/locales/fr/launch.json
@@ -33,5 +33,11 @@
"recording": {
"selectSource": "Veuillez sélectionner une source à enregistrer"
},
- "language": "Langue"
+ "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
index 48ce7e3..66df1ba 100644
--- a/src/i18n/locales/fr/settings.json
+++ b/src/i18n/locales/fr/settings.json
@@ -8,12 +8,21 @@
"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"
+ "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"
@@ -24,7 +33,9 @@
"selectPreset": "Choisir un préréglage",
"pictureInPicture": "Incrustation d'image",
"verticalStack": "Empilement vertical",
- "webcamShape": "Forme de la caméra"
+ "dualFrame": "Double cadre",
+ "webcamShape": "Forme de la caméra",
+ "webcamSize": "Taille de la caméra"
},
"effects": {
"title": "Effets vidéo",
@@ -100,6 +111,7 @@
"typeText": "Texte",
"typeImage": "Image",
"typeArrow": "Flèche",
+ "typeBlur": "Flou",
"textContent": "Contenu du texte",
"textPlaceholder": "Saisissez votre texte...",
"fontStyle": "Style de police",
@@ -118,6 +130,18 @@
"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.",
diff --git a/src/i18n/locales/fr/shortcuts.json b/src/i18n/locales/fr/shortcuts.json
index 5c6e494..a0ce83f 100644
--- a/src/i18n/locales/fr/shortcuts.json
+++ b/src/i18n/locales/fr/shortcuts.json
@@ -18,6 +18,7 @@
"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"
diff --git a/src/i18n/locales/fr/timeline.json b/src/i18n/locales/fr/timeline.json
index abee16c..b4c6e16 100644
--- a/src/i18n/locales/fr/timeline.json
+++ b/src/i18n/locales/fr/timeline.json
@@ -4,23 +4,28 @@
"suggestZooms": "Suggérer des zooms depuis le curseur",
"addTrim": "Ajouter une coupe (T)",
"addAnnotation": "Ajouter une annotation (A)",
- "addSpeed": "Ajouter une vitesse (S)"
+ "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"
+ "pressSpeed": "Appuyez sur S pour ajouter une vitesse",
+ "pressBlur": "Appuyez sur B pour ajouter une zone de flou"
},
"labels": {
"pan": "Panoramique",
"zoom": "Zoom",
+ "trim": "Couper",
+ "speed": "Vitesse",
"zoomItem": "Zoom {{index}}",
"trimItem": "Coupe {{index}}",
"speedItem": "Vitesse {{index}}",
"annotationItem": "Annotation",
"imageItem": "Image",
- "emptyText": "Texte vide"
+ "emptyText": "Texte vide",
+ "blurItem": "Flou {{index}}"
},
"emptyState": {
"noVideo": "Aucune vidéo chargée",
@@ -28,20 +33,20 @@
},
"errors": {
"cannotPlaceZoom": "Impossible de placer le zoom ici",
- "zoomExistsAtLocation": "Un zoom existe déjà à cet emplacement ou l'espace disponible est insuffisant.",
+ "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'abord un screencast pour générer des suggestions basées sur le curseur.",
+ "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'enregistrement ne contient pas suffisamment de données de mouvement du curseur.",
+ "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'espace disponible est insuffisant.",
+ "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'espace disponible est insuffisant."
+ "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",
diff --git a/src/i18n/locales/ja-JP/common.json b/src/i18n/locales/ja-JP/common.json
new file mode 100644
index 0000000..ee2205a
--- /dev/null
+++ b/src/i18n/locales/ja-JP/common.json
@@ -0,0 +1,30 @@
+{
+ "actions": {
+ "cancel": "キャンセル",
+ "save": "保存",
+ "delete": "削除",
+ "close": "閉じる",
+ "share": "共有",
+ "done": "完了",
+ "open": "開く",
+ "upload": "アップロード",
+ "export": "エクスポート",
+ "showInFolder": "フォルダに表示",
+ "file": "ファイル",
+ "edit": "編集",
+ "view": "表示",
+ "window": "ウィンドウ",
+ "quit": "終了",
+ "stopRecording": "録画停止"
+ },
+ "playback": {
+ "play": "再生",
+ "pause": "一時停止",
+ "fullscreen": "全画面表示",
+ "exitFullscreen": "全画面表示を終了"
+ },
+ "locale": {
+ "name": "日本語",
+ "short": "JA"
+ }
+}
diff --git a/src/i18n/locales/ja-JP/dialogs.json b/src/i18n/locales/ja-JP/dialogs.json
new file mode 100644
index 0000000..3c3fce5
--- /dev/null
+++ b/src/i18n/locales/ja-JP/dialogs.json
@@ -0,0 +1,71 @@
+{
+ "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/ja-JP/editor.json b/src/i18n/locales/ja-JP/editor.json
new file mode 100644
index 0000000..1501230
--- /dev/null
+++ b/src/i18n/locales/ja-JP/editor.json
@@ -0,0 +1,42 @@
+{
+ "newRecording": {
+ "title": "レコーダーに戻る",
+ "description": "現在の作業内容が保存されました。",
+ "cancel": "キャンセル",
+ "confirm": "確認"
+ },
+ "loadingVideo": "ビデオを読み込み中...",
+ "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/ja-JP/launch.json b/src/i18n/locales/ja-JP/launch.json
new file mode 100644
index 0000000..4504b00
--- /dev/null
+++ b/src/i18n/locales/ja-JP/launch.json
@@ -0,0 +1,43 @@
+{
+ "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": "言語",
+ "systemLanguagePrompt": {
+ "title": "システム言語を使用しますか?",
+ "description": "システム言語として{{language}}が検出されました。OpenScreenを{{language}}に切り替えますか?",
+ "switch": "{{language}}に切り替え",
+ "keepDefault": "現在の言語を保持"
+ }
+}
diff --git a/src/i18n/locales/ja-JP/settings.json b/src/i18n/locales/ja-JP/settings.json
new file mode 100644
index 0000000..9cad3ef
--- /dev/null
+++ b/src/i18n/locales/ja-JP/settings.json
@@ -0,0 +1,183 @@
+{
+ "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": "矢印の色",
+ "blurType": "ぼかしの種類",
+ "blurTypeBlur": "ぼかし",
+ "blurTypeMosaic": "モザイクぼかし",
+ "blurColor": "ぼかしの色",
+ "blurColorWhite": "白",
+ "blurColorBlack": "黒",
+ "blurShape": "ぼかしの形状",
+ "blurIntensity": "ぼかしの強さ",
+ "mosaicBlockSize": "モザイクブロックのサイズ",
+ "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フォントのインポートURL",
+ "urlPlaceholder": "https://fonts.googleapis.com/css2?family=Roboto&display=swap",
+ "urlHelp": "Googleフォントから取得: フォントを選択 → 「フォントを取得」をクリック → @import URLをコピー",
+ "nameLabel": "表示名",
+ "namePlaceholder": "マイカスタムフォント",
+ "nameHelp": "フォントセレクターに表示される名前です",
+ "addButton": "フォントを追加",
+ "addingButton": "追加中...",
+ "errorEmptyUrl": "GoogleフォントのインポートURLを入力してください",
+ "errorInvalidUrl": "有効なGoogleフォントURLを入力してください",
+ "errorEmptyName": "フォント名を入力してください",
+ "errorExtractFailed": "URLからフォントファミリーを抽出できませんでした",
+ "successMessage": "フォント \"{{fontName}}\" が正常に追加されました",
+ "failedToAdd": "フォントの追加に失敗しました",
+ "errorTimeout": "フォントの読み込みに時間がかかりすぎました。URLを確認して再試行してください。",
+ "errorLoadFailed": "フォントを読み込めませんでした。GoogleフォントのURLが正しいことを確認してください。"
+ },
+ "language": {
+ "title": "言語"
+ }
+}
diff --git a/src/i18n/locales/ja-JP/shortcuts.json b/src/i18n/locales/ja-JP/shortcuts.json
new file mode 100644
index 0000000..d4b4a90
--- /dev/null
+++ b/src/i18n/locales/ja-JP/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": "選択を削除 (alt)",
+ "panTimeline": "タイムラインをパン",
+ "zoomTimeline": "タイムラインをズーム",
+ "frameBack": "フレームを戻す",
+ "frameForward": "フレームを進める"
+ }
+}
diff --git a/src/i18n/locales/ja-JP/timeline.json b/src/i18n/locales/ja-JP/timeline.json
new file mode 100644
index 0000000..e0507f6
--- /dev/null
+++ b/src/i18n/locales/ja-JP/timeline.json
@@ -0,0 +1,55 @@
+{
+ "buttons": {
+ "addZoom": "ズームを追加 (Z)",
+ "suggestZooms": "カーソル位置からズームを提案",
+ "addTrim": "トリムを追加 (T)",
+ "addAnnotation": "注釈を追加 (A)",
+ "addBlur": "ぼかしを追加 (B)",
+ "addSpeed": "速度を追加 (S)"
+ },
+ "hints": {
+ "pressZoom": "Zキーを押してズームを追加",
+ "pressTrim": "Tキーを押してトリムを追加",
+ "pressAnnotation": "Aキーを押して注釈を追加",
+ "pressBlur": "Bキーを押してぼかしを追加",
+ "pressSpeed": "Sキーを押して速度を追加"
+ },
+ "labels": {
+ "pan": "移動",
+ "zoom": "ズーム",
+ "trim": "トリム",
+ "speed": "速度",
+ "zoomItem": "ズーム {{index}}",
+ "trimItem": "トリム {{index}}",
+ "speedItem": "速度 {{index}}",
+ "annotationItem": "注釈",
+ "blurItem": "ぼかし {{index}}",
+ "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/ko-KR/common.json b/src/i18n/locales/ko-KR/common.json
new file mode 100644
index 0000000..5d90118
--- /dev/null
+++ b/src/i18n/locales/ko-KR/common.json
@@ -0,0 +1,30 @@
+{
+ "actions": {
+ "cancel": "취소",
+ "save": "저장",
+ "delete": "삭제",
+ "close": "닫기",
+ "share": "공유",
+ "done": "완료",
+ "open": "열기",
+ "upload": "업로드",
+ "export": "내보내기",
+ "showInFolder": "폴더에 표시",
+ "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..466e656
--- /dev/null
+++ b/src/i18n/locales/ko-KR/editor.json
@@ -0,0 +1,43 @@
+{
+ "newRecording": {
+ "title": "녹화로 돌아가기",
+ "description": "현재 세션이 저장되었습니다.",
+ "cancel": "취소",
+ "confirm": "확인"
+ },
+ "loadingVideo": "비디오 로드 중...",
+ "errors": {
+ "noVideoLoaded": "불러온 비디오가 없습니다",
+ "videoNotReady": "비디오가 준비되지 않았습니다",
+ "unableToDetermineSourcePath": "소스 비디오 경로를 확인할 수 없습니다",
+ "failedToSaveGif": "GIF 저장에 실패했습니다",
+ "gifExportFailed": "GIF 내보내기에 실패했습니다",
+ "failedToSaveVideo": "비디오 저장에 실패했습니다",
+ "exportFailed": "내보내기에 실패했습니다",
+ "exportFailedWithError": "내보내기 실패: {{error}}",
+ "exportBackgroundLoadFailed": "내보내기 실패: 배경 이미지를 불러올 수 없습니다 ({{url}})",
+ "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..7009e68
--- /dev/null
+++ b/src/i18n/locales/ko-KR/timeline.json
@@ -0,0 +1,52 @@
+{
+ "buttons": {
+ "addZoom": "줌 추가 (Z)",
+ "suggestZooms": "커서 기반 줌 제안",
+ "addTrim": "트림 추가 (T)",
+ "addAnnotation": "주석 추가 (A)",
+ "addSpeed": "속도 추가 (S)"
+ },
+ "hints": {
+ "pressZoom": "Z를 눌러 줌 추가",
+ "pressTrim": "T를 눌러 트림 추가",
+ "pressAnnotation": "A를 눌러 주석 추가",
+ "pressSpeed": "S를 눌러 속도 추가"
+ },
+ "labels": {
+ "pan": "이동",
+ "zoom": "줌",
+ "trim": "트림",
+ "speed": "속도",
+ "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
index 3ec132c..13d006b 100644
--- a/src/i18n/locales/tr/common.json
+++ b/src/i18n/locales/tr/common.json
@@ -9,6 +9,7 @@
"open": "Aç",
"upload": "Yükle",
"export": "Dışa Aktar",
+ "showInFolder": "Klasörde Göster",
"file": "Dosya",
"edit": "Düzenle",
"view": "Görünüm",
diff --git a/src/i18n/locales/tr/dialogs.json b/src/i18n/locales/tr/dialogs.json
index 5661e45..9fab50d 100644
--- a/src/i18n/locales/tr/dialogs.json
+++ b/src/i18n/locales/tr/dialogs.json
@@ -27,10 +27,11 @@
"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.",
- "explanation": "Kırpma aracı, kaldırmak istediğiniz bölümleri tanımlayarak çalışır.",
- "explanationRemove": "kaldırmak",
- "explanationCovered": "kaplanan",
- "explanationEnd": "kırmızı kırpma bölgesi ile işaretlenen kısımlar dışa aktarımda kesilecektir.",
+ "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",
@@ -39,7 +40,8 @@
"part3": "Bölüm 3",
"finalVideo": "Son Video",
"step1Title": "1. Kırpma Ekle",
- "step1Description": "Kaldırılacak bölümü işaretlemek için T tuşuna basın veya makas simgesine tıklayın.",
+ "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."
},
diff --git a/src/i18n/locales/tr/editor.json b/src/i18n/locales/tr/editor.json
index dfa4cb1..c34d64b 100644
--- a/src/i18n/locales/tr/editor.json
+++ b/src/i18n/locales/tr/editor.json
@@ -8,6 +8,7 @@
"failedToSaveVideo": "Video kaydedilemedi",
"exportFailed": "Dışa aktarım başarısız oldu",
"exportFailedWithError": "Dışa aktarım başarısız: {{error}}",
+ "exportBackgroundLoadFailed": "Dışa aktarım başarısız: arka plan görüntüsü yüklenemedi ({{url}})",
"failedToSaveExport": "Dışa aktarım kaydedilemedi",
"failedToSaveExportedVideo": "Dışa aktarılan video kaydedilemedi",
"failedToRevealInFolder": "Klasörde gösterme hatası: {{error}}"
diff --git a/src/i18n/locales/tr/settings.json b/src/i18n/locales/tr/settings.json
index 3cf33b1..7bc60e4 100644
--- a/src/i18n/locales/tr/settings.json
+++ b/src/i18n/locales/tr/settings.json
@@ -100,6 +100,7 @@
"typeText": "Metin",
"typeImage": "Görüntü",
"typeArrow": "Ok",
+ "typeBlur": "Bulanık",
"textContent": "Metin İçeriği",
"textPlaceholder": "Metninizi girin...",
"fontStyle": "Yazı Tipi Stili",
@@ -118,6 +119,11 @@
"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.",
diff --git a/src/i18n/locales/tr/shortcuts.json b/src/i18n/locales/tr/shortcuts.json
index 8eb7931..69b28ed 100644
--- a/src/i18n/locales/tr/shortcuts.json
+++ b/src/i18n/locales/tr/shortcuts.json
@@ -18,6 +18,7 @@
"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"
diff --git a/src/i18n/locales/tr/timeline.json b/src/i18n/locales/tr/timeline.json
index b39a5d1..2632f7a 100644
--- a/src/i18n/locales/tr/timeline.json
+++ b/src/i18n/locales/tr/timeline.json
@@ -4,23 +4,28 @@
"suggestZooms": "İmleçten Yakınlaştırma Öner",
"addTrim": "Kırpma Ekle (T)",
"addAnnotation": "Açıklama Ekle (A)",
- "addSpeed": "Hız Ekle (S)"
+ "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"
+ "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",
+ "trim": "Kırp",
+ "speed": "Hız",
"zoomItem": "Yakınlaştırma {{index}}",
"trimItem": "Kırpma {{index}}",
"speedItem": "Hız {{index}}",
"annotationItem": "Açıklama",
"imageItem": "Görüntü",
- "emptyText": "Boş metin"
+ "emptyText": "Boş metin",
+ "blurItem": "Bulanık {{index}}"
},
"emptyState": {
"noVideo": "Video Yüklenmedi",
diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json
index 9a3cc1c..2291fa6 100644
--- a/src/i18n/locales/zh-CN/common.json
+++ b/src/i18n/locales/zh-CN/common.json
@@ -9,6 +9,7 @@
"open": "打开",
"upload": "上传",
"export": "导出",
+ "showInFolder": "在文件夹中显示",
"file": "文件",
"edit": "编辑",
"view": "视图",
@@ -23,7 +24,7 @@
"exitFullscreen": "退出全屏"
},
"locale": {
- "name": "中文",
- "short": "中文"
+ "name": "简体中文",
+ "short": "简中"
}
}
diff --git a/src/i18n/locales/zh-CN/editor.json b/src/i18n/locales/zh-CN/editor.json
index 5d27bef..f6c02d4 100644
--- a/src/i18n/locales/zh-CN/editor.json
+++ b/src/i18n/locales/zh-CN/editor.json
@@ -1,4 +1,11 @@
{
+ "newRecording": {
+ "title": "返回录屏",
+ "description": "当前会话已保存。",
+ "cancel": "取消",
+ "confirm": "确认"
+ },
+ "loadingVideo": "正在加载视频...",
"errors": {
"noVideoLoaded": "未加载视频",
"videoNotReady": "视频未就绪",
@@ -8,6 +15,7 @@
"failedToSaveVideo": "保存视频失败",
"exportFailed": "导出失败",
"exportFailedWithError": "导出失败:{{error}}",
+ "exportBackgroundLoadFailed": "导出失败:无法加载背景图片({{url}})",
"failedToSaveExport": "保存导出文件失败",
"failedToSaveExportedVideo": "保存导出的视频失败",
"failedToRevealInFolder": "在文件夹中显示时出错:{{error}}"
@@ -30,6 +38,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 72b4b48..5cffcb5 100644
--- a/src/i18n/locales/zh-CN/settings.json
+++ b/src/i18n/locales/zh-CN/settings.json
@@ -26,6 +26,7 @@
"selectPreset": "选择预设",
"pictureInPicture": "画中画",
"verticalStack": "垂直堆叠",
+ "dualFrame": "双画框",
"webcamShape": "摄像头形状",
"webcamSize": "摄像头大小"
},
@@ -103,6 +104,7 @@
"typeText": "文本",
"typeImage": "图片",
"typeArrow": "箭头",
+ "typeBlur": "模糊",
"textContent": "文本内容",
"textPlaceholder": "输入您的文本...",
"fontStyle": "字体样式",
@@ -121,6 +123,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..783fef6 100644
--- a/src/i18n/locales/zh-CN/timeline.json
+++ b/src/i18n/locales/zh-CN/timeline.json
@@ -4,23 +4,28 @@
"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": "平移",
"zoom": "缩放",
+ "trim": "剪辑",
+ "speed": "速度",
"zoomItem": "缩放 {{index}}",
"trimItem": "剪辑 {{index}}",
"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..1ddab48
--- /dev/null
+++ b/src/i18n/locales/zh-TW/common.json
@@ -0,0 +1,30 @@
+{
+ "actions": {
+ "cancel": "取消",
+ "save": "儲存",
+ "delete": "刪除",
+ "close": "關閉",
+ "share": "分享",
+ "done": "完成",
+ "open": "開啟",
+ "upload": "上傳",
+ "export": "匯出",
+ "showInFolder": "在資料夾中顯示",
+ "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..5d948a3
--- /dev/null
+++ b/src/i18n/locales/zh-TW/editor.json
@@ -0,0 +1,43 @@
+{
+ "newRecording": {
+ "title": "返回錄影",
+ "description": "目前工作階段已儲存。",
+ "cancel": "取消",
+ "confirm": "確認"
+ },
+ "loadingVideo": "正在載入影片...",
+ "errors": {
+ "noVideoLoaded": "未載入影片",
+ "videoNotReady": "影片未就緒",
+ "unableToDetermineSourcePath": "無法確定來源影片路徑",
+ "failedToSaveGif": "儲存 GIF 失敗",
+ "gifExportFailed": "GIF 匯出失敗",
+ "failedToSaveVideo": "儲存影片失敗",
+ "exportFailed": "匯出失敗",
+ "exportFailedWithError": "匯出失敗:{{error}}",
+ "exportBackgroundLoadFailed": "匯出失敗:無法載入背景圖片({{url}})",
+ "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..cd47b74
--- /dev/null
+++ b/src/i18n/locales/zh-TW/timeline.json
@@ -0,0 +1,55 @@
+{
+ "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": "縮放",
+ "trim": "剪輯",
+ "speed": "速度",
+ "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/assetPath.ts b/src/lib/assetPath.ts
index 8188de5..edba758 100644
--- a/src/lib/assetPath.ts
+++ b/src/lib/assetPath.ts
@@ -1,9 +1,29 @@
+export class UnsafeAssetPathError extends Error {
+ constructor(segment: string) {
+ super(`Unsafe asset path segment: ${segment}`);
+ this.name = "UnsafeAssetPathError";
+ }
+}
+
+export class AssetBaseUnavailableError extends Error {
+ constructor() {
+ super("electronAPI.assetBaseUrl is not available; preload did not load correctly");
+ this.name = "AssetBaseUnavailableError";
+ }
+}
+
function encodeRelativeAssetPath(relativePath: string): string {
return relativePath
.replace(/^\/+/, "")
.split("/")
.filter(Boolean)
- .map((part) => encodeURIComponent(part))
+ .map((part) => {
+ const decoded = decodeURIComponent(part);
+ if (decoded === "." || decoded === "..") {
+ throw new UnsafeAssetPathError(decoded);
+ }
+ return encodeURIComponent(decoded);
+ })
.join("/");
}
@@ -11,33 +31,22 @@ function ensureTrailingSlash(value: string): string {
return value.endsWith("/") ? value : `${value}/`;
}
-export async function getAssetPath(relativePath: string): Promise {
- const encodedRelativePath = encodeRelativeAssetPath(relativePath);
+export function getAssetPath(relativePath: string): string {
+ const encoded = encodeRelativeAssetPath(relativePath);
- try {
- if (typeof window !== "undefined") {
- // If running in a dev server (http/https), prefer the web-served path
- if (
- window.location &&
- window.location.protocol &&
- window.location.protocol.startsWith("http")
- ) {
- return `/${encodedRelativePath}`;
- }
-
- if (window.electronAPI && typeof window.electronAPI.getAssetBasePath === "function") {
- const base = await window.electronAPI.getAssetBasePath();
- if (base) {
- return new URL(encodedRelativePath, ensureTrailingSlash(base)).toString();
- }
- }
- }
- } catch {
- // ignore and use fallback
+ if (typeof window === "undefined") {
+ return `/${encoded}`;
}
- // Fallback for web/dev server: public/wallpapers are served at '/wallpapers/...'
- return `/${encodedRelativePath}`;
+ if (window.location?.protocol?.startsWith("http")) {
+ return `/${encoded}`;
+ }
+
+ const base = window.electronAPI?.assetBaseUrl;
+ if (!base) {
+ throw new AssetBaseUnavailableError();
+ }
+ return new URL(encoded, ensureTrailingSlash(base)).toString();
}
export default getAssetPath;
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 90883b1..596eb75 100644
--- a/src/lib/compositeLayout.test.ts
+++ b/src/lib/compositeLayout.test.ts
@@ -169,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 48583a3..e6db733 100644
--- a/src/lib/compositeLayout.ts
+++ b/src/lib/compositeLayout.ts
@@ -15,7 +15,7 @@ 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;
@@ -44,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;
}
@@ -54,6 +62,7 @@ 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;
}
@@ -101,6 +110,22 @@ const WEBCAM_LAYOUT_PRESET_MAP: Record {
+ it("stores samples captured during an active session", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10 });
+ buf.startSession(1);
+ for (let i = 0; i < 3; i++) buf.push(sample(i));
+ buf.endSession();
+
+ const batch = buf.takeNextBatch();
+ expect(batch?.recordingId).toBe(1);
+ expect(batch?.samples).toHaveLength(3);
+ expect(batch?.samples[0]?.timeMs).toBe(0);
+ });
+
+ it("trims active samples past maxActiveSamples (ring behaviour)", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 2 });
+ buf.startSession(1);
+ buf.push(sample(1));
+ buf.push(sample(2));
+ buf.push(sample(3));
+ buf.endSession();
+
+ const batch = buf.takeNextBatch();
+ expect(batch?.samples).toEqual([sample(2), sample(3)]);
+ });
+
+ it("preserves earlier pending batches when a new session starts before store", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10 });
+
+ // Recording 1
+ buf.startSession(1);
+ buf.push(sample(101));
+ buf.push(sample(102));
+ buf.endSession();
+
+ // Recording 2 starts before recording 1's batch has been consumed
+ buf.startSession(2);
+ buf.push(sample(201));
+ buf.endSession();
+
+ const batch1 = buf.takeNextBatch();
+ const batch2 = buf.takeNextBatch();
+ expect(batch1?.recordingId).toBe(1);
+ expect(batch1?.samples.map((s) => s.timeMs)).toEqual([101, 102]);
+ expect(batch2?.recordingId).toBe(2);
+ expect(batch2?.samples.map((s) => s.timeMs)).toEqual([201]);
+ });
+
+ it("returns null when nothing is pending", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10 });
+ expect(buf.takeNextBatch()).toBeNull();
+ });
+
+ it("drops empty sessions instead of queuing empty batches", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10 });
+ buf.startSession(1);
+ buf.endSession();
+ expect(buf.pendingCount).toBe(0);
+ expect(buf.takeNextBatch()).toBeNull();
+ });
+
+ it("caps the pending queue at maxPendingBatches to bound memory", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10, maxPendingBatches: 3 });
+
+ for (let round = 1; round <= 5; round++) {
+ buf.startSession(round);
+ buf.push(sample(round));
+ buf.endSession();
+ }
+
+ expect(buf.pendingCount).toBe(3);
+ // Oldest two batches (rounds 1 and 2) should have been dropped
+ expect(buf.takeNextBatch()?.recordingId).toBe(3);
+ expect(buf.takeNextBatch()?.recordingId).toBe(4);
+ expect(buf.takeNextBatch()?.recordingId).toBe(5);
+ });
+
+ it("starting a new session clears in-progress samples but keeps pending batches", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10 });
+
+ buf.startSession(1);
+ buf.push(sample(1));
+ buf.endSession();
+
+ buf.startSession(2);
+ buf.push(sample(99));
+ // Simulate another startSession before endSession (e.g. rapid restart)
+ buf.startSession(3);
+ expect(buf.activeCount).toBe(0);
+ expect(buf.pendingCount).toBe(1);
+
+ const batch = buf.takeNextBatch();
+ expect(batch?.recordingId).toBe(1);
+ expect(batch?.samples.map((s) => s.timeMs)).toEqual([1]);
+ });
+
+ it("discardBatch(id) drops only the batch produced by that recording id", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10 });
+
+ buf.startSession(1);
+ buf.push(sample(1));
+ buf.endSession();
+
+ buf.startSession(2);
+ buf.push(sample(2));
+ buf.endSession();
+
+ expect(buf.pendingCount).toBe(2);
+ expect(buf.discardBatch(1)).toBe(true);
+ expect(buf.pendingCount).toBe(1);
+ expect(buf.takeNextBatch()?.recordingId).toBe(2);
+ });
+
+ it("discardBatch(id) targets the correct batch even when a later recording sits in front of it", () => {
+ // Regression test for the rapid Stop → Record → Discard sequence:
+ // recording A's finalize callback does async work (fixWebmDuration),
+ // recording B finishes in the meantime, then A's callback resolves
+ // with discard intent. The discard must drop A — not B, which
+ // happens to be the *latest* pending batch by the time discard runs.
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10 });
+
+ buf.startSession(1);
+ buf.push(sample(11));
+ buf.endSession();
+
+ buf.startSession(2);
+ buf.push(sample(22));
+ buf.endSession();
+
+ expect(buf.pendingCount).toBe(2);
+ expect(buf.discardBatch(1)).toBe(true);
+
+ const remaining = buf.takeNextBatch();
+ expect(remaining?.recordingId).toBe(2);
+ expect(remaining?.samples.map((s) => s.timeMs)).toEqual([22]);
+ expect(buf.takeNextBatch()).toBeNull();
+ });
+
+ it("discardBatch(id) is a no-op (returns false) when the id is unknown or already drained", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10 });
+ expect(buf.discardBatch(42)).toBe(false);
+
+ buf.startSession(1);
+ buf.push(sample(1));
+ buf.endSession();
+ buf.takeNextBatch();
+ expect(buf.discardBatch(1)).toBe(false);
+ expect(buf.pendingCount).toBe(0);
+ });
+
+ it("prependBatch() re-inserts a batch at the front of the queue", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10 });
+
+ buf.startSession(1);
+ buf.push(sample(1));
+ buf.endSession();
+
+ const batch = buf.takeNextBatch();
+ expect(batch).not.toBeNull();
+ expect(buf.pendingCount).toBe(0);
+
+ if (batch) buf.prependBatch(batch);
+ expect(buf.pendingCount).toBe(1);
+ const next = buf.takeNextBatch();
+ expect(next?.recordingId).toBe(1);
+ expect(next?.samples.map((s) => s.timeMs)).toEqual([1]);
+ });
+
+ it("prependBatch() ignores empty batches", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10 });
+ buf.prependBatch({ recordingId: 1, samples: [] });
+ expect(buf.pendingCount).toBe(0);
+ });
+
+ it("endSession() returns the number of dropped batches and warns when the cap is exceeded", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10, maxPendingBatches: 2 });
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => undefined);
+
+ for (let round = 1; round <= 2; round++) {
+ buf.startSession(round);
+ buf.push(sample(round));
+ expect(buf.endSession()).toBe(0);
+ }
+ expect(warn).not.toHaveBeenCalled();
+
+ buf.startSession(3);
+ buf.push(sample(3));
+ const dropped = buf.endSession();
+ expect(dropped).toBe(1);
+ expect(warn).toHaveBeenCalledTimes(1);
+ expect(warn.mock.calls[0]?.[0]).toMatch(/dropped 1 pending batch/);
+ expect(buf.pendingCount).toBe(2);
+
+ warn.mockRestore();
+ });
+
+ it("prependBatch() defensively trims and warns when it would exceed the cap", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10, maxPendingBatches: 2 });
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => undefined);
+
+ // Fill the queue to the cap without dropping anything.
+ for (let round = 1; round <= 2; round++) {
+ buf.startSession(round);
+ buf.push(sample(round));
+ buf.endSession();
+ }
+ expect(buf.pendingCount).toBe(2);
+ expect(warn).not.toHaveBeenCalled();
+
+ // Simulate a misuse where a retry prepends without first draining:
+ // queue would grow to 3, so the oldest-trailing entry must be evicted.
+ buf.prependBatch({ recordingId: 99, samples: [sample(99)] });
+ expect(buf.pendingCount).toBe(2);
+ expect(warn).toHaveBeenCalledTimes(1);
+ expect(warn.mock.calls[0]?.[0]).toMatch(/prependBatch trimmed 1 trailing batch/);
+
+ // Front is the prepended batch; the preserved trailing batch is round 1.
+ expect(buf.takeNextBatch()?.recordingId).toBe(99);
+ expect(buf.takeNextBatch()?.recordingId).toBe(1);
+ expect(buf.pendingCount).toBe(0);
+
+ warn.mockRestore();
+ });
+
+ it("sanitizes non-finite or non-positive option values to safe defaults", () => {
+ // Infinity / NaN / negative would otherwise turn the trim loops
+ // into infinite loops. The buffer must fall back to defaults.
+ const buf = createCursorTelemetryBuffer({
+ maxActiveSamples: Number.POSITIVE_INFINITY,
+ maxPendingBatches: Number.NaN,
+ });
+
+ buf.startSession(1);
+ buf.push(sample(1));
+ expect(() => buf.endSession()).not.toThrow();
+ expect(buf.pendingCount).toBe(1);
+
+ const buf2 = createCursorTelemetryBuffer({
+ maxActiveSamples: -5,
+ maxPendingBatches: 0,
+ });
+ buf2.startSession(2);
+ buf2.push(sample(2));
+ expect(() => buf2.endSession()).not.toThrow();
+ expect(buf2.pendingCount).toBe(1);
+ });
+
+ it("reset() clears both active and pending state", () => {
+ const buf = createCursorTelemetryBuffer({ maxActiveSamples: 10 });
+ buf.startSession(1);
+ buf.push(sample(1));
+ buf.endSession();
+ buf.startSession(2);
+ buf.push(sample(2));
+
+ buf.reset();
+
+ expect(buf.activeCount).toBe(0);
+ expect(buf.pendingCount).toBe(0);
+ expect(buf.takeNextBatch()).toBeNull();
+ });
+});
diff --git a/src/lib/cursorTelemetryBuffer.ts b/src/lib/cursorTelemetryBuffer.ts
new file mode 100644
index 0000000..0c7e0e1
--- /dev/null
+++ b/src/lib/cursorTelemetryBuffer.ts
@@ -0,0 +1,213 @@
+/**
+ * A single cursor telemetry sample captured during a recording session.
+ *
+ * Coordinates (`cx`, `cy`) are clamped ratios in the `[0, 1]` range,
+ * normalised against the captured surface's width and height by the
+ * main-process `sampleCursorPoint()` before being pushed. `timeMs` is the
+ * offset (in milliseconds) from the recording's start.
+ */
+export interface CursorTelemetryPoint {
+ timeMs: number;
+ cx: number;
+ cy: number;
+}
+
+/**
+ * A completed batch of cursor samples, tagged with the recording id that
+ * produced them. The id is supplied at `startSession()` time and travels
+ * with the batch through the pending queue, retries, and discards.
+ */
+export interface CursorTelemetryBatch {
+ recordingId: number;
+ samples: CursorTelemetryPoint[];
+}
+
+/**
+ * Per-session cursor telemetry buffer with bounded memory.
+ *
+ * Flow: `startSession(recordingId)` → `push(point)` N times → `endSession()`
+ * enqueues the collected samples as a completed batch tagged with that
+ * `recordingId`. The main process later drains batches in FIFO order via
+ * `takeNextBatch()` to persist them to disk, and can `prependBatch()` on
+ * write failure to retry without losing order. A discard request keys on
+ * the recording id so an asynchronous "discard recording A" decision that
+ * arrives after recording B has already enqueued its batch still drops
+ * the right one.
+ *
+ * Memory is bounded by `maxActiveSamples` (ring buffer on the in-progress
+ * batch) and `maxPendingBatches` (FIFO cap across completed batches).
+ */
+export interface CursorTelemetryBuffer {
+ /**
+ * Begin a new recording session under the given `recordingId`. Clears
+ * any in-progress active samples (without touching already-completed
+ * pending batches). Safe to call repeatedly — e.g. a rapid Stop →
+ * Record sequence — and the most recent id wins.
+ */
+ startSession(recordingId: number): void;
+
+ /**
+ * Append a telemetry sample to the current active session. When the
+ * active buffer exceeds `maxActiveSamples`, the oldest sample is
+ * dropped (ring behaviour).
+ */
+ push(point: CursorTelemetryPoint): void;
+
+ /**
+ * Finalize the active session, moving its samples into the pending
+ * queue as a single batch tagged with the current recording id. Empty
+ * sessions are dropped (no empty batch is enqueued).
+ *
+ * If the pending queue would exceed `maxPendingBatches`, the oldest
+ * batches are evicted to bound memory. A `console.warn` is emitted
+ * whenever at least one batch is dropped so that pathological rapid-
+ * restart scenarios are observable.
+ *
+ * @returns the number of pending batches dropped by this call (0 under
+ * normal operation).
+ */
+ endSession(): number;
+
+ /**
+ * Remove and return the oldest pending batch, or `null` if the queue
+ * is empty.
+ */
+ takeNextBatch(): CursorTelemetryBatch | null;
+
+ /**
+ * Re-insert a batch at the front of the queue, preserving FIFO order
+ * on retry paths (e.g. when persisting the batch failed and the
+ * caller wants the next `takeNextBatch()` to yield it again).
+ *
+ * Empty batches are ignored. The pending cap is enforced defensively
+ * — if prepending would push the queue past `maxPendingBatches`, the
+ * oldest entries are evicted and a `console.warn` is emitted. In
+ * normal retry usage this trim is a no-op because the caller has just
+ * removed the batch via `takeNextBatch()`.
+ */
+ prependBatch(batch: CursorTelemetryBatch): void;
+
+ /**
+ * Drop the pending batch produced by the given `recordingId`. Used
+ * when a recording is discarded after its `endSession()` has run but
+ * before it has been persisted. Returns `true` if a batch was
+ * removed, `false` otherwise (no matching id, or the batch was
+ * already drained).
+ *
+ * Keying on the recording id (rather than "the latest pending batch")
+ * avoids a real bug: when finalizing a recording does asynchronous
+ * work like `fixWebmDuration`, a quick Stop → Record → Discard
+ * sequence can interleave such that the latest pending batch belongs
+ * to a *later* recording than the one being discarded.
+ */
+ discardBatch(recordingId: number): boolean;
+
+ /**
+ * Clear both the active and pending state. Intended for tests and
+ * full teardown paths.
+ */
+ reset(): void;
+
+ readonly activeCount: number;
+ readonly pendingCount: number;
+}
+
+export interface CursorTelemetryBufferOptions {
+ maxActiveSamples: number;
+ maxPendingBatches?: number;
+}
+
+const DEFAULT_MAX_PENDING_BATCHES = 8;
+const DEFAULT_MAX_ACTIVE_SAMPLES = 10_000;
+
+/** Coerce a numeric option into a safe, finite, positive integer. */
+function sanitizeLimit(value: number | undefined, fallback: number): number {
+ if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
+ const floored = Math.floor(value);
+ return floored >= 1 ? floored : fallback;
+}
+
+/**
+ * Create a cursor telemetry buffer.
+ *
+ * Numeric options are sanitized: non-finite, negative, or zero values fall
+ * back to safe defaults so a bad caller cannot disable the memory bounds
+ * (which would turn the trim loops into infinite loops).
+ *
+ * @see CursorTelemetryBuffer for the full lifecycle contract.
+ */
+export function createCursorTelemetryBuffer(
+ options: CursorTelemetryBufferOptions,
+): CursorTelemetryBuffer {
+ const maxActive = sanitizeLimit(options.maxActiveSamples, DEFAULT_MAX_ACTIVE_SAMPLES);
+ const maxPending = sanitizeLimit(options.maxPendingBatches, DEFAULT_MAX_PENDING_BATCHES);
+
+ let active: CursorTelemetryPoint[] = [];
+ let activeRecordingId: number | null = null;
+ let pending: CursorTelemetryBatch[] = [];
+
+ return {
+ startSession(recordingId) {
+ active = [];
+ activeRecordingId = recordingId;
+ },
+ push(point) {
+ active.push(point);
+ if (active.length > maxActive) {
+ active.shift();
+ }
+ },
+ endSession() {
+ let dropped = 0;
+ if (active.length > 0 && activeRecordingId !== null) {
+ pending.push({ recordingId: activeRecordingId, samples: active });
+ while (pending.length > maxPending) {
+ pending.shift();
+ dropped++;
+ }
+ }
+ active = [];
+ activeRecordingId = null;
+ if (dropped > 0) {
+ console.warn(
+ `[cursorTelemetryBuffer] dropped ${dropped} pending batch(es) to stay within maxPendingBatches=${maxPending}`,
+ );
+ }
+ return dropped;
+ },
+ takeNextBatch() {
+ return pending.shift() ?? null;
+ },
+ prependBatch(batch) {
+ if (batch.samples.length === 0) return;
+ pending.unshift(batch);
+ let dropped = 0;
+ while (pending.length > maxPending) {
+ pending.pop();
+ dropped++;
+ }
+ if (dropped > 0) {
+ console.warn(
+ `[cursorTelemetryBuffer] prependBatch trimmed ${dropped} trailing batch(es) to stay within maxPendingBatches=${maxPending}`,
+ );
+ }
+ },
+ discardBatch(recordingId) {
+ const idx = pending.findIndex((b) => b.recordingId === recordingId);
+ if (idx === -1) return false;
+ pending.splice(idx, 1);
+ return true;
+ },
+ reset() {
+ active = [];
+ activeRecordingId = null;
+ pending = [];
+ },
+ get activeCount() {
+ return active.length;
+ },
+ get pendingCount() {
+ return pending.length;
+ },
+ };
+}
diff --git a/src/lib/exporter/annotationRenderer.ts b/src/lib/exporter/annotationRenderer.ts
index 7420c80..c0d5657 100644
--- a/src/lib/exporter/annotationRenderer.ts
+++ b/src/lib/exporter/annotationRenderer.ts
@@ -1,4 +1,47 @@
-import type { AnnotationRegion, ArrowDirection } from "@/components/video-editor/types";
+import { type AnnotationRegion, type ArrowDirection } from "@/components/video-editor/types";
+import {
+ applyMosaicToImageData,
+ getBlurOverlayColor,
+ getNormalizedBlurIntensity,
+ getNormalizedMosaicBlockSize,
+ normalizeBlurType,
+} from "@/lib/blurEffects";
+
+let blurScratchCanvas: HTMLCanvasElement | null = null;
+let blurScratchCtx: CanvasRenderingContext2D | null = null;
+
+// Matches a single code point whose script is Han (including non-BMP
+// Extension A-F), Hiragana, Katakana (including halfwidth forms), or
+// Hangul. Used to split CJK text at character boundaries during wrap,
+// since CJK scripts have no word-separating whitespace. Unicode script
+// property escapes require ES2018+; tsconfig target is ES2020.
+const CJK_CHAR = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
+
+function tokenizeForWrap(line: string): string[] {
+ // Split Latin text on whitespace (preserving the whitespace as its own token,
+ // matching the original behavior), and split CJK runs into individual
+ // characters so each one becomes a breakable unit. This mirrors the editor's
+ // CSS `word-break: break-word` handling for CJK content.
+ const tokens: string[] = [];
+ let buffer = "";
+ const chars = Array.from(line);
+ const flushBuffer = () => {
+ 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 44102b4..ad65a08 100644
--- a/src/lib/exporter/frameRenderer.ts
+++ b/src/lib/exporter/frameRenderer.ts
@@ -45,6 +45,7 @@ import {
type Size,
type StyledRenderRect,
} from "@/lib/compositeLayout";
+import { BackgroundLoadError, classifyWallpaper, resolveImageWallpaperUrl } from "@/lib/wallpaper";
import { drawCanvasClipPath } from "@/lib/webcamMaskShapes";
import { renderAnnotations } from "./annotationRenderer";
import {
@@ -78,6 +79,7 @@ interface FrameRenderConfig {
previewWidth?: number;
previewHeight?: number;
cursorTelemetry?: import("@/components/video-editor/types").CursorTelemetryPoint[];
+ platform: string;
}
interface AnimationState {
@@ -124,9 +126,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,
@@ -187,8 +191,10 @@ 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) {
@@ -226,123 +232,93 @@ export class FrameRenderer {
private async setupBackground(): Promise {
const wallpaper = this.config.wallpaper;
- // Create background canvas for separate rendering (not affected by zoom)
const bgCanvas = document.createElement("canvas");
bgCanvas.width = this.config.width;
bgCanvas.height = this.config.height;
const bgCtx = bgCanvas.getContext("2d")!;
- try {
- // Render background based on type
- if (
- wallpaper.startsWith("file://") ||
- wallpaper.startsWith("data:") ||
- wallpaper.startsWith("/") ||
- wallpaper.startsWith("http")
- ) {
- // Image background
- const img = new Image();
- // Don't set crossOrigin for same-origin images to avoid CORS taint
- // Only set it for cross-origin URLs
- let imageUrl: string;
- if (wallpaper.startsWith("http")) {
- imageUrl = wallpaper;
- if (!imageUrl.startsWith(window.location.origin)) {
- img.crossOrigin = "anonymous";
- }
- } else if (wallpaper.startsWith("file://") || wallpaper.startsWith("data:")) {
- imageUrl = wallpaper;
- } else {
- imageUrl = window.location.origin + wallpaper;
- }
+ const classified = classifyWallpaper(wallpaper);
+ if (classified.kind === "color") {
+ bgCtx.fillStyle = classified.value;
+ bgCtx.fillRect(0, 0, this.config.width, this.config.height);
+ } else if (classified.kind === "gradient") {
+ const parsedGradient = parseCssGradient(classified.value);
+ if (!parsedGradient) {
+ throw new BackgroundLoadError(classified.value);
+ }
+ const gradient =
+ parsedGradient.type === "linear"
+ ? (() => {
+ const points = getLinearGradientPoints(
+ resolveLinearGradientAngle(parsedGradient.descriptor),
+ this.config.width,
+ this.config.height,
+ );
+ return bgCtx.createLinearGradient(points.x0, points.y0, points.x1, points.y1);
+ })()
+ : (() => {
+ const shape = getRadialGradientShape(
+ parsedGradient.descriptor,
+ this.config.width,
+ this.config.height,
+ );
+ return bgCtx.createRadialGradient(
+ shape.cx,
+ shape.cy,
+ 0,
+ shape.cx,
+ shape.cy,
+ shape.radius,
+ );
+ })();
+
+ parsedGradient.stops.forEach((stop) => {
+ gradient.addColorStop(stop.offset, stop.color);
+ });
+
+ bgCtx.fillStyle = gradient;
+ bgCtx.fillRect(0, 0, this.config.width, this.config.height);
+ } else {
+ const imageUrl = resolveImageWallpaperUrl(classified.path);
+ const img = new Image();
+ if (imageUrl.startsWith("http") && !imageUrl.startsWith(window.location.origin)) {
+ img.crossOrigin = "anonymous";
+ }
+
+ try {
await new Promise((resolve, reject) => {
img.onload = () => resolve();
- img.onerror = (err) => {
- console.error("[FrameRenderer] Failed to load background image:", imageUrl, err);
- reject(new Error(`Failed to load background image: ${imageUrl}`));
- };
+ img.onerror = (err) => reject(err);
img.src = imageUrl;
});
-
- // Draw the image using cover and center positioning
- const imgAspect = img.width / img.height;
- const canvasAspect = this.config.width / this.config.height;
-
- let drawWidth, drawHeight, drawX, drawY;
-
- if (imgAspect > canvasAspect) {
- drawHeight = this.config.height;
- drawWidth = drawHeight * imgAspect;
- drawX = (this.config.width - drawWidth) / 2;
- drawY = 0;
- } else {
- drawWidth = this.config.width;
- drawHeight = drawWidth / imgAspect;
- drawX = 0;
- drawY = (this.config.height - drawHeight) / 2;
- }
-
- bgCtx.drawImage(img, drawX, drawY, drawWidth, drawHeight);
- } else if (wallpaper.startsWith("#")) {
- bgCtx.fillStyle = wallpaper;
- bgCtx.fillRect(0, 0, this.config.width, this.config.height);
- } else if (
- wallpaper.startsWith("linear-gradient") ||
- wallpaper.startsWith("radial-gradient")
- ) {
- const parsedGradient = parseCssGradient(wallpaper);
- if (parsedGradient) {
- const gradient =
- parsedGradient.type === "linear"
- ? (() => {
- const points = getLinearGradientPoints(
- resolveLinearGradientAngle(parsedGradient.descriptor),
- this.config.width,
- this.config.height,
- );
-
- return bgCtx.createLinearGradient(points.x0, points.y0, points.x1, points.y1);
- })()
- : (() => {
- const shape = getRadialGradientShape(
- parsedGradient.descriptor,
- this.config.width,
- this.config.height,
- );
-
- return bgCtx.createRadialGradient(
- shape.cx,
- shape.cy,
- 0,
- shape.cx,
- shape.cy,
- shape.radius,
- );
- })();
-
- parsedGradient.stops.forEach((stop) => {
- gradient.addColorStop(stop.offset, stop.color);
- });
-
- bgCtx.fillStyle = gradient;
- bgCtx.fillRect(0, 0, this.config.width, this.config.height);
- } else {
- console.warn("[FrameRenderer] Could not parse gradient, using black fallback");
- bgCtx.fillStyle = "#000000";
- bgCtx.fillRect(0, 0, this.config.width, this.config.height);
- }
- } else {
- bgCtx.fillStyle = wallpaper;
- bgCtx.fillRect(0, 0, this.config.width, this.config.height);
+ } catch (err) {
+ throw new BackgroundLoadError(imageUrl, err);
}
- } catch (error) {
- console.error("[FrameRenderer] Error setting up background, using fallback:", error);
- bgCtx.fillStyle = "#000000";
- bgCtx.fillRect(0, 0, this.config.width, this.config.height);
+
+ const imgAspect = img.width / img.height;
+ const canvasAspect = this.config.width / this.config.height;
+
+ let drawWidth: number;
+ let drawHeight: number;
+ let drawX: number;
+ let drawY: number;
+
+ if (imgAspect > canvasAspect) {
+ drawHeight = this.config.height;
+ drawWidth = drawHeight * imgAspect;
+ drawX = (this.config.width - drawWidth) / 2;
+ drawY = 0;
+ } else {
+ drawWidth = this.config.width;
+ drawHeight = drawWidth / imgAspect;
+ drawX = 0;
+ drawY = (this.config.height - drawHeight) / 2;
+ }
+
+ bgCtx.drawImage(img, drawX, drawY, drawWidth, drawHeight);
}
- // Store the background canvas for compositing
this.backgroundSprite = bgCanvas;
}
@@ -418,8 +394,8 @@ export class FrameRenderer {
this.compositeCtx
) {
// Calculate scale factor based on export vs preview dimensions
- const previewWidth = this.config.previewWidth || 1920;
- const previewHeight = this.config.previewHeight || 1080;
+ const previewWidth = this.config.previewWidth ?? this.config.width;
+ const previewHeight = this.config.previewHeight ?? this.config.height;
const scaleX = this.config.width / previewWidth;
const scaleY = this.config.height / previewHeight;
const scaleFactor = (scaleX + scaleY) / 2;
@@ -504,10 +480,15 @@ export class FrameRenderer {
this.videoContainer.y = screenRect.y;
// scale border radius by export/preview canvas ratio
- const previewWidth = this.config.previewWidth || 1920;
- const previewHeight = this.config.previewHeight || 1080;
+ 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);
@@ -535,16 +516,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;
@@ -727,7 +702,10 @@ export class FrameRenderer {
private compositeWithShadows(webcamFrame?: VideoFrame | null): void {
if (!this.compositeCanvas || !this.compositeCtx || !this.app) return;
- const videoCanvas = this.readbackVideoCanvas();
+ 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;
@@ -784,6 +762,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,
@@ -805,6 +799,10 @@ export class FrameRenderer {
ctx.clip();
ctx.drawImage(
webcamFrame as unknown as CanvasImageSource,
+ sourceCropX,
+ sourceCropY,
+ sourceCropWidth,
+ sourceCropHeight,
webcamRect.x,
webcamRect.y,
webcamRect.width,
diff --git a/src/lib/exporter/gifExporter.browser.test.ts b/src/lib/exporter/gifExporter.browser.test.ts
index db9b144..1d96076 100644
--- a/src/lib/exporter/gifExporter.browser.test.ts
+++ b/src/lib/exporter/gifExporter.browser.test.ts
@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import sampleVideoUrl from "../../../tests/fixtures/sample.webm?url";
+import { BackgroundLoadError } from "../wallpaper";
import { GifExporter } from "./gifExporter";
import type { ExportProgress } from "./types";
@@ -40,4 +41,48 @@ describe("GifExporter (real browser)", () => {
expect(finalizing.length).toBeGreaterThan(0);
expect(finalizing.at(-1)!.percentage).toBe(100);
});
+
+ it("exports successfully with an image wallpaper (served by Vite dev server)", async () => {
+ const exporter = new GifExporter({
+ videoUrl: sampleVideoUrl,
+ width: 320,
+ height: 180,
+ frameRate: 15,
+ loop: true,
+ sizePreset: "medium",
+ wallpaper: "/wallpapers/wallpaper1.jpg",
+ zoomRegions: [],
+ showShadow: false,
+ shadowIntensity: 0,
+ showBlur: false,
+ cropRegion: { x: 0, y: 0, width: 1, height: 1 },
+ });
+
+ const result = await exporter.export();
+ expect(result.success, result.error).toBe(true);
+ expect(result.blob!.size).toBeGreaterThan(1024);
+ });
+
+ it("throws BackgroundLoadError when wallpaper fails to load (no silent black fallback)", async () => {
+ const exporter = new GifExporter({
+ videoUrl: sampleVideoUrl,
+ width: 320,
+ height: 180,
+ frameRate: 15,
+ loop: true,
+ sizePreset: "medium",
+ wallpaper: "/wallpapers/does-not-exist.jpg",
+ zoomRegions: [],
+ showShadow: false,
+ shadowIntensity: 0,
+ showBlur: false,
+ cropRegion: { x: 0, y: 0, width: 1, height: 1 },
+ });
+
+ const rejection = exporter.export();
+ await expect(rejection).rejects.toBeInstanceOf(BackgroundLoadError);
+ await expect(rejection).rejects.toMatchObject({
+ url: expect.stringContaining("does-not-exist"),
+ });
+ });
});
diff --git a/src/lib/exporter/gifExporter.ts b/src/lib/exporter/gifExporter.ts
index 747e34e..f41b58d 100644
--- a/src/lib/exporter/gifExporter.ts
+++ b/src/lib/exporter/gifExporter.ts
@@ -8,6 +8,8 @@ import type {
WebcamSizePreset,
ZoomRegion,
} from "@/components/video-editor/types";
+import { BackgroundLoadError } from "@/lib/wallpaper";
+import { getPlatform } from "@/utils/platformUtils";
import { AsyncVideoFrameQueue } from "./asyncVideoFrameQueue";
import { FrameRenderer } from "./frameRenderer";
import { StreamingVideoDecoder } from "./streamingDecoder";
@@ -115,7 +117,13 @@ export class GifExporter {
async export(): Promise {
let webcamFrameQueue: AsyncVideoFrameQueue | null = null;
+
+ const warnings: string[] = [];
+ const onWarning = (message: string) => warnings.push(message);
+
try {
+ const platform = await getPlatform();
+
this.cleanup();
this.cancelled = false;
@@ -153,6 +161,7 @@ export class GifExporter {
previewWidth: this.config.previewWidth,
previewHeight: this.config.previewHeight,
cursorTelemetry: this.config.cursorTelemetry,
+ platform,
});
await this.renderer.initialize();
@@ -174,11 +183,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);
@@ -214,6 +223,7 @@ export class GifExporter {
}
queue.enqueue(webcamFrame);
},
+ onWarning,
)
.catch((error) => {
webcamDecodeError = error instanceof Error ? error : new Error(String(error));
@@ -273,6 +283,7 @@ export class GifExporter {
webcamFrame?.close();
}
},
+ onWarning,
);
if (this.cancelled) {
@@ -319,8 +330,11 @@ export class GifExporter {
this.gif!.render();
});
- return { success: true, blob };
+ return { success: true, blob, warnings: warnings.length > 0 ? warnings : undefined };
} catch (error) {
+ if (error instanceof BackgroundLoadError) {
+ throw error;
+ }
console.error("GIF Export error:", error);
return {
success: false,
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 ee67576..e8093df 100644
--- a/src/lib/exporter/streamingDecoder.ts
+++ b/src/lib/exporter/streamingDecoder.ts
@@ -2,7 +2,7 @@ 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
@@ -68,8 +68,39 @@ type EarlyDecodeEndCheck = {
};
const EARLY_DECODE_END_THRESHOLD_SEC = 1;
-const METADATA_TAIL_TOLERANCE_SEC = 1.5;
+const METADATA_TAIL_TOLERANCE_SEC = 2;
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,
@@ -98,11 +129,8 @@ export function shouldFailDecodeEndedEarly({
const decodedNearStreamEnd =
Math.abs(lastDecodedFrameSec - streamDurationSec) <= STREAM_DURATION_MATCH_TOLERANCE_SEC;
- if (
- decodedNearStreamEnd &&
- metadataTailSec > 0 &&
- metadataTailSec <= METADATA_TAIL_TOLERANCE_SEC
- ) {
+ const maxTailSec = Math.max(METADATA_TAIL_TOLERANCE_SEC, requiredEndSec * 0.01);
+ if (decodedNearStreamEnd && metadataTailSec > 0 && metadataTailSec <= maxTailSec) {
return false;
}
@@ -201,10 +229,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
@@ -217,12 +278,21 @@ 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,
speedRegions: SpeedRegion[] | undefined,
onFrame: OnFrameCallback,
+ onWarning?: (message: string) => void,
): Promise {
if (!this.demuxer || !this.metadata) {
throw new Error("Must call loadMetadata() before decodeAll()");
@@ -230,26 +300,50 @@ export class StreamingVideoDecoder {
const decoderConfig = await this.demuxer.getDecoderConfig("video");
- // 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").
+ console.log("[StreamingVideoDecoder] decoderConfig.codec:", decoderConfig.codec);
+ console.log("[StreamingVideoDecoder] decoderConfig.description:", decoderConfig.description);
+
+ // web-demuxer may return bare four-character code strings ("av01", "vp08",
+ // "vp09", "avc1") that WebCodecs rejects. Normalize them to the short or
+ // full parametrized forms that VideoDecoder accepts.
if (/^av01$/i.test(decoderConfig.codec)) {
decoderConfig.codec = buildAV1CodecString(
decoderConfig.description as BufferSource | undefined,
);
}
+ if (/^vp08$/i.test(decoderConfig.codec)) {
+ decoderConfig.codec = "vp8";
+ }
+ if (/^vp09$/i.test(decoderConfig.codec)) {
+ decoderConfig.codec = "vp9";
+ }
+
+ if (/^avc1$/i.test(decoderConfig.codec)) {
+ decoderConfig.codec = "avc1.640033";
+ }
+ if (/^h264$/i.test(decoderConfig.codec)) {
+ decoderConfig.codec = "avc1.640033";
+ }
+
const codec = decoderConfig.codec.toLowerCase();
- const shouldPreferSoftwareDecode = codec.includes("av01") || codec.includes("av1");
+ const shouldPreferSoftwareDecode =
+ codec.includes("av01") ||
+ codec.includes("av1") ||
+ codec.includes("vp09") ||
+ codec.includes("vp9");
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[] = [];
@@ -268,6 +362,10 @@ export class StreamingVideoDecoder {
}
},
error: (e: DOMException) => {
+ console.warn(
+ `[StreamingVideoDecoder] decoder error for codec "${decoderConfig.codec}":`,
+ e.message,
+ );
decodeError = new Error(`VideoDecoder error: ${e.message}`);
if (frameResolve) {
const resolve = frameResolve;
@@ -284,13 +382,28 @@ export class StreamingVideoDecoder {
: decoderConfig;
try {
+ const support = await VideoDecoder.isConfigSupported(preferredDecoderConfig);
+ console.log(
+ `[StreamingVideoDecoder] isConfigSupported for "${preferredDecoderConfig.codec}":`,
+ support.supported,
+ );
+ if (!support.supported) {
+ throw new Error(`Unsupported codec: ${preferredDecoderConfig.codec}`);
+ }
this.decoder.configure(preferredDecoderConfig);
} catch (error) {
- if (!shouldPreferSoftwareDecode) {
+ if (shouldPreferSoftwareDecode) {
+ this.decoder.configure(decoderConfig);
+ } else if (/^avc1/i.test(codec)) {
+ const fallback = { ...decoderConfig, codec: "avc1.640033" };
+ console.warn(
+ `[StreamingVideoDecoder] codec "${codec}" unsupported, ` +
+ `falling back to "${fallback.codec}"`,
+ );
+ this.decoder.configure(fallback);
+ } else {
throw error;
}
- // Fall back to default decoder config if software preference isn't supported.
- this.decoder.configure(decoderConfig);
}
const getNextFrame = (): Promise => {
@@ -304,7 +417,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
@@ -360,7 +473,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);
@@ -379,7 +492,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))) {
@@ -391,7 +504,7 @@ export class StreamingVideoDecoder {
if (
heldFrame &&
segmentIdx < segments.length &&
- heldFrameSec < segments[segmentIdx].startSec - epsilonSec
+ heldFrameSec < segments[segmentIdx].startSec - EPSILON_SEC
) {
heldFrame.close();
heldFrame = null;
@@ -406,7 +519,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;
}
@@ -427,7 +540,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) {
@@ -449,7 +562,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;
}
@@ -461,7 +574,7 @@ export class StreamingVideoDecoder {
segmentFrameIndex = 0;
if (
segmentIdx < segments.length &&
- heldFrameSec < segments[segmentIdx].startSec - epsilonSec
+ heldFrameSec < segments[segmentIdx].startSec - EPSILON_SEC
) {
break;
}
@@ -491,7 +604,6 @@ export class StreamingVideoDecoder {
}
this.decoder = null;
- const requiredEndSec = segments.length > 0 ? segments[segments.length - 1].endSec : 0;
if (
shouldFailDecodeEndedEarly({
cancelled: this.cancelled,
@@ -502,9 +614,9 @@ 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 message = `Decode ended early at ${decodedAtLabel} (needed ${requiredEndSec.toFixed(3)}s) – export may be slightly shorter than expected.`;
+ console.warn(`[StreamingVideoDecoder] ${message}`);
+ onWarning?.(message);
}
}
@@ -536,11 +648,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/types.ts b/src/lib/exporter/types.ts
index b6e08e8..3873341 100644
--- a/src/lib/exporter/types.ts
+++ b/src/lib/exporter/types.ts
@@ -19,6 +19,7 @@ export interface ExportResult {
success: boolean;
blob?: Blob;
error?: string;
+ warnings?: string[];
}
export interface VideoFrameData {
diff --git a/src/lib/exporter/videoExporter.browser.test.ts b/src/lib/exporter/videoExporter.browser.test.ts
index ec2b0f6..cca896f 100644
--- a/src/lib/exporter/videoExporter.browser.test.ts
+++ b/src/lib/exporter/videoExporter.browser.test.ts
@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import sampleVideoUrl from "../../../tests/fixtures/sample.webm?url";
+import { BackgroundLoadError } from "../wallpaper";
import type { ExportProgress } from "./types";
import { VideoExporter } from "./videoExporter";
@@ -40,4 +41,46 @@ describe("VideoExporter (real browser)", () => {
expect(finalizing.length).toBeGreaterThan(0);
expect(finalizing.at(-1)!.percentage).toBe(100);
});
+
+ it("exports successfully with an image wallpaper (served by Vite dev server)", async () => {
+ const exporter = new VideoExporter({
+ videoUrl: sampleVideoUrl,
+ width: 320,
+ height: 180,
+ frameRate: 15,
+ bitrate: 1_000_000,
+ wallpaper: "/wallpapers/wallpaper1.jpg",
+ zoomRegions: [],
+ showShadow: false,
+ shadowIntensity: 0,
+ showBlur: false,
+ cropRegion: { x: 0, y: 0, width: 1, height: 1 },
+ });
+
+ const result = await exporter.export();
+ expect(result.success, result.error).toBe(true);
+ expect(result.blob!.size).toBeGreaterThan(1024);
+ });
+
+ it("throws BackgroundLoadError when wallpaper fails to load (no silent black fallback)", async () => {
+ const exporter = new VideoExporter({
+ videoUrl: sampleVideoUrl,
+ width: 320,
+ height: 180,
+ frameRate: 15,
+ bitrate: 1_000_000,
+ wallpaper: "/wallpapers/does-not-exist.jpg",
+ zoomRegions: [],
+ showShadow: false,
+ shadowIntensity: 0,
+ showBlur: false,
+ cropRegion: { x: 0, y: 0, width: 1, height: 1 },
+ });
+
+ const rejection = exporter.export();
+ await expect(rejection).rejects.toBeInstanceOf(BackgroundLoadError);
+ await expect(rejection).rejects.toMatchObject({
+ url: expect.stringContaining("does-not-exist"),
+ });
+ });
});
diff --git a/src/lib/exporter/videoExporter.ts b/src/lib/exporter/videoExporter.ts
index d0affd1..d44bf40 100644
--- a/src/lib/exporter/videoExporter.ts
+++ b/src/lib/exporter/videoExporter.ts
@@ -7,6 +7,8 @@ import type {
WebcamSizePreset,
ZoomRegion,
} from "@/components/video-editor/types";
+import { BackgroundLoadError } from "@/lib/wallpaper";
+import { getPlatform } from "@/utils/platformUtils";
import { AsyncVideoFrameQueue } from "./asyncVideoFrameQueue";
import { AudioProcessor } from "./audioEncoder";
import { FrameRenderer } from "./frameRenderer";
@@ -81,6 +83,10 @@ export class VideoExporter {
return { success: false, error: "Export cancelled" };
}
+ if (normalizedError instanceof BackgroundLoadError) {
+ throw normalizedError;
+ }
+
if (encoderPreferences.length > 1) {
console.warn(
`[VideoExporter] ${encoderPreference} export attempt failed:`,
@@ -106,12 +112,16 @@ export class VideoExporter {
let webcamDecodeError: Error | null = null;
let webcamDecodePromise: Promise | null = null;
let webcamDecoder: StreamingVideoDecoder | null = null;
+ const warnings: string[] = [];
+ const onWarning = (message: string) => warnings.push(message);
this.cleanup();
this.cancelled = false;
this.fatalEncoderError = null;
try {
+ const platform = await getPlatform();
+
const streamingDecoder = new StreamingVideoDecoder();
this.streamingDecoder = streamingDecoder;
const videoInfo = await streamingDecoder.loadMetadata(this.config.videoUrl);
@@ -146,6 +156,7 @@ export class VideoExporter {
previewWidth: this.config.previewWidth,
previewHeight: this.config.previewHeight,
cursorTelemetry: this.config.cursorTelemetry,
+ platform,
});
this.renderer = renderer;
await renderer.initialize();
@@ -157,17 +168,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;
@@ -196,6 +201,7 @@ export class VideoExporter {
}
queue.enqueue(webcamFrame);
},
+ onWarning,
)
.catch((error) => {
webcamDecodeError = error instanceof Error ? error : new Error(String(error));
@@ -237,25 +243,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 &&
@@ -296,6 +306,7 @@ export class VideoExporter {
webcamFrame?.close();
}
},
+ onWarning,
);
if (this.cancelled) {
@@ -346,13 +357,13 @@ export class VideoExporter {
this.config.videoUrl,
this.config.trimRegions,
this.config.speedRegions,
- readEndSec,
+ videoInfo.duration,
);
}
}
const blob = await muxer.finalize();
- return { success: true, blob };
+ return { success: true, blob, warnings: warnings.length > 0 ? warnings : undefined };
} finally {
stopWebcamDecode = true;
webcamFrameQueue?.destroy();
@@ -422,7 +433,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/lib/wallpaper.test.ts b/src/lib/wallpaper.test.ts
new file mode 100644
index 0000000..02596aa
--- /dev/null
+++ b/src/lib/wallpaper.test.ts
@@ -0,0 +1,276 @@
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import { AssetBaseUnavailableError, UnsafeAssetPathError } from "./assetPath";
+import {
+ BackgroundLoadError,
+ classifyWallpaper,
+ DEFAULT_WALLPAPER,
+ resolveImageWallpaperUrl,
+ UnsafeImagePrefixError,
+ WALLPAPER_COUNT,
+ WALLPAPER_PATHS,
+} from "./wallpaper";
+
+describe("WALLPAPER_PATHS", () => {
+ it("contains WALLPAPER_COUNT entries", () => {
+ expect(WALLPAPER_PATHS).toHaveLength(WALLPAPER_COUNT);
+ });
+
+ it("DEFAULT_WALLPAPER is WALLPAPER_PATHS[0]", () => {
+ expect(DEFAULT_WALLPAPER).toBe(WALLPAPER_PATHS[0]);
+ });
+});
+
+describe("classifyWallpaper", () => {
+ it("hex color", () => {
+ expect(classifyWallpaper("#1a1a2e")).toEqual({ kind: "color", value: "#1a1a2e" });
+ });
+
+ it("rgb() color", () => {
+ expect(classifyWallpaper("rgb(1, 2, 3)")).toEqual({ kind: "color", value: "rgb(1, 2, 3)" });
+ });
+
+ it("rgba() color", () => {
+ expect(classifyWallpaper("rgba(1, 2, 3, 0.5)")).toEqual({
+ kind: "color",
+ value: "rgba(1, 2, 3, 0.5)",
+ });
+ });
+
+ it("hsl() color", () => {
+ expect(classifyWallpaper("hsl(180, 50%, 50%)")).toEqual({
+ kind: "color",
+ value: "hsl(180, 50%, 50%)",
+ });
+ });
+
+ it("oklch() color", () => {
+ expect(classifyWallpaper("oklch(50% 0.1 180)")).toEqual({
+ kind: "color",
+ value: "oklch(50% 0.1 180)",
+ });
+ });
+
+ it("linear gradient", () => {
+ const v = "linear-gradient(90deg, red, blue)";
+ expect(classifyWallpaper(v)).toEqual({ kind: "gradient", value: v });
+ });
+
+ it("radial gradient", () => {
+ const v = "radial-gradient(circle, red, blue)";
+ expect(classifyWallpaper(v)).toEqual({ kind: "gradient", value: v });
+ });
+
+ it("conic gradient", () => {
+ const v = "conic-gradient(red, blue)";
+ expect(classifyWallpaper(v)).toEqual({ kind: "gradient", value: v });
+ });
+
+ it("repeating-linear gradient", () => {
+ const v = "repeating-linear-gradient(45deg, red 0 10px, blue 10px 20px)";
+ expect(classifyWallpaper(v)).toEqual({ kind: "gradient", value: v });
+ });
+
+ it("repeating-radial gradient", () => {
+ const v = "repeating-radial-gradient(circle, red, blue 20px)";
+ expect(classifyWallpaper(v)).toEqual({ kind: "gradient", value: v });
+ });
+
+ it("leading-slash image path", () => {
+ expect(classifyWallpaper("/wallpapers/wallpaper1.jpg")).toEqual({
+ kind: "image",
+ path: "/wallpapers/wallpaper1.jpg",
+ });
+ });
+
+ it("http URL as image", () => {
+ expect(classifyWallpaper("https://example.com/bg.jpg")).toEqual({
+ kind: "image",
+ path: "https://example.com/bg.jpg",
+ });
+ });
+
+ it("file:// URL as image", () => {
+ expect(classifyWallpaper("file:///tmp/bg.jpg")).toEqual({
+ kind: "image",
+ path: "file:///tmp/bg.jpg",
+ });
+ });
+
+ it("data URI as image", () => {
+ expect(classifyWallpaper("data:image/png;base64,AAA")).toEqual({
+ kind: "image",
+ path: "data:image/png;base64,AAA",
+ });
+ });
+
+ it("named color falls back to color", () => {
+ expect(classifyWallpaper("red")).toEqual({ kind: "color", value: "red" });
+ });
+
+ it("empty string falls back to black", () => {
+ expect(classifyWallpaper("")).toEqual({ kind: "color", value: "#000000" });
+ });
+
+ it("trims whitespace", () => {
+ expect(classifyWallpaper(" #abcdef ")).toEqual({ kind: "color", value: "#abcdef" });
+ });
+
+ it("DEFAULT_WALLPAPER classifies as image", () => {
+ expect(classifyWallpaper(DEFAULT_WALLPAPER)).toEqual({
+ kind: "image",
+ path: DEFAULT_WALLPAPER,
+ });
+ });
+});
+
+describe("resolveImageWallpaperUrl", () => {
+ beforeEach(() => {
+ vi.stubGlobal("window", {
+ ...globalThis.window,
+ location: { protocol: "http:" },
+ electronAPI: undefined,
+ });
+ });
+
+ afterEach(() => {
+ vi.unstubAllGlobals();
+ });
+
+ it("passes through http URL", () => {
+ expect(resolveImageWallpaperUrl("http://example.com/bg.jpg")).toBe("http://example.com/bg.jpg");
+ });
+
+ it("passes through https URL", () => {
+ expect(resolveImageWallpaperUrl("https://example.com/bg.jpg")).toBe(
+ "https://example.com/bg.jpg",
+ );
+ });
+
+ it("passes through file:// URL", () => {
+ expect(resolveImageWallpaperUrl("file:///tmp/bg.jpg")).toBe("file:///tmp/bg.jpg");
+ });
+
+ it("passes through data URI", () => {
+ const uri = "data:image/png;base64,AAAA";
+ expect(resolveImageWallpaperUrl(uri)).toBe(uri);
+ });
+
+ it("resolves leading-slash wallpaper path via http fallback", () => {
+ expect(resolveImageWallpaperUrl("/wallpapers/wallpaper1.jpg")).toBe(
+ "/wallpapers/wallpaper1.jpg",
+ );
+ });
+
+ it("resolves bare relative wallpaper path", () => {
+ expect(resolveImageWallpaperUrl("wallpapers/wallpaper1.jpg")).toBe(
+ "/wallpapers/wallpaper1.jpg",
+ );
+ });
+
+ it("encodes special characters in path segments", () => {
+ expect(resolveImageWallpaperUrl("/wallpapers/my image.jpg")).toBe("/wallpapers/my%20image.jpg");
+ });
+
+ it("rejects image paths outside /wallpapers/ with UnsafeImagePrefixError as cause", () => {
+ try {
+ resolveImageWallpaperUrl("/etc/passwd");
+ expect.fail("should have thrown");
+ } catch (err) {
+ if (!(err instanceof BackgroundLoadError)) throw err;
+ expect(err.cause).toBeInstanceOf(UnsafeImagePrefixError);
+ }
+ });
+
+ it("wraps traversal attempts in BackgroundLoadError (preserves UnsafeAssetPathError as cause)", () => {
+ try {
+ resolveImageWallpaperUrl("/wallpapers/../etc/passwd");
+ expect.fail("should have thrown");
+ } catch (err) {
+ if (!(err instanceof BackgroundLoadError)) throw err;
+ expect(err.cause).toBeInstanceOf(UnsafeAssetPathError);
+ }
+ });
+
+ it("wraps percent-encoded traversal in BackgroundLoadError", () => {
+ try {
+ resolveImageWallpaperUrl("/wallpapers/%2e%2e/app.asar");
+ expect.fail("should have thrown");
+ } catch (err) {
+ if (!(err instanceof BackgroundLoadError)) throw err;
+ expect(err.cause).toBeInstanceOf(UnsafeAssetPathError);
+ }
+ });
+
+ it("resolves via electronAPI.assetBaseUrl when not http", () => {
+ vi.stubGlobal("window", {
+ ...globalThis.window,
+ location: { protocol: "file:" },
+ electronAPI: { assetBaseUrl: "file:///opt/app/public/" },
+ });
+ expect(resolveImageWallpaperUrl("/wallpapers/wallpaper1.jpg")).toBe(
+ "file:///opt/app/public/wallpapers/wallpaper1.jpg",
+ );
+ });
+
+ it("appends trailing slash to assetBaseUrl if missing", () => {
+ vi.stubGlobal("window", {
+ ...globalThis.window,
+ location: { protocol: "file:" },
+ electronAPI: { assetBaseUrl: "file:///opt/app/public" },
+ });
+ expect(resolveImageWallpaperUrl("/wallpapers/wallpaper1.jpg")).toBe(
+ "file:///opt/app/public/wallpapers/wallpaper1.jpg",
+ );
+ });
+
+ it("wraps AssetBaseUnavailableError in BackgroundLoadError when assetBaseUrl is empty", () => {
+ vi.stubGlobal("window", {
+ ...globalThis.window,
+ location: { protocol: "file:" },
+ electronAPI: { assetBaseUrl: "" },
+ });
+ try {
+ resolveImageWallpaperUrl("/wallpapers/wallpaper1.jpg");
+ expect.fail("should have thrown");
+ } catch (err) {
+ if (!(err instanceof BackgroundLoadError)) throw err;
+ expect(err.cause).toBeInstanceOf(AssetBaseUnavailableError);
+ }
+ });
+});
+
+describe("BackgroundLoadError", () => {
+ it("carries the failing URL and is instanceof Error", () => {
+ const err = new BackgroundLoadError("/home/user/secret/wallpaper.jpg");
+ expect(err).toBeInstanceOf(Error);
+ expect(err).toBeInstanceOf(BackgroundLoadError);
+ expect(err.url).toBe("/home/user/secret/wallpaper.jpg");
+ expect(err.name).toBe("BackgroundLoadError");
+ });
+
+ it("displayUrl hides parent directories to avoid leaking PII", () => {
+ const err = new BackgroundLoadError("file:///home/enrique/projects/openscreen/wallpaper1.jpg");
+ expect(err.displayUrl).toBe("wallpaper1.jpg");
+ });
+
+ it("displayUrl abbreviates data URIs", () => {
+ const err = new BackgroundLoadError("data:image/png;base64,AAA");
+ expect(err.displayUrl).toBe("data:…");
+ });
+
+ it("displayUrl returns sentinel for empty-basename URLs", () => {
+ const err = new BackgroundLoadError("file:///");
+ expect(err.displayUrl).toBe("(unknown)");
+ });
+
+ it("displayUrl returns sentinel for unparseable bare slash", () => {
+ const err = new BackgroundLoadError("/");
+ expect(err.displayUrl).toBe("(unknown)");
+ });
+
+ it("preserves cause when provided", () => {
+ const cause = new Error("inner");
+ const err = new BackgroundLoadError("file:///missing.jpg", cause);
+ expect(err.cause).toBe(cause);
+ });
+});
diff --git a/src/lib/wallpaper.ts b/src/lib/wallpaper.ts
new file mode 100644
index 0000000..6974a04
--- /dev/null
+++ b/src/lib/wallpaper.ts
@@ -0,0 +1,96 @@
+import { getAssetPath } from "@/lib/assetPath";
+
+export const WALLPAPER_COUNT = 18;
+
+export const WALLPAPER_PATHS: readonly string[] = Array.from(
+ { length: WALLPAPER_COUNT },
+ (_, i) => `/wallpapers/wallpaper${i + 1}.jpg`,
+);
+
+export const DEFAULT_WALLPAPER = WALLPAPER_PATHS[0];
+
+export type WallpaperClassification =
+ | { kind: "color"; value: string }
+ | { kind: "gradient"; value: string }
+ | { kind: "image"; path: string };
+
+const GRADIENT_RE = /^(repeating-)?(linear|radial|conic)-gradient\(/;
+const COLOR_FUNC_RE = /^(rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch|color)\(/;
+const IMAGE_URL_RE = /^(\/|https?:\/\/|file:\/\/|data:)/;
+
+export function classifyWallpaper(value: string): WallpaperClassification {
+ const trimmed = value.trim();
+ if (trimmed === "") {
+ return { kind: "color", value: "#000000" };
+ }
+ if (trimmed.startsWith("#") || COLOR_FUNC_RE.test(trimmed)) {
+ return { kind: "color", value: trimmed };
+ }
+ if (GRADIENT_RE.test(trimmed)) {
+ return { kind: "gradient", value: trimmed };
+ }
+ if (IMAGE_URL_RE.test(trimmed)) {
+ return { kind: "image", path: trimmed };
+ }
+ return { kind: "color", value: trimmed };
+}
+
+const ALLOWED_IMAGE_PREFIX = "/wallpapers/";
+
+export class UnsafeImagePrefixError extends Error {
+ constructor(prefix: string) {
+ super(`Image wallpaper path must live under ${prefix}`);
+ this.name = "UnsafeImagePrefixError";
+ }
+}
+
+export function resolveImageWallpaperUrl(imagePath: string): string {
+ if (
+ imagePath.startsWith("http://") ||
+ imagePath.startsWith("https://") ||
+ imagePath.startsWith("file://") ||
+ imagePath.startsWith("data:")
+ ) {
+ return imagePath;
+ }
+ const withLeadingSlash = imagePath.startsWith("/") ? imagePath : `/${imagePath}`;
+ if (!withLeadingSlash.startsWith(ALLOWED_IMAGE_PREFIX)) {
+ throw new BackgroundLoadError(imagePath, new UnsafeImagePrefixError(ALLOWED_IMAGE_PREFIX));
+ }
+ try {
+ return getAssetPath(withLeadingSlash.slice(1));
+ } catch (cause) {
+ if (cause instanceof BackgroundLoadError) throw cause;
+ throw new BackgroundLoadError(imagePath, cause);
+ }
+}
+
+export class BackgroundLoadError extends Error {
+ readonly url: string;
+ readonly cause?: unknown;
+
+ constructor(url: string, cause?: unknown) {
+ super(`Failed to load background image: ${displayBasename(url)}`);
+ this.name = "BackgroundLoadError";
+ this.url = url;
+ this.cause = cause;
+ }
+
+ get displayUrl(): string {
+ return displayBasename(this.url);
+ }
+}
+
+function displayBasename(url: string): string {
+ if (url.startsWith("data:")) {
+ return "data:…";
+ }
+ try {
+ const parsed = new URL(url);
+ const last = parsed.pathname.split("/").filter(Boolean).pop();
+ return last ? decodeURIComponent(last) : "(unknown)";
+ } catch {
+ const last = url.split("/").filter(Boolean).pop();
+ return last ?? "(unknown)";
+ }
+}
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 d76ee15..b7a0735 100644
--- a/src/vite-env.d.ts
+++ b/src/vite-env.d.ts
@@ -1,126 +1,2 @@
///
///
-
-interface ProcessedDesktopSource {
- id: string;
- name: string;
- display_id: string;
- thumbnail: string | null;
- appIcon: string | null;
-}
-
-interface CursorTelemetryPoint {
- timeMs: number;
- cx: number;
- cy: number;
-}
-
-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;
- requestCameraAccess: () => Promise<{
- success: boolean;
- granted: boolean;
- status: string;
- error?: string;
- }>;
- storeRecordedVideo: (
- videoData: ArrayBuffer,
- fileName: string,
- ) => Promise<{
- success: boolean;
- path?: string;
- session?: import("./lib/recordingSession").RecordingSession;
- message?: string;
- error?: string;
- }>;
- storeRecordedSession: (
- payload: import("./lib/recordingSession").StoreRecordedSessionInput,
- ) => Promise<{
- success: boolean;
- path?: string;
- session?: import("./lib/recordingSession").RecordingSession;
- message?: string;
- error?: string;
- }>;
- getRecordedVideoPath: () => Promise<{
- success: boolean;
- path?: string;
- message?: string;
- error?: string;
- }>;
- getAssetBasePath: () => Promise;
- setRecordingState: (recording: boolean) => Promise;
- getCursorTelemetry: (videoPath?: string) => Promise<{
- success: boolean;
- samples: CursorTelemetryPoint[];
- message?: string;
- error?: string;
- }>;
- onStopRecordingFromTray: (callback: () => void) => () => void;
- openExternalUrl: (url: string) => Promise<{ success: boolean; error?: string }>;
- saveExportedVideo: (
- videoData: ArrayBuffer,
- fileName: string,
- ) => Promise<{
- success: boolean;
- path?: string;
- message?: string;
- canceled?: boolean;
- }>;
- openVideoFilePicker: () => Promise<{ success: boolean; path?: string; canceled?: boolean }>;
- setCurrentVideoPath: (path: string) => Promise<{ success: boolean }>;
- setCurrentRecordingSession: (
- session: import("./lib/recordingSession").RecordingSession | null,
- ) => Promise<{
- success: boolean;
- session?: import("./lib/recordingSession").RecordingSession;
- }>;
- getCurrentVideoPath: () => Promise<{ success: boolean; path?: string }>;
- getCurrentRecordingSession: () => Promise<{
- success: boolean;
- session?: import("./lib/recordingSession").RecordingSession;
- }>;
- clearCurrentVideoPath: () => Promise<{ success: boolean }>;
- saveProjectFile: (
- projectData: unknown,
- suggestedName?: string,
- existingProjectPath?: string,
- ) => Promise<{
- success: boolean;
- path?: string;
- message?: string;
- canceled?: boolean;
- error?: string;
- }>;
- loadProjectFile: () => Promise<{
- success: boolean;
- path?: string;
- project?: unknown;
- message?: string;
- canceled?: boolean;
- error?: string;
- }>;
- loadCurrentProjectFile: () => Promise<{
- success: boolean;
- path?: string;
- project?: unknown;
- message?: string;
- canceled?: boolean;
- error?: string;
- }>;
- onMenuLoadProject: (callback: () => void) => () => void;
- onMenuSaveProject: (callback: () => void) => () => void;
- onMenuSaveProjectAs: (callback: () => void) => () => void;
- setMicrophoneExpanded: (expanded: boolean) => void;
- setHasUnsavedChanges: (hasChanges: boolean) => void;
- onRequestSaveBeforeClose: (callback: () => Promise | boolean) => () => void;
- setLocale: (locale: string) => Promise;
- };
-}
diff --git a/tests/fixtures/sample-inflated-duration.webm b/tests/fixtures/sample-inflated-duration.webm
new file mode 100644
index 0000000..a90d19e
Binary files /dev/null and b/tests/fixtures/sample-inflated-duration.webm differ
diff --git a/tsconfig.node.json b/tsconfig.node.json
index 1caabef..3be14b6 100644
--- a/tsconfig.node.json
+++ b/tsconfig.node.json
@@ -7,5 +7,10 @@
"allowSyntheticDefaultImports": true,
"strict": true
},
- "include": ["vite.config.ts"]
+ "include": [
+ "vite.config.ts",
+ "electron-builder.json5",
+ "playwright.config.ts",
+ "vitest.config.ts"
+ ]
}
diff --git a/vite.config.ts b/vite.config.ts
index 9a44766..55b5596 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -9,25 +9,15 @@ export default defineConfig({
react(),
electron({
main: {
- // Shortcut of `build.lib.entry`.
entry: "electron/main.ts",
vite: {
build: {},
},
},
preload: {
- // Shortcut of `build.rollupOptions.input`.
- // Preload scripts may contain Web assets, so use the `build.rollupOptions.input` instead `build.lib.entry`.
input: path.join(__dirname, "electron/preload.ts"),
},
- // Ployfill the Electron and Node.js API for Renderer process.
- // If you want use Node.js in Renderer process, the `nodeIntegration` needs to be enabled in the Main process.
- // See https://github.com/electron-vite/vite-plugin-electron-renderer
- renderer:
- process.env.NODE_ENV === "test"
- ? // https://github.com/electron-vite/vite-plugin-electron-renderer/issues/78#issuecomment-2053600808
- undefined
- : {},
+ renderer: process.env.NODE_ENV === "test" ? undefined : {},
}),
],
resolve: {
@@ -47,10 +37,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";
},
},
},