This commit is contained in:
duanfuxiang
2025-01-05 11:51:39 +08:00
commit 0c7ee142cb
215 changed files with 20611 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
import * as React from "react";
import SettingsItem from "./SettingsItem";
interface IProps {
name: string;
description: string;
setEnabled(value: boolean): void;
enabled: boolean;
}
export default function CheckBoxSettingItem(props: IProps): React.JSX.Element {
const { enabled, setEnabled, name, description } = props;
const checkContainerClasses = ["checkbox-container"];
if (enabled) {
checkContainerClasses.push("is-enabled");
}
const onClick = () => {
setEnabled(!enabled);
};
return (
<SettingsItem name={name} description={description}>
<div onClick={onClick} className={checkContainerClasses.join(" ")}>
<input type="checkbox" tabIndex={0} />
</div>
</SettingsItem>
);
}

View File

@@ -0,0 +1,43 @@
import * as React from "react";
import SettingsItem from "./SettingsItem";
interface IProps {
name: string;
description: string;
value: string;
setValue(value: string): void;
options: { [key: string]: string };
errorMessage?: string;
disabled?: boolean;
}
export default function DropDownSettingItem(props: IProps): React.JSX.Element {
const { name, description, errorMessage } = props;
const onChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
props.setValue(e.target.value);
};
return (
<SettingsItem
name={name}
description={description}
errorMessage={errorMessage}
>
<select
className="dropdown"
value={props.value}
onChange={onChange}
disabled={props.disabled}
>
{Object.entries(props.options).map(([key, value]) => (
<option key={key} value={key}>
{value}
</option>
))}
</select>
</SettingsItem>
);
}

View File

