diff options
| author | soryu <soryu@soryu.co> | 2026-02-07 01:11:26 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-02-07 01:11:26 +0000 |
| commit | 9e9f18884c78c21f5785908fb7ccd00e2fa5436b (patch) | |
| tree | f2ca7c2a3db5350186282ae0be0e539aa77c0320 /makima/frontend/src/components/directives/DirectiveList.tsx | |
| parent | b8d563d45f14a2b1db1f684aa0a8dcd7e5b6ad56 (diff) | |
| download | soryu-9e9f18884c78c21f5785908fb7ccd00e2fa5436b.tar.gz soryu-9e9f18884c78c21f5785908fb7ccd00e2fa5436b.zip | |
Add new directive initial implementation
Diffstat (limited to 'makima/frontend/src/components/directives/DirectiveList.tsx')
| -rw-r--r-- | makima/frontend/src/components/directives/DirectiveList.tsx | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/makima/frontend/src/components/directives/DirectiveList.tsx b/makima/frontend/src/components/directives/DirectiveList.tsx new file mode 100644 index 0000000..a900b7b --- /dev/null +++ b/makima/frontend/src/components/directives/DirectiveList.tsx @@ -0,0 +1,135 @@ +import { useState } from "react"; +import type { DirectiveSummary, DirectiveStatus } from "../../lib/api"; + +interface DirectiveListProps { + directives: DirectiveSummary[]; + loading: boolean; + onSelect: (id: string) => void; + onCreate: () => void; + onDelete?: (directive: DirectiveSummary) => void; + selectedId?: string; +} + +const statusColors: Record<DirectiveStatus, string> = { + draft: "text-[#888]", + planning: "text-yellow-400", + active: "text-green-400", + paused: "text-orange-400", + completed: "text-blue-400", + archived: "text-[#555]", + failed: "text-red-400", +}; + +export function DirectiveList({ + directives, + loading, + onSelect, + onCreate, + onDelete, + selectedId, +}: DirectiveListProps) { + const [filter, setFilter] = useState<DirectiveStatus | "all">("all"); + + const filteredDirectives = + filter === "all" + ? directives + : directives.filter((d) => d.status === filter); + + if (loading) { + return ( + <div className="panel h-full flex items-center justify-center"> + <div className="font-mono text-[#9bc3ff] text-sm">Loading...</div> + </div> + ); + } + + return ( + <div className="panel h-full flex flex-col"> + {/* Header */} + <div className="p-4 border-b border-dashed border-[rgba(117,170,252,0.35)]"> + <div className="flex items-center justify-between mb-3"> + <h2 className="font-mono text-sm text-[#75aafc] uppercase tracking-wider"> + Directives + </h2> + <button + onClick={onCreate} + className="px-3 py-1.5 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors uppercase" + > + + New + </button> + </div> + {/* Filter tabs */} + <div className="flex gap-1 flex-wrap"> + {(["all", "draft", "planning", "active", "paused", "completed", "failed"] as const).map( + (status) => ( + <button + key={status} + onClick={() => setFilter(status)} + className={`px-2 py-0.5 font-mono text-[10px] uppercase tracking-wider border transition-colors ${ + filter === status + ? "bg-[#0f3c78] border-[#3f6fb3] text-[#dbe7ff]" + : "bg-transparent border-[rgba(117,170,252,0.2)] text-[#7788aa] hover:border-[rgba(117,170,252,0.4)]" + }`} + > + {status} + </button> + ) + )} + </div> + </div> + + {/* List */} + <div className="flex-1 overflow-y-auto"> + {filteredDirectives.length === 0 ? ( + <div className="p-4 text-center"> + <p className="font-mono text-sm text-[#7788aa]"> + {directives.length === 0 + ? "No directives yet" + : "No matching directives"} + </p> + </div> + ) : ( + filteredDirectives.map((directive) => ( + <div + key={directive.id} + onClick={() => onSelect(directive.id)} + onContextMenu={(e) => { + if (onDelete) { + e.preventDefault(); + } + }} + className={`p-3 border-b border-dashed border-[rgba(117,170,252,0.15)] cursor-pointer transition-colors hover:bg-[rgba(117,170,252,0.05)] ${ + selectedId === directive.id + ? "bg-[rgba(117,170,252,0.1)]" + : "" + }`} + > + <div className="flex items-start justify-between gap-2"> + <div className="flex-1 min-w-0"> + <div className="font-mono text-sm text-[#dbe7ff] truncate"> + {directive.title} + </div> + <div className="font-mono text-xs text-[#7788aa] mt-0.5 line-clamp-2"> + {directive.goal} + </div> + </div> + <div className="flex flex-col items-end gap-1 shrink-0"> + <span + className={`font-mono text-[10px] uppercase ${ + statusColors[directive.status] || "text-[#888]" + }`} + > + {directive.status} + </span> + <span className="font-mono text-[10px] text-[#7788aa]"> + {directive.chainCount}ch / {directive.stepCount}st + </span> + </div> + </div> + </div> + )) + )} + </div> + </div> + ); +} |
