feat: add sound and browser notifications for agent state changes (#6530)

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: amanape <83104063+amanape@users.noreply.github.com>
This commit is contained in:
Xingyao Wang
2025-02-27 13:40:26 -05:00
committed by GitHub
parent 0aa508c382
commit 616ff49787
16 changed files with 557 additions and 7 deletions

View File

@@ -1,6 +1,7 @@
import { render, screen, within } from "@testing-library/react";
import { screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";
import { renderWithProviders } from "test-utils";
import { TrajectoryActions } from "#/components/features/trajectory/trajectory-actions";
describe("TrajectoryActions", () => {
@@ -14,7 +15,7 @@ describe("TrajectoryActions", () => {
});
it("should render correctly", () => {
render(
renderWithProviders(
<TrajectoryActions
onPositiveFeedback={onPositiveFeedback}
onNegativeFeedback={onNegativeFeedback}
@@ -25,10 +26,11 @@ describe("TrajectoryActions", () => {
const actions = screen.getByTestId("feedback-actions");
within(actions).getByTestId("positive-feedback");
within(actions).getByTestId("negative-feedback");
within(actions).getByTestId("export-trajectory");
});
it("should call onPositiveFeedback when positive feedback is clicked", async () => {
render(
renderWithProviders(
<TrajectoryActions
onPositiveFeedback={onPositiveFeedback}
onNegativeFeedback={onNegativeFeedback}
@@ -43,7 +45,7 @@ describe("TrajectoryActions", () => {
});
it("should call onNegativeFeedback when negative feedback is clicked", async () => {
render(
renderWithProviders(
<TrajectoryActions
onPositiveFeedback={onPositiveFeedback}
onNegativeFeedback={onNegativeFeedback}
@@ -58,7 +60,7 @@ describe("TrajectoryActions", () => {
});
it("should call onExportTrajectory when negative feedback is clicked", async () => {
render(
renderWithProviders(
<TrajectoryActions
onPositiveFeedback={onPositiveFeedback}
onNegativeFeedback={onNegativeFeedback}

Binary file not shown.

View File

@@ -9,12 +9,21 @@ import {
useWsClient,
WsClientProviderStatus,
} from "#/context/ws-client-provider";
import { useNotification } from "#/hooks/useNotification";
import { browserTab } from "#/utils/browser-tab";
const notificationStates = [
AgentState.AWAITING_USER_INPUT,
AgentState.FINISHED,
AgentState.AWAITING_USER_CONFIRMATION,
];
export function AgentStatusBar() {
const { t, i18n } = useTranslation();
const { curAgentState } = useSelector((state: RootState) => state.agent);
const { curStatusMessage } = useSelector((state: RootState) => state.status);
const { status } = useWsClient();
const { notify } = useNotification();
const [statusMessage, setStatusMessage] = React.useState<string>("");
@@ -45,13 +54,40 @@ export function AgentStatusBar() {
updateStatusMessage();
}, [curStatusMessage.id]);
// Handle window focus/blur
React.useEffect(() => {
if (typeof window === "undefined") return undefined;
const handleFocus = () => {
browserTab.stopNotification();
};
window.addEventListener("focus", handleFocus);
return () => {
window.removeEventListener("focus", handleFocus);
browserTab.stopNotification();
};
}, []);
React.useEffect(() => {
if (status === WsClientProviderStatus.DISCONNECTED) {
setStatusMessage("Connecting...");
} else {
setStatusMessage(AGENT_STATUS_MAP[curAgentState].message);
if (notificationStates.includes(curAgentState)) {
const message = t(AGENT_STATUS_MAP[curAgentState].message);
notify(t(AGENT_STATUS_MAP[curAgentState].message), {
body: t(`Agent state changed to ${curAgentState}`),
playSound: true,
});
// Update browser tab if window exists and is not focused
if (typeof document !== "undefined" && !document.hasFocus()) {
browserTab.startNotification(message);
}
}
}
}, [curAgentState]);
}, [curAgentState, notify, t]);
return (
<div className="flex flex-col items-center">

View File

@@ -1,3 +1,4 @@
import { useTranslation } from "react-i18next";
import ThumbsUpIcon from "#/icons/thumbs-up.svg?react";
import ThumbDownIcon from "#/icons/thumbs-down.svg?react";
import ExportIcon from "#/icons/export.svg?react";
@@ -14,22 +15,27 @@ export function TrajectoryActions({
onNegativeFeedback,
onExportTrajectory,
}: TrajectoryActionsProps) {
const { t } = useTranslation();
return (
<div data-testid="feedback-actions" className="flex gap-1">
<TrajectoryActionButton
testId="positive-feedback"
onClick={onPositiveFeedback}
icon={<ThumbsUpIcon width={15} height={15} />}
tooltip={t("BUTTON$MARK_HELPFUL")}
/>
<TrajectoryActionButton
testId="negative-feedback"
onClick={onNegativeFeedback}
icon={<ThumbDownIcon width={15} height={15} />}
tooltip={t("BUTTON$MARK_NOT_HELPFUL")}
/>
<TrajectoryActionButton
testId="export-trajectory"
onClick={onExportTrajectory}
icon={<ExportIcon width={15} height={15} />}
tooltip={t("BUTTON$EXPORT_CONVERSATION")}
/>
</div>
);

View File

@@ -1,15 +1,19 @@
import { Tooltip } from "@heroui/react";
interface TrajectoryActionButtonProps {
testId?: string;
onClick: () => void;
icon: React.ReactNode;
tooltip?: string;
}
export function TrajectoryActionButton({
testId,
onClick,
icon,
tooltip,
}: TrajectoryActionButtonProps) {
return (
const button = (
<button
type="button"
data-testid={testId}
@@ -19,4 +23,14 @@ export function TrajectoryActionButton({
{icon}
</button>
);
if (tooltip) {
return (
<Tooltip content={tooltip} closeDelay={100}>
{button}
</Tooltip>
);
}
return button;
}

View File

@@ -20,6 +20,7 @@ const saveSettingsMutationFn = async (settings: Partial<PostSettings>) => {
github_token: settings.github_token,
unset_github_token: settings.unset_github_token,
enable_default_condenser: settings.ENABLE_DEFAULT_CONDENSER,
enable_sound_notifications: settings.ENABLE_SOUND_NOTIFICATIONS,
user_consents_to_analytics: settings.user_consents_to_analytics,
};

View File

@@ -20,6 +20,7 @@ const getSettingsQueryFn = async () => {
REMOTE_RUNTIME_RESOURCE_FACTOR: apiSettings.remote_runtime_resource_factor,
GITHUB_TOKEN_IS_SET: apiSettings.github_token_is_set,
ENABLE_DEFAULT_CONDENSER: apiSettings.enable_default_condenser,
ENABLE_SOUND_NOTIFICATIONS: apiSettings.enable_sound_notifications,
USER_CONSENTS_TO_ANALYTICS: apiSettings.user_consents_to_analytics,
};
};

View File

@@ -0,0 +1,56 @@
import { useCallback, useRef } from "react";
import notificationSound from "../assets/notification.mp3";
import { useCurrentSettings } from "../context/settings-context";
export const useNotification = () => {
const { settings } = useCurrentSettings();
const audioRef = useRef<HTMLAudioElement | undefined>(undefined);
// Initialize audio only in browser environment
if (typeof window !== "undefined" && !audioRef.current) {
audioRef.current = new Audio(notificationSound);
audioRef.current.volume = 0.5;
}
const notify = useCallback(
async (
title: string,
options?: NotificationOptions & { playSound?: boolean },
): Promise<Notification | undefined> => {
if (typeof window === "undefined") return undefined;
// Only play sound if:
// 1. Explicitly requested via playSound option
// 2. Sound notifications are enabled in settings
// 3. Audio is available
// 4. Not a settings-related notification
if (
options?.playSound === true && // Must be explicitly true
settings?.ENABLE_SOUND_NOTIFICATIONS &&
audioRef.current &&
!title.includes("BUTTON$") // Don't play for button/settings actions
) {
// Reset and play sound
audioRef.current.currentTime = 0;
audioRef.current.play().catch(() => {
// Ignore autoplay errors
});
}
if (Notification.permission === "default") {
await Notification.requestPermission();
}
if (Notification.permission === "granted") {
// Remove playSound from options before passing to Notification
const { playSound, ...notificationOptions } = options || {};
return new Notification(title, notificationOptions);
}
return undefined;
},
[settings?.ENABLE_SOUND_NOTIFICATIONS],
);
return { notify };
};

View File

@@ -0,0 +1,316 @@
// this file generate by script, don't modify it manually!!!
export enum I18nKey {
APP$TITLE = "APP$TITLE",
BROWSER$TITLE = "BROWSER$TITLE",
BROWSER$EMPTY_MESSAGE = "BROWSER$EMPTY_MESSAGE",
SETTINGS$TITLE = "SETTINGS$TITLE",
PROJECT$START_NEW = "PROJECT$START_NEW",
PROJECT$NEW = "PROJECT$NEW",
ACCOUNT_SETTINGS$TITLE = "ACCOUNT_SETTINGS$TITLE",
WORKSPACE$TITLE = "WORKSPACE$TITLE",
WORKSPACE$TERMINAL_TAB_LABEL = "WORKSPACE$TERMINAL_TAB_LABEL",
TERMINAL$WAITING_FOR_CLIENT = "TERMINAL$WAITING_FOR_CLIENT",
CODE_EDITOR$FILE_SAVED_SUCCESSFULLY = "CODE_EDITOR$FILE_SAVED_SUCCESSFULLY",
CODE_EDITOR$SAVING_LABEL = "CODE_EDITOR$SAVING_LABEL",
CODE_EDITOR$SAVE_LABEL = "CODE_EDITOR$SAVE_LABEL",
CODE_EDITOR$OPTIONS = "CODE_EDITOR$OPTIONS",
CODE_EDITOR$FILE_SAVE_ERROR = "CODE_EDITOR$FILE_SAVE_ERROR",
CODE_EDITOR$EMPTY_MESSAGE = "CODE_EDITOR$EMPTY_MESSAGE",
FILE_SERVICE$SELECT_FILE_ERROR = "FILE_SERVICE$SELECT_FILE_ERROR",
FILE_SERVICE$UPLOAD_FILES_ERROR = "FILE_SERVICE$UPLOAD_FILES_ERROR",
FILE_SERVICE$LIST_FILES_ERROR = "FILE_SERVICE$LIST_FILES_ERROR",
FILE_SERVICE$SAVE_FILE_ERROR = "FILE_SERVICE$SAVE_FILE_ERROR",
SUGGESTIONS$INCREASE_TEST_COVERAGE = "SUGGESTIONS$INCREASE_TEST_COVERAGE",
SUGGESTIONS$AUTO_MERGE_PRS = "SUGGESTIONS$AUTO_MERGE_PRS",
SUGGESTIONS$FIX_README = "SUGGESTIONS$FIX_README",
SUGGESTIONS$CLEAN_DEPENDENCIES = "SUGGESTIONS$CLEAN_DEPENDENCIES",
FILE_SERVICE$INVALID_FILE_PATH = "FILE_SERVICE$INVALID_FILE_PATH",
WORKSPACE$PLANNER_TAB_LABEL = "WORKSPACE$PLANNER_TAB_LABEL",
WORKSPACE$JUPYTER_TAB_LABEL = "WORKSPACE$JUPYTER_TAB_LABEL",
WORKSPACE$CODE_EDITOR_TAB_LABEL = "WORKSPACE$CODE_EDITOR_TAB_LABEL",
WORKSPACE$BROWSER_TAB_LABEL = "WORKSPACE$BROWSER_TAB_LABEL",
VSCODE$OPEN = "VSCODE$OPEN",
INCREASE_TEST_COVERAGE = "INCREASE_TEST_COVERAGE",
AUTO_MERGE_PRS = "AUTO_MERGE_PRS",
FIX_README = "FIX_README",
CLEAN_DEPENDENCIES = "CLEAN_DEPENDENCIES",
CONFIGURATION$OPENHANDS_WORKSPACE_DIRECTORY_INPUT_LABEL = "CONFIGURATION$OPENHANDS_WORKSPACE_DIRECTORY_INPUT_LABEL",
LLM$PROVIDER = "LLM$PROVIDER",
LLM$SELECT_PROVIDER_PLACEHOLDER = "LLM$SELECT_PROVIDER_PLACEHOLDER",
API$KEY = "API$KEY",
API$DONT_KNOW_KEY = "API$DONT_KNOW_KEY",
BUTTON$SAVE = "BUTTON$SAVE",
BUTTON$CLOSE = "BUTTON$CLOSE",
BUTTON$RESET_TO_DEFAULTS = "BUTTON$RESET_TO_DEFAULTS",
MODAL$CONFIRM_RESET_TITLE = "MODAL$CONFIRM_RESET_TITLE",
MODAL$CONFIRM_RESET_MESSAGE = "MODAL$CONFIRM_RESET_MESSAGE",
MODAL$END_SESSION_TITLE = "MODAL$END_SESSION_TITLE",
MODAL$END_SESSION_MESSAGE = "MODAL$END_SESSION_MESSAGE",
BUTTON$END_SESSION = "BUTTON$END_SESSION",
BUTTON$CANCEL = "BUTTON$CANCEL",
LANGUAGE$LABEL = "LANGUAGE$LABEL",
GITHUB$TOKEN_LABEL = "GITHUB$TOKEN_LABEL",
GITHUB$TOKEN_OPTIONAL = "GITHUB$TOKEN_OPTIONAL",
GITHUB$GET_TOKEN = "GITHUB$GET_TOKEN",
COMMON$HERE = "COMMON$HERE",
ANALYTICS$ENABLE = "ANALYTICS$ENABLE",
GITHUB$TOKEN_INVALID = "GITHUB$TOKEN_INVALID",
BUTTON$DISCONNECT = "BUTTON$DISCONNECT",
GITHUB$CONFIGURE_REPOS = "GITHUB$CONFIGURE_REPOS",
COMMON$CLICK_FOR_INSTRUCTIONS = "COMMON$CLICK_FOR_INSTRUCTIONS",
LLM$SELECT_MODEL_PLACEHOLDER = "LLM$SELECT_MODEL_PLACEHOLDER",
LLM$MODEL = "LLM$MODEL",
CONFIGURATION$OPENHANDS_WORKSPACE_DIRECTORY_INPUT_PLACEHOLDER = "CONFIGURATION$OPENHANDS_WORKSPACE_DIRECTORY_INPUT_PLACEHOLDER",
CONFIGURATION$MODAL_TITLE = "CONFIGURATION$MODAL_TITLE",
CONFIGURATION$MODEL_SELECT_LABEL = "CONFIGURATION$MODEL_SELECT_LABEL",
CONFIGURATION$MODEL_SELECT_PLACEHOLDER = "CONFIGURATION$MODEL_SELECT_PLACEHOLDER",
CONFIGURATION$AGENT_SELECT_LABEL = "CONFIGURATION$AGENT_SELECT_LABEL",
CONFIGURATION$AGENT_SELECT_PLACEHOLDER = "CONFIGURATION$AGENT_SELECT_PLACEHOLDER",
CONFIGURATION$LANGUAGE_SELECT_LABEL = "CONFIGURATION$LANGUAGE_SELECT_LABEL",
CONFIGURATION$LANGUAGE_SELECT_PLACEHOLDER = "CONFIGURATION$LANGUAGE_SELECT_PLACEHOLDER",
CONFIGURATION$SECURITY_SELECT_LABEL = "CONFIGURATION$SECURITY_SELECT_LABEL",
CONFIGURATION$SECURITY_SELECT_PLACEHOLDER = "CONFIGURATION$SECURITY_SELECT_PLACEHOLDER",
CONFIGURATION$MODAL_CLOSE_BUTTON_LABEL = "CONFIGURATION$MODAL_CLOSE_BUTTON_LABEL",
CONFIGURATION$MODAL_SAVE_BUTTON_LABEL = "CONFIGURATION$MODAL_SAVE_BUTTON_LABEL",
CONFIGURATION$MODAL_RESET_BUTTON_LABEL = "CONFIGURATION$MODAL_RESET_BUTTON_LABEL",
STATUS$CONNECTED_TO_SERVER = "STATUS$CONNECTED_TO_SERVER",
PROJECT$NEW_PROJECT = "PROJECT$NEW_PROJECT",
BROWSER$SCREENSHOT = "BROWSER$SCREENSHOT",
TIME$MINUTES_AGO = "TIME$MINUTES_AGO",
TIME$HOURS_AGO = "TIME$HOURS_AGO",
TIME$DAYS_AGO = "TIME$DAYS_AGO",
SETTINGS_FORM$RUNTIME_SIZE_LABEL = "SETTINGS_FORM$RUNTIME_SIZE_LABEL",
CONFIGURATION$SETTINGS_NEED_UPDATE_MESSAGE = "CONFIGURATION$SETTINGS_NEED_UPDATE_MESSAGE",
CONFIGURATION$AGENT_LOADING = "CONFIGURATION$AGENT_LOADING",
CONFIGURATION$AGENT_RUNNING = "CONFIGURATION$AGENT_RUNNING",
CONFIGURATION$ERROR_FETCH_MODELS = "CONFIGURATION$ERROR_FETCH_MODELS",
CONFIGURATION$SETTINGS_NOT_FOUND = "CONFIGURATION$SETTINGS_NOT_FOUND",
CONNECT_TO_GITHUB_BY_TOKEN_MODAL$TERMS_OF_SERVICE = "CONNECT_TO_GITHUB_BY_TOKEN_MODAL$TERMS_OF_SERVICE",
SESSION$SERVER_CONNECTED_MESSAGE = "SESSION$SERVER_CONNECTED_MESSAGE",
SESSION$SESSION_HANDLING_ERROR_MESSAGE = "SESSION$SESSION_HANDLING_ERROR_MESSAGE",
SESSION$SESSION_CONNECTION_ERROR_MESSAGE = "SESSION$SESSION_CONNECTION_ERROR_MESSAGE",
SESSION$SOCKET_NOT_INITIALIZED_ERROR_MESSAGE = "SESSION$SOCKET_NOT_INITIALIZED_ERROR_MESSAGE",
EXPLORER$UPLOAD_ERROR_MESSAGE = "EXPLORER$UPLOAD_ERROR_MESSAGE",
EXPLORER$LABEL_DROP_FILES = "EXPLORER$LABEL_DROP_FILES",
EXPLORER$LABEL_WORKSPACE = "EXPLORER$LABEL_WORKSPACE",
EXPLORER$EMPTY_WORKSPACE_MESSAGE = "EXPLORER$EMPTY_WORKSPACE_MESSAGE",
EXPLORER$LOADING_WORKSPACE_MESSAGE = "EXPLORER$LOADING_WORKSPACE_MESSAGE",
EXPLORER$REFRESH_ERROR_MESSAGE = "EXPLORER$REFRESH_ERROR_MESSAGE",
EXPLORER$UPLOAD_SUCCESS_MESSAGE = "EXPLORER$UPLOAD_SUCCESS_MESSAGE",
EXPLORER$NO_FILES_UPLOADED_MESSAGE = "EXPLORER$NO_FILES_UPLOADED_MESSAGE",
EXPLORER$UPLOAD_PARTIAL_SUCCESS_MESSAGE = "EXPLORER$UPLOAD_PARTIAL_SUCCESS_MESSAGE",
EXPLORER$UPLOAD_UNEXPECTED_RESPONSE_MESSAGE = "EXPLORER$UPLOAD_UNEXPECTED_RESPONSE_MESSAGE",
EXPLORER$VSCODE_SWITCHING_MESSAGE = "EXPLORER$VSCODE_SWITCHING_MESSAGE",
EXPLORER$VSCODE_SWITCHING_ERROR_MESSAGE = "EXPLORER$VSCODE_SWITCHING_ERROR_MESSAGE",
LOAD_SESSION$MODAL_TITLE = "LOAD_SESSION$MODAL_TITLE",
LOAD_SESSION$MODAL_CONTENT = "LOAD_SESSION$MODAL_CONTENT",
LOAD_SESSION$RESUME_SESSION_MODAL_ACTION_LABEL = "LOAD_SESSION$RESUME_SESSION_MODAL_ACTION_LABEL",
LOAD_SESSION$START_NEW_SESSION_MODAL_ACTION_LABEL = "LOAD_SESSION$START_NEW_SESSION_MODAL_ACTION_LABEL",
FEEDBACK$MODAL_TITLE = "FEEDBACK$MODAL_TITLE",
FEEDBACK$MODAL_CONTENT = "FEEDBACK$MODAL_CONTENT",
FEEDBACK$EMAIL_LABEL = "FEEDBACK$EMAIL_LABEL",
FEEDBACK$CONTRIBUTE_LABEL = "FEEDBACK$CONTRIBUTE_LABEL",
FEEDBACK$SHARE_LABEL = "FEEDBACK$SHARE_LABEL",
FEEDBACK$CANCEL_LABEL = "FEEDBACK$CANCEL_LABEL",
FEEDBACK$EMAIL_PLACEHOLDER = "FEEDBACK$EMAIL_PLACEHOLDER",
FEEDBACK$PASSWORD_COPIED_MESSAGE = "FEEDBACK$PASSWORD_COPIED_MESSAGE",
FEEDBACK$GO_TO_FEEDBACK = "FEEDBACK$GO_TO_FEEDBACK",
FEEDBACK$PASSWORD = "FEEDBACK$PASSWORD",
FEEDBACK$INVALID_EMAIL_FORMAT = "FEEDBACK$INVALID_EMAIL_FORMAT",
FEEDBACK$FAILED_TO_SHARE = "FEEDBACK$FAILED_TO_SHARE",
FEEDBACK$COPY_LABEL = "FEEDBACK$COPY_LABEL",
FEEDBACK$SHARING_SETTINGS_LABEL = "FEEDBACK$SHARING_SETTINGS_LABEL",
SECURITY$UNKNOWN_ANALYZER_LABEL = "SECURITY$UNKNOWN_ANALYZER_LABEL",
INVARIANT$UPDATE_POLICY_LABEL = "INVARIANT$UPDATE_POLICY_LABEL",
INVARIANT$UPDATE_SETTINGS_LABEL = "INVARIANT$UPDATE_SETTINGS_LABEL",
INVARIANT$SETTINGS_LABEL = "INVARIANT$SETTINGS_LABEL",
INVARIANT$ASK_CONFIRMATION_RISK_SEVERITY_LABEL = "INVARIANT$ASK_CONFIRMATION_RISK_SEVERITY_LABEL",
INVARIANT$DONT_ASK_FOR_CONFIRMATION_LABEL = "INVARIANT$DONT_ASK_FOR_CONFIRMATION_LABEL",
INVARIANT$INVARIANT_ANALYZER_LABEL = "INVARIANT$INVARIANT_ANALYZER_LABEL",
INVARIANT$INVARIANT_ANALYZER_MESSAGE = "INVARIANT$INVARIANT_ANALYZER_MESSAGE",
INVARIANT$CLICK_TO_LEARN_MORE_LABEL = "INVARIANT$CLICK_TO_LEARN_MORE_LABEL",
INVARIANT$POLICY_LABEL = "INVARIANT$POLICY_LABEL",
INVARIANT$LOG_LABEL = "INVARIANT$LOG_LABEL",
INVARIANT$EXPORT_TRACE_LABEL = "INVARIANT$EXPORT_TRACE_LABEL",
INVARIANT$TRACE_EXPORTED_MESSAGE = "INVARIANT$TRACE_EXPORTED_MESSAGE",
INVARIANT$POLICY_UPDATED_MESSAGE = "INVARIANT$POLICY_UPDATED_MESSAGE",
INVARIANT$SETTINGS_UPDATED_MESSAGE = "INVARIANT$SETTINGS_UPDATED_MESSAGE",
CHAT_INTERFACE$INITIALIZING_AGENT_LOADING_MESSAGE = "CHAT_INTERFACE$INITIALIZING_AGENT_LOADING_MESSAGE",
CHAT_INTERFACE$AGENT_INIT_MESSAGE = "CHAT_INTERFACE$AGENT_INIT_MESSAGE",
CHAT_INTERFACE$AGENT_RUNNING_MESSAGE = "CHAT_INTERFACE$AGENT_RUNNING_MESSAGE",
CHAT_INTERFACE$AGENT_AWAITING_USER_INPUT_MESSAGE = "CHAT_INTERFACE$AGENT_AWAITING_USER_INPUT_MESSAGE",
CHAT_INTERFACE$AGENT_RATE_LIMITED_MESSAGE = "CHAT_INTERFACE$AGENT_RATE_LIMITED_MESSAGE",
CHAT_INTERFACE$AGENT_PAUSED_MESSAGE = "CHAT_INTERFACE$AGENT_PAUSED_MESSAGE",
LANDING$TITLE = "LANDING$TITLE",
LANDING$SUBTITLE = "LANDING$SUBTITLE",
LANDING$START_HELP = "LANDING$START_HELP",
LANDING$START_HELP_LINK = "LANDING$START_HELP_LINK",
SUGGESTIONS$HELLO_WORLD = "SUGGESTIONS$HELLO_WORLD",
SUGGESTIONS$TODO_APP = "SUGGESTIONS$TODO_APP",
SUGGESTIONS$HACKER_NEWS = "SUGGESTIONS$HACKER_NEWS",
LANDING$CHANGE_PROMPT = "LANDING$CHANGE_PROMPT",
GITHUB$CONNECT = "GITHUB$CONNECT",
GITHUB$NO_RESULTS = "GITHUB$NO_RESULTS",
GITHUB$ADD_MORE_REPOS = "GITHUB$ADD_MORE_REPOS",
GITHUB$YOUR_REPOS = "GITHUB$YOUR_REPOS",
GITHUB$PUBLIC_REPOS = "GITHUB$PUBLIC_REPOS",
DOWNLOAD$PREPARING = "DOWNLOAD$PREPARING",
DOWNLOAD$DOWNLOADING = "DOWNLOAD$DOWNLOADING",
DOWNLOAD$FOUND_FILES = "DOWNLOAD$FOUND_FILES",
DOWNLOAD$SCANNING = "DOWNLOAD$SCANNING",
DOWNLOAD$FILES_PROGRESS = "DOWNLOAD$FILES_PROGRESS",
DOWNLOAD$CANCEL = "DOWNLOAD$CANCEL",
ACTION$CONFIRM = "ACTION$CONFIRM",
ACTION$REJECT = "ACTION$REJECT",
BADGE$BETA = "BADGE$BETA",
AGENT$RESUME_TASK = "AGENT$RESUME_TASK",
AGENT$PAUSE_TASK = "AGENT$PAUSE_TASK",
TOS$ACCEPT = "TOS$ACCEPT",
TOS$TERMS = "TOS$TERMS",
USER$ACCOUNT_SETTINGS = "USER$ACCOUNT_SETTINGS",
JUPYTER$OUTPUT_LABEL = "JUPYTER$OUTPUT_LABEL",
BUTTON$STOP = "BUTTON$STOP",
LANDING$ATTACH_IMAGES = "LANDING$ATTACH_IMAGES",
LANDING$OPEN_REPO = "LANDING$OPEN_REPO",
LANDING$IMPORT_PROJECT = "LANDING$IMPORT_PROJECT",
LANDING$UPLOAD_ZIP = "LANDING$UPLOAD_ZIP",
LANDING$RECENT_CONVERSATION = "LANDING$RECENT_CONVERSATION",
LANDING$OR = "LANDING$OR",
SUGGESTIONS$TEST_COVERAGE = "SUGGESTIONS$TEST_COVERAGE",
SUGGESTIONS$AUTO_MERGE = "SUGGESTIONS$AUTO_MERGE",
CHAT_INTERFACE$AGENT_STOPPED_MESSAGE = "CHAT_INTERFACE$AGENT_STOPPED_MESSAGE",
CHAT_INTERFACE$AGENT_FINISHED_MESSAGE = "CHAT_INTERFACE$AGENT_FINISHED_MESSAGE",
CHAT_INTERFACE$AGENT_REJECTED_MESSAGE = "CHAT_INTERFACE$AGENT_REJECTED_MESSAGE",
CHAT_INTERFACE$AGENT_ERROR_MESSAGE = "CHAT_INTERFACE$AGENT_ERROR_MESSAGE",
CHAT_INTERFACE$AGENT_AWAITING_USER_CONFIRMATION_MESSAGE = "CHAT_INTERFACE$AGENT_AWAITING_USER_CONFIRMATION_MESSAGE",
CHAT_INTERFACE$AGENT_ACTION_USER_CONFIRMED_MESSAGE = "CHAT_INTERFACE$AGENT_ACTION_USER_CONFIRMED_MESSAGE",
CHAT_INTERFACE$AGENT_ACTION_USER_REJECTED_MESSAGE = "CHAT_INTERFACE$AGENT_ACTION_USER_REJECTED_MESSAGE",
CHAT_INTERFACE$INPUT_PLACEHOLDER = "CHAT_INTERFACE$INPUT_PLACEHOLDER",
CHAT_INTERFACE$INPUT_CONTINUE_MESSAGE = "CHAT_INTERFACE$INPUT_CONTINUE_MESSAGE",
CHAT_INTERFACE$USER_ASK_CONFIRMATION = "CHAT_INTERFACE$USER_ASK_CONFIRMATION",
CHAT_INTERFACE$USER_CONFIRMED = "CHAT_INTERFACE$USER_CONFIRMED",
CHAT_INTERFACE$USER_REJECTED = "CHAT_INTERFACE$USER_REJECTED",
CHAT_INTERFACE$INPUT_SEND_MESSAGE_BUTTON_CONTENT = "CHAT_INTERFACE$INPUT_SEND_MESSAGE_BUTTON_CONTENT",
CHAT_INTERFACE$CHAT_MESSAGE_COPIED = "CHAT_INTERFACE$CHAT_MESSAGE_COPIED",
CHAT_INTERFACE$CHAT_MESSAGE_COPY_FAILED = "CHAT_INTERFACE$CHAT_MESSAGE_COPY_FAILED",
CHAT_INTERFACE$TOOLTIP_COPY_MESSAGE = "CHAT_INTERFACE$TOOLTIP_COPY_MESSAGE",
CHAT_INTERFACE$TOOLTIP_SEND_MESSAGE = "CHAT_INTERFACE$TOOLTIP_SEND_MESSAGE",
CHAT_INTERFACE$TOOLTIP_UPLOAD_IMAGE = "CHAT_INTERFACE$TOOLTIP_UPLOAD_IMAGE",
CHAT_INTERFACE$INITIAL_MESSAGE = "CHAT_INTERFACE$INITIAL_MESSAGE",
CHAT_INTERFACE$ASSISTANT = "CHAT_INTERFACE$ASSISTANT",
CHAT_INTERFACE$TO_BOTTOM = "CHAT_INTERFACE$TO_BOTTOM",
CHAT_INTERFACE$MESSAGE_ARIA_LABEL = "CHAT_INTERFACE$MESSAGE_ARIA_LABEL",
CHAT_INTERFACE$CHAT_CONVERSATION = "CHAT_INTERFACE$CHAT_CONVERSATION",
CHAT_INTERFACE$UNKNOWN_SENDER = "CHAT_INTERFACE$UNKNOWN_SENDER",
SECURITY_ANALYZER$UNKNOWN_RISK = "SECURITY_ANALYZER$UNKNOWN_RISK",
SECURITY_ANALYZER$LOW_RISK = "SECURITY_ANALYZER$LOW_RISK",
SECURITY_ANALYZER$MEDIUM_RISK = "SECURITY_ANALYZER$MEDIUM_RISK",
SECURITY_ANALYZER$HIGH_RISK = "SECURITY_ANALYZER$HIGH_RISK",
SETTINGS$MODEL_TOOLTIP = "SETTINGS$MODEL_TOOLTIP",
SETTINGS$AGENT_TOOLTIP = "SETTINGS$AGENT_TOOLTIP",
SETTINGS$LANGUAGE_TOOLTIP = "SETTINGS$LANGUAGE_TOOLTIP",
SETTINGS$DISABLED_RUNNING = "SETTINGS$DISABLED_RUNNING",
SETTINGS$API_KEY_PLACEHOLDER = "SETTINGS$API_KEY_PLACEHOLDER",
SETTINGS$CONFIRMATION_MODE = "SETTINGS$CONFIRMATION_MODE",
SETTINGS$CONFIRMATION_MODE_TOOLTIP = "SETTINGS$CONFIRMATION_MODE_TOOLTIP",
SETTINGS$AGENT_SELECT_ENABLED = "SETTINGS$AGENT_SELECT_ENABLED",
SETTINGS$SECURITY_ANALYZER = "SETTINGS$SECURITY_ANALYZER",
PLANNER$EMPTY_MESSAGE = "PLANNER$EMPTY_MESSAGE",
FEEDBACK$PUBLIC_LABEL = "FEEDBACK$PUBLIC_LABEL",
FEEDBACK$PRIVATE_LABEL = "FEEDBACK$PRIVATE_LABEL",
STATUS$STARTING_RUNTIME = "STATUS$STARTING_RUNTIME",
STATUS$STARTING_CONTAINER = "STATUS$STARTING_CONTAINER",
STATUS$PREPARING_CONTAINER = "STATUS$PREPARING_CONTAINER",
STATUS$CONTAINER_STARTED = "STATUS$CONTAINER_STARTED",
ACCOUNT_SETTINGS_MODAL$DISCONNECT = "ACCOUNT_SETTINGS_MODAL$DISCONNECT",
ACCOUNT_SETTINGS_MODAL$SAVE = "ACCOUNT_SETTINGS_MODAL$SAVE",
ACCOUNT_SETTINGS_MODAL$CLOSE = "ACCOUNT_SETTINGS_MODAL$CLOSE",
ACCOUNT_SETTINGS_MODAL$GITHUB_TOKEN_INVALID = "ACCOUNT_SETTINGS_MODAL$GITHUB_TOKEN_INVALID",
CONNECT_TO_GITHUB_MODAL$GET_YOUR_TOKEN = "CONNECT_TO_GITHUB_MODAL$GET_YOUR_TOKEN",
CONNECT_TO_GITHUB_MODAL$HERE = "CONNECT_TO_GITHUB_MODAL$HERE",
CONNECT_TO_GITHUB_MODAL$CONNECT = "CONNECT_TO_GITHUB_MODAL$CONNECT",
CONNECT_TO_GITHUB_MODAL$CLOSE = "CONNECT_TO_GITHUB_MODAL$CLOSE",
CONNECT_TO_GITHUB_BY_TOKEN_MODAL$BY_CONNECTING_YOU_AGREE = "CONNECT_TO_GITHUB_BY_TOKEN_MODAL$BY_CONNECTING_YOU_AGREE",
CONNECT_TO_GITHUB_BY_TOKEN_MODAL$CONTINUE = "CONNECT_TO_GITHUB_BY_TOKEN_MODAL$CONTINUE",
LOADING_PROJECT$LOADING = "LOADING_PROJECT$LOADING",
CUSTOM_INPUT$OPTIONAL_LABEL = "CUSTOM_INPUT$OPTIONAL_LABEL",
SETTINGS_FORM$CUSTOM_MODEL_LABEL = "SETTINGS_FORM$CUSTOM_MODEL_LABEL",
SETTINGS_FORM$BASE_URL_LABEL = "SETTINGS_FORM$BASE_URL_LABEL",
SETTINGS_FORM$API_KEY_LABEL = "SETTINGS_FORM$API_KEY_LABEL",
SETTINGS_FORM$DONT_KNOW_API_KEY_LABEL = "SETTINGS_FORM$DONT_KNOW_API_KEY_LABEL",
SETTINGS_FORM$CLICK_HERE_FOR_INSTRUCTIONS_LABEL = "SETTINGS_FORM$CLICK_HERE_FOR_INSTRUCTIONS_LABEL",
SETTINGS_FORM$AGENT_LABEL = "SETTINGS_FORM$AGENT_LABEL",
SETTINGS_FORM$SECURITY_ANALYZER_LABEL = "SETTINGS_FORM$SECURITY_ANALYZER_LABEL",
SETTINGS_FORM$ENABLE_CONFIRMATION_MODE_LABEL = "SETTINGS_FORM$ENABLE_CONFIRMATION_MODE_LABEL",
SETTINGS_FORM$SAVE_LABEL = "SETTINGS_FORM$SAVE_LABEL",
SETTINGS_FORM$CLOSE_LABEL = "SETTINGS_FORM$CLOSE_LABEL",
SETTINGS_FORM$RESET_TO_DEFAULTS_LABEL = "SETTINGS_FORM$RESET_TO_DEFAULTS_LABEL",
SETTINGS_FORM$CANCEL_LABEL = "SETTINGS_FORM$CANCEL_LABEL",
SETTINGS_FORM$END_SESSION_LABEL = "SETTINGS_FORM$END_SESSION_LABEL",
SETTINGS_FORM$CHANGING_WORKSPACE_WARNING_MESSAGE = "SETTINGS_FORM$CHANGING_WORKSPACE_WARNING_MESSAGE",
SETTINGS_FORM$ARE_YOU_SURE_LABEL = "SETTINGS_FORM$ARE_YOU_SURE_LABEL",
SETTINGS_FORM$ALL_INFORMATION_WILL_BE_DELETED_MESSAGE = "SETTINGS_FORM$ALL_INFORMATION_WILL_BE_DELETED_MESSAGE",
PROJECT_MENU_DETAILS_PLACEHOLDER$NEW_PROJECT_LABEL = "PROJECT_MENU_DETAILS_PLACEHOLDER$NEW_PROJECT_LABEL",
PROJECT_MENU_DETAILS_PLACEHOLDER$CONNECT_TO_GITHUB = "PROJECT_MENU_DETAILS_PLACEHOLDER$CONNECT_TO_GITHUB",
PROJECT_MENU_DETAILS_PLACEHOLDER$CONNECTED = "PROJECT_MENU_DETAILS_PLACEHOLDER$CONNECTED",
PROJECT_MENU_DETAILS$AGO_LABEL = "PROJECT_MENU_DETAILS$AGO_LABEL",
STATUS$ERROR_LLM_AUTHENTICATION = "STATUS$ERROR_LLM_AUTHENTICATION",
STATUS$ERROR_LLM_SERVICE_UNAVAILABLE = "STATUS$ERROR_LLM_SERVICE_UNAVAILABLE",
STATUS$ERROR_LLM_INTERNAL_SERVER_ERROR = "STATUS$ERROR_LLM_INTERNAL_SERVER_ERROR",
STATUS$ERROR_RUNTIME_DISCONNECTED = "STATUS$ERROR_RUNTIME_DISCONNECTED",
STATUS$LLM_RETRY = "STATUS$LLM_RETRY",
AGENT_ERROR$BAD_ACTION = "AGENT_ERROR$BAD_ACTION",
AGENT_ERROR$ACTION_TIMEOUT = "AGENT_ERROR$ACTION_TIMEOUT",
PROJECT_MENU_CARD_CONTEXT_MENU$CONNECT_TO_GITHUB_LABEL = "PROJECT_MENU_CARD_CONTEXT_MENU$CONNECT_TO_GITHUB_LABEL",
PROJECT_MENU_CARD_CONTEXT_MENU$PUSH_TO_GITHUB_LABEL = "PROJECT_MENU_CARD_CONTEXT_MENU$PUSH_TO_GITHUB_LABEL",
PROJECT_MENU_CARD_CONTEXT_MENU$DOWNLOAD_FILES_LABEL = "PROJECT_MENU_CARD_CONTEXT_MENU$DOWNLOAD_FILES_LABEL",
PROJECT_MENU_CARD$OPEN = "PROJECT_MENU_CARD$OPEN",
ACTION_BUTTON$RESUME = "ACTION_BUTTON$RESUME",
ACTION_BUTTON$PAUSE = "ACTION_BUTTON$PAUSE",
BROWSER$SCREENSHOT_ALT = "BROWSER$SCREENSHOT_ALT",
ERROR_TOAST$CLOSE_BUTTON_LABEL = "ERROR_TOAST$CLOSE_BUTTON_LABEL",
FILE_EXPLORER$UPLOAD = "FILE_EXPLORER$UPLOAD",
FILE_EXPLORER$REFRESH_WORKSPACE = "FILE_EXPLORER$REFRESH_WORKSPACE",
FILE_EXPLORER$OPEN_WORKSPACE = "FILE_EXPLORER$OPEN_WORKSPACE",
FILE_EXPLORER$CLOSE_WORKSPACE = "FILE_EXPLORER$CLOSE_WORKSPACE",
ACTION_MESSAGE$RUN = "ACTION_MESSAGE$RUN",
ACTION_MESSAGE$RUN_IPYTHON = "ACTION_MESSAGE$RUN_IPYTHON",
ACTION_MESSAGE$READ = "ACTION_MESSAGE$READ",
ACTION_MESSAGE$EDIT = "ACTION_MESSAGE$EDIT",
ACTION_MESSAGE$WRITE = "ACTION_MESSAGE$WRITE",
ACTION_MESSAGE$BROWSE = "ACTION_MESSAGE$BROWSE",
OBSERVATION_MESSAGE$RUN = "OBSERVATION_MESSAGE$RUN",
OBSERVATION_MESSAGE$RUN_IPYTHON = "OBSERVATION_MESSAGE$RUN_IPYTHON",
OBSERVATION_MESSAGE$READ = "OBSERVATION_MESSAGE$READ",
OBSERVATION_MESSAGE$EDIT = "OBSERVATION_MESSAGE$EDIT",
OBSERVATION_MESSAGE$WRITE = "OBSERVATION_MESSAGE$WRITE",
OBSERVATION_MESSAGE$BROWSE = "OBSERVATION_MESSAGE$BROWSE",
EXPANDABLE_MESSAGE$SHOW_DETAILS = "EXPANDABLE_MESSAGE$SHOW_DETAILS",
EXPANDABLE_MESSAGE$HIDE_DETAILS = "EXPANDABLE_MESSAGE$HIDE_DETAILS",
AI_SETTINGS$TITLE = "AI_SETTINGS$TITLE",
SETTINGS$DESCRIPTION = "SETTINGS$DESCRIPTION",
SETTINGS$WARNING = "SETTINGS$WARNING",
SIDEBAR$SETTINGS = "SIDEBAR$SETTINGS",
SIDEBAR$DOCS = "SIDEBAR$DOCS",
SUGGESTIONS$ADD_DOCS = "SUGGESTIONS$ADD_DOCS",
SUGGESTIONS$ADD_DOCKERFILE = "SUGGESTIONS$ADD_DOCKERFILE",
STATUS$CONNECTED = "STATUS$CONNECTED",
BROWSER$NO_PAGE_LOADED = "BROWSER$NO_PAGE_LOADED",
USER$AVATAR_PLACEHOLDER = "USER$AVATAR_PLACEHOLDER",
ACCOUNT_SETTINGS$SETTINGS = "ACCOUNT_SETTINGS$SETTINGS",
ACCOUNT_SETTINGS$LOGOUT = "ACCOUNT_SETTINGS$LOGOUT",
SETTINGS_FORM$ADVANCED_OPTIONS_LABEL = "SETTINGS_FORM$ADVANCED_OPTIONS_LABEL",
CONVERSATION$NO_CONVERSATIONS = "CONVERSATION$NO_CONVERSATIONS",
LANDING$SELECT_REPO = "LANDING$SELECT_REPO",
BUTTON$SEND = "BUTTON$SEND",
STATUS$WAITING_FOR_CLIENT = "STATUS$WAITING_FOR_CLIENT",
SUGGESTIONS$WHAT_TO_BUILD = "SUGGESTIONS$WHAT_TO_BUILD",
SETTINGS_FORM$ENABLE_DEFAULT_CONDENSER_SWITCH_LABEL = "SETTINGS_FORM$ENABLE_DEFAULT_CONDENSER_SWITCH_LABEL",
BUTTON$ENABLE_SOUND = "BUTTON$ENABLE_SOUND",
BUTTON$DISABLE_SOUND = "BUTTON$DISABLE_SOUND",
BUTTON$MARK_HELPFUL = "BUTTON$MARK_HELPFUL",
BUTTON$MARK_NOT_HELPFUL = "BUTTON$MARK_NOT_HELPFUL",
NOTIFICATION$SOUND_ENABLED = "NOTIFICATION$SOUND_ENABLED",
NOTIFICATION$SOUND_DISABLED = "NOTIFICATION$SOUND_DISABLED",
BUTTON$EXPORT_CONVERSATION = "BUTTON$EXPORT_CONVERSATION",
}

View File

@@ -4573,6 +4573,51 @@
"en": "Enable Memory Condenser",
"zh-TW": "啟用記憶體壓縮器"
},
"BUTTON$MARK_HELPFUL": {
"en": "Mark this solution as helpful",
"zh-CN": "标记此解决方案有帮助",
"zh-TW": "標記此解決方案有幫助",
"de": "Diese Lösung als hilfreich markieren",
"ko-KR": "이 해결책이 도움이 되었다고 표시",
"no": "Marker denne løsningen som nyttig",
"it": "Segna questa soluzione come utile",
"pt": "Marcar esta solução como útil",
"es": "Marcar esta solución como útil",
"ar": "وضع علامة على هذا الحل كمفيد",
"fr": "Marquer cette solution comme utile",
"tr": "Bu çözümü yararlı olarak işaretle",
"ja": "このソリューションが役立つと評価"
},
"BUTTON$MARK_NOT_HELPFUL": {
"en": "Mark this solution as not helpful",
"zh-CN": "标记此解决方案没有帮助",
"zh-TW": "標記此解決方案沒有幫助",
"de": "Diese Lösung als nicht hilfreich markieren",
"ko-KR": "이 해결책이 도움이 되지 않았다고 표시",
"no": "Marker denne løsningen som ikke nyttig",
"it": "Segna questa soluzione come non utile",
"pt": "Marcar esta solução como não útil",
"es": "Marcar esta solución como no útil",
"ar": "وضع علامة على هذا الحل كغير مفيد",
"fr": "Marquer cette solution comme non utile",
"tr": "Bu çözümü yararlı değil olarak işaretle",
"ja": "このソリューションが役立たないと評価"
},
"BUTTON$EXPORT_CONVERSATION": {
"en": "Export conversation",
"zh-CN": "导出对话",
"zh-TW": "導出對話",
"de": "Konversation exportieren",
"ko-KR": "대화 내보내기",
"no": "Eksporter samtale",
"it": "Esporta conversazione",
"pt": "Exportar conversa",
"es": "Exportar conversación",
"ar": "تصدير المحادثة",
"fr": "Exporter la conversation",
"tr": "Konuşmayı dışa aktar",
"ja": "会話をエクスポート"
},
"BILLING$CLICK_TO_TOP_UP": {
"en": "Add funds to Your Account",
"ja": "アカウントに資金を追加",

View File

@@ -20,6 +20,7 @@ export const MOCK_DEFAULT_USER_SETTINGS: ApiSettings | PostApiSettings = {
DEFAULT_SETTINGS.REMOTE_RUNTIME_RESOURCE_FACTOR,
github_token_is_set: DEFAULT_SETTINGS.GITHUB_TOKEN_IS_SET,
enable_default_condenser: DEFAULT_SETTINGS.ENABLE_DEFAULT_CONDENSER,
enable_sound_notifications: DEFAULT_SETTINGS.ENABLE_SOUND_NOTIFICATIONS,
user_consents_to_analytics: DEFAULT_SETTINGS.USER_CONSENTS_TO_ANALYTICS,
};

View File

@@ -104,6 +104,8 @@ function AccountSettings() {
formData.get("enable-analytics-switch")?.toString() === "on";
const enableMemoryCondenser =
formData.get("enable-memory-condenser-switch")?.toString() === "on";
const enableSoundNotifications =
formData.get("enable-sound-notifications-switch")?.toString() === "on";
saveSettings(
{
@@ -112,6 +114,7 @@ function AccountSettings() {
LANGUAGE: languageValue,
user_consents_to_analytics: userConsentsToAnalytics,
ENABLE_DEFAULT_CONDENSER: enableMemoryCondenser,
ENABLE_SOUND_NOTIFICATIONS: enableSoundNotifications,
LLM_MODEL: customLlmModel || fullLlmModel,
LLM_BASE_URL: formData.get("base-url-input")?.toString() || "",
LLM_API_KEY:
@@ -398,6 +401,14 @@ function AccountSettings() {
>
Enable analytics
</SettingsSwitch>
<SettingsSwitch
testId="enable-sound-notifications-switch"
name="enable-sound-notifications-switch"
defaultIsToggled={!!settings.ENABLE_SOUND_NOTIFICATIONS}
>
Enable sound notifications
</SettingsSwitch>
</section>
</div>
</form>

View File

@@ -13,6 +13,7 @@ export const DEFAULT_SETTINGS: Settings = {
REMOTE_RUNTIME_RESOURCE_FACTOR: 1,
GITHUB_TOKEN_IS_SET: false,
ENABLE_DEFAULT_CONDENSER: true,
ENABLE_SOUND_NOTIFICATIONS: false,
USER_CONSENTS_TO_ANALYTICS: false,
};

View File

@@ -9,6 +9,7 @@ export type Settings = {
REMOTE_RUNTIME_RESOURCE_FACTOR: number | null;
GITHUB_TOKEN_IS_SET: boolean;
ENABLE_DEFAULT_CONDENSER: boolean;
ENABLE_SOUND_NOTIFICATIONS: boolean;
USER_CONSENTS_TO_ANALYTICS: boolean | null;
};
@@ -23,6 +24,7 @@ export type ApiSettings = {
remote_runtime_resource_factor: number | null;
github_token_is_set: boolean;
enable_default_condenser: boolean;
enable_sound_notifications: boolean;
user_consents_to_analytics: boolean | null;
};

View File

@@ -0,0 +1,57 @@
let originalTitle = "";
let titleInterval: number | undefined;
const isBrowser =
typeof window !== "undefined" && typeof document !== "undefined";
export const browserTab = {
startNotification(message: string) {
if (!isBrowser) return;
// Store original title if not already stored
if (!originalTitle) {
originalTitle = document.title;
}
// Clear any existing interval
if (titleInterval) {
this.stopNotification();
}
// Alternate between original title and notification message
titleInterval = window.setInterval(() => {
document.title =
document.title === originalTitle ? message : originalTitle;
}, 1000);
// Set favicon to indicate notification
const favicon = document.querySelector(
'link[rel="icon"]',
) as HTMLLinkElement;
if (favicon) {
favicon.href = favicon.href.includes("?notification")
? favicon.href
: `${favicon.href}?notification`;
}
},
stopNotification() {
if (!isBrowser) return;
if (titleInterval) {
window.clearInterval(titleInterval);
titleInterval = undefined;
}
if (originalTitle) {
document.title = originalTitle;
}
// Reset favicon
const favicon = document.querySelector(
'link[rel="icon"]',
) as HTMLLinkElement;
if (favicon) {
favicon.href = favicon.href.replace("?notification", "");
}
},
};

View File

@@ -23,6 +23,7 @@ class Settings(BaseModel):
remote_runtime_resource_factor: int | None = None
github_token: SecretStr | None = None
enable_default_condenser: bool = False
enable_sound_notifications: bool = False
user_consents_to_analytics: bool | None = None
@field_serializer('llm_api_key')