Merge branch 'develop' into plugins_system

# Conflicts:
#	src-tauri/tauri.conf.json
This commit is contained in:
Sakamoto Shiina
2025-04-24 02:20:00 +09:00
42 changed files with 716 additions and 589 deletions

View File

@@ -24,7 +24,7 @@ const _Entry = forwardRef((props, ref) => {
<div className={styles.entry_container}>
<div
className={input_wrapper_class_names}
style={{width: props.width }}
style={{width: props.width || "20rem" }}
>
<input
ref={inputRef}

View File

@@ -0,0 +1,32 @@
import styles from "./EntryWithSaveButton.module.scss";
import { _Entry } from "../_atoms/_entry/_Entry";
import CircularProgress from "@mui/material/CircularProgress";
import { useTranslation } from "react-i18next";
import { clsx } from "clsx";
export const EntryWithSaveButton = (props) => {
const { t } = useTranslation();
const onChangeFunction = (e) => {
props.onChangeFunction?.(e.target.value);
};
const saveFunction = () => {
props.saveFunction();
};
const is_disabled = props.state === "pending";
const save_button_class_names = clsx(styles.save_button, {
[styles.is_disabled]: is_disabled
});
return (
<div className={styles.container}>
<_Entry width={props.width} onChange={onChangeFunction} ui_variable={props.variable} is_disabled={is_disabled}/>
<button className={save_button_class_names} onClick={saveFunction}>
{is_disabled
? <CircularProgress size="1.4rem" sx={{ color: "var(--dark_basic_text_color)" }}/>
: <p className={styles.save_button_label}>{t("config_page.translation.deepl_auth_key.save")}</p>
}
</button>
</div>
);
};

View File

@@ -0,0 +1,30 @@
.container {
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
flex-shrink: 0;
}
.save_button {
padding: 0.8rem 1.2rem;
background-color: var(--primary_600_color);
border-radius: 0.4rem;
text-align: center;
flex-shrink: 0;
min-width: 5.4rem;
&:hover {
background-color: var(--primary_500_color);
}
&:active {
background-color: var(--primary_700_color);
}
&.is_disabled {
pointer-events: none;
background-color: var(--primary_800_color);
}
}
.save_button_label {
font-size: 1.4rem;
}

View File

@@ -3,6 +3,7 @@ export { ComputeDevice } from "./compute_device/ComputeDevice";
export { DeeplAuthKey, OpenWebpage_DeeplAuthKey } from "./deepl_auth_key/DeeplAuthKey";
export { DropdownMenu } from "./dropdown_menu/DropdownMenu";
export { Entry } from "./entry/Entry";
export { EntryWithSaveButton } from "./entry_with_save_button/EntryWithSaveButton";
export { HotkeysEntry } from "./hotkeys_entry/HotkeysEntry";
export { LabelComponent } from "./label_component/LabelComponent";
export { RadioButton } from "./radio_button/RadioButton";

View File

@@ -8,6 +8,7 @@ import {
Slider,
SwitchBox,
Entry,
EntryWithSaveButton,
HotkeysEntry,
RadioButton,
OpenWebpage_DeeplAuthKey,
@@ -75,6 +76,9 @@ export const SwitchBoxContainer = (props) => (
export const EntryContainer = (props) => (
<CommonContainer Component={Entry} {...props} add_break_point={false} />
);
export const EntryWithSaveButtonContainer = (props) => (
<CommonContainer Component={EntryWithSaveButton} {...props} add_break_point={false} />
);
export const HotkeysEntryContainer = (props) => (
<CommonContainer Component={HotkeysEntry} {...props} />

View File

@@ -10,6 +10,7 @@ import contributor_iya from "@images/about_vrct/contributor_iya.png";
import contributor_rera from "@images/about_vrct/contributor_rera.png";
import contributor_poposuke from "@images/about_vrct/contributor_poposuke.png";
import contributor_kumaguma from "@images/about_vrct/contributor_kumaguma.png";
import contributor_riku from "@images/about_vrct/contributor_riku.png";
import localization_section_title from "@images/about_vrct/localization_section_title.png";
import localization_1 from "@images/about_vrct/localization_1.png";
@@ -86,6 +87,10 @@ export const AboutVrct = () => {
<img src={contributor_kumaguma} className={clsx(styles.contributors_img, styles.contributors)} />
<OpenLinkContainer className={styles.contributors_kumaguma_x} href_id="contributors_kumaguma_x" />
</div>
<div className={styles.contributor_card_wrapper}>
<img src={contributor_riku} className={clsx(styles.contributors_img, styles.contributors)} />
<OpenLinkContainer className={styles.contributors_riku_x} href_id="contributors_riku_x" />
</div>
</div>
</div>
@@ -158,6 +163,7 @@ const about_vrct_links = {
contributors_rera_github: { img: contributors_github_icon, href: "https://github.com/soumt-r" },
contributors_poposuke_x: { img: contributors_x_icon, href: "https://twitter.com/sig_popo" },
contributors_kumaguma_x: { img: contributors_x_icon, href: "https://twitter.com/K_kumaguma_A" },
contributors_riku_x: { img: contributors_x_icon, href: "https://twitter.com/Riku7302" },
};
const OpenLinkContainer = ({className, href_id}) => {

View File

@@ -160,6 +160,9 @@ $sns_left_pos: 0.8rem;
.contributors_kumaguma_x {
@include contributors_sns_styles($bottom_pos, $sns_left_pos);
}
.contributors_riku_x {
@include contributors_sns_styles($bottom_pos, $sns_left_pos);
}
.localization_section {
display: flex;

View File

@@ -11,6 +11,7 @@ import {
import {
ActionButtonContainer,
EntryContainer,
EntryWithSaveButtonContainer,
} from "../_templates/Templates";
@@ -30,54 +31,59 @@ export const AdvancedSettings = () => {
const OscIpAddressContainer = () => {
const { t } = useTranslation();
const [ui_variable, setUiVariable] = useState("");
const { currentOscIpAddress, setOscIpAddress } = useOscIpAddress();
const onChangeFunction = (e) => {
const value = e.currentTarget.value;
if (value === "") {
setUiVariable("");
} else {
setUiVariable(value);
setOscIpAddress(value);
}
const [input_value, setInputValue] = useState(currentOscIpAddress.data);
const onChangeFunction = (value) => {
setInputValue(value);
};
const saveFunction = () => {
setOscIpAddress(input_value);
};
useEffect(()=> {
setUiVariable(currentOscIpAddress.data);
setInputValue(currentOscIpAddress.data);
}, [currentOscIpAddress]);
return (
<EntryContainer
<EntryWithSaveButtonContainer
label={t("config_page.advanced_settings.osc_ip_address.label")}
ui_variable={ui_variable}
onChange={onChangeFunction}
variable={input_value}
saveFunction={saveFunction}
onChangeFunction={onChangeFunction}
state={currentOscIpAddress.state}
width="14rem"
/>
);
};
const OscPortContainer = () => {
const { t } = useTranslation();
const [ui_variable, setUiVariable] = useState("");
const { currentOscPort, setOscPort } = useOscPort();
const onChangeFunction = (e) => {
const value = e.currentTarget.value;
if (value === "") {
setUiVariable("");
} else {
setUiVariable(value);
setOscPort(value);
}
const [input_value, setInputValue] = useState(currentOscPort.data);
const onChangeFunction = (value) => {
value = value.replace(/[^0-9]/g, "");
setInputValue(value);
};
const saveFunction = () => {
setOscPort(input_value);
};
useEffect(()=> {
setUiVariable(currentOscPort.data);
setInputValue(currentOscPort.data);
}, [currentOscPort]);
return (
<EntryContainer
<EntryWithSaveButtonContainer
label={t("config_page.advanced_settings.osc_port.label")}
ui_variable={ui_variable}
onChange={onChangeFunction}
variable={input_value}
saveFunction={saveFunction}
onChangeFunction={onChangeFunction}
state={currentOscPort.state}
width="10rem"
/>
);
};

View File

@@ -260,6 +260,8 @@ const WhisperWeightType_Box = () => {
{ id: "large-v1", label: t("config_page.transcription.whisper_weight_type.model_template", {model_name: "large-v1", capacity: "2.87GB"}) },
{ id: "large-v2", label: t("config_page.transcription.whisper_weight_type.model_template", {model_name: "large-v2", capacity: "2.87GB"}) },
{ id: "large-v3", label: t("config_page.transcription.whisper_weight_type.model_template", {model_name: "large-v3", capacity: "2.87GB"}) },
{ id: "large-v3-turbo-int8", label: t("config_page.transcription.whisper_weight_type.model_template", {model_name: "large-v3-turbo-int8", capacity: "794MB"}) },
{ id: "large-v3-turbo", label: t("config_page.transcription.whisper_weight_type.model_template", {model_name: "large-v3-turbo", capacity: "1.58GB"}) },
];
const whisper_weight_types = updateLabelsById(currentWhisperWeightTypeStatus.data, new_labels);

View File

@@ -62,7 +62,7 @@ const CTranslate2WeightType_Box = () => {
)}
desc={t(
"config_page.translation.ctranslate2_weight_type.desc",
{translator: t("main_page.translator")}
{ctranslate2: "CTranslate2"}
)}
name="ctransalte2_weight_type"
options={c_translate2_weight_types}

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -14,6 +14,8 @@ import {
useSpeakerMaxWords,
useDeepLAuthKey,
useOscIpAddress,
} from "@logics_configs";
import { ui_configs } from "../ui_configs";
@@ -29,14 +31,16 @@ export const _useBackendErrorHandling = () => {
const { updateSpeakerPhraseTimeout } = useSpeakerPhraseTimeout();
const { updateSpeakerMaxWords } = useSpeakerMaxWords();
const { updateDeepLAuthKey } = useDeepLAuthKey();
const { updateDeepLAuthKey, saveErrorDeepLAuthKey } = useDeepLAuthKey();
const errorHandling_Backend = ({message, data, endpoint}) => {
const { updateOscIpAddress } = useOscIpAddress();
const errorHandling_Backend = ({message, data, endpoint, _result}) => {
switch (message) {
case "No mic device detected":
showNotification_Error(t("common_error.no_device_mic"));
break;
case "No Speaker device detected":
case "No speaker device detected":
showNotification_Error(t("common_error.no_device_speaker"));
break;
@@ -47,9 +51,9 @@ export const _useBackendErrorHandling = () => {
break;
case "Speaker energy threshold value is out of range":
showNotification_Error(t("common_error.threshold_invalid_value",
{ min: ui_configs.speaker_threshold_min, max: ui_configs.speaker_threshold_max },
));
break;
{ min: ui_configs.speaker_threshold_min, max: ui_configs.speaker_threshold_max },
));
break;
case "CTranslate2 weight download error":
showNotification_Error(t("common_error.failed_download_weight_ctranslate2"));
@@ -110,10 +114,23 @@ export const _useBackendErrorHandling = () => {
showNotification_Error(t("common_error.invalid_value_speaker_max_phrase"));
break;
default:
if (endpoint === "/set/data/deepl_auth_key") updateDeepLAuthKey(data);
// Advanced Settings, error messages are set by Backend (EN only)
case "Invalid IP address":
updateOscIpAddress(data);
showNotification_Error(message);
break;
case "Cannot set IP address":
updateOscIpAddress(data);
showNotification_Error(message);
break;
default:
// determine by endpoint, not message.
if (endpoint === "/set/data/deepl_auth_key") saveErrorDeepLAuthKey({message, data, endpoint, _result});
break;
}
}

View File

@@ -1,7 +1,9 @@
import { useStore_OscPort } from "@store";
import { useStdoutToPython } from "@logics/useStdoutToPython";
import { useNotificationStatus } from "@logics_common";
export const useOscPort = () => {
const { showNotification_Error } = useNotificationStatus();
const { asyncStdoutToPython } = useStdoutToPython();
const { currentOscPort, updateOscPort, pendingOscPort } = useStore_OscPort();
@@ -15,10 +17,17 @@ export const useOscPort = () => {
asyncStdoutToPython("/set/data/osc_port", osc_port);
};
const saveErrorOscPort = ({data, message, _result}) => {
updateOscPort(d => d.data);
showNotification_Error(_result);
};
return {
currentOscPort,
getOscPort,
updateOscPort,
setOscPort,
saveErrorOscPort,
};
};

View File

@@ -18,22 +18,30 @@ export const useDeepLAuthKey = () => {
pendingDeepLAuthKey();
asyncStdoutToPython("/set/data/deepl_auth_key", selected_deepl_auth_key);
};
const saveSuccessDeepLAuthKey = (saved_deepl_auth_key) => {
updateDeepLAuthKey(saved_deepl_auth_key);
showNotification_Success(t("config_page.translation.deepl_auth_key.auth_key_success"));
};
const deleteDeepLAuthKey = () => {
pendingDeepLAuthKey();
asyncStdoutToPython("/delete/data/deepl_auth_key");
};
const savedDeepLAuthKey = (data) => {
updateDeepLAuthKey(data);
showNotification_Success(t("config_page.translation.deepl_auth_key.auth_key_success"));
};
const saveErrorDeepLAuthKey = ({data, message}) => {
updateDeepLAuthKey(data);
showNotification_Error(message);
};
return {
currentDeepLAuthKey,
getDeepLAuthKey,
updateDeepLAuthKey,
setDeepLAuthKey,
saveSuccessDeepLAuthKey,
deleteDeepLAuthKey,
saveErrorDeepLAuthKey,
savedDeepLAuthKey,
};
};

View File

@@ -148,7 +148,7 @@ export const useReceiveRoutes = () => {
const { updateSpeakerPhraseTimeout } = useSpeakerPhraseTimeout();
const { updateSpeakerMaxWords } = useSpeakerMaxWords();
const { updateDeepLAuthKey, saveSuccessDeepLAuthKey } = useDeepLAuthKey();
const { updateDeepLAuthKey, savedDeepLAuthKey } = useDeepLAuthKey();
const { updateSelectedCTranslate2WeightType } = useSelectedCTranslate2WeightType();
const {
updateDownloadedCTranslate2WeightTypeStatus,
@@ -353,7 +353,7 @@ export const useReceiveRoutes = () => {
// Translation
"/get/data/deepl_auth_key": updateDeepLAuthKey,
"/set/data/deepl_auth_key": saveSuccessDeepLAuthKey,
"/set/data/deepl_auth_key": savedDeepLAuthKey,
"/delete/data/deepl_auth_key": () => updateDeepLAuthKey(""),
"/get/data/ctranslate2_weight_type": updateSelectedCTranslate2WeightType,
@@ -528,6 +528,8 @@ export const useReceiveRoutes = () => {
"/set/data/speaker_record_timeout": errorHandling_Backend,
"/set/data/speaker_phrase_timeout": errorHandling_Backend,
"/set/data/speaker_max_phrases": errorHandling_Backend,
"/set/data/osc_ip_address": errorHandling_Backend,
};
@@ -566,11 +568,16 @@ export const useReceiveRoutes = () => {
message: parsed_data.result.message,
data: parsed_data.result.data,
endpoint: parsed_data.endpoint,
_result: parsed_data.result,
});
} else {
handleInvalidEndpoint(parsed_data);
}
break;
case 500:
showNotification_Error(
`An error occurred. Please restart VRCT or contact the developers. ${JSON.stringify(parsed_data.result)}`, { hide_duration: null });
break;
case 348:
// console.log(`from backend: %c ${JSON.stringify(parsed_data)}`, style_348);

View File

@@ -1,8 +1,13 @@
import { Command } from "@tauri-apps/api/shell";
import { store } from "@store";
import { useReceiveRoutes } from "./useReceiveRoutes";
import {
useNotificationStatus,
} from "@logics_common";
export const useStartPython = () => {
const { receiveRoutes } = useReceiveRoutes();
const { showNotification_Success, showNotification_Error } = useNotificationStatus();
const asyncStartPython = async () => {
const command = Command.sidecar("bin/VRCT-sidecar");
@@ -16,7 +21,11 @@ export const useStartPython = () => {
console.log(error, line);
}
});
command.stderr.on("data", line => console.error("stderr:", line));
command.stderr.on("data", line => {
showNotification_Error(
`An error occurred. Please restart VRCT or contact the developers. The last line:${JSON.stringify(line)}`, { hide_duration: null });
console.error("stderr", line)
});
const backend_subprocess = await command.spawn();
store.backend_subprocess = backend_subprocess;
};

View File

@@ -132,37 +132,38 @@ export const { atomInstance: Atom_NotificationStatus, useHook: useStore_Notifica
}, "NotificationStatus");
// Main Page
// Functions
// Common
export const { atomInstance: Atom_IsMainPageCompactMode, useHook: useStore_IsMainPageCompactMode } = createAtomWithHook(false, "IsMainPageCompactMode");
// Sidebar Section
export const { atomInstance: Atom_TranslationStatus, useHook: useStore_TranslationStatus } = createAtomWithHook(false, "TranslationStatus", {is_state_ok: true});
export const { atomInstance: Atom_TranscriptionSendStatus, useHook: useStore_TranscriptionSendStatus } = createAtomWithHook(false, "TranscriptionSendStatus", {is_state_ok: true});
export const { atomInstance: Atom_TranscriptionReceiveStatus, useHook: useStore_TranscriptionReceiveStatus } = createAtomWithHook(false, "TranscriptionReceiveStatus", {is_state_ok: true});
export const { atomInstance: Atom_ForegroundStatus, useHook: useStore_ForegroundStatus } = createAtomWithHook(false, "ForegroundStatus", {is_state_ok: true});
export const { atomInstance: Atom_MessageLogs, useHook: useStore_MessageLogs } = createAtomWithHook([], "MessageLogs");
// export const { atomInstance: Atom_MessageLogs, useHook: useStore_MessageLogs } = createAtomWithHook(generateTestData(20), "MessageLogs"); // For testing
export const { atomInstance: Atom_MessageInputValue, useHook: useStore_MessageInputValue } = createAtomWithHook("", "MessageInputValue");
export const { atomInstance: Atom_IsVisibleResendButton, useHook: useStore_IsVisibleResendButton } = createAtomWithHook(false, "IsVisibleResendButton", {is_state_ok: true});
export const { atomInstance: Atom_IsAppliedInitMessageBoxHeight, useHook: useStore_IsAppliedInitMessageBoxHeight } = createAtomWithHook(false, "IsAppliedInitMessageBoxHeight");
export const { atomInstance: Atom_SelectableLanguageList, useHook: useStore_SelectableLanguageList } = createAtomWithHook([], "SelectableLanguageList");
export const { atomInstance: Atom_SelectedPresetTabNumber, useHook: useStore_SelectedPresetTabNumber } = createAtomWithHook("1", "SelectedPresetTabNumber");
export const { atomInstance: Atom_EnableMultiTranslation, useHook: useStore_EnableMultiTranslation } = createAtomWithHook(false, "EnableMultiTranslation");
export const { atomInstance: Atom_SelectedYourLanguages, useHook: useStore_SelectedYourLanguages } = createAtomWithHook({}, "SelectedYourLanguages");
export const { atomInstance: Atom_SelectedTargetLanguages, useHook: useStore_SelectedTargetLanguages } = createAtomWithHook({}, "SelectedTargetLanguages");
export const { atomInstance: Atom_TranslationEngines, useHook: useStore_TranslationEngines } = createAtomWithHook(translator_status, "TranslationEngines");
export const { atomInstance: Atom_SelectedTranslationEngines, useHook: useStore_SelectedTranslationEngines } = createAtomWithHook({1:"", 2:"", 3:""}, "SelectedTranslationEngines");
export const { atomInstance: Atom_IsOpenedTranslatorSelector, useHook: useStore_IsOpenedTranslatorSelector } = createAtomWithHook(false, "IsOpenedTranslatorSelector");
// Designs
export const { atomInstance: Atom_IsMainPageCompactMode, useHook: useStore_IsMainPageCompactMode } = createAtomWithHook(false, "IsMainPageCompactMode");
export const { atomInstance: Atom_MessageInputBoxRatio, useHook: useStore_MessageInputBoxRatio } = createAtomWithHook(20, "MessageInputBoxRatio");
// Language Selector
export const { atomInstance: Atom_IsOpenedLanguageSelector, useHook: useStore_IsOpenedLanguageSelector } = createAtomWithHook(
{ your_language: false, target_language: false, target_key: "1" },
"IsOpenedLanguageSelector"
);
export const { atomInstance: Atom_SelectableLanguageList, useHook: useStore_SelectableLanguageList } = createAtomWithHook([], "SelectableLanguageList");
// Message Container
export const { atomInstance: Atom_MessageLogs, useHook: useStore_MessageLogs } = createAtomWithHook([], "MessageLogs");
// export const { atomInstance: Atom_MessageLogs, useHook: useStore_MessageLogs } = createAtomWithHook(generateTestData(20), "MessageLogs"); // For testing
export const { atomInstance: Atom_MessageInputBoxRatio, useHook: useStore_MessageInputBoxRatio } = createAtomWithHook(20, "MessageInputBoxRatio");
export const { atomInstance: Atom_MessageInputValue, useHook: useStore_MessageInputValue } = createAtomWithHook("", "MessageInputValue");
export const { atomInstance: Atom_IsVisibleResendButton, useHook: useStore_IsVisibleResendButton } = createAtomWithHook(false, "IsVisibleResendButton", {is_state_ok: true});
// Config Page
@@ -170,8 +171,6 @@ export const { atomInstance: Atom_IsOpenedLanguageSelector, useHook: useStore_Is
export const { atomInstance: Atom_SoftwareVersion, useHook: useStore_SoftwareVersion } = createAtomWithHook("-", "SoftwareVersion");
export const { atomInstance: Atom_SelectedConfigTabId, useHook: useStore_SelectedConfigTabId } = createAtomWithHook("device", "SelectedConfigTabId");
export const { atomInstance: Atom_SettingBoxScrollPosition, useHook: useStore_SettingBoxScrollPosition } = createAtomWithHook(0, "SettingBoxScrollPosition");
// Designs
export const { atomInstance: Atom_IsOpenedDropdownMenu, useHook: useStore_IsOpenedDropdownMenu } = createAtomWithHook("", "IsOpenedDropdownMenu");
// Device
@@ -292,9 +291,9 @@ export const { atomInstance: Atom_OscPort, useHook: useStore_OscPort } = createA
export const { atomInstance: Atom_IsOpenedTranslatorSelector, useHook: useStore_IsOpenedTranslatorSelector } = createAtomWithHook(false, "IsOpenedTranslatorSelector");
// Supporters
export const { atomInstance: Atom_SupportersData, useHook: useStore_SupportersData } = createAtomWithHook(null, "SupportersData", {is_state_ok: true});
// About VRCT
export const { atomInstance: Atom_VrctPosterIndex, useHook: useStore_VrctPosterIndex } = createAtomWithHook(0, "VrctPosterIndex");
export const { atomInstance: Atom_PosterShowcaseWorldPageIndex, useHook: useStore_PosterShowcaseWorldPageIndex } = createAtomWithHook(0, "PosterShowcaseWorldPageIndex");

View File

@@ -89,6 +89,8 @@ export const whisper_weight_type_status = [
{ id: "large-v1", label: "large-v1", is_downloaded: false, progress: null },
{ id: "large-v2", label: "large-v2", is_downloaded: false, progress: null },
{ id: "large-v3", label: "large-v3", is_downloaded: false, progress: null },
{ id: "large-v3-turbo-int8", label: "large-v3-turbo-int8", is_downloaded: false, progress: null },
{ id: "large-v3-turbo", label: "large-v3-turbo", is_downloaded: false, progress: null },
];
export const supporters_data_url = "https://shiinasakamoto.github.io/vrct_supporters/assets/supporters/data.json";