[Refactor] Move to src-ui/views and src-ui/logics structure.

This commit is contained in:
Sakamoto Shiina
2025-11-05 11:49:48 +09:00
parent 62f7c6d534
commit db820375f1
339 changed files with 19 additions and 19 deletions

View File

@@ -0,0 +1,74 @@
export const generateTestConversationData = (num) => {
const testDataArray = [];
const messagesJa = [
"今日はとてもいい天気ですね。",
"次の会議は何時に始まりますか?",
"新しいプロジェクトについて話しましょう。",
"お疲れ様です。今日は早く帰れますか?",
"この書類にサインをお願いします。",
"次の休暇はどこに行きますか?",
"先週のレポートを見ましたか?",
"この問題をどうやって解決しますか?",
"週末は何をする予定ですか?",
"新しいアイデアを聞かせてください。",
"こんにちは、調子はどうですか?",
"おはようございます、今日の予定は何ですか?",
"こんばんは、今日は楽しかったですか?",
"ありがとう、助かりました。",
"さようなら、また会いましょう。",
"はい、分かりました。",
"いいえ、ちょっと難しいです。",
"すみません、もう一度言ってください。",
"お願いします、手伝ってください。",
"お疲れ様です、今日も頑張りましょう。",
];
const messagesEn = [
"The weather is very nice today.",
"What time does the next meeting start?",
"Let's talk about the new project.",
"Good job today. Can you leave early today?",
"Please sign this document.",
"Where are you going for the next vacation?",
"Did you see last week's report?",
"How do we solve this problem?",
"What are your plans for the weekend?",
"Tell me about your new idea.",
"Hello, how are you?",
"Good morning, what are your plans for today?",
"Good evening, did you have a good day?",
"Thank you, that was helpful.",
"Goodbye, see you again.",
"Yes, understood.",
"No, it's a bit difficult.",
"Sorry, could you say that again?",
"Please, help me out.",
"Good job today, let's do our best again tomorrow.",
];
const statuses = ["sent", "received"];
for (let i = 0; i < num; i++) {
const uuid = crypto.randomUUID();
const date = new Date().toLocaleTimeString(
"ja-JP",
{ hour12: false, hour: "2-digit", minute: "2-digit" }
);
const messageIndex = Math.floor(Math.random() * messagesJa.length);
const status = statuses[Math.floor(Math.random() * statuses.length)];
const testData = {
id: uuid,
category: status,
status: status,
created_at: date,
messages: {
original: messagesJa[messageIndex],
translated: [
messagesEn[messageIndex],
],
},
};
testDataArray.push(testData);
}
return testDataArray;
};

View File