@@ -0,0 +1,233 @@
import { Notice } from "obsidian";
import * as React from "react";
import Context from "../../core/autocomplete/context-detection";
import { FewShotExample } from "../versions";
interface IProps {
name: string;
description: string;
errorMessages: Map<string, string>;
fewShotExamples: FewShotExample[];
setFewShotExamples(fewShotExamples: FewShotExample[]): void;
}
export default function FewShotExampleSettings(
props: IProps
): React.JSX.Element {
const onClickRemoveButton = (index: number) => {
return () => {
const newFewShotExamples = props.fewShotExamples
.slice(0, index)
.concat(props.fewShotExamples.slice(index + 1));
props.setFewShotExamples(newFewShotExamples);
};
};
const onClickAddButton = () => {
const newFewShotExamples = [
{
context: Context.Text,
input: "TODO",
answer: "Thought: TODO\nAnswer: TODO",
},
...props.fewShotExamples,
];
props.setFewShotExamples(newFewShotExamples);
};
const onChangeContext = (index: number) => {
return (e: React.ChangeEvent<HTMLSelectElement>) => {
const newFewShotExamples = [...props.fewShotExamples];
const context = Context.get(e.target.value);
if (context === undefined) {
new Notice("Invalid context");
return;
}
newFewShotExamples[index] = {
...newFewShotExamples[index],
context,
};
props.setFewShotExamples(newFewShotExamples);
};
};
const onChangeInput = (index: number) => {
return (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const newFewShotExamples = [...props.fewShotExamples];
newFewShotExamples[index] = {
...newFewShotExamples[index],
input: e.target.value,
};
props.setFewShotExamples(newFewShotExamples);
};
};
const onAnswerInput = (index: number) => {
return (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const newFewShotExamples = [...props.fewShotExamples];
newFewShotExamples[index] = {
...newFewShotExamples[index],
answer: e.target.value,
};
props.setFewShotExamples(newFewShotExamples);
};
};
return (
<div>
<div className="setting-item" style={{ display: "flex" }}>
<div className="setting-item-info">
<div className="setting-item-name">{props.name}</div>
<div className="setting-item-description">
{props.description}
</div>
{props.errorMessages.get("fewShotExamples") !== undefined && (
<div className="setting-item-description ">
<span className={"mod-warning"}>
{props.errorMessages.get("fewShotExamples")}
</span>
</div>
)}
</div>
<div style={{ top: 0, right: 0 }}>
<span
className="clickable-icon setting-editor-extra-setting-button"
aria-label="Add"
onClick={onClickAddButton}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="svg-icon lucide-plus"
>
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
</span>
</div>
</div>
{props.fewShotExamples.map((example, index) => (
<div
key={`few-shot-example-${index}`}
style={{
borderBottom:
"1px solid var(--background-modifier-border)",
}}
>
<div className="setting-item" style={{ display: "flex" }}>
<div className="setting-item-info">
<div className="setting-item-name">
Example {index + 1}
</div>
</div>
<div style={{ top: 0, right: 0 }}>
<span
className="clickable-icon setting-editor-extra-setting-button"
aria-label="Remove"
onClick={onClickRemoveButton(index)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="svg-icon lucide-x"
>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</span>
</div>
</div>
<div
className="setting-item"
style={{ display: "block", borderTop: 0 }}
>
<div className="setting-item-control">
<div
className="setting-item-name"
style={{ textAlign: "left", width: "100%" }}
>
Context
</div>
<select
className="dropdown"
value={example.context}
onChange={onChangeContext(index)}
>
{Context.values().map((key) => (
<option key={key} value={key}>
{key}
</option>
))}
</select>
</div>
<div
className="setting-item-control"
style={{ display: "block" }}
>
<div
className="setting-item-name"
style={{ width: "100%", textAlign: "left" }}
>
Human Message
</div>
{props.errorMessages.get(`fewShotExamples.${index}.input`) !== undefined && (
<div className="setting-item-description" style={{ width: "100%", textAlign: "left" }}>
<span className={"mod-warning"}>
{props.errorMessages.get(`fewShotExamples.${index}.input`)}
</span>
</div>
)}
<textarea
className="infio-autocomplete-setting-item-textarea"
rows={5}
style={{ width: "100%" }}
value={example.input}
onChange={onChangeInput(index)}
/>
<div
className="setting-item-name"
style={{ width: "100%", textAlign: "left" }}
>
Assistant Message
</div>
{props.errorMessages.get(`fewShotExamples.${index}.answer`) !== undefined && (
<div className="setting-item-description" style={{ width: "100%", textAlign: "left" }}>
<span className={"mod-warning"}>
{props.errorMessages.get(`fewShotExamples.${index}.answer`)}
</span>
</div>
)}
<textarea
className="infio-autocomplete-setting-item-textarea"
rows={5}
style={{ width: "100%" }}
value={example.answer}
onChange={onAnswerInput(index)}
/>
</div>
</div>
</div>
))}
</div>
);
}

View File

@@ -0,0 +1,34 @@
import * as React from "react";
interface IProps {
name: string;
description: string | React.ReactNode;
errorMessage?: string;
children: React.ReactNode;
display?: "block" | "inline-block" | "flex";
}
export default function SettingsItem({
name,
description,
children,
errorMessage,
display = "flex",
}: IProps): React.JSX.Element {
return (
<div className="setting-item" style={{ display: display }}>
<div className="setting-item-info">
<div className="setting-item-name">{name}</div>
<div className="setting-item-description">{description}</div>
{errorMessage !== undefined && (
<div className="setting-item-description">
<span className={"mod-warning"}>{errorMessage}</span>
</div>
)}
</div>
<div className="setting-item-control">{children}</div>
</div>
);
}

View File

@@ -0,0 +1,73 @@
import * as React from "react";
import { useRef } from "react";
import SettingsItem from "./SettingsItem";
interface IProps {
name: string;
description: string;
setValue(value: number): void;
errorMessage?: string;
suffix?: string;
value: number;
min: number;
max: number;
step: number;
}
export default function SliderSettingsItem(props: IProps): React.JSX.Element {
const { errorMessage } = props;
const [isFocused, setIsFocused] = React.useState<boolean>(false);
const sliderRef = useRef<HTMLInputElement>(null);
const onValueChange = (e: React.ChangeEvent<HTMLInputElement>) => {
let value = Number(e.target.value);
if (isNaN(value)) {
return;
}
value = Math.min(props.max, Math.max(props.min, value));
props.setValue(value);
};
const displayValue = props.value + (props.suffix ? props.suffix : "");
return (
<SettingsItem
name={props.name}
description={props.description}
errorMessage={errorMessage}
>
<input
ref={sliderRef}
onChange={onValueChange}
className="slider"
type="range"
min={props.min}
max={props.max}
step={props.step}
value={props.value}
onMouseEnter={() => setIsFocused(true)}
onMouseLeave={() => setIsFocused(false)}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
/>
{isFocused && sliderRef.current !== null && (
<div
className="tooltip mod-top"
style={{
top: sliderRef.current.getBoundingClientRect().top - 30,
left:
sliderRef.current.getBoundingClientRect().left +
sliderRef.current.getBoundingClientRect().width / 2,
}}
>
{displayValue}
</div>
)}
</SettingsItem>
);
}

View File

@@ -0,0 +1,50 @@
import * as React from "react";
import SettingsItem from "./SettingsItem";
interface IProps {
name: string;
description: string;
placeholder: string;
setValue(value: string): void;
errorMessage?: string;
value: string;
password?: boolean;
disabled?: boolean;
}
export default function TextSettingItem(props: IProps): React.JSX.Element {
const {
value,
setValue,
password,
placeholder,
name,
description,
errorMessage,
} = props;
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
setValue(newValue);
};
return (
<SettingsItem
name={name}
description={description}
errorMessage={errorMessage}
>
<input
type={password ? "password" : "text"}
placeholder={placeholder}
onChange={onChange}
value={value}
disabled={props.disabled}
/>
</SettingsItem>
);
}

