summaryrefslogtreecommitdiff
path: root/makima/frontend/src
diff options
context:
space:
mode:
Diffstat (limited to 'makima/frontend/src')
-rw-r--r--makima/frontend/src/components/directives/DirectiveContextMenu.tsx28
-rw-r--r--makima/frontend/src/components/directives/DirectiveDetail.tsx1
-rw-r--r--makima/frontend/src/components/directives/DirectiveList.tsx1
-rw-r--r--makima/frontend/src/lib/api.ts26
-rw-r--r--makima/frontend/src/routes/document-directives.tsx15
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" && (