@@ -17,7 +17,7 @@ import {
useAdvancedSettings,
} from "@logics_configs";
import { ui_configs } from "../ui_configs";
import { ui_configs } from "./ui_configs";
export const _useBackendErrorHandling = () => {
const { t } = useI18n();

View File

@@ -0,0 +1,634 @@
import { createAtomWithHook } from "@store";
import {
ctranslate2_weight_type_status,
whisper_weight_type_status,
ui_configs,
} from "@ui_configs";
import {
useSettingsLogics,
useConfigFunctions,
} from "./useSettingsLogics";
export const SETTINGS_ARRAY = [
// Device
{
Category: "Device",
Base_Name: "EnableAutoMicSelect",
default_value: true,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "auto_mic_select",
},
{
Category: "Device",
Base_Name: "MicHostList",
default_value: [],
ui_template_id: "list",
logics_template_id: "get_set",
add_endpoint_run_array: ["from_backend"],
base_endpoint_name: "mic_host_list",
response_transform: "arrayToObject",
},
{
Category: "Device",
Base_Name: "MicDeviceList",
default_value: [],
ui_template_id: "list",
logics_template_id: "get_set",
add_endpoint_run_array: ["from_backend"],
base_endpoint_name: "mic_device_list",
response_transform: "arrayToObject",
},
{
Category: "Device",
Base_Name: "SelectedMicHost",
default_value: "",
ui_template_id: "select",
logics_template_id: "get_set",
add_endpoint_run_array: ["from_backend"],
base_endpoint_name: "selected_mic_host",
},
{
Category: "Device",
Base_Name: "SelectedMicDevice",
default_value: "",
ui_template_id: "select",
logics_template_id: "get_set",
add_endpoint_run_array: ["from_backend"],
base_endpoint_name: "selected_mic_device",
},
{
Category: "Device",
Base_Name: "EnableAutomaticMicThreshold",
default_value: true,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "mic_automatic_threshold",
},
{
Category: "Device",
Base_Name: "MicThreshold",
default_value: 0,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "mic_threshold",
},
{
Category: "Device",
Base_Name: "EnableAutoSpeakerSelect",
default_value: true,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "auto_speaker_select",
},
{
Category: "Device",
Base_Name: "SpeakerDeviceList",
default_value: [],
ui_template_id: "list",
logics_template_id: "get_set",
add_endpoint_run_array: ["from_backend"],
base_endpoint_name: "speaker_device_list",
response_transform: "arrayToObject",
},
{
Category: "Device",
Base_Name: "SelectedSpeakerDevice",
default_value: "",
ui_template_id: "select",
logics_template_id: "get_set",
add_endpoint_run_array: ["from_backend"],
base_endpoint_name: "selected_speaker_device",
},
{
Category: "Device",
Base_Name: "EnableAutomaticSpeakerThreshold",
default_value: true,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "speaker_automatic_threshold",
},
{
Category: "Device",
Base_Name: "SpeakerThreshold",
default_value: 0,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "speaker_threshold",
},
// Appearance
{
Category: "Appearance",
Base_Name: "UiLanguage",
default_value: "en",
ui_template_id: "select",
logics_template_id: "get_set",
base_endpoint_name: "ui_language",
},
{
Category: "Appearance",
Base_Name: "UiScaling",
default_value: 100,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "ui_scaling",
},
{
Category: "Appearance",
Base_Name: "MessageLogUiScaling",
default_value: 100,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "textbox_ui_scaling",
},
{
Category: "Appearance",
Base_Name: "SendMessageButtonType",
default_value: "primary",
ui_template_id: "select",
logics_template_id: "get_set",
base_endpoint_name: "send_message_button_type",
},
{
Category: "Appearance",
Base_Name: "ShowResendButton",
default_value: true,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "show_resend_button",
},
{
Category: "Appearance",
Base_Name: "SelectedFontFamily",
default_value: "Yu Gothic UI",
ui_template_id: "select",
logics_template_id: "get_set",
base_endpoint_name: "font_family",
},
{
Category: "Appearance",
Base_Name: "Transparency",
default_value: 100,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "transparency",
},
// Translation
// CTranslate2/Whisper weights
{
Category: "Translation",
Base_Name: "CTranslate2WeightTypeStatus",
default_value: ctranslate2_weight_type_status,
ui_template_id: "list",
logics_template_id: "weight_download_status",
base_endpoint_name: "ctranslate2_weight",
},
{
Category: "Translation",
Base_Name: "SelectedCTranslate2WeightType",
default_value: "",
ui_template_id: "select",
logics_template_id: "get_set",
base_endpoint_name: "ctranslate2_weight_type",
},
{
Category: "Translation",
Base_Name: "SelectedTranslationComputeType",
default_value: "",
ui_template_id: "select",
logics_template_id: "get_set",
base_endpoint_name: "selected_translation_compute_type",
},
{
Category: "Translation",
Base_Name: "SelectableTranslationComputeDeviceList",
default_value: [],
ui_template_id: "list",
logics_template_id: "get_set",
base_endpoint_name: "translation_compute_device_list",
response_transform: "transformToIndexedArray",
},
{
Category: "Translation",
Base_Name: "SelectedTranslationComputeDevice",
default_value: "",
ui_template_id: "select",
logics_template_id: "get_set",
base_endpoint_name: "selected_translation_compute_device",
},
{
Category: "Translation",
Base_Name: "DeepLAuthKey",
default_value: "",
ui_template_id: "input",
logics_template_id: "get_set",
base_endpoint_name: "deepl_auth_key",
},
// Transcription
// Mic
{
Category: "Transcription",
Base_Name: "MicRecordTimeout",
default_value: 0,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "mic_record_timeout",
},
{
Category: "Transcription",
Base_Name: "MicPhraseTimeout",
default_value: 0,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "mic_phrase_timeout",
},
{
Category: "Transcription",
Base_Name: "MicMaxWords",
default_value: 0,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "mic_max_phrases",
},
{
Category: "Transcription",
Base_Name: "MicWordFilterList",
default_value: [],
ui_template_id: "list",
logics_template_id: "get_set",
base_endpoint_name: "mic_word_filter",
},
// Speaker
{
Category: "Transcription",
Base_Name: "SpeakerRecordTimeout",
default_value: 0,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "speaker_record_timeout",
},
{
Category: "Transcription",
Base_Name: "SpeakerPhraseTimeout",
default_value: 0,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "speaker_phrase_timeout",
},
{
Category: "Transcription",
Base_Name: "SpeakerMaxWords",
default_value: 0,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "speaker_max_phrases",
},
// Engines
{
Category: "Transcription",
Base_Name: "SelectedTranscriptionEngine",
default_value: "",
ui_template_id: "select",
logics_template_id: "get_set",
base_endpoint_name: "selected_transcription_engine",
},
{
Category: "Transcription",
Base_Name: "WhisperWeightTypeStatus",
default_value: whisper_weight_type_status,
ui_template_id: "list",
logics_template_id: "weight_download_status",
base_endpoint_name: "whisper_weight",
},
{
Category: "Transcription",
Base_Name: "SelectedWhisperWeightType",
default_value: "",
ui_template_id: "select",
logics_template_id: "get_set",
base_endpoint_name: "whisper_weight_type",
},
{
Category: "Transcription",
Base_Name: "SelectedTranscriptionComputeType",
default_value: "",
ui_template_id: "select",
logics_template_id: "get_set",
base_endpoint_name: "selected_transcription_compute_type",
},
{
Category: "Transcription",
Base_Name: "SelectableTranscriptionComputeDeviceList",
default_value: [],
ui_template_id: "list",
logics_template_id: "get_set",
base_endpoint_name: "transcription_compute_device_list",
response_transform: "transformToIndexedArray",
},
{
Category: "Transcription",
Base_Name: "SelectedTranscriptionComputeDevice",
default_value: "",
ui_template_id: "select",
logics_template_id: "get_set",
base_endpoint_name: "selected_transcription_compute_device",
},
// Advanced
{
Category: "Transcription",
Base_Name: "MicAvgLogprob",
default_value: 0,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "mic_avg_logprob",
},
{
Category: "Transcription",
Base_Name: "MicNoSpeechProb",
default_value: 0,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "mic_no_speech_prob",
},
{
Category: "Transcription",
Base_Name: "SpeakerAvgLogprob",
default_value: 0,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "speaker_avg_logprob",
},
{
Category: "Transcription",
Base_Name: "SpeakerNoSpeechProb",
default_value: 0,
ui_template_id: "slider",
logics_template_id: "get_set",
base_endpoint_name: "speaker_no_speech_prob",
},
// Vr
{
Category: "Vr",
Base_Name: "IsEnabledOverlaySmallLog",
default_value: false,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "overlay_small_log",
},
{
Category: "Vr",
Base_Name: "OverlaySmallLogSettings",
default_value: ui_configs.overlay_small_log_default_settings,
ui_template_id: "object",
logics_template_id: "get_set",
base_endpoint_name: "overlay_small_log_settings",
},
{
Category: "Vr",
Base_Name: "IsEnabledOverlayLargeLog",
default_value: false,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "overlay_large_log",
},
{
Category: "Vr",
Base_Name: "OverlayLargeLogSettings",
default_value: ui_configs.overlay_large_log_default_settings,
ui_template_id: "object",
logics_template_id: "get_set",
base_endpoint_name: "overlay_large_log_settings",
},
{
Category: "Vr",
Base_Name: "OverlayShowOnlyTranslatedMessages",
default_value: false,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "overlay_show_only_translated_messages",
},
// Others
{
Category: "Others",
Base_Name: "EnableAutoClearMessageInputBox",
default_value: false,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "auto_clear_message_box",
},
{
Category: "Others",
Base_Name: "EnableSendOnlyTranslatedMessages",
default_value: false,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "send_only_translated_messages",
},
{
Category: "Others",
Base_Name: "EnableAutoExportMessageLogs",
default_value: false,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "logger_feature",
},
{
Category: "Others",
Base_Name: "EnableVrcMicMuteSync",
default_value: false,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "vrc_mic_mute_sync",
},
{
Category: "Others",
Base_Name: "EnableSendMessageToVrc",
default_value: false,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "send_message_to_vrc",
},
{
Category: "Others",
Base_Name: "EnableNotificationVrcSfx",
default_value: false,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "notification_vrc_sfx",
},
{
Category: "Others",
Base_Name: "EnableSendReceivedMessageToVrc",
default_value: false,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "send_received_message_to_vrc",
},
{
Category: "Others",
Base_Name: "SendMessageFormatParts",
default_value: [],
ui_template_id: "list",
logics_template_id: "get_set",
base_endpoint_name: "send_message_format_parts",
},
{
Category: "Others",
Base_Name: "ReceivedMessageFormatParts",
default_value: [],
ui_template_id: "list",
logics_template_id: "get_set",
base_endpoint_name: "received_message_format_parts",
},
{
Category: "Others",
Base_Name: "ConvertMessageToRomaji",
default_value: false,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "convert_message_to_romaji",
},
{
Category: "Others",
Base_Name: "ConvertMessageToHiragana",
default_value: false,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "convert_message_to_hiragana",
},
// AdvancedSettings
{
Category: "AdvancedSettings",
Base_Name: "OscIpAddress",
default_value: "127.0.0.1",
ui_template_id: "input",
logics_template_id: "get_set",
base_endpoint_name: "osc_ip_address",
},
{
Category: "AdvancedSettings",
Base_Name: "OscPort",
default_value: 9000,
ui_template_id: "input",
logics_template_id: "get_set",
base_endpoint_name: "osc_port",
},
{
Category: "AdvancedSettings",
Base_Name: "EnableWebsocket",
default_value: false,
ui_template_id: "toggle",
logics_template_id: "toggle_enable_disable",
base_endpoint_name: "websocket_server",
},
{
Category: "AdvancedSettings",
Base_Name: "WebsocketHost",
default_value: "127.0.0.1",
ui_template_id: "input",
logics_template_id: "get_set",
base_endpoint_name: "websocket_host",
},
{
Category: "AdvancedSettings",
Base_Name: "WebsocketPort",
default_value: 9001,
ui_template_id: "input",
logics_template_id: "get_set",
base_endpoint_name: "websocket_port",
},
];
for (const setting_data of SETTINGS_ARRAY) {
createAtomWithHook(setting_data.default_value, setting_data.Base_Name);
}
const buildCategoryApiFromSettings = (settings, settingsArray, Category, extraFunctions = {}) => {
const api = {};
const filtered = settingsArray.filter((s) => s.Category === Category);
for (const s of filtered) {
const base = s.Base_Name;
const currentKey = `current${base}`;
const updateKey = `update${base}`;
const getKey = `get${base}`;
const setKey = `set${base}`;
const toggleKey = `toggle${base}`;
const setSuccessKey = `setSuccess${base}`;
const updateFromBackendKey = `updateFromBackend${base}`;
if (settings[currentKey] !== undefined) api[currentKey] = settings[currentKey];
if (settings[updateKey] !== undefined) api[updateKey] = settings[updateKey];
if (typeof settings[getKey] === "function") api[getKey] = settings[getKey];
if (typeof settings[setKey] === "function") api[setKey] = settings[setKey];
if (typeof settings[toggleKey] === "function") api[toggleKey] = settings[toggleKey];
if (typeof settings[setSuccessKey] === "function") api[setSuccessKey] = settings[setSuccessKey];
if (typeof settings[updateFromBackendKey] === "function") api[updateFromBackendKey] = settings[updateFromBackendKey];
if (s.logics_template_id === "weight_download_status") {
const updateDownloadProgressKey = `updateDownloadProgress${base}`;
const updateDownloadedKey = `updateDownloaded${base}`;
const pendingKey = `pending${base}`;
const downloadedKey = `downloaded${base}`;
const downloadKey = `download${base}`;
if (typeof settings[updateDownloadProgressKey] === "function") api[updateDownloadProgressKey] = settings[updateDownloadProgressKey];
if (typeof settings[updateDownloadedKey] === "function") api[updateDownloadedKey] = settings[updateDownloadedKey];
if (typeof settings[pendingKey] === "function") api[pendingKey] = settings[pendingKey];
if (typeof settings[downloadedKey] === "function") api[downloadedKey] = settings[downloadedKey];
if (typeof settings[downloadKey] === "function") api[downloadKey] = settings[downloadKey];
if (typeof settings[updateFromBackendKey] === "function") api[updateFromBackendKey] = settings[updateFromBackendKey];
}
}
return { ...api, ...extraFunctions };
};
const createCategoryHook = (Category) => {
return () => {
const { settings } = useSettingsLogics(SETTINGS_ARRAY, Category);
const extraFunctions = useConfigFunctions(Category);
const autoApi = buildCategoryApiFromSettings(settings, SETTINGS_ARRAY, Category, extraFunctions);
return { ...autoApi };
};
};
// --- 自動エクスポート: SETTINGS_ARRAY に含まれるユニークな Category ごとに use<Category> を作って export ---
// 例: Category === "Appearance" -> export const useAppearance = createCategoryHook("Appearance");
// const uniqueCategories = Array.from(new Set(SETTINGS_ARRAY.map((s) => s.Category)));
// 動的に named export を作る(静的解析を壊さないために明示的に定義)
/* eslint-disable import/prefer-default-export */
export const useAppearance = createCategoryHook("Appearance");
export const useDevice = createCategoryHook("Device");
export const useTranslation = createCategoryHook("Translation");
export const useTranscription = createCategoryHook("Transcription");
export const useVr = createCategoryHook("Vr");
export const useOthers = createCategoryHook("Others");
// export const useHotkeys = createCategoryHook("Hotkeys");
export const useAdvancedSettings = createCategoryHook("AdvancedSettings");
// If you later add other categories, you can either manually add:
// export const useDevice = createCategoryHook("Device");
// or uncomment the code below to auto-attach to module.exports (less ideal for tree-shaking).
//
// Auto-attach (not recommended for tree-shaking in bundlers):
// uniqueCategories.forEach((Category) => {
// const hookName = `use${Category}`;
// module.exports[hookName] = createCategoryHook(Category);
// });
/* eslint-enable import/prefer-default-export */

View File

@@ -0,0 +1,203 @@
import * as stores from "@store";
import { useStdoutToPython } from "@useStdoutToPython";
import { useNotificationStatus } from "@logics_common";
import { arrayToObject } from "@utils";
const transformResponse = (transformName, payload) => {
if (!transformName) return payload;
switch (transformName) {
case "arrayToObject":
return arrayToObject(payload);
default:
return payload;
}
};
export const useSettingsLogics = (settingsArray, Category) => {
const { asyncStdoutToPython } = useStdoutToPython();
const { showNotification_SaveSuccess } = useNotificationStatus();
const filtered = settingsArray.filter((s) => s.Category === Category);
const result = {};
for (const s of filtered) {
const base = s.Base_Name;
let storeHook = undefined;
if (typeof stores.getStoreHook === "function") {
storeHook = stores.getStoreHook(base);
}
if (!storeHook) {
const hookName = `useStore_${base}`;
storeHook = stores[hookName];
}
if (!storeHook) {
console.warn(`[useSettingsLogics] store hook not found for ${base}`);
continue;
}
const store = storeHook();
const currentKey = `current${base}`;
const updateKey = `update${base}`;
const pendingKey = `pending${base}`;
const current = store[currentKey];
const update = store[updateKey];
const pending = store[pendingKey];
const currentExportName = `current${base}`;
const updateExportName = `update${base}`;
const updateFromBackendExportName = `updateFromBackend${base}`;
const getExportName = `get${base}`;
const setExportName = `set${base}`;
const toggleExportName = `toggle${base}`;
const setSuccessExportName = `setSuccess${base}`;
const runExportName = `runSuccess${base}`;
// To use by UI------------------------------------
const buildGet = () => {
return () => {
if (pending) pending();
asyncStdoutToPython(`/get/data/${s.base_endpoint_name}`);
};
};
const buildSet = () => {
return (value) => {
if (pending) pending();
asyncStdoutToPython(`/set/data/${s.base_endpoint_name}`, value);
};
};
const buildRun = () => {
return () => {
asyncStdoutToPython(`/run/${s.base_endpoint_name}`);
};
};
// To use a response from backend------------------------------------
const buildSetSuccess = (transformName) => {
return (payload) => {
const transformed = transformResponse(transformName, payload);
if (update) update(transformed);
showNotification_SaveSuccess();
};
};
const buildUpdateFromBackend = (transformName) => {
return (payload) => {
const transformed = transformResponse(transformName, payload);
if (update) update(transformed);
};
};
result[currentExportName] = current;
result[updateExportName] = update;
result[updateFromBackendExportName] = buildUpdateFromBackend(s.response_transform ?? null);
if (s.add_endpoint_run_array?.includes("to_backend")) {
result[runExportName] = buildRun();
}
if (s.logics_template_id === "get_list") {
result[getExportName] = buildGet();
continue;
}
if (s.logics_template_id === "get_set") {
result[getExportName] = buildGet();
result[setExportName] = buildSet();
result[setSuccessExportName] = buildSetSuccess(s.response_transform ?? null);
continue;
}
if (s.logics_template_id === "toggle_enable_disable") {
result[getExportName] = buildGet();
result[toggleExportName] = () => {
if (pending) pending();
const isOn = current && current.data;
if (isOn) {
asyncStdoutToPython(`/set/disable/${s.base_endpoint_name}`);
} else {
asyncStdoutToPython(`/set/enable/${s.base_endpoint_name}`);
}
};
result[setSuccessExportName] = buildSetSuccess(s.response_transform ?? null);
continue;
}
if (s.logics_template_id === "weight_download_status") {
result[setSuccessExportName] = buildSetSuccess(s.response_transform ?? null);
result[`updateDownloadProgress${base}`] = (payload) => {
update((old_status) => {
return old_status.data.map((item) =>
payload.weight_type === item.id
? { ...item, progress: payload.progress * 100 }
: item
);
});
};
result[`updateDownloaded${base}`] = (downloaded_weight_type_status) => {
update((old_status) => {
return old_status.data.map((item) => ({
...item,
is_downloaded: downloaded_weight_type_status[item.id] ?? item.is_downloaded,
}));
});
};
result[`pending${base}`] = (id) => {
update((old_status) => {
return old_status.data.map((item) =>
id === item.id
? { ...item, is_pending: true }
: item
);
});
};
result[`downloaded${base}`] = (id) => {
update((old_status) => {
return old_status.data.map((item) =>
id === item.id
? { ...item, is_downloaded: true, is_pending: false, progress: null }
: item
);
});
};
result[`download${base}`] = (weight_type) => {
asyncStdoutToPython(`/run/download_${s.base_endpoint_name}`, weight_type);
};
continue;
}
}
return { settings: result };
};
export const useConfigFunctions = (Category) => {
const { asyncStdoutToPython } = useStdoutToPython();
switch (Category) {
case "Vr":
return {
sendTextToOverlay: (text) => {
asyncStdoutToPython("/run/send_text_overlay", text);
},
};
default:
return {};
}
};

View File

@@ -10,7 +10,7 @@ export {
useVr,
// useHotkeys,
useAdvancedSettings,
} from "../../ui_config_setter.js";
} from "./config_page_setter/ui_config_setter.js";
// export { useOthers } from "./others/useOthers";
// export { useTranscription } from "./transcription/useTranscription";

