From e1125ae241660a6ec6e516f09f885f41c8f0a8de Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 19 Nov 2025 08:31:07 +0900 Subject: [PATCH 01/12] [Update] UI: Add new translation engines and adjust UI styles for better layout. --- src-ui/logics/ui_configs.js | 11 ++++++++--- .../TranslatorSelector.module.scss | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src-ui/logics/ui_configs.js b/src-ui/logics/ui_configs.js index 191693c8..8d1d4487 100644 --- a/src-ui/logics/ui_configs.js +++ b/src-ui/logics/ui_configs.js @@ -101,12 +101,17 @@ export const getPluginsList = () => { if (IS_PLUGIN_PATH_DEV_MODE || IS_PLUGIN_LIST_URL_DEV_MODE) console.warn("ui_configs IS_PLUGIN_PATH_DEV_MODE or IS_PLUGIN_LIST_URL_DEV_MODE is true. Turn to 'false' when it's production environment."); export const translator_status = [ - { id: "DeepL", label: "DeepL", is_available: false }, - { id: "DeepL_API", label: `DeepL API`, is_available: false }, + { id: "CTranslate2", label: `AI\nCTranslate2`, is_available: false, is_default: true }, { id: "Google", label: "Google", is_available: false }, { id: "Bing", label: "Bing", is_available: false }, { id: "Papago", label: "Papago", is_available: false }, - { id: "CTranslate2", label: `AI\nCTranslate2`, is_available: false, is_default: true }, + { id: "DeepL", label: "DeepL", is_available: false }, + { id: "DeepL_API", label: `DeepL API`, is_available: false }, + { id: "Plamo_API", label: `Plamo API`, is_available: false }, + { id: "Gemini_API", label: `Gemini API`, is_available: false }, + { id: "OpenAI_API", label: `OpenAI API`, is_available: false }, + { id: "LMStudio", label: `LMStudio`, is_available: false }, + { id: "Ollama", label: `Ollama`, is_available: false }, ]; export const ctranslate2_weight_type_status = [ diff --git a/src-ui/views/app/main_page/sidebar_section/language_settings/translator_selector_open_button/translator_selector/TranslatorSelector.module.scss b/src-ui/views/app/main_page/sidebar_section/language_settings/translator_selector_open_button/translator_selector/TranslatorSelector.module.scss index b73fef4c..50bc5d8d 100644 --- a/src-ui/views/app/main_page/sidebar_section/language_settings/translator_selector_open_button/translator_selector/TranslatorSelector.module.scss +++ b/src-ui/views/app/main_page/sidebar_section/language_settings/translator_selector_open_button/translator_selector/TranslatorSelector.module.scss @@ -8,6 +8,7 @@ display: flex; justify-content: center; align-items: center; + overflow-y: auto; } .relative_container { @@ -17,8 +18,7 @@ } .wrapper { - width: 100%; - height: 100%; + padding: 2rem 0; display: flex; flex-direction: column; justify-content: center; From ef06cd1c7a30b0208aa53f3fcb93a5d52f5acae2 Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Wed, 19 Nov 2025 17:19:39 +0900 Subject: [PATCH 02/12] [Update] UI: Implement LLM connection handling and add connection check UI components.(Test UI) --- src-ui/logics/_useBackendErrorHandling.js | 18 +++++++ src-ui/logics/common/index.js | 3 +- src-ui/logics/common/useLLMConnection.js | 47 +++++++++++++++++++ src-ui/logics/store.js | 2 + src-ui/logics/useReceiveRoutes.js | 3 ++ .../ConnectionCheckButton.jsx | 19 ++++++++ .../ConnectionCheckButton.module.scss | 15 ++++++ .../setting_box/_components/index.js | 3 +- .../setting_box/_templates/Templates.jsx | 5 ++ .../setting_box/translation/Translation.jsx | 39 +++++++++++++++ 10 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 src-ui/logics/common/useLLMConnection.js create mode 100644 src-ui/views/app/config_page/setting_section/setting_box/_components/connection_check_button/ConnectionCheckButton.jsx create mode 100644 src-ui/views/app/config_page/setting_section/setting_box/_components/connection_check_button/ConnectionCheckButton.module.scss diff --git a/src-ui/logics/_useBackendErrorHandling.js b/src-ui/logics/_useBackendErrorHandling.js index f224686c..eb8bcae0 100644 --- a/src-ui/logics/_useBackendErrorHandling.js +++ b/src-ui/logics/_useBackendErrorHandling.js @@ -2,6 +2,7 @@ import { useI18n } from "@useI18n"; import { useNotificationStatus, + useLLMConnection, } from "@logics_common"; import { @@ -46,6 +47,11 @@ export const _useBackendErrorHandling = () => { updateWebsocketPort, } = useAdvancedSettings(); + const { + updateIsOllamaConnected, + updateIsLMStudioConnected, + } = useLLMConnection(); + const errorHandling_Backend = ({message, data, endpoint, result}) => { switch (endpoint) { case "/run/error_device": @@ -221,6 +227,18 @@ export const _useBackendErrorHandling = () => { } return; + case "/run/lmstudio_connection": + updateIsLMStudioConnected(data); + showNotification_Error(message); + console.error(message); + return; + + case "/run/ollama_connection": + updateIsOllamaConnected(data); + showNotification_Error(message); + console.error(message); + return; + default: console.error(`Invalid endpoint or message: ${endpoint}\nmessage: ${message}\nresult: ${JSON.stringify(result)}`); return; diff --git a/src-ui/logics/common/index.js b/src-ui/logics/common/index.js index 7cac177c..5975f52f 100644 --- a/src-ui/logics/common/index.js +++ b/src-ui/logics/common/index.js @@ -14,4 +14,5 @@ export { useHandleNetworkConnection } from "./useHandleNetworkConnection"; export { useHandleOscQuery } from "./useHandleOscQuery"; export { useIsOscAvailable } from "./useIsOscAvailable"; export { useIsVrctAvailable } from "./useIsVrctAvailable"; -export { useFetch } from "./useFetch"; \ No newline at end of file +export { useFetch } from "./useFetch"; +export { useLLMConnection } from "./useLLMConnection"; \ No newline at end of file diff --git a/src-ui/logics/common/useLLMConnection.js b/src-ui/logics/common/useLLMConnection.js new file mode 100644 index 00000000..70bd350a --- /dev/null +++ b/src-ui/logics/common/useLLMConnection.js @@ -0,0 +1,47 @@ +import { useStdoutToPython } from "@useStdoutToPython"; +import { + useStore_IsLMStudioConnected, + useStore_IsOllamaConnected, +} from "@store"; + +export const useLLMConnection = () => { + const { asyncStdoutToPython } = useStdoutToPython(); + const { + currentIsLMStudioConnected, + updateIsLMStudioConnected, + pendingIsLMStudioConnected, + } = useStore_IsLMStudioConnected(); + const { + currentIsOllamaConnected, + updateIsOllamaConnected, + pendingIsOllamaConnected, + } = useStore_IsOllamaConnected(); + + const checkConnection_LMStudio = () => { + pendingIsLMStudioConnected(); + asyncStdoutToPython("/run/lmstudio_connection"); + }; + const setConnectionStatus_LMStudio = (is_connected) => { + updateIsLMStudioConnected(is_connected); + }; + + const checkConnection_Ollama = () => { + pendingIsOllamaConnected(); + asyncStdoutToPython("/run/ollama_connection"); + }; + const setConnectionStatus_Ollama = (is_connected) => { + updateIsOllamaConnected(is_connected); + }; + + return { + currentIsLMStudioConnected, + updateIsLMStudioConnected, + setConnectionStatus_LMStudio, + checkConnection_LMStudio, + + currentIsOllamaConnected, + updateIsOllamaConnected, + setConnectionStatus_Ollama, + checkConnection_Ollama, + }; +}; \ No newline at end of file diff --git a/src-ui/logics/store.js b/src-ui/logics/store.js index 8f332a6e..831d63da 100644 --- a/src-ui/logics/store.js +++ b/src-ui/logics/store.js @@ -167,6 +167,8 @@ export const { atomInstance: Atom_NotificationStatus, useHook: useStore_Notifica key: 0, message: "", }, "NotificationStatus"); +export const { atomInstance: Atom_IsLMStudioConnected, useHook: useStore_IsLMStudioConnected } = createAtomWithHook(false, "IsLMStudioConnected", {is_state_ok: true}); +export const { atomInstance: Atom_IsOllamaConnected, useHook: useStore_IsOllamaConnected } = createAtomWithHook(false, "IsOllamaConnected", {is_state_ok: true}); // Main Page // Common diff --git a/src-ui/logics/useReceiveRoutes.js b/src-ui/logics/useReceiveRoutes.js index e9936936..c3368c96 100644 --- a/src-ui/logics/useReceiveRoutes.js +++ b/src-ui/logics/useReceiveRoutes.js @@ -20,6 +20,9 @@ export const STATIC_ROUTE_META_LIST = [ { endpoint: "/run/open_filepath_logs", ns: common, hook_name: "useOpenFolder", method_name: "openedFolder_MessageLogs" }, { endpoint: "/run/open_filepath_config_file", ns: common, hook_name: "useOpenFolder", method_name: "openedFolder_ConfigFile" }, + { endpoint: "/run/lmstudio_connection", ns: common, hook_name: "useLLMConnection", method_name: "setConnectionStatus_LMStudio" }, + { endpoint: "/run/ollama_connection", ns: common, hook_name: "useLLMConnection", method_name: "setConnectionStatus_Ollama" }, + // Software Version { endpoint: "/get/data/version", ns: common, hook_name: "useSoftwareVersion", method_name: "updateSoftwareVersion" }, // Latest Software Version Info diff --git a/src-ui/views/app/config_page/setting_section/setting_box/_components/connection_check_button/ConnectionCheckButton.jsx b/src-ui/views/app/config_page/setting_section/setting_box/_components/connection_check_button/ConnectionCheckButton.jsx new file mode 100644 index 00000000..075a0e10 --- /dev/null +++ b/src-ui/views/app/config_page/setting_section/setting_box/_components/connection_check_button/ConnectionCheckButton.jsx @@ -0,0 +1,19 @@ +import styles from "./ConnectionCheckButton.module.scss"; + +export const ConnectionCheckButton = (props) => { + const label = props.state === "pending" + ? "Checking... 🌀" + : props.variable === true + ? "Connected ✅" + : "Disconnected ❌"; + + return ( +
+

{label}

+

{`UI Status: ${props.state}`}

+ +
+ ); +}; \ No newline at end of file diff --git a/src-ui/views/app/config_page/setting_section/setting_box/_components/connection_check_button/ConnectionCheckButton.module.scss b/src-ui/views/app/config_page/setting_section/setting_box/_components/connection_check_button/ConnectionCheckButton.module.scss new file mode 100644 index 00000000..116feab4 --- /dev/null +++ b/src-ui/views/app/config_page/setting_section/setting_box/_components/connection_check_button/ConnectionCheckButton.module.scss @@ -0,0 +1,15 @@ +.button_wrapper { + padding: 1.6rem; + border-radius: 0.4rem; + &:hover { + background-color: var(--dark_825_color); + } + &:active { + background-color: var(--dark_900_color); + } +} + +.button_svg { + width: 2.4rem; + color: var(--dark_400_color); +} \ No newline at end of file diff --git a/src-ui/views/app/config_page/setting_section/setting_box/_components/index.js b/src-ui/views/app/config_page/setting_section/setting_box/_components/index.js index 2e7b54ca..d28f063d 100644 --- a/src-ui/views/app/config_page/setting_section/setting_box/_components/index.js +++ b/src-ui/views/app/config_page/setting_section/setting_box/_components/index.js @@ -13,4 +13,5 @@ export { SwitchBox } from "./switch_box/SwitchBox"; export { ThresholdComponent } from "./threshold_component/ThresholdComponent"; export { WordFilter, WordFilterListToggleComponent } from "./word_filter/WordFilter"; export { DownloadModels } from "./download_models/DownloadModels"; -export { MessageFormat } from "./message_format/MessageFormat"; \ No newline at end of file +export { MessageFormat } from "./message_format/MessageFormat"; +export { ConnectionCheckButton } from "./connection_check_button/ConnectionCheckButton"; \ No newline at end of file diff --git a/src-ui/views/app/config_page/setting_section/setting_box/_templates/Templates.jsx b/src-ui/views/app/config_page/setting_section/setting_box/_templates/Templates.jsx index 0c0845e1..b4952c99 100644 --- a/src-ui/views/app/config_page/setting_section/setting_box/_templates/Templates.jsx +++ b/src-ui/views/app/config_page/setting_section/setting_box/_templates/Templates.jsx @@ -19,6 +19,7 @@ import { WordFilterListToggleComponent, DownloadModels, MessageFormat, + ConnectionCheckButton, } from "../_components"; import { Checkbox } from "@common_components"; @@ -181,6 +182,10 @@ export const DownloadModelsContainer = (props) => ( ); +export const ConnectionCheckButtonContainer = (props) => ( + +); + export const MessageFormatContainer = (props) => { return ( <> diff --git a/src-ui/views/app/config_page/setting_section/setting_box/translation/Translation.jsx b/src-ui/views/app/config_page/setting_section/setting_box/translation/Translation.jsx index 7ad243ff..5a496206 100644 --- a/src-ui/views/app/config_page/setting_section/setting_box/translation/Translation.jsx +++ b/src-ui/views/app/config_page/setting_section/setting_box/translation/Translation.jsx @@ -17,6 +17,7 @@ import { EntryWithSaveButtonContainer, RadioButtonContainer, DropdownMenuContainer, + ConnectionCheckButtonContainer, useOnMouseLeaveDropdownMenu, } from "../_templates/Templates"; @@ -25,9 +26,11 @@ import { DropdownMenu, MultiDropdownMenu, LabelComponent, + ConnectionCheckButton, } from "../_components"; import { deepl_auth_key_url } from "@ui_configs"; +import { useLLMConnection } from "@logics_common"; export const Translation = () => { return ( @@ -46,9 +49,11 @@ export const Translation = () => { + + ); @@ -422,6 +427,23 @@ const OpenAIModelContainer = () => { +const LMStudioConnectionCheck_Box = () => { + const { t } = useI18n(); + const { currentIsLMStudioConnected, checkConnection_LMStudio } = useLLMConnection(); + + return ( + <> + + + ); +}; const LMStudioURL_Box = () => { const { t } = useI18n(); const { currentLMStudioURL, setLMStudioURL, deleteLMStudioURL } = useTranslation(); @@ -475,6 +497,23 @@ const LMStudioModelContainer = () => { ); }; +const OllamaConnectionCheck_Box = () => { + const { t } = useI18n(); + const { currentIsOllamaConnected, checkConnection_Ollama } = useLLMConnection(); + + return ( + <> + + + ); +}; const OllamaModelContainer = () => { const { t } = useI18n(); const { From 27b3006ffd88cc5e095742220df2f296bfdf6ff8 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Thu, 20 Nov 2025 01:38:50 +0900 Subject: [PATCH 03/12] [Update] Controller: Add error handling for empty translation model lists and improve logging for translation engine availability checks. --- src-python/controller.py | 48 +++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src-python/controller.py b/src-python/controller.py index 06df12bf..db326cb2 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -1909,10 +1909,12 @@ class Controller: config.SELECTABLE_TRANSLATION_ENGINE_STATUS[translator_name] = True config.SELECTABLE_LMSTUDIO_MODEL_LIST = model.getTranslatorLMStudioModelList() self.run(200, self.run_mapping["selectable_lmstudio_model_list"], config.SELECTABLE_LMSTUDIO_MODEL_LIST) + if len(config.SELECTABLE_LMSTUDIO_MODEL_LIST) == 0: + raise Exception("No LMStudio models available") if config.SELECTED_LMSTUDIO_MODEL not in config.SELECTABLE_LMSTUDIO_MODEL_LIST: config.SELECTED_LMSTUDIO_MODEL = config.SELECTABLE_LMSTUDIO_MODEL_LIST[0] - model.setTranslatorLMStudioModel(model=config.SELECTED_LMSTUDIO_MODEL) - self.run(200, self.run_mapping["selected_lmstudio_model"], config.SELECTED_LMSTUDIO_MODEL) + model.setTranslatorLMStudioModel(model=config.SELECTED_LMSTUDIO_MODEL) + self.run(200, self.run_mapping["selected_lmstudio_model"], config.SELECTED_LMSTUDIO_MODEL) model.updateTranslatorLMStudioClient() self.updateTranslationEngineAndEngineList() response = {"status":200, "result":True} @@ -1949,10 +1951,12 @@ class Controller: config.SELECTABLE_TRANSLATION_ENGINE_STATUS[translator_name] = True config.SELECTABLE_LMSTUDIO_MODEL_LIST = model.getTranslatorLMStudioModelList() self.run(200, self.run_mapping["selectable_lmstudio_model_list"], config.SELECTABLE_LMSTUDIO_MODEL_LIST) + if len(config.SELECTABLE_LMSTUDIO_MODEL_LIST) == 0: + raise Exception("No LMStudio models available") if config.SELECTED_LMSTUDIO_MODEL not in config.SELECTABLE_LMSTUDIO_MODEL_LIST: config.SELECTED_LMSTUDIO_MODEL = config.SELECTABLE_LMSTUDIO_MODEL_LIST[0] - model.setTranslatorLMStudioModel(model=config.SELECTED_LMSTUDIO_MODEL) - self.run(200, self.run_mapping["selected_lmstudio_model"], config.SELECTED_LMSTUDIO_MODEL) + model.setTranslatorLMStudioModel(model=config.SELECTED_LMSTUDIO_MODEL) + self.run(200, self.run_mapping["selected_lmstudio_model"], config.SELECTED_LMSTUDIO_MODEL) model.updateTranslatorLMStudioClient() self.updateTranslationEngineAndEngineList() response = {"status":200, "result":config.LMSTUDIO_URL} @@ -2020,10 +2024,12 @@ class Controller: config.SELECTABLE_TRANSLATION_ENGINE_STATUS[translator_name] = True config.SELECTABLE_OLLAMA_MODEL_LIST = model.getTranslatorOllamaModelList() self.run(200, self.run_mapping["selectable_ollama_model_list"], config.SELECTABLE_OLLAMA_MODEL_LIST) + if len(config.SELECTABLE_OLLAMA_MODEL_LIST) == 0: + raise Exception("No Ollama models available") if config.SELECTED_OLLAMA_MODEL not in config.SELECTABLE_OLLAMA_MODEL_LIST: config.SELECTED_OLLAMA_MODEL = config.SELECTABLE_OLLAMA_MODEL_LIST[0] - model.setTranslatorOllamaModel(model=config.SELECTED_OLLAMA_MODEL) - self.run(200, self.run_mapping["selected_ollama_model"], config.SELECTED_OLLAMA_MODEL) + model.setTranslatorOllamaModel(model=config.SELECTED_OLLAMA_MODEL) + self.run(200, self.run_mapping["selected_ollama_model"], config.SELECTED_OLLAMA_MODEL) model.updateTranslatorOllamaClient() self.updateTranslationEngineAndEngineList() response = {"status":200, "result":True} @@ -2957,13 +2963,13 @@ class Controller: config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = False if config.AUTH_KEYS[engine] is not None: if model.authenticationTranslatorPlamoAuthKey(auth_key=config.AUTH_KEYS[engine]) is True: - config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True - printLog("Plamo API Key is valid") config.SELECTABLE_PLAMO_MODEL_LIST = model.getTranslatorPlamoModelList() if config.SELECTED_PLAMO_MODEL not in config.SELECTABLE_PLAMO_MODEL_LIST: config.SELECTED_PLAMO_MODEL = config.SELECTABLE_PLAMO_MODEL_LIST[0] model.setTranslatorPlamoModel(config.SELECTED_PLAMO_MODEL) model.updateTranslatorPlamoClient() + config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True + printLog("Plamo API Key is valid") else: # error update Auth key auth_keys = config.AUTH_KEYS @@ -2975,13 +2981,13 @@ class Controller: config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = False if config.AUTH_KEYS[engine] is not None: if model.authenticationTranslatorGeminiAuthKey(auth_key=config.AUTH_KEYS[engine]) is True: - config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True - printLog("Gemini API Key is valid") config.SELECTABLE_GEMINI_MODEL_LIST = model.getTranslatorGeminiModelList() if config.SELECTED_GEMINI_MODEL not in config.SELECTABLE_GEMINI_MODEL_LIST: config.SELECTED_GEMINI_MODEL = config.SELECTABLE_GEMINI_MODEL_LIST[0] model.setTranslatorGeminiModel(config.SELECTED_GEMINI_MODEL) model.updateTranslatorGeminiClient() + config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True + printLog("Gemini API Key is valid") else: # error update Auth key auth_keys = config.AUTH_KEYS @@ -2993,13 +2999,13 @@ class Controller: config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = False if config.AUTH_KEYS[engine] is not None: if model.authenticationTranslatorOpenAIAuthKey(auth_key=config.AUTH_KEYS[engine]) is True: - config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True - printLog("OpenAI API Key is valid") config.SELECTABLE_OPENAI_MODEL_LIST = model.getTranslatorOpenAIModelList() if config.SELECTED_OPENAI_MODEL not in config.SELECTABLE_OPENAI_MODEL_LIST: config.SELECTED_OPENAI_MODEL = config.SELECTABLE_OPENAI_MODEL_LIST[0] model.setTranslatorOpenAIModel(config.SELECTED_OPENAI_MODEL) model.updateTranslatorOpenAIClient() + config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True + printLog("OpenAI API Key is valid") else: # error update Auth key auth_keys = config.AUTH_KEYS @@ -3007,30 +3013,36 @@ class Controller: config.AUTH_KEYS = auth_keys printLog("OpenAI API Key is invalid") case "LMStudio": - printLog("Start check LMStudio API Key") + printLog("Start check LMStudio Server") config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = False if config.LMSTUDIO_URL is not None: if model.authenticationTranslatorLMStudio(base_url=config.LMSTUDIO_URL) is True: - config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True - printLog("LMStudio URL is valid") config.SELECTABLE_LMSTUDIO_MODEL_LIST = model.getTranslatorLMStudioModelList() + if len(config.SELECTABLE_LMSTUDIO_MODEL_LIST) == 0: + printLog("LMStudio model list is empty") + break if config.SELECTED_LMSTUDIO_MODEL not in config.SELECTABLE_LMSTUDIO_MODEL_LIST: config.SELECTED_LMSTUDIO_MODEL = config.SELECTABLE_LMSTUDIO_MODEL_LIST[0] model.setTranslatorLMStudioModel(config.SELECTED_LMSTUDIO_MODEL) model.updateTranslatorLMStudioClient() + config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True + printLog("LMStudio is available") else: printLog("LMStudio is not available") case "Ollama": - printLog("Start check Ollama API Key") + printLog("Start check Ollama Server") config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = False if model.authenticationTranslatorOllama() is True: - config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True - printLog("Ollama is available") config.SELECTABLE_OLLAMA_MODEL_LIST = model.getTranslatorOllamaModelList() + if len(config.SELECTABLE_OLLAMA_MODEL_LIST) == 0: + printLog("Ollama model list is empty") + break if config.SELECTED_OLLAMA_MODEL not in config.SELECTABLE_OLLAMA_MODEL_LIST: config.SELECTED_OLLAMA_MODEL = config.SELECTABLE_OLLAMA_MODEL_LIST[0] model.setTranslatorOllamaModel(config.SELECTED_OLLAMA_MODEL) model.updateTranslatorOllamaClient() + config.SELECTABLE_TRANSLATION_ENGINE_STATUS[engine] = True + printLog("Ollama is available") else: printLog("Ollama is not available") case _: From add96c1fda61e74ab82dbaa9749d9da9ee0822e0 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Sun, 23 Nov 2025 00:07:32 +0900 Subject: [PATCH 04/12] [Update] Config: Enhance configuration persistence by filtering serializable properties and updating loading logic for managed properties. --- src-python/config.py | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src-python/config.py b/src-python/config.py index 3a3e7c3b..c841b2ae 100644 --- a/src-python/config.py +++ b/src-python/config.py @@ -558,8 +558,16 @@ class Config: return cls._instance def saveConfigToFile(self) -> None: + # 永続化対象を descriptor 情報 (json_serializable_vars) から再構成 + filtered = {} + for var_name, var_func in json_serializable_vars.items(): + try: + filtered[var_name] = var_func(self) + except Exception: + pass + self._config_data = filtered with open(self.PATH_CONFIG, "w", encoding="utf-8") as fp: - json_dump(self._config_data, fp, indent=4, ensure_ascii=False) + json_dump(filtered, fp, indent=4, ensure_ascii=False) def saveConfig(self, key: str, value: Any, immediate_save: bool = False) -> None: self._config_data[key] = value @@ -601,12 +609,12 @@ class Config: # Read Write # --- Simple boolean flags (managed by descriptor) --- - ENABLE_TRANSLATION = ManagedProperty('ENABLE_TRANSLATION', type_=bool) - ENABLE_TRANSCRIPTION_SEND = ManagedProperty('ENABLE_TRANSCRIPTION_SEND', type_=bool) - ENABLE_TRANSCRIPTION_RECEIVE = ManagedProperty('ENABLE_TRANSCRIPTION_RECEIVE', type_=bool) - ENABLE_FOREGROUND = ManagedProperty('ENABLE_FOREGROUND', type_=bool) - ENABLE_CHECK_ENERGY_SEND = ManagedProperty('ENABLE_CHECK_ENERGY_SEND', type_=bool) - ENABLE_CHECK_ENERGY_RECEIVE = ManagedProperty('ENABLE_CHECK_ENERGY_RECEIVE', type_=bool) + ENABLE_TRANSLATION = ManagedProperty('ENABLE_TRANSLATION', type_=bool, serialize=False) + ENABLE_TRANSCRIPTION_SEND = ManagedProperty('ENABLE_TRANSCRIPTION_SEND', type_=bool, serialize=False) + ENABLE_TRANSCRIPTION_RECEIVE = ManagedProperty('ENABLE_TRANSCRIPTION_RECEIVE', type_=bool, serialize=False) + ENABLE_FOREGROUND = ManagedProperty('ENABLE_FOREGROUND', type_=bool, serialize=False) + ENABLE_CHECK_ENERGY_SEND = ManagedProperty('ENABLE_CHECK_ENERGY_SEND', type_=bool, serialize=False) + ENABLE_CHECK_ENERGY_RECEIVE = ManagedProperty('ENABLE_CHECK_ENERGY_RECEIVE', type_=bool, serialize=False) # --- Selectable dict/list properties (managed by descriptor, not serialized) --- # These are dynamically generated in init_config() based on installed packages/APIs @@ -1006,15 +1014,24 @@ class Config: self._config_data = json_load(fp) for key, value in self._config_data.items(): + # 読み込み時: serialize=True かつ readonlyでない Descriptor のみ反映。 + # 未知キー(Descriptorなし)は無視して注入を防止。 try: - setattr(self, key, value) + descriptor = getattr(type(self), key, None) + if isinstance(descriptor, ManagedProperty): + if descriptor.readonly or not descriptor.serialize: + continue + setattr(self, key, value) + elif isinstance(descriptor, ValidatedProperty): + if not descriptor.serialize: + continue + setattr(self, key, value) + else: + # 不明キーは破棄(古い/不要/改竄の可能性) + continue except Exception: errorLogging() - - with open(self.PATH_CONFIG, 'w', encoding="utf-8") as fp: - for var_name, var_func in json_serializable_vars.items(): - self._config_data[var_name] = var_func(self) - json_dump(self._config_data, fp, indent=4, ensure_ascii=False) + self.saveConfigToFile() # Auto-register all descriptors after Config class definition _auto_register_descriptors() From 5ca809c8dd92cdd86a7e43e84ef26c0555c87053 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Sun, 23 Nov 2025 00:59:09 +0900 Subject: [PATCH 05/12] [Update] Config: Add model revalidation logic to ensure selected models are valid based on available options. --- src-python/config.py | 43 +++++++++++++++++++++++++++++++++++----- src-python/controller.py | 3 +++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src-python/config.py b/src-python/config.py index c841b2ae..55a46122 100644 --- a/src-python/config.py +++ b/src-python/config.py @@ -527,6 +527,19 @@ def _compute_device_validator(val, inst): return copy.deepcopy(val) return None +def _allowed_in_populated(list_attr_name: str): + def _inner(value, inst): + try: + lst = getattr(inst, list_attr_name) + except Exception: + return True # インスタンス状態取得失敗時も弾かない + if not lst: # 空/未初期化 + return True + if value is None: + return True + return value in lst + return _inner + class Config: """Application configuration singleton. @@ -719,11 +732,11 @@ class Config: USE_EXCLUDE_WORDS = ManagedProperty('USE_EXCLUDE_WORDS', type_=bool) CTRANSLATE2_WEIGHT_TYPE = ManagedProperty('CTRANSLATE2_WEIGHT_TYPE', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_LIST) WHISPER_WEIGHT_TYPE = ManagedProperty('WHISPER_WEIGHT_TYPE', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_WHISPER_WEIGHT_TYPE_LIST) - SELECTED_PLAMO_MODEL = ManagedProperty('SELECTED_PLAMO_MODEL', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_PLAMO_MODEL_LIST) - SELECTED_GEMINI_MODEL = ManagedProperty('SELECTED_GEMINI_MODEL', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_GEMINI_MODEL_LIST) - SELECTED_OPENAI_MODEL = ManagedProperty('SELECTED_OPENAI_MODEL', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_OPENAI_MODEL_LIST) - SELECTED_LMSTUDIO_MODEL = ManagedProperty('SELECTED_LMSTUDIO_MODEL', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_LMSTUDIO_MODEL_LIST) - SELECTED_OLLAMA_MODEL = ManagedProperty('SELECTED_OLLAMA_MODEL', type_=str, allowed=lambda v, inst: v in inst.SELECTABLE_OLLAMA_MODEL_LIST) + SELECTED_PLAMO_MODEL = ManagedProperty('SELECTED_PLAMO_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_PLAMO_MODEL_LIST')) + SELECTED_GEMINI_MODEL = ManagedProperty('SELECTED_GEMINI_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_GEMINI_MODEL_LIST')) + SELECTED_OPENAI_MODEL = ManagedProperty('SELECTED_OPENAI_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_OPENAI_MODEL_LIST')) + SELECTED_LMSTUDIO_MODEL = ManagedProperty('SELECTED_LMSTUDIO_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_LMSTUDIO_MODEL_LIST')) + SELECTED_OLLAMA_MODEL = ManagedProperty('SELECTED_OLLAMA_MODEL', type_=str, allowed=_allowed_in_populated('SELECTABLE_OLLAMA_MODEL_LIST')) # --- Translation and language settings --- MIC_WORD_FILTER = ValidatedProperty('MIC_WORD_FILTER', _mic_word_filter_validator) @@ -1033,6 +1046,26 @@ class Config: errorLogging() self.saveConfigToFile() + def revalidate_selected_models(self): + pairs = [ + ('SELECTED_PLAMO_MODEL', 'SELECTABLE_PLAMO_MODEL_LIST'), + ('SELECTED_GEMINI_MODEL', 'SELECTABLE_GEMINI_MODEL_LIST'), + ('SELECTED_OPENAI_MODEL', 'SELECTABLE_OPENAI_MODEL_LIST'), + ('SELECTED_LMSTUDIO_MODEL', 'SELECTABLE_LMSTUDIO_MODEL_LIST'), + ('SELECTED_OLLAMA_MODEL', 'SELECTABLE_OLLAMA_MODEL_LIST'), + ] + for sel_attr, list_attr in pairs: + try: + current = getattr(self, sel_attr) + lst = getattr(self, list_attr) + if lst and current is not None and current not in lst: + if len(lst) > 0: + setattr(self, sel_attr, lst[0]) + else: + setattr(self, sel_attr, None) + except Exception: + errorLogging() + # Auto-register all descriptors after Config class definition _auto_register_descriptors() diff --git a/src-python/controller.py b/src-python/controller.py index db326cb2..2f3aa530 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -3138,6 +3138,9 @@ class Controller: model.stopWebSocketServer() printLog("WebSocket server host or port is not available") + printLog("Revalidate Selected Models") + config.revalidate_selected_models() + printLog("Update settings") self.updateConfigSettings() From 9a35577ec64a8ea707d429270a2beeac727b415e Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Mon, 24 Nov 2025 18:01:30 +0900 Subject: [PATCH 06/12] [Update] Controller: Add methods for LMStudio and Ollama connection status checks --- src-python/controller.py | 10 ++++++ src-python/mainloop.py | 2 ++ src-python/model.py | 8 +++++ .../translation/translation_lmstudio.py | 32 ++++++++++--------- .../models/translation/translation_ollama.py | 7 ++-- .../translation/translation_translator.py | 20 ++++++++++++ 6 files changed, 61 insertions(+), 18 deletions(-) diff --git a/src-python/controller.py b/src-python/controller.py index 2f3aa530..714b6a85 100644 --- a/src-python/controller.py +++ b/src-python/controller.py @@ -1900,6 +1900,9 @@ class Controller: } return response + def getTranslatorLMStudioConnection(self, *args, **kwargs) -> dict: + return {"status":200, "result":model.getTranslatorLMStudioConnected()} + def checkTranslatorLMStudioConnection(self, *args, **kwargs) -> dict: printLog("Check Translator LMStudio Connection") translator_name = "LMStudio" @@ -1937,6 +1940,10 @@ class Controller: } return response + def getConnectedLMStudio(self, *args, **kwargs) -> dict: + is_connected = model.getTranslatorLMStudioConnectedStatus() + return {"status":200, "result": is_connected} + def getTranslatorLMStudioURL(self, *args, **kwargs) -> dict: return {"status":200, "result":config.LMSTUDIO_URL} @@ -2015,6 +2022,9 @@ class Controller: } return response + def getTranslatorOllamaConnection(self, *args, **kwargs) -> dict: + return {"status":200, "result":model.getTranslatorOllamaConnected()} + def checkTranslatorOllamaConnection(self, *args, **kwargs) -> dict: printLog("Check Translator Ollama Connection") translator_name = "Ollama" diff --git a/src-python/mainloop.py b/src-python/mainloop.py index 58a760a5..20b4bf2f 100644 --- a/src-python/mainloop.py +++ b/src-python/mainloop.py @@ -207,6 +207,7 @@ mapping = { "/set/data/openai_auth_key": {"status": True, "variable":controller.setOpenAIAuthKey}, "/delete/data/openai_auth_key": {"status": True, "variable":controller.delOpenAIAuthKey}, + "/get/data/connected_lmstudio": {"status": True, "variable":controller.getTranslatorLMStudioConnection}, "/run/lmstudio_connection": {"status": True, "variable":controller.checkTranslatorLMStudioConnection}, "/get/data/selectable_lmstudio_model_list": {"status": True, "variable":controller.getTranslatorLStudioModelList}, "/get/data/selected_lmstudio_model": {"status": True, "variable":controller.getTranslatorLMStudioModel}, @@ -214,6 +215,7 @@ mapping = { "/get/data/lmstudio_url": {"status": True, "variable":controller.getTranslatorLMStudioURL}, "/set/data/lmstudio_url": {"status": True, "variable":controller.setTranslatorLMStudioURL}, + "/get/data/connected_ollama": {"status": True, "variable":controller.getTranslatorOllamaConnection}, "/run/ollama_connection": {"status": True, "variable":controller.checkTranslatorOllamaConnection}, "/get/data/selectable_ollama_model_list": {"status": True, "variable":controller.getTranslatorOllamaModelList}, "/get/data/selected_ollama_model": {"status": True, "variable":controller.getTranslatorOllamaModel}, diff --git a/src-python/model.py b/src-python/model.py index 58f947dc..0283d7d7 100644 --- a/src-python/model.py +++ b/src-python/model.py @@ -249,6 +249,10 @@ class Model: self.ensure_initialized() self.translator.updateOpenAIClient() + def getTranslatorLMStudioConnected(self) -> bool: + self.ensure_initialized() + return self.translator.getLMStudioConnected() + def authenticationTranslatorLMStudio(self, base_url: str) -> bool: result = self.translator.setLMStudioClientURL(base_url=base_url, root_path=config.PATH_LOCAL) return result @@ -265,6 +269,10 @@ class Model: self.ensure_initialized() self.translator.updateLMStudioClient() + def getTranslatorOllamaConnected(self) -> bool: + self.ensure_initialized() + return self.translator.getOllamaConnected() + def authenticationTranslatorOllama(self) -> bool: result = self.translator.checkOllamaClient(root_path=config.PATH_LOCAL) return result diff --git a/src-python/models/translation/translation_lmstudio.py b/src-python/models/translation/translation_lmstudio.py index 7751dc16..aa8509e8 100644 --- a/src-python/models/translation/translation_lmstudio.py +++ b/src-python/models/translation/translation_lmstudio.py @@ -1,6 +1,6 @@ -from openai import OpenAI from langchain_openai import ChatOpenAI from pydantic import SecretStr +import requests try: from .translation_languages import translation_lang @@ -8,33 +8,35 @@ try: except Exception: import sys from os import path as os_path - sys.path.append(os_path.dirname(os_path.dirname(os_path.dirname(os_path.abspath(__file__))))) - from translation_languages import translation_lang + sys.path.append(os_path.dirname(os_path.abspath(__file__))) + from translation_languages import translation_lang, loadTranslationLanguages from translation_utils import loadPromptConfig + loadTranslationLanguages(path=".", force=True) -def _authentication_check(api_key: str, base_url: str | None = None) -> bool: +def _authentication_check(base_url: str | None = None) -> bool: """Check if the provided API key is valid by attempting to list models. """ try: - client = OpenAI(api_key=api_key, base_url=base_url) - client.models.list() - return True + response = requests.get(f"{base_url}/models", timeout=0.2) + if response.status_code == 200: + return True + else: + return False except Exception: return False -def _get_available_text_models(api_key: str, base_url: str | None = None) -> list[str]: +def _get_available_text_models(base_url: str | None = None) -> list[str]: """Extract the list of available text models from the LM Studio. """ try: - client = OpenAI(api_key=api_key, base_url=base_url) - res = client.models.list() - models = res.data + response = requests.get(f"{base_url}/models", timeout=0.2) + models = response.json()["data"] except Exception: models = [] allowed_models = [] for model in models: - allowed_models.append(model.id) + allowed_models.append(model["id"]) allowed_models.sort() return allowed_models @@ -58,13 +60,13 @@ class LMStudioClient: return self.base_url def setBaseURL(self, base_url: str | None) -> None: - result = _authentication_check(api_key=self.api_key, base_url=base_url) + result = _authentication_check(base_url=base_url) if result: self.base_url = base_url return result def getModelList(self) -> list[str]: - return _get_available_text_models(api_key=self.api_key, base_url=self.base_url) if self.base_url else [] + return _get_available_text_models(base_url=self.base_url) if self.base_url else [] def getModel(self) -> str: return self.model @@ -108,7 +110,7 @@ class LMStudioClient: return content.strip() if __name__ == "__main__": - client = LMStudioClient(base_url="http://192.168.68.110:1234/v1") + client = LMStudioClient(base_url="http://127.0.0.1:1234/v1") models = client.getModelList() if models: print("Available models:", models) diff --git a/src-python/models/translation/translation_ollama.py b/src-python/models/translation/translation_ollama.py index 4152c953..ae4cc860 100644 --- a/src-python/models/translation/translation_ollama.py +++ b/src-python/models/translation/translation_ollama.py @@ -7,15 +7,16 @@ try: except Exception: import sys from os import path as os_path - sys.path.append(os_path.dirname(os_path.dirname(os_path.dirname(os_path.abspath(__file__))))) - from translation_languages import translation_lang + sys.path.append(os_path.dirname(os_path.abspath(__file__))) + from translation_languages import translation_lang, loadTranslationLanguages from translation_utils import loadPromptConfig + loadTranslationLanguages(path=".", force=True) def _authentication_check(base_url: str | None = None) -> bool: """Check authentication for Ollama API. """ try: - response = requests.get(f"{base_url}") + response = requests.get(f"{base_url}", timeout=0.2) if response.status_code == 200: return True else: diff --git a/src-python/models/translation/translation_translator.py b/src-python/models/translation/translation_translator.py index 249a09f1..4023bcc4 100644 --- a/src-python/models/translation/translation_translator.py +++ b/src-python/models/translation/translation_translator.py @@ -176,6 +176,16 @@ class Translator: """Update the OpenAI client (fetch available models).""" self.openai_client.updateClient() + def getLMStudioConnected(self) -> bool: + """Get LM Studio connection status. + + Returns True if connected, False otherwise. + """ + if self.lmstudio_client is None: + return False + else: + return True + def setLMStudioClientURL(self, base_url: str | None = None, root_path: str = None) -> bool: """Authenticate LM Studio with the provided base URL. @@ -207,6 +217,16 @@ class Translator: """Update the LM Studio client (fetch available models).""" self.lmstudio_client.updateClient() + def getOllamaConnected(self) -> bool: + """Get Ollama connection status. + + Returns True if connected, False otherwise. + """ + if self.ollama_client is None: + return False + else: + return True + def checkOllamaClient(self, root_path: str = None) -> bool: """Check if Ollama client is available. From 54ec5ba45d2db85cbb15f7adcca3f8fb9c919156 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Tue, 25 Nov 2025 00:22:23 +0900 Subject: [PATCH 07/12] =?UTF-8?q?[Update]=20=E3=83=90=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E7=AE=A1=E7=90=86:=20package.json,=20tauri.c?= =?UTF-8?q?onf.json,=20config.py=E3=81=AE=E3=83=90=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=82=92=E6=9B=B4=E6=96=B0=E3=81=97=E3=80=81?= =?UTF-8?q?update=5Fversion.py=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 19 ++++++++++--------- src-python/config.py | 2 +- src-tauri/tauri.conf.json | 33 +++++++++++++++++++-------------- update_version.py | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 24 deletions(-) create mode 100644 update_version.py diff --git a/package.json b/package.json index 184d5c9a..1c66c4f5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vrct", "private": true, - "version": "0.0.0", + "version": "3.3.2", "type": "module", "scripts": { "setup-python": "install.bat", @@ -14,14 +14,15 @@ "tauri-dev": "tauri dev", "task-kill": "python task_kill.py", "clean": "python clean.py", - "dev": "npm run task-kill && npm run build-python && npm run dev-ui", - "dev-cuda": "npm run task-kill && npm run build-python-cuda && npm run dev-ui", - "dev-ui": "npm-run-all --parallel vite tauri-dev", - "build": "npm run clean && npm run build-python && npm run vite-build && npm run tauri build", - "build-cuda": "npm run clean && npm run build-python-cuda && npm run vite-build && npm run tauri build", - "release": "npm run build && python zip.py --zip_name VRCT.zip", - "release-cuda": "npm run build-cuda && python zip.py --zip_name VRCT_cuda.zip", - "release-all": "npm run release && npm run release-cuda" + "update-version": "python update_version.py", + "dev": "npm run task-kill && npm run clean && npm run update-version && npm run build-python && npm run dev-ui", + "dev-cuda": "npm run task-kill && npm run clean && npm run update-version && npm run build-python-cuda && npm run dev-ui", + "dev-ui": "npm run task-kill && npm-run-all --parallel vite tauri-dev", + "build": "npm run task-kill && npm run clean && npm run update-version && npm run build-python && npm run vite-build && npm run tauri build", + "build-cuda": "npm run task-kill && npm run clean && npm run update-version && npm run build-python-cuda && npm run vite-build && npm run tauri build", + "release": "npm run update-version && npm run build && python zip.py --zip_name VRCT.zip", + "release-cuda": "npm run update-version && npm run build-cuda && python zip.py --zip_name VRCT_cuda.zip", + "release-all": "npm run update-version && npm run release && npm run release-cuda" }, "dependencies": { "@babel/standalone": "7.27.0", diff --git a/src-python/config.py b/src-python/config.py index 3a3e7c3b..cc53ab16 100644 --- a/src-python/config.py +++ b/src-python/config.py @@ -736,7 +736,7 @@ class Config: def init_config(self): # Read Only - self._VERSION = "3.3.1" + self._VERSION = "0.0.0" if getattr(sys, 'frozen', False): self._PATH_LOCAL = os_path.dirname(sys.executable) else: diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 10b04d98..0bbba778 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "VRCT", - "version": "3.3.1", + "version": "0.0.0", "identifier": "com.vrct.app", "build": { "beforeDevCommand": "", @@ -11,20 +11,25 @@ }, "app": { "enableGTKAppId": false, - "windows": [{ - "title": "VRCT", - "center": true, - "width": 450, - "height": 220, - "minWidth": 400, - "minHeight": 200, - "transparent": true, - "decorations": false, - "shadow": false - }], + "windows": [ + { + "title": "VRCT", + "center": true, + "width": 450, + "height": 220, + "minWidth": 400, + "minHeight": 200, + "transparent": true, + "decorations": false, + "shadow": false + } + ], "security": { "csp": null, - "capabilities": ["default", "vrct-capability"] + "capabilities": [ + "default", + "vrct-capability" + ] } }, "bundle": { @@ -56,4 +61,4 @@ } } } -} +} \ No newline at end of file diff --git a/update_version.py b/update_version.py new file mode 100644 index 00000000..38b11a1a --- /dev/null +++ b/update_version.py @@ -0,0 +1,39 @@ +import json +from pathlib import Path + +def update_versions(): + root = Path(__file__).parent + + # package.jsonからバージョンを読み取る + with open(root / "package.json", "r", encoding="utf-8") as f: + package_json = json.load(f) + version = package_json["version"] + + # tauri.conf.jsonを更新 + tauri_conf_path = root / "src-tauri" / "tauri.conf.json" + with open(tauri_conf_path, "r", encoding="utf-8") as f: + tauri_conf = json.load(f) + + tauri_conf["version"] = version + + with open(tauri_conf_path, "w", encoding="utf-8") as f: + json.dump(tauri_conf, f, indent=4, ensure_ascii=False) + + # config.pyを更新 + config_path = root / "src-python" / "config.py" + with open(config_path, "r", encoding="utf-8") as f: + content = f.read() + + # VERSION行を置換 + import re + pattern = r'(self\._VERSION = ")[^"]+(")' + replacement = rf'\g<1>{version}\g<2>' + new_content = re.sub(pattern, replacement, content) + + with open(config_path, "w", encoding="utf-8") as f: + f.write(new_content) + + print(f"✓ バージョン {version} に更新しました") + +if __name__ == "__main__": + update_versions() \ No newline at end of file From ff6ac43feebb12298df2906044817da3456ed2f1 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:20:55 +0900 Subject: [PATCH 08/12] [Update] Build scripts and configuration: Added new build and install scripts, updated versioning in config files, and improved project structure. --- bat/build.bat | 2 ++ bat/build_cuda.bat | 2 ++ install.bat => bat/install.bat | 0 build.bat | 2 -- build_cuda.bat | 2 -- clean.py | 6 ------ package.json | 16 ++++++++-------- backend_cuda.spec => spec/backend.spec | 16 ++++++++-------- backend.spec => spec/backend_cuda.spec | 16 ++++++++-------- src-python/config.py | 2 +- src-tauri/tauri.conf.json | 2 +- utils/clean.py | 8 ++++++++ task_kill.py => utils/task_kill.py | 0 update_version.py => utils/update_version.py | 10 +++++----- zip.py => utils/zip.py | 0 15 files changed, 43 insertions(+), 41 deletions(-) create mode 100644 bat/build.bat create mode 100644 bat/build_cuda.bat rename install.bat => bat/install.bat (100%) delete mode 100644 build.bat delete mode 100644 build_cuda.bat delete mode 100644 clean.py rename backend_cuda.spec => spec/backend.spec (62%) rename backend.spec => spec/backend_cuda.spec (61%) create mode 100644 utils/clean.py rename task_kill.py => utils/task_kill.py (100%) rename update_version.py => utils/update_version.py (76%) rename zip.py => utils/zip.py (100%) diff --git a/bat/build.bat b/bat/build.bat new file mode 100644 index 00000000..7760b7ee --- /dev/null +++ b/bat/build.bat @@ -0,0 +1,2 @@ +call .venv/Scripts/activate +pyinstaller spec/backend.spec --distpath src-tauri/bin --clean --noconfirm --log-level ERROR \ No newline at end of file diff --git a/bat/build_cuda.bat b/bat/build_cuda.bat new file mode 100644 index 00000000..0c86f70d --- /dev/null +++ b/bat/build_cuda.bat @@ -0,0 +1,2 @@ +call .venv_cuda/Scripts/activate +pyinstaller spec/backend_cuda.spec --distpath src-tauri/bin --clean --noconfirm --log-level ERROR \ No newline at end of file diff --git a/install.bat b/bat/install.bat similarity index 100% rename from install.bat rename to bat/install.bat diff --git a/build.bat b/build.bat deleted file mode 100644 index 3c08629c..00000000 --- a/build.bat +++ /dev/null @@ -1,2 +0,0 @@ -call .venv/Scripts/activate -pyinstaller backend.spec --distpath src-tauri/bin --clean --noconfirm --log-level ERROR \ No newline at end of file diff --git a/build_cuda.bat b/build_cuda.bat deleted file mode 100644 index 308b9174..00000000 --- a/build_cuda.bat +++ /dev/null @@ -1,2 +0,0 @@ -call .venv_cuda/Scripts/activate -pyinstaller backend_cuda.spec --distpath src-tauri/bin --clean --noconfirm --log-level ERROR \ No newline at end of file diff --git a/clean.py b/clean.py deleted file mode 100644 index 93084e62..00000000 --- a/clean.py +++ /dev/null @@ -1,6 +0,0 @@ -import shutil - -shutil.rmtree('build', ignore_errors=True) -shutil.rmtree('dist', ignore_errors=True) -shutil.rmtree('src-tauri\\bin', ignore_errors=True) -shutil.rmtree('src-tauri\\target', ignore_errors=True) \ No newline at end of file diff --git a/package.json b/package.json index 1c66c4f5..69bb278d 100644 --- a/package.json +++ b/package.json @@ -4,24 +4,24 @@ "version": "3.3.2", "type": "module", "scripts": { - "setup-python": "install.bat", - "build-python": "build.bat", - "build-python-cuda": "build_cuda.bat", + "setup-python": "bat\\install.bat", + "build-python": "bat\\build.bat", + "build-python-cuda": "bat\\build_cuda.bat", "vite": "vite", "vite-build": "vite build", "vite-preview": "vite preview", "tauri": "tauri", "tauri-dev": "tauri dev", - "task-kill": "python task_kill.py", - "clean": "python clean.py", - "update-version": "python update_version.py", + "task-kill": "python utils\\task_kill.py", + "clean": "python utils\\clean.py", + "update-version": "python utils\\update_version.py", "dev": "npm run task-kill && npm run clean && npm run update-version && npm run build-python && npm run dev-ui", "dev-cuda": "npm run task-kill && npm run clean && npm run update-version && npm run build-python-cuda && npm run dev-ui", "dev-ui": "npm run task-kill && npm-run-all --parallel vite tauri-dev", "build": "npm run task-kill && npm run clean && npm run update-version && npm run build-python && npm run vite-build && npm run tauri build", "build-cuda": "npm run task-kill && npm run clean && npm run update-version && npm run build-python-cuda && npm run vite-build && npm run tauri build", - "release": "npm run update-version && npm run build && python zip.py --zip_name VRCT.zip", - "release-cuda": "npm run update-version && npm run build-cuda && python zip.py --zip_name VRCT_cuda.zip", + "release": "npm run update-version && npm run build && python utils\\zip.py --zip_name VRCT.zip", + "release-cuda": "npm run update-version && npm run build-cuda && python utils\\zip.py --zip_name VRCT_cuda.zip", "release-all": "npm run update-version && npm run release && npm run release-cuda" }, "dependencies": { diff --git a/backend_cuda.spec b/spec/backend.spec similarity index 62% rename from backend_cuda.spec rename to spec/backend.spec index 8d80b6ec..57d72f6f 100644 --- a/backend_cuda.spec +++ b/spec/backend.spec @@ -2,17 +2,17 @@ a = Analysis( - ['src-python\\mainloop.py'], + ['..\\src-python\\mainloop.py'], pathex=[], binaries=[], datas=[ - ('./src-python/models/overlay/fonts', 'fonts/'), - ('./src-python/models/translation/prompt', 'prompt/'), - ('./src-python/models/translation/languages', 'languages/'), - ('.venv_cuda/Lib/site-packages/zeroconf', 'zeroconf/'), - ('.venv_cuda/Lib/site-packages/openvr', 'openvr/'), - ('.venv_cuda/Lib/site-packages/faster_whisper', 'faster_whisper/'), - ('.venv/Lib/site-packages/hf_xet', 'hf_xet/') + ('./../src-python/models/overlay/fonts', 'fonts/'), + ('./../src-python/models/translation/prompt', 'prompt/'), + ('./../src-python/models/translation/languages', 'languages/'), + ('./../.venv/Lib/site-packages/zeroconf', 'zeroconf/'), + ('./../.venv/Lib/site-packages/openvr', 'openvr/'), + ('./../.venv/Lib/site-packages/faster_whisper', 'faster_whisper/'), + ('./../.venv/Lib/site-packages/hf_xet', 'hf_xet/') ], hiddenimports=[], hookspath=[], diff --git a/backend.spec b/spec/backend_cuda.spec similarity index 61% rename from backend.spec rename to spec/backend_cuda.spec index 605c82fb..3d665926 100644 --- a/backend.spec +++ b/spec/backend_cuda.spec @@ -2,17 +2,17 @@ a = Analysis( - ['src-python\\mainloop.py'], + ['..\\src-python\\mainloop.py'], pathex=[], binaries=[], datas=[ - ('./src-python/models/overlay/fonts', 'fonts/'), - ('./src-python/models/translation/prompt', 'prompt/'), - ('./src-python/models/translation/languages', 'languages/'), - ('.venv/Lib/site-packages/zeroconf', 'zeroconf/'), - ('.venv/Lib/site-packages/openvr', 'openvr/'), - ('.venv/Lib/site-packages/faster_whisper', 'faster_whisper/'), - ('.venv/Lib/site-packages/hf_xet', 'hf_xet/') + ('./../src-python/models/overlay/fonts', 'fonts/'), + ('./../src-python/models/translation/prompt', 'prompt/'), + ('./../src-python/models/translation/languages', 'languages/'), + ('./../.venv_cuda/Lib/site-packages/zeroconf', 'zeroconf/'), + ('./../.venv_cuda/Lib/site-packages/openvr', 'openvr/'), + ('./../.venv_cuda/Lib/site-packages/faster_whisper', 'faster_whisper/'), + ('./../.venv/Lib/site-packages/hf_xet', 'hf_xet/') ], hiddenimports=[], hookspath=[], diff --git a/src-python/config.py b/src-python/config.py index cc53ab16..1b47d961 100644 --- a/src-python/config.py +++ b/src-python/config.py @@ -736,7 +736,7 @@ class Config: def init_config(self): # Read Only - self._VERSION = "0.0.0" + self._VERSION = "3.3.2" if getattr(sys, 'frozen', False): self._PATH_LOCAL = os_path.dirname(sys.executable) else: diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 0bbba778..d7695cf9 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "VRCT", - "version": "0.0.0", + "version": "3.3.2", "identifier": "com.vrct.app", "build": { "beforeDevCommand": "", diff --git a/utils/clean.py b/utils/clean.py new file mode 100644 index 00000000..c97ffd0c --- /dev/null +++ b/utils/clean.py @@ -0,0 +1,8 @@ +import os +import shutil + +root = os.path.dirname(os.path.dirname(__file__)) +shutil.rmtree(os.path.join(root, 'build'), ignore_errors=True) +shutil.rmtree(os.path.join(root, 'dist'), ignore_errors=True) +shutil.rmtree(os.path.join(root, 'src-tauri', 'bin'), ignore_errors=True) +shutil.rmtree(os.path.join(root, 'src-tauri', 'target'), ignore_errors=True) \ No newline at end of file diff --git a/task_kill.py b/utils/task_kill.py similarity index 100% rename from task_kill.py rename to utils/task_kill.py diff --git a/update_version.py b/utils/update_version.py similarity index 76% rename from update_version.py rename to utils/update_version.py index 38b11a1a..fac75c76 100644 --- a/update_version.py +++ b/utils/update_version.py @@ -1,16 +1,16 @@ +import os import json -from pathlib import Path def update_versions(): - root = Path(__file__).parent + root = os.path.join(os.path.dirname(os.path.dirname(__file__))) # package.jsonからバージョンを読み取る - with open(root / "package.json", "r", encoding="utf-8") as f: + with open(os.path.join(root, "package.json"), "r", encoding="utf-8") as f: package_json = json.load(f) version = package_json["version"] # tauri.conf.jsonを更新 - tauri_conf_path = root / "src-tauri" / "tauri.conf.json" + tauri_conf_path = os.path.join(root, "src-tauri", "tauri.conf.json") with open(tauri_conf_path, "r", encoding="utf-8") as f: tauri_conf = json.load(f) @@ -20,7 +20,7 @@ def update_versions(): json.dump(tauri_conf, f, indent=4, ensure_ascii=False) # config.pyを更新 - config_path = root / "src-python" / "config.py" + config_path = os.path.join(root, "src-python", "config.py") with open(config_path, "r", encoding="utf-8") as f: content = f.read() diff --git a/zip.py b/utils/zip.py similarity index 100% rename from zip.py rename to utils/zip.py From 7f7413e050b5c2bd320dbc5e4f4befb28d628778 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:22:22 +0900 Subject: [PATCH 09/12] [Update] utils: Change version update message to English --- utils/update_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/update_version.py b/utils/update_version.py index fac75c76..6aad694f 100644 --- a/utils/update_version.py +++ b/utils/update_version.py @@ -33,7 +33,7 @@ def update_versions(): with open(config_path, "w", encoding="utf-8") as f: f.write(new_content) - print(f"✓ バージョン {version} に更新しました") + print(f"updated to version {version}") if __name__ == "__main__": update_versions() \ No newline at end of file From fca4be59510954eb92c6e18b33894655ea13e9db Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Tue, 25 Nov 2025 13:41:02 +0900 Subject: [PATCH 10/12] [Update] UI: Add 'get' endpoints for connection status retrieval. --- src-ui/logics/store.js | 4 ++-- src-ui/logics/useReceiveRoutes.js | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src-ui/logics/store.js b/src-ui/logics/store.js index 831d63da..abca4809 100644 --- a/src-ui/logics/store.js +++ b/src-ui/logics/store.js @@ -167,8 +167,8 @@ export const { atomInstance: Atom_NotificationStatus, useHook: useStore_Notifica key: 0, message: "", }, "NotificationStatus"); -export const { atomInstance: Atom_IsLMStudioConnected, useHook: useStore_IsLMStudioConnected } = createAtomWithHook(false, "IsLMStudioConnected", {is_state_ok: true}); -export const { atomInstance: Atom_IsOllamaConnected, useHook: useStore_IsOllamaConnected } = createAtomWithHook(false, "IsOllamaConnected", {is_state_ok: true}); +export const { atomInstance: Atom_IsLMStudioConnected, useHook: useStore_IsLMStudioConnected } = createAtomWithHook(false, "IsLMStudioConnected"); +export const { atomInstance: Atom_IsOllamaConnected, useHook: useStore_IsOllamaConnected } = createAtomWithHook(false, "IsOllamaConnected"); // Main Page // Common diff --git a/src-ui/logics/useReceiveRoutes.js b/src-ui/logics/useReceiveRoutes.js index c3368c96..164cce7e 100644 --- a/src-ui/logics/useReceiveRoutes.js +++ b/src-ui/logics/useReceiveRoutes.js @@ -20,7 +20,10 @@ export const STATIC_ROUTE_META_LIST = [ { endpoint: "/run/open_filepath_logs", ns: common, hook_name: "useOpenFolder", method_name: "openedFolder_MessageLogs" }, { endpoint: "/run/open_filepath_config_file", ns: common, hook_name: "useOpenFolder", method_name: "openedFolder_ConfigFile" }, + { endpoint: "/get/data/connected_lmstudio", ns: common, hook_name: "useLLMConnection", method_name: "setConnectionStatus_LMStudio" }, { endpoint: "/run/lmstudio_connection", ns: common, hook_name: "useLLMConnection", method_name: "setConnectionStatus_LMStudio" }, + + { endpoint: "/get/data/connected_ollama", ns: common, hook_name: "useLLMConnection", method_name: "setConnectionStatus_Ollama" }, { endpoint: "/run/ollama_connection", ns: common, hook_name: "useLLMConnection", method_name: "setConnectionStatus_Ollama" }, // Software Version From ea4863f19512168e38d5fad4f180c6d43587f963 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Tue, 25 Nov 2025 18:43:16 +0900 Subject: [PATCH 11/12] =?UTF-8?q?=E5=85=A5=E3=82=8C=E8=BE=BC=E3=81=BF?= =?UTF-8?q?=E5=BF=98=E3=82=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-python/models/translation/translation_translator.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src-python/models/translation/translation_translator.py b/src-python/models/translation/translation_translator.py index 4023bcc4..175a84ba 100644 --- a/src-python/models/translation/translation_translator.py +++ b/src-python/models/translation/translation_translator.py @@ -233,7 +233,10 @@ class Translator: Returns True if Ollama is reachable, False otherwise. """ self.ollama_client = OllamaClient(root_path=root_path) - return self.ollama_client.authenticationCheck() + result = self.ollama_client.authenticationCheck() + if result is False: + self.ollama_client = None + return result def getOllamaModelList(self, root_path: str = None) -> bool: """Initialize Ollama client and fetch available models. From 3ce8590ce28a060a7debd6a063c8a2f1b8377185 Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Tue, 25 Nov 2025 21:46:48 +0900 Subject: [PATCH 12/12] =?UTF-8?q?build=E3=83=89=E3=82=AD=E3=83=A5=E3=83=A1?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/readme_build.md | 422 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 422 insertions(+) create mode 100644 docs/readme_build.md diff --git a/docs/readme_build.md b/docs/readme_build.md new file mode 100644 index 00000000..644c7b4c --- /dev/null +++ b/docs/readme_build.md @@ -0,0 +1,422 @@ +# VRCTビルドガイド + +このドキュメントでは、VRCTプロジェクトのビルド方法について説明します。 + +## 目次 + +- [必要な環境](#必要な環境) +- [初回セットアップ](#初回セットアップ) +- [ビルドの種類](#ビルドの種類) +- [開発ビルド](#開発ビルド) +- [リリースビルド](#リリースビルド) +- [ビルドプロセスの詳細](#ビルドプロセスの詳細) +- [トラブルシューティング](#トラブルシューティング) + +## 必要な環境 + +### 必須ソフトウェア + +- **Node.js** (npm含む) +- **Python 3.x** +- **Rust** (Tauri用) +- **Git** + +### 推奨環境 + +- Windows 10/11 +- メモリ: 8GB以上 +- ストレージ: 5GB以上の空き容量 + +## 初回セットアップ + +### 1. リポジトリのクローン + +```bash +git clone +cd VRCT +``` + +### 2. Node.js依存関係のインストール + +```bash +npm install +``` + +### 3. Python環境のセットアップ + +以下のコマンドで、CPU版とCUDA版の両方の仮想環境を作成します: + +```bash +npm run setup-python +``` + +このコマンドは以下の処理を実行します: +- `.venv` (CPU版) の作成と依存関係のインストール +- `.venv_cuda` (CUDA版) の作成と依存関係のインストール + +> **注意**: CUDA版を使用する場合は、NVIDIAのGPUとCUDA Toolkit 12.8が必要です。 + +## ビルドの種類 + +VRCTでは、以下の2種類のビルドが可能です: + +### CPU版 +標準的なCPUで動作するバージョン。GPUは不要。 + +### CUDA版 +NVIDIA GPUを活用した高速処理版。CUDA対応GPUが必要。 + +## 開発ビルド + +開発中にアプリケーションを実行・テストするためのビルドです。 + +### CPU版の開発ビルド + +```bash +npm run dev +``` + +このコマンドは以下を実行します: +1. 実行中のプロセスを終了 (`task-kill`) +2. ビルドファイルのクリーンアップ (`clean`) +3. バージョン情報の更新 (`update-version`) +4. Pythonバックエンドのビルド (`build-python`) +5. ViteとTauriの開発サーバー起動 + +### CUDA版の開発ビルド + +```bash +npm run dev-cuda +``` + +CPU版と同様ですが、CUDA対応のPythonバックエンドをビルドします。 + +### UIのみの開発 + +バックエンドのビルドをスキップして、UIのみを開発する場合: + +```bash +npm run dev-ui +``` + +## リリースビルド + +配布用のインストーラーを作成するビルドです。 + +### CPU版のリリースビルド + +```bash +npm run build +``` + +または、ZIP形式でパッケージング: + +```bash +npm run release +``` + +生成されるファイル: +- インストーラー: `src-tauri/target/release/bundle/nsis/` +- ZIPファイル: `VRCT.zip` (releaseコマンド使用時) + +### CUDA版のリリースビルド + +```bash +npm run build-cuda +``` + +または、ZIP形式でパッケージング: + +```bash +npm run release-cuda +``` + +生成されるファイル: +- インストーラー: `src-tauri/target/release/bundle/nsis/` +- ZIPファイル: `VRCT_cuda.zip` (release-cudaコマンド使用時) + +### 両バージョンの同時ビルド + +CPU版とCUDA版の両方をビルドする場合: + +```bash +npm run release-all +``` + +## ビルドプロセスの詳細 + +### バージョン管理 + +バージョンは `package.json` で一元管理され、以下のファイルに自動で同期されます: + +```bash +npm run update-version +``` + +更新されるファイル: +- `src-tauri/tauri.conf.json` +- `src-python/config.py` + +### どこにバージョンを設定すればReleaseに反映されるか + +- **設定箇所**: `package.json` の `version` が唯一のソース・オブ・トゥルース。 +- **反映方法**: `npm run update-version`(`build`/`build-cuda`/`release`コマンド内でも自動実行)により、 + - `src-tauri/tauri.conf.json` の `version` に同期(Tauri/NSISインストーラーの表示・メタデータに使用) + - `src-python/config.py` の `self._VERSION` に同期(ランタイム表示等に使用) +- **成果物への影響**: + - インストーラー(NSIS)は `tauri.conf.json` の `version` を取り込み、プロダクトバージョンとして反映。 + - ZIPパッケージ名はスクリプト既定では固定(`VRCT.zip`/`VRCT_cuda.zip`)。ファイル名にバージョンを含めたい場合は、`package.json` の `release` スクリプトを調整してください。 + +### Pythonバックエンドのビルド + +#### CPU版 + +```bash +npm run build-python +``` + +実行内容: + +- `.venv` 環境をアクティベート +- PyInstallerで `spec/backend.spec` を使用してビルド +- 出力先: `src-tauri/bin/` + +#### CUDA版 + +```bash +npm run build-python-cuda +``` + +実行内容: + +- `.venv_cuda` 環境をアクティベート +- PyInstallerで `spec/backend_cuda.spec` を使用してビルド +- 出力先: `src-tauri/bin/` + +### フロントエンドのビルド + +```bash +npm run vite-build +``` + +Viteを使用してフロントエンド(React)をビルドし、`dist/` ディレクトリに出力します。 + +### Tauriアプリケーションのビルド + +```bash +npm run tauri build +``` + +Tauriを使用して最終的なデスクトップアプリケーションをビルドします。 + +## GitHub ActionsでのRelease自動化 + +Windows用のReleaseをGitHub Actionsで自動生成・公開する例です。`package.json` のバージョンをタグ・リリース名に使い、TauriのNSISインストーラーとZIPを添付します。 + +### 推奨トリガー + +- タグプッシュ(例: `v*`)または手動実行(`workflow_dispatch`) + +### サンプルワークフロー(Windows) + +```yaml +name: Release (Windows) + +on: + workflow_dispatch: {} + push: + tags: + - 'v*' + +jobs: + build-release-windows: + runs-on: windows-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Install dependencies + run: | + npm ci + + - name: Setup Python envs (.venv/.venv_cuda) + run: | + npm run setup-python + + - name: Sync versions from package.json + run: | + npm run update-version + + - name: Build (CPU) + run: | + npm run build + + - name: Package ZIP (CPU) + run: | + python utils/zip.py --zip_name VRCT.zip + + - name: Read version from package.json + id: pkg + shell: pwsh + run: | + $version = (Get-Content package.json | ConvertFrom-Json).version + echo "version=$version" >> $env:GITHUB_OUTPUT + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: VRCT-windows-${{ steps.pkg.outputs.version }} + path: | + src-tauri/target/release/bundle/nsis/**/* + VRCT.zip + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ steps.pkg.outputs.version }} + name: VRCT v${{ steps.pkg.outputs.version }} + files: | + src-tauri/target/release/bundle/nsis/**/* + VRCT.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` + +### ポイント +- ビルド前に必ず `npm run update-version` を実行して、`tauri.conf.json` と `config.py` にバージョンを同期します。 +- アーティファクトのパスは既定構成に合わせています: + - インストーラー: `src-tauri/target/release/bundle/nsis/` + - ZIP: ルート直下の `VRCT.zip` +- CUDA版も同様にビルドする場合は、`npm run build-cuda` と `python utils/zip.py --zip_name VRCT_cuda.zip` を追加して、別アーティファクト名でアップロード・添付してください。 + +## ユーティリティコマンド + +### クリーンアップ + +```bash +npm run clean +``` + +以下のディレクトリを削除します: +- `build/` +- `dist/` +- `src-tauri/bin/` +- `src-tauri/target/` + +### プロセスの強制終了 + +```bash +npm run task-kill +``` + +VRCTに関連する実行中のプロセスを終了します。 + +## ディレクトリ構成 + +``` +VRCT/ +├── bat/ # バッチスクリプト +│ ├── build.bat # CPU版Pythonビルド +│ ├── build_cuda.bat # CUDA版Pythonビルド +│ └── install.bat # Python環境セットアップ +├── spec/ # PyInstallerスペックファイル +│ ├── backend.spec # CPU版ビルド設定 +│ └── backend_cuda.spec # CUDA版ビルド設定 +├── src-python/ # Pythonバックエンドソースコード +├── src-tauri/ # Tauriアプリケーション設定 +│ ├── bin/ # ビルド済みPythonバイナリ(生成) +│ └── target/ # Tauriビルド出力(生成) +├── src-ui/ # Reactフロントエンドソースコード +├── utils/ # ユーティリティスクリプト +│ ├── clean.py # クリーンアップスクリプト +│ ├── task_kill.py # プロセス終了スクリプト +│ ├── update_version.py # バージョン更新スクリプト +│ └── zip.py # ZIPパッケージング +├── package.json # Node.js設定とバージョン管理 +├── requirements.txt # Python依存関係(CPU版) +└── requirements_cuda.txt # Python依存関係(CUDA版) +``` + +## トラブルシューティング + +### Python環境のエラー + +仮想環境を再作成してください: + +```bash +npm run setup-python +``` + +### ビルドが失敗する + +1. クリーンアップを実行: +```bash +npm run clean +``` + +2. Node.js依存関係を再インストール: +```bash +npm install +``` + +3. 再度ビルド: +```bash +npm run build +``` + +### CUDA版が動作しない + +- CUDA Toolkit 12.8がインストールされているか確認 +- NVIDIA GPUドライバーが最新か確認 +- `requirements_cuda.txt` の依存関係が正しくインストールされているか確認 + +### プロセスが残っている + +```bash +npm run task-kill +``` + +を実行して、すべてのVRCTプロセスを終了してください。 + +## 参考情報 + +### PyInstallerスペックファイル + +- `spec/backend.spec` - CPU版の設定 +- `spec/backend_cuda.spec` - CUDA版の設定 + +これらのファイルでは、以下を設定しています: +- エントリーポイント: `src-python/mainloop.py` +- データファイル(フォント、プロンプト、言語ファイル等)のパス +- 依存ライブラリのパス + +### バージョン管理フロー + +1. `package.json` のバージョンを更新 +2. `npm run update-version` を実行 +3. 自動的に `tauri.conf.json` と `config.py` が更新される + +### リリースパッケージの内容 + +ZIPファイルには以下が含まれます: +- `VRCT.exe` - メインアプリケーション +- `VRCT-sidecar.exe` - Pythonバックエンド +- `_internal/` - 必要な依存ファイル + +## ライセンス + +プロジェクトのライセンスについては、`LICENSE` ファイルを参照してください。