haiku.rag/app/frontend/components/SettingsPanel.tsx
2026-01-16 15:03:41 +02:00

188 lines
4.2 KiB
TypeScript

"use client";
import { useCallback, useEffect, useState } from "react";
const STORAGE_KEY = "haiku.rag.settings.initial_context";
interface SettingsPanelProps {
isOpen: boolean;
onClose: () => void;
onSave: (initialContext: string) => void;
currentValue: string;
}
export default function SettingsPanel({
isOpen,
onClose,
onSave,
currentValue,
}: SettingsPanelProps) {
const [value, setValue] = useState(currentValue);
useEffect(() => {
if (isOpen) {
setValue(currentValue);
}
}, [isOpen, currentValue]);
const handleSave = useCallback(() => {
onSave(value);
onClose();
}, [value, onSave, onClose]);
const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => {
if (e.key === "Escape") {
onClose();
}
},
[onClose],
);
if (!isOpen) {
return null;
}
return (
<>
<style>{`
.settings-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.settings-modal {
background: white;
border-radius: 12px;
padding: 1.5rem;
width: 90%;
max-width: 600px;
max-height: 80vh;
overflow: auto;
position: relative;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.settings-modal-title {
margin: 0 0 0.5rem 0;
font-size: 1.25rem;
font-weight: 600;
color: #1e293b;
}
.settings-modal-description {
margin: 0 0 1rem 0;
font-size: 0.875rem;
color: #64748b;
line-height: 1.5;
}
.settings-textarea {
width: 100%;
min-height: 150px;
padding: 0.75rem;
border: 1px solid #e2e8f0;
border-radius: 8px;
font-size: 0.875rem;
font-family: inherit;
resize: vertical;
color: #334155;
line-height: 1.5;
}
.settings-textarea:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.settings-textarea::placeholder {
color: #94a3b8;
}
.settings-modal-actions {
display: flex;
justify-content: flex-end;
gap: 0.75rem;
margin-top: 1.25rem;
}
.settings-btn {
padding: 0.5rem 1rem;
font-size: 0.875rem;
font-weight: 500;
border-radius: 6px;
cursor: pointer;
transition: all 0.15s;
}
.settings-btn-cancel {
background: white;
color: #475569;
border: 1px solid #e2e8f0;
}
.settings-btn-cancel:hover {
background: #f8fafc;
border-color: #cbd5e1;
}
.settings-btn-save {
background: #3b82f6;
color: white;
border: none;
}
.settings-btn-save:hover {
background: #2563eb;
}
`}</style>
{/* biome-ignore lint/a11y/useKeyWithClickEvents: handled via onKeyDown on overlay */}
<div
className="settings-modal-overlay"
onClick={onClose}
onKeyDown={handleKeyDown}
role="dialog"
aria-modal="true"
aria-labelledby="settings-title"
>
{/* biome-ignore lint/a11y/noStaticElementInteractions: modal content wrapper */}
<div
className="settings-modal"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
>
<h2 id="settings-title" className="settings-modal-title">
Background Context
</h2>
<p className="settings-modal-description">
Provide background information that will be used throughout your
conversation. This helps the assistant understand domain-specific
context, terminology, or any relevant details about your questions.
</p>
<textarea
className="settings-textarea"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="e.g., Focus on Python programming concepts and best practices..."
autoFocus
/>
<div className="settings-modal-actions">
<button
type="button"
className="settings-btn settings-btn-cancel"
onClick={onClose}
>
Cancel
</button>
<button
type="button"
className="settings-btn settings-btn-save"
onClick={handleSave}
>
Save
</button>
</div>
</div>
</div>
</>
);
}
export { STORAGE_KEY };