247
src-ui/logics/store.js Normal file
View File

@@ -0,0 +1,247 @@
import {
atom,
useAtomValue,
useSetAtom
} from "jotai";
import {
generateTestConversationData,
} from "./_test_data.js"
import {
translator_status,
} from "@ui_configs";
export const store = {
backend_subprocess: null,
setting_box_scroll_container: null,
log_box_ref: null,
text_area_ref: null,
is_initialized_load_plugin: false,
is_fetched_plugins_info_already: false,
is_initialized_fetched_plugin_info: false,
last_executed_time_startTyping: 0,
};
const generatePropertyNames = (base_name) => ({
error: `error${base_name}`,
pending: `pending${base_name}`,
current: `current${base_name}`,
update: `update${base_name}`,
add: `add${base_name}`,
});
export const dynamicStoreRegistry = {};
export const createAtomWithHook = (initialValue, base_name, options) => {
const property_names = generatePropertyNames(base_name);
const atomInstance = atom({
state: (options?.is_state_ok) ? "ok" : "pending",
data: initialValue,
});
const useHook = () => {
const currentAtom = useAtomValue(atomInstance);
const setAtom = useSetAtom(atomInstance);
const pendingAtom = () => {
setAtom((old_value) => {
let new_value = {
state: "pending",
data: old_value.data,
};
return new_value;
});
};
const updateAtom = (payload, options = {}) => {
const { remain_state = false, set_state, lock_state } = options;
setAtom((currentValue) => {
let new_state;
if (lock_state) {
new_state = set_state;
} else {
if (currentValue.lock_state) {
new_state = currentValue.state;
} else {
new_state = set_state ?? (remain_state ? currentValue.state : "ok");
}
}
const updated_data = typeof payload === "function"
? payload(currentValue)
: payload;
return {
state: new_state,
data: updated_data,
};
});
};
const errorAtom = () => {
setAtom((old_value) => {
let new_value = {
state: "error",
data: old_value.data,
};
return new_value;
});
};
const addAtom = (value) => {
setAtom((old_value) => {
return {
state: "ok",
data: [...old_value.data, value],
};
});
};
return {
[property_names.error]: errorAtom,
[property_names.pending]: pendingAtom,
[property_names.current]: currentAtom,
[property_names.update]: updateAtom,
[property_names.add]: addAtom,
};
};
try {
const hookName = `useStore_${base_name}`;
const atomName = `Atom_${base_name}`;
dynamicStoreRegistry[hookName] = useHook;
dynamicStoreRegistry[atomName] = atomInstance;
} catch (e) {
console.warn("dynamic registration failed for", base_name, e);
}
return { atomInstance, useHook };
};
export const getStoreHook = (baseName) => {
const hookName = `useStore_${baseName}`;
return dynamicStoreRegistry[hookName];
};
export const registerMany = (settingsArray = []) => {
for (const s of settingsArray) {
try {
const hookName = `useStore_${s.Base_Name}`;
if (dynamicStoreRegistry[hookName]) {
continue;
}
createAtomWithHook(s.default_value, s.Base_Name, s.options || {});
} catch (e) {
console.warn("registerMany failed for", s.Base_Name, e);
}
}
};
// Common
export const { atomInstance: Atom_IsBackendReady, useHook: useStore_IsBackendReady } = createAtomWithHook(false, "IsBackendReady");
export const { atomInstance: Atom_IsVrctAvailable, useHook: useStore_IsVrctAvailable } = createAtomWithHook(true, "IsVrctAvailable");
export const { atomInstance: Atom_ComputeMode, useHook: useStore_ComputeMode } = createAtomWithHook("", "ComputeMode");
export const { atomInstance: Atom_IsOpenedConfigPage, useHook: useStore_IsOpenedConfigPage } = createAtomWithHook(false, "IsOpenedConfigPage");
export const { atomInstance: Atom_MainFunctionsStateMemory, useHook: useStore_MainFunctionsStateMemory } = createAtomWithHook({
transcription_send: false,
transcription_receive: false,
}, "MainFunctionsStateMemory");
export const { atomInstance: Atom_OpenedQuickSetting, useHook: useStore_OpenedQuickSetting } = createAtomWithHook("", "OpenedQuickSetting");
export const { atomInstance: Atom_LatestSoftwareVersionInfo, useHook: useStore_LatestSoftwareVersionInfo } = createAtomWithHook({
is_update_available: false,
new_version: "0.0.0",
}, "LatestSoftwareVersionInfo");
export const { atomInstance: Atom_InitProgress, useHook: useStore_InitProgress } = createAtomWithHook(0, "InitProgress");
export const { atomInstance: Atom_IsBreakPoint, useHook: useStore_IsBreakPoint } = createAtomWithHook(false, "IsBreakPoint");
export const { atomInstance: Atom_IsSoftwareUpdating, useHook: useStore_IsSoftwareUpdating } = createAtomWithHook(false, "IsSoftwareUpdating");
export const { atomInstance: Atom_NotificationStatus, useHook: useStore_NotificationStatus } = createAtomWithHook({
status: "",
is_open: false,
key: 0,
message: "",
}, "NotificationStatus");
// Main Page
// 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_SelectedPresetTabNumber, useHook: useStore_SelectedPresetTabNumber } = createAtomWithHook("1", "SelectedPresetTabNumber");
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");
// 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(generateTestConversationData(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");
// Config Page
// Common
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");
export const { atomInstance: Atom_IsOpenedDropdownMenu, useHook: useStore_IsOpenedDropdownMenu } = createAtomWithHook("", "IsOpenedDropdownMenu");
// Device
export const { atomInstance: Atom_MicVolume, useHook: useStore_MicVolume } = createAtomWithHook(0, "MicVolume");
export const { atomInstance: Atom_SpeakerVolume, useHook: useStore_SpeakerVolume } = createAtomWithHook(0, "SpeakerVolume");
export const { atomInstance: Atom_MicThresholdCheckStatus, useHook: useStore_MicThresholdCheckStatus } = createAtomWithHook(false, "MicThresholdCheckStatus", {is_state_ok: true});
export const { atomInstance: Atom_SpeakerThresholdCheckStatus, useHook: useStore_SpeakerThresholdCheckStatus } = createAtomWithHook(false, "SpeakerThresholdCheckStatus", {is_state_ok: true});
export const { atomInstance: Atom_SelectableFontFamilyList, useHook: useStore_SelectableFontFamilyList } = createAtomWithHook({}, "SelectableFontFamilyList");
export const { atomInstance: Atom_IsOpenedMicWordFilterList, useHook: useStore_IsOpenedMicWordFilterList } = createAtomWithHook(false, "IsOpenedMicWordFilterList");
export const { atomInstance: Atom_MessageFormat_ExampleViewFilter, useHook: useStore_MessageFormat_ExampleViewFilter } = createAtomWithHook({
send: "Simplified",
received: "Simplified",
}, "MessageFormat_ExampleViewFilter");
// 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");
// Plugins
export const { atomInstance: Atom_FetchedPluginsInfo, useHook: useStore_FetchedPluginsInfo } = createAtomWithHook([], "FetchedPluginsInfo");
export const { atomInstance: Atom_LoadedPlugins, useHook: useStore_LoadedPlugins } = createAtomWithHook([], "LoadedPlugins");
export const { atomInstance: Atom_SavedPluginsStatus, useHook: useStore_SavedPluginsStatus } = createAtomWithHook([], "SavedPluginsStatus");
export const { atomInstance: Atom_PluginsData, useHook: useStore_PluginsData } = createAtomWithHook([], "PluginsData");
// 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");

