Merge branch 'tauri_v2' into develop
This commit is contained in:
4845
package-lock.json
generated
4845
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
37
package.json
37
package.json
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "tauri-app",
|
||||
"name": "vrct",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
@@ -23,33 +23,38 @@
|
||||
"release-all": "npm run release && npm run release-cuda"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/standalone": "7.26.9",
|
||||
"@babel/standalone": "7.27.0",
|
||||
"@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",
|
||||
"@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",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-plugin-react": "7.37.2",
|
||||
"i18next": "24.1.0",
|
||||
"jotai": "2.10.3",
|
||||
"i18next": "25.0.1",
|
||||
"jotai": "2.12.3",
|
||||
"js-base64": "3.7.7",
|
||||
"jszip": "3.10.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-error-boundary": "5.0.0",
|
||||
"react-i18next": "15.2.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": "1.6.3",
|
||||
"@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",
|
||||
"sass": "1.79.4",
|
||||
"source-map": "0.7.4",
|
||||
"vite": "6.2.5",
|
||||
"vite": "6.3.4",
|
||||
"vite-plugin-svgr": "4.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
3516
src-tauri/Cargo.lock
generated
3516
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
|
||||
10
src-tauri/capabilities/default.json
Normal file
10
src-tauri/capabilities/default.json
Normal 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"
|
||||
]
|
||||
}
|
||||
63
src-tauri/capabilities/vrct_capability.json
Normal file
63
src-tauri/capabilities/vrct_capability.json
Normal 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 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
|
||||
64
src-tauri/src/lib.rs
Normal file
64
src-tauri/src/lib.rs
Normal 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))
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
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))
|
||||
vrct_lib::run()
|
||||
}
|
||||
@@ -1,62 +1,16 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "VRCT",
|
||||
"version": "3.0.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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = () => {
|
||||
<UiSizeController />
|
||||
<FontFamilyController />
|
||||
<TransparencyController />
|
||||
<WindowGeometryController />
|
||||
|
||||
{(currentIsBackendReady.data === false || currentIsVrctAvailable.data === false)
|
||||
? <SplashComponent />
|
||||
@@ -54,9 +52,11 @@ export const App = () => {
|
||||
};
|
||||
|
||||
const Contents = () => {
|
||||
const { WindowGeometryController } = useWindow();
|
||||
const { currentIsSoftwareUpdating } = useIsSoftwareUpdating();
|
||||
return (
|
||||
<>
|
||||
<WindowGeometryController />
|
||||
<PluginsController />
|
||||
|
||||
<WindowTitleBar />
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -126,7 +126,6 @@ export const MergePluginsController = () => {
|
||||
}
|
||||
}
|
||||
|
||||
console.log("merged plugin data", new_data);
|
||||
return new_data;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<button className={styles.close_button_wrapper} onClick={close}>
|
||||
<button className={styles.close_button_wrapper} onClick={asyncClose}>
|
||||
<div className={styles.close_button}>
|
||||
<XMarkSvg className={styles.x_mark_svg}/>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useMessage } from "@logics_common";
|
||||
import { useSendMessageButtonType, useEnableAutoClearMessageInputBox } from "@logics_configs";
|
||||
import { useMessageLogScroll } from "@logics_main";
|
||||
import { store } from "@store";
|
||||
import { appWindow } from "@tauri-apps/api/window";
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
|
||||
export const MessageInputBox = () => {
|
||||
const [message_history, setMessageHistory] = useState([]);
|
||||
@@ -41,6 +41,7 @@ export const MessageInputBox = () => {
|
||||
|
||||
const onSubmitFunction = (e) => {
|
||||
e.preventDefault();
|
||||
// const appWindow = getCurrentWindow();
|
||||
// appWindow.minimize();
|
||||
|
||||
if (!currentMessageInputValue.data.trim()) return updateMessageInputValue("");
|
||||
|
||||
@@ -4,7 +4,7 @@ import { StartUpProgressContainer } from "./start_up_progress_container/StartUpP
|
||||
import { DownloadModelsContainer } from "./download_models_container/DownloadModelsContainer/";
|
||||
import MegaphoneSvg from "@images/megaphone.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";
|
||||
|
||||
export const SplashComponent = () => {
|
||||
@@ -73,12 +73,13 @@ const AnnouncementsContainer = () => {
|
||||
|
||||
|
||||
const CloseButtonContainer = () => {
|
||||
const close = () => {
|
||||
const asyncClose = async () => {
|
||||
const appWindow = await getCurrentWindow();
|
||||
appWindow.close();
|
||||
};
|
||||
|
||||
return (
|
||||
<button className={styles.close_button_wrapper} onClick={close}>
|
||||
<button className={styles.close_button_wrapper} onClick={asyncClose}>
|
||||
<div className={styles.close_button}>
|
||||
<XMarkSvg className={styles.x_mark_svg}/>
|
||||
</div>
|
||||
|
||||
@@ -5,15 +5,17 @@ import SquareSvg from "@images/square.svg?react";
|
||||
import LineSvg from "@images/line.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 = () => {
|
||||
|
||||
const minimize = () => {
|
||||
const asyncMinimize = async () => {
|
||||
const appWindow = await getCurrentWindow();
|
||||
appWindow.minimize();
|
||||
};
|
||||
|
||||
const maximize = async () => {
|
||||
const asyncMaximize = async () => {
|
||||
const appWindow = await getCurrentWindow();
|
||||
const maximizeState = await appWindow.isMaximized();
|
||||
if (!maximizeState) {
|
||||
appWindow.maximize();
|
||||
@@ -22,7 +24,8 @@ export const WindowTitleBar = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
const asyncClose = async () => {
|
||||
const appWindow = await getCurrentWindow();
|
||||
appWindow.close();
|
||||
};
|
||||
|
||||
@@ -34,13 +37,13 @@ export const WindowTitleBar = () => {
|
||||
</div>
|
||||
|
||||
<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}/>
|
||||
</div>
|
||||
<div className={styles.maximize_button} onClick={maximize}>
|
||||
<div className={styles.maximize_button} onClick={asyncMaximize}>
|
||||
<SquareSvg className={styles.square_svg}/>
|
||||
</div>
|
||||
<div className={styles.close_button} onClick={close}>
|
||||
<div className={styles.close_button} onClick={asyncClose}>
|
||||
<XMarkSvg className={styles.x_mark_svg}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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/<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 });
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,42 @@ export default defineConfig(async () => {
|
||||
|
||||
|
||||
|
||||
// 各プラグインのエイリアスを動的に読み込む関数
|
||||
|
||||
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)) {
|
||||
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("/"));
|
||||
|
||||
// ホスト側の絶対パスに変換
|
||||
aliases[alias_key] = path.resolve(__dirname, "src-ui/plugins", entry_path, alias_relative_path);
|
||||
for (const plugin of dev_plugins) {
|
||||
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 {
|
||||
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;
|
||||
};
|
||||
Reference in New Issue
Block a user