View File

@@ -0,0 +1,169 @@
import * as React from "react";
import { Trigger } from "../versions";
interface IProps {
name: string;
description: string;
triggers: Trigger[];
setValues(value: Trigger[]): void;
errorMessage?: string;
errorMessages: Map<string, string>;
}
function TriggerSettings(props: IProps): React.JSX.Element {
const { name, triggers, description, setValues, errorMessage } = props;
const onClickAddButton = () => {
setValues([{ value: "TODO...", type: "string" }, ...triggers]);
};
const onClickRemoveButton = (index: number) => {
return () => {
const newTriggers = triggers
.slice(0, index)
.concat(triggers.slice(index + 1));
setValues(newTriggers);
};
};
const onChangeType = (index: number) => {
return (e: React.ChangeEvent<HTMLSelectElement>) => {
if (e.target.value === "regex" || e.target.value === "string") {
const value = triggers[index].value;
const newTriggers = [...triggers];
newTriggers[index] = { type: e.target.value as Trigger["type"], value };
setValues(newTriggers);
}
};
}
const onChangeValue = (index: number) => {
return (e: React.ChangeEvent<HTMLInputElement>) => {
const type = triggers[index].type;
const newTriggers = [...triggers];
newTriggers[index] = { type, value: decodeHiddenCharacters(e.target.value) };
setValues(newTriggers);
};
};
return (
<>
<div className="setting-item">
<div className="setting-item-info">
<div className="setting-item-name">{name}</div>
<div className="setting-item-description">
{description}
</div>
{props.errorMessages.get("triggerWords") !== undefined && (
<div className="setting-item-description ">
<span className={"mod-warning"}>
{errorMessage}
</span>
</div>
)}
</div>
<div className="setting-item-control">
<div
className="clickable-icon setting-editor-extra-setting-button"
aria-label="Add"
onClick={onClickAddButton}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="svg-icon lucide-plus"
>
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
</div>
</div>
</div>
{triggers.map((trigger: Trigger, index: number) => (
<div
className="infio-autocomplete-setting-list-item"
key={`setting-list-item-${name.replace(" ", "-")}-${index}`}
>
{(props.errorMessages.get(`triggers.${index}.value`) !== undefined || props.errorMessages.get(`triggers.${index}.type`) !== undefined) && (
<div className="setting-item-description" style={{ width: "100%", textAlign: "left" }}>
{props.errorMessages.get(`triggers.${index}.value`) !== undefined && (
<span className={"mod-warning"}>
{props.errorMessages.get(`triggers.${index}.value`)}
</span>
)}
{props.errorMessages.get(`triggers.${index}.type`) !== undefined && (
<span className={"mod-warning"}>
{props.errorMessages.get(`triggers.${index}.type`)}
</span>
)}
</div>
)}
<div className="setting-item-info">
<div className="setting-item-control">
<select
className="dropdown"
value={trigger.type}
onChange={onChangeType(index)}
>
<option value={"string"}>
string
</option>
<option value={"regex"}>
regex
</option>
</select>
<input
style={{ whiteSpace: "pre-wrap" }}
type="text"
placeholder={"TODO..."}
value={encodeHiddenCharacters(trigger.value)}
onChange={onChangeValue(index)}
/>
<div
className="clickable-icon setting-editor-extra-setting-button"
aria-label="Remove"
onClick={onClickRemoveButton(index)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="svg-icon lucide-x"
>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</div>
</div>
</div>
</div>
))}
</>
);
}
function encodeHiddenCharacters(value: string) {
return value.replace("\t", "\\t").replace("\n", "\\n");
}
function decodeHiddenCharacters(value: string) {
return value.replace("\\t", "\t").replace("\\n", "\n");
}
export default TriggerSettings;