132
src-ui/logics/ui_configs.js Normal file
View File

@@ -0,0 +1,132 @@
export const ui_configs = {
mic_threshold_min: 0,
mic_threshold_max: 2000,
speaker_threshold_min: 0,
speaker_threshold_max: 4000,
overlay_small_log: {
x_pos: { step: 0.05, min: -0.5, max: 0.5 },
y_pos: { step: 0.05, min: -0.8, max: 0.8 },
z_pos: { step: 0.05, min: -0.5, max: 1.5 },
x_rotation: { min: -180, max: 180, step: 5 },
y_rotation: { min: -180, max: 180, step: 5 },
z_rotation: { min: -180, max: 180, step: 5 },
ui_scaling: { step: 10, min: 40, max: 200 },
},
overlay_large_log: {
x_pos: { step: 0.05, min: -0.5, max: 0.5 },
y_pos: { step: 0.05, min: -0.8, max: 0.8 },
z_pos: { step: 0.05, min: -0.5, max: 1.5 },
x_rotation: { min: -180, max: 180, step: 5 },
y_rotation: { min: -180, max: 180, step: 5 },
z_rotation: { min: -180, max: 180, step: 5 },
ui_scaling: { step: 10, min: 40, max: 200 },
},
overlay_small_log_default_settings: {
x_pos: 0.0,
y_pos: 0.0,
z_pos: 0.0,
x_rotation: 0.0,
y_rotation: 0.0,
z_rotation: 0.0,
display_duration: 5,
fadeout_duration: 2,
opacity: 1.0,
ui_scaling: 1.0,
tracker: "HMD",
},
overlay_large_log_default_settings: {
x_pos: 0.0,
y_pos: 0.0,
z_pos: 0.0,
x_rotation: 0.0,
y_rotation: 0.0,
z_rotation: 0.0,
display_duration: 5,
fadeout_duration: 2,
opacity: 1.0,
ui_scaling: 1.0,
tracker: "LeftHand",
},
send_message_format_parts: {
message: {
prefix: "",
suffix: ""
},
separator: "\n",
translation: {
prefix: "",
separator: "\n",
suffix: ""
},
translation_first: false,
},
received_message_format_parts: {
message: {
prefix: "",
suffix: ""
},
separator: "\n",
translation: {
prefix: "",
separator: "\n",
suffix: ""
},
translation_first: false,
},
selectable_ui_languages: [
{id: "en", label: "English"},
{id: "ja", label: "日本語"},
{id: "ko", label: "한국어"},
{id: "zh-Hant", label: "繁體中文"},
{id: "zh-Hans", label: "简体中文"},
]
};
// true: src-ui\plugins false: src-tauri\target\debug\plugins
export const IS_PLUGIN_PATH_DEV_MODE = false;
// true: dev_vrct_plugins_list.json false: vrct_plugins_list.json
export const IS_PLUGIN_LIST_URL_DEV_MODE = false;
export const getPluginsList = () => {
const base_url = "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/";
const plugins_list_url = (IS_PLUGIN_LIST_URL_DEV_MODE)
? base_url + "dev_vrct_plugins_list.json"
: base_url + "vrct_plugins_list.json";
return plugins_list_url;
};
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: "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 },
];
export const ctranslate2_weight_type_status = [
{ id: "m2m100_418M-ct2-int8", capacity: "418MB"},
{ id: "m2m100_1.2B-ct2-int8", capacity: "1.2GB"},
{ id: "nllb-200-distilled-1.3B-ct2-int8", capacity: "1.3GB"},
{ id: "nllb-200-3.3B-ct2-int8", capacity: "3.3GB"},
].map(item => ({ ...item, is_downloaded: false, progress: null }));
export const whisper_weight_type_status = [
{ id: "tiny", capacity: "74.5MB"},
{ id: "base", capacity: "141MB"},
{ id: "small", capacity: "463MB"},
{ id: "medium", capacity: "1.42GB"},
{ id: "large-v1", capacity: "2.87GB"},
{ id: "large-v2", capacity: "2.87GB"},
{ id: "large-v3", capacity: "2.87GB"},
{ id: "large-v3-turbo-int8", capacity: "794MB"},
{ id: "large-v3-turbo", capacity: "1.58GB"},
].map(item => ({ ...item, is_downloaded: false, progress: null }));
export const supporters_data_url = "https://shiinasakamoto.github.io/vrct_supporters/assets/supporters/data.json";
export const supporters_images_url = "https://ShiinaSakamoto.github.io/vrct_supporters/assets/supporters";

