diff options
| -rw-r--r-- | makima/frontend/src/components/directives/NewEphemeralTaskModal.tsx | 120 | ||||
| -rw-r--r-- | makima/frontend/src/routes/document-directives.tsx | 119 | ||||
| -rw-r--r-- | makima/frontend/tsconfig.tsbuildinfo | 2 |
3 files changed, 122 insertions, 119 deletions
diff --git a/makima/frontend/src/components/directives/NewEphemeralTaskModal.tsx b/makima/frontend/src/components/directives/NewEphemeralTaskModal.tsx new file mode 100644 index 0000000..88bc52b --- /dev/null +++ b/makima/frontend/src/components/directives/NewEphemeralTaskModal.tsx @@ -0,0 +1,120 @@ +import { useEffect, useRef, useState } from "react"; +import type { DirectiveSummary } from "../../lib/api"; + +/** + * Modal for spawning an ephemeral task under a directive. Mirrors the + * existing right-click "+ New task" flow. + */ +export function NewEphemeralTaskModal({ + directive, + onClose, + onSubmit, +}: { + directive: DirectiveSummary; + onClose: () => void; + onSubmit: (name: string, plan: string) => Promise<void>; +}) { + const [name, setName] = useState(""); + const [plan, setPlan] = useState(""); + const [submitting, setSubmitting] = useState(false); + const [err, setErr] = useState<string | null>(null); + const nameRef = useRef<HTMLInputElement>(null); + + useEffect(() => { + nameRef.current?.focus(); + const onKey = (e: KeyboardEvent) => { + if (e.key === "Escape") onClose(); + }; + document.addEventListener("keydown", onKey); + return () => document.removeEventListener("keydown", onKey); + }, [onClose]); + + const submit = async (e: React.FormEvent) => { + e.preventDefault(); + const n = name.trim(); + const p = plan.trim(); + if (!n || !p || submitting) return; + setSubmitting(true); + setErr(null); + try { + await onSubmit(n, p); + } catch (caught) { + setErr(caught instanceof Error ? caught.message : String(caught)); + } finally { + setSubmitting(false); + } + }; + + return ( + <div + className="fixed inset-0 z-50 flex items-center justify-center bg-black/60" + onClick={onClose} + > + <form + onSubmit={submit} + onClick={(e) => e.stopPropagation()} + className="w-[480px] max-w-[90vw] bg-[#0a1628] border border-[rgba(117,170,252,0.4)] shadow-xl flex flex-col" + > + <div className="px-4 py-3 border-b border-dashed border-[rgba(117,170,252,0.25)]"> + <p className="text-[10px] font-mono text-[#556677] uppercase tracking-wide"> + New ephemeral task in + </p> + <p className="text-[12px] font-mono text-white truncate"> + {directive.title} + </p> + </div> + <div className="px-4 py-4 space-y-3"> + <div className="space-y-1"> + <label className="text-[10px] font-mono text-[#7788aa] uppercase tracking-wide"> + Name + </label> + <input + ref={nameRef} + type="text" + value={name} + onChange={(e) => setName(e.target.value)} + placeholder="e.g. Investigate flaky test" + className="w-full bg-transparent border border-[rgba(117,170,252,0.2)] focus:border-[#75aafc] outline-none px-3 py-2 text-[13px] text-[#dbe7ff] placeholder-[#445566]" + /> + </div> + <div className="space-y-1"> + <label className="text-[10px] font-mono text-[#7788aa] uppercase tracking-wide"> + Plan / instructions + </label> + <textarea + value={plan} + onChange={(e) => setPlan(e.target.value)} + onKeyDown={(e) => { + if ((e.metaKey || e.ctrlKey) && e.key === "Enter") { + void submit(e as unknown as React.FormEvent); + } + }} + rows={5} + placeholder="What should the task do?" + className="w-full bg-transparent border border-[rgba(117,170,252,0.2)] focus:border-[#75aafc] outline-none px-3 py-2 text-[13px] text-[#dbe7ff] placeholder-[#445566] resize-none" + /> + </div> + {err && ( + <p className="text-[11px] font-mono text-red-400">{err}</p> + )} + </div> + <div className="px-4 py-3 border-t border-dashed border-[rgba(117,170,252,0.25)] flex items-center justify-end gap-2"> + <button + type="button" + onClick={onClose} + className="px-3 py-1.5 text-[11px] font-mono uppercase tracking-wide text-[#7788aa] border border-[#2a3a5a] hover:text-white" + > + Cancel + </button> + <button + type="submit" + disabled={!name.trim() || !plan.trim() || submitting} + className="px-3 py-1.5 text-[11px] font-mono uppercase tracking-wide text-[#c084fc] border border-[#c084fc]/40 hover:border-[#c084fc] disabled:opacity-40 disabled:cursor-not-allowed" + > + {submitting ? "Spawning…" : "Spawn task"} + </button> + </div> + </form> + </div> + ); +} diff --git a/makima/frontend/src/routes/document-directives.tsx b/makima/frontend/src/routes/document-directives.tsx index 1714aed..25f96cb 100644 --- a/makima/frontend/src/routes/document-directives.tsx +++ b/makima/frontend/src/routes/document-directives.tsx @@ -29,6 +29,7 @@ import { } from "../lib/api"; import { DirectiveContextMenu } from "../components/directives/DirectiveContextMenu"; import { DocumentTaskStream } from "../components/directives/DocumentTaskStream"; +import { NewEphemeralTaskModal } from "../components/directives/NewEphemeralTaskModal"; // Status dot color, matching the existing tabular UI's badge palette so the // document mode feels like a sibling of the existing list, not a foreign UI. @@ -1637,121 +1638,3 @@ function NewContractModal({ </div> ); } - -/** - * Modal for spawning an ephemeral task under a directive. Mirrors the - * existing right-click "+ New task" flow. - */ -function NewEphemeralTaskModal({ - directive, - onClose, - onSubmit, -}: { - directive: DirectiveSummary; - onClose: () => void; - onSubmit: (name: string, plan: string) => Promise<void>; -}) { - const [name, setName] = useState(""); - const [plan, setPlan] = useState(""); - const [submitting, setSubmitting] = useState(false); - const [err, setErr] = useState<string | null>(null); - const nameRef = useRef<HTMLInputElement>(null); - - useEffect(() => { - nameRef.current?.focus(); - const onKey = (e: KeyboardEvent) => { - if (e.key === "Escape") onClose(); - }; - document.addEventListener("keydown", onKey); - return () => document.removeEventListener("keydown", onKey); - }, [onClose]); - - const submit = async (e: React.FormEvent) => { - e.preventDefault(); - const n = name.trim(); - const p = plan.trim(); - if (!n || !p || submitting) return; - setSubmitting(true); - setErr(null); - try { - await onSubmit(n, p); - } catch (caught) { - setErr(caught instanceof Error ? caught.message : String(caught)); - } finally { - setSubmitting(false); - } - }; - - return ( - <div - className="fixed inset-0 z-50 flex items-center justify-center bg-black/60" - onClick={onClose} - > - <form - onSubmit={submit} - onClick={(e) => e.stopPropagation()} - className="w-[480px] max-w-[90vw] bg-[#0a1628] border border-[rgba(117,170,252,0.4)] shadow-xl flex flex-col" - > - <div className="px-4 py-3 border-b border-dashed border-[rgba(117,170,252,0.25)]"> - <p className="text-[10px] font-mono text-[#556677] uppercase tracking-wide"> - New ephemeral task in - </p> - <p className="text-[12px] font-mono text-white truncate"> - {directive.title} - </p> - </div> - <div className="px-4 py-4 space-y-3"> - <div className="space-y-1"> - <label className="text-[10px] font-mono text-[#7788aa] uppercase tracking-wide"> - Name - </label> - <input - ref={nameRef} - type="text" - value={name} - onChange={(e) => setName(e.target.value)} - placeholder="e.g. Investigate flaky test" - className="w-full bg-transparent border border-[rgba(117,170,252,0.2)] focus:border-[#75aafc] outline-none px-3 py-2 text-[13px] text-[#dbe7ff] placeholder-[#445566]" - /> - </div> - <div className="space-y-1"> - <label className="text-[10px] font-mono text-[#7788aa] uppercase tracking-wide"> - Plan / instructions - </label> - <textarea - value={plan} - onChange={(e) => setPlan(e.target.value)} - onKeyDown={(e) => { - if ((e.metaKey || e.ctrlKey) && e.key === "Enter") { - void submit(e as unknown as React.FormEvent); - } - }} - rows={5} - placeholder="What should the task do?" - className="w-full bg-transparent border border-[rgba(117,170,252,0.2)] focus:border-[#75aafc] outline-none px-3 py-2 text-[13px] text-[#dbe7ff] placeholder-[#445566] resize-none" - /> - </div> - {err && ( - <p className="text-[11px] font-mono text-red-400">{err}</p> - )} - </div> - <div className="px-4 py-3 border-t border-dashed border-[rgba(117,170,252,0.25)] flex items-center justify-end gap-2"> - <button - type="button" - onClick={onClose} - className="px-3 py-1.5 text-[11px] font-mono uppercase tracking-wide text-[#7788aa] border border-[#2a3a5a] hover:text-white" - > - Cancel - </button> - <button - type="submit" - disabled={!name.trim() || !plan.trim() || submitting} - className="px-3 py-1.5 text-[11px] font-mono uppercase tracking-wide text-[#c084fc] border border-[#c084fc]/40 hover:border-[#c084fc] disabled:opacity-40 disabled:cursor-not-allowed" - > - {submitting ? "Spawning…" : "Spawn task"} - </button> - </div> - </form> - </div> - ); -} diff --git a/makima/frontend/tsconfig.tsbuildinfo b/makima/frontend/tsconfig.tsbuildinfo index a520296..7b830ce 100644 --- a/makima/frontend/tsconfig.tsbuildinfo +++ b/makima/frontend/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/components/gridoverlay.tsx","./src/components/japanesehovertext.tsx","./src/components/logo.tsx","./src/components/masthead.tsx","./src/components/navstrip.tsx","./src/components/phaseconfirmationnotification.tsx","./src/components/protectedroute.tsx","./src/components/quickswitcher.tsx","./src/components/rewritelink.tsx","./src/components/simplemarkdown.tsx","./src/components/supervisorquestionnotification.tsx","./src/components/charts/chartrenderer.tsx","./src/components/contracts/commandmodepanel.tsx","./src/components/contracts/contractcliinput.tsx","./src/components/contracts/contractcontextmenu.tsx","./src/components/contracts/contractdetail.tsx","./src/components/contracts/contractlist.tsx","./src/components/contracts/phasebadge.tsx","./src/components/contracts/phaseconfirmationmodal.tsx","./src/components/contracts/phasedeliverablespanel.tsx","./src/components/contracts/phasehint.tsx","./src/components/contracts/phaseprogressbar.tsx","./src/components/contracts/quickactionbuttons.tsx","./src/components/contracts/repositorypanel.tsx","./src/components/contracts/taskderivationpreview.tsx","./src/components/directives/doglist.tsx","./src/components/directives/directivecontextmenu.tsx","./src/components/directives/directivedag.tsx","./src/components/directives/directivedetail.tsx","./src/components/directives/directivelist.tsx","./src/components/directives/directivelogstream.tsx","./src/components/directives/documenteditor.tsx","./src/components/directives/documenttaskstream.tsx","./src/components/directives/orchestratorstepnode.tsx","./src/components/directives/stepnode.tsx","./src/components/directives/stepsblocknode.tsx","./src/components/directives/taskslideoutpanel.tsx","./src/components/files/bodyrenderer.tsx","./src/components/files/cliinput.tsx","./src/components/files/conflictnotification.tsx","./src/components/files/elementcontextmenu.tsx","./src/components/files/filedetail.tsx","./src/components/files/filelist.tsx","./src/components/files/reposyncindicator.tsx","./src/components/files/updatenotification.tsx","./src/components/files/versionhistorydropdown.tsx","./src/components/history/checkpointcard.tsx","./src/components/history/checkpointlist.tsx","./src/components/history/conversationmessage.tsx","./src/components/history/conversationview.tsx","./src/components/history/historyfilters.tsx","./src/components/history/resumecontrols.tsx","./src/components/history/timelineeventcard.tsx","./src/components/history/timelinelist.tsx","./src/components/history/index.ts","./src/components/listen/contractpickermodal.tsx","./src/components/listen/controlpanel.tsx","./src/components/listen/discusscontractmodal.tsx","./src/components/listen/speakerpanel.tsx","./src/components/listen/transcriptanalysispanel.tsx","./src/components/listen/transcriptpanel.tsx","./src/components/mesh/branchtaskmodal.tsx","./src/components/mesh/contractcompletequestion.tsx","./src/components/mesh/directoryinput.tsx","./src/components/mesh/gitactionspanel.tsx","./src/components/mesh/inlinesubtaskeditor.tsx","./src/components/mesh/mergeconflictresolver.tsx","./src/components/mesh/overlaydiffviewer.tsx","./src/components/mesh/prpreview.tsx","./src/components/mesh/patcheslistpanel.tsx","./src/components/mesh/subtasktree.tsx","./src/components/mesh/taskdetail.tsx","./src/components/mesh/tasklist.tsx","./src/components/mesh/taskoutput.tsx","./src/components/mesh/tasktree.tsx","./src/components/mesh/unifiedmeshchatinput.tsx","./src/components/mesh/worktreefilespanel.tsx","./src/components/orders/ordercontextmenu.tsx","./src/components/orders/orderdetail.tsx","./src/components/orders/orderlist.tsx","./src/contexts/authcontext.tsx","./src/contexts/supervisorquestionscontext.tsx","./src/hooks/usecontracts.ts","./src/hooks/usedirectives.ts","./src/hooks/usedogs.ts","./src/hooks/usefilesubscription.ts","./src/hooks/usefiles.ts","./src/hooks/usemeshchathistory.ts","./src/hooks/usemicrophone.ts","./src/hooks/usemultitasksubscription.ts","./src/hooks/useorders.ts","./src/hooks/usespeakwebsocket.ts","./src/hooks/usetasksubscription.ts","./src/hooks/usetasks.ts","./src/hooks/usetextscramble.ts","./src/hooks/useusersettings.ts","./src/hooks/useversionhistory.ts","./src/hooks/usewebsocket.ts","./src/lib/api.ts","./src/lib/listenapi.ts","./src/lib/markdown.ts","./src/lib/supabase.ts","./src/routes/_index.tsx","./src/routes/contract-file.tsx","./src/routes/contracts.tsx","./src/routes/daemons.tsx","./src/routes/directives.tsx","./src/routes/document-directives.tsx","./src/routes/exec-redirect.tsx","./src/routes/files.tsx","./src/routes/history.tsx","./src/routes/listen.tsx","./src/routes/login.tsx","./src/routes/mesh.tsx","./src/routes/orders.tsx","./src/routes/settings.tsx","./src/routes/speak.tsx","./src/routes/tmp.tsx","./src/types/messages.ts"],"version":"5.9.3"}
\ No newline at end of file +{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/components/gridoverlay.tsx","./src/components/japanesehovertext.tsx","./src/components/logo.tsx","./src/components/masthead.tsx","./src/components/navstrip.tsx","./src/components/phaseconfirmationnotification.tsx","./src/components/protectedroute.tsx","./src/components/quickswitcher.tsx","./src/components/rewritelink.tsx","./src/components/simplemarkdown.tsx","./src/components/supervisorquestionnotification.tsx","./src/components/charts/chartrenderer.tsx","./src/components/contracts/phaseconfirmationmodal.tsx","./src/components/directives/doglist.tsx","./src/components/directives/directivecontextmenu.tsx","./src/components/directives/directivedag.tsx","./src/components/directives/directivedetail.tsx","./src/components/directives/directivelist.tsx","./src/components/directives/directivelogstream.tsx","./src/components/directives/documenteditor.tsx","./src/components/directives/documenttaskstream.tsx","./src/components/directives/newephemeraltaskmodal.tsx","./src/components/directives/orchestratorstepnode.tsx","./src/components/directives/stepnode.tsx","./src/components/directives/stepsblocknode.tsx","./src/components/directives/taskslideoutpanel.tsx","./src/components/files/bodyrenderer.tsx","./src/components/files/cliinput.tsx","./src/components/files/conflictnotification.tsx","./src/components/files/elementcontextmenu.tsx","./src/components/files/filedetail.tsx","./src/components/files/filelist.tsx","./src/components/files/reposyncindicator.tsx","./src/components/files/updatenotification.tsx","./src/components/files/versionhistorydropdown.tsx","./src/components/history/checkpointcard.tsx","./src/components/history/checkpointlist.tsx","./src/components/history/conversationmessage.tsx","./src/components/history/conversationview.tsx","./src/components/history/historyfilters.tsx","./src/components/history/resumecontrols.tsx","./src/components/history/timelineeventcard.tsx","./src/components/history/timelinelist.tsx","./src/components/history/index.ts","./src/components/listen/contractpickermodal.tsx","./src/components/listen/controlpanel.tsx","./src/components/listen/discusscontractmodal.tsx","./src/components/listen/speakerpanel.tsx","./src/components/listen/transcriptanalysispanel.tsx","./src/components/listen/transcriptpanel.tsx","./src/components/mesh/branchtaskmodal.tsx","./src/components/mesh/contractcompletequestion.tsx","./src/components/mesh/directoryinput.tsx","./src/components/mesh/gitactionspanel.tsx","./src/components/mesh/inlinesubtaskeditor.tsx","./src/components/mesh/mergeconflictresolver.tsx","./src/components/mesh/overlaydiffviewer.tsx","./src/components/mesh/prpreview.tsx","./src/components/mesh/patcheslistpanel.tsx","./src/components/mesh/subtasktree.tsx","./src/components/mesh/taskdetail.tsx","./src/components/mesh/tasklist.tsx","./src/components/mesh/taskoutput.tsx","./src/components/mesh/tasktree.tsx","./src/components/mesh/unifiedmeshchatinput.tsx","./src/components/mesh/worktreefilespanel.tsx","./src/components/orders/ordercontextmenu.tsx","./src/components/orders/orderdetail.tsx","./src/components/orders/orderlist.tsx","./src/contexts/authcontext.tsx","./src/contexts/supervisorquestionscontext.tsx","./src/hooks/usecontracts.ts","./src/hooks/usedirectives.ts","./src/hooks/usedogs.ts","./src/hooks/usefilesubscription.ts","./src/hooks/usefiles.ts","./src/hooks/usemeshchathistory.ts","./src/hooks/usemicrophone.ts","./src/hooks/usemultitasksubscription.ts","./src/hooks/useorders.ts","./src/hooks/usespeakwebsocket.ts","./src/hooks/usetasksubscription.ts","./src/hooks/usetasks.ts","./src/hooks/usetextscramble.ts","./src/hooks/useusersettings.ts","./src/hooks/useversionhistory.ts","./src/hooks/usewebsocket.ts","./src/lib/api.ts","./src/lib/listenapi.ts","./src/lib/markdown.ts","./src/lib/supabase.ts","./src/routes/_index.tsx","./src/routes/daemons.tsx","./src/routes/directives.tsx","./src/routes/document-directives.tsx","./src/routes/exec-redirect.tsx","./src/routes/files.tsx","./src/routes/history.tsx","./src/routes/listen.tsx","./src/routes/login.tsx","./src/routes/mesh.tsx","./src/routes/orders.tsx","./src/routes/settings.tsx","./src/routes/speak.tsx","./src/routes/tmp.tsx","./src/types/messages.ts"],"version":"5.9.3"}
\ No newline at end of file |
