summaryrefslogblamecommitdiff
path: root/makima/frontend/src/components/directives/CreateDirectiveModal.tsx
blob: 7f52a7ef4b42d5a4094e6487e7e4e149aabb1293 (plain) (tree)

















































































































































                                                                                                                                                                                              
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>
  );
}