diff options
Diffstat (limited to 'makima/frontend/src/routes')
| -rw-r--r-- | makima/frontend/src/routes/contracts.tsx | 64 | ||||
| -rw-r--r-- | makima/frontend/src/routes/mesh.tsx | 15 |
2 files changed, 78 insertions, 1 deletions
diff --git a/makima/frontend/src/routes/contracts.tsx b/makima/frontend/src/routes/contracts.tsx index f09ec5b..5e9bf60 100644 --- a/makima/frontend/src/routes/contracts.tsx +++ b/makima/frontend/src/routes/contracts.tsx @@ -6,7 +6,7 @@ import { ContractDetail } from "../components/contracts/ContractDetail"; import { DirectoryInput } from "../components/mesh/DirectoryInput"; import { useContracts } from "../hooks/useContracts"; import { useAuth } from "../contexts/AuthContext"; -import { createTask, getDaemonDirectories } from "../lib/api"; +import { createTask, getDaemonDirectories, getRepositorySuggestions } from "../lib/api"; import type { ContractWithRelations, ContractPhase, @@ -15,6 +15,7 @@ import type { CreateContractRequest, RepositorySourceType, DaemonDirectory, + RepositoryHistoryEntry, } from "../lib/api"; import { getValidPhases, getDefaultPhase } from "../lib/api"; @@ -81,6 +82,38 @@ function ContractsPageContent() { const [repoPath, setRepoPath] = useState(""); const [createError, setCreateError] = useState<string | null>(null); const [suggestedDirectories, setSuggestedDirectories] = useState<DaemonDirectory[]>([]); + const [repoSuggestions, setRepoSuggestions] = useState<RepositoryHistoryEntry[]>([]); + const [showRepoSuggestions, setShowRepoSuggestions] = useState(false); + + // Fetch repository suggestions when modal opens and repo type changes + useEffect(() => { + if (isCreating && (repoType === "remote" || repoType === "local")) { + getRepositorySuggestions(repoType, undefined, 10) + .then((res) => { + setRepoSuggestions(res.entries); + setShowRepoSuggestions(res.entries.length > 0); + }) + .catch(() => { + setRepoSuggestions([]); + setShowRepoSuggestions(false); + }); + } else { + setRepoSuggestions([]); + setShowRepoSuggestions(false); + } + }, [isCreating, repoType]); + + // Apply a repository suggestion + const applyRepoSuggestion = useCallback((suggestion: RepositoryHistoryEntry) => { + setRepoName(suggestion.name); + if (suggestion.repositoryUrl) { + setRepoUrl(suggestion.repositoryUrl); + } + if (suggestion.localPath) { + setRepoPath(suggestion.localPath); + } + setShowRepoSuggestions(false); + }, []); // Fetch daemon directories when "local" repo type is selected useEffect(() => { @@ -540,6 +573,35 @@ function ContractsPageContent() { </button> </div> + {/* Repository suggestions */} + {showRepoSuggestions && repoSuggestions.length > 0 && ( + <div className="mb-3"> + <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1"> + Recent Repositories + </label> + <div className="border border-[rgba(117,170,252,0.2)] bg-[#0a1525] max-h-32 overflow-y-auto"> + {repoSuggestions.map((suggestion) => ( + <button + key={suggestion.id} + type="button" + onClick={() => applyRepoSuggestion(suggestion)} + className="w-full text-left px-3 py-2 font-mono text-xs hover:bg-[rgba(117,170,252,0.1)] border-b border-[rgba(117,170,252,0.1)] last:border-b-0" + > + <div className="flex items-center justify-between"> + <span className="text-[#9bc3ff] truncate">{suggestion.name}</span> + <span className="text-[10px] text-[#556677] ml-2"> + {suggestion.useCount}× + </span> + </div> + <div className="text-[10px] text-[#556677] truncate"> + {suggestion.repositoryUrl || suggestion.localPath} + </div> + </button> + ))} + </div> + </div> + )} + {/* Repository name */} <div className="mb-3"> <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1"> diff --git a/makima/frontend/src/routes/mesh.tsx b/makima/frontend/src/routes/mesh.tsx index ed5a6d0..050381a 100644 --- a/makima/frontend/src/routes/mesh.tsx +++ b/makima/frontend/src/routes/mesh.tsx @@ -11,6 +11,7 @@ import type { TaskWithSubtasks, MeshChatContext, ContractSummary, ContractWithRe import { startTask as startTaskApi, stopTask as stopTaskApi, getTaskOutput, listContracts, getContract, getDaemonDirectories, continueTask as continueTaskApi } from "../lib/api"; import { DirectoryInput } from "../components/mesh/DirectoryInput"; import { useAuth } from "../contexts/AuthContext"; +import { useSupervisorQuestions } from "../contexts/SupervisorQuestionsContext"; // View modes for the task detail page type ViewMode = "split" | "task" | "output"; @@ -80,6 +81,18 @@ export default function MeshPage() { const navigate = useNavigate(); const { isAuthenticated, isAuthConfigured, isLoading: authLoading } = useAuth(); const { tasks, loading, error, conflict, clearConflict, fetchTask, fetchTasks, editTask, removeTask, saveTask } = useTasks(); + const { pendingQuestions, submitAnswer } = useSupervisorQuestions(); + + // Memoize pending question IDs for efficient lookup + const pendingQuestionIds = useMemo( + () => new Set(pendingQuestions.map(q => q.questionId)), + [pendingQuestions] + ); + + // Handler for answering supervisor questions + const handleAnswerQuestion = useCallback(async (questionId: string, response: string) => { + await submitAnswer(questionId, response); + }, [submitAnswer]); // Redirect to login if not authenticated useEffect(() => { @@ -720,6 +733,8 @@ export default function MeshPage() { }} taskId={activeOutputTaskId} onUserInput={handleUserInput} + pendingQuestionIds={pendingQuestionIds} + onAnswerQuestion={handleAnswerQuestion} /> </div> )} |
