Merge branch 'feature_hotkey' into develop

This commit is contained in:
Sakamoto Shiina
2025-01-17 06:19:49 +09:00
29 changed files with 1194 additions and 439 deletions

View File

@@ -94,6 +94,7 @@ config_page:
transcription: Transcription
vr: VR
others: Others
hotkeys: Hotkeys
advanced_settings: Advanced Settings
supporters: Supporters
about_vrct: About VRCT
@@ -257,6 +258,16 @@ config_page:
label: Send Received Message To VRChat
desc: Send the message you received from the speaker's sound to VRChat's chatbox.
hotkeys:
toggle_vrct_visibility:
label: Toggle VRCT Visibility
toggle_translation:
label: Toggle {{translation}}
toggle_transcription_send:
label: Toggle {{transcription_send}}
toggle_transcription_receive:
label: Toggle {{transcription_receive}}
advanced_settings:
osc_ip_address:
label: OSC IP Address

View File

@@ -92,6 +92,7 @@ config_page:
translation: 翻訳
transcription: 音声認識
others: その他
hotkeys: ホットキー
advanced_settings: 高度な設定
device:
@@ -255,6 +256,16 @@ config_page:
label: 受信したメッセージをVRChatに送信する
desc: スピーカーから聞き取り、文字起こしされたメッセージをVRChatに送信します。
hotkeys:
toggle_vrct_visibility:
label: VRCTの最小化/アクティブ化の切り替え
toggle_translation:
label: '{{translation}}機能切り替え'
toggle_transcription_send:
label: '{{transcription_send}}機能切り替え'
toggle_transcription_receive:
label: '{{transcription_receive}}機能切り替え'
advanced_settings:
osc_ip_address:
label: OSC IP Address

View File

@@ -533,6 +533,19 @@ class Config:
self._MIC_WORD_FILTER = sorted(set(value), key=value.index)
self.saveConfig(inspect.currentframe().f_code.co_name, value)
@property
@json_serializable('HOTKEYS')
def HOTKEYS(self):
return self._HOTKEYS
@HOTKEYS.setter
def HOTKEYS(self, value):
if isinstance(value, dict) and set(value.keys()) == set(self.HOTKEYS.keys()):
for key, value in value.items():
if isinstance(value, list) or value is None:
self._HOTKEYS[key] = value
self.saveConfig(inspect.currentframe().f_code.co_name, self.HOTKEYS, immediate_save=True)
@property
@json_serializable('MIC_AVG_LOGPROB')
def MIC_AVG_LOGPROB(self):
@@ -1026,6 +1039,12 @@ class Config:
self._MIC_PHRASE_TIMEOUT = 3
self._MIC_MAX_PHRASES = 10
self._MIC_WORD_FILTER = []
self._HOTKEYS = {
"toggle_vrct_visibility": None,
"toggle_translation": None,
"toggle_transcription_send": None,
"toggle_transcription_receive": None,
}
self._MIC_AVG_LOGPROB = -0.8
self._MIC_NO_SPEECH_PROB = 0.6
self._AUTO_SPEAKER_SELECT = True

View File

@@ -996,6 +996,15 @@ class Controller:
response = {"status":200, "result":config.SPEAKER_MAX_PHRASES}
return response
@staticmethod
def getHotkeys(*args, **kwargs) -> dict:
return {"status":200, "result":config.HOTKEYS}
@staticmethod
def setHotkeys(data, *args, **kwargs) -> dict:
config.HOTKEYS = data
return {"status":200, "result":config.HOTKEYS}
@staticmethod
def getSpeakerAvgLogprob(*args, **kwargs) -> dict:
return {"status":200, "result":config.SPEAKER_AVG_LOGPROB}

View File

