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");