summaryrefslogtreecommitdiff
path: root/makima/frontend/src/components/files/CliInput.tsx
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-06 04:08:11 +0000
committersoryu <soryu@soryu.co>2026-01-11 03:01:13 +0000
commit8b17a175c3e7e27b789812eba4e3cd760beadb10 (patch)
tree7864dcaa2fa9db47fdfd4e8bfdb0b1dde832aa33 /makima/frontend/src/components/files/CliInput.tsx
parentf79c416c58557d2f946aa5332989afdfa8c021cd (diff)
downloadsoryu-8b17a175c3e7e27b789812eba4e3cd760beadb10.tar.gz
soryu-8b17a175c3e7e27b789812eba4e3cd760beadb10.zip
Initial Control system
Diffstat (limited to 'makima/frontend/src/components/files/CliInput.tsx')
-rw-r--r--makima/frontend/src/components/files/CliInput.tsx58
1 files changed, 53 insertions, 5 deletions
diff --git a/makima/frontend/src/components/files/CliInput.tsx b/makima/frontend/src/components/files/CliInput.tsx
index ff2b0a4..47e7616 100644
--- a/makima/frontend/src/components/files/CliInput.tsx
+++ b/makima/frontend/src/components/files/CliInput.tsx
@@ -8,10 +8,15 @@ import {
type UserAnswer,
} from "../../lib/api";
import { SimpleMarkdown } from "../SimpleMarkdown";
+import type { FocusedElement } from "./FileDetail";
interface CliInputProps {
fileId: string;
onUpdate: (body: BodyElement[], summary: string | null) => void;
+ focusedElement?: FocusedElement | null;
+ onClearFocus?: () => void;
+ suggestedPrompt?: string | null;
+ onClearSuggestedPrompt?: () => void;
}
interface Message {
@@ -28,7 +33,7 @@ const MODEL_OPTIONS: { value: LlmModel; label: string }[] = [
{ value: "groq", label: "Groq Kimi" },
];
-export function CliInput({ fileId, onUpdate }: CliInputProps) {
+export function CliInput({ fileId, onUpdate, focusedElement, onClearFocus, suggestedPrompt, onClearSuggestedPrompt }: CliInputProps) {
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
const [messages, setMessages] = useState<Message[]>([]);
@@ -53,6 +58,21 @@ export function CliInput({ fileId, onUpdate }: CliInputProps) {
}
}, [messages]);
+ // Auto-focus input when an element is focused
+ useEffect(() => {
+ if (focusedElement && inputRef.current) {
+ inputRef.current.focus();
+ }
+ }, [focusedElement]);
+
+ // Handle suggested prompt from generate actions
+ useEffect(() => {
+ if (suggestedPrompt) {
+ setInput(suggestedPrompt);
+ onClearSuggestedPrompt?.();
+ }
+ }, [suggestedPrompt, onClearSuggestedPrompt]);
+
const handleSubmit = useCallback(
async (e: React.FormEvent) => {
e.preventDefault();
@@ -73,7 +93,13 @@ export function CliInput({ fileId, onUpdate }: CliInputProps) {
try {
// Send request with conversation history for context
- const response = await chatWithFile(fileId, userMessage, model, conversationHistory);
+ const response = await chatWithFile(
+ fileId,
+ userMessage,
+ model,
+ conversationHistory,
+ focusedElement?.index
+ );
// Add assistant response
const assistantMsgId = (Date.now() + 1).toString();
@@ -128,7 +154,7 @@ export function CliInput({ fileId, onUpdate }: CliInputProps) {
inputRef.current?.focus();
}
},
- [input, loading, fileId, model, onUpdate, conversationHistory]
+ [input, loading, fileId, model, onUpdate, conversationHistory, focusedElement]
);
// Handle option selection for a question
@@ -206,7 +232,13 @@ export function CliInput({ fileId, onUpdate }: CliInputProps) {
try {
// Send answers as the next message
- const response = await chatWithFile(fileId, answerText, model, conversationHistory);
+ const response = await chatWithFile(
+ fileId,
+ answerText,
+ model,
+ conversationHistory,
+ focusedElement?.index
+ );
// Add assistant response
const assistantMsgId = (Date.now() + 1).toString();
@@ -258,7 +290,7 @@ export function CliInput({ fileId, onUpdate }: CliInputProps) {
} finally {
setLoading(false);
}
- }, [pendingQuestions, userAnswers, customInputs, loading, fileId, model, conversationHistory, onUpdate]);
+ }, [pendingQuestions, userAnswers, customInputs, loading, fileId, model, conversationHistory, onUpdate, focusedElement]);
// Cancel answering questions
const handleCancelQuestions = useCallback(() => {
@@ -397,6 +429,22 @@ export function CliInput({ fileId, onUpdate }: CliInputProps) {
</option>
))}
</select>
+
+ {/* Focus Badge */}
+ {focusedElement && (
+ <button
+ type="button"
+ onClick={onClearFocus}
+ className="flex items-center gap-1 px-2 py-0.5 font-mono text-[10px] bg-[rgba(117,170,252,0.1)] border border-[rgba(117,170,252,0.3)] text-[#9bc3ff] hover:border-[#75aafc] transition-colors group"
+ title="Click to clear focus"
+ >
+ <span className="text-[#75aafc]">{focusedElement.type}</span>
+ <span className="text-[#555]">:</span>
+ <span>{focusedElement.index}</span>
+ <span className="text-[#555] group-hover:text-red-400 ml-1">&times;</span>
+ </button>
+ )}
+
<span className="text-[#9bc3ff] font-mono text-sm">&gt;</span>
<input
ref={inputRef}