[Update] update tauri v1-> v2 (development)

This commit is contained in:
Sakamoto Shiina
2025-05-03 08:47:02 +09:00
parent c6f669336a
commit 3210d5c898
26 changed files with 9570 additions and 7229 deletions

4845
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{ {
"name": "tauri-app", "name": "vrct",
"private": true, "private": true,
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
@@ -23,33 +23,38 @@
"release-all": "npm run release && npm run release-cuda" "release-all": "npm run release && npm run release-cuda"
}, },
"dependencies": { "dependencies": {
"@babel/standalone": "7.26.9", "@babel/standalone": "7.27.0",
"@emotion/react": "11.14.0", "@emotion/react": "11.14.0",
"@emotion/styled": "11.14.0", "@emotion/styled": "11.14.0",
"@mui/material": "6.2.0", "@mui/material": "7.0.2",
"@tauri-apps/api": "1.6.0", "@tauri-apps/api": "2.5.0",
"@vitejs/plugin-react": "4.3.4", "@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", "clsx": "2.1.1",
"eslint": "8.57.0", "i18next": "25.0.1",
"eslint-plugin-react": "7.37.2", "jotai": "2.12.3",
"i18next": "24.1.0",
"jotai": "2.10.3",
"js-base64": "3.7.7", "js-base64": "3.7.7",
"jszip": "3.10.1", "jszip": "3.10.1",
"react": "18.2.0", "react": "18.3.1",
"react-dom": "18.2.0", "react-dom": "18.3.1",
"react-error-boundary": "5.0.0", "react-error-boundary": "5.0.0",
"react-i18next": "15.2.0", "react-i18next": "15.5.1",
"react-resizable-layout": "0.7.2", "react-resizable-layout": "0.7.2",
"sass": "1.79.4",
"semver": "7.7.1" "semver": "7.7.1"
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-yaml": "^4.1.2", "@rollup/plugin-yaml": "4.1.2",
"@tauri-apps/cli": "1.6.3", "@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", "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": "6.3.4",
"vite-plugin-svgr": "4.3.0" "vite-plugin-svgr": "4.3.0"
} }
} }

3516
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,19 +7,27 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # 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] [build-dependencies]
tauri-build = { version = "1", features = [] } tauri-build = { version = "=2.2.0", features = [] }
[dependencies] [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"] } tauri = { version = "=2.5.1", features = ["devtools"] }
serde = { version = "1", features = ["derive"] } tauri-plugin-opener = "=2.2.6"
serde_json = "1" serde = { version = "=1.0.219", features = ["derive"] }
font-kit = "0.14.2" serde_json = "=1.0.140"
window-shadows = { git = "https://github.com/tauri-apps/window-shadows.git" } tauri-plugin-fs = "=2.2.1"
reqwest = { version = "0.11", features = ["json", "rustls-tls"] } tauri-plugin-http = "=2.4.3"
base64 = "0.22.1" tauri-plugin-shell = "2.2.1"
font-kit = "=0.14.2"
reqwest = "=0.12.15"
base64 = "=0.22.1"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
[features] tauri-plugin-global-shortcut = "=2.2.0"
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
custom-protocol = ["tauri/custom-protocol"]

View File

@@ -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"
]
}

View File

@@ -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 }
]
}
]
}

64
src-tauri/src/lib.rs Normal file
View File

@@ -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<String> {
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<String, String> {
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))
}

View File

@@ -1,67 +1,6 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!! // Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use tauri::Manager;
use window_shadows::set_shadow;
fn main() { fn main() {
tauri::Builder::default() vrct_lib::run()
.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");
}
use font_kit::{source::SystemSource};
use std::collections::HashSet;
#[tauri::command]
async fn get_font_list() -> Vec<String> {
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<String, String> {
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))
} }

View File

