Skip to content

Commit a27a8e9

Browse files
committed
backup
1 parent b7ce7e5 commit a27a8e9

5 files changed

Lines changed: 109 additions & 10 deletions

File tree

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/renderer/src/components/sidebar/setting/general.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,34 @@ function General({ onSave, onCancel }: GeneralProps): JSX.Element {
139139
onChange={(value) => handleSettingChange("baseUrl", value)}
140140
placeholder="Enter Base URL"
141141
/>
142+
143+
<InputField
144+
label={t("settings.general.imageCompressionQuality")}
145+
value={settings.imageCompressionQuality.toString()}
146+
onChange={(value) => {
147+
const quality = parseFloat(value as string);
148+
if (!Number.isNaN(quality) && quality >= 0.1 && quality <= 1.0) {
149+
handleSettingChange("imageCompressionQuality", quality);
150+
} else if (value === "") {
151+
handleSettingChange("imageCompressionQuality", settings.imageCompressionQuality);
152+
}
153+
}}
154+
placeholder={t("settings.general.imageCompressionQualityPlaceholder")}
155+
/>
156+
157+
<InputField
158+
label={t("settings.general.imageMaxWidth")}
159+
value={settings.imageMaxWidth.toString()}
160+
onChange={(value) => {
161+
const maxWidth = parseInt(value as string, 10);
162+
if (!Number.isNaN(maxWidth) && maxWidth > 0) {
163+
handleSettingChange("imageMaxWidth", maxWidth);
164+
} else if (value === "") {
165+
handleSettingChange("imageMaxWidth", settings.imageMaxWidth);
166+
}
167+
}}
168+
placeholder={t("settings.general.imageMaxWidthPlaceholder")}
169+
/>
142170
</Stack>
143171
);
144172
}

