[TMP 2] Plugins system. support directory structure and store system.(the plugin needs build at each project)

This commit is contained in:
Sakamoto Shiina
2025-03-08 18:43:56 +09:00
parent 22ada89fa6
commit 48c6e7d69f
5 changed files with 59 additions and 42 deletions

View File

@@ -1,22 +1,19 @@
import React from "react"; import React from "react";
import { initStore } from "./store/store";
import { MainContainer } from "./main_container/MainContainer"; import { MainContainer } from "./main_container/MainContainer";
export const init = (plugin_context) => { export const init = (plugin_context) => {
const { useHook: useStore_CountPluginState } = plugin_context.createAtomWithHook({ count: 6 }, "CountPluginState"); initStore(plugin_context.createAtomWithHook);
const EntryComponents = () => { const EntryComponents = () => {
return <MainContainer />;
return (
<MainContainer useStore_CountPluginState={useStore_CountPluginState}/>
);
}; };
// UI の"main_section"拡張ポイントにコンポーネントを登録
plugin_context.registerComponent({ plugin_context.registerComponent({
plugin_id: "dev_vrct_plugin_example_1", plugin_id: "plugin_example_1_my_plugin",
location: "main_section", location: "main_section",
component: EntryComponents, component: EntryComponents,
}); });
}; };
export default init;

View File

@@ -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 = () => { const incrementCount = () => {
updateCountPluginState((prev_value) => { updateCountPluginState((prev_value) => ({
return { count: prev_value.data.count + 1 } count: prev_value.data.count + 1,
}); }));
}; };
return ( return (
<div> <div>
<p>Dev Plugin Count: {currentCountPluginState?.data?.count}</p> <p>1 Zipped Dev Plugin Count: {currentCountPluginState?.data?.count}</p>
<button onClick={incrementCount}> <button onClick={incrementCount}>Increment Plugin Count</button>
Increment Plugin Count
</button>
</div> </div>
); );
}; };

View File

@@ -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]();
};

View File

