summaryrefslogtreecommitdiff
path: root/makima/frontend/src/routes
diff options
context:
space:
mode:
Diffstat (limited to 'makima/frontend/src/routes')
-rw-r--r--makima/frontend/src/routes/contracts.tsx64
-rw-r--r--makima/frontend/src/routes/mesh.tsx15
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>
)}