mirror of
https://github.com/EthanMarti/infio-copilot.git
synced 2026-05-04 22:06:28 +00:00
init
This commit is contained in:
32
src/settings/components/CheckBoxSettingItem.tsx
Normal file
32
src/settings/components/CheckBoxSettingItem.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
43
src/settings/components/DropDownSettingItem.tsx
Normal file
43
src/settings/components/DropDownSettingItem.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
233
src/settings/components/FewShotExampleSettings.tsx
Normal file
233
src/settings/components/FewShotExampleSettings.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
34
src/settings/components/SettingsItem.tsx
Normal file
34
src/settings/components/SettingsItem.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
73
src/settings/components/SliderSettingsItem.tsx
Normal file
73
src/settings/components/SliderSettingsItem.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
50
src/settings/components/TextSettingItem.tsx
Normal file
50
src/settings/components/TextSettingItem.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
169
src/settings/components/TriggerSettings.tsx
Normal file
169
src/settings/components/TriggerSettings.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user