Toggle switch component (#4890)
* replace all toggle switches with toggle component * add variant, label, and description support for toggle component * refactor Toggle with subcomponents and JSDoc * use checked value from Toggle onChange callback * replace missed inline toggles with Toggle component * fix jsdoc to use optional props --------- Co-authored-by: Timothy Carambat <rambat1010@gmail.com>
This commit is contained in:
parent
fdeb7b9acf
commit
49a2b8f6f3
@ -6,6 +6,7 @@ import { TagsInput } from "react-tag-input-component";
|
||||
import { Info, Warning } from "@phosphor-icons/react";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
const DEFAULT_BRANCHES = ["main", "master"];
|
||||
export default function GitlabOptions() {
|
||||
@ -128,32 +129,18 @@ export default function GitlabOptions() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2 mb-3">
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="fetchIssues"
|
||||
value={true}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<div className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
<span className="ml-3 text-sm font-medium text-white">
|
||||
{t("connectors.gitlab.fetch_issues")}
|
||||
</span>
|
||||
</label>
|
||||
<Toggle
|
||||
name="fetchIssues"
|
||||
size="md"
|
||||
label={t("connectors.gitlab.fetch_issues")}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="fetchWikis"
|
||||
value={true}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<div className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
<span className="ml-3 text-sm font-medium text-white">
|
||||
Fetch Wikis as Documents
|
||||
</span>
|
||||
</label>
|
||||
<Toggle
|
||||
name="fetchWikis"
|
||||
size="md"
|
||||
label="Fetch Wikis as Documents"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<GitLabBranchSelection
|
||||
|
||||
@ -11,6 +11,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import { safeJsonParse } from "@/utils/request";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
import {
|
||||
USERNAME_MIN_LENGTH,
|
||||
USERNAME_MAX_LENGTH,
|
||||
@ -294,10 +295,9 @@ function AutoSubmitPreference() {
|
||||
setAutoSubmitSttInput(settings.autoSubmitSttInput ?? true);
|
||||
}, []);
|
||||
|
||||
const handleChange = (e) => {
|
||||
const newValue = e.target.checked;
|
||||
setAutoSubmitSttInput(newValue);
|
||||
Appearance.updateSettings({ autoSubmitSttInput: newValue });
|
||||
const handleChange = (checked) => {
|
||||
setAutoSubmitSttInput(checked);
|
||||
Appearance.updateSettings({ autoSubmitSttInput: checked });
|
||||
};
|
||||
|
||||
return (
|
||||
@ -317,19 +317,7 @@ function AutoSubmitPreference() {
|
||||
<Info size={16} weight="bold" className="text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-4">
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
id="autoSubmit"
|
||||
type="checkbox"
|
||||
name="autoSubmit"
|
||||
checked={autoSubmitSttInput}
|
||||
onChange={handleChange}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<div className="pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
</label>
|
||||
</div>
|
||||
<Toggle size="lg" enabled={autoSubmitSttInput} onChange={handleChange} />
|
||||
<Tooltip
|
||||
id="auto-submit-info"
|
||||
place="bottom"
|
||||
@ -352,10 +340,9 @@ function AutoSpeakPreference() {
|
||||
);
|
||||
}, []);
|
||||
|
||||
const handleChange = (e) => {
|
||||
const newValue = e.target.checked;
|
||||
setAutoPlayAssistantTtsResponse(newValue);
|
||||
Appearance.updateSettings({ autoPlayAssistantTtsResponse: newValue });
|
||||
const handleChange = (checked) => {
|
||||
setAutoPlayAssistantTtsResponse(checked);
|
||||
Appearance.updateSettings({ autoPlayAssistantTtsResponse: checked });
|
||||
};
|
||||
|
||||
return (
|
||||
@ -375,19 +362,11 @@ function AutoSpeakPreference() {
|
||||
<Info size={16} weight="bold" className="text-white" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-4">
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
id="autoSpeak"
|
||||
type="checkbox"
|
||||
name="autoSpeak"
|
||||
checked={autoPlayAssistantTtsResponse}
|
||||
onChange={handleChange}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<div className="pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
</label>
|
||||
</div>
|
||||
<Toggle
|
||||
size="lg"
|
||||
enabled={autoPlayAssistantTtsResponse}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<Tooltip
|
||||
id="auto-speak-info"
|
||||
place="bottom"
|
||||
|
||||
163
frontend/src/components/lib/Toggle/index.jsx
Normal file
163
frontend/src/components/lib/Toggle/index.jsx
Normal file
@ -0,0 +1,163 @@
|
||||
import { Info } from "@phosphor-icons/react";
|
||||
|
||||
const TOGGLE_STYLES = {
|
||||
sm: "h-[12px] w-[20px] after:h-[8px] after:w-[8px] after:top-[2px] after:left-[2px] peer-checked:after:translate-x-full",
|
||||
md: "h-[16px] w-[28px] after:h-[12px] after:w-[12px] after:top-[2px] after:left-[2px] peer-checked:after:translate-x-full",
|
||||
lg: "h-[19px] w-[36px] after:h-[15px] after:w-[15px] after:top-[2px] after:left-[2px] peer-checked:after:translate-x-[17px]",
|
||||
};
|
||||
|
||||
const LABEL_STYLES = {
|
||||
sm: {
|
||||
label: "text-[12px] leading-[10px] font-medium mt-[1.5px]",
|
||||
description: "text-[10px] leading-[16px] font-normal",
|
||||
gap: "gap-[2px]",
|
||||
},
|
||||
md: {
|
||||
label: "text-[14px] leading-[18px] font-medium -mt-[2px]",
|
||||
description: "text-[12px] leading-[16px] font-normal",
|
||||
gap: "gap-[2px]",
|
||||
},
|
||||
lg: {
|
||||
label: "text-[16px] leading-[14px] font-medium mt-[2.5px]",
|
||||
description: "text-[14px] leading-[24px] font-normal",
|
||||
gap: "gap-[2px]",
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Object} props - Component props
|
||||
* @param {string} [props.className] - Additional CSS classes
|
||||
* @param {boolean} [props.enabled] - Controlled checked state
|
||||
* @param {(checked: boolean) => void} [props.onChange] - Change handler receiving new checked state
|
||||
* @param {boolean} [props.disabled=false] - Whether toggle is disabled
|
||||
* @param {"sm" | "md" | "lg"} [props.size="sm"] - Toggle size
|
||||
* @param {string} [props.name] - Input name for form submission
|
||||
* @param {string} [props.label] - Label text next to toggle
|
||||
* @param {string} [props.description] - Description text below label
|
||||
* @param {"default" | "horizontal"} [props.variant="default"] - Layout variant
|
||||
* @param {string} [props.hint] - Tooltip ID for info icon hint next to label
|
||||
* @param {string} [props.value] - Input value for form submission
|
||||
*/
|
||||
export default function Toggle({
|
||||
className,
|
||||
enabled,
|
||||
onChange,
|
||||
disabled = false,
|
||||
size = "sm",
|
||||
name,
|
||||
label,
|
||||
description,
|
||||
variant = "default",
|
||||
hint,
|
||||
value,
|
||||
}) {
|
||||
const inputProps =
|
||||
enabled !== undefined
|
||||
? { checked: enabled, onChange: (e) => onChange?.(e.target.checked) }
|
||||
: { defaultChecked: false };
|
||||
|
||||
const labelStyles = LABEL_STYLES[size] || LABEL_STYLES.sm;
|
||||
|
||||
if (variant === "horizontal") {
|
||||
return (
|
||||
<label
|
||||
className={`flex items-start justify-between max-w-[700px] ${disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"} ${className ?? ""}`}
|
||||
>
|
||||
<TextContent
|
||||
label={label}
|
||||
description={description}
|
||||
labelStyles={labelStyles}
|
||||
hint={hint}
|
||||
/>
|
||||
<div className="shrink-0 ml-4">
|
||||
<ToggleSwitch
|
||||
name={name}
|
||||
disabled={disabled}
|
||||
size={size}
|
||||
inputProps={inputProps}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<label
|
||||
className={`inline-flex items-start ${disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"} ${className ?? ""}`}
|
||||
>
|
||||
<ToggleSwitch
|
||||
name={name}
|
||||
disabled={disabled}
|
||||
size={size}
|
||||
inputProps={inputProps}
|
||||
value={value}
|
||||
/>
|
||||
{(label || description) && (
|
||||
<div className="ml-3">
|
||||
<TextContent
|
||||
label={label}
|
||||
description={description}
|
||||
labelStyles={labelStyles}
|
||||
hint={hint}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
function ToggleSwitch({ name, disabled, size, inputProps, value }) {
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="checkbox"
|
||||
name={name}
|
||||
disabled={disabled}
|
||||
className="peer sr-only"
|
||||
value={value}
|
||||
{...inputProps}
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
relative shrink-0 peer pointer-events-none rounded-full
|
||||
${TOGGLE_STYLES[size] || TOGGLE_STYLES.sm}
|
||||
after:absolute after:rounded-full after:bg-white
|
||||
after:transition-all after:content-['']
|
||||
peer-focus:ring-2
|
||||
bg-zinc-500 light:bg-zinc-300 peer-focus:ring-zinc-700 light:peer-focus:bg-green-100 light:peer-focus:ring-green-200
|
||||
peer-checked:bg-green-400 peer-checked:peer-focus:bg-green-300 peer-checked:peer-focus:ring-green-900 light:peer-checked:peer-focus:bg-green-300 light:peer-checked:peer-focus:ring-green-200
|
||||
`}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function TextContent({ label, description, labelStyles = {}, hint }) {
|
||||
if (!label && !description) return null;
|
||||
return (
|
||||
<div className={`flex flex-col ${labelStyles.gap}`}>
|
||||
{label && (
|
||||
<span
|
||||
className={`flex items-center gap-x-1 text-white light:text-slate-950 ${labelStyles.label}`}
|
||||
>
|
||||
{label}
|
||||
{hint && (
|
||||
<Info
|
||||
size={14}
|
||||
className="text-theme-text-secondary cursor-pointer"
|
||||
data-tooltip-id={hint}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{description && (
|
||||
<span
|
||||
className={`text-zinc-400 light:text-zinc-600 ${labelStyles.description}`}
|
||||
>
|
||||
{description}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -11,6 +11,7 @@ import {
|
||||
BracketsCurly,
|
||||
} from "@phosphor-icons/react";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
import StartNode from "../nodes/StartNode";
|
||||
import ApiCallNode from "../nodes/ApiCallNode";
|
||||
import WebsiteNode from "../nodes/WebsiteNode";
|
||||
@ -172,32 +173,20 @@ export default function BlockList({
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{renderBlockConfigContent(block, props)}
|
||||
<div className="flex justify-between items-center pt-4 border-t border-white/10">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-theme-text-primary">
|
||||
Direct Output
|
||||
</label>
|
||||
<p className="text-xs text-theme-text-secondary">
|
||||
The output of this block will be returned directly to the chat.
|
||||
<br />
|
||||
This will prevent any further tool calls from being executed.
|
||||
</p>
|
||||
</div>
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={props.config.directOutput || false}
|
||||
onChange={(e) =>
|
||||
props.onConfigChange({
|
||||
...props.config,
|
||||
directOutput: e.target.checked,
|
||||
})
|
||||
}
|
||||
className="peer sr-only"
|
||||
aria-label="Toggle direct output"
|
||||
/>
|
||||
<div className="pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
</label>
|
||||
<div className="pt-4 border-t border-white/10">
|
||||
<Toggle
|
||||
size="md"
|
||||
variant="horizontal"
|
||||
label="Direct Output"
|
||||
description="The output of this block will be returned directly to the chat. This will prevent any further tool calls from being executed."
|
||||
enabled={props.config.directOutput || false}
|
||||
onChange={(checked) =>
|
||||
props.onConfigChange({
|
||||
...props.config,
|
||||
directOutput: checked,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { Info } from "@phosphor-icons/react";
|
||||
import React from "react";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
export default function WebScrapingNode({
|
||||
config,
|
||||
@ -72,35 +71,16 @@ export default function WebScrapingNode({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex flex-row items-center gap-x-1 mb-2">
|
||||
<label className="block text-sm font-medium text-theme-text-primary">
|
||||
Content Summarization
|
||||
</label>
|
||||
<Info
|
||||
size={16}
|
||||
className="text-theme-text-secondary cursor-pointer"
|
||||
data-tooltip-id="content-summarization-tooltip"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.enableSummarization ?? true}
|
||||
onChange={(e) =>
|
||||
onConfigChange({
|
||||
...config,
|
||||
enableSummarization: e.target.checked,
|
||||
})
|
||||
}
|
||||
className="sr-only peer"
|
||||
aria-label="Toggle content summarization"
|
||||
/>
|
||||
<div className="w-11 h-6 bg-theme-settings-input-bg peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-primary-button/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary-button"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<Toggle
|
||||
size="md"
|
||||
variant="horizontal"
|
||||
label="Content Summarization"
|
||||
hint="content-summarization-tooltip"
|
||||
enabled={config.enableSummarization ?? true}
|
||||
onChange={(checked) =>
|
||||
onConfigChange({ ...config, enableSummarization: checked })
|
||||
}
|
||||
/>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-theme-text-primary mb-2">
|
||||
Result Variable
|
||||
|
||||
@ -4,6 +4,7 @@ import showToast from "@/utils/toast";
|
||||
import { FlowArrow, Gear } from "@phosphor-icons/react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import paths from "@/utils/paths";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
function ManageFlowMenu({ flow, onDelete }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
@ -97,22 +98,17 @@ export default function FlowPanel({ flow, toggleFlow, onDelete }) {
|
||||
<>
|
||||
<div className="p-2">
|
||||
<div className="flex flex-col gap-y-[18px] max-w-[500px]">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<FlowArrow size={24} weight="bold" className="text-white" />
|
||||
<label htmlFor="name" className="text-white text-md font-bold">
|
||||
{flow.name}
|
||||
</label>
|
||||
<label className="border-none relative inline-flex items-center ml-auto cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="peer sr-only"
|
||||
checked={isActive}
|
||||
onChange={handleToggle}
|
||||
/>
|
||||
<div className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
<span className="ml-3 text-sm font-medium"></span>
|
||||
</label>
|
||||
<ManageFlowMenu flow={flow} onDelete={onDelete} />
|
||||
<div className="flex w-full justify-between items-center">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<FlowArrow size={24} weight="bold" className="text-white" />
|
||||
<label htmlFor="name" className="text-white text-md font-bold">
|
||||
{flow.name}
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Toggle size="lg" enabled={isActive} onChange={handleToggle} />
|
||||
<ManageFlowMenu flow={flow} onDelete={onDelete} />
|
||||
</div>
|
||||
</div>
|
||||
<p className="whitespace-pre-wrap text-white text-opacity-60 text-xs font-medium py-1.5">
|
||||
{flow.description || "No description provided"}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import { DefaultBadge } from "../Badges/default";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
export default function DefaultSkillPanel({
|
||||
title,
|
||||
@ -29,18 +30,11 @@ export default function DefaultSkillPanel({
|
||||
</label>
|
||||
<DefaultBadge title={title} />
|
||||
</div>
|
||||
<label
|
||||
className={`border-none relative inline-flex items-center ml-auto cursor-pointer`}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="peer sr-only"
|
||||
checked={enabled}
|
||||
onChange={() => toggleSkill(skill)}
|
||||
/>
|
||||
<div className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
<span className="ml-3 text-sm font-medium"></span>
|
||||
</label>
|
||||
<Toggle
|
||||
size="lg"
|
||||
enabled={enabled}
|
||||
onChange={() => toggleSkill(skill)}
|
||||
/>
|
||||
</div>
|
||||
<img src={image} alt={title} className="w-full rounded-md" />
|
||||
<p className="text-theme-text-secondary text-opacity-60 text-xs font-medium py-1.5">
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import React from "react";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
export default function GenericSkillPanel({
|
||||
title,
|
||||
@ -13,34 +14,27 @@ export default function GenericSkillPanel({
|
||||
return (
|
||||
<div className="p-2">
|
||||
<div className="flex flex-col gap-y-[18px] max-w-[500px]">
|
||||
<div className="flex items-center gap-x-2">
|
||||
{icon &&
|
||||
React.createElement(icon, {
|
||||
size: 24,
|
||||
color: "var(--theme-text-primary)",
|
||||
weight: "bold",
|
||||
})}
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="text-theme-text-primary text-md font-bold"
|
||||
>
|
||||
{title}
|
||||
</label>
|
||||
<label
|
||||
className={`border-none relative inline-flex items-center ml-auto ${
|
||||
disabled ? "cursor-not-allowed" : "cursor-pointer"
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
disabled={disabled}
|
||||
className="peer sr-only"
|
||||
checked={enabled}
|
||||
onChange={() => toggleSkill(skill)}
|
||||
/>
|
||||
<div className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
<span className="ml-3 text-sm font-medium"></span>
|
||||
</label>
|
||||
<div className="flex w-full justify-between items-center">
|
||||
<div className="flex items-center gap-x-2">
|
||||
{icon &&
|
||||
React.createElement(icon, {
|
||||
size: 24,
|
||||
color: "var(--theme-text-primary)",
|
||||
weight: "bold",
|
||||
})}
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="text-theme-text-primary text-md font-bold"
|
||||
>
|
||||
{title}
|
||||
</label>
|
||||
</div>
|
||||
<Toggle
|
||||
size="lg"
|
||||
enabled={enabled}
|
||||
disabled={disabled}
|
||||
onChange={() => toggleSkill(skill)}
|
||||
/>
|
||||
</div>
|
||||
<img src={image} alt={title} className="w-full rounded-md" />
|
||||
<p className="text-theme-text-secondary text-opacity-60 text-xs font-medium py-1.5">
|
||||
|
||||
@ -3,6 +3,7 @@ import showToast from "@/utils/toast";
|
||||
import { Gear, Plug } from "@phosphor-icons/react";
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { sentenceCase } from "text-case";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
/**
|
||||
* Converts setup_args to inputs for the form builder
|
||||
@ -110,25 +111,24 @@ export default function ImportedSkillConfig({
|
||||
<>
|
||||
<div className="p-2">
|
||||
<div className="flex flex-col gap-y-[18px] max-w-[500px]">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Plug size={24} weight="bold" className="text-white" />
|
||||
<label htmlFor="name" className="text-white text-md font-bold">
|
||||
{sentenceCase(config.name)}
|
||||
</label>
|
||||
<label className="border-none relative inline-flex items-center ml-auto cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="peer sr-only"
|
||||
checked={config.active}
|
||||
onChange={() => toggleSkill()}
|
||||
<div className="flex w-full justify-between items-center">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Plug size={24} weight="bold" className="text-white" />
|
||||
<label htmlFor="name" className="text-white text-md font-bold">
|
||||
{sentenceCase(config.name)}
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Toggle
|
||||
size="lg"
|
||||
enabled={config.active}
|
||||
onChange={toggleSkill}
|
||||
/>
|
||||
<div className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
<span className="ml-3 text-sm font-medium"></span>
|
||||
</label>
|
||||
<ManageSkillMenu
|
||||
config={config}
|
||||
setImportedSkills={setImportedSkills}
|
||||
/>
|
||||
<ManageSkillMenu
|
||||
config={config}
|
||||
setImportedSkills={setImportedSkills}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-white text-opacity-60 text-xs font-medium py-1.5">
|
||||
{config.description} by{" "}
|
||||
|
||||
@ -5,6 +5,7 @@ import { WarningOctagon, X } from "@phosphor-icons/react";
|
||||
import { DB_LOGOS } from "./DBConnection";
|
||||
import System from "@/models/system";
|
||||
import showToast from "@/utils/toast";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
function assembleConnectionString({
|
||||
engine,
|
||||
@ -288,21 +289,13 @@ export default function NewSQLConnection({
|
||||
)}
|
||||
|
||||
{engine === "sql-server" && (
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="encrypt"
|
||||
value="true"
|
||||
className="sr-only peer"
|
||||
checked={config.encrypt}
|
||||
/>
|
||||
<div className="w-11 h-6 bg-theme-settings-input-bg peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-800 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
||||
<span className="ml-3 text-sm font-medium text-white">
|
||||
Enable Encryption
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<Toggle
|
||||
name="encrypt"
|
||||
value="true"
|
||||
size="md"
|
||||
label="Enable Encryption"
|
||||
enabled={config.encrypt}
|
||||
/>
|
||||
)}
|
||||
|
||||
<p className="text-theme-text-secondary text-sm">
|
||||
|
||||
@ -5,6 +5,7 @@ import NewSQLConnection from "./NewConnectionModal";
|
||||
import { useModal } from "@/hooks/useModal";
|
||||
import SQLAgentImage from "@/media/agents/sql-agent.png";
|
||||
import Admin from "@/models/admin";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
export default function AgentSQLConnectorSelection({
|
||||
skill,
|
||||
@ -36,28 +37,25 @@ export default function AgentSQLConnectorSelection({
|
||||
<>
|
||||
<div className="p-2">
|
||||
<div className="flex flex-col gap-y-[18px] max-w-[500px]">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Database
|
||||
size={24}
|
||||
color="var(--theme-text-primary)"
|
||||
weight="bold"
|
||||
/>
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="text-theme-text-primary text-md font-bold"
|
||||
>
|
||||
SQL Agent
|
||||
</label>
|
||||
<label className="border-none relative inline-flex items-center ml-auto cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="peer sr-only"
|
||||
checked={enabled}
|
||||
onChange={() => toggleSkill(skill)}
|
||||
<div className="flex w-full justify-between items-center">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Database
|
||||
size={24}
|
||||
color="var(--theme-text-primary)"
|
||||
weight="bold"
|
||||
/>
|
||||
<div className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
<span className="ml-3 text-sm font-medium"></span>
|
||||
</label>
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="text-theme-text-primary text-md font-bold"
|
||||
>
|
||||
SQL Agent
|
||||
</label>
|
||||
</div>
|
||||
<Toggle
|
||||
size="lg"
|
||||
enabled={enabled}
|
||||
onChange={() => toggleSkill(skill)}
|
||||
/>
|
||||
</div>
|
||||
<img
|
||||
src={SQLAgentImage}
|
||||
|
||||
@ -17,6 +17,7 @@ import {
|
||||
X,
|
||||
ListMagnifyingGlass,
|
||||
} from "@phosphor-icons/react";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
import SearchProviderItem from "./SearchProviderItem";
|
||||
import WebSearchImage from "@/media/agents/scrape-websites.png";
|
||||
import {
|
||||
@ -170,28 +171,25 @@ export default function AgentWebSearchSelection({
|
||||
return (
|
||||
<div className="p-2">
|
||||
<div className="flex flex-col gap-y-[18px] max-w-[500px]">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<ListMagnifyingGlass
|
||||
size={24}
|
||||
color="var(--theme-text-primary)"
|
||||
weight="bold"
|
||||
/>
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="text-theme-text-primary text-md font-bold"
|
||||
>
|
||||
Live web search and browsing
|
||||
</label>
|
||||
<label className="border-none relative inline-flex items-center ml-auto cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="peer sr-only"
|
||||
checked={enabled}
|
||||
onChange={() => toggleSkill(skill)}
|
||||
<div className="flex w-full justify-between items-center">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<ListMagnifyingGlass
|
||||
size={24}
|
||||
color="var(--theme-text-primary)"
|
||||
weight="bold"
|
||||
/>
|
||||
<div className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
<span className="ml-3 text-sm font-medium"></span>
|
||||
</label>
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="text-theme-text-primary text-md font-bold"
|
||||
>
|
||||
Live web search and browsing
|
||||
</label>
|
||||
</div>
|
||||
<Toggle
|
||||
size="lg"
|
||||
enabled={enabled}
|
||||
onChange={() => toggleSkill(skill)}
|
||||
/>
|
||||
</div>
|
||||
<img
|
||||
src={WebSearchImage}
|
||||
|
||||
@ -4,6 +4,7 @@ import showToast from "@/utils/toast";
|
||||
import { ArrowSquareOut } from "@phosphor-icons/react";
|
||||
import { useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
export default function LiveSyncToggle({ enabled = false, onToggle }) {
|
||||
const [status, setStatus] = useState(enabled);
|
||||
@ -36,15 +37,7 @@ export default function LiveSyncToggle({ enabled = false, onToggle }) {
|
||||
<h2 className="text-theme-text-primary text-md font-bold">
|
||||
Automatic Document Content Sync
|
||||
</h2>
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
onClick={toggleFeatureFlag}
|
||||
checked={status}
|
||||
className="peer sr-only pointer-events-none"
|
||||
/>
|
||||
<div className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
</label>
|
||||
<Toggle size="lg" enabled={status} onChange={toggleFeatureFlag} />
|
||||
</div>
|
||||
<div className="flex flex-col space-y-4">
|
||||
<p className="text-theme-text-secondary text-sm">
|
||||
|
||||
@ -11,6 +11,7 @@ import NewUserModal from "./NewUserModal";
|
||||
import { useModal } from "@/hooks/useModal";
|
||||
import ModalWrapper from "@/components/ModalWrapper";
|
||||
import CTAButton from "@/components/lib/CTAButton";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
export default function AdminUsers() {
|
||||
const { isOpen, openModal, closeModal } = useModal();
|
||||
@ -147,31 +148,19 @@ export function MessageLimitInput({ enabled, limit, updateState, role }) {
|
||||
if (role === "admin") return null;
|
||||
return (
|
||||
<div className="mt-4 mb-8">
|
||||
<div className="flex flex-col gap-y-1">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<h2 className="text-base leading-6 font-bold text-white">
|
||||
Limit messages per day
|
||||
</h2>
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={enabled}
|
||||
onChange={(e) => {
|
||||
updateState((prev) => ({
|
||||
...prev,
|
||||
enabled: e.target.checked,
|
||||
}));
|
||||
}}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<div className="pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
</label>
|
||||
</div>
|
||||
<p className="text-xs leading-[18px] font-base text-white/60">
|
||||
Restrict this user to a number of successful queries or chats within a
|
||||
24 hour window.
|
||||
</p>
|
||||
</div>
|
||||
<Toggle
|
||||
size="md"
|
||||
variant="horizontal"
|
||||
label="Limit messages per day"
|
||||
description="Restrict this user to a number of successful queries or chats within a 24 hour window."
|
||||
enabled={enabled}
|
||||
onChange={(checked) => {
|
||||
updateState((prev) => ({
|
||||
...prev,
|
||||
enabled: checked,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
{enabled && (
|
||||
<div className="mt-4">
|
||||
<label className="text-white text-sm font-semibold block mb-4">
|
||||
|
||||
@ -3,6 +3,7 @@ import { X } from "@phosphor-icons/react";
|
||||
import Workspace from "@/models/workspace";
|
||||
import { TagsInput } from "react-tag-input-component";
|
||||
import Embed from "@/models/embed";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
export function enforceSubmissionSchema(form) {
|
||||
const data = {};
|
||||
@ -343,23 +344,14 @@ export const BooleanInput = ({ name, title, hint, defaultValue = null }) => {
|
||||
const [status, setStatus] = useState(defaultValue ?? false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-col mb-2">
|
||||
<label htmlFor={name} className="block text-sm font-medium text-white">
|
||||
{title}
|
||||
</label>
|
||||
<p className="text-theme-text-secondary text-xs">{hint}</p>
|
||||
</div>
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
name={name}
|
||||
type="checkbox"
|
||||
onClick={() => setStatus(!status)}
|
||||
checked={status}
|
||||
className="peer sr-only pointer-events-none"
|
||||
/>
|
||||
<div className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
</label>
|
||||
</div>
|
||||
<Toggle
|
||||
name={name}
|
||||
size="md"
|
||||
variant="horizontal"
|
||||
label={title}
|
||||
description={hint}
|
||||
enabled={status}
|
||||
onChange={(checked) => setStatus(checked)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -6,6 +6,7 @@ import System from "@/models/system";
|
||||
import PreLoader from "@/components/Preloader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ProviderPrivacy from "@/components/ProviderPrivacy";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
export default function PrivacyAndDataHandling() {
|
||||
const [settings, setSettings] = useState({});
|
||||
@ -80,18 +81,13 @@ function TelemetryLogs({ settings }) {
|
||||
<div className="space-y-6 flex h-full w-full">
|
||||
<div className="w-full flex flex-col gap-y-4">
|
||||
<div className="">
|
||||
<label className="mb-2.5 block font-medium text-theme-text-primary">
|
||||
{t("privacy.anonymous")}
|
||||
</label>
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
onClick={toggleTelemetry}
|
||||
checked={telemetry}
|
||||
className="peer sr-only pointer-events-none"
|
||||
/>
|
||||
<div className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
</label>
|
||||
<Toggle
|
||||
size="lg"
|
||||
className="mb-4"
|
||||
label={t("privacy.anonymous")}
|
||||
enabled={telemetry}
|
||||
onChange={toggleTelemetry}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -8,6 +8,7 @@ import { AUTH_TIMESTAMP, AUTH_TOKEN, AUTH_USER } from "@/utils/constants";
|
||||
import PreLoader from "@/components/Preloader";
|
||||
import CTAButton from "@/components/lib/CTAButton";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
import {
|
||||
USERNAME_MIN_LENGTH,
|
||||
USERNAME_MAX_LENGTH,
|
||||
@ -125,26 +126,19 @@ function MultiUserMode() {
|
||||
<div className="flex items-start justify-between px-6 py-4"></div>
|
||||
<div className="space-y-6 flex h-full w-full">
|
||||
<div className="w-full flex flex-col gap-y-4">
|
||||
<div className="">
|
||||
<label className="text-white text-sm font-semibold block mb-3">
|
||||
{multiUserModeEnabled
|
||||
? t("security.multiuser.enable.is-enable")
|
||||
: t("security.multiuser.enable.enable")}
|
||||
</label>
|
||||
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
onClick={() => setUseMultiUserMode(!useMultiUserMode)}
|
||||
defaultChecked={useMultiUserMode}
|
||||
className="peer sr-only pointer-events-none"
|
||||
/>
|
||||
<div
|
||||
hidden={multiUserModeEnabled}
|
||||
className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
{multiUserModeEnabled ? (
|
||||
<p className="text-white text-sm font-semibold">
|
||||
{t("security.multiuser.enable.is-enable")}
|
||||
</p>
|
||||
) : (
|
||||
<Toggle
|
||||
size="lg"
|
||||
className="mb-4"
|
||||
label={t("security.multiuser.enable.enable")}
|
||||
enabled={useMultiUserMode}
|
||||
onChange={(checked) => setUseMultiUserMode(checked)}
|
||||
/>
|
||||
)}
|
||||
{useMultiUserMode && (
|
||||
<div className="w-full flex flex-col gap-y-2 my-5">
|
||||
<div className="w-80">
|
||||
@ -307,21 +301,13 @@ function PasswordProtection() {
|
||||
<div className="flex items-start justify-between px-6 py-4"></div>
|
||||
<div className="space-y-6 flex h-full w-full">
|
||||
<div className="w-full flex flex-col gap-y-4">
|
||||
<div className="">
|
||||
<label className="text-white text-sm font-semibold block mb-3">
|
||||
{t("security.password.title")}
|
||||
</label>
|
||||
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
onClick={() => setUsePassword(!usePassword)}
|
||||
defaultChecked={usePassword}
|
||||
className="peer sr-only pointer-events-none"
|
||||
/>
|
||||
<div className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent" />
|
||||
</label>
|
||||
</div>
|
||||
<Toggle
|
||||
size="lg"
|
||||
className="mb-4"
|
||||
label={t("security.password.title")}
|
||||
enabled={usePassword}
|
||||
onChange={(checked) => setUsePassword(checked)}
|
||||
/>
|
||||
{usePassword && (
|
||||
<div className="w-full flex flex-col gap-y-2 my-5">
|
||||
<div className="mt-4 w-80">
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Appearance from "@/models/appearance";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
export default function AutoSpeak() {
|
||||
const [saving, setSaving] = useState(false);
|
||||
@ -8,15 +9,14 @@ export default function AutoSpeak() {
|
||||
useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChange = async (e) => {
|
||||
const newValue = e.target.checked;
|
||||
setAutoPlayAssistantTtsResponse(newValue);
|
||||
const handleChange = async (checked) => {
|
||||
setAutoPlayAssistantTtsResponse(checked);
|
||||
setSaving(true);
|
||||
try {
|
||||
Appearance.updateSettings({ autoPlayAssistantTtsResponse: newValue });
|
||||
Appearance.updateSettings({ autoPlayAssistantTtsResponse: checked });
|
||||
} catch (error) {
|
||||
console.error("Failed to update appearance settings:", error);
|
||||
setAutoPlayAssistantTtsResponse(!newValue);
|
||||
setAutoPlayAssistantTtsResponse(!checked);
|
||||
}
|
||||
setSaving(false);
|
||||
};
|
||||
@ -32,28 +32,16 @@ export default function AutoSpeak() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-0.5 my-4">
|
||||
<p className="text-sm leading-6 font-semibold text-white">
|
||||
{t("customization.chat.auto_speak.title")}
|
||||
</p>
|
||||
<p className="text-xs text-white/60">
|
||||
{t("customization.chat.auto_speak.description")}
|
||||
</p>
|
||||
<div className="flex items-center gap-x-4">
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
id="auto_speak"
|
||||
type="checkbox"
|
||||
name="auto_speak"
|
||||
value="yes"
|
||||
checked={autoPlayAssistantTtsResponse}
|
||||
onChange={handleChange}
|
||||
disabled={saving}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<div className="pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="my-4">
|
||||
<Toggle
|
||||
size="md"
|
||||
variant="horizontal"
|
||||
enabled={autoPlayAssistantTtsResponse}
|
||||
onChange={handleChange}
|
||||
disabled={saving}
|
||||
label={t("customization.chat.auto_speak.title")}
|
||||
description={t("customization.chat.auto_speak.description")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Appearance from "@/models/appearance";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
export default function AutoSubmit() {
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [autoSubmitSttInput, setAutoSubmitSttInput] = useState(true);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChange = async (e) => {
|
||||
const newValue = e.target.checked;
|
||||
setAutoSubmitSttInput(newValue);
|
||||
const handleChange = async (checked) => {
|
||||
setAutoSubmitSttInput(checked);
|
||||
setSaving(true);
|
||||
try {
|
||||
Appearance.updateSettings({ autoSubmitSttInput: newValue });
|
||||
Appearance.updateSettings({ autoSubmitSttInput: checked });
|
||||
} catch (error) {
|
||||
console.error("Failed to update appearance settings:", error);
|
||||
setAutoSubmitSttInput(!newValue);
|
||||
setAutoSubmitSttInput(!checked);
|
||||
}
|
||||
setSaving(false);
|
||||
};
|
||||
@ -29,28 +29,16 @@ export default function AutoSubmit() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-0.5 my-4">
|
||||
<p className="text-sm leading-6 font-semibold text-white">
|
||||
{t("customization.chat.auto_submit.title")}
|
||||
</p>
|
||||
<p className="text-xs text-white/60">
|
||||
{t("customization.chat.auto_submit.description")}
|
||||
</p>
|
||||
<div className="flex items-center gap-x-4">
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
id="auto_submit"
|
||||
type="checkbox"
|
||||
name="auto_submit"
|
||||
value="yes"
|
||||
checked={autoSubmitSttInput}
|
||||
onChange={handleChange}
|
||||
disabled={saving}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<div className="pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="my-4">
|
||||
<Toggle
|
||||
size="md"
|
||||
variant="horizontal"
|
||||
enabled={autoSubmitSttInput}
|
||||
onChange={handleChange}
|
||||
disabled={saving}
|
||||
label={t("customization.chat.auto_submit.title")}
|
||||
description={t("customization.chat.auto_submit.description")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Appearance from "@/models/appearance";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
export default function ChatRenderHTML() {
|
||||
const { t } = useTranslation();
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [renderHTML, setRenderHTML] = useState(false);
|
||||
|
||||
const handleChange = async (e) => {
|
||||
const newValue = e.target.checked;
|
||||
setRenderHTML(newValue);
|
||||
const handleChange = async (checked) => {
|
||||
setRenderHTML(checked);
|
||||
setSaving(true);
|
||||
try {
|
||||
Appearance.updateSettings({ renderHTML: newValue });
|
||||
Appearance.updateSettings({ renderHTML: checked });
|
||||
} catch (error) {
|
||||
console.error("Failed to update appearance settings:", error);
|
||||
setRenderHTML(!newValue);
|
||||
setRenderHTML(!checked);
|
||||
}
|
||||
setSaving(false);
|
||||
};
|
||||
@ -29,28 +29,16 @@ export default function ChatRenderHTML() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-0.5 my-4">
|
||||
<p className="text-sm leading-6 font-semibold text-white">
|
||||
{t("customization.items.render-html.title")}
|
||||
</p>
|
||||
<p className="text-xs text-white/60 w-1/2 whitespace-pre-line">
|
||||
{t("customization.items.render-html.description")}
|
||||
</p>
|
||||
<div className="flex items-center gap-x-4 pt-1">
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
id="render_html"
|
||||
type="checkbox"
|
||||
name="render_html"
|
||||
value="yes"
|
||||
checked={renderHTML}
|
||||
onChange={handleChange}
|
||||
disabled={saving}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<div className="pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="my-4">
|
||||
<Toggle
|
||||
size="md"
|
||||
variant="horizontal"
|
||||
enabled={renderHTML}
|
||||
onChange={handleChange}
|
||||
disabled={saving}
|
||||
label={t("customization.items.render-html.title")}
|
||||
description={t("customization.items.render-html.description")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Appearance from "@/models/appearance";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
export default function ShowScrollbar() {
|
||||
const { t } = useTranslation();
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [showScrollbar, setShowScrollbar] = useState(false);
|
||||
|
||||
const handleChange = async (e) => {
|
||||
const newValue = e.target.checked;
|
||||
setShowScrollbar(newValue);
|
||||
const handleChange = async (checked) => {
|
||||
setShowScrollbar(checked);
|
||||
setSaving(true);
|
||||
try {
|
||||
Appearance.updateSettings({ showScrollbar: newValue });
|
||||
Appearance.updateSettings({ showScrollbar: checked });
|
||||
} catch (error) {
|
||||
console.error("Failed to update appearance settings:", error);
|
||||
setShowScrollbar(!newValue);
|
||||
setShowScrollbar(!checked);
|
||||
}
|
||||
setSaving(false);
|
||||
};
|
||||
@ -29,28 +29,16 @@ export default function ShowScrollbar() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-0.5 my-4">
|
||||
<p className="text-sm leading-6 font-semibold text-white">
|
||||
{t("customization.items.show-scrollbar.title")}
|
||||
</p>
|
||||
<p className="text-xs text-white/60">
|
||||
{t("customization.items.show-scrollbar.description")}
|
||||
</p>
|
||||
<div className="flex items-center gap-x-4">
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
id="show_scrollbar"
|
||||
type="checkbox"
|
||||
name="show_scrollbar"
|
||||
value="yes"
|
||||
checked={showScrollbar}
|
||||
onChange={handleChange}
|
||||
disabled={saving}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<div className="pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="my-4">
|
||||
<Toggle
|
||||
size="md"
|
||||
variant="horizontal"
|
||||
enabled={showScrollbar}
|
||||
onChange={handleChange}
|
||||
disabled={saving}
|
||||
label={t("customization.items.show-scrollbar.title")}
|
||||
description={t("customization.items.show-scrollbar.description")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState } from "react";
|
||||
import Appearance from "@/models/appearance";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Toggle from "@/components/lib/Toggle";
|
||||
|
||||
export default function SpellCheck() {
|
||||
const { t } = useTranslation();
|
||||
@ -9,42 +10,29 @@ export default function SpellCheck() {
|
||||
Appearance.get("enableSpellCheck")
|
||||
);
|
||||
|
||||
const handleChange = async (e) => {
|
||||
const newValue = e.target.checked;
|
||||
setEnableSpellCheck(newValue);
|
||||
const handleChange = async (checked) => {
|
||||
setEnableSpellCheck(checked);
|
||||
setSaving(true);
|
||||
try {
|
||||
Appearance.set("enableSpellCheck", newValue);
|
||||
Appearance.set("enableSpellCheck", checked);
|
||||
} catch (error) {
|
||||
console.error("Failed to update appearance settings:", error);
|
||||
setEnableSpellCheck(!newValue);
|
||||
setEnableSpellCheck(!checked);
|
||||
}
|
||||
setSaving(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-0.5 my-4">
|
||||
<p className="text-sm leading-6 font-semibold text-white">
|
||||
{t("customization.chat.spellcheck.title")}
|
||||
</p>
|
||||
<p className="text-xs text-white/60">
|
||||
{t("customization.chat.spellcheck.description")}
|
||||
</p>
|
||||
<div className="flex items-center gap-x-4">
|
||||
<label className="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
id="spellcheck"
|
||||
type="checkbox"
|
||||
name="spellcheck"
|
||||
value="yes"
|
||||
checked={enableSpellCheck}
|
||||
onChange={handleChange}
|
||||
disabled={saving}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<div className="pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="my-4">
|
||||
<Toggle
|
||||
size="md"
|
||||
variant="horizontal"
|
||||
enabled={enableSpellCheck}
|
||||
onChange={handleChange}
|
||||
disabled={saving}
|
||||
label={t("customization.chat.spellcheck.title")}
|
||||
description={t("customization.chat.spellcheck.description")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user