View File

@@ -2,7 +2,7 @@ import * as common from "@logics_common";
import * as main from "@logics_main";
import * as configs from "@logics_configs";
import { _useBackendErrorHandling } from "./_useBackendErrorHandling";
import { SETTINGS_ARRAY } from "../ui_config_setter";
import { SETTINGS_ARRAY } from "./configs/config_page_setter/ui_config_setter";
export const STATIC_ROUTE_META_LIST = [
// Common

68
src-ui/logics/utils.js Normal file
View File

@@ -0,0 +1,68 @@
export const arrayToObject = (array) => {
return array.reduce((obj, item) => {
obj[item] = item;
return obj;
}, {});
};
export const chunkArray = (array, size) => {
const chunked = [];
for (let i = 0; i < array.length; i += size) {
chunked.push(array.slice(i, i + size));
}
return chunked;
};
export const clampMinMax = (value, min, max) => {
return Math.min(Math.max(value, min), max);
};
// console.log(clamp(5, 1, 10)); // 5 (範囲内)
// console.log(clamp(-3, 0, 10)); // 0 (minより小さい)
// console.log(clamp(15, 1, 10)); // 10 (maxより大きい)
// console.log(clamp(7.5, 1, 10)); // 7.5 (範囲内、少数)
export const randomIntMinMax = (min, max) => {
if (min === max) return min;
if (max === undefined) {
max = min;
min = 0;
}
const int = Math.floor(Math.random() * (max - min + 1)) + min;
return int;
};
export const randomMinMax = (min, max) => {
return Math.random() * (max - min) + min;
};
export const shuffleArray = (array) => {
const new_array = [...array];
for (let i = new_array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[new_array[i], new_array[j]] = [new_array[j], new_array[i]];
}
return new_array;
};
export const updateLabelsById = (data_array, updates) => {
return data_array.map(item => {
const update = updates.find(update_item => update_item.id === item.id);
return update ? { ...item, label: update.label } : item;
});
};
export const genNumArray = (count, start_from = 0) => {
return [...Array(count).keys()].map(i => i + start_from);
};
export const genNumObjArray = (count, start_from = 0) => {
return arrayToObject(genNumArray(count, start_from));
};
// This is using for only AI models compute device list, currently. (CTranslate2, Whisper)
export const transformToIndexedArray = (devices) => {
return devices.reduce((result, device, index) => {
result[index] = device;
return result;
}, {});
};