summaryrefslogtreecommitdiff
path: root/makima/frontend/src/components/directives/CreateDirectiveModal.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'makima/frontend/src/components/directives/CreateDirectiveModal.tsx')
-rw-r--r--makima/frontend/src/components/directives/CreateDirectiveModal.tsx146
1 files changed, 146 insertions, 0 deletions
diff --git a/makima/frontend/src/components/directives/CreateDirectiveModal.tsx b/makima/frontend/src/components/directives/CreateDirectiveModal.tsx
new file mode 100644
index 0000000..7f52a7e
--- /dev/null
+++ b/makima/frontend/src/components/directives/CreateDirectiveModal.tsx
@@ -0,0 +1,146 @@
+import { useState, useEffect } from "react";
+import type { AutonomyLevel, RepositoryHistoryEntry } from "../../lib/api";
+import { getRepositorySuggestions } from "../../lib/api";
+
+interface CreateDirectiveModalProps {
+ onSubmit: (goal: string, repositoryUrl: string | undefined, autonomyLevel: AutonomyLevel) => void;
+ onCancel: () => void;
+}
+
+export function CreateDirectiveModal({ onSubmit, onCancel }: CreateDirectiveModalProps) {
+ const [goal, setGoal] = useState("");
+ const [repositoryUrl, setRepositoryUrl] = useState("");
+ const [autonomyLevel, setAutonomyLevel] = useState<AutonomyLevel>("guardrails");
+ const [suggestions, setSuggestions] = useState<RepositoryHistoryEntry[]>([]);
+ const [showSuggestions, setShowSuggestions] = useState(false);
+
+ // Load suggestions
+ useEffect(() => {
+ getRepositorySuggestions("remote", undefined, 5)
+ .then((res) => {
+ setSuggestions(res.entries);
+ })
+ .catch(() => {
+ setSuggestions([]);
+ });
+ }, []);
+
+ const handleSubmit = () => {
+ if (goal.trim()) {
+ onSubmit(goal.trim(), repositoryUrl.trim() || undefined, autonomyLevel);
+ }
+ };
+
+ return (
+ <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
+ <div className="w-full max-w-lg p-6 bg-[#0a1628] border border-[rgba(117,170,252,0.3)] max-h-[90vh] overflow-y-auto">
+ <h3 className="font-mono text-sm text-[#75aafc] uppercase mb-4">
+ Create Directive
+ </h3>
+
+ <div className="space-y-4">
+ {/* Goal */}
+ <div>
+ <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1">
+ Goal *
+ </label>
+ <textarea
+ value={goal}
+ onChange={(e) => setGoal(e.target.value)}
+ placeholder="Describe what you want to accomplish..."
+ rows={3}
+ className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc] resize-none"
+ autoFocus
+ />
+ </div>
+
+ {/* Repository URL */}
+ <div>
+ <label className="block font-mono text-xs text-[#8b949e] uppercase mb-1">
+ Repository URL (optional)
+ </label>
+ <div className="relative">
+ <input
+ type="text"
+ value={repositoryUrl}
+ onChange={(e) => setRepositoryUrl(e.target.value)}
+ onFocus={() => suggestions.length > 0 && setShowSuggestions(true)}
+ onBlur={() => setTimeout(() => setShowSuggestions(false), 200)}
+ placeholder="https://github.com/owner/repo"
+ className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]"
+ />
+ {showSuggestions && suggestions.length > 0 && (
+ <div className="absolute top-full left-0 right-0 mt-1 border border-[rgba(117,170,252,0.2)] bg-[#0a1525] max-h-32 overflow-y-auto z-10">
+ {suggestions.map((s) => (
+ <button
+ key={s.id}
+ type="button"
+ onClick={() => {
+ setRepositoryUrl(s.repositoryUrl || "");
+ setShowSuggestions(false);
+ }}
+ 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="text-[#9bc3ff] truncate">{s.name}</div>
+ <div className="text-[10px] text-[#556677] truncate">{s.repositoryUrl}</div>
+ </button>
+ ))}
+ </div>
+ )}
+ </div>
+ </div>
+
+ {/* Autonomy Level */}
+ <div>
+ <label className="block font-mono text-xs text-[#8b949e] uppercase mb-2">
+ Autonomy Level
+ </label>
+ <div className="flex gap-2">
+ {(["full_auto", "guardrails", "manual"] as const).map((level) => (
+ <button
+ key={level}
+ type="button"
+ onClick={() => setAutonomyLevel(level)}
+ className={`flex-1 px-3 py-2 font-mono text-xs uppercase ${
+ autonomyLevel === level
+ ? "text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3]"
+ : "text-[#556677] border border-[rgba(117,170,252,0.2)] hover:border-[#3f6fb3]"
+ }`}
+ >
+ {level.replace("_", " ")}
+ </button>
+ ))}
+ </div>
+ <p className="font-mono text-[10px] text-[#556677] mt-1">
+ {autonomyLevel === "full_auto" && "Automatic progression without approval gates"}
+ {autonomyLevel === "guardrails" && "Request approval for yellow/red confidence scores"}
+ {autonomyLevel === "manual" && "Request approval for all step completions"}
+ </p>
+ </div>
+
+ <p className="font-mono text-xs text-[#8b949e]">
+ A directive is a top-level goal that generates a chain of steps. Each step spawns
+ contracts that are verified before progression.
+ </p>
+
+ {/* Actions */}
+ <div className="flex gap-2 justify-end pt-2">
+ <button
+ onClick={onCancel}
+ className="px-4 py-2 font-mono text-xs text-[#9bc3ff] hover:text-[#dbe7ff] transition-colors"
+ >
+ Cancel
+ </button>
+ <button
+ onClick={handleSubmit}
+ disabled={!goal.trim()}
+ className="px-4 py-2 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
+ >
+ Create
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+}