@@ -183,6 +183,9 @@ mapping = {
"/get/data/mic_max_phrases": {"status": True, "variable":controller.getMicMaxPhrases},
"/set/data/mic_max_phrases": {"status": True, "variable":controller.setMicMaxPhrases},
"/get/data/hotkeys": {"status": True, "variable":controller.getHotkeys},
"/set/data/hotkeys": {"status": True, "variable":controller.setHotkeys},
"/get/data/mic_avg_logprob": {"status": True, "variable":controller.getMicAvgLogprob},
"/set/data/mic_avg_logprob": {"status": True, "variable":controller.setMicAvgLogprob},

1070
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,12 +11,13 @@ edition = "2021"
tauri-build = { version = "1", features = [] }
[dependencies]
tauri = { version = "1", features = [ "window-set-size", "window-set-position", "window-unmaximize", "window-close", "window-maximize", "window-minimize", "window-unminimize", "window-start-dragging", "window-set-decorations", "window-set-always-on-top", "shell-sidecar", "shell-open", "devtools"] }
tauri = { version = "1", features = [ "window-hide", "window-set-focus", "global-shortcut-all", "window-set-size", "window-set-position", "window-unmaximize", "window-close", "window-maximize", "window-minimize", "window-unminimize", "window-start-dragging", "window-set-decorations", "window-set-always-on-top", "shell-sidecar", "shell-open", "devtools"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
font-kit = "0.14.2"
window-shadows = { git = "https://github.com/tauri-apps/window-shadows.git" }
[features]
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
custom-protocol = ["tauri/custom-protocol"]

View File

@@ -15,8 +15,10 @@
"window": {
"all": false,
"setAlwaysOnTop": true,
"setFocus": true,
"setDecorations": true,
"close": true,
"hide": true,
"setPosition": true,
"setSize": true,
"maximize": true,
@@ -25,6 +27,9 @@
"unminimize": true,
"startDragging": true
},
"globalShortcut": {
"all": true
},
"shell": {
"all": false,
"open": true,

View File

@@ -4,8 +4,6 @@ import {
useWindow,
} from "@logics_common";
// import React from "react";
import {
KeyEventController,
StartPythonController,

View File

@@ -9,6 +9,8 @@ import {
useMainFunction,
} from "@logics_main";
import { useHotkeys } from "@logics_configs";
import { useStore_MainFunctionsStateMemory } from "@store";
export const ConfigPageCloseTriggerController = () => {
@@ -27,6 +29,8 @@ export const ConfigPageCloseTriggerController = () => {
volumeCheckStop_Speaker,
} = useVolume();
const { registerShortcuts, unregisterAll } = useHotkeys();
const memorizeLatestMainFunctionsState = () => {
updateMainFunctionsStateMemory({
@@ -43,9 +47,11 @@ export const ConfigPageCloseTriggerController = () => {
useEffect(() => {
if (currentIsOpenedConfigPage.data === true) { // When config page is opened.
memorizeLatestMainFunctionsState();
unregisterAll();
if (currentTranscriptionSendStatus.data === true) setTranscriptionSend(false);
if (currentTranscriptionReceiveStatus.data === true) setTranscriptionReceive(false);
} else if (currentIsOpenedConfigPage.data === false) { // When config page is closed.
registerShortcuts();
if (currentMicThresholdCheckStatus.data === true) volumeCheckStop_Mic();
if (currentSpeakerThresholdCheckStatus.data === true) volumeCheckStop_Speaker();
restoreMainFunctionState();

View File

@@ -3,8 +3,11 @@ import { useEffect } from "react";
export const KeyEventController = () => {
useEffect(() => {
const handleKeydown = (event) => {
if (event.key === "F5" || (event.ctrlKey && event.key === "r") ||
(event.metaKey && event.key === "r")) {
if (
event.key === "F5" ||
(event.ctrlKey && event.key === "r") ||
(event.metaKey && event.key === "r")
) {
event.preventDefault();
}
};

View File

@@ -8,6 +8,7 @@ import {
Others,
AdvancedSettings,
Vr,
Hotkeys,
Supporters,
AboutVrct,
} from "@setting_box";
@@ -27,6 +28,8 @@ export const SettingBox = () => {
return <Others />;
case "vr":
return <Vr />;
case "hotkeys":
return <Hotkeys />;
case "advanced_settings":
return <AdvancedSettings />;
case "supporters":

View File

@@ -8,23 +8,36 @@ const _Entry = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
blur: () => {
inputRef.current.blur();
}
}));
const input_class_names = clsx(styles.entry_input_area, {
[styles.is_disabled]: props.is_disabled
[styles.is_disabled]: props.is_disabled,
});
const input_wrapper_class_names = clsx(styles.entry_wrapper, {
[styles.is_activated]: props.is_activated,
});
return (
<div className={styles.entry_container}>
<div
className={styles.entry_wrapper}
className={input_wrapper_class_names}
style={{width: props.width }}
>
<input
ref={inputRef}
text={props.text ? props.text : "text"}
placeholder={props.placeholder ? props.placeholder : ""}
className={input_class_names}
value={props.ui_variable === null ? "" : props.ui_variable}
onChange={(e) => props.onChange(e)}
onChange={(e) => props.onChange?.(e)}
onFocus={(e) => props.onFocus?.(e)}
onBlur={(e) => props.onBlur?.(e)}
onKeyDown={(e) => props.onKeyDown?.(e)}
onKeyUp={(e) => props.onKeyUp?.(e)}
readOnly={props.readOnly === true ? true : false}
/>
</div>
</div>

View File

@@ -9,6 +9,9 @@
background-color: var(--dark_875_color);
border: 0.1rem solid var(--dark_750_color);
border-radius: 0.4rem;
&.is_activated {
border: 0.1rem solid var(--primary_400_color);
}
}
.entry_input_area {

View File

@@ -0,0 +1,122 @@
import styles from "./HotkeysEntry.module.scss";
import { _Entry } from "../_atoms/_entry/_Entry";
import { useState, useRef, useEffect } from "react";
import DeleteSvg from "@images/cancel.svg?react";
import { clsx } from "clsx";
export const HotkeysEntry = (props) => {
const [isAcceptingInput, setIsAcceptingInput] = useState(false);
const [displayValue, setDisplayValue] = useState("");
const lastKeyRef = useRef(null);
const isModifierOnlyRef = useRef(false);
const entryRef = useRef(null);
const pressedKeys = useRef(new Set());
const keysRef = useRef([]);
useEffect(() => {
const init_display_value = props.value[props.hotkey_id] ? props.value[props.hotkey_id].join(" + ") : "";
setDisplayValue(init_display_value);
}, []);
const updateHotkeys = (keys) => {
entryRef.current.blur();
const result = props.setHotkeys({ [props.hotkey_id]: keys });
if (result === false) setDisplayValue("");
};
const processKey = (key) => {
if (/^[a-zA-Z]$/.test(key)) return key.toUpperCase();
if (key === "Meta") return "Super";
return key;
};
const handleKeyInput = (event) => {
const keys = [];
const nonModifierKeys = [];
["Ctrl", "Shift", "Alt", "Meta"].forEach((modKey) => {
if (event[`${modKey.toLowerCase()}Key`] && !keys.includes(modKey)) {
let register_mod_key = (modKey === "Meta") ? "Super" : modKey;
keys.push(register_mod_key);
}
});
const key = processKey(event.key);
if (!["Control", "Shift", "Alt", "Meta"].includes(event.key)) {
keys.push(key);
nonModifierKeys.push(key);
}
if (!pressedKeys.current.has(key)) {
pressedKeys.current.add(key);
}
keysRef.current = keys;
setDisplayValue(keys.join(" + "));
isModifierOnlyRef.current = nonModifierKeys.length === 0;
};
const handleKeyDown = (event) => {
event.preventDefault();
if (lastKeyRef.current === event.key) return;
lastKeyRef.current = event.key;
handleKeyInput(event);
};
const handleKeyUp = (event) => {
lastKeyRef.current = null;
const key = processKey(event.key);
pressedKeys.current.delete(key);
if (isModifierOnlyRef.current) {
setDisplayValue("");
}
if (pressedKeys.current.size === 0) {
const hasNonModifierKeys = keysRef.current.some(
(key) => !["Ctrl", "Shift", "Alt", "Super"].includes(key)
);
if (hasNonModifierKeys) {
updateHotkeys(keysRef.current);
} else {
const display_value = props.value[props.hotkey_id] ? props.value[props.hotkey_id].join(" + ") : "";
setDisplayValue(display_value);
}
}
};
const handleBlur = () => {
setIsAcceptingInput(false);
pressedKeys.current.clear();
};
const handleDelete = () => {
updateHotkeys(null);
setDisplayValue("");
};
const is_pending = props.state === "pending";
return (
<div className={styles.container}>
{is_pending && <span className={styles.loader}></span>}
<_Entry
ref={entryRef}
type="text"
onFocus={() => setIsAcceptingInput(true)}
onBlur={handleBlur}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
ui_variable={displayValue}
width="20rem"
is_activated={isAcceptingInput}
is_disabled={is_pending}
readOnly
/>
<button className={clsx(styles.delete_button, { [styles.is_pending]: is_pending })} onClick={handleDelete}>
<DeleteSvg className={styles.delete_svg}/>
</button>
</div>
);
};

View File

@@ -0,0 +1,41 @@
@import "@scss_mixins";
.container {
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
position: relative;
}
.delete_button {
padding: 0.4rem;
font-size: 1.4rem;
// background-color: var(--dark_800_color);
flex-shrink: 0;
display: flex;
justify-content: center;
align-items: center;
border-radius: 0.2rem;
&:hover {
background-color: var(--dark_850_color);
}
&:active {
background-color: var(--dark_900_color);
}
&.is_pending {
pointer-events: none;
& .delete_svg {
color: var(--dark_600_color);
}
}
}
.delete_svg {
width: 2.2rem;
color: var(--error_bc_color);
}
.loader {
@include loader(2rem, 0.2rem, left, -2.2rem);
}

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 { HotkeysEntry } from "./hotkeys_entry/HotkeysEntry";
export { LabelComponent } from "./label_component/LabelComponent";
export { RadioButton } from "./radio_button/RadioButton";
export { SectionLabelComponent } from "./section_label_component/SectionLabelComponent";

View File

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

View File

@@ -0,0 +1,49 @@
import { useHotkeys } from "@logics_configs";
import styles from "./Hotkeys.module.scss";
import { HotkeysEntryContainer } from "../_templates/Templates";
import { useTranslation } from "react-i18next";
export const Hotkeys = () => {
return (
<div className={styles.container}>
<HotkeysBoxContainer />
</div>
);
};
const HotkeysBoxContainer = () => {
const { t } = useTranslation();
const { currentHotkeys, setHotkeys } = useHotkeys();
return (
<div className={styles.container}>
<HotkeysEntryContainer
label={t("config_page.hotkeys.toggle_vrct_visibility.label")}
hotkey_id="toggle_vrct_visibility"
value={currentHotkeys.data}
state={currentHotkeys.state}
setHotkeys={setHotkeys}
/>
<HotkeysEntryContainer
label={t("config_page.hotkeys.toggle_translation.label", {translation: t("main_page.translation")})}
hotkey_id="toggle_translation"
value={currentHotkeys.data}
state={currentHotkeys.state}
setHotkeys={setHotkeys}
/>
<HotkeysEntryContainer
label={t("config_page.hotkeys.toggle_transcription_send.label", {transcription_send: t("main_page.transcription_send")})}
hotkey_id="toggle_transcription_send"
value={currentHotkeys.data}
state={currentHotkeys.state}
setHotkeys={setHotkeys}
/>
<HotkeysEntryContainer
label={t("config_page.hotkeys.toggle_transcription_receive.label", {transcription_receive: t("main_page.transcription_receive")})}
hotkey_id="toggle_transcription_receive"
value={currentHotkeys.data}
state={currentHotkeys.state}
setHotkeys={setHotkeys}
/>
</div>
);
};

View File

@@ -0,0 +1,5 @@
.container {
display: flex;
// gap: 6.4rem;
flex-direction: column;
}

View File

@@ -5,5 +5,6 @@ export { Transcription } from "./transcription/Transcription";
export { Others, VrcMicMuteSyncContainer } from "./others/Others";
export { AdvancedSettings } from "./advanced_settings/AdvancedSettings";
export { Vr } from "./vr/Vr";
export { Hotkeys } from "./hotkeys/Hotkeys";
export { AboutVrct } from "./about_vrct/AboutVrct";
export { Supporters } from "./supporters/Supporters";

View File

@@ -10,6 +10,7 @@ export const SidebarSection = () => {
<Tab tab_id="transcription" />
<Tab tab_id="vr" />
<Tab tab_id="others" />
<Tab tab_id="hotkeys" />
<Tab tab_id="advanced_settings" />
</div>
<div className={styles.separated_tabs_wrapper}>

View File

@@ -1,9 +1,11 @@
import { useState, useEffect } from "react";
import { useState, useEffect, useLayoutEffect, useRef } from "react";
import styles from "./MessageInputBox.module.scss";
import SendMessageSvg from "@images/send_message.svg?react";
import { useMessage } from "@logics_common";
import { useSendMessageButtonType, useEnableAutoClearMessageInputBox } from "@logics_configs";
import { useMessageLogScroll } from "@logics_main";
import { store } from "@store";
import { appWindow } from "@tauri-apps/api/window";
export const MessageInputBox = () => {
const [message_history, setMessageHistory] = useState([]);
@@ -22,6 +24,12 @@ export const MessageInputBox = () => {
const { scrollToBottom } = useMessageLogScroll();
const log_box_ref = useRef(null);
useLayoutEffect(() => {
store.text_area_ref = log_box_ref;
}, []);
useEffect(() => {
if (currentMessageLogs.data) {
const sentMessages = currentMessageLogs.data
@@ -33,6 +41,7 @@ export const MessageInputBox = () => {
const onSubmitFunction = (e) => {
e.preventDefault();
// appWindow.minimize();
if (!currentMessageInputValue.data.trim()) return updateMessageInputValue("");
@@ -90,6 +99,7 @@ export const MessageInputBox = () => {
<div className={styles.container}>
<div className={styles.message_box_wrapper}>
<textarea
ref={log_box_ref}
className={styles.message_box_input_area}
onChange={onChangeFunction}
onBlur={stopTyping}

View File

@@ -0,0 +1,131 @@
import { appWindow } from "@tauri-apps/api/window";
import { store, useStore_Hotkeys } from "@store";
import { useStdoutToPython } from "@logics/useStdoutToPython";
import { useNotificationStatus } from "@logics_common";
import { useMainFunction } from "@logics_main";
import { register, unregisterAll, isRegistered } from "@tauri-apps/api/globalShortcut";
export const useHotkeys = () => {
const { asyncStdoutToPython } = useStdoutToPython();
const { currentHotkeys, updateHotkeys, pendingHotkeys } = useStore_Hotkeys();
const {
toggleTranslation,
toggleTranscriptionSend,
toggleTranscriptionReceive,
} = useMainFunction();
const getHotkeys = () => {
pendingHotkeys();
asyncStdoutToPython("/get/data/hotkeys");
};
const { showNotification_Success, 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 () => {
switch (actionKey) {
case "toggle_vrct_visibility": {
const minimized = await appWindow.isMinimized();
if (minimized) {
appWindow.unminimize();
await appWindow.setFocus();
store.text_area_ref.current?.focus();
} else {
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);
}
};
return {
currentHotkeys,
getHotkeys,
updateHotkeys,
setHotkeys,
registerShortcuts,
unregisterAll,
};
};
// 修飾キーのパースを行う関数
const parseHotkey = (hotkeyString) => {
const keyMap = {
Ctrl: "Control",
Alt: "Alt",
Shift: "Shift",
Meta: "Super",
};
return hotkeyString
.map((key) => keyMap[key] || key)
.join("+");
};

View File

@@ -51,6 +51,8 @@ export { useOverlayShowOnlyTranslatedMessages } from "./vr/useOverlayShowOnlyTra
export { useOverlayLargeLogSettings } from "./vr/useOverlayLargeLogSettings";
export { useSendTextToOverlay } from "./vr/useSendTextToOverlay";
export { useHotkeys } from "./hotkeys/useHotkeys";
export { useOscIpAddress } from "./advanced_settings/useOscIpAddress";
export { useOscPort } from "./advanced_settings/useOscPort";

View File

@@ -6,7 +6,7 @@ import {
useStore_TranscriptionReceiveStatus,
useStore_ForegroundStatus,
} from "@store";
import { useCallback } from "react";
import { useStdoutToPython } from "@logics/useStdoutToPython";
export const useMainFunction = () => {
@@ -40,7 +40,11 @@ export const useMainFunction = () => {
asyncStdoutToPython("/set/disable/translation");
}
};
const toggleTranslation = () => setTranslation(!currentTranslationStatus.data);
const toggleTranslation = () => {
updateTranslationStatus(prev_state => {
if (prev_state.state === "ok") setTranslation(!prev_state.data);
}, { set_state: "pending" });
};
const setTranscriptionSend = (to_enable) => {
pendingTranscriptionSendStatus();
@@ -50,7 +54,11 @@ export const useMainFunction = () => {
asyncStdoutToPython("/set/disable/transcription_send");
}
};
const toggleTranscriptionSend = () => setTranscriptionSend(!currentTranscriptionSendStatus.data);
const toggleTranscriptionSend = () => {
updateTranscriptionSendStatus(prev_state => {
if (prev_state.state === "ok") setTranscriptionSend(!prev_state.data);
}, { set_state: "pending" });
};
const setTranscriptionReceive = (to_enable) => {
pendingTranscriptionReceiveStatus();
@@ -60,7 +68,11 @@ export const useMainFunction = () => {
asyncStdoutToPython("/set/disable/transcription_receive");
}
};
const toggleTranscriptionReceive = () => setTranscriptionReceive(!currentTranscriptionReceiveStatus.data);
const toggleTranscriptionReceive = () => {
updateTranscriptionReceiveStatus(prev_state => {
if (prev_state.state === "ok") setTranscriptionReceive(!prev_state.data);
}, { set_state: "pending" });
};
const toggleForeground = () => {

View File

@@ -68,6 +68,7 @@ import {
useIsEnabledOverlayLargeLog,
useOverlayLargeLogSettings,
useOverlayShowOnlyTranslatedMessages,
useHotkeys,
useOscIpAddress,
useOscPort,
} from "@logics_configs";
@@ -168,6 +169,8 @@ export const useReceiveRoutes = () => {
const { updateIsEnabledOverlayLargeLog } = useIsEnabledOverlayLargeLog();
const { updateOverlayShowOnlyTranslatedMessages } = useOverlayShowOnlyTranslatedMessages();
const { updateHotkeys } = useHotkeys();
const { updateOscIpAddress } = useOscIpAddress();
const { updateOscPort } = useOscPort();
@@ -458,6 +461,10 @@ export const useReceiveRoutes = () => {
"/set/enable/send_received_message_to_vrc": updateEnableSendReceivedMessageToVrc,
"/set/disable/send_received_message_to_vrc": updateEnableSendReceivedMessageToVrc,
// Hotkeys
"/get/data/hotkeys": updateHotkeys,
"/set/data/hotkeys": updateHotkeys,
// Advanced Settings
"/get/data/osc_ip_address": updateOscIpAddress,
"/set/data/osc_ip_address": updateOscIpAddress,

View File

@@ -18,6 +18,7 @@ export const store = {
config_page: null,
setting_box_scroll_container: null,
log_box_ref: null,
text_area_ref: null,
is_applied_init_message_box_height: false,
};
@@ -54,20 +55,20 @@ const createAtomWithHook = (initialValue, base_name, options) => {
});
};
const updateAtom = (payload) => {
const updateAtom = (payload, options = {}) => {
const { remain_state = false, set_state } = options;
setAtom((currentValue) => {
if (typeof payload === "function") {
const updated_data = payload(currentValue);
const new_state = set_state ?? (remain_state ? currentValue.state : "ok");
const updated_data = typeof payload === "function"
? payload(currentValue)
: payload;
return {
state: "ok",
state: new_state,
data: updated_data,
};
} else {
return {
state: "ok",
data: payload,
};
}
});
};
@@ -263,6 +264,14 @@ export const { atomInstance: Atom_EnableVrcMicMuteSync, useHook: useStore_Enable
export const { atomInstance: Atom_EnableSendMessageToVrc, useHook: useStore_EnableSendMessageToVrc } = createAtomWithHook(true, "EnableSendMessageToVrc");
export const { atomInstance: Atom_EnableSendReceivedMessageToVrc, useHook: useStore_EnableSendReceivedMessageToVrc } = createAtomWithHook(false, "EnableSendReceivedMessageToVrc");
// Hotkeys
export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createAtomWithHook({
toggle_vrct_visibility: null,
toggle_translation: null,
toggle_transcription_send: null,
toggle_transcription_receive: null,
}, "Hotkeys");
// Advanced Settings
export const { atomInstance: Atom_OscIpAddress, useHook: useStore_OscIpAddress } = createAtomWithHook("127.0.0.1", "OscIpAddress");
export const { atomInstance: Atom_OscPort, useHook: useStore_OscPort } = createAtomWithHook("9000", "OscPort");