@@ -1,62 +1,16 @@
{ {
"$schema": "https://schema.tauri.app/config/2",
"productName": "VRCT",
"version": "0.1.0",
"identifier": "com.vrct.app",
"build": { "build": {
"beforeDevCommand": "", "beforeDevCommand": "",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "", "beforeBuildCommand": "",
"devPath": "http://localhost:1420", "frontendDist": "../dist"
"distDir": "../dist"
}, },
"package": { "app": {
"productName": "VRCT", "enableGTKAppId": false,
"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 }
]
}
},
"windows": [{ "windows": [{
"title": "VRCT", "title": "VRCT",
"center": true, "center": true,
@@ -65,37 +19,40 @@
"minWidth": 400, "minWidth": 400,
"minHeight": 200, "minHeight": 200,
"transparent": true, "transparent": true,
"decorations": false "decorations": false,
"shadow": false
}], }],
"security": { "csp": null }, "security": {
"bundle": { "csp": null,
"active": true, "capabilities": ["default", "vrct-capability"]
"targets": "nsis", }
"identifier": "com.vrct.dev", },
"publisher": "m's software", "bundle": {
"copyright": "Copyright m's software", "active": true,
"shortDescription": "VRCT", "targets": "nsis",
"icon": [ "publisher": "m's software",
"icons/32x32.png", "copyright": "Copyright m's software",
"icons/128x128.png", "licenseFile": "../LICENSE",
"icons/128x128@2x.png", "shortDescription": "VRCT",
"icons/icon.icns", "icon": [
"icons/icon.ico" "icons/32x32.png",
], "icons/128x128.png",
"externalBin": [ "icons/128x128@2x.png",
"bin/VRCT-sidecar" "icons/icon.icns",
], "icons/icon.ico"
"resources": { ],
"bin/_internal": "_internal", "externalBin": [
"plugins": "plugins" "bin/VRCT-sidecar"
}, ],
"windows": { "resources": {
"nsis": { "bin/_internal": "_internal",
"template": "nsis/template.nsi", "plugins": "plugins"
"license": "../LICENSE", },
"installMode": "currentUser", "windows": {
"displayLanguageSelector": true "nsis": {
} "template": "nsis/template.nsi",
"installMode": "currentUser",
"displayLanguageSelector": true
} }
} }
} }

View File

