[Update] Config Page: Translation Tab. Add Internal Translation Weight Type Selector. Downloadable.

This commit is contained in:
Sakamoto Shiina
2024-11-12 13:02:05 +09:00
parent 43b061b007
commit 36c53c49a2
17 changed files with 365 additions and 139 deletions

View File

@@ -0,0 +1,79 @@
import { useState, useEffect } from "react";
import clsx from "clsx";
import CircularProgress from '@mui/material/CircularProgress';
import styles from "./DownloadModels.module.scss";
import {
RadioButton,
// DownloadModels,
} from "../index";
export const DownloadModels = (props) => {
const options = props.options.map(item => ({
...item,
disabled: !item.is_downloaded
}));
return (
<>
<RadioButton
selectFunction={props.selectFunction}
name={props.name}
options={options}
checked_variable={props.checked_variable}
column={true}
ChildComponent={ModelSelector}
downloadStartFunction={props.downloadStartFunction}
/>
</>
// <div className={styles.container}>
// {props.models.map((option) => (
// <ModelSelector key={option.model_id} option={option} {...props}/>
// ))}
// </div>
);
};
const ModelSelector = ({option, ...props}) => {
const [circular_color, setCircularColor] = useState("");
const [circular_color_2, setCircularColor2] = useState("");
useEffect(() => {
const circular_color = getComputedStyle(document.documentElement).getPropertyValue("--dark_600_color");
setCircularColor(circular_color.trim());
const circular_color_2 = getComputedStyle(document.documentElement).getPropertyValue("--primary_300_color");
setCircularColor2(circular_color_2.trim());
}, []);
const renderContent = () => {
const circular_progress = Math.floor(option.progress / 10) * 10;
switch (true) {
case option.progress !== null:
return (
<>
<CircularProgress
variant={(option.progress === 100) ? "indeterminate" : "determinate"}
value={circular_progress}
size="3rem"
sx={{ color: circular_color_2 }}
/>
<p className={styles.progress_label}>{`${Math.round(option.progress)}%`}</p>
</>
);
case option.is_pending:
return <CircularProgress size="3rem" sx={{ color: circular_color }}/>;
case !option.is_downloaded:
return (
<button
className={styles.download_button}
onClick={() => props.downloadStartFunction(option.id)}
>
<p className={styles.download_button_label}>Download</p>
</button>
);
default:
return null;
}
};
return <div className={styles.download_container}>{renderContent()}</div>;
};

View File

@@ -0,0 +1,32 @@
@import "@scss_mixins";
.download_container {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
max-width: 8rem;
}
.download_button {
pointer-events: auto;
background-color: var(--dark_800_color);
padding: 0.8rem;
flex-shrink: 0;
&:hover {
background-color: var(--dark_750_color);
}
&:active {
background-color: var(--dark_800_color);
}
}
.download_button_label {
font-size: 1.2rem;
color: var(--dark_basic_text_color);
}
.progress_label {
position: absolute;
font-size: 1rem;
color: var(--dark_basic_text_color);
}

View File

@@ -8,4 +8,5 @@ export { RadioButton } from "./radio_button/RadioButton";
export { Slider } from "./slider/Slider";
export { SwitchBox } from "./switch_box/SwitchBox";
export { ThresholdComponent } from "./threshold_component/ThresholdComponent";
export { WordFilter, WordFilterListToggleComponent } from "./word_filter/WordFilter";
export { WordFilter, WordFilterListToggleComponent } from "./word_filter/WordFilter";
export { DownloadModels } from "./download_models/DownloadModels";

View File