@@ -44,6 +44,7 @@ const PluginDownloadContainer = () => {
{plugin_list.map((plugin) => ( {plugin_list.map((plugin) => (
<div key={plugin.plugin_id}> <div key={plugin.plugin_id}>
<h3>{plugin.title}</h3> <h3>{plugin.title}</h3>
<h3>{plugin.plugin_id}</h3>
<button onClick={() => handleDownload(plugin)}> <button onClick={() => handleDownload(plugin)}>
Download and Load Plugin Download and Load Plugin
</button> </button>

View File

@@ -1,12 +1,21 @@
import { invoke } from '@tauri-apps/api/tauri'; import { invoke } from '@tauri-apps/api/tauri';
import { createAtomWithHook, useStore_LoadedPluginsList } from "@store"; import { createAtomWithHook, useStore_LoadedPluginsList } from "@store";
import { transform } from "@babel/standalone"; import { transform } from "@babel/standalone";
import { writeFile, createDir, exists, readDir, BaseDirectory, readTextFile } from "@tauri-apps/api/fs"; 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 }); const dev_plugin_mapping = import.meta.glob("/src-tauri/plugins/**/index.jsx", { eager: true });
import JSZip from "jszip"; import JSZip from "jszip";
import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; 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 { updateLoadedPluginsList } = useStore_LoadedPluginsList();
const plugin_context = { const plugin_context = {
@@ -24,7 +33,6 @@
const asyncLoadPlugin = async (plugin_relative_path) => { const asyncLoadPlugin = async (plugin_relative_path) => {
plugin_relative_path = "plugins/" + plugin_relative_path; plugin_relative_path = "plugins/" + plugin_relative_path;
console.log("plugin_relative_path",plugin_relative_path);
try { try {
const plugin_code = await readTextFile(plugin_relative_path, { dir: BaseDirectory.Resource, recursive: true }); const plugin_code = await readTextFile(plugin_relative_path, { dir: BaseDirectory.Resource, recursive: true });
@@ -32,10 +40,11 @@
const transpiled_code = transform(cleanedCode, { const transpiled_code = transform(cleanedCode, {
presets: [ presets: [
["env", { modules: false }], ["env", { modules: false }],
"react" "react",
], ],
sourceType: "module" sourceType: "module"
}).code; }).code;
const blob = new Blob([transpiled_code], { type: "text/javascript" }); const blob = new Blob([transpiled_code], { type: "text/javascript" });
const blob_url = URL.createObjectURL(blob); const blob_url = URL.createObjectURL(blob);
const plugin_module = await import(/* @vite-ignore */ blob_url); const plugin_module = await import(/* @vite-ignore */ blob_url);
@@ -53,7 +62,6 @@
if (import.meta.env.DEV) { if (import.meta.env.DEV) {
// ホットリロード対応 src-tauri以下にあるpluginsディレクトリから直接読み込み開発用 // ホットリロード対応 src-tauri以下にあるpluginsディレクトリから直接読み込み開発用
Object.entries(dev_plugin_mapping).forEach(([key, plugin_module]) => { Object.entries(dev_plugin_mapping).forEach(([key, plugin_module]) => {
console.log(plugin_module);
if (plugin_module && plugin_module.init) { if (plugin_module && plugin_module.init) {
plugin_module.init(plugin_context); plugin_module.init(plugin_context);
} }
@@ -62,9 +70,7 @@
try { try {
const plugin_files = await readDir("plugins", { dir: BaseDirectory.Resource, recursive: true }); const plugin_files = await readDir("plugins", { dir: BaseDirectory.Resource, recursive: true });
for (const target_dir of plugin_files) { for (const target_dir of plugin_files) {
console.log(target_dir); const target_path = target_dir.name + "/index.es.js";
const target_path = target_dir.name + "\\index.jsx";
await asyncLoadPlugin(target_path, plugin_context); await asyncLoadPlugin(target_path, plugin_context);
} }
} catch (error) { } catch (error) {
@@ -76,8 +82,6 @@
const downloadAndExtractPlugin = async (plugin) => { const downloadAndExtractPlugin = async (plugin) => {
try { try {
const plugin_zip_url = await fetchLatestPluginZipUrl(plugin); const plugin_zip_url = await fetchLatestPluginZipUrl(plugin);
console.log("Latest plugin zip URL:", plugin_zip_url);
// Rust コマンド経由で zip をダウンロード // Rust コマンド経由で zip をダウンロード
const base64Zip = await invoke("download_zip_asset", { url: plugin_zip_url }); const base64Zip = await invoke("download_zip_asset", { url: plugin_zip_url });
// base64Zip は文字列なので、デコードして Uint8Array に変換 // base64Zip は文字列なので、デコードして Uint8Array に変換
@@ -102,19 +106,15 @@
zip.forEach((relativePath, zipEntry) => { zip.forEach((relativePath, zipEntry) => {
// .git 以下のファイルはスキップ // .git 以下のファイルはスキップ
if (relativePath.startsWith(".git") || relativePath.includes("/.git/")) { if (relativePath.startsWith(".git") || relativePath.includes("/.git/")) {
// console.log("Skipping .git file: " + relativePath);
return; return;
} }
const filePath = target_plugin_path + "/" + relativePath; const filePath = target_plugin_path + "/" + relativePath;
if (zipEntry.dir) { if (zipEntry.dir) {
// フォルダの場合、ディレクトリを作成 // フォルダの場合、ディレクトリを作成
filePromises.push( filePromises.push(
createDir(filePath, { dir: BaseDirectory.Resource, recursive: true }).catch((err) => { createDir(filePath, { dir: BaseDirectory.Resource, recursive: true }).catch((err) => {
console.log(err);
if (!err.message?.includes("already exists")) { if (!err.message?.includes("already exists")) {
console.error("Failed to create directory:", filePath, err); console.error("Failed to create directory:", filePath, err);
} }
@@ -142,9 +142,7 @@
await Promise.all(filePromises); await Promise.all(filePromises);
console.log("Plugin downloaded successfully."); console.log("Plugin downloaded successfully.");
const index_file_relative_path = plugin.asset_name.replace(".zip", "") + "/" + "index.jsx" const index_file_relative_path = plugin.asset_name.replace(".zip", "") + "/" + "index.es.js"
console.log("index_file_relative_path", index_file_relative_path);
await asyncLoadPlugin(index_file_relative_path); await asyncLoadPlugin(index_file_relative_path);
console.log("Plugin loaded successfully."); console.log("Plugin loaded successfully.");