@@ -26,7 +26,6 @@ import { AppErrorBoundary } from "./error_boundary/AppErrorBoundary";
export const App = () => { export const App = () => {
const { currentIsVrctAvailable } = useIsVrctAvailable(); const { currentIsVrctAvailable } = useIsVrctAvailable();
const { currentIsBackendReady } = useIsBackendReady(); const { currentIsBackendReady } = useIsBackendReady();
const { WindowGeometryController } = useWindow();
const { i18n } = useTranslation(); const { i18n } = useTranslation();
return ( return (
@@ -40,7 +39,6 @@ export const App = () => {
<UiSizeController /> <UiSizeController />
<FontFamilyController /> <FontFamilyController />
<TransparencyController /> <TransparencyController />
<WindowGeometryController />
{(currentIsBackendReady.data === false || currentIsVrctAvailable.data === false) {(currentIsBackendReady.data === false || currentIsVrctAvailable.data === false)
? <SplashComponent /> ? <SplashComponent />
@@ -54,9 +52,11 @@ export const App = () => {
}; };
const Contents = () => { const Contents = () => {
const { WindowGeometryController } = useWindow();
const { currentIsSoftwareUpdating } = useIsSoftwareUpdating(); const { currentIsSoftwareUpdating } = useIsSoftwareUpdating();
return ( return (
<> <>
<WindowGeometryController />
<PluginsController /> <PluginsController />
<WindowTitleBar /> <WindowTitleBar />

View File

@@ -1,4 +1,4 @@
import { invoke } from "@tauri-apps/api/tauri"; import { invoke } from "@tauri-apps/api/core";
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import { useStartPython } from "@logics/useStartPython"; import { useStartPython } from "@logics/useStartPython";
import { useStdoutToPython } from "@logics/useStdoutToPython"; import { useStdoutToPython } from "@logics/useStdoutToPython";

View File

@@ -126,7 +126,6 @@ export const MergePluginsController = () => {
} }
} }
console.log("merged plugin data", new_data);
return new_data; return new_data;
}); });
}; };

View File

@@ -24,7 +24,8 @@
html, body { html, body {
height: 100%; height: 100%;
font-family: var(--font_family); /* If not found the font family where 'root:' that is selected by user*/ 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;
} }

View File

@@ -1,5 +1,5 @@
import { useState } from "react"; 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 { ErrorBoundary } from "react-error-boundary";
import XMarkSvg from "@images/cancel.svg?react"; import XMarkSvg from "@images/cancel.svg?react";
import CopySvg from "@images/copy.svg?react"; import CopySvg from "@images/copy.svg?react";
@@ -65,12 +65,14 @@ const ErrorContainer = ({error}) => {
}; };
const CloseButtonContainer = () => { const CloseButtonContainer = () => {
const close = () => {
const asyncClose = async () => {
const appWindow = await getCurrentWindow();
appWindow.close(); appWindow.close();
}; };
return ( return (
<button className={styles.close_button_wrapper} onClick={close}> <button className={styles.close_button_wrapper} onClick={asyncClose}>
<div className={styles.close_button}> <div className={styles.close_button}>
<XMarkSvg className={styles.x_mark_svg}/> <XMarkSvg className={styles.x_mark_svg}/>
</div> </div>

View File

@@ -5,7 +5,7 @@ import { useMessage } from "@logics_common";
import { useSendMessageButtonType, useEnableAutoClearMessageInputBox } from "@logics_configs"; import { useSendMessageButtonType, useEnableAutoClearMessageInputBox } from "@logics_configs";
import { useMessageLogScroll } from "@logics_main"; import { useMessageLogScroll } from "@logics_main";
import { store } from "@store"; import { store } from "@store";
import { appWindow } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
export const MessageInputBox = () => { export const MessageInputBox = () => {
const [message_history, setMessageHistory] = useState([]); const [message_history, setMessageHistory] = useState([]);
@@ -41,6 +41,7 @@ export const MessageInputBox = () => {
const onSubmitFunction = (e) => { const onSubmitFunction = (e) => {
e.preventDefault(); e.preventDefault();
// const appWindow = getCurrentWindow();
// appWindow.minimize(); // appWindow.minimize();
if (!currentMessageInputValue.data.trim()) return updateMessageInputValue(""); if (!currentMessageInputValue.data.trim()) return updateMessageInputValue("");

View File

@@ -4,7 +4,7 @@ import { StartUpProgressContainer } from "./start_up_progress_container/StartUpP
import { DownloadModelsContainer } from "./download_models_container/DownloadModelsContainer/"; import { DownloadModelsContainer } from "./download_models_container/DownloadModelsContainer/";
import MegaphoneSvg from "@images/megaphone.svg?react"; import MegaphoneSvg from "@images/megaphone.svg?react";
import XMarkSvg from "@images/cancel.svg?react"; import XMarkSvg from "@images/cancel.svg?react";
import { appWindow } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import clsx from "clsx"; import clsx from "clsx";
export const SplashComponent = () => { export const SplashComponent = () => {
@@ -73,12 +73,13 @@ const AnnouncementsContainer = () => {
const CloseButtonContainer = () => { const CloseButtonContainer = () => {
const close = () => { const asyncClose = async () => {
const appWindow = await getCurrentWindow();
appWindow.close(); appWindow.close();
}; };
return ( return (
<button className={styles.close_button_wrapper} onClick={close}> <button className={styles.close_button_wrapper} onClick={asyncClose}>
<div className={styles.close_button}> <div className={styles.close_button}>
<XMarkSvg className={styles.x_mark_svg}/> <XMarkSvg className={styles.x_mark_svg}/>
</div> </div>

View File

@@ -5,15 +5,17 @@ import SquareSvg from "@images/square.svg?react";
import LineSvg from "@images/line.svg?react"; import LineSvg from "@images/line.svg?react";
import VrctSvg from "@images/vrct.svg?react"; import VrctSvg from "@images/vrct.svg?react";
import { appWindow } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
export const WindowTitleBar = () => { export const WindowTitleBar = () => {
const minimize = () => { const asyncMinimize = async () => {
const appWindow = await getCurrentWindow();
appWindow.minimize(); appWindow.minimize();
}; };
const maximize = async () => { const asyncMaximize = async () => {
const appWindow = await getCurrentWindow();
const maximizeState = await appWindow.isMaximized(); const maximizeState = await appWindow.isMaximized();
if (!maximizeState) { if (!maximizeState) {
appWindow.maximize(); appWindow.maximize();
@@ -22,7 +24,8 @@ export const WindowTitleBar = () => {
} }
}; };
const close = () => { const asyncClose = async () => {
const appWindow = await getCurrentWindow();
appWindow.close(); appWindow.close();
}; };
@@ -34,13 +37,13 @@ export const WindowTitleBar = () => {
</div> </div>
<div className={styles.window_control_wrapper}> <div className={styles.window_control_wrapper}>
<div className={styles.minimize_button} onClick={minimize}> <div className={styles.minimize_button} onClick={asyncMinimize}>
<LineSvg className={styles.line_svg}/> <LineSvg className={styles.line_svg}/>
</div> </div>
<div className={styles.maximize_button} onClick={maximize}> <div className={styles.maximize_button} onClick={asyncMaximize}>
<SquareSvg className={styles.square_svg}/> <SquareSvg className={styles.square_svg}/>
</div> </div>
<div className={styles.close_button} onClick={close}> <div className={styles.close_button} onClick={asyncClose}>
<XMarkSvg className={styles.x_mark_svg}/> <XMarkSvg className={styles.x_mark_svg}/>
</div> </div>
</div> </div>

View File

@@ -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 = () => { export const useFetch = () => {
const asyncTauriFetchGithub = async (url) => { const asyncTauriFetchGithub = async (url, {return_row = false} = {}) => {
console.log("tauriFetch", url); console.log("tauriFetch", url);
const release_response = await tauriFetch(url, { const response = await tauriFetch(url, {
method: "GET", method: "GET",
responseType: ResponseType.Json,
headers: { headers: {
"Accept": "application/vnd.github+json", "Accept": "application/vnd.github+json",
"User-Agent": "VRCTPluginApp" "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 { return {

View File

@@ -1,15 +1,18 @@
import { useEffect } from "react"; import { useEffect, useRef } from "react";
import { appWindow, currentMonitor, availableMonitors, PhysicalPosition, PhysicalSize } from "@tauri-apps/api/window"; import { getCurrentWindow, currentMonitor, availableMonitors, PhysicalPosition, PhysicalSize } from "@tauri-apps/api/window";
import { useStdoutToPython } from "@logics/useStdoutToPython"; import { useStdoutToPython } from "@logics/useStdoutToPython";
import { useStore_IsBreakPoint } from "@store"; import { useStore_IsBreakPoint } from "@store";
import { useUiScaling } from "@logics_configs"; import { useUiScaling } from "@logics_configs";
import { store } from "@store";
export const useWindow = () => { export const useWindow = () => {
const { asyncStdoutToPython } = useStdoutToPython(); const { asyncStdoutToPython } = useStdoutToPython();
const { currentUiScaling } = useUiScaling(); const { currentUiScaling } = useUiScaling();
const { updateIsBreakPoint } = useStore_IsBreakPoint(); const { updateIsBreakPoint } = useStore_IsBreakPoint();
const asyncGetWindowGeometry = async () => { const asyncGetWindowGeometry = async () => {
const appWindow = await getCurrentWindow();
try { try {
const position = await appWindow.outerPosition(); const position = await appWindow.outerPosition();
const { x: x_pos, y: y_pos } = position; const { x: x_pos, y: y_pos } = position;
@@ -29,6 +32,7 @@ export const useWindow = () => {
}; };
const asyncSaveWindowGeometry = async () => { const asyncSaveWindowGeometry = async () => {
const appWindow = await getCurrentWindow();
const minimized = await appWindow.isMinimized(); const minimized = await appWindow.isMinimized();
if (minimized === true) return; // don't save while the window is minimized. if (minimized === true) return; // don't save while the window is minimized.
const data = await asyncGetWindowGeometry(); const data = await asyncGetWindowGeometry();
@@ -36,6 +40,8 @@ export const useWindow = () => {
}; };
const restoreWindowGeometry = async (data) => { const restoreWindowGeometry = async (data) => {
const appWindow = await getCurrentWindow();
try { try {
const monitors = await availableMonitors(); const monitors = await availableMonitors();
const { x_pos, y_pos, width, height } = data; const { x_pos, y_pos, width, height } = data;
@@ -89,38 +95,53 @@ export const useWindow = () => {
}; };
const asyncUpdateBreakPoint = async () => { const asyncUpdateBreakPoint = async () => {
const appWindow = await getCurrentWindow();
const size = await appWindow.innerSize(); const size = await appWindow.innerSize();
const dynamicBreakPoint = 800 * (currentUiScaling.data / 100); const dynamicBreakPoint = 800 * (currentUiScaling.data / 100);
updateIsBreakPoint(size.width <= dynamicBreakPoint); updateIsBreakPoint(size.width <= dynamicBreakPoint);
}; };
const WindowGeometryController = () => { const WindowGeometryController = () => {
useEffect(() => {
let resizeTimeout;
const asyncFunction = () => {
asyncSaveWindowGeometry();
asyncUpdateBreakPoint();
};
const unlistenResize = appWindow.onResized(() => { const resizeTimeout = useRef(null);
clearTimeout(resizeTimeout); const moveTimeout = useRef(null);
resizeTimeout = setTimeout(asyncFunction, 200); const unlistenResize = useRef(null);
}); const unlistenMove = useRef(null);
return () => {
unlistenResize.then((dispose) => dispose());
};
}, []);
useEffect(() => { useEffect(() => {
let moveTimeout; const setup = async () => {
const unlistenMove = appWindow.onMoved(() => { if (store.is_register_window_geometry_controller) return;
clearTimeout(moveTimeout); const appWindow = await getCurrentWindow();
moveTimeout = setTimeout(asyncSaveWindowGeometry, 200);
}); 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 () => { 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);
}; };
}, []); }, []);

View File

@@ -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 { store, useStore_Hotkeys } from "@store";
import { useStdoutToPython } from "@logics/useStdoutToPython"; import { useStdoutToPython } from "@logics/useStdoutToPython";
import { useNotificationStatus } from "@logics_common"; import { useNotificationStatus } from "@logics_common";
import { useMainFunction } from "@logics_main"; 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 = () => { export const useHotkeys = () => {
const { asyncStdoutToPython } = useStdoutToPython(); const { asyncStdoutToPython } = useStdoutToPython();
@@ -66,16 +66,19 @@ export const useHotkeys = () => {
const isAlreadyRegistered = await isRegistered(shortcut); const isAlreadyRegistered = await isRegistered(shortcut);
if (!isAlreadyRegistered) { if (!isAlreadyRegistered) {
await register(shortcut, async () => { await register(shortcut, async (event) => {
if (event.state !== "Pressed") return;
const appWindow = await getCurrentWindow();
switch (actionKey) { switch (actionKey) {
case "toggle_vrct_visibility": { case "toggle_vrct_visibility": {
const minimized = await appWindow.isMinimized(); const minimized = await appWindow.isMinimized();
if (minimized) { if (minimized) {
appWindow.unminimize(); await appWindow.unminimize();
await appWindow.setFocus(); await appWindow.setFocus();
store.text_area_ref.current?.focus(); store.text_area_ref.current?.focus();
} else { } else {
appWindow.minimize(); await appWindow.minimize();
} }
break; break;
} }

View File

@@ -1,4 +1,4 @@
import { invoke } from "@tauri-apps/api/tauri"; import { invoke } from "@tauri-apps/api/core";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { IS_PLUGIN_PATH_DEV_MODE, getPluginsList } from "@ui_configs"; import { IS_PLUGIN_PATH_DEV_MODE, getPluginsList } from "@ui_configs";
import { import {
@@ -14,7 +14,7 @@ import {
import { useStdoutToPython } from "@logics/useStdoutToPython"; import { useStdoutToPython } from "@logics/useStdoutToPython";
import { transform } from "@babel/standalone"; 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"; import { dev_plugins } from "@plugins_index";
const imported_dev_plugins = []; const imported_dev_plugins = [];
dev_plugins.forEach(async ({entry_path}) => { 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 downloaded_plugin_info_path = "plugins/" + plugin_folder_relative_path + "/plugin_info.json";
const plugin_css_path = "plugins/" + plugin_folder_relative_path + "/main.css"; const plugin_css_path = "plugins/" + plugin_folder_relative_path + "/main.css";
try { 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 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 cleaned_code = removeImportStatements(plugin_code);
const transpiled_code = transform(cleaned_code, { const transpiled_code = transform(cleaned_code, {
presets: [ presets: [
@@ -123,12 +123,12 @@ export const usePlugins = () => {
} }
}); });
} else { } 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; if (!is_plugins_dir_exists) return;
try { try {
const plugin_entries = await readDir("plugins", { dir: BaseDirectory.Resource, recursive: true }); const plugin_entries = await readDir("plugins", { baseDir: BaseDirectory.Resource, recursive: true });
const plugin_files = plugin_entries.filter(entry => entry.children && Array.isArray(entry.children)); const plugin_files = plugin_entries.filter(entry => entry.isDirectory === true);
for (const target_dir of plugin_files) { for (const target_dir of plugin_files) {
const target_path = target_dir.name; const target_path = target_dir.name;
@@ -139,83 +139,76 @@ export const usePlugins = () => {
} }
} }
}; };
const downloadAndExtractPlugin = async (plugin) => { const downloadAndExtractPlugin = async (plugin) => {
const latest_plugin_info = plugin.latest_plugin_info; const { latest_plugin_info } = plugin;
try { try {
const plugin_zip_url = await fetchLatestPluginZipUrl(latest_plugin_info); // 1. ZIP をダウンロード (ブラウザの fetch を使用)
console.log("start download", plugin_zip_url); const pluginZipUrl = await fetchLatestPluginZipUrl(latest_plugin_info);
// Rust コマンド経由で ZIP をダウンロード console.log('start download', pluginZipUrl);
const base64_zip = await invoke("download_zip_asset", { url: plugin_zip_url }); const res = await asyncTauriFetchGithub(pluginZipUrl, {return_row: true});
// base64_zip をデコードして Uint8Array に変換 if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
const binary_string = atob(base64_zip); const arrayBuffer = await res.arrayBuffer();
const len = binary_string.length; const bytes = new Uint8Array(arrayBuffer);
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
// JSZip で ZIP を解凍 // 2. JSZip で ZIP を解凍
const zip = await JSZip.loadAsync(bytes); const zip = await JSZip.loadAsync(bytes);
// 展開先ディレクトリのパス(例:"plugins/<plugin_id>" とする) // 3. 展開先ディレクトリを準備
const target_plugin_path = "plugins/" + latest_plugin_info.plugin_id; const targetPath = `plugins/${latest_plugin_info.plugin_id}`;
// 既に存在する場合は削除してから新規作成 if (await exists(targetPath, { baseDir: BaseDirectory.Resource })) {
if (await exists(target_plugin_path, { dir: BaseDirectory.Resource, recursive: true })) { await remove(targetPath, { baseDir: BaseDirectory.Resource, recursive: true });
await removeDir(target_plugin_path, { dir: 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 = []; // 4. ZIP 内のエントリをひとつずつ展開 & 書き出し
zip.forEach((relative_path, zip_entry) => { const filePromises = [];
// .git 以下のファイルはスキップ zip.forEach((relativePath, entry) => {
if (relative_path.startsWith(".git") || relative_path.includes("/.git/")) { // .git 以下はスキップ
if (relativePath.startsWith('.git') || relativePath.includes('/.git/')) {
return; return;
} }
const file_path = target_plugin_path + "/" + relative_path; const filePath = `${targetPath}/${relativePath}`;
if (zip_entry.dir) { if (entry.dir) {
file_promises.push( // ディレクトリの場合は mkdir
createDir(file_path, { dir: BaseDirectory.Resource, recursive: true }).catch((err) => { filePromises.push(
if (!err.message?.includes("already exists")) { mkdir(filePath, { baseDir: BaseDirectory.Resource, recursive: true })
console.error("Failed to create directory:", file_path, err); .catch(err => {
} if (!err.message.includes('already exists')) {
}) console.error('Failed to create directory:', filePath, err);
}
})
); );
} else { } else {
const dir_path = file_path.substring(0, file_path.lastIndexOf("/")); // ファイルの場合は親ディレクトリを確保してからバイナリ書き込み
const promise = createDir(dir_path, { dir: BaseDirectory.Resource, recursive: true }) const dirPath = filePath.substring(0, filePath.lastIndexOf('/'));
.catch((err) => { filePromises.push(
if (!err.message?.includes("already exists")) { mkdir(dirPath, { baseDir: BaseDirectory.Resource, recursive: true })
console.error("Failed to create parent directory:", dir_path, err); .catch(err => {
} if (!err.message.includes('already exists')) {
}) console.error('Failed to create parent directory:', dirPath, err);
.then(() => zip_entry.async("text")) }
.then(async (file_data) => { })
await writeFile(file_path, file_data, { dir: BaseDirectory.Resource, recursive: true }); .then(() => entry.async('uint8array'))
}); .then(data =>
file_promises.push(promise); writeFile(filePath, data, { baseDir: BaseDirectory.Resource })
)
);
} }
}); });
await Promise.all(filePromises);
await Promise.all(file_promises); console.log('Plugin downloaded successfully.');
console.log("Plugin downloaded successfully."); // 5. プラグインをロード
await asyncLoadPlugin(latest_plugin_info.plugin_id);
const index_file_relative_path = plugin.plugin_id; console.log('Plugin loaded successfully.');
await asyncLoadPlugin(index_file_relative_path);
console.log("Plugin loaded successfully.");
} catch (error) { } catch (error) {
console.error("Error downloading and extracting plugin:", error); console.error('Error downloading and extracting plugin:', error);
} }
}; };
const fetchLatestPluginZipUrl = async (plugin) => { const fetchLatestPluginZipUrl = async (plugin) => {
const api_url = plugin.url; const api_url = plugin.url;
const response = await asyncTauriFetchGithub(api_url); const release_info = 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 asset = release_info.assets.find((a) => a.name === plugin.asset_name); const asset = release_info.assets.find((a) => a.name === plugin.asset_name);
if (!asset) { if (!asset) {
throw new Error(`Asset ${plugin.asset_name} not found in the latest release`); 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; store.is_fetched_plugins_info_already = true;
try { try {
const response = await asyncTauriFetchGithub(PLUGIN_LIST_URL); const plugins_data = 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 updated_list = await Promise.all( const updated_list = await Promise.all(
plugins_data.map(async (plugin_data) => { plugins_data.map(async (plugin_data) => {
try { try {
@@ -263,19 +252,11 @@ export const usePlugins = () => {
const asyncFetchPluginInfo = async (plugin_info_asset_url) => { const asyncFetchPluginInfo = async (plugin_info_asset_url) => {
const release_response = await asyncTauriFetchGithub(plugin_info_asset_url); const release_response = await asyncTauriFetchGithub(plugin_info_asset_url);
if (release_response.status !== 200) { const plugin_info_json = release_response.assets.find(asset => asset.name === "plugin_info.json");
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) { if (!plugin_info_json) {
throw new Error("plugin_info.json not found in release assets"); throw new Error("plugin_info.json not found in release assets");
} }
const plugin_info_json_response = await asyncTauriFetchGithub(plugin_info_json.browser_download_url); const plugin_info = 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 { is_plugin_supported, is_plugin_supported_latest_vrct } = checkVrctVerCompatibility(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);
@@ -432,17 +413,16 @@ const removeImportStatements = (code) => {
// import { readTextFile, BaseDirectory } from "@tauri-apps/api/fs"; // import { readTextFile, BaseDirectory } from "@tauri-apps/api/fs";
const loadPluginCSS = async (plugin_css_path) => { 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 { try {
// プラグインフォルダのルートにある main.css を読み込む // プラグインフォルダのルートにある 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 に挿入する // style タグを作成して head に挿入する
const style_tag = document.createElement("style"); const style_tag = document.createElement("style");
style_tag.id = `plugin-css-${plugin_css_path.replace(/[^a-zA-Z0-9_-]/g, "")}`; style_tag.id = `plugin-css-${plugin_css_path.replace(/[^a-zA-Z0-9_-]/g, "")}`;
style_tag.textContent = css_content; style_tag.textContent = css_content;
document.head.appendChild(style_tag); document.head.appendChild(style_tag);
console.log("Plugin CSS loaded for:", plugin_css_path);
} catch (error) { } catch (error) {
console.error("Failed to load plugin CSS from", plugin_css_path, error); console.error("Failed to load plugin CSS from", plugin_css_path, error);
} }

View File

@@ -1,4 +1,4 @@
import { getCurrent } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import { import {
useStore_TranslationStatus, useStore_TranslationStatus,
@@ -75,8 +75,8 @@ export const useMainFunction = () => {
}; };
const toggleForeground = () => { const toggleForeground = async () => {
const main_page = getCurrent(); const main_page = await getCurrentWindow();
const is_foreground_enabled = !currentForegroundStatus.data; const is_foreground_enabled = !currentForegroundStatus.data;
main_page.setAlwaysOnTop(is_foreground_enabled); main_page.setAlwaysOnTop(is_foreground_enabled);
updateForegroundStatus(is_foreground_enabled); updateForegroundStatus(is_foreground_enabled);

View File

@@ -1,8 +1,9 @@
import { appWindow } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import { useStore_MessageInputBoxRatio } from "@store"; import { useStore_MessageInputBoxRatio } from "@store";
import { useStdoutToPython } from "@logics/useStdoutToPython"; import { useStdoutToPython } from "@logics/useStdoutToPython";
import { clampMinMax } from "@utils"; import { clampMinMax } from "@utils";
export const useMessageInputBoxRatio = () => { export const useMessageInputBoxRatio = () => {
const { asyncStdoutToPython } = useStdoutToPython(); const { asyncStdoutToPython } = useStdoutToPython();
const { currentMessageInputBoxRatio, updateMessageInputBoxRatio } = useStore_MessageInputBoxRatio(); const { currentMessageInputBoxRatio, updateMessageInputBoxRatio } = useStore_MessageInputBoxRatio();
@@ -11,6 +12,7 @@ export const useMessageInputBoxRatio = () => {
}; };
const asyncSetMessageInputBoxRatio = async (ratio) => { const asyncSetMessageInputBoxRatio = async (ratio) => {
const appWindow = getCurrentWindow();
const minimized = await appWindow.isMinimized(); const minimized = await appWindow.isMinimized();
if (minimized === true) return; // don't save while the window is minimized. if (minimized === true) return; // don't save while the window is minimized.
const parsed = parseFloat(ratio.toFixed(2)); const parsed = parseFloat(ratio.toFixed(2));

View File

@@ -1,4 +1,4 @@
import { Command } from "@tauri-apps/api/shell"; import { Command } from "@tauri-apps/plugin-shell";
import { store } from "@store"; import { store } from "@store";
import { useReceiveRoutes } from "./useReceiveRoutes"; import { useReceiveRoutes } from "./useReceiveRoutes";
import { import {

View File

@@ -19,6 +19,7 @@ export const store = {
setting_box_scroll_container: null, setting_box_scroll_container: null,
log_box_ref: null, log_box_ref: null,
text_area_ref: null, text_area_ref: null,
is_register_window_geometry_controller: false,
is_applied_init_message_box_height: false, is_applied_init_message_box_height: false,
is_initialized_load_plugin: false, is_initialized_load_plugin: false,
is_fetched_plugins_info_already: false, is_fetched_plugins_info_already: false,

View File

@@ -3,9 +3,12 @@ import react from "@vitejs/plugin-react";
import svgr from "vite-plugin-svgr"; import svgr from "vite-plugin-svgr";
import yaml from "@rollup/plugin-yaml"; import yaml from "@rollup/plugin-yaml";
import path from "path"; import path from "path";
import { globSync } from "node:fs";
import { pathToFileURL } from "url";
import { dev_plugins } from "./src-ui/plugins/plugins_index.js"; import { dev_plugins } from "./src-ui/plugins/plugins_index.js";
const host = process.env.TAURI_DEV_HOST;
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig(async () => { export default defineConfig(async () => {
@@ -26,6 +29,14 @@ export default defineConfig(async () => {
server: { server: {
port: 1420, port: 1420,
strictPort: true, strictPort: true,
host: host || false,
hmr: host
? {
protocol: "ws",
host,
port: 1421,
}
: undefined,
watch: { watch: {
// 3. tell vite to ignore watching `src-tauri` // 3. tell vite to ignore watching `src-tauri`
ignored: ["**/src-tauri/**"], ignored: ["**/src-tauri/**"],
@@ -79,25 +90,44 @@ export default defineConfig(async () => {
// 各プラグインのエイリアスを動的に読み込む関数
const getPluginAliases = async () => { const getPluginAliases = async () => {
const aliases = {}; 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) { for (const plugin of dev_plugins) {
const entry_path = plugin.entry_path; // 例: "dev_plugin_subtitles" const entry_path = plugin.entry_path; // 例: "dev_plugin_subtitles"
try { const relative_config_path = `src-ui/plugins/${entry_path}/plugin_configs.js`;
// エイリアス設定ファイルは各プラグインフォルダ内の "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); 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) { } 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; return aliases;
}; };