src/renderer/src/context/vad-context.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ export function VADProvider({ children }: { children: React.ReactNode }) {
222222
if (autoStopMicRef.current) {
223223
stopMic();
224224
} else {
225-
console.log('Auto stop mic is on, keeping mic active');
225+
console.log('Auto stop mic is OFF, keeping mic active');
226226
}
227227

228228
setPreviousTriggeredProbability(0);

src/renderer/src/hooks/sidebar/setting/use-general-settings.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import { useSwitchCharacter } from '@/hooks/utils/use-switch-character';
99
import { useConfig } from '@/context/character-config-context';
1010
import i18n from 'i18next';
1111

12+
export const IMAGE_COMPRESSION_QUALITY_KEY = 'appImageCompressionQuality';
13+
export const DEFAULT_IMAGE_COMPRESSION_QUALITY = 0.8;
14+
export const IMAGE_MAX_WIDTH_KEY = 'appImageMaxWidth';
15+
export const DEFAULT_IMAGE_MAX_WIDTH = 1080;
16+
1217
interface GeneralSettings {
1318
language: string[]
1419
customBgUrl: string
@@ -19,6 +24,8 @@ interface GeneralSettings {
1924
wsUrl: string
2025
baseUrl: string
2126
showSubtitle: boolean
27+
imageCompressionQuality: number;
28+
imageMaxWidth: number;
2229
}
2330

2431
interface UseGeneralSettingsProps {
@@ -33,6 +40,28 @@ interface UseGeneralSettingsProps {
3340
onCancel?: (callback: () => void) => () => void
3441
}
3542

43+
const loadInitialCompressionQuality = (): number => {
44+
const storedQuality = localStorage.getItem(IMAGE_COMPRESSION_QUALITY_KEY);
45+
if (storedQuality) {
46+
const quality = parseFloat(storedQuality);
47+
if (!Number.isNaN(quality) && quality >= 0.1 && quality <= 1.0) {
48+
return quality;
49+
}
50+
}
51+
return DEFAULT_IMAGE_COMPRESSION_QUALITY;
52+
};
53+
54+
const loadInitialImageMaxWidth = (): number => {
55+
const storedMaxWidth = localStorage.getItem(IMAGE_MAX_WIDTH_KEY);
56+
if (storedMaxWidth) {
57+
const maxWidth = parseInt(storedMaxWidth, 10);
58+
if (!Number.isNaN(maxWidth) && maxWidth > 0) {
59+
return maxWidth;
60+
}
61+
}
62+
return DEFAULT_IMAGE_MAX_WIDTH;
63+
};
64+
3665
export const useGeneralSettings = ({
3766
bgUrlContext,
3867
confName,
@@ -75,6 +104,8 @@ export const useGeneralSettings = ({
75104
wsUrl: wsUrl || defaultWsUrl,
76105
baseUrl: baseUrl || defaultBaseUrl,
77106
showSubtitle,
107+
imageCompressionQuality: loadInitialCompressionQuality(),
108+
imageMaxWidth: loadInitialImageMaxWidth(),
78109
};
79110

80111
const [settings, setSettings] = useState<GeneralSettings>(initialSettings);
@@ -97,7 +128,9 @@ export const useGeneralSettings = ({
97128
if (settings.language && settings.language[0] && settings.language[0] !== i18n.language) {
98129
i18n.changeLanguage(settings.language[0]);
99130
}
100-
}, [settings]);
131+
localStorage.setItem(IMAGE_COMPRESSION_QUALITY_KEY, settings.imageCompressionQuality.toString());
132+
localStorage.setItem(IMAGE_MAX_WIDTH_KEY, settings.imageMaxWidth.toString());
133+
}, [settings, bgUrlContext, baseUrl, onWsUrlChange, onBaseUrlChange, setShowSubtitle]);
101134

102135
useEffect(() => {
103136
if (confName) {

src/renderer/src/hooks/utils/use-media-capture.tsx

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
/* eslint-disable operator-assignment */
12
/* eslint-disable object-shorthand */
23
import { useCallback } from 'react';
34
import { useTranslation } from 'react-i18next';
45
import { useCamera } from '@/context/camera-context';
56
import { useScreenCaptureContext } from '@/context/screen-capture-context';
67
import { toaster } from "@/components/ui/toaster";
8+
import {
9+
IMAGE_COMPRESSION_QUALITY_KEY,
10+
DEFAULT_IMAGE_COMPRESSION_QUALITY,
11+
IMAGE_MAX_WIDTH_KEY,
12+
DEFAULT_IMAGE_MAX_WIDTH,
13+
} from '@/hooks/sidebar/setting/use-general-settings';
714

815
// Add type definition for ImageCapture
916
declare class ImageCapture {
@@ -23,6 +30,28 @@ export function useMediaCapture() {
2330
const { stream: cameraStream } = useCamera();
2431
const { stream: screenStream } = useScreenCaptureContext();
2532

33+
const getCompressionQuality = useCallback(() => {
34+
const storedQuality = localStorage.getItem(IMAGE_COMPRESSION_QUALITY_KEY);
35+
if (storedQuality) {
36+
const quality = parseFloat(storedQuality);
37+
if (!Number.isNaN(quality) && quality >= 0.1 && quality <= 1.0) {
38+
return quality;
39+
}
40+
}
41+
return DEFAULT_IMAGE_COMPRESSION_QUALITY;
42+
}, []);
43+
44+
const getImageMaxWidth = useCallback(() => {
45+
const storedMaxWidth = localStorage.getItem(IMAGE_MAX_WIDTH_KEY);
46+
if (storedMaxWidth) {
47+
const maxWidth = parseInt(storedMaxWidth, 10);
48+
if (!Number.isNaN(maxWidth) && maxWidth > 0) {
49+
return maxWidth;
50+
}
51+
}
52+
return DEFAULT_IMAGE_MAX_WIDTH;
53+
}, []);
54+
2655
const captureFrame = useCallback(async (stream: MediaStream | null, source: 'camera' | 'screen') => {
2756
if (!stream) {
2857
console.warn(`No ${source} stream available`);
@@ -39,16 +68,25 @@ export function useMediaCapture() {
3968
try {
4069
const bitmap = await imageCapture.grabFrame();
4170
const canvas = document.createElement('canvas');
42-
canvas.width = bitmap.width;
43-
canvas.height = bitmap.height;
71+
let { width, height } = bitmap;
72+
73+
const maxWidth = getImageMaxWidth();
74+
if (width > maxWidth) {
75+
height = (maxWidth / width) * height;
76+
width = maxWidth;
77+
}
78+
79+
canvas.width = width;
80+
canvas.height = height;
4481
const ctx = canvas.getContext('2d');
4582
if (!ctx) {
4683
console.error('Failed to get canvas context');
4784
return null;
4885
}
4986

5087
ctx.drawImage(bitmap, 0, 0);
51-
return canvas.toDataURL('image/jpeg', 0.8);
88+
const quality = getCompressionQuality();
89+
return canvas.toDataURL('image/jpeg', quality);
5290
} catch (error) {
5391
console.error(`Error capturing ${source} frame:`, error);
5492
toaster.create({
@@ -58,7 +96,7 @@ export function useMediaCapture() {
5896
});
5997
return null;
6098
}
61-
}, [t]);
99+
}, [t, getCompressionQuality, getImageMaxWidth]);
62100

63101
const captureAllMedia = useCallback(async () => {
64102
const images: ImageData[] = [];

0 commit comments

Comments
 (0)