56 lines
2.0 KiB
TypeScript
56 lines
2.0 KiB
TypeScript
import { useState } from "react";
|
|
import { patch } from "../api";
|
|
import { useT } from "../i18n";
|
|
|
|
/** Instructions for one chat: added to the system prompt of every turn. */
|
|
export function InstructionsDialog({ sessionId, value, onClose }: { sessionId: string; value: string; onClose: () => void }) {
|
|
const t = useT();
|
|
const [text, setText] = useState(value);
|
|
const [busy, setBusy] = useState(false);
|
|
const [error, setError] = useState<string>();
|
|
|
|
const save = async (next: string) => {
|
|
setBusy(true);
|
|
try {
|
|
await patch(`/sessions/${sessionId}`, { instructions: next });
|
|
onClose();
|
|
} catch (e: any) {
|
|
setError(e.message);
|
|
setBusy(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="modal-backdrop" onClick={onClose}>
|
|
<div className="modal instructions-modal" role="dialog" aria-label={t("instructions.title")} onClick={(e) => e.stopPropagation()}>
|
|
<h3>{t("instructions.title")}</h3>
|
|
<div className="hint" style={{ marginBottom: 8 }}>
|
|
{t("instructions.hint")}
|
|
</div>
|
|
<textarea
|
|
autoFocus
|
|
rows={10}
|
|
style={{ width: "100%" }}
|
|
value={text}
|
|
placeholder={t("instructions.placeholder")}
|
|
onChange={(e) => setText(e.target.value)}
|
|
onKeyDown={(e) => (e.ctrlKey || e.metaKey) && e.key === "Enter" && !busy && void save(text)}
|
|
/>
|
|
{error && <div className="notice err">{error}</div>}
|
|
<div className="row" style={{ marginTop: 10, justifyContent: "flex-end", gap: 6 }}>
|
|
{value && (
|
|
<button className="danger" onClick={() => void save("")} disabled={busy}>
|
|
{t("instructions.clear")}
|
|
</button>
|
|
)}
|
|
<span className="spacer" />
|
|
<button onClick={onClose}>{t("common.cancel")}</button>
|
|
<button className="primary" onClick={() => void save(text)} disabled={busy || text.trim() === value.trim()}>
|
|
{t("common.save")}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|