[Refactor] Reorganize config hooks into config_page_setter folder.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import { store, useStore_SettingBoxScrollPosition } from "@store";
|
||||
|
||||
export const useSettingBoxScrollPosition = () => {
|
||||
const { currentSettingBoxScrollPosition, updateSettingBoxScrollPosition, pendingSettingBoxScrollPosition } = useStore_SettingBoxScrollPosition();
|
||||
|
||||
const saveScrollPosition = () => {
|
||||
if (store.setting_box_scroll_container.current) {
|
||||
updateSettingBoxScrollPosition(store.setting_box_scroll_container.current.scrollTop);
|
||||
}
|
||||
};
|
||||
const restoreScrollPosition = () => {
|
||||
if (store.setting_box_scroll_container.current) {
|
||||
updateSettingBoxScrollPosition((pre) => {
|
||||
store.setting_box_scroll_container.current.scrollTop = pre.data;
|
||||
return pre.data;
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
const resetScrollPosition = () => {
|
||||
if (store.setting_box_scroll_container.current) {
|
||||
store.setting_box_scroll_container.current.scrollTop = 0;
|
||||
updateSettingBoxScrollPosition(0);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
saveScrollPosition,
|
||||
restoreScrollPosition,
|
||||
resetScrollPosition,
|
||||
currentSettingBoxScrollPosition,
|
||||
updateSettingBoxScrollPosition,
|
||||
};
|
||||
};
|
||||
138
src-ui/logics/configs/config_page_setter/hotkeys/useHotkeys.js
Normal file
138
src-ui/logics/configs/config_page_setter/hotkeys/useHotkeys.js
Normal file
@@ -0,0 +1,138 @@
|
||||
import { store, useStore_Hotkeys } from "@store";
|
||||
import { useStdoutToPython } from "@useStdoutToPython";
|
||||
import { useNotificationStatus } from "@logics_common";
|
||||
import { useMainFunction } from "@logics_main";
|
||||
import { register, unregisterAll, isRegistered } from "@tauri-apps/plugin-global-shortcut";
|
||||
|
||||
export const useHotkeys = () => {
|
||||
const appWindow = store.appWindow;
|
||||
|
||||
const { asyncStdoutToPython } = useStdoutToPython();
|
||||
const { currentHotkeys, updateHotkeys, pendingHotkeys } = useStore_Hotkeys();
|
||||
const {
|
||||
toggleTranslation,
|
||||
toggleTranscriptionSend,
|
||||
toggleTranscriptionReceive,
|
||||
} = useMainFunction();
|
||||
|
||||
|
||||
const getHotkeys = () => {
|
||||
pendingHotkeys();
|
||||
asyncStdoutToPython("/get/data/hotkeys");
|
||||
};
|
||||
const { showNotification_SaveSuccess, showNotification_Error, closeNotification } = useNotificationStatus();
|
||||
|
||||
const setHotkeys = (hotkeys) => {
|
||||
pendingHotkeys();
|
||||
|
||||
const updatedHotkeys = { ...currentHotkeys.data, ...hotkeys };
|
||||
const usedShortcuts = new Set();
|
||||
const conflictingKeys = [];
|
||||
|
||||
for (const [actionKey, hotkey] of Object.entries(updatedHotkeys)) {
|
||||
if (!hotkey) continue;
|
||||
|
||||
const shortcut = parseHotkey(hotkey);
|
||||
if (usedShortcuts.has(shortcut)) {
|
||||
showNotification_Error(`The hotkey ${shortcut} is already in use.`);
|
||||
updatedHotkeys[actionKey] = null;
|
||||
conflictingKeys.push(actionKey);
|
||||
} else {
|
||||
usedShortcuts.add(shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
updateHotkeys(updatedHotkeys);
|
||||
|
||||
if (conflictingKeys.length === 0) {
|
||||
asyncStdoutToPython("/set/data/hotkeys", updatedHotkeys);
|
||||
closeNotification();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const registerShortcuts = async () => {
|
||||
try {
|
||||
await unregisterAll();
|
||||
|
||||
const hotkeyEntries = Object.entries(currentHotkeys.data);
|
||||
|
||||
for (const [actionKey, hotkeyRaw] of hotkeyEntries) {
|
||||
if (!hotkeyRaw) continue;
|
||||
|
||||
const shortcut = parseHotkey(hotkeyRaw);
|
||||
const isAlreadyRegistered = await isRegistered(shortcut);
|
||||
|
||||
if (!isAlreadyRegistered) {
|
||||
await register(shortcut, async (event) => {
|
||||
if (event.state !== "Pressed") return;
|
||||
switch (actionKey) {
|
||||
case "toggle_vrct_visibility": {
|
||||
const minimized = await appWindow.isMinimized();
|
||||
if (minimized) {
|
||||
await appWindow.unminimize();
|
||||
await appWindow.setFocus();
|
||||
store.text_area_ref.current?.focus();
|
||||
} else {
|
||||
await appWindow.minimize();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "toggle_translation": {
|
||||
toggleTranslation();
|
||||
break;
|
||||
}
|
||||
case "toggle_transcription_send": {
|
||||
toggleTranscriptionSend();
|
||||
break;
|
||||
}
|
||||
case "toggle_transcription_receive": {
|
||||
toggleTranscriptionReceive();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.warn(`No handler defined for action: ${actionKey}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to register global shortcuts:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const setSuccessHotkeys = (hotkeys) => {
|
||||
updateHotkeys(hotkeys);
|
||||
showNotification_SaveSuccess();
|
||||
};
|
||||
|
||||
return {
|
||||
currentHotkeys,
|
||||
getHotkeys,
|
||||
updateHotkeys,
|
||||
setHotkeys,
|
||||
setSuccessHotkeys,
|
||||
registerShortcuts,
|
||||
unregisterAll,
|
||||
};
|
||||
};
|
||||
|
||||
// 修飾キーのパースを行う関数
|
||||
const parseHotkey = (hotkeyString) => {
|
||||
const keyMap = {
|
||||
Ctrl: "Control",
|
||||
Alt: "Alt",
|
||||
Shift: "Shift",
|
||||
Meta: "Super",
|
||||
};
|
||||
|
||||
|
||||
return hotkeyString
|
||||
.map((key) => keyMap[key] || key)
|
||||
.join("+");
|
||||
};
|
||||
468
src-ui/logics/configs/config_page_setter/plugins/usePlugins.js
Normal file
468
src-ui/logics/configs/config_page_setter/plugins/usePlugins.js
Normal file
@@ -0,0 +1,468 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { useI18n } from "@useI18n";
|
||||
import { IS_PLUGIN_PATH_DEV_MODE, getPluginsList } from "@ui_configs";
|
||||
import {
|
||||
store,
|
||||
|
||||
createAtomWithHook,
|
||||
useStore_SavedPluginsStatus,
|
||||
useStore_PluginsData,
|
||||
|
||||
useStore_FetchedPluginsInfo,
|
||||
useStore_LoadedPlugins,
|
||||
} from "@store";
|
||||
import { useStdoutToPython } from "@useStdoutToPython";
|
||||
|
||||
import { transform } from "@babel/standalone";
|
||||
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}) => {
|
||||
imported_dev_plugins.push({
|
||||
index: await import(`@plugins_path/${entry_path}/index.jsx`),
|
||||
downloaded_plugin_info: await import(`@plugins_path/${entry_path}/plugin_info.json`),
|
||||
});
|
||||
})
|
||||
|
||||
import JSZip from "jszip";
|
||||
|
||||
import { useFetch, useSoftwareVersion, useNotificationStatus } from "@logics_common";
|
||||
|
||||
import * as logics_configs from "@logics_configs";
|
||||
import * as logics_main from "@logics_main";
|
||||
import * as logics_common from "@logics_common";
|
||||
|
||||
// PLUGIN_LIST_URL は中央リポジトリにある、各プラグインの plugin_info.json への URL の配列を保持する JSON の URL
|
||||
const PLUGIN_LIST_URL = getPluginsList();
|
||||
|
||||
export const usePlugins = () => {
|
||||
const { t, i18n } = useI18n();
|
||||
const { showNotification_SaveSuccess, showNotification_Success, showNotification_Error } = useNotificationStatus();
|
||||
const { asyncStdoutToPython } = useStdoutToPython();
|
||||
|
||||
const { currentFetchedPluginsInfo, updateFetchedPluginsInfo, pendingFetchedPluginsInfo, errorFetchedPluginsInfo } = useStore_FetchedPluginsInfo();
|
||||
const { currentLoadedPlugins, updateLoadedPlugins, pendingLoadedPlugins } = useStore_LoadedPlugins();
|
||||
|
||||
const { currentSavedPluginsStatus, updateSavedPluginsStatus, pendingSavedPluginsStatus } = useStore_SavedPluginsStatus();
|
||||
const { currentPluginsData, updatePluginsData, pendingPluginsData } = useStore_PluginsData();
|
||||
const { checkVrctVerCompatibility } = useSoftwareVersion();
|
||||
|
||||
const { asyncTauriFetchGithub } = useFetch();
|
||||
|
||||
|
||||
const generatePluginContext = (downloaded_plugin_info) => {
|
||||
const plugin_context = {
|
||||
registerComponent: (component) => {
|
||||
if (!downloaded_plugin_info.plugin_id || !downloaded_plugin_info.location || !component) {
|
||||
return console.error("An invalid plugin was detected.", downloaded_plugin_info.plugin_id, downloaded_plugin_info.location, component);
|
||||
}
|
||||
|
||||
updateLoadedPlugins(prev => {
|
||||
const prev_map = new Map(prev.data.map(item => [item.plugin_id, item]));
|
||||
prev_map.set(downloaded_plugin_info.plugin_id, {
|
||||
...downloaded_plugin_info,
|
||||
component: component,
|
||||
});
|
||||
return Array.from(prev_map.values());
|
||||
});
|
||||
|
||||
},
|
||||
createAtomWithHook: (...args) => createAtomWithHook(...args),
|
||||
logics: { ...logics_common, ...logics_configs, ...logics_main },
|
||||
i18n: i18n,
|
||||
};
|
||||
return plugin_context;
|
||||
}
|
||||
|
||||
const asyncLoadPlugin = async (plugin_folder_relative_path) => {
|
||||
const init_path = "plugins/" + plugin_folder_relative_path + "/index.esm.js";
|
||||
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, { baseDir: BaseDirectory.Resource, recursive: true });
|
||||
const downloaded_plugin_info = JSON.parse(downloaded_plugin_info_json);
|
||||
|
||||
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: [
|
||||
["env", { modules: false }],
|
||||
"react",
|
||||
],
|
||||
sourceType: "module"
|
||||
}).code;
|
||||
const blob = new Blob([transpiled_code], { type: "text/javascript" });
|
||||
const blob_url = URL.createObjectURL(blob);
|
||||
const plugin_module = await import(/* @vite-ignore */ blob_url);
|
||||
URL.revokeObjectURL(blob_url);
|
||||
|
||||
if (plugin_module && plugin_module.init) {
|
||||
plugin_module.init(generatePluginContext(downloaded_plugin_info));
|
||||
}
|
||||
await loadPluginCSS(plugin_css_path);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Failed to load plugin from", plugin_folder_relative_path, error);
|
||||
}
|
||||
};
|
||||
|
||||
const asyncLoadAllPlugins = async () => {
|
||||
if (IS_PLUGIN_PATH_DEV_MODE) {
|
||||
imported_dev_plugins.forEach(({ index, downloaded_plugin_info }) => {
|
||||
if (!index || !downloaded_plugin_info) {
|
||||
console.error("Invalid development plugin detected", index, downloaded_plugin_info);
|
||||
return;
|
||||
}
|
||||
const plugin_context = generatePluginContext(downloaded_plugin_info);
|
||||
if (index.init) {
|
||||
index.init(plugin_context);
|
||||
} else {
|
||||
console.error("Plugin missing init function", downloaded_plugin_info);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const is_plugins_dir_exists = await exists("plugins", { baseDir: BaseDirectory.Resource });
|
||||
if (!is_plugins_dir_exists) return;
|
||||
|
||||
try {
|
||||
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;
|
||||
await asyncLoadPlugin(target_path);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading plugins:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
const downloadAndExtractPlugin = async (plugin) => {
|
||||
const { latest_plugin_info } = plugin;
|
||||
try {
|
||||
// 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);
|
||||
|
||||
// 2. JSZip で ZIP を解凍
|
||||
const zip = await JSZip.loadAsync(bytes);
|
||||
|
||||
// 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 mkdir(targetPath, { baseDir: BaseDirectory.Resource, recursive: true });
|
||||
|
||||
// 4. ZIP 内のエントリをひとつずつ展開 & 書き出し
|
||||
const filePromises = [];
|
||||
zip.forEach((relativePath, entry) => {
|
||||
// .git 以下はスキップ
|
||||
if (relativePath.startsWith('.git') || relativePath.includes('/.git/')) {
|
||||
return;
|
||||
}
|
||||
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 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);
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchLatestPluginZipUrl = async (plugin) => {
|
||||
const api_url = plugin.url;
|
||||
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`);
|
||||
}
|
||||
return asset.browser_download_url;
|
||||
};
|
||||
|
||||
|
||||
const asyncFetchPluginsInfo = async () => {
|
||||
if (store.is_fetched_plugins_info_already) return;
|
||||
store.is_fetched_plugins_info_already = true;
|
||||
|
||||
try {
|
||||
const plugins_data = await asyncTauriFetchGithub(PLUGIN_LIST_URL);
|
||||
const updated_list = await Promise.all(
|
||||
plugins_data.map(async (plugin_data) => {
|
||||
try {
|
||||
const plugin_info = await asyncFetchPluginInfo(plugin_data.url);
|
||||
return {
|
||||
...plugin_info,
|
||||
homepage_link: plugin_data.homepage_link,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching plugin info for URL: ", plugin_data.url, error);
|
||||
return {
|
||||
title: plugin_data.title,
|
||||
plugin_id: plugin_data.plugin_id || plugin_data.title,
|
||||
is_error: true,
|
||||
error_message: error.message,
|
||||
url: plugin_data.url,
|
||||
homepage_link: plugin_data.homepage_link,
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
updateFetchedPluginsInfo(updated_list);
|
||||
} catch (error) {
|
||||
console.error("Error fetching plugin info list: ", error);
|
||||
errorFetchedPluginsInfo();
|
||||
}
|
||||
|
||||
store.is_initialized_fetched_plugin_info = true;
|
||||
}
|
||||
|
||||
const asyncFetchPluginInfo = async (plugin_info_asset_url) => {
|
||||
|
||||
const release_response = await asyncTauriFetchGithub(plugin_info_asset_url);
|
||||
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 = 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);
|
||||
|
||||
return {
|
||||
...plugin_info,
|
||||
is_plugin_supported: is_plugin_supported,
|
||||
is_plugin_supported_latest_vrct: is_plugin_supported_latest_vrct,
|
||||
url: plugin_info_asset_url,
|
||||
};
|
||||
}
|
||||
|
||||
const handlePendingPlugin = (target_plugin_id, is_pending) => {
|
||||
updatePluginsData((old_value) => {
|
||||
const new_value = old_value.data.map((d) => {
|
||||
if (d.plugin_id === target_plugin_id) {
|
||||
d.is_pending = is_pending;
|
||||
}
|
||||
return d;
|
||||
});
|
||||
return new_value;
|
||||
});
|
||||
};
|
||||
|
||||
const setSavedPluginEnabled = (target_plugin_id, is_enabled) => {
|
||||
const notify = () => {
|
||||
const msg_key = is_enabled
|
||||
? "plugin_notifications.is_enabled"
|
||||
: "plugin_notifications.is_disabled";
|
||||
showNotification_Success(t(msg_key), {
|
||||
hide_duration: 1000,
|
||||
category_id: "switch_enable_plugin",
|
||||
});
|
||||
}
|
||||
|
||||
const exists = currentSavedPluginsStatus.data.some(
|
||||
(d) => d.plugin_id === target_plugin_id
|
||||
);
|
||||
|
||||
let new_value = [];
|
||||
|
||||
if (exists) {
|
||||
new_value = currentSavedPluginsStatus.data.map((d) => {
|
||||
if (d.plugin_id === target_plugin_id) {
|
||||
d.is_enabled = is_enabled;
|
||||
notify();
|
||||
}
|
||||
return d;
|
||||
});
|
||||
} else {
|
||||
// 存在しない場合は追加
|
||||
new_value = [
|
||||
...currentSavedPluginsStatus.data,
|
||||
{ plugin_id: target_plugin_id, is_enabled: is_enabled }
|
||||
];
|
||||
notify();
|
||||
}
|
||||
|
||||
// ダウンロード済みプラグインのみ残す
|
||||
new_value = new_value.filter((item) =>
|
||||
currentPluginsData.data.some(
|
||||
(p) => p.plugin_id === item.plugin_id && p.is_downloaded
|
||||
)
|
||||
);
|
||||
|
||||
setSavedPluginsStatus(new_value);
|
||||
};
|
||||
|
||||
const toggleSavedPluginsStatus = (plugin_id) => {
|
||||
// 現在の状態を探す(未登録なら false とみなす)
|
||||
const current = currentSavedPluginsStatus.data.find(
|
||||
(d) => d.plugin_id === plugin_id
|
||||
)?.is_enabled ?? false;
|
||||
setSavedPluginEnabled(plugin_id, !current);
|
||||
};
|
||||
|
||||
// Init時の処理 非対応のものを無効化する際に、savedDPluginsStatusから不要なものを削除する処理が邪魔になるので該当コードを削除したバージョン。Init以外で使用する時にはリファクタが必要になる。
|
||||
const setTargetSavedPluginsStatus_Init = (target_plugin_id, is_enabled) => {
|
||||
const is_exists = currentSavedPluginsStatus.data.some(
|
||||
(d) => d.plugin_id === target_plugin_id
|
||||
);
|
||||
let new_value = [];
|
||||
if (is_exists) {
|
||||
new_value = currentSavedPluginsStatus.data.map((d) => {
|
||||
if (d.plugin_id === target_plugin_id) {
|
||||
d.is_enabled = is_enabled;
|
||||
}
|
||||
return d;
|
||||
});
|
||||
} else {
|
||||
new_value.push(...currentSavedPluginsStatus.data);
|
||||
new_value.push({
|
||||
plugin_id: target_plugin_id,
|
||||
is_enabled: is_enabled,
|
||||
});
|
||||
}
|
||||
|
||||
setSavedPluginsStatus(new_value);
|
||||
};
|
||||
|
||||
|
||||
const setSavedPluginsStatus = (plugins_status) => {
|
||||
pendingSavedPluginsStatus();
|
||||
asyncStdoutToPython("/set/data/plugins_status", plugins_status);
|
||||
};
|
||||
|
||||
// init時、currentPluginsDataからのデータではデータ更新が間に合わないので、currentSavedPluginsStatusから直接取得
|
||||
const isAnyPluginEnabled_Init = () => {
|
||||
return currentSavedPluginsStatus.data.some(plugin => plugin.is_enabled);
|
||||
};
|
||||
|
||||
const isAnyPluginEnabled = () => {
|
||||
return currentPluginsData.data.some(plugin => plugin.is_enabled);
|
||||
};
|
||||
|
||||
const enabledPluginsList = () => {
|
||||
return currentPluginsData.data.filter(plugin => plugin.is_enabled);
|
||||
}
|
||||
|
||||
const updateTargetPluginData = (target_plugin_id, attribute, value) => {
|
||||
updatePluginsData(prev => {
|
||||
prev.data.forEach(plugin => {
|
||||
if (plugin.plugin_id === target_plugin_id) {
|
||||
plugin[attribute] = value;
|
||||
}
|
||||
});
|
||||
return prev.data;
|
||||
});
|
||||
}
|
||||
|
||||
const setSuccessSavedPluginsStatus = (plugins_status) => {
|
||||
updateSavedPluginsStatus(plugins_status);
|
||||
showNotification_SaveSuccess();
|
||||
};
|
||||
|
||||
const setErrorPlugin = (plugin_id, error_message_type) => {
|
||||
const error_message = t("plugin_notifications.disabled_due_to_an_error");
|
||||
|
||||
setSavedPluginEnabled(plugin_id, false);
|
||||
updateTargetPluginData(plugin_id, "is_error", true);
|
||||
updateTargetPluginData(plugin_id, "error_message_type", error_message_type);
|
||||
showNotification_Error(error_message);
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
asyncFetchPluginsInfo,
|
||||
|
||||
isAnyPluginEnabled_Init,
|
||||
isAnyPluginEnabled,
|
||||
enabledPluginsList,
|
||||
|
||||
asyncLoadAllPlugins,
|
||||
downloadAndExtractPlugin,
|
||||
|
||||
currentSavedPluginsStatus,
|
||||
updateSavedPluginsStatus,
|
||||
setSuccessSavedPluginsStatus,
|
||||
|
||||
currentPluginsData,
|
||||
updatePluginsData,
|
||||
|
||||
updateTargetPluginData,
|
||||
|
||||
currentFetchedPluginsInfo,
|
||||
updateFetchedPluginsInfo,
|
||||
|
||||
currentLoadedPlugins,
|
||||
updateLoadedPlugins,
|
||||
|
||||
setSavedPluginEnabled,
|
||||
toggleSavedPluginsStatus,
|
||||
setTargetSavedPluginsStatus_Init,
|
||||
setSavedPluginsStatus,
|
||||
|
||||
|
||||
handlePendingPlugin,
|
||||
|
||||
setErrorPlugin,
|
||||
};
|
||||
};
|
||||
|
||||
const removeImportStatements = (code) => {
|
||||
return code
|
||||
.split("\n")
|
||||
.filter(line => !line.match(/^import\s+.*['"]react['"]/))
|
||||
.join("\n");
|
||||
};
|
||||
|
||||
// import { readTextFile, BaseDirectory } from "@tauri-apps/api/fs";
|
||||
|
||||
const loadPluginCSS = async (plugin_css_path) => {
|
||||
if (!await exists(plugin_css_path, { baseDir: BaseDirectory.Resource, recursive: true })) return;
|
||||
try {
|
||||
// プラグインフォルダのルートにある main.css を読み込む
|
||||
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);
|
||||
} catch (error) {
|
||||
console.error("Failed to load plugin CSS from", plugin_css_path, error);
|
||||
}
|
||||
};
|
||||
|
||||
export { loadPluginCSS };
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useStore_SupportersData } from "@store";
|
||||
import { supporters_data_url } from "@ui_configs";
|
||||
export const useSupporters = () => {
|
||||
const { currentSupportersData, updateSupportersData, pendingSupportersData, errorSupportersData } = useStore_SupportersData();
|
||||
|
||||
const asyncFetchSupportersData = async () => {
|
||||
if (currentSupportersData.state === "pending") return;
|
||||
pendingSupportersData();
|
||||
try {
|
||||
const res = await fetch(supporters_data_url);
|
||||
// const res = await fetch(supporters_data_url, { cache: "no-store" });
|
||||
if (!res.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
const data = await res.json();
|
||||
updateSupportersData(data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching supporters' data:", error);
|
||||
errorSupportersData();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
asyncFetchSupportersData,
|
||||
currentSupportersData,
|
||||
updateSupportersData,
|
||||
pendingSupportersData,
|
||||
};
|
||||
};
|
||||
@@ -619,8 +619,16 @@ export const useTranslation = createCategoryHook("Translation");
|
||||
export const useTranscription = createCategoryHook("Transcription");
|
||||
export const useVr = createCategoryHook("Vr");
|
||||
export const useOthers = createCategoryHook("Others");
|
||||
// export const useHotkeys = createCategoryHook("Hotkeys");
|
||||
export const useAdvancedSettings = createCategoryHook("AdvancedSettings");
|
||||
|
||||
// Exceptional exports that are not part of SETTINGS_ARRAY or have custom logic.
|
||||
export { useHotkeys } from "./hotkeys/useHotkeys.js";
|
||||
export { useSupporters } from "./supporters/useSupporters.js";
|
||||
export { usePlugins } from "./plugins/usePlugins.js";
|
||||
|
||||
export { useSettingBoxScrollPosition } from "./_aux/useSettingBoxScrollPosition.js";
|
||||
|
||||
|
||||
// If you later add other categories, you can either manually add:
|
||||
// export const useDevice = createCategoryHook("Device");
|
||||
// or uncomment the code below to auto-attach to module.exports (less ideal for tree-shaking).
|
||||
|
||||
Reference in New Issue
Block a user