summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-05-06 00:45:00 +0100
committersoryu <soryu@soryu.co>2026-05-06 00:45:00 +0100
commit698efc70824e802c105fa61a250138c507f04fb5 (patch)
tree988205b009ce57fa88bf391952824691dbd89abc
parent928598b1b8399a95918dc1b315274a9d175eb8d9 (diff)
parentf8cf48987ea8e0c8ba00dad7c552cf7cb9d9d5a9 (diff)
downloadsoryu-makima/directive-soryu-co---makima-7beaafdc.tar.gz
soryu-makima/directive-soryu-co---makima-7beaafdc.zip
Merge remote-tracking branch 'origin/makima/soryu-co---makima--extract-newephemeraltaskmodal-i-5db6d04e' into makima/directive-soryu-co---makima-7beaafdcmakima/directive-soryu-co---makima-7beaafdc
# Conflicts: # makima/frontend/src/routes/document-directives.tsx
-rw-r--r--makima/frontend/src/components/directives/NewEphemeralTaskModal.tsx120
-rw-r--r--makima/frontend/src/routes/document-directives.tsx119
-rw-r--r--makima/frontend/tsconfig.tsbuildinfo2
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