From 65ee75b49e50deb669ffbe2b17fc614978ad2b5f Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:32:48 +0900 Subject: [PATCH 01/68] [Update/TMP] Add feature Scheduled message send. --- src-ui/app/main_page/MainPage.jsx | 2 + .../plugins_section/PluginsSection.jsx | 349 ++++++++++++++++++ .../PluginsSection.module.scss | 159 ++++++++ 3 files changed, 510 insertions(+) create mode 100644 src-ui/app/main_page/plugins_section/PluginsSection.jsx create mode 100644 src-ui/app/main_page/plugins_section/PluginsSection.module.scss diff --git a/src-ui/app/main_page/MainPage.jsx b/src-ui/app/main_page/MainPage.jsx index a83ef09a..324bc463 100644 --- a/src-ui/app/main_page/MainPage.jsx +++ b/src-ui/app/main_page/MainPage.jsx @@ -2,6 +2,7 @@ import clsx from "clsx"; import styles from "./MainPage.module.scss"; import { SidebarSection } from "./sidebar_section/SidebarSection"; import { MainSection } from "./main_section/MainSection"; +import { PluginsSection } from "./plugins_section/PluginsSection"; import { useIsOpenedConfigPage } from "@logics_common"; export const MainPage = () => { @@ -15,6 +16,7 @@ export const MainPage = () => {
+
); diff --git a/src-ui/app/main_page/plugins_section/PluginsSection.jsx b/src-ui/app/main_page/plugins_section/PluginsSection.jsx new file mode 100644 index 00000000..7925c4d5 --- /dev/null +++ b/src-ui/app/main_page/plugins_section/PluginsSection.jsx @@ -0,0 +1,349 @@ +import React, { useState, useRef, useEffect } from "react"; +import styles from "./PluginsSection.module.scss"; +import { useSendTextToOverlay } from "@logics_configs"; + +export const PluginsSection = () => { + const { sendTextToOverlay } = useSendTextToOverlay(); + const [srtContent, setSrtContent] = useState(""); + const [cues, setCues] = useState([]); + const [isPlaying, setIsPlaying] = useState(false); + + // 再生モード ("relative": ボタン押下から、"absolute": 指定時刻から) + const [playbackMode, setPlaybackMode] = useState("relative"); + // 絶対モード用の再生開始時刻(ドロップダウンで選択、HH:MM) + const [targetHour, setTargetHour] = useState("19"); + const [targetMinute, setTargetMinute] = useState("00"); + + // カウントダウンの状態 + // initialCountdown: 開始ボタン押下時に計算される元の残り秒数 + const [initialCountdown, setInitialCountdown] = useState(null); + // countdownAdjustment: ユーザーが上下ボタンで調整する値(秒単位) + const [countdownAdjustment, setCountdownAdjustment] = useState(0); + // effectiveCountdown: (initialCountdown + countdownAdjustment) から経過秒数を差し引いた表示用の値 + const [effectiveCountdown, setEffectiveCountdown] = useState(null); + // cuesScheduled: 字幕タイマーが一度スケジュールされたか + const [cuesScheduled, setCuesScheduled] = useState(false); + + // setTimeout/setInterval のタイマーID管理用 + const timersRef = useRef([]); + // ファイル入力リセット用の ref + const fileInputRef = useRef(null); + + // ファイルアップロード処理 + const handleFileUpload = (event) => { + const file = event.target.files[0]; + if (!file) return; + const reader = new FileReader(); + reader.onload = (e) => { + const content = e.target.result; + setSrtContent(content); + const parsedCues = parseSRT(content); + setCues(parsedCues); + console.log("パース結果:", parsedCues); + if (fileInputRef.current) { + fileInputRef.current.value = ""; + } + }; + reader.readAsText(file); + }; + + // 字幕開始時の処理 + const startFunction = (cue) => { + console.log(`字幕開始 (index: ${cue.index}): ${cue.text}`); + sendTextToOverlay(cue.text); + }; + + // 字幕終了時の処理 + const endFunction = (cue) => { + console.log(`字幕終了 (index: ${cue.index}): ${cue.text}`); + // 必要に応じて終了処理(例:テキストクリア) + // sendTextToOverlay(""); + }; + + // すべてのタイマーを停止し、各状態を初期化する + const handleStop = () => { + timersRef.current.forEach((timerId) => { + clearTimeout(timerId); + clearInterval(timerId); + }); + timersRef.current = []; + console.log("再生を停止しました。"); + setIsPlaying(false); + setInitialCountdown(null); + setEffectiveCountdown(null); + setCountdownAdjustment(0); + setCuesScheduled(false); + }; + + // cues のスケジュールを行う(offset は countdownAdjustment * 1000) + const scheduleCues = (offset) => { + cues.forEach((cue) => { + const startDelay = cue.startTime * 1000 + offset; + const endDelay = cue.endTime * 1000 + offset; + if (startDelay >= 0) { + const timerId = setTimeout(() => startFunction(cue), startDelay); + timersRef.current.push(timerId); + } + if (endDelay >= 0) { + const timerId = setTimeout(() => endFunction(cue), endDelay); + timersRef.current.push(timerId); + } + }); + }; + + // カウントダウンタイマーの開始 + const startCountdownInterval = (initialValue) => { + // 初期表示は (initialValue + countdownAdjustment) + setEffectiveCountdown(initialValue + countdownAdjustment); + const countdownInterval = setInterval(() => { + setEffectiveCountdown((prev) => { + if (prev <= 1) { + clearInterval(countdownInterval); + return 0; + } + return prev - 1; + }); + }, 1000); + timersRef.current.push(countdownInterval); + }; + + // 「再生開始」ボタン押下時の処理 + const handleStart = () => { + handleStop(); + setIsPlaying(true); + setCuesScheduled(false); + + let computedCountdown = 0; + if (playbackMode === "absolute") { + const now = new Date(); + const hour = parseInt(targetHour, 10); + const minute = parseInt(targetMinute, 10); + let targetDate = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate(), + hour, + minute, + 0, + 0 + ); + if (targetDate.getTime() < now.getTime()) { + targetDate.setDate(targetDate.getDate() + 1); + } + computedCountdown = Math.ceil((targetDate.getTime() - now.getTime()) / 1000); + } else { + computedCountdown = 10; // relative モードの場合は固定値 + } + setInitialCountdown(computedCountdown); + setEffectiveCountdown(computedCountdown + countdownAdjustment); + sendTextToOverlay((computedCountdown + countdownAdjustment).toString()); + startCountdownInterval(computedCountdown); + }; + + // effectiveCountdown が 0 になったとき、字幕開始 + useEffect(() => { + if ( + isPlaying && + effectiveCountdown !== null && + effectiveCountdown <= 0 && + !cuesScheduled + ) { + sendTextToOverlay("Start."); + console.log("Start."); + scheduleCues(0); + setCuesScheduled(true); + } + }, [effectiveCountdown, isPlaying, cuesScheduled, countdownAdjustment]); + + // テーブル内の字幕行をクリック(relative モードのみ)でジャンプ + const handleJump = (jumpCue) => { + if (playbackMode !== "relative") return; + handleStop(); + const offset = -jumpCue.startTime * 1000; + scheduleCues(offset); + setIsPlaying(true); + }; + + // HH:MM:SS 形式に変換する補助関数 + const formatTime = (timeInSeconds) => { + const hours = Math.floor(timeInSeconds / 3600); + const minutes = Math.floor((timeInSeconds % 3600) / 60); + const seconds = Math.floor(timeInSeconds % 60); + return ( + String(hours).padStart(2, "0") + + ":" + + String(minutes).padStart(2, "0") + + ":" + + String(seconds).padStart(2, "0") + ); + }; + + // ファイルクリア + const handleClearFile = () => { + handleStop(); + setSrtContent(""); + setCues([]); + }; + + return ( +
+

字幕プレイヤー

+
+ + +
+
+ + {playbackMode === "absolute" && ( +
+ +
+ + : + +
+
+ )} +
+
+ + +
+
+ カウントダウン: {effectiveCountdown} 秒 + + +
+ {cues.length > 0 && ( +
+

字幕一覧

+ + + + + + + + + + + {cues.map((cue) => ( + handleJump(cue)} + className={styles.tableRow} + > + + + + + + ))} + +
番号開始終了テキスト
{cue.index}{formatTime(cue.startTime)}{formatTime(cue.endTime)}{cue.text}
+

+ ※ 行をクリックすると、その字幕の位置にジャンプします。(相対モードのみ) +

+
+ )} +
+ ); +}; + +/** + * SRT形式の文字列を解析する関数 + * ユーザー提示のサンプルに基づき、改行コードを正規化後、空行で分割して解析 + */ +const parseSRT = (data) => { + const cues = []; + const normalizedData = data.replace(/\r\n/g, "\n").trim(); + const blocks = normalizedData.split(/\n\s*\n/); + blocks.forEach((block) => { + const lines = block.split("\n").filter((line) => line.trim() !== ""); + if (lines.length >= 3) { + const index = parseInt(lines[0], 10); + const timeMatch = lines[1].match(/([\d:,]+)\s+-->\s+([\d:,]+)/); + if (!timeMatch) return; + const start = parseTime(timeMatch[1]); + const end = parseTime(timeMatch[2]); + const text = lines.slice(2).join("\n"); + cues.push({ index, startTime: start, endTime: end, text }); + } + }); + return cues; +}; + +/** + * "HH:MM:SS,mmm" 形式の文字列を秒数に変換する関数 + */ +const parseTime = (timeString) => { + const [hms, ms] = timeString.split(","); + const [hours, minutes, seconds] = hms.split(":").map(Number); + return hours * 3600 + minutes * 60 + seconds + Number(ms) / 1000; +}; diff --git a/src-ui/app/main_page/plugins_section/PluginsSection.module.scss b/src-ui/app/main_page/plugins_section/PluginsSection.module.scss new file mode 100644 index 00000000..c6a1a6c7 --- /dev/null +++ b/src-ui/app/main_page/plugins_section/PluginsSection.module.scss @@ -0,0 +1,159 @@ +.container { + padding: 2rem; + background: #1e1e1e; + color: #ffffff; + border-radius: 1rem; + max-width: 60rem; + margin: 0 auto; + flex-shrink: 0; + height: 100%; + overflow: auto; + + h1 { + font-size: 2.4rem; + margin-bottom: 1.5rem; + text-align: center; + } + + label { + display: block; + font-size: 1.6rem; + margin-bottom: 0.5rem; + } + + input, + select { + font-size: 1.6rem; + padding: 0.5rem; + border-radius: 0.5rem; + border: 0.1rem solid #ccc; + background: #333; + color: #fff; + } + + input[type="file"] { + padding: 0.5rem; + } + + // ボタンの基本スタイル + button { + font-size: 1.8rem; + padding: 1rem 2rem; + border: none; + border-radius: 0.5rem; + cursor: pointer; + transition: background 0.3s; + margin-right: 1rem; + + &:focus { + outline: none; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); + } + } + + // 再生開始用ボタン(通常時) + .primary { + background: #007bff; + color: #fff; + &:hover { + background: #0056b3; + } + } + + // 再生停止用ボタン + .secondary { + background: #dc3545; + color: #fff; + &:hover { + background: #a71d2a; + } + } + + // ファイルクリア用ボタン + .file-clear { + background: #6c757d; + color: #fff; + &:hover { + background: #5a6268; + } + } + + // 「再生中」状態(クリック不可)用のスタイル + .is_playing { + background: #6c757d; + cursor: not-allowed; + } + + // カウントダウン表示用エリア + .countdown { + margin-top: 1rem; + font-size: 1.6rem; + display: flex; + align-items: center; + gap: 1rem; + + span { + font-weight: bold; + } + + button { + font-size: 1.6rem; + padding: 0.5rem 1rem; + border: none; + border-radius: 0.4rem; + background: #28a745; + color: #fff; + cursor: pointer; + transition: background 0.3s; + + &:hover { + background: #218838; + } + } + } + + // 再生モードや時刻指定の select 関連(横並びの場合などに調整) + .time-selects { + display: flex; + align-items: center; + gap: 0.5rem; + } + + // 字幕一覧のテーブル + table { + width: 100%; + border-collapse: collapse; + margin-top: 2rem; + + th, + td { + padding: 1rem; + border: 0.1rem solid #444; + text-align: left; + font-size: 1.4rem; + } + + th { + background: #555; + } + + tbody { + tr { + cursor: pointer; + transition: background 0.2s; + + &:nth-child(even) { + background: #2a2a2a; + } + + &:hover { + background: #444; + } + } + } + } + + .subtitle_lists { + font-size: 1.4rem; + } +} From 31deeae53fa76445b4bf58bee5f02eac2468db5d Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 12 Feb 2025 18:32:31 +0900 Subject: [PATCH 02/68] [Update] Support ASS format file. and show actor combine with text. --- .../plugins_section/PluginsSection.jsx | 131 +++++++++++++----- 1 file changed, 96 insertions(+), 35 deletions(-) diff --git a/src-ui/app/main_page/plugins_section/PluginsSection.jsx b/src-ui/app/main_page/plugins_section/PluginsSection.jsx index 7925c4d5..84d966a2 100644 --- a/src-ui/app/main_page/plugins_section/PluginsSection.jsx +++ b/src-ui/app/main_page/plugins_section/PluginsSection.jsx @@ -14,8 +14,8 @@ export const PluginsSection = () => { const [targetHour, setTargetHour] = useState("19"); const [targetMinute, setTargetMinute] = useState("00"); - // カウントダウンの状態 - // initialCountdown: 開始ボタン押下時に計算される元の残り秒数 + // カウントダウン状態 + // initialCountdown: 再生開始ボタン押下時に算出される元の残り秒数 const [initialCountdown, setInitialCountdown] = useState(null); // countdownAdjustment: ユーザーが上下ボタンで調整する値(秒単位) const [countdownAdjustment, setCountdownAdjustment] = useState(0); @@ -24,12 +24,12 @@ export const PluginsSection = () => { // cuesScheduled: 字幕タイマーが一度スケジュールされたか const [cuesScheduled, setCuesScheduled] = useState(false); - // setTimeout/setInterval のタイマーID管理用 + // タイマー(setTimeout/setInterval)のID管理用 const timersRef = useRef([]); // ファイル入力リセット用の ref const fileInputRef = useRef(null); - // ファイルアップロード処理 + // ファイルアップロード時の処理 const handleFileUpload = (event) => { const file = event.target.files[0]; if (!file) return; @@ -37,9 +37,15 @@ export const PluginsSection = () => { reader.onload = (e) => { const content = e.target.result; setSrtContent(content); - const parsedCues = parseSRT(content); + let parsedCues = []; + // 拡張子により ASS と SRT を判定 + if (file.name.toLowerCase().endsWith(".ass")) { + parsedCues = parseASS(content); + } else { + parsedCues = parseSRT(content); + } setCues(parsedCues); - console.log("パース結果:", parsedCues); + console.log("Parsed cues:", parsedCues); if (fileInputRef.current) { fileInputRef.current.value = ""; } @@ -49,14 +55,20 @@ export const PluginsSection = () => { // 字幕開始時の処理 const startFunction = (cue) => { - console.log(`字幕開始 (index: ${cue.index}): ${cue.text}`); - sendTextToOverlay(cue.text); + let send_text = ""; + if (cue.actor !== "") { + send_text = `[${cue.actor}] ${cue.text}`; + } else { + send_text = `${cue.text}`; + } + console.log(`字幕開始 (index: ${cue.index}) send_text:${send_text}`); + sendTextToOverlay(send_text); }; // 字幕終了時の処理 const endFunction = (cue) => { console.log(`字幕終了 (index: ${cue.index}): ${cue.text}`); - // 必要に応じて終了処理(例:テキストクリア) + // 必要に応じた終了処理(例:テキストクリア)を実装可能 // sendTextToOverlay(""); }; @@ -75,7 +87,7 @@ export const PluginsSection = () => { setCuesScheduled(false); }; - // cues のスケジュールを行う(offset は countdownAdjustment * 1000) + // cues のスケジュールを行う(字幕開始時のオフセットは countdownAdjustment * 1000) const scheduleCues = (offset) => { cues.forEach((cue) => { const startDelay = cue.startTime * 1000 + offset; @@ -93,7 +105,6 @@ export const PluginsSection = () => { // カウントダウンタイマーの開始 const startCountdownInterval = (initialValue) => { - // 初期表示は (initialValue + countdownAdjustment) setEffectiveCountdown(initialValue + countdownAdjustment); const countdownInterval = setInterval(() => { setEffectiveCountdown((prev) => { @@ -150,7 +161,8 @@ export const PluginsSection = () => { ) { sendTextToOverlay("Start."); console.log("Start."); - scheduleCues(0); + // オフセットは countdownAdjustment × 1000 を字幕に反映 + scheduleCues(countdownAdjustment * 1000); setCuesScheduled(true); } }, [effectiveCountdown, isPlaying, cuesScheduled, countdownAdjustment]); @@ -190,11 +202,11 @@ export const PluginsSection = () => {

字幕プレイヤー

{playbackMode === "absolute" && (
- +
{playbackMode === "absolute" && ( -
+
-
+
setTargetMinute(e.target.value)} - className={styles.select} + className={styles.time_selects_item} > {Array.from({ length: 60 }, (_, i) => { const minute = i.toString().padStart(2, "0"); @@ -278,23 +293,55 @@ export const SubtitleSystemContainer = () => { {/* カウントダウン表示:字幕開始前は常に表示 */} {effectiveCountdown !== null && !cuesScheduled && (
- カウントダウン: {effectiveCountdown} 秒 - - + カウントダウン: {secToDayTime(effectiveCountdown)} +
+ {/* 1分単位の調整ボタン */} +
+ + +
+ {/* 1秒単位の調整ボタン */} +
+ + +
+
)} {/* 字幕一覧の表示(relative モードの場合、クリックでジャンプ) */} @@ -408,3 +455,22 @@ const parseTime = (timeString) => { const [hours, minutes, seconds] = hms.split(":").map(Number); return hours * 3600 + minutes * 60 + seconds + Number(ms) / 1000; }; + +const secToDayTime = (seconds) => { + const day = Math.floor(seconds / 86400); + const hour = Math.floor((seconds % 86400) / 3600); + const min = Math.floor((seconds % 3600) / 60); + const sec = seconds % 60; + let time = ""; + // day が 0 の場合は「日」は出力しない(hour や min も同様) + if (day !== 0) { + time = `${day}日${hour}時間${min}分${sec}秒`; + } else if (hour !== 0) { + time = `${hour}:${min}:${sec}`; + } else if (min !== 0) { + time = `${String(min).padStart(2, "0")}:${String(sec).padStart(2, "0")}`; + } else { + time = `${String(sec).padStart(2, "0")}`; + } + return time; +}; diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.module.scss b/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.module.scss index 515842e7..ca0bc25d 100644 --- a/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.module.scss +++ b/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.module.scss @@ -1,157 +1,179 @@ .container { - padding: 4rem; - background: #1e1e1e; - color: #ffffff; - border-radius: 1rem; - flex-shrink: 0; - height: 100%; - overflow: auto; - - h1 { - font-size: 2.4rem; - margin-bottom: 1.5rem; - text-align: center; - } - - label { - display: block; - font-size: 1.6rem; - margin-bottom: 0.5rem; - } - - input, - select { - font-size: 1.6rem; - padding: 0.5rem; - border-radius: 0.5rem; - border: 0.1rem solid #ccc; - background: #333; - color: #fff; - } - - input[type="file"] { - padding: 0.5rem; - } - - // ボタンの基本スタイル - button { - font-size: 1.8rem; - padding: 1rem 2rem; - border: none; - border-radius: 0.5rem; - cursor: pointer; - transition: background 0.3s; - margin-right: 1rem; - - &:focus { - outline: none; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); - } - } - - // 再生開始用ボタン(通常時) - .primary { - background: #007bff; - color: #fff; - &:hover { - background: #0056b3; - } - } - - // 再生停止用ボタン - .secondary { - background: #dc3545; - color: #fff; - &:hover { - background: #a71d2a; - } - } - - // ファイルクリア用ボタン - .file-clear { - background: #6c757d; - color: #fff; - &:hover { - background: #5a6268; - } - } - - // 「再生中」状態(クリック不可)用のスタイル - .is_playing { - background: #6c757d; - cursor: not-allowed; - } - - // カウントダウン表示用エリア - .countdown { - margin-top: 1rem; - font-size: 1.6rem; + padding: 4rem; + background: #1e1e1e; + color: #ffffff; + border-radius: 1rem; + flex-shrink: 0; + height: 100%; + overflow: auto; display: flex; - align-items: center; - gap: 1rem; + flex-direction: column; + gap: 2rem; - span { - font-weight: bold; + h1 { + font-size: 2.4rem; + margin-bottom: 1.5rem; + text-align: center; } + label { + display: block; + font-size: 1.6rem; + margin-bottom: 0.5rem; + } + + input, + select { + font-size: 1.6rem; + padding: 0.5rem; + border-radius: 0.5rem; + border: 0.1rem solid #ccc; + background: #333; + color: #fff; + } + + input[type="file"] { + padding: 0.5rem; + } + + // ボタンの基本スタイル button { - font-size: 1.6rem; - padding: 0.5rem 1rem; - border: none; - border-radius: 0.4rem; - background: #28a745; - color: #fff; - cursor: pointer; - transition: background 0.3s; + font-size: 1.8rem; + padding: 1rem 2rem; + border: none; + border-radius: 0.5rem; + cursor: pointer; + transition: background 0.3s; + margin-right: 1rem; - &:hover { - background: #218838; - } + &:focus { + outline: none; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); + } } - } - // 再生モードや時刻指定の select 関連(横並びの場合などに調整) - .time-selects { - display: flex; - align-items: center; - gap: 0.5rem; - } + // 再生開始用ボタン(通常時) + .primary { + background: #007bff; + color: #fff; + &:hover { + background: #0056b3; + } + } - // 字幕一覧のテーブル - table { - width: 100%; - border-collapse: collapse; - margin-top: 2rem; + // 再生停止用ボタン + .secondary { + background: #dc3545; + color: #fff; + &:hover { + background: #a71d2a; + } + } + + // ファイルクリア用ボタン + .file_clear { + background: #6c757d; + color: #fff; + &:hover { + background: #5a6268; + } + } + + // 「再生中」状態(クリック不可)用のスタイル + .is_playing { + background: #6c757d; + cursor: not-allowed; + pointer-events: none; + } + + // カウントダウン表示用エリア + .countdown { + margin-top: 1rem; + font-size: 2.6rem; + display: flex; + align-items: center; + gap: 1rem; + flex-direction: column; + + span { + font-weight: bold; + } + + button { + padding: 0.5rem 1rem; + font-size: 1.6rem; + border: none; + border-radius: 0.4rem; + background: #28a745; + color: #fff; + cursor: pointer; + transition: background 0.3s; + + &:hover { + background: #218838; + } + } + } + + // 再生モードや時刻指定の select 関連(横並びの場合などに調整) + .time_selects { + display: flex; + align-items: center; + gap: 0.5rem; + } + + // 字幕一覧のテーブル + table { + width: 100%; + border-collapse: collapse; + margin-top: 2rem; th, td { - padding: 1rem; - border: 0.1rem solid #444; - text-align: left; - font-size: 1.4rem; + padding: 1rem; + border: 0.1rem solid #444; + text-align: left; + font-size: 1.4rem; } th { - background: #555; + background: #555; } tbody { - tr { + tr { cursor: pointer; transition: background 0.2s; &:nth-child(even) { - background: #2a2a2a; + background: #2a2a2a; } &:hover { - background: #444; + background: #444; + } } - } } - } + } - .subtitle_lists { - font-size: 1.4rem; - } } +.subtitle_lists { + font-size: 1.4rem; +} + +.time_section { + display: flex; + gap: 2rem; + justify-content: center; + align-items: center; +} +.time_selects_item { + gap: 0.4rem; + width: 6rem; + text-align: center; +} + +.adjust_button_wrapper { + display: flex; + gap: 2rem; +} \ No newline at end of file From a9c5ccdbb871d67be7a9177485a80a62e1df82f5 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sat, 15 Feb 2025 08:46:15 +0900 Subject: [PATCH 05/68] [Update/Refactor] Subtitle system v1.1. Organize file system and change the design. --- src-ui/app/App.jsx | 4 + .../SubtitleSystemContainer.jsx | 485 +----------------- .../SubtitleSystemContainer.module.scss | 145 ++---- .../_controllers/SubtitlesController.jsx | 39 ++ .../_logics/useSubtitles.jsx | 193 +++++++ .../_subtitles_utils.js | 112 ++++ .../CountdownContainer.jsx | 72 +++ .../CountdownContainer.module.scss | 47 ++ .../InputFileContainer.jsx | 62 +++ .../InputFileContainer.module.scss | 42 ++ .../ModeSelectorContainer.jsx | 74 +++ .../ModeSelectorContainer.module.scss | 50 ++ .../PlayControlContainer.jsx | 33 ++ .../PlayControlContainer.module.scss | 31 ++ .../SubtitlesListContainer.jsx | 45 ++ .../SubtitlesListContainer.module.scss | 0 src-ui/store.js | 20 +- 17 files changed, 895 insertions(+), 559 deletions(-) create mode 100644 src-ui/app/main_page/main_section/subtitle_system_container/_controllers/SubtitlesController.jsx create mode 100644 src-ui/app/main_page/main_section/subtitle_system_container/_logics/useSubtitles.jsx create mode 100644 src-ui/app/main_page/main_section/subtitle_system_container/_subtitles_utils.js create mode 100644 src-ui/app/main_page/main_section/subtitle_system_container/countdown_container/CountdownContainer.jsx create mode 100644 src-ui/app/main_page/main_section/subtitle_system_container/countdown_container/CountdownContainer.module.scss create mode 100644 src-ui/app/main_page/main_section/subtitle_system_container/input_file_container/InputFileContainer.jsx create mode 100644 src-ui/app/main_page/main_section/subtitle_system_container/input_file_container/InputFileContainer.module.scss create mode 100644 src-ui/app/main_page/main_section/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx create mode 100644 src-ui/app/main_page/main_section/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss create mode 100644 src-ui/app/main_page/main_section/subtitle_system_container/play_control_container/PlayControlContainer.jsx create mode 100644 src-ui/app/main_page/main_section/subtitle_system_container/play_control_container/PlayControlContainer.module.scss create mode 100644 src-ui/app/main_page/main_section/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx create mode 100644 src-ui/app/main_page/main_section/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index 86b5430d..03fa2f8c 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -21,6 +21,8 @@ import { SnackbarController } from "./snackbar_controller/SnackbarController"; import styles from "./App.module.scss"; import { useIsBackendReady, useIsSoftwareUpdating, useIsVrctAvailable, useWindow } from "@logics_common"; +import { SubtitlesController } from "./main_page/main_section/subtitle_system_container/_controllers/subtitlesController.jsx"; + export const App = () => { const { currentIsVrctAvailable } = useIsVrctAvailable(); const { currentIsBackendReady } = useIsBackendReady(); @@ -53,6 +55,8 @@ const Contents = () => { const { currentIsSoftwareUpdating } = useIsSoftwareUpdating(); return ( <> + + {currentIsSoftwareUpdating.data === false ? diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.jsx b/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.jsx index 17e1dc82..144aefb0 100644 --- a/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.jsx +++ b/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.jsx @@ -1,476 +1,45 @@ -import React, { useState, useRef, useEffect } from "react"; import styles from "./SubtitleSystemContainer.module.scss"; -import { useSendTextToOverlay } from "@logics_configs"; +import { InputFileContainer } from "./input_file_container/InputFileContainer"; +import { ModeSelectorContainer } from "./mode_selector_container/ModeSelectorContainer"; +import { PlayControlContainer } from "./play_control_container/PlayControlContainer"; +import { CountdownContainer } from "./countdown_container/CountdownContainer"; +import { SubtitlesListContainer } from "./subtitles_list_container/SubtitlesListContainer"; export const SubtitleSystemContainer = () => { - const { sendTextToOverlay } = useSendTextToOverlay(); - const [srtContent, setSrtContent] = useState(""); - const [cues, setCues] = useState([]); - const [isPlaying, setIsPlaying] = useState(false); + // const [srtContent, setSrtContent] = useState(""); + // const [cues, setCues] = useState([]); + // const [isPlaying, setIsPlaying] = useState(false); // 再生モード ("relative": ボタン押下から、"absolute": 指定時刻から) - const [playbackMode, setPlaybackMode] = useState("relative"); + // const [playbackMode, setPlaybackMode] = useState("relative"); // 絶対モード用の再生開始時刻(ドロップダウンで選択、HH:MM) - const [targetHour, setTargetHour] = useState("23"); - const [targetMinute, setTargetMinute] = useState("00"); + // const [targetHour, setTargetHour] = useState("23"); + // const [targetMinute, setTargetMinute] = useState("00"); // カウントダウン状態 - // initialCountdown: 再生開始ボタン押下時に算出される元の残り秒数 - const [initialCountdown, setInitialCountdown] = useState(null); + // // initialCountdown: 再生開始ボタン押下時に算出される元の残り秒数 + // const [initialCountdown, setInitialCountdown] = useState(null); // countdownAdjustment: ユーザーが上下ボタンで調整する値(秒単位) - const [countdownAdjustment, setCountdownAdjustment] = useState(0); + // const [countdownAdjustment, setCountdownAdjustment] = useState(0); // effectiveCountdown: (initialCountdown + countdownAdjustment) から経過秒数を差し引いた表示用の値 - const [effectiveCountdown, setEffectiveCountdown] = useState(null); + // const [effectiveCountdown, setEffectiveCountdown] = useState(null); // cuesScheduled: 字幕タイマーが一度スケジュールされたか - const [cuesScheduled, setCuesScheduled] = useState(false); + // const [cuesScheduled, setCuesScheduled] = useState(false); - // タイマー(setTimeout/setInterval)のID管理用 - const timersRef = useRef([]); - // カウントダウンタイマー専用の ref - const countdownIntervalRef = useRef(null); - // ファイル入力リセット用の ref - const fileInputRef = useRef(null); - - // ファイルアップロード時の処理 - const handleFileUpload = (event) => { - const file = event.target.files[0]; - if (!file) return; - const reader = new FileReader(); - reader.onload = (e) => { - const content = e.target.result; - setSrtContent(content); - let parsedCues = []; - // 拡張子により ASS と SRT を判定 - if (file.name.toLowerCase().endsWith(".ass")) { - parsedCues = parseASS(content); - } else { - parsedCues = parseSRT(content); - } - setCues(parsedCues); - console.log("Parsed cues:", parsedCues); - if (fileInputRef.current) { - fileInputRef.current.value = ""; - } - }; - reader.readAsText(file); - }; - - // 字幕開始時の処理 - const startFunction = (cue) => { - let send_text = ""; - if (cue.actor !== "") { - send_text = `[${cue.actor}] ${cue.text}`; - } else { - send_text = `${cue.text}`; - } - console.log(`字幕開始 (index: ${cue.index}) send_text:${send_text}`); - sendTextToOverlay(send_text); - }; - - // 字幕終了時の処理 - const endFunction = (cue) => { - console.log(`字幕終了 (index: ${cue.index}): ${cue.text}`); - // 必要に応じた終了処理(例:テキストクリア)を実装可能 - // sendTextToOverlay(""); - }; - - // すべてのタイマーを停止し、各状態を初期化する - const handleStop = () => { - timersRef.current.forEach((timerId) => { - clearTimeout(timerId); - clearInterval(timerId); - }); - timersRef.current = []; - if (countdownIntervalRef.current) { - clearInterval(countdownIntervalRef.current); - countdownIntervalRef.current = null; - } - console.log("再生を停止しました。"); - setIsPlaying(false); - setInitialCountdown(null); - setEffectiveCountdown(null); - setCountdownAdjustment(0); - setCuesScheduled(false); - }; - - // cues のスケジュールを行う(字幕開始時のオフセットは調整後のタイミングに合わせる) - const scheduleCues = (offset) => { - cues.forEach((cue) => { - const startDelay = cue.startTime * 1000 + offset; - const endDelay = cue.endTime * 1000 + offset; - if (startDelay >= 0) { - const timerId = setTimeout(() => startFunction(cue), startDelay); - timersRef.current.push(timerId); - } - if (endDelay >= 0) { - const timerId = setTimeout(() => endFunction(cue), endDelay); - timersRef.current.push(timerId); - } - }); - }; - - // カウントダウンタイマーの開始/再登録(指定した値から1秒ごとに減らす) - const startCountdownInterval = (startValue) => { - // 既存のタイマーがあればクリア - if (countdownIntervalRef.current) { - clearInterval(countdownIntervalRef.current); - } - // 新たな開始値を設定 - setEffectiveCountdown(startValue); - countdownIntervalRef.current = setInterval(() => { - setEffectiveCountdown((prev) => { - if (prev <= 1) { - clearInterval(countdownIntervalRef.current); - return 0; - } - return prev - 1; - }); - }, 1000); - timersRef.current.push(countdownIntervalRef.current); - }; - - // 「再生開始」ボタン押下時の処理 - const handleStart = () => { - handleStop(); - setIsPlaying(true); - setCuesScheduled(false); - - let computedCountdown = 0; - if (playbackMode === "absolute") { - const now = new Date(); - const hour = parseInt(targetHour, 10); - const minute = parseInt(targetMinute, 10); - let targetDate = new Date( - now.getFullYear(), - now.getMonth(), - now.getDate(), - hour, - minute, - 0, - 0 - ); - if (targetDate.getTime() < now.getTime()) { - targetDate.setDate(targetDate.getDate() + 1); - } - computedCountdown = Math.ceil((targetDate.getTime() - now.getTime()) / 1000); - } else { - computedCountdown = 10; // relative モードの場合は固定値 - } - setInitialCountdown(computedCountdown); - // 調整値を反映した開始値 - const startValue = computedCountdown + countdownAdjustment; - startCountdownInterval(startValue); - sendTextToOverlay(startValue.toString()); - }; - - // effectiveCountdown が 0 になったとき、字幕開始 - useEffect(() => { - if ( - isPlaying && - effectiveCountdown !== null && - effectiveCountdown <= 0 && - !cuesScheduled - ) { - sendTextToOverlay("Start."); - console.log("Start."); - // 調整後のタイミングで字幕スケジュールを開始 - scheduleCues(0); - setCuesScheduled(true); - } - - console.log(secToDayTime(effectiveCountdown)); - sendTextToOverlay(secToDayTime(effectiveCountdown)); - }, [effectiveCountdown, isPlaying, cuesScheduled, countdownAdjustment]); - - // テーブル内の字幕行をクリック(relative モードのみ)でジャンプ - const handleJump = (jumpCue) => { - if (playbackMode !== "relative") return; - handleStop(); - const offset = -jumpCue.startTime * 1000; - scheduleCues(offset); - setIsPlaying(true); - }; - - // HH:MM:SS 形式に変換する補助関数 - const formatTime = (timeInSeconds) => { - const hours = Math.floor(timeInSeconds / 3600); - const minutes = Math.floor((timeInSeconds % 3600) / 60); - const seconds = Math.floor(timeInSeconds % 60); - return ( - String(hours).padStart(2, "0") + - ":" + - String(minutes).padStart(2, "0") + - ":" + - String(seconds).padStart(2, "0") - ); - }; - - // ファイルクリア - const handleClearFile = () => { - handleStop(); - setSrtContent(""); - setCues([]); - }; + // // タイマー(setTimeout/setInterval)のID管理用 + // const timersRef = useRef([]); + // // カウントダウンタイマー専用の ref + // const countdownIntervalRef = useRef(null); return (
-

字幕プレイヤー

-
- - -
-
- - {playbackMode === "absolute" && ( -
- -
- - : - -
-
- )} -
-
- - -
- {/* カウントダウン表示:字幕開始前は常に表示 */} - {effectiveCountdown !== null && !cuesScheduled && ( -
- カウントダウン: {secToDayTime(effectiveCountdown)} -
- {/* 1分単位の調整ボタン */} -
- - -
- {/* 1秒単位の調整ボタン */} -
- - -
-
-
- )} - {/* 字幕一覧の表示(relative モードの場合、クリックでジャンプ) */} - {cues.length > 0 && ( -
-

字幕一覧

- - - - - - - - - - - - {cues.map((cue) => ( - handleJump(cue)} - className={styles.tableRow} - > - - - - - - - ))} - -
番号開始終了Actorテキスト
{cue.index}{formatTime(cue.startTime)}{formatTime(cue.endTime)}{cue.actor}{cue.text}
-

- ※ 行をクリックすると、その字幕の位置にジャンプします。(相対モードのみ) -

-
- )} +

字幕プレイヤー

+ + + +
+ +
); }; - -/** - * SRT形式の文字列を解析する関数 - * 改行コードを正規化し、空行で分割して解析する - * (actor は存在しないため、空文字列をセット) - */ -const parseSRT = (data) => { - const cues = []; - const normalizedData = data.replace(/\r\n/g, "\n").trim(); - const blocks = normalizedData.split(/\n\s*\n/); - blocks.forEach((block) => { - const lines = block.split("\n").filter((line) => line.trim() !== ""); - if (lines.length >= 3) { - const index = parseInt(lines[0], 10); - const timeMatch = lines[1].match(/([\d:,]+)\s+-->\s+([\d:,]+)/); - if (!timeMatch) return; - const start = parseTime(timeMatch[1]); - const end = parseTime(timeMatch[2]); - const text = lines.slice(2).join("\n"); - cues.push({ index, startTime: start, endTime: end, actor: "", text }); - } - }); - return cues; -}; - -/** - * ASS形式の文字列を解析する関数 - * [Events] セクション内の "Dialogue:" 行から、 - * フォーマット "Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" - * に沿って分割する。 - * ここでは Name を actor、Text を text として抽出する。 - */ -const parseASS = (data) => { - const cues = []; - const lines = data.split(/\r?\n/); - let index = 1; - lines.forEach((line) => { - if (line.startsWith("Dialogue:")) { - const dialogueLine = line.substring("Dialogue:".length).trim(); - const parts = dialogueLine.split(","); - // parts[0]: Layer, parts[1]: Start, parts[2]: End, parts[3]: Style, parts[4]: Name, parts[5]: MarginL, parts[6]: MarginR, parts[7]: MarginV, parts[8]: Effect, parts[9]~: Text - if (parts.length < 10) return; - const startTime = parseASSTime(parts[1].trim()); - const endTime = parseASSTime(parts[2].trim()); - const actor = parts[4].trim(); - const text = parts.slice(9).join(",").trim(); - cues.push({ index: index++, startTime, endTime, actor, text }); - } - }); - return cues; -}; - -/** - * "H:MM:SS.cc" 形式の ASS 時刻文字列を秒数に変換する関数 - * 例: "0:00:10.52" → 10.52 秒 - */ -const parseASSTime = (timeString) => { - const parts = timeString.split(":"); - if (parts.length !== 3) return 0; - const hours = parseFloat(parts[0]); - const minutes = parseFloat(parts[1]); - const seconds = parseFloat(parts[2]); - return hours * 3600 + minutes * 60 + seconds; -}; - -/** - * "HH:MM:SS,mmm" 形式の SRT 時刻文字列を秒数に変換する関数 - */ -const parseTime = (timeString) => { - const [hms, ms] = timeString.split(","); - const [hours, minutes, seconds] = hms.split(":").map(Number); - return hours * 3600 + minutes * 60 + seconds + Number(ms) / 1000; -}; - -const secToDayTime = (seconds) => { - const day = Math.floor(seconds / 86400); - const hour = Math.floor((seconds % 86400) / 3600); - const min = Math.floor((seconds % 3600) / 60); - const sec = seconds % 60; - let time = ""; - // day が 0 の場合は「日」は出力しない(hour や min も同様) - if (day !== 0) { - time = `${day}日${hour}時間${min}分${sec}秒`; - } else if (hour !== 0) { - time = `${hour}:${min}:${sec}`; - } else if (min !== 0) { - time = `${String(min).padStart(2, "0")}:${String(sec).padStart(2, "0")}`; - } else { - time = `${String(sec).padStart(2, "0")}`; - } - return time; -}; diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.module.scss b/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.module.scss index ca0bc25d..ef618213 100644 --- a/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.module.scss +++ b/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.module.scss @@ -1,7 +1,6 @@ .container { - padding: 4rem; - background: #1e1e1e; - color: #ffffff; + padding: 2rem 4rem; + background: var(--dark_900_color); border-radius: 1rem; flex-shrink: 0; height: 100%; @@ -9,48 +8,54 @@ display: flex; flex-direction: column; gap: 2rem; +} - h1 { - font-size: 2.4rem; - margin-bottom: 1.5rem; - text-align: center; - } +.title { + font-size: 1.8rem; + text-align: center; + flex-shrink: 0; +} - label { - display: block; - font-size: 1.6rem; - margin-bottom: 0.5rem; - } +.border { + width: 100%; + height: 0.2rem; + background-color: var(--dark_800_color); + flex-shrink: 0; - input, - select { - font-size: 1.6rem; - padding: 0.5rem; - border-radius: 0.5rem; - border: 0.1rem solid #ccc; - background: #333; - color: #fff; - } +} + + // label { + // display: block; + // font-size: 1.6rem; + // margin-bottom: 0.5rem; + // } + + // input, + // select { + // font-size: 1.6rem; + // padding: 0.5rem; + // border-radius: 0.5rem; + // border: 0.1rem solid #ccc; + // background: #333; + // color: #fff; + // } - input[type="file"] { - padding: 0.5rem; - } // ボタンの基本スタイル - button { - font-size: 1.8rem; - padding: 1rem 2rem; - border: none; - border-radius: 0.5rem; - cursor: pointer; - transition: background 0.3s; - margin-right: 1rem; + // button { + // // font-size: 1.8rem; + // // padding: 1rem 2rem; + // // border: none; + // // border-radius: 0.5rem; + // // cursor: pointer; + // // // transition: background 0.3s; + // // margin-right: 1rem; - &:focus { - outline: none; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); - } - } + // &:focus { + // outline: none; + // box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); + // } + // } // 再生開始用ボタン(通常時) .primary { @@ -70,14 +75,7 @@ } } - // ファイルクリア用ボタン - .file_clear { - background: #6c757d; - color: #fff; - &:hover { - background: #5a6268; - } - } + // 「再生中」状態(クリック不可)用のスタイル .is_playing { @@ -86,41 +84,6 @@ pointer-events: none; } - // カウントダウン表示用エリア - .countdown { - margin-top: 1rem; - font-size: 2.6rem; - display: flex; - align-items: center; - gap: 1rem; - flex-direction: column; - - span { - font-weight: bold; - } - - button { - padding: 0.5rem 1rem; - font-size: 1.6rem; - border: none; - border-radius: 0.4rem; - background: #28a745; - color: #fff; - cursor: pointer; - transition: background 0.3s; - - &:hover { - background: #218838; - } - } - } - - // 再生モードや時刻指定の select 関連(横並びの場合などに調整) - .time_selects { - display: flex; - align-items: center; - gap: 0.5rem; - } // 字幕一覧のテーブル table { @@ -154,26 +117,8 @@ } } } - } - } + .subtitle_lists { font-size: 1.4rem; } - -.time_section { - display: flex; - gap: 2rem; - justify-content: center; - align-items: center; -} -.time_selects_item { - gap: 0.4rem; - width: 6rem; - text-align: center; -} - -.adjust_button_wrapper { - display: flex; - gap: 2rem; -} \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/_controllers/SubtitlesController.jsx b/src-ui/app/main_page/main_section/subtitle_system_container/_controllers/SubtitlesController.jsx new file mode 100644 index 00000000..720c3d28 --- /dev/null +++ b/src-ui/app/main_page/main_section/subtitle_system_container/_controllers/SubtitlesController.jsx @@ -0,0 +1,39 @@ +import { useSendTextToOverlay } from "@logics_configs"; +import { useSubtitles } from "../_logics/useSubtitles"; +import { secToDayTime } from "../_subtitles_utils" +import { useEffect } from "react"; +export const SubtitlesController = () => { + const { sendTextToOverlay } = useSendTextToOverlay(); + const { + currentIsSubtitlePlaying, + currentIsCuesScheduled, + updateIsCuesScheduled, + currentCountdownAdjustment, + currentEffectiveCountdown, + scheduleCues, + } = useSubtitles(); + + // currentEffectiveCountdown.data が 0 になったとき、字幕開始 + useEffect(() => { + if ( + currentIsSubtitlePlaying.data && + currentEffectiveCountdown.data !== null && + currentEffectiveCountdown.data <= 0 && + !currentIsCuesScheduled.data + ) { + sendTextToOverlay("スタート!"); + console.log("スタート!"); + // 調整後のタイミングで字幕スケジュールを開始 + scheduleCues(0); + updateIsCuesScheduled(true); + } + + if (currentEffectiveCountdown.data > 0) { + console.log(secToDayTime(currentEffectiveCountdown.data)); + sendTextToOverlay(secToDayTime(currentEffectiveCountdown.data)); + } + + }, [currentEffectiveCountdown.data, currentIsSubtitlePlaying.data, currentIsCuesScheduled.data, currentCountdownAdjustment.data]); + + return null; +}; \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/_logics/useSubtitles.jsx b/src-ui/app/main_page/main_section/subtitle_system_container/_logics/useSubtitles.jsx new file mode 100644 index 00000000..9ce1d626 --- /dev/null +++ b/src-ui/app/main_page/main_section/subtitle_system_container/_logics/useSubtitles.jsx @@ -0,0 +1,193 @@ +import { useSendTextToOverlay } from "@logics_configs"; +import { + useStore_SubtitleFileName, + useStore_IsSubtitlePlaying, + useStore_SubtitlePlaybackMode, + useStore_SubtitleAbsoluteTargetTime, + useStore_IsCuesScheduled, + useStore_CountdownAdjustment, + useStore_EffectiveCountdown, + useStore_SubtitleCues, + + useStore_SubtitleTimers, + useStore_SubtitleCountdownTimerId, +} from "@store"; + +export const useSubtitles = () => { + const { sendTextToOverlay } = useSendTextToOverlay(); + const { currentSubtitleFileName, updateSubtitleFileName } = useStore_SubtitleFileName(); + const { currentIsSubtitlePlaying, updateIsSubtitlePlaying } = useStore_IsSubtitlePlaying(); + const { currentSubtitlePlaybackMode, updateSubtitlePlaybackMode } = useStore_SubtitlePlaybackMode(); + const { currentSubtitleAbsoluteTargetTime, updateSubtitleAbsoluteTargetTime } = useStore_SubtitleAbsoluteTargetTime(); + const { currentIsCuesScheduled, updateIsCuesScheduled } = useStore_IsCuesScheduled(); + + const { currentCountdownAdjustment, updateCountdownAdjustment } = useStore_CountdownAdjustment(); + const { currentEffectiveCountdown, updateEffectiveCountdown } = useStore_EffectiveCountdown(); + const { currentSubtitleCues, updateSubtitleCues } = useStore_SubtitleCues(); + + // タイマー(setTimeout/setInterval)のID管理用 + const { currentSubtitleTimers, updateSubtitleTimers, addSubtitleTimers } = useStore_SubtitleTimers(); + // const timersRef = useRef([]); + // カウントダウンタイマー専用の ref + const { currentSubtitleCountdownTimerId, updateSubtitleCountdownTimerId, AddSubtitleCountdownTimerId } = useStore_SubtitleCountdownTimerId(); + + // cues のスケジュールを行う(字幕開始時のオフセットは調整後のタイミングに合わせる) + const scheduleCues = (offset) => { + // 字幕開始時の処理 + const startFunction = (cue) => { + let send_text = ""; + if (cue.actor !== "") { + send_text = `[${cue.actor}] ${cue.text}`; + } else { + send_text = `${cue.text}`; + } + console.log(`字幕開始 (index: ${cue.index}) send_text:${send_text}`); + sendTextToOverlay(send_text); + }; + + // 字幕終了時の処理 + const endFunction = (cue) => { + console.log(`字幕終了 (index: ${cue.index}): ${cue.text}`); + // 必要に応じた終了処理(例:テキストクリア)を実装可能 + // sendTextToOverlay(""); + }; + + currentSubtitleCues.data.forEach((cue) => { + const startDelay = cue.startTime * 1000 + offset; + const endDelay = cue.endTime * 1000 + offset; + if (startDelay >= 0) { + const timerId = setTimeout(() => startFunction(cue), startDelay); + addSubtitleTimers(timerId); + } + if (endDelay >= 0) { + const timerId = setTimeout(() => endFunction(cue), endDelay); + addSubtitleTimers(timerId); + } + }); + }; + + + // カウントダウンタイマーの開始/再登録(指定した値から1秒ごとに減らす) + const startCountdownInterval = (startValue) => { + // 既存のタイマーがあればクリア + if (currentSubtitleCountdownTimerId.data) { + clearInterval(currentSubtitleCountdownTimerId.data); + } + // 新たな開始値を設定 + updateEffectiveCountdown(startValue); + const countdown_timer_id = setInterval(() => { + updateEffectiveCountdown((prev) => { + if (prev.data <= 1) { + clearInterval(currentSubtitleCountdownTimerId.data); + return 0; + } + return prev.data - 1; + }); + }, 1000); + updateSubtitleCountdownTimerId(countdown_timer_id); + addSubtitleTimers(currentSubtitleCountdownTimerId.data); + }; + + + // 字幕一覧の表示(relative モードの場合、クリックでジャンプ) + // テーブル内の字幕行をクリック(relative モードのみ)でジャンプ + const handleJump = (jumpCue) => { + if (currentSubtitlePlaybackMode.data !== "relative") return; + handleSubtitlesStop(); + const offset = -jumpCue.startTime * 1000; + scheduleCues(offset); + updateIsSubtitlePlaying(true); + }; + + + + // 「再生開始」ボタン押下時の処理 + const handleSubtitlesStart = () => { + handleSubtitlesStop(); + updateIsSubtitlePlaying(true); + updateIsCuesScheduled(false); + const target_time = currentSubtitleAbsoluteTargetTime.data; + + let computedCountdown = 0; + if (currentSubtitlePlaybackMode.data === "absolute") { + const now = new Date(); + const hour = parseInt(target_time.hour, 10); + const minute = parseInt(target_time.minute, 10); + let targetDate = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate(), + hour, + minute, + 0, + 0 + ); + if (targetDate.getTime() < now.getTime()) { + targetDate.setDate(targetDate.getDate() + 1); + } + computedCountdown = Math.ceil((targetDate.getTime() - now.getTime()) / 1000); + } else { + computedCountdown = 10; // relative モードの場合は固定値 + } + // setInitialCountdown(computedCountdown); + // 調整値を反映した開始値 + const startValue = computedCountdown + currentCountdownAdjustment.data; + startCountdownInterval(startValue); + sendTextToOverlay(startValue.toString()); + }; + + + // すべてのタイマーを停止し、各状態を初期化する + const handleSubtitlesStop = () => { + currentSubtitleTimers.data.forEach((timerId) => { + clearTimeout(timerId); + clearInterval(timerId); + }); + + updateSubtitleTimers([]); + if (currentSubtitleCountdownTimerId.data) { + clearInterval(currentSubtitleCountdownTimerId.data); + updateSubtitleCountdownTimerId(null); + } + console.log("再生を停止しました。"); + updateIsSubtitlePlaying(false); + // setInitialCountdown(null); + updateEffectiveCountdown(null); + updateCountdownAdjustment(0); + updateIsCuesScheduled(false); + }; + + + return { + currentSubtitleFileName, + updateSubtitleFileName, + + currentIsSubtitlePlaying, + updateIsSubtitlePlaying, + + currentSubtitlePlaybackMode, + updateSubtitlePlaybackMode, + + currentSubtitleAbsoluteTargetTime, + updateSubtitleAbsoluteTargetTime, + + currentIsCuesScheduled, + updateIsCuesScheduled, + + currentCountdownAdjustment, + updateCountdownAdjustment, + + currentEffectiveCountdown, + updateEffectiveCountdown, + + currentSubtitleCues, + updateSubtitleCues, + + handleSubtitlesStart, + handleSubtitlesStop, + startCountdownInterval, + scheduleCues, + handleJump, + } + +}; \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/_subtitles_utils.js b/src-ui/app/main_page/main_section/subtitle_system_container/_subtitles_utils.js new file mode 100644 index 00000000..8bed1f7b --- /dev/null +++ b/src-ui/app/main_page/main_section/subtitle_system_container/_subtitles_utils.js @@ -0,0 +1,112 @@ + +/** + * SRT形式の文字列を解析する関数 + * 改行コードを正規化し、空行で分割して解析する + * (actor は存在しないため、空文字列をセット) + */ +export const parseSRT = (data) => { + const cues = []; + const normalizedData = data.replace(/\r\n/g, "\n").trim(); + const blocks = normalizedData.split(/\n\s*\n/); + blocks.forEach((block) => { + const lines = block.split("\n").filter((line) => line.trim() !== ""); + if (lines.length >= 3) { + const index = parseInt(lines[0], 10); + const timeMatch = lines[1].match(/([\d:,]+)\s+-->\s+([\d:,]+)/); + if (!timeMatch) return; + const start = parseTime(timeMatch[1]); + const end = parseTime(timeMatch[2]); + const text = lines.slice(2).join("\n"); + cues.push({ index, startTime: start, endTime: end, actor: "", text }); + } + }); + return cues; +}; + +/** + * ASS形式の文字列を解析する関数 + * [Events] セクション内の "Dialogue:" 行から、 + * フォーマット "Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" + * に沿って分割する。 + * ここでは Name を actor、Text を text として抽出する。 + */ +export const parseASS = (data) => { + const cues = []; + const lines = data.split(/\r?\n/); + let index = 1; + lines.forEach((line) => { + if (line.startsWith("Dialogue:")) { + const dialogueLine = line.substring("Dialogue:".length).trim(); + const parts = dialogueLine.split(","); + // parts[0]: Layer, parts[1]: Start, parts[2]: End, parts[3]: Style, parts[4]: Name, parts[5]: MarginL, parts[6]: MarginR, parts[7]: MarginV, parts[8]: Effect, parts[9]~: Text + if (parts.length < 10) return; + const startTime = parseASSTime(parts[1].trim()); + const endTime = parseASSTime(parts[2].trim()); + const actor = parts[4].trim(); + const text = parts.slice(9).join(",").trim(); + cues.push({ index: index++, startTime, endTime, actor, text }); + } + }); + return cues; +}; + +/** + * "H:MM:SS.cc" 形式の ASS 時刻文字列を秒数に変換する関数 + * 例: "0:00:10.52" → 10.52 秒 + */ +export const parseASSTime = (timeString) => { + const parts = timeString.split(":"); + if (parts.length !== 3) return 0; + const hours = parseFloat(parts[0]); + const minutes = parseFloat(parts[1]); + const seconds = parseFloat(parts[2]); + return hours * 3600 + minutes * 60 + seconds; +}; + +/** + * "HH:MM:SS,mmm" 形式の SRT 時刻文字列を秒数に変換する関数 + */ +export const parseTime = (timeString) => { + const [hms, ms] = timeString.split(","); + const [hours, minutes, seconds] = hms.split(":").map(Number); + return hours * 3600 + minutes * 60 + seconds + Number(ms) / 1000; +}; + +const padTime = (int) => { + return String(int).padStart(2, "0"); +}; + +export const secToDayTime = (seconds) => { + const day = Math.floor(seconds / 86400); + const hour = Math.floor((seconds % 86400) / 3600); + const min = Math.floor((seconds % 3600) / 60); + const sec = seconds % 60; + let time = ""; + // day が 0 の場合は「日」は出力しない(hour や min も同様) + if (day !== 0) { + time = `${day}日${hour}時間${min}分${sec}秒`; + } else if (hour !== 0) { + time = `${padTime(hour)}:${padTime(min)}:${padTime(sec)}`; + } else { + time = `${padTime(min)}:${padTime(sec)}`; + } + // } else { + // time = `${padTime(sec)}`; + // } + return time; +}; + + +// HH:MM:SS 形式に変換する補助関数 +export const formatTime = (timeInSeconds) => { + const hours = Math.floor(timeInSeconds / 3600); + const minutes = Math.floor((timeInSeconds % 3600) / 60); + const seconds = Math.floor(timeInSeconds % 60); + return ( + String(hours).padStart(2, "0") + + ":" + + String(minutes).padStart(2, "0") + + ":" + + String(seconds).padStart(2, "0") + ); +}; diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/countdown_container/CountdownContainer.jsx b/src-ui/app/main_page/main_section/subtitle_system_container/countdown_container/CountdownContainer.jsx new file mode 100644 index 00000000..419db219 --- /dev/null +++ b/src-ui/app/main_page/main_section/subtitle_system_container/countdown_container/CountdownContainer.jsx @@ -0,0 +1,72 @@ +import React, { useState, useRef, useEffect } from "react"; +import styles from "./CountdownContainer.module.scss"; +import { secToDayTime } from "../_subtitles_utils"; +import { useSubtitles } from "../_logics/useSubtitles"; + +export const CountdownContainer = () => { + const { + updateCountdownAdjustment, + currentEffectiveCountdown, + currentIsCuesScheduled, + startCountdownInterval, + } = useSubtitles(); + // カウントダウン表示:字幕開始前は常に表示 + + // if (currentEffectiveCountdown.data === 0) return null; + if (currentEffectiveCountdown.data === null && currentIsCuesScheduled.data) return null; + + return ( +
+ カウントダウン: {secToDayTime(currentEffectiveCountdown.data)} +
+ {/* 1分単位の調整ボタン */} +
+ + +
+
+ {/* 1秒単位の調整ボタン */} +
+ + +
+
+
+ ); +}; \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/countdown_container/CountdownContainer.module.scss b/src-ui/app/main_page/main_section/subtitle_system_container/countdown_container/CountdownContainer.module.scss new file mode 100644 index 00000000..0ad5a7ff --- /dev/null +++ b/src-ui/app/main_page/main_section/subtitle_system_container/countdown_container/CountdownContainer.module.scss @@ -0,0 +1,47 @@ +.container { + margin-top: 1rem; + font-size: 2.6rem; + display: flex; + align-items: center; + gap: 1rem; + flex-direction: column; + + span { + font-weight: bold; + } + +} + +.adjust_button_container { + display: flex; + gap: 10rem; +} + +.adjust_button_wrapper { + display: flex; + flex-direction: column; + gap: 4rem; +} + + + +.adjust_button { + padding: 1rem 1.4rem; + font-size: 1.8rem; + border-radius: 0.4rem; + background: var(--primary_600_color); + color: #fff; + cursor: pointer; + + &:hover { + background: var(--primary_400_color); + } + &:active { + background: var(--primary_650_color); + } +} + +.adjust_button_border { + background-color: var(--dark_600_color); + width: 0.2rem; +} \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/input_file_container/InputFileContainer.jsx b/src-ui/app/main_page/main_section/subtitle_system_container/input_file_container/InputFileContainer.jsx new file mode 100644 index 00000000..1e234765 --- /dev/null +++ b/src-ui/app/main_page/main_section/subtitle_system_container/input_file_container/InputFileContainer.jsx @@ -0,0 +1,62 @@ +import React, { useState, useRef, useEffect } from "react"; +import styles from "./InputFileContainer.module.scss"; +import { useSubtitles } from "../_logics/useSubtitles"; +import { parseSRT, parseASS } from "../_subtitles_utils"; + +export const InputFileContainer = () => { + const { + updateSubtitleFileName, + currentSubtitleFileName, + updateSubtitleCues, + handleSubtitlesStop + } = useSubtitles(); + + // ファイルアップロード時の処理 + const handleFileUpload = (event) => { + const file = event.target.files[0]; + if (!file) return; + const reader = new FileReader(); + reader.onload = (e) => { + const content = e.target.result; + let parsedCues = []; + // 拡張子により ASS と SRT を判定 + if (file.name.toLowerCase().endsWith(".ass")) { + parsedCues = parseASS(content); + } else { + parsedCues = parseSRT(content); + } + updateSubtitleCues(parsedCues); + console.log("Parsed cues:", parsedCues); + updateSubtitleFileName(file.name); + + }; + reader.readAsText(file); + }; + + + // ファイルクリア + const handleClearFile = () => { + handleSubtitlesStop(); + updateSubtitleFileName("ファイルが選択されていません"); + updateSubtitleCues([]); + }; + + return ( +
+
+ + +

{currentSubtitleFileName.data}

+
+ +
+ ); +}; \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/input_file_container/InputFileContainer.module.scss b/src-ui/app/main_page/main_section/subtitle_system_container/input_file_container/InputFileContainer.module.scss new file mode 100644 index 00000000..91966cbb --- /dev/null +++ b/src-ui/app/main_page/main_section/subtitle_system_container/input_file_container/InputFileContainer.module.scss @@ -0,0 +1,42 @@ +.container { + display: flex; + align-items: center; + justify-content: space-between; + gap: 6rem; +} + +.input_file_wrapper { + display: flex; + align-items: center; + gap: 1rem; +} +.input_file_label { + font-size: 1.4rem; + border-radius: 0.4rem; + padding: 1rem; + background-color: var(--dark_850_color); + border: 0.1rem solid var(--dark_400_color); + flex-shrink: 0; +} +.input_file_i { + display: none; +} +.file_name { + font-size: 1.6rem; + padding: 0.5rem; + width: 100%; + max-width: 60rem; +} + +.file_clear { + background: var(--dark_800_color); + color: var(--dark_200_color); + border-radius: 0.4rem; + font-size: 1.2rem; + padding: 0.6rem 1rem; + cursor: pointer; + + &:hover { + background: var(--error_bc_color); + } +} \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx b/src-ui/app/main_page/main_section/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx new file mode 100644 index 00000000..e19ddba2 --- /dev/null +++ b/src-ui/app/main_page/main_section/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx @@ -0,0 +1,74 @@ +import styles from "./ModeSelectorContainer.module.scss"; +import { useSubtitles } from "../_logics/useSubtitles"; +export const ModeSelectorContainer = () => { + const { + currentSubtitlePlaybackMode, + updateSubtitlePlaybackMode, + currentSubtitleAbsoluteTargetTime, + updateSubtitleAbsoluteTargetTime, + } = useSubtitles(); + + const target_time = currentSubtitleAbsoluteTargetTime.data; + + const handleOnchangeTargetTime = (key, value) => { + updateSubtitleAbsoluteTargetTime((old_value) => { + return { + ...old_value.data, + [key]: value, + } + }); + }; + + + return ( +
+
+ +
+ + {currentSubtitlePlaybackMode.data === "absolute" && ( +
+ +
+ + : + +
+
+ )} +
+ ); +}; \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss b/src-ui/app/main_page/main_section/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss new file mode 100644 index 00000000..a3c805d9 --- /dev/null +++ b/src-ui/app/main_page/main_section/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss @@ -0,0 +1,50 @@ +.container { + // background-color: red; + display: flex; + gap: 4rem; +} +.mode_selector_wrapper { + display: flex; + align-items: center; + gap: 2rem; +} +.absolute_time_label { + font-size: 1.4rem; +} + +.mode_selector { + font-size: 1.6rem; + padding: 0.6rem 1rem; + border-radius: 0.5rem; + border: 0.1rem solid var(--dark_400_color); + cursor: pointer; +} + +.mode_selector_item { + background-color: var(--dark_800_color); +} + + +.time_section { + display: flex; + gap: 2rem; + justify-content: center; + align-items: center; +} + +.time_selects { + display: flex; + align-items: center; + gap: 0.5rem; +} + + +.time_selects_item { + width: 6rem; + text-align: center; + padding: 0.6rem 1rem; + font-size: 1.8rem; + background-color: var(--dark_850_color); + border: 0.1rem solid var(--dark_400_color); + cursor: pointer; +} \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/play_control_container/PlayControlContainer.jsx b/src-ui/app/main_page/main_section/subtitle_system_container/play_control_container/PlayControlContainer.jsx new file mode 100644 index 00000000..0e4fdbeb --- /dev/null +++ b/src-ui/app/main_page/main_section/subtitle_system_container/play_control_container/PlayControlContainer.jsx @@ -0,0 +1,33 @@ +// import React, { useState, useRef, useEffect } from "react"; +import styles from "./PlayControlContainer.module.scss"; +import { useSubtitles } from "../_logics/useSubtitles"; +import { clsx } from "clsx"; + +export const PlayControlContainer = () => { + const { + currentIsSubtitlePlaying, + handleSubtitlesStart, + handleSubtitlesStop, + } = useSubtitles(); + + const is_playing = currentIsSubtitlePlaying.data; + + const playback_button_classname = clsx(styles.playback_button, { + [styles.is_playing]: is_playing, + }); + return ( +
+ + {is_playing && + + } +
+ ); +}; \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/play_control_container/PlayControlContainer.module.scss b/src-ui/app/main_page/main_section/subtitle_system_container/play_control_container/PlayControlContainer.module.scss new file mode 100644 index 00000000..ea5de457 --- /dev/null +++ b/src-ui/app/main_page/main_section/subtitle_system_container/play_control_container/PlayControlContainer.module.scss @@ -0,0 +1,31 @@ +.container { + display: flex; + gap: 4rem; + display: flex; + justify-content: center; +} + +.playback_button, .playback_stop_button { + font-size: 1.6rem; + padding: 1rem 2rem; + cursor: pointer; + border-radius: 0.4rem; +} + +.playback_button { + background-color: var(--primary_550_color); + &:hover { + background-color: var(--primary_400_color); + } + &.is_playing { + background-color: var(--primary_650_color); + pointer-events: none; + color: var(--dark_400_color); + } +} +.playback_stop_button { + background-color: var(--dark_800_color); + &:hover { + background-color: var(--error_bc_color); + } +} \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx b/src-ui/app/main_page/main_section/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx new file mode 100644 index 00000000..b997870e --- /dev/null +++ b/src-ui/app/main_page/main_section/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx @@ -0,0 +1,45 @@ +import React, { useState, useRef, useEffect } from "react"; +import styles from "./SubtitlesListContainer.module.scss"; +import { useSubtitles } from "../_logics/useSubtitles"; +import { formatTime } from "../_subtitles_utils"; + +export const SubtitlesListContainer = () => { + const { currentSubtitleCues, handleJump } = useSubtitles(); + + if (currentSubtitleCues.data.length < 0 ) return null; + + return ( +
+

字幕一覧

+ + + + + + + + + + + + {currentSubtitleCues.data.map((cue) => ( + handleJump(cue)} + className={styles.tableRow} + > + + + + + + + ))} + +
番号開始終了Actorテキスト
{cue.index}{formatTime(cue.startTime)}{formatTime(cue.endTime)}{cue.actor}{cue.text}
+

+ ※ 行をクリックすると、その字幕の位置にジャンプします。(相対モードのみ) +

+
+ ); +}; \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss b/src-ui/app/main_page/main_section/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss new file mode 100644 index 00000000..e69de29b diff --git a/src-ui/store.js b/src-ui/store.js index 17c74d20..dede547b 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -284,4 +284,22 @@ export const { atomInstance: Atom_IsOpenedTranslatorSelector, useHook: useStore_ export const { atomInstance: Atom_SupportersData, useHook: useStore_SupportersData } = createAtomWithHook(null, "SupportersData", {is_state_ok: true}); export const { atomInstance: Atom_VrctPosterIndex, useHook: useStore_VrctPosterIndex } = createAtomWithHook(0, "VrctPosterIndex"); -export const { atomInstance: Atom_PosterShowcaseWorldPageIndex, useHook: useStore_PosterShowcaseWorldPageIndex } = createAtomWithHook(0, "PosterShowcaseWorldPageIndex"); \ No newline at end of file +export const { atomInstance: Atom_PosterShowcaseWorldPageIndex, useHook: useStore_PosterShowcaseWorldPageIndex } = createAtomWithHook(0, "PosterShowcaseWorldPageIndex"); + +// ----------------------------------------------- +// Subtitles +// ----------------------------------------------- +export const { atomInstance: Atom_IsSubtitlePlaying, useHook: useStore_IsSubtitlePlaying } = createAtomWithHook(false, "IsSubtitlePlaying", { is_state_ok: true }); +export const { atomInstance: Atom_SubtitlePlaybackMode, useHook: useStore_SubtitlePlaybackMode } = createAtomWithHook("relative", "SubtitlePlaybackMode", { is_state_ok: true }); +export const { atomInstance: Atom_SubtitleAbsoluteTargetTime, useHook: useStore_SubtitleAbsoluteTargetTime } = createAtomWithHook({ + hour: "23", + minute: "00", +}, "SubtitleAbsoluteTargetTime", { is_state_ok: true }); +export const { atomInstance: Atom_IsCuesScheduled, useHook: useStore_IsCuesScheduled } = createAtomWithHook(false, "IsCuesScheduled", { is_state_ok: true }); +export const { atomInstance: Atom_CountdownAdjustment, useHook: useStore_CountdownAdjustment } = createAtomWithHook(0, "CountdownAdjustment", { is_state_ok: true }); +export const { atomInstance: Atom_EffectiveCountdown, useHook: useStore_EffectiveCountdown } = createAtomWithHook(null, "EffectiveCountdown", { is_state_ok: true }); +export const { atomInstance: Atom_SubtitleCues, useHook: useStore_SubtitleCues } = createAtomWithHook([], "SubtitleCues", { is_state_ok: true }); + +export const { atomInstance: Atom_SubtitleTimers, useHook: useStore_SubtitleTimers } = createAtomWithHook([], "SubtitleTimers", { is_state_ok: true }); +export const { atomInstance: Atom_SubtitleCountdownTimerId, useHook: useStore_SubtitleCountdownTimerId } = createAtomWithHook([], "SubtitleCountdownTimerId", { is_state_ok: true }); +export const { atomInstance: Atom_SubtitleFileName, useHook: useStore_SubtitleFileName } = createAtomWithHook("ファイルが選択されていません", "SubtitleFileName", { is_state_ok: true }); \ No newline at end of file From 22ada89fa68b8ea2cb09951f05b4bc7eaeee8f9c Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 5 Mar 2025 23:22:22 +0900 Subject: [PATCH 06/68] [TMP] Plugins system. --- package-lock.json | 91 ++++ package.json | 2 + src-tauri/Cargo.lock | 507 +++++++++++++++++- src-tauri/Cargo.toml | 4 +- src-tauri/plugins/plugin_examples/index.jsx | 22 + .../main_container/MainContainer.jsx | 19 + src-tauri/src/main.rs | 21 +- src-tauri/tauri.conf.json | 15 +- src-ui/app/App.jsx | 2 + .../_app_controllers/PluginsController.jsx | 17 + src-ui/app/_app_controllers/index.js | 3 +- .../setting_box/SettingBox.jsx | 3 + .../setting_section/setting_box/index.js | 1 + .../setting_box/plugins/Plugins.jsx | 60 +++ .../setting_box/plugins/Plugins.module.scss | 5 + .../sidebar_section/SidebarSection.jsx | 1 + .../main_page/main_section/MainSection.jsx | 6 +- .../app/main_page/main_section/PluginHost.jsx | 20 + src-ui/logics/configs/index.js | 2 + src-ui/logics/configs/plugins/usePlugins.js | 190 +++++++ src-ui/store.js | 6 +- 21 files changed, 990 insertions(+), 7 deletions(-) create mode 100644 src-tauri/plugins/plugin_examples/index.jsx create mode 100644 src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx create mode 100644 src-ui/app/_app_controllers/PluginsController.jsx create mode 100644 src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx create mode 100644 src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss create mode 100644 src-ui/app/main_page/main_section/PluginHost.jsx create mode 100644 src-ui/logics/configs/plugins/usePlugins.js diff --git a/package-lock.json b/package-lock.json index ebda8372..8e87cde6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "tauri-app", "version": "0.0.0", "dependencies": { + "@babel/standalone": "^7.26.9", "@emotion/react": "11.14.0", "@emotion/styled": "11.14.0", "@mui/material": "6.2.0", @@ -20,6 +21,7 @@ "jotai": "2.10.3", "js-base64": "3.7.7", "js-yaml": "4.1.0", + "jszip": "^3.10.1", "react": "18.2.0", "react-dom": "18.2.0", "react-i18next": "15.2.0", @@ -271,6 +273,14 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/standalone": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/standalone/-/standalone-7.26.9.tgz", + "integrity": "sha512-UTeQKy0kzJwWRe55kT1uK4G9H6D0lS6G4207hCU/bDaOhA5t2aC0qHN6GmID0Axv3OFLNXm27NdqcWp+BXcGtA==", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", @@ -2330,6 +2340,11 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -3424,6 +3439,11 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "node_modules/immutable": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", @@ -3933,6 +3953,17 @@ "node": ">=4.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3953,6 +3984,14 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -4364,6 +4403,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4510,6 +4554,11 @@ "node": ">= 0.8.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==" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4659,6 +4708,25 @@ "node": ">=4" } }, + "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==", + "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/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/readdirp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", @@ -4836,6 +4904,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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==" + }, "node_modules/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", @@ -4919,6 +4992,11 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5022,6 +5100,14 @@ "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", "dev": true }, + "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==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", @@ -5338,6 +5424,11 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "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", diff --git a/package.json b/package.json index 48ee7f7e..eafff304 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "release-all": "npm run release && npm run release-cuda" }, "dependencies": { + "@babel/standalone": "^7.26.9", "@emotion/react": "11.14.0", "@emotion/styled": "11.14.0", "@mui/material": "6.2.0", @@ -35,6 +36,7 @@ "jotai": "2.10.3", "js-base64": "3.7.7", "js-yaml": "4.1.0", + "jszip": "^3.10.1", "react": "18.2.0", "react-dom": "18.2.0", "react-i18next": "15.2.0", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 347908d6..861e96ec 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -6,7 +6,9 @@ version = 3 name = "VRCT" version = "0.0.0" dependencies = [ + "base64 0.22.1", "font-kit", + "reqwest", "serde", "serde_json", "tauri", @@ -218,6 +220,9 @@ name = "bytes" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +dependencies = [ + "serde", +] [[package]] name = "cairo-rs" @@ -729,7 +734,7 @@ dependencies = [ "rustc_version", "toml 0.8.19", "vswhom", - "winreg", + "winreg 0.52.0", ] [[package]] @@ -971,6 +976,12 @@ dependencies = [ "syn 2.0.94", ] +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + [[package]] name = "futures-task" version = "0.3.31" @@ -984,8 +995,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-io", "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -1297,6 +1311,25 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.7.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1361,12 +1394,86 @@ dependencies = [ "itoa 1.0.14", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + [[package]] name = "http-range" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" +[[package]] +name = "httparse" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 1.0.14", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.61" @@ -1613,6 +1720,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + [[package]] name = "itoa" version = "0.4.8" @@ -1849,6 +1962,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniz_oxide" version = "0.8.2" @@ -1859,6 +1978,34 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ndk" version = "0.6.0" @@ -1998,6 +2145,50 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.94", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -2516,6 +2707,67 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", + "winreg 0.50.0", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2544,6 +2796,37 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.19" @@ -2565,6 +2848,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -2577,6 +2869,39 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "selectors" version = "0.22.0" @@ -2659,6 +2984,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.14", + "ryu", + "serde", +] + [[package]] name = "serde_with" version = "3.12.0" @@ -2784,6 +3121,16 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "soup2" version = "0.2.1" @@ -2812,6 +3159,12 @@ dependencies = [ "system-deps 5.0.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -2881,6 +3234,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "synstructure" version = "0.13.1" @@ -2892,6 +3251,27 @@ dependencies = [ "syn 2.0.94", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "system-deps" version = "5.0.0" @@ -3000,6 +3380,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bf327e247698d3f39af8aa99401c9708384290d1f5c544bf5d251d44c2fea22" dependencies = [ "anyhow", + "bytes", "cocoa 0.24.1", "dirs-next", "dunce", @@ -3014,6 +3395,7 @@ dependencies = [ "heck 0.5.0", "http", "ignore", + "indexmap 1.9.3", "log", "objc", "once_cell", @@ -3024,6 +3406,7 @@ dependencies = [ "rand 0.8.5", "raw-window-handle", "regex", + "reqwest", "semver", "serde", "serde_json", @@ -3296,7 +3679,44 @@ checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", + "libc", + "mio", "pin-project-lite", + "socket2", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", ] [[package]] @@ -3367,6 +3787,12 @@ dependencies = [ "winnow 0.6.22", ] +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.41" @@ -3428,6 +3854,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.17.0" @@ -3446,6 +3878,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.4" @@ -3491,6 +3929,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version-compare" version = "0.0.11" @@ -3539,6 +3983,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -3576,6 +4029,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.99" @@ -3605,6 +4071,29 @@ version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webkit2gtk" version = "0.18.2" @@ -3652,6 +4141,12 @@ dependencies = [ "system-deps 6.2.2", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "webview2-com" version = "0.19.1" @@ -4058,6 +4553,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "winreg" version = "0.52.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 06160d79..21966762 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -11,11 +11,13 @@ edition = "2021" tauri-build = { version = "1", features = [] } [dependencies] -tauri = { version = "1", features = [ "window-hide", "window-set-focus", "global-shortcut-all", "window-set-size", "window-set-position", "window-unmaximize", "window-close", "window-maximize", "window-minimize", "window-unminimize", "window-start-dragging", "window-set-decorations", "window-set-always-on-top", "shell-sidecar", "shell-open", "devtools"] } +tauri = { version = "1", features = [ "fs-read-file", "fs-create-dir", "fs-write-file", "fs-exists", "http-request", "fs-read-dir", "window-hide", "window-set-focus", "global-shortcut-all", "window-set-size", "window-set-position", "window-unmaximize", "window-close", "window-maximize", "window-minimize", "window-unminimize", "window-start-dragging", "window-set-decorations", "window-set-always-on-top", "shell-sidecar", "shell-open", "devtools"] } serde = { version = "1", features = ["derive"] } serde_json = "1" font-kit = "0.14.2" window-shadows = { git = "https://github.com/tauri-apps/window-shadows.git" } +reqwest = { version = "0.11", features = ["json", "rustls-tls"] } +base64 = "0.22.1" [features] diff --git a/src-tauri/plugins/plugin_examples/index.jsx b/src-tauri/plugins/plugin_examples/index.jsx new file mode 100644 index 00000000..a2b966b7 --- /dev/null +++ b/src-tauri/plugins/plugin_examples/index.jsx @@ -0,0 +1,22 @@ +import React from "react"; + +import { MainContainer } from "./main_container/MainContainer"; + +export const init = (plugin_context) => { + const { useHook: useStore_CountPluginState } = plugin_context.createAtomWithHook({ count: 6 }, "CountPluginState"); + + const EntryComponents = () => { + + return ( + + ); + + }; + + // UI の"main_section"拡張ポイントにコンポーネントを登録 + plugin_context.registerComponent({ + plugin_id: "dev_vrct_plugin_example_1", + location: "main_section", + component: EntryComponents, + }); +}; \ No newline at end of file diff --git a/src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx b/src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx new file mode 100644 index 00000000..91dcdf90 --- /dev/null +++ b/src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx @@ -0,0 +1,19 @@ + + +export const MainContainer = ({useStore_CountPluginState}) => { + const { updateCountPluginState, currentCountPluginState } = useStore_CountPluginState(); + const incrementCount = () => { + updateCountPluginState((prev_value) => { + return { count: prev_value.data.count + 1 } + }); + }; + + return ( +
+

Dev Plugin Count: {currentCountPluginState?.data?.count}

+ +
+ ); +}; \ No newline at end of file diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 163cc78a..36e07028 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -22,7 +22,7 @@ fn main() { event.window().set_size(tauri::Size::Physical(*new_inner_size)).unwrap(); } }) - .invoke_handler(tauri::generate_handler![get_font_list]) + .invoke_handler(tauri::generate_handler![get_font_list, download_zip_asset]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } @@ -45,4 +45,23 @@ async fn get_font_list() -> Vec { } font_families.into_iter().collect() +} + +#[tauri::command] +async fn download_zip_asset(url: String) -> Result { + use reqwest; + // reqwest のクライアントを作成 + let client = reqwest::Client::new(); + // GET リクエストを送信(リダイレクトも自動追従します) + let resp = client.get(&url) + .header("Accept", "application/octet-stream") + .send() + .await.map_err(|e| format!("Request error: {}", e))?; + if !resp.status().is_success() { + return Err(format!("HTTP error: {}", resp.status())); + } + // レスポンスのバイナリデータを取得 + let bytes = resp.bytes().await.map_err(|e| format!("Reading bytes error: {}", e))?; + // バイナリデータを base64 エンコードして返す + Ok(base64::encode(&bytes)) } \ No newline at end of file diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 9b869f1a..6e6fca0c 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -30,6 +30,18 @@ "globalShortcut": { "all": true }, + "fs": { + "readDir": true, + "readFile": true, + "exists": true, + "writeFile": true, + "createDir": true, + "scope": ["$RESOURCE/**", "**/src-tauri/target/debug/plugins/**"] + }, + "http": { + "request": true, + "scope": ["https://api.github.com/repos/**", "https://github.com/**"] + }, "shell": { "all": false, "open": true, @@ -74,7 +86,8 @@ "bin/VRCT-sidecar" ], "resources":{ - "bin/_internal": "_internal" + "bin/_internal": "_internal", + "plugins": "plugins" }, "windows": { "nsis": { diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index 03fa2f8c..fe4f5745 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -9,6 +9,7 @@ import { UiSizeController, FontFamilyController, TransparencyController, + PluginsController, } from "./_app_controllers/index.js"; import { WindowTitleBar } from "./window_title_bar/WindowTitleBar"; @@ -40,6 +41,7 @@ export const App = () => { + {(currentIsBackendReady.data === false || currentIsVrctAvailable.data === false) ? diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx new file mode 100644 index 00000000..6ccc231b --- /dev/null +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -0,0 +1,17 @@ +import { useEffect } from "react"; +import { usePlugins } from "@logics_configs"; + +// ホスト側でReactやjotaiをグローバル変数として提供 +import ReactModule from "react"; +if (typeof window !== "undefined") { + window.React = ReactModule; +} + +export const PluginsController = () => { + const { loadAllPlugins } = usePlugins(); + useEffect(() => { + loadAllPlugins(); + }, []); + + return null; +}; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/index.js b/src-ui/app/_app_controllers/index.js index 547c78f4..d71c8b6e 100644 --- a/src-ui/app/_app_controllers/index.js +++ b/src-ui/app/_app_controllers/index.js @@ -5,4 +5,5 @@ export { UiLanguageController } from "./UiLanguageController"; export { ConfigPageCloseTriggerController } from "./ConfigPageCloseTriggerController"; export { UiSizeController } from "./UiSizeController"; export { FontFamilyController } from "./FontFamilyController"; -export { TransparencyController } from "./TransparencyController"; \ No newline at end of file +export { TransparencyController } from "./TransparencyController"; +export { PluginsController } from "./PluginsController"; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/SettingBox.jsx b/src-ui/app/config_page/setting_section/setting_box/SettingBox.jsx index e61067a7..b149da0d 100644 --- a/src-ui/app/config_page/setting_section/setting_box/SettingBox.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/SettingBox.jsx @@ -9,6 +9,7 @@ import { AdvancedSettings, Vr, Hotkeys, + Plugins, Supporters, AboutVrct, } from "@setting_box"; @@ -32,6 +33,8 @@ export const SettingBox = () => { return ; case "advanced_settings": return ; + case "plugins": + return ; case "supporters": return ; case "about_vrct": diff --git a/src-ui/app/config_page/setting_section/setting_box/index.js b/src-ui/app/config_page/setting_section/setting_box/index.js index dc5798c3..abb876ae 100644 --- a/src-ui/app/config_page/setting_section/setting_box/index.js +++ b/src-ui/app/config_page/setting_section/setting_box/index.js @@ -6,5 +6,6 @@ export { Others, VrcMicMuteSyncContainer } from "./others/Others"; export { AdvancedSettings } from "./advanced_settings/AdvancedSettings"; export { Vr } from "./vr/Vr"; export { Hotkeys } from "./hotkeys/Hotkeys"; +export { Plugins } from "./plugins/Plugins"; export { AboutVrct } from "./about_vrct/AboutVrct"; export { Supporters } from "./supporters/Supporters"; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx new file mode 100644 index 00000000..1954b48d --- /dev/null +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -0,0 +1,60 @@ +import React, { useState, useEffect } from "react"; +import { usePlugins } from "@logics_configs"; +import styles from "./Plugins.module.scss"; + +export const Plugins = () => { + return ( +
+ +
+ ); +}; + +const PluginDownloadContainer = () => { + const [plugin_list, set_plugin_list] = useState([]); + const [download_progress, set_download_progress] = useState({}); + + const { downloadAndExtractPlugin } = usePlugins(); + + useEffect(() => { + // GitHub上のJSONファイルからプラグインリストを取得 + const fetchPluginList = async () => { + try { + const response = await fetch( + "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json" + ); + if (!response.ok) { + throw new Error("Failed to fetch plugin list"); + } + const data = await response.json(); + set_plugin_list(data); + } catch (error) { + console.error("Error fetching plugin list:", error); + } + }; + fetchPluginList(); + }, []); + + const handleDownload = async (plugin) => { + await downloadAndExtractPlugin(plugin); + }; + + return ( +
+ {plugin_list.map((plugin) => ( +
+

{plugin.title}

+ + {download_progress[plugin.plugin_id] !== undefined && ( +
+ Download Progress: {download_progress[plugin.plugin_id].toFixed(0)}% +
+ )} +
+ ))} +
+ ); +}; + diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss new file mode 100644 index 00000000..a49fed11 --- /dev/null +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss @@ -0,0 +1,5 @@ +.container { + display: flex; + gap: 6.4rem; + flex-direction: column; +} \ No newline at end of file diff --git a/src-ui/app/config_page/sidebar_section/SidebarSection.jsx b/src-ui/app/config_page/sidebar_section/SidebarSection.jsx index 6938f97f..5339de0d 100644 --- a/src-ui/app/config_page/sidebar_section/SidebarSection.jsx +++ b/src-ui/app/config_page/sidebar_section/SidebarSection.jsx @@ -12,6 +12,7 @@ export const SidebarSection = () => { +
diff --git a/src-ui/app/main_page/main_section/MainSection.jsx b/src-ui/app/main_page/main_section/MainSection.jsx index a918f1dd..2dec160c 100644 --- a/src-ui/app/main_page/main_section/MainSection.jsx +++ b/src-ui/app/main_page/main_section/MainSection.jsx @@ -9,12 +9,16 @@ import { useStore_IsOpenedLanguageSelector } from "@store"; import { useLanguageSettings } from "@logics_main"; import { useEffect } from "react"; import { SubtitleSystemContainer } from "./subtitle_system_container/SubtitleSystemContainer"; + +import { PluginHost } from "./PluginHost"; + export const MainSection = () => { return (
- + {/* */} + {/* */}
diff --git a/src-ui/app/main_page/main_section/PluginHost.jsx b/src-ui/app/main_page/main_section/PluginHost.jsx new file mode 100644 index 00000000..8fc162ff --- /dev/null +++ b/src-ui/app/main_page/main_section/PluginHost.jsx @@ -0,0 +1,20 @@ +// PluginHost.jsx +import React from "react"; +import { useStore_LoadedPluginsList } from "@store"; + +// export const PluginHost = ({ location }) => { +export const PluginHost = () => { + const { currentLoadedPluginsList } = useStore_LoadedPluginsList(); + console.log(currentLoadedPluginsList.data); + + return ( +
+ {currentLoadedPluginsList.data + .filter((plugin) => plugin.location === "main_section") + .map((plugin, index) => { + const PluginComponent = plugin.component; + return PluginComponent ? : null; + })} +
+ ); +}; \ No newline at end of file diff --git a/src-ui/logics/configs/index.js b/src-ui/logics/configs/index.js index 22439030..85d7c553 100644 --- a/src-ui/logics/configs/index.js +++ b/src-ui/logics/configs/index.js @@ -60,6 +60,8 @@ export { useOscPort } from "./advanced_settings/useOscPort"; export { useSupporters } from "./supporters/useSupporters"; +export { usePlugins } from "./plugins/usePlugins"; + export { useSettingBoxScrollPosition } from "./useSettingBoxScrollPosition"; export { useSoftwareVersion } from "./useSoftwareVersion"; \ No newline at end of file diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js new file mode 100644 index 00000000..41832944 --- /dev/null +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -0,0 +1,190 @@ + import { invoke } from '@tauri-apps/api/tauri'; + import { createAtomWithHook, useStore_LoadedPluginsList } from "@store"; + import { transform } from "@babel/standalone"; + import { writeFile, createDir, exists, readDir, BaseDirectory, readTextFile } from "@tauri-apps/api/fs"; + const dev_plugin_mapping = import.meta.glob("/src-tauri/plugins/**/index.jsx", { eager: true }); + import JSZip from "jszip"; + import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; + + export const usePlugins = () => { + const { updateLoadedPluginsList } = useStore_LoadedPluginsList(); + + const plugin_context = { + registerComponent: ({ plugin_id, location, component }) => { + if (!plugin_id || !location || !component) { + return console.error("An invalid plugin was detected.", plugin_id, location, component); + } + updateLoadedPluginsList((prev) => { + const filtered = prev.data.filter(item => item.plugin_id !== plugin_id); + return [...filtered, { plugin_id, location, component }]; + }); + }, + createAtomWithHook: (...args) => createAtomWithHook(...args) + }; + + const asyncLoadPlugin = async (plugin_relative_path) => { + plugin_relative_path = "plugins/" + plugin_relative_path; + console.log("plugin_relative_path",plugin_relative_path); + + try { + const plugin_code = await readTextFile(plugin_relative_path, { dir: BaseDirectory.Resource, recursive: true }); + const cleanedCode = removeImportStatements(plugin_code); + const transpiled_code = transform(cleanedCode, { + presets: [ + ["env", { modules: false }], + "react" + ], + sourceType: "module" + }).code; + const blob = new Blob([transpiled_code], { type: "text/javascript" }); + const blob_url = URL.createObjectURL(blob); + const plugin_module = await import(/* @vite-ignore */ blob_url); + URL.revokeObjectURL(blob_url); + + if (plugin_module && plugin_module.init) { + plugin_module.init(plugin_context); + } + } catch (error) { + console.error("Failed to load plugin from", plugin_relative_path, error); + } + }; + + const loadAllPlugins = async () => { + if (import.meta.env.DEV) { + // ホットリロード対応 src-tauri以下にあるpluginsディレクトリから直接読み込み(開発用) + Object.entries(dev_plugin_mapping).forEach(([key, plugin_module]) => { + console.log(plugin_module); + if (plugin_module && plugin_module.init) { + plugin_module.init(plugin_context); + } + }); + } else { + try { + const plugin_files = await readDir("plugins", { dir: BaseDirectory.Resource, recursive: true }); + for (const target_dir of plugin_files) { + console.log(target_dir); + + const target_path = target_dir.name + "\\index.jsx"; + await asyncLoadPlugin(target_path, plugin_context); + } + } catch (error) { + console.error("Error loading plugins:", error); + } + } + }; + + const downloadAndExtractPlugin = async (plugin) => { + try { + const plugin_zip_url = await fetchLatestPluginZipUrl(plugin); + console.log("Latest plugin zip URL:", plugin_zip_url); + + // Rust コマンド経由で zip をダウンロード + const base64Zip = await invoke("download_zip_asset", { url: plugin_zip_url }); + // base64Zip は文字列なので、デコードして Uint8Array に変換 + const binaryString = atob(base64Zip); + const len = binaryString.length; + const bytes = new Uint8Array(len); + for (let i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + // JSZip で zip を解凍 + const zip = await JSZip.loadAsync(bytes); + + // const plugin_dir_exists = await exists("plugins", { dir: BaseDirectory.Resource, recursive: true }); + await createDir("plugins/" + plugin.asset_name.replace(".zip", ""), { dir: BaseDirectory.Resource, recursive: true }); + // if (!plugin_dir_exists) { + // } + const target_plugin_path = "plugins/" + plugin.asset_name.replace(".zip", ""); + + + const filePromises = []; + zip.forEach((relativePath, zipEntry) => { + // .git 以下のファイルはスキップ + if (relativePath.startsWith(".git") || relativePath.includes("/.git/")) { + // console.log("Skipping .git file: " + relativePath); + return; + } + + + const filePath = target_plugin_path + "/" + relativePath; + + if (zipEntry.dir) { + // フォルダの場合、ディレクトリを作成 + filePromises.push( + createDir(filePath, { dir: BaseDirectory.Resource, recursive: true }).catch((err) => { + console.log(err); + + if (!err.message?.includes("already exists")) { + console.error("Failed to create directory:", filePath, err); + } + }) + ); + } else { + // ファイルの場合、ディレクトリを作成してから書き込む + const dirPath = filePath.substring(0, filePath.lastIndexOf("/")); // 親ディレクトリのパス + + const promise = createDir(dirPath, { dir: BaseDirectory.Resource, recursive: true }) + .catch((err) => { + if (!err.message?.includes("already exists")) { + console.error("Failed to create parent directory:", dirPath, err); + } + }) + .then(() => zipEntry.async("text")) + .then(async (fileData) => { + await writeFile(filePath, fileData, { dir: BaseDirectory.Resource, recursive: true }); + }); + + filePromises.push(promise); + } + }); + + await Promise.all(filePromises); + console.log("Plugin downloaded successfully."); + + const index_file_relative_path = plugin.asset_name.replace(".zip", "") + "/" + "index.jsx" + console.log("index_file_relative_path", index_file_relative_path); + + await asyncLoadPlugin(index_file_relative_path); + + console.log("Plugin loaded successfully."); + } catch (error) { + console.error("Error downloading and extracting plugin:", error); + } + }; + + // JSON内のURLから GitHub API を使って最新リリース情報を取得し、 + // assets 配列から plugin.asset_name に一致するアセットの browser_download_url を返す + const fetchLatestPluginZipUrl = async (plugin) => { + const api_url = plugin.url; + const response = await tauriFetch(api_url, { + method: "GET", + responseType: ResponseType.Json, + headers: { + "Accept": "application/vnd.github+json", + "User-Agent": "VRCTPluginApp" + } + }); + if (response.status !== 200) { + throw new Error("Failed to fetch latest release info, status: " + response.status); + } + const release_info = response.data; + const asset = release_info.assets.find((a) => a.name === plugin.asset_name); + if (!asset) { + throw new Error(`Asset ${plugin.asset_name} not found in the latest release`); + } + return asset.browser_download_url; + }; + + return { + loadAllPlugins, + downloadAndExtractPlugin, + }; +}; + +const removeImportStatements = (code) => { + return code + .split("\n") + .filter(line => !line.match(/^import\s+.*['"]react['"]/)) + .join("\n"); +}; diff --git a/src-ui/store.js b/src-ui/store.js index 9a2967c4..da4c1f45 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -34,7 +34,7 @@ const generatePropertyNames = (base_name) => ({ }); -const createAtomWithHook = (initialValue, base_name, options) => { +export const createAtomWithHook = (initialValue, base_name, options) => { const property_names = generatePropertyNames(base_name); const atomInstance = atom({ state: (options?.is_state_ok) ? "ok" : "pending", @@ -274,6 +274,10 @@ export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createA toggle_transcription_receive: null, }, "Hotkeys"); +// Plugins +export const { atomInstance: Atom_InstalledPluginsPath, useHook: useStore_InstalledPluginsPath } = createAtomWithHook([], "InstalledPluginsPath"); +export const { atomInstance: Atom_LoadedPluginsList, useHook: useStore_LoadedPluginsList } = createAtomWithHook([], "LoadedPluginsList"); + // Advanced Settings export const { atomInstance: Atom_OscIpAddress, useHook: useStore_OscIpAddress } = createAtomWithHook("127.0.0.1", "OscIpAddress"); export const { atomInstance: Atom_OscPort, useHook: useStore_OscPort } = createAtomWithHook("9000", "OscPort"); From 48c6e7d69f5d54c79336166b14556b79180464b8 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sat, 8 Mar 2025 18:43:56 +0900 Subject: [PATCH 07/68] [TMP 2] Plugins system. support directory structure and store system.(the plugin needs build at each project) --- src-tauri/plugins/plugin_examples/index.jsx | 17 +++---- .../main_container/MainContainer.jsx | 17 ++++--- .../plugins/plugin_examples/store/store.js | 22 ++++++++++ .../setting_box/plugins/Plugins.jsx | 1 + src-ui/logics/configs/plugins/usePlugins.js | 44 +++++++++---------- 5 files changed, 59 insertions(+), 42 deletions(-) create mode 100644 src-tauri/plugins/plugin_examples/store/store.js diff --git a/src-tauri/plugins/plugin_examples/index.jsx b/src-tauri/plugins/plugin_examples/index.jsx index a2b966b7..80f3befa 100644 --- a/src-tauri/plugins/plugin_examples/index.jsx +++ b/src-tauri/plugins/plugin_examples/index.jsx @@ -1,22 +1,19 @@ import React from "react"; - +import { initStore } from "./store/store"; import { MainContainer } from "./main_container/MainContainer"; export const init = (plugin_context) => { - const { useHook: useStore_CountPluginState } = plugin_context.createAtomWithHook({ count: 6 }, "CountPluginState"); + initStore(plugin_context.createAtomWithHook); const EntryComponents = () => { - - return ( - - ); - + return ; }; - // UI の"main_section"拡張ポイントにコンポーネントを登録 plugin_context.registerComponent({ - plugin_id: "dev_vrct_plugin_example_1", + plugin_id: "plugin_example_1_my_plugin", location: "main_section", component: EntryComponents, }); -}; \ No newline at end of file +}; + +export default init; \ No newline at end of file diff --git a/src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx b/src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx index 91dcdf90..3a56e747 100644 --- a/src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx +++ b/src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx @@ -1,19 +1,18 @@ +import { useStore } from "../store/store"; +export const MainContainer = () => { + const { updateCountPluginState, currentCountPluginState } = useStore("useStore_CountPluginState"); -export const MainContainer = ({useStore_CountPluginState}) => { - const { updateCountPluginState, currentCountPluginState } = useStore_CountPluginState(); const incrementCount = () => { - updateCountPluginState((prev_value) => { - return { count: prev_value.data.count + 1 } - }); + updateCountPluginState((prev_value) => ({ + count: prev_value.data.count + 1, + })); }; return (
-

Dev Plugin Count: {currentCountPluginState?.data?.count}

- +

1 Zipped Dev Plugin Count: {currentCountPluginState?.data?.count}

+
); }; \ No newline at end of file diff --git a/src-tauri/plugins/plugin_examples/store/store.js b/src-tauri/plugins/plugin_examples/store/store.js new file mode 100644 index 00000000..9efd9dcf --- /dev/null +++ b/src-tauri/plugins/plugin_examples/store/store.js @@ -0,0 +1,22 @@ +const store_hooks = {}; + +export const initStore = (createAtomWithHook) => { + Object.assign(store_hooks, { + useStore_CountPluginState: createAtomWithHook( + { count: 10 }, + "CountPluginState" + ).useHook, + + useStore_AnotherState: createAtomWithHook( + { value: "initial" }, + "AnotherState" + ).useHook, + }); +}; + +export const useStore = (hook_name) => { + if (!store_hooks[hook_name]) { + throw new Error(`Hook ${hook_name} is not initialized.`); + } + return store_hooks[hook_name](); +}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index 1954b48d..954e4b33 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -44,6 +44,7 @@ const PluginDownloadContainer = () => { {plugin_list.map((plugin) => (

{plugin.title}

+

{plugin.plugin_id}

diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 41832944..4120cd5d 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -1,12 +1,21 @@ - import { invoke } from '@tauri-apps/api/tauri'; - import { createAtomWithHook, useStore_LoadedPluginsList } from "@store"; - import { transform } from "@babel/standalone"; - import { writeFile, createDir, exists, readDir, BaseDirectory, readTextFile } from "@tauri-apps/api/fs"; - const dev_plugin_mapping = import.meta.glob("/src-tauri/plugins/**/index.jsx", { eager: true }); - import JSZip from "jszip"; - import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; +import { invoke } from '@tauri-apps/api/tauri'; +import { createAtomWithHook, useStore_LoadedPluginsList } from "@store"; +import { transform } from "@babel/standalone"; +import { writeFile, createDir, exists, readDir, BaseDirectory, readTextFile } from "@tauri-apps/api/fs"; +const dev_plugin_mapping = import.meta.glob("/src-tauri/plugins/**/index.jsx", { eager: true }); +import JSZip from "jszip"; +import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; - export const usePlugins = () => { +import React from "react"; +// import ReactDOM from "react-dom/client"; + +// グローバルに公開 +window.React = React; +// window.ReactDOM = ReactDOM; +window.React$1 = React; +window.we = React; + +export const usePlugins = () => { const { updateLoadedPluginsList } = useStore_LoadedPluginsList(); const plugin_context = { @@ -24,7 +33,6 @@ const asyncLoadPlugin = async (plugin_relative_path) => { plugin_relative_path = "plugins/" + plugin_relative_path; - console.log("plugin_relative_path",plugin_relative_path); try { const plugin_code = await readTextFile(plugin_relative_path, { dir: BaseDirectory.Resource, recursive: true }); @@ -32,10 +40,11 @@ const transpiled_code = transform(cleanedCode, { presets: [ ["env", { modules: false }], - "react" + "react", ], sourceType: "module" }).code; + const blob = new Blob([transpiled_code], { type: "text/javascript" }); const blob_url = URL.createObjectURL(blob); const plugin_module = await import(/* @vite-ignore */ blob_url); @@ -53,7 +62,6 @@ if (import.meta.env.DEV) { // ホットリロード対応 src-tauri以下にあるpluginsディレクトリから直接読み込み(開発用) Object.entries(dev_plugin_mapping).forEach(([key, plugin_module]) => { - console.log(plugin_module); if (plugin_module && plugin_module.init) { plugin_module.init(plugin_context); } @@ -62,9 +70,7 @@ try { const plugin_files = await readDir("plugins", { dir: BaseDirectory.Resource, recursive: true }); for (const target_dir of plugin_files) { - console.log(target_dir); - - const target_path = target_dir.name + "\\index.jsx"; + const target_path = target_dir.name + "/index.es.js"; await asyncLoadPlugin(target_path, plugin_context); } } catch (error) { @@ -76,8 +82,6 @@ const downloadAndExtractPlugin = async (plugin) => { try { const plugin_zip_url = await fetchLatestPluginZipUrl(plugin); - console.log("Latest plugin zip URL:", plugin_zip_url); - // Rust コマンド経由で zip をダウンロード const base64Zip = await invoke("download_zip_asset", { url: plugin_zip_url }); // base64Zip は文字列なので、デコードして Uint8Array に変換 @@ -102,19 +106,15 @@ zip.forEach((relativePath, zipEntry) => { // .git 以下のファイルはスキップ if (relativePath.startsWith(".git") || relativePath.includes("/.git/")) { - // console.log("Skipping .git file: " + relativePath); return; } - const filePath = target_plugin_path + "/" + relativePath; if (zipEntry.dir) { // フォルダの場合、ディレクトリを作成 filePromises.push( createDir(filePath, { dir: BaseDirectory.Resource, recursive: true }).catch((err) => { - console.log(err); - if (!err.message?.includes("already exists")) { console.error("Failed to create directory:", filePath, err); } @@ -142,9 +142,7 @@ await Promise.all(filePromises); console.log("Plugin downloaded successfully."); - const index_file_relative_path = plugin.asset_name.replace(".zip", "") + "/" + "index.jsx" - console.log("index_file_relative_path", index_file_relative_path); - + const index_file_relative_path = plugin.asset_name.replace(".zip", "") + "/" + "index.es.js" await asyncLoadPlugin(index_file_relative_path); console.log("Plugin loaded successfully."); From 77795192a0eba99f1b63f273914cdb2c8475fdb5 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Tue, 11 Mar 2025 01:03:18 +0900 Subject: [PATCH 08/68] [Update] Change download and load plugins structure.(change the plugins' build method vite to webpack. load esm.js ) --- package-lock.json | 425 ++++++++++-------- package.json | 9 +- src-tauri/Cargo.toml | 2 +- src-tauri/plugins/plugin_examples/index.jsx | 1 - .../main_container/MainContainer.jsx | 4 + src-tauri/tauri.conf.json | 3 +- .../_app_controllers/PluginsController.jsx | 7 +- .../setting_box/plugins/Plugins.jsx | 143 +++++- src-ui/logics/configs/plugins/usePlugins.js | 107 ++--- vite.config.js | 5 + 10 files changed, 428 insertions(+), 278 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8e87cde6..6cb00da1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,13 +25,14 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-i18next": "15.2.0", - "react-resizable-layout": "0.7.2" + "react-resizable-layout": "0.7.2", + "semver": "^7.7.1" }, "devDependencies": { "@tauri-apps/cli": "1.6.3", "npm-run-all": "4.1.5", "sass": "1.79.4", - "vite": "6.0.3", + "vite": "^6.2.1", "vite-plugin-svgr": "4.3.0" } }, @@ -457,9 +458,9 @@ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", - "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", "cpu": [ "ppc64" ], @@ -472,9 +473,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", - "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", "cpu": [ "arm" ], @@ -487,9 +488,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", - "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", "cpu": [ "arm64" ], @@ -502,9 +503,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", - "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", "cpu": [ "x64" ], @@ -517,9 +518,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", - "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", "cpu": [ "arm64" ], @@ -532,9 +533,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", - "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", "cpu": [ "x64" ], @@ -547,9 +548,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", - "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", "cpu": [ "arm64" ], @@ -562,9 +563,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", - "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", "cpu": [ "x64" ], @@ -577,9 +578,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", - "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", "cpu": [ "arm" ], @@ -592,9 +593,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", - "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", "cpu": [ "arm64" ], @@ -607,9 +608,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", - "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", "cpu": [ "ia32" ], @@ -622,9 +623,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", - "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", "cpu": [ "loong64" ], @@ -637,9 +638,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", - "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", "cpu": [ "mips64el" ], @@ -652,9 +653,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", - "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", "cpu": [ "ppc64" ], @@ -667,9 +668,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", - "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", "cpu": [ "riscv64" ], @@ -682,9 +683,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", - "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", "cpu": [ "s390x" ], @@ -697,9 +698,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", - "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", "cpu": [ "x64" ], @@ -711,10 +712,25 @@ "node": ">=18" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", - "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", "cpu": [ "x64" ], @@ -727,9 +743,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", - "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", "cpu": [ "arm64" ], @@ -742,9 +758,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", - "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", "cpu": [ "x64" ], @@ -757,9 +773,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", - "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", "cpu": [ "x64" ], @@ -772,9 +788,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", - "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", "cpu": [ "arm64" ], @@ -787,9 +803,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", - "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", "cpu": [ "ia32" ], @@ -802,9 +818,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", - "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", "cpu": [ "x64" ], @@ -1218,9 +1234,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", - "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz", + "integrity": "sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==", "cpu": [ "arm" ], @@ -1230,9 +1246,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", - "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.35.0.tgz", + "integrity": "sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==", "cpu": [ "arm64" ], @@ -1242,9 +1258,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", - "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.35.0.tgz", + "integrity": "sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==", "cpu": [ "arm64" ], @@ -1254,9 +1270,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", - "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.35.0.tgz", + "integrity": "sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==", "cpu": [ "x64" ], @@ -1265,10 +1281,34 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.35.0.tgz", + "integrity": "sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.35.0.tgz", + "integrity": "sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", - "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.35.0.tgz", + "integrity": "sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==", "cpu": [ "arm" ], @@ -1278,9 +1318,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", - "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.35.0.tgz", + "integrity": "sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==", "cpu": [ "arm" ], @@ -1290,9 +1330,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", - "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.35.0.tgz", + "integrity": "sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==", "cpu": [ "arm64" ], @@ -1302,9 +1342,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", - "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.35.0.tgz", + "integrity": "sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==", "cpu": [ "arm64" ], @@ -1313,10 +1353,22 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.35.0.tgz", + "integrity": "sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", - "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.35.0.tgz", + "integrity": "sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==", "cpu": [ "ppc64" ], @@ -1326,9 +1378,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", - "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.35.0.tgz", + "integrity": "sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==", "cpu": [ "riscv64" ], @@ -1338,9 +1390,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", - "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.35.0.tgz", + "integrity": "sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==", "cpu": [ "s390x" ], @@ -1350,9 +1402,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", - "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.35.0.tgz", + "integrity": "sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==", "cpu": [ "x64" ], @@ -1362,9 +1414,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", - "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.35.0.tgz", + "integrity": "sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==", "cpu": [ "x64" ], @@ -1374,9 +1426,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", - "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.35.0.tgz", + "integrity": "sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==", "cpu": [ "arm64" ], @@ -1386,9 +1438,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", - "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.35.0.tgz", + "integrity": "sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==", "cpu": [ "ia32" ], @@ -1398,9 +1450,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", - "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.35.0.tgz", + "integrity": "sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==", "cpu": [ "x64" ], @@ -2704,9 +2756,9 @@ } }, "node_modules/esbuild": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", - "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -2715,30 +2767,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.0", - "@esbuild/android-arm": "0.24.0", - "@esbuild/android-arm64": "0.24.0", - "@esbuild/android-x64": "0.24.0", - "@esbuild/darwin-arm64": "0.24.0", - "@esbuild/darwin-x64": "0.24.0", - "@esbuild/freebsd-arm64": "0.24.0", - "@esbuild/freebsd-x64": "0.24.0", - "@esbuild/linux-arm": "0.24.0", - "@esbuild/linux-arm64": "0.24.0", - "@esbuild/linux-ia32": "0.24.0", - "@esbuild/linux-loong64": "0.24.0", - "@esbuild/linux-mips64el": "0.24.0", - "@esbuild/linux-ppc64": "0.24.0", - "@esbuild/linux-riscv64": "0.24.0", - "@esbuild/linux-s390x": "0.24.0", - "@esbuild/linux-x64": "0.24.0", - "@esbuild/netbsd-x64": "0.24.0", - "@esbuild/openbsd-arm64": "0.24.0", - "@esbuild/openbsd-x64": "0.24.0", - "@esbuild/sunos-x64": "0.24.0", - "@esbuild/win32-arm64": "0.24.0", - "@esbuild/win32-ia32": "0.24.0", - "@esbuild/win32-x64": "0.24.0" + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" } }, "node_modules/escalade": { @@ -4106,9 +4159,9 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.9.tgz", + "integrity": "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==", "funding": [ { "type": "github", @@ -4520,9 +4573,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "funding": [ { "type": "opencollective", @@ -4538,7 +4591,7 @@ } ], "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -4832,9 +4885,9 @@ } }, "node_modules/rollup": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", - "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.35.0.tgz", + "integrity": "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg==", "dependencies": { "@types/estree": "1.0.6" }, @@ -4846,22 +4899,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.0", - "@rollup/rollup-android-arm64": "4.24.0", - "@rollup/rollup-darwin-arm64": "4.24.0", - "@rollup/rollup-darwin-x64": "4.24.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", - "@rollup/rollup-linux-arm-musleabihf": "4.24.0", - "@rollup/rollup-linux-arm64-gnu": "4.24.0", - "@rollup/rollup-linux-arm64-musl": "4.24.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", - "@rollup/rollup-linux-riscv64-gnu": "4.24.0", - "@rollup/rollup-linux-s390x-gnu": "4.24.0", - "@rollup/rollup-linux-x64-gnu": "4.24.0", - "@rollup/rollup-linux-x64-musl": "4.24.0", - "@rollup/rollup-win32-arm64-msvc": "4.24.0", - "@rollup/rollup-win32-ia32-msvc": "4.24.0", - "@rollup/rollup-win32-x64-msvc": "4.24.0", + "@rollup/rollup-android-arm-eabi": "4.35.0", + "@rollup/rollup-android-arm64": "4.35.0", + "@rollup/rollup-darwin-arm64": "4.35.0", + "@rollup/rollup-darwin-x64": "4.35.0", + "@rollup/rollup-freebsd-arm64": "4.35.0", + "@rollup/rollup-freebsd-x64": "4.35.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.35.0", + "@rollup/rollup-linux-arm-musleabihf": "4.35.0", + "@rollup/rollup-linux-arm64-gnu": "4.35.0", + "@rollup/rollup-linux-arm64-musl": "4.35.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.35.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0", + "@rollup/rollup-linux-riscv64-gnu": "4.35.0", + "@rollup/rollup-linux-s390x-gnu": "4.35.0", + "@rollup/rollup-linux-x64-gnu": "4.35.0", + "@rollup/rollup-linux-x64-musl": "4.35.0", + "@rollup/rollup-win32-arm64-msvc": "4.35.0", + "@rollup/rollup-win32-ia32-msvc": "4.35.0", + "@rollup/rollup-win32-x64-msvc": "4.35.0", "fsevents": "~2.3.2" } }, @@ -4951,10 +5007,9 @@ } }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -5440,13 +5495,13 @@ } }, "node_modules/vite": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.3.tgz", - "integrity": "sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.1.tgz", + "integrity": "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==", "dependencies": { - "esbuild": "^0.24.0", - "postcss": "^8.4.49", - "rollup": "^4.23.0" + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" }, "bin": { "vite": "bin/vite.js" diff --git a/package.json b/package.json index eafff304..9de73950 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "release-all": "npm run release && npm run release-cuda" }, "dependencies": { - "@babel/standalone": "^7.26.9", + "@babel/standalone": "7.26.9", "@emotion/react": "11.14.0", "@emotion/styled": "11.14.0", "@mui/material": "6.2.0", @@ -36,17 +36,18 @@ "jotai": "2.10.3", "js-base64": "3.7.7", "js-yaml": "4.1.0", - "jszip": "^3.10.1", + "jszip": "3.10.1", "react": "18.2.0", "react-dom": "18.2.0", "react-i18next": "15.2.0", - "react-resizable-layout": "0.7.2" + "react-resizable-layout": "0.7.2", + "semver": "7.7.1" }, "devDependencies": { "@tauri-apps/cli": "1.6.3", "npm-run-all": "4.1.5", "sass": "1.79.4", - "vite": "6.0.3", + "vite": "6.2.1", "vite-plugin-svgr": "4.3.0" } } diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 21966762..cf7e0ada 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" tauri-build = { version = "1", features = [] } [dependencies] -tauri = { version = "1", features = [ "fs-read-file", "fs-create-dir", "fs-write-file", "fs-exists", "http-request", "fs-read-dir", "window-hide", "window-set-focus", "global-shortcut-all", "window-set-size", "window-set-position", "window-unmaximize", "window-close", "window-maximize", "window-minimize", "window-unminimize", "window-start-dragging", "window-set-decorations", "window-set-always-on-top", "shell-sidecar", "shell-open", "devtools"] } +tauri = { version = "1", features = [ "fs-remove-dir", "fs-read-file", "fs-create-dir", "fs-write-file", "fs-exists", "http-request", "fs-read-dir", "window-hide", "window-set-focus", "global-shortcut-all", "window-set-size", "window-set-position", "window-unmaximize", "window-close", "window-maximize", "window-minimize", "window-unminimize", "window-start-dragging", "window-set-decorations", "window-set-always-on-top", "shell-sidecar", "shell-open", "devtools"] } serde = { version = "1", features = ["derive"] } serde_json = "1" font-kit = "0.14.2" diff --git a/src-tauri/plugins/plugin_examples/index.jsx b/src-tauri/plugins/plugin_examples/index.jsx index 80f3befa..58441bd8 100644 --- a/src-tauri/plugins/plugin_examples/index.jsx +++ b/src-tauri/plugins/plugin_examples/index.jsx @@ -1,4 +1,3 @@ -import React from "react"; import { initStore } from "./store/store"; import { MainContainer } from "./main_container/MainContainer"; diff --git a/src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx b/src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx index 3a56e747..eaccc0aa 100644 --- a/src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx +++ b/src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx @@ -1,4 +1,5 @@ import { useStore } from "../store/store"; +import { useEffect } from "react"; export const MainContainer = () => { const { updateCountPluginState, currentCountPluginState } = useStore("useStore_CountPluginState"); @@ -9,6 +10,9 @@ export const MainContainer = () => { })); }; + useEffect(() => { + }, []) + return (

1 Zipped Dev Plugin Count: {currentCountPluginState?.data?.count}

diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 6e6fca0c..b36e8ce4 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -36,11 +36,12 @@ "exists": true, "writeFile": true, "createDir": true, + "removeDir": true, "scope": ["$RESOURCE/**", "**/src-tauri/target/debug/plugins/**"] }, "http": { "request": true, - "scope": ["https://api.github.com/repos/**", "https://github.com/**"] + "scope": ["https://api.github.com/repos/**", "https://github.com/**", "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json"] }, "shell": { "all": false, diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx index 6ccc231b..82abb5d2 100644 --- a/src-ui/app/_app_controllers/PluginsController.jsx +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -1,10 +1,9 @@ -import { useEffect } from "react"; import { usePlugins } from "@logics_configs"; -// ホスト側でReactやjotaiをグローバル変数として提供 -import ReactModule from "react"; + +import React, { useEffect } from "react"; if (typeof window !== "undefined") { - window.React = ReactModule; + window.React = React; } export const PluginsController = () => { diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index 954e4b33..e290f292 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -1,6 +1,12 @@ import React, { useState, useEffect } from "react"; +import semver from "semver"; import { usePlugins } from "@logics_configs"; import styles from "./Plugins.module.scss"; +import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; + +const MAIN_VRCT_VERSION = "3.0.5"; +// PLUGIN_LIST_URL は中央リポジトリにある、各プラグインの plugin_info.json への URL の配列を保持する JSON の URL +const PLUGIN_LIST_URL = "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json"; export const Plugins = () => { return ( @@ -13,26 +19,45 @@ export const Plugins = () => { const PluginDownloadContainer = () => { const [plugin_list, set_plugin_list] = useState([]); const [download_progress, set_download_progress] = useState({}); - const { downloadAndExtractPlugin } = usePlugins(); useEffect(() => { - // GitHub上のJSONファイルからプラグインリストを取得 - const fetchPluginList = async () => { + async function asyncFetchPluginInfoList() { try { - const response = await fetch( - "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json" - ); - if (!response.ok) { - throw new Error("Failed to fetch plugin list"); + // tauriFetch を使用して vrct_plugins_list.json を取得(CORS 対策) + const response = await tauriFetch(PLUGIN_LIST_URL, { + method: "GET", + responseType: ResponseType.Json, + headers: { "Cache-Control": "no-cache" } + }); + if (response.status !== 200) { + throw new Error("Failed to fetch plugin list, status: " + response.status); } - const data = await response.json(); - set_plugin_list(data); + // 取得される plugin_list.json は各プラグインの plugin_info.json への raw URL の配列とする + const plugins_data = response.data; + const updated_list = await Promise.all( + plugins_data.map(async (plugin_data) => { + try { + const plugin_manifest = await asyncFetchPluginManifest(plugin_data.url); + return { ...plugin_manifest }; + } catch (error) { + console.error("Error fetching manifest for URL:", plugin_data.url, error); + // エラー発生時は、plugin_data.title とエラーメッセージを返す + return { + title: plugin_data.title, + plugin_id: plugin_data.plugin_id || plugin_data.title, + error: error.message, + url: plugin_data.url + }; + } + }) + ); + set_plugin_list(updated_list); } catch (error) { - console.error("Error fetching plugin list:", error); + console.error("Error fetching plugin info list:", error); } - }; - fetchPluginList(); + } + asyncFetchPluginInfoList(); }, []); const handleDownload = async (plugin) => { @@ -44,14 +69,24 @@ const PluginDownloadContainer = () => { {plugin_list.map((plugin) => (

{plugin.title}

-

{plugin.plugin_id}

- - {download_progress[plugin.plugin_id] !== undefined && ( -
- Download Progress: {download_progress[plugin.plugin_id].toFixed(0)}% -
+

{plugin.plugin_id}

+ {plugin.error ? ( +

Error: {plugin.error}

+ ) : ( + <> +

Version: {plugin.plugin_version}

+

+ Compatible: {plugin.min_compatible_version} ~ {plugin.max_compatible_version} +

+ + {download_progress[plugin.plugin_id] !== undefined && ( +
+ Download Progress: {download_progress[plugin.plugin_id].toFixed(0)}% +
+ )} + )}
))} @@ -59,3 +94,69 @@ const PluginDownloadContainer = () => { ); }; +// GitHub Releases の latest 情報から plugin_info.json を取得する(tauriFetch を使用) +async function asyncFetchPluginManifest(manifest_url) { + // リリース情報を取得 + const release_response = await tauriFetch(manifest_url, { + method: "GET", + responseType: ResponseType.Json, + headers: { + "Accept": "application/vnd.github+json", + "User-Agent": "VRCTPluginApp" + } + }); + if (release_response.status !== 200) { + throw new Error(`Failed to fetch release info from ${manifest_url}`); + } + const release_data = release_response.data; + // assets 内に plugin_info.json があるかチェック + const manifest_asset = release_data.assets.find(asset => asset.name === "plugin_info.json"); + if (!manifest_asset) { + throw new Error("plugin_info.json not found in release assets"); + } + // plugin_info.json の内容を取得 + const manifest_response = await tauriFetch(manifest_asset.browser_download_url, { + method: "GET", + responseType: ResponseType.Json, + headers: { + "Accept": "application/json", + "User-Agent": "VRCTPluginApp", + "Cache-Control": "no-cache" + } + }); + if (manifest_response.status !== 200) { + throw new Error(`Failed to fetch plugin_info.json from ${manifest_asset.browser_download_url}`); + } + const plugin_manifest = manifest_response.data; + return { + title: plugin_manifest.title, + plugin_id: plugin_manifest.plugin_id, + plugin_version: plugin_manifest.plugin_version, + min_compatible_version: plugin_manifest.min_compatible_version, + max_compatible_version: plugin_manifest.max_compatible_version, + asset_name: plugin_manifest.asset_name, + url: manifest_url + }; +} + +export { PluginDownloadContainer }; + + + + // // プラグインのマニフェスト(plugin.json から取得した情報の例) + // const plugin_manifest = { + // compatible_lower_version: "3.0.4", + // compatible_upper_version: "3.0.6", + // // 他の情報... + // }; + + // const isPluginCompatible = (main_version, lower_version, upper_version) => { + // // lower_version 以上かつ upper_version 以下なら互換性ありと判定 + // return semver.gte(main_version, lower_version) && semver.lte(main_version, upper_version); + // }; + + // if (isPluginCompatible(currentSoftwareVersion.data, plugin_manifest.compatible_lower_version, plugin_manifest.compatible_upper_version)) { + // console.log("プラグインは互換性があります。"); + // } else { + // console.error("プラグインは現在の VRCT バージョンと互換性がありません。"); + // } \ No newline at end of file diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 4120cd5d..b9ebc97c 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -1,50 +1,41 @@ import { invoke } from '@tauri-apps/api/tauri'; import { createAtomWithHook, useStore_LoadedPluginsList } from "@store"; +import { useSoftwareVersion } from "@logics_configs"; import { transform } from "@babel/standalone"; -import { writeFile, createDir, exists, readDir, BaseDirectory, readTextFile } from "@tauri-apps/api/fs"; +import { writeFile, createDir, exists, removeDir, readDir, BaseDirectory, readTextFile } from "@tauri-apps/api/fs"; const dev_plugin_mapping = import.meta.glob("/src-tauri/plugins/**/index.jsx", { eager: true }); import JSZip from "jszip"; import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; -import React from "react"; -// import ReactDOM from "react-dom/client"; - -// グローバルに公開 -window.React = React; -// window.ReactDOM = ReactDOM; -window.React$1 = React; -window.we = React; - export const usePlugins = () => { const { updateLoadedPluginsList } = useStore_LoadedPluginsList(); + const { currentSoftwareVersion } = useSoftwareVersion(); const plugin_context = { registerComponent: ({ plugin_id, location, component }) => { - if (!plugin_id || !location || !component) { - return console.error("An invalid plugin was detected.", plugin_id, location, component); - } - updateLoadedPluginsList((prev) => { - const filtered = prev.data.filter(item => item.plugin_id !== plugin_id); - return [...filtered, { plugin_id, location, component }]; - }); + if (!plugin_id || !location || !component) { + return console.error("An invalid plugin was detected.", plugin_id, location, component); + } + updateLoadedPluginsList((prev) => { + const filtered = prev.data.filter(item => item.plugin_id !== plugin_id); + return [...filtered, { plugin_id, location, component }]; + }); }, createAtomWithHook: (...args) => createAtomWithHook(...args) }; const asyncLoadPlugin = async (plugin_relative_path) => { plugin_relative_path = "plugins/" + plugin_relative_path; - try { const plugin_code = await readTextFile(plugin_relative_path, { dir: BaseDirectory.Resource, recursive: true }); - const cleanedCode = removeImportStatements(plugin_code); - const transpiled_code = transform(cleanedCode, { + const cleaned_code = removeImportStatements(plugin_code); + const transpiled_code = transform(cleaned_code, { presets: [ ["env", { modules: false }], "react", ], sourceType: "module" }).code; - const blob = new Blob([transpiled_code], { type: "text/javascript" }); const blob_url = URL.createObjectURL(blob); const plugin_module = await import(/* @vite-ignore */ blob_url); @@ -60,7 +51,7 @@ export const usePlugins = () => { const loadAllPlugins = async () => { if (import.meta.env.DEV) { - // ホットリロード対応 src-tauri以下にあるpluginsディレクトリから直接読み込み(開発用) + // 開発時: ホットリロード対応、src-tauri以下のpluginsから直接読み込み Object.entries(dev_plugin_mapping).forEach(([key, plugin_module]) => { if (plugin_module && plugin_module.init) { plugin_module.init(plugin_context); @@ -70,7 +61,7 @@ export const usePlugins = () => { try { const plugin_files = await readDir("plugins", { dir: BaseDirectory.Resource, recursive: true }); for (const target_dir of plugin_files) { - const target_path = target_dir.name + "/index.es.js"; + const target_path = target_dir.name + "/index.esm.js"; await asyncLoadPlugin(target_path, plugin_context); } } catch (error) { @@ -82,68 +73,63 @@ export const usePlugins = () => { const downloadAndExtractPlugin = async (plugin) => { try { const plugin_zip_url = await fetchLatestPluginZipUrl(plugin); - // Rust コマンド経由で zip をダウンロード - const base64Zip = await invoke("download_zip_asset", { url: plugin_zip_url }); - // base64Zip は文字列なので、デコードして Uint8Array に変換 - const binaryString = atob(base64Zip); - const len = binaryString.length; + // Rust コマンド経由で ZIP をダウンロード + const base64_zip = await invoke("download_zip_asset", { url: plugin_zip_url }); + // base64_zip をデコードして Uint8Array に変換 + const binary_string = atob(base64_zip); + const len = binary_string.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { - bytes[i] = binaryString.charCodeAt(i); + bytes[i] = binary_string.charCodeAt(i); } - // JSZip で zip を解凍 + // JSZip で ZIP を解凍 const zip = await JSZip.loadAsync(bytes); - // const plugin_dir_exists = await exists("plugins", { dir: BaseDirectory.Resource, recursive: true }); - await createDir("plugins/" + plugin.asset_name.replace(".zip", ""), { dir: BaseDirectory.Resource, recursive: true }); - // if (!plugin_dir_exists) { - // } + // 展開先ディレクトリのパス(例:"plugins/" とする) const target_plugin_path = "plugins/" + plugin.asset_name.replace(".zip", ""); + // 既に存在する場合は削除してから新規作成 + if (await exists(target_plugin_path, { dir: BaseDirectory.Resource, recursive: true })) { + await removeDir(target_plugin_path, { dir: BaseDirectory.Resource, recursive: true }); + } + await createDir(target_plugin_path, { dir: BaseDirectory.Resource, recursive: true }); - - const filePromises = []; - zip.forEach((relativePath, zipEntry) => { + const file_promises = []; + zip.forEach((relative_path, zip_entry) => { // .git 以下のファイルはスキップ - if (relativePath.startsWith(".git") || relativePath.includes("/.git/")) { + if (relative_path.startsWith(".git") || relative_path.includes("/.git/")) { return; } - - const filePath = target_plugin_path + "/" + relativePath; - - if (zipEntry.dir) { - // フォルダの場合、ディレクトリを作成 - filePromises.push( - createDir(filePath, { dir: BaseDirectory.Resource, recursive: true }).catch((err) => { + const file_path = target_plugin_path + "/" + relative_path; + if (zip_entry.dir) { + file_promises.push( + createDir(file_path, { dir: BaseDirectory.Resource, recursive: true }).catch((err) => { if (!err.message?.includes("already exists")) { - console.error("Failed to create directory:", filePath, err); + console.error("Failed to create directory:", file_path, err); } }) ); } else { - // ファイルの場合、ディレクトリを作成してから書き込む - const dirPath = filePath.substring(0, filePath.lastIndexOf("/")); // 親ディレクトリのパス - - const promise = createDir(dirPath, { dir: BaseDirectory.Resource, recursive: true }) + const dir_path = file_path.substring(0, file_path.lastIndexOf("/")); + const promise = createDir(dir_path, { dir: BaseDirectory.Resource, recursive: true }) .catch((err) => { if (!err.message?.includes("already exists")) { - console.error("Failed to create parent directory:", dirPath, err); + console.error("Failed to create parent directory:", dir_path, err); } }) - .then(() => zipEntry.async("text")) - .then(async (fileData) => { - await writeFile(filePath, fileData, { dir: BaseDirectory.Resource, recursive: true }); + .then(() => zip_entry.async("text")) + .then(async (file_data) => { + await writeFile(file_path, file_data, { dir: BaseDirectory.Resource, recursive: true }); }); - - filePromises.push(promise); + file_promises.push(promise); } }); - await Promise.all(filePromises); + await Promise.all(file_promises); console.log("Plugin downloaded successfully."); - const index_file_relative_path = plugin.asset_name.replace(".zip", "") + "/" + "index.es.js" - await asyncLoadPlugin(index_file_relative_path); + const index_file_relative_path = plugin.asset_name.replace(".zip", "") + "/index.esm.js"; + await asyncLoadPlugin(index_file_relative_path, plugin_context); console.log("Plugin loaded successfully."); } catch (error) { @@ -151,8 +137,7 @@ export const usePlugins = () => { } }; - // JSON内のURLから GitHub API を使って最新リリース情報を取得し、 - // assets 配列から plugin.asset_name に一致するアセットの browser_download_url を返す + // GitHub API を使用して、最新リリース情報から asset_name に一致するアセットのブラウザダウンロード URL を返す const fetchLatestPluginZipUrl = async (plugin) => { const api_url = plugin.url; const response = await tauriFetch(api_url, { diff --git a/vite.config.js b/vite.config.js index 0d44e3e2..1abffdf1 100644 --- a/vite.config.js +++ b/vite.config.js @@ -33,6 +33,11 @@ export default defineConfig(async () => ({ resolve: { alias: { + "react": path.resolve(__dirname, "node_modules/react"), + "react-dom": path.resolve(__dirname, "node_modules/react-dom"), + + + "@root": path.resolve(__dirname), "@test_data": path.resolve(__dirname, "./test_data.js"), From b0f5751e1129f23260e6e6353e9770847b4ba9fc Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 13 Mar 2025 12:17:36 +0900 Subject: [PATCH 09/68] [Update/Refactor] Fetch and show the plugins info list. Refactor some functions. Try to fetch functions from github api just once when vrct started.(It used to every time plugin tab has opened so easy to reach to the api limit) --- package-lock.json | 8 +- src-ui/app/App.jsx | 2 +- .../_app_controllers/PluginsController.jsx | 17 +- .../_download_button/_DownloadButton.jsx | 41 ++++ .../_DownloadButton.module.scss | 30 +++ .../download_models/DownloadModels.jsx | 46 +--- .../DownloadModels.module.scss | 30 --- .../download_plugins/DownloadPlugins.jsx | 33 +++ .../DownloadPlugins.module.scss | 0 .../setting_box/_components/index.js | 3 +- .../setting_box/plugins/Plugins.jsx | 202 ++++++------------ .../setting_box/plugins/Plugins.module.scss | 30 +++ .../app/main_page/main_section/PluginHost.jsx | 6 +- src-ui/logics/common/index.js | 3 +- src-ui/logics/common/useFetch.js | 21 ++ src-ui/logics/configs/plugins/usePlugins.js | 113 ++++++++-- src-ui/store.js | 2 +- 17 files changed, 350 insertions(+), 237 deletions(-) create mode 100644 src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.jsx create mode 100644 src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss create mode 100644 src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx create mode 100644 src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.module.scss create mode 100644 src-ui/logics/common/useFetch.js diff --git a/package-lock.json b/package-lock.json index 6cb00da1..62bd3e7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "tauri-app", "version": "0.0.0", "dependencies": { - "@babel/standalone": "^7.26.9", + "@babel/standalone": "7.26.9", "@emotion/react": "11.14.0", "@emotion/styled": "11.14.0", "@mui/material": "6.2.0", @@ -21,18 +21,18 @@ "jotai": "2.10.3", "js-base64": "3.7.7", "js-yaml": "4.1.0", - "jszip": "^3.10.1", + "jszip": "3.10.1", "react": "18.2.0", "react-dom": "18.2.0", "react-i18next": "15.2.0", "react-resizable-layout": "0.7.2", - "semver": "^7.7.1" + "semver": "7.7.1" }, "devDependencies": { "@tauri-apps/cli": "1.6.3", "npm-run-all": "4.1.5", "sass": "1.79.4", - "vite": "^6.2.1", + "vite": "6.2.1", "vite-plugin-svgr": "4.3.0" } }, diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index fe4f5745..22c5107c 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -41,7 +41,6 @@ export const App = () => { - {(currentIsBackendReady.data === false || currentIsVrctAvailable.data === false) ? @@ -57,6 +56,7 @@ const Contents = () => { const { currentIsSoftwareUpdating } = useIsSoftwareUpdating(); return ( <> + diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx index 82abb5d2..16305224 100644 --- a/src-ui/app/_app_controllers/PluginsController.jsx +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -1,15 +1,24 @@ +import React, { useEffect, useRef } from "react"; import { usePlugins } from "@logics_configs"; - -import React, { useEffect } from "react"; if (typeof window !== "undefined") { window.React = React; } export const PluginsController = () => { - const { loadAllPlugins } = usePlugins(); + const hasRunRef = useRef(false); + const { + loadAllPlugins, + asyncUpdatePluginInfoList, + } = usePlugins(); + useEffect(() => { - loadAllPlugins(); + if (!hasRunRef.current) { + asyncUpdatePluginInfoList().then(() => { + loadAllPlugins(); + }); + } + return () => hasRunRef.current = true; }, []); return null; diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.jsx new file mode 100644 index 00000000..41c3b7d9 --- /dev/null +++ b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.jsx @@ -0,0 +1,41 @@ +import { useTranslation } from "react-i18next"; +import CircularProgress from "@mui/material/CircularProgress"; +import styles from "./_DownloadButton.module.scss"; + +export const _DownloadButton = ({option, ...props}) => { + const { t } = useTranslation(); + + const renderContent = () => { + const circular_progress = Math.floor(option.progress / 10) * 10; + + switch (true) { + case option.progress !== null: + return ( + <> + +

{`${Math.round(option.progress)}%`}

+ + ); + case option.is_pending: + return ; + case !option.is_downloaded: + return ( + + ); + default: + return null; + } + }; + + return
{renderContent()}
; +}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss new file mode 100644 index 00000000..59fabfe3 --- /dev/null +++ b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss @@ -0,0 +1,30 @@ +@import "@scss_mixins"; + +.download_container { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + max-width: 8rem; +} + +.download_button { + pointer-events: auto; + background-color: var(--dark_800_color); + padding: 0.8rem; + flex-shrink: 0; + &:hover { + background-color: var(--dark_750_color); + } + &:active { + background-color: var(--dark_800_color); + } +} +.download_button_label { + font-size: 1.2rem; +} + +.progress_label { + position: absolute; + font-size: 1rem; +} \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.jsx index b1f2360b..73656a0f 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.jsx @@ -1,9 +1,7 @@ -import { useTranslation } from "react-i18next"; -import CircularProgress from "@mui/material/CircularProgress"; -import styles from "./DownloadModels.module.scss"; import { RadioButton, } from "../index"; +import { _DownloadButton } from "../_atoms/_download_button/_DownloadButton"; export const DownloadModels = (props) => { const options = props.options.map(item => ({ @@ -19,47 +17,9 @@ export const DownloadModels = (props) => { options={options} checked_variable={props.checked_variable} column={true} - ChildComponent={ModelSelector} + ChildComponent={_DownloadButton} downloadStartFunction={props.downloadStartFunction} /> ); -}; - -const ModelSelector = ({option, ...props}) => { - const { t } = useTranslation(); - - const renderContent = () => { - const circular_progress = Math.floor(option.progress / 10) * 10; - - switch (true) { - case option.progress !== null: - return ( - <> - -

{`${Math.round(option.progress)}%`}

- - ); - case option.is_pending: - return ; - case !option.is_downloaded: - return ( - - ); - default: - return null; - } - }; - - return
{renderContent()}
; -}; +}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.module.scss index 59fabfe3..e69de29b 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.module.scss @@ -1,30 +0,0 @@ -@import "@scss_mixins"; - -.download_container { - display: flex; - align-items: center; - justify-content: center; - width: 100%; - max-width: 8rem; -} - -.download_button { - pointer-events: auto; - background-color: var(--dark_800_color); - padding: 0.8rem; - flex-shrink: 0; - &:hover { - background-color: var(--dark_750_color); - } - &:active { - background-color: var(--dark_800_color); - } -} -.download_button_label { - font-size: 1.2rem; -} - -.progress_label { - position: absolute; - font-size: 1rem; -} \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx new file mode 100644 index 00000000..7c1a17b9 --- /dev/null +++ b/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx @@ -0,0 +1,33 @@ +import { + SwitchBox, +} from "../index"; +import { _DownloadButton } from "../_atoms/_download_button/_DownloadButton"; + +export const DownloadPlugins = ({plugin_info, ...props}) => { + + const option = { + is_pending: plugin_info.is_pending, + is_downloaded: plugin_info.is_downloaded, + } + console.log(plugin_info); + + + return ( +
+ {/* */} + {plugin_info.is_plugin_supported ? + <_DownloadButton + option={option} + downloadStartFunction={props.downloadStartFunction} + /> + : +
+ Unavailable +
+ } +
+ ); +}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.module.scss new file mode 100644 index 00000000..e69de29b diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/index.js b/src-ui/app/config_page/setting_section/setting_box/_components/index.js index 3a295d3c..33b0837d 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/index.js +++ b/src-ui/app/config_page/setting_section/setting_box/_components/index.js @@ -11,4 +11,5 @@ export { Slider } from "./slider/Slider"; export { SwitchBox } from "./switch_box/SwitchBox"; export { ThresholdComponent } from "./threshold_component/ThresholdComponent"; export { WordFilter, WordFilterListToggleComponent } from "./word_filter/WordFilter"; -export { DownloadModels } from "./download_models/DownloadModels"; \ No newline at end of file +export { DownloadModels } from "./download_models/DownloadModels"; +export { DownloadPlugins } from "./download_plugins/DownloadPlugins"; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index e290f292..8201cdd8 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -1,12 +1,7 @@ import React, { useState, useEffect } from "react"; -import semver from "semver"; import { usePlugins } from "@logics_configs"; import styles from "./Plugins.module.scss"; -import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; - -const MAIN_VRCT_VERSION = "3.0.5"; -// PLUGIN_LIST_URL は中央リポジトリにある、各プラグインの plugin_info.json への URL の配列を保持する JSON の URL -const PLUGIN_LIST_URL = "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json"; +import { DownloadPlugins } from "../_components"; export const Plugins = () => { return ( @@ -17,146 +12,85 @@ export const Plugins = () => { }; const PluginDownloadContainer = () => { - const [plugin_list, set_plugin_list] = useState([]); - const [download_progress, set_download_progress] = useState({}); - const { downloadAndExtractPlugin } = usePlugins(); + const { + downloadAndExtractPlugin, + currentPluginsInfoList, + updatePluginsInfoList, + } = usePlugins(); - useEffect(() => { - async function asyncFetchPluginInfoList() { - try { - // tauriFetch を使用して vrct_plugins_list.json を取得(CORS 対策) - const response = await tauriFetch(PLUGIN_LIST_URL, { - method: "GET", - responseType: ResponseType.Json, - headers: { "Cache-Control": "no-cache" } - }); - if (response.status !== 200) { - throw new Error("Failed to fetch plugin list, status: " + response.status); + const downloadStartFunction = async (plugin) => { + updatePluginsInfoList((old_value) => { + const new_value = old_value.data.map(d => { + if (d.plugin_id === plugin.plugin_id) { + d.is_pending = true; } - // 取得される plugin_list.json は各プラグインの plugin_info.json への raw URL の配列とする - const plugins_data = response.data; - const updated_list = await Promise.all( - plugins_data.map(async (plugin_data) => { - try { - const plugin_manifest = await asyncFetchPluginManifest(plugin_data.url); - return { ...plugin_manifest }; - } catch (error) { - console.error("Error fetching manifest for URL:", plugin_data.url, error); - // エラー発生時は、plugin_data.title とエラーメッセージを返す - return { - title: plugin_data.title, - plugin_id: plugin_data.plugin_id || plugin_data.title, - error: error.message, - url: plugin_data.url - }; - } - }) - ); - set_plugin_list(updated_list); - } catch (error) { - console.error("Error fetching plugin info list:", error); - } - } - asyncFetchPluginInfoList(); - }, []); - - const handleDownload = async (plugin) => { + return d; + }); + return new_value; + }); await downloadAndExtractPlugin(plugin); + updatePluginsInfoList((old_value) => { + const new_value = old_value.data.map(d => { + if (d.plugin_id === plugin.plugin_id) { + d.is_pending = false; + } + return d; + }); + return new_value; + }); }; + const plugin_list = currentPluginsInfoList.data; + // const plugin_list = [ + // { + // title: "VRCT Example Plugins 1", + // plugin_id: "vrct_plugin_example_1", + // asset_name: "vrct_plugin_example_1.zip", + // plugin_version: "0.0.6", + // min_supported_vrct_version: "3.0.4", + // max_supported_vrct_version: "3.0.6", + // is_plugin_supported: true, + // // url: manifest_url + // }, + // { + // title: "VRCT Example Plugins 2", + // plugin_id: "vrct_plugin_example_2", + // asset_name: "vrct_plugin_example_2.zip", + // plugin_version: "0.0.1", + // min_supported_vrct_version: "3.0.4", + // max_supported_vrct_version: "3.0.7", + // is_plugin_supported: true, + // // url: manifest_url + // }, + // ]; + + + + return ( -
+
{plugin_list.map((plugin) => ( -
-

{plugin.title}

-

{plugin.plugin_id}

+
+

{plugin.title}

+

{plugin.plugin_id}

{plugin.error ? (

Error: {plugin.error}

) : ( - <> -

Version: {plugin.plugin_version}

-

- Compatible: {plugin.min_compatible_version} ~ {plugin.max_compatible_version} -

- - {download_progress[plugin.plugin_id] !== undefined && ( -
- Download Progress: {download_progress[plugin.plugin_id].toFixed(0)}% -
- )} - +
+
+

Version: {plugin.plugin_version}

+

+ Compatible: {plugin.min_supported_vrct_version} ~ {plugin.max_supported_vrct_version} +

+
+ +
)}
))}
); -}; - -// GitHub Releases の latest 情報から plugin_info.json を取得する(tauriFetch を使用) -async function asyncFetchPluginManifest(manifest_url) { - // リリース情報を取得 - const release_response = await tauriFetch(manifest_url, { - method: "GET", - responseType: ResponseType.Json, - headers: { - "Accept": "application/vnd.github+json", - "User-Agent": "VRCTPluginApp" - } - }); - if (release_response.status !== 200) { - throw new Error(`Failed to fetch release info from ${manifest_url}`); - } - const release_data = release_response.data; - // assets 内に plugin_info.json があるかチェック - const manifest_asset = release_data.assets.find(asset => asset.name === "plugin_info.json"); - if (!manifest_asset) { - throw new Error("plugin_info.json not found in release assets"); - } - // plugin_info.json の内容を取得 - const manifest_response = await tauriFetch(manifest_asset.browser_download_url, { - method: "GET", - responseType: ResponseType.Json, - headers: { - "Accept": "application/json", - "User-Agent": "VRCTPluginApp", - "Cache-Control": "no-cache" - } - }); - if (manifest_response.status !== 200) { - throw new Error(`Failed to fetch plugin_info.json from ${manifest_asset.browser_download_url}`); - } - const plugin_manifest = manifest_response.data; - return { - title: plugin_manifest.title, - plugin_id: plugin_manifest.plugin_id, - plugin_version: plugin_manifest.plugin_version, - min_compatible_version: plugin_manifest.min_compatible_version, - max_compatible_version: plugin_manifest.max_compatible_version, - asset_name: plugin_manifest.asset_name, - url: manifest_url - }; -} - -export { PluginDownloadContainer }; - - - - // // プラグインのマニフェスト(plugin.json から取得した情報の例) - // const plugin_manifest = { - // compatible_lower_version: "3.0.4", - // compatible_upper_version: "3.0.6", - // // 他の情報... - // }; - - // const isPluginCompatible = (main_version, lower_version, upper_version) => { - // // lower_version 以上かつ upper_version 以下なら互換性ありと判定 - // return semver.gte(main_version, lower_version) && semver.lte(main_version, upper_version); - // }; - - // if (isPluginCompatible(currentSoftwareVersion.data, plugin_manifest.compatible_lower_version, plugin_manifest.compatible_upper_version)) { - // console.log("プラグインは互換性があります。"); - // } else { - // console.error("プラグインは現在の VRCT バージョンと互換性がありません。"); - // } \ No newline at end of file +}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss index a49fed11..70eb0860 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss @@ -2,4 +2,34 @@ display: flex; gap: 6.4rem; flex-direction: column; +} + +.plugins_list_container { + display: flex; + flex-direction: column; + align-items: center; +} + +.plugin_wrapper { + width: 100%; + display: flex; + flex-direction: column; + padding: 2rem; + &:not(:last-child) { + border-bottom: 0.1rem solid var(--dark_750_color); + } +} + +.title { + font-size: 1.6rem; +} + +.plugin_id { + font-size: 1rem; +} + +.download_button { + background-color: var(--dark_750_color); + padding: 0.4rem 0.6rem; + font-size: 1.2rem; } \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/PluginHost.jsx b/src-ui/app/main_page/main_section/PluginHost.jsx index 8fc162ff..d4d50774 100644 --- a/src-ui/app/main_page/main_section/PluginHost.jsx +++ b/src-ui/app/main_page/main_section/PluginHost.jsx @@ -1,10 +1,8 @@ -// PluginHost.jsx import React from "react"; -import { useStore_LoadedPluginsList } from "@store"; +import { usePlugins } from "@logics_configs"; -// export const PluginHost = ({ location }) => { export const PluginHost = () => { - const { currentLoadedPluginsList } = useStore_LoadedPluginsList(); + const { currentLoadedPluginsList } = usePlugins(); console.log(currentLoadedPluginsList.data); return ( diff --git a/src-ui/logics/common/index.js b/src-ui/logics/common/index.js index 035f844b..5feee60e 100644 --- a/src-ui/logics/common/index.js +++ b/src-ui/logics/common/index.js @@ -11,4 +11,5 @@ export { useMessage } from "./useMessage"; export { useUpdateSoftware } from "./useUpdateSoftware"; export { useVolume } from "./useVolume"; export { useHandleNetworkConnection } from "./useHandleNetworkConnection"; -export { useIsVrctAvailable } from "./useIsVrctAvailable"; \ No newline at end of file +export { useIsVrctAvailable } from "./useIsVrctAvailable"; +export { useFetch } from "./useFetch"; \ No newline at end of file diff --git a/src-ui/logics/common/useFetch.js b/src-ui/logics/common/useFetch.js new file mode 100644 index 00000000..af9c82f6 --- /dev/null +++ b/src-ui/logics/common/useFetch.js @@ -0,0 +1,21 @@ +import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; + +export const useFetch = () => { + const asyncTauriFetchGithub = async (url) => { + console.log("tauriFetch"); + + const release_response = await tauriFetch(url, { + method: "GET", + responseType: ResponseType.Json, + headers: { + "Accept": "application/vnd.github+json", + "User-Agent": "VRCTPluginApp" + } + }); + return release_response; + }; + + return { + asyncTauriFetchGithub, + }; +}; \ No newline at end of file diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index b9ebc97c..4e385366 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -1,16 +1,31 @@ -import { invoke } from '@tauri-apps/api/tauri'; -import { createAtomWithHook, useStore_LoadedPluginsList } from "@store"; -import { useSoftwareVersion } from "@logics_configs"; +import semver from "semver"; + +import { invoke } from "@tauri-apps/api/tauri"; +import { + createAtomWithHook, + useStore_LoadedPluginsList, + useStore_PluginsInfoList, +} from "@store"; + import { transform } from "@babel/standalone"; import { writeFile, createDir, exists, removeDir, readDir, BaseDirectory, readTextFile } from "@tauri-apps/api/fs"; const dev_plugin_mapping = import.meta.glob("/src-tauri/plugins/**/index.jsx", { eager: true }); import JSZip from "jszip"; -import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; + +import { useFetch } from "@logics_common"; +import { useSoftwareVersion } from "@logics_configs"; + + +// PLUGIN_LIST_URL は中央リポジトリにある、各プラグインの plugin_info.json への URL の配列を保持する JSON の URL +const PLUGIN_LIST_URL = "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json"; export const usePlugins = () => { - const { updateLoadedPluginsList } = useStore_LoadedPluginsList(); + const { currentLoadedPluginsList, updateLoadedPluginsList } = useStore_LoadedPluginsList(); + const { currentPluginsInfoList, updatePluginsInfoList, pendingPluginsInfoList } = useStore_PluginsInfoList(); const { currentSoftwareVersion } = useSoftwareVersion(); + const { asyncTauriFetchGithub } = useFetch(); + const plugin_context = { registerComponent: ({ plugin_id, location, component }) => { if (!plugin_id || !location || !component) { @@ -137,17 +152,9 @@ export const usePlugins = () => { } }; - // GitHub API を使用して、最新リリース情報から asset_name に一致するアセットのブラウザダウンロード URL を返す const fetchLatestPluginZipUrl = async (plugin) => { const api_url = plugin.url; - const response = await tauriFetch(api_url, { - method: "GET", - responseType: ResponseType.Json, - headers: { - "Accept": "application/vnd.github+json", - "User-Agent": "VRCTPluginApp" - } - }); + const response = await asyncTauriFetchGithub(api_url); if (response.status !== 200) { throw new Error("Failed to fetch latest release info, status: " + response.status); } @@ -159,9 +166,87 @@ export const usePlugins = () => { return asset.browser_download_url; }; + + const asyncUpdatePluginInfoList = async () => { + pendingPluginsInfoList(); + try { + const response = await asyncTauriFetchGithub(PLUGIN_LIST_URL); + if (response.status !== 200) { + throw new Error("Failed to fetch plugin list, status: " + response.status); + } + const plugins_data = response.data; + const updated_list = await Promise.all( + plugins_data.map(async (plugin_data) => { + try { + const plugin_info = await asyncFetchPluginInfo(plugin_data.url); + return { ...plugin_info }; + } catch (error) { + console.error("Error fetching plugin info for URL:", plugin_data.url, error); + // エラー発生時は、plugin_data.title とエラーメッセージを返す + return { + title: plugin_data.title, + plugin_id: plugin_data.plugin_id || plugin_data.title, + error: error.message, + url: plugin_data.url + }; + } + }) + ); + updatePluginsInfoList(updated_list); + } catch (error) { + console.error("Error fetching plugin info list:", error); + } + } + + const asyncFetchPluginInfo = async (plugin_info_asset_url) => { + const release_response = await asyncTauriFetchGithub(plugin_info_asset_url); + if (release_response.status !== 200) { + throw new Error(`Failed to fetch release info from ${plugin_info_asset_url}`); + } + const plugin_info_json = release_response.data.assets.find(asset => asset.name === "plugin_info.json"); + if (!plugin_info_json) { + throw new Error("plugin_info.json not found in release assets"); + } + const plugin_info_json_response = await asyncTauriFetchGithub(plugin_info_json.browser_download_url); + if (plugin_info_json_response.status !== 200) { + throw new Error(`Failed to fetch plugin_info.json from ${plugin_info_json.browser_download_url}`); + } + const plugin_info = plugin_info_json_response.data; + + const isPluginCompatible = (main_version, lower_version, upper_version) => { + console.log(main_version, lower_version, upper_version); + + // lower_version 以上かつ upper_version 以下なら互換性ありと判定 + return semver.gte(main_version, lower_version) && semver.lte(main_version, upper_version); + }; + + const is_plugin_supported = isPluginCompatible(currentSoftwareVersion.data, plugin_info.min_supported_vrct_version, plugin_info.max_supported_vrct_version); + + return { + title: plugin_info.title, + plugin_id: plugin_info.plugin_id, + plugin_version: plugin_info.plugin_version, + min_supported_vrct_version: plugin_info.min_supported_vrct_version, + max_supported_vrct_version: plugin_info.max_supported_vrct_version, + is_plugin_supported: is_plugin_supported, + asset_name: plugin_info.asset_name, + url: plugin_info_asset_url + }; + } + + + return { + asyncUpdatePluginInfoList, + loadAllPlugins, downloadAndExtractPlugin, + + currentLoadedPluginsList, + updateLoadedPluginsList, + + currentPluginsInfoList, + updatePluginsInfoList, }; }; diff --git a/src-ui/store.js b/src-ui/store.js index da4c1f45..d35f02aa 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -275,8 +275,8 @@ export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createA }, "Hotkeys"); // Plugins -export const { atomInstance: Atom_InstalledPluginsPath, useHook: useStore_InstalledPluginsPath } = createAtomWithHook([], "InstalledPluginsPath"); export const { atomInstance: Atom_LoadedPluginsList, useHook: useStore_LoadedPluginsList } = createAtomWithHook([], "LoadedPluginsList"); +export const { atomInstance: Atom_PluginsInfoList, useHook: useStore_PluginsInfoList } = createAtomWithHook([], "PluginsInfoList"); // Advanced Settings export const { atomInstance: Atom_OscIpAddress, useHook: useStore_OscIpAddress } = createAtomWithHook("127.0.0.1", "OscIpAddress"); From 3ebd60509236d44607a1a8ff0b1e2667f372da7d Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Fri, 14 Mar 2025 23:22:00 +0900 Subject: [PATCH 10/68] [Update/TMP] Save plugin status that if enabled or not(on store.js, yet). Handle download. --- .../_DownloadButton.module.scss | 1 + .../download_plugins/DownloadPlugins.jsx | 24 ++++---- .../DownloadPlugins.module.scss | 7 +++ .../setting_box/plugins/Plugins.jsx | 55 +++++++++++++++---- .../setting_box/plugins/Plugins.module.scss | 7 +++ .../app/main_page/main_section/PluginHost.jsx | 2 +- src-ui/logics/common/useFetch.js | 2 +- src-ui/logics/configs/plugins/usePlugins.js | 5 ++ src-ui/store.js | 4 ++ 9 files changed, 82 insertions(+), 25 deletions(-) diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss index 59fabfe3..6b42ca5d 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss @@ -13,6 +13,7 @@ background-color: var(--dark_800_color); padding: 0.8rem; flex-shrink: 0; + border-radius: 0.2rem; &:hover { background-color: var(--dark_750_color); } diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx index 7c1a17b9..1e66b9c7 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx @@ -2,30 +2,32 @@ import { SwitchBox, } from "../index"; import { _DownloadButton } from "../_atoms/_download_button/_DownloadButton"; +import styles from "./DownloadPlugins.module.scss"; -export const DownloadPlugins = ({plugin_info, ...props}) => { - +export const DownloadPlugins = ({plugin_info, plugin_status, ...props}) => { const option = { + id: plugin_info.plugin_id, is_pending: plugin_info.is_pending, is_downloaded: plugin_info.is_downloaded, - } - console.log(plugin_info); + progress: null, + }; return ( -
- {/* */} +
+ {plugin_info.is_downloaded && plugin_info.is_plugin_supported && + } {plugin_info.is_plugin_supported ? <_DownloadButton option={option} downloadStartFunction={props.downloadStartFunction} /> : -
- Unavailable +
+ Downloaded but outdated.
}
diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.module.scss index e69de29b..99a37516 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.module.scss @@ -0,0 +1,7 @@ +.container { +} + +.unavailable_text { + padding: 1rem; + font-size: 1.2rem; +} \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index 8201cdd8..ea96223c 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -16,31 +16,41 @@ const PluginDownloadContainer = () => { downloadAndExtractPlugin, currentPluginsInfoList, updatePluginsInfoList, + currentSavedPluginsStatus, + updateSavedPluginsStatus, + currentLoadedPluginsList, + // updateLoadedPluginsList, } = usePlugins(); - const downloadStartFunction = async (plugin) => { + const downloadStartFunction = async (target_plugin_id) => { updatePluginsInfoList((old_value) => { const new_value = old_value.data.map(d => { - if (d.plugin_id === plugin.plugin_id) { + if (d.plugin_id === target_plugin_id) { d.is_pending = true; } return d; }); return new_value; }); - await downloadAndExtractPlugin(plugin); - updatePluginsInfoList((old_value) => { - const new_value = old_value.data.map(d => { - if (d.plugin_id === plugin.plugin_id) { - d.is_pending = false; - } - return d; + const target_plugin_info = currentPluginsInfoList.data.find(d => d.plugin_id === target_plugin_id); + downloadAndExtractPlugin(target_plugin_info).then(() => { + updatePluginsInfoList((old_value) => { + const new_value = old_value.data.map(d => { + if (d.plugin_id === target_plugin_id) { + d.is_pending = false; + d.is_downloaded = true; + } + return d; + }); + return new_value; }); - return new_value; - }); + }) }; + // console.log(currentPluginsInfoList.data); - const plugin_list = currentPluginsInfoList.data; + + // const plugin_list = currentPluginsInfoList.data; + const plugin_list = [...currentPluginsInfoList.data, ...currentLoadedPluginsList.data]; // const plugin_list = [ // { // title: "VRCT Example Plugins 1", @@ -64,6 +74,26 @@ const PluginDownloadContainer = () => { // }, // ]; + const getTargetPluginStatus = (target_plugin_id) => { + let plugin_status = currentSavedPluginsStatus.data.find(d => d.plugin_id === target_plugin_id) ?? {}; + const is_downloaded = currentLoadedPluginsList.data.find(d => d.plugin_id === target_plugin_id) ? true : false; + + plugin_status.toggleFunction = () => { + updateSavedPluginsStatus((old_value) => { + const new_value = old_value.data.map(d => { + if (d.plugin_id === target_plugin_id) { + d.data = !d.data; + d.state = "ok"; + } + return d; + }); + return new_value; + }); + } + plugin_status.is_downloaded = is_downloaded; + plugin_status.is_pending = false; + return plugin_status; + }; @@ -85,6 +115,7 @@ const PluginDownloadContainer = () => {
diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss index 70eb0860..3320d590 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss @@ -20,6 +20,13 @@ } } +.plugin_info_wrapper { + display: flex; + width: 100%; + justify-content: space-between; + align-items: center; +} + .title { font-size: 1.6rem; } diff --git a/src-ui/app/main_page/main_section/PluginHost.jsx b/src-ui/app/main_page/main_section/PluginHost.jsx index d4d50774..264bd579 100644 --- a/src-ui/app/main_page/main_section/PluginHost.jsx +++ b/src-ui/app/main_page/main_section/PluginHost.jsx @@ -3,7 +3,7 @@ import { usePlugins } from "@logics_configs"; export const PluginHost = () => { const { currentLoadedPluginsList } = usePlugins(); - console.log(currentLoadedPluginsList.data); + // console.log(currentLoadedPluginsList.data); return (
diff --git a/src-ui/logics/common/useFetch.js b/src-ui/logics/common/useFetch.js index af9c82f6..12ba5def 100644 --- a/src-ui/logics/common/useFetch.js +++ b/src-ui/logics/common/useFetch.js @@ -2,7 +2,7 @@ import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; export const useFetch = () => { const asyncTauriFetchGithub = async (url) => { - console.log("tauriFetch"); + console.log("tauriFetch", url); const release_response = await tauriFetch(url, { method: "GET", diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 4e385366..c8c18749 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -4,6 +4,7 @@ import { invoke } from "@tauri-apps/api/tauri"; import { createAtomWithHook, useStore_LoadedPluginsList, + useStore_SavedPluginsStatus, useStore_PluginsInfoList, } from "@store"; @@ -21,6 +22,7 @@ const PLUGIN_LIST_URL = "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_p export const usePlugins = () => { const { currentLoadedPluginsList, updateLoadedPluginsList } = useStore_LoadedPluginsList(); + const { currentSavedPluginsStatus, updateSavedPluginsStatus } = useStore_SavedPluginsStatus(); const { currentPluginsInfoList, updatePluginsInfoList, pendingPluginsInfoList } = useStore_PluginsInfoList(); const { currentSoftwareVersion } = useSoftwareVersion(); @@ -245,6 +247,9 @@ export const usePlugins = () => { currentLoadedPluginsList, updateLoadedPluginsList, + currentSavedPluginsStatus, + updateSavedPluginsStatus, + currentPluginsInfoList, updatePluginsInfoList, }; diff --git a/src-ui/store.js b/src-ui/store.js index d35f02aa..2da4c128 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -276,6 +276,10 @@ export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createA // Plugins export const { atomInstance: Atom_LoadedPluginsList, useHook: useStore_LoadedPluginsList } = createAtomWithHook([], "LoadedPluginsList"); +export const { atomInstance: Atom_SavedPluginsStatus, useHook: useStore_SavedPluginsStatus } = createAtomWithHook([ + { plugin_id: "vrct_plugin_example_1", is_enabled: true }, + { plugin_id: "vrct_plugin_example_2", is_enabled: false } +], "SavedPluginsStatus"); export const { atomInstance: Atom_PluginsInfoList, useHook: useStore_PluginsInfoList } = createAtomWithHook([], "PluginsInfoList"); // Advanced Settings From 0148f9bee0938cbd90a5e6f4cf46c57e3e1d1084 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Tue, 25 Mar 2025 11:17:04 +0900 Subject: [PATCH 11/68] [Update] (Affect to backend) Plugin System. Saveable if the plugin is enabled or not. Add functions that merge plugins data. --- src-python/config.py | 13 +++ src-python/controller.py | 9 ++ src-python/mainloop.py | 3 + src-ui/app/App.jsx | 8 +- .../_app_controllers/PluginsController.jsx | 77 +++++++++++++-- .../download_plugins/DownloadPlugins.jsx | 35 ------- .../setting_box/_components/index.js | 2 +- .../PluginsControlComponent.jsx | 48 ++++++++++ .../PluginsControlComponent.module.scss} | 0 .../switch_box/SwitchBox.module.scss | 1 + .../setting_box/plugins/Plugins.jsx | 95 ++++++++----------- .../app/main_page/main_section/PluginHost.jsx | 7 +- src-ui/logics/configs/plugins/usePlugins.js | 50 ++++++---- src-ui/logics/useReceiveRoutes.js | 6 ++ src-ui/store.js | 9 +- 15 files changed, 229 insertions(+), 134 deletions(-) delete mode 100644 src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx create mode 100644 src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx rename src-ui/app/config_page/setting_section/setting_box/_components/{download_plugins/DownloadPlugins.module.scss => plugins_control_component/PluginsControlComponent.module.scss} (100%) diff --git a/src-python/config.py b/src-python/config.py index b607863b..8038b047 100644 --- a/src-python/config.py +++ b/src-python/config.py @@ -555,6 +555,18 @@ class Config: self._HOTKEYS[key] = value self.saveConfig(inspect.currentframe().f_code.co_name, self.HOTKEYS, immediate_save=True) + @property + @json_serializable('PLUGINS_STATUS') + def PLUGINS_STATUS(self): + return self._PLUGINS_STATUS + + @PLUGINS_STATUS.setter + def PLUGINS_STATUS(self, value): + if isinstance(value, list): + if all(isinstance(item, dict) for item in value): + self._PLUGINS_STATUS = value + self.saveConfig(inspect.currentframe().f_code.co_name, self.PLUGINS_STATUS, immediate_save=True) + @property @json_serializable('MIC_AVG_LOGPROB') def MIC_AVG_LOGPROB(self): @@ -1068,6 +1080,7 @@ class Config: "toggle_transcription_send": None, "toggle_transcription_receive": None, } + self._PLUGINS_STATUS = [] self._MIC_AVG_LOGPROB = -0.8 self._MIC_NO_SPEECH_PROB = 0.6 self._AUTO_SPEAKER_SELECT = True diff --git a/src-python/controller.py b/src-python/controller.py index f0356287..02ba5e34 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -1061,6 +1061,15 @@ class Controller: config.HOTKEYS = data return {"status":200, "result":config.HOTKEYS} + @staticmethod + def getPluginsStatus(*args, **kwargs) -> dict: + return {"status":200, "result":config.PLUGINS_STATUS} + + @staticmethod + def setPluginsStatus(data, *args, **kwargs) -> dict: + config.PLUGINS_STATUS = data + return {"status":200, "result":config.PLUGINS_STATUS} + @staticmethod def getSpeakerAvgLogprob(*args, **kwargs) -> dict: return {"status":200, "result":config.SPEAKER_AVG_LOGPROB} diff --git a/src-python/mainloop.py b/src-python/mainloop.py index cd50a503..edd5356a 100644 --- a/src-python/mainloop.py +++ b/src-python/mainloop.py @@ -190,6 +190,9 @@ mapping = { "/get/data/hotkeys": {"status": True, "variable":controller.getHotkeys}, "/set/data/hotkeys": {"status": True, "variable":controller.setHotkeys}, + "/get/data/plugins_status": {"status": True, "variable":controller.getPluginsStatus}, + "/set/data/plugins_status": {"status": True, "variable":controller.setPluginsStatus}, + "/get/data/mic_avg_logprob": {"status": True, "variable":controller.getMicAvgLogprob}, "/set/data/mic_avg_logprob": {"status": True, "variable":controller.setMicAvgLogprob}, diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index 22c5107c..269bf824 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -1,3 +1,4 @@ +import { useRef } from "react"; import { useTranslation } from "react-i18next"; import { @@ -29,6 +30,7 @@ export const App = () => { const { currentIsBackendReady } = useIsBackendReady(); const { WindowGeometryController } = useWindow(); const { i18n } = useTranslation(); + const fetchPluginsHasRunRef = useRef(false); return (
@@ -44,7 +46,7 @@ export const App = () => { {(currentIsBackendReady.data === false || currentIsVrctAvailable.data === false) ? - : + : } @@ -52,11 +54,11 @@ export const App = () => { ); }; -const Contents = () => { +const Contents = ({ fetchPluginsHasRunRef }) => { const { currentIsSoftwareUpdating } = useIsSoftwareUpdating(); return ( <> - + diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx index 16305224..d6f97ab4 100644 --- a/src-ui/app/_app_controllers/PluginsController.jsx +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -5,21 +5,80 @@ if (typeof window !== "undefined") { window.React = React; } -export const PluginsController = () => { - const hasRunRef = useRef(false); +export const PluginsController = ({ fetchPluginsHasRunRef }) => { const { - loadAllPlugins, - asyncUpdatePluginInfoList, + asyncLoadAllPlugins, + asyncFetchPluginsInfo, + currentPluginsData, + updatePluginsData, + currentSavedPluginsStatus, } = usePlugins(); useEffect(() => { - if (!hasRunRef.current) { - asyncUpdatePluginInfoList().then(() => { - loadAllPlugins(); - }); + const loadPlugins = async () => { + try { + await asyncLoadAllPlugins(); + const info_array = await asyncFetchPluginsInfo(); + updatePluginsData(prev => { + // Map を利用してそれぞれの配列を plugin_id で参照できるようにする + const infoMap = new Map(info_array.map(info => [info.plugin_id, info])); + const prevMap = new Map(prev.data.map(item => [item.plugin_id, item])); + + // info_array にある各アイテムについて、prev.data に同じ plugin_id があればマージ + const merged = info_array.map(info => { + if (prevMap.has(info.plugin_id)) { + return { ...info, ...prevMap.get(info.plugin_id) }; + } + return info; + }); + + // prev.data にのみ存在するアイテムを追加し、is_outdated: true を付与 + prev.data.forEach(item => { + if (!infoMap.has(item.plugin_id)) { + merged.push({ ...item, is_outdated: true }); + } + }); + + return merged; + }); + } catch (error) { + console.error(error); + } + }; + + if (!fetchPluginsHasRunRef.current) { + loadPlugins(); } - return () => hasRunRef.current = true; + return () => fetchPluginsHasRunRef.current = true; }, []); + + useEffect(() => { + updatePluginsData(prev => { + // currentSavedPluginsStatus.data の各要素を Map 化して plugin_id で参照 + const savedMap = new Map(currentSavedPluginsStatus.data.map(saved => [saved.plugin_id, saved])); + const prevMap = new Map(prev.data.map(item => [item.plugin_id, item])); + + // prev.data にある各アイテムについて、保存済みの状態情報があればマージ + const merged = prev.data.map(item => { + if (savedMap.has(item.plugin_id)) { + return { ...item, is_enabled: savedMap.get(item.plugin_id).is_enabled }; + } + return item; + }); + + // currentSavedPluginsStatus.data にのみ存在する項目があれば追加 + currentSavedPluginsStatus.data.forEach(saved => { + if (!prevMap.has(saved.plugin_id)) { + merged.push({ plugin_id: saved.plugin_id, is_enabled: saved.is_enabled }); + } + }); + + return merged; + }); + }, [currentSavedPluginsStatus]); + + + return null; }; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx deleted file mode 100644 index 1e66b9c7..00000000 --- a/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import { - SwitchBox, -} from "../index"; -import { _DownloadButton } from "../_atoms/_download_button/_DownloadButton"; -import styles from "./DownloadPlugins.module.scss"; - -export const DownloadPlugins = ({plugin_info, plugin_status, ...props}) => { - const option = { - id: plugin_info.plugin_id, - is_pending: plugin_info.is_pending, - is_downloaded: plugin_info.is_downloaded, - progress: null, - }; - - - return ( -
- {plugin_info.is_downloaded && plugin_info.is_plugin_supported && - } - {plugin_info.is_plugin_supported ? - <_DownloadButton - option={option} - downloadStartFunction={props.downloadStartFunction} - /> - : -
- Downloaded but outdated. -
- } -
- ); -}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/index.js b/src-ui/app/config_page/setting_section/setting_box/_components/index.js index 33b0837d..123a1400 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/index.js +++ b/src-ui/app/config_page/setting_section/setting_box/_components/index.js @@ -12,4 +12,4 @@ export { SwitchBox } from "./switch_box/SwitchBox"; export { ThresholdComponent } from "./threshold_component/ThresholdComponent"; export { WordFilter, WordFilterListToggleComponent } from "./word_filter/WordFilter"; export { DownloadModels } from "./download_models/DownloadModels"; -export { DownloadPlugins } from "./download_plugins/DownloadPlugins"; \ No newline at end of file +export { PluginsControlComponent } from "./plugins_control_component/PluginsControlComponent"; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx new file mode 100644 index 00000000..33ca0fa7 --- /dev/null +++ b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx @@ -0,0 +1,48 @@ +import { + SwitchBox, +} from "../index"; +import { _DownloadButton } from "../_atoms/_download_button/_DownloadButton"; +import styles from "./PluginsControlComponent.module.scss"; + +export const PluginsControlComponent = ({ variable_state, plugin_status, toggleFunction, ...props }) => { + const option = { + id: plugin_status.plugin_id, + is_pending: plugin_status.is_pending, + is_downloaded: plugin_status.is_downloaded, + data: plugin_status.is_enabled, + state: variable_state, + progress: null, + }; + + const adjustedToggleFunction = () => { + toggleFunction(plugin_status.plugin_id); + }; + + let is_turn_on_able = false; + if (plugin_status.is_downloaded && plugin_status.is_plugin_supported) { + is_turn_on_able = true; + } + if (plugin_status.is_outdated) { + is_turn_on_able = true; + } + + return ( +
+ {is_turn_on_able && + } + {plugin_status.is_plugin_supported ? + <_DownloadButton + option={option} + downloadStartFunction={props.downloadStartFunction} + /> + : +
+ Downloaded but outdated. +
+ } +
+ ); +}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.module.scss similarity index 100% rename from src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.module.scss rename to src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.module.scss diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/switch_box/SwitchBox.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/switch_box/SwitchBox.module.scss index 8f76c0b6..29c26df5 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/switch_box/SwitchBox.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/_components/switch_box/SwitchBox.module.scss @@ -22,6 +22,7 @@ } .toggle_control { + position: relative; @include toggle_control_styles; display: flex; justify-content: center; diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index ea96223c..cddda330 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; import { usePlugins } from "@logics_configs"; import styles from "./Plugins.module.scss"; -import { DownloadPlugins } from "../_components"; +import { PluginsControlComponent } from "../_components"; export const Plugins = () => { return ( @@ -14,16 +14,15 @@ export const Plugins = () => { const PluginDownloadContainer = () => { const { downloadAndExtractPlugin, - currentPluginsInfoList, - updatePluginsInfoList, + currentPluginsData, + updatePluginsData, currentSavedPluginsStatus, - updateSavedPluginsStatus, - currentLoadedPluginsList, - // updateLoadedPluginsList, + setSavedPluginsStatus, } = usePlugins(); + const downloadStartFunction = async (target_plugin_id) => { - updatePluginsInfoList((old_value) => { + updatePluginsData((old_value) => { const new_value = old_value.data.map(d => { if (d.plugin_id === target_plugin_id) { d.is_pending = true; @@ -32,9 +31,9 @@ const PluginDownloadContainer = () => { }); return new_value; }); - const target_plugin_info = currentPluginsInfoList.data.find(d => d.plugin_id === target_plugin_id); + const target_plugin_info = currentPluginsData.data.find(d => d.plugin_id === target_plugin_id); downloadAndExtractPlugin(target_plugin_info).then(() => { - updatePluginsInfoList((old_value) => { + updatePluginsData((old_value) => { const new_value = old_value.data.map(d => { if (d.plugin_id === target_plugin_id) { d.is_pending = false; @@ -46,60 +45,41 @@ const PluginDownloadContainer = () => { }); }) }; - // console.log(currentPluginsInfoList.data); - // const plugin_list = currentPluginsInfoList.data; - const plugin_list = [...currentPluginsInfoList.data, ...currentLoadedPluginsList.data]; - // const plugin_list = [ - // { - // title: "VRCT Example Plugins 1", - // plugin_id: "vrct_plugin_example_1", - // asset_name: "vrct_plugin_example_1.zip", - // plugin_version: "0.0.6", - // min_supported_vrct_version: "3.0.4", - // max_supported_vrct_version: "3.0.6", - // is_plugin_supported: true, - // // url: manifest_url - // }, - // { - // title: "VRCT Example Plugins 2", - // plugin_id: "vrct_plugin_example_2", - // asset_name: "vrct_plugin_example_2.zip", - // plugin_version: "0.0.1", - // min_supported_vrct_version: "3.0.4", - // max_supported_vrct_version: "3.0.7", - // is_plugin_supported: true, - // // url: manifest_url - // }, - // ]; - - const getTargetPluginStatus = (target_plugin_id) => { - let plugin_status = currentSavedPluginsStatus.data.find(d => d.plugin_id === target_plugin_id) ?? {}; - const is_downloaded = currentLoadedPluginsList.data.find(d => d.plugin_id === target_plugin_id) ? true : false; - - plugin_status.toggleFunction = () => { - updateSavedPluginsStatus((old_value) => { - const new_value = old_value.data.map(d => { - if (d.plugin_id === target_plugin_id) { - d.data = !d.data; - d.state = "ok"; - } - return d; - }); - return new_value; + const toggleFunction = (target_plugin_id) => { + const is_exists = currentSavedPluginsStatus.data.some(d => d.plugin_id === target_plugin_id); + let new_value = []; + if (is_exists) { + new_value = currentSavedPluginsStatus.data.map(d => { + if (d.plugin_id === target_plugin_id) { + d.is_enabled = !d.is_enabled; + } + return d; + }); + } else { + new_value.push(...currentSavedPluginsStatus.data); + new_value.push({ + plugin_id: target_plugin_id, + is_enabled: true, }); } - plugin_status.is_downloaded = is_downloaded; - plugin_status.is_pending = false; - return plugin_status; - }; + // currentPluginsData.data で、is_downloaded が true のものだけ残す + new_value = new_value.filter(item => { + return currentPluginsData.data.some(plugin => plugin.plugin_id === item.plugin_id && plugin.is_downloaded); + }); + + setSavedPluginsStatus(new_value); + } + + const variable_state = currentSavedPluginsStatus.state; + return (
- {plugin_list.map((plugin) => ( + {currentPluginsData.data.map((plugin) => (

{plugin.title}

{plugin.plugin_id}

@@ -113,9 +93,10 @@ const PluginDownloadContainer = () => { Compatible: {plugin.min_supported_vrct_version} ~ {plugin.max_supported_vrct_version}

-
diff --git a/src-ui/app/main_page/main_section/PluginHost.jsx b/src-ui/app/main_page/main_section/PluginHost.jsx index 264bd579..5c01328c 100644 --- a/src-ui/app/main_page/main_section/PluginHost.jsx +++ b/src-ui/app/main_page/main_section/PluginHost.jsx @@ -2,13 +2,12 @@ import React from "react"; import { usePlugins } from "@logics_configs"; export const PluginHost = () => { - const { currentLoadedPluginsList } = usePlugins(); - // console.log(currentLoadedPluginsList.data); + const { currentPluginsData } = usePlugins(); return (
- {currentLoadedPluginsList.data - .filter((plugin) => plugin.location === "main_section") + {currentPluginsData.data + .filter((plugin) => plugin.is_enabled && plugin.location === "main_section") .map((plugin, index) => { const PluginComponent = plugin.component; return PluginComponent ? : null; diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index c8c18749..7bc55256 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -3,10 +3,10 @@ import semver from "semver"; import { invoke } from "@tauri-apps/api/tauri"; import { createAtomWithHook, - useStore_LoadedPluginsList, useStore_SavedPluginsStatus, - useStore_PluginsInfoList, + useStore_PluginsData, } from "@store"; +import { useStdoutToPython } from "@logics/useStdoutToPython"; import { transform } from "@babel/standalone"; import { writeFile, createDir, exists, removeDir, readDir, BaseDirectory, readTextFile } from "@tauri-apps/api/fs"; @@ -21,9 +21,10 @@ import { useSoftwareVersion } from "@logics_configs"; const PLUGIN_LIST_URL = "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json"; export const usePlugins = () => { - const { currentLoadedPluginsList, updateLoadedPluginsList } = useStore_LoadedPluginsList(); - const { currentSavedPluginsStatus, updateSavedPluginsStatus } = useStore_SavedPluginsStatus(); - const { currentPluginsInfoList, updatePluginsInfoList, pendingPluginsInfoList } = useStore_PluginsInfoList(); + const { asyncStdoutToPython } = useStdoutToPython(); + // const { currentLoadedPluginsList, updateLoadedPluginsList } = useStore_LoadedPluginsList(); + const { currentSavedPluginsStatus, updateSavedPluginsStatus, pendingSavedPluginsStatus } = useStore_SavedPluginsStatus(); + const { currentPluginsData, updatePluginsData, pendingPluginsData } = useStore_PluginsData(); const { currentSoftwareVersion } = useSoftwareVersion(); const { asyncTauriFetchGithub } = useFetch(); @@ -33,9 +34,17 @@ export const usePlugins = () => { if (!plugin_id || !location || !component) { return console.error("An invalid plugin was detected.", plugin_id, location, component); } - updateLoadedPluginsList((prev) => { - const filtered = prev.data.filter(item => item.plugin_id !== plugin_id); - return [...filtered, { plugin_id, location, component }]; + updatePluginsData(prev => { + const is_already_registered = prev.data.some(old_value => old_value.plugin_id === plugin_id); + const new_value = prev.data.map(old_value => + old_value.plugin_id === plugin_id + ? { ...old_value, location, component, is_downloaded: true } + : old_value + ); + + return is_already_registered + ? new_value + : [...new_value, { plugin_id, location, component, is_downloaded: true }]; }); }, createAtomWithHook: (...args) => createAtomWithHook(...args) @@ -66,7 +75,7 @@ export const usePlugins = () => { } }; - const loadAllPlugins = async () => { + const asyncLoadAllPlugins = async () => { if (import.meta.env.DEV) { // 開発時: ホットリロード対応、src-tauri以下のpluginsから直接読み込み Object.entries(dev_plugin_mapping).forEach(([key, plugin_module]) => { @@ -169,8 +178,7 @@ export const usePlugins = () => { }; - const asyncUpdatePluginInfoList = async () => { - pendingPluginsInfoList(); + const asyncFetchPluginsInfo = async () => { try { const response = await asyncTauriFetchGithub(PLUGIN_LIST_URL); if (response.status !== 200) { @@ -194,7 +202,7 @@ export const usePlugins = () => { } }) ); - updatePluginsInfoList(updated_list); + return updated_list; } catch (error) { console.error("Error fetching plugin info list:", error); } @@ -236,22 +244,26 @@ export const usePlugins = () => { }; } + const setSavedPluginsStatus = (plugins_status) => { + pendingSavedPluginsStatus(); + asyncStdoutToPython("/set/data/plugins_status", plugins_status); + }; + return { - asyncUpdatePluginInfoList, + asyncFetchPluginsInfo, - loadAllPlugins, + asyncLoadAllPlugins, downloadAndExtractPlugin, - currentLoadedPluginsList, - updateLoadedPluginsList, - currentSavedPluginsStatus, updateSavedPluginsStatus, - currentPluginsInfoList, - updatePluginsInfoList, + currentPluginsData, + updatePluginsData, + + setSavedPluginsStatus, }; }; diff --git a/src-ui/logics/useReceiveRoutes.js b/src-ui/logics/useReceiveRoutes.js index 53a2feb3..4575c3d8 100644 --- a/src-ui/logics/useReceiveRoutes.js +++ b/src-ui/logics/useReceiveRoutes.js @@ -73,6 +73,7 @@ import { useOverlayShowOnlyTranslatedMessages, useEnableNotificationVrcSfx, useHotkeys, + usePlugins, useOscIpAddress, useOscPort, } from "@logics_configs"; @@ -176,6 +177,7 @@ export const useReceiveRoutes = () => { const { updateEnableNotificationVrcSfx } = useEnableNotificationVrcSfx(); const { updateHotkeys } = useHotkeys(); + const { updateSavedPluginsStatus } = usePlugins(); const { updateOscIpAddress } = useOscIpAddress(); const { updateOscPort } = useOscPort(); @@ -488,6 +490,10 @@ export const useReceiveRoutes = () => { "/get/data/hotkeys": updateHotkeys, "/set/data/hotkeys": updateHotkeys, + // Plugins + "/get/data/plugins_status": updateSavedPluginsStatus, + "/set/data/plugins_status": updateSavedPluginsStatus, + // Advanced Settings "/get/data/osc_ip_address": updateOscIpAddress, "/set/data/osc_ip_address": updateOscIpAddress, diff --git a/src-ui/store.js b/src-ui/store.js index 2da4c128..410a3809 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -275,12 +275,9 @@ export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createA }, "Hotkeys"); // Plugins -export const { atomInstance: Atom_LoadedPluginsList, useHook: useStore_LoadedPluginsList } = createAtomWithHook([], "LoadedPluginsList"); -export const { atomInstance: Atom_SavedPluginsStatus, useHook: useStore_SavedPluginsStatus } = createAtomWithHook([ - { plugin_id: "vrct_plugin_example_1", is_enabled: true }, - { plugin_id: "vrct_plugin_example_2", is_enabled: false } -], "SavedPluginsStatus"); -export const { atomInstance: Atom_PluginsInfoList, useHook: useStore_PluginsInfoList } = createAtomWithHook([], "PluginsInfoList"); +// export const { atomInstance: Atom_LoadedPluginsList, useHook: useStore_LoadedPluginsList } = createAtomWithHook([], "LoadedPluginsList"); +export const { atomInstance: Atom_SavedPluginsStatus, useHook: useStore_SavedPluginsStatus } = createAtomWithHook([], "SavedPluginsStatus"); +export const { atomInstance: Atom_PluginsData, useHook: useStore_PluginsData } = createAtomWithHook([], "PluginsData"); // Advanced Settings export const { atomInstance: Atom_OscIpAddress, useHook: useStore_OscIpAddress } = createAtomWithHook("127.0.0.1", "OscIpAddress"); From e65e9be052225aada1d675149a1b066ebed00e4b Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Tue, 25 Mar 2025 16:35:17 +0900 Subject: [PATCH 12/68] [Update] Add plugin_info.json load function. --- src-tauri/plugins/plugin_examples/index.jsx | 1 - .../plugins/plugin_examples/plugin_info.json | 8 ++ src-ui/logics/configs/plugins/usePlugins.js | 77 ++++++++++++------- 3 files changed, 56 insertions(+), 30 deletions(-) create mode 100644 src-tauri/plugins/plugin_examples/plugin_info.json diff --git a/src-tauri/plugins/plugin_examples/index.jsx b/src-tauri/plugins/plugin_examples/index.jsx index 58441bd8..64c0fe86 100644 --- a/src-tauri/plugins/plugin_examples/index.jsx +++ b/src-tauri/plugins/plugin_examples/index.jsx @@ -9,7 +9,6 @@ export const init = (plugin_context) => { }; plugin_context.registerComponent({ - plugin_id: "plugin_example_1_my_plugin", location: "main_section", component: EntryComponents, }); diff --git a/src-tauri/plugins/plugin_examples/plugin_info.json b/src-tauri/plugins/plugin_examples/plugin_info.json new file mode 100644 index 00000000..ffcd0a76 --- /dev/null +++ b/src-tauri/plugins/plugin_examples/plugin_info.json @@ -0,0 +1,8 @@ +{ + "title": "VRCT Example Plugins 1", + "plugin_id": "vrct_plugin_example_1", + "asset_name": "vrct_plugin_example_1.zip", + "plugin_version": "0.0.1", + "min_supported_vrct_version": "3.0.4", + "max_supported_vrct_version": "3.0.6" +} \ No newline at end of file diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 7bc55256..2438aeef 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -11,6 +11,7 @@ import { useStdoutToPython } from "@logics/useStdoutToPython"; import { transform } from "@babel/standalone"; import { writeFile, createDir, exists, removeDir, readDir, BaseDirectory, readTextFile } from "@tauri-apps/api/fs"; const dev_plugin_mapping = import.meta.glob("/src-tauri/plugins/**/index.jsx", { eager: true }); +const dev_plugin_info_mapping = import.meta.glob("/src-tauri/plugins/**/plugin_info.json", { eager: true }); import JSZip from "jszip"; import { useFetch } from "@logics_common"; @@ -29,31 +30,37 @@ export const usePlugins = () => { const { asyncTauriFetchGithub } = useFetch(); - const plugin_context = { - registerComponent: ({ plugin_id, location, component }) => { - if (!plugin_id || !location || !component) { - return console.error("An invalid plugin was detected.", plugin_id, location, component); - } - updatePluginsData(prev => { - const is_already_registered = prev.data.some(old_value => old_value.plugin_id === plugin_id); - const new_value = prev.data.map(old_value => - old_value.plugin_id === plugin_id - ? { ...old_value, location, component, is_downloaded: true } - : old_value - ); + const generatePluginContext= (plugin_info) => { + const plugin_context = { + registerComponent: ({ location, component }) => { + if (!plugin_info.plugin_id || !location || !component) { + return console.error("An invalid plugin was detected.", plugin_info.plugin_id, location, component); + } + updatePluginsData(prev => { + const is_already_registered = prev.data.some(old_value => old_value.plugin_id === plugin_info.plugin_id); - return is_already_registered - ? new_value - : [...new_value, { plugin_id, location, component, is_downloaded: true }]; - }); - }, - createAtomWithHook: (...args) => createAtomWithHook(...args) - }; + const new_value = prev.data.map(old_value => + old_value.plugin_id === plugin_info.plugin_id + ? { ...old_value, ...plugin_info, location, component, is_downloaded: true } + : old_value + ); - const asyncLoadPlugin = async (plugin_relative_path) => { - plugin_relative_path = "plugins/" + plugin_relative_path; + return is_already_registered + ? new_value + : [...new_value, { plugin_id: plugin_info.plugin_id, location, component, is_downloaded: true }]; + }); + }, + createAtomWithHook: (...args) => createAtomWithHook(...args) + }; + return plugin_context; + } + + const asyncLoadPlugin = async (plugin_folder_relative_path) => { + const init_path = "plugins/" + plugin_folder_relative_path +"/index.esm.js"; + const plugin_info_path = "plugins/" + plugin_folder_relative_path +"/plugin_info.json"; try { - const plugin_code = await readTextFile(plugin_relative_path, { dir: BaseDirectory.Resource, recursive: true }); + const plugin_info = await readTextFile(plugin_info_path, { dir: BaseDirectory.Resource, recursive: true }); + const plugin_code = await readTextFile(init_path, { dir: BaseDirectory.Resource, recursive: true }); const cleaned_code = removeImportStatements(plugin_code); const transpiled_code = transform(cleaned_code, { presets: [ @@ -68,10 +75,10 @@ export const usePlugins = () => { URL.revokeObjectURL(blob_url); if (plugin_module && plugin_module.init) { - plugin_module.init(plugin_context); + plugin_module.init(generatePluginContext(plugin_info)); } } catch (error) { - console.error("Failed to load plugin from", plugin_relative_path, error); + console.error("Failed to load plugin from", plugin_folder_relative_path, error); } }; @@ -79,6 +86,18 @@ export const usePlugins = () => { if (import.meta.env.DEV) { // 開発時: ホットリロード対応、src-tauri以下のpluginsから直接読み込み Object.entries(dev_plugin_mapping).forEach(([key, plugin_module]) => { + // 例: key が "/src-tauri/plugins/sample/index.jsx" の場合、plugin_info.json のパスは同じディレクトリ内にある + const pluginInfoKey = key.replace("index.jsx", "plugin_info.json"); + const plugin_info = dev_plugin_info_mapping[pluginInfoKey]; + + if (!plugin_info) { + console.error("plugin_info.json has not found:", pluginInfoKey); + return; + } + + // plugin_info を使ってプラグインコンテキストを生成 + const plugin_context = generatePluginContext(plugin_info); + if (plugin_module && plugin_module.init) { plugin_module.init(plugin_context); } @@ -87,8 +106,8 @@ export const usePlugins = () => { try { const plugin_files = await readDir("plugins", { dir: BaseDirectory.Resource, recursive: true }); for (const target_dir of plugin_files) { - const target_path = target_dir.name + "/index.esm.js"; - await asyncLoadPlugin(target_path, plugin_context); + const target_path = target_dir.name; + await asyncLoadPlugin(target_path); } } catch (error) { console.error("Error loading plugins:", error); @@ -113,7 +132,7 @@ export const usePlugins = () => { const zip = await JSZip.loadAsync(bytes); // 展開先ディレクトリのパス(例:"plugins/" とする) - const target_plugin_path = "plugins/" + plugin.asset_name.replace(".zip", ""); + const target_plugin_path = "plugins/" + plugin.plugin_id; // 既に存在する場合は削除してから新規作成 if (await exists(target_plugin_path, { dir: BaseDirectory.Resource, recursive: true })) { await removeDir(target_plugin_path, { dir: BaseDirectory.Resource, recursive: true }); @@ -154,8 +173,8 @@ export const usePlugins = () => { await Promise.all(file_promises); console.log("Plugin downloaded successfully."); - const index_file_relative_path = plugin.asset_name.replace(".zip", "") + "/index.esm.js"; - await asyncLoadPlugin(index_file_relative_path, plugin_context); + const index_file_relative_path = plugin.plugin_id; + await asyncLoadPlugin(index_file_relative_path); console.log("Plugin loaded successfully."); } catch (error) { From 92ea06eb776ab30f25fb9869fb6976cb229d7f15 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Tue, 25 Mar 2025 16:53:52 +0900 Subject: [PATCH 13/68] [bugfix] Fix load json format error. --- src-ui/logics/configs/plugins/usePlugins.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 2438aeef..f0a4073b 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -56,10 +56,12 @@ export const usePlugins = () => { } const asyncLoadPlugin = async (plugin_folder_relative_path) => { - const init_path = "plugins/" + plugin_folder_relative_path +"/index.esm.js"; - const plugin_info_path = "plugins/" + plugin_folder_relative_path +"/plugin_info.json"; + const init_path = "plugins/" + plugin_folder_relative_path + "/index.esm.js"; + const plugin_info_path = "plugins/" + plugin_folder_relative_path + "/plugin_info.json"; try { - const plugin_info = await readTextFile(plugin_info_path, { dir: BaseDirectory.Resource, recursive: true }); + const plugin_info_json = await readTextFile(plugin_info_path, { dir: BaseDirectory.Resource, recursive: true }); + const plugin_info = JSON.parse(plugin_info_json); + const plugin_code = await readTextFile(init_path, { dir: BaseDirectory.Resource, recursive: true }); const cleaned_code = removeImportStatements(plugin_code); const transpiled_code = transform(cleaned_code, { From 824a9fa0a9f4c2f9c6949f89d53dbd3761839b2e Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Tue, 25 Mar 2025 17:57:03 +0900 Subject: [PATCH 14/68] [Update] Plugins: Move location from index to plugin_info.json. --- src-tauri/plugins/plugin_examples/index.jsx | 5 +---- src-tauri/plugins/plugin_examples/plugin_info.json | 1 + src-ui/logics/configs/plugins/usePlugins.js | 11 +++++------ 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src-tauri/plugins/plugin_examples/index.jsx b/src-tauri/plugins/plugin_examples/index.jsx index 64c0fe86..4ded2716 100644 --- a/src-tauri/plugins/plugin_examples/index.jsx +++ b/src-tauri/plugins/plugin_examples/index.jsx @@ -8,10 +8,7 @@ export const init = (plugin_context) => { return ; }; - plugin_context.registerComponent({ - location: "main_section", - component: EntryComponents, - }); + plugin_context.registerComponent(EntryComponents); }; export default init; \ No newline at end of file diff --git a/src-tauri/plugins/plugin_examples/plugin_info.json b/src-tauri/plugins/plugin_examples/plugin_info.json index ffcd0a76..303f0ecb 100644 --- a/src-tauri/plugins/plugin_examples/plugin_info.json +++ b/src-tauri/plugins/plugin_examples/plugin_info.json @@ -2,6 +2,7 @@ "title": "VRCT Example Plugins 1", "plugin_id": "vrct_plugin_example_1", "asset_name": "vrct_plugin_example_1.zip", + "location": "main_section", "plugin_version": "0.0.1", "min_supported_vrct_version": "3.0.4", "max_supported_vrct_version": "3.0.6" diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index f0a4073b..984ee189 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -23,7 +23,6 @@ const PLUGIN_LIST_URL = "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_p export const usePlugins = () => { const { asyncStdoutToPython } = useStdoutToPython(); - // const { currentLoadedPluginsList, updateLoadedPluginsList } = useStore_LoadedPluginsList(); const { currentSavedPluginsStatus, updateSavedPluginsStatus, pendingSavedPluginsStatus } = useStore_SavedPluginsStatus(); const { currentPluginsData, updatePluginsData, pendingPluginsData } = useStore_PluginsData(); const { currentSoftwareVersion } = useSoftwareVersion(); @@ -32,22 +31,22 @@ export const usePlugins = () => { const generatePluginContext= (plugin_info) => { const plugin_context = { - registerComponent: ({ location, component }) => { - if (!plugin_info.plugin_id || !location || !component) { - return console.error("An invalid plugin was detected.", plugin_info.plugin_id, location, component); + registerComponent: (component) => { + if (!plugin_info.plugin_id || !plugin_info.location || !component) { + return console.error("An invalid plugin was detected.", plugin_info.plugin_id, plugin_info.location, component); } updatePluginsData(prev => { const is_already_registered = prev.data.some(old_value => old_value.plugin_id === plugin_info.plugin_id); const new_value = prev.data.map(old_value => old_value.plugin_id === plugin_info.plugin_id - ? { ...old_value, ...plugin_info, location, component, is_downloaded: true } + ? { ...old_value, ...plugin_info, location: plugin_info.location, component, is_downloaded: true } : old_value ); return is_already_registered ? new_value - : [...new_value, { plugin_id: plugin_info.plugin_id, location, component, is_downloaded: true }]; + : [...new_value, { plugin_id: plugin_info.plugin_id, location: plugin_info.location, component, is_downloaded: true }]; }); }, createAtomWithHook: (...args) => createAtomWithHook(...args) From 1ebdefcd43d1adec7545e8d313cf7d1d0ec847f4 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 26 Mar 2025 00:45:29 +0900 Subject: [PATCH 15/68] [Update/bugfix] Plugins: For development, fix hot reload issue that was restart every time updated the files. --- src-tauri/plugins/index.js | 1 + src-ui/logics/configs/plugins/usePlugins.js | 25 ++++++++----------- src-ui/plugins/index.js | 9 +++++++ .../plugins/plugin_examples/index.jsx | 0 .../main_container/MainContainer.jsx | 0 .../plugins/plugin_examples/plugin_info.json | 0 .../plugins/plugin_examples/store/store.js | 0 vite.config.js | 7 ++---- 8 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 src-tauri/plugins/index.js create mode 100644 src-ui/plugins/index.js rename {src-tauri => src-ui}/plugins/plugin_examples/index.jsx (100%) rename {src-tauri => src-ui}/plugins/plugin_examples/main_container/MainContainer.jsx (100%) rename {src-tauri => src-ui}/plugins/plugin_examples/plugin_info.json (100%) rename {src-tauri => src-ui}/plugins/plugin_examples/store/store.js (100%) diff --git a/src-tauri/plugins/index.js b/src-tauri/plugins/index.js new file mode 100644 index 00000000..c8024261 --- /dev/null +++ b/src-tauri/plugins/index.js @@ -0,0 +1 @@ +// This is for preserving plugins folder. It will be detected and created 'plugins' folder to target/debug/ by tauri build. do not delete it. \ No newline at end of file diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 984ee189..2c337f7c 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -10,8 +10,8 @@ import { useStdoutToPython } from "@logics/useStdoutToPython"; import { transform } from "@babel/standalone"; import { writeFile, createDir, exists, removeDir, readDir, BaseDirectory, readTextFile } from "@tauri-apps/api/fs"; -const dev_plugin_mapping = import.meta.glob("/src-tauri/plugins/**/index.jsx", { eager: true }); -const dev_plugin_info_mapping = import.meta.glob("/src-tauri/plugins/**/plugin_info.json", { eager: true }); +import { dev_plugins } from "@dev_plugins_path"; + import JSZip from "jszip"; import { useFetch } from "@logics_common"; @@ -85,22 +85,17 @@ export const usePlugins = () => { const asyncLoadAllPlugins = async () => { if (import.meta.env.DEV) { - // 開発時: ホットリロード対応、src-tauri以下のpluginsから直接読み込み - Object.entries(dev_plugin_mapping).forEach(([key, plugin_module]) => { - // 例: key が "/src-tauri/plugins/sample/index.jsx" の場合、plugin_info.json のパスは同じディレクトリ内にある - const pluginInfoKey = key.replace("index.jsx", "plugin_info.json"); - const plugin_info = dev_plugin_info_mapping[pluginInfoKey]; - - if (!plugin_info) { - console.error("plugin_info.json has not found:", pluginInfoKey); + // `dev_plugins` を利用してプラグインを登録 + dev_plugins.forEach(({ index, plugin_info }) => { + if (!index || !plugin_info) { + console.error("Invalid development plugin detected", index, plugin_info); return; } - - // plugin_info を使ってプラグインコンテキストを生成 const plugin_context = generatePluginContext(plugin_info); - - if (plugin_module && plugin_module.init) { - plugin_module.init(plugin_context); + if (index.init) { + index.init(plugin_context); + } else { + console.error("Plugin missing init function", plugin_info); } }); } else { diff --git a/src-ui/plugins/index.js b/src-ui/plugins/index.js new file mode 100644 index 00000000..010c3ff0 --- /dev/null +++ b/src-ui/plugins/index.js @@ -0,0 +1,9 @@ +import plugin_index_1 from "./plugin_examples/index.jsx"; +import plugin_info_1 from "./plugin_examples/plugin_info.json"; + +export const dev_plugins = [ + { + index: { init: plugin_index_1 }, + plugin_info: plugin_info_1, + } +]; \ No newline at end of file diff --git a/src-tauri/plugins/plugin_examples/index.jsx b/src-ui/plugins/plugin_examples/index.jsx similarity index 100% rename from src-tauri/plugins/plugin_examples/index.jsx rename to src-ui/plugins/plugin_examples/index.jsx diff --git a/src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx b/src-ui/plugins/plugin_examples/main_container/MainContainer.jsx similarity index 100% rename from src-tauri/plugins/plugin_examples/main_container/MainContainer.jsx rename to src-ui/plugins/plugin_examples/main_container/MainContainer.jsx diff --git a/src-tauri/plugins/plugin_examples/plugin_info.json b/src-ui/plugins/plugin_examples/plugin_info.json similarity index 100% rename from src-tauri/plugins/plugin_examples/plugin_info.json rename to src-ui/plugins/plugin_examples/plugin_info.json diff --git a/src-tauri/plugins/plugin_examples/store/store.js b/src-ui/plugins/plugin_examples/store/store.js similarity index 100% rename from src-tauri/plugins/plugin_examples/store/store.js rename to src-ui/plugins/plugin_examples/store/store.js diff --git a/vite.config.js b/vite.config.js index 1abffdf1..58c824fa 100644 --- a/vite.config.js +++ b/vite.config.js @@ -33,11 +33,6 @@ export default defineConfig(async () => ({ resolve: { alias: { - "react": path.resolve(__dirname, "node_modules/react"), - "react-dom": path.resolve(__dirname, "node_modules/react-dom"), - - - "@root": path.resolve(__dirname), "@test_data": path.resolve(__dirname, "./test_data.js"), @@ -53,6 +48,8 @@ export default defineConfig(async () => ({ "@setting_box": path.resolve(__dirname, "src-ui/app/config_page/setting_section/setting_box/index.js"), "@common_components": path.resolve(__dirname, "src-ui/common_components/index.js"), + + "@dev_plugins_path": path.resolve(__dirname, "src-ui/plugins/index.js"), }, }, From 0c6829616c60df57df53169f212d016b38f07b91 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 26 Mar 2025 21:46:43 +0900 Subject: [PATCH 16/68] [Update/bugfix] Plugins: Fix plugins position. Switchable rendering MessageContainer and Plugins. --- src-ui/app/main_page/main_section/MainSection.jsx | 14 ++++++++++---- .../main_page/main_section/MainSection.module.scss | 2 +- src-ui/app/main_page/main_section/PluginHost.jsx | 11 ++++------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src-ui/app/main_page/main_section/MainSection.jsx b/src-ui/app/main_page/main_section/MainSection.jsx index 2dec160c..d6b1f298 100644 --- a/src-ui/app/main_page/main_section/MainSection.jsx +++ b/src-ui/app/main_page/main_section/MainSection.jsx @@ -2,7 +2,7 @@ import { useTranslation } from "react-i18next"; import styles from "./MainSection.module.scss"; import { TopBar } from "./top_bar/TopBar"; -// import { MessageContainer } from "./message_container/MessageContainer"; +import { MessageContainer } from "./message_container/MessageContainer"; import { LanguageSelector } from "./language_selector/LanguageSelector"; import { useStore_IsOpenedLanguageSelector } from "@store"; @@ -12,14 +12,20 @@ import { SubtitleSystemContainer } from "./subtitle_system_container/SubtitleSys import { PluginHost } from "./PluginHost"; +import { usePlugins } from "@logics_configs"; + export const MainSection = () => { + const { currentPluginsData } = usePlugins(); + + const render_plugins = currentPluginsData.data.filter((plugin) => plugin.is_enabled && plugin.location === "main_section"); return (
- {/* */} - - {/* */} + {render_plugins.length + ? + : + }
); diff --git a/src-ui/app/main_page/main_section/MainSection.module.scss b/src-ui/app/main_page/main_section/MainSection.module.scss index 29a7907a..0f592a3a 100644 --- a/src-ui/app/main_page/main_section/MainSection.module.scss +++ b/src-ui/app/main_page/main_section/MainSection.module.scss @@ -4,7 +4,7 @@ height: 100%; display: flex; flex-direction: column; - justify-content: space-between; + // justify-content: space-between; } .language_selector_container { diff --git a/src-ui/app/main_page/main_section/PluginHost.jsx b/src-ui/app/main_page/main_section/PluginHost.jsx index 5c01328c..071ac934 100644 --- a/src-ui/app/main_page/main_section/PluginHost.jsx +++ b/src-ui/app/main_page/main_section/PluginHost.jsx @@ -1,17 +1,14 @@ import React from "react"; -import { usePlugins } from "@logics_configs"; -export const PluginHost = () => { - const { currentPluginsData } = usePlugins(); +export const PluginHost = ({render_components}) => { return ( -
- {currentPluginsData.data - .filter((plugin) => plugin.is_enabled && plugin.location === "main_section") + <> + {render_components .map((plugin, index) => { const PluginComponent = plugin.component; return PluginComponent ? : null; })} -
+ ); }; \ No newline at end of file From a59b9282df73346bfda02b0c00016921d2fc8fd6 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 27 Mar 2025 02:43:13 +0900 Subject: [PATCH 17/68] [Update] Plugins(VRCT Subtitles as testing one): Provide-able store and functions from main app. --- src-ui/app/App.jsx | 3 -- .../main_page/main_section/MainSection.jsx | 1 - src-ui/logics/configs/plugins/usePlugins.js | 4 +- src-ui/plugins/plugin_examples/index.jsx | 14 +++-- .../main_container/MainContainer.jsx | 22 -------- .../plugins/plugin_examples/plugin_info.json | 6 +-- src-ui/plugins/plugin_examples/store/store.js | 43 +++++++++++---- .../SubtitleSystemContainer.jsx | 0 .../SubtitleSystemContainer.module.scss | 0 .../_controllers/SubtitlesController.jsx | 7 ++- .../_logics/useSubtitles.jsx | 52 ++++++++++--------- .../_subtitles_utils.js | 0 .../CountdownContainer.jsx | 0 .../CountdownContainer.module.scss | 0 .../InputFileContainer.jsx | 0 .../InputFileContainer.module.scss | 0 .../ModeSelectorContainer.jsx | 0 .../ModeSelectorContainer.module.scss | 0 .../PlayControlContainer.jsx | 0 .../PlayControlContainer.module.scss | 0 .../SubtitlesListContainer.jsx | 0 .../SubtitlesListContainer.module.scss | 0 src-ui/store.js | 20 +------ 23 files changed, 85 insertions(+), 87 deletions(-) delete mode 100644 src-ui/plugins/plugin_examples/main_container/MainContainer.jsx rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/SubtitleSystemContainer.jsx (100%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/SubtitleSystemContainer.module.scss (100%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/_controllers/SubtitlesController.jsx (86%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/_logics/useSubtitles.jsx (82%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/_subtitles_utils.js (100%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/countdown_container/CountdownContainer.jsx (100%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/countdown_container/CountdownContainer.module.scss (100%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/input_file_container/InputFileContainer.jsx (100%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/input_file_container/InputFileContainer.module.scss (100%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx (100%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss (100%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/play_control_container/PlayControlContainer.jsx (100%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/play_control_container/PlayControlContainer.module.scss (100%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx (100%) rename src-ui/{app/main_page/main_section => plugins/plugin_examples}/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss (100%) diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index 269bf824..c102af71 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -23,8 +23,6 @@ import { SnackbarController } from "./snackbar_controller/SnackbarController"; import styles from "./App.module.scss"; import { useIsBackendReady, useIsSoftwareUpdating, useIsVrctAvailable, useWindow } from "@logics_common"; -import { SubtitlesController } from "./main_page/main_section/subtitle_system_container/_controllers/subtitlesController.jsx"; - export const App = () => { const { currentIsVrctAvailable } = useIsVrctAvailable(); const { currentIsBackendReady } = useIsBackendReady(); @@ -59,7 +57,6 @@ const Contents = ({ fetchPluginsHasRunRef }) => { return ( <> - {currentIsSoftwareUpdating.data === false diff --git a/src-ui/app/main_page/main_section/MainSection.jsx b/src-ui/app/main_page/main_section/MainSection.jsx index d6b1f298..0eaf272d 100644 --- a/src-ui/app/main_page/main_section/MainSection.jsx +++ b/src-ui/app/main_page/main_section/MainSection.jsx @@ -8,7 +8,6 @@ import { LanguageSelector } from "./language_selector/LanguageSelector"; import { useStore_IsOpenedLanguageSelector } from "@store"; import { useLanguageSettings } from "@logics_main"; import { useEffect } from "react"; -import { SubtitleSystemContainer } from "./subtitle_system_container/SubtitleSystemContainer"; import { PluginHost } from "./PluginHost"; diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 2c337f7c..9a801674 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -16,6 +16,7 @@ import JSZip from "jszip"; import { useFetch } from "@logics_common"; import { useSoftwareVersion } from "@logics_configs"; +import * as logic_configs from "@logics_configs"; // PLUGIN_LIST_URL は中央リポジトリにある、各プラグインの plugin_info.json への URL の配列を保持する JSON の URL @@ -49,7 +50,8 @@ export const usePlugins = () => { : [...new_value, { plugin_id: plugin_info.plugin_id, location: plugin_info.location, component, is_downloaded: true }]; }); }, - createAtomWithHook: (...args) => createAtomWithHook(...args) + createAtomWithHook: (...args) => createAtomWithHook(...args), + logic_configs: logic_configs, }; return plugin_context; } diff --git a/src-ui/plugins/plugin_examples/index.jsx b/src-ui/plugins/plugin_examples/index.jsx index 4ded2716..011c133d 100644 --- a/src-ui/plugins/plugin_examples/index.jsx +++ b/src-ui/plugins/plugin_examples/index.jsx @@ -1,11 +1,19 @@ -import { initStore } from "./store/store"; -import { MainContainer } from "./main_container/MainContainer"; +import { initStore, StoreContext } from "./store/store.js"; +import { SubtitleSystemContainer } from "./subtitle_system_container/SubtitleSystemContainer"; +import { SubtitlesController } from "./subtitle_system_container/_controllers/subtitlesController.jsx"; export const init = (plugin_context) => { initStore(plugin_context.createAtomWithHook); + const { logic_configs } = plugin_context; const EntryComponents = () => { - return ; + return ( + + + + + + ); }; plugin_context.registerComponent(EntryComponents); diff --git a/src-ui/plugins/plugin_examples/main_container/MainContainer.jsx b/src-ui/plugins/plugin_examples/main_container/MainContainer.jsx deleted file mode 100644 index eaccc0aa..00000000 --- a/src-ui/plugins/plugin_examples/main_container/MainContainer.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useStore } from "../store/store"; -import { useEffect } from "react"; - -export const MainContainer = () => { - const { updateCountPluginState, currentCountPluginState } = useStore("useStore_CountPluginState"); - - const incrementCount = () => { - updateCountPluginState((prev_value) => ({ - count: prev_value.data.count + 1, - })); - }; - - useEffect(() => { - }, []) - - return ( -
-

1 Zipped Dev Plugin Count: {currentCountPluginState?.data?.count}

- -
- ); -}; \ No newline at end of file diff --git a/src-ui/plugins/plugin_examples/plugin_info.json b/src-ui/plugins/plugin_examples/plugin_info.json index 303f0ecb..f388a9cc 100644 --- a/src-ui/plugins/plugin_examples/plugin_info.json +++ b/src-ui/plugins/plugin_examples/plugin_info.json @@ -1,7 +1,7 @@ { - "title": "VRCT Example Plugins 1", - "plugin_id": "vrct_plugin_example_1", - "asset_name": "vrct_plugin_example_1.zip", + "title": "VRCT Subtitles", + "plugin_id": "vrct_plugin_subtitles", + "asset_name": "vrct_plugin_subtitles.zip", "location": "main_section", "plugin_version": "0.0.1", "min_supported_vrct_version": "3.0.4", diff --git a/src-ui/plugins/plugin_examples/store/store.js b/src-ui/plugins/plugin_examples/store/store.js index 9efd9dcf..40d80bb4 100644 --- a/src-ui/plugins/plugin_examples/store/store.js +++ b/src-ui/plugins/plugin_examples/store/store.js @@ -2,15 +2,30 @@ const store_hooks = {}; export const initStore = (createAtomWithHook) => { Object.assign(store_hooks, { - useStore_CountPluginState: createAtomWithHook( - { count: 10 }, - "CountPluginState" - ).useHook, + // useStore_CountPluginState: createAtomWithHook( + // { count: 10 }, + // "CountPluginState" + // ).useHook, - useStore_AnotherState: createAtomWithHook( - { value: "initial" }, - "AnotherState" - ).useHook, + // useStore_AnotherState: createAtomWithHook( + // { value: "initial" }, + // "AnotherState" + // ).useHook, + + useStore_IsSubtitlePlaying: createAtomWithHook(false, "IsSubtitlePlaying", { is_state_ok: true }).useHook, + useStore_SubtitlePlaybackMode: createAtomWithHook("relative", "SubtitlePlaybackMode", { is_state_ok: true }).useHook, + useStore_SubtitleAbsoluteTargetTime: createAtomWithHook({ + hour: "23", + minute: "00", + }, "SubtitleAbsoluteTargetTime", { is_state_ok: true }).useHook, + useStore_IsCuesScheduled: createAtomWithHook(false, "IsCuesScheduled", { is_state_ok: true }).useHook, + useStore_CountdownAdjustment: createAtomWithHook(0, "CountdownAdjustment", { is_state_ok: true }).useHook, + useStore_EffectiveCountdown: createAtomWithHook(null, "EffectiveCountdown", { is_state_ok: true }).useHook, + useStore_SubtitleCues: createAtomWithHook([], "SubtitleCues", { is_state_ok: true }).useHook, + + useStore_SubtitleTimers: createAtomWithHook([], "SubtitleTimers", { is_state_ok: true }).useHook, + useStore_SubtitleCountdownTimerId: createAtomWithHook([], "SubtitleCountdownTimerId", { is_state_ok: true }).useHook, + useStore_SubtitleFileName: createAtomWithHook("ファイルが選択されていません", "SubtitleFileName", { is_state_ok: true }).useHook, }); }; @@ -19,4 +34,14 @@ export const useStore = (hook_name) => { throw new Error(`Hook ${hook_name} is not initialized.`); } return store_hooks[hook_name](); -}; \ No newline at end of file +}; + + +// StoreContext.js +import React, { createContext, useContext } from "react"; + +export const StoreContext = createContext(null); + +export const useStoreContext = () => { + return useContext(StoreContext); +}; diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.jsx b/src-ui/plugins/plugin_examples/subtitle_system_container/SubtitleSystemContainer.jsx similarity index 100% rename from src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.jsx rename to src-ui/plugins/plugin_examples/subtitle_system_container/SubtitleSystemContainer.jsx diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.module.scss b/src-ui/plugins/plugin_examples/subtitle_system_container/SubtitleSystemContainer.module.scss similarity index 100% rename from src-ui/app/main_page/main_section/subtitle_system_container/SubtitleSystemContainer.module.scss rename to src-ui/plugins/plugin_examples/subtitle_system_container/SubtitleSystemContainer.module.scss diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/_controllers/SubtitlesController.jsx b/src-ui/plugins/plugin_examples/subtitle_system_container/_controllers/SubtitlesController.jsx similarity index 86% rename from src-ui/app/main_page/main_section/subtitle_system_container/_controllers/SubtitlesController.jsx rename to src-ui/plugins/plugin_examples/subtitle_system_container/_controllers/SubtitlesController.jsx index 720c3d28..53380205 100644 --- a/src-ui/app/main_page/main_section/subtitle_system_container/_controllers/SubtitlesController.jsx +++ b/src-ui/plugins/plugin_examples/subtitle_system_container/_controllers/SubtitlesController.jsx @@ -1,9 +1,12 @@ -import { useSendTextToOverlay } from "@logics_configs"; +import { useStoreContext } from "../../store/store.js"; + import { useSubtitles } from "../_logics/useSubtitles"; import { secToDayTime } from "../_subtitles_utils" import { useEffect } from "react"; export const SubtitlesController = () => { - const { sendTextToOverlay } = useSendTextToOverlay(); + const logic_configs = useStoreContext(); + const { sendTextToOverlay } = logic_configs.useSendTextToOverlay(); + const { currentIsSubtitlePlaying, currentIsCuesScheduled, diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/_logics/useSubtitles.jsx b/src-ui/plugins/plugin_examples/subtitle_system_container/_logics/useSubtitles.jsx similarity index 82% rename from src-ui/app/main_page/main_section/subtitle_system_container/_logics/useSubtitles.jsx rename to src-ui/plugins/plugin_examples/subtitle_system_container/_logics/useSubtitles.jsx index 9ce1d626..fe0b6e3c 100644 --- a/src-ui/app/main_page/main_section/subtitle_system_container/_logics/useSubtitles.jsx +++ b/src-ui/plugins/plugin_examples/subtitle_system_container/_logics/useSubtitles.jsx @@ -1,35 +1,39 @@ -import { useSendTextToOverlay } from "@logics_configs"; -import { - useStore_SubtitleFileName, - useStore_IsSubtitlePlaying, - useStore_SubtitlePlaybackMode, - useStore_SubtitleAbsoluteTargetTime, - useStore_IsCuesScheduled, - useStore_CountdownAdjustment, - useStore_EffectiveCountdown, - useStore_SubtitleCues, +import { useStore, useStoreContext } from "../../store/store.js"; - useStore_SubtitleTimers, - useStore_SubtitleCountdownTimerId, -} from "@store"; +// import { useSendTextToOverlay } from "@logics_configs"; +// import { +// useStore_SubtitleFileName, +// useStore_IsSubtitlePlaying, +// useStore_SubtitlePlaybackMode, +// useStore_SubtitleAbsoluteTargetTime, +// useStore_IsCuesScheduled, +// useStore_CountdownAdjustment, +// useStore_EffectiveCountdown, +// useStore_SubtitleCues, + +// useStore_SubtitleTimers, +// useStore_SubtitleCountdownTimerId, +// } from "../../store/store.js"; export const useSubtitles = () => { - const { sendTextToOverlay } = useSendTextToOverlay(); - const { currentSubtitleFileName, updateSubtitleFileName } = useStore_SubtitleFileName(); - const { currentIsSubtitlePlaying, updateIsSubtitlePlaying } = useStore_IsSubtitlePlaying(); - const { currentSubtitlePlaybackMode, updateSubtitlePlaybackMode } = useStore_SubtitlePlaybackMode(); - const { currentSubtitleAbsoluteTargetTime, updateSubtitleAbsoluteTargetTime } = useStore_SubtitleAbsoluteTargetTime(); - const { currentIsCuesScheduled, updateIsCuesScheduled } = useStore_IsCuesScheduled(); + const logic_configs = useStoreContext(); + const { sendTextToOverlay } = logic_configs.useSendTextToOverlay(); - const { currentCountdownAdjustment, updateCountdownAdjustment } = useStore_CountdownAdjustment(); - const { currentEffectiveCountdown, updateEffectiveCountdown } = useStore_EffectiveCountdown(); - const { currentSubtitleCues, updateSubtitleCues } = useStore_SubtitleCues(); + const { currentSubtitleFileName, updateSubtitleFileName } = useStore("useStore_SubtitleFileName"); + const { currentIsSubtitlePlaying, updateIsSubtitlePlaying } = useStore("useStore_IsSubtitlePlaying"); + const { currentSubtitlePlaybackMode, updateSubtitlePlaybackMode } = useStore("useStore_SubtitlePlaybackMode"); + const { currentSubtitleAbsoluteTargetTime, updateSubtitleAbsoluteTargetTime } = useStore("useStore_SubtitleAbsoluteTargetTime"); + const { currentIsCuesScheduled, updateIsCuesScheduled } = useStore("useStore_IsCuesScheduled"); + + const { currentCountdownAdjustment, updateCountdownAdjustment } = useStore("useStore_CountdownAdjustment"); + const { currentEffectiveCountdown, updateEffectiveCountdown } = useStore("useStore_EffectiveCountdown"); + const { currentSubtitleCues, updateSubtitleCues } = useStore("useStore_SubtitleCues"); // タイマー(setTimeout/setInterval)のID管理用 - const { currentSubtitleTimers, updateSubtitleTimers, addSubtitleTimers } = useStore_SubtitleTimers(); + const { currentSubtitleTimers, updateSubtitleTimers, addSubtitleTimers } = useStore("useStore_SubtitleTimers"); // const timersRef = useRef([]); // カウントダウンタイマー専用の ref - const { currentSubtitleCountdownTimerId, updateSubtitleCountdownTimerId, AddSubtitleCountdownTimerId } = useStore_SubtitleCountdownTimerId(); + const { currentSubtitleCountdownTimerId, updateSubtitleCountdownTimerId, AddSubtitleCountdownTimerId } = useStore("useStore_SubtitleCountdownTimerId"); // cues のスケジュールを行う(字幕開始時のオフセットは調整後のタイミングに合わせる) const scheduleCues = (offset) => { diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/_subtitles_utils.js b/src-ui/plugins/plugin_examples/subtitle_system_container/_subtitles_utils.js similarity index 100% rename from src-ui/app/main_page/main_section/subtitle_system_container/_subtitles_utils.js rename to src-ui/plugins/plugin_examples/subtitle_system_container/_subtitles_utils.js diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/countdown_container/CountdownContainer.jsx b/src-ui/plugins/plugin_examples/subtitle_system_container/countdown_container/CountdownContainer.jsx similarity index 100% rename from src-ui/app/main_page/main_section/subtitle_system_container/countdown_container/CountdownContainer.jsx rename to src-ui/plugins/plugin_examples/subtitle_system_container/countdown_container/CountdownContainer.jsx diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/countdown_container/CountdownContainer.module.scss b/src-ui/plugins/plugin_examples/subtitle_system_container/countdown_container/CountdownContainer.module.scss similarity index 100% rename from src-ui/app/main_page/main_section/subtitle_system_container/countdown_container/CountdownContainer.module.scss rename to src-ui/plugins/plugin_examples/subtitle_system_container/countdown_container/CountdownContainer.module.scss diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/input_file_container/InputFileContainer.jsx b/src-ui/plugins/plugin_examples/subtitle_system_container/input_file_container/InputFileContainer.jsx similarity index 100% rename from src-ui/app/main_page/main_section/subtitle_system_container/input_file_container/InputFileContainer.jsx rename to src-ui/plugins/plugin_examples/subtitle_system_container/input_file_container/InputFileContainer.jsx diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/input_file_container/InputFileContainer.module.scss b/src-ui/plugins/plugin_examples/subtitle_system_container/input_file_container/InputFileContainer.module.scss similarity index 100% rename from src-ui/app/main_page/main_section/subtitle_system_container/input_file_container/InputFileContainer.module.scss rename to src-ui/plugins/plugin_examples/subtitle_system_container/input_file_container/InputFileContainer.module.scss diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx b/src-ui/plugins/plugin_examples/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx similarity index 100% rename from src-ui/app/main_page/main_section/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx rename to src-ui/plugins/plugin_examples/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss b/src-ui/plugins/plugin_examples/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss similarity index 100% rename from src-ui/app/main_page/main_section/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss rename to src-ui/plugins/plugin_examples/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/play_control_container/PlayControlContainer.jsx b/src-ui/plugins/plugin_examples/subtitle_system_container/play_control_container/PlayControlContainer.jsx similarity index 100% rename from src-ui/app/main_page/main_section/subtitle_system_container/play_control_container/PlayControlContainer.jsx rename to src-ui/plugins/plugin_examples/subtitle_system_container/play_control_container/PlayControlContainer.jsx diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/play_control_container/PlayControlContainer.module.scss b/src-ui/plugins/plugin_examples/subtitle_system_container/play_control_container/PlayControlContainer.module.scss similarity index 100% rename from src-ui/app/main_page/main_section/subtitle_system_container/play_control_container/PlayControlContainer.module.scss rename to src-ui/plugins/plugin_examples/subtitle_system_container/play_control_container/PlayControlContainer.module.scss diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx b/src-ui/plugins/plugin_examples/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx similarity index 100% rename from src-ui/app/main_page/main_section/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx rename to src-ui/plugins/plugin_examples/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx diff --git a/src-ui/app/main_page/main_section/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss b/src-ui/plugins/plugin_examples/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss similarity index 100% rename from src-ui/app/main_page/main_section/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss rename to src-ui/plugins/plugin_examples/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss diff --git a/src-ui/store.js b/src-ui/store.js index 410a3809..7e88792b 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -290,22 +290,4 @@ export const { atomInstance: Atom_IsOpenedTranslatorSelector, useHook: useStore_ export const { atomInstance: Atom_SupportersData, useHook: useStore_SupportersData } = createAtomWithHook(null, "SupportersData", {is_state_ok: true}); export const { atomInstance: Atom_VrctPosterIndex, useHook: useStore_VrctPosterIndex } = createAtomWithHook(0, "VrctPosterIndex"); -export const { atomInstance: Atom_PosterShowcaseWorldPageIndex, useHook: useStore_PosterShowcaseWorldPageIndex } = createAtomWithHook(0, "PosterShowcaseWorldPageIndex"); - -// ----------------------------------------------- -// Subtitles -// ----------------------------------------------- -export const { atomInstance: Atom_IsSubtitlePlaying, useHook: useStore_IsSubtitlePlaying } = createAtomWithHook(false, "IsSubtitlePlaying", { is_state_ok: true }); -export const { atomInstance: Atom_SubtitlePlaybackMode, useHook: useStore_SubtitlePlaybackMode } = createAtomWithHook("relative", "SubtitlePlaybackMode", { is_state_ok: true }); -export const { atomInstance: Atom_SubtitleAbsoluteTargetTime, useHook: useStore_SubtitleAbsoluteTargetTime } = createAtomWithHook({ - hour: "23", - minute: "00", -}, "SubtitleAbsoluteTargetTime", { is_state_ok: true }); -export const { atomInstance: Atom_IsCuesScheduled, useHook: useStore_IsCuesScheduled } = createAtomWithHook(false, "IsCuesScheduled", { is_state_ok: true }); -export const { atomInstance: Atom_CountdownAdjustment, useHook: useStore_CountdownAdjustment } = createAtomWithHook(0, "CountdownAdjustment", { is_state_ok: true }); -export const { atomInstance: Atom_EffectiveCountdown, useHook: useStore_EffectiveCountdown } = createAtomWithHook(null, "EffectiveCountdown", { is_state_ok: true }); -export const { atomInstance: Atom_SubtitleCues, useHook: useStore_SubtitleCues } = createAtomWithHook([], "SubtitleCues", { is_state_ok: true }); - -export const { atomInstance: Atom_SubtitleTimers, useHook: useStore_SubtitleTimers } = createAtomWithHook([], "SubtitleTimers", { is_state_ok: true }); -export const { atomInstance: Atom_SubtitleCountdownTimerId, useHook: useStore_SubtitleCountdownTimerId } = createAtomWithHook([], "SubtitleCountdownTimerId", { is_state_ok: true }); -export const { atomInstance: Atom_SubtitleFileName, useHook: useStore_SubtitleFileName } = createAtomWithHook("ファイルが選択されていません", "SubtitleFileName", { is_state_ok: true }); \ No newline at end of file +export const { atomInstance: Atom_PosterShowcaseWorldPageIndex, useHook: useStore_PosterShowcaseWorldPageIndex } = createAtomWithHook(0, "PosterShowcaseWorldPageIndex"); \ No newline at end of file From 5681038c22979106d44e26f833873bb859d29fc2 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sat, 29 Mar 2025 16:22:00 +0900 Subject: [PATCH 18/68] [bugfix] Plugins(VRCT Subtitles as testing one): Apply styles by importing css file. --- .../_app_controllers/PluginsController.jsx | 2 ++ .../hotkeys_entry/HotkeysEntry.jsx | 2 +- .../setting_box/_components/slider/Slider.jsx | 2 +- .../SupportUsContainer.jsx | 2 +- .../setting_section/setting_box/vr/Vr.jsx | 2 +- .../version_label/VersionLabel.jsx | 2 +- .../SnackbarController.jsx | 2 +- .../common_components/checkbox/Checkbox.jsx | 2 +- src-ui/logics/configs/plugins/usePlugins.js | 22 +++++++++++++++++++ .../index.jsx | 19 ++++++++++++++-- .../plugin_info.json | 2 +- .../store/store.js | 0 .../SubtitleSystemContainer.jsx | 0 .../SubtitleSystemContainer.module.scss | 0 .../_controllers/SubtitlesController.jsx | 0 .../_logics/useSubtitles.jsx | 0 .../_subtitles_utils.js | 0 .../CountdownContainer.jsx | 0 .../CountdownContainer.module.scss | 0 .../InputFileContainer.jsx | 0 .../InputFileContainer.module.scss | 0 .../ModeSelectorContainer.jsx | 0 .../ModeSelectorContainer.module.scss | 0 .../PlayControlContainer.jsx | 2 +- .../PlayControlContainer.module.scss | 0 .../SubtitlesListContainer.jsx | 0 .../SubtitlesListContainer.module.scss | 0 src-ui/plugins/index.js | 4 ++-- 28 files changed, 52 insertions(+), 13 deletions(-) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/index.jsx (55%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/plugin_info.json (86%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/store/store.js (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/SubtitleSystemContainer.jsx (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/SubtitleSystemContainer.module.scss (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/_controllers/SubtitlesController.jsx (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/_logics/useSubtitles.jsx (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/_subtitles_utils.js (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/countdown_container/CountdownContainer.jsx (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/countdown_container/CountdownContainer.module.scss (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/input_file_container/InputFileContainer.jsx (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/input_file_container/InputFileContainer.module.scss (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/play_control_container/PlayControlContainer.jsx (94%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/play_control_container/PlayControlContainer.module.scss (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx (100%) rename src-ui/plugins/{plugin_examples => dev_plugin_subtitles}/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss (100%) diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx index d6f97ab4..8689e2f0 100644 --- a/src-ui/app/_app_controllers/PluginsController.jsx +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -1,8 +1,10 @@ import React, { useEffect, useRef } from "react"; import { usePlugins } from "@logics_configs"; +import clsx from "clsx"; if (typeof window !== "undefined") { window.React = React; + window.clsx = clsx; } export const PluginsController = ({ fetchPluginsHasRunRef }) => { diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/hotkeys_entry/HotkeysEntry.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/hotkeys_entry/HotkeysEntry.jsx index 750095e0..4e9efb65 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/hotkeys_entry/HotkeysEntry.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/hotkeys_entry/HotkeysEntry.jsx @@ -2,7 +2,7 @@ import styles from "./HotkeysEntry.module.scss"; import { _Entry } from "../_atoms/_entry/_Entry"; import { useState, useRef, useEffect } from "react"; import DeleteSvg from "@images/cancel.svg?react"; -import { clsx } from "clsx"; +import clsx from "clsx"; export const HotkeysEntry = (props) => { const [isAcceptingInput, setIsAcceptingInput] = useState(false); diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/slider/Slider.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/slider/Slider.jsx index 07c70ff2..8f61d725 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/slider/Slider.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/slider/Slider.jsx @@ -1,7 +1,7 @@ import React from "react"; import styles from "./Slider.module.scss"; import MUI_Slider from "@mui/material/Slider"; -import { clsx } from "clsx"; +import clsx from "clsx"; export const Slider = (props) => { return ( diff --git a/src-ui/app/config_page/setting_section/setting_box/supporters/support_us_container/SupportUsContainer.jsx b/src-ui/app/config_page/setting_section/setting_box/supporters/support_us_container/SupportUsContainer.jsx index 7c357a9f..1c305fe6 100644 --- a/src-ui/app/config_page/setting_section/setting_box/supporters/support_us_container/SupportUsContainer.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/supporters/support_us_container/SupportUsContainer.jsx @@ -3,7 +3,7 @@ import fanbox_logo from "@images/supporters/fanbox_logo.png"; import kofi_logo from "@images/supporters/kofi_logo.png"; import patreon_logo from "@images/supporters/patreon_logo.png"; import styles from "./SupportUsContainer.module.scss"; -import { clsx } from "clsx"; +import clsx from "clsx"; export const SupportUsContainer = () => { return ( diff --git a/src-ui/app/config_page/setting_section/setting_box/vr/Vr.jsx b/src-ui/app/config_page/setting_section/setting_box/vr/Vr.jsx index e1197b22..06920712 100644 --- a/src-ui/app/config_page/setting_section/setting_box/vr/Vr.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/vr/Vr.jsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; -import { clsx } from "clsx"; +import clsx from "clsx"; import styles from "./Vr.module.scss"; import { ui_configs } from "@ui_configs"; import { Slider } from "../_components/"; diff --git a/src-ui/app/config_page/version_label/VersionLabel.jsx b/src-ui/app/config_page/version_label/VersionLabel.jsx index f6325165..be095e5c 100644 --- a/src-ui/app/config_page/version_label/VersionLabel.jsx +++ b/src-ui/app/config_page/version_label/VersionLabel.jsx @@ -1,6 +1,6 @@ import { useTranslation } from "react-i18next"; import { useState } from "react"; -import { clsx } from "clsx"; +import clsx from "clsx"; import styles from "./VersionLabel.module.scss"; import { useSoftwareVersion } from "@logics_configs"; diff --git a/src-ui/app/snackbar_controller/SnackbarController.jsx b/src-ui/app/snackbar_controller/SnackbarController.jsx index 0c116c09..0b7c9609 100644 --- a/src-ui/app/snackbar_controller/SnackbarController.jsx +++ b/src-ui/app/snackbar_controller/SnackbarController.jsx @@ -1,4 +1,4 @@ -import { clsx } from "clsx"; +import clsx from "clsx"; import Snackbar from "@mui/material/Snackbar"; import Slide from "@mui/material/Slide"; diff --git a/src-ui/common_components/checkbox/Checkbox.jsx b/src-ui/common_components/checkbox/Checkbox.jsx index b9cdd7d3..50ee51a9 100644 --- a/src-ui/common_components/checkbox/Checkbox.jsx +++ b/src-ui/common_components/checkbox/Checkbox.jsx @@ -1,4 +1,4 @@ -import { clsx } from "clsx"; +import clsx from "clsx"; import styles from "./Checkbox.module.scss"; export const Checkbox = ({ checkboxId, diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 9a801674..a2b7857f 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -59,6 +59,7 @@ export const usePlugins = () => { const asyncLoadPlugin = async (plugin_folder_relative_path) => { const init_path = "plugins/" + plugin_folder_relative_path + "/index.esm.js"; const plugin_info_path = "plugins/" + plugin_folder_relative_path + "/plugin_info.json"; + const plugin_css_path = "plugins/" + plugin_folder_relative_path + "/main.css"; try { const plugin_info_json = await readTextFile(plugin_info_path, { dir: BaseDirectory.Resource, recursive: true }); const plugin_info = JSON.parse(plugin_info_json); @@ -80,6 +81,8 @@ export const usePlugins = () => { if (plugin_module && plugin_module.init) { plugin_module.init(generatePluginContext(plugin_info)); } + await loadPluginCSS(plugin_css_path); + } catch (error) { console.error("Failed to load plugin from", plugin_folder_relative_path, error); } @@ -290,3 +293,22 @@ const removeImportStatements = (code) => { .filter(line => !line.match(/^import\s+.*['"]react['"]/)) .join("\n"); }; + +// import { readTextFile, BaseDirectory } from "@tauri-apps/api/fs"; + +const loadPluginCSS = async (plugin_css_path) => { + try { + // プラグインフォルダのルートにある main.css を読み込む + const css_content = await readTextFile(plugin_css_path, { dir: BaseDirectory.Resource }); + // style タグを作成して head に挿入する + const style_tag = document.createElement("style"); + style_tag.id = `plugin-css-${plugin_css_path.replace(/[^a-zA-Z0-9_-]/g, "")}`; + style_tag.textContent = css_content; + document.head.appendChild(style_tag); + console.log("Plugin CSS loaded for:", plugin_css_path); + } catch (error) { + console.error("Failed to load plugin CSS from", plugin_css_path, error); + } +}; + +export { loadPluginCSS }; diff --git a/src-ui/plugins/plugin_examples/index.jsx b/src-ui/plugins/dev_plugin_subtitles/index.jsx similarity index 55% rename from src-ui/plugins/plugin_examples/index.jsx rename to src-ui/plugins/dev_plugin_subtitles/index.jsx index 011c133d..2312d9d9 100644 --- a/src-ui/plugins/plugin_examples/index.jsx +++ b/src-ui/plugins/dev_plugin_subtitles/index.jsx @@ -1,11 +1,13 @@ import { initStore, StoreContext } from "./store/store.js"; import { SubtitleSystemContainer } from "./subtitle_system_container/SubtitleSystemContainer"; -import { SubtitlesController } from "./subtitle_system_container/_controllers/subtitlesController.jsx"; +import { SubtitlesController } from "./subtitle_system_container/_controllers/SubtitlesController.jsx"; export const init = (plugin_context) => { initStore(plugin_context.createAtomWithHook); const { logic_configs } = plugin_context; + loadPluginCSS("./main.css"); + const EntryComponents = () => { return ( @@ -19,4 +21,17 @@ export const init = (plugin_context) => { plugin_context.registerComponent(EntryComponents); }; -export default init; \ No newline at end of file +export default init; + + +// CSS を動的に読み込む関数 +const loadPluginCSS = (cssUrl) => { + if (typeof document === "undefined") return; + // すでに読み込まれているかチェック + if (document.getElementById("plugin-main-css")) return; + const link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = cssUrl; + link.id = "plugin-main-css"; + document.head.appendChild(link); +}; \ No newline at end of file diff --git a/src-ui/plugins/plugin_examples/plugin_info.json b/src-ui/plugins/dev_plugin_subtitles/plugin_info.json similarity index 86% rename from src-ui/plugins/plugin_examples/plugin_info.json rename to src-ui/plugins/dev_plugin_subtitles/plugin_info.json index f388a9cc..3bb37720 100644 --- a/src-ui/plugins/plugin_examples/plugin_info.json +++ b/src-ui/plugins/dev_plugin_subtitles/plugin_info.json @@ -3,7 +3,7 @@ "plugin_id": "vrct_plugin_subtitles", "asset_name": "vrct_plugin_subtitles.zip", "location": "main_section", - "plugin_version": "0.0.1", + "plugin_version": "0.0.0", "min_supported_vrct_version": "3.0.4", "max_supported_vrct_version": "3.0.6" } \ No newline at end of file diff --git a/src-ui/plugins/plugin_examples/store/store.js b/src-ui/plugins/dev_plugin_subtitles/store/store.js similarity index 100% rename from src-ui/plugins/plugin_examples/store/store.js rename to src-ui/plugins/dev_plugin_subtitles/store/store.js diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/SubtitleSystemContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/SubtitleSystemContainer.jsx rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/SubtitleSystemContainer.module.scss b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.module.scss similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/SubtitleSystemContainer.module.scss rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.module.scss diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/_controllers/SubtitlesController.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_controllers/SubtitlesController.jsx similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/_controllers/SubtitlesController.jsx rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_controllers/SubtitlesController.jsx diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/_logics/useSubtitles.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_logics/useSubtitles.jsx similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/_logics/useSubtitles.jsx rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_logics/useSubtitles.jsx diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/_subtitles_utils.js b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_subtitles_utils.js similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/_subtitles_utils.js rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_subtitles_utils.js diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/countdown_container/CountdownContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/countdown_container/CountdownContainer.jsx similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/countdown_container/CountdownContainer.jsx rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/countdown_container/CountdownContainer.jsx diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/countdown_container/CountdownContainer.module.scss b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/countdown_container/CountdownContainer.module.scss similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/countdown_container/CountdownContainer.module.scss rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/countdown_container/CountdownContainer.module.scss diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/input_file_container/InputFileContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/input_file_container/InputFileContainer.jsx similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/input_file_container/InputFileContainer.jsx rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/input_file_container/InputFileContainer.jsx diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/input_file_container/InputFileContainer.module.scss b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/input_file_container/InputFileContainer.module.scss similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/input_file_container/InputFileContainer.module.scss rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/input_file_container/InputFileContainer.module.scss diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/play_control_container/PlayControlContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/play_control_container/PlayControlContainer.jsx similarity index 94% rename from src-ui/plugins/plugin_examples/subtitle_system_container/play_control_container/PlayControlContainer.jsx rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/play_control_container/PlayControlContainer.jsx index 0e4fdbeb..05e45e01 100644 --- a/src-ui/plugins/plugin_examples/subtitle_system_container/play_control_container/PlayControlContainer.jsx +++ b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/play_control_container/PlayControlContainer.jsx @@ -1,7 +1,7 @@ // import React, { useState, useRef, useEffect } from "react"; import styles from "./PlayControlContainer.module.scss"; import { useSubtitles } from "../_logics/useSubtitles"; -import { clsx } from "clsx"; +import clsx from "clsx"; export const PlayControlContainer = () => { const { diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/play_control_container/PlayControlContainer.module.scss b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/play_control_container/PlayControlContainer.module.scss similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/play_control_container/PlayControlContainer.module.scss rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/play_control_container/PlayControlContainer.module.scss diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx diff --git a/src-ui/plugins/plugin_examples/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss similarity index 100% rename from src-ui/plugins/plugin_examples/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss rename to src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss diff --git a/src-ui/plugins/index.js b/src-ui/plugins/index.js index 010c3ff0..f92cb86b 100644 --- a/src-ui/plugins/index.js +++ b/src-ui/plugins/index.js @@ -1,5 +1,5 @@ -import plugin_index_1 from "./plugin_examples/index.jsx"; -import plugin_info_1 from "./plugin_examples/plugin_info.json"; +import plugin_index_1 from "./dev_plugin_subtitles/index.jsx"; +import plugin_info_1 from "./dev_plugin_subtitles/plugin_info.json"; export const dev_plugins = [ { From b0b68233aa391f7eb604a6e8e1471387d2ddbc9e Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sun, 30 Mar 2025 04:18:54 +0900 Subject: [PATCH 19/68] [Update] Plugins: Send all logics to the plugins when it's registered. --- src-ui/logics/configs/plugins/usePlugins.js | 6 ++++-- src-ui/plugins/dev_plugin_subtitles/index.jsx | 21 +++---------------- .../dev_plugin_subtitles/store/store.js | 10 --------- .../_controllers/SubtitlesController.jsx | 4 ++-- .../_logics/useSubtitles.jsx | 19 ++--------------- 5 files changed, 11 insertions(+), 49 deletions(-) diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index a2b7857f..d03e88d0 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -16,7 +16,9 @@ import JSZip from "jszip"; import { useFetch } from "@logics_common"; import { useSoftwareVersion } from "@logics_configs"; -import * as logic_configs from "@logics_configs"; +import * as logics_configs from "@logics_configs"; +import * as logics_main from "@logics_main"; +import * as logics_common from "@logics_common"; // PLUGIN_LIST_URL は中央リポジトリにある、各プラグインの plugin_info.json への URL の配列を保持する JSON の URL @@ -51,7 +53,7 @@ export const usePlugins = () => { }); }, createAtomWithHook: (...args) => createAtomWithHook(...args), - logic_configs: logic_configs, + logics: { ...logics_common, ...logics_configs, ...logics_main } }; return plugin_context; } diff --git a/src-ui/plugins/dev_plugin_subtitles/index.jsx b/src-ui/plugins/dev_plugin_subtitles/index.jsx index 2312d9d9..56a3b91f 100644 --- a/src-ui/plugins/dev_plugin_subtitles/index.jsx +++ b/src-ui/plugins/dev_plugin_subtitles/index.jsx @@ -4,13 +4,11 @@ import { SubtitlesController } from "./subtitle_system_container/_controllers/Su export const init = (plugin_context) => { initStore(plugin_context.createAtomWithHook); - const { logic_configs } = plugin_context; - - loadPluginCSS("./main.css"); + const { logics } = plugin_context; const EntryComponents = () => { return ( - + @@ -21,17 +19,4 @@ export const init = (plugin_context) => { plugin_context.registerComponent(EntryComponents); }; -export default init; - - -// CSS を動的に読み込む関数 -const loadPluginCSS = (cssUrl) => { - if (typeof document === "undefined") return; - // すでに読み込まれているかチェック - if (document.getElementById("plugin-main-css")) return; - const link = document.createElement("link"); - link.rel = "stylesheet"; - link.href = cssUrl; - link.id = "plugin-main-css"; - document.head.appendChild(link); -}; \ No newline at end of file +export default init; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/store/store.js b/src-ui/plugins/dev_plugin_subtitles/store/store.js index 40d80bb4..6f338573 100644 --- a/src-ui/plugins/dev_plugin_subtitles/store/store.js +++ b/src-ui/plugins/dev_plugin_subtitles/store/store.js @@ -2,16 +2,6 @@ const store_hooks = {}; export const initStore = (createAtomWithHook) => { Object.assign(store_hooks, { - // useStore_CountPluginState: createAtomWithHook( - // { count: 10 }, - // "CountPluginState" - // ).useHook, - - // useStore_AnotherState: createAtomWithHook( - // { value: "initial" }, - // "AnotherState" - // ).useHook, - useStore_IsSubtitlePlaying: createAtomWithHook(false, "IsSubtitlePlaying", { is_state_ok: true }).useHook, useStore_SubtitlePlaybackMode: createAtomWithHook("relative", "SubtitlePlaybackMode", { is_state_ok: true }).useHook, useStore_SubtitleAbsoluteTargetTime: createAtomWithHook({ diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_controllers/SubtitlesController.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_controllers/SubtitlesController.jsx index 53380205..d58be5aa 100644 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_controllers/SubtitlesController.jsx +++ b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_controllers/SubtitlesController.jsx @@ -4,8 +4,8 @@ import { useSubtitles } from "../_logics/useSubtitles"; import { secToDayTime } from "../_subtitles_utils" import { useEffect } from "react"; export const SubtitlesController = () => { - const logic_configs = useStoreContext(); - const { sendTextToOverlay } = logic_configs.useSendTextToOverlay(); + const { useSendTextToOverlay } = useStoreContext(); + const { sendTextToOverlay } = useSendTextToOverlay(); const { currentIsSubtitlePlaying, diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_logics/useSubtitles.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_logics/useSubtitles.jsx index fe0b6e3c..80585abf 100644 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_logics/useSubtitles.jsx +++ b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_logics/useSubtitles.jsx @@ -1,23 +1,8 @@ import { useStore, useStoreContext } from "../../store/store.js"; -// import { useSendTextToOverlay } from "@logics_configs"; -// import { -// useStore_SubtitleFileName, -// useStore_IsSubtitlePlaying, -// useStore_SubtitlePlaybackMode, -// useStore_SubtitleAbsoluteTargetTime, -// useStore_IsCuesScheduled, -// useStore_CountdownAdjustment, -// useStore_EffectiveCountdown, -// useStore_SubtitleCues, - -// useStore_SubtitleTimers, -// useStore_SubtitleCountdownTimerId, -// } from "../../store/store.js"; - export const useSubtitles = () => { - const logic_configs = useStoreContext(); - const { sendTextToOverlay } = logic_configs.useSendTextToOverlay(); + const { useSendTextToOverlay } = useStoreContext(); + const { sendTextToOverlay } = useSendTextToOverlay(); const { currentSubtitleFileName, updateSubtitleFileName } = useStore("useStore_SubtitleFileName"); const { currentIsSubtitlePlaying, updateIsSubtitlePlaying } = useStore("useStore_IsSubtitlePlaying"); From c02d7c49e901b8201d2c95d05baadca9825f103d Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Tue, 1 Apr 2025 06:02:58 +0900 Subject: [PATCH 20/68] [Update] Plugins dev: Apply-able aliases. --- src-ui/logics/configs/plugins/usePlugins.js | 15 +- src-ui/plugins/dev_plugin_subtitles/index.jsx | 2 +- .../dev_plugin_subtitles/plugin_configs.js | 5 + src-ui/plugins/index.js | 9 -- src-ui/plugins/plugins_index.js | 3 + vite.config.js | 130 +++++++++++------- 6 files changed, 102 insertions(+), 62 deletions(-) create mode 100644 src-ui/plugins/dev_plugin_subtitles/plugin_configs.js delete mode 100644 src-ui/plugins/index.js create mode 100644 src-ui/plugins/plugins_index.js diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index d03e88d0..9031bc57 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -10,12 +10,20 @@ import { useStdoutToPython } from "@logics/useStdoutToPython"; import { transform } from "@babel/standalone"; import { writeFile, createDir, exists, removeDir, readDir, BaseDirectory, readTextFile } from "@tauri-apps/api/fs"; -import { dev_plugins } from "@dev_plugins_path"; +import { dev_plugins } from "@plugins_index"; +const imported_dev_plugins = []; +dev_plugins.forEach(async ({entry_path}) => { + imported_dev_plugins.push({ + index: await import(`@plugins_path/${entry_path}/index.jsx`), + plugin_info: await import(`@plugins_path/${entry_path}/plugin_info.json`), + }); +}) import JSZip from "jszip"; import { useFetch } from "@logics_common"; import { useSoftwareVersion } from "@logics_configs"; + import * as logics_configs from "@logics_configs"; import * as logics_main from "@logics_main"; import * as logics_common from "@logics_common"; @@ -91,9 +99,8 @@ export const usePlugins = () => { }; const asyncLoadAllPlugins = async () => { - if (import.meta.env.DEV) { - // `dev_plugins` を利用してプラグインを登録 - dev_plugins.forEach(({ index, plugin_info }) => { + if (!import.meta.env.DEV) { + imported_dev_plugins.forEach(({ index, plugin_info }) => { if (!index || !plugin_info) { console.error("Invalid development plugin detected", index, plugin_info); return; diff --git a/src-ui/plugins/dev_plugin_subtitles/index.jsx b/src-ui/plugins/dev_plugin_subtitles/index.jsx index 56a3b91f..1b8942d4 100644 --- a/src-ui/plugins/dev_plugin_subtitles/index.jsx +++ b/src-ui/plugins/dev_plugin_subtitles/index.jsx @@ -1,4 +1,4 @@ -import { initStore, StoreContext } from "./store/store.js"; +import { initStore, StoreContext } from "@plugin_store"; import { SubtitleSystemContainer } from "./subtitle_system_container/SubtitleSystemContainer"; import { SubtitlesController } from "./subtitle_system_container/_controllers/SubtitlesController.jsx"; diff --git a/src-ui/plugins/dev_plugin_subtitles/plugin_configs.js b/src-ui/plugins/dev_plugin_subtitles/plugin_configs.js new file mode 100644 index 00000000..0944d0b6 --- /dev/null +++ b/src-ui/plugins/dev_plugin_subtitles/plugin_configs.js @@ -0,0 +1,5 @@ +export const configs = { + alias: { + "@plugin_store": "store/store.js", + } +} \ No newline at end of file diff --git a/src-ui/plugins/index.js b/src-ui/plugins/index.js deleted file mode 100644 index f92cb86b..00000000 --- a/src-ui/plugins/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import plugin_index_1 from "./dev_plugin_subtitles/index.jsx"; -import plugin_info_1 from "./dev_plugin_subtitles/plugin_info.json"; - -export const dev_plugins = [ - { - index: { init: plugin_index_1 }, - plugin_info: plugin_info_1, - } -]; \ No newline at end of file diff --git a/src-ui/plugins/plugins_index.js b/src-ui/plugins/plugins_index.js new file mode 100644 index 00000000..ea240627 --- /dev/null +++ b/src-ui/plugins/plugins_index.js @@ -0,0 +1,3 @@ +export const dev_plugins = [ + { entry_path: "dev_plugin_subtitles" } +]; \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index 58c824fa..948083e4 100644 --- a/vite.config.js +++ b/vite.config.js @@ -3,62 +3,96 @@ import react from "@vitejs/plugin-react"; import svgr from "vite-plugin-svgr"; import path from "path"; +import { dev_plugins } from "./src-ui/plugins/plugins_index.js"; + + // https://vitejs.dev/config/ -export default defineConfig(async () => ({ - plugins: [react(), svgr()], - assetsInclude: ["**/*.yml"], +export default defineConfig(async () => { + const plugin_aliases = await getPluginAliases(); - // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` - // - // 1. prevent vite from obscuring rust errors - clearScreen: false, - // 2. tauri expects a fixed port, fail if that port is not available - server: { - port: 1420, - strictPort: true, - watch: { - // 3. tell vite to ignore watching `src-tauri` - ignored: ["**/src-tauri/**"], - }, - }, + return { + plugins: [react(), svgr()], + assetsInclude: ["**/*.yml"], - build: { - outDir: path.resolve(__dirname, "dist"), - rollupOptions: { - input: { - main: path.resolve(__dirname, "index.html"), + // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` + // + // 1. prevent vite from obscuring rust errors + clearScreen: false, + // 2. tauri expects a fixed port, fail if that port is not available + server: { + port: 1420, + strictPort: true, + watch: { + // 3. tell vite to ignore watching `src-tauri` + ignored: ["**/src-tauri/**"], }, }, - }, - resolve: { - alias: { - "@root": path.resolve(__dirname), - "@test_data": path.resolve(__dirname, "./test_data.js"), - - "@ui_configs": path.resolve(__dirname, "src-ui/ui_configs.js"), - "@scss_mixins": path.resolve(__dirname, "src-ui/common_css/mixins.scss"), - "@store": path.resolve(__dirname, "src-ui/store.js"), - "@images": path.resolve(__dirname, "src-ui/assets"), - "@utils": path.resolve(__dirname, "src-ui/utils.js"), - "@logics": path.resolve(__dirname, "src-ui/logics"), - "@logics_common": path.resolve(__dirname, "src-ui/logics/common"), - "@logics_main": path.resolve(__dirname, "src-ui/logics/main"), - "@logics_configs": path.resolve(__dirname, "src-ui/logics/configs"), - - "@setting_box": path.resolve(__dirname, "src-ui/app/config_page/setting_section/setting_box/index.js"), - "@common_components": path.resolve(__dirname, "src-ui/common_components/index.js"), - - "@dev_plugins_path": path.resolve(__dirname, "src-ui/plugins/index.js"), + build: { + outDir: path.resolve(__dirname, "dist"), + rollupOptions: { + input: { + main: path.resolve(__dirname, "index.html"), + }, + }, }, - }, - css: { - preprocessorOptions: { - scss: { - api: "modern-compiler" + resolve: { + alias: { + "@root": path.resolve(__dirname), + "@test_data": path.resolve(__dirname, "./test_data.js"), + + "@ui_configs": path.resolve(__dirname, "src-ui/ui_configs.js"), + "@scss_mixins": path.resolve(__dirname, "src-ui/common_css/mixins.scss"), + "@store": path.resolve(__dirname, "src-ui/store.js"), + "@images": path.resolve(__dirname, "src-ui/assets"), + "@utils": path.resolve(__dirname, "src-ui/utils.js"), + "@logics": path.resolve(__dirname, "src-ui/logics"), + "@logics_common": path.resolve(__dirname, "src-ui/logics/common"), + "@logics_main": path.resolve(__dirname, "src-ui/logics/main"), + "@logics_configs": path.resolve(__dirname, "src-ui/logics/configs"), + + "@setting_box": path.resolve(__dirname, "src-ui/app/config_page/setting_section/setting_box/index.js"), + "@common_components": path.resolve(__dirname, "src-ui/common_components/index.js"), + + // Plugins + "@plugins_path": path.resolve(__dirname, "src-ui/plugins"), + "@plugins_index": path.resolve(__dirname, "src-ui/plugins/plugins_index.js"), + ...plugin_aliases, + }, + }, + + css: { + preprocessorOptions: { + scss: { + api: "modern-compiler" + } } } - } + }; +}); -})); + + +// 各プラグインのエイリアスを動的に読み込む関数 +const getPluginAliases = async () => { + const aliases = {}; + // dev_plugins 配列の各プラグインについて処理する + for (const plugin of dev_plugins) { + const entry_path = plugin.entry_path; // 例: "dev_plugin_subtitles" + try { + // エイリアス設定ファイルは各プラグインフォルダ内の "configs.js" に記述されている前提 + const pluginConfig = await import(`./src-ui/plugins/${entry_path}/plugin_configs.js`); + if (pluginConfig.configs && pluginConfig.configs.alias) { + for (const [alias_key, alias_relative_path] of Object.entries(pluginConfig.configs.alias)) { + + // ホスト側の絶対パスに変換 + aliases[alias_key] = path.resolve(__dirname, "src-ui/plugins", entry_path, alias_relative_path); + } + } + } catch (error) { + console.error(`Error loading alias config for plugin ${plugin.plugin_info.plugin_id}:`, error); + } + } + return aliases; +}; \ No newline at end of file From 67f32ad7b9025960d52c62a0bd56a05f1f398850 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 2 Apr 2025 22:52:20 +0900 Subject: [PATCH 21/68] [Update] Plugins: Add update function. --- .../_app_controllers/PluginsController.jsx | 38 ++++++++++---- .../_download_button/_DownloadButton.jsx | 9 ++++ .../_DownloadButton.module.scss | 14 +++++ .../PluginsControlComponent.jsx | 51 +++++++------------ .../PluginsControlComponent.module.scss | 4 ++ .../setting_box/plugins/Plugins.jsx | 3 +- src-ui/logics/configs/plugins/usePlugins.js | 16 +++--- 7 files changed, 83 insertions(+), 52 deletions(-) diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx index 8689e2f0..ec181bb9 100644 --- a/src-ui/app/_app_controllers/PluginsController.jsx +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -23,25 +23,43 @@ export const PluginsController = ({ fetchPluginsHasRunRef }) => { const info_array = await asyncFetchPluginsInfo(); updatePluginsData(prev => { // Map を利用してそれぞれの配列を plugin_id で参照できるようにする - const infoMap = new Map(info_array.map(info => [info.plugin_id, info])); - const prevMap = new Map(prev.data.map(item => [item.plugin_id, item])); + const info_map = new Map(info_array.map(info => [info.plugin_id, info])); + const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); // info_array にある各アイテムについて、prev.data に同じ plugin_id があればマージ const merged = info_array.map(info => { - if (prevMap.has(info.plugin_id)) { - return { ...info, ...prevMap.get(info.plugin_id) }; + if (prev_map.has(info.plugin_id)) { + return { + ...info, + latest_plugin_version: info.plugin_version, + ...prev_map.get(info.plugin_id), + }; } - return info; + return { + ...info, + latest_plugin_version: info.plugin_version, + }; }); - // prev.data にのみ存在するアイテムを追加し、is_outdated: true を付与 + // prev.data にのみ存在するアイテム = latest plugin infoには存在しない + // を追加し、is_outdated: true を付与 prev.data.forEach(item => { - if (!infoMap.has(item.plugin_id)) { + if (!info_map.has(item.plugin_id)) { merged.push({ ...item, is_outdated: true }); } }); - return merged; + let new_value = []; + for (const plugin of merged) { + if (plugin.downloaded_plugin_version !== plugin.latest_plugin_version && plugin.is_plugin_supported) { + plugin.is_latest_version_available = true; + } else { + plugin.is_latest_version_available = false; + } + new_value.push(plugin); + } + + return new_value; }); } catch (error) { console.error(error); @@ -59,7 +77,7 @@ export const PluginsController = ({ fetchPluginsHasRunRef }) => { updatePluginsData(prev => { // currentSavedPluginsStatus.data の各要素を Map 化して plugin_id で参照 const savedMap = new Map(currentSavedPluginsStatus.data.map(saved => [saved.plugin_id, saved])); - const prevMap = new Map(prev.data.map(item => [item.plugin_id, item])); + const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); // prev.data にある各アイテムについて、保存済みの状態情報があればマージ const merged = prev.data.map(item => { @@ -71,7 +89,7 @@ export const PluginsController = ({ fetchPluginsHasRunRef }) => { // currentSavedPluginsStatus.data にのみ存在する項目があれば追加 currentSavedPluginsStatus.data.forEach(saved => { - if (!prevMap.has(saved.plugin_id)) { + if (!prev_map.has(saved.plugin_id)) { merged.push({ plugin_id: saved.plugin_id, is_enabled: saved.is_enabled }); } }); diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.jsx index 41c3b7d9..edaf7fa5 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.jsx @@ -32,6 +32,15 @@ export const _DownloadButton = ({option, ...props}) => {

{t("config_page.model_download_button_label")}

); + case option.update_button: + return ( + + ); default: return null; } diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss index 6b42ca5d..752dca38 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss @@ -28,4 +28,18 @@ .progress_label { position: absolute; font-size: 1rem; +} + +.update_button { + pointer-events: auto; + background-color: var(--primary_400_color); + padding: 0.8rem; + flex-shrink: 0; + border-radius: 0.2rem; + &:hover { + background-color: var(--primary_450_color); + } + &:active { + background-color: var(--primary_500_color); + } } \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx index 33ca0fa7..a8f70b73 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx @@ -1,48 +1,31 @@ -import { - SwitchBox, -} from "../index"; +import { SwitchBox } from "../index"; import { _DownloadButton } from "../_atoms/_download_button/_DownloadButton"; import styles from "./PluginsControlComponent.module.scss"; -export const PluginsControlComponent = ({ variable_state, plugin_status, toggleFunction, ...props }) => { +export const PluginsControlComponent = ({ variable_state, plugin_status, toggleFunction, downloadStartFunction }) => { + const { plugin_id, is_pending, is_downloaded, is_enabled, is_latest_version_available, is_plugin_supported, is_outdated } = plugin_status; + const option = { - id: plugin_status.plugin_id, - is_pending: plugin_status.is_pending, - is_downloaded: plugin_status.is_downloaded, - data: plugin_status.is_enabled, + id: plugin_id, + is_pending: is_pending, + is_downloaded: is_downloaded, + data: is_enabled, + update_button: is_downloaded && is_latest_version_available, state: variable_state, progress: null, }; - const adjustedToggleFunction = () => { - toggleFunction(plugin_status.plugin_id); - }; - - let is_turn_on_able = false; - if (plugin_status.is_downloaded && plugin_status.is_plugin_supported) { - is_turn_on_able = true; - } - if (plugin_status.is_outdated) { - is_turn_on_able = true; - } + const togglePlugin = () => toggleFunction(plugin_id); + const is_turn_on_able = is_downloaded && (is_plugin_supported || is_outdated); return (
- {is_turn_on_able && - } - {plugin_status.is_plugin_supported ? - <_DownloadButton - option={option} - downloadStartFunction={props.downloadStartFunction} - /> - : -
- Downloaded but outdated. -
- } + {is_plugin_supported ? ( + <_DownloadButton option={option} downloadStartFunction={downloadStartFunction} /> + ) : ( +
Downloaded but outdated.
+ )} + {is_turn_on_able && }
); }; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.module.scss index 99a37516..816119fc 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.module.scss @@ -1,4 +1,8 @@ .container { + display: flex; + justify-content: center; + align-items: center; + gap: 2rem; } .unavailable_text { diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index cddda330..4c54df95 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -88,7 +88,8 @@ const PluginDownloadContainer = () => { ) : (
-

Version: {plugin.plugin_version}

+

Downloaded Version: {plugin.downloaded_plugin_version}

+

Latest Version: {plugin.latest_plugin_version}

Compatible: {plugin.min_supported_vrct_version} ~ {plugin.max_supported_vrct_version}

diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 9031bc57..22137654 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -47,17 +47,19 @@ export const usePlugins = () => { return console.error("An invalid plugin was detected.", plugin_info.plugin_id, plugin_info.location, component); } updatePluginsData(prev => { - const is_already_registered = prev.data.some(old_value => old_value.plugin_id === plugin_info.plugin_id); - const new_value = prev.data.map(old_value => old_value.plugin_id === plugin_info.plugin_id - ? { ...old_value, ...plugin_info, location: plugin_info.location, component, is_downloaded: true } - : old_value + ? { + ...old_value, + ...plugin_info, + downloaded_plugin_version: plugin_info.plugin_version, + component: component, + is_downloaded: true, + is_latest_version_available: false, + } : old_value ); - return is_already_registered - ? new_value - : [...new_value, { plugin_id: plugin_info.plugin_id, location: plugin_info.location, component, is_downloaded: true }]; + return new_value; }); }, createAtomWithHook: (...args) => createAtomWithHook(...args), From 7e637b795d627300aafbb6b7b7f0a409d6f419da Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 9 Apr 2025 17:29:31 +0900 Subject: [PATCH 22/68] [Update/Refactor/bugfix] Update: Add functions and test ui. Update, backend: send latest vrct version to frontend. Refactor: Change the plugins data structure. bugfix: fix endless showing update button. --- src-python/controller.py | 6 +- src-python/mainloop.py | 2 +- src-python/model.py | 6 +- .../_app_controllers/PluginsController.jsx | 72 +++++---- .../PluginsControlComponent.jsx | 145 +++++++++++++++--- .../setting_box/plugins/Plugins.jsx | 81 +++++----- .../version_label/VersionLabel.jsx | 3 +- .../main_page/main_section/MainSection.jsx | 2 +- .../RightSideComponents.jsx | 6 +- .../update_modal/UpdateModal.jsx | 35 ++++- src-ui/logics/common/index.js | 2 +- .../common/useIsSoftwareUpdateAvailable.js | 10 -- src-ui/logics/common/useSoftwareVersion.js | 40 +++++ src-ui/logics/configs/index.js | 4 +- src-ui/logics/configs/plugins/usePlugins.js | 138 ++++++++++++----- src-ui/logics/configs/useSoftwareVersion.js | 18 --- src-ui/logics/useReceiveRoutes.js | 7 +- src-ui/store.js | 7 +- 18 files changed, 404 insertions(+), 180 deletions(-) delete mode 100644 src-ui/logics/common/useIsSoftwareUpdateAvailable.js create mode 100644 src-ui/logics/common/useSoftwareVersion.js delete mode 100644 src-ui/logics/configs/useSoftwareVersion.js diff --git a/src-python/controller.py b/src-python/controller.py index 02ba5e34..faa659b2 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -447,11 +447,11 @@ class Controller: return {"status":200, "result":config.VERSION} def checkSoftwareUpdated(self) -> dict: - update_flag = model.checkSoftwareUpdated() + software_update_info = model.checkSoftwareUpdated() self.run( 200, - self.run_mapping["update_software_flag"], - update_flag, + self.run_mapping["software_update_info"], + software_update_info, ) @staticmethod diff --git a/src-python/mainloop.py b/src-python/mainloop.py index edd5356a..5c5fb744 100644 --- a/src-python/mainloop.py +++ b/src-python/mainloop.py @@ -38,7 +38,7 @@ run_mapping = { "mic_device_list":"/run/mic_device_list", "speaker_device_list":"/run/speaker_device_list", - "update_software_flag":"/run/update_software_flag", + "software_update_info":"/run/software_update_info", "initialization_progress":"/run/initialization_progress", "initialization_complete":"/run/initialization_complete", diff --git a/src-python/model.py b/src-python/model.py index f393314d..afe0d565 100644 --- a/src-python/model.py +++ b/src-python/model.py @@ -320,6 +320,7 @@ class Model: def checkSoftwareUpdated(): # check update update_flag = False + version = "" try: response = requests_get(config.GITHUB_URL) json_data = response.json() @@ -331,7 +332,10 @@ class Model: update_flag = True except Exception: errorLogging() - return update_flag + return { + "is_update_available": update_flag, + "new_version": version, + } @staticmethod def updateSoftware(): diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx index ec181bb9..2fad4376 100644 --- a/src-ui/app/_app_controllers/PluginsController.jsx +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -14,6 +14,7 @@ export const PluginsController = ({ fetchPluginsHasRunRef }) => { currentPluginsData, updatePluginsData, currentSavedPluginsStatus, + updateIsPluginsInitialized, } = usePlugins(); useEffect(() => { @@ -26,40 +27,55 @@ export const PluginsController = ({ fetchPluginsHasRunRef }) => { const info_map = new Map(info_array.map(info => [info.plugin_id, info])); const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); - // info_array にある各アイテムについて、prev.data に同じ plugin_id があればマージ - const merged = info_array.map(info => { - if (prev_map.has(info.plugin_id)) { - return { - ...info, - latest_plugin_version: info.plugin_version, - ...prev_map.get(info.plugin_id), + const new_data = []; + for (const info of info_array) { + let new_plugin_info = {}; + if (prev_map.has(info.plugin_id)) { // plugin_id 登録済み + const target_downloaded_plugin = prev_map.get(info.plugin_id); + if (target_downloaded_plugin.is_downloaded) { // 既にダウンロード済み + const is_latest_version_available = !(target_downloaded_plugin.plugin_version === info.plugin_version); + + new_plugin_info = { + is_downloaded: true, + is_latest_version_already: (target_downloaded_plugin.downloaded_plugin_info?.plugin_version === info.plugin_version), + is_latest_version_available: is_latest_version_available, + latest_plugin_info: { ...info }, + ...target_downloaded_plugin, + }; + } else { // infoにもあり登録済みだがダウンロードされていない + new_plugin_info = { + is_downloaded: false, + is_latest_version_already: false, + is_latest_version_available: info.is_latest_version_available, + latest_plugin_info: { ...info }, + ...target_downloaded_plugin, + } + } + } else { // 未ダウンロード + new_plugin_info = { + plugin_id: info.plugin_id, + is_downloaded: false, + is_latest_version_already: false, + latest_plugin_info: { ...info }, }; } - return { - ...info, - latest_plugin_version: info.plugin_version, - }; - }); + new_data.push(new_plugin_info); + } // prev.data にのみ存在するアイテム = latest plugin infoには存在しない // を追加し、is_outdated: true を付与 prev.data.forEach(item => { if (!info_map.has(item.plugin_id)) { - merged.push({ ...item, is_outdated: true }); + new_data.push({ ...item, is_outdated: true }); } }); - let new_value = []; - for (const plugin of merged) { - if (plugin.downloaded_plugin_version !== plugin.latest_plugin_version && plugin.is_plugin_supported) { - plugin.is_latest_version_available = true; - } else { - plugin.is_latest_version_available = false; + new_data.forEach(plugin => { + if (!plugin.is_outdated) { + plugin.is_latest_version_available = (plugin.latest_plugin_info.is_plugin_supported); } - new_value.push(plugin); - } - - return new_value; + }); + return new_data; }); } catch (error) { console.error(error); @@ -68,6 +84,7 @@ export const PluginsController = ({ fetchPluginsHasRunRef }) => { if (!fetchPluginsHasRunRef.current) { loadPlugins(); + updateIsPluginsInitialized(true); } return () => fetchPluginsHasRunRef.current = true; }, []); @@ -76,13 +93,13 @@ export const PluginsController = ({ fetchPluginsHasRunRef }) => { useEffect(() => { updatePluginsData(prev => { // currentSavedPluginsStatus.data の各要素を Map 化して plugin_id で参照 - const savedMap = new Map(currentSavedPluginsStatus.data.map(saved => [saved.plugin_id, saved])); + const saved_map = new Map(currentSavedPluginsStatus.data.map(saved => [saved.plugin_id, saved])); const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); - // prev.data にある各アイテムについて、保存済みの状態情報があればマージ const merged = prev.data.map(item => { - if (savedMap.has(item.plugin_id)) { - return { ...item, is_enabled: savedMap.get(item.plugin_id).is_enabled }; + + if (saved_map.has(item.plugin_id)) { + return { ...item, is_enabled: saved_map.get(item.plugin_id).is_enabled }; } return item; }); @@ -93,7 +110,6 @@ export const PluginsController = ({ fetchPluginsHasRunRef }) => { merged.push({ plugin_id: saved.plugin_id, is_enabled: saved.is_enabled }); } }); - return merged; }); }, [currentSavedPluginsStatus]); diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx index a8f70b73..0fd7c33a 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx @@ -1,31 +1,136 @@ +import React from "react"; import { SwitchBox } from "../index"; import { _DownloadButton } from "../_atoms/_download_button/_DownloadButton"; import styles from "./PluginsControlComponent.module.scss"; -export const PluginsControlComponent = ({ variable_state, plugin_status, toggleFunction, downloadStartFunction }) => { - const { plugin_id, is_pending, is_downloaded, is_enabled, is_latest_version_available, is_plugin_supported, is_outdated } = plugin_status; - +// メインのコントロールコンポーネント。ダウンロード済み / 未ダウンロードで分岐して表示する +export const PluginsControlComponent = ({ + variable_state, + plugin_status, + toggleFunction, + downloadStartFunction, +}) => { + // 共通オプション(各子コンポーネントに引き回す情報) const option = { - id: plugin_id, - is_pending: is_pending, - is_downloaded: is_downloaded, - data: is_enabled, - update_button: is_downloaded && is_latest_version_available, + id: plugin_status.plugin_id, + is_pending: plugin_status.is_pending, + is_downloaded: plugin_status.is_downloaded, + data: plugin_status.is_enabled, + update_button: plugin_status.is_downloaded && plugin_status.is_latest_version_available, state: variable_state, progress: null, }; - const togglePlugin = () => toggleFunction(plugin_id); - const is_turn_on_able = is_downloaded && (is_plugin_supported || is_outdated); + if (plugin_status.is_downloaded) { + return ( + + ); + } else { + return ( + + ); + } +}; - return ( -
- {is_plugin_supported ? ( +// ------------------------- +// ダウンロード済みのプラグイン用コンポーネント +// 状態により以下の分岐を行う +// ・ is_latest_version_already が true なら「最新版を使用中」 +// ・ is_latest_version_already が false かつ is_latest_version_available が true なら「最新版を利用可能」(アップデートボタン+スイッチ) +// ・ それ以外(is_latest_version_already:false && is_latest_version_available: false)なら、desc等の情報とスイッチのみ表示 +const DownloadedPluginControl = ({ + option, + plugin_status, + toggleFunction, + downloadStartFunction, +}) => { + // on/off トグル時の処理 + const togglePlugin = () => { + toggleFunction(plugin_status.plugin_id); + }; + + + // ダウンロード済みの場合、ダウンロードされた情報からタイトルやバージョンを取得 + const title = plugin_status.downloaded_plugin_info?.title || plugin_status.latest_plugin_info.title; + const current_version = + plugin_status.downloaded_plugin_info?.plugin_version || plugin_status.latest_plugin_info.plugin_version; + + // コンポーネントごとに表示内容を分岐 + if (plugin_status.is_latest_version_already) { + // 最新版が既に使用中 + return ( +
+

{title}

+

現在のバージョン: {current_version}

+

最新版を使用中

+ +
+ ); + } else if (plugin_status.is_latest_version_available) { + // 最新版の利用可能なお知らせとアップデートボタン+スイッチ + return ( +
+

{title}

+

現在のバージョン: {current_version}

+

最新版を利用可能

<_DownloadButton option={option} downloadStartFunction={downloadStartFunction} /> - ) : ( -
Downloaded but outdated.
- )} - {is_turn_on_able && } -
- ); -}; \ No newline at end of file + +
+ ); + } else { + // 最新版利用可能ではないがダウンロード済み + // ※「desc」に関しては、情報があればplugin_status.descやlatest_plugin_info.descを利用してください + const desc = + plugin_status.latest_plugin_info.desc || + "追加情報がありません。"; + return ( +
+

{title}

+

現在のバージョン: {current_version}

+

{desc}

+ +
+ ); + } +}; + +// ------------------------- +// 未ダウンロードのプラグイン用コンポーネント +// 状態により以下の分岐を行う +// ・ is_latest_version_available が true なら:info_title, 最新バージョン情報とダウンロードボタン +// ・ is_latest_version_available が false なら:info_title, 最新バージョン情報と「現在利用不可」 +const NotDownloadedPluginControl = ({ option, plugin_status, downloadStartFunction }) => { + const title = plugin_status.latest_plugin_info.title; + const latest_version = plugin_status.latest_plugin_info.plugin_version; + // ※未ダウンロードの場合、current_versionは「未ダウンロード」や「なし」といった扱いとする + const current_version = "未ダウンロード"; + + if (plugin_status.is_latest_version_available) { + return ( +
+

{title}

+

現在のバージョン: {current_version}

+

最新バージョン: {latest_version}

+ <_DownloadButton option={option} downloadStartFunction={downloadStartFunction} /> +
+ ); + } else { + return ( +
+

{title}

+

現在のバージョン: {current_version}

+

最新バージョン: {latest_version}

+

現在利用不可

+
+ ); + } +}; diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index 4c54df95..2a3fd42d 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -1,9 +1,15 @@ -import React, { useState, useEffect } from "react"; +import React from "react"; import { usePlugins } from "@logics_configs"; import styles from "./Plugins.module.scss"; -import { PluginsControlComponent } from "../_components"; +import { PluginsControlComponent } from "../_components/plugins_control_component/PluginsControlComponent"; export const Plugins = () => { + const { + currentIsPluginsInitialized, + } = usePlugins(); + + if (!currentIsPluginsInitialized.data) return null; + return (
@@ -11,6 +17,7 @@ export const Plugins = () => { ); }; + const PluginDownloadContainer = () => { const { downloadAndExtractPlugin, @@ -18,40 +25,29 @@ const PluginDownloadContainer = () => { updatePluginsData, currentSavedPluginsStatus, setSavedPluginsStatus, + handlePendingPlugin, } = usePlugins(); - + // ダウンロード開始時の状態更新処理 const downloadStartFunction = async (target_plugin_id) => { - updatePluginsData((old_value) => { - const new_value = old_value.data.map(d => { - if (d.plugin_id === target_plugin_id) { - d.is_pending = true; - } - return d; - }); - return new_value; - }); - const target_plugin_info = currentPluginsData.data.find(d => d.plugin_id === target_plugin_id); + handlePendingPlugin(target_plugin_id, true); + + const target_plugin_info = currentPluginsData.data.find( + (d) => d.plugin_id === target_plugin_id + ); downloadAndExtractPlugin(target_plugin_info).then(() => { - updatePluginsData((old_value) => { - const new_value = old_value.data.map(d => { - if (d.plugin_id === target_plugin_id) { - d.is_pending = false; - d.is_downloaded = true; - } - return d; - }); - return new_value; - }); - }) + handlePendingPlugin(target_plugin_id, false); + }); }; - + // プラグインのオンオフ切り替え処理 const toggleFunction = (target_plugin_id) => { - const is_exists = currentSavedPluginsStatus.data.some(d => d.plugin_id === target_plugin_id); + const is_exists = currentSavedPluginsStatus.data.some( + (d) => d.plugin_id === target_plugin_id + ); let new_value = []; if (is_exists) { - new_value = currentSavedPluginsStatus.data.map(d => { + new_value = currentSavedPluginsStatus.data.map((d) => { if (d.plugin_id === target_plugin_id) { d.is_enabled = !d.is_enabled; } @@ -65,40 +61,45 @@ const PluginDownloadContainer = () => { }); } - - // currentPluginsData.data で、is_downloaded が true のものだけ残す - new_value = new_value.filter(item => { - return currentPluginsData.data.some(plugin => plugin.plugin_id === item.plugin_id && plugin.is_downloaded); - }); + // 「currentPluginsData.data」でis_downloadedがtrueのものだけ残す + new_value = new_value.filter((item) => + currentPluginsData.data.some( + (plugin) => plugin.plugin_id === item.plugin_id && plugin.is_downloaded + ) + ); setSavedPluginsStatus(new_value); - } + }; const variable_state = currentSavedPluginsStatus.state; - return (
{currentPluginsData.data.map((plugin) => (
-

{plugin.title}

+

+ {plugin.is_downloaded + ? plugin.downloaded_plugin_info?.title || plugin.latest_plugin_info.title + : plugin.latest_plugin_info.title} +

{plugin.plugin_id}

{plugin.error ? (

Error: {plugin.error}

) : (
-

Downloaded Version: {plugin.downloaded_plugin_version}

-

Latest Version: {plugin.latest_plugin_version}

+ {/* 状態に応じた情報表示(例:バージョン等) */}

- Compatible: {plugin.min_supported_vrct_version} ~ {plugin.max_supported_vrct_version} + {plugin.is_downloaded + ? `現在のバージョン: ${plugin.downloaded_plugin_info?.plugin_version}` + : `最新バージョン: ${plugin.latest_plugin_info.plugin_version}`}

)} @@ -106,4 +107,4 @@ const PluginDownloadContainer = () => { ))}
); -}; \ No newline at end of file +}; diff --git a/src-ui/app/config_page/version_label/VersionLabel.jsx b/src-ui/app/config_page/version_label/VersionLabel.jsx index be095e5c..95abd94d 100644 --- a/src-ui/app/config_page/version_label/VersionLabel.jsx +++ b/src-ui/app/config_page/version_label/VersionLabel.jsx @@ -3,8 +3,7 @@ import { useState } from "react"; import clsx from "clsx"; import styles from "./VersionLabel.module.scss"; -import { useSoftwareVersion } from "@logics_configs"; -import { useComputeMode } from "@logics_common"; +import { useSoftwareVersion, useComputeMode } from "@logics_common"; import CopySvg from "@images/copy.svg?react"; import CheckMarkSvg from "@images/check_mark.svg?react"; diff --git a/src-ui/app/main_page/main_section/MainSection.jsx b/src-ui/app/main_page/main_section/MainSection.jsx index 0eaf272d..e89071a8 100644 --- a/src-ui/app/main_page/main_section/MainSection.jsx +++ b/src-ui/app/main_page/main_section/MainSection.jsx @@ -16,7 +16,7 @@ import { usePlugins } from "@logics_configs"; export const MainSection = () => { const { currentPluginsData } = usePlugins(); - const render_plugins = currentPluginsData.data.filter((plugin) => plugin.is_enabled && plugin.location === "main_section"); + const render_plugins = currentPluginsData.data.filter((plugin) => (plugin.is_downloaded && plugin.is_enabled && plugin.downloaded_plugin_info.location === "main_section")); return (
diff --git a/src-ui/app/main_page/main_section/top_bar/right_side_components/RightSideComponents.jsx b/src-ui/app/main_page/main_section/top_bar/right_side_components/RightSideComponents.jsx index 995266e2..1917d523 100644 --- a/src-ui/app/main_page/main_section/top_bar/right_side_components/RightSideComponents.jsx +++ b/src-ui/app/main_page/main_section/top_bar/right_side_components/RightSideComponents.jsx @@ -4,7 +4,7 @@ import RefreshSvg from "@images/refresh.svg?react"; import HelpSvg from "@images/help.svg?react"; import { useStore_OpenedQuickSetting } from "@store"; -import { useIsSoftwareUpdateAvailable } from "@logics_common"; +import { useSoftwareVersion } from "@logics_common"; import { useIsEnabledOverlaySmallLog, useIsEnabledOverlayLargeLog, useEnableVrcMicMuteSync } from "@logics_configs"; import { OpenQuickSettingButton } from "./_buttons/OpenQuickSettingButton"; @@ -66,9 +66,9 @@ const OpenVrcMicMuteSyncQuickSetting = () => { }; const SoftwareUpdateAvailableButton = () => { - const { currentIsSoftwareUpdateAvailable } = useIsSoftwareUpdateAvailable(); + const { currentLatestSoftwareVersionInfo } = useSoftwareVersion(); const { t } = useTranslation(); - if (currentIsSoftwareUpdateAvailable.data === false) return null; + if (currentLatestSoftwareVersionInfo.data.is_update_available === false) return null; const { updateOpenedQuickSetting } = useStore_OpenedQuickSetting(); diff --git a/src-ui/app/modal_controller/update_modal/UpdateModal.jsx b/src-ui/app/modal_controller/update_modal/UpdateModal.jsx index 94335cd2..4fef1c71 100644 --- a/src-ui/app/modal_controller/update_modal/UpdateModal.jsx +++ b/src-ui/app/modal_controller/update_modal/UpdateModal.jsx @@ -1,8 +1,14 @@ import styles from "./UpdateModal.module.scss"; import { useTranslation } from "react-i18next"; import { useStore_OpenedQuickSetting } from "@store"; -import { useComputeMode, useUpdateSoftware } from "@logics_common"; -import { useIsSoftwareUpdating, useIsSoftwareUpdateAvailable } from "@logics_common"; +import { usePlugins } from "@logics_configs"; +import { + useComputeMode, + useUpdateSoftware, + useIsSoftwareUpdating, + useSoftwareVersion, +} from "@logics_common"; + import clsx from "clsx"; export const UpdateModal = () => { @@ -11,9 +17,13 @@ export const UpdateModal = () => { const { updateSoftware, updateSoftware_CUDA } = useUpdateSoftware(); const { updateIsSoftwareUpdating } = useIsSoftwareUpdating(); const { currentComputeMode } = useComputeMode(); - const { currentIsSoftwareUpdateAvailable } = useIsSoftwareUpdateAvailable(); + const { currentLatestSoftwareVersionInfo } = useSoftwareVersion(); + const { isAnyPluginEnabled } = usePlugins(); - const is_latest_version_already = currentIsSoftwareUpdateAvailable.data === false; + console.log(isAnyPluginEnabled()); + + + const is_latest_version_already = currentLatestSoftwareVersionInfo.data.is_update_available === false; const is_cpu_version = currentComputeMode.data === "cpu"; const onClickUpdateSoftware = () => { @@ -37,6 +47,7 @@ export const UpdateModal = () => { return (
+ {isAnyPluginEnabled() && }
@@ -85,4 +96,20 @@ const CurrentVersionLabel = (props) => { return

{t("update_modal.is_latest_version_already")}

; } return

{t("update_modal.is_current_compute_device")}

; +}; + +const PluginUpdateNotification = () => { + const { enabledPluginsList } = usePlugins(); + + const incompatible_plugins_list = enabledPluginsList(); + + return ( +
+ {incompatible_plugins_list.map(plugin => { + console.log(plugin); + + return

{plugin.title}

+ })} +
+ ); }; \ No newline at end of file diff --git a/src-ui/logics/common/index.js b/src-ui/logics/common/index.js index 5feee60e..e95bae8f 100644 --- a/src-ui/logics/common/index.js +++ b/src-ui/logics/common/index.js @@ -1,9 +1,9 @@ +export { useSoftwareVersion } from "./useSoftwareVersion"; export { useComputeMode } from "./useComputeMode"; export { useInitProgress } from "./useInitProgress"; export { useIsBackendReady } from "./useIsBackendReady"; export { useWindow } from "./useWindow"; export { useIsOpenedConfigPage } from "./useIsOpenedConfigPage"; -export { useIsSoftwareUpdateAvailable } from "./useIsSoftwareUpdateAvailable"; export { useIsSoftwareUpdating } from "./useIsSoftwareUpdating"; export { useNotificationStatus } from "./useNotificationStatus"; export { useOpenFolder } from "./useOpenFolder"; diff --git a/src-ui/logics/common/useIsSoftwareUpdateAvailable.js b/src-ui/logics/common/useIsSoftwareUpdateAvailable.js deleted file mode 100644 index 569a3949..00000000 --- a/src-ui/logics/common/useIsSoftwareUpdateAvailable.js +++ /dev/null @@ -1,10 +0,0 @@ -import { useStore_IsSoftwareUpdateAvailable } from "@store"; - -export const useIsSoftwareUpdateAvailable = () => { - const { currentIsSoftwareUpdateAvailable, updateIsSoftwareUpdateAvailable } = useStore_IsSoftwareUpdateAvailable(); - - return { - currentIsSoftwareUpdateAvailable, - updateIsSoftwareUpdateAvailable, - }; -}; \ No newline at end of file diff --git a/src-ui/logics/common/useSoftwareVersion.js b/src-ui/logics/common/useSoftwareVersion.js new file mode 100644 index 00000000..18f24a7f --- /dev/null +++ b/src-ui/logics/common/useSoftwareVersion.js @@ -0,0 +1,40 @@ +import semver from "semver"; + +import { useStore_SoftwareVersion, useStore_LatestSoftwareVersionInfo } from "@store"; +import { useStdoutToPython } from "@logics/useStdoutToPython"; + +export const useSoftwareVersion = () => { + const { asyncStdoutToPython } = useStdoutToPython(); + const { currentLatestSoftwareVersionInfo, updateLatestSoftwareVersionInfo } = useStore_LatestSoftwareVersionInfo(); + const { currentSoftwareVersion, updateSoftwareVersion, pendingSoftwareVersion } = useStore_SoftwareVersion(); + + const getSoftwareVersion = () => { + pendingSoftwareVersion(); + asyncStdoutToPython("/get/data/version"); + }; + + const isPluginCompatible = (main_version, lower_version, upper_version) => { + // lower_version 以上かつ upper_version 以下なら互換性ありと判定 + return semver.gte(main_version, lower_version) && semver.lte(main_version, upper_version); + }; + + const checkVrctVerCompatibility = (min_version, max_version) => { + const current_vrct_version = currentSoftwareVersion.data; + const latest_vrct_version = currentLatestSoftwareVersionInfo.data.new_version; + + const is_plugin_supported = isPluginCompatible(current_vrct_version, min_version, max_version); + const is_plugin_supported_latest_vrct = isPluginCompatible(latest_vrct_version, min_version, max_version); + return { is_plugin_supported, is_plugin_supported_latest_vrct }; + }; + + return { + currentSoftwareVersion, + getSoftwareVersion, + updateSoftwareVersion, + + currentLatestSoftwareVersionInfo, + updateLatestSoftwareVersionInfo, + + checkVrctVerCompatibility, + }; +}; \ No newline at end of file diff --git a/src-ui/logics/configs/index.js b/src-ui/logics/configs/index.js index 85d7c553..912e2a7d 100644 --- a/src-ui/logics/configs/index.js +++ b/src-ui/logics/configs/index.js @@ -62,6 +62,4 @@ export { useSupporters } from "./supporters/useSupporters"; export { usePlugins } from "./plugins/usePlugins"; - -export { useSettingBoxScrollPosition } from "./useSettingBoxScrollPosition"; -export { useSoftwareVersion } from "./useSoftwareVersion"; \ No newline at end of file +export { useSettingBoxScrollPosition } from "./useSettingBoxScrollPosition"; \ No newline at end of file diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 22137654..d4910f46 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -1,10 +1,9 @@ -import semver from "semver"; - import { invoke } from "@tauri-apps/api/tauri"; import { createAtomWithHook, useStore_SavedPluginsStatus, useStore_PluginsData, + useStore_IsPluginsInitialized, } from "@store"; import { useStdoutToPython } from "@logics/useStdoutToPython"; @@ -21,8 +20,7 @@ dev_plugins.forEach(async ({entry_path}) => { import JSZip from "jszip"; -import { useFetch } from "@logics_common"; -import { useSoftwareVersion } from "@logics_configs"; +import { useFetch, useSoftwareVersion } from "@logics_common"; import * as logics_configs from "@logics_configs"; import * as logics_main from "@logics_main"; @@ -36,30 +34,66 @@ export const usePlugins = () => { const { asyncStdoutToPython } = useStdoutToPython(); const { currentSavedPluginsStatus, updateSavedPluginsStatus, pendingSavedPluginsStatus } = useStore_SavedPluginsStatus(); const { currentPluginsData, updatePluginsData, pendingPluginsData } = useStore_PluginsData(); - const { currentSoftwareVersion } = useSoftwareVersion(); + const { currentIsPluginsInitialized, updateIsPluginsInitialized, pendingIsPluginsInitialized } = useStore_IsPluginsInitialized(); + const { checkVrctVerCompatibility } = useSoftwareVersion(); const { asyncTauriFetchGithub } = useFetch(); - const generatePluginContext= (plugin_info) => { + const generatePluginContext= (downloaded_plugin_info) => { const plugin_context = { registerComponent: (component) => { - if (!plugin_info.plugin_id || !plugin_info.location || !component) { - return console.error("An invalid plugin was detected.", plugin_info.plugin_id, plugin_info.location, component); + if (!downloaded_plugin_info.plugin_id || !downloaded_plugin_info.location || !component) { + return console.error("An invalid plugin was detected.", downloaded_plugin_info.plugin_id, downloaded_plugin_info.location, component); } updatePluginsData(prev => { - const new_value = prev.data.map(old_value => - old_value.plugin_id === plugin_info.plugin_id - ? { - ...old_value, - ...plugin_info, - downloaded_plugin_version: plugin_info.plugin_version, + const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); + const new_data = []; + let new_value = {}; + + if (!prev_map.has(downloaded_plugin_info.plugin_id)) { // 未ダウンロード 新規登録 + new_value = { + plugin_id: downloaded_plugin_info.plugin_id, + component: component, + is_downloaded: true, + is_latest_version_available: false, + downloaded_plugin_info: { + ...downloaded_plugin_info, + component: component, + is_plugin_supported: is_plugin_supported, + is_plugin_supported_latest_vrct: is_plugin_supported_latest_vrct, + }, + }; + return [...prev.data, new_value]; + } + + for (const old_plugin_data of prev.data) { + const { is_plugin_supported, is_plugin_supported_latest_vrct } = checkVrctVerCompatibility(downloaded_plugin_info.min_supported_vrct_version, downloaded_plugin_info.max_supported_vrct_version); + + if (prev_map.has(downloaded_plugin_info.plugin_id) && old_plugin_data.plugin_id === downloaded_plugin_info.plugin_id) { // ダウンロード済み + + + const target_prev_plugin = prev_map.get(downloaded_plugin_info.plugin_id); + const is_latest_version_available = (target_prev_plugin.is_downloaded) && !(downloaded_plugin_info.plugin_version === target_prev_plugin.latest_plugin_info.plugin_version); + + new_value = { + ...target_prev_plugin, + plugin_id: downloaded_plugin_info.plugin_id, component: component, is_downloaded: true, - is_latest_version_available: false, - } : old_value - ); - - return new_value; + is_latest_version_available: is_latest_version_available, + downloaded_plugin_info: { + ...downloaded_plugin_info, + component: component, + is_plugin_supported: is_plugin_supported, + is_plugin_supported_latest_vrct: is_plugin_supported_latest_vrct, + }, + }; + } else { + new_value = old_plugin_data; + } + new_data.push(new_value); + } + return new_data; }); }, createAtomWithHook: (...args) => createAtomWithHook(...args), @@ -70,11 +104,11 @@ export const usePlugins = () => { const asyncLoadPlugin = async (plugin_folder_relative_path) => { const init_path = "plugins/" + plugin_folder_relative_path + "/index.esm.js"; - const plugin_info_path = "plugins/" + plugin_folder_relative_path + "/plugin_info.json"; + const downloaded_plugin_info_path = "plugins/" + plugin_folder_relative_path + "/plugin_info.json"; const plugin_css_path = "plugins/" + plugin_folder_relative_path + "/main.css"; try { - const plugin_info_json = await readTextFile(plugin_info_path, { dir: BaseDirectory.Resource, recursive: true }); - const plugin_info = JSON.parse(plugin_info_json); + const downloaded_plugin_info_json = await readTextFile(downloaded_plugin_info_path, { dir: BaseDirectory.Resource, recursive: true }); + const downloaded_plugin_info = JSON.parse(downloaded_plugin_info_json); const plugin_code = await readTextFile(init_path, { dir: BaseDirectory.Resource, recursive: true }); const cleaned_code = removeImportStatements(plugin_code); @@ -91,7 +125,7 @@ export const usePlugins = () => { URL.revokeObjectURL(blob_url); if (plugin_module && plugin_module.init) { - plugin_module.init(generatePluginContext(plugin_info)); + plugin_module.init(generatePluginContext(downloaded_plugin_info)); } await loadPluginCSS(plugin_css_path); @@ -102,16 +136,16 @@ export const usePlugins = () => { const asyncLoadAllPlugins = async () => { if (!import.meta.env.DEV) { - imported_dev_plugins.forEach(({ index, plugin_info }) => { - if (!index || !plugin_info) { - console.error("Invalid development plugin detected", index, plugin_info); + imported_dev_plugins.forEach(({ index, downloaded_plugin_info }) => { + if (!index || !downloaded_plugin_info) { + console.error("Invalid development plugin detected", index, downloaded_plugin_info); return; } - const plugin_context = generatePluginContext(plugin_info); + const plugin_context = generatePluginContext(downloaded_plugin_info); if (index.init) { index.init(plugin_context); } else { - console.error("Plugin missing init function", plugin_info); + console.error("Plugin missing init function", downloaded_plugin_info); } }); } else { @@ -128,8 +162,9 @@ export const usePlugins = () => { }; const downloadAndExtractPlugin = async (plugin) => { + const latest_plugin_info = plugin.latest_plugin_info; try { - const plugin_zip_url = await fetchLatestPluginZipUrl(plugin); + const plugin_zip_url = await fetchLatestPluginZipUrl(latest_plugin_info); // Rust コマンド経由で ZIP をダウンロード const base64_zip = await invoke("download_zip_asset", { url: plugin_zip_url }); // base64_zip をデコードして Uint8Array に変換 @@ -144,7 +179,7 @@ export const usePlugins = () => { const zip = await JSZip.loadAsync(bytes); // 展開先ディレクトリのパス(例:"plugins/" とする) - const target_plugin_path = "plugins/" + plugin.plugin_id; + const target_plugin_path = "plugins/" + latest_plugin_info.plugin_id; // 既に存在する場合は削除してから新規作成 if (await exists(target_plugin_path, { dir: BaseDirectory.Resource, recursive: true })) { await removeDir(target_plugin_path, { dir: BaseDirectory.Resource, recursive: true }); @@ -220,13 +255,13 @@ export const usePlugins = () => { plugins_data.map(async (plugin_data) => { try { const plugin_info = await asyncFetchPluginInfo(plugin_data.url); - return { ...plugin_info }; + return plugin_info; } catch (error) { console.error("Error fetching plugin info for URL:", plugin_data.url, error); - // エラー発生時は、plugin_data.title とエラーメッセージを返す return { title: plugin_data.title, plugin_id: plugin_data.plugin_id || plugin_data.title, + is_error: true, error: error.message, url: plugin_data.url }; @@ -254,14 +289,8 @@ export const usePlugins = () => { } const plugin_info = plugin_info_json_response.data; - const isPluginCompatible = (main_version, lower_version, upper_version) => { - console.log(main_version, lower_version, upper_version); - // lower_version 以上かつ upper_version 以下なら互換性ありと判定 - return semver.gte(main_version, lower_version) && semver.lte(main_version, upper_version); - }; - - const is_plugin_supported = isPluginCompatible(currentSoftwareVersion.data, plugin_info.min_supported_vrct_version, plugin_info.max_supported_vrct_version); + const { is_plugin_supported, is_plugin_supported_latest_vrct } = checkVrctVerCompatibility(plugin_info.min_supported_vrct_version, plugin_info.max_supported_vrct_version); return { title: plugin_info.title, @@ -270,21 +299,47 @@ export const usePlugins = () => { min_supported_vrct_version: plugin_info.min_supported_vrct_version, max_supported_vrct_version: plugin_info.max_supported_vrct_version, is_plugin_supported: is_plugin_supported, + is_plugin_supported_latest_vrct: is_plugin_supported_latest_vrct, asset_name: plugin_info.asset_name, url: plugin_info_asset_url }; } + const handlePendingPlugin = (target_plugin_id, is_pending) => { + updatePluginsData((old_value) => { + const new_value = old_value.data.map((d) => { + if (d.plugin_id === target_plugin_id) { + d.is_pending = is_pending; + } + return d; + }); + return new_value; + }); + }; + + + const setSavedPluginsStatus = (plugins_status) => { pendingSavedPluginsStatus(); asyncStdoutToPython("/set/data/plugins_status", plugins_status); }; + const isAnyPluginEnabled = () => { + return currentPluginsData.data.some(plugin => plugin.is_enabled); + }; + + const enabledPluginsList = () => { + return currentPluginsData.data.filter(plugin => plugin.is_enabled); + } + return { asyncFetchPluginsInfo, + isAnyPluginEnabled, + enabledPluginsList, + asyncLoadAllPlugins, downloadAndExtractPlugin, @@ -294,7 +349,12 @@ export const usePlugins = () => { currentPluginsData, updatePluginsData, + currentIsPluginsInitialized, + updateIsPluginsInitialized, + setSavedPluginsStatus, + + handlePendingPlugin, }; }; diff --git a/src-ui/logics/configs/useSoftwareVersion.js b/src-ui/logics/configs/useSoftwareVersion.js deleted file mode 100644 index c81b81c2..00000000 --- a/src-ui/logics/configs/useSoftwareVersion.js +++ /dev/null @@ -1,18 +0,0 @@ -import { useStore_SoftwareVersion } from "@store"; -import { useStdoutToPython } from "@logics/useStdoutToPython"; - -export const useSoftwareVersion = () => { - const { asyncStdoutToPython } = useStdoutToPython(); - const { currentSoftwareVersion, updateSoftwareVersion, pendingSoftwareVersion } = useStore_SoftwareVersion(); - - const getSoftwareVersion = () => { - pendingSoftwareVersion(); - asyncStdoutToPython("/get/data/version"); - }; - - return { - currentSoftwareVersion, - getSoftwareVersion, - updateSoftwareVersion, - }; -}; \ No newline at end of file diff --git a/src-ui/logics/useReceiveRoutes.js b/src-ui/logics/useReceiveRoutes.js index 4575c3d8..b73fe62e 100644 --- a/src-ui/logics/useReceiveRoutes.js +++ b/src-ui/logics/useReceiveRoutes.js @@ -8,13 +8,13 @@ import { useNotificationStatus, useHandleNetworkConnection, + useSoftwareVersion, useComputeMode, useInitProgress, useIsBackendReady, useWindow, useMessage, useVolume, - useIsSoftwareUpdateAvailable, } from "@logics_common"; import { @@ -26,7 +26,6 @@ import { } from "@logics_main"; import { - useSoftwareVersion, useEnableAutoMicSelect, useEnableAutoSpeakerSelect, useMicHostList, @@ -105,7 +104,7 @@ export const useReceiveRoutes = () => { addSentMessageLog, addReceivedMessageLog, } = useMessage(); - const { updateIsSoftwareUpdateAvailable } = useIsSoftwareUpdateAvailable(); + const { updateLatestSoftwareVersionInfo } = useSoftwareVersion(); const { updateSoftwareVersion } = useSoftwareVersion(); const { updateEnableAutoMicSelect } = useEnableAutoMicSelect(); const { updateEnableAutoSpeakerSelect } = useEnableAutoSpeakerSelect(); @@ -207,7 +206,7 @@ export const useReceiveRoutes = () => { "/set/data/main_window_geometry": () => {}, "/run/open_filepath_logs": () => console.log("Opened Directory, Message Logs"), "/run/open_filepath_config_file": () => console.log("Opened Directory, Config File"), - "/run/update_software_flag": updateIsSoftwareUpdateAvailable, + "/run/software_update_info": updateLatestSoftwareVersionInfo, "/run/connected_network": handleNetworkConnection, // Main Page diff --git a/src-ui/store.js b/src-ui/store.js index 7e88792b..5adbb4c7 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -114,7 +114,10 @@ export const { atomInstance: Atom_MainFunctionsStateMemory, useHook: useStore_Ma transcription_receive: false, }, "MainFunctionsStateMemory"); export const { atomInstance: Atom_OpenedQuickSetting, useHook: useStore_OpenedQuickSetting } = createAtomWithHook("", "OpenedQuickSetting"); -export const { atomInstance: Atom_IsSoftwareUpdateAvailable, useHook: useStore_IsSoftwareUpdateAvailable } = createAtomWithHook(false, "IsSoftwareUpdateAvailable"); +export const { atomInstance: Atom_LatestSoftwareVersionInfo, useHook: useStore_LatestSoftwareVersionInfo } = createAtomWithHook({ + is_update_available: false, + new_version: "", +}, "LatestSoftwareVersionInfo"); export const { atomInstance: Atom_InitProgress, useHook: useStore_InitProgress } = createAtomWithHook(0, "InitProgress"); export const { atomInstance: Atom_IsBreakPoint, useHook: useStore_IsBreakPoint } = createAtomWithHook(false, "IsBreakPoint"); export const { atomInstance: Atom_IsSoftwareUpdating, useHook: useStore_IsSoftwareUpdating } = createAtomWithHook(false, "IsSoftwareUpdating"); @@ -275,7 +278,7 @@ export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createA }, "Hotkeys"); // Plugins -// export const { atomInstance: Atom_LoadedPluginsList, useHook: useStore_LoadedPluginsList } = createAtomWithHook([], "LoadedPluginsList"); +export const { atomInstance: Atom_IsPluginsInitialized, useHook: useStore_IsPluginsInitialized } = createAtomWithHook(false, "IsPluginsInitialized"); export const { atomInstance: Atom_SavedPluginsStatus, useHook: useStore_SavedPluginsStatus } = createAtomWithHook([], "SavedPluginsStatus"); export const { atomInstance: Atom_PluginsData, useHook: useStore_PluginsData } = createAtomWithHook([], "PluginsData"); From 2157d5952c12124d654d8450d7cb19b653a70481 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 10 Apr 2025 21:47:18 +0900 Subject: [PATCH 23/68] [UPdate] Add error boundary. --- .gitignore | 3 +- map-stack.js | 78 +++++++++++++ package-lock.json | 80 ++++++++----- package.json | 4 +- src-ui/app/App.jsx | 31 ++--- .../app/error_boundary/AppErrorBoundary.jsx | 89 ++++++++++++++ .../AppErrorBoundary.module.scss | 110 ++++++++++++++++++ .../contacts_container/ContactsContainer.jsx | 29 +++++ .../ContactsContainer.module.scss | 28 +++++ src-ui/assets/document.png | Bin 0 -> 1126 bytes vite.config.js | 1 + 11 files changed, 408 insertions(+), 45 deletions(-) create mode 100644 map-stack.js create mode 100644 src-ui/app/error_boundary/AppErrorBoundary.jsx create mode 100644 src-ui/app/error_boundary/AppErrorBoundary.module.scss create mode 100644 src-ui/app/error_boundary/contacts_container/ContactsContainer.jsx create mode 100644 src-ui/app/error_boundary/contacts_container/ContactsContainer.module.scss create mode 100644 src-ui/assets/document.png diff --git a/.gitignore b/.gitignore index 43c32504..e628117a 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,5 @@ dist-ssr .venv # Customize -/build \ No newline at end of file +/build +error.txt \ No newline at end of file diff --git a/map-stack.js b/map-stack.js new file mode 100644 index 00000000..9deb67f1 --- /dev/null +++ b/map-stack.js @@ -0,0 +1,78 @@ +#!/usr/bin/env node +// 使用例: node map-stack.js +// カレントディレクトリの error.txt を読み込み、各エラーフレームから +// 対応するソースマップ(dist/assets/ 以下の *.js.map)を用いて元の位置情報を出力する + +import fs from "fs"; +import path from "path"; +import { SourceMapConsumer } from "source-map"; + +// 各スタックフレームにマッチする正規表現 +const FRAME_REGEX = /^\s*at\s+(.*?)\s+\((.*):(\d+):(\d+)\)$/; + +// スタックトレースのパース関数 +const parseStackTrace = (text) => { + return text + .split(/\r?\n/) + .map((line) => { + const match = line.match(FRAME_REGEX); + if (match) { + return { + original: line, + functionName: match[1], + file: match[2], + line: Number(match[3]), + column: Number(match[4]) + }; + } else { + return { original: line }; + } + }); +}; + +// エラースタックをソースマップから逆引きする関数(位置情報のみを表示) +const mapStackTrace = async () => { + const errorTxtPath = path.resolve(process.cwd(), "error.txt"); + const stackTraceText = fs.readFileSync(errorTxtPath, "utf8"); + const frames = parseStackTrace(stackTraceText); + + const consumerMap = new Map(); + + const mappedFrames = await Promise.all(frames.map(async (frame) => { + if (frame.file && frame.line && frame.column) { + const relativeFile = frame.file.replace(/^\//, ""); // 例: "assets/main-Td8-sruo.js" + const mapFilePath = path.resolve(process.cwd(), "dist", relativeFile + ".map"); + let consumer = consumerMap.get(mapFilePath); + if (!consumer) { + const rawSourceMap = fs.readFileSync(mapFilePath, "utf8"); + consumer = await new SourceMapConsumer(rawSourceMap); + consumerMap.set(mapFilePath, consumer); + } + const pos = consumer.originalPositionFor({ + line: frame.line, + column: frame.column + }); + + if (pos && pos.source && pos.line != null && pos.column != null) { + return ` at ${frame.functionName} (${pos.source}:${pos.line}:${pos.column})`; + } else { + return frame.original; + } + } else { + return frame.original; + } + })); + + consumerMap.forEach((consumer) => consumer.destroy()); + return mappedFrames.join("\n"); +}; + +mapStackTrace() + .then((mapped) => { + console.log("--- Mapped Stack Trace ---"); + console.log(mapped); + }) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/package-lock.json b/package-lock.json index 62bd3e7f..85dac096 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "jszip": "3.10.1", "react": "18.2.0", "react-dom": "18.2.0", + "react-error-boundary": "5.0.0", "react-i18next": "15.2.0", "react-resizable-layout": "0.7.2", "semver": "7.7.1" @@ -32,7 +33,8 @@ "@tauri-apps/cli": "1.6.3", "npm-run-all": "4.1.5", "sass": "1.79.4", - "vite": "6.2.1", + "source-map": "^0.7.4", + "vite": "6.2.5", "vite-plugin-svgr": "4.3.0" } }, @@ -210,23 +212,23 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.27.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -264,9 +266,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -283,13 +285,13 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" @@ -313,9 +315,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -342,6 +344,14 @@ "stylis": "4.2.0" } }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@emotion/cache": { "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", @@ -4677,6 +4687,17 @@ "react": "^18.2.0" } }, + "node_modules/react-error-boundary": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-5.0.0.tgz", + "integrity": "sha512-tnjAxG+IkpLephNcePNA7v6F/QpWLH8He65+DmedchDwg162JZqx4NmbXj0mlAYVVEd81OW7aFhmbsScYfiAFQ==", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, "node_modules/react-i18next": { "version": "15.2.0", "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.2.0.tgz", @@ -5108,11 +5129,12 @@ } }, "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, "node_modules/source-map-js": { @@ -5495,9 +5517,9 @@ } }, "node_modules/vite": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.1.tgz", - "integrity": "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==", + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz", + "integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==", "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", diff --git a/package.json b/package.json index 9de73950..de100d5d 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "jszip": "3.10.1", "react": "18.2.0", "react-dom": "18.2.0", + "react-error-boundary": "5.0.0", "react-i18next": "15.2.0", "react-resizable-layout": "0.7.2", "semver": "7.7.1" @@ -47,7 +48,8 @@ "@tauri-apps/cli": "1.6.3", "npm-run-all": "4.1.5", "sass": "1.79.4", - "vite": "6.2.1", + "source-map": "0.7.4", + "vite": "6.2.5", "vite-plugin-svgr": "4.3.0" } } diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index c102af71..8c33ed37 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -22,6 +22,7 @@ import { ModalController } from "./modal_controller/ModalController"; import { SnackbarController } from "./snackbar_controller/SnackbarController"; import styles from "./App.module.scss"; import { useIsBackendReady, useIsSoftwareUpdating, useIsVrctAvailable, useWindow } from "@logics_common"; +import { AppErrorBoundary } from "./error_boundary/AppErrorBoundary"; export const App = () => { const { currentIsVrctAvailable } = useIsVrctAvailable(); @@ -32,22 +33,24 @@ export const App = () => { return (
- - - - - - - - - + + + + + + + + + + - {(currentIsBackendReady.data === false || currentIsVrctAvailable.data === false) - ? - : - } + {(currentIsBackendReady.data === false || currentIsVrctAvailable.data === false) + ? + : + } - + +
); }; diff --git a/src-ui/app/error_boundary/AppErrorBoundary.jsx b/src-ui/app/error_boundary/AppErrorBoundary.jsx new file mode 100644 index 00000000..cb8550a0 --- /dev/null +++ b/src-ui/app/error_boundary/AppErrorBoundary.jsx @@ -0,0 +1,89 @@ +import { useState } from "react"; +import { appWindow } from "@tauri-apps/api/window"; +import { ErrorBoundary } from "react-error-boundary"; +import XMarkSvg from "@images/cancel.svg?react"; +import CopySvg from "@images/copy.svg?react"; +import CheckMarkSvg from "@images/check_mark.svg?react"; + +import { ContactsContainer } from "./contacts_container/ContactsContainer"; + +import styles from "./AppErrorBoundary.module.scss"; + +export const AppErrorBoundary = ({children}) => { + return ( + ( + + ) + }> + {children} + + ); +}; + +const ErrorContainer = ({error}) => { + const [is_copied, setIsCopied] = useState(false); + + const formatted_stack = error ? formatStackTrace(error.stack) : "Unknown error"; + + const copyToClipboard = async () => { + if (is_copied) return; + + await navigator.clipboard.writeText(formatted_stack); + setIsCopied(true); + + setTimeout(() => { + setIsCopied(false); + }, 1000); + }; + + return ( +
+ +
+

An error occurred. Please restart VRCT or contact the developers.

+ {error ? +
+
+

+ {formatted_stack} +

+
+ +
+ : null} + +
+
+ ); +}; + +const CloseButtonContainer = () => { + const close = () => { + appWindow.close(); + }; + + return ( + + ); +}; + + +const formatStackTrace = (stack) => { + if (!stack) return ""; + // フルパスの除去(例として window.location.origin や絶対パス部分を削除) + // ※必要に応じて正規表現を調整してください + const formatted = stack.replace(new RegExp(window.location.origin, "g"), ""); + + return formatted; +}; \ No newline at end of file diff --git a/src-ui/app/error_boundary/AppErrorBoundary.module.scss b/src-ui/app/error_boundary/AppErrorBoundary.module.scss new file mode 100644 index 00000000..2abb2710 --- /dev/null +++ b/src-ui/app/error_boundary/AppErrorBoundary.module.scss @@ -0,0 +1,110 @@ +.container { + width: 100%; + height: 100%; + position: relative; +} + +.wrapper { + width: 100%; + height: 100vh; + display: flex; + justify-content: safe center; + align-items: center; + flex-direction: column; + padding: 2rem; + overflow-y: auto; +} + +.error_message { + font-size: 2rem; + text-align: center; + user-select: text; + margin-bottom: 3.2rem; +} + + +.error_detail_container { + display: flex; + flex-direction: column; + align-items: end; + gap: 1rem; +} +.error_stack_container { + max-height: 10rem; + width: 100%; + overflow-y: scroll; + padding: 1rem; + background-color: var(--dark_950_color); + border-radius: 0.4rem; +} +.error_stack { + font-size: 1rem; + user-select: text; +} + +.copy_error_message_button { + // background-color: var(--dark_800_color); + padding: 0.8rem 1rem; + font-size: 1.4rem; + display: flex; + gap: 1rem; + justify-content: center; + align-items: center; + border-radius: 0.4rem; + background-color: var(--dark_825_color); + &:hover { + background-color: var(--dark_800_color); + } + &:active { + background-color: var(--dark_850_color); + } +} + +.copy_svg { + width: 1.4rem; + color: var(--dark_500_color); +} + +.check_mark_svg { + width: 1.4rem; + color: var(--primary_300_color); +} + + + +.close_button_wrapper { + position: absolute; + top: 0; + left: 100%; + transform: translate(-50%, -50%) rotate(45deg); + display: flex; + justify-content: center; + align-items: end; + width: 68px; + aspect-ratio: 1 / 1; + background-color: var(--error_bc_color); + & .x_mark_svg { + color: var(--dark_200_color); + } + &:hover { + & .x_mark_svg { + transform: rotate(45deg); + } + } + &:active { + background-color: var(--error_bc_active_color); + } + transition: all 0.1s ease; +} + +.close_button { + // width: 100%; + // height: 100%; +} + +.x_mark_svg { + width: 24px; + transform: rotate(-45deg); + color: var(--dark_700_color); + transition: transform 0.3s ease; +} \ No newline at end of file diff --git a/src-ui/app/error_boundary/contacts_container/ContactsContainer.jsx b/src-ui/app/error_boundary/contacts_container/ContactsContainer.jsx new file mode 100644 index 00000000..965e7365 --- /dev/null +++ b/src-ui/app/error_boundary/contacts_container/ContactsContainer.jsx @@ -0,0 +1,29 @@ +import styles from "./ContactsContainer.module.scss"; + +export const ContactsContainer = () => { + return ( +
+ + +
+ ); +}; + +import dev_github_icon from "@images/about_vrct/dev_github_icon.png"; +import document from "@images/document.png"; + +const contacts_links = { + github_issues: { img: dev_github_icon, href: "https://github.com/misyaguziya/VRCT/issues" }, + google_forms: { img: document, href: "https://docs.google.com/forms/d/e/1FAIpQLSei-xoydOY60ivXqhOjaTzNN8PiBQIDcNhzfy6cw2sjYkcg_g/viewform" }, +}; + +const OpenLinkContainer = ({className, href_id, text}) => { + const href = contacts_links[href_id].href; + const img = contacts_links[href_id].img; + return ( + + +

{text}

+
+ ); +}; \ No newline at end of file diff --git a/src-ui/app/error_boundary/contacts_container/ContactsContainer.module.scss b/src-ui/app/error_boundary/contacts_container/ContactsContainer.module.scss new file mode 100644 index 00000000..5e291d0f --- /dev/null +++ b/src-ui/app/error_boundary/contacts_container/ContactsContainer.module.scss @@ -0,0 +1,28 @@ +.container { + display: flex; + gap: 3.2rem; +} + +.github_issues, .google_forms { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + height: 100%; + padding: 1rem; + border-radius: 0.4rem; + gap: 1rem; + &:hover { + background-color: var(--dark_800_color); + } + &:active { + background-color: var(--dark_900_color) + } +} +.contact_button_icon { + width: 5.2rem; +} +.contact_button_label { + font-size: 1.4rem; + white-space: nowrap; +} \ No newline at end of file diff --git a/src-ui/assets/document.png b/src-ui/assets/document.png new file mode 100644 index 0000000000000000000000000000000000000000..cfe54e2d0491e01096a2f12c92b9242031399d1b GIT binary patch literal 1126 zcmeAS@N?(olHy`uVBq!ia0vp^A3&Ic4M^IBzMKT4I14-?iy0XBj({-ZRBb+K1_l-h zPZ!6Kid%2*TIVUdi#S}IcyLBz(AqP=)91@YD44C%q41$KG)Y;sjKat z&7dFm>B83!FVAhu-F@fxtTV5_e*Jp-@#Dw$-R<44%DdY$WLwYmtGBhcuebYUxr*h? zLzfzBMq?Ji`)&t#oR3@=HgFdFSjuvyz-7<;2FXbs-vtd03w=D!a^`}|pMCAs@dfYI zuKldp_j|A8@xA7sa&q>bWl=k*Qa8Kdq`=4XEIwTw`_&WpN_WQm+xg8}>7UmDnO9Gr zKJ^#8?+0YPeEBl;$aZ0acHxh2S9FOD+8%Qeso7vzg`0+c7PhZD=Ap=jPfBg+7g*G;HM%yMa zPTpR0gei?{wVO`DvW-bVrq*g#AaipNkm8%~CJoXwu^%c&l zNsNc}MU=!0_9TA+ipon^0tNUM1C70rd89-BUG2f#+j`Y@<#zMm*X1AE_`QC^tm^;o zeG@o@4O%!Q4zchgGPM~nI?rfO^f@4)mcU^x`mdKI;Vst0Hz(8&j0`b literal 0 HcmV?d00001 diff --git a/vite.config.js b/vite.config.js index 948083e4..b46e13f1 100644 --- a/vite.config.js +++ b/vite.config.js @@ -35,6 +35,7 @@ export default defineConfig(async () => { main: path.resolve(__dirname, "index.html"), }, }, + sourcemap: true, }, resolve: { From d4219b5ce08de2ba31d3f180278e2ec2bc6dc9fe Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Fri, 11 Apr 2025 19:14:55 +0900 Subject: [PATCH 24/68] [Update/Refactor] Plugins: Organize codes. --- .../PluginsControlComponent.jsx | 47 +++---------------- .../setting_box/plugins/Plugins.jsx | 11 ++--- src-ui/logics/configs/plugins/usePlugins.js | 6 +-- 3 files changed, 14 insertions(+), 50 deletions(-) diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx index 0fd7c33a..0bdbfd02 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx @@ -3,14 +3,12 @@ import { SwitchBox } from "../index"; import { _DownloadButton } from "../_atoms/_download_button/_DownloadButton"; import styles from "./PluginsControlComponent.module.scss"; -// メインのコントロールコンポーネント。ダウンロード済み / 未ダウンロードで分岐して表示する export const PluginsControlComponent = ({ variable_state, plugin_status, toggleFunction, downloadStartFunction, }) => { - // 共通オプション(各子コンポーネントに引き回す情報) const option = { id: plugin_status.plugin_id, is_pending: plugin_status.is_pending, @@ -41,84 +39,53 @@ export const PluginsControlComponent = ({ } }; -// ------------------------- -// ダウンロード済みのプラグイン用コンポーネント -// 状態により以下の分岐を行う -// ・ is_latest_version_already が true なら「最新版を使用中」 -// ・ is_latest_version_already が false かつ is_latest_version_available が true なら「最新版を利用可能」(アップデートボタン+スイッチ) -// ・ それ以外(is_latest_version_already:false && is_latest_version_available: false)なら、desc等の情報とスイッチのみ表示 + const DownloadedPluginControl = ({ option, plugin_status, toggleFunction, downloadStartFunction, }) => { - // on/off トグル時の処理 const togglePlugin = () => { toggleFunction(plugin_status.plugin_id); }; + const latest_version = plugin_status.latest_plugin_info?.plugin_version; - // ダウンロード済みの場合、ダウンロードされた情報からタイトルやバージョンを取得 - const title = plugin_status.downloaded_plugin_info?.title || plugin_status.latest_plugin_info.title; - const current_version = - plugin_status.downloaded_plugin_info?.plugin_version || plugin_status.latest_plugin_info.plugin_version; - - // コンポーネントごとに表示内容を分岐 if (plugin_status.is_latest_version_already) { - // 最新版が既に使用中 return (
-

{title}

-

現在のバージョン: {current_version}

+

最新のバージョン: {latest_version}

最新版を使用中

); } else if (plugin_status.is_latest_version_available) { - // 最新版の利用可能なお知らせとアップデートボタン+スイッチ return (
-

{title}

-

現在のバージョン: {current_version}

+

最新のバージョン: {latest_version}

最新版を利用可能

<_DownloadButton option={option} downloadStartFunction={downloadStartFunction} />
); } else { - // 最新版利用可能ではないがダウンロード済み - // ※「desc」に関しては、情報があればplugin_status.descやlatest_plugin_info.descを利用してください - const desc = - plugin_status.latest_plugin_info.desc || - "追加情報がありません。"; return (
-

{title}

-

現在のバージョン: {current_version}

-

{desc}

+

最新のバージョン: {latest_version}

); } }; -// ------------------------- -// 未ダウンロードのプラグイン用コンポーネント -// 状態により以下の分岐を行う -// ・ is_latest_version_available が true なら:info_title, 最新バージョン情報とダウンロードボタン -// ・ is_latest_version_available が false なら:info_title, 最新バージョン情報と「現在利用不可」 + const NotDownloadedPluginControl = ({ option, plugin_status, downloadStartFunction }) => { - const title = plugin_status.latest_plugin_info.title; const latest_version = plugin_status.latest_plugin_info.plugin_version; - // ※未ダウンロードの場合、current_versionは「未ダウンロード」や「なし」といった扱いとする - const current_version = "未ダウンロード"; if (plugin_status.is_latest_version_available) { return (
-

{title}

-

現在のバージョン: {current_version}

最新バージョン: {latest_version}

<_DownloadButton option={option} downloadStartFunction={downloadStartFunction} />
@@ -126,8 +93,6 @@ const NotDownloadedPluginControl = ({ option, plugin_status, downloadStartFuncti } else { return (
-

{title}

-

現在のバージョン: {current_version}

最新バージョン: {latest_version}

現在利用不可

diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index 2a3fd42d..9baed8fb 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -79,20 +79,19 @@ const PluginDownloadContainer = () => {

{plugin.is_downloaded - ? plugin.downloaded_plugin_info?.title || plugin.latest_plugin_info.title - : plugin.latest_plugin_info.title} + ? plugin.downloaded_plugin_info?.title + : plugin.latest_plugin_info?.title}

{plugin.plugin_id}

- {plugin.error ? ( -

Error: {plugin.error}

+ {plugin.is_error ? ( +

Error: {plugin.error_message}

) : (
- {/* 状態に応じた情報表示(例:バージョン等) */}

{plugin.is_downloaded ? `現在のバージョン: ${plugin.downloaded_plugin_info?.plugin_version}` - : `最新バージョン: ${plugin.latest_plugin_info.plugin_version}`} + : null}

{ const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); const new_data = []; let new_value = {}; + const { is_plugin_supported, is_plugin_supported_latest_vrct } = checkVrctVerCompatibility(downloaded_plugin_info.min_supported_vrct_version, downloaded_plugin_info.max_supported_vrct_version); if (!prev_map.has(downloaded_plugin_info.plugin_id)) { // 未ダウンロード 新規登録 new_value = { @@ -67,9 +68,8 @@ export const usePlugins = () => { } for (const old_plugin_data of prev.data) { - const { is_plugin_supported, is_plugin_supported_latest_vrct } = checkVrctVerCompatibility(downloaded_plugin_info.min_supported_vrct_version, downloaded_plugin_info.max_supported_vrct_version); - if (prev_map.has(downloaded_plugin_info.plugin_id) && old_plugin_data.plugin_id === downloaded_plugin_info.plugin_id) { // ダウンロード済み + if (prev_map.has(downloaded_plugin_info.plugin_id) && old_plugin_data.plugin_id === downloaded_plugin_info.plugin_id) { // ダウンロード済み アップデート const target_prev_plugin = prev_map.get(downloaded_plugin_info.plugin_id); @@ -262,7 +262,7 @@ export const usePlugins = () => { title: plugin_data.title, plugin_id: plugin_data.plugin_id || plugin_data.title, is_error: true, - error: error.message, + error_message: error.message, url: plugin_data.url }; } From 1bea61c45a48aec161e1d4e26ebe6320e02365af Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sat, 12 Apr 2025 14:28:01 +0900 Subject: [PATCH 25/68] [Update] Add dev plugin mode and dev plugin list. --- src-tauri/tauri.conf.json | 7 ++++++- .../PluginsControlComponent.jsx | 2 +- src-ui/logics/configs/plugins/usePlugins.js | 13 +++++++++---- src-ui/ui_configs.js | 9 +++++++++ 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index b36e8ce4..a23b5b08 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -41,7 +41,12 @@ }, "http": { "request": true, - "scope": ["https://api.github.com/repos/**", "https://github.com/**", "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json"] + "scope": [ + "https://api.github.com/repos/**", + "https://github.com/**", + "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json", + "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/dev_vrct_plugins_list.json" + ] }, "shell": { "all": false, diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx index 0bdbfd02..4408d6cc 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx @@ -81,7 +81,7 @@ const DownloadedPluginControl = ({ const NotDownloadedPluginControl = ({ option, plugin_status, downloadStartFunction }) => { - const latest_version = plugin_status.latest_plugin_info.plugin_version; + const latest_version = plugin_status.latest_plugin_info?.plugin_version; if (plugin_status.is_latest_version_available) { return ( diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 682a6d40..ef1acd04 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -1,4 +1,5 @@ import { invoke } from "@tauri-apps/api/tauri"; +import { IS_PLUGIN_DEV_MODE, getPluginsList } from "@ui_configs"; import { createAtomWithHook, useStore_SavedPluginsStatus, @@ -14,7 +15,7 @@ const imported_dev_plugins = []; dev_plugins.forEach(async ({entry_path}) => { imported_dev_plugins.push({ index: await import(`@plugins_path/${entry_path}/index.jsx`), - plugin_info: await import(`@plugins_path/${entry_path}/plugin_info.json`), + downloaded_plugin_info: await import(`@plugins_path/${entry_path}/plugin_info.json`), }); }) @@ -28,7 +29,7 @@ import * as logics_common from "@logics_common"; // PLUGIN_LIST_URL は中央リポジトリにある、各プラグインの plugin_info.json への URL の配列を保持する JSON の URL -const PLUGIN_LIST_URL = "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json"; +const PLUGIN_LIST_URL = getPluginsList(); export const usePlugins = () => { const { asyncStdoutToPython } = useStdoutToPython(); @@ -135,7 +136,7 @@ export const usePlugins = () => { }; const asyncLoadAllPlugins = async () => { - if (!import.meta.env.DEV) { + if (IS_PLUGIN_DEV_MODE) { imported_dev_plugins.forEach(({ index, downloaded_plugin_info }) => { if (!index || !downloaded_plugin_info) { console.error("Invalid development plugin detected", index, downloaded_plugin_info); @@ -150,7 +151,9 @@ export const usePlugins = () => { }); } else { try { - const plugin_files = await readDir("plugins", { dir: BaseDirectory.Resource, recursive: true }); + const plugin_entries = await readDir("plugins", { dir: BaseDirectory.Resource, recursive: true }); + const plugin_files = plugin_entries.filter(entry => entry.children && Array.isArray(entry.children)); + for (const target_dir of plugin_files) { const target_path = target_dir.name; await asyncLoadPlugin(target_path); @@ -368,9 +371,11 @@ const removeImportStatements = (code) => { // import { readTextFile, BaseDirectory } from "@tauri-apps/api/fs"; const loadPluginCSS = async (plugin_css_path) => { + if (!await exists(plugin_css_path, { dir: BaseDirectory.Resource, recursive: true })) return; try { // プラグインフォルダのルートにある main.css を読み込む const css_content = await readTextFile(plugin_css_path, { dir: BaseDirectory.Resource }); + // style タグを作成して head に挿入する const style_tag = document.createElement("style"); style_tag.id = `plugin-css-${plugin_css_path.replace(/[^a-zA-Z0-9_-]/g, "")}`; diff --git a/src-ui/ui_configs.js b/src-ui/ui_configs.js index 4bee9957..f5f89b0c 100644 --- a/src-ui/ui_configs.js +++ b/src-ui/ui_configs.js @@ -52,6 +52,15 @@ export const ui_configs = { ] }; +export const IS_PLUGIN_DEV_MODE = false; +export const getPluginsList = () => { + const base_url = "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/"; + const plugins_list_url = (IS_PLUGIN_DEV_MODE) + ? base_url + "dev_vrct_plugins_list.json" + : base_url + "vrct_plugins_list.json"; + return plugins_list_url; +}; + export const translator_status = [ { id: "DeepL", label: "DeepL", is_available: false }, { id: "DeepL_API", label: `DeepL API`, is_available: false }, From ddc6408828e33c35b442d9a8647411f78ddf4ef4 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sun, 13 Apr 2025 17:14:54 +0900 Subject: [PATCH 26/68] [Update/bugfix] Fix handling flag 'is_latest_version_already' that was overrode unexpectedly. Add some patterns to plugin list. it shows update-able if the user update vrct. Add auto update function if the plugin is enabled. Add safety that turn to disable the plugin if it's not supported with current vrct version. --- .../_app_controllers/PluginsController.jsx | 32 ++++++++++++++++--- .../PluginsControlComponent.jsx | 18 +++++++++-- src-ui/logics/configs/plugins/usePlugins.js | 10 +++--- src-ui/ui_configs.js | 1 + 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx index 2fad4376..98327baf 100644 --- a/src-ui/app/_app_controllers/PluginsController.jsx +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -15,6 +15,7 @@ export const PluginsController = ({ fetchPluginsHasRunRef }) => { updatePluginsData, currentSavedPluginsStatus, updateIsPluginsInitialized, + downloadAndExtractPlugin, } = usePlugins(); useEffect(() => { @@ -36,19 +37,19 @@ export const PluginsController = ({ fetchPluginsHasRunRef }) => { const is_latest_version_available = !(target_downloaded_plugin.plugin_version === info.plugin_version); new_plugin_info = { - is_downloaded: true, - is_latest_version_already: (target_downloaded_plugin.downloaded_plugin_info?.plugin_version === info.plugin_version), - is_latest_version_available: is_latest_version_available, - latest_plugin_info: { ...info }, ...target_downloaded_plugin, + is_downloaded: true, + latest_plugin_info: { ...info }, + is_latest_version_available: is_latest_version_available, + is_latest_version_already: (target_downloaded_plugin.downloaded_plugin_info?.plugin_version === info.plugin_version), }; } else { // infoにもあり登録済みだがダウンロードされていない new_plugin_info = { + ...target_downloaded_plugin, is_downloaded: false, is_latest_version_already: false, is_latest_version_available: info.is_latest_version_available, latest_plugin_info: { ...info }, - ...target_downloaded_plugin, } } } else { // 未ダウンロード @@ -75,8 +76,29 @@ export const PluginsController = ({ fetchPluginsHasRunRef }) => { plugin.is_latest_version_available = (plugin.latest_plugin_info.is_plugin_supported); } }); + + // ダウンロード済みで最新版じゃない場合、自動的にアップデート + // is_latest_version_supported: true のみ。 + // 失敗した場合、現在のバージョンが非対応の場合はdisabledにする。 + new_data.forEach(async plugin => { + if (plugin.is_enabled) { + if (!plugin.is_latest_version_already && plugin.is_latest_version_available) { + await downloadAndExtractPlugin(plugin); + } + } + }); + + new_data.forEach(async plugin => { + if (plugin.is_enabled) { + if (!plugin.downloaded_plugin_info?.is_plugin_supported) { + plugin.is_enabled = false + } + } + }); + return new_data; }); + } catch (error) { console.error(error); } diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx index 4408d6cc..e6ddd500 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx @@ -52,7 +52,14 @@ const DownloadedPluginControl = ({ const latest_version = plugin_status.latest_plugin_info?.plugin_version; - if (plugin_status.is_latest_version_already) { + + if (!plugin_status.downloaded_plugin_info.is_plugin_supported) { + return ( +
+

現在利用不可 使用中VRCTバージョンとの互換性なし

+
+ ); + } else if (plugin_status.is_latest_version_already) { return (

最新のバージョン: {latest_version}

@@ -72,7 +79,7 @@ const DownloadedPluginControl = ({ } else { return (
-

最新のバージョン: {latest_version}

+

最新版は現在利用不可

); @@ -90,6 +97,13 @@ const NotDownloadedPluginControl = ({ option, plugin_status, downloadStartFuncti <_DownloadButton option={option} downloadStartFunction={downloadStartFunction} />
); + } else if (plugin_status.latest_plugin_info?.is_plugin_supported_latest_vrct) { + return ( +
+

最新のバージョン: {latest_version}

+

VRCT最新版で利用可能

+
+ ); } else { return (
diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index ef1acd04..9f1be315 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -52,12 +52,14 @@ export const usePlugins = () => { let new_value = {}; const { is_plugin_supported, is_plugin_supported_latest_vrct } = checkVrctVerCompatibility(downloaded_plugin_info.min_supported_vrct_version, downloaded_plugin_info.max_supported_vrct_version); - if (!prev_map.has(downloaded_plugin_info.plugin_id)) { // 未ダウンロード 新規登録 + const target_plugin = prev_map.get(downloaded_plugin_info.plugin_id); + if (!target_plugin) { // 未ダウンロード 新規登録 new_value = { plugin_id: downloaded_plugin_info.plugin_id, component: component, is_downloaded: true, is_latest_version_available: false, + is_latest_version_already: true, downloaded_plugin_info: { ...downloaded_plugin_info, component: component, @@ -70,9 +72,7 @@ export const usePlugins = () => { for (const old_plugin_data of prev.data) { - if (prev_map.has(downloaded_plugin_info.plugin_id) && old_plugin_data.plugin_id === downloaded_plugin_info.plugin_id) { // ダウンロード済み アップデート - - + if (old_plugin_data.plugin_id === downloaded_plugin_info.plugin_id) { // ダウンロード済み or 登録済 アップデート const target_prev_plugin = prev_map.get(downloaded_plugin_info.plugin_id); const is_latest_version_available = (target_prev_plugin.is_downloaded) && !(downloaded_plugin_info.plugin_version === target_prev_plugin.latest_plugin_info.plugin_version); @@ -82,6 +82,7 @@ export const usePlugins = () => { component: component, is_downloaded: true, is_latest_version_available: is_latest_version_available, + is_latest_version_already: (target_plugin.plugin_version === old_plugin_data.latest_plugin_info?.plugin_info), downloaded_plugin_info: { ...downloaded_plugin_info, component: component, @@ -168,6 +169,7 @@ export const usePlugins = () => { const latest_plugin_info = plugin.latest_plugin_info; try { const plugin_zip_url = await fetchLatestPluginZipUrl(latest_plugin_info); + console.log("start download", plugin_zip_url); // Rust コマンド経由で ZIP をダウンロード const base64_zip = await invoke("download_zip_asset", { url: plugin_zip_url }); // base64_zip をデコードして Uint8Array に変換 diff --git a/src-ui/ui_configs.js b/src-ui/ui_configs.js index f5f89b0c..6db3193f 100644 --- a/src-ui/ui_configs.js +++ b/src-ui/ui_configs.js @@ -53,6 +53,7 @@ export const ui_configs = { }; export const IS_PLUGIN_DEV_MODE = false; +if (IS_PLUGIN_DEV_MODE) console.warn("ui_configs IS_PLUGIN_DEV_MODE: true. Turn to 'false' when it's production environment."); export const getPluginsList = () => { const base_url = "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/"; const plugins_list_url = (IS_PLUGIN_DEV_MODE) From 379ca86b4564bda744a0e517194d04216644228a Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Tue, 15 Apr 2025 16:48:50 +0900 Subject: [PATCH 27/68] [Refactor/bugfix/TMP] TMP: Separate plugins controller. Add ui pattern for outdated plugins and the plugin that is not supported current vrct version but supported in newest vrct version. --- src-ui/app/App.jsx | 5 +- .../_app_controllers/PluginsController.jsx | 151 ++---------------- .../FetchLatestPluginsDataController.jsx | 115 +++++++++++++ .../LoadPluginsController.jsx | 36 +++++ .../MergeSavedPluginsStatusController.jsx | 35 ++++ .../PluginsControlComponent.jsx | 16 ++ .../update_modal/UpdateModal.jsx | 14 +- src-ui/logics/configs/plugins/usePlugins.js | 18 ++- src-ui/logics/useReceiveRoutes.js | 8 +- src-ui/store.js | 3 + 10 files changed, 251 insertions(+), 150 deletions(-) create mode 100644 src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx create mode 100644 src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx create mode 100644 src-ui/app/_app_controllers/plugins_controllers/MergeSavedPluginsStatusController.jsx diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index 8c33ed37..f8314681 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -29,7 +29,10 @@ export const App = () => { const { currentIsBackendReady } = useIsBackendReady(); const { WindowGeometryController } = useWindow(); const { i18n } = useTranslation(); - const fetchPluginsHasRunRef = useRef(false); + const fetchPluginsHasRunRef = useRef({ + is_initialized_load_plugin: false, + is_fetched_plugins_info: false, + }); return (
diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx index 98327baf..9939bfe3 100644 --- a/src-ui/app/_app_controllers/PluginsController.jsx +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -1,142 +1,15 @@ -import React, { useEffect, useRef } from "react"; -import { usePlugins } from "@logics_configs"; -import clsx from "clsx"; +import React, { useEffect } from "react"; +import { LoadPluginsController } from "./plugins_controllers/LoadPluginsController"; +import { FetchLatestPluginsDataController } from "./plugins_controllers/FetchLatestPluginsDataController"; +import { MergeSavedPluginsStatusController } from "./plugins_controllers/MergeSavedPluginsStatusController"; -if (typeof window !== "undefined") { - window.React = React; - window.clsx = clsx; -} +export const PluginsController = () => { -export const PluginsController = ({ fetchPluginsHasRunRef }) => { - const { - asyncLoadAllPlugins, - asyncFetchPluginsInfo, - currentPluginsData, - updatePluginsData, - currentSavedPluginsStatus, - updateIsPluginsInitialized, - downloadAndExtractPlugin, - } = usePlugins(); - - useEffect(() => { - const loadPlugins = async () => { - try { - await asyncLoadAllPlugins(); - const info_array = await asyncFetchPluginsInfo(); - updatePluginsData(prev => { - // Map を利用してそれぞれの配列を plugin_id で参照できるようにする - const info_map = new Map(info_array.map(info => [info.plugin_id, info])); - const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); - - const new_data = []; - for (const info of info_array) { - let new_plugin_info = {}; - if (prev_map.has(info.plugin_id)) { // plugin_id 登録済み - const target_downloaded_plugin = prev_map.get(info.plugin_id); - if (target_downloaded_plugin.is_downloaded) { // 既にダウンロード済み - const is_latest_version_available = !(target_downloaded_plugin.plugin_version === info.plugin_version); - - new_plugin_info = { - ...target_downloaded_plugin, - is_downloaded: true, - latest_plugin_info: { ...info }, - is_latest_version_available: is_latest_version_available, - is_latest_version_already: (target_downloaded_plugin.downloaded_plugin_info?.plugin_version === info.plugin_version), - }; - } else { // infoにもあり登録済みだがダウンロードされていない - new_plugin_info = { - ...target_downloaded_plugin, - is_downloaded: false, - is_latest_version_already: false, - is_latest_version_available: info.is_latest_version_available, - latest_plugin_info: { ...info }, - } - } - } else { // 未ダウンロード - new_plugin_info = { - plugin_id: info.plugin_id, - is_downloaded: false, - is_latest_version_already: false, - latest_plugin_info: { ...info }, - }; - } - new_data.push(new_plugin_info); - } - - // prev.data にのみ存在するアイテム = latest plugin infoには存在しない - // を追加し、is_outdated: true を付与 - prev.data.forEach(item => { - if (!info_map.has(item.plugin_id)) { - new_data.push({ ...item, is_outdated: true }); - } - }); - - new_data.forEach(plugin => { - if (!plugin.is_outdated) { - plugin.is_latest_version_available = (plugin.latest_plugin_info.is_plugin_supported); - } - }); - - // ダウンロード済みで最新版じゃない場合、自動的にアップデート - // is_latest_version_supported: true のみ。 - // 失敗した場合、現在のバージョンが非対応の場合はdisabledにする。 - new_data.forEach(async plugin => { - if (plugin.is_enabled) { - if (!plugin.is_latest_version_already && plugin.is_latest_version_available) { - await downloadAndExtractPlugin(plugin); - } - } - }); - - new_data.forEach(async plugin => { - if (plugin.is_enabled) { - if (!plugin.downloaded_plugin_info?.is_plugin_supported) { - plugin.is_enabled = false - } - } - }); - - return new_data; - }); - - } catch (error) { - console.error(error); - } - }; - - if (!fetchPluginsHasRunRef.current) { - loadPlugins(); - updateIsPluginsInitialized(true); - } - return () => fetchPluginsHasRunRef.current = true; - }, []); - - - useEffect(() => { - updatePluginsData(prev => { - // currentSavedPluginsStatus.data の各要素を Map 化して plugin_id で参照 - const saved_map = new Map(currentSavedPluginsStatus.data.map(saved => [saved.plugin_id, saved])); - const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); - // prev.data にある各アイテムについて、保存済みの状態情報があればマージ - const merged = prev.data.map(item => { - - if (saved_map.has(item.plugin_id)) { - return { ...item, is_enabled: saved_map.get(item.plugin_id).is_enabled }; - } - return item; - }); - - // currentSavedPluginsStatus.data にのみ存在する項目があれば追加 - currentSavedPluginsStatus.data.forEach(saved => { - if (!prev_map.has(saved.plugin_id)) { - merged.push({ plugin_id: saved.plugin_id, is_enabled: saved.is_enabled }); - } - }); - return merged; - }); - }, [currentSavedPluginsStatus]); - - - - return null; + return ( + <> + + + + + ); }; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx b/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx new file mode 100644 index 00000000..e0fde914 --- /dev/null +++ b/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx @@ -0,0 +1,115 @@ +import { useEffect } from "react"; +import { usePlugins } from "@logics_configs"; + +export const FetchLatestPluginsDataController = () => { + const { + asyncFetchPluginsInfo, + updatePluginsData, + downloadAndExtractPlugin, + currentIsInitializedLoadPlugin, + currentIsFetchedPluginsInfo, + updateIsFetchedPluginsInfo, + } = usePlugins(); + + const asyncUpdateLatestPluginsData = async () => { + try { + const info_array = await asyncFetchPluginsInfo(); + updatePluginsData(prev => { + // Map を利用してそれぞれの配列を plugin_id で参照できるようにする + const info_map = new Map(info_array.map(info => [info.plugin_id, info])); + const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); + + console.log(prev_map); + + const new_data = []; + for (const info of info_array) { + let new_plugin_info = {}; + if (prev_map.has(info.plugin_id)) { // plugin_id 登録済み + const target_downloaded_plugin = prev_map.get(info.plugin_id); + + if (target_downloaded_plugin.is_downloaded) { // 既にダウンロード済み + const is_latest_version_available = !(target_downloaded_plugin.plugin_version === info.plugin_version); + + new_plugin_info = { + ...target_downloaded_plugin, + is_downloaded: true, + latest_plugin_info: { ...info }, + is_latest_version_available: is_latest_version_available, + is_latest_version_already: (target_downloaded_plugin.downloaded_plugin_info?.plugin_version === info.plugin_version), + }; + } else { // infoにもあり登録済みだがダウンロードされていない + new_plugin_info = { + ...target_downloaded_plugin, + is_downloaded: false, + is_latest_version_already: false, + is_latest_version_available: info.is_latest_version_available, + latest_plugin_info: { ...info }, + } + } + } else { // 未ダウンロード + new_plugin_info = { + plugin_id: info.plugin_id, + is_downloaded: false, + is_latest_version_already: false, + latest_plugin_info: { ...info }, + }; + } + + new_data.push(new_plugin_info); + } + + // prev.data にのみ存在するアイテム = latest plugin infoには存在しない + // を追加し、is_outdated: true を付与 + prev.data.forEach(item => { + if (!info_map.has(item.plugin_id)) { + new_data.push({ ...item, is_outdated: true }); + } + }); + + new_data.forEach(plugin => { + if (!plugin.is_outdated) { + plugin.is_latest_version_available = (plugin.latest_plugin_info.is_plugin_supported); + } + }); + + // ダウンロード済みで最新版じゃない場合、自動的にアップデート + // is_latest_version_supported: true のみ。 + // 失敗した場合、現在のバージョンが非対応の場合はdisabledにする。 + new_data.forEach(async plugin => { + if (plugin.is_enabled) { + console.log(plugin); + + if (!plugin.is_latest_version_already && plugin.is_latest_version_available) { + await downloadAndExtractPlugin(plugin); + } + } + }); + + new_data.forEach(async plugin => { + if (plugin.is_downloaded && plugin.is_enabled) { + if (!plugin.downloaded_plugin_info?.is_plugin_supported && !plugin.latest_plugin_info?.is_plugin_supported) { + plugin.is_enabled = false + } + } + }); + + return new_data; + }); + + } catch (error) { + console.error(error); + } + }; + + useEffect(() => { + console.log(currentIsInitializedLoadPlugin.data); + if (currentIsInitializedLoadPlugin.data && !currentIsFetchedPluginsInfo.data) { + asyncUpdateLatestPluginsData().then(() => { + updateIsFetchedPluginsInfo(true); + }); + } + + }, [currentIsInitializedLoadPlugin.data]); + + return null; +}; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx new file mode 100644 index 00000000..23422cc5 --- /dev/null +++ b/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx @@ -0,0 +1,36 @@ +import React, { useEffect } from "react"; +import { usePlugins } from "@logics_configs"; +import clsx from "clsx"; + +if (typeof window !== "undefined") { + window.React = React; + window.clsx = clsx; +} + +export const LoadPluginsController = () => { + + const { + asyncLoadAllPlugins, + currentIsInitializedLoadPlugin, + updateIsInitializedLoadPlugin, + } = usePlugins(); + + const asyncInitLoadPlugins = async () => { + try { + await asyncLoadAllPlugins(); + } catch (error) { + console.error(error); + } + }; + + useEffect(() => { + + if (!currentIsInitializedLoadPlugin.data) { + asyncInitLoadPlugins().then(() => { + updateIsInitializedLoadPlugin(true); + }); + } + }, []); + + return null; +}; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/plugins_controllers/MergeSavedPluginsStatusController.jsx b/src-ui/app/_app_controllers/plugins_controllers/MergeSavedPluginsStatusController.jsx new file mode 100644 index 00000000..9b3dcb23 --- /dev/null +++ b/src-ui/app/_app_controllers/plugins_controllers/MergeSavedPluginsStatusController.jsx @@ -0,0 +1,35 @@ +import { useEffect } from "react"; +import { usePlugins } from "@logics_configs"; + +export const MergeSavedPluginsStatusController = () => { + const { + updatePluginsData, + currentSavedPluginsStatus, + } = usePlugins(); + + useEffect(() => { + updatePluginsData(prev => { + // currentSavedPluginsStatus.data の各要素を Map 化して plugin_id で参照 + const saved_map = new Map(currentSavedPluginsStatus.data.map(saved => [saved.plugin_id, saved])); + const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); + // prev.data にある各アイテムについて、保存済みの状態情報があればマージ + const merged = prev.data.map(item => { + + if (saved_map.has(item.plugin_id)) { + return { ...item, is_enabled: saved_map.get(item.plugin_id).is_enabled }; + } + return item; + }); + + // currentSavedPluginsStatus.data にのみ存在する項目があれば追加 + currentSavedPluginsStatus.data.forEach(saved => { + if (!prev_map.has(saved.plugin_id)) { + merged.push({ plugin_id: saved.plugin_id, is_enabled: saved.is_enabled }); + } + }); + return merged; + }); + }, [currentSavedPluginsStatus]); + + return null; +}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx index e6ddd500..9c33c90d 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx @@ -54,11 +54,27 @@ const DownloadedPluginControl = ({ if (!plugin_status.downloaded_plugin_info.is_plugin_supported) { + if (plugin_status.is_latest_version_available) { + return ( +
+

最新のバージョン: {latest_version}

+

最新版にアップデート後 利用可能

+ <_DownloadButton option={option} downloadStartFunction={downloadStartFunction} /> +
+ ); + } return (

現在利用不可 使用中VRCTバージョンとの互換性なし

); + } else if (plugin_status.is_outdated) { + return ( +
+

最新情報が取得できません

+ +
+ ); } else if (plugin_status.is_latest_version_already) { return (
diff --git a/src-ui/app/modal_controller/update_modal/UpdateModal.jsx b/src-ui/app/modal_controller/update_modal/UpdateModal.jsx index 4fef1c71..caf89d43 100644 --- a/src-ui/app/modal_controller/update_modal/UpdateModal.jsx +++ b/src-ui/app/modal_controller/update_modal/UpdateModal.jsx @@ -20,9 +20,6 @@ export const UpdateModal = () => { const { currentLatestSoftwareVersionInfo } = useSoftwareVersion(); const { isAnyPluginEnabled } = usePlugins(); - console.log(isAnyPluginEnabled()); - - const is_latest_version_already = currentLatestSoftwareVersionInfo.data.is_update_available === false; const is_cpu_version = currentComputeMode.data === "cpu"; @@ -101,14 +98,17 @@ const CurrentVersionLabel = (props) => { const PluginUpdateNotification = () => { const { enabledPluginsList } = usePlugins(); - const incompatible_plugins_list = enabledPluginsList(); + // ダウンロード済みのもの or プラグイン最新版が、VRCT最新版(VRCTアプデ後)に非対応のもの + const incompatible_plugins_list = enabledPluginsList().filter(plugin => { + if (!plugin.is_downloaded) return false; + if (!plugin.downloaded_plugin_info?.is_plugin_supported_latest_vrct || !plugin.latest_plugin_info.is_plugin_supported_latest_vrct) return true; + }); return (
{incompatible_plugins_list.map(plugin => { - console.log(plugin); - - return

{plugin.title}

+ const target_data = plugin.downloaded_plugin_info; + return

{target_data.title}

})}
); diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 9f1be315..aa8c5ad7 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -5,6 +5,8 @@ import { useStore_SavedPluginsStatus, useStore_PluginsData, useStore_IsPluginsInitialized, + useStore_IsInitializedLoadPlugin, + useStore_IsFetchedPluginsInfo, } from "@store"; import { useStdoutToPython } from "@logics/useStdoutToPython"; @@ -33,6 +35,11 @@ const PLUGIN_LIST_URL = getPluginsList(); export const usePlugins = () => { const { asyncStdoutToPython } = useStdoutToPython(); + + const { currentIsInitializedLoadPlugin, updateIsInitializedLoadPlugin, pendingIsInitializedLoadPlugin } = useStore_IsInitializedLoadPlugin(); + const { currentIsFetchedPluginsInfo, updateIsFetchedPluginsInfo, pendingIsFetchedPluginsInfo } = useStore_IsFetchedPluginsInfo(); + + const { currentSavedPluginsStatus, updateSavedPluginsStatus, pendingSavedPluginsStatus } = useStore_SavedPluginsStatus(); const { currentPluginsData, updatePluginsData, pendingPluginsData } = useStore_PluginsData(); const { currentIsPluginsInitialized, updateIsPluginsInitialized, pendingIsPluginsInitialized } = useStore_IsPluginsInitialized(); @@ -40,13 +47,15 @@ export const usePlugins = () => { const { asyncTauriFetchGithub } = useFetch(); - const generatePluginContext= (downloaded_plugin_info) => { + const generatePluginContext = (downloaded_plugin_info) => { const plugin_context = { registerComponent: (component) => { if (!downloaded_plugin_info.plugin_id || !downloaded_plugin_info.location || !component) { return console.error("An invalid plugin was detected.", downloaded_plugin_info.plugin_id, downloaded_plugin_info.location, component); } updatePluginsData(prev => { + console.log("-----updated downloaded plugin info----"); + const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); const new_data = []; let new_value = {}; @@ -74,7 +83,7 @@ export const usePlugins = () => { if (old_plugin_data.plugin_id === downloaded_plugin_info.plugin_id) { // ダウンロード済み or 登録済 アップデート const target_prev_plugin = prev_map.get(downloaded_plugin_info.plugin_id); - const is_latest_version_available = (target_prev_plugin.is_downloaded) && !(downloaded_plugin_info.plugin_version === target_prev_plugin.latest_plugin_info.plugin_version); + const is_latest_version_available = (target_prev_plugin.is_downloaded) && (target_prev_plugin.latest_plugin_info?.plugin_version) && !(downloaded_plugin_info.plugin_version === target_prev_plugin.latest_plugin_info?.plugin_version); new_value = { ...target_prev_plugin, @@ -357,6 +366,11 @@ export const usePlugins = () => { currentIsPluginsInitialized, updateIsPluginsInitialized, + currentIsInitializedLoadPlugin, + updateIsInitializedLoadPlugin, + currentIsFetchedPluginsInfo, + updateIsFetchedPluginsInfo, + setSavedPluginsStatus, handlePendingPlugin, diff --git a/src-ui/logics/useReceiveRoutes.js b/src-ui/logics/useReceiveRoutes.js index b73fe62e..fb826f8c 100644 --- a/src-ui/logics/useReceiveRoutes.js +++ b/src-ui/logics/useReceiveRoutes.js @@ -206,7 +206,13 @@ export const useReceiveRoutes = () => { "/set/data/main_window_geometry": () => {}, "/run/open_filepath_logs": () => console.log("Opened Directory, Message Logs"), "/run/open_filepath_config_file": () => console.log("Opened Directory, Config File"), - "/run/software_update_info": updateLatestSoftwareVersionInfo, + "/run/software_update_info": () => { + updateLatestSoftwareVersionInfo({ + is_update_available: true, + new_version: "3.0.3", + }) + }, + // "/run/software_update_info": updateLatestSoftwareVersionInfo, "/run/connected_network": handleNetworkConnection, // Main Page diff --git a/src-ui/store.js b/src-ui/store.js index 5adbb4c7..58012777 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -279,6 +279,9 @@ export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createA // Plugins export const { atomInstance: Atom_IsPluginsInitialized, useHook: useStore_IsPluginsInitialized } = createAtomWithHook(false, "IsPluginsInitialized"); +export const { atomInstance: Atom_IsInitializedLoadPlugin, useHook: useStore_IsInitializedLoadPlugin } = createAtomWithHook(false, "IsInitializedLoadPlugin"); +export const { atomInstance: Atom_IsFetchedPluginsInfo, useHook: useStore_IsFetchedPluginsInfo } = createAtomWithHook(false, "IsFetchedPluginsInfo"); +export const { atomInstance: Atom_FetchedPluginsInfo, useHook: useStore_FetchedPluginsInfo } = createAtomWithHook(false, "FetchedPluginsInfo"); export const { atomInstance: Atom_SavedPluginsStatus, useHook: useStore_SavedPluginsStatus } = createAtomWithHook([], "SavedPluginsStatus"); export const { atomInstance: Atom_PluginsData, useHook: useStore_PluginsData } = createAtomWithHook([], "PluginsData"); From 4c12e8b94644b0ae0f5904cb2bb833e7cde7aacf Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 16 Apr 2025 19:02:41 +0900 Subject: [PATCH 28/68] [Update/Refactor] For data integration correctly, the plugins data (saved, downloaded, fetched) merge whenever update data each of it. Separate plugins controllers. --- src-ui/app/App.jsx | 10 +- .../_app_controllers/PluginsController.jsx | 21 +- .../FetchLatestPluginsDataController.jsx | 104 +-------- .../LoadPluginsController.jsx | 14 +- .../MergePluginsController.jsx | 207 ++++++++++++++++++ .../setting_box/plugins/Plugins.jsx | 2 +- src-ui/logics/configs/plugins/usePlugins.js | 81 +++---- src-ui/store.js | 4 +- 8 files changed, 268 insertions(+), 175 deletions(-) create mode 100644 src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index f8314681..9c7e8aa0 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -29,9 +29,9 @@ export const App = () => { const { currentIsBackendReady } = useIsBackendReady(); const { WindowGeometryController } = useWindow(); const { i18n } = useTranslation(); - const fetchPluginsHasRunRef = useRef({ + const pluginsControllerHasRunRef = useRef({ is_initialized_load_plugin: false, - is_fetched_plugins_info: false, + is_init_fetched_plugins_info: false, }); return ( @@ -49,7 +49,7 @@ export const App = () => { {(currentIsBackendReady.data === false || currentIsVrctAvailable.data === false) ? - : + : } @@ -58,11 +58,11 @@ export const App = () => { ); }; -const Contents = ({ fetchPluginsHasRunRef }) => { +const Contents = ({ pluginsControllerHasRunRef }) => { const { currentIsSoftwareUpdating } = useIsSoftwareUpdating(); return ( <> - + {currentIsSoftwareUpdating.data === false diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx index 9939bfe3..9840afd5 100644 --- a/src-ui/app/_app_controllers/PluginsController.jsx +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -1,15 +1,24 @@ -import React, { useEffect } from "react"; +import React from "react"; +import clsx from "clsx"; + +if (typeof window !== "undefined") { + window.React = React; + window.clsx = clsx; +} + import { LoadPluginsController } from "./plugins_controllers/LoadPluginsController"; import { FetchLatestPluginsDataController } from "./plugins_controllers/FetchLatestPluginsDataController"; -import { MergeSavedPluginsStatusController } from "./plugins_controllers/MergeSavedPluginsStatusController"; +// import { MergeSavedPluginsStatusController } from "./plugins_controllers/MergeSavedPluginsStatusController"; +import { MergePluginsController } from "./plugins_controllers/MergePluginsController"; -export const PluginsController = () => { +export const PluginsController = ({ pluginsControllerHasRunRef }) => { return ( <> - - - + + + + {/* */} ); }; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx b/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx index e0fde914..088d9cae 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx @@ -1,115 +1,25 @@ import { useEffect } from "react"; import { usePlugins } from "@logics_configs"; -export const FetchLatestPluginsDataController = () => { +export const FetchLatestPluginsDataController = ({ pluginsControllerHasRunRef }) => { const { asyncFetchPluginsInfo, - updatePluginsData, - downloadAndExtractPlugin, - currentIsInitializedLoadPlugin, - currentIsFetchedPluginsInfo, - updateIsFetchedPluginsInfo, } = usePlugins(); - const asyncUpdateLatestPluginsData = async () => { + const asyncInitFetchPluginsInfo = async () => { try { - const info_array = await asyncFetchPluginsInfo(); - updatePluginsData(prev => { - // Map を利用してそれぞれの配列を plugin_id で参照できるようにする - const info_map = new Map(info_array.map(info => [info.plugin_id, info])); - const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); - - console.log(prev_map); - - const new_data = []; - for (const info of info_array) { - let new_plugin_info = {}; - if (prev_map.has(info.plugin_id)) { // plugin_id 登録済み - const target_downloaded_plugin = prev_map.get(info.plugin_id); - - if (target_downloaded_plugin.is_downloaded) { // 既にダウンロード済み - const is_latest_version_available = !(target_downloaded_plugin.plugin_version === info.plugin_version); - - new_plugin_info = { - ...target_downloaded_plugin, - is_downloaded: true, - latest_plugin_info: { ...info }, - is_latest_version_available: is_latest_version_available, - is_latest_version_already: (target_downloaded_plugin.downloaded_plugin_info?.plugin_version === info.plugin_version), - }; - } else { // infoにもあり登録済みだがダウンロードされていない - new_plugin_info = { - ...target_downloaded_plugin, - is_downloaded: false, - is_latest_version_already: false, - is_latest_version_available: info.is_latest_version_available, - latest_plugin_info: { ...info }, - } - } - } else { // 未ダウンロード - new_plugin_info = { - plugin_id: info.plugin_id, - is_downloaded: false, - is_latest_version_already: false, - latest_plugin_info: { ...info }, - }; - } - - new_data.push(new_plugin_info); - } - - // prev.data にのみ存在するアイテム = latest plugin infoには存在しない - // を追加し、is_outdated: true を付与 - prev.data.forEach(item => { - if (!info_map.has(item.plugin_id)) { - new_data.push({ ...item, is_outdated: true }); - } - }); - - new_data.forEach(plugin => { - if (!plugin.is_outdated) { - plugin.is_latest_version_available = (plugin.latest_plugin_info.is_plugin_supported); - } - }); - - // ダウンロード済みで最新版じゃない場合、自動的にアップデート - // is_latest_version_supported: true のみ。 - // 失敗した場合、現在のバージョンが非対応の場合はdisabledにする。 - new_data.forEach(async plugin => { - if (plugin.is_enabled) { - console.log(plugin); - - if (!plugin.is_latest_version_already && plugin.is_latest_version_available) { - await downloadAndExtractPlugin(plugin); - } - } - }); - - new_data.forEach(async plugin => { - if (plugin.is_downloaded && plugin.is_enabled) { - if (!plugin.downloaded_plugin_info?.is_plugin_supported && !plugin.latest_plugin_info?.is_plugin_supported) { - plugin.is_enabled = false - } - } - }); - - return new_data; - }); - + await asyncFetchPluginsInfo(); } catch (error) { console.error(error); } }; useEffect(() => { - console.log(currentIsInitializedLoadPlugin.data); - if (currentIsInitializedLoadPlugin.data && !currentIsFetchedPluginsInfo.data) { - asyncUpdateLatestPluginsData().then(() => { - updateIsFetchedPluginsInfo(true); - }); + if (!pluginsControllerHasRunRef.current.is_init_fetched_plugins_info) { + asyncInitFetchPluginsInfo(); + pluginsControllerHasRunRef.current.is_init_fetched_plugins_info = true; } - - }, [currentIsInitializedLoadPlugin.data]); + }, []); return null; }; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx index 23422cc5..0945df96 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx @@ -1,17 +1,9 @@ import React, { useEffect } from "react"; import { usePlugins } from "@logics_configs"; -import clsx from "clsx"; - -if (typeof window !== "undefined") { - window.React = React; - window.clsx = clsx; -} - -export const LoadPluginsController = () => { +export const LoadPluginsController = ({ pluginsControllerHasRunRef }) => { const { asyncLoadAllPlugins, - currentIsInitializedLoadPlugin, updateIsInitializedLoadPlugin, } = usePlugins(); @@ -24,11 +16,11 @@ export const LoadPluginsController = () => { }; useEffect(() => { - - if (!currentIsInitializedLoadPlugin.data) { + if (!pluginsControllerHasRunRef.current.is_initialized_load_plugin) { asyncInitLoadPlugins().then(() => { updateIsInitializedLoadPlugin(true); }); + pluginsControllerHasRunRef.current.is_initialized_load_plugin = true; } }, []); diff --git a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx new file mode 100644 index 00000000..2fdb51c9 --- /dev/null +++ b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx @@ -0,0 +1,207 @@ +import { useEffect, useRef } from "react"; +import { usePlugins } from "@logics_configs"; +import { useSoftwareVersion } from "@logics_common"; + +export const MergePluginsController = () => { + const { + currentLoadedPlugins, + updatePluginsData, + currentPluginsData, + currentFetchedPluginsInfo, + currentSavedPluginsStatus, + downloadAndExtractPlugin, + } = usePlugins(); + const { checkVrctVerCompatibility } = useSoftwareVersion(); + + // downloaded, fetched, saved の各情報をまとめてマージ + useEffect(() => { + const mergePluginData = () => { + updatePluginsData(prev => { + // downloaded, fetched, 保存済み状態のMapをそれぞれ作成(plugin_id をキー) + const downloaded_map = new Map( + currentLoadedPlugins.data.map(info => [info.plugin_id, info]) + ); + const fetched_map = new Map( + currentFetchedPluginsInfo.data.map(info => [info.plugin_id, info]) + ); + const saved_map = new Map( + currentSavedPluginsStatus.data.map(saved => [saved.plugin_id, saved]) + ); + const prev_map = new Map( + prev.data.map(item => [item.plugin_id, item]) + ); + + // union_keys: Saved以外の情報に対して重複なくキーを取得する + const union_keys = new Set([ + ...downloaded_map.keys(), + ...fetched_map.keys(), + ...prev_map.keys(), + ]); + + const new_data = []; + for (const id of union_keys) { + const downloaded = downloaded_map.get(id); + const fetched = fetched_map.get(id); + const prev_plugin = prev_map.get(id); + let plugin = {}; + + if (downloaded) { + // ダウンロード済み情報に対してサポート確認 + const { is_plugin_supported, is_plugin_supported_latest_vrct } = + checkVrctVerCompatibility( + downloaded.min_supported_vrct_version, + downloaded.max_supported_vrct_version + ); + plugin = { + // prevの情報があれば引き継ぎつつ上書き + ...(prev_plugin || {}), + plugin_id: downloaded.plugin_id, + component: downloaded.component, + is_downloaded: true, + downloaded_plugin_info: { + ...downloaded, + is_plugin_supported, + is_plugin_supported_latest_vrct, + }, + }; + + if (fetched) { + const is_latest_version_available = + (downloaded.plugin_version !== fetched.plugin_version && fetched.is_plugin_supported); + plugin = { + ...plugin, + is_outdated: false, + latest_plugin_info: { ...fetched }, + is_latest_version_available: + plugin.is_downloaded && is_latest_version_available, + is_latest_version_already: + downloaded.plugin_version === fetched.plugin_version, + }; + } else { + // フェッチ情報がない場合の初期状態 + plugin = { + ...plugin, + is_latest_version_available: false, + is_latest_version_already: true, + }; + } + } else if (fetched) { + // フェッチ情報のみの場合は、ダウンロードしていない初期状態 + plugin = { + ...(prev_plugin || {}), + plugin_id: fetched.plugin_id, + is_downloaded: false, + is_latest_version_available: fetched.is_plugin_supported, + is_latest_version_already: false, + is_outdated: false, + latest_plugin_info: { ...fetched }, + }; + } else if (prev_plugin) { + // 既存情報のみ存在する場合は outdated フラグを付与 + plugin = { ...prev_plugin, is_outdated: true }; + } + // いずれかの情報がある場合のみ new_data に追加 + if (plugin.plugin_id) { + new_data.push(plugin); + } + } + + // 保存済み状態(currentSavedPluginsStatus)のマージ + // ・new_dataに存在する各プラグインに対して、保存済みの is_enabled を上書き + new_data.forEach(plugin => { + if (saved_map.has(plugin.plugin_id)) { + plugin.is_enabled = saved_map.get(plugin.plugin_id).is_enabled; + } + }); + // ・prev.data には存在せず、保存済み情報にのみある場合は追加 + for (const [id, saved] of saved_map.entries()) { + if (!new_data.some(item => item.plugin_id === id)) { + new_data.push({ plugin_id: saved.plugin_id, is_enabled: saved.is_enabled }); + } + } + + + // 追加処理: ダウンロード済みかつ有効なプラグインで、サポート対象でない場合は無効化 + new_data.forEach(plugin => { + if (plugin.is_downloaded && plugin.is_enabled) { + if ( + !plugin.downloaded_plugin_info?.is_plugin_supported && + !plugin.latest_plugin_info?.is_plugin_supported + ) { + plugin.is_enabled = false; + } + } + }); + + console.log("merged plugin data", new_data); + return new_data; + }); + }; + + mergePluginData(); + }, [currentFetchedPluginsInfo.data, currentLoadedPlugins.data, currentSavedPluginsStatus]); + + + + // --- 自動アップデート(ダウンロード処理)のuseEffect --- + // ※downloadAndExtractPlugin の重複実行を防ぐため、実行中の plugin_id を useRef で管理 + const downloadingRef = useRef(new Set()); + + useEffect(() => { + if (!currentPluginsData.data.length) return; + // マージ結果の currentPluginsData.data を元にダウンロード処理をチェック + currentPluginsData.data.forEach(plugin => { + if (plugin.is_downloaded && + plugin.is_enabled && + !plugin.is_latest_version_already && + plugin.is_latest_version_available + ) { + console.log(!downloadingRef.current.has(plugin.plugin_id)); + + if (!downloadingRef.current.has(plugin.plugin_id)) { + downloadingRef.current.add(plugin.plugin_id); + // ※ downloadAndExtractPlugin は外部通信を伴い currentLoadedPlugins.data 更新を引き起こすので注意 + downloadAndExtractPlugin(plugin) + .then(() => { + console.log(`Plugin ${plugin.plugin_id} updated successfully`); + downloadingRef.current.delete(plugin.plugin_id); + }) + .catch((error) => { + console.error(`Plugin ${plugin.plugin_id} update failed`, error); + downloadingRef.current.delete(plugin.plugin_id); + }); + } + } + }); + }, [currentPluginsData.data]); + + + + + return null; +}; + + + + + +// ダウンロード済みで最新版じゃない場合、自動的にアップデート +// // is_latest_version_supported: true のみ。 +// // 失敗した場合、現在のバージョンが非対応の場合はdisabledにする。 +// new_data.forEach(async plugin => { +// if (plugin.is_enabled) { +// console.log(plugin); + +// if (!plugin.is_latest_version_already && plugin.is_latest_version_available) { +// await downloadAndExtractPlugin(plugin); +// } +// } +// }); + +// new_data.forEach(plugin => { +// if (plugin.is_downloaded && plugin.is_enabled) { +// if (!plugin.downloaded_plugin_info?.is_plugin_supported && !plugin.latest_plugin_info?.is_plugin_supported) { +// plugin.is_enabled = false +// } +// } +// }); \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index 9baed8fb..9f7f490e 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -8,7 +8,7 @@ export const Plugins = () => { currentIsPluginsInitialized, } = usePlugins(); - if (!currentIsPluginsInitialized.data) return null; + // if (!currentIsPluginsInitialized.data) return null; return (
diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index aa8c5ad7..80e7e717 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -7,6 +7,9 @@ import { useStore_IsPluginsInitialized, useStore_IsInitializedLoadPlugin, useStore_IsFetchedPluginsInfo, + + useStore_FetchedPluginsInfo, + useStore_LoadedPlugins, } from "@store"; import { useStdoutToPython } from "@logics/useStdoutToPython"; @@ -39,6 +42,8 @@ export const usePlugins = () => { const { currentIsInitializedLoadPlugin, updateIsInitializedLoadPlugin, pendingIsInitializedLoadPlugin } = useStore_IsInitializedLoadPlugin(); const { currentIsFetchedPluginsInfo, updateIsFetchedPluginsInfo, pendingIsFetchedPluginsInfo } = useStore_IsFetchedPluginsInfo(); + const { currentFetchedPluginsInfo, updateFetchedPluginsInfo, pendingFetchedPluginsInfo } = useStore_FetchedPluginsInfo(); + const { currentLoadedPlugins, updateLoadedPlugins, pendingLoadedPlugins } = useStore_LoadedPlugins(); const { currentSavedPluginsStatus, updateSavedPluginsStatus, pendingSavedPluginsStatus } = useStore_SavedPluginsStatus(); const { currentPluginsData, updatePluginsData, pendingPluginsData } = useStore_PluginsData(); @@ -47,65 +52,24 @@ export const usePlugins = () => { const { asyncTauriFetchGithub } = useFetch(); + + const generatePluginContext = (downloaded_plugin_info) => { const plugin_context = { registerComponent: (component) => { if (!downloaded_plugin_info.plugin_id || !downloaded_plugin_info.location || !component) { return console.error("An invalid plugin was detected.", downloaded_plugin_info.plugin_id, downloaded_plugin_info.location, component); } - updatePluginsData(prev => { - console.log("-----updated downloaded plugin info----"); + updateLoadedPlugins(prev => { const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); - const new_data = []; - let new_value = {}; - const { is_plugin_supported, is_plugin_supported_latest_vrct } = checkVrctVerCompatibility(downloaded_plugin_info.min_supported_vrct_version, downloaded_plugin_info.max_supported_vrct_version); - - const target_plugin = prev_map.get(downloaded_plugin_info.plugin_id); - if (!target_plugin) { // 未ダウンロード 新規登録 - new_value = { - plugin_id: downloaded_plugin_info.plugin_id, - component: component, - is_downloaded: true, - is_latest_version_available: false, - is_latest_version_already: true, - downloaded_plugin_info: { - ...downloaded_plugin_info, - component: component, - is_plugin_supported: is_plugin_supported, - is_plugin_supported_latest_vrct: is_plugin_supported_latest_vrct, - }, - }; - return [...prev.data, new_value]; - } - - for (const old_plugin_data of prev.data) { - - if (old_plugin_data.plugin_id === downloaded_plugin_info.plugin_id) { // ダウンロード済み or 登録済 アップデート - const target_prev_plugin = prev_map.get(downloaded_plugin_info.plugin_id); - const is_latest_version_available = (target_prev_plugin.is_downloaded) && (target_prev_plugin.latest_plugin_info?.plugin_version) && !(downloaded_plugin_info.plugin_version === target_prev_plugin.latest_plugin_info?.plugin_version); - - new_value = { - ...target_prev_plugin, - plugin_id: downloaded_plugin_info.plugin_id, - component: component, - is_downloaded: true, - is_latest_version_available: is_latest_version_available, - is_latest_version_already: (target_plugin.plugin_version === old_plugin_data.latest_plugin_info?.plugin_info), - downloaded_plugin_info: { - ...downloaded_plugin_info, - component: component, - is_plugin_supported: is_plugin_supported, - is_plugin_supported_latest_vrct: is_plugin_supported_latest_vrct, - }, - }; - } else { - new_value = old_plugin_data; - } - new_data.push(new_value); - } - return new_data; + prev_map.set(downloaded_plugin_info.plugin_id, { + ...downloaded_plugin_info, + component: component, + }); + return Array.from(prev_map.values()); }); + }, createAtomWithHook: (...args) => createAtomWithHook(...args), logics: { ...logics_common, ...logics_configs, ...logics_main } @@ -259,10 +223,11 @@ export const usePlugins = () => { const asyncFetchPluginsInfo = async () => { + if (currentIsFetchedPluginsInfo.data) return; try { const response = await asyncTauriFetchGithub(PLUGIN_LIST_URL); if (response.status !== 200) { - throw new Error("Failed to fetch plugin list, status: " + response.status); + throw new Error("Failed to fetch plugins list, status: " + response.status); } const plugins_data = response.data; const updated_list = await Promise.all( @@ -271,7 +236,7 @@ export const usePlugins = () => { const plugin_info = await asyncFetchPluginInfo(plugin_data.url); return plugin_info; } catch (error) { - console.error("Error fetching plugin info for URL:", plugin_data.url, error); + console.error("Error fetching plugin info for URL: ", plugin_data.url, error); return { title: plugin_data.title, plugin_id: plugin_data.plugin_id || plugin_data.title, @@ -282,13 +247,15 @@ export const usePlugins = () => { } }) ); - return updated_list; + updateFetchedPluginsInfo(updated_list); + updateIsFetchedPluginsInfo(true); } catch (error) { - console.error("Error fetching plugin info list:", error); + console.error("Error fetching plugin info list: ", error); } } const asyncFetchPluginInfo = async (plugin_info_asset_url) => { + const release_response = await asyncTauriFetchGithub(plugin_info_asset_url); if (release_response.status !== 200) { throw new Error(`Failed to fetch release info from ${plugin_info_asset_url}`); @@ -371,6 +338,12 @@ export const usePlugins = () => { currentIsFetchedPluginsInfo, updateIsFetchedPluginsInfo, + currentFetchedPluginsInfo, + updateFetchedPluginsInfo, + + currentLoadedPlugins, + updateLoadedPlugins, + setSavedPluginsStatus, handlePendingPlugin, diff --git a/src-ui/store.js b/src-ui/store.js index 58012777..3d4bac13 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -281,7 +281,9 @@ export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createA export const { atomInstance: Atom_IsPluginsInitialized, useHook: useStore_IsPluginsInitialized } = createAtomWithHook(false, "IsPluginsInitialized"); export const { atomInstance: Atom_IsInitializedLoadPlugin, useHook: useStore_IsInitializedLoadPlugin } = createAtomWithHook(false, "IsInitializedLoadPlugin"); export const { atomInstance: Atom_IsFetchedPluginsInfo, useHook: useStore_IsFetchedPluginsInfo } = createAtomWithHook(false, "IsFetchedPluginsInfo"); -export const { atomInstance: Atom_FetchedPluginsInfo, useHook: useStore_FetchedPluginsInfo } = createAtomWithHook(false, "FetchedPluginsInfo"); + +export const { atomInstance: Atom_FetchedPluginsInfo, useHook: useStore_FetchedPluginsInfo } = createAtomWithHook([], "FetchedPluginsInfo"); +export const { atomInstance: Atom_LoadedPlugins, useHook: useStore_LoadedPlugins } = createAtomWithHook([], "LoadedPlugins"); export const { atomInstance: Atom_SavedPluginsStatus, useHook: useStore_SavedPluginsStatus } = createAtomWithHook([], "SavedPluginsStatus"); export const { atomInstance: Atom_PluginsData, useHook: useStore_PluginsData } = createAtomWithHook([], "PluginsData"); From ee4bbf772f4431f84370540ae049e8cb76d82904 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 17 Apr 2025 00:11:50 +0900 Subject: [PATCH 29/68] [Refactor] Remove the file and code that is no longer in use. --- .../_app_controllers/PluginsController.jsx | 2 -- .../LoadPluginsController.jsx | 2 +- .../MergePluginsController.jsx | 35 ++----------------- .../MergeSavedPluginsStatusController.jsx | 35 ------------------- 4 files changed, 4 insertions(+), 70 deletions(-) delete mode 100644 src-ui/app/_app_controllers/plugins_controllers/MergeSavedPluginsStatusController.jsx diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx index 9840afd5..4f47db05 100644 --- a/src-ui/app/_app_controllers/PluginsController.jsx +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -8,7 +8,6 @@ if (typeof window !== "undefined") { import { LoadPluginsController } from "./plugins_controllers/LoadPluginsController"; import { FetchLatestPluginsDataController } from "./plugins_controllers/FetchLatestPluginsDataController"; -// import { MergeSavedPluginsStatusController } from "./plugins_controllers/MergeSavedPluginsStatusController"; import { MergePluginsController } from "./plugins_controllers/MergePluginsController"; export const PluginsController = ({ pluginsControllerHasRunRef }) => { @@ -18,7 +17,6 @@ export const PluginsController = ({ pluginsControllerHasRunRef }) => { - {/* */} ); }; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx index 0945df96..ee4caeac 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import { useEffect } from "react"; import { usePlugins } from "@logics_configs"; export const LoadPluginsController = ({ pluginsControllerHasRunRef }) => { diff --git a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx index 2fdb51c9..c4545bc3 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx @@ -121,7 +121,7 @@ export const MergePluginsController = () => { } - // 追加処理: ダウンロード済みかつ有効なプラグインで、サポート対象でない場合は無効化 + // ダウンロード済みかつ有効なプラグインで、サポート対象でない場合は無効化 new_data.forEach(plugin => { if (plugin.is_downloaded && plugin.is_enabled) { if ( @@ -143,7 +143,7 @@ export const MergePluginsController = () => { - // --- 自動アップデート(ダウンロード処理)のuseEffect --- + // --- 自動アップデート(ダウンロード処理)--- // ※downloadAndExtractPlugin の重複実行を防ぐため、実行中の plugin_id を useRef で管理 const downloadingRef = useRef(new Set()); @@ -160,7 +160,6 @@ export const MergePluginsController = () => { if (!downloadingRef.current.has(plugin.plugin_id)) { downloadingRef.current.add(plugin.plugin_id); - // ※ downloadAndExtractPlugin は外部通信を伴い currentLoadedPlugins.data 更新を引き起こすので注意 downloadAndExtractPlugin(plugin) .then(() => { console.log(`Plugin ${plugin.plugin_id} updated successfully`); @@ -175,33 +174,5 @@ export const MergePluginsController = () => { }); }, [currentPluginsData.data]); - - - return null; -}; - - - - - -// ダウンロード済みで最新版じゃない場合、自動的にアップデート -// // is_latest_version_supported: true のみ。 -// // 失敗した場合、現在のバージョンが非対応の場合はdisabledにする。 -// new_data.forEach(async plugin => { -// if (plugin.is_enabled) { -// console.log(plugin); - -// if (!plugin.is_latest_version_already && plugin.is_latest_version_available) { -// await downloadAndExtractPlugin(plugin); -// } -// } -// }); - -// new_data.forEach(plugin => { -// if (plugin.is_downloaded && plugin.is_enabled) { -// if (!plugin.downloaded_plugin_info?.is_plugin_supported && !plugin.latest_plugin_info?.is_plugin_supported) { -// plugin.is_enabled = false -// } -// } -// }); \ No newline at end of file +}; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/plugins_controllers/MergeSavedPluginsStatusController.jsx b/src-ui/app/_app_controllers/plugins_controllers/MergeSavedPluginsStatusController.jsx deleted file mode 100644 index 9b3dcb23..00000000 --- a/src-ui/app/_app_controllers/plugins_controllers/MergeSavedPluginsStatusController.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useEffect } from "react"; -import { usePlugins } from "@logics_configs"; - -export const MergeSavedPluginsStatusController = () => { - const { - updatePluginsData, - currentSavedPluginsStatus, - } = usePlugins(); - - useEffect(() => { - updatePluginsData(prev => { - // currentSavedPluginsStatus.data の各要素を Map 化して plugin_id で参照 - const saved_map = new Map(currentSavedPluginsStatus.data.map(saved => [saved.plugin_id, saved])); - const prev_map = new Map(prev.data.map(item => [item.plugin_id, item])); - // prev.data にある各アイテムについて、保存済みの状態情報があればマージ - const merged = prev.data.map(item => { - - if (saved_map.has(item.plugin_id)) { - return { ...item, is_enabled: saved_map.get(item.plugin_id).is_enabled }; - } - return item; - }); - - // currentSavedPluginsStatus.data にのみ存在する項目があれば追加 - currentSavedPluginsStatus.data.forEach(saved => { - if (!prev_map.has(saved.plugin_id)) { - merged.push({ plugin_id: saved.plugin_id, is_enabled: saved.is_enabled }); - } - }); - return merged; - }); - }, [currentSavedPluginsStatus]); - - return null; -}; \ No newline at end of file From 1c56167dbadde88ba45fa1ceab125d0015bd4462 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 17 Apr 2025 17:30:30 +0900 Subject: [PATCH 30/68] [Update] Plugins: Show enabled plugins compatibility when update modal is opened. --- .../update_modal/UpdateModal.jsx | 67 +++++++------------ .../update_modal/UpdateModal.module.scss | 10 ++- .../PluginCompatibilityList.jsx | 61 +++++++++++++++++ .../PluginCompatibilityList.module.scss | 63 +++++++++++++++++ src-ui/assets/x_mark.svg | 1 + 5 files changed, 160 insertions(+), 42 deletions(-) create mode 100644 src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx create mode 100644 src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.module.scss create mode 100644 src-ui/assets/x_mark.svg diff --git a/src-ui/app/modal_controller/update_modal/UpdateModal.jsx b/src-ui/app/modal_controller/update_modal/UpdateModal.jsx index caf89d43..c35a240c 100644 --- a/src-ui/app/modal_controller/update_modal/UpdateModal.jsx +++ b/src-ui/app/modal_controller/update_modal/UpdateModal.jsx @@ -1,3 +1,4 @@ +import clsx from "clsx"; import styles from "./UpdateModal.module.scss"; import { useTranslation } from "react-i18next"; import { useStore_OpenedQuickSetting } from "@store"; @@ -9,7 +10,7 @@ import { useSoftwareVersion, } from "@logics_common"; -import clsx from "clsx"; +import { PluginCompatibilityList } from "./plugins_compatibility_list/PluginCompatibilityList"; export const UpdateModal = () => { const { t } = useTranslation(); @@ -44,31 +45,34 @@ export const UpdateModal = () => { return (
- {isAnyPluginEnabled() && } -
-
-
- - {is_cpu_version ? : null} +
+ {isAnyPluginEnabled() && } +
+
+
+ + {is_cpu_version ? : null} +
+
+ +
-
- +
+
+ + {!is_cpu_version ? : null} +
+
+ + + +
-
-
-
- - {!is_cpu_version ? : null} -
-
- - - -
-
-

{t("update_modal.download_latest_and_restart")}

+

{t("update_modal.download_latest_and_restart")}

+
+
@@ -93,23 +97,4 @@ const CurrentVersionLabel = (props) => { return

{t("update_modal.is_latest_version_already")}

; } return

{t("update_modal.is_current_compute_device")}

; -}; - -const PluginUpdateNotification = () => { - const { enabledPluginsList } = usePlugins(); - - // ダウンロード済みのもの or プラグイン最新版が、VRCT最新版(VRCTアプデ後)に非対応のもの - const incompatible_plugins_list = enabledPluginsList().filter(plugin => { - if (!plugin.is_downloaded) return false; - if (!plugin.downloaded_plugin_info?.is_plugin_supported_latest_vrct || !plugin.latest_plugin_info.is_plugin_supported_latest_vrct) return true; - }); - - return ( -
- {incompatible_plugins_list.map(plugin => { - const target_data = plugin.downloaded_plugin_info; - return

{target_data.title}

- })} -
- ); }; \ No newline at end of file diff --git a/src-ui/app/modal_controller/update_modal/UpdateModal.module.scss b/src-ui/app/modal_controller/update_modal/UpdateModal.module.scss index 57881896..4fe27802 100644 --- a/src-ui/app/modal_controller/update_modal/UpdateModal.module.scss +++ b/src-ui/app/modal_controller/update_modal/UpdateModal.module.scss @@ -3,7 +3,7 @@ height: 100%; display: flex; flex-direction: column; - justify-content: center; + justify-content: safe center; align-items: center; gap: 2.4rem; } @@ -16,6 +16,14 @@ gap: 8rem; } +.update_section_wrapper { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 4rem; +} + .update_section { border: 0.1rem solid var(--dark_600_color); border-radius: 0.4rem; diff --git a/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx new file mode 100644 index 00000000..96e1054a --- /dev/null +++ b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx @@ -0,0 +1,61 @@ +import styles from "./PluginCompatibilityList.module.scss"; +import { usePlugins } from "@logics_configs"; +import CheckMarkSvg from "@images/check_mark.svg?react"; +import XSvg from "@images/x_mark.svg?react"; +import WarningSvg from "@images/warning.svg?react"; + +export const PluginCompatibilityList = () => { + const { enabledPluginsList } = usePlugins(); + + // ダウンロード済みのもの + const downloaded_plugin = enabledPluginsList().filter(p => p.is_downloaded); + + // プラグイン最新版が、VRCT最新版(VRCTアプデ後)に非対応のもの + const compatible_plugins_list = []; + const incompatible_plugins_list = []; + for (const p of downloaded_plugin) { + if (!p.downloaded_plugin_info?.is_plugin_supported_latest_vrct || !p.latest_plugin_info?.is_plugin_supported_latest_vrct) { + incompatible_plugins_list.push(p); + } else { + compatible_plugins_list.push(p); + } + } + + const is_any_compatible_plugin = incompatible_plugins_list.length > 0; + + return ( +
+

使用中プラグインの互換性チェック

+
+ {incompatible_plugins_list.map(plugin => { + const target_data = plugin.downloaded_plugin_info; + return ; + })} + {compatible_plugins_list.map(plugin => { + const target_data = plugin.downloaded_plugin_info; + return ; + })} +
+ {is_any_compatible_plugin && +
+ +

VRCT最新バージョンで互換性のないプラグインはアップデート後に無効化されます。引き続き使用したい場合は、各プラグインの更新を待ってください。

+
+ } +
+ ); +}; + +const PluginContainer = ({ target_data, is_compatible }) => { + console.log(target_data.plugin_id); + + return ( +
+

{target_data.title}

+ {is_compatible + ? + : + } +
+ ); +}; \ No newline at end of file diff --git a/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.module.scss b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.module.scss new file mode 100644 index 00000000..0b439619 --- /dev/null +++ b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.module.scss @@ -0,0 +1,63 @@ +.container { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + gap: 1rem; +} + +.title { + font-size: 1.6rem; +} + +.plugins_compatibility_container { + display: flex; + justify-content: center; + align-items: center; + gap: 0.2rem 1rem; + flex-wrap: wrap; +} + +.plugin_box { + display: flex; + justify-content: center; + align-items: center; + padding: 0.4rem 0.6rem; + gap: 0.6rem; +} + +.plugin_label { + font-size: 1.4rem; + color: var(--error_bc_color); + &.is_compatible { + color: var(--primary_300_color); + } +} + + +.check_mark_svg { + width: 1.8rem; + color: var(--primary_300_color); +} +.x_svg { + width: 1.8rem; + color: var(--error_bc_color); +} + +.warning_container { + display: flex; + justify-content: center; + align-items: center; + gap: 1rem; +} + +.warning_svg { + padding-bottom: 0.4rem; + width: 2.4rem; + color: var(--waring_color); + flex-shrink: 0; +} + +.warning_text { + font-size: 1.2rem; +} \ No newline at end of file diff --git a/src-ui/assets/x_mark.svg b/src-ui/assets/x_mark.svg new file mode 100644 index 00000000..e4a0922e --- /dev/null +++ b/src-ui/assets/x_mark.svg @@ -0,0 +1 @@ + \ No newline at end of file From b8c1688172c22f75773ac601e8e8b3a2f730c521 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 17 Apr 2025 17:46:17 +0900 Subject: [PATCH 31/68] [Update] Plugins: separate dev mode. url and import path. --- .../PluginCompatibilityList.jsx | 2 -- src-ui/logics/configs/plugins/usePlugins.js | 4 ++-- src-ui/ui_configs.js | 15 ++++++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx index 96e1054a..3591f962 100644 --- a/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx +++ b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx @@ -47,8 +47,6 @@ export const PluginCompatibilityList = () => { }; const PluginContainer = ({ target_data, is_compatible }) => { - console.log(target_data.plugin_id); - return (

{target_data.title}

diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 80e7e717..2e92ef74 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -1,5 +1,5 @@ import { invoke } from "@tauri-apps/api/tauri"; -import { IS_PLUGIN_DEV_MODE, getPluginsList } from "@ui_configs"; +import { IS_PLUGIN_PATH_DEV_MODE, getPluginsList } from "@ui_configs"; import { createAtomWithHook, useStore_SavedPluginsStatus, @@ -110,7 +110,7 @@ export const usePlugins = () => { }; const asyncLoadAllPlugins = async () => { - if (IS_PLUGIN_DEV_MODE) { + if (IS_PLUGIN_PATH_DEV_MODE) { imported_dev_plugins.forEach(({ index, downloaded_plugin_info }) => { if (!index || !downloaded_plugin_info) { console.error("Invalid development plugin detected", index, downloaded_plugin_info); diff --git a/src-ui/ui_configs.js b/src-ui/ui_configs.js index 6db3193f..6b8d0031 100644 --- a/src-ui/ui_configs.js +++ b/src-ui/ui_configs.js @@ -52,15 +52,20 @@ export const ui_configs = { ] }; -export const IS_PLUGIN_DEV_MODE = false; -if (IS_PLUGIN_DEV_MODE) console.warn("ui_configs IS_PLUGIN_DEV_MODE: true. Turn to 'false' when it's production environment."); +// true: src-ui\plugins false: src-tauri\target\debug\plugins +export const IS_PLUGIN_PATH_DEV_MODE = false; + +// true: dev_vrct_plugins_list.json false: vrct_plugins_list.json +export const IS_PLUGIN_LIST_URL_DEV_MODE = false; + export const getPluginsList = () => { const base_url = "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/"; - const plugins_list_url = (IS_PLUGIN_DEV_MODE) - ? base_url + "dev_vrct_plugins_list.json" - : base_url + "vrct_plugins_list.json"; + const plugins_list_url = (IS_PLUGIN_LIST_URL_DEV_MODE) + ? base_url + "dev_vrct_plugins_list.json" + : base_url + "vrct_plugins_list.json"; return plugins_list_url; }; +if (IS_PLUGIN_PATH_DEV_MODE || IS_PLUGIN_LIST_URL_DEV_MODE) console.warn("ui_configs IS_PLUGIN_PATH_DEV_MODE or IS_PLUGIN_LIST_URL_DEV_MODE is true. Turn to 'false' when it's production environment."); export const translator_status = [ { id: "DeepL", label: "DeepL", is_available: false }, From fd59c0b28ff9aaace218010dc025ba9a1047cd9b Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 17 Apr 2025 19:44:14 +0900 Subject: [PATCH 32/68] [Update/Refactor] Automatically disable and save the status to config.json if the plugin is not supported. --- .../MergePluginsController.jsx | 2 ++ .../setting_box/plugins/Plugins.jsx | 31 ++----------------- src-ui/logics/configs/plugins/usePlugins.js | 29 +++++++++++++++++ 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx index c4545bc3..e0e258b7 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx @@ -10,6 +10,7 @@ export const MergePluginsController = () => { currentFetchedPluginsInfo, currentSavedPluginsStatus, downloadAndExtractPlugin, + toggleSavedPluginStatus, } = usePlugins(); const { checkVrctVerCompatibility } = useSoftwareVersion(); @@ -129,6 +130,7 @@ export const MergePluginsController = () => { !plugin.latest_plugin_info?.is_plugin_supported ) { plugin.is_enabled = false; + toggleSavedPluginStatus(plugin.plugin_id); } } }); diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index 9f7f490e..e60927f4 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -22,9 +22,8 @@ const PluginDownloadContainer = () => { const { downloadAndExtractPlugin, currentPluginsData, - updatePluginsData, currentSavedPluginsStatus, - setSavedPluginsStatus, + toggleSavedPluginStatus, handlePendingPlugin, } = usePlugins(); @@ -42,33 +41,7 @@ const PluginDownloadContainer = () => { // プラグインのオンオフ切り替え処理 const toggleFunction = (target_plugin_id) => { - const is_exists = currentSavedPluginsStatus.data.some( - (d) => d.plugin_id === target_plugin_id - ); - let new_value = []; - if (is_exists) { - new_value = currentSavedPluginsStatus.data.map((d) => { - if (d.plugin_id === target_plugin_id) { - d.is_enabled = !d.is_enabled; - } - return d; - }); - } else { - new_value.push(...currentSavedPluginsStatus.data); - new_value.push({ - plugin_id: target_plugin_id, - is_enabled: true, - }); - } - - // 「currentPluginsData.data」でis_downloadedがtrueのものだけ残す - new_value = new_value.filter((item) => - currentPluginsData.data.some( - (plugin) => plugin.plugin_id === item.plugin_id && plugin.is_downloaded - ) - ); - - setSavedPluginsStatus(new_value); + toggleSavedPluginStatus(target_plugin_id); }; const variable_state = currentSavedPluginsStatus.state; diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 2e92ef74..53db0c0b 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -298,7 +298,35 @@ export const usePlugins = () => { }); }; + const toggleSavedPluginStatus = (target_plugin_id) => { + const is_exists = currentSavedPluginsStatus.data.some( + (d) => d.plugin_id === target_plugin_id + ); + let new_value = []; + if (is_exists) { + new_value = currentSavedPluginsStatus.data.map((d) => { + if (d.plugin_id === target_plugin_id) { + d.is_enabled = !d.is_enabled; + } + return d; + }); + } else { + new_value.push(...currentSavedPluginsStatus.data); + new_value.push({ + plugin_id: target_plugin_id, + is_enabled: true, + }); + } + // 「currentPluginsData.data」でis_downloadedがtrueのものだけ残す + new_value = new_value.filter((item) => + currentPluginsData.data.some( + (plugin) => plugin.plugin_id === item.plugin_id && plugin.is_downloaded + ) + ); + + setSavedPluginsStatus(new_value); + }; const setSavedPluginsStatus = (plugins_status) => { pendingSavedPluginsStatus(); @@ -344,6 +372,7 @@ export const usePlugins = () => { currentLoadedPlugins, updateLoadedPlugins, + toggleSavedPluginStatus, setSavedPluginsStatus, handlePendingPlugin, From 9ef56db2dd70d000b256ab410e45edfb9ee176f4 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Fri, 18 Apr 2025 00:10:09 +0900 Subject: [PATCH 33/68] [Update] Add safety that prevent to show the title Plugin compatibility list unnecessary. Remove the test code that is for showing the update modal. --- .../PluginCompatibilityList.jsx | 10 +++++++--- src-ui/logics/useReceiveRoutes.js | 8 +------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx index 3591f962..d460b104 100644 --- a/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx +++ b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx @@ -10,18 +10,22 @@ export const PluginCompatibilityList = () => { // ダウンロード済みのもの const downloaded_plugin = enabledPluginsList().filter(p => p.is_downloaded); - // プラグイン最新版が、VRCT最新版(VRCTアプデ後)に非対応のもの const compatible_plugins_list = []; const incompatible_plugins_list = []; for (const p of downloaded_plugin) { if (!p.downloaded_plugin_info?.is_plugin_supported_latest_vrct || !p.latest_plugin_info?.is_plugin_supported_latest_vrct) { + // プラグイン最新版でも、VRCT最新版(VRCTアプデ後)に非対応のもの incompatible_plugins_list.push(p); } else { + // 現プラグイン or 最新版が、VRCT最新版(VRCTアプデ後)に対応しているもの compatible_plugins_list.push(p); } } - const is_any_compatible_plugin = incompatible_plugins_list.length > 0; + const is_any_incompatible_plugin = incompatible_plugins_list.length > 0; + const is_any_compatible_plugin = compatible_plugins_list.length > 0; + + if (!is_any_incompatible_plugin && !is_any_compatible_plugin) return null; // This is just for safety. return (
@@ -36,7 +40,7 @@ export const PluginCompatibilityList = () => { return ; })}
- {is_any_compatible_plugin && + {is_any_incompatible_plugin &&

VRCT最新バージョンで互換性のないプラグインはアップデート後に無効化されます。引き続き使用したい場合は、各プラグインの更新を待ってください。

diff --git a/src-ui/logics/useReceiveRoutes.js b/src-ui/logics/useReceiveRoutes.js index fb826f8c..b73fe62e 100644 --- a/src-ui/logics/useReceiveRoutes.js +++ b/src-ui/logics/useReceiveRoutes.js @@ -206,13 +206,7 @@ export const useReceiveRoutes = () => { "/set/data/main_window_geometry": () => {}, "/run/open_filepath_logs": () => console.log("Opened Directory, Message Logs"), "/run/open_filepath_config_file": () => console.log("Opened Directory, Config File"), - "/run/software_update_info": () => { - updateLatestSoftwareVersionInfo({ - is_update_available: true, - new_version: "3.0.3", - }) - }, - // "/run/software_update_info": updateLatestSoftwareVersionInfo, + "/run/software_update_info": updateLatestSoftwareVersionInfo, "/run/connected_network": handleNetworkConnection, // Main Page From 01b7d6c94581d647b2b6c6827907957c8700222b Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Fri, 18 Apr 2025 00:38:31 +0900 Subject: [PATCH 34/68] [Update] Plugins: Sort plugins list and remain the order of it. --- .../setting_section/setting_box/plugins/Plugins.jsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index e60927f4..2a02a4b0 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -17,7 +17,6 @@ export const Plugins = () => { ); }; - const PluginDownloadContainer = () => { const { downloadAndExtractPlugin, @@ -46,9 +45,14 @@ const PluginDownloadContainer = () => { const variable_state = currentSavedPluginsStatus.state; + // plugin_id で ABC 順にソート + const sorted_plugins_data = [...currentPluginsData.data].sort((a, b) => + a.plugin_id.localeCompare(b.plugin_id) + ); + return (
- {currentPluginsData.data.map((plugin) => ( + {sorted_plugins_data.map((plugin) => (

{plugin.is_downloaded @@ -79,4 +83,4 @@ const PluginDownloadContainer = () => { ))}

); -}; +}; \ No newline at end of file From 0be9674f69a4608fa03d6b2311870dc04714a2a0 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Fri, 18 Apr 2025 01:50:30 +0900 Subject: [PATCH 35/68] [bugfix] Plugins: Fix the error that was happened if 'plugins' directory is not exist and try to load. --- src-ui/logics/configs/plugins/usePlugins.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 53db0c0b..32149def 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -124,6 +124,9 @@ export const usePlugins = () => { } }); } else { + const is_plugins_dir_exists = await exists("plugins", { dir: BaseDirectory.Resource }); + if (!is_plugins_dir_exists) return; + try { const plugin_entries = await readDir("plugins", { dir: BaseDirectory.Resource, recursive: true }); const plugin_files = plugin_entries.filter(entry => entry.children && Array.isArray(entry.children)); From 74e38f931822ad7c5cac4befdc59744e96959c00 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Fri, 18 Apr 2025 14:30:47 +0900 Subject: [PATCH 36/68] [bugfix] Fix the bug that the plugin update unintentionally when its latest version available and it turn to enabled. --- .../plugins_controllers/MergePluginsController.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx index e0e258b7..73b6f615 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx @@ -126,7 +126,8 @@ export const MergePluginsController = () => { new_data.forEach(plugin => { if (plugin.is_downloaded && plugin.is_enabled) { if ( - !plugin.downloaded_plugin_info?.is_plugin_supported && + !plugin.downloaded_plugin_info.is_plugin_supported && + plugin.latest_plugin_info && !plugin.latest_plugin_info?.is_plugin_supported ) { plugin.is_enabled = false; @@ -155,6 +156,7 @@ export const MergePluginsController = () => { currentPluginsData.data.forEach(plugin => { if (plugin.is_downloaded && plugin.is_enabled && + !plugin.downloaded_plugin_info.is_plugin_supported && !plugin.is_latest_version_already && plugin.is_latest_version_available ) { From 7bcbefaf0697bd15f466569a56d0e3dd0cfc4537 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Fri, 18 Apr 2025 14:53:36 +0900 Subject: [PATCH 37/68] [Refactor] Remove the code that is no longer in use. --- .../plugins_controllers/LoadPluginsController.jsx | 5 +---- .../plugins_controllers/MergePluginsController.jsx | 4 ++-- .../setting_section/setting_box/plugins/Plugins.jsx | 7 ++----- src-ui/logics/configs/plugins/usePlugins.js | 13 ++----------- src-ui/store.js | 2 -- 5 files changed, 7 insertions(+), 24 deletions(-) diff --git a/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx index ee4caeac..c9c953da 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx @@ -4,7 +4,6 @@ import { usePlugins } from "@logics_configs"; export const LoadPluginsController = ({ pluginsControllerHasRunRef }) => { const { asyncLoadAllPlugins, - updateIsInitializedLoadPlugin, } = usePlugins(); const asyncInitLoadPlugins = async () => { @@ -17,9 +16,7 @@ export const LoadPluginsController = ({ pluginsControllerHasRunRef }) => { useEffect(() => { if (!pluginsControllerHasRunRef.current.is_initialized_load_plugin) { - asyncInitLoadPlugins().then(() => { - updateIsInitializedLoadPlugin(true); - }); + asyncInitLoadPlugins(); pluginsControllerHasRunRef.current.is_initialized_load_plugin = true; } }, []); diff --git a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx index 73b6f615..85df42d1 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx @@ -10,7 +10,7 @@ export const MergePluginsController = () => { currentFetchedPluginsInfo, currentSavedPluginsStatus, downloadAndExtractPlugin, - toggleSavedPluginStatus, + toggleSavedPluginsStatus, } = usePlugins(); const { checkVrctVerCompatibility } = useSoftwareVersion(); @@ -131,7 +131,7 @@ export const MergePluginsController = () => { !plugin.latest_plugin_info?.is_plugin_supported ) { plugin.is_enabled = false; - toggleSavedPluginStatus(plugin.plugin_id); + toggleSavedPluginsStatus(plugin.plugin_id); } } }); diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index 2a02a4b0..46661133 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -5,11 +5,8 @@ import { PluginsControlComponent } from "../_components/plugins_control_componen export const Plugins = () => { const { - currentIsPluginsInitialized, } = usePlugins(); - // if (!currentIsPluginsInitialized.data) return null; - return (
@@ -22,7 +19,7 @@ const PluginDownloadContainer = () => { downloadAndExtractPlugin, currentPluginsData, currentSavedPluginsStatus, - toggleSavedPluginStatus, + toggleSavedPluginsStatus, handlePendingPlugin, } = usePlugins(); @@ -40,7 +37,7 @@ const PluginDownloadContainer = () => { // プラグインのオンオフ切り替え処理 const toggleFunction = (target_plugin_id) => { - toggleSavedPluginStatus(target_plugin_id); + toggleSavedPluginsStatus(target_plugin_id); }; const variable_state = currentSavedPluginsStatus.state; diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 32149def..813bf0f7 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -4,8 +4,6 @@ import { createAtomWithHook, useStore_SavedPluginsStatus, useStore_PluginsData, - useStore_IsPluginsInitialized, - useStore_IsInitializedLoadPlugin, useStore_IsFetchedPluginsInfo, useStore_FetchedPluginsInfo, @@ -39,7 +37,6 @@ const PLUGIN_LIST_URL = getPluginsList(); export const usePlugins = () => { const { asyncStdoutToPython } = useStdoutToPython(); - const { currentIsInitializedLoadPlugin, updateIsInitializedLoadPlugin, pendingIsInitializedLoadPlugin } = useStore_IsInitializedLoadPlugin(); const { currentIsFetchedPluginsInfo, updateIsFetchedPluginsInfo, pendingIsFetchedPluginsInfo } = useStore_IsFetchedPluginsInfo(); const { currentFetchedPluginsInfo, updateFetchedPluginsInfo, pendingFetchedPluginsInfo } = useStore_FetchedPluginsInfo(); @@ -47,7 +44,6 @@ export const usePlugins = () => { const { currentSavedPluginsStatus, updateSavedPluginsStatus, pendingSavedPluginsStatus } = useStore_SavedPluginsStatus(); const { currentPluginsData, updatePluginsData, pendingPluginsData } = useStore_PluginsData(); - const { currentIsPluginsInitialized, updateIsPluginsInitialized, pendingIsPluginsInitialized } = useStore_IsPluginsInitialized(); const { checkVrctVerCompatibility } = useSoftwareVersion(); const { asyncTauriFetchGithub } = useFetch(); @@ -301,7 +297,7 @@ export const usePlugins = () => { }); }; - const toggleSavedPluginStatus = (target_plugin_id) => { + const toggleSavedPluginsStatus = (target_plugin_id) => { const is_exists = currentSavedPluginsStatus.data.some( (d) => d.plugin_id === target_plugin_id ); @@ -361,11 +357,6 @@ export const usePlugins = () => { currentPluginsData, updatePluginsData, - currentIsPluginsInitialized, - updateIsPluginsInitialized, - - currentIsInitializedLoadPlugin, - updateIsInitializedLoadPlugin, currentIsFetchedPluginsInfo, updateIsFetchedPluginsInfo, @@ -375,7 +366,7 @@ export const usePlugins = () => { currentLoadedPlugins, updateLoadedPlugins, - toggleSavedPluginStatus, + toggleSavedPluginsStatus, setSavedPluginsStatus, handlePendingPlugin, diff --git a/src-ui/store.js b/src-ui/store.js index 3d4bac13..260285c7 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -278,8 +278,6 @@ export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createA }, "Hotkeys"); // Plugins -export const { atomInstance: Atom_IsPluginsInitialized, useHook: useStore_IsPluginsInitialized } = createAtomWithHook(false, "IsPluginsInitialized"); -export const { atomInstance: Atom_IsInitializedLoadPlugin, useHook: useStore_IsInitializedLoadPlugin } = createAtomWithHook(false, "IsInitializedLoadPlugin"); export const { atomInstance: Atom_IsFetchedPluginsInfo, useHook: useStore_IsFetchedPluginsInfo } = createAtomWithHook(false, "IsFetchedPluginsInfo"); export const { atomInstance: Atom_FetchedPluginsInfo, useHook: useStore_FetchedPluginsInfo } = createAtomWithHook([], "FetchedPluginsInfo"); From cd63e2e3ca005211441aa26512be90edc0f587fd Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sat, 19 Apr 2025 16:15:47 +0900 Subject: [PATCH 38/68] [bugfix/Refactor] Fix plugin compatibility list that was showed incorrectly. Change init function. --- src-ui/app/App.jsx | 11 ++--- .../_app_controllers/PluginsController.jsx | 6 +-- .../FetchLatestPluginsDataController.jsx | 16 ++----- .../LoadPluginsController.jsx | 7 +-- .../MergePluginsController.jsx | 12 ++++- .../setting_box/plugins/Plugins.jsx | 11 ++++- .../PluginCompatibilityList.jsx | 12 ++++- src-ui/logics/configs/plugins/usePlugins.js | 47 +++++++++++++++---- src-ui/store.js | 4 +- 9 files changed, 85 insertions(+), 41 deletions(-) diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index 9c7e8aa0..c2b0e4f3 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -1,4 +1,3 @@ -import { useRef } from "react"; import { useTranslation } from "react-i18next"; import { @@ -29,10 +28,6 @@ export const App = () => { const { currentIsBackendReady } = useIsBackendReady(); const { WindowGeometryController } = useWindow(); const { i18n } = useTranslation(); - const pluginsControllerHasRunRef = useRef({ - is_initialized_load_plugin: false, - is_init_fetched_plugins_info: false, - }); return (
@@ -49,7 +44,7 @@ export const App = () => { {(currentIsBackendReady.data === false || currentIsVrctAvailable.data === false) ? - : + : } @@ -58,11 +53,11 @@ export const App = () => { ); }; -const Contents = ({ pluginsControllerHasRunRef }) => { +const Contents = () => { const { currentIsSoftwareUpdating } = useIsSoftwareUpdating(); return ( <> - + {currentIsSoftwareUpdating.data === false diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx index 4f47db05..0abeb5cb 100644 --- a/src-ui/app/_app_controllers/PluginsController.jsx +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -10,13 +10,13 @@ import { LoadPluginsController } from "./plugins_controllers/LoadPluginsControll import { FetchLatestPluginsDataController } from "./plugins_controllers/FetchLatestPluginsDataController"; import { MergePluginsController } from "./plugins_controllers/MergePluginsController"; -export const PluginsController = ({ pluginsControllerHasRunRef }) => { +export const PluginsController = () => { return ( <> - - + + ); }; \ No newline at end of file diff --git a/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx b/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx index 088d9cae..1f9d5e75 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx @@ -1,23 +1,15 @@ import { useEffect } from "react"; import { usePlugins } from "@logics_configs"; -export const FetchLatestPluginsDataController = ({ pluginsControllerHasRunRef }) => { +export const FetchLatestPluginsDataController = () => { const { asyncFetchPluginsInfo, + isAnyPluginEnabled_Init, } = usePlugins(); - const asyncInitFetchPluginsInfo = async () => { - try { - await asyncFetchPluginsInfo(); - } catch (error) { - console.error(error); - } - }; - useEffect(() => { - if (!pluginsControllerHasRunRef.current.is_init_fetched_plugins_info) { - asyncInitFetchPluginsInfo(); - pluginsControllerHasRunRef.current.is_init_fetched_plugins_info = true; + if (isAnyPluginEnabled_Init()) { + asyncFetchPluginsInfo(); } }, []); diff --git a/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx index c9c953da..46ad274b 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx @@ -1,7 +1,8 @@ import { useEffect } from "react"; import { usePlugins } from "@logics_configs"; +import { store } from "@store"; -export const LoadPluginsController = ({ pluginsControllerHasRunRef }) => { +export const LoadPluginsController = () => { const { asyncLoadAllPlugins, } = usePlugins(); @@ -15,9 +16,9 @@ export const LoadPluginsController = ({ pluginsControllerHasRunRef }) => { }; useEffect(() => { - if (!pluginsControllerHasRunRef.current.is_initialized_load_plugin) { + if (!store.is_initialized_load_plugin) { asyncInitLoadPlugins(); - pluginsControllerHasRunRef.current.is_initialized_load_plugin = true; + store.is_initialized_load_plugin = true; } }, []); diff --git a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx index 85df42d1..9c8e02ca 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx @@ -10,7 +10,7 @@ export const MergePluginsController = () => { currentFetchedPluginsInfo, currentSavedPluginsStatus, downloadAndExtractPlugin, - toggleSavedPluginsStatus, + setTargetSavedPluginsStatus_Init, } = usePlugins(); const { checkVrctVerCompatibility } = useSoftwareVersion(); @@ -131,7 +131,15 @@ export const MergePluginsController = () => { !plugin.latest_plugin_info?.is_plugin_supported ) { plugin.is_enabled = false; - toggleSavedPluginsStatus(plugin.plugin_id); + setTargetSavedPluginsStatus_Init(plugin.plugin_id, false); + } + + if ( + !plugin.downloaded_plugin_info.is_plugin_supported && + plugin.is_outdated + ) { + plugin.is_enabled = false; + setTargetSavedPluginsStatus_Init(plugin.plugin_id, false); } } }); diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index 46661133..5f6df29e 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -1,11 +1,20 @@ -import React from "react"; +import { useEffect, useRef } from "react"; import { usePlugins } from "@logics_configs"; import styles from "./Plugins.module.scss"; import { PluginsControlComponent } from "../_components/plugins_control_component/PluginsControlComponent"; export const Plugins = () => { const { + asyncFetchPluginsInfo, } = usePlugins(); + const hasRunRef = useRef(false); + + useEffect(() => { + if (!hasRunRef.current) { + asyncFetchPluginsInfo(); + } + return () => hasRunRef.current = true; + }, []); return (
diff --git a/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx index d460b104..dd0133b1 100644 --- a/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx +++ b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx @@ -1,3 +1,4 @@ +import { useEffect } from "react"; import styles from "./PluginCompatibilityList.module.scss"; import { usePlugins } from "@logics_configs"; import CheckMarkSvg from "@images/check_mark.svg?react"; @@ -5,7 +6,14 @@ import XSvg from "@images/x_mark.svg?react"; import WarningSvg from "@images/warning.svg?react"; export const PluginCompatibilityList = () => { - const { enabledPluginsList } = usePlugins(); + const { + enabledPluginsList, + asyncFetchPluginsInfo, + } = usePlugins(); + + useEffect(() => { + asyncFetchPluginsInfo(); + }, []); // ダウンロード済みのもの const downloaded_plugin = enabledPluginsList().filter(p => p.is_downloaded); @@ -13,7 +21,7 @@ export const PluginCompatibilityList = () => { const compatible_plugins_list = []; const incompatible_plugins_list = []; for (const p of downloaded_plugin) { - if (!p.downloaded_plugin_info?.is_plugin_supported_latest_vrct || !p.latest_plugin_info?.is_plugin_supported_latest_vrct) { + if (!p.downloaded_plugin_info?.is_plugin_supported_latest_vrct && !p.latest_plugin_info?.is_plugin_supported_latest_vrct) { // プラグイン最新版でも、VRCT最新版(VRCTアプデ後)に非対応のもの incompatible_plugins_list.push(p); } else { diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 813bf0f7..0a3e65f1 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -1,10 +1,11 @@ import { invoke } from "@tauri-apps/api/tauri"; import { IS_PLUGIN_PATH_DEV_MODE, getPluginsList } from "@ui_configs"; import { + store, + createAtomWithHook, useStore_SavedPluginsStatus, useStore_PluginsData, - useStore_IsFetchedPluginsInfo, useStore_FetchedPluginsInfo, useStore_LoadedPlugins, @@ -37,8 +38,6 @@ const PLUGIN_LIST_URL = getPluginsList(); export const usePlugins = () => { const { asyncStdoutToPython } = useStdoutToPython(); - const { currentIsFetchedPluginsInfo, updateIsFetchedPluginsInfo, pendingIsFetchedPluginsInfo } = useStore_IsFetchedPluginsInfo(); - const { currentFetchedPluginsInfo, updateFetchedPluginsInfo, pendingFetchedPluginsInfo } = useStore_FetchedPluginsInfo(); const { currentLoadedPlugins, updateLoadedPlugins, pendingLoadedPlugins } = useStore_LoadedPlugins(); @@ -222,7 +221,9 @@ export const usePlugins = () => { const asyncFetchPluginsInfo = async () => { - if (currentIsFetchedPluginsInfo.data) return; + if (store.is_fetched_plugins_info) return; + store.is_fetched_plugins_info = true; + try { const response = await asyncTauriFetchGithub(PLUGIN_LIST_URL); if (response.status !== 200) { @@ -247,7 +248,6 @@ export const usePlugins = () => { }) ); updateFetchedPluginsInfo(updated_list); - updateIsFetchedPluginsInfo(true); } catch (error) { console.error("Error fetching plugin info list: ", error); } @@ -327,11 +327,43 @@ export const usePlugins = () => { setSavedPluginsStatus(new_value); }; + + // Init時の処理 非対応のものを無効化する際に、savedDPluginsStatusから不要なものを削除する処理が邪魔になるので該当コードを削除したバージョン。Init以外で使用する時にはリファクタが必要になる。 + const setTargetSavedPluginsStatus_Init = (target_plugin_id, is_enabled) => { + const is_exists = currentSavedPluginsStatus.data.some( + (d) => d.plugin_id === target_plugin_id + ); + let new_value = []; + if (is_exists) { + new_value = currentSavedPluginsStatus.data.map((d) => { + if (d.plugin_id === target_plugin_id) { + d.is_enabled = is_enabled; + } + return d; + }); + } else { + new_value.push(...currentSavedPluginsStatus.data); + new_value.push({ + plugin_id: target_plugin_id, + is_enabled: is_enabled, + }); + } + + setSavedPluginsStatus(new_value); + }; + + const setSavedPluginsStatus = (plugins_status) => { pendingSavedPluginsStatus(); asyncStdoutToPython("/set/data/plugins_status", plugins_status); }; + // init時、currentPluginsDataからのデータではデータ更新が間に合わないので、currentSavedPluginsStatusから直接取得 + const isAnyPluginEnabled_Init = () => { + console.log(currentSavedPluginsStatus); + return currentSavedPluginsStatus.data.some(plugin => plugin.is_enabled); + }; + const isAnyPluginEnabled = () => { return currentPluginsData.data.some(plugin => plugin.is_enabled); }; @@ -345,6 +377,7 @@ export const usePlugins = () => { return { asyncFetchPluginsInfo, + isAnyPluginEnabled_Init, isAnyPluginEnabled, enabledPluginsList, @@ -357,9 +390,6 @@ export const usePlugins = () => { currentPluginsData, updatePluginsData, - currentIsFetchedPluginsInfo, - updateIsFetchedPluginsInfo, - currentFetchedPluginsInfo, updateFetchedPluginsInfo, @@ -367,6 +397,7 @@ export const usePlugins = () => { updateLoadedPlugins, toggleSavedPluginsStatus, + setTargetSavedPluginsStatus_Init, setSavedPluginsStatus, handlePendingPlugin, diff --git a/src-ui/store.js b/src-ui/store.js index 260285c7..f99ed73b 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -20,6 +20,8 @@ export const store = { log_box_ref: null, text_area_ref: null, is_applied_init_message_box_height: false, + is_initialized_load_plugin: false, + is_fetched_plugins_info: false, }; const generatePropertyNames = (base_name) => ({ @@ -278,8 +280,6 @@ export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createA }, "Hotkeys"); // Plugins -export const { atomInstance: Atom_IsFetchedPluginsInfo, useHook: useStore_IsFetchedPluginsInfo } = createAtomWithHook(false, "IsFetchedPluginsInfo"); - export const { atomInstance: Atom_FetchedPluginsInfo, useHook: useStore_FetchedPluginsInfo } = createAtomWithHook([], "FetchedPluginsInfo"); export const { atomInstance: Atom_LoadedPlugins, useHook: useStore_LoadedPlugins } = createAtomWithHook([], "LoadedPlugins"); export const { atomInstance: Atom_SavedPluginsStatus, useHook: useStore_SavedPluginsStatus } = createAtomWithHook([], "SavedPluginsStatus"); From 33f1e54f4d17a7c67840f0b12710f707433605d8 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sat, 19 Apr 2025 22:09:20 +0900 Subject: [PATCH 39/68] [bugfix] Plugins: fix auto disable plugins function. --- .../MergePluginsController.jsx | 56 ++++++++++--------- src-ui/logics/configs/plugins/usePlugins.js | 7 ++- src-ui/store.js | 3 +- 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx index 9c8e02ca..21a892a3 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx @@ -1,4 +1,5 @@ import { useEffect, useRef } from "react"; +import { store } from "@store"; import { usePlugins } from "@logics_configs"; import { useSoftwareVersion } from "@logics_common"; @@ -121,29 +122,6 @@ export const MergePluginsController = () => { } } - - // ダウンロード済みかつ有効なプラグインで、サポート対象でない場合は無効化 - new_data.forEach(plugin => { - if (plugin.is_downloaded && plugin.is_enabled) { - if ( - !plugin.downloaded_plugin_info.is_plugin_supported && - plugin.latest_plugin_info && - !plugin.latest_plugin_info?.is_plugin_supported - ) { - plugin.is_enabled = false; - setTargetSavedPluginsStatus_Init(plugin.plugin_id, false); - } - - if ( - !plugin.downloaded_plugin_info.is_plugin_supported && - plugin.is_outdated - ) { - plugin.is_enabled = false; - setTargetSavedPluginsStatus_Init(plugin.plugin_id, false); - } - } - }); - console.log("merged plugin data", new_data); return new_data; }); @@ -168,8 +146,6 @@ export const MergePluginsController = () => { !plugin.is_latest_version_already && plugin.is_latest_version_available ) { - console.log(!downloadingRef.current.has(plugin.plugin_id)); - if (!downloadingRef.current.has(plugin.plugin_id)) { downloadingRef.current.add(plugin.plugin_id); downloadAndExtractPlugin(plugin) @@ -186,5 +162,35 @@ export const MergePluginsController = () => { }); }, [currentPluginsData.data]); + + useEffect(() => { + // ダウンロード済みかつ有効なプラグインで、サポート対象でない場合は無効化 + if (store.is_initialized_fetched_plugin_info) { + updatePluginsData(prev => { + prev.data.forEach(plugin => { + if (plugin.is_downloaded && plugin.is_enabled) { + if ( + !plugin.downloaded_plugin_info.is_plugin_supported && + plugin.latest_plugin_info && + !plugin.latest_plugin_info?.is_plugin_supported + ) { + plugin.is_enabled = false; + setTargetSavedPluginsStatus_Init(plugin.plugin_id, false); + } + + if ( + !plugin.downloaded_plugin_info.is_plugin_supported && + plugin.is_outdated + ) { + plugin.is_enabled = false; + setTargetSavedPluginsStatus_Init(plugin.plugin_id, false); + } + } + }); + return prev.data; + }); + } + }, [store.is_initialized_fetched_plugin_info]); + return null; }; \ No newline at end of file diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 0a3e65f1..2fd911b5 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -221,8 +221,8 @@ export const usePlugins = () => { const asyncFetchPluginsInfo = async () => { - if (store.is_fetched_plugins_info) return; - store.is_fetched_plugins_info = true; + if (store.is_fetched_plugins_info_already) return; + store.is_fetched_plugins_info_already = true; try { const response = await asyncTauriFetchGithub(PLUGIN_LIST_URL); @@ -251,6 +251,8 @@ export const usePlugins = () => { } catch (error) { console.error("Error fetching plugin info list: ", error); } + + store.is_initialized_fetched_plugin_info = true; } const asyncFetchPluginInfo = async (plugin_info_asset_url) => { @@ -360,7 +362,6 @@ export const usePlugins = () => { // init時、currentPluginsDataからのデータではデータ更新が間に合わないので、currentSavedPluginsStatusから直接取得 const isAnyPluginEnabled_Init = () => { - console.log(currentSavedPluginsStatus); return currentSavedPluginsStatus.data.some(plugin => plugin.is_enabled); }; diff --git a/src-ui/store.js b/src-ui/store.js index f99ed73b..c181ced5 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -21,7 +21,8 @@ export const store = { text_area_ref: null, is_applied_init_message_box_height: false, is_initialized_load_plugin: false, - is_fetched_plugins_info: false, + is_fetched_plugins_info_already: false, + is_initialized_fetched_plugin_info: false, }; const generatePropertyNames = (base_name) => ({ From ec33d4020db22638b48d0d24149bc4d62b708c51 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 23 Apr 2025 01:37:23 +0900 Subject: [PATCH 40/68] [Update] Readmes: Remove the outdated information 'How to run in python'. --- README.ja.md | 9 --------- README.ko.md | 9 --------- README.md | 9 --------- README.zh-Hant.md | 9 --------- 4 files changed, 36 deletions(-) diff --git a/README.ja.md b/README.ja.md index 1d7a8c2a..d89c95a6 100644 --- a/README.ja.md +++ b/README.ja.md @@ -91,15 +91,6 @@ VRCTはあなたの会話を以下でサポートをします。
-# pythonで実行したい場合 -1. 以下のバージョンのpythonをインストールしてください。 - `python version 3.11.5` -2. packageのインストールとmain.pyを起動してください。 - ```bash - ./install.bat - python main.py - ``` - ## Author - [みしゃ(misyaguzi)](https://github.com/misyaguziya) (メイン開発) - [しいな(Shiina_12siy)](https://twitter.com/Shiina_12siy) (UI/UX, UI多言語対応) diff --git a/README.ko.md b/README.ko.md index cffa325c..bb260ba0 100644 --- a/README.ko.md +++ b/README.ko.md @@ -91,15 +91,6 @@ VRCT는 다음과 같이 당신의 대화를 도와드려요.
-# python으로 실행하고 싶은 경우 -1. 다음 버전의 python을 설치합니다. - `python version 3.11.5` -2. 패키지를 설치하고 main.py를 실행합니다. - ```bash - ./install.bat - python main.py - ``` - ## Author - [みしゃ(misyaguzi)](https://github.com/misyaguziya) (주요 개발) - [しいな(Shiina_12siy)](https://twitter.com/Shiina_12siy) (UI/UX, UI 다국어 지원) diff --git a/README.md b/README.md index 13c563a6..c7af06b9 100644 --- a/README.md +++ b/README.md @@ -91,15 +91,6 @@ Initial setup, basic functions, and other features are also described.
-# If you want to run it in python -1. Install the following version of python. - `python version 3.11.5` -2. Install package and run main.py. - ```bash - ./install.bat - python main.py - ``` - ## Author - [みしゃ(misyaguzi)](https://github.com/misyaguziya) (Main Development) - [しいな(Shiina_12siy)](https://twitter.com/Shiina_12siy) (UI/UX, UI multilingual support) diff --git a/README.zh-Hant.md b/README.zh-Hant.md index ea79a1b1..e9535ceb 100644 --- a/README.zh-Hant.md +++ b/README.zh-Hant.md @@ -90,15 +90,6 @@ VRCT 可以:
-# 原始碼啟動 -1. 安裝此版本的 Python。 - `python version 3.11.5` -2. 安裝 package 並啟動 main.py。 - ```bash - ./install.bat - python main.py - ``` - ## 作者 - [みしゃ(misyaguzi)](https://github.com/misyaguziya) (主要開發) - [しいな(Shiina_12siy)](https://twitter.com/Shiina_12siy) (UI/UX, UI 多語系支援) From d0dbdf69f0484298a231a043b05d9abd66e1a2d0 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 23 Apr 2025 06:36:55 +0900 Subject: [PATCH 41/68] [Refactor] Organize readme files. --- README.md | 108 +----------------- docs/{ => img}/kofi_logo.png | Bin docs/{ => img}/main_window.png | Bin docs/{ => img}/patreon_logo_black.png | Bin docs/{ => img}/patreon_logo_white.png | Bin docs/{ => img}/pixiv_fanbox_black.png | Bin docs/{ => img}/pixiv_fanbox_white.png | Bin docs/{ => img}/supporter_section_border_d.png | Bin docs/{ => img}/supporter_section_border_l.png | Bin docs/{ => img}/vrct_logo.png | Bin docs/{ => img}/vrct_logo_black.png | Bin docs/{ => img}/vrct_logo_white.png | Bin docs/readmes/README.en.md | 107 +++++++++++++++++ README.ja.md => docs/readmes/README.ja.md | 30 ++--- README.ko.md => docs/readmes/README.ko.md | 30 ++--- .../readmes/README.zh-Hant.md | 30 ++--- 16 files changed, 153 insertions(+), 152 deletions(-) rename docs/{ => img}/kofi_logo.png (100%) rename docs/{ => img}/main_window.png (100%) rename docs/{ => img}/patreon_logo_black.png (100%) rename docs/{ => img}/patreon_logo_white.png (100%) rename docs/{ => img}/pixiv_fanbox_black.png (100%) rename docs/{ => img}/pixiv_fanbox_white.png (100%) rename docs/{ => img}/supporter_section_border_d.png (100%) rename docs/{ => img}/supporter_section_border_l.png (100%) rename docs/{ => img}/vrct_logo.png (100%) rename docs/{ => img}/vrct_logo_black.png (100%) rename docs/{ => img}/vrct_logo_white.png (100%) create mode 100644 docs/readmes/README.en.md rename README.ja.md => docs/readmes/README.ja.md (70%) rename README.ko.md => docs/readmes/README.ko.md (70%) rename README.zh-Hant.md => docs/readmes/README.zh-Hant.md (67%) diff --git a/README.md b/README.md index c7af06b9..ab3e36da 100644 --- a/README.md +++ b/README.md @@ -1,107 +1 @@ -
- - - - - VRCT Logo - - -
-
- -[![GitHub release](https://img.shields.io/github/v/release/misyaguziya/VRCT.svg)](https://github.com/misyaguziya/VRCT/releases) -[![Downloads](https://img.shields.io/github/downloads/misyaguziya/VRCT/total)](https://github.com/misyaguziya/VRCT/releases) -[![Licence](https://img.shields.io/github/license/misyaguziya/VRCT)](https://github.com/misyaguziya/VRCT/blob/master/LICENSE) -[![Booth](https://img.shields.io/badge/Store-Booth.pm-red)](https://misyaguziya.booth.pm/items/5155325) -[![Github Sponsors](https://img.shields.io/badge/GitHub%20Sponsors-30363D?&logo=GitHub-Sponsors&logoColor=EA4AAA)](https://github.com/sponsors/misyaguziya) - -

-Become a VRCT Supporter on: -

- - - - - - PIXIV FANBOX - -   - - - - - - Patreon - -   - - - - Ko-fi - -   - -
- - - - - Supporter Section Border - - -
-
- -| **English** | [日本語](./README.ja.md) | [한국어](./README.ko.md) | [繁體中文](./README.zh-Hant.md) | - -

-VRCT is software that supports VRChat conversations with translation and transcription. -

- -![](docs/main_window.png) - -
- -# Download & Install -Download from anywhere you like. -- [Github.com](https://github.com/misyaguziya/VRCT/releases/) -- [BOOTH.pm](https://misyaguziya.booth.pm/items/5155325) - -Just download and run the exe. - -# What is VRCT? -VRCT is software that supports conversations between people who speak different languages by providing chat or voice translation. -These features are designed for use within VRChat. -*Although not supported, it is also used for other purposes such as watching movies. - -VRCT supports your conversations with -- 💬 **Send chat to VRChat** -- 🌐 **Translation** -- 🎙 **Transcription of audio from microphone** -- 🔈 **Transcription of audio from Speaker** - -# Documents -Initial setup, basic functions, and other features are also described. -- [Documents Link](https://mzsoftware.notion.site/VRCT-Documents-be79b7a165f64442ad8f326d86c22246?pvs=4) - -# How to Use (YouTube) -
- -[![](https://img.youtube.com/vi/rUTad037n8Q/0.jpg)](https://www.youtube.com/watch?v=rUTad037n8Q) - -
- -## Author -- [みしゃ(misyaguzi)](https://github.com/misyaguziya) (Main Development) -- [しいな(Shiina_12siy)](https://twitter.com/Shiina_12siy) (UI/UX, UI multilingual support) -- [レラ](https://github.com/soumt-r) (Technical Support) -- [どね](https://twitter.com/done_vrc) (Logo Design) - -## Thanks to our contributors - - - - ---- - -VRCT is not endorsed by VRChat and does not reflect the views or opinions of VRChat or anyone officially involved in producing or managing VRChat properties. VRChat and all associated properties are trademarks or registered trademarks of VRChat Inc. VRChat © VRChat Inc. \ No newline at end of file +docs/readmes/README.en.md \ No newline at end of file diff --git a/docs/kofi_logo.png b/docs/img/kofi_logo.png similarity index 100% rename from docs/kofi_logo.png rename to docs/img/kofi_logo.png diff --git a/docs/main_window.png b/docs/img/main_window.png similarity index 100% rename from docs/main_window.png rename to docs/img/main_window.png diff --git a/docs/patreon_logo_black.png b/docs/img/patreon_logo_black.png similarity index 100% rename from docs/patreon_logo_black.png rename to docs/img/patreon_logo_black.png diff --git a/docs/patreon_logo_white.png b/docs/img/patreon_logo_white.png similarity index 100% rename from docs/patreon_logo_white.png rename to docs/img/patreon_logo_white.png diff --git a/docs/pixiv_fanbox_black.png b/docs/img/pixiv_fanbox_black.png similarity index 100% rename from docs/pixiv_fanbox_black.png rename to docs/img/pixiv_fanbox_black.png diff --git a/docs/pixiv_fanbox_white.png b/docs/img/pixiv_fanbox_white.png similarity index 100% rename from docs/pixiv_fanbox_white.png rename to docs/img/pixiv_fanbox_white.png diff --git a/docs/supporter_section_border_d.png b/docs/img/supporter_section_border_d.png similarity index 100% rename from docs/supporter_section_border_d.png rename to docs/img/supporter_section_border_d.png diff --git a/docs/supporter_section_border_l.png b/docs/img/supporter_section_border_l.png similarity index 100% rename from docs/supporter_section_border_l.png rename to docs/img/supporter_section_border_l.png diff --git a/docs/vrct_logo.png b/docs/img/vrct_logo.png similarity index 100% rename from docs/vrct_logo.png rename to docs/img/vrct_logo.png diff --git a/docs/vrct_logo_black.png b/docs/img/vrct_logo_black.png similarity index 100% rename from docs/vrct_logo_black.png rename to docs/img/vrct_logo_black.png diff --git a/docs/vrct_logo_white.png b/docs/img/vrct_logo_white.png similarity index 100% rename from docs/vrct_logo_white.png rename to docs/img/vrct_logo_white.png diff --git a/docs/readmes/README.en.md b/docs/readmes/README.en.md new file mode 100644 index 00000000..b99a1da8 --- /dev/null +++ b/docs/readmes/README.en.md @@ -0,0 +1,107 @@ +
+ + + + + VRCT Logo + + +
+
+ +[![GitHub release](https://img.shields.io/github/v/release/misyaguziya/VRCT.svg)](https://github.com/misyaguziya/VRCT/releases) +[![Downloads](https://img.shields.io/github/downloads/misyaguziya/VRCT/total)](https://github.com/misyaguziya/VRCT/releases) +[![Licence](https://img.shields.io/github/license/misyaguziya/VRCT)](https://github.com/misyaguziya/VRCT/blob/master/LICENSE) +[![Booth](https://img.shields.io/badge/Store-Booth.pm-red)](https://misyaguziya.booth.pm/items/5155325) +[![Github Sponsors](https://img.shields.io/badge/GitHub%20Sponsors-30363D?&logo=GitHub-Sponsors&logoColor=EA4AAA)](https://github.com/sponsors/misyaguziya) + +

+Become a VRCT Supporter on: +

+ + + + + + PIXIV FANBOX + +   + + + + + + Patreon + +   + + + + Ko-fi + +   + +
+ + + + + Supporter Section Border + + +
+
+ +| **English** | [日本語](./README.ja.md) | [한국어](./README.ko.md) | [繁體中文](./README.zh-Hant.md) | + +

+VRCT is software that supports VRChat conversations with translation and transcription. +

+ +![](../img/main_window.png) + +
+ +# Download & Install +Download from anywhere you like. +- [Github.com](https://github.com/misyaguziya/VRCT/releases/) +- [BOOTH.pm](https://misyaguziya.booth.pm/items/5155325) + +Just download and run the exe. + +# What is VRCT? +VRCT is software that supports conversations between people who speak different languages by providing chat or voice translation. +These features are designed for use within VRChat. +*Although not supported, it is also used for other purposes such as watching movies. + +VRCT supports your conversations with +- 💬 **Send chat to VRChat** +- 🌐 **Translation** +- 🎙 **Transcription of audio from microphone** +- 🔈 **Transcription of audio from Speaker** + +# Documents +Initial setup, basic functions, and other features are also described. +- [Documents Link](https://mzsoftware.notion.site/VRCT-Documents-be79b7a165f64442ad8f326d86c22246?pvs=4) + +# How to Use (YouTube) +
+ +[![](https://img.youtube.com/vi/rUTad037n8Q/0.jpg)](https://www.youtube.com/watch?v=rUTad037n8Q) + +
+ +## Author +- [みしゃ(misyaguzi)](https://github.com/misyaguziya) (Main Development) +- [しいな(Shiina_12siy)](https://twitter.com/Shiina_12siy) (UI/UX, UI multilingual support) +- [レラ](https://github.com/soumt-r) (Technical Support) +- [どね](https://twitter.com/done_vrc) (Logo Design) + +## Thanks to our contributors + + + + +--- + +VRCT is not endorsed by VRChat and does not reflect the views or opinions of VRChat or anyone officially involved in producing or managing VRChat properties. VRChat and all associated properties are trademarks or registered trademarks of VRChat Inc. VRChat © VRChat Inc. \ No newline at end of file diff --git a/README.ja.md b/docs/readmes/README.ja.md similarity index 70% rename from README.ja.md rename to docs/readmes/README.ja.md index d89c95a6..e760bd7f 100644 --- a/README.ja.md +++ b/docs/readmes/README.ja.md @@ -1,9 +1,9 @@
- - - VRCT Logo + + + VRCT Logo
@@ -21,44 +21,44 @@ Become a VRCT Supporter on: - - - PIXIV FANBOX + + + PIXIV FANBOX    - - - Patreon + + + Patreon    - Ko-fi + Ko-fi   
- - - Supporter Section Border + + + Supporter Section Border

-| [English](./README.md) | **日本語** | [한국어](./README.ko.md) | [繁體中文](./README.zh-Hant.md) | +| [English](./README.en.md) | **日本語** | [한국어](./README.ko.md) | [繁體中文](./README.zh-Hant.md) |

VRCTは翻訳や文字起こしでVRChatの会話をサポートするソフトウェアです。

-![](docs/main_window.png) +![](../img/main_window.png)
diff --git a/README.ko.md b/docs/readmes/README.ko.md similarity index 70% rename from README.ko.md rename to docs/readmes/README.ko.md index bb260ba0..08498602 100644 --- a/README.ko.md +++ b/docs/readmes/README.ko.md @@ -1,9 +1,9 @@
- - - VRCT Logo + + + VRCT Logo
@@ -21,44 +21,44 @@ Become a VRCT Supporter on: - - - PIXIV FANBOX + + + PIXIV FANBOX    - - - Patreon + + + Patreon    - Ko-fi + Ko-fi   
- - - Supporter Section Border + + + Supporter Section Border

-| [English](./README.md) | [日本語](./README.ja.md) | **한국어** | [繁體中文](./README.zh-Hant.md) | +| [English](./README.en.md) | [日本語](./README.ja.md) | **한국어** | [繁體中文](./README.zh-Hant.md) |

VRCT는 음성인식 및 번역 기능을 통해 VRChat의 대화를 지원하는 소프트웨어입니다.

-![](docs/main_window.png) +![](../img/main_window.png)
diff --git a/README.zh-Hant.md b/docs/readmes/README.zh-Hant.md similarity index 67% rename from README.zh-Hant.md rename to docs/readmes/README.zh-Hant.md index e9535ceb..a4dae847 100644 --- a/README.zh-Hant.md +++ b/docs/readmes/README.zh-Hant.md @@ -1,9 +1,9 @@
- - - VRCT Logo + + + VRCT Logo
@@ -21,44 +21,44 @@ Become a VRCT Supporter on: - - - PIXIV FANBOX + + + PIXIV FANBOX    - - - Patreon + + + Patreon    - Ko-fi + Ko-fi   
- - - Supporter Section Border + + + Supporter Section Border

-| [English](./README.md) | [日本語](./README.ja.md) | [한국어](./README.ko.md) | **繁體中文** | +| [English](./README.en.md) | [日本語](./README.ja.md) | [한국어](./README.ko.md) | **繁體中文** |

VRCT 是一個支援 VRChat 對話翻譯和紀錄的軟體。

-![](docs/main_window.png) +![](../img/main_window.png)
From 1b7e315c2b874123a7d7d2e945a3366f8759e7c1 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 23 Apr 2025 13:51:34 +0900 Subject: [PATCH 42/68] [Update] Add Symbolic link. --- README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 120000 README.md diff --git a/README.md b/README.md deleted file mode 100644 index ab3e36da..00000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -docs/readmes/README.en.md \ No newline at end of file diff --git a/README.md b/README.md new file mode 120000 index 00000000..ab3e36da --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +docs/readmes/README.en.md \ No newline at end of file From 1f4c4e76401010c42f3dfc96910fe29b15570824 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 23 Apr 2025 14:34:31 +0900 Subject: [PATCH 43/68] [Update] Adjust file paths. --- docs/readmes/README.en.md | 30 +++++++++++++++--------------- docs/readmes/README.ja.md | 30 +++++++++++++++--------------- docs/readmes/README.ko.md | 30 +++++++++++++++--------------- docs/readmes/README.zh-Hant.md | 30 +++++++++++++++--------------- 4 files changed, 60 insertions(+), 60 deletions(-) diff --git a/docs/readmes/README.en.md b/docs/readmes/README.en.md index b99a1da8..5edf2acc 100644 --- a/docs/readmes/README.en.md +++ b/docs/readmes/README.en.md @@ -1,9 +1,9 @@
- - - VRCT Logo + + + VRCT Logo
@@ -21,44 +21,44 @@ Become a VRCT Supporter on: - - - PIXIV FANBOX + + + PIXIV FANBOX    - - - Patreon + + + Patreon    - Ko-fi + Ko-fi   
- - - Supporter Section Border + + + Supporter Section Border

-| **English** | [日本語](./README.ja.md) | [한국어](./README.ko.md) | [繁體中文](./README.zh-Hant.md) | +| **English** | [日本語](docs/readmes/README.ja.md) | [한국어](docs/readmes/README.ko.md) | [繁體中文](docs/readmes/README.zh-Hant.md) |

VRCT is software that supports VRChat conversations with translation and transcription.

-![](../img/main_window.png) +![](docs/img/main_window.png)
diff --git a/docs/readmes/README.ja.md b/docs/readmes/README.ja.md index e760bd7f..1bc6c764 100644 --- a/docs/readmes/README.ja.md +++ b/docs/readmes/README.ja.md @@ -1,9 +1,9 @@
- - - VRCT Logo + + + VRCT Logo
@@ -21,44 +21,44 @@ Become a VRCT Supporter on: - - - PIXIV FANBOX + + + PIXIV FANBOX    - - - Patreon + + + Patreon    - Ko-fi + Ko-fi   
- - - Supporter Section Border + + + Supporter Section Border

-| [English](./README.en.md) | **日本語** | [한국어](./README.ko.md) | [繁體中文](./README.zh-Hant.md) | +| [English](docs/readmes/README.en.md) | **日本語** | [한국어](docs/readmes/README.ko.md) | [繁體中文](docs/readmes/README.zh-Hant.md) |

VRCTは翻訳や文字起こしでVRChatの会話をサポートするソフトウェアです。

-![](../img/main_window.png) +![](docs/img/main_window.png)
diff --git a/docs/readmes/README.ko.md b/docs/readmes/README.ko.md index 08498602..21406969 100644 --- a/docs/readmes/README.ko.md +++ b/docs/readmes/README.ko.md @@ -1,9 +1,9 @@
- - - VRCT Logo + + + VRCT Logo
@@ -21,44 +21,44 @@ Become a VRCT Supporter on: - - - PIXIV FANBOX + + + PIXIV FANBOX    - - - Patreon + + + Patreon    - Ko-fi + Ko-fi   
- - - Supporter Section Border + + + Supporter Section Border

-| [English](./README.en.md) | [日本語](./README.ja.md) | **한국어** | [繁體中文](./README.zh-Hant.md) | +| [English](docs/readmes/README.en.md) | [日本語](docs/readmes/README.ja.md) | **한국어** | [繁體中文](docs/readmes/README.zh-Hant.md) |

VRCT는 음성인식 및 번역 기능을 통해 VRChat의 대화를 지원하는 소프트웨어입니다.

-![](../img/main_window.png) +![](docs/img/main_window.png)
diff --git a/docs/readmes/README.zh-Hant.md b/docs/readmes/README.zh-Hant.md index a4dae847..d75ba4f2 100644 --- a/docs/readmes/README.zh-Hant.md +++ b/docs/readmes/README.zh-Hant.md @@ -1,9 +1,9 @@
- - - VRCT Logo + + + VRCT Logo
@@ -21,44 +21,44 @@ Become a VRCT Supporter on: - - - PIXIV FANBOX + + + PIXIV FANBOX    - - - Patreon + + + Patreon    - Ko-fi + Ko-fi   
- - - Supporter Section Border + + + Supporter Section Border

-| [English](./README.en.md) | [日本語](./README.ja.md) | [한국어](./README.ko.md) | **繁體中文** | +| [English](docs/readmes/README.en.md) | [日本語](docs/readmes/README.ja.md) | [한국어](docs/readmes/README.ko.md) | **繁體中文** |

VRCT 是一個支援 VRChat 對話翻譯和紀錄的軟體。

-![](../img/main_window.png) +![](docs/img/main_window.png)
From 3e010376f4f9ba511fc8859774b9210cec9b3b49 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 24 Apr 2025 01:48:15 +0900 Subject: [PATCH 44/68] [bugfix] Change the links relative path to absolute path that is from root dir. --- docs/readmes/README.en.md | 30 +++++++++++++++--------------- docs/readmes/README.ja.md | 30 +++++++++++++++--------------- docs/readmes/README.ko.md | 30 +++++++++++++++--------------- docs/readmes/README.zh-Hant.md | 30 +++++++++++++++--------------- 4 files changed, 60 insertions(+), 60 deletions(-) diff --git a/docs/readmes/README.en.md b/docs/readmes/README.en.md index 5edf2acc..f3f35a16 100644 --- a/docs/readmes/README.en.md +++ b/docs/readmes/README.en.md @@ -1,9 +1,9 @@
- - - VRCT Logo + + + VRCT Logo
@@ -21,44 +21,44 @@ Become a VRCT Supporter on: - - - PIXIV FANBOX + + + PIXIV FANBOX    - - - Patreon + + + Patreon    - Ko-fi + Ko-fi   
- - - Supporter Section Border + + + Supporter Section Border

-| **English** | [日本語](docs/readmes/README.ja.md) | [한국어](docs/readmes/README.ko.md) | [繁體中文](docs/readmes/README.zh-Hant.md) | +| **English** | [日本語](/docs/readmes/README.ja.md) | [한국어](/docs/readmes/README.ko.md) | [繁體中文](/docs/readmes/README.zh-Hant.md) |

VRCT is software that supports VRChat conversations with translation and transcription.

-![](docs/img/main_window.png) +![](/docs/img/main_window.png)
diff --git a/docs/readmes/README.ja.md b/docs/readmes/README.ja.md index 1bc6c764..86efe858 100644 --- a/docs/readmes/README.ja.md +++ b/docs/readmes/README.ja.md @@ -1,9 +1,9 @@
- - - VRCT Logo + + + VRCT Logo
@@ -21,44 +21,44 @@ Become a VRCT Supporter on: - - - PIXIV FANBOX + + + PIXIV FANBOX    - - - Patreon + + + Patreon    - Ko-fi + Ko-fi   
- - - Supporter Section Border + + + Supporter Section Border

-| [English](docs/readmes/README.en.md) | **日本語** | [한국어](docs/readmes/README.ko.md) | [繁體中文](docs/readmes/README.zh-Hant.md) | +| [English](/docs/readmes/README.en.md) | **日本語** | [한국어](/docs/readmes/README.ko.md) | [繁體中文](/docs/readmes/README.zh-Hant.md) |

VRCTは翻訳や文字起こしでVRChatの会話をサポートするソフトウェアです。

-![](docs/img/main_window.png) +![](/docs/img/main_window.png)
diff --git a/docs/readmes/README.ko.md b/docs/readmes/README.ko.md index 21406969..d09e9d36 100644 --- a/docs/readmes/README.ko.md +++ b/docs/readmes/README.ko.md @@ -1,9 +1,9 @@
- - - VRCT Logo + + + VRCT Logo
@@ -21,44 +21,44 @@ Become a VRCT Supporter on: - - - PIXIV FANBOX + + + PIXIV FANBOX    - - - Patreon + + + Patreon    - Ko-fi + Ko-fi   
- - - Supporter Section Border + + + Supporter Section Border

-| [English](docs/readmes/README.en.md) | [日本語](docs/readmes/README.ja.md) | **한국어** | [繁體中文](docs/readmes/README.zh-Hant.md) | +| [English](/docs/readmes/README.en.md) | [日本語](/docs/readmes/README.ja.md) | **한국어** | [繁體中文](/docs/readmes/README.zh-Hant.md) |

VRCT는 음성인식 및 번역 기능을 통해 VRChat의 대화를 지원하는 소프트웨어입니다.

-![](docs/img/main_window.png) +![](/docs/img/main_window.png)
diff --git a/docs/readmes/README.zh-Hant.md b/docs/readmes/README.zh-Hant.md index d75ba4f2..05a8fece 100644 --- a/docs/readmes/README.zh-Hant.md +++ b/docs/readmes/README.zh-Hant.md @@ -1,9 +1,9 @@
- - - VRCT Logo + + + VRCT Logo
@@ -21,44 +21,44 @@ Become a VRCT Supporter on: - - - PIXIV FANBOX + + + PIXIV FANBOX    - - - Patreon + + + Patreon    - Ko-fi + Ko-fi   
- - - Supporter Section Border + + + Supporter Section Border

-| [English](docs/readmes/README.en.md) | [日本語](docs/readmes/README.ja.md) | [한국어](docs/readmes/README.ko.md) | **繁體中文** | +| [English](/docs/readmes/README.en.md) | [日本語](/docs/readmes/README.ja.md) | [한국어](/docs/readmes/README.ko.md) | **繁體中文** |

VRCT 是一個支援 VRChat 對話翻譯和紀錄的軟體。

-![](docs/img/main_window.png) +![](/docs/img/main_window.png)
From b9d9edb7c352010eb10e2d92c3ddf082e523acea Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 24 Apr 2025 21:48:05 +0900 Subject: [PATCH 45/68] [Update] Add the safety that is not show on UI if the plugin, downloaded, is not supported. --- .../plugins_controllers/MergePluginsController.jsx | 10 ++++++---- src-ui/app/main_page/main_section/MainSection.jsx | 7 ++++++- .../main_page/main_section/MainSection.module.scss | 1 + src-ui/logics/configs/plugins/usePlugins.js | 13 +++++++++++++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx index 21a892a3..e8175c43 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx @@ -7,6 +7,7 @@ export const MergePluginsController = () => { const { currentLoadedPlugins, updatePluginsData, + updateTargetPluginData, currentPluginsData, currentFetchedPluginsInfo, currentSavedPluginsStatus, @@ -148,14 +149,15 @@ export const MergePluginsController = () => { ) { if (!downloadingRef.current.has(plugin.plugin_id)) { downloadingRef.current.add(plugin.plugin_id); + const target_plugin_id = plugin.plugin_id; downloadAndExtractPlugin(plugin) .then(() => { - console.log(`Plugin ${plugin.plugin_id} updated successfully`); - downloadingRef.current.delete(plugin.plugin_id); + console.log(`Plugin ${target_plugin_id} updated successfully`); + downloadingRef.current.delete(target_plugin_id); }) .catch((error) => { - console.error(`Plugin ${plugin.plugin_id} update failed`, error); - downloadingRef.current.delete(plugin.plugin_id); + console.error(`Plugin ${target_plugin_id} update failed`, error); + downloadingRef.current.delete(target_plugin_id); }); } } diff --git a/src-ui/app/main_page/main_section/MainSection.jsx b/src-ui/app/main_page/main_section/MainSection.jsx index e89071a8..b0ffb77c 100644 --- a/src-ui/app/main_page/main_section/MainSection.jsx +++ b/src-ui/app/main_page/main_section/MainSection.jsx @@ -16,7 +16,12 @@ import { usePlugins } from "@logics_configs"; export const MainSection = () => { const { currentPluginsData } = usePlugins(); - const render_plugins = currentPluginsData.data.filter((plugin) => (plugin.is_downloaded && plugin.is_enabled && plugin.downloaded_plugin_info.location === "main_section")); + const render_plugins = currentPluginsData.data.filter((plugin) => ( + plugin.is_downloaded && + plugin.is_enabled && + plugin.downloaded_plugin_info.is_plugin_supported && + plugin.downloaded_plugin_info.location === "main_section" + )); return (
diff --git a/src-ui/app/main_page/main_section/MainSection.module.scss b/src-ui/app/main_page/main_section/MainSection.module.scss index 0f592a3a..c132e869 100644 --- a/src-ui/app/main_page/main_section/MainSection.module.scss +++ b/src-ui/app/main_page/main_section/MainSection.module.scss @@ -5,6 +5,7 @@ display: flex; flex-direction: column; // justify-content: space-between; + overflow-y: auto; } .language_selector_container { diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 2fd911b5..8b9dd142 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -373,6 +373,17 @@ export const usePlugins = () => { return currentPluginsData.data.filter(plugin => plugin.is_enabled); } + const updateTargetPluginData = (target_plugin_id, attribute, value) => { + updatePluginsData(prev => { + prev.data.forEach(plugin => { + if (plugin.plugin_id === target_plugin_id) { + plugin[attribute] = value; + } + }); + return prev.data; + }); + } + return { @@ -391,6 +402,8 @@ export const usePlugins = () => { currentPluginsData, updatePluginsData, + updateTargetPluginData, + currentFetchedPluginsInfo, updateFetchedPluginsInfo, From 81a422b07d0bffd83427b099ca4b1c1f487db285 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 24 Apr 2025 23:44:11 +0900 Subject: [PATCH 46/68] [Update] Plugins: Add notifications. --- locales/en.yml | 16 +++++++++++++++- .../MergePluginsController.jsx | 10 +++++++++- .../setting_box/plugins/Plugins.jsx | 9 +++++++++ src-ui/logics/configs/plugins/usePlugins.js | 10 ++++++++-- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index a52ac107..a72200cb 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -243,4 +243,18 @@ config_page: open_config_filepath: label: "Open Config File" switch_compute_device: - label: "Switch VRCT To CPU/GPU Version" \ No newline at end of file + label: "Switch VRCT To CPU/GPU Version" + +plugin_notifications: + downloading: Downloading the plugin. + downloaded_success: Downloaded successfully. + downloaded_error: Download failed. + + updating: Updating the plugin. + updated_success: Updated successfully. + updated_error: Update failed. + + disabled_out_of_support: THe plugin has been disabled. It's not supported on this VRCT version. + + is_enabled: The plugin has enabled. + is_disabled: The plugin has disabled. \ No newline at end of file diff --git a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx index e8175c43..255ede1c 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx @@ -1,13 +1,15 @@ import { useEffect, useRef } from "react"; +import { useTranslation } from "react-i18next"; import { store } from "@store"; import { usePlugins } from "@logics_configs"; import { useSoftwareVersion } from "@logics_common"; +import { useNotificationStatus } from "@logics_common"; export const MergePluginsController = () => { + const { t } = useTranslation(); const { currentLoadedPlugins, updatePluginsData, - updateTargetPluginData, currentPluginsData, currentFetchedPluginsInfo, currentSavedPluginsStatus, @@ -15,6 +17,7 @@ export const MergePluginsController = () => { setTargetSavedPluginsStatus_Init, } = usePlugins(); const { checkVrctVerCompatibility } = useSoftwareVersion(); + const { showNotification_Success, showNotification_Error } = useNotificationStatus(); // downloaded, fetched, saved の各情報をまとめてマージ useEffect(() => { @@ -148,16 +151,19 @@ export const MergePluginsController = () => { plugin.is_latest_version_available ) { if (!downloadingRef.current.has(plugin.plugin_id)) { + showNotification_Success(t("plugin_notifications.updating")); downloadingRef.current.add(plugin.plugin_id); const target_plugin_id = plugin.plugin_id; downloadAndExtractPlugin(plugin) .then(() => { console.log(`Plugin ${target_plugin_id} updated successfully`); downloadingRef.current.delete(target_plugin_id); + showNotification_Success(t("plugin_notifications.updated_success")); }) .catch((error) => { console.error(`Plugin ${target_plugin_id} update failed`, error); downloadingRef.current.delete(target_plugin_id); + showNotification_Error(t("plugin_notifications.updated_error")); }); } } @@ -176,6 +182,7 @@ export const MergePluginsController = () => { plugin.latest_plugin_info && !plugin.latest_plugin_info?.is_plugin_supported ) { + showNotification_Error(t("plugin_notifications.disabled_out_of_support")); plugin.is_enabled = false; setTargetSavedPluginsStatus_Init(plugin.plugin_id, false); } @@ -184,6 +191,7 @@ export const MergePluginsController = () => { !plugin.downloaded_plugin_info.is_plugin_supported && plugin.is_outdated ) { + showNotification_Error(t("plugin_notifications.disabled_out_of_support")); plugin.is_enabled = false; setTargetSavedPluginsStatus_Init(plugin.plugin_id, false); } diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index 5f6df29e..aadaf64f 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -1,7 +1,9 @@ import { useEffect, useRef } from "react"; +import { useTranslation } from "react-i18next"; import { usePlugins } from "@logics_configs"; import styles from "./Plugins.module.scss"; import { PluginsControlComponent } from "../_components/plugins_control_component/PluginsControlComponent"; +import { useNotificationStatus } from "@logics_common"; export const Plugins = () => { const { @@ -24,6 +26,7 @@ export const Plugins = () => { }; const PluginDownloadContainer = () => { + const { t } = useTranslation(); const { downloadAndExtractPlugin, currentPluginsData, @@ -31,16 +34,22 @@ const PluginDownloadContainer = () => { toggleSavedPluginsStatus, handlePendingPlugin, } = usePlugins(); + const { showNotification_Success, showNotification_Error } = useNotificationStatus(); // ダウンロード開始時の状態更新処理 const downloadStartFunction = async (target_plugin_id) => { handlePendingPlugin(target_plugin_id, true); + showNotification_Success(t("plugin_notifications.downloading")); const target_plugin_info = currentPluginsData.data.find( (d) => d.plugin_id === target_plugin_id ); downloadAndExtractPlugin(target_plugin_info).then(() => { handlePendingPlugin(target_plugin_id, false); + showNotification_Success(t("plugin_notifications.downloaded_success")); + }).catch(error => { + console.error(error); + showNotification_Error(t("plugin_notifications.downloaded_error")); }); }; diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 8b9dd142..b3b1e561 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -1,4 +1,5 @@ import { invoke } from "@tauri-apps/api/tauri"; +import { useTranslation } from "react-i18next"; import { IS_PLUGIN_PATH_DEV_MODE, getPluginsList } from "@ui_configs"; import { store, @@ -25,17 +26,18 @@ dev_plugins.forEach(async ({entry_path}) => { import JSZip from "jszip"; -import { useFetch, useSoftwareVersion } from "@logics_common"; +import { useFetch, useSoftwareVersion, useNotificationStatus } from "@logics_common"; import * as logics_configs from "@logics_configs"; import * as logics_main from "@logics_main"; import * as logics_common from "@logics_common"; - // PLUGIN_LIST_URL は中央リポジトリにある、各プラグインの plugin_info.json への URL の配列を保持する JSON の URL const PLUGIN_LIST_URL = getPluginsList(); export const usePlugins = () => { + const { t } = useTranslation(); + const { showNotification_Success, showNotification_Error } = useNotificationStatus(); const { asyncStdoutToPython } = useStdoutToPython(); const { currentFetchedPluginsInfo, updateFetchedPluginsInfo, pendingFetchedPluginsInfo } = useStore_FetchedPluginsInfo(); @@ -308,6 +310,9 @@ export const usePlugins = () => { new_value = currentSavedPluginsStatus.data.map((d) => { if (d.plugin_id === target_plugin_id) { d.is_enabled = !d.is_enabled; + (d.is_enabled) + ? showNotification_Success(t("plugin_notifications.is_enabled")) + : showNotification_Success(t("plugin_notifications.is_disabled")); } return d; }); @@ -317,6 +322,7 @@ export const usePlugins = () => { plugin_id: target_plugin_id, is_enabled: true, }); + showNotification_Success(t("plugin_notifications.is_enabled")) } // 「currentPluginsData.data」でis_downloadedがtrueのものだけ残す From 249c686442220e86c5a6e4505cbf25600718ca82 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Fri, 25 Apr 2025 18:51:16 +0900 Subject: [PATCH 47/68] [Update] Plugins: Add error handlings when network is not connected and failed to fetch plugins info. Also add pending ui(TMP) while fetching. --- .../setting_section/setting_box/plugins/Plugins.jsx | 7 +++++++ .../plugins_compatibility_list/PluginCompatibilityList.jsx | 7 +++++++ src-ui/logics/configs/plugins/usePlugins.js | 3 ++- src-ui/logics/useReceiveRoutes.js | 7 ++++++- src-ui/store.js | 2 +- 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index aadaf64f..b42ea31b 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -33,6 +33,7 @@ const PluginDownloadContainer = () => { currentSavedPluginsStatus, toggleSavedPluginsStatus, handlePendingPlugin, + currentFetchedPluginsInfo, } = usePlugins(); const { showNotification_Success, showNotification_Error } = useNotificationStatus(); @@ -65,8 +66,14 @@ const PluginDownloadContainer = () => { a.plugin_id.localeCompare(b.plugin_id) ); + // Duplicate + const is_failed_to_fetch = currentFetchedPluginsInfo.state === "error"; + const is_fetching = currentFetchedPluginsInfo.state === "pending"; + return (
+ {is_failed_to_fetch &&

Failed to fetch plugins data

} + {is_fetching &&

Fetching plugins data...

} {sorted_plugins_data.map((plugin) => (

diff --git a/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx index dd0133b1..e23c320c 100644 --- a/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx +++ b/src-ui/app/modal_controller/update_modal/plugins_compatibility_list/PluginCompatibilityList.jsx @@ -9,6 +9,7 @@ export const PluginCompatibilityList = () => { const { enabledPluginsList, asyncFetchPluginsInfo, + currentFetchedPluginsInfo, } = usePlugins(); useEffect(() => { @@ -35,9 +36,15 @@ export const PluginCompatibilityList = () => { if (!is_any_incompatible_plugin && !is_any_compatible_plugin) return null; // This is just for safety. + // Duplicate + const is_failed_to_fetch = currentFetchedPluginsInfo.state === "error"; + const is_fetching = currentFetchedPluginsInfo.state === "pending"; + return (

使用中プラグインの互換性チェック

+ {is_failed_to_fetch &&

Failed to fetch plugins data

} + {is_fetching &&

Fetching plugins data...

}
{incompatible_plugins_list.map(plugin => { const target_data = plugin.downloaded_plugin_info; diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index b3b1e561..309c012c 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -40,7 +40,7 @@ export const usePlugins = () => { const { showNotification_Success, showNotification_Error } = useNotificationStatus(); const { asyncStdoutToPython } = useStdoutToPython(); - const { currentFetchedPluginsInfo, updateFetchedPluginsInfo, pendingFetchedPluginsInfo } = useStore_FetchedPluginsInfo(); + const { currentFetchedPluginsInfo, updateFetchedPluginsInfo, pendingFetchedPluginsInfo, errorFetchedPluginsInfo } = useStore_FetchedPluginsInfo(); const { currentLoadedPlugins, updateLoadedPlugins, pendingLoadedPlugins } = useStore_LoadedPlugins(); const { currentSavedPluginsStatus, updateSavedPluginsStatus, pendingSavedPluginsStatus } = useStore_SavedPluginsStatus(); @@ -252,6 +252,7 @@ export const usePlugins = () => { updateFetchedPluginsInfo(updated_list); } catch (error) { console.error("Error fetching plugin info list: ", error); + errorFetchedPluginsInfo(); } store.is_initialized_fetched_plugin_info = true; diff --git a/src-ui/logics/useReceiveRoutes.js b/src-ui/logics/useReceiveRoutes.js index c015d43e..4a4f944c 100644 --- a/src-ui/logics/useReceiveRoutes.js +++ b/src-ui/logics/useReceiveRoutes.js @@ -206,7 +206,12 @@ export const useReceiveRoutes = () => { "/set/data/main_window_geometry": () => {}, "/run/open_filepath_logs": () => console.log("Opened Directory, Message Logs"), "/run/open_filepath_config_file": () => console.log("Opened Directory, Config File"), - "/run/software_update_info": updateLatestSoftwareVersionInfo, + "/run/software_update_info": (payload) => { + updateLatestSoftwareVersionInfo(prev => ({ + is_update_available: payload.is_update_available, + new_version: payload.new_version || prev.data.new_version, + })); + }, "/run/connected_network": handleNetworkConnection, // Main Page diff --git a/src-ui/store.js b/src-ui/store.js index e648f946..d1712f1c 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -119,7 +119,7 @@ export const { atomInstance: Atom_MainFunctionsStateMemory, useHook: useStore_Ma export const { atomInstance: Atom_OpenedQuickSetting, useHook: useStore_OpenedQuickSetting } = createAtomWithHook("", "OpenedQuickSetting"); export const { atomInstance: Atom_LatestSoftwareVersionInfo, useHook: useStore_LatestSoftwareVersionInfo } = createAtomWithHook({ is_update_available: false, - new_version: "", + new_version: "0.0.0", }, "LatestSoftwareVersionInfo"); export const { atomInstance: Atom_InitProgress, useHook: useStore_InitProgress } = createAtomWithHook(0, "InitProgress"); export const { atomInstance: Atom_IsBreakPoint, useHook: useStore_IsBreakPoint } = createAtomWithHook(false, "IsBreakPoint"); From 819783ea0909addaaa9e7d955564ac3497262654 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sat, 26 Apr 2025 01:55:28 +0900 Subject: [PATCH 48/68] [Update] Plugins: Add localization system. --- src-ui/app/_app_controllers/PluginsController.jsx | 2 ++ src-ui/logics/configs/plugins/usePlugins.js | 4 +++- src-ui/plugins/dev_plugin_subtitles/index.jsx | 7 +++++-- src-ui/plugins/dev_plugin_subtitles/locales/en.yml | 2 ++ .../dev_plugin_subtitles/locales/initI18n.js | 14 ++++++++++++++ src-ui/plugins/dev_plugin_subtitles/locales/ja.yml | 2 ++ .../locales/usePluginTranslation.jsx | 7 +++++++ .../plugins/dev_plugin_subtitles/plugin_configs.js | 2 ++ .../plugins/dev_plugin_subtitles/plugin_info.json | 6 +++--- .../SubtitleSystemContainer.jsx | 6 +++++- 10 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 src-ui/plugins/dev_plugin_subtitles/locales/en.yml create mode 100644 src-ui/plugins/dev_plugin_subtitles/locales/initI18n.js create mode 100644 src-ui/plugins/dev_plugin_subtitles/locales/ja.yml create mode 100644 src-ui/plugins/dev_plugin_subtitles/locales/usePluginTranslation.jsx diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx index 0abeb5cb..9980f080 100644 --- a/src-ui/app/_app_controllers/PluginsController.jsx +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -1,9 +1,11 @@ import React from "react"; import clsx from "clsx"; +import * as reactI18next from "react-i18next"; if (typeof window !== "undefined") { window.React = React; window.clsx = clsx; + window.reactI18next = reactI18next; } import { LoadPluginsController } from "./plugins_controllers/LoadPluginsController"; diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 309c012c..7e4e3be9 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -50,6 +50,7 @@ export const usePlugins = () => { const { asyncTauriFetchGithub } = useFetch(); + const { i18n } = useTranslation(); const generatePluginContext = (downloaded_plugin_info) => { const plugin_context = { @@ -69,7 +70,8 @@ export const usePlugins = () => { }, createAtomWithHook: (...args) => createAtomWithHook(...args), - logics: { ...logics_common, ...logics_configs, ...logics_main } + logics: { ...logics_common, ...logics_configs, ...logics_main }, + i18n: i18n, }; return plugin_context; } diff --git a/src-ui/plugins/dev_plugin_subtitles/index.jsx b/src-ui/plugins/dev_plugin_subtitles/index.jsx index 1b8942d4..fc496275 100644 --- a/src-ui/plugins/dev_plugin_subtitles/index.jsx +++ b/src-ui/plugins/dev_plugin_subtitles/index.jsx @@ -1,10 +1,13 @@ import { initStore, StoreContext } from "@plugin_store"; +import { initI18n } from "@initI18n"; import { SubtitleSystemContainer } from "./subtitle_system_container/SubtitleSystemContainer"; import { SubtitlesController } from "./subtitle_system_container/_controllers/SubtitlesController.jsx"; export const init = (plugin_context) => { - initStore(plugin_context.createAtomWithHook); - const { logics } = plugin_context; + const { createAtomWithHook, i18n, logics } = plugin_context; + + initStore(createAtomWithHook); + initI18n(i18n); const EntryComponents = () => { return ( diff --git a/src-ui/plugins/dev_plugin_subtitles/locales/en.yml b/src-ui/plugins/dev_plugin_subtitles/locales/en.yml new file mode 100644 index 00000000..6a5ae6e1 --- /dev/null +++ b/src-ui/plugins/dev_plugin_subtitles/locales/en.yml @@ -0,0 +1,2 @@ +main_page: + title: "VRCT Subtitles" \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/locales/initI18n.js b/src-ui/plugins/dev_plugin_subtitles/locales/initI18n.js new file mode 100644 index 00000000..8154ded5 --- /dev/null +++ b/src-ui/plugins/dev_plugin_subtitles/locales/initI18n.js @@ -0,0 +1,14 @@ +import en from "./en.yml"; +import ja from "./ja.yml"; +import plugin_info from "../plugin_info.json"; + +export const initI18n = (i18n) => { + const ns = plugin_info.plugin_id; + // parse once + const en_res = en; + const ja_res = ja; + + // addResourceBundle will merge into i18n’s store + i18n.addResourceBundle("en", ns, en_res, /* deep = */ true, /* overwrite = */ true); + i18n.addResourceBundle("ja", ns, ja_res, /* deep = */ true, /* overwrite = */ true); +}; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/locales/ja.yml b/src-ui/plugins/dev_plugin_subtitles/locales/ja.yml new file mode 100644 index 00000000..3862be1e --- /dev/null +++ b/src-ui/plugins/dev_plugin_subtitles/locales/ja.yml @@ -0,0 +1,2 @@ +main_page: + title: "字幕プレイヤー" \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/locales/usePluginTranslation.jsx b/src-ui/plugins/dev_plugin_subtitles/locales/usePluginTranslation.jsx new file mode 100644 index 00000000..2e9e8d79 --- /dev/null +++ b/src-ui/plugins/dev_plugin_subtitles/locales/usePluginTranslation.jsx @@ -0,0 +1,7 @@ +import { useTranslation } from "react-i18next"; +import plugin_info from "../plugin_info.json"; + +export const usePluginTranslation = () => { + const ns = plugin_info.plugin_id; + return useTranslation(ns); +}; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/plugin_configs.js b/src-ui/plugins/dev_plugin_subtitles/plugin_configs.js index 0944d0b6..36f116d1 100644 --- a/src-ui/plugins/dev_plugin_subtitles/plugin_configs.js +++ b/src-ui/plugins/dev_plugin_subtitles/plugin_configs.js @@ -1,5 +1,7 @@ export const configs = { alias: { "@plugin_store": "store/store.js", + "@initI18n": "locales/initI18n.js", + "@usePluginTranslation": "locales/usePluginTranslation.jsx", } } \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/plugin_info.json b/src-ui/plugins/dev_plugin_subtitles/plugin_info.json index 3bb37720..c5e3023a 100644 --- a/src-ui/plugins/dev_plugin_subtitles/plugin_info.json +++ b/src-ui/plugins/dev_plugin_subtitles/plugin_info.json @@ -3,7 +3,7 @@ "plugin_id": "vrct_plugin_subtitles", "asset_name": "vrct_plugin_subtitles.zip", "location": "main_section", - "plugin_version": "0.0.0", - "min_supported_vrct_version": "3.0.4", - "max_supported_vrct_version": "3.0.6" + "plugin_version": "0.0.4", + "min_supported_vrct_version": "3.0.5", + "max_supported_vrct_version": "3.0.5" } \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx index 144aefb0..8a1874f9 100644 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx +++ b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx @@ -4,8 +4,10 @@ import { ModeSelectorContainer } from "./mode_selector_container/ModeSelectorCon import { PlayControlContainer } from "./play_control_container/PlayControlContainer"; import { CountdownContainer } from "./countdown_container/CountdownContainer"; import { SubtitlesListContainer } from "./subtitles_list_container/SubtitlesListContainer"; +import { usePluginTranslation } from "@usePluginTranslation"; export const SubtitleSystemContainer = () => { + const { t } = usePluginTranslation(); // const [srtContent, setSrtContent] = useState(""); // const [cues, setCues] = useState([]); // const [isPlaying, setIsPlaying] = useState(false); @@ -31,9 +33,11 @@ export const SubtitleSystemContainer = () => { // // カウントダウンタイマー専用の ref // const countdownIntervalRef = useRef(null); + console.log(t("main_page.title")); + return (
-

字幕プレイヤー

+

{t("main_page.title")}

From c665f1d3559c62d2f101eaabc3fa59e777bbc33e Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sat, 26 Apr 2025 04:13:18 +0900 Subject: [PATCH 49/68] [Update/bugfix] Plugins: Add localization system to plugins list(plugin_info.json). Fix the issue that the i18n didn't work in development environment. --- package-lock.json | 35 ++++++++- package.json | 2 +- .../setting_box/plugins/Plugins.jsx | 72 +++++++++++-------- .../dev_plugin_subtitles/locales/initI18n.js | 7 +- .../dev_plugin_subtitles/plugin_info.json | 15 +++- .../SubtitleSystemContainer.jsx | 2 - vite.config.js | 8 ++- 7 files changed, 98 insertions(+), 43 deletions(-) diff --git a/package-lock.json b/package-lock.json index 85dac096..308c262d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,6 @@ "i18next": "24.1.0", "jotai": "2.10.3", "js-base64": "3.7.7", - "js-yaml": "4.1.0", "jszip": "3.10.1", "react": "18.2.0", "react-dom": "18.2.0", @@ -30,10 +29,11 @@ "semver": "7.7.1" }, "devDependencies": { + "@rollup/plugin-yaml": "^4.1.2", "@tauri-apps/cli": "1.6.3", "npm-run-all": "4.1.5", "sass": "1.79.4", - "source-map": "^0.7.4", + "source-map": "0.7.4", "vite": "6.2.5", "vite-plugin-svgr": "4.3.0" } @@ -1221,6 +1221,28 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rollup/plugin-yaml": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-yaml/-/plugin-yaml-4.1.2.tgz", + "integrity": "sha512-RpupciIeZMUqhgFE97ba0s98mOFS7CWzN3EJNhJkqSv9XLlWYtwVdtE6cDw6ASOF/sZVFS7kRJXftaqM2Vakdw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "js-yaml": "^4.1.0", + "tosource": "^2.0.0-alpha.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/pluginutils": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.3.tgz", @@ -5353,6 +5375,15 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, + "node_modules/tosource": { + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/tosource/-/tosource-2.0.0-alpha.3.tgz", + "integrity": "sha512-KAB2lrSS48y91MzFPFuDg4hLbvDiyTjOVgaK7Erw+5AmZXNq4sFRVn8r6yxSLuNs15PaokrDRpS61ERY9uZOug==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", diff --git a/package.json b/package.json index de100d5d..f9948b13 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ "i18next": "24.1.0", "jotai": "2.10.3", "js-base64": "3.7.7", - "js-yaml": "4.1.0", "jszip": "3.10.1", "react": "18.2.0", "react-dom": "18.2.0", @@ -45,6 +44,7 @@ "semver": "7.7.1" }, "devDependencies": { + "@rollup/plugin-yaml": "^4.1.2", "@tauri-apps/cli": "1.6.3", "npm-run-all": "4.1.5", "sass": "1.79.4", diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx index b42ea31b..d0715698 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx @@ -26,7 +26,7 @@ export const Plugins = () => { }; const PluginDownloadContainer = () => { - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const { downloadAndExtractPlugin, currentPluginsData, @@ -74,35 +74,49 @@ const PluginDownloadContainer = () => {
{is_failed_to_fetch &&

Failed to fetch plugins data

} {is_fetching &&

Fetching plugins data...

} - {sorted_plugins_data.map((plugin) => ( -
-

- {plugin.is_downloaded - ? plugin.downloaded_plugin_info?.title - : plugin.latest_plugin_info?.title} -

-

{plugin.plugin_id}

- {plugin.is_error ? ( -

Error: {plugin.error_message}

- ) : ( -
-
-

- {plugin.is_downloaded - ? `現在のバージョン: ${plugin.downloaded_plugin_info?.plugin_version}` - : null} -

+ {sorted_plugins_data.map((plugin) => { + const target_info = plugin.is_downloaded + ? plugin.downloaded_plugin_info + : plugin.latest_plugin_info; + + const target_locale = target_info.locales && target_info.locales[i18n.language] + ? target_info.locales[i18n.language] + : { + title: target_info.title, + desc: target_info.desc || null, + }; + + return ( +
+

+ {target_locale.title} +

+

{plugin.plugin_id}

+

+ {target_locale.desc} +

+ {plugin.is_error ? ( +

Error: {plugin.error_message}

+ ) : ( +
+
+

+ {plugin.is_downloaded + ? `現在のバージョン: ${plugin.downloaded_plugin_info?.plugin_version}` + : null} +

+
+
- -
- )} -
- ))} + )} +
+ ); + })}
); }; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/locales/initI18n.js b/src-ui/plugins/dev_plugin_subtitles/locales/initI18n.js index 8154ded5..6e71957e 100644 --- a/src-ui/plugins/dev_plugin_subtitles/locales/initI18n.js +++ b/src-ui/plugins/dev_plugin_subtitles/locales/initI18n.js @@ -4,11 +4,8 @@ import plugin_info from "../plugin_info.json"; export const initI18n = (i18n) => { const ns = plugin_info.plugin_id; - // parse once - const en_res = en; - const ja_res = ja; // addResourceBundle will merge into i18n’s store - i18n.addResourceBundle("en", ns, en_res, /* deep = */ true, /* overwrite = */ true); - i18n.addResourceBundle("ja", ns, ja_res, /* deep = */ true, /* overwrite = */ true); + i18n.addResourceBundle("en", ns, en, /* deep = */ true, /* overwrite = */ true); + i18n.addResourceBundle("ja", ns, ja, /* deep = */ true, /* overwrite = */ true); }; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/plugin_info.json b/src-ui/plugins/dev_plugin_subtitles/plugin_info.json index c5e3023a..ec673298 100644 --- a/src-ui/plugins/dev_plugin_subtitles/plugin_info.json +++ b/src-ui/plugins/dev_plugin_subtitles/plugin_info.json @@ -1,9 +1,20 @@ { "title": "VRCT Subtitles", + "desc": "No description", "plugin_id": "vrct_plugin_subtitles", "asset_name": "vrct_plugin_subtitles.zip", "location": "main_section", - "plugin_version": "0.0.4", + "plugin_version": "0.0.5", "min_supported_vrct_version": "3.0.5", - "max_supported_vrct_version": "3.0.5" + "max_supported_vrct_version": "3.0.5", + "locales": { + "en": { + "title": "VRCT Subtitles", + "desc": "No description" + }, + "ja": { + "title": "VRCT 字幕表示機能", + "desc": "VRCTのオーバーレイ機能を使い、目の前に字幕としてテキストを表示する機能です。ワールドギミックの開始タイミングに合わせて字幕を設定し、同時に表示しているだけではあります。" + } + } } \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx index 8a1874f9..64349850 100644 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx +++ b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx @@ -33,8 +33,6 @@ export const SubtitleSystemContainer = () => { // // カウントダウンタイマー専用の ref // const countdownIntervalRef = useRef(null); - console.log(t("main_page.title")); - return (

{t("main_page.title")}

diff --git a/vite.config.js b/vite.config.js index b46e13f1..78a27f37 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,6 +1,7 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import svgr from "vite-plugin-svgr"; +import yaml from "@rollup/plugin-yaml"; import path from "path"; import { dev_plugins } from "./src-ui/plugins/plugins_index.js"; @@ -11,8 +12,11 @@ export default defineConfig(async () => { const plugin_aliases = await getPluginAliases(); return { - plugins: [react(), svgr()], - assetsInclude: ["**/*.yml"], + plugins: [ + yaml({ include: ["**/*.yml", "**/*.yaml"] }), + react(), + svgr(), + ], // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` // From 56936d0285d93018eb68fc2581dac4291619244f Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sat, 26 Apr 2025 19:41:28 +0900 Subject: [PATCH 50/68] [Update] Plugins -> AdvancedSettings. Move to AdvancedSettings because the plugin just only one is available and it's for specific user, actually. so put it to AdvancedSettings for now. --- locales/en.yml | 3 +- locales/ja.yml | 3 +- .../setting_box/SettingBox.jsx | 6 ++-- .../advanced_settings/AdvancedSettings.jsx | 29 ++++++++++++++----- .../AdvancedSettings.module.scss | 21 ++------------ .../plugins/Plugins.jsx | 2 +- .../plugins/Plugins.module.scss | 0 .../setting_section/setting_box/index.js | 2 +- .../sidebar_section/SidebarSection.jsx | 2 +- 9 files changed, 34 insertions(+), 34 deletions(-) rename src-ui/app/config_page/setting_section/setting_box/{ => advanced_settings}/plugins/Plugins.jsx (95%) rename src-ui/app/config_page/setting_section/setting_box/{ => advanced_settings}/plugins/Plugins.module.scss (100%) diff --git a/locales/en.yml b/locales/en.yml index a72200cb..cdefbfe5 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -244,6 +244,7 @@ config_page: label: "Open Config File" switch_compute_device: label: "Switch VRCT To CPU/GPU Version" + section_label_plugins: Plugins plugin_notifications: downloading: Downloading the plugin. @@ -254,7 +255,7 @@ plugin_notifications: updated_success: Updated successfully. updated_error: Update failed. - disabled_out_of_support: THe plugin has been disabled. It's not supported on this VRCT version. + disabled_out_of_support: The plugin has been disabled. It's not supported on this VRCT version. is_enabled: The plugin has enabled. is_disabled: The plugin has disabled. \ No newline at end of file diff --git a/locales/ja.yml b/locales/ja.yml index 188c5325..e4c40eb5 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -243,4 +243,5 @@ config_page: open_config_filepath: label: "設定ファイルを開く" switch_compute_device: - label: "VRCT CPU/GPUバージョンの切り替え" \ No newline at end of file + label: "VRCT CPU/GPUバージョンの切り替え" + section_label_plugins: プラグイン \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/SettingBox.jsx b/src-ui/app/config_page/setting_section/setting_box/SettingBox.jsx index b149da0d..9fc67c13 100644 --- a/src-ui/app/config_page/setting_section/setting_box/SettingBox.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/SettingBox.jsx @@ -9,7 +9,7 @@ import { AdvancedSettings, Vr, Hotkeys, - Plugins, + // Plugins, Supporters, AboutVrct, } from "@setting_box"; @@ -33,8 +33,8 @@ export const SettingBox = () => { return ; case "advanced_settings": return ; - case "plugins": - return ; + // case "plugins": + // return ; case "supporters": return ; case "about_vrct": diff --git a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/AdvancedSettings.jsx b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/AdvancedSettings.jsx index b96c7fa9..206ed7ca 100644 --- a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/AdvancedSettings.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/AdvancedSettings.jsx @@ -2,6 +2,8 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import styles from "./AdvancedSettings.module.scss"; +import { Plugins } from "./plugins/Plugins"; + import { useOpenFolder } from "@logics_common"; import { useOscIpAddress, @@ -10,22 +12,35 @@ import { import { ActionButtonContainer, - EntryContainer, EntryWithSaveButtonContainer, } from "../_templates/Templates"; +import { + SectionLabelComponent, +} from "../_components/"; + import OpenFolderSvg from "@images/open_folder.svg?react"; import HelpSvg from "@images/help.svg?react"; export const AdvancedSettings = () => { + const { t } = useTranslation(); + return ( - <> - - - - - +
+
+ + + + + +
+ +
+ + +
+
); }; diff --git a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/AdvancedSettings.module.scss b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/AdvancedSettings.module.scss index fa5eefb3..a49fed11 100644 --- a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/AdvancedSettings.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/AdvancedSettings.module.scss @@ -1,22 +1,5 @@ .container { display: flex; - width: 100%; - justify-content: space-between; - align-items: center; - padding: 2rem; - align-items: center; - gap: 2rem; - &.flex_column { - flex-direction: column; - } - border-bottom: solid 0.1rem var(--dark_800_color); -} - -.switch_section_container { - display: flex; - width: 100%; - justify-content: space-between; - align-items: center; - align-items: center; - gap: 2rem; + gap: 6.4rem; + flex-direction: column; } \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx similarity index 95% rename from src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx rename to src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx index d0715698..850e83dd 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx @@ -2,7 +2,7 @@ import { useEffect, useRef } from "react"; import { useTranslation } from "react-i18next"; import { usePlugins } from "@logics_configs"; import styles from "./Plugins.module.scss"; -import { PluginsControlComponent } from "../_components/plugins_control_component/PluginsControlComponent"; +import { PluginsControlComponent } from "../../_components/plugins_control_component/PluginsControlComponent"; import { useNotificationStatus } from "@logics_common"; export const Plugins = () => { diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss similarity index 100% rename from src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss rename to src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss diff --git a/src-ui/app/config_page/setting_section/setting_box/index.js b/src-ui/app/config_page/setting_section/setting_box/index.js index abb876ae..80cfca10 100644 --- a/src-ui/app/config_page/setting_section/setting_box/index.js +++ b/src-ui/app/config_page/setting_section/setting_box/index.js @@ -6,6 +6,6 @@ export { Others, VrcMicMuteSyncContainer } from "./others/Others"; export { AdvancedSettings } from "./advanced_settings/AdvancedSettings"; export { Vr } from "./vr/Vr"; export { Hotkeys } from "./hotkeys/Hotkeys"; -export { Plugins } from "./plugins/Plugins"; +// export { Plugins } from "./plugins/Plugins"; export { AboutVrct } from "./about_vrct/AboutVrct"; export { Supporters } from "./supporters/Supporters"; \ No newline at end of file diff --git a/src-ui/app/config_page/sidebar_section/SidebarSection.jsx b/src-ui/app/config_page/sidebar_section/SidebarSection.jsx index e1691ce9..f845a733 100644 --- a/src-ui/app/config_page/sidebar_section/SidebarSection.jsx +++ b/src-ui/app/config_page/sidebar_section/SidebarSection.jsx @@ -12,7 +12,7 @@ export const SidebarSection = () => { - + {/* */}
From e3b75f2cfb6af9b0f0e50bed90ecea7e9a839d68 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sun, 27 Apr 2025 02:15:31 +0900 Subject: [PATCH 51/68] [Update] Plugins: Adjust designs. Add localizations. --- locales/en.yml | 14 ++++- locales/ja.yml | 28 ++++++++- .../PluginsControlComponent.jsx | 63 +++++++++++++------ .../switch_box/SwitchBox.module.scss | 1 - .../advanced_settings/plugins/Plugins.jsx | 41 ++++++------ .../plugins/Plugins.module.scss | 39 ++++++++---- 6 files changed, 131 insertions(+), 55 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index cdefbfe5..f72907ac 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -244,7 +244,19 @@ config_page: label: "Open Config File" switch_compute_device: label: "Switch VRCT To CPU/GPU Version" - section_label_plugins: Plugins + section_label_plugins: Plugins # Exception, It'll be moved later. + + plugins: + downloaded_version: "Downloaded version: {{downloaded_version}}" + latest_version: "Latest version: {{latest_version}}" + available_after_updating: "Available after updating to the latest version" + unavailable_downloaded: "Currently unavailable due to incompatibility with the VRCT version in use" + no_latest_info: "Unable to retrieve the latest information" + using_latest_version: "Using the latest version" + available_latest_version: "Latest version available" + unavailable_latest_version: "Latest version currently unavailable" + available_in_latest_vrct_version: "Available in the latest VRCT version" + unavailable_not_downloaded: "Currently unavailable" plugin_notifications: downloading: Downloading the plugin. diff --git a/locales/ja.yml b/locales/ja.yml index e4c40eb5..3d109b8a 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -244,4 +244,30 @@ config_page: label: "設定ファイルを開く" switch_compute_device: label: "VRCT CPU/GPUバージョンの切り替え" - section_label_plugins: プラグイン \ No newline at end of file + section_label_plugins: プラグイン # Exception, It'll be moved later. + + plugins: + downloaded_version: "ダウンロード済バージョン: {{downloaded_version}}" + latest_version: "最新バージョン: {{latest_version}}" + available_after_updating: 最新版にアップデート後 利用可能 + unavailable_downloaded: 現在利用不可 使用中VRCTバージョンとの互換性なし + no_latest_info: 最新情報が取得できません + using_latest_version: 最新版を使用中 + available_latest_version: 最新版を利用可能 + unavailable_latest_version: 最新版は現在利用不可 + available_in_latest_vrct_version: VRCT最新版で利用可能 + unavailable_not_downloaded: 現在利用不可 + +plugin_notifications: + downloading: プラグインをダウンロード中。 + downloaded_success: プラグインのダウンロードが完了しました。 + downloaded_error: プラグインのダウンロードに失敗しました。 + + updating: プラグインをアップデート中。 + updated_success: プラグインのアップデートが完了しました。 + updated_error: プラグインのアップデートに失敗しました。 + + disabled_out_of_support: 現在のバージョンとの互換性がありません。プラグインを無効にしました。 + + is_enabled: プラグインを有効にしました。 + is_disabled: プラグインを無効にしました。 \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx index 9c33c90d..c7c6ace0 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/plugins_control_component/PluginsControlComponent.jsx @@ -2,6 +2,7 @@ import React from "react"; import { SwitchBox } from "../index"; import { _DownloadButton } from "../_atoms/_download_button/_DownloadButton"; import styles from "./PluginsControlComponent.module.scss"; +import { useTranslation } from "react-i18next"; export const PluginsControlComponent = ({ variable_state, @@ -9,6 +10,8 @@ export const PluginsControlComponent = ({ toggleFunction, downloadStartFunction, }) => { + const { t } = useTranslation(); + const option = { id: plugin_status.plugin_id, is_pending: plugin_status.is_pending, @@ -19,6 +22,16 @@ export const PluginsControlComponent = ({ progress: null, }; + const downloaded_version = plugin_status.downloaded_plugin_info?.plugin_version; + const latest_version = plugin_status.latest_plugin_info?.plugin_version; + + const downloaded_version_label = t("config_page.plugins.downloaded_version", + { downloaded_version: downloaded_version } + ); + const latest_version_label = t("config_page.plugins.latest_version", + { latest_version: latest_version } + ); + if (plugin_status.is_downloaded) { return ( ); } else { @@ -34,6 +49,8 @@ export const PluginsControlComponent = ({ option={option} plugin_status={plugin_status} downloadStartFunction={downloadStartFunction} + downloaded_version_label={downloaded_version_label} + latest_version_label={latest_version_label} /> ); } @@ -45,49 +62,51 @@ const DownloadedPluginControl = ({ plugin_status, toggleFunction, downloadStartFunction, + downloaded_version_label, + latest_version_label, }) => { + const { t } = useTranslation(); + const togglePlugin = () => { toggleFunction(plugin_status.plugin_id); }; - const latest_version = plugin_status.latest_plugin_info?.plugin_version; - - if (!plugin_status.downloaded_plugin_info.is_plugin_supported) { if (plugin_status.is_latest_version_available) { return (
-

最新のバージョン: {latest_version}

-

最新版にアップデート後 利用可能

+

{downloaded_version_label}

+

{latest_version_label}

+

{t("config_page.plugins.available_after_updating")}

<_DownloadButton option={option} downloadStartFunction={downloadStartFunction} />
); } return (
-

現在利用不可 使用中VRCTバージョンとの互換性なし

+

{t("config_page.plugins.unavailable_downloaded")}

); } else if (plugin_status.is_outdated) { return (
-

最新情報が取得できません

+

{t("config_page.plugins.no_latest_info")}

); } else if (plugin_status.is_latest_version_already) { return (
-

最新のバージョン: {latest_version}

-

最新版を使用中

+

{latest_version_label}

+

{t("config_page.plugins.using_latest_version")}

); } else if (plugin_status.is_latest_version_available) { return (
-

最新のバージョン: {latest_version}

-

最新版を利用可能

+

{latest_version_label}

+

{t("config_page.plugins.available_latest_version")}

<_DownloadButton option={option} downloadStartFunction={downloadStartFunction} />
@@ -95,7 +114,7 @@ const DownloadedPluginControl = ({ } else { return (
-

最新版は現在利用不可

+

{t("config_page.plugins.available_latest_version")}

); @@ -103,28 +122,34 @@ const DownloadedPluginControl = ({ }; -const NotDownloadedPluginControl = ({ option, plugin_status, downloadStartFunction }) => { - const latest_version = plugin_status.latest_plugin_info?.plugin_version; +const NotDownloadedPluginControl = ({ + option, + plugin_status, + downloadStartFunction, + downloaded_version_label, + latest_version_label, +}) => { + const { t } = useTranslation(); if (plugin_status.is_latest_version_available) { return (
-

最新バージョン: {latest_version}

+

{latest_version_label}

<_DownloadButton option={option} downloadStartFunction={downloadStartFunction} />
); } else if (plugin_status.latest_plugin_info?.is_plugin_supported_latest_vrct) { return (
-

最新のバージョン: {latest_version}

-

VRCT最新版で利用可能

+

{latest_version_label}

+

{t("config_page.plugins.available_in_latest_vrct_version")}

); } else { return (
-

最新バージョン: {latest_version}

-

現在利用不可

+

{latest_version_label}

+

{t("config_page.plugins.unavailable_not_downloaded")}

); } diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/switch_box/SwitchBox.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/switch_box/SwitchBox.module.scss index 29c26df5..87bf6600 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/switch_box/SwitchBox.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/_components/switch_box/SwitchBox.module.scss @@ -1,7 +1,6 @@ @import "@scss_mixins"; .switchbox_container { - width: 100%; display: flex; justify-content: end; align-items: center; diff --git a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx index 850e83dd..272fff80 100644 --- a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx @@ -61,14 +61,18 @@ const PluginDownloadContainer = () => { const variable_state = currentSavedPluginsStatus.state; + const filtered_plugins_data = currentPluginsData.data.filter(plugin => !plugin.is_outdated) + // plugin_id で ABC 順にソート - const sorted_plugins_data = [...currentPluginsData.data].sort((a, b) => + const sorted_plugins_data = filtered_plugins_data.sort((a, b) => a.plugin_id.localeCompare(b.plugin_id) ); // Duplicate const is_failed_to_fetch = currentFetchedPluginsInfo.state === "error"; const is_fetching = currentFetchedPluginsInfo.state === "pending"; + console.log(sorted_plugins_data); + return (
@@ -88,32 +92,27 @@ const PluginDownloadContainer = () => { return (
-

- {target_locale.title} -

-

{plugin.plugin_id}

-

- {target_locale.desc} -

- {plugin.is_error ? ( -

Error: {plugin.error_message}

- ) : ( -
-
-

- {plugin.is_downloaded - ? `現在のバージョン: ${plugin.downloaded_plugin_info?.plugin_version}` - : null} -

-
+
+

+ {target_locale.title} +

+

+ {target_locale.desc} +

+ {/*

{plugin.plugin_id}

*/} +
+
+ {plugin.is_error ? ( +

Error: {plugin.error_message}

+ ) : ( -
- )} + )} +
); })} diff --git a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss index 3320d590..c65a82ec 100644 --- a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss @@ -13,30 +13,45 @@ .plugin_wrapper { width: 100%; display: flex; - flex-direction: column; + justify-content: space-between; + align-items: center; padding: 2rem; &:not(:last-child) { border-bottom: 0.1rem solid var(--dark_750_color); } } +.labels_wrapper { + display: flex; + flex-direction: column; + gap: 0.4rem; + max-width: 50%; +} + .plugin_info_wrapper { display: flex; - width: 100%; - justify-content: space-between; + justify-content: end; align-items: center; } .title { font-size: 1.6rem; + width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } - -.plugin_id { - font-size: 1rem; +.desc { + font-size: 1.4rem; + width: 100%; + overflow: hidden; + color: var(--dark_500_color); } - -.download_button { - background-color: var(--dark_750_color); - padding: 0.4rem 0.6rem; - font-size: 1.2rem; -} \ No newline at end of file +// .plugin_id { +// font-size: 1rem; +// color: var(--dark_600_color); +// width: 100%; +// overflow: hidden; +// white-space: nowrap; +// text-overflow: ellipsis; +// } \ No newline at end of file From 7cdd3f8603ab63463d5a57bb8f45d1d6e503f084 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sun, 27 Apr 2025 02:27:25 +0900 Subject: [PATCH 52/68] [bugfix/Chore] Plugins: Fix the bug that localization and the description can't see form latest information. Add gap between labels and controller component. --- .../setting_box/advanced_settings/plugins/Plugins.jsx | 2 -- .../advanced_settings/plugins/Plugins.module.scss | 1 + src-ui/logics/configs/plugins/usePlugins.js | 9 ++------- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx index 272fff80..b19d48d7 100644 --- a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx @@ -71,8 +71,6 @@ const PluginDownloadContainer = () => { // Duplicate const is_failed_to_fetch = currentFetchedPluginsInfo.state === "error"; const is_fetching = currentFetchedPluginsInfo.state === "pending"; - console.log(sorted_plugins_data); - return (
diff --git a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss index c65a82ec..af4bae9a 100644 --- a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss @@ -16,6 +16,7 @@ justify-content: space-between; align-items: center; padding: 2rem; + gap: 2rem; &:not(:last-child) { border-bottom: 0.1rem solid var(--dark_750_color); } diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 7e4e3be9..8bd533e0 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -280,15 +280,10 @@ export const usePlugins = () => { const { is_plugin_supported, is_plugin_supported_latest_vrct } = checkVrctVerCompatibility(plugin_info.min_supported_vrct_version, plugin_info.max_supported_vrct_version); return { - title: plugin_info.title, - plugin_id: plugin_info.plugin_id, - plugin_version: plugin_info.plugin_version, - min_supported_vrct_version: plugin_info.min_supported_vrct_version, - max_supported_vrct_version: plugin_info.max_supported_vrct_version, + ...plugin_info, is_plugin_supported: is_plugin_supported, is_plugin_supported_latest_vrct: is_plugin_supported_latest_vrct, - asset_name: plugin_info.asset_name, - url: plugin_info_asset_url + url: plugin_info_asset_url, }; } From a599c398146049be6daa5c6d80f6ef0d7730e666 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sun, 27 Apr 2025 04:35:29 +0900 Subject: [PATCH 53/68] [Update] Remove unnecessary plugins files that is for plugin development. --- src-ui/plugins/dev_plugin_subtitles/index.jsx | 25 --- .../dev_plugin_subtitles/locales/en.yml | 2 - .../dev_plugin_subtitles/locales/initI18n.js | 11 -- .../dev_plugin_subtitles/locales/ja.yml | 2 - .../locales/usePluginTranslation.jsx | 7 - .../dev_plugin_subtitles/plugin_configs.js | 7 - .../dev_plugin_subtitles/plugin_info.json | 20 -- .../dev_plugin_subtitles/store/store.js | 37 ---- .../SubtitleSystemContainer.jsx | 47 ----- .../SubtitleSystemContainer.module.scss | 124 ------------ .../_controllers/SubtitlesController.jsx | 42 ---- .../_logics/useSubtitles.jsx | 182 ------------------ .../_subtitles_utils.js | 112 ----------- .../CountdownContainer.jsx | 72 ------- .../CountdownContainer.module.scss | 47 ----- .../InputFileContainer.jsx | 62 ------ .../InputFileContainer.module.scss | 42 ---- .../ModeSelectorContainer.jsx | 74 ------- .../ModeSelectorContainer.module.scss | 50 ----- .../PlayControlContainer.jsx | 33 ---- .../PlayControlContainer.module.scss | 31 --- .../SubtitlesListContainer.jsx | 45 ----- .../SubtitlesListContainer.module.scss | 0 src-ui/plugins/plugins_index.js | 2 +- 24 files changed, 1 insertion(+), 1075 deletions(-) delete mode 100644 src-ui/plugins/dev_plugin_subtitles/index.jsx delete mode 100644 src-ui/plugins/dev_plugin_subtitles/locales/en.yml delete mode 100644 src-ui/plugins/dev_plugin_subtitles/locales/initI18n.js delete mode 100644 src-ui/plugins/dev_plugin_subtitles/locales/ja.yml delete mode 100644 src-ui/plugins/dev_plugin_subtitles/locales/usePluginTranslation.jsx delete mode 100644 src-ui/plugins/dev_plugin_subtitles/plugin_configs.js delete mode 100644 src-ui/plugins/dev_plugin_subtitles/plugin_info.json delete mode 100644 src-ui/plugins/dev_plugin_subtitles/store/store.js delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.module.scss delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_controllers/SubtitlesController.jsx delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_logics/useSubtitles.jsx delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_subtitles_utils.js delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/countdown_container/CountdownContainer.jsx delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/countdown_container/CountdownContainer.module.scss delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/input_file_container/InputFileContainer.jsx delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/input_file_container/InputFileContainer.module.scss delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/play_control_container/PlayControlContainer.jsx delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/play_control_container/PlayControlContainer.module.scss delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx delete mode 100644 src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss diff --git a/src-ui/plugins/dev_plugin_subtitles/index.jsx b/src-ui/plugins/dev_plugin_subtitles/index.jsx deleted file mode 100644 index fc496275..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/index.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import { initStore, StoreContext } from "@plugin_store"; -import { initI18n } from "@initI18n"; -import { SubtitleSystemContainer } from "./subtitle_system_container/SubtitleSystemContainer"; -import { SubtitlesController } from "./subtitle_system_container/_controllers/SubtitlesController.jsx"; - -export const init = (plugin_context) => { - const { createAtomWithHook, i18n, logics } = plugin_context; - - initStore(createAtomWithHook); - initI18n(i18n); - - const EntryComponents = () => { - return ( - - - - - - ); - }; - - plugin_context.registerComponent(EntryComponents); -}; - -export default init; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/locales/en.yml b/src-ui/plugins/dev_plugin_subtitles/locales/en.yml deleted file mode 100644 index 6a5ae6e1..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/locales/en.yml +++ /dev/null @@ -1,2 +0,0 @@ -main_page: - title: "VRCT Subtitles" \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/locales/initI18n.js b/src-ui/plugins/dev_plugin_subtitles/locales/initI18n.js deleted file mode 100644 index 6e71957e..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/locales/initI18n.js +++ /dev/null @@ -1,11 +0,0 @@ -import en from "./en.yml"; -import ja from "./ja.yml"; -import plugin_info from "../plugin_info.json"; - -export const initI18n = (i18n) => { - const ns = plugin_info.plugin_id; - - // addResourceBundle will merge into i18n’s store - i18n.addResourceBundle("en", ns, en, /* deep = */ true, /* overwrite = */ true); - i18n.addResourceBundle("ja", ns, ja, /* deep = */ true, /* overwrite = */ true); -}; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/locales/ja.yml b/src-ui/plugins/dev_plugin_subtitles/locales/ja.yml deleted file mode 100644 index 3862be1e..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/locales/ja.yml +++ /dev/null @@ -1,2 +0,0 @@ -main_page: - title: "字幕プレイヤー" \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/locales/usePluginTranslation.jsx b/src-ui/plugins/dev_plugin_subtitles/locales/usePluginTranslation.jsx deleted file mode 100644 index 2e9e8d79..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/locales/usePluginTranslation.jsx +++ /dev/null @@ -1,7 +0,0 @@ -import { useTranslation } from "react-i18next"; -import plugin_info from "../plugin_info.json"; - -export const usePluginTranslation = () => { - const ns = plugin_info.plugin_id; - return useTranslation(ns); -}; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/plugin_configs.js b/src-ui/plugins/dev_plugin_subtitles/plugin_configs.js deleted file mode 100644 index 36f116d1..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/plugin_configs.js +++ /dev/null @@ -1,7 +0,0 @@ -export const configs = { - alias: { - "@plugin_store": "store/store.js", - "@initI18n": "locales/initI18n.js", - "@usePluginTranslation": "locales/usePluginTranslation.jsx", - } -} \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/plugin_info.json b/src-ui/plugins/dev_plugin_subtitles/plugin_info.json deleted file mode 100644 index ec673298..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/plugin_info.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "title": "VRCT Subtitles", - "desc": "No description", - "plugin_id": "vrct_plugin_subtitles", - "asset_name": "vrct_plugin_subtitles.zip", - "location": "main_section", - "plugin_version": "0.0.5", - "min_supported_vrct_version": "3.0.5", - "max_supported_vrct_version": "3.0.5", - "locales": { - "en": { - "title": "VRCT Subtitles", - "desc": "No description" - }, - "ja": { - "title": "VRCT 字幕表示機能", - "desc": "VRCTのオーバーレイ機能を使い、目の前に字幕としてテキストを表示する機能です。ワールドギミックの開始タイミングに合わせて字幕を設定し、同時に表示しているだけではあります。" - } - } -} \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/store/store.js b/src-ui/plugins/dev_plugin_subtitles/store/store.js deleted file mode 100644 index 6f338573..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/store/store.js +++ /dev/null @@ -1,37 +0,0 @@ -const store_hooks = {}; - -export const initStore = (createAtomWithHook) => { - Object.assign(store_hooks, { - useStore_IsSubtitlePlaying: createAtomWithHook(false, "IsSubtitlePlaying", { is_state_ok: true }).useHook, - useStore_SubtitlePlaybackMode: createAtomWithHook("relative", "SubtitlePlaybackMode", { is_state_ok: true }).useHook, - useStore_SubtitleAbsoluteTargetTime: createAtomWithHook({ - hour: "23", - minute: "00", - }, "SubtitleAbsoluteTargetTime", { is_state_ok: true }).useHook, - useStore_IsCuesScheduled: createAtomWithHook(false, "IsCuesScheduled", { is_state_ok: true }).useHook, - useStore_CountdownAdjustment: createAtomWithHook(0, "CountdownAdjustment", { is_state_ok: true }).useHook, - useStore_EffectiveCountdown: createAtomWithHook(null, "EffectiveCountdown", { is_state_ok: true }).useHook, - useStore_SubtitleCues: createAtomWithHook([], "SubtitleCues", { is_state_ok: true }).useHook, - - useStore_SubtitleTimers: createAtomWithHook([], "SubtitleTimers", { is_state_ok: true }).useHook, - useStore_SubtitleCountdownTimerId: createAtomWithHook([], "SubtitleCountdownTimerId", { is_state_ok: true }).useHook, - useStore_SubtitleFileName: createAtomWithHook("ファイルが選択されていません", "SubtitleFileName", { is_state_ok: true }).useHook, - }); -}; - -export const useStore = (hook_name) => { - if (!store_hooks[hook_name]) { - throw new Error(`Hook ${hook_name} is not initialized.`); - } - return store_hooks[hook_name](); -}; - - -// StoreContext.js -import React, { createContext, useContext } from "react"; - -export const StoreContext = createContext(null); - -export const useStoreContext = () => { - return useContext(StoreContext); -}; diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx deleted file mode 100644 index 64349850..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import styles from "./SubtitleSystemContainer.module.scss"; -import { InputFileContainer } from "./input_file_container/InputFileContainer"; -import { ModeSelectorContainer } from "./mode_selector_container/ModeSelectorContainer"; -import { PlayControlContainer } from "./play_control_container/PlayControlContainer"; -import { CountdownContainer } from "./countdown_container/CountdownContainer"; -import { SubtitlesListContainer } from "./subtitles_list_container/SubtitlesListContainer"; -import { usePluginTranslation } from "@usePluginTranslation"; - -export const SubtitleSystemContainer = () => { - const { t } = usePluginTranslation(); - // const [srtContent, setSrtContent] = useState(""); - // const [cues, setCues] = useState([]); - // const [isPlaying, setIsPlaying] = useState(false); - - // 再生モード ("relative": ボタン押下から、"absolute": 指定時刻から) - // const [playbackMode, setPlaybackMode] = useState("relative"); - // 絶対モード用の再生開始時刻(ドロップダウンで選択、HH:MM) - // const [targetHour, setTargetHour] = useState("23"); - // const [targetMinute, setTargetMinute] = useState("00"); - - // カウントダウン状態 - // // initialCountdown: 再生開始ボタン押下時に算出される元の残り秒数 - // const [initialCountdown, setInitialCountdown] = useState(null); - // countdownAdjustment: ユーザーが上下ボタンで調整する値(秒単位) - // const [countdownAdjustment, setCountdownAdjustment] = useState(0); - // effectiveCountdown: (initialCountdown + countdownAdjustment) から経過秒数を差し引いた表示用の値 - // const [effectiveCountdown, setEffectiveCountdown] = useState(null); - // cuesScheduled: 字幕タイマーが一度スケジュールされたか - // const [cuesScheduled, setCuesScheduled] = useState(false); - - // // タイマー(setTimeout/setInterval)のID管理用 - // const timersRef = useRef([]); - // // カウントダウンタイマー専用の ref - // const countdownIntervalRef = useRef(null); - - return ( -
-

{t("main_page.title")}

- - - -
- - -
- ); -}; diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.module.scss b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.module.scss deleted file mode 100644 index ef618213..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/SubtitleSystemContainer.module.scss +++ /dev/null @@ -1,124 +0,0 @@ -.container { - padding: 2rem 4rem; - background: var(--dark_900_color); - border-radius: 1rem; - flex-shrink: 0; - height: 100%; - overflow: auto; - display: flex; - flex-direction: column; - gap: 2rem; -} - -.title { - font-size: 1.8rem; - text-align: center; - flex-shrink: 0; -} - -.border { - width: 100%; - height: 0.2rem; - background-color: var(--dark_800_color); - flex-shrink: 0; - -} - - // label { - // display: block; - // font-size: 1.6rem; - // margin-bottom: 0.5rem; - // } - - // input, - // select { - // font-size: 1.6rem; - // padding: 0.5rem; - // border-radius: 0.5rem; - // border: 0.1rem solid #ccc; - // background: #333; - // color: #fff; - // } - - - // ボタンの基本スタイル - // button { - // // font-size: 1.8rem; - // // padding: 1rem 2rem; - // // border: none; - // // border-radius: 0.5rem; - // // cursor: pointer; - // // // transition: background 0.3s; - // // margin-right: 1rem; - - // &:focus { - // outline: none; - // box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); - // } - // } - - // 再生開始用ボタン(通常時) - .primary { - background: #007bff; - color: #fff; - &:hover { - background: #0056b3; - } - } - - // 再生停止用ボタン - .secondary { - background: #dc3545; - color: #fff; - &:hover { - background: #a71d2a; - } - } - - - - // 「再生中」状態(クリック不可)用のスタイル - .is_playing { - background: #6c757d; - cursor: not-allowed; - pointer-events: none; - } - - - // 字幕一覧のテーブル - table { - width: 100%; - border-collapse: collapse; - margin-top: 2rem; - - th, - td { - padding: 1rem; - border: 0.1rem solid #444; - text-align: left; - font-size: 1.4rem; - } - - th { - background: #555; - } - - tbody { - tr { - cursor: pointer; - transition: background 0.2s; - - &:nth-child(even) { - background: #2a2a2a; - } - - &:hover { - background: #444; - } - } - } -} - -.subtitle_lists { - font-size: 1.4rem; -} diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_controllers/SubtitlesController.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_controllers/SubtitlesController.jsx deleted file mode 100644 index d58be5aa..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_controllers/SubtitlesController.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import { useStoreContext } from "../../store/store.js"; - -import { useSubtitles } from "../_logics/useSubtitles"; -import { secToDayTime } from "../_subtitles_utils" -import { useEffect } from "react"; -export const SubtitlesController = () => { - const { useSendTextToOverlay } = useStoreContext(); - const { sendTextToOverlay } = useSendTextToOverlay(); - - const { - currentIsSubtitlePlaying, - currentIsCuesScheduled, - updateIsCuesScheduled, - currentCountdownAdjustment, - currentEffectiveCountdown, - scheduleCues, - } = useSubtitles(); - - // currentEffectiveCountdown.data が 0 になったとき、字幕開始 - useEffect(() => { - if ( - currentIsSubtitlePlaying.data && - currentEffectiveCountdown.data !== null && - currentEffectiveCountdown.data <= 0 && - !currentIsCuesScheduled.data - ) { - sendTextToOverlay("スタート!"); - console.log("スタート!"); - // 調整後のタイミングで字幕スケジュールを開始 - scheduleCues(0); - updateIsCuesScheduled(true); - } - - if (currentEffectiveCountdown.data > 0) { - console.log(secToDayTime(currentEffectiveCountdown.data)); - sendTextToOverlay(secToDayTime(currentEffectiveCountdown.data)); - } - - }, [currentEffectiveCountdown.data, currentIsSubtitlePlaying.data, currentIsCuesScheduled.data, currentCountdownAdjustment.data]); - - return null; -}; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_logics/useSubtitles.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_logics/useSubtitles.jsx deleted file mode 100644 index 80585abf..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_logics/useSubtitles.jsx +++ /dev/null @@ -1,182 +0,0 @@ -import { useStore, useStoreContext } from "../../store/store.js"; - -export const useSubtitles = () => { - const { useSendTextToOverlay } = useStoreContext(); - const { sendTextToOverlay } = useSendTextToOverlay(); - - const { currentSubtitleFileName, updateSubtitleFileName } = useStore("useStore_SubtitleFileName"); - const { currentIsSubtitlePlaying, updateIsSubtitlePlaying } = useStore("useStore_IsSubtitlePlaying"); - const { currentSubtitlePlaybackMode, updateSubtitlePlaybackMode } = useStore("useStore_SubtitlePlaybackMode"); - const { currentSubtitleAbsoluteTargetTime, updateSubtitleAbsoluteTargetTime } = useStore("useStore_SubtitleAbsoluteTargetTime"); - const { currentIsCuesScheduled, updateIsCuesScheduled } = useStore("useStore_IsCuesScheduled"); - - const { currentCountdownAdjustment, updateCountdownAdjustment } = useStore("useStore_CountdownAdjustment"); - const { currentEffectiveCountdown, updateEffectiveCountdown } = useStore("useStore_EffectiveCountdown"); - const { currentSubtitleCues, updateSubtitleCues } = useStore("useStore_SubtitleCues"); - - // タイマー(setTimeout/setInterval)のID管理用 - const { currentSubtitleTimers, updateSubtitleTimers, addSubtitleTimers } = useStore("useStore_SubtitleTimers"); - // const timersRef = useRef([]); - // カウントダウンタイマー専用の ref - const { currentSubtitleCountdownTimerId, updateSubtitleCountdownTimerId, AddSubtitleCountdownTimerId } = useStore("useStore_SubtitleCountdownTimerId"); - - // cues のスケジュールを行う(字幕開始時のオフセットは調整後のタイミングに合わせる) - const scheduleCues = (offset) => { - // 字幕開始時の処理 - const startFunction = (cue) => { - let send_text = ""; - if (cue.actor !== "") { - send_text = `[${cue.actor}] ${cue.text}`; - } else { - send_text = `${cue.text}`; - } - console.log(`字幕開始 (index: ${cue.index}) send_text:${send_text}`); - sendTextToOverlay(send_text); - }; - - // 字幕終了時の処理 - const endFunction = (cue) => { - console.log(`字幕終了 (index: ${cue.index}): ${cue.text}`); - // 必要に応じた終了処理(例:テキストクリア)を実装可能 - // sendTextToOverlay(""); - }; - - currentSubtitleCues.data.forEach((cue) => { - const startDelay = cue.startTime * 1000 + offset; - const endDelay = cue.endTime * 1000 + offset; - if (startDelay >= 0) { - const timerId = setTimeout(() => startFunction(cue), startDelay); - addSubtitleTimers(timerId); - } - if (endDelay >= 0) { - const timerId = setTimeout(() => endFunction(cue), endDelay); - addSubtitleTimers(timerId); - } - }); - }; - - - // カウントダウンタイマーの開始/再登録(指定した値から1秒ごとに減らす) - const startCountdownInterval = (startValue) => { - // 既存のタイマーがあればクリア - if (currentSubtitleCountdownTimerId.data) { - clearInterval(currentSubtitleCountdownTimerId.data); - } - // 新たな開始値を設定 - updateEffectiveCountdown(startValue); - const countdown_timer_id = setInterval(() => { - updateEffectiveCountdown((prev) => { - if (prev.data <= 1) { - clearInterval(currentSubtitleCountdownTimerId.data); - return 0; - } - return prev.data - 1; - }); - }, 1000); - updateSubtitleCountdownTimerId(countdown_timer_id); - addSubtitleTimers(currentSubtitleCountdownTimerId.data); - }; - - - // 字幕一覧の表示(relative モードの場合、クリックでジャンプ) - // テーブル内の字幕行をクリック(relative モードのみ)でジャンプ - const handleJump = (jumpCue) => { - if (currentSubtitlePlaybackMode.data !== "relative") return; - handleSubtitlesStop(); - const offset = -jumpCue.startTime * 1000; - scheduleCues(offset); - updateIsSubtitlePlaying(true); - }; - - - - // 「再生開始」ボタン押下時の処理 - const handleSubtitlesStart = () => { - handleSubtitlesStop(); - updateIsSubtitlePlaying(true); - updateIsCuesScheduled(false); - const target_time = currentSubtitleAbsoluteTargetTime.data; - - let computedCountdown = 0; - if (currentSubtitlePlaybackMode.data === "absolute") { - const now = new Date(); - const hour = parseInt(target_time.hour, 10); - const minute = parseInt(target_time.minute, 10); - let targetDate = new Date( - now.getFullYear(), - now.getMonth(), - now.getDate(), - hour, - minute, - 0, - 0 - ); - if (targetDate.getTime() < now.getTime()) { - targetDate.setDate(targetDate.getDate() + 1); - } - computedCountdown = Math.ceil((targetDate.getTime() - now.getTime()) / 1000); - } else { - computedCountdown = 10; // relative モードの場合は固定値 - } - // setInitialCountdown(computedCountdown); - // 調整値を反映した開始値 - const startValue = computedCountdown + currentCountdownAdjustment.data; - startCountdownInterval(startValue); - sendTextToOverlay(startValue.toString()); - }; - - - // すべてのタイマーを停止し、各状態を初期化する - const handleSubtitlesStop = () => { - currentSubtitleTimers.data.forEach((timerId) => { - clearTimeout(timerId); - clearInterval(timerId); - }); - - updateSubtitleTimers([]); - if (currentSubtitleCountdownTimerId.data) { - clearInterval(currentSubtitleCountdownTimerId.data); - updateSubtitleCountdownTimerId(null); - } - console.log("再生を停止しました。"); - updateIsSubtitlePlaying(false); - // setInitialCountdown(null); - updateEffectiveCountdown(null); - updateCountdownAdjustment(0); - updateIsCuesScheduled(false); - }; - - - return { - currentSubtitleFileName, - updateSubtitleFileName, - - currentIsSubtitlePlaying, - updateIsSubtitlePlaying, - - currentSubtitlePlaybackMode, - updateSubtitlePlaybackMode, - - currentSubtitleAbsoluteTargetTime, - updateSubtitleAbsoluteTargetTime, - - currentIsCuesScheduled, - updateIsCuesScheduled, - - currentCountdownAdjustment, - updateCountdownAdjustment, - - currentEffectiveCountdown, - updateEffectiveCountdown, - - currentSubtitleCues, - updateSubtitleCues, - - handleSubtitlesStart, - handleSubtitlesStop, - startCountdownInterval, - scheduleCues, - handleJump, - } - -}; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_subtitles_utils.js b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_subtitles_utils.js deleted file mode 100644 index 8bed1f7b..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/_subtitles_utils.js +++ /dev/null @@ -1,112 +0,0 @@ - -/** - * SRT形式の文字列を解析する関数 - * 改行コードを正規化し、空行で分割して解析する - * (actor は存在しないため、空文字列をセット) - */ -export const parseSRT = (data) => { - const cues = []; - const normalizedData = data.replace(/\r\n/g, "\n").trim(); - const blocks = normalizedData.split(/\n\s*\n/); - blocks.forEach((block) => { - const lines = block.split("\n").filter((line) => line.trim() !== ""); - if (lines.length >= 3) { - const index = parseInt(lines[0], 10); - const timeMatch = lines[1].match(/([\d:,]+)\s+-->\s+([\d:,]+)/); - if (!timeMatch) return; - const start = parseTime(timeMatch[1]); - const end = parseTime(timeMatch[2]); - const text = lines.slice(2).join("\n"); - cues.push({ index, startTime: start, endTime: end, actor: "", text }); - } - }); - return cues; -}; - -/** - * ASS形式の文字列を解析する関数 - * [Events] セクション内の "Dialogue:" 行から、 - * フォーマット "Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" - * に沿って分割する。 - * ここでは Name を actor、Text を text として抽出する。 - */ -export const parseASS = (data) => { - const cues = []; - const lines = data.split(/\r?\n/); - let index = 1; - lines.forEach((line) => { - if (line.startsWith("Dialogue:")) { - const dialogueLine = line.substring("Dialogue:".length).trim(); - const parts = dialogueLine.split(","); - // parts[0]: Layer, parts[1]: Start, parts[2]: End, parts[3]: Style, parts[4]: Name, parts[5]: MarginL, parts[6]: MarginR, parts[7]: MarginV, parts[8]: Effect, parts[9]~: Text - if (parts.length < 10) return; - const startTime = parseASSTime(parts[1].trim()); - const endTime = parseASSTime(parts[2].trim()); - const actor = parts[4].trim(); - const text = parts.slice(9).join(",").trim(); - cues.push({ index: index++, startTime, endTime, actor, text }); - } - }); - return cues; -}; - -/** - * "H:MM:SS.cc" 形式の ASS 時刻文字列を秒数に変換する関数 - * 例: "0:00:10.52" → 10.52 秒 - */ -export const parseASSTime = (timeString) => { - const parts = timeString.split(":"); - if (parts.length !== 3) return 0; - const hours = parseFloat(parts[0]); - const minutes = parseFloat(parts[1]); - const seconds = parseFloat(parts[2]); - return hours * 3600 + minutes * 60 + seconds; -}; - -/** - * "HH:MM:SS,mmm" 形式の SRT 時刻文字列を秒数に変換する関数 - */ -export const parseTime = (timeString) => { - const [hms, ms] = timeString.split(","); - const [hours, minutes, seconds] = hms.split(":").map(Number); - return hours * 3600 + minutes * 60 + seconds + Number(ms) / 1000; -}; - -const padTime = (int) => { - return String(int).padStart(2, "0"); -}; - -export const secToDayTime = (seconds) => { - const day = Math.floor(seconds / 86400); - const hour = Math.floor((seconds % 86400) / 3600); - const min = Math.floor((seconds % 3600) / 60); - const sec = seconds % 60; - let time = ""; - // day が 0 の場合は「日」は出力しない(hour や min も同様) - if (day !== 0) { - time = `${day}日${hour}時間${min}分${sec}秒`; - } else if (hour !== 0) { - time = `${padTime(hour)}:${padTime(min)}:${padTime(sec)}`; - } else { - time = `${padTime(min)}:${padTime(sec)}`; - } - // } else { - // time = `${padTime(sec)}`; - // } - return time; -}; - - -// HH:MM:SS 形式に変換する補助関数 -export const formatTime = (timeInSeconds) => { - const hours = Math.floor(timeInSeconds / 3600); - const minutes = Math.floor((timeInSeconds % 3600) / 60); - const seconds = Math.floor(timeInSeconds % 60); - return ( - String(hours).padStart(2, "0") + - ":" + - String(minutes).padStart(2, "0") + - ":" + - String(seconds).padStart(2, "0") - ); -}; diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/countdown_container/CountdownContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/countdown_container/CountdownContainer.jsx deleted file mode 100644 index 419db219..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/countdown_container/CountdownContainer.jsx +++ /dev/null @@ -1,72 +0,0 @@ -import React, { useState, useRef, useEffect } from "react"; -import styles from "./CountdownContainer.module.scss"; -import { secToDayTime } from "../_subtitles_utils"; -import { useSubtitles } from "../_logics/useSubtitles"; - -export const CountdownContainer = () => { - const { - updateCountdownAdjustment, - currentEffectiveCountdown, - currentIsCuesScheduled, - startCountdownInterval, - } = useSubtitles(); - // カウントダウン表示:字幕開始前は常に表示 - - // if (currentEffectiveCountdown.data === 0) return null; - if (currentEffectiveCountdown.data === null && currentIsCuesScheduled.data) return null; - - return ( -
- カウントダウン: {secToDayTime(currentEffectiveCountdown.data)} -
- {/* 1分単位の調整ボタン */} -
- - -
-
- {/* 1秒単位の調整ボタン */} -
- - -
-
-
- ); -}; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/countdown_container/CountdownContainer.module.scss b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/countdown_container/CountdownContainer.module.scss deleted file mode 100644 index 0ad5a7ff..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/countdown_container/CountdownContainer.module.scss +++ /dev/null @@ -1,47 +0,0 @@ -.container { - margin-top: 1rem; - font-size: 2.6rem; - display: flex; - align-items: center; - gap: 1rem; - flex-direction: column; - - span { - font-weight: bold; - } - -} - -.adjust_button_container { - display: flex; - gap: 10rem; -} - -.adjust_button_wrapper { - display: flex; - flex-direction: column; - gap: 4rem; -} - - - -.adjust_button { - padding: 1rem 1.4rem; - font-size: 1.8rem; - border-radius: 0.4rem; - background: var(--primary_600_color); - color: #fff; - cursor: pointer; - - &:hover { - background: var(--primary_400_color); - } - &:active { - background: var(--primary_650_color); - } -} - -.adjust_button_border { - background-color: var(--dark_600_color); - width: 0.2rem; -} \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/input_file_container/InputFileContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/input_file_container/InputFileContainer.jsx deleted file mode 100644 index 1e234765..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/input_file_container/InputFileContainer.jsx +++ /dev/null @@ -1,62 +0,0 @@ -import React, { useState, useRef, useEffect } from "react"; -import styles from "./InputFileContainer.module.scss"; -import { useSubtitles } from "../_logics/useSubtitles"; -import { parseSRT, parseASS } from "../_subtitles_utils"; - -export const InputFileContainer = () => { - const { - updateSubtitleFileName, - currentSubtitleFileName, - updateSubtitleCues, - handleSubtitlesStop - } = useSubtitles(); - - // ファイルアップロード時の処理 - const handleFileUpload = (event) => { - const file = event.target.files[0]; - if (!file) return; - const reader = new FileReader(); - reader.onload = (e) => { - const content = e.target.result; - let parsedCues = []; - // 拡張子により ASS と SRT を判定 - if (file.name.toLowerCase().endsWith(".ass")) { - parsedCues = parseASS(content); - } else { - parsedCues = parseSRT(content); - } - updateSubtitleCues(parsedCues); - console.log("Parsed cues:", parsedCues); - updateSubtitleFileName(file.name); - - }; - reader.readAsText(file); - }; - - - // ファイルクリア - const handleClearFile = () => { - handleSubtitlesStop(); - updateSubtitleFileName("ファイルが選択されていません"); - updateSubtitleCues([]); - }; - - return ( -
-
- - -

{currentSubtitleFileName.data}

-
- -
- ); -}; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/input_file_container/InputFileContainer.module.scss b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/input_file_container/InputFileContainer.module.scss deleted file mode 100644 index 91966cbb..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/input_file_container/InputFileContainer.module.scss +++ /dev/null @@ -1,42 +0,0 @@ -.container { - display: flex; - align-items: center; - justify-content: space-between; - gap: 6rem; -} - -.input_file_wrapper { - display: flex; - align-items: center; - gap: 1rem; -} -.input_file_label { - font-size: 1.4rem; - border-radius: 0.4rem; - padding: 1rem; - background-color: var(--dark_850_color); - border: 0.1rem solid var(--dark_400_color); - flex-shrink: 0; -} -.input_file_i { - display: none; -} -.file_name { - font-size: 1.6rem; - padding: 0.5rem; - width: 100%; - max-width: 60rem; -} - -.file_clear { - background: var(--dark_800_color); - color: var(--dark_200_color); - border-radius: 0.4rem; - font-size: 1.2rem; - padding: 0.6rem 1rem; - cursor: pointer; - - &:hover { - background: var(--error_bc_color); - } -} \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx deleted file mode 100644 index e19ddba2..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/mode_selector_container/ModeSelectorContainer.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import styles from "./ModeSelectorContainer.module.scss"; -import { useSubtitles } from "../_logics/useSubtitles"; -export const ModeSelectorContainer = () => { - const { - currentSubtitlePlaybackMode, - updateSubtitlePlaybackMode, - currentSubtitleAbsoluteTargetTime, - updateSubtitleAbsoluteTargetTime, - } = useSubtitles(); - - const target_time = currentSubtitleAbsoluteTargetTime.data; - - const handleOnchangeTargetTime = (key, value) => { - updateSubtitleAbsoluteTargetTime((old_value) => { - return { - ...old_value.data, - [key]: value, - } - }); - }; - - - return ( -
-
- -
- - {currentSubtitlePlaybackMode.data === "absolute" && ( -
- -
- - : - -
-
- )} -
- ); -}; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss deleted file mode 100644 index a3c805d9..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/mode_selector_container/ModeSelectorContainer.module.scss +++ /dev/null @@ -1,50 +0,0 @@ -.container { - // background-color: red; - display: flex; - gap: 4rem; -} -.mode_selector_wrapper { - display: flex; - align-items: center; - gap: 2rem; -} -.absolute_time_label { - font-size: 1.4rem; -} - -.mode_selector { - font-size: 1.6rem; - padding: 0.6rem 1rem; - border-radius: 0.5rem; - border: 0.1rem solid var(--dark_400_color); - cursor: pointer; -} - -.mode_selector_item { - background-color: var(--dark_800_color); -} - - -.time_section { - display: flex; - gap: 2rem; - justify-content: center; - align-items: center; -} - -.time_selects { - display: flex; - align-items: center; - gap: 0.5rem; -} - - -.time_selects_item { - width: 6rem; - text-align: center; - padding: 0.6rem 1rem; - font-size: 1.8rem; - background-color: var(--dark_850_color); - border: 0.1rem solid var(--dark_400_color); - cursor: pointer; -} \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/play_control_container/PlayControlContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/play_control_container/PlayControlContainer.jsx deleted file mode 100644 index 05e45e01..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/play_control_container/PlayControlContainer.jsx +++ /dev/null @@ -1,33 +0,0 @@ -// import React, { useState, useRef, useEffect } from "react"; -import styles from "./PlayControlContainer.module.scss"; -import { useSubtitles } from "../_logics/useSubtitles"; -import clsx from "clsx"; - -export const PlayControlContainer = () => { - const { - currentIsSubtitlePlaying, - handleSubtitlesStart, - handleSubtitlesStop, - } = useSubtitles(); - - const is_playing = currentIsSubtitlePlaying.data; - - const playback_button_classname = clsx(styles.playback_button, { - [styles.is_playing]: is_playing, - }); - return ( -
- - {is_playing && - - } -
- ); -}; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/play_control_container/PlayControlContainer.module.scss b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/play_control_container/PlayControlContainer.module.scss deleted file mode 100644 index ea5de457..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/play_control_container/PlayControlContainer.module.scss +++ /dev/null @@ -1,31 +0,0 @@ -.container { - display: flex; - gap: 4rem; - display: flex; - justify-content: center; -} - -.playback_button, .playback_stop_button { - font-size: 1.6rem; - padding: 1rem 2rem; - cursor: pointer; - border-radius: 0.4rem; -} - -.playback_button { - background-color: var(--primary_550_color); - &:hover { - background-color: var(--primary_400_color); - } - &.is_playing { - background-color: var(--primary_650_color); - pointer-events: none; - color: var(--dark_400_color); - } -} -.playback_stop_button { - background-color: var(--dark_800_color); - &:hover { - background-color: var(--error_bc_color); - } -} \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx deleted file mode 100644 index b997870e..00000000 --- a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import React, { useState, useRef, useEffect } from "react"; -import styles from "./SubtitlesListContainer.module.scss"; -import { useSubtitles } from "../_logics/useSubtitles"; -import { formatTime } from "../_subtitles_utils"; - -export const SubtitlesListContainer = () => { - const { currentSubtitleCues, handleJump } = useSubtitles(); - - if (currentSubtitleCues.data.length < 0 ) return null; - - return ( -
-

字幕一覧

- - - - - - - - - - - - {currentSubtitleCues.data.map((cue) => ( - handleJump(cue)} - className={styles.tableRow} - > - - - - - - - ))} - -
番号開始終了Actorテキスト
{cue.index}{formatTime(cue.startTime)}{formatTime(cue.endTime)}{cue.actor}{cue.text}
-

- ※ 行をクリックすると、その字幕の位置にジャンプします。(相対モードのみ) -

-
- ); -}; \ No newline at end of file diff --git a/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss b/src-ui/plugins/dev_plugin_subtitles/subtitle_system_container/subtitles_list_container/SubtitlesListContainer.module.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/src-ui/plugins/plugins_index.js b/src-ui/plugins/plugins_index.js index ea240627..6c1be762 100644 --- a/src-ui/plugins/plugins_index.js +++ b/src-ui/plugins/plugins_index.js @@ -1,3 +1,3 @@ export const dev_plugins = [ - { entry_path: "dev_plugin_subtitles" } + // { entry_path: "dev_plugin_subtitles" } ]; \ No newline at end of file From 0d96c7c690ffdeb52bcde1382c5e3dd04b5d081a Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Mon, 28 Apr 2025 11:16:47 +0900 Subject: [PATCH 54/68] [Update] Change the contributor's title for not making misunderstanding. --- docs/readmes/README.en.md | 2 +- src-ui/assets/about_vrct/contributor_rera.png | Bin 59695 -> 59633 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/readmes/README.en.md b/docs/readmes/README.en.md index f3f35a16..4618e9ff 100644 --- a/docs/readmes/README.en.md +++ b/docs/readmes/README.en.md @@ -94,7 +94,7 @@ Initial setup, basic functions, and other features are also described. ## Author - [みしゃ(misyaguzi)](https://github.com/misyaguziya) (Main Development) - [しいな(Shiina_12siy)](https://twitter.com/Shiina_12siy) (UI/UX, UI multilingual support) -- [レラ](https://github.com/soumt-r) (Technical Support) +- [レラ](https://github.com/soumt-r) (Technical Advisor) - [どね](https://twitter.com/done_vrc) (Logo Design) ## Thanks to our contributors diff --git a/src-ui/assets/about_vrct/contributor_rera.png b/src-ui/assets/about_vrct/contributor_rera.png index 32b6792ea21f751d35290b68f3a170ac18a79502..29b2466bb08c6884ee3c8742dcc6901f761b0f54 100644 GIT binary patch delta 11799 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 FV1md;HsAmN delta 11899 zcmV->E`-tX(F3p21CUk$#IaX|xPMEG99Nc@;S-UMdazhkq@D{%sdTXmDW-PwOgG zuh`GnccTiyL~ISsYUFDlD$qpSltC6vF|$9+ZF5aTh!^2_IRKYN_e;|QX1;D*iQ>eECbSP z?&|9Z1tZH0XJJ+^3B&ER`K_(h3)JP& z+Am~Q-{VW?=Uq0#)%}RVdR*77q-Rzwzg@dojVycPGA#OMJ3CEzw7%`8#BI6RdqX$5 zG!D<;>!L?laI=qo86evZ)(xv2X1KCo%Ywkot!=sg<(K+CA8HWf9}~h8_k31u-d?ZU zj;Y-G!+-wwnSXK@IH4XqcrdTW_WSzl6}zv;?FVynb4!9_XP-r_R%____O0CFH9g0! z&dki*69Cx9Q$GeIZ*K1V&9vQI(1zt(ei~R4_cqjxb$N>XknFOm=&m6fSzQk|P{Y-+ zI5RyhFTeB^H-nWG2uxa=`pCA+F!+Xa>SkNAf_7Gz&wnj*V?81_=sHdOd{!07UC+-( zK-)SaKXa30<4@A4J1!zDOQZb*kW?gB<3l=)_^gp_H89-!wSn#WpXMkuSlW>L57vDV zK8aigpk>|2np-aO^U_z&Yv!Zw2g;`{3Hg`8R=4dJwso3K_v;^%wEgy}*C+I*8IkW~Ua!iOW^i^x7daPzq}`do zvVBE2X7}~|-FW!$;hF{^ zx8xLAUthnY=l(`^Uju=Ksi~>Y<%n6(q2Cqn>VL0Sbo~q6@7gX~ru(6NHY<8t)?nl# z0f3xmDgbd)^L}{e>O?iVqgTEgX&8l_Z>&q_+PC;=W9`~^>c>Mwp;wWsXHoGpK-rQr z6Vr0;+`K%ew~EV%_~La@2xMHe;%2aF`ze4UKMRwqEg?}TfM@;P66sC?EIk9lqa+)= zk$)`fJ-CaKrzgDlw za})DYt!dWf;fAhjH)Kk5i&y>g`w0SMEPp=Oi`+PytPY8>z{+a2OipSLWa*cXA#<~w zX_mw?Ao^Zb+sXE2!ILttw}u?Od3p8~x8<|;o*xTT^|*gWf1IiSBm^G~IYt`jSr3C7 zy6&O&DGN~SroqUPUhG*S5CM>RrUDS7LCEUA_;+`%PRWE`9g_L-Y(d&-sJqG_X@8~D z70a%m`v4^JV>a}^`S}@n{<%3f{j3eTjyjV#`M|>h4LAFe_`%spR#_tTRlqHzaRaw) zY(!^lLKw&$WPwQ{xv6PGq>^@mix56V{yk(Fk+^fUU%Ee|Y!3z+`V?)H{U>wkOv$_vlS{LGZZ$*#{Rg!dZzxqPL3BCdR9BAaE) z)zR>;RRgSAJ$Av^*_mlS%T{YwZ&pvF>i)VCf~+y)k6iaNpTSB(Tx83v3*AJm>Vit& z1uIO|Y~8eyWUZa-UjC=ouC5Az6U{Cb=&}1&VezWq$b_uN#}-V;c&wY&vVR6J7w9gB zg9`iO5A(}3%X?GsRC(qC5c5y|-sOA$>W}{0dvOwdm`yX=88a=}6@BT>ywn*l68mCx zZfaI>mA>nm9k43rXQn3I)%@6veTYMbA`~`EeaqR$3w|6!9G=#ybGw1h)hJxyPs&^H zQ8tz%3jZ9Y1n&HE+RZ{_|9_nQTYfs>o?s;Mvszg;z)eeAlPwKAzF5=DhZPvq;KRnM zSXZfS4L+=E*$dCl%h@v%zLQqu11UeCy^x{Fzwk1lGPxTVANv9RZCAK(SL*e;OzZaC z+ikk>8ErRv?2SRW-dRLklC^QMwij}I6~Ukv+iESpf3vs zZ-<}m*s|l(9&5ph{@#-*vFwRvL*`~@XX)ySgRqOsONC{e1D-O^WB_9R;qQKD`Coth z-(S%-@RAL5?ya9u>3@!==yr$_m#N4K(OLGR7Q1P8XQt|5y4|*Gu=_y3|9vH@h*h_U zBi|(}s{-!2T~%Y*N|MmU$4{l}F3NWTj5IfHqZ49;|Hs{_uCiCUPYsYP0O=O?%NpOY z3qGPsU7DJK*j(R`2kVykh+OeOd+$w+M3VNN23i$){`{QGX@7Pi7JcvbHsY)sQxe8Z zgd&MO<1h*VK`zkC2E+U9)pBD*Ec4K8icC({UGYW>N~~gh_DQrmy}6=&-mCQ02|z*t z%B(AvpVh45GF#PZwX@yyO)kCrgJ1nf@I-|M$xw}w(D!{IyRV<~Ev|l@9_P-TThS|^ zm-KUg#~QtQjDLTk%Lpuwh-NKrl>{Fvy6usgN^U04bC|L4s{xJ4KvW9gkX-_E>8$Zy^I#~8&=8UgpVdFboF+-bD z)~2+z`)+0^KckX0c&jdL($s%%x`u96n_^>QOU~5Kx_`A=U}EdHE=XDBb>CIVuU~pz zjYz&IVm7mt%cF!q!`(~#U}_)SWCKMaH?^(R_}FbF@<=lck2L$S-O?K3~@0;o2~Nub>Bcwxawbriy177Q}r`%X~to9ukEJUwL#yz8U$G8!-@sE_t9>< z!henDX3x4ntLmm5PF$uZ%f9rCCy5Hv2lwb^lKlAh*k?mR*R(hUQZ|6!x^`LMW1)d{ z#jT+SRP2c;D064Xt`2fWH{`WNqiGSlIn@-j;9KPyx6e?f)qJx##3ecsY%D1?n z!=`SvDO|0=YQ+pAn@TrI++*_G^tu&ab=?)Rb0sYjcctHT_t9YB{+DZ-^|0)T6~3$M zI;>EkpAOdrC214}MrXIEQ)j~kM=sn5716CwU=q7JAr^e3?(Z#=V)aKRbbWO<#(!gd zb4R|oZv)OZ^nT*%a#%q|7wK4q_IfPOpPQ8#d!JUPBm`rfu@uoT!@+%*vrcuy$Y*Gz zGsSUtnqwP36K9hGB!2tYXL4rxj2mZDweD@Mn_JuyaHOHGN7;0bd1;}GS}ML?iJN6C zO5EpCb)S2*^!o%K0MM77gBJ?-mw#joA!BtrzpPL@1i&ftOn1S`vmoT({)Zpi3|32> z5!?A`rhK2F&MZ~d{c37*(#@V^4QFd8x7$g0te<|jd&NF>*%Pb$ZP|rgy}Iu!cE|2{ zZ1}_0_J(}9wl3%A&${a1mH|jzHT1+y+3T*_r%BJ%{;UCB@u?8Eu67uHl7G5sgI$we zTGo)aZdDp!?CEn^)$gs%ZQ0n~k=-P9RnILD(ko7~ySpnp+uJf}-Ji74e{Oc#S9_1r z&TLpA?cv{Rl#IE>v+bK7$0FjZviodQn#AtSS&%n9HRZahHQTMu`t##Wia#Hhl#K1q zW*iHp4t=+G7R`y4l)>v41MC<;B6y+=9V@i6Fp=FWd_XWjHPI9-{Yo&Biz+OpqUtyZIW!Lj&x zm#qsY%c%)K%nvjO`S<_v$JPp6a&zf+0uZ0laNVlHbh`C=O#_9R3p}jV@5fhUqX|SL zUth#sVUJ~^Zb3_3wtuyepLE8X_{M)lvmReOc%;EeOI~>Xyv&?E$wV!d^Rs8!ntS z!ed<5wu%e%ELhvq_t8!h$zrbVVD{p8n8&%yjkL2n;Zq8AUHtPb{oXqSA1G&EgnF!4 zor51{bqhq*puftCKaVT}yCzu)__f&FLBocUy! z=d{LyKHr_(bAK%Bk8Ae3y+{4`53@-Fit7sn>sCjp5n<1%>-#LXXjN|)WCYoE*za{2 zE2O8EwSjqa+y-X8VBdc03ETXh6^uNAbuxPPOKnI#kTGe4()OpJ_9<(0>u3C7?*6ib zeQH=?pKN6Jt%15L;D4AI`STla+}OEjDI0%t7{|?i zvh$uz>Y%UZt+MqQTc7?|8TEK5ewO;amn6(sRjezMTW>?c_RkcK`Nx#~dwyLwc}_(a zti0d*o{b5Kmb%@XA~#?>u7vygd#zTN$*D=#Jxe-uJJRk{x!DX>+2#w2YSo%dPfy9D zW;o(7BY#%A-Et?3T75#>@-=z1u`NIS*?qbHv>`u#@K7FT7GXEh;G|NQM1zmS>Ws%#7m#dh zY-sRNk(Xb5PF^@aCv}TUlBS#SD{`|``5?j#tbdPeoXD8FhfmRTZza{{-AEX>iIOlb zB-U*=?E=44vKk_Z^I1!-BYL?gVv_vMILV=K&} zc7G=wuBOGJ?H8kN3kz_!AI^P#A0G7c z3l}Et76<>AXdHB($=+WG0tR)7YluG{?%MfF4oIJ4S=VdF{Anl-ixQ_U05QM&+uy!f zaRbxi&Q!5kgVn}^Ot|dFM5pnajfsfD=znHkl+OnV`9^Nmb!+DAj8v*|XArp+BedDC z>Qhrv)nH?DdryAy)1S-Fe*O#D+}zQV`d&}vZeKY)yXs$YH$Qs5;?~vDhWURT8SJheukbX;Oo<3B6UqwvBURL57(TtN7U1g@`Vw zT)O%_#(bchH7>{eBD6;4$U%WkYkHjPGulX548hgV?D69^G(scFuI`TV9uS{L=O}ZOmJbrdhp`KUJ^0-z#nmMSt5}oo`$3 zAPpiWr_Z=R>V!4mo6_D}m&s~d zzW&m@y!iaQpJrEsn7Yk|Wq-eiDTAX>-^6C{vg|{Yn_73ITO^%|CO*Chfks+!Hw%Um zSC6snYp1wkhx&T-^-fGwUAHhBENFr9bR%_TzGXj_NcCDmX;z$j~u)ZH2*MIuL^EdRzjWO0` z$I<&^G^VWUku}z{*SMm$#p3$H@}ldyz6CjCxyEE8zuexl6*RNgl)b(%@Orsqdn^tL zkixbq3shG0`L2iee{ObmHnQKvch4&J@zJp zM)mvZV;Mu(wvB9^`hUYNRxAv0tj>=T_=I33e~e~UBzLUlkB8!OgbNf)G9t^g6wbLT zp{U?uSl8k{>s!cfdiP4df6s2MR_i0di8c7L@-yz^|8%zz*Wx?XNbEp!vtON=o|I{u zem8c5?p*T0jajhiX+xtt3Ue%F3*6*Xu0Dr#E~?7zPSZ_)`+w!yrWk+I zG@Fqs@wG}?ds5TD=-lj-Ja_J#Yusmp?Bl|CihPD4TY9jaqnj1#`=}_N>8NP%l*;}4 z4`p*}M`Fw5B!B+>M;2UJ1Q+|D$@q{Y>D-U9*|#D$o@F&o+E>5#TbB-fpdOyAVj)5s z+cQof!q6!FtgzVnIA85Ia8duxx=$4wqPGTBceX2zQfQo7<5RPNSNrv;)-U-R#o34Y zz2|VHezCZG+;z!-_Z9~1-ziz2UfhYV=e-aSEI0=C0)yJ?Cfl4L%J2}1KP}0sT*)@g#v3+XMbdJW>%`28Hs8WvZn!zCOl-bnaJjD zTQ>LFvSSGrZG=~+W~4qnqZyQG7l_!zTo#zrG;ncaPc#S8+}V<})s*R}ntb)8^YW5r zJ*G9g5T|a^Uq7(E-(YSzjL5VboDF)MpQXxYLM$+|_u&S|OO_ppa<9z)EbEfxtF_yo zM1S{M&Gu!25A+kRaCTUMHT&`&G;50f`>k*VKkxbwS09U+7Hec&9yFjcG+u8N{`UHa zS&-F14Vv7#idVfCWemeKF3R4US~$!uNMXSFVt!c&3i<|&S^bLQ)n?sq-Yt8)Wo{mu z3Vev*!&rbGKeTYpoaJ!~A+$|E{?Q3^?L$)40koMlL z20l~r@{8x?FMai8nV&uDrUuTw81v&Q(qRX8A0vCOHvYy6IJ)hg`XYu|FzCBk)zdzUS+6ks2ERnAiQjqHB-(tm$%*uS?5&r)cp7mKzP*0)%)zN^Dd5ot5eNN*$1 zu429|L(=2s`mi(C=(>vAVSd#9UTgJgMO?GUZpfN0C3xCJx(4U_^ z)*ALb$EeM&B_j&K$57{ma2{IDFY6Z&*ymCldtrf+ea14}`qoN{N)8Nqibgpf;n_t1 zlJncY_WFC(TJ61R9Iv^oMw)cSGNi7HQyM0Aiu_NJP{rJ5Ke8+NZahiq$AwI4Ff(^{ zTF#%Fm8prE3o}}+UDxeOl7A%Brsx*hOS0*Bqiifi6kbESb4!w@B>Eg}y(O(ZY3*%E zMVHS^O~^|cczo@p7vwoN#cm{>8N8$$e59eU-$@8Sve%HG?aBfsSFJsC7wgPcX0Jc1 z8=q#GnoeM{7E5~l@BHTL?_IrmX{eJoPPLvdVe85;>U{CqDEpwWXn%3P$CeDZTpr~M z*j`%*43@K--XAJaX$`3`1?sTtvhFoK4U$@|_NI)WWZU#xpKbS^^_6t_8H-(+FAh2n z0ngAv_Yw;Zmc#9KzXrB#YwIpFYL_9a?@(COkP{+T&3w7f!y4j`fseHj{J^?OKF!&Y zQO^nXSeuGBzf3>VMSmI1@~92-l4EV(45CemD}@Uo8YB;WteWupmJ822N}#n?tnq=< z=~UGL8OE=E`y03J{`pUD*V5*lG;KE?&vk?W)lu(~Z0cBR3|n<_`)5D1RZ}MF6}?<( z-KHjGduLBJH@9SGcUSgynyx;FYgpTu5BF?g`dvxf_y_l#nt$c60K)}A?Zgd5ab*r; zU&a0GnJGDQW>Ti7Cf&N+bhNJgpwAw}o!P#mr=s{UGfN$XV)iA?6&$nx%x2@Vi2&T# z|0Lv_LXk#E^W#>;MQT(yy4H?eliCthOE}W7ku8QuOr>mBGtvwOSw8l<&Dk zjk0q>AGc*hcC6|4U6I`JqZbU`kwP7f;GO)Pl1T=JqJP`wBYmHXxkU>yJi121Ap=t4 z(yK48zW(Z8)>L}B1Jmp&Psd!j5b)>ve#_M z)_=~n1{uDaS3B_ya+^`hIvrIsNU7E)U5$}Ro8GtPXTQqEg}BDJb@_6G%(GXPxGYK( z#ktp4>8WdSQgmO7A9P-+RHbI)aP+;kL*QYVAB&e8>dv5 zmNsuK1SJQ4E)AN?t_T;qFL4F%m{?b?!hgB$iEew#hS_XP)-({wWTEWwp`OYsqjpa_ZU+q7d$M^Y=bQbkj@k`0=hMRdM)3imTUO6L zcECa4E0P-z6}oA1Zo|+BKn8f@jc={oz5CgviHY=vo|djh(rvtF)8s~-UP9e53x76N z!`jXj%b3`VS~k2qN?Z>jyYHsY)duyXrZbYHq8W?{4L(8uBXMCWTtC%p6oL*H_avcS zN78nIkJZr-m-Xo8X2R%{PWPss*Db?$!76r_%8dFvWzUgc=CVcZMf-rw3PSF+cHFzL z>51LreDHCrwYhim`;7I#7<7MqrGKywE{@SxyHr_Yqqyt{ulA!rw|!58OV1;#a*V=M ztMBB4jI0Z6e*D|BAauL4+XrFX$AShVH%i9ySUr`sXAT=!uF9A+ zb8^ovFy@qHBN~UbzM$tkHp2S^90HIbT)K3@4z%|)5c!~1Pw%9i138kOdVd#b)*w&A z(yYNRZbm0}?cNPmw;V-VT#dporEF}=V)p#`o%AS=VV}Dq=fy&FeJr+v>5u2j+lYaFho>ux<7x%H_XqXSYsG-64 zd?7G8(1nm!;t%2PH*5Ly^s( z<)pr@hzlRWLSI)e?u;#w=<&K2-Md+%Jt?f~XabC*4_y3!^Ej@!u776rdRumz&9yX% zm%jTOuZ=WY2LK$W{VJ~S$Hq?BBmp+&K!1L&i^i&;eUjPa3A$ZYibd?gbg?S};8Y?2 z8N`A^~ra8tEajYMo3$@zE%>_93hxUd%9L7+2bU2<3;j8L)!CuPadP^ zo|g|kqWn;8IY=P#B!8~jbq1pA_Ndn@Yqe_S)`QmW2UmaNHM*_fs4Q#4Xju+HtJV6v zWO1>pLPKy|RwWkUT&sPi**$MA~5hb#B_MLL)ynZo}35$oMW=5}rRT1R!Z=4dH$i zhlhqPVe&Mw8YNakrLtU&lbe6@JAZXmfJ0PLZ(;2yEAp1~32z9FMR97|ot>Qv`r~ss z0^y2&qp@ex-miP8{FTDEKzJBf1 zS6!Dk;83;O?bX7fx8w*F1D=yo;rcWvHj4C0@kj*~N3t33joxMu%(W9pktXJh5 za&!)Ig?>TCq~DJV`THX@%o_)&ApdsZ{$uM5nWmoYKWYW@yyAI`$u%%40+2&u<3nE8 zK%}LCh<^qg%YJD#6g|q_VizPlwyY;;h(fjX!XS6aKWkB#7T5xwH2ht9ur64~$WB~K zt=8U!ixo?@!dfuZ3Aj3`y(2x;?ntv1r*`8ws1J(_%*f7`Qf9L=-6c8Lo zalhU#=vm3%(SZDI8AHkTJ<=>Po=pTGhs*+zix(R{5NW5^G;CPS!VE9`o*P8osomjg z7)q#R*puw&4Ol{ek_8>%$$amxury6`vto_TWj=HrR+l8d-cD8~mj&s`l#I5z5O_Sg z@PEb|-?&NOf#Xn8ENouCh~Jel4AV#{D^8~C`=N|T8-a@(8yg>HH@$x(jp=8JHgG>F zELsRt#EvAt-R)NLxfk!Rj#0?&q!l7u&EE;r2#>8;0Mvx3dmmFWQ`|^Fd!v+x^ z=NQ%XF68gqBm<-D2O6|4%80^tZRD5jSAWnjj6>?s*LPjUpzpJ|ZB_&r5dp{%ae>IC z3(FTTUbvu*@@v|BzwL_!Chlg_@`kyu3ODJwnS8L3{Vs)@|4+KXOz!#iQ`W^QTkb>^ zR}|3?mXAZ>#kf=4u&gWAa5J`s0==gy)0Dtak zFz|lAVut&yuOXwkVGWe*CgWaRI7LF^ZzaD>KeG>I1opY+x4Yi13^8zefi zf*#$jmX+(NVE*vo!;ylJeYWqi0DmJQ067L4ge+@A`)a$jr@=_uf{}a0AY-8XgQMXe z+V^oK<2i~&2S=HerT=5o74g{)4LZJgS+@!4T0AlKMQm3mbBipPcwa`4Jw9}~xULP; z5Bsf83Sa!%IQe+k297oomLzwT(_VY1LX83RYVgg%qJ^+M2eQwnq~UzK_Dy^Ka#sS#|nhKT~^Mg_+Er$hoL2z&c29j3Kn5{zd$3rx7=0k(3rwf#RkuUXIpEiWwD?I)!tBo*deb&gZMnYlnvS28o*xgEg znQhNOL5O|M)oS&zX?05inoQ5Re3-_%t;6;?dAtw*6Ia$)6(6YI*?-vB7^;htWsfXF zsL#{$_|RzW+c+l=Gq#QI zj(J{K8MIw3uq<4BaocrJ1KwUw+3}!%`=s{W)%+bjm%h)~x~B`%@IE2gG``}e^0gDr zLKIc(#JV6)sG?{w`+t!n<-td`ET24a_w9tAM)xF+ZdWQ(w_khhtDg&SLTTgRwl*%T zftBxqaK*k*b@{UX{)v96+1&ZAA!9T4Xwb4?6|SXljM_{$b8~Ya4Ori@KJT0P^;r;N zjSXFXTYvw^>O*Azrt7ow!Ik0_PHQ~q>!2v-2sCK8RxE^Pjej@Y?(f<(xqA20vd_6V zFuY%*(gKsh7y#E;*M{Op`t$b8%!~_ALSXY&$-cBAC#)NmHCA=|T(SK$x7fCi?aNEz zU-KvV8IZyhwr_@MS{I7XX*DYJ?=Q;`>(=dN$4dS)Xy|sntwD{|Q@IxkIoj*Fl7C#d z7_cN`ewW(chkw^@x9c|Es<@x%>zLQum3__!t9m}TDWh2tAH>>wwtdGY0azaKyl`#M zN-VG}smbE@)&0(U`rNk8#j@k!NzL+R0cySuTXrtBeM5Y`okp|S{3u+2zzIYEasq^4 zWI5dA&;IPQx7+PRPxz6ZRMUgb<}s3dz>#G^`p^?pw11+3$cklhE?)c=-Ky|Z(W`~; z>6fnkTI>hCXgAyc^T$BhxTo;Ne^rKDpEgL(6|M-|@oiu3i~5Dx_wU2HEbN zU?B)G4Sxy8N#C)Az02IOGE8P5toyyPF+0}nF`I3w#v9-G;Egx_igjDk#(zqDDNSR$!n0%z{-fx&RhH$*cfpF{ zXjRX)xAnJM)oQ$0txh%UwOzdUjjQ%r2|n<&vM>Gom9!Dv*5l;TC|xb|OM7w5^{s^9 zV^uIR+isy@xFRFc&+C?cZUe6_TZ3%W&o*nYp42)Dw%zo7T{^%qlF9bpp})T@Ygp{M z*niJqHj3?i>$+`!8Rc_1@G)wEM4^jRU%87CBee^{w!JjUy2ARbxaajJ8GM+~jrMZC z*PkA6<~AHHG9XWZ*I&QjQi^)BfnJ@1{!jn(v$x_{=6tq9<~2@ebgu>^^Hy0^m)X^u z{W2(PdI2Xpxvr+ERSkB_^lDtX8cVcRtAEG$e)*SwiLO+5rrDRNUJ{!;j#car9#S(F{ z3_S@G0Gt-u7<^m5Xl;;qCwSNU)|j%ZqCYy!FW@jH;LYOt?8;uPR$G~xn&JY$5ec&< zy_pY04)b%)rq{J~NVpJW&uwFd4u5+;I&M~sPT$unVXQ|ZTW;^qwxRCJ_gbyir-%I> z?br-s5$qz{>S3(IiYyk!6Ij61bA`WbUpdGzY6JB3@AKm@YykP?LF>%6?NJo|%YUZF zvA)BWy?*_5J`{jlrMoHs002%L3qX#b>ieK|F67s#w>tuyB8&k60001RY)%M300000 z92)`<00000$A$m|0002Mu^|8f0001RYzROA0000S8v+mj0002ThDzuF0N~WJVte{y zHM<|Gejlk;t7QfLRPJedy+Kv@6)002ovPDHLkV1hLO BbXoua From 3210d5c89807749ac56f43634d360212015fc7b9 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sat, 3 May 2025 08:47:02 +0900 Subject: [PATCH 55/68] [Update] update tauri v1-> v2 (development) --- package-lock.json | 12463 +++++++++------- package.json | 115 +- src-tauri/Cargo.lock | 3516 +++-- src-tauri/Cargo.toml | 32 +- src-tauri/capabilities/default.json | 10 + src-tauri/capabilities/vrct_capability.json | 63 + src-tauri/src/lib.rs | 64 + src-tauri/src/main.rs | 63 +- src-tauri/tauri.conf.json | 125 +- src-ui/app/App.jsx | 4 +- .../StartPythonController.jsx | 2 +- .../MergePluginsController.jsx | 1 - src-ui/app/_index_css/root.css | 3 +- .../app/error_boundary/AppErrorBoundary.jsx | 8 +- .../message_input_box/MessageInputBox.jsx | 3 +- .../app/splash_component/SplashComponent.jsx | 7 +- .../app/window_title_bar/WindowTitleBar.jsx | 17 +- src-ui/logics/common/useFetch.js | 16 +- src-ui/logics/common/useWindow.js | 67 +- src-ui/logics/configs/hotkeys/useHotkeys.js | 13 +- src-ui/logics/configs/plugins/usePlugins.js | 144 +- src-ui/logics/main/useMainFunction.js | 6 +- src-ui/logics/main/useMessageInputBoxRatio.js | 4 +- src-ui/logics/useStartPython.js | 2 +- src-ui/store.js | 1 + vite.config.js | 50 +- 26 files changed, 9570 insertions(+), 7229 deletions(-) create mode 100644 src-tauri/capabilities/default.json create mode 100644 src-tauri/capabilities/vrct_capability.json create mode 100644 src-tauri/src/lib.rs diff --git a/package-lock.json b/package-lock.json index 308c262d..f131d4a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5775 +1,6688 @@ -{ - "name": "tauri-app", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "tauri-app", - "version": "0.0.0", - "dependencies": { - "@babel/standalone": "7.26.9", - "@emotion/react": "11.14.0", - "@emotion/styled": "11.14.0", - "@mui/material": "6.2.0", - "@tauri-apps/api": "1.6.0", - "@vitejs/plugin-react": "4.3.4", - "clsx": "2.1.1", - "eslint": "8.57.0", - "eslint-plugin-react": "7.37.2", - "i18next": "24.1.0", - "jotai": "2.10.3", - "js-base64": "3.7.7", - "jszip": "3.10.1", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-error-boundary": "5.0.0", - "react-i18next": "15.2.0", - "react-resizable-layout": "0.7.2", - "semver": "7.7.1" - }, - "devDependencies": { - "@rollup/plugin-yaml": "^4.1.2", - "@tauri-apps/cli": "1.6.3", - "npm-run-all": "4.1.5", - "sass": "1.79.4", - "source-map": "0.7.4", - "vite": "6.2.5", - "vite-plugin-svgr": "4.3.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", - "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" - }, - "node_modules/@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==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", - "dependencies": { - "@babel/compat-data": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "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==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", - "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", - "dependencies": { - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", - "dependencies": { - "@babel/types": "^7.27.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", - "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", - "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/standalone": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/standalone/-/standalone-7.26.9.tgz", - "integrity": "sha512-UTeQKy0kzJwWRe55kT1uK4G9H6D0lS6G4207hCU/bDaOhA5t2aC0qHN6GmID0Axv3OFLNXm27NdqcWp+BXcGtA==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", - "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@emotion/babel-plugin": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", - "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/serialize": "^1.3.3", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/babel-plugin/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@emotion/cache": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", - "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", - "dependencies": { - "@emotion/memoize": "^0.9.0", - "@emotion/sheet": "^1.4.0", - "@emotion/utils": "^1.4.2", - "@emotion/weak-memoize": "^0.4.0", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/hash": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", - "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" - }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", - "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", - "dependencies": { - "@emotion/memoize": "^0.9.0" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", - "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" - }, - "node_modules/@emotion/react": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", - "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.13.5", - "@emotion/cache": "^11.14.0", - "@emotion/serialize": "^1.3.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", - "@emotion/utils": "^1.4.2", - "@emotion/weak-memoize": "^0.4.0", - "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/serialize": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", - "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", - "dependencies": { - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/unitless": "^0.10.0", - "@emotion/utils": "^1.4.2", - "csstype": "^3.0.2" - } - }, - "node_modules/@emotion/sheet": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", - "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" - }, - "node_modules/@emotion/styled": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", - "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.13.5", - "@emotion/is-prop-valid": "^1.3.0", - "@emotion/serialize": "^1.3.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", - "@emotion/utils": "^1.4.2" - }, - "peerDependencies": { - "@emotion/react": "^11.0.0-rc.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/unitless": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", - "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" - }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", - "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/@emotion/utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", - "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" - }, - "node_modules/@emotion/weak-memoize": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", - "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", - "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", - "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", - "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", - "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", - "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", - "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", - "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", - "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", - "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", - "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", - "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", - "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", - "cpu": [ - "loong64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", - "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", - "cpu": [ - "mips64el" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", - "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", - "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", - "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", - "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", - "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", - "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", - "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", - "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", - "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", - "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", - "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", - "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead" - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@mui/core-downloads-tracker": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.2.0.tgz", - "integrity": "sha512-Nn5PSkUqbDrvezpiiiYZiAbX4SFEiy3CbikUL6pWOXEUsq+L1j50OOyyUIHpaX2Hr+5V5UxTh+fPeC4nsGNhdw==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - } - }, - "node_modules/@mui/material": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.2.0.tgz", - "integrity": "sha512-7FXXUPIyYzP02a7GvqwJ7ocmdP+FkvLvmy/uxG1TDmTlsr8nEClQp75uxiVznJqAY/jJy4d+Rj/fNWNxwidrYQ==", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.2.0", - "@mui/system": "^6.2.0", - "@mui/types": "^7.2.19", - "@mui/utils": "^6.2.0", - "@popperjs/core": "^2.11.8", - "@types/react-transition-group": "^4.4.11", - "clsx": "^2.1.1", - "csstype": "^3.1.3", - "prop-types": "^15.8.1", - "react-is": "^19.0.0", - "react-transition-group": "^4.4.5" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.2.0", - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@mui/material-pigment-css": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/private-theming": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.2.0.tgz", - "integrity": "sha512-lYd2MrVddhentF1d/cMXKnwlDjr/shbO3A2eGq22PCYUoZaqtAGZMc0U86KnJ/Sh5YzNYePqTOaaowAN8Qea8A==", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/utils": "^6.2.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/styled-engine": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.2.0.tgz", - "integrity": "sha512-rV4YCu6kcCjMnHFXU/tQcL6XfYVfFVR8n3ZVNGnk2rpXnt/ctOPJsF+eUQuhkHciueLVKpI06+umr1FxWWhVmQ==", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@emotion/cache": "^11.13.5", - "@emotion/serialize": "^1.3.3", - "@emotion/sheet": "^1.4.0", - "csstype": "^3.1.3", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } - } - }, - "node_modules/@mui/system": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.2.0.tgz", - "integrity": "sha512-DCeqev9Cd4f4pm3O1lqSGW/DIHHBG6ZpqMX9iIAvN4asYv+pPWv2/lKov9kWk5XThhxFnGSv93SRNE1kNRRg5Q==", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/private-theming": "^6.2.0", - "@mui/styled-engine": "^6.2.0", - "@mui/types": "^7.2.19", - "@mui/utils": "^6.2.0", - "clsx": "^2.1.1", - "csstype": "^3.1.3", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/types": { - "version": "7.2.19", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.19.tgz", - "integrity": "sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==", - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/utils": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.2.0.tgz", - "integrity": "sha512-77CaFJi+OIi2SjbPwCis8z5DXvE0dfx9hBz5FguZHt1VYFlWEPCWTHcMsQCahSErnfik5ebLsYK8+D+nsjGVfw==", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/types": "^7.2.19", - "@types/prop-types": "^15.7.14", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-is": "^19.0.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@rollup/plugin-yaml": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-yaml/-/plugin-yaml-4.1.2.tgz", - "integrity": "sha512-RpupciIeZMUqhgFE97ba0s98mOFS7CWzN3EJNhJkqSv9XLlWYtwVdtE6cDw6ASOF/sZVFS7kRJXftaqM2Vakdw==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "js-yaml": "^4.1.0", - "tosource": "^2.0.0-alpha.3" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.3.tgz", - "integrity": "sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz", - "integrity": "sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.35.0.tgz", - "integrity": "sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.35.0.tgz", - "integrity": "sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.35.0.tgz", - "integrity": "sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.35.0.tgz", - "integrity": "sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.35.0.tgz", - "integrity": "sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.35.0.tgz", - "integrity": "sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.35.0.tgz", - "integrity": "sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.35.0.tgz", - "integrity": "sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.35.0.tgz", - "integrity": "sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.35.0.tgz", - "integrity": "sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==", - "cpu": [ - "loong64" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.35.0.tgz", - "integrity": "sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.35.0.tgz", - "integrity": "sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.35.0.tgz", - "integrity": "sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.35.0.tgz", - "integrity": "sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.35.0.tgz", - "integrity": "sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.35.0.tgz", - "integrity": "sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.35.0.tgz", - "integrity": "sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.35.0.tgz", - "integrity": "sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", - "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", - "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", - "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", - "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", - "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", - "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", - "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/babel-preset": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", - "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", - "dev": true, - "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", - "@svgr/babel-plugin-transform-svg-component": "8.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@svgr/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", - "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3", - "snake-case": "^3.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/core/node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "dev": true, - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", - "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.21.3", - "entities": "^4.4.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/plugin-jsx": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", - "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "@svgr/hast-util-to-babel-ast": "8.0.0", - "svg-parser": "^2.0.4" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@svgr/core": "*" - } - }, - "node_modules/@tauri-apps/api": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.6.0.tgz", - "integrity": "sha512-rqI++FWClU5I2UBp4HXFvl+sBWkdigBkxnpJDQUWttNyG7IZP4FwQGhTNL5EOw0vI8i6eSAJ5frLqO7n7jbJdg==", - "engines": { - "node": ">= 14.6.0", - "npm": ">= 6.6.0", - "yarn": ">= 1.19.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/tauri" - } - }, - "node_modules/@tauri-apps/cli": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-1.6.3.tgz", - "integrity": "sha512-q46umd6QLRKDd4Gg6WyZBGa2fWvk0pbeUA5vFomm4uOs1/17LIciHv2iQ4UD+2Yv5H7AO8YiE1t50V0POiEGEw==", - "dev": true, - "dependencies": { - "semver": ">=7.5.2" - }, - "bin": { - "tauri": "tauri.js" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/tauri" - }, - "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "1.6.3", - "@tauri-apps/cli-darwin-x64": "1.6.3", - "@tauri-apps/cli-linux-arm-gnueabihf": "1.6.3", - "@tauri-apps/cli-linux-arm64-gnu": "1.6.3", - "@tauri-apps/cli-linux-arm64-musl": "1.6.3", - "@tauri-apps/cli-linux-x64-gnu": "1.6.3", - "@tauri-apps/cli-linux-x64-musl": "1.6.3", - "@tauri-apps/cli-win32-arm64-msvc": "1.6.3", - "@tauri-apps/cli-win32-ia32-msvc": "1.6.3", - "@tauri-apps/cli-win32-x64-msvc": "1.6.3" - } - }, - "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.6.3.tgz", - "integrity": "sha512-fQN6IYSL8bG4NvkdKE4sAGF4dF/QqqQq4hOAU+t8ksOzHJr0hUlJYfncFeJYutr/MMkdF7hYKadSb0j5EE9r0A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.6.3.tgz", - "integrity": "sha512-1yTXZzLajKAYINJOJhZfmMhCzweHSgKQ3bEgJSn6t+1vFkOgY8Yx4oFgWcybrrWI5J1ZLZAl47+LPOY81dLcyA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.6.3.tgz", - "integrity": "sha512-CjTEr9r9xgjcvos09AQw8QMRPuH152B1jvlZt4PfAsyJNPFigzuwed5/SF7XAd8bFikA7zArP4UT12RdBxrx7w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.6.3.tgz", - "integrity": "sha512-G9EUUS4M8M/Jz1UKZqvJmQQCKOzgTb8/0jZKvfBuGfh5AjFBu8LHvlFpwkKVm1l4951Xg4ulUp6P9Q7WRJ9XSA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.6.3.tgz", - "integrity": "sha512-MuBTHJyNpZRbPVG8IZBN8+Zs7aKqwD22tkWVBcL1yOGL4zNNTJlkfL+zs5qxRnHlUsn6YAlbW/5HKocfpxVwBw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.6.3.tgz", - "integrity": "sha512-Uvi7M+NK3tAjCZEY1WGel+dFlzJmqcvu3KND+nqa22762NFmOuBIZ4KJR/IQHfpEYqKFNUhJfCGnpUDfiC3Oxg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.6.3.tgz", - "integrity": "sha512-rc6B342C0ra8VezB/OJom9j/N+9oW4VRA4qMxS2f4bHY2B/z3J9NPOe6GOILeg4v/CV62ojkLsC3/K/CeF3fqQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-1.6.3.tgz", - "integrity": "sha512-cSH2qOBYuYC4UVIFtrc1YsGfc5tfYrotoHrpTvRjUGu0VywvmyNk82+ZsHEnWZ2UHmu3l3lXIGRqSWveLln0xg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.6.3.tgz", - "integrity": "sha512-T8V6SJQqE4PSWmYBl0ChQVmS6AR2hXFHURH2DwAhgSGSQ6uBXgwlYFcfIeQpBQA727K2Eq8X2hGfvmoySyHMRw==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.6.3.tgz", - "integrity": "sha512-HUkWZ+lYHI/Gjkh2QjHD/OBDpqLVmvjZGpLK9losur1Eg974Jip6k+vsoTUxQBCBDfj30eDBct9E1FvXOspWeg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" - }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" - }, - "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" - }, - "node_modules/@types/react": { - "version": "18.3.11", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", - "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", - "peer": true, - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-transition-group": { - "version": "4.4.12", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", - "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", - "peerDependencies": { - "@types/react": "*" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" - }, - "node_modules/@vitejs/plugin-react": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", - "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", - "dependencies": { - "@babel/core": "^7.26.0", - "@babel/plugin-transform-react-jsx-self": "^7.25.9", - "@babel/plugin-transform-react-jsx-source": "^7.25.9", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" - } - }, - "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "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, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, - "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==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001688", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001688.tgz", - "integrity": "sha512-Nmqpru91cuABu/DTCXbM2NSRHzM2uVHfPnhJ/1zEAJx/ILBRVmz3pzH4N7DZqbdG0gWClsCC05Oj0mJ/1AWMbA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "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, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/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, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/chokidar": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", - "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", - "devOptional": true, - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "engines": { - "node": ">=6" - } - }, - "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, - "dependencies": { - "color-name": "1.1.3" - } - }, - "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 - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cosmiconfig/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" - }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz", - "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.73", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.73.tgz", - "integrity": "sha512-8wGNxG9tAG5KhGd3eeA0o6ixhiNdgr0DcHWm85XPCphwZgD1lIEoi6t3VERayWao7SF7AAZTw6oARGJeVjH8Kg==" - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.23.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.5.tgz", - "integrity": "sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz", - "integrity": "sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.3", - "safe-array-concat": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/esbuild": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", - "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.0", - "@esbuild/android-arm": "0.25.0", - "@esbuild/android-arm64": "0.25.0", - "@esbuild/android-x64": "0.25.0", - "@esbuild/darwin-arm64": "0.25.0", - "@esbuild/darwin-x64": "0.25.0", - "@esbuild/freebsd-arm64": "0.25.0", - "@esbuild/freebsd-x64": "0.25.0", - "@esbuild/linux-arm": "0.25.0", - "@esbuild/linux-arm64": "0.25.0", - "@esbuild/linux-ia32": "0.25.0", - "@esbuild/linux-loong64": "0.25.0", - "@esbuild/linux-mips64el": "0.25.0", - "@esbuild/linux-ppc64": "0.25.0", - "@esbuild/linux-riscv64": "0.25.0", - "@esbuild/linux-s390x": "0.25.0", - "@esbuild/linux-x64": "0.25.0", - "@esbuild/netbsd-arm64": "0.25.0", - "@esbuild/netbsd-x64": "0.25.0", - "@esbuild/openbsd-arm64": "0.25.0", - "@esbuild/openbsd-x64": "0.25.0", - "@esbuild/sunos-x64": "0.25.0", - "@esbuild/win32-arm64": "0.25.0", - "@esbuild/win32-ia32": "0.25.0", - "@esbuild/win32-x64": "0.25.0" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.37.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", - "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.1.0", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.8", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", - "string.prototype.repeat": "^1.0.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", - "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "dunder-proto": "^1.0.0", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "function-bind": "^1.1.2", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/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==" - }, - "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 - }, - "node_modules/html-parse-stringify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", - "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", - "dependencies": { - "void-elements": "3.1.0" - } - }, - "node_modules/i18next": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.1.0.tgz", - "integrity": "sha512-suKlX82AlptkMUO5YRfaAeH4FQyyKvR66jNaubTMiyPPMx7INU6PXAiy3PGULc0q6K+t9nxmDf/TRj9KjAivmw==", - "funding": [ - { - "type": "individual", - "url": "https://locize.com" - }, - { - "type": "individual", - "url": "https://locize.com/i18next.html" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - } - ], - "dependencies": { - "@babel/runtime": "^7.23.2" - }, - "peerDependencies": { - "typescript": "^5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" - }, - "node_modules/immutable": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", - "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "devOptional": true - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==" - }, - "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", - "dependencies": { - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.0.tgz", - "integrity": "sha512-qfMdqbAQEwBw78ZyReKnlA8ezmPdb9BemzIIip/JkjaZUhitfXDkkr+3QTboW0JrSXT1QWyYShpvnNHGZ4c4yA==", - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/iterator.prototype": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.4.tgz", - "integrity": "sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "reflect.getprototypeof": "^1.0.8", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/jotai": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.10.3.tgz", - "integrity": "sha512-Nnf4IwrLhNfuz2JOQLI0V/AgwcpxvVy8Ec8PidIIDeRi4KCFpwTFIpHAAcU+yCgnw/oASYElq9UY0YdUUegsSA==", - "engines": { - "node": ">=12.20.0" - }, - "peerDependencies": { - "@types/react": ">=17.0.0", - "react": ">=17.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - } - } - }, - "node_modules/js-base64": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", - "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/math-intrinsics": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.0.0.tgz", - "integrity": "sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/nanoid": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.9.tgz", - "integrity": "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" - }, - "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, - "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/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "bin": { - "npm-run-all": "bin/npm-run-all/index.js", - "run-p": "bin/run-p/index.js", - "run-s": "bin/run-s/index.js" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", - "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/npm-run-all/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/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, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/npm-run-all/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-all/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-all/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "engines": { - "node": ">= 0.8.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==" - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "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==" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-error-boundary": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-5.0.0.tgz", - "integrity": "sha512-tnjAxG+IkpLephNcePNA7v6F/QpWLH8He65+DmedchDwg162JZqx4NmbXj0mlAYVVEd81OW7aFhmbsScYfiAFQ==", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "peerDependencies": { - "react": ">=16.13.1" - } - }, - "node_modules/react-i18next": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.2.0.tgz", - "integrity": "sha512-iJNc8111EaDtVTVMKigvBtPHyrJV+KblWG73cUxqp+WmJCcwkzhWNFXmkAD5pwP2Z4woeDj/oXDdbjDsb3Gutg==", - "dependencies": { - "@babel/runtime": "^7.25.0", - "html-parse-stringify": "^3.0.1" - }, - "peerDependencies": { - "i18next": ">= 23.2.3", - "react": ">= 16.8.0" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/react-is": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", - "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==" - }, - "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-resizable-layout": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/react-resizable-layout/-/react-resizable-layout-0.7.2.tgz", - "integrity": "sha512-GrVzAecB6+osdAx5PPP3G8R9n7A2uDd3NL+PyrHWNRaVBivZmW/o0+yFjQdS5Bo016A2fIP11fAhefsknWN4aw==", - "peerDependencies": { - "react": ">=17.0.0", - "react-dom": ">=17.0.0" - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "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==", - "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/readable-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/readdirp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", - "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", - "devOptional": true, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz", - "integrity": "sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ==", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "dunder-proto": "^1.0.0", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.2.0", - "which-builtin-type": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", - "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rollup": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.35.0.tgz", - "integrity": "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg==", - "dependencies": { - "@types/estree": "1.0.6" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.35.0", - "@rollup/rollup-android-arm64": "4.35.0", - "@rollup/rollup-darwin-arm64": "4.35.0", - "@rollup/rollup-darwin-x64": "4.35.0", - "@rollup/rollup-freebsd-arm64": "4.35.0", - "@rollup/rollup-freebsd-x64": "4.35.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.35.0", - "@rollup/rollup-linux-arm-musleabihf": "4.35.0", - "@rollup/rollup-linux-arm64-gnu": "4.35.0", - "@rollup/rollup-linux-arm64-musl": "4.35.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.35.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0", - "@rollup/rollup-linux-riscv64-gnu": "4.35.0", - "@rollup/rollup-linux-s390x-gnu": "4.35.0", - "@rollup/rollup-linux-x64-gnu": "4.35.0", - "@rollup/rollup-linux-x64-musl": "4.35.0", - "@rollup/rollup-win32-arm64-msvc": "4.35.0", - "@rollup/rollup-win32-ia32-msvc": "4.35.0", - "@rollup/rollup-win32-x64-msvc": "4.35.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==" - }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/sass": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.4.tgz", - "integrity": "sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==", - "devOptional": true, - "dependencies": { - "chokidar": "^4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" - }, - "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==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "engines": { - "node": ">=0.10.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, - "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 - }, - "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, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", - "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", - "dev": true - }, - "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==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", - "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.padend": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", - "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" - }, - "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, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "dev": true - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" - }, - "node_modules/tosource": { - "version": "2.0.0-alpha.3", - "resolved": "https://registry.npmjs.org/tosource/-/tosource-2.0.0-alpha.3.tgz", - "integrity": "sha512-KAB2lrSS48y91MzFPFuDg4hLbvDiyTjOVgaK7Erw+5AmZXNq4sFRVn8r6yxSLuNs15PaokrDRpS61ERY9uZOug==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "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, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/vite": { - "version": "6.2.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz", - "integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==", - "dependencies": { - "esbuild": "^0.25.0", - "postcss": "^8.5.3", - "rollup": "^4.30.1" - }, - "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/vite-plugin-svgr": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.3.0.tgz", - "integrity": "sha512-Jy9qLB2/PyWklpYy0xk0UU3TlU0t2UMpJXZvf+hWII1lAmRHrOUKi11Uw8N3rxoNk7atZNYO3pR3vI1f7oi+6w==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^5.1.3", - "@svgr/core": "^8.1.0", - "@svgr/plugin-jsx": "^8.1.0" - }, - "peerDependencies": { - "vite": ">=2.6.0" - } - }, - "node_modules/void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.0.tgz", - "integrity": "sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA==", - "dependencies": { - "call-bind": "^1.0.7", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "node_modules/yaml": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", - "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} +{ + "name": "vrct", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vrct", + "version": "0.0.0", + "dependencies": { + "@babel/standalone": "7.27.0", + "@emotion/react": "11.14.0", + "@emotion/styled": "11.14.0", + "@mui/material": "7.0.2", + "@tauri-apps/api": "2.5.0", + "@tauri-apps/plugin-fs": "2.2.1", + "@tauri-apps/plugin-global-shortcut": "2.2.0", + "@tauri-apps/plugin-http": "2.4.3", + "@tauri-apps/plugin-opener": "2.2.6", + "@tauri-apps/plugin-shell": "2.2.1", + "clsx": "2.1.1", + "i18next": "25.0.1", + "jotai": "2.12.3", + "js-base64": "3.7.7", + "jszip": "3.10.1", + "react": "18.3.1", + "react-dom": "18.3.1", + "react-error-boundary": "5.0.0", + "react-i18next": "15.5.1", + "react-resizable-layout": "0.7.2", + "sass": "1.79.4", + "semver": "7.7.1" + }, + "devDependencies": { + "@rollup/plugin-yaml": "4.1.2", + "@tauri-apps/cli": "2.5.0", + "@vitejs/plugin-react": "4.4.1", + "eslint": "9.25.1", + "eslint-plugin-react": "7.37.5", + "npm-run-all": "4.1.5", + "source-map": "0.7.4", + "vite": "6.3.4", + "vite-plugin-svgr": "4.3.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "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.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", + "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "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-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/standalone": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/standalone/-/standalone-7.27.0.tgz", + "integrity": "sha512-UxFDpi+BuSz6Q1X73P3ZSM1CB7Nbbqys+7COi/tdouRuaqRsJ6GAzUyxTswbqItHSItVY3frQdd+paBHHGEk9g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", + "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", + "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", + "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", + "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", + "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", + "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", + "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", + "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", + "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", + "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", + "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", + "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", + "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", + "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", + "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", + "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", + "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", + "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", + "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", + "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", + "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", + "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", + "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", + "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", + "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz", + "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", + "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz", + "integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.13.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.0.2.tgz", + "integrity": "sha512-TfeFU9TgN1N06hyb/pV/63FfO34nijZRMqgHk0TJ3gkl4Fbd+wZ73+ZtOd7jag6hMmzO9HSrBc6Vdn591nhkAg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.0.2.tgz", + "integrity": "sha512-rjJlJ13+3LdLfobRplkXbjIFEIkn6LgpetgU/Cs3Xd8qINCCQK9qXQIjjQ6P0FXFTPFzEVMj0VgBR1mN+FhOcA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.0", + "@mui/core-downloads-tracker": "^7.0.2", + "@mui/system": "^7.0.2", + "@mui/types": "^7.4.1", + "@mui/utils": "^7.0.2", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.1.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^7.0.2", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", + "license": "MIT" + }, + "node_modules/@mui/private-theming": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.0.2.tgz", + "integrity": "sha512-6lt8heDC9wN8YaRqEdhqnm0cFCv08AMf4IlttFvOVn7ZdKd81PNpD/rEtPGLLwQAFyyKSxBG4/2XCgpbcdNKiA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.0", + "@mui/utils": "^7.0.2", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.0.2.tgz", + "integrity": "sha512-11Bt4YdHGlh7sB8P75S9mRCUxTlgv7HGbr0UKz6m6Z9KLeiw1Bm9y/t3iqLLVMvSHYB6zL8X8X+LmfTE++gyBw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.0", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.0.2.tgz", + "integrity": "sha512-yFUraAWYWuKIISPPEVPSQ1NLeqmTT4qiQ+ktmyS8LO/KwHxB+NNVOacEZaIofh5x1NxY8rzphvU5X2heRZ/RDA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.0", + "@mui/private-theming": "^7.0.2", + "@mui/styled-engine": "^7.0.2", + "@mui/types": "^7.4.1", + "@mui/utils": "^7.0.2", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.1.tgz", + "integrity": "sha512-gUL8IIAI52CRXP/MixT1tJKt3SI6tVv4U/9soFsTtAsHzaJQptZ42ffdHZV3niX1ei0aUgMvOxBBN0KYqdG39g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.0" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.0.2.tgz", + "integrity": "sha512-72gcuQjPzhj/MLmPHLCgZjy2VjOH4KniR/4qRtXTTXIEwbkgcN+Y5W/rC90rWtMmZbjt9svZev/z+QHUI4j74w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.0", + "@mui/types": "^7.4.1", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", + "license": "MIT" + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/plugin-yaml": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-yaml/-/plugin-yaml-4.1.2.tgz", + "integrity": "sha512-RpupciIeZMUqhgFE97ba0s98mOFS7CWzN3EJNhJkqSv9XLlWYtwVdtE6cDw6ASOF/sZVFS7kRJXftaqM2Vakdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "js-yaml": "^4.1.0", + "tosource": "^2.0.0-alpha.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@tauri-apps/api": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.5.0.tgz", + "integrity": "sha512-Ldux4ip+HGAcPUmuLT8EIkk6yafl5vK0P0c0byzAKzxJh7vxelVtdPONjfgTm96PbN24yjZNESY8CKo8qniluA==", + "license": "Apache-2.0 OR MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + } + }, + "node_modules/@tauri-apps/cli": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.5.0.tgz", + "integrity": "sha512-rAtHqG0Gh/IWLjN2zTf3nZqYqbo81oMbqop56rGTjrlWk9pTTAjkqOjSL9XQLIMZ3RbeVjveCqqCA0s8RnLdMg==", + "dev": true, + "license": "Apache-2.0 OR MIT", + "bin": { + "tauri": "tauri.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + }, + "optionalDependencies": { + "@tauri-apps/cli-darwin-arm64": "2.5.0", + "@tauri-apps/cli-darwin-x64": "2.5.0", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.5.0", + "@tauri-apps/cli-linux-arm64-gnu": "2.5.0", + "@tauri-apps/cli-linux-arm64-musl": "2.5.0", + "@tauri-apps/cli-linux-riscv64-gnu": "2.5.0", + "@tauri-apps/cli-linux-x64-gnu": "2.5.0", + "@tauri-apps/cli-linux-x64-musl": "2.5.0", + "@tauri-apps/cli-win32-arm64-msvc": "2.5.0", + "@tauri-apps/cli-win32-ia32-msvc": "2.5.0", + "@tauri-apps/cli-win32-x64-msvc": "2.5.0" + } + }, + "node_modules/@tauri-apps/cli-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-VuVAeTFq86dfpoBDNYAdtQVLbP0+2EKCHIIhkaxjeoPARR0sLpFHz2zs0PcFU76e+KAaxtEtAJAXGNUc8E1PzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.5.0.tgz", + "integrity": "sha512-hUF01sC06cZVa8+I0/VtsHOk9BbO75rd+YdtHJ48xTdcYaQ5QIwL4yZz9OR1AKBTaUYhBam8UX9Pvd5V2/4Dpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.5.0.tgz", + "integrity": "sha512-LQKqttsK252LlqYyX8R02MinUsfFcy3+NZiJwHFgi5Y3+ZUIAED9cSxJkyNtuY5KMnR4RlpgWyLv4P6akN1xhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-gnu": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.5.0.tgz", + "integrity": "sha512-mTQufsPcpdHg5RW0zypazMo4L55EfeE5snTzrPqbLX4yCK2qalN7+rnP8O8GT06xhp6ElSP/Ku1M2MR297SByQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-rQO1HhRUQqyEaal5dUVOQruTRda/TD36s9kv1hTxZiFuSq3558lsTjAcUEnMAtBcBkps20sbyTJNMT0AwYIk8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.5.0.tgz", + "integrity": "sha512-7oS18FN46yDxyw1zX/AxhLAd7T3GrLj3Ai6s8hZKd9qFVzrAn36ESL7d3G05s8wEtsJf26qjXnVF4qleS3dYsA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-gnu": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.5.0.tgz", + "integrity": "sha512-SG5sFNL7VMmDBdIg3nO3EzNRT306HsiEQ0N90ILe3ZABYAVoPDO/ttpCO37ApLInTzrq/DLN+gOlC/mgZvLw1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-QXDM8zp/6v05PNWju5ELsVwF0VH1n6b5pk2E6W/jFbbiwz80Vs1lACl9pv5kEHkrxBj+aWU/03JzGuIj2g3SkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-arm64-msvc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.5.0.tgz", + "integrity": "sha512-pFSHFK6b+o9y4Un8w0gGLwVyFTZaC3P0kQ7umRt/BLDkzD5RnQ4vBM7CF8BCU5nkwmEBUCZd7Wt3TWZxe41o6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-ia32-msvc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.5.0.tgz", + "integrity": "sha512-EArv1IaRlogdLAQyGlKmEqZqm5RfHCUMhJoedWu7GtdbOMUfSAz6FMX2boE1PtEmNO4An+g188flLeVErrxEKg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-x64-msvc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.5.0.tgz", + "integrity": "sha512-lj43EFYbnAta8pd9JnUq87o+xRUR0odz+4rixBtTUwUgdRdwQ2V9CzFtsMu6FQKpFQ6mujRK6P1IEwhL6ADRsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/plugin-fs": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-fs/-/plugin-fs-2.2.1.tgz", + "integrity": "sha512-KdGzvvA4Eg0Dhw55MwczFbjxLxsTx0FvwwC/0StXlr6IxwPUxh5ziZQoaugkBFs8t+wfebdQrjBEzd8NmmDXNw==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-global-shortcut": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-global-shortcut/-/plugin-global-shortcut-2.2.0.tgz", + "integrity": "sha512-clI9Bg/BcxWXNDK+ij601o1qC2WxMEy8ovhGgEW5Ai17oPy0KK8uwzmc59KiVnOYKpBWHCUPqBxG+KBNUFXgzw==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-http": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-http/-/plugin-http-2.4.3.tgz", + "integrity": "sha512-Us8X+FikzpaZRNr4kH4HLwyXascHbM42p6LxAqRTQnHPrrqp1usaH4vxWAZalPvTbHJ3gBEMJPHusFJgtjGJjA==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-opener": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.2.6.tgz", + "integrity": "sha512-bSdkuP71ZQRepPOn8BOEdBKYJQvl6+jb160QtJX/i2H9BF6ZySY/kYljh76N2Ne5fJMQRge7rlKoStYQY5Jq1w==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-shell": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.2.1.tgz", + "integrity": "sha512-G1GFYyWe/KlCsymuLiNImUgC8zGY0tI0Y3p8JgBCWduR5IEXlIJS+JuG1qtveitwYXlfJrsExt3enhv5l2/yhA==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", + "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz", + "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.10", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-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/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-macros/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/babel-plugin-macros/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-plugin-macros/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-macros/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "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/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001715", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz", + "integrity": "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "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/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/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/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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cosmiconfig/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" + } + }, + "node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/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_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.143", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.143.tgz", + "integrity": "sha512-QqklJMOFBMqe46k8iIOwA9l2hz57V2OKMmP5eSWcUvwx+mASAsbU+wkF1pHjn9ZVSBPrsYWr4/W/95y5SwYg2g==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", + "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.3", + "@esbuild/android-arm": "0.25.3", + "@esbuild/android-arm64": "0.25.3", + "@esbuild/android-x64": "0.25.3", + "@esbuild/darwin-arm64": "0.25.3", + "@esbuild/darwin-x64": "0.25.3", + "@esbuild/freebsd-arm64": "0.25.3", + "@esbuild/freebsd-x64": "0.25.3", + "@esbuild/linux-arm": "0.25.3", + "@esbuild/linux-arm64": "0.25.3", + "@esbuild/linux-ia32": "0.25.3", + "@esbuild/linux-loong64": "0.25.3", + "@esbuild/linux-mips64el": "0.25.3", + "@esbuild/linux-ppc64": "0.25.3", + "@esbuild/linux-riscv64": "0.25.3", + "@esbuild/linux-s390x": "0.25.3", + "@esbuild/linux-x64": "0.25.3", + "@esbuild/netbsd-arm64": "0.25.3", + "@esbuild/netbsd-x64": "0.25.3", + "@esbuild/openbsd-arm64": "0.25.3", + "@esbuild/openbsd-x64": "0.25.3", + "@esbuild/sunos-x64": "0.25.3", + "@esbuild/win32-arm64": "0.25.3", + "@esbuild/win32-ia32": "0.25.3", + "@esbuild/win32-x64": "0.25.3" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/eslint": { + "version": "9.25.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz", + "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.13.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.25.1", + "@eslint/plugin-kit": "^0.2.8", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/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/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/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" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/cross-spawn": { + "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", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/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/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "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/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, + "node_modules/i18next": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.0.1.tgz", + "integrity": "sha512-8S8PyZbrymJZn3DaN70/34JYWNhsqrU6yA4MuzcygJBv+41dgNMocEA8h+kV1P7MCc1ll03lOTOIXE7mpNCicw==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.10" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "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/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "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/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jotai": { + "version": "2.12.3", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.12.3.tgz", + "integrity": "sha512-DpoddSkmPGXMFtdfnoIHfueFeGP643nqYUWC6REjUcME+PG2UkAtYnLbffRDw3OURI9ZUTcRWkRGLsOvxuWMCg==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=17.0.0", + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/js-base64": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", + "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==", + "license": "BSD-3-Clause" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/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_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.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==", + "license": "MIT" + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-error-boundary": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-5.0.0.tgz", + "integrity": "sha512-tnjAxG+IkpLephNcePNA7v6F/QpWLH8He65+DmedchDwg162JZqx4NmbXj0mlAYVVEd81OW7aFhmbsScYfiAFQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, + "node_modules/react-i18next": { + "version": "15.5.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.5.1.tgz", + "integrity": "sha512-C8RZ7N7H0L+flitiX6ASjq9p5puVJU1Z8VyL3OgM/QOMRf40BMZX+5TkpxzZVcTmOLPX5zlti4InEX5pFyiVeA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.0", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0", + "typescript": "^5" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "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/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-resizable-layout": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/react-resizable-layout/-/react-resizable-layout-0.7.2.tgz", + "integrity": "sha512-GrVzAecB6+osdAx5PPP3G8R9n7A2uDd3NL+PyrHWNRaVBivZmW/o0+yFjQdS5Bo016A2fIP11fAhefsknWN4aw==", + "license": "MIT", + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "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/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rollup": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sass": { + "version": "1.79.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.4.tgz", + "integrity": "sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true, + "license": "CC0-1.0" + }, + "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==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.padend": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "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/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tosource": { + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/tosource/-/tosource-2.0.0-alpha.3.tgz", + "integrity": "sha512-KAB2lrSS48y91MzFPFuDg4hLbvDiyTjOVgaK7Erw+5AmZXNq4sFRVn8r6yxSLuNs15PaokrDRpS61ERY9uZOug==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/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/vite": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.4.tgz", + "integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "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/vite-plugin-svgr": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.3.0.tgz", + "integrity": "sha512-Jy9qLB2/PyWklpYy0xk0UU3TlU0t2UMpJXZvf+hWII1lAmRHrOUKi11Uw8N3rxoNk7atZNYO3pR3vI1f7oi+6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.3", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" + }, + "peerDependencies": { + "vite": ">=2.6.0" + } + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index f9948b13..a2d9030f 100644 --- a/package.json +++ b/package.json @@ -1,55 +1,60 @@ -{ - "name": "tauri-app", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "setup-python": "install.bat", - "build-python": "build.bat", - "build-python-cuda": "build_cuda.bat", - "vite": "vite", - "vite-build": "vite build", - "vite-preview": "vite preview", - "tauri": "tauri", - "tauri-dev": "tauri dev", - "clean": "python clean.py", - "dev": "npm run build-python && npm run dev-ui", - "dev-cuda": "npm run build-python-cuda && npm run dev-ui", - "dev-ui": "npm-run-all --parallel vite tauri-dev", - "build": "npm run clean && npm run build-python && npm run vite-build && npm run tauri build", - "build-cuda": "npm run clean && npm run build-python-cuda && npm run vite-build && npm run tauri build", - "release": "npm run build && python zip.py --zip_name VRCT.zip", - "release-cuda": "npm run build-cuda && python zip.py --zip_name VRCT_cuda.zip", - "release-all": "npm run release && npm run release-cuda" - }, - "dependencies": { - "@babel/standalone": "7.26.9", - "@emotion/react": "11.14.0", - "@emotion/styled": "11.14.0", - "@mui/material": "6.2.0", - "@tauri-apps/api": "1.6.0", - "@vitejs/plugin-react": "4.3.4", - "clsx": "2.1.1", - "eslint": "8.57.0", - "eslint-plugin-react": "7.37.2", - "i18next": "24.1.0", - "jotai": "2.10.3", - "js-base64": "3.7.7", - "jszip": "3.10.1", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-error-boundary": "5.0.0", - "react-i18next": "15.2.0", - "react-resizable-layout": "0.7.2", - "semver": "7.7.1" - }, - "devDependencies": { - "@rollup/plugin-yaml": "^4.1.2", - "@tauri-apps/cli": "1.6.3", - "npm-run-all": "4.1.5", - "sass": "1.79.4", - "source-map": "0.7.4", - "vite": "6.2.5", - "vite-plugin-svgr": "4.3.0" - } -} +{ + "name": "vrct", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "setup-python": "install.bat", + "build-python": "build.bat", + "build-python-cuda": "build_cuda.bat", + "vite": "vite", + "vite-build": "vite build", + "vite-preview": "vite preview", + "tauri": "tauri", + "tauri-dev": "tauri dev", + "clean": "python clean.py", + "dev": "npm run build-python && npm run dev-ui", + "dev-cuda": "npm run build-python-cuda && npm run dev-ui", + "dev-ui": "npm-run-all --parallel vite tauri-dev", + "build": "npm run clean && npm run build-python && npm run vite-build && npm run tauri build", + "build-cuda": "npm run clean && npm run build-python-cuda && npm run vite-build && npm run tauri build", + "release": "npm run build && python zip.py --zip_name VRCT.zip", + "release-cuda": "npm run build-cuda && python zip.py --zip_name VRCT_cuda.zip", + "release-all": "npm run release && npm run release-cuda" + }, + "dependencies": { + "@babel/standalone": "7.27.0", + "@emotion/react": "11.14.0", + "@emotion/styled": "11.14.0", + "@mui/material": "7.0.2", + "@tauri-apps/api": "2.5.0", + "@tauri-apps/plugin-fs": "2.2.1", + "@tauri-apps/plugin-global-shortcut": "2.2.0", + "@tauri-apps/plugin-http": "2.4.3", + "@tauri-apps/plugin-opener": "2.2.6", + "@tauri-apps/plugin-shell": "2.2.1", + "clsx": "2.1.1", + "i18next": "25.0.1", + "jotai": "2.12.3", + "js-base64": "3.7.7", + "jszip": "3.10.1", + "react": "18.3.1", + "react-dom": "18.3.1", + "react-error-boundary": "5.0.0", + "react-i18next": "15.5.1", + "react-resizable-layout": "0.7.2", + "sass": "1.79.4", + "semver": "7.7.1" + }, + "devDependencies": { + "@rollup/plugin-yaml": "4.1.2", + "@tauri-apps/cli": "2.5.0", + "@vitejs/plugin-react": "4.4.1", + "eslint": "9.25.1", + "eslint-plugin-react": "7.37.5", + "npm-run-all": "4.1.5", + "source-map": "0.7.4", + "vite": "6.3.4", + "vite-plugin-svgr": "4.3.0" + } +} diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 861e96ec..8dc64208 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "VRCT" @@ -13,7 +13,11 @@ dependencies = [ "serde_json", "tauri", "tauri-build", - "window-shadows", + "tauri-plugin-fs", + "tauri-plugin-global-shortcut", + "tauri-plugin-http", + "tauri-plugin-opener", + "tauri-plugin-shell", ] [[package]] @@ -72,34 +76,183 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 0.38.44", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 0.38.44", + "tracing", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.44", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] [[package]] name = "atk" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" dependencies = [ "atk-sys", - "bitflags 1.3.2", "glib", "libc", ] [[package]] name = "atk-sys" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.2.2", + "system-deps", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.4.0" @@ -121,12 +274,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -147,15 +294,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +dependencies = [ + "serde", +] [[package]] name = "block-buffer" @@ -167,10 +311,41 @@ dependencies = [ ] [[package]] -name = "brotli" -version = "6.0.0" +name = "block2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.2", +] + +[[package]] +name = "block2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" +dependencies = [ + "objc2 0.6.1", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -179,35 +354,25 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.1" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", ] -[[package]] -name = "bstr" -version = "1.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.21.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" [[package]] name = "byteorder" @@ -217,52 +382,85 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] [[package]] name = "cairo-rs" -version = "0.15.12" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "cairo-sys-rs", "glib", "libc", - "thiserror", + "once_cell", + "thiserror 1.0.69", ] [[package]] name = "cairo-sys-rs" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" dependencies = [ "glib-sys", "libc", - "system-deps 6.2.2", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.12", ] [[package]] name = "cargo_toml" -version = "0.15.3" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "599aa35200ffff8f04c1925aa1acc92fa2e08874379ef42e210a80e527e60838" +checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257" dependencies = [ "serde", - "toml 0.7.8", + "toml", ] [[package]] name = "cc" -version = "1.2.7" +version = "1.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" dependencies = [ "shlex", ] @@ -284,15 +482,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "cfg-expr" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7" -dependencies = [ - "smallvec", -] - [[package]] name = "cfg-expr" version = "0.15.8" @@ -310,70 +499,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "chrono" -version = "0.4.39" +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.6", + "windows-link", ] -[[package]] -name = "cocoa" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics 0.22.3", - "foreign-types 0.3.2", - "libc", - "objc", -] - -[[package]] -name = "cocoa" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics 0.23.2", - "foreign-types 0.5.0", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "libc", - "objc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "combine" version = "4.6.7" @@ -384,12 +527,50 @@ dependencies = [ "memchr", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" +dependencies = [ + "cookie", + "document-features", + "idna", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -400,25 +581,22 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types 0.3.2", - "libc", -] - [[package]] name = "core-graphics" version = "0.23.2" @@ -426,8 +604,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", "foreign-types 0.5.0", "libc", ] @@ -439,7 +630,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.10.0", "libc", ] @@ -449,7 +651,7 @@ version = "20.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" dependencies = [ - "core-foundation", + "core-foundation 0.9.4", "core-graphics 0.23.2", "foreign-types 0.5.0", "libc", @@ -457,9 +659,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -475,28 +677,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -541,7 +724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] @@ -551,14 +734,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -566,34 +749,40 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] -name = "deranged" -version = "0.3.11" +name = "data-url" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", @@ -601,15 +790,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.18" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] @@ -628,17 +817,16 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.1", ] [[package]] -name = "dirs-next" -version = "2.0.0" +name = "dirs" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "cfg-if", - "dirs-sys-next", + "dirs-sys 0.5.0", ] [[package]] @@ -649,19 +837,20 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.6", "windows-sys 0.48.0", ] [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "dirs-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", - "redox_users", - "winapi", + "option-ext", + "redox_users 0.5.0", + "windows-sys 0.59.0", ] [[package]] @@ -670,6 +859,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.1", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -678,7 +877,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] @@ -687,14 +886,55 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading", + "libloading 0.8.6", +] + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "document-features" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", +] + +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +dependencies = [ + "serde", ] [[package]] name = "dtoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" +checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" [[package]] name = "dtoa-short" @@ -713,9 +953,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dwrote" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70182709525a3632b2ba96b6569225467b18ecb4a77f46d255f713a6bebf05fd" +checksum = "bfe1f192fcce01590bd8d839aca53ce0d11d803bf291b2a6c4ad925a8f0024be" dependencies = [ "lazy_static", "libc", @@ -724,17 +964,23 @@ dependencies = [ ] [[package]] -name = "embed-resource" -version = "2.5.1" +name = "dyn-clone" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68b6f9f63a0b6a38bc447d4ce84e2b388f3ec95c99c641c8ff0dd3ef89a6379" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + +[[package]] +name = "embed-resource" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbc6e0d8e0c03a655b53ca813f0463d2c956bc4db8138dbc89f120b066551e3" dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.8.19", + "toml", "vswhom", - "winreg 0.52.0", + "winreg", ] [[package]] @@ -753,21 +999,79 @@ dependencies = [ ] [[package]] -name = "equivalent" -version = "1.0.1" +name = "endi" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +dependencies = [ + "serde", + "typeid", +] [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", ] +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -793,23 +1097,11 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "filetime" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.59.0", -] - [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -821,15 +1113,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" -[[package]] -name = "fluent-uri" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "fnv" version = "1.0.7" @@ -842,12 +1125,12 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b64b34f4efd515f905952d91bc185039863705592c0c53ae6d979805dd154520" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "byteorder", - "core-foundation", + "core-foundation 0.9.4", "core-graphics 0.23.2", "core-text", - "dirs", + "dirs 5.0.1", "dwrote", "float-ord", "freetype-sys", @@ -888,7 +1171,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] @@ -965,6 +1248,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -973,7 +1269,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] @@ -1016,11 +1312,10 @@ dependencies = [ [[package]] name = "gdk" -version = "0.15.4" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" dependencies = [ - "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", @@ -1032,35 +1327,35 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.15.11" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" dependencies = [ - "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", "libc", + "once_cell", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" dependencies = [ "gio-sys", "glib-sys", "gobject-sys", "libc", - "system-deps 6.2.2", + "system-deps", ] [[package]] name = "gdk-sys" -version = "0.15.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1070,47 +1365,48 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps 6.2.2", + "system-deps", ] [[package]] name = "gdkwayland-sys" -version = "0.15.3" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca49a59ad8cfdf36ef7330fe7bdfbe1d34323220cc16a0de2679ee773aee2c2" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" dependencies = [ "gdk-sys", "glib-sys", "gobject-sys", "libc", "pkg-config", - "system-deps 6.2.2", + "system-deps", ] [[package]] -name = "gdkx11-sys" -version = "0.15.1" +name = "gdkx11" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" dependencies = [ - "gdk-sys", - "glib-sys", + "gdk", + "gdkx11-sys", + "gio", + "glib", "libc", - "system-deps 6.2.2", "x11", ] [[package]] -name = "generator" -version = "0.7.5" +name = "gdkx11-sys" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" dependencies = [ - "cc", + "gdk-sys", + "glib-sys", "libc", - "log", - "rustversion", - "windows 0.48.0", + "system-deps", + "x11", ] [[package]] @@ -1136,13 +1432,29 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -1153,77 +1465,81 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "gio" -version = "0.15.12" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" dependencies = [ - "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", + "futures-util", "gio-sys", "glib", "libc", "once_cell", - "thiserror", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", ] [[package]] name = "gio-sys" -version = "0.15.10" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.2.2", + "system-deps", "winapi", ] [[package]] name = "glib" -version = "0.15.12" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "futures-channel", "futures-core", "futures-executor", "futures-task", + "futures-util", + "gio-sys", "glib-macros", "glib-sys", "gobject-sys", "libc", + "memchr", "once_cell", "smallvec", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "glib-macros" -version = "0.15.13" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ - "anyhow", "heck 0.4.1", - "proc-macro-crate", + "proc-macro-crate 2.0.0", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.101", ] [[package]] name = "glib-sys" -version = "0.15.10" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" dependencies = [ "libc", - "system-deps 6.2.2", + "system-deps", ] [[package]] @@ -1233,37 +1549,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] -name = "globset" -version = "0.4.15" +name = "global-hotkey" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +checksum = "41fbb3a4e56c901ee66c190fdb3fa08344e6d09593cc6c61f8eb9add7144b271" dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", + "crossbeam-channel", + "keyboard-types", + "objc2 0.6.1", + "objc2-app-kit", + "once_cell", + "serde", + "thiserror 2.0.12", + "windows-sys 0.59.0", + "x11-dl", ] [[package]] name = "gobject-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" dependencies = [ "glib-sys", "libc", - "system-deps 6.2.2", + "system-deps", ] [[package]] name = "gtk" -version = "0.15.5" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" dependencies = [ "atk", - "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", @@ -1274,16 +1593,15 @@ dependencies = [ "gtk-sys", "gtk3-macros", "libc", - "once_cell", "pango", "pkg-config", ] [[package]] name = "gtk-sys" -version = "0.15.3" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" dependencies = [ "atk-sys", "cairo-sys-rs", @@ -1294,36 +1612,35 @@ dependencies = [ "gobject-sys", "libc", "pango-sys", - "system-deps 6.2.2", + "system-deps", ] [[package]] name = "gtk3-macros" -version = "0.15.6" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" dependencies = [ - "anyhow", - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.101", ] [[package]] name = "h2" -version = "0.3.26" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http", - "indexmap 2.7.0", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -1338,18 +1655,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" [[package]] name = "heck" @@ -1363,6 +1671,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -1385,107 +1699,131 @@ dependencies = [ [[package]] name = "http" -version = "0.2.12" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", - "itoa 1.0.14", + "itoa 1.0.15", ] [[package]] name = "http-body" -version = "0.4.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", "pin-project-lite", ] -[[package]] -name = "http-range" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" - [[package]] name = "httparse" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "0.14.32" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "h2", "http", "http-body", "httparse", - "httpdate", - "itoa 1.0.14", + "itoa 1.0.15", "pin-project-lite", - "socket2", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", "http", "hyper", + "hyper-util", "rustls", + "rustls-pki-types", "tokio", "tokio-rustls", + "tower-service", + "webpki-roots", ] [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", + "http-body-util", "hyper", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", ] [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "windows-core", + "windows-core 0.60.1", ] [[package]] @@ -1499,9 +1837,9 @@ dependencies = [ [[package]] name = "ico" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" +checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" dependencies = [ "byteorder", "png", @@ -1548,9 +1886,9 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" @@ -1572,9 +1910,9 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" @@ -1593,9 +1931,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" @@ -1622,7 +1960,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] @@ -1652,34 +1990,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "ignore" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata 0.4.9", - "same-file", - "walkdir", - "winapi-util", -] - -[[package]] -name = "image" -version = "0.24.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "num-traits", -] - [[package]] name = "indexmap" version = "1.9.3" @@ -1693,39 +2003,49 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "serde", ] [[package]] name = "infer" -version = "0.13.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" dependencies = [ "cfb", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "ipnet" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "itoa" version = "0.4.8" @@ -1734,15 +2054,15 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "javascriptcore-rs" -version = "0.16.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" dependencies = [ "bitflags 1.3.2", "glib", @@ -1751,28 +2071,30 @@ dependencies = [ [[package]] name = "javascriptcore-rs-sys" -version = "0.4.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 5.0.0", + "system-deps", ] [[package]] name = "jni" -version = "0.20.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ "cesu8", + "cfg-if", "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.69", "walkdir", + "windows-sys 0.45.0", ] [[package]] @@ -1783,9 +2105,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -1793,27 +2115,37 @@ dependencies = [ [[package]] name = "json-patch" -version = "2.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" dependencies = [ "jsonptr", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "jsonptr" -version = "0.4.7" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" dependencies = [ - "fluent-uri", "serde", "serde_json", ] +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.9.0", + "serde", + "unicode-segmentation", +] + [[package]] name = "kuchikiki" version = "0.8.2" @@ -1834,10 +2166,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] -name = "libc" -version = "0.2.169" +name = "libappindicator" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading 0.7.4", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] [[package]] name = "libloading" @@ -1855,22 +2221,33 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "libc", - "redox_syscall", ] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" @@ -1884,24 +2261,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "loom" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "mac" @@ -1909,15 +2271,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - [[package]] name = "markup5ever" version = "0.11.0" @@ -1932,15 +2285,6 @@ dependencies = [ "tendril", ] -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - [[package]] name = "matches" version = "0.1.10" @@ -1970,9 +2314,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.2" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "simd-adler32", @@ -1990,10 +2334,31 @@ dependencies = [ ] [[package]] -name = "native-tls" -version = "0.2.12" +name = "muda" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "4de14a9b5d569ca68d7c891d613b390cf5ab4f851c77aaa2f9e435555d3d9492" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2 0.6.1", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "once_cell", + "png", + "serde", + "thiserror 2.0.12", + "windows-sys 0.59.0", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", @@ -2008,15 +2373,17 @@ dependencies = [ [[package]] name = "ndk" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.0", "jni-sys", + "log", "ndk-sys", "num_enum", - "thiserror", + "raw-window-handle", + "thiserror 1.0.69", ] [[package]] @@ -2027,9 +2394,9 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.3.0" +version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ "jni-sys", ] @@ -2040,22 +2407,25 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "nodrop" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - [[package]] name = "num-conv" version = "0.1.0" @@ -2073,51 +2443,237 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.101", ] [[package]] -name = "objc" -version = "0.2.7" +name = "objc-sys" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" dependencies = [ - "malloc_buf", - "objc_exception", + "objc-sys", + "objc2-encode", ] [[package]] -name = "objc_exception" -version = "0.1.2" +name = "objc2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.1", + "libc", + "objc2 0.6.1", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-foundation 0.3.1", + "objc2-quartz-core 0.3.1", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.1", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.1", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags 2.9.0", + "dispatch2", + "objc2 0.6.1", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" +dependencies = [ + "bitflags 2.9.0", + "dispatch2", + "objc2 0.6.1", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e" +dependencies = [ + "objc2 0.6.1", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" dependencies = [ "cc", ] [[package]] -name = "objc_id" -version = "0.1.1" +name = "objc2-foundation" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "objc", + "bitflags 2.9.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.1", + "libc", + "objc2 0.6.1", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.1", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.9.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.9.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.1", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b1312ad7bc8a0e92adae17aa10f90aae1fb618832f9b993b022b591027daed" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.1", + "objc2-core-foundation", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91672909de8b1ce1c2252e95bbee8c1649c9ad9d14b9248b3d7b4c47903c47ad" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.1", + "objc2 0.6.1", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", ] [[package]] @@ -2131,27 +2687,29 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "open" -version = "3.2.0" +version = "5.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" dependencies = [ + "dunce", + "is-wsl", + "libc", "pathdiff", - "windows-sys 0.42.0", ] [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -2168,20 +2726,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" dependencies = [ "cc", "libc", @@ -2195,6 +2753,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "os_pipe" version = "1.2.1" @@ -2205,19 +2773,13 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "pango" -version = "0.15.10" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" dependencies = [ - "bitflags 1.3.2", + "gio", "glib", "libc", "once_cell", @@ -2226,16 +2788,22 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.15.10" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.2.2", + "system-deps", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -2312,12 +2880,12 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros 0.11.2", - "phf_shared 0.11.2", + "phf_macros 0.11.3", + "phf_shared 0.11.3", ] [[package]] @@ -2362,11 +2930,11 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.2", + "phf_shared 0.11.3", "rand 0.8.5", ] @@ -2386,15 +2954,15 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", + "phf_generator 0.11.3", + "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] @@ -2403,7 +2971,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" dependencies = [ - "siphasher", + "siphasher 0.3.11", ] [[package]] @@ -2412,23 +2980,23 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ - "siphasher", + "siphasher 0.3.11", ] [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher", + "siphasher 1.0.1", ] [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -2437,19 +3005,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "pkg-config" -version = "0.3.31" +name = "piper" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" dependencies = [ "base64 0.22.1", - "indexmap 2.7.0", + "indexmap 2.9.0", "quick-xml", "serde", "time", @@ -2468,6 +3047,21 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "3.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 0.38.44", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2476,9 +3070,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] @@ -2499,6 +3093,24 @@ dependencies = [ "toml_edit 0.19.15", ] +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit 0.22.26", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -2531,13 +3143,29 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna", + "psl-types", +] + [[package]] name = "quick-xml" version = "0.32.0" @@ -2548,14 +3176,74 @@ dependencies = [ ] [[package]] -name = "quote" -version = "1.0.38" +name = "quinn" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.12", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcbafbbdbb0f638fe3f35f3c56739f77a8a1d070cb25603226c83339b391472b" +dependencies = [ + "bytes", + "getrandom 0.3.2", + "rand 0.9.1", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.12", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.7.3" @@ -2581,6 +3269,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -2601,6 +3299,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -2616,7 +3324,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", ] [[package]] @@ -2639,17 +3356,17 @@ dependencies = [ [[package]] name = "raw-window-handle" -version = "0.5.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", ] [[package]] @@ -2658,9 +3375,20 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", - "thiserror", + "thiserror 1.0.69", +] + +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 2.0.12", ] [[package]] @@ -2671,17 +3399,8 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -2692,15 +3411,9 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.5" @@ -2709,21 +3422,25 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", + "cookie", + "cookie_store", "encoding_rs", "futures-core", "futures-util", "h2", "http", "http-body", + "http-body-util", "hyper", "hyper-rustls", "hyper-tls", + "hyper-util", "ipnet", "js-sys", "log", @@ -2732,8 +3449,10 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "quinn", "rustls", "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", @@ -2743,6 +3462,7 @@ dependencies = [ "tokio-native-tls", "tokio-rustls", "tokio-util", + "tower", "tower-service", "url", "wasm-bindgen", @@ -2750,20 +3470,19 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots", - "winreg 0.50.0", + "windows-registry", ] [[package]] name = "ring" -version = "0.17.8" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -2774,6 +3493,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -2785,59 +3510,84 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.21.12" +version = "0.23.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" dependencies = [ - "log", + "once_cell", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.21.7", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +dependencies = [ + "web-time", ] [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.103.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -2850,18 +3600,39 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ "windows-sys 0.59.0", ] [[package]] -name = "scoped-tls" -version = "1.0.1" +name = "schemars" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.101", +] [[package]] name = "scopeguard" @@ -2869,24 +3640,14 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "security-framework" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", - "core-foundation", + "bitflags 2.9.0", + "core-foundation 0.9.4", "core-foundation-sys", "libc", "security-framework-sys", @@ -2894,9 +3655,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -2924,41 +3685,62 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] -name = "serde_derive" -version = "1.0.217" +name = "serde-untagged" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e" +dependencies = [ + "erased-serde", + "serde", + "typeid", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "indexmap 2.7.0", - "itoa 1.0.14", + "itoa 1.0.15", "memchr", "ryu", "serde", @@ -2966,13 +3748,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] @@ -2991,7 +3773,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.14", + "itoa 1.0.15", "ryu", "serde", ] @@ -3006,7 +3788,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.7.0", + "indexmap 2.9.0", "serde", "serde_derive", "serde_json", @@ -3023,14 +3805,14 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] name = "serialize-to-javascript" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" +checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" dependencies = [ "serde", "serde_json", @@ -3039,13 +3821,13 @@ dependencies = [ [[package]] name = "serialize-to-javascript-impl" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" +checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 1.0.109", ] [[package]] @@ -3060,24 +3842,15 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", "digest", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - [[package]] name = "shared_child" version = "1.0.1" @@ -3094,6 +3867,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +dependencies = [ + "libc", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -3106,6 +3888,12 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -3117,54 +3905,68 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] -name = "soup2" -version = "0.2.1" +name = "softbuffer" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" +checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" dependencies = [ - "bitflags 1.3.2", - "gio", - "glib", - "libc", - "once_cell", - "soup2-sys", + "bytemuck", + "cfg_aliases", + "core-graphics 0.24.0", + "foreign-types 0.5.0", + "js-sys", + "log", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", + "raw-window-handle", + "redox_syscall", + "wasm-bindgen", + "web-sys", + "windows-sys 0.59.0", ] [[package]] -name = "soup2-sys" -version = "0.2.0" +name = "soup3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" dependencies = [ - "bitflags 1.3.2", "gio-sys", "glib-sys", "gobject-sys", "libc", - "system-deps 5.0.0", + "system-deps", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -3172,36 +3974,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] -name = "state" -version = "0.5.3" +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" -dependencies = [ - "loom", -] +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "string_cache" -version = "0.8.7" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", - "once_cell", "parking_lot", - "phf_shared 0.10.0", + "phf_shared 0.11.3", "precomputed-hash", "serde", ] [[package]] name = "string_cache_codegen" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", + "phf_generator 0.11.3", + "phf_shared 0.11.3", "proc-macro2", "quote", ] @@ -3212,6 +4010,23 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + [[package]] name = "syn" version = "1.0.109" @@ -3225,9 +4040,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.94" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -3236,93 +4051,74 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 1.3.2", - "core-foundation", + "bitflags 2.9.0", + "core-foundation 0.9.4", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", ] -[[package]] -name = "system-deps" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e" -dependencies = [ - "cfg-expr 0.9.1", - "heck 0.3.3", - "pkg-config", - "toml 0.5.11", - "version-compare 0.0.11", -] - [[package]] name = "system-deps" version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ - "cfg-expr 0.15.8", + "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.19", - "version-compare 0.2.0", + "toml", + "version-compare", ] [[package]] name = "tao" -version = "0.16.10" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d298c441a1da46e28e8ad8ec205aab7fd8cd71b9d10e05454224eef422e1ae" +checksum = "1e59c1f38e657351a2e822eadf40d6a2ad4627b9c25557bc1180ec1b3295ef82" dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "cc", - "cocoa 0.24.1", - "core-foundation", - "core-graphics 0.22.3", + "bitflags 2.9.0", + "core-foundation 0.10.0", + "core-graphics 0.24.0", "crossbeam-channel", "dispatch", - "gdk", - "gdk-pixbuf", - "gdk-sys", + "dlopen2", + "dpi", "gdkwayland-sys", "gdkx11-sys", - "gio", - "glib", - "glib-sys", "gtk", - "image", - "instant", "jni", "lazy_static", "libc", @@ -3330,18 +4126,19 @@ dependencies = [ "ndk", "ndk-context", "ndk-sys", - "objc", + "objc2 0.6.1", + "objc2-app-kit", + "objc2-foundation 0.3.1", "once_cell", "parking_lot", - "png", "raw-window-handle", "scopeguard", - "serde", "tao-macros", "unicode-segmentation", - "uuid", - "windows 0.39.0", - "windows-implement", + "url", + "windows 0.61.1", + "windows-core 0.61.0", + "windows-version", "x11-dl", ] @@ -3353,18 +4150,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", -] - -[[package]] -name = "tar" -version = "0.4.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" -dependencies = [ - "filetime", - "libc", - "xattr", + "syn 2.0.101", ] [[package]] @@ -3375,86 +4161,84 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "1.8.1" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf327e247698d3f39af8aa99401c9708384290d1f5c544bf5d251d44c2fea22" +checksum = "e7b0bc1aec81bda6bc455ea98fcaed26b3c98c1648c627ad6ff1c704e8bf8cbc" dependencies = [ "anyhow", "bytes", - "cocoa 0.24.1", - "dirs-next", + "dirs 6.0.0", "dunce", "embed_plist", - "encoding_rs", - "flate2", "futures-util", - "getrandom 0.2.15", - "glib", + "getrandom 0.2.16", "glob", "gtk", "heck 0.5.0", "http", - "ignore", - "indexmap 1.9.3", + "jni", + "libc", "log", - "objc", - "once_cell", - "open", - "os_pipe", + "mime", + "muda", + "objc2 0.6.1", + "objc2-app-kit", + "objc2-foundation 0.3.1", + "objc2-ui-kit", "percent-encoding", "plist", - "rand 0.8.5", "raw-window-handle", - "regex", "reqwest", - "semver", "serde", "serde_json", "serde_repr", "serialize-to-javascript", - "shared_child", - "state", - "tar", + "swift-rs", + "tauri-build", "tauri-macros", "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "tempfile", - "thiserror", + "thiserror 2.0.12", "tokio", + "tray-icon", "url", - "uuid", + "urlpattern", "webkit2gtk", "webview2-com", - "windows 0.39.0", + "window-vibrancy", + "windows 0.61.1", ] [[package]] name = "tauri-build" -version = "1.5.5" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "586f3e677f940c8bb4f70c52eda05dc59b79e61543f1182de83516810bb8e35d" +checksum = "d7a0350f0df1db385ca5c02888a83e0e66655c245b7443db8b78a70da7d7f8fc" dependencies = [ "anyhow", "cargo_toml", - "dirs-next", + "dirs 6.0.0", + "glob", "heck 0.5.0", "json-patch", + "schemars", "semver", "serde", "serde_json", "tauri-utils", "tauri-winres", + "toml", "walkdir", ] [[package]] name = "tauri-codegen" -version = "1.4.5" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93a9e3f5cebf779a63bf24903e714ec91196c307d8249a0008b882424328bcda" +checksum = "f93f035551bf7b11b3f51ad9bc231ebbe5e085565527991c16cf326aa38cdf47" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "brotli", "ico", "json-patch", @@ -3462,124 +4246,263 @@ dependencies = [ "png", "proc-macro2", "quote", - "regex", "semver", "serde", "serde_json", "sha2", + "syn 2.0.101", "tauri-utils", - "thiserror", + "thiserror 2.0.12", "time", + "url", "uuid", "walkdir", ] [[package]] name = "tauri-macros" -version = "1.4.6" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1d0e989f54fe06c5ef0875c5e19cf96453d099a0a774d5192ab47e80471cdab" +checksum = "8db4df25e2d9d45de0c4c910da61cd5500190da14ae4830749fee3466dddd112" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.101", "tauri-codegen", "tauri-utils", ] [[package]] -name = "tauri-runtime" -version = "0.14.5" +name = "tauri-plugin" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33fda7d213e239077fad52e96c6b734cecedb30c2382118b64f94cb5103ff3a" +checksum = "37a5ebe6a610d1b78a94650896e6f7c9796323f408800cef436e0fa0539de601" dependencies = [ + "anyhow", + "glob", + "plist", + "schemars", + "serde", + "serde_json", + "tauri-utils", + "toml", + "walkdir", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88371e340ad2f07409a3b68294abe73f20bc9c1bc1b631a31dc37a3d0161f682" +dependencies = [ + "anyhow", + "dunce", + "glob", + "percent-encoding", + "schemars", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.12", + "toml", + "url", + "uuid", +] + +[[package]] +name = "tauri-plugin-global-shortcut" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f646a09511e8d283267dcdaa08c2ef27c4116bf271d9114849d9ca215606c3" +dependencies = [ + "global-hotkey", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", +] + +[[package]] +name = "tauri-plugin-http" +version = "2.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40dcd6c922a1885e1f0bcebc6768fec6e005bd4b9001c5d90a2f5d4cab297729" +dependencies = [ + "bytes", + "cookie_store", + "data-url", + "http", + "regex", + "reqwest", + "schemars", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.12", + "tokio", + "url", + "urlpattern", +] + +[[package]] +name = "tauri-plugin-opener" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fdc6cb608e04b7d2b6d1f21e9444ad49245f6d03465ba53323d692d1ceb1a30" +dependencies = [ + "dunce", + "glob", + "objc2-app-kit", + "objc2-foundation 0.3.1", + "open", + "schemars", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", + "url", + "windows 0.60.0", + "zbus", +] + +[[package]] +name = "tauri-plugin-shell" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d5eb3368b959937ad2aeaf6ef9a8f5d11e01ffe03629d3530707bbcb27ff5d" +dependencies = [ + "encoding_rs", + "log", + "open", + "os_pipe", + "regex", + "schemars", + "serde", + "serde_json", + "shared_child", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", + "tokio", +] + +[[package]] +name = "tauri-runtime" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f004905d549854069e6774533d742b03cacfd6f03deb08940a8677586cbe39" +dependencies = [ + "cookie", + "dpi", "gtk", "http", - "http-range", - "rand 0.8.5", + "jni", + "objc2 0.6.1", + "objc2-ui-kit", "raw-window-handle", "serde", "serde_json", "tauri-utils", - "thiserror", + "thiserror 2.0.12", "url", - "uuid", - "webview2-com", - "windows 0.39.0", + "windows 0.61.1", ] [[package]] name = "tauri-runtime-wry" -version = "0.14.10" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c447dcd9b0f09c7dc4b752cc33e72788805bfd761fbda5692d30c48289efec" +checksum = "f85d056f4d4b014fe874814034f3416d57114b617a493a4fe552580851a3f3a2" dependencies = [ - "cocoa 0.24.1", "gtk", + "http", + "jni", + "log", + "objc2 0.6.1", + "objc2-app-kit", + "objc2-foundation 0.3.1", + "once_cell", "percent-encoding", - "rand 0.8.5", "raw-window-handle", + "softbuffer", + "tao", "tauri-runtime", "tauri-utils", - "uuid", + "url", "webkit2gtk", "webview2-com", - "windows 0.39.0", + "windows 0.61.1", "wry", ] [[package]] name = "tauri-utils" -version = "1.6.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0c939e88d82903a0a7dfb28388b12a3c03504d6bd6086550edaa3b6d8beaa" +checksum = "b2900399c239a471bcff7f15c4399eb1a8c4fe511ba2853e07c996d771a5e0a4" dependencies = [ + "anyhow", "brotli", + "cargo_metadata", "ctor", "dunce", "glob", - "heck 0.5.0", "html5ever", + "http", "infer", "json-patch", "kuchikiki", "log", "memchr", - "phf 0.11.2", + "phf 0.11.3", "proc-macro2", "quote", + "regex", + "schemars", "semver", "serde", + "serde-untagged", "serde_json", "serde_with", - "thiserror", + "swift-rs", + "thiserror 2.0.12", + "toml", "url", + "urlpattern", + "uuid", "walkdir", - "windows-version", ] [[package]] name = "tauri-winres" -version = "0.1.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" +checksum = "e8d321dbc6f998d825ab3f0d62673e810c861aac2d0de2cc2c395328f1d113b4" dependencies = [ "embed-resource", - "toml 0.7.8", + "indexmap 2.9.0", + "toml", ] [[package]] name = "tempfile" -version = "3.15.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "cfg-if", "fastrand", - "getrandom 0.2.15", + "getrandom 0.3.2", "once_cell", - "rustix", + "rustix 1.0.5", "windows-sys 0.59.0", ] @@ -3606,7 +4529,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -3617,27 +4549,28 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] -name = "thread_local" -version = "1.1.8" +name = "thiserror-impl" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ - "cfg-if", - "once_cell", + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] name = "time" -version = "0.3.37" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", - "itoa 1.0.14", + "itoa 1.0.15", "num-conv", "powerfmt", "serde", @@ -3647,15 +4580,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -3672,10 +4605,25 @@ dependencies = [ ] [[package]] -name = "tokio" -version = "1.42.0" +name = "tinyvec" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.44.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" dependencies = [ "backtrace", "bytes", @@ -3683,9 +4631,21 @@ dependencies = [ "mio", "pin-project-lite", "socket2", + "tokio-macros", "windows-sys 0.52.0", ] +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "tokio-native-tls" version = "0.3.1" @@ -3698,9 +4658,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ "rustls", "tokio", @@ -3708,9 +4668,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -3721,42 +4681,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.11" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.19.15", -] - -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.22", + "toml_edit 0.22.26", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] @@ -3767,26 +4706,63 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.7.0", - "serde", - "serde_spanned", + "indexmap 2.9.0", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.7.0", + "indexmap 2.9.0", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +dependencies = [ + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.22", + "toml_write", + "winnow 0.7.9", ] +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -3812,7 +4788,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] @@ -3822,36 +4798,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", - "valuable", ] [[package]] -name = "tracing-log" -version = "0.2.0" +name = "tray-icon" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +checksum = "9f7eee98ec5c90daf179d55c20a49d8c0d043054ce7c26336c09a24d31f14fa0" dependencies = [ - "log", + "crossbeam-channel", + "dirs 6.0.0", + "libappindicator", + "muda", + "objc2 0.6.1", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.1", "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", + "png", + "serde", + "thiserror 2.0.12", + "windows-sys 0.59.0", ] [[package]] @@ -3861,16 +4829,74 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "typenum" -version = "1.17.0" +name = "typeid" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-segmentation" @@ -3896,6 +4922,18 @@ dependencies = [ "serde", ] +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + [[package]] name = "utf-8" version = "0.7.6" @@ -3916,31 +4954,20 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.11.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.3.2", + "serde", ] -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "version-compare" -version = "0.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" - [[package]] name = "version-compare" version = "0.2.0" @@ -3965,9 +4992,9 @@ dependencies = [ [[package]] name = "vswhom-sys" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b17ae1f6c8a2b28506cd96d412eebf83b4a0ff2cbefeeb952f2f9dfa44ba18" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" dependencies = [ "cc", "libc", @@ -4005,35 +5032,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasm-bindgen" -version = "0.2.99" +name = "wasi" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -4044,9 +5081,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4054,22 +5091,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" @@ -4086,9 +5126,19 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", @@ -4096,9 +5146,9 @@ dependencies = [ [[package]] name = "webkit2gtk" -version = "0.18.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -4114,20 +5164,18 @@ dependencies = [ "javascriptcore-rs", "libc", "once_cell", - "soup2", + "soup3", "webkit2gtk-sys", ] [[package]] name = "webkit2gtk-sys" -version = "0.18.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" dependencies = [ - "atk-sys", "bitflags 1.3.2", "cairo-sys-rs", - "gdk-pixbuf-sys", "gdk-sys", "gio-sys", "glib-sys", @@ -4135,54 +5183,54 @@ dependencies = [ "gtk-sys", "javascriptcore-rs-sys", "libc", - "pango-sys", "pkg-config", - "soup2-sys", - "system-deps 6.2.2", + "soup3-sys", + "system-deps", ] [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "37493cadf42a2a939ed404698ded7fb378bf301b5011f973361779a3a74f8c93" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "webview2-com" -version = "0.19.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178" +checksum = "b542b5cfbd9618c46c2784e4d41ba218c336ac70d44c55e47b251033e7d85601" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.39.0", - "windows-implement", + "windows 0.61.1", + "windows-core 0.61.0", + "windows-implement 0.60.0", + "windows-interface", ] [[package]] name = "webview2-com-macros" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac" +checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.101", ] [[package]] name = "webview2-com-sys" -version = "0.19.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7" +checksum = "8ae2d11c4a686e4409659d7891791254cf9286d3cfe0eef54df1523533d22295" dependencies = [ - "regex", - "serde", - "serde_json", - "thiserror", - "windows 0.39.0", - "windows-bindgen", - "windows-metadata", + "thiserror 2.0.12", + "windows 0.61.1", + "windows-core 0.61.0", ] [[package]] @@ -4217,87 +5265,214 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "window-shadows" -version = "0.2.2" -source = "git+https://github.com/tauri-apps/window-shadows.git#48fa81bbd9553b1b69e61590b4669bfad8b13ec8" +name = "window-vibrancy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "cocoa 0.25.0", - "objc", + "objc2 0.6.1", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", "raw-window-handle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", + "windows-version", ] [[package]] name = "windows" -version = "0.39.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" +checksum = "ddf874e74c7a99773e62b1c671427abf01a425e77c3d3fb9fb1e4883ea934529" dependencies = [ - "windows-implement", - "windows_aarch64_msvc 0.39.0", - "windows_i686_gnu 0.39.0", - "windows_i686_msvc 0.39.0", - "windows_x86_64_gnu 0.39.0", - "windows_x86_64_msvc 0.39.0", + "windows-collections 0.1.1", + "windows-core 0.60.1", + "windows-future 0.1.1", + "windows-link", + "windows-numerics 0.1.1", ] [[package]] name = "windows" -version = "0.48.0" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ - "windows-targets 0.48.5", + "windows-collections 0.2.0", + "windows-core 0.61.0", + "windows-future 0.2.0", + "windows-link", + "windows-numerics 0.2.0", ] [[package]] -name = "windows-bindgen" -version = "0.39.0" +name = "windows-collections" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41" +checksum = "5467f79cc1ba3f52ebb2ed41dbb459b8e7db636cc3429458d9a852e15bc24dec" dependencies = [ - "windows-metadata", - "windows-tokens", + "windows-core 0.60.1", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.0", ] [[package]] name = "windows-core" -version = "0.52.0" +version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247" dependencies = [ - "windows-targets 0.52.6", + "windows-implement 0.59.0", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings 0.3.1", +] + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings 0.4.0", +] + +[[package]] +name = "windows-future" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a787db4595e7eb80239b74ce8babfb1363d8e343ab072f2ffe901400c03349f0" +dependencies = [ + "windows-core 0.60.1", + "windows-link", +] + +[[package]] +name = "windows-future" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" +dependencies = [ + "windows-core 0.61.0", + "windows-link", ] [[package]] name = "windows-implement" -version = "0.39.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" +checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" dependencies = [ - "syn 1.0.109", - "windows-tokens", + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] -name = "windows-metadata" -version = "0.39.0" +name = "windows-implement" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-numerics" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005dea54e2f6499f2cee279b8f703b3cf3b5734a2d8d21867c8f44003182eeed" +dependencies = [ + "windows-core 0.60.1", + "windows-link", +] + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.0", + "windows-link", +] + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result", + "windows-strings 0.3.1", + "windows-targets 0.53.0", +] + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.42.2", ] [[package]] @@ -4327,6 +5502,21 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -4351,7 +5541,7 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", @@ -4359,18 +5549,28 @@ dependencies = [ ] [[package]] -name = "windows-tokens" -version = "0.39.0" +name = "windows-targets" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] [[package]] name = "windows-version" -version = "0.1.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" +checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" dependencies = [ - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -4392,10 +5592,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] -name = "windows_aarch64_msvc" -version = "0.39.0" +name = "windows_aarch64_gnullvm" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" @@ -4416,10 +5616,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] -name = "windows_i686_gnu" -version = "0.39.0" +name = "windows_aarch64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_i686_gnu" @@ -4439,6 +5639,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" @@ -4446,10 +5652,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] -name = "windows_i686_msvc" -version = "0.39.0" +name = "windows_i686_gnullvm" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" @@ -4470,10 +5676,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] -name = "windows_x86_64_gnu" -version = "0.39.0" +name = "windows_i686_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[package]] name = "windows_x86_64_gnu" @@ -4493,6 +5699,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -4512,10 +5724,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] -name = "windows_x86_64_msvc" -version = "0.39.0" +name = "windows_x86_64_gnullvm" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" [[package]] name = "windows_x86_64_msvc" @@ -4535,6 +5747,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.5.40" @@ -4546,23 +5764,13 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.22" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" +checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "winreg" version = "0.52.0" @@ -4582,6 +5790,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "write16" version = "1.0.0" @@ -4596,40 +5813,46 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.24.11" +version = "0.51.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55c80b12287eb1ff7c365fc2f7a5037cb6181bd44c9fce81c8d1cf7605ffad6" +checksum = "c886a0a9d2a94fd90cfa1d929629b79cfefb1546e2c7430c63a47f0664c0e4e2" dependencies = [ - "base64 0.13.1", - "block", - "cocoa 0.24.1", - "core-graphics 0.22.3", + "base64 0.22.1", + "block2 0.6.1", + "cookie", "crossbeam-channel", + "dpi", "dunce", - "gdk", - "gio", - "glib", + "gdkx11", "gtk", "html5ever", "http", + "javascriptcore-rs", + "jni", "kuchikiki", "libc", - "log", - "objc", - "objc_id", + "ndk", + "objc2 0.6.1", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.1", + "objc2-ui-kit", + "objc2-web-kit", "once_cell", - "serde", - "serde_json", + "percent-encoding", + "raw-window-handle", "sha2", - "soup2", - "tao", - "thiserror", + "soup3", + "tao-macros", + "thiserror 2.0.12", "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.39.0", - "windows-implement", + "windows 0.61.1", + "windows-core 0.61.0", + "windows-version", + "x11-dl", ] [[package]] @@ -4654,14 +5877,13 @@ dependencies = [ ] [[package]] -name = "xattr" -version = "1.3.1" +name = "xdg-home" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" dependencies = [ "libc", - "linux-raw-sys", - "rustix", + "windows-sys 0.59.0", ] [[package]] @@ -4695,52 +5917,120 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", "synstructure", ] [[package]] -name = "zerocopy" -version = "0.7.35" +name = "zbus" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "nix", + "ordered-stream", + "serde", + "serde_repr", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.59.0", + "winnow 0.7.9", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.101", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +dependencies = [ + "serde", + "static_assertions", + "winnow 0.7.9", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", ] [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + [[package]] name = "zerovec" version = "0.10.4" @@ -4760,5 +6050,47 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.101", +] + +[[package]] +name = "zvariant" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2df9ee044893fcffbdc25de30546edef3e32341466811ca18421e3cd6c5a3ac" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "winnow 0.7.9", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74170caa85b8b84cc4935f2d56a57c7a15ea6185ccdd7eadb57e6edd90f94b2f" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.101", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "static_assertions", + "syn 2.0.101", + "winnow 0.7.9", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index cf7e0ada..313db980 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -7,19 +7,27 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +# The `_lib` suffix may seem redundant but it is necessary +# to make the lib name unique and wouldn't conflict with the bin name. +# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519 +name = "vrct_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + [build-dependencies] -tauri-build = { version = "1", features = [] } +tauri-build = { version = "=2.2.0", features = [] } [dependencies] -tauri = { version = "1", features = [ "fs-remove-dir", "fs-read-file", "fs-create-dir", "fs-write-file", "fs-exists", "http-request", "fs-read-dir", "window-hide", "window-set-focus", "global-shortcut-all", "window-set-size", "window-set-position", "window-unmaximize", "window-close", "window-maximize", "window-minimize", "window-unminimize", "window-start-dragging", "window-set-decorations", "window-set-always-on-top", "shell-sidecar", "shell-open", "devtools"] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -font-kit = "0.14.2" -window-shadows = { git = "https://github.com/tauri-apps/window-shadows.git" } -reqwest = { version = "0.11", features = ["json", "rustls-tls"] } -base64 = "0.22.1" +tauri = { version = "=2.5.1", features = ["devtools"] } +tauri-plugin-opener = "=2.2.6" +serde = { version = "=1.0.219", features = ["derive"] } +serde_json = "=1.0.140" +tauri-plugin-fs = "=2.2.1" +tauri-plugin-http = "=2.4.3" +tauri-plugin-shell = "2.2.1" +font-kit = "=0.14.2" +reqwest = "=0.12.15" +base64 = "=0.22.1" - -[features] -# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! -custom-protocol = ["tauri/custom-protocol"] +[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] +tauri-plugin-global-shortcut = "=2.2.0" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json new file mode 100644 index 00000000..18388acf --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,10 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "Capability for the main window", + "windows": ["main"], + "permissions": [ + "core:default", + "opener:default" + ] +} diff --git a/src-tauri/capabilities/vrct_capability.json b/src-tauri/capabilities/vrct_capability.json new file mode 100644 index 00000000..886363e7 --- /dev/null +++ b/src-tauri/capabilities/vrct_capability.json @@ -0,0 +1,63 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "vrct-capability", + "description": "VRCT main window capabilities", + "windows": ["main"], + "permissions": [ + "core:window:default", + "core:window:allow-start-dragging", + "core:window:allow-close", + "core:window:allow-set-position", + "core:window:allow-set-size", + "core:window:allow-set-always-on-top", + "core:window:allow-maximize", + "core:window:allow-unmaximize", + "core:window:allow-minimize", + "core:window:allow-unminimize", + "core:window:allow-set-focus", + + "global-shortcut:allow-is-registered", + "global-shortcut:allow-register", + "global-shortcut:allow-register-all", + "global-shortcut:allow-unregister", + "global-shortcut:allow-unregister-all", + + "fs:default", + "fs:allow-write-file", + "fs:allow-remove", + "fs:allow-resource-read-recursive", + "fs:allow-resource-meta-recursive", + { + "identifier": "fs:scope", + "allow": [ + { "path": "$RESOURCE/plugins/**" }, + { "path": "src-tauri/target/debug/plugins/**" } + ] + }, + + { + "identifier": "http:default", + "allow": [ + { "url": "https://api.github.com/repos/**" }, + { "url": "https://github.com/**" }, + { "url": "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json" }, + { "url": "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/dev_vrct_plugins_list.json" } + ] + }, + + "shell:allow-open", + "shell:allow-stdin-write", + { + "identifier": "shell:allow-spawn", + "allow": [ + { "name": "bin/VRCT-sidecar", "sidecar": true, "args": true } + ] + }, + { + "identifier": "shell:allow-execute", + "allow": [ + { "name": "bin/VRCT-sidecar", "sidecar": true, "args": true } + ] + } + ] +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 00000000..d47b3d77 --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1,64 @@ +use tauri::Manager; + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .setup(|app| { + let main_window = app.get_webview_window("main").unwrap(); // `main_window` is declared here for all builds + + #[cfg(debug_assertions)] + { main_window.open_devtools(); } + + Ok(()) + }) + .plugin(tauri_plugin_shell::init()) + .plugin(tauri_plugin_http::init()) + .plugin(tauri_plugin_fs::init()) + .plugin(tauri_plugin_global_shortcut::Builder::new().build()) + .plugin(tauri_plugin_opener::init()) + .invoke_handler(tauri::generate_handler![get_font_list, download_zip_asset]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} + + +use font_kit::{source::SystemSource}; +use std::collections::HashSet; + +#[tauri::command] +async fn get_font_list() -> Vec { + let source = SystemSource::new(); + let mut font_families = HashSet::new(); + + if let Ok(fonts) = source.all_fonts() { + for font in fonts { + if let Ok(info) = font.load() { + font_families.insert(info.family_name().to_string()); + } + } + } + + font_families.into_iter().collect() +} + + +use base64::engine::general_purpose::STANDARD as BASE64; +use base64::Engine; + +#[tauri::command] +async fn download_zip_asset(url: String) -> Result { + use reqwest; + + let client = reqwest::Client::new(); + let resp = client.get(&url) + .header("Accept", "application/octet-stream") + .send() + .await.map_err(|e| format!("Request error: {}", e))?; + if !resp.status().is_success() { + return Err(format!("HTTP error: {}", resp.status())); + } + + let bytes = resp.bytes().await.map_err(|e| format!("Reading bytes error: {}", e))?; + + Ok(BASE64.encode(&bytes)) +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 36e07028..19c80041 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,67 +1,6 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use tauri::Manager; -use window_shadows::set_shadow; - fn main() { - tauri::Builder::default() - .setup(|app| { - let main_window = app.get_window("main").unwrap(); // `main_window` is declared here for all builds - - #[cfg(debug_assertions)] - { main_window.open_devtools(); } - - #[cfg(any(windows, target_os = "macos"))] - set_shadow(main_window, true).unwrap(); - - Ok(()) - }) - .on_window_event(|event| { // This is for fix the bug that the window scaling issue when dragging between monitors. - if let tauri::WindowEvent::ScaleFactorChanged { new_inner_size, .. } = event.event() { - event.window().set_size(tauri::Size::Physical(*new_inner_size)).unwrap(); - } - }) - .invoke_handler(tauri::generate_handler![get_font_list, download_zip_asset]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); + vrct_lib::run() } - - -use font_kit::{source::SystemSource}; -use std::collections::HashSet; - -#[tauri::command] -async fn get_font_list() -> Vec { - let source = SystemSource::new(); - let mut font_families = HashSet::new(); - - if let Ok(fonts) = source.all_fonts() { - for font in fonts { - if let Ok(info) = font.load() { - font_families.insert(info.family_name().to_string()); - } - } - } - - font_families.into_iter().collect() -} - -#[tauri::command] -async fn download_zip_asset(url: String) -> Result { - use reqwest; - // reqwest のクライアントを作成 - let client = reqwest::Client::new(); - // GET リクエストを送信(リダイレクトも自動追従します) - let resp = client.get(&url) - .header("Accept", "application/octet-stream") - .send() - .await.map_err(|e| format!("Request error: {}", e))?; - if !resp.status().is_success() { - return Err(format!("HTTP error: {}", resp.status())); - } - // レスポンスのバイナリデータを取得 - let bytes = resp.bytes().await.map_err(|e| format!("Reading bytes error: {}", e))?; - // バイナリデータを base64 エンコードして返す - Ok(base64::encode(&bytes)) -} \ No newline at end of file diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 14b4c7b2..bc4fd9ad 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,62 +1,16 @@ { + "$schema": "https://schema.tauri.app/config/2", + "productName": "VRCT", + "version": "0.1.0", + "identifier": "com.vrct.app", "build": { "beforeDevCommand": "", + "devUrl": "http://localhost:1420", "beforeBuildCommand": "", - "devPath": "http://localhost:1420", - "distDir": "../dist" + "frontendDist": "../dist" }, - "package": { - "productName": "VRCT", - "version": "3.0.0" - }, - "tauri": { - "allowlist": { - "all": false, - "window": { - "all": false, - "setAlwaysOnTop": true, - "setFocus": true, - "setDecorations": true, - "close": true, - "hide": true, - "setPosition": true, - "setSize": true, - "maximize": true, - "minimize": true, - "unmaximize": true, - "unminimize": true, - "startDragging": true - }, - "globalShortcut": { - "all": true - }, - "fs": { - "readDir": true, - "readFile": true, - "exists": true, - "writeFile": true, - "createDir": true, - "removeDir": true, - "scope": ["$RESOURCE/**", "**/src-tauri/target/debug/plugins/**"] - }, - "http": { - "request": true, - "scope": [ - "https://api.github.com/repos/**", - "https://github.com/**", - "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json", - "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/dev_vrct_plugins_list.json" - ] - }, - "shell": { - "all": false, - "open": true, - "sidecar": true, - "scope": [ - { "name": "bin/VRCT-sidecar", "sidecar": true, "args": true } - ] - } - }, + "app": { + "enableGTKAppId": false, "windows": [{ "title": "VRCT", "center": true, @@ -65,37 +19,40 @@ "minWidth": 400, "minHeight": 200, "transparent": true, - "decorations": false + "decorations": false, + "shadow": false }], - "security": { "csp": null }, - "bundle": { - "active": true, - "targets": "nsis", - "identifier": "com.vrct.dev", - "publisher": "m's software", - "copyright": "Copyright m's software", - "shortDescription": "VRCT", - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.icns", - "icons/icon.ico" - ], - "externalBin": [ - "bin/VRCT-sidecar" - ], - "resources": { - "bin/_internal": "_internal", - "plugins": "plugins" - }, - "windows": { - "nsis": { - "template": "nsis/template.nsi", - "license": "../LICENSE", - "installMode": "currentUser", - "displayLanguageSelector": true - } + "security": { + "csp": null, + "capabilities": ["default", "vrct-capability"] + } + }, + "bundle": { + "active": true, + "targets": "nsis", + "publisher": "m's software", + "copyright": "Copyright m's software", + "licenseFile": "../LICENSE", + "shortDescription": "VRCT", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "externalBin": [ + "bin/VRCT-sidecar" + ], + "resources": { + "bin/_internal": "_internal", + "plugins": "plugins" + }, + "windows": { + "nsis": { + "template": "nsis/template.nsi", + "installMode": "currentUser", + "displayLanguageSelector": true } } } diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index c2b0e4f3..d645d774 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -26,7 +26,6 @@ import { AppErrorBoundary } from "./error_boundary/AppErrorBoundary"; export const App = () => { const { currentIsVrctAvailable } = useIsVrctAvailable(); const { currentIsBackendReady } = useIsBackendReady(); - const { WindowGeometryController } = useWindow(); const { i18n } = useTranslation(); return ( @@ -40,7 +39,6 @@ export const App = () => { - {(currentIsBackendReady.data === false || currentIsVrctAvailable.data === false) ? @@ -54,9 +52,11 @@ export const App = () => { }; const Contents = () => { + const { WindowGeometryController } = useWindow(); const { currentIsSoftwareUpdating } = useIsSoftwareUpdating(); return ( <> + diff --git a/src-ui/app/_app_controllers/StartPythonController.jsx b/src-ui/app/_app_controllers/StartPythonController.jsx index 62bf0b28..c351120c 100644 --- a/src-ui/app/_app_controllers/StartPythonController.jsx +++ b/src-ui/app/_app_controllers/StartPythonController.jsx @@ -1,4 +1,4 @@ -import { invoke } from "@tauri-apps/api/tauri"; +import { invoke } from "@tauri-apps/api/core"; import { useEffect, useRef } from "react"; import { useStartPython } from "@logics/useStartPython"; import { useStdoutToPython } from "@logics/useStdoutToPython"; diff --git a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx index 255ede1c..8d2c1b4f 100644 --- a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx +++ b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx @@ -126,7 +126,6 @@ export const MergePluginsController = () => { } } - console.log("merged plugin data", new_data); return new_data; }); }; diff --git a/src-ui/app/_index_css/root.css b/src-ui/app/_index_css/root.css index 4d96f38b..003b7978 100644 --- a/src-ui/app/_index_css/root.css +++ b/src-ui/app/_index_css/root.css @@ -24,7 +24,8 @@ html, body { height: 100%; font-family: var(--font_family); /* If not found the font family where 'root:' that is selected by user*/ - border-radius: 1.8rem; + border-radius: 10px; /* fixed by px */ + overflow: hidden; } diff --git a/src-ui/app/error_boundary/AppErrorBoundary.jsx b/src-ui/app/error_boundary/AppErrorBoundary.jsx index cb8550a0..0cc5dfb2 100644 --- a/src-ui/app/error_boundary/AppErrorBoundary.jsx +++ b/src-ui/app/error_boundary/AppErrorBoundary.jsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { appWindow } from "@tauri-apps/api/window"; +import { getCurrentWindow } from "@tauri-apps/api/window"; import { ErrorBoundary } from "react-error-boundary"; import XMarkSvg from "@images/cancel.svg?react"; import CopySvg from "@images/copy.svg?react"; @@ -65,12 +65,14 @@ const ErrorContainer = ({error}) => { }; const CloseButtonContainer = () => { - const close = () => { + + const asyncClose = async () => { + const appWindow = await getCurrentWindow(); appWindow.close(); }; return ( -

-
+
-
+
-
+
diff --git a/src-ui/logics/common/useFetch.js b/src-ui/logics/common/useFetch.js index 12ba5def..f008166f 100644 --- a/src-ui/logics/common/useFetch.js +++ b/src-ui/logics/common/useFetch.js @@ -1,18 +1,24 @@ -import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; +import { fetch as tauriFetch } from "@tauri-apps/plugin-http"; export const useFetch = () => { - const asyncTauriFetchGithub = async (url) => { + const asyncTauriFetchGithub = async (url, {return_row = false} = {}) => { console.log("tauriFetch", url); - const release_response = await tauriFetch(url, { + const response = await tauriFetch(url, { method: "GET", - responseType: ResponseType.Json, headers: { "Accept": "application/vnd.github+json", "User-Agent": "VRCTPluginApp" } }); - return release_response; + + if (response.status !== 200) { + throw new Error(url, "Failed to fetch, response: " + response); + } + + if (return_row === true) return await response; + + return await response.json(); }; return { diff --git a/src-ui/logics/common/useWindow.js b/src-ui/logics/common/useWindow.js index f94f3293..01e3618d 100644 --- a/src-ui/logics/common/useWindow.js +++ b/src-ui/logics/common/useWindow.js @@ -1,15 +1,18 @@ -import { useEffect } from "react"; -import { appWindow, currentMonitor, availableMonitors, PhysicalPosition, PhysicalSize } from "@tauri-apps/api/window"; +import { useEffect, useRef } from "react"; +import { getCurrentWindow, currentMonitor, availableMonitors, PhysicalPosition, PhysicalSize } from "@tauri-apps/api/window"; import { useStdoutToPython } from "@logics/useStdoutToPython"; import { useStore_IsBreakPoint } from "@store"; import { useUiScaling } from "@logics_configs"; +import { store } from "@store"; export const useWindow = () => { const { asyncStdoutToPython } = useStdoutToPython(); const { currentUiScaling } = useUiScaling(); const { updateIsBreakPoint } = useStore_IsBreakPoint(); + const asyncGetWindowGeometry = async () => { + const appWindow = await getCurrentWindow(); try { const position = await appWindow.outerPosition(); const { x: x_pos, y: y_pos } = position; @@ -29,6 +32,7 @@ export const useWindow = () => { }; const asyncSaveWindowGeometry = async () => { + const appWindow = await getCurrentWindow(); const minimized = await appWindow.isMinimized(); if (minimized === true) return; // don't save while the window is minimized. const data = await asyncGetWindowGeometry(); @@ -36,6 +40,8 @@ export const useWindow = () => { }; const restoreWindowGeometry = async (data) => { + const appWindow = await getCurrentWindow(); + try { const monitors = await availableMonitors(); const { x_pos, y_pos, width, height } = data; @@ -89,38 +95,53 @@ export const useWindow = () => { }; const asyncUpdateBreakPoint = async () => { + const appWindow = await getCurrentWindow(); const size = await appWindow.innerSize(); const dynamicBreakPoint = 800 * (currentUiScaling.data / 100); updateIsBreakPoint(size.width <= dynamicBreakPoint); }; const WindowGeometryController = () => { - useEffect(() => { - let resizeTimeout; - const asyncFunction = () => { - asyncSaveWindowGeometry(); - asyncUpdateBreakPoint(); - }; - const unlistenResize = appWindow.onResized(() => { - clearTimeout(resizeTimeout); - resizeTimeout = setTimeout(asyncFunction, 200); - }); - - return () => { - unlistenResize.then((dispose) => dispose()); - }; - }, []); + const resizeTimeout = useRef(null); + const moveTimeout = useRef(null); + const unlistenResize = useRef(null); + const unlistenMove = useRef(null); useEffect(() => { - let moveTimeout; - const unlistenMove = appWindow.onMoved(() => { - clearTimeout(moveTimeout); - moveTimeout = setTimeout(asyncSaveWindowGeometry, 200); - }); + const setup = async () => { + if (store.is_register_window_geometry_controller) return; + const appWindow = await getCurrentWindow(); + + unlistenResize.current = appWindow.onResized(() => { + clearTimeout(resizeTimeout.current); + resizeTimeout.current = setTimeout(() => { + asyncSaveWindowGeometry(); + asyncUpdateBreakPoint(); + }, 200); + }); + + unlistenMove.current = appWindow.onMoved(() => { + clearTimeout(moveTimeout.current); + moveTimeout.current = setTimeout(() => { + asyncSaveWindowGeometry(); + }, 200); + }); + store.is_register_window_geometry_controller = true; + }; + + setup(); return () => { - unlistenMove.then((dispose) => dispose()); + if (unlistenResize.current) { + unlistenResize.current.then(dispose => dispose()); + } + if (unlistenMove.current) { + unlistenMove.current.then(dispose => dispose()); + } + + clearTimeout(resizeTimeout.current); + clearTimeout(moveTimeout.current); }; }, []); diff --git a/src-ui/logics/configs/hotkeys/useHotkeys.js b/src-ui/logics/configs/hotkeys/useHotkeys.js index a80cfb78..6991d0f6 100644 --- a/src-ui/logics/configs/hotkeys/useHotkeys.js +++ b/src-ui/logics/configs/hotkeys/useHotkeys.js @@ -1,10 +1,10 @@ -import { appWindow } from "@tauri-apps/api/window"; +import { getCurrentWindow } from "@tauri-apps/api/window"; import { store, useStore_Hotkeys } from "@store"; import { useStdoutToPython } from "@logics/useStdoutToPython"; import { useNotificationStatus } from "@logics_common"; import { useMainFunction } from "@logics_main"; -import { register, unregisterAll, isRegistered } from "@tauri-apps/api/globalShortcut"; +import { register, unregisterAll, isRegistered } from "@tauri-apps/plugin-global-shortcut"; export const useHotkeys = () => { const { asyncStdoutToPython } = useStdoutToPython(); @@ -66,16 +66,19 @@ export const useHotkeys = () => { const isAlreadyRegistered = await isRegistered(shortcut); if (!isAlreadyRegistered) { - await register(shortcut, async () => { + await register(shortcut, async (event) => { + if (event.state !== "Pressed") return; + const appWindow = await getCurrentWindow(); + switch (actionKey) { case "toggle_vrct_visibility": { const minimized = await appWindow.isMinimized(); if (minimized) { - appWindow.unminimize(); + await appWindow.unminimize(); await appWindow.setFocus(); store.text_area_ref.current?.focus(); } else { - appWindow.minimize(); + await appWindow.minimize(); } break; } diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index 8bd533e0..bbeb8439 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -1,4 +1,4 @@ -import { invoke } from "@tauri-apps/api/tauri"; +import { invoke } from "@tauri-apps/api/core"; import { useTranslation } from "react-i18next"; import { IS_PLUGIN_PATH_DEV_MODE, getPluginsList } from "@ui_configs"; import { @@ -14,7 +14,7 @@ import { import { useStdoutToPython } from "@logics/useStdoutToPython"; import { transform } from "@babel/standalone"; -import { writeFile, createDir, exists, removeDir, readDir, BaseDirectory, readTextFile } from "@tauri-apps/api/fs"; +import { writeFile, mkdir, exists, remove, readDir, BaseDirectory, readTextFile } from "@tauri-apps/plugin-fs"; import { dev_plugins } from "@plugins_index"; const imported_dev_plugins = []; dev_plugins.forEach(async ({entry_path}) => { @@ -81,10 +81,10 @@ export const usePlugins = () => { const downloaded_plugin_info_path = "plugins/" + plugin_folder_relative_path + "/plugin_info.json"; const plugin_css_path = "plugins/" + plugin_folder_relative_path + "/main.css"; try { - const downloaded_plugin_info_json = await readTextFile(downloaded_plugin_info_path, { dir: BaseDirectory.Resource, recursive: true }); + const downloaded_plugin_info_json = await readTextFile(downloaded_plugin_info_path, { baseDir: BaseDirectory.Resource, recursive: true }); const downloaded_plugin_info = JSON.parse(downloaded_plugin_info_json); - const plugin_code = await readTextFile(init_path, { dir: BaseDirectory.Resource, recursive: true }); + const plugin_code = await readTextFile(init_path, { baseDir: BaseDirectory.Resource, recursive: true }); const cleaned_code = removeImportStatements(plugin_code); const transpiled_code = transform(cleaned_code, { presets: [ @@ -123,12 +123,12 @@ export const usePlugins = () => { } }); } else { - const is_plugins_dir_exists = await exists("plugins", { dir: BaseDirectory.Resource }); + const is_plugins_dir_exists = await exists("plugins", { baseDir: BaseDirectory.Resource }); if (!is_plugins_dir_exists) return; try { - const plugin_entries = await readDir("plugins", { dir: BaseDirectory.Resource, recursive: true }); - const plugin_files = plugin_entries.filter(entry => entry.children && Array.isArray(entry.children)); + const plugin_entries = await readDir("plugins", { baseDir: BaseDirectory.Resource, recursive: true }); + const plugin_files = plugin_entries.filter(entry => entry.isDirectory === true); for (const target_dir of plugin_files) { const target_path = target_dir.name; @@ -139,83 +139,76 @@ export const usePlugins = () => { } } }; - const downloadAndExtractPlugin = async (plugin) => { - const latest_plugin_info = plugin.latest_plugin_info; + const { latest_plugin_info } = plugin; try { - const plugin_zip_url = await fetchLatestPluginZipUrl(latest_plugin_info); - console.log("start download", plugin_zip_url); - // Rust コマンド経由で ZIP をダウンロード - const base64_zip = await invoke("download_zip_asset", { url: plugin_zip_url }); - // base64_zip をデコードして Uint8Array に変換 - const binary_string = atob(base64_zip); - const len = binary_string.length; - const bytes = new Uint8Array(len); - for (let i = 0; i < len; i++) { - bytes[i] = binary_string.charCodeAt(i); - } + // 1. ZIP をダウンロード (ブラウザの fetch を使用) + const pluginZipUrl = await fetchLatestPluginZipUrl(latest_plugin_info); + console.log('start download', pluginZipUrl); + const res = await asyncTauriFetchGithub(pluginZipUrl, {return_row: true}); + if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`); + const arrayBuffer = await res.arrayBuffer(); + const bytes = new Uint8Array(arrayBuffer); - // JSZip で ZIP を解凍 + // 2. JSZip で ZIP を解凍 const zip = await JSZip.loadAsync(bytes); - // 展開先ディレクトリのパス(例:"plugins/" とする) - const target_plugin_path = "plugins/" + latest_plugin_info.plugin_id; - // 既に存在する場合は削除してから新規作成 - if (await exists(target_plugin_path, { dir: BaseDirectory.Resource, recursive: true })) { - await removeDir(target_plugin_path, { dir: BaseDirectory.Resource, recursive: true }); + // 3. 展開先ディレクトリを準備 + const targetPath = `plugins/${latest_plugin_info.plugin_id}`; + if (await exists(targetPath, { baseDir: BaseDirectory.Resource })) { + await remove(targetPath, { baseDir: BaseDirectory.Resource, recursive: true }); } - await createDir(target_plugin_path, { dir: BaseDirectory.Resource, recursive: true }); + await mkdir(targetPath, { baseDir: BaseDirectory.Resource, recursive: true }); - const file_promises = []; - zip.forEach((relative_path, zip_entry) => { - // .git 以下のファイルはスキップ - if (relative_path.startsWith(".git") || relative_path.includes("/.git/")) { + // 4. ZIP 内のエントリをひとつずつ展開 & 書き出し + const filePromises = []; + zip.forEach((relativePath, entry) => { + // .git 以下はスキップ + if (relativePath.startsWith('.git') || relativePath.includes('/.git/')) { return; } - const file_path = target_plugin_path + "/" + relative_path; - if (zip_entry.dir) { - file_promises.push( - createDir(file_path, { dir: BaseDirectory.Resource, recursive: true }).catch((err) => { - if (!err.message?.includes("already exists")) { - console.error("Failed to create directory:", file_path, err); - } - }) + const filePath = `${targetPath}/${relativePath}`; + if (entry.dir) { + // ディレクトリの場合は mkdir + filePromises.push( + mkdir(filePath, { baseDir: BaseDirectory.Resource, recursive: true }) + .catch(err => { + if (!err.message.includes('already exists')) { + console.error('Failed to create directory:', filePath, err); + } + }) ); } else { - const dir_path = file_path.substring(0, file_path.lastIndexOf("/")); - const promise = createDir(dir_path, { dir: BaseDirectory.Resource, recursive: true }) - .catch((err) => { - if (!err.message?.includes("already exists")) { - console.error("Failed to create parent directory:", dir_path, err); - } - }) - .then(() => zip_entry.async("text")) - .then(async (file_data) => { - await writeFile(file_path, file_data, { dir: BaseDirectory.Resource, recursive: true }); - }); - file_promises.push(promise); + // ファイルの場合は親ディレクトリを確保してからバイナリ書き込み + const dirPath = filePath.substring(0, filePath.lastIndexOf('/')); + filePromises.push( + mkdir(dirPath, { baseDir: BaseDirectory.Resource, recursive: true }) + .catch(err => { + if (!err.message.includes('already exists')) { + console.error('Failed to create parent directory:', dirPath, err); + } + }) + .then(() => entry.async('uint8array')) + .then(data => + writeFile(filePath, data, { baseDir: BaseDirectory.Resource }) + ) + ); } }); + await Promise.all(filePromises); - await Promise.all(file_promises); - console.log("Plugin downloaded successfully."); - - const index_file_relative_path = plugin.plugin_id; - await asyncLoadPlugin(index_file_relative_path); - - console.log("Plugin loaded successfully."); + console.log('Plugin downloaded successfully.'); + // 5. プラグインをロード + await asyncLoadPlugin(latest_plugin_info.plugin_id); + console.log('Plugin loaded successfully.'); } catch (error) { - console.error("Error downloading and extracting plugin:", error); + console.error('Error downloading and extracting plugin:', error); } }; const fetchLatestPluginZipUrl = async (plugin) => { const api_url = plugin.url; - const response = await asyncTauriFetchGithub(api_url); - if (response.status !== 200) { - throw new Error("Failed to fetch latest release info, status: " + response.status); - } - const release_info = response.data; + const release_info = await asyncTauriFetchGithub(api_url); const asset = release_info.assets.find((a) => a.name === plugin.asset_name); if (!asset) { throw new Error(`Asset ${plugin.asset_name} not found in the latest release`); @@ -229,11 +222,7 @@ export const usePlugins = () => { store.is_fetched_plugins_info_already = true; try { - const response = await asyncTauriFetchGithub(PLUGIN_LIST_URL); - if (response.status !== 200) { - throw new Error("Failed to fetch plugins list, status: " + response.status); - } - const plugins_data = response.data; + const plugins_data = await asyncTauriFetchGithub(PLUGIN_LIST_URL); const updated_list = await Promise.all( plugins_data.map(async (plugin_data) => { try { @@ -263,19 +252,11 @@ export const usePlugins = () => { const asyncFetchPluginInfo = async (plugin_info_asset_url) => { const release_response = await asyncTauriFetchGithub(plugin_info_asset_url); - if (release_response.status !== 200) { - throw new Error(`Failed to fetch release info from ${plugin_info_asset_url}`); - } - const plugin_info_json = release_response.data.assets.find(asset => asset.name === "plugin_info.json"); + const plugin_info_json = release_response.assets.find(asset => asset.name === "plugin_info.json"); if (!plugin_info_json) { throw new Error("plugin_info.json not found in release assets"); } - const plugin_info_json_response = await asyncTauriFetchGithub(plugin_info_json.browser_download_url); - if (plugin_info_json_response.status !== 200) { - throw new Error(`Failed to fetch plugin_info.json from ${plugin_info_json.browser_download_url}`); - } - const plugin_info = plugin_info_json_response.data; - + const plugin_info = await asyncTauriFetchGithub(plugin_info_json.browser_download_url); const { is_plugin_supported, is_plugin_supported_latest_vrct } = checkVrctVerCompatibility(plugin_info.min_supported_vrct_version, plugin_info.max_supported_vrct_version); @@ -432,17 +413,16 @@ const removeImportStatements = (code) => { // import { readTextFile, BaseDirectory } from "@tauri-apps/api/fs"; const loadPluginCSS = async (plugin_css_path) => { - if (!await exists(plugin_css_path, { dir: BaseDirectory.Resource, recursive: true })) return; + if (!await exists(plugin_css_path, { baseDir: BaseDirectory.Resource, recursive: true })) return; try { // プラグインフォルダのルートにある main.css を読み込む - const css_content = await readTextFile(plugin_css_path, { dir: BaseDirectory.Resource }); + const css_content = await readTextFile(plugin_css_path, { baseDir: BaseDirectory.Resource }); // style タグを作成して head に挿入する const style_tag = document.createElement("style"); style_tag.id = `plugin-css-${plugin_css_path.replace(/[^a-zA-Z0-9_-]/g, "")}`; style_tag.textContent = css_content; document.head.appendChild(style_tag); - console.log("Plugin CSS loaded for:", plugin_css_path); } catch (error) { console.error("Failed to load plugin CSS from", plugin_css_path, error); } diff --git a/src-ui/logics/main/useMainFunction.js b/src-ui/logics/main/useMainFunction.js index 9345f090..714ea2af 100644 --- a/src-ui/logics/main/useMainFunction.js +++ b/src-ui/logics/main/useMainFunction.js @@ -1,4 +1,4 @@ -import { getCurrent } from "@tauri-apps/api/window"; +import { getCurrentWindow } from "@tauri-apps/api/window"; import { useStore_TranslationStatus, @@ -75,8 +75,8 @@ export const useMainFunction = () => { }; - const toggleForeground = () => { - const main_page = getCurrent(); + const toggleForeground = async () => { + const main_page = await getCurrentWindow(); const is_foreground_enabled = !currentForegroundStatus.data; main_page.setAlwaysOnTop(is_foreground_enabled); updateForegroundStatus(is_foreground_enabled); diff --git a/src-ui/logics/main/useMessageInputBoxRatio.js b/src-ui/logics/main/useMessageInputBoxRatio.js index 466fc02b..328a97e0 100644 --- a/src-ui/logics/main/useMessageInputBoxRatio.js +++ b/src-ui/logics/main/useMessageInputBoxRatio.js @@ -1,8 +1,9 @@ -import { appWindow } from "@tauri-apps/api/window"; +import { getCurrentWindow } from "@tauri-apps/api/window"; import { useStore_MessageInputBoxRatio } from "@store"; import { useStdoutToPython } from "@logics/useStdoutToPython"; import { clampMinMax } from "@utils"; export const useMessageInputBoxRatio = () => { + const { asyncStdoutToPython } = useStdoutToPython(); const { currentMessageInputBoxRatio, updateMessageInputBoxRatio } = useStore_MessageInputBoxRatio(); @@ -11,6 +12,7 @@ export const useMessageInputBoxRatio = () => { }; const asyncSetMessageInputBoxRatio = async (ratio) => { + const appWindow = getCurrentWindow(); const minimized = await appWindow.isMinimized(); if (minimized === true) return; // don't save while the window is minimized. const parsed = parseFloat(ratio.toFixed(2)); diff --git a/src-ui/logics/useStartPython.js b/src-ui/logics/useStartPython.js index 7db442b9..c8aaf2f5 100644 --- a/src-ui/logics/useStartPython.js +++ b/src-ui/logics/useStartPython.js @@ -1,4 +1,4 @@ -import { Command } from "@tauri-apps/api/shell"; +import { Command } from "@tauri-apps/plugin-shell"; import { store } from "@store"; import { useReceiveRoutes } from "./useReceiveRoutes"; import { diff --git a/src-ui/store.js b/src-ui/store.js index d1712f1c..8f5317a3 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -19,6 +19,7 @@ export const store = { setting_box_scroll_container: null, log_box_ref: null, text_area_ref: null, + is_register_window_geometry_controller: false, is_applied_init_message_box_height: false, is_initialized_load_plugin: false, is_fetched_plugins_info_already: false, diff --git a/vite.config.js b/vite.config.js index 78a27f37..7998dc59 100644 --- a/vite.config.js +++ b/vite.config.js @@ -3,9 +3,12 @@ import react from "@vitejs/plugin-react"; import svgr from "vite-plugin-svgr"; import yaml from "@rollup/plugin-yaml"; import path from "path"; +import { globSync } from "node:fs"; +import { pathToFileURL } from "url"; import { dev_plugins } from "./src-ui/plugins/plugins_index.js"; +const host = process.env.TAURI_DEV_HOST; // https://vitejs.dev/config/ export default defineConfig(async () => { @@ -26,6 +29,14 @@ export default defineConfig(async () => { server: { port: 1420, strictPort: true, + host: host || false, + hmr: host + ? { + protocol: "ws", + host, + port: 1421, + } + : undefined, watch: { // 3. tell vite to ignore watching `src-tauri` ignored: ["**/src-tauri/**"], @@ -79,25 +90,44 @@ export default defineConfig(async () => { -// 各プラグインのエイリアスを動的に読み込む関数 + const getPluginAliases = async () => { const aliases = {}; - // dev_plugins 配列の各プラグインについて処理する + const raw_config_files = globSync("src-ui/plugins/*/plugin_configs.js"); + const config_files = raw_config_files.map(p => p.split(path.sep).join("/")); + for (const plugin of dev_plugins) { const entry_path = plugin.entry_path; // 例: "dev_plugin_subtitles" - try { - // エイリアス設定ファイルは各プラグインフォルダ内の "configs.js" に記述されている前提 - const pluginConfig = await import(`./src-ui/plugins/${entry_path}/plugin_configs.js`); - if (pluginConfig.configs && pluginConfig.configs.alias) { - for (const [alias_key, alias_relative_path] of Object.entries(pluginConfig.configs.alias)) { + const relative_config_path = `src-ui/plugins/${entry_path}/plugin_configs.js`; - // ホスト側の絶対パスに変換 - aliases[alias_key] = path.resolve(__dirname, "src-ui/plugins", entry_path, alias_relative_path); + // 該当エントリのファイルがなければスキップ + if (!config_files.includes(relative_config_path)) { + continue; + } + + try { + // Node 実行環境用に絶対パスを生成し、動的 import + const full_path = path.resolve(__dirname, relative_config_path); + const file_url = pathToFileURL(full_path).href; + const plugin_config = await import(file_url); + + if (plugin_config.configs?.alias) { + for (const [alias_key, alias_relative_path] of Object.entries(plugin_config.configs.alias)) { + aliases[alias_key] = path.resolve( + __dirname, + "src-ui/plugins", + entry_path, + alias_relative_path + ); } } } catch (error) { - console.error(`Error loading alias config for plugin ${plugin.plugin_info.plugin_id}:`, error); + console.error( + `Error loading alias config for plugin ${plugin.plugin_id}:`, + error + ); } } + return aliases; }; \ No newline at end of file From c20b58a011a0f654b1523402a749eb13a5862355 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Sat, 3 May 2025 12:41:15 +0900 Subject: [PATCH 56/68] =?UTF-8?q?=F0=9F=90=9B[bugfix]=20=E3=83=93=E3=83=AB?= =?UTF-8?q?=E3=83=89=E6=99=82=E3=81=AE=E3=82=A8=E3=83=A9=E3=83=BC/?= =?UTF-8?q?=E3=83=AF=E3=83=BC=E3=83=8B=E3=83=B3=E3=82=B0=E3=81=8C=E7=99=BA?= =?UTF-8?q?=E7=94=9F=E3=81=99=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit インストーラーのURLを修正 --- src-tauri/nsis/template.nsi | 18 +----------------- src-tauri/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src-tauri/nsis/template.nsi b/src-tauri/nsis/template.nsi index d4d22429..9ee85abe 100644 --- a/src-tauri/nsis/template.nsi +++ b/src-tauri/nsis/template.nsi @@ -588,25 +588,9 @@ Section Install !insertmacro CheckIfAppIsRunning - ; ; Copy main executable - ; File "${MAINBINARYSRCPATH}" - - ; ; Copy resources - ; {{#each resources_dirs}} - ; CreateDirectory "$INSTDIR\\{{this}}" - ; {{/each}} - ; {{#each resources}} - ; File /a "/oname={{this.[1]}}" "{{unescape-dollar-sign @key}}" - ; {{/each}} - - ; ; Copy external binaries - ; {{#each binaries}} - ; File /a "/oname={{this}}" "{{unescape-dollar-sign @key}}" - ; {{/each}} - !addplugindir "..\..\..\..\nsis\plugins\x86-unicode" ; 指定のURLからファイルをダウンロード - !define SOFTWARE_RELEASE_URL "https://huggingface.co/misyaguziya/VRCT/resolve/main" + !define SOFTWARE_RELEASE_URL "https://huggingface.co/ms-software/VRCT/resolve/main" !define SOFTWARE_DOWNLOAD_FILENAME "VRCT.zip" Var /GLOBAL i Var /GLOBAL cmder_dl diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index d47b3d77..ef8c6e4c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -4,7 +4,7 @@ use tauri::Manager; pub fn run() { tauri::Builder::default() .setup(|app| { - let main_window = app.get_webview_window("main").unwrap(); // `main_window` is declared here for all builds + let _main_window = app.get_webview_window("main").unwrap(); // `main_window` is declared here for all builds #[cfg(debug_assertions)] { main_window.open_devtools(); } From 6f1ab3201907dcf26d312a3f0d8b26aeda3955f7 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Sat, 3 May 2025 15:31:50 +0900 Subject: [PATCH 57/68] =?UTF-8?q?[Update]=20Speaker2Chatbox=E3=81=ABword?= =?UTF-8?q?=20filter=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-python/controller.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src-python/controller.py b/src-python/controller.py index c0f5dabc..cdc1b037 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -321,7 +321,14 @@ class Controller: elif isinstance(message, str) and len(message) > 0: translation = [] transliteration = [] - if model.detectRepeatReceiveMessage(message): + if model.checkKeywords(message): + self.run( + 200, + self.run_mapping["word_filter"], + {"message":f"Detected by word filter: {message}"}, + ) + return + elif model.detectRepeatReceiveMessage(message): return elif config.ENABLE_TRANSLATION is False: pass From 8316c24d0ddc28883526a9da26bf42d1292b3b3f Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sun, 4 May 2025 02:22:44 +0900 Subject: [PATCH 58/68] [bugfix] More fixes. c20b58a011a0f654b1523402a749eb13a5862355 --- src-tauri/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ef8c6e4c..f22ba70f 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -7,7 +7,7 @@ pub fn run() { let _main_window = app.get_webview_window("main").unwrap(); // `main_window` is declared here for all builds #[cfg(debug_assertions)] - { main_window.open_devtools(); } + { _main_window.open_devtools(); } Ok(()) }) From b5364cc7051ed7ffee9458cc84e3aa9b03437d4e Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sun, 4 May 2025 02:41:14 +0900 Subject: [PATCH 59/68] [Chore] Add a note for experimental feature globalSync. Remove comments. Set version to tauri.conf.json(3.0.0 is just restore from pre-checkout develop branch, though.) --- src-tauri/tauri.conf.json | 2 +- vite.config.js | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index bc4fd9ad..4d519488 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "VRCT", - "version": "0.1.0", + "version": "3.0.0", "identifier": "com.vrct.app", "build": { "beforeDevCommand": "", diff --git a/vite.config.js b/vite.config.js index 7998dc59..092db909 100644 --- a/vite.config.js +++ b/vite.config.js @@ -93,20 +93,18 @@ export default defineConfig(async () => { const getPluginAliases = async () => { const aliases = {}; - const raw_config_files = globSync("src-ui/plugins/*/plugin_configs.js"); + const raw_config_files = globSync("src-ui/plugins/*/plugin_configs.js"); // [Note] globSync is an experimental feature Node.js. If any error happened, use node.js v22.15.0 that I confirmed it works. const config_files = raw_config_files.map(p => p.split(path.sep).join("/")); for (const plugin of dev_plugins) { - const entry_path = plugin.entry_path; // 例: "dev_plugin_subtitles" + const entry_path = plugin.entry_path; const relative_config_path = `src-ui/plugins/${entry_path}/plugin_configs.js`; - // 該当エントリのファイルがなければスキップ if (!config_files.includes(relative_config_path)) { continue; } try { - // Node 実行環境用に絶対パスを生成し、動的 import const full_path = path.resolve(__dirname, relative_config_path); const file_url = pathToFileURL(full_path).href; const plugin_config = await import(file_url); From af17e057e8195ef94a984d7e5b2c41891b217313 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sun, 4 May 2025 10:03:37 +0900 Subject: [PATCH 60/68] [Update] Add crown emoji to supporter label. Add tooltip that shows their supporting period. Add icon rotate animation when hovered supporter's image. --- .../SupportUsContainer.module.scss | 1 + .../supporters_wrapper/SupportersWrapper.jsx | 26 +++++++++++++++++-- .../SupportersWrapper.module.scss | 16 +++++++++--- .../sidebar_section/SidebarSection.jsx | 4 ++- .../SidebarSection.module.scss | 13 ++++++++-- .../DownloadModelsContainer.module.scss | 1 + 6 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src-ui/app/config_page/setting_section/setting_box/supporters/support_us_container/SupportUsContainer.module.scss b/src-ui/app/config_page/setting_section/setting_box/supporters/support_us_container/SupportUsContainer.module.scss index 47ea8636..ef7ef6fc 100644 --- a/src-ui/app/config_page/setting_section/setting_box/supporters/support_us_container/SupportUsContainer.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/supporters/support_us_container/SupportUsContainer.module.scss @@ -1,4 +1,5 @@ $progress_ease: cubic-bezier(0, 1, 0.75, 1); +// Duplicated @keyframes revealTopImg { 0% { diff --git a/src-ui/app/config_page/setting_section/setting_box/supporters/supporters_container/supporters_wrapper/SupportersWrapper.jsx b/src-ui/app/config_page/setting_section/setting_box/supporters/supporters_container/supporters_wrapper/SupportersWrapper.jsx index c6397255..73209c10 100644 --- a/src-ui/app/config_page/setting_section/setting_box/supporters/supporters_container/supporters_wrapper/SupportersWrapper.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/supporters/supporters_container/supporters_wrapper/SupportersWrapper.jsx @@ -1,5 +1,6 @@ import React, { useState, useCallback, useEffect } from "react"; import clsx from "clsx"; +import Tooltip, { tooltipClasses } from '@mui/material/Tooltip'; import ArrowLeftSvg from "@images/arrow_left.svg?react"; import styles from "./SupportersWrapper.module.scss"; import { shuffleArray, randomIntMinMax, randomMinMax } from "@utils"; @@ -261,18 +262,39 @@ const SupporterLabelComponent = ({ item, target_plan, chato_src }) => { const SupporterPeriodContainer = ({ settings, calc_support_period }) => { const period_data = extractKeys(settings, calc_support_period); + const offset = { + popper: { + sx: { + [`&.${tooltipClasses.popper}[data-popper-placement*="top"] .${tooltipClasses.tooltip}`]: { marginBottom: "0.2em" }, + } + } + }; + return (
{Object.entries(period_data).map(([key, item], index) => { if (item === "") return null; - const class_name = clsx(styles.period_box, { + const period_box_class_name = clsx(styles.period_box, { [styles.mogu_bar]: item === "mogu_2000", [styles.mochi_bar]: item === "mochi_1000", [styles.fuwa_bar]: item === "fuwa_500", [styles.basic_bar]: item === "basic_300", }); - return
; + return ( + {key}

+ } + placement="top" + slotProps={offset} + > +
+
+
+
+ ); })}
); diff --git a/src-ui/app/config_page/setting_section/setting_box/supporters/supporters_container/supporters_wrapper/SupportersWrapper.module.scss b/src-ui/app/config_page/setting_section/setting_box/supporters/supporters_container/supporters_wrapper/SupportersWrapper.module.scss index 19cbb0ac..c0c35d76 100644 --- a/src-ui/app/config_page/setting_section/setting_box/supporters/supporters_container/supporters_wrapper/SupportersWrapper.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/supporters/supporters_container/supporters_wrapper/SupportersWrapper.module.scss @@ -13,7 +13,7 @@ align-content: start; flex-wrap: wrap; column-gap: 1.8rem; - row-gap: 1rem; + row-gap: 0.4rem; } .supporter_image_container { @@ -21,12 +21,14 @@ width: 18rem; display: flex; flex-direction: column; - gap: 0.3rem; } .supporter_image_wrapper { position: relative; overflow: hidden; + &:hover .supporter_icon_wrapper{ + transform: rotate(360deg); + } } .supporter_image { @@ -54,7 +56,8 @@ height: 1.4rem; } } - +$progress_ease: cubic-bezier(0, 1, 0.75, 1); +// Duplicated .supporter_icon_wrapper { height: 4rem; aspect-ratio: 1 /1; @@ -62,6 +65,7 @@ background-color: #ffffff; overflow: hidden; position: relative; + transition: transform 0.6s $progress_ease; } .supporter_icon { @@ -227,6 +231,12 @@ background-color: var(--dark_800_color); } } +.period_box_wrapper { + padding: 0.3rem 0 0.4rem 0; +} +.tooltip_period_label { + font-size: 1.4rem; +} .progress_bar { diff --git a/src-ui/app/config_page/sidebar_section/SidebarSection.jsx b/src-ui/app/config_page/sidebar_section/SidebarSection.jsx index f845a733..c2342aa2 100644 --- a/src-ui/app/config_page/sidebar_section/SidebarSection.jsx +++ b/src-ui/app/config_page/sidebar_section/SidebarSection.jsx @@ -45,7 +45,9 @@ const Tab = (props) => { const getLabel = () => { if (props.tab_id === "vr") return "VR"; - if (props.tab_id === "supporters") return "Supporters"; + if (props.tab_id === "supporters") return ( + <>Supporters👑 + ); if (props.tab_id === "about_vrct") return "About VRCT"; return t(`config_page.side_menu_labels.${props.tab_id}`); }; diff --git a/src-ui/app/config_page/sidebar_section/SidebarSection.module.scss b/src-ui/app/config_page/sidebar_section/SidebarSection.module.scss index 2e27f0a8..56977d6f 100644 --- a/src-ui/app/config_page/sidebar_section/SidebarSection.module.scss +++ b/src-ui/app/config_page/sidebar_section/SidebarSection.module.scss @@ -60,9 +60,18 @@ } .tab_text { - overflow: hidden; + // overflow: hidden; font-size: 1.6rem; - text-overflow: ellipsis; + // text-overflow: ellipsis; + position: relative; +} +.crown_emoji { + font-size: 1.6rem; + position: absolute; + top: 40%; + left: 100%; + transform: translateY(-50%); + padding-left: 0.4rem; } .separated_tabs_wrapper { diff --git a/src-ui/app/splash_component/download_models_container/DownloadModelsContainer.module.scss b/src-ui/app/splash_component/download_models_container/DownloadModelsContainer.module.scss index 0eabf677..dc60a0d0 100644 --- a/src-ui/app/splash_component/download_models_container/DownloadModelsContainer.module.scss +++ b/src-ui/app/splash_component/download_models_container/DownloadModelsContainer.module.scss @@ -51,6 +51,7 @@ } $progress_ease: cubic-bezier(0, 1, 0.75, 1); +// Duplicated .progress_bar { height: 8px; transition: width 0.3s $progress_ease; From 8f687d0a557fb3e1f89b2d1706e53281e84016e1 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Sun, 4 May 2025 13:39:51 +0900 Subject: [PATCH 61/68] [Update] Add a special thanks member. --- .../about_vrct/special_thanks_members.png | Bin 39807 -> 41164 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src-ui/assets/about_vrct/special_thanks_members.png b/src-ui/assets/about_vrct/special_thanks_members.png index f759c01a623eca46fb1ae8445ca11ad9c0a29bf7..8bb4ed60ee11b064441a166aae59e2d1a7e60547 100644 GIT binary patch delta 37817 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 N002ovPDHLkV1hU* delta 36354 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 From 2e424fcc2a8e93c9327a0d9ed857b168adbaeedd Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Mon, 5 May 2025 17:06:23 +0900 Subject: [PATCH 62/68] [Update] Optimize OSCQueryBrowser usage and reduce CPU load in OSC server --- src-python/models/osc/osc.py | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src-python/models/osc/osc.py b/src-python/models/osc/osc.py index 5336fdb6..758918af 100644 --- a/src-python/models/osc/osc.py +++ b/src-python/models/osc/osc.py @@ -24,6 +24,7 @@ class OSCHandler: self.osc_server_ip_address = ip_address self.http_port = None self.osc_server_port = None + self.browser = None def setOscIpAddress(self, ip_address:str) -> None: self.osc_ip_address = ip_address @@ -45,18 +46,26 @@ class OSCHandler: def getOSCParameterValue(self, address:str) -> Any: value = None try: - browser = OSCQueryBrowser() - sleep(1) - service = browser.find_service_by_name(self.osc_server_name) + # browserインスタンスを再利用し、毎回の生成と破棄を避ける + if self.browser is None: + self.browser = OSCQueryBrowser() + sleep(1) # 初回のみスリープ + + service = self.browser.find_service_by_name(self.osc_server_name) if service is not None: osc_query_client = OSCQueryClient(service) mute_self_node = osc_query_client.query_node(address) value = mute_self_node.value[0] - browser.zc.close() - browser.browser.cancel() - except Exception: errorLogging() + # エラー発生時にbrowserをリセットして次回再初期化 + if self.browser is not None: + try: + self.browser.zc.close() + self.browser.browser.cancel() + except Exception: + pass + self.browser = None return value def getOSCParameterMuteSelf(self) -> bool: @@ -82,7 +91,8 @@ class OSCHandler: sleep(1) def oscServerServe(self) -> None: - self.osc_server.serve_forever(2) + # ポーリング間隔を長くして(2秒から10秒に)CPUの使用率を削減 + self.osc_server.serve_forever(10) def oscServerStop(self) -> None: if isinstance(self.osc_server, osc_server.ThreadingOSCUDPServer): @@ -91,6 +101,14 @@ class OSCHandler: if isinstance(self.osc_query_service, OSCQueryService): self.osc_query_service.http_server.shutdown() self.osc_query_service = None + # browserがある場合はクリーンアップ + if self.browser is not None: + try: + self.browser.zc.close() + self.browser.browser.cancel() + except Exception: + pass + self.browser = None if __name__ == "__main__": handler = OSCHandler() From da577b5116c2104b7c452b7ffe385a0579358fc3 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Tue, 6 May 2025 10:57:38 +0900 Subject: [PATCH 63/68] [bugfix] Fix corner rounded bug. before: win10, corner rounded true. win 11 corner rounded true and when window maximized likewise. after: win10, corner rounded false. win 11 corner rounded true but turn to false when window maximized. --- src-ui/app/App.jsx | 2 + .../CornerRadiusController.jsx | 53 +++++++++++++++++++ src-ui/app/_app_controllers/index.js | 3 +- 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 src-ui/app/_app_controllers/CornerRadiusController.jsx diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index d645d774..7dc99689 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -9,6 +9,7 @@ import { UiSizeController, FontFamilyController, TransparencyController, + CornerRadiusController, PluginsController, } from "./_app_controllers/index.js"; @@ -39,6 +40,7 @@ export const App = () => { + {(currentIsBackendReady.data === false || currentIsVrctAvailable.data === false) ? diff --git a/src-ui/app/_app_controllers/CornerRadiusController.jsx b/src-ui/app/_app_controllers/CornerRadiusController.jsx new file mode 100644 index 00000000..1b84b9ad --- /dev/null +++ b/src-ui/app/_app_controllers/CornerRadiusController.jsx @@ -0,0 +1,53 @@ +import { useState, useEffect } from "react"; +import { getCurrentWindow } from "@tauri-apps/api/window"; + +export const CornerRadiusController = () => { + const [is_win11, setIsWin11] = useState(false); + const [is_maximized, setIsMaximized] = useState(false); + + // OS 判定(Win11 なら platformVersion の major ≥13) + useEffect(() => { + if (navigator.userAgentData?.getHighEntropyValues) { + navigator.userAgentData + .getHighEntropyValues(["platformVersion"]) + .then(({ platformVersion }) => { + const major = parseInt(platformVersion.split(".")[0], 10); + setIsWin11(major >= 13); + }) + .catch(() => { + setIsWin11(false); + }) + } else { + // フォールバックで Win10 扱い + setIsWin11(false); + } + }, []) + + + useEffect(() => { + let unlisten; + const setup = async () => { + const window = await getCurrentWindow(); + // 初期状態取得 + setIsMaximized(await window.isMaximized()); + // リサイズ時にも再取得 + const updateMax = () => window.isMaximized().then(setIsMaximized); + unlisten = await window.listen("tauri://resize", updateMax); + } + setup(); + + return () => { + if (unlisten) { + unlisten(); + } + } + }, []); + + // 角丸の適用 + useEffect(() => { + const radius = is_win11 && !is_maximized ? "10px" : "0"; + document.body.style.borderRadius = radius; + }, [is_win11, is_maximized]); + + return null;; +} \ No newline at end of file diff --git a/src-ui/app/_app_controllers/index.js b/src-ui/app/_app_controllers/index.js index d71c8b6e..ea67d783 100644 --- a/src-ui/app/_app_controllers/index.js +++ b/src-ui/app/_app_controllers/index.js @@ -6,4 +6,5 @@ export { ConfigPageCloseTriggerController } from "./ConfigPageCloseTriggerContro export { UiSizeController } from "./UiSizeController"; export { FontFamilyController } from "./FontFamilyController"; export { TransparencyController } from "./TransparencyController"; -export { PluginsController } from "./PluginsController"; \ No newline at end of file +export { PluginsController } from "./PluginsController"; +export { CornerRadiusController } from "./CornerRadiusController"; \ No newline at end of file From 37d78e1d87a8dcbf8e0b11516b8df54fd83a3ea3 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Tue, 6 May 2025 13:12:50 +0900 Subject: [PATCH 64/68] [Update] Plugins: Add homepage link button. --- src-ui/app/_index_css/variables.css | 4 +- .../advanced_settings/plugins/Plugins.jsx | 57 ++++++++++++++++++- .../plugins/Plugins.module.scss | 43 +++++++++++++- src-ui/logics/configs/plugins/usePlugins.js | 8 ++- 4 files changed, 107 insertions(+), 5 deletions(-) diff --git a/src-ui/app/_index_css/variables.css b/src-ui/app/_index_css/variables.css index d212b415..6a3c48e4 100644 --- a/src-ui/app/_index_css/variables.css +++ b/src-ui/app/_index_css/variables.css @@ -19,6 +19,7 @@ --primary_600_color_44: #36877744; --sent_400_color: #6197b4; + --sent_300_color: #6197b4; --received_300_color: #a861b4; --error_bc_color: #bb4448; --error_bc_active_color: #9c3938; @@ -65,4 +66,5 @@ --config_page_topbar_height: 8rem; --font_family: "Yu Gothic UI"; -} \ No newline at end of file +} +/* https://m2.material.io/design/color/the-color-system.html#tools-for-picking-colors */ \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx index b19d48d7..2980ebab 100644 --- a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.jsx @@ -1,9 +1,10 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState, useCallback } from "react"; import { useTranslation } from "react-i18next"; import { usePlugins } from "@logics_configs"; import styles from "./Plugins.module.scss"; import { PluginsControlComponent } from "../../_components/plugins_control_component/PluginsControlComponent"; import { useNotificationStatus } from "@logics_common"; +import ExternalLink from "@images/external_link.svg?react"; export const Plugins = () => { const { @@ -87,6 +88,7 @@ const PluginDownloadContainer = () => { title: target_info.title, desc: target_info.desc || null, }; + const homepage_link = plugin.latest_plugin_info?.homepage_link; return (
@@ -98,6 +100,8 @@ const PluginDownloadContainer = () => { {target_locale.desc}

{/*

{plugin.plugin_id}

*/} + {homepage_link && + }
{plugin.is_error ? ( @@ -116,4 +120,55 @@ const PluginDownloadContainer = () => { })}
); +}; + +const HomepageLinkButton = ({ homepage_link, speed = 40 /* px/s */ }) => { + const containerRef = useRef(null); + const textRef = useRef(null); + const [inlineStyle, setInlineStyle] = useState({}); + + const handleMouseEnter = useCallback(() => { + const container = containerRef.current; + const text = textRef.current; + if (!container || !text) return; + const overflow = text.scrollWidth - container.clientWidth; + if (overflow > 0) { + const duration = overflow / speed; + setInlineStyle({ + transform: `translateX(-${overflow}px)`, + transition: `transform ${duration}s linear`, + }); + } + }, [speed]); + + const handleMouseLeave = useCallback(() => { + setInlineStyle({ + transform: 'translateX(0)', + transition: 'transform 0.3s ease-out', + }); + }, []); + + return ( + + ); }; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss index af4bae9a..fbac276f 100644 --- a/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/advanced_settings/plugins/Plugins.module.scss @@ -55,4 +55,45 @@ // overflow: hidden; // white-space: nowrap; // text-overflow: ellipsis; -// } \ No newline at end of file +// } + +.open_homepage_button_wrapper { + width: 100%; +} + +.open_homepage_button { + padding: 0.6rem 0; + display: flex; + align-items: center; + gap: 1rem; + cursor: pointer; + text-decoration: none; + &:hover .external_link_svg { + color: var(--dark_200_color); + } + &:hover .open_homepage_text { + border-bottom: 0.1rem solid var(--dark_500_color); + } +} + +.text_container { + position: relative; + overflow: hidden; + flex: 1; /* テキスト部分をアイコンまで伸ばす */ +} + +.open_homepage_text { + margin: 0; /* pタグのデフォルトマージン除去 */ + font-size: 1.4rem; + color: var(--sent_400_color); + white-space: nowrap; + display: inline-block; + transform: translateX(0); + border-bottom: 0.1rem solid var(--sent_400_color); +} + +.external_link_svg { + flex-shrink: 0; + width: 1.6rem; + color: var(--dark_500_color); +} \ No newline at end of file diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index bbeb8439..d4d398fa 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -227,7 +227,10 @@ export const usePlugins = () => { plugins_data.map(async (plugin_data) => { try { const plugin_info = await asyncFetchPluginInfo(plugin_data.url); - return plugin_info; + return { + ...plugin_info, + homepage_link: plugin_data.homepage_link, + }; } catch (error) { console.error("Error fetching plugin info for URL: ", plugin_data.url, error); return { @@ -235,7 +238,8 @@ export const usePlugins = () => { plugin_id: plugin_data.plugin_id || plugin_data.title, is_error: true, error_message: error.message, - url: plugin_data.url + url: plugin_data.url, + homepage_link: plugin_data.homepage_link, }; } }) From e9715651d94e949d701c3254e38587ceb7c6f9c1 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Tue, 6 May 2025 14:44:10 +0900 Subject: [PATCH 65/68] [Update] Upgrade torch version to 2.7.0 and adjust pip install options in install scripts --- install.bat | 4 ++-- requirements.txt | 2 +- requirements_cuda.txt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/install.bat b/install.bat index 03469d35..5a57e817 100644 --- a/install.bat +++ b/install.bat @@ -3,8 +3,8 @@ python -m venv .venv_cuda call .venv/Scripts/activate python.exe -m pip install --upgrade pip -pip install -r requirements.txt +pip install --no-cache-dir --force-reinstall -r requirements.txt call .venv_cuda/Scripts/activate python.exe -m pip install --upgrade pip -pip install -r requirements_cuda.txt \ No newline at end of file +pip install --no-cache-dir --force-reinstall -r requirements_cuda.txt \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 950c83d7..a4b5d371 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -torch==2.2.2 +torch==2.7.0 faster-whisper==1.0.3 ctranslate2==4.3.1 transformers==4.40.2 diff --git a/requirements_cuda.txt b/requirements_cuda.txt index df90696a..b28ff16e 100644 --- a/requirements_cuda.txt +++ b/requirements_cuda.txt @@ -1,5 +1,5 @@ -torch==2.2.2 ---extra-index-url https://download.pytorch.org/whl/cu121 +torch==2.7.0 +--extra-index-url https://download.pytorch.org/whl/cu128 faster-whisper==1.0.3 ctranslate2==4.3.1 transformers==4.40.2 From c6bcb36ab16b356ada3a230118d43131ca41ccbb Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Thu, 8 May 2025 10:35:57 +0900 Subject: [PATCH 66/68] =?UTF-8?q?[Update]=20Package=20:=20RTX5000=E7=95=AA?= =?UTF-8?q?=E5=8F=B0=E5=AF=BE=E5=BF=9C=E3=81=AE=E3=81=9F=E3=82=81=E3=81=AB?= =?UTF-8?q?fast-whisper/ctranslate2=E3=81=AE=E3=83=90=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=82=92=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | 4 ++-- requirements_cuda.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index a4b5d371..b384b8a0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ torch==2.7.0 -faster-whisper==1.0.3 -ctranslate2==4.3.1 +faster-whisper==1.1.1 +ctranslate2==4.6.0 transformers==4.40.2 pillow == 10.0.0 PyAudioWPatch == 0.2.12.6 diff --git a/requirements_cuda.txt b/requirements_cuda.txt index b28ff16e..8642db8b 100644 --- a/requirements_cuda.txt +++ b/requirements_cuda.txt @@ -1,7 +1,7 @@ torch==2.7.0 --extra-index-url https://download.pytorch.org/whl/cu128 -faster-whisper==1.0.3 -ctranslate2==4.3.1 +faster-whisper==1.1.1 +ctranslate2==4.6.0 transformers==4.40.2 pillow == 10.0.0 PyAudioWPatch == 0.2.12.6 From a556486c83856d130ed984e5e40b3f9cc552ecdc Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 8 May 2025 13:05:35 +0900 Subject: [PATCH 67/68] [Update] Add a special thanks member. --- .../about_vrct/special_thanks_members.png | Bin 41164 -> 42205 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src-ui/assets/about_vrct/special_thanks_members.png b/src-ui/assets/about_vrct/special_thanks_members.png index 8bb4ed60ee11b064441a166aae59e2d1a7e60547..fde6114ae687e87c292579d768893d7941c54363 100644 GIT binary patch literal 42205 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 RKw+SdSE{-%YnAPy{tug*05$*s From 8d6da2eacccb3b7d928850f73527c4ea848b2b63 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Thu, 8 May 2025 16:10:19 +0900 Subject: [PATCH 68/68] [Update] Version 3.0.5 -> 3.1.0 --- src-python/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-python/config.py b/src-python/config.py index 879b90e5..b4a248df 100644 --- a/src-python/config.py +++ b/src-python/config.py @@ -956,7 +956,7 @@ class Config: def init_config(self): # Read Only - self._VERSION = "3.0.5" + self._VERSION = "3.1.0" if getattr(sys, 'frozen', False): self._PATH_LOCAL = os_path.dirname(sys.executable) else: