md-first memory extraction framework for AI agents. Markdown is the single source of truth; SQLite holds state and LanceDB provides the rebuildable vector + BM25 + scalar index. The codebase follows a single-direction DDD layering (entrypoints -> service -> memory -> infra, with component / core / config cross-cutting) enforced by import-linter. Engineering surface: - Coding conventions in .claude/rules/ (path-scoped) and workflows in .claude/skills/ (/commit, /new-branch, /pr). - GitHub Actions CI runs make lint + test + integration; pre-commit mirrors the gates locally (ruff, hygiene hooks, gitlint commit-msg). - Commit messages follow Conventional Commits, enforced by gitlint. - make lint also enforces datetime two-zone discipline and OpenAPI drift.
60 lines
1.5 KiB
TypeScript
60 lines
1.5 KiB
TypeScript
import { useState, useRef, useEffect } from 'react';
|
|
|
|
interface ChatInputProps {
|
|
onSend: (message: string) => void;
|
|
disabled: boolean;
|
|
}
|
|
|
|
export function ChatInput({ onSend, disabled }: ChatInputProps) {
|
|
const [input, setInput] = useState('');
|
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (input.trim() && !disabled) {
|
|
onSend(input.trim());
|
|
setInput('');
|
|
}
|
|
};
|
|
|
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
e.preventDefault();
|
|
handleSubmit(e);
|
|
}
|
|
};
|
|
|
|
// Auto-resize textarea
|
|
useEffect(() => {
|
|
if (textareaRef.current) {
|
|
textareaRef.current.style.height = 'auto';
|
|
const scrollHeight = textareaRef.current.scrollHeight;
|
|
const maxHeight = 5 * 24; // 5 lines * 24px line height
|
|
textareaRef.current.style.height = `${Math.min(scrollHeight, maxHeight)}px`;
|
|
}
|
|
}, [input]);
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit} className="chat-input-form">
|
|
<textarea
|
|
ref={textareaRef}
|
|
value={input}
|
|
onChange={(e) => setInput(e.target.value)}
|
|
onKeyDown={handleKeyDown}
|
|
placeholder="Ask me anything about A Song of Ice and Fire..."
|
|
disabled={disabled}
|
|
className="chat-input-textarea"
|
|
rows={1}
|
|
maxLength={1000}
|
|
/>
|
|
<button
|
|
type="submit"
|
|
disabled={disabled || !input.trim()}
|
|
className="chat-input-button"
|
|
>
|
|
Send
|
|
</button>
|
|
</form>
|
|
);
|
|
}
|