diff options
| author | soryu <soryu@soryu.co> | 2026-01-18 18:55:04 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-01-18 18:55:04 +0000 |
| commit | 9dbc2c3199047609a9f8496fec07ecdb10aee73d (patch) | |
| tree | c5a4e19b2a83cac1901e81f2ff1bc7a465f8a9ff /makima/frontend/src/components/mesh/TaskOutput.tsx | |
| parent | 273da072fa0573c935798dc723ed79fd71ab037a (diff) | |
| download | soryu-9dbc2c3199047609a9f8496fec07ecdb10aee73d.tar.gz soryu-9dbc2c3199047609a9f8496fec07ecdb10aee73d.zip | |
Add pushed heartbeats and multi-question select
Diffstat (limited to 'makima/frontend/src/components/mesh/TaskOutput.tsx')
| -rw-r--r-- | makima/frontend/src/components/mesh/TaskOutput.tsx | 76 |
1 files changed, 65 insertions, 11 deletions
diff --git a/makima/frontend/src/components/mesh/TaskOutput.tsx b/makima/frontend/src/components/mesh/TaskOutput.tsx index 7140c8a..c98b174 100644 --- a/makima/frontend/src/components/mesh/TaskOutput.tsx +++ b/makima/frontend/src/components/mesh/TaskOutput.tsx @@ -343,18 +343,48 @@ function SupervisorQuestionEntry({ const questionId = entry.toolInput?.question_id as string; const choices = (entry.toolInput?.choices as string[]) || []; const context = entry.toolInput?.context as string | null; + const multiSelect = (entry.toolInput?.multi_select as boolean) ?? false; const [customInput, setCustomInput] = useState(""); const [showOther, setShowOther] = useState(false); const [submitting, setSubmitting] = useState(false); + const [selectedChoices, setSelectedChoices] = useState<Set<string>>(new Set()); const isPending = pendingQuestionIds?.has(questionId) ?? false; const handleChoiceSelect = async (choice: string) => { if (!onAnswerQuestion || submitting) return; + + if (multiSelect) { + // Toggle selection for multi-select mode + setSelectedChoices(prev => { + const newSet = new Set(prev); + if (newSet.has(choice)) { + newSet.delete(choice); + } else { + newSet.add(choice); + } + return newSet; + }); + } else { + // Single select - submit immediately + setSubmitting(true); + try { + await onAnswerQuestion(questionId, choice); + } finally { + setSubmitting(false); + } + } + }; + + const handleMultiSelectSubmit = async () => { + if (!onAnswerQuestion || submitting || selectedChoices.size === 0) return; setSubmitting(true); try { - await onAnswerQuestion(questionId, choice); + // Join selected choices with comma + const response = Array.from(selectedChoices).join(", "); + await onAnswerQuestion(questionId, response); + setSelectedChoices(new Set()); } finally { setSubmitting(false); } @@ -376,6 +406,9 @@ function SupervisorQuestionEntry({ <div className="flex items-center gap-2 text-amber-400 font-semibold mb-2"> <span>?</span> <span>Question</span> + {multiSelect && isPending && ( + <span className="text-amber-300 text-xs font-normal">(select multiple)</span> + )} {!isPending && ( <span className="text-green-400 text-xs font-normal">(Answered)</span> )} @@ -391,19 +424,40 @@ function SupervisorQuestionEntry({ <div className="space-y-2"> {choices.length > 0 && ( <div className="flex flex-wrap gap-2"> - {choices.map((choice, idx) => ( - <button - key={idx} - onClick={() => handleChoiceSelect(choice)} - disabled={submitting} - className="px-3 py-1.5 text-sm font-mono bg-amber-500/20 border border-amber-500/50 hover:bg-amber-500/30 disabled:opacity-50 text-amber-100 transition-colors" - > - {choice} - </button> - ))} + {choices.map((choice, idx) => { + const isSelected = selectedChoices.has(choice); + return ( + <button + key={idx} + onClick={() => handleChoiceSelect(choice)} + disabled={submitting} + className={`px-3 py-1.5 text-sm font-mono border transition-colors disabled:opacity-50 ${ + multiSelect && isSelected + ? "bg-amber-500/50 border-amber-400 text-amber-50" + : "bg-amber-500/20 border-amber-500/50 hover:bg-amber-500/30 text-amber-100" + }`} + > + {multiSelect && ( + <span className="mr-1.5">{isSelected ? "✓" : "○"}</span> + )} + {choice} + </button> + ); + })} </div> )} + {/* Submit button for multi-select mode */} + {multiSelect && selectedChoices.size > 0 && ( + <button + onClick={handleMultiSelectSubmit} + disabled={submitting} + className="px-4 py-1.5 bg-amber-500 text-black text-sm font-medium rounded disabled:opacity-50 disabled:cursor-not-allowed transition-colors hover:bg-amber-400" + > + {submitting ? "Submitting..." : `Submit (${selectedChoices.size} selected)`} + </button> + )} + {/* Other option */} {!showOther ? ( <button |