@@ -2,7 +2,7 @@
display: flex;
flex-direction: column;
gap: 0.4rem;
flex-shrink: 0;
// flex-shrink: 0;
}
.label {

View File

@@ -1,33 +1,42 @@
import styles from "./RadioButton.module.scss";
import clsx from "clsx";
export const RadioButton = (props) => {
const containerClass = clsx(styles.container, {
[styles.column]: props.column === true,
});
return (
<div className={styles.container}>
{props.options.map((option) => (
<label key={option.radio_button_id} className={styles.radio_button_wrapper}>
{props.checked_variable.data === option.radio_button_id
? <>
{ props.checked_variable.state === "pending" && <span className={styles.loader}></span> }
<div className={containerClass}>
{props.checked_variable.state === "pending" && <span className={styles.loader}></span>}
{props.options.map((option) => {
const radioWrapperClass = clsx(styles.radio_button_container, {
[styles.is_selected]: props.checked_variable.data === option.id,
});
const labelClass = clsx(styles.radio_button_wrapper, {
[styles.is_selected]: props.checked_variable.data === option.id,
[styles.disabled]: option.disabled === true,
});
return (
<div key={option.id} className={radioWrapperClass}>
<label className={labelClass}>
<input
className={styles.radio_button_input}
type="radio"
name={props.name}
value={option.radio_button_id}
onChange={() => props.selectFunction(option.radio_button_id)}
checked
value={option.id}
onChange={() => props.selectFunction(option.id)}
checked={props.checked_variable.data === option.id}
disabled={option.disabled === true}
/>
</>
: <input
className={styles.radio_button_input}
type="radio"
name={props.name}
value={option.radio_button_id}
onChange={() => props.selectFunction(option.radio_button_id)}
/>
}
<p className={styles.radio_button_label}>{option.label}</p>
</label>
))}
<p className={styles.radio_button_label}>{option.label}</p>
</label>
{props.ChildComponent && <props.ChildComponent option={option} {...props} />}
</div>
);
})}
</div>
);
};

View File

@@ -2,24 +2,44 @@
.container {
display: flex;
flex-direction: column;
justify-content: center;
gap: 0.4rem;
position: relative;
flex-shrink: 0;
&.column {
flex-direction: column;
}
}
.radio_button_container {
display: flex;
align-items: center;
justify-content: space-between;
gap: 2rem;
position: relative;
}
.radio_button_wrapper {
display: flex;
flex-shrink: 0;
align-items: center;
cursor: pointer;
gap: 1rem;
padding: 0.6rem 0.8rem;
border-radius: 0.4rem;
position: relative;
min-width: 20rem;
&:hover {
background-color: var(--dark_800_color);
background-color: var(--dark_850_color);
}
&:active {
background-color: var(--dark_850_color);
background-color: var(--dark_925_color);
}
&.is_selected {
pointer-events: none;
}
&.disabled {
pointer-events: none;
color: var(--dark_600_color);
}
}
@@ -28,19 +48,24 @@
margin: 0;
width: 2rem;
height: 2rem;
border: 0.2rem solid var(--dark_600_color);
border: 0.3rem solid var(--dark_600_color);
border-radius: 50%;
transition: border-color .1s ease, border-width .1s ease;
flex-shrink: 0;
cursor: inherit;
&:checked {
border-color: var(--primary_400_color);
border-width: 0.6rem;
}
&:disabled {
border-color: var(--dark_825_color);
}
}
.radio_button_label {
font-size: 1.4rem;
font-weight: 300;
font-weight: 400;
flex-shrink: 0;
}
.loader {

View File

@@ -14,6 +14,7 @@ import {
ActionButton,
WordFilter,
WordFilterListToggleComponent,
DownloadModels,
} from "../_components/";
export const useOnMouseLeaveDropdownMenu = () => {
@@ -118,4 +119,13 @@ export const WordFilterContainer = (props) => {
</div>
</div>
);
};
export const DownloadModelsContainer = (props) => {
return (
<div className={styles.container}>
<LabelComponent label={props.label} desc={props.desc} />
<DownloadModels {...props}/>
</div>
);
};

View File

@@ -4,7 +4,7 @@
justify-content: space-between;
align-items: center;
padding: 2rem;
gap: 2rem;
gap: 6rem;
&.flex_column {
flex-direction: column;
}

View File

@@ -13,10 +13,6 @@ import {
useTransparency,
} from "@logics_configs";
import {
LabelComponent
} from "../_components/";
import {
SliderContainer,
DropdownMenuContainer,
@@ -42,34 +38,14 @@ const UiLanguageContainer = () => {
const is_not_en_lang = currentUiLanguage.data !== "en" && currentUiLanguage.data !== undefined;
return (
<div className={styles.ui_language_container}>
<div className={styles.ui_language_label_wrapper}>
{is_not_en_lang
?
<>
<LabelComponent label="UI Language" desc={t("config_page.ui_language.label")}/>
</>
:
<LabelComponent label={t("config_page.ui_language.label")}/>
}
</div>
<div className={styles.ui_language_selector_container}>
{currentUiLanguage.state === "pending" && <span className={styles.loader}></span>}
{Object.entries(ui_configs.selectable_ui_languages).map(([key, value]) => (
<label key={key} className={clsx(styles.radio_button_wrapper, { [styles.is_selected]: currentUiLanguage.data === key } )}>
<input
className={styles.radio_button_input}
type="radio"
name="ui_language"
value={key}
onChange={() => setUiLanguage(key)}
checked={currentUiLanguage.data === key}
/>
<p className={styles.radio_button_label}>{value}</p>
</label>
))}
</div>
</div>
<RadioButtonContainer
label={is_not_en_lang ? "UI Language" : t("config_page.ui_language.label")}
desc={is_not_en_lang ? t("config_page.ui_language.label") : false}
selectFunction={setUiLanguage}
name="ui_language"
options={ui_configs.selectable_ui_languages}
checked_variable={currentUiLanguage}
/>
);
};
@@ -166,11 +142,12 @@ const SendMessageButtonTypeContainer = () => {
selectFunction={setSendMessageButtonType}
name="send_message_button_type"
options={[
{ radio_button_id: "hide", label: t("config_page.send_message_button_type.hide") },
{ radio_button_id: "show", label: t("config_page.send_message_button_type.show") },
{ radio_button_id: "show_and_disable_enter_key", label: t("config_page.send_message_button_type.show_and_disable_enter_key") },
{ id: "hide", label: t("config_page.send_message_button_type.hide") },
{ id: "show", label: t("config_page.send_message_button_type.show") },
{ id: "show_and_disable_enter_key", label: t("config_page.send_message_button_type.show_and_disable_enter_key") },
]}
checked_variable={currentSendMessageButtonType}
column={true}
/>
);
};

View File

@@ -1,66 +1 @@
@import "@scss_mixins";
.ui_language_container {
display: flex;
width: 100%;
justify-content: space-between;
align-items: center;
padding: 2rem;
border-radius: 0.6rem;
}
.ui_language_selector_container {
display: flex;
gap: 1rem;
position: relative;
}
.ui_language_label_wrapper {
display: flex;
align-items: center;
}
.ui_language_secondly_label {
font-size: 1.2rem;
}
.radio_button_wrapper {
display: flex;
align-items: center;
cursor: pointer;
gap: 0.6rem;
padding: 0.6rem 1rem;
border-radius: 0.4rem;
position: relative;
&:hover {
background-color: var(--dark_850_color);
}
&:active {
background-color: var(--dark_925_color);
}
&.is_selected {
pointer-events: none;
}
}
.radio_button_input {
appearance: none;
margin: 0;
width: 2rem;
height: 2rem;
border: 0.3rem solid var(--dark_600_color);
border-radius: 50%;
transition: border-color .1s ease, border-width .1s ease;
cursor: inherit;
&:checked {
border-color: var(--primary_400_color);
border-width: 0.6rem;
}
}
.radio_button_label {
font-size: 1.4rem;
font-weight: 400;
}
.loader {
@include loader(2rem, 0.2rem, right, -3rem);
}
@import "@scss_mixins";

View File

@@ -4,13 +4,61 @@ import styles from "./Translation.module.scss";
import {
useDeepLAuthKey,
useCTranslate2WeightTypeStatus,
useSelectedCTranslate2WeightType,
} from "@logics_configs";
import {
DownloadModelsContainer,
DeeplAuthKeyContainer,
} from "../_templates/Templates";
export const Translation = () => {
return (
<>
<CTranslate2WeightType_Box />
<DeeplAuthKey_Box />
</>
);
};
const CTranslate2WeightType_Box = () => {
const { t } = useTranslation();
const {
currentCTranslate2WeightTypeStatus,
pendingCTranslate2WeightType,
downloadCTranslate2Weight,
} = useCTranslate2WeightTypeStatus();
const { currentSelectedCTranslate2WeightType, setSelectedCTranslate2WeightType } = useSelectedCTranslate2WeightType();
const selectFunction = (id) => {
setSelectedCTranslate2WeightType(id);
};
const downloadStartFunction = (id) => {
pendingCTranslate2WeightType(id);
downloadCTranslate2Weight(id);
};
return (
<>
<DownloadModelsContainer
label={t("config_page.ctranslate2_weight_type.label")}
desc={t(
"config_page.ctranslate2_weight_type.desc",
{translator: t("main_page.translator")}
)}
name="ctransalte2_weight_type"
options={currentCTranslate2WeightTypeStatus.data}
checked_variable={currentSelectedCTranslate2WeightType}
selectFunction={selectFunction}
downloadStartFunction={downloadStartFunction}
/>
</>
);
};
const DeeplAuthKey_Box = () => {
const [input_value, seInputValue] = useState("");
const { t } = useTranslation();
const { currentDeepLAuthKey, setDeepLAuthKey, deleteDeepLAuthKey } = useDeepLAuthKey();

View File

@@ -31,7 +31,9 @@ export { useSpeakerRecordTimeout } from "./transcription/useSpeakerRecordTimeout
export { useSpeakerPhraseTimeout } from "./transcription/useSpeakerPhraseTimeout";
export { useSpeakerMaxWords } from "./transcription/useSpeakerMaxWords";
export { useCTranslate2WeightTypeStatus } from "./translation/useCTranslate2WeightTypeStatus";
export { useDeepLAuthKey } from "./translation/useDeepLAuthKey";
export { useSelectedCTranslate2WeightType } from "./translation/useSelectedCTranslate2WeightType";
export { useIsEnabledOverlaySmallLog } from "./vr/useIsEnabledOverlaySmallLog";
export { useOverlaySettings } from "./vr/useOverlaySettings";

View File

@@ -0,0 +1,60 @@
import { useStore_CTranslate2WeightTypeStatus } from "@store";
import { useStdoutToPython } from "@logics/useStdoutToPython";
export const useCTranslate2WeightTypeStatus = () => {
const { asyncStdoutToPython } = useStdoutToPython();
const { currentCTranslate2WeightTypeStatus, updateCTranslate2WeightTypeStatus, pendingCTranslate2WeightTypeStatus } = useStore_CTranslate2WeightTypeStatus();
const updateDownloadedCTranslate2WeightTypeStatus = (downloaded_weight_type_status) => {
updateCTranslate2WeightTypeStatus((old_status) =>
old_status.data.map((item) => ({
...item,
is_downloaded: downloaded_weight_type_status[item.id] ?? item.is_downloaded,
}))
);
};
const updateDownloadProgressCTranslate2WeightTypeStatus = (payload) => {
if (payload === true) return console.error("fix me.");
updateCTranslate2WeightTypeStatus((old_status) =>
old_status.data.map((item) =>
payload.weight_type === item.id
? { ...item, progress: payload.progress * 100 }
: item
)
);
};
const pendingCTranslate2WeightType = (id) => {
updateCTranslate2WeightTypeStatus((old_status) =>
old_status.data.map((item) =>
id === item.id
? { ...item, is_pending: true }
: item
)
);
};
const downloadedCTranslate2WeightType = (id) => {
updateCTranslate2WeightTypeStatus((old_status) =>
old_status.data.map((item) =>
id === item.id
? { ...item, is_downloaded: true, is_pending: false, progress: null }
: item
)
);
};
const downloadCTranslate2Weight = (weight_type) => {
asyncStdoutToPython("/run/download_ctranslate2_weight", weight_type);
};
return {
currentCTranslate2WeightTypeStatus,
updateCTranslate2WeightTypeStatus,
updateDownloadedCTranslate2WeightTypeStatus,
updateDownloadProgressCTranslate2WeightTypeStatus,
pendingCTranslate2WeightType,
downloadedCTranslate2WeightType,
downloadCTranslate2Weight,
};
};

View File

@@ -0,0 +1,24 @@
import { useStore_SelectedCTranslate2WeightType } from "@store";
import { useStdoutToPython } from "@logics/useStdoutToPython";
export const useSelectedCTranslate2WeightType = () => {
const { asyncStdoutToPython } = useStdoutToPython();
const { currentSelectedCTranslate2WeightType, updateSelectedCTranslate2WeightType, pendingSelectedCTranslate2WeightType } = useStore_SelectedCTranslate2WeightType();
const getSelectedCTranslate2WeightType = () => {
pendingSelectedCTranslate2WeightType();
asyncStdoutToPython("/get/data/ctranslate2_weight_type");
};
const setSelectedCTranslate2WeightType = (selected_ctranslate2_weight_type) => {
pendingSelectedCTranslate2WeightType();
asyncStdoutToPython("/set/data/ctranslate2_weight_type", selected_ctranslate2_weight_type);
};
return {
currentSelectedCTranslate2WeightType,
getSelectedCTranslate2WeightType,
updateSelectedCTranslate2WeightType,
setSelectedCTranslate2WeightType,
};
};

View File

@@ -46,6 +46,8 @@ import {
useSpeakerPhraseTimeout,
useSpeakerMaxWords,
useDeepLAuthKey,
useCTranslate2WeightTypeStatus,
useSelectedCTranslate2WeightType,
useOverlaySettings,
useIsEnabledOverlaySmallLog,
useOverlaySmallLogSettings,
@@ -118,6 +120,12 @@ export const useReceiveRoutes = () => {
const { updateSpeakerMaxWords } = useSpeakerMaxWords();
const { updateDeepLAuthKey } = useDeepLAuthKey();
const { updateSelectedCTranslate2WeightType } = useSelectedCTranslate2WeightType();
const {
updateDownloadedCTranslate2WeightTypeStatus,
updateDownloadProgressCTranslate2WeightTypeStatus,
downloadedCTranslate2WeightType,
} = useCTranslate2WeightTypeStatus();
const { updateOverlaySettings } = useOverlaySettings();
const { updateOverlaySmallLogSettings } = useOverlaySmallLogSettings();
@@ -278,6 +286,14 @@ export const useReceiveRoutes = () => {
"/set/data/deepl_auth_key": updateDeepLAuthKey,
"/delete/data/deepl_auth_key": () => updateDeepLAuthKey(""),
"/get/data/ctranslate2_weight_type": updateSelectedCTranslate2WeightType,
"/set/data/ctranslate2_weight_type": updateSelectedCTranslate2WeightType,
"/get/data/selectable_ctranslate2_weight_type_dict": updateDownloadedCTranslate2WeightTypeStatus,
"/run/download_ctranslate2_weight": updateDownloadProgressCTranslate2WeightTypeStatus,
"/run/downloaded_ctranslate2_weight": downloadedCTranslate2WeightType,
// Transcription
"/get/data/mic_record_timeout": updateMicRecordTimeout,
"/set/data/mic_record_timeout": updateMicRecordTimeout,

View File

@@ -9,6 +9,7 @@ import {
} from "@test_data";
import {
translator_status,
ctranslate2_weight_type_status,
} from "@ui_configs";
export const store = {
@@ -185,6 +186,8 @@ export const { atomInstance: Atom_MicWordFilterList, useHook: useStore_MicWordFi
// Translation
export const { atomInstance: Atom_DeepLAuthKey, useHook: useStore_DeepLAuthKey } = createAtomWithHook(null, "DeepLAuthKey");
export const { atomInstance: Atom_SelectedCTranslate2WeightType, useHook: useStore_SelectedCTranslate2WeightType } = createAtomWithHook("", "SelectedCTranslate2WeightType");
export const { atomInstance: Atom_CTranslate2WeightTypeStatus, useHook: useStore_CTranslate2WeightTypeStatus } = createAtomWithHook(ctranslate2_weight_type_status, "CTranslate2WeightTypeStatus");
// Transcription
export const { atomInstance: Atom_MicRecordTimeout, useHook: useStore_MicRecordTimeout } = createAtomWithHook(0, "MicRecordTimeout");

View File

@@ -12,10 +12,15 @@ export const ui_configs = {
mic_threshold_max: 2000,
speaker_threshold_min: 0,
speaker_threshold_max: 4000,
selectable_ui_languages: {
en: "English",
ja: "日本語",
ko: "한국어",
"zh-Hant": "繁體中文",
}
};
selectable_ui_languages: [
{id: "en", label: "English"},
{id: "ja", label: "日本語"},
{id: "ko", label: "한국어"},
{id: "zh-Hant", label: "繁體中文"},
]
};
export const ctranslate2_weight_type_status = [
{ id: "small", label: "small", is_downloaded: false, progress: null },
{ id: "large", label: "large", is_downloaded: false, progress: null },
];