diff options
Diffstat (limited to 'makima/frontend/src')
5 files changed, 68 insertions, 3 deletions
diff --git a/makima/frontend/src/components/directives/DirectiveContextMenu.tsx b/makima/frontend/src/components/directives/DirectiveContextMenu.tsx index 07322e2..3f24ce1 100644 --- a/makima/frontend/src/components/directives/DirectiveContextMenu.tsx +++ b/makima/frontend/src/components/directives/DirectiveContextMenu.tsx @@ -11,6 +11,12 @@ interface DirectiveContextMenuProps { onArchive: () => void; onDelete: () => void; onGoToPR: () => void; + /** + * Reset the contract to a fresh empty draft (clears goal + pr_url, status + * back to 'draft'). Past revisions stay as history. Optional so the legacy + * tabular UI doesn't have to wire it up. + */ + onNewDraft?: () => void; } export function DirectiveContextMenu({ @@ -23,6 +29,7 @@ export function DirectiveContextMenu({ onArchive, onDelete, onGoToPR, + onNewDraft, }: DirectiveContextMenuProps) { const menuRef = useRef<HTMLDivElement>(null); @@ -73,6 +80,10 @@ export function DirectiveContextMenu({ const showPause = directive.status === "active"; const showArchive = directive.status !== "archived"; const showGoToPR = !!directive.prUrl; + // "New draft" appears once the contract is inactive (its iteration has + // shipped) — that's the explicit affordance for starting the next cycle + // on a clean slate while keeping prior revisions as history. + const showNewDraft = !!onNewDraft && directive.status === "inactive"; return ( <div @@ -85,6 +96,23 @@ export function DirectiveContextMenu({ {directive.title} </div> + {/* New draft — the canonical action on an inactive (shipped) contract. */} + {showNewDraft && ( + <> + <button + className={menuItemClass} + onClick={() => { + onNewDraft?.(); + onClose(); + }} + > + <span className="text-emerald-300">+</span> + New draft + </button> + <div className={dividerClass} /> + </> + )} + {/* Status actions */} {showStart && ( <button diff --git a/makima/frontend/src/components/directives/DirectiveDetail.tsx b/makima/frontend/src/components/directives/DirectiveDetail.tsx index e3302e4..4931afa 100644 --- a/makima/frontend/src/components/directives/DirectiveDetail.tsx +++ b/makima/frontend/src/components/directives/DirectiveDetail.tsx @@ -13,6 +13,7 @@ const STATUS_BADGE: Record<DirectiveStatus, { color: string; label: string }> = active: { color: "text-green-400 border-green-800", label: "ACTIVE" }, idle: { color: "text-yellow-400 border-yellow-800", label: "IDLE" }, paused: { color: "text-orange-400 border-orange-800", label: "PAUSED" }, + inactive: { color: "text-[#9bc3ff] border-[#3f6fb3]", label: "INACTIVE" }, archived: { color: "text-[#556677] border-[#2a3a5a]", label: "ARCHIVED" }, }; diff --git a/makima/frontend/src/components/directives/DirectiveList.tsx b/makima/frontend/src/components/directives/DirectiveList.tsx index 38a7caa..a35c8b1 100644 --- a/makima/frontend/src/components/directives/DirectiveList.tsx +++ b/makima/frontend/src/components/directives/DirectiveList.tsx @@ -8,6 +8,7 @@ const STATUS_BADGE: Record<DirectiveStatus, { color: string; label: string }> = active: { color: "text-green-400 border-green-800", label: "ACTIVE" }, idle: { color: "text-yellow-400 border-yellow-800", label: "IDLE" }, paused: { color: "text-orange-400 border-orange-800", label: "PAUSED" }, + inactive: { color: "text-[#9bc3ff] border-[#3f6fb3]", label: "INACTIVE" }, archived: { color: "text-[#556677] border-[#2a3a5a]", label: "ARCHIVED" }, }; diff --git a/makima/frontend/src/lib/api.ts b/makima/frontend/src/lib/api.ts index e3dbc30..3fcd728 100644 --- a/makima/frontend/src/lib/api.ts +++ b/makima/frontend/src/lib/api.ts @@ -3194,7 +3194,13 @@ export async function listTaskPatches(taskId: string, contractId: string): Promi // Directive Types & API // ============================================================================= -export type DirectiveStatus = "draft" | "active" | "idle" | "paused" | "archived"; +export type DirectiveStatus = + | "draft" + | "active" + | "idle" + | "paused" + | "inactive" + | "archived"; export type StepStatus = "pending" | "ready" | "running" | "completed" | "failed" | "skipped"; export interface Directive { @@ -3485,6 +3491,24 @@ export async function listDirectiveRevisions( return res.json(); } +/** + * Reset a directive for a new draft cycle: clears its goal and detaches the + * current PR linkage. Past revisions remain attached as history. Used by the + * sidebar's "New draft" right-click on an inactive contract. + */ +export async function newDirectiveDraft( + directiveId: string, +): Promise<Directive> { + const res = await authFetch( + `${API_BASE}/api/v1/directives/${directiveId}/new-draft`, + { method: "POST" }, + ); + if (!res.ok) { + throw new Error(`Failed to reset directive for new draft: ${res.statusText}`); + } + return res.json(); +} + export async function createDirectivePR(id: string): Promise<DirectiveWithSteps> { const res = await authFetch(`${API_BASE}/api/v1/directives/${id}/create-pr`, { method: "POST" }); if (!res.ok) throw new Error(`Failed to create PR: ${res.statusText}`); diff --git a/makima/frontend/src/routes/document-directives.tsx b/makima/frontend/src/routes/document-directives.tsx index 87102a2..d442a41 100644 --- a/makima/frontend/src/routes/document-directives.tsx +++ b/makima/frontend/src/routes/document-directives.tsx @@ -17,6 +17,7 @@ import { skipDirectiveStep, stopTask, listDirectiveRevisions, + newDirectiveDraft, } from "../lib/api"; import type { DirectiveStatus, @@ -32,6 +33,7 @@ const STATUS_DOT: Record<DirectiveStatus, string> = { active: "bg-green-400", idle: "bg-yellow-400", paused: "bg-orange-400", + inactive: "bg-[#75aafc]", archived: "bg-[#3a4a6a]", }; @@ -797,14 +799,16 @@ function DocumentSidebar({ return { directivesWithPending: dirs, tasksWithPending: tasks }; }, [pendingQuestions]); - // Sort active first, then idle, then paused, then archived. + // Sort active first, then idle, then paused, then drafts, then inactive + // (shipped contracts are quieter), then archived. const sorted = useMemo(() => { const order: Record<DirectiveStatus, number> = { active: 0, paused: 1, idle: 2, draft: 3, - archived: 4, + inactive: 4, + archived: 5, }; return [...directives].sort((a, b) => { const oa = order[a.status] ?? 99; @@ -1177,6 +1181,13 @@ export default function DocumentDirectivesPage() { window.open(contextMenu.directive.prUrl, "_blank", "noreferrer"); } }} + onNewDraft={async () => { + await newDirectiveDraft(contextMenu.directive.id); + await refreshList(); + // Send the user into the freshly-cleared contract so they can + // start typing the next iteration immediately. + navigate(`/directives/${contextMenu.directive.id}`); + }} /> )} {contextMenu?.kind === "task" && ( |
