@elizaos/app-core 2.0.0-alpha.335 → 2.0.0-alpha.337

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/apps/app-lifeops/src/routes/lifeops-routes.d.ts.map +1 -1
  2. package/apps/app-lifeops/src/routes/lifeops-routes.js +10 -1
  3. package/i18n/locales/en.json +5 -5
  4. package/package.json +5 -5
  5. package/packages/agent/src/runtime/eliza.d.ts.map +1 -1
  6. package/packages/agent/src/runtime/eliza.js +7 -2
  7. package/packages/app-core/src/api/automations-compat-routes.js +1 -1
  8. package/packages/app-core/src/api/client-n8n.d.ts +4 -1
  9. package/packages/app-core/src/api/client-n8n.d.ts.map +1 -1
  10. package/packages/app-core/src/api/client-n8n.js +18 -0
  11. package/packages/app-core/src/api/client-types-chat.d.ts +37 -0
  12. package/packages/app-core/src/api/client-types-chat.d.ts.map +1 -1
  13. package/packages/app-core/src/api/n8n-routes.d.ts +6 -0
  14. package/packages/app-core/src/api/n8n-routes.d.ts.map +1 -1
  15. package/packages/app-core/src/api/n8n-routes.js +334 -0
  16. package/packages/app-core/src/components/character/CharacterEditorPanels.d.ts.map +1 -1
  17. package/packages/app-core/src/components/character/CharacterEditorPanels.js +192 -85
  18. package/packages/app-core/src/components/character/CharacterExperienceWorkspace.d.ts.map +1 -1
  19. package/packages/app-core/src/components/character/CharacterExperienceWorkspace.js +182 -31
  20. package/packages/app-core/src/components/character/CharacterHubView.d.ts.map +1 -1
  21. package/packages/app-core/src/components/character/CharacterHubView.js +253 -23
  22. package/packages/app-core/src/components/character/CharacterOverviewSection.d.ts +14 -1
  23. package/packages/app-core/src/components/character/CharacterOverviewSection.d.ts.map +1 -1
  24. package/packages/app-core/src/components/character/CharacterOverviewSection.js +35 -8
  25. package/packages/app-core/src/components/character/character-hub-helpers.d.ts.map +1 -1
  26. package/packages/app-core/src/components/character/character-hub-helpers.js +3 -7
  27. package/packages/app-core/src/components/pages/AutomationsView.d.ts.map +1 -1
  28. package/packages/app-core/src/components/pages/AutomationsView.js +523 -274
  29. package/packages/app-core/src/components/pages/HeartbeatForm.d.ts.map +1 -1
  30. package/packages/app-core/src/components/pages/HeartbeatForm.js +11 -13
  31. package/packages/app-core/src/components/pages/KnowledgeView.d.ts.map +1 -1
  32. package/packages/app-core/src/components/pages/KnowledgeView.js +20 -6
  33. package/packages/app-core/src/components/pages/RelationshipsGraphPanel.d.ts +2 -1
  34. package/packages/app-core/src/components/pages/RelationshipsGraphPanel.d.ts.map +1 -1
  35. package/packages/app-core/src/components/pages/RelationshipsGraphPanel.js +3 -3
  36. package/packages/app-core/src/components/pages/WorkflowGraphViewer.d.ts +0 -9
  37. package/packages/app-core/src/components/pages/WorkflowGraphViewer.d.ts.map +1 -1
  38. package/packages/app-core/src/components/pages/WorkflowGraphViewer.js +52 -26
  39. package/packages/app-core/src/components/pages/knowledge-detail.d.ts.map +1 -1
  40. package/packages/app-core/src/components/pages/knowledge-detail.js +28 -2
  41. package/packages/app-core/src/components/pages/page-scoped-conversations.d.ts +1 -1
  42. package/packages/app-core/src/components/pages/page-scoped-conversations.js +4 -4
  43. package/packages/app-core/src/components/pages/relationships/RelationshipsPersonPanels.d.ts +2 -1
  44. package/packages/app-core/src/components/pages/relationships/RelationshipsPersonPanels.d.ts.map +1 -1
  45. package/packages/app-core/src/components/pages/relationships/RelationshipsPersonPanels.js +69 -32
  46. package/packages/app-core/src/components/pages/relationships/RelationshipsSidebar.d.ts +1 -6
  47. package/packages/app-core/src/components/pages/relationships/RelationshipsSidebar.d.ts.map +1 -1
  48. package/packages/app-core/src/components/pages/relationships/RelationshipsSidebar.js +4 -4
  49. package/packages/app-core/src/components/pages/relationships/RelationshipsWorkspaceView.d.ts +2 -1
  50. package/packages/app-core/src/components/pages/relationships/RelationshipsWorkspaceView.d.ts.map +1 -1
  51. package/packages/app-core/src/components/pages/relationships/RelationshipsWorkspaceView.js +25 -15
  52. package/packages/app-core/src/i18n/locales/en.json +5 -5
  53. package/packages/app-core/src/registry/generate-apps.js +2 -2
  54. package/packages/app-core/src/runtime/eliza.d.ts.map +1 -1
  55. package/packages/app-core/src/runtime/eliza.js +6 -1
  56. package/packages/app-core/src/services/n8n-dispatch.d.ts +10 -2
  57. package/packages/app-core/src/services/n8n-dispatch.d.ts.map +1 -1
  58. package/packages/app-core/src/services/n8n-dispatch.js +102 -8
  59. package/packages/shared/src/contracts/lifeops.d.ts +4 -0
  60. package/packages/shared/src/contracts/lifeops.d.ts.map +1 -1
  61. package/packages/typescript/src/features/advanced-capabilities/clipboard/services/clipboardService.d.ts.map +1 -1
  62. package/packages/typescript/src/features/advanced-capabilities/clipboard/services/clipboardService.js +6 -1
@@ -42,6 +42,8 @@ const NODE_CLASS_ORDER = [
42
42
  ];
43
43
  const PAGE_CHAT_PREFILL_EVENT = "milady:chat:prefill";
44
44
  const DESCRIBE_WORKFLOW_PROMPT = "Describe your workflow";
45
+ const WORKFLOW_PROMPT_PLACEHOLDER = "Describe the trigger and steps, e.g. when a GitHub issue opens, summarize it and post to Discord";
46
+ const AUTOMATIONS_OVERVIEW_VISIBILITY_EVENT = "milady:automations:overview-visibility";
45
47
  function createWorkflowDraftId() {
46
48
  return globalThis.crypto.randomUUID();
47
49
  }
@@ -55,6 +57,24 @@ function prefillPageChat(text, options) {
55
57
  },
56
58
  }));
57
59
  }
60
+ function buildWorkflowCopyRequest(workflow, name) {
61
+ return {
62
+ name,
63
+ nodes: workflow.nodes?.map((node) => ({
64
+ name: node.name,
65
+ type: node.type,
66
+ typeVersion: node.typeVersion ?? 1,
67
+ position: node.position ?? [0, 0],
68
+ parameters: node.parameters ?? {},
69
+ ...(node.notes ? { notes: node.notes } : {}),
70
+ ...(node.notesInFlow !== undefined
71
+ ? { notesInFlow: node.notesInFlow }
72
+ : {}),
73
+ })) ?? [],
74
+ connections: workflow.connections ?? {},
75
+ settings: {},
76
+ };
77
+ }
58
78
  // Reads `#automations.trigger=<id>` from the URL hash. The LifeOps chat-sidebar
59
79
  // Automations widget writes this when a row is clicked, so /automations can
60
80
  // focus the matching trigger card on navigation. Duplicated here (instead of
@@ -199,6 +219,35 @@ function getAutomationIndicatorTone(item) {
199
219
  }
200
220
  return undefined;
201
221
  }
222
+ function getAutomationStatusTone(item) {
223
+ if (item.isDraft)
224
+ return "warning";
225
+ if (item.type === "n8n_workflow") {
226
+ return item.enabled ? "success" : "muted";
227
+ }
228
+ if (item.trigger) {
229
+ const lastTone = toneForLastStatus(item.trigger.lastStatus);
230
+ if (lastTone === "danger")
231
+ return "danger";
232
+ return item.trigger.enabled ? "success" : "muted";
233
+ }
234
+ if (item.task) {
235
+ return item.task.isCompleted ? "muted" : "success";
236
+ }
237
+ return "muted";
238
+ }
239
+ function getTriggerWakeModeLabel(trigger) {
240
+ return trigger.wakeMode === "inject_now"
241
+ ? "Interrupt and run now"
242
+ : "Queue for next cycle";
243
+ }
244
+ function getTriggerStartModeLabel(trigger) {
245
+ if (trigger.triggerType === "once")
246
+ return "One time";
247
+ if (trigger.triggerType === "cron")
248
+ return "Cron schedule";
249
+ return "Repeating";
250
+ }
202
251
  function buildTriggerSchedulePrompt(trigger) {
203
252
  if (trigger.triggerType === "interval") {
204
253
  return `Schedule: interval every ${trigger.intervalMs ?? 0}ms.`;
@@ -236,31 +285,6 @@ function buildWorkflowCompilationPrompt(item) {
236
285
  lines.push("Ask follow-up questions only when workflow intent is genuinely ambiguous.");
237
286
  return lines.join("\n");
238
287
  }
239
- function buildWorkflowDuplicationPrompt(item) {
240
- const lines = [
241
- "Duplicate this n8n workflow into a new workflow draft.",
242
- `Existing workflow name: ${item.title}`,
243
- `Description: ${item.description || "No description provided."}`,
244
- "Recreate the same workflow structure, preserving the intent, nodes, and connections.",
245
- ];
246
- if (item.schedules.length > 0) {
247
- lines.push("Preserve these schedules on the new workflow:");
248
- for (const schedule of item.schedules) {
249
- lines.push(`- ${buildTriggerSchedulePrompt(schedule)}`);
250
- }
251
- }
252
- if (item.workflow) {
253
- lines.push("Existing workflow JSON:");
254
- lines.push(JSON.stringify({
255
- id: item.workflow.id,
256
- name: item.workflow.name,
257
- description: item.workflow.description,
258
- nodes: item.workflow.nodes ?? [],
259
- connections: item.workflow.connections ?? {},
260
- }, null, 2));
261
- }
262
- return lines.join("\n");
263
- }
264
288
  function getNodeClassLabel(className) {
265
289
  switch (className) {
266
290
  case "agent":
@@ -558,9 +582,7 @@ function useAutomationsViewController() {
558
582
  setEditorOpen(true);
559
583
  };
560
584
  const openCreateTask = () => {
561
- resetEditor();
562
- setEditorMode("task");
563
- setEditorOpen(true);
585
+ openCreateTrigger();
564
586
  };
565
587
  const openEditTrigger = (trigger) => {
566
588
  setEditingId(trigger.id);
@@ -591,8 +613,14 @@ function useAutomationsViewController() {
591
613
  if (editingId) {
592
614
  const updated = await updateTrigger(editingId, buildUpdateRequest(form));
593
615
  if (updated) {
594
- setSelectedItemId(`trigger:${updated.id}`);
595
- setSelectedItemKind("trigger");
616
+ if (updated.kind === "workflow" && updated.workflowId) {
617
+ setSelectedItemId(`workflow:${updated.workflowId}`);
618
+ setSelectedItemKind("workflow");
619
+ }
620
+ else {
621
+ setSelectedItemId(`trigger:${updated.id}`);
622
+ setSelectedItemKind("trigger");
623
+ }
596
624
  await refreshAutomations();
597
625
  closeEditor();
598
626
  }
@@ -600,8 +628,14 @@ function useAutomationsViewController() {
600
628
  }
601
629
  const created = await createTrigger(buildCreateRequest(form));
602
630
  if (created) {
603
- setSelectedItemId(`trigger:${created.id}`);
604
- setSelectedItemKind("trigger");
631
+ if (created.kind === "workflow" && created.workflowId) {
632
+ setSelectedItemId(`workflow:${created.workflowId}`);
633
+ setSelectedItemKind("workflow");
634
+ }
635
+ else {
636
+ setSelectedItemId(`trigger:${created.id}`);
637
+ setSelectedItemKind("trigger");
638
+ }
605
639
  void loadTriggerRuns(created.id);
606
640
  await refreshAutomations();
607
641
  closeEditor();
@@ -715,17 +749,21 @@ function useAutomationsViewController() {
715
749
  return allItems[0] ?? null;
716
750
  }, [allItems, editingId, editingTaskId, editorOpen, selectedItemId]);
717
751
  const modalTitle = editorMode === "trigger"
718
- ? editingId
719
- ? t("heartbeatsview.editTitle", {
720
- name: form.displayName.trim() ||
721
- t("automations.taskLabel", {
722
- defaultValue: "Task",
723
- }),
724
- defaultValue: "Edit {{name}}",
725
- })
726
- : t("automations.newTask", {
727
- defaultValue: "New task",
728
- })
752
+ ? form.kind === "workflow"
753
+ ? editingId
754
+ ? `Edit ${form.displayName.trim() || "schedule"}`
755
+ : "New schedule"
756
+ : editingId
757
+ ? t("heartbeatsview.editTitle", {
758
+ name: form.displayName.trim() ||
759
+ t("automations.taskLabel", {
760
+ defaultValue: "Task",
761
+ }),
762
+ defaultValue: "Edit {{name}}",
763
+ })
764
+ : t("automations.newTask", {
765
+ defaultValue: "New task",
766
+ })
729
767
  : editingTaskId
730
768
  ? t("automations.editTask", {
731
769
  defaultValue: "Edit task",
@@ -760,6 +798,7 @@ function useAutomationsViewController() {
760
798
  editorOpen,
761
799
  setEditorOpen,
762
800
  editorMode,
801
+ setEditorMode,
763
802
  formError,
764
803
  setFormError,
765
804
  editorEnabled,
@@ -895,10 +934,8 @@ function WorkflowTemplatesModal({ open, onOpenChange, onSelectTemplate, onSelect
895
934
  defaultValue: "Use template",
896
935
  }) })] })] })] }) }));
897
936
  }
898
- function CreateAutomationDialog({ open, mode, onOpenChange, onCreateTask, onCreateScheduledTask, onCreateWorkflow, }) {
899
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "w-[min(calc(100vw-1.5rem),34rem)] max-w-none", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: mode === "tasks" ? "Create task" : "Create" }), _jsx(DialogDescription, { children: mode === "tasks"
900
- ? "Choose a simple task or a task with a schedule."
901
- : "Choose whether you want a task or a workflow." })] }), _jsxs("div", { className: `grid gap-3 ${mode === "tasks" ? "sm:grid-cols-2" : "sm:grid-cols-3"}`, children: [_jsxs("button", { type: "button", onClick: onCreateTask, className: "rounded-xl border border-border/30 bg-bg/30 p-4 text-left transition-colors hover:border-accent/40 hover:bg-accent/5", children: [_jsx("div", { className: "text-sm font-semibold text-txt", children: "Task" }), _jsx("div", { className: "mt-1 text-xs-tight text-muted/80", children: "A simple text editor for something the agent should work on." })] }), _jsxs("button", { type: "button", onClick: onCreateScheduledTask, className: "rounded-xl border border-border/30 bg-bg/30 p-4 text-left transition-colors hover:border-accent/40 hover:bg-accent/5", children: [_jsx("div", { className: "text-sm font-semibold text-txt", children: "Task with schedule" }), _jsx("div", { className: "mt-1 text-xs-tight text-muted/80", children: "A text task that runs on a schedule instead of a workflow." })] }), mode === "all" ? (_jsxs("button", { type: "button", onClick: onCreateWorkflow, className: "rounded-xl border border-border/30 bg-bg/30 p-4 text-left transition-colors hover:border-accent/40 hover:bg-accent/5", children: [_jsx("div", { className: "text-sm font-semibold text-txt", children: "Workflow" }), _jsx("div", { className: "mt-1 text-xs-tight text-muted/80", children: "Open a graph-based workflow draft and wire the steps visually." })] })) : null] })] }) }));
937
+ function CreateAutomationDialog({ open, onOpenChange, onCreateTask, onCreateWorkflow, }) {
938
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "w-[min(calc(100vw-1.5rem),34rem)] max-w-none", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Create automation" }) }), _jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [_jsx("button", { type: "button", onClick: onCreateTask, className: "rounded-xl border border-border/30 bg-bg/30 p-4 text-left transition-colors hover:border-accent/40 hover:bg-accent/5", children: _jsx("div", { className: "text-sm font-semibold text-txt", children: "Task" }) }), _jsx("button", { type: "button", onClick: onCreateWorkflow, className: "rounded-xl border border-border/30 bg-bg/30 p-4 text-left transition-colors hover:border-accent/40 hover:bg-accent/5", children: _jsx("div", { className: "text-sm font-semibold text-txt", children: "Workflow" }) })] })] }) }));
902
939
  }
903
940
  // ---------------------------------------------------------------------------
904
941
  // Zero-state onboarding CTA (Item 9)
@@ -917,11 +954,7 @@ function AutomationsZeroState({ onBrowseTemplates, onNewTrigger, onNewTask, }) {
917
954
  }
918
955
  function TaskForm() {
919
956
  const { taskFormName, setTaskFormName, taskFormDescription, setTaskFormDescription, editingTaskId, formError, taskSaving, onSubmitTask, onDeleteTask, closeEditor, modalTitle, t, } = useAutomationsViewContext();
920
- return (_jsxs(PagePanel, { variant: "padded", className: "space-y-5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-lg font-semibold text-txt", children: modalTitle }), _jsx(Button, { variant: "ghost", size: "sm", onClick: closeEditor, children: t("common.cancel") })] }), formError && (_jsx("div", { className: "rounded-lg border border-danger/20 bg-danger/10 p-3 text-sm text-danger", children: formError })), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx(FieldLabel, { children: t("common.name", { defaultValue: "Name" }) }), _jsx(Input, { value: taskFormName, onChange: (event) => setTaskFormName(event.target.value), placeholder: t("automations.taskNamePlaceholder", {
921
- defaultValue: "Task name...",
922
- }), autoFocus: true })] }), _jsxs("div", { children: [_jsx(FieldLabel, { children: t("common.description", { defaultValue: "Description" }) }), _jsx(Textarea, { value: taskFormDescription, onChange: (event) => setTaskFormDescription(event.target.value), placeholder: t("automations.taskDescriptionPlaceholder", {
923
- defaultValue: "What should this task do...",
924
- }), rows: 4 })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "default", size: "sm", disabled: taskSaving || !taskFormName.trim(), onClick: () => void onSubmitTask(), children: editingTaskId
957
+ return (_jsxs(PagePanel, { variant: "padded", className: "space-y-5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-lg font-semibold text-txt", children: modalTitle }), _jsx(Button, { variant: "ghost", size: "sm", onClick: closeEditor, children: t("common.cancel") })] }), formError && (_jsx("div", { className: "rounded-lg border border-danger/20 bg-danger/10 p-3 text-sm text-danger", children: formError })), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx(FieldLabel, { children: "Task name" }), _jsx(Input, { value: taskFormName, onChange: (event) => setTaskFormName(event.target.value), placeholder: "Task name...", autoFocus: true })] }), _jsxs("div", { children: [_jsx(FieldLabel, { children: "Prompt" }), _jsx(Textarea, { value: taskFormDescription, onChange: (event) => setTaskFormDescription(event.target.value), placeholder: "What should this task do?", rows: 4 })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "default", size: "sm", disabled: taskSaving || !taskFormName.trim(), onClick: () => void onSubmitTask(), children: editingTaskId
925
958
  ? t("automations.saveTask", {
926
959
  defaultValue: "Save task",
927
960
  })
@@ -972,7 +1005,7 @@ function AutomationNodeCatalogPane({ nodes, }) {
972
1005
  return _jsx(AutomationNodePalette, { nodes: nodes, title: "Nodes" });
973
1006
  }
974
1007
  function TaskAutomationDetailPane({ automation, onPromoteToWorkflow, }) {
975
- const { openEditTask, onDeleteTask, onToggleTaskCompleted, setEditorOpen, setTaskFormDescription, setTaskFormName, setEditingTaskId, setSelectedItemId, setSelectedItemKind, t, uiLanguage, } = useAutomationsViewContext();
1008
+ const { openEditTask, onDeleteTask, onToggleTaskCompleted, setEditorOpen, setEditorMode, setTaskFormDescription, setTaskFormName, setEditingTaskId, setSelectedItemId, setSelectedItemKind, t, uiLanguage, } = useAutomationsViewContext();
976
1009
  const task = automation.task;
977
1010
  if (!task) {
978
1011
  return null;
@@ -981,28 +1014,59 @@ function TaskAutomationDetailPane({ automation, onPromoteToWorkflow, }) {
981
1014
  ? "System"
982
1015
  : task.isCompleted
983
1016
  ? "Completed"
984
- : "Active";
1017
+ : "Open";
985
1018
  const statusTone = automation.system ? "muted" : task.isCompleted ? "muted" : "success";
986
- return (_jsxs("div", { className: "space-y-3", children: [_jsx(DetailHeader, { icon: automation.system ? (_jsx(Settings, { className: "h-3.5 w-3.5", "aria-hidden": true })) : (_jsx(FileText, { className: "h-3.5 w-3.5", "aria-hidden": true })), title: getAutomationDisplayTitle(automation), description: automation.description, status: _jsx(DetailStatusIndicator, { label: statusLabel, tone: statusTone, dotOnly: !automation.system && !task.isCompleted }), actions: !automation.system ? (_jsxs(_Fragment, { children: [_jsx(IconAction, { label: task.isCompleted ? "Reopen" : "Complete", onClick: () => void onToggleTaskCompleted(task.id, task.isCompleted), icon: task.isCompleted ? (_jsx(Circle, { className: "h-3.5 w-3.5" })) : (_jsx(CheckCircle2, { className: "h-3.5 w-3.5" })), tone: task.isCompleted ? "ok" : undefined }), _jsx(IconAction, { label: "Duplicate", onClick: () => {
1019
+ const nextScheduledRun = automation.schedules
1020
+ .map((schedule) => schedule.nextRunAtMs ?? 0)
1021
+ .filter((value) => value > 0)
1022
+ .sort((left, right) => left - right)[0];
1023
+ const taskTypeLabel = automation.system ? "Agent owned" : "Task";
1024
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(DetailHeader, { icon: automation.system ? (_jsx(Settings, { className: "h-3.5 w-3.5", "aria-hidden": true })) : (_jsx(FileText, { className: "h-3.5 w-3.5", "aria-hidden": true })), title: getAutomationDisplayTitle(automation), description: automation.description ||
1025
+ (automation.system ? "Agent-owned manual task." : "Simple text task."), status: _jsx(DetailStatusIndicator, { label: statusLabel, tone: statusTone, dotOnly: !automation.system && !task.isCompleted }), actions: !automation.system ? (_jsxs(_Fragment, { children: [_jsx(IconAction, { label: task.isCompleted ? "Reopen" : "Complete", onClick: () => void onToggleTaskCompleted(task.id, task.isCompleted), icon: task.isCompleted ? (_jsx(Circle, { className: "h-3.5 w-3.5" })) : (_jsx(CheckCircle2, { className: "h-3.5 w-3.5" })), tone: task.isCompleted ? "ok" : undefined }), _jsx(IconAction, { label: "Duplicate", onClick: () => {
987
1026
  setTaskFormName(`${task.name} copy`);
988
1027
  setTaskFormDescription(task.description);
989
1028
  setEditingTaskId(null);
1029
+ setEditorMode("task");
990
1030
  setSelectedItemId(null);
991
1031
  setSelectedItemKind(null);
992
1032
  setEditorOpen(true);
993
- }, icon: _jsx(Copy, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: "Compile to Workflow", onClick: () => void onPromoteToWorkflow(automation), icon: _jsx(GitBranch, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: t("triggersview.Edit"), onClick: () => openEditTask(task), icon: _jsx(EditIcon, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: t("triggersview.Delete"), onClick: () => void onDeleteTask(task.id), icon: _jsx(Trash2, { className: "h-3.5 w-3.5" }), tone: "danger" })] })) : null }), _jsx(DetailStatsRow, { items: [
994
- {
995
- label: "Type",
996
- value: automation.system ? "Agent owned" : "Text task",
997
- },
998
- {
999
- label: "Updated",
1000
- value: formatDateTime(automation.updatedAt, {
1001
- fallback: "—",
1002
- }),
1003
- },
1004
- { label: "Tags", value: task.tags.length },
1005
- ] }), task.tags.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-1 px-1", children: task.tags.map((tag) => (_jsx("span", { className: "rounded bg-bg/50 px-1.5 py-0.5 text-[10px] text-muted", children: tag }, tag))) })), _jsx(DetailSection, { title: "Task brief", children: _jsx("div", { className: "px-3 py-2 text-xs-tight text-muted/80", children: task.description || "No description yet." }) }), automation.schedules.length > 0 && (_jsx(DetailSection, { title: "Schedules", children: _jsx("div", { className: "divide-y divide-border/20", children: automation.schedules.map((schedule) => (_jsxs("div", { className: "flex items-center gap-2 px-3 py-1.5 text-xs-tight", children: [_jsx("span", { className: "truncate font-medium text-txt", children: schedule.displayName }), _jsx(DetailStatusIndicator, { label: schedule.enabled ? "Active" : "Paused", tone: schedule.enabled ? "success" : "muted", dotOnly: schedule.enabled }), _jsx("span", { className: "ml-auto text-muted", children: scheduleLabel(schedule, t, uiLanguage) })] }, schedule.id))) }) }))] }));
1033
+ }, icon: _jsx(Copy, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: "Compile to Workflow", onClick: () => void onPromoteToWorkflow(automation), icon: _jsx(GitBranch, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: t("triggersview.Edit"), onClick: () => openEditTask(task), icon: _jsx(EditIcon, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: t("triggersview.Delete"), onClick: () => void onDeleteTask(task.id), icon: _jsx(Trash2, { className: "h-3.5 w-3.5" }), tone: "danger" })] })) : null }), _jsxs("div", { className: "grid gap-2 sm:grid-cols-2 xl:grid-cols-4", children: [_jsx(OverviewMetricCard, { label: "Task type", value: taskTypeLabel, detail: automation.schedules.length > 0
1034
+ ? formatScheduleCount(automation.schedules.length)
1035
+ : "Run it manually" }), _jsx(OverviewMetricCard, { label: "Status", value: statusLabel, detail: task.isCompleted ? "Already completed" : "Still open", tone: task.isCompleted ? "default" : "ok" }), _jsx(OverviewMetricCard, { label: "Starts", value: nextScheduledRun
1036
+ ? formatRelativeFuture(nextScheduledRun, t)
1037
+ : "Manual", detail: nextScheduledRun
1038
+ ? formatDateTime(nextScheduledRun, {
1039
+ fallback: "",
1040
+ locale: uiLanguage,
1041
+ })
1042
+ : "Run it yourself or attach a schedule", tone: nextScheduledRun ? "ok" : "default" }), _jsx(OverviewMetricCard, { label: "Updated", value: formatRelativePast(automation.updatedAt, t), detail: formatDateTime(automation.updatedAt, { fallback: "—" }) })] }), _jsxs("div", { className: "grid gap-4 xl:grid-cols-[minmax(0,1.12fr)_minmax(320px,0.88fr)]", children: [_jsxs("div", { className: "space-y-4", children: [_jsx(DetailSection, { title: "Prompt", children: _jsx("div", { className: "min-h-[10rem] px-4 py-4 text-sm leading-relaxed text-muted/85 whitespace-pre-wrap", children: task.description || "Describe what this task should do." }) }), _jsx(DetailSection, { title: automation.schedules.length > 0 ? "Starts when" : "How it starts", children: automation.schedules.length > 0 ? (_jsx("div", { className: "divide-y divide-border/20", children: automation.schedules.map((schedule) => (_jsx(OverviewListItem, { title: schedule.displayName, meta: scheduleLabel(schedule, t, uiLanguage), detail: formatDateTime(schedule.nextRunAtMs ?? null, {
1043
+ fallback: "No next run queued",
1044
+ locale: uiLanguage,
1045
+ }), trailing: schedule.enabled ? "Live" : "Paused", tone: schedule.enabled ? "success" : "muted" }, schedule.id))) })) : (_jsx("div", { className: "px-4 py-4 text-xs-tight text-muted/70", children: "This task is manual right now. Add a schedule if you want it to start on its own." })) })] }), _jsx("div", { className: "space-y-4", children: _jsx(DetailSection, { title: "Details", children: _jsx(DetailFactList, { items: [
1046
+ {
1047
+ label: "State",
1048
+ value: task.isCompleted ? "Completed" : "Open",
1049
+ },
1050
+ {
1051
+ label: "Source",
1052
+ value: automation.system
1053
+ ? "Agent-owned checklist item"
1054
+ : "Simple prompt task",
1055
+ },
1056
+ {
1057
+ label: "Next run",
1058
+ value: nextScheduledRun
1059
+ ? formatDateTime(nextScheduledRun, {
1060
+ fallback: "—",
1061
+ locale: uiLanguage,
1062
+ })
1063
+ : "Manual",
1064
+ },
1065
+ {
1066
+ label: "Tags",
1067
+ value: task.tags.length > 0 ? (_jsx("div", { className: "flex flex-wrap justify-end gap-1", children: task.tags.map((tag) => (_jsx("span", { className: "rounded bg-bg/50 px-1.5 py-0.5 text-[10px] text-muted", children: tag }, tag))) })) : ("None"),
1068
+ },
1069
+ ] }) }) })] })] }));
1006
1070
  }
1007
1071
  const AUTOMATION_DRAFT_EXAMPLES = [
1008
1072
  {
@@ -1067,22 +1131,26 @@ function OverviewMetricCard({ label, value, detail, tone = "default", }) {
1067
1131
  : "text-txt";
1068
1132
  return (_jsxs("div", { className: "rounded-xl border border-border/25 bg-bg/35 px-3 py-3 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]", children: [_jsx("div", { className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-muted/70", children: label }), _jsx("div", { className: `mt-2 text-lg font-semibold leading-none ${valueClass}`, children: value }), _jsx("div", { className: "mt-2 text-[11px] leading-snug text-muted/70", children: detail })] }));
1069
1133
  }
1134
+ function OverviewListItem({ title, badge, meta, detail, trailing, tone = "muted", onClick, }) {
1135
+ const content = (_jsxs("div", { className: "flex items-start gap-2", children: [_jsx(StatusDot, { tone: tone, className: "mt-1 shrink-0" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "truncate font-medium text-txt", children: title }), badge ? (_jsx("span", { className: "rounded bg-bg/50 px-1.5 py-0.5 text-[10px] uppercase tracking-[0.12em] text-muted/70", children: badge })) : null, trailing ? (_jsx("span", { className: "ml-auto shrink-0 text-[11px] text-muted/70", children: trailing })) : null] }), meta ? (_jsx("div", { className: "mt-0.5 text-[11px] leading-snug text-muted/70", children: meta })) : null, detail ? (_jsx("div", { className: "mt-1 line-clamp-1 text-[11px] leading-snug text-muted/60", children: detail })) : null] })] }));
1136
+ if (!onClick) {
1137
+ return _jsx("div", { className: "px-3 py-2 text-xs-tight", children: content });
1138
+ }
1139
+ return (_jsx("button", { type: "button", onClick: onClick, className: "w-full px-3 py-2 text-left text-xs-tight transition-colors hover:bg-bg-muted/40", children: content }));
1140
+ }
1070
1141
  function AutomationsDashboard({ items, onSelectItem, onCreateTask, onCreateWorkflow, onUseIdea, }) {
1071
1142
  const { t, uiLanguage } = useAutomationsViewContext();
1072
1143
  const now = Date.now();
1073
1144
  const visibleItems = useMemo(() => items.filter((item) => !item.system), [items]);
1074
1145
  const scheduledEntries = useMemo(() => collectScheduledAutomationEntries(visibleItems), [visibleItems]);
1075
1146
  const draftItems = useMemo(() => sortAutomationsByUpdatedAtDesc(visibleItems.filter((item) => item.isDraft)).slice(0, 4), [visibleItems]);
1076
- const taskHighlights = useMemo(() => sortAutomationsByUpdatedAtDesc(visibleItems.filter((item) => !item.isDraft && item.type !== "n8n_workflow")).slice(0, 5), [visibleItems]);
1077
- const workflowHighlights = useMemo(() => sortAutomationsByUpdatedAtDesc(visibleItems.filter((item) => item.type === "n8n_workflow" && !item.isDraft)).slice(0, 5), [visibleItems]);
1078
- const recentlyChanged = useMemo(() => sortAutomationsByUpdatedAtDesc(visibleItems).slice(0, 6), [visibleItems]);
1079
- const taskCount = visibleItems.filter((item) => item.type === "automation_draft" || item.trigger != null || item.task).length;
1147
+ const taskHighlights = useMemo(() => sortAutomationsByUpdatedAtDesc(visibleItems.filter((item) => item.type !== "n8n_workflow")).slice(0, 6), [visibleItems]);
1148
+ const workflowHighlights = useMemo(() => sortAutomationsByUpdatedAtDesc(visibleItems.filter((item) => item.type === "n8n_workflow")).slice(0, 6), [visibleItems]);
1149
+ const taskCount = visibleItems.filter((item) => item.type !== "n8n_workflow").length;
1080
1150
  const workflowCount = visibleItems.filter((item) => item.type === "n8n_workflow").length;
1081
1151
  const activeCount = visibleItems.filter((item) => item.enabled && !item.isDraft).length;
1082
1152
  const activeScheduleCount = scheduledEntries.filter(({ schedule }) => schedule.enabled).length;
1083
- const failingCount = scheduledEntries.filter(({ schedule }) => toneForLastStatus(schedule.lastStatus) === "danger").length;
1084
1153
  const draftCount = visibleItems.filter((item) => item.isDraft).length;
1085
- const totalCount = visibleItems.filter((item) => !item.isDraft).length;
1086
1154
  const upcoming = useMemo(() => scheduledEntries
1087
1155
  .filter(({ schedule }) => schedule.enabled &&
1088
1156
  typeof schedule.nextRunAtMs === "number" &&
@@ -1112,7 +1180,6 @@ function AutomationsDashboard({ items, onSelectItem, onCreateTask, onCreateWorkf
1112
1180
  key,
1113
1181
  item,
1114
1182
  tone: "danger",
1115
- statusLabel: "Failed",
1116
1183
  title: getOverviewDisplayTitle(item),
1117
1184
  groupLabel: getAutomationGroupLabel(item),
1118
1185
  meta: scheduleLabel(schedule, t, uiLanguage),
@@ -1120,101 +1187,54 @@ function AutomationsDashboard({ items, onSelectItem, onCreateTask, onCreateWorkf
1120
1187
  `Last run ${formatRelativePast(schedule.lastRunAtIso, t)}.`,
1121
1188
  trailing: formatRelativePast(schedule.lastRunAtIso, t),
1122
1189
  }));
1123
- for (const item of draftItems) {
1124
- next.push({
1125
- key: `draft:${item.id}`,
1126
- item,
1127
- tone: "warning",
1128
- statusLabel: "Draft",
1129
- title: getOverviewDisplayTitle(item),
1130
- groupLabel: getAutomationGroupLabel(item),
1131
- meta: "Finish this in the sidebar agent.",
1132
- detail: item.description.trim() ||
1133
- "Describe the trigger, timing, or result you want so this becomes a real automation.",
1134
- trailing: formatRelativePast(item.updatedAt, t),
1135
- });
1136
- }
1137
1190
  return next.slice(0, 6);
1138
- }, [draftItems, failures, t, uiLanguage]);
1139
- if (totalCount === 0 && draftCount === 0) {
1140
- return (_jsxs("div", { className: "space-y-4 px-1 pt-4", children: [_jsx("section", { className: "overflow-hidden rounded-xl border border-border/25 bg-[radial-gradient(circle_at_top_left,rgba(34,211,238,0.14),transparent_38%),radial-gradient(circle_at_top_right,rgba(34,197,94,0.12),transparent_32%),rgba(255,255,255,0.02)]", children: _jsxs("div", { className: "space-y-3 px-4 py-4 sm:px-5", children: [_jsxs("div", { className: "inline-flex items-center gap-2 rounded-full border border-border/25 bg-bg/40 px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-muted/70", children: [_jsx(LayoutDashboard, { className: "h-3 w-3", "aria-hidden": true }), "Overview"] }), _jsxs("div", { className: "space-y-1", children: [_jsx("h2", { className: "text-lg font-semibold text-txt", children: "Build your first task or workflow" }), _jsx("p", { className: "text-xs-tight text-muted/80", children: "Workflows handle multi-step pipelines; tasks are simple prompts that run on a schedule or from an event." })] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { variant: "default", size: "sm", onClick: onCreateTask, children: "New task" }), _jsx(Button, { variant: "outline", size: "sm", onClick: onCreateWorkflow, children: "New workflow" })] })] }) }), _jsxs("div", { className: "grid gap-3 xl:grid-cols-2", children: [_jsx(DetailSection, { title: "Task ideas", children: _jsx("div", { className: "p-2", children: _jsx(OverviewIdeaGrid, { ideas: taskIdeas, onSelect: (idea) => onUseIdea(idea) }) }) }), _jsx(DetailSection, { title: "Workflow ideas", children: _jsx("div", { className: "p-2", children: _jsx(OverviewIdeaGrid, { ideas: workflowIdeas, onSelect: (idea) => onUseIdea(idea) }) }) })] })] }));
1191
+ }, [failures, t, uiLanguage]);
1192
+ if (taskCount === 0 && workflowCount === 0) {
1193
+ return (_jsxs("div", { className: "space-y-4 px-1 pt-4", children: [_jsx("section", { className: "overflow-hidden rounded-xl border border-border/25 bg-[radial-gradient(circle_at_top_left,rgba(34,211,238,0.14),transparent_38%),radial-gradient(circle_at_top_right,rgba(34,197,94,0.12),transparent_32%),rgba(255,255,255,0.02)]", children: _jsxs("div", { className: "space-y-3 px-4 py-4 sm:px-5", children: [_jsxs("div", { className: "inline-flex items-center gap-2 rounded-full border border-border/25 bg-bg/40 px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-muted/70", children: [_jsx(LayoutDashboard, { className: "h-3 w-3", "aria-hidden": true }), "Overview"] }), _jsxs("div", { className: "space-y-1", children: [_jsx("h2", { className: "text-lg font-semibold text-txt", children: "Build your first automation" }), _jsx("p", { className: "text-xs-tight text-muted/80", children: "Workflows handle multi-step pipelines; tasks are simple prompts that run on a schedule or from an event." })] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { variant: "default", size: "sm", onClick: onCreateTask, children: "New task" }), _jsx(Button, { variant: "outline", size: "sm", onClick: onCreateWorkflow, children: "New workflow" })] })] }) }), _jsxs("div", { className: "grid gap-3 xl:grid-cols-2", children: [_jsx(DetailSection, { title: "Task ideas", children: _jsx("div", { className: "p-2", children: _jsx(OverviewIdeaGrid, { ideas: taskIdeas, onSelect: (idea) => onUseIdea(idea) }) }) }), _jsx(DetailSection, { title: "Workflow ideas", children: _jsx("div", { className: "p-2", children: _jsx(OverviewIdeaGrid, { ideas: workflowIdeas, onSelect: (idea) => onUseIdea(idea) }) }) })] }), draftItems.length > 0 && (_jsx(DetailSection, { title: "Drafts in progress", children: _jsx("div", { className: "divide-y divide-border/20", children: draftItems.map((item) => (_jsx(OverviewListItem, { onClick: () => onSelectItem(item), title: getOverviewDisplayTitle(item), badge: "Draft", meta: formatRelativePast(item.updatedAt, t), detail: item.description.trim() ||
1194
+ "Open it and keep shaping it in the sidebar agent.", tone: "warning" }, item.id))) }) }))] }));
1141
1195
  }
1142
- return (_jsxs("div", { className: "space-y-4 px-1 pt-4", children: [_jsx("section", { className: "overflow-hidden rounded-xl border border-border/25 bg-[radial-gradient(circle_at_top_left,rgba(34,197,94,0.12),transparent_36%),radial-gradient(circle_at_top_right,rgba(56,189,248,0.14),transparent_34%),rgba(255,255,255,0.02)]", children: _jsxs("div", { className: "grid gap-4 px-4 py-4 sm:px-5 xl:grid-cols-[minmax(0,1.2fr)_minmax(300px,0.8fr)]", children: [_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "inline-flex items-center gap-2 rounded-full border border-border/25 bg-bg/40 px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-muted/70", children: [_jsx(LayoutDashboard, { className: "h-3 w-3", "aria-hidden": true }), "Overview"] }), _jsxs("div", { className: "space-y-1", children: [_jsx("h2", { className: "text-lg font-semibold text-txt", children: "Keep tasks and workflows moving" }), _jsx("p", { className: "max-w-2xl text-xs-tight text-muted/80", children: "See what runs next, what needs attention, and which automations changed most recently." })] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { variant: "default", size: "sm", onClick: onCreateTask, children: "New task" }), _jsx(Button, { variant: "outline", size: "sm", onClick: onCreateWorkflow, children: "New workflow" })] }), _jsxs("div", { className: "grid gap-2 sm:grid-cols-2 xl:grid-cols-3", children: [_jsx(OverviewMetricCard, { label: "Automations", value: _jsx("span", { className: "tabular-nums", children: totalCount }), detail: `${taskCount} tasks and ${workflowCount} workflows` }), _jsx(OverviewMetricCard, { label: "Active", value: _jsx("span", { className: "tabular-nums", children: activeCount }), detail: activeCount > 0
1143
- ? "Enabled and ready to run."
1144
- : "Nothing is active right now.", tone: activeCount > 0 ? "ok" : "default" }), _jsx(OverviewMetricCard, { label: "Scheduled", value: _jsx("span", { className: "tabular-nums", children: activeScheduleCount }), detail: activeScheduleCount > 0
1145
- ? "Enabled time-based schedules."
1146
- : "No enabled time-based schedules.", tone: activeScheduleCount > 0 ? "ok" : "default" }), _jsx(OverviewMetricCard, { label: "Next run", value: nextUpcoming?.schedule.nextRunAtMs
1147
- ? formatRelativeFuture(nextUpcoming.schedule.nextRunAtMs, t)
1148
- : scheduledEntries.length > 0
1149
- ? "Not queued"
1150
- : "Event only", detail: nextUpcoming?.schedule.nextRunAtMs
1151
- ? `${getOverviewDisplayTitle(nextUpcoming.item)} at ${formatDateTime(nextUpcoming.schedule.nextRunAtMs, {
1152
- fallback: "—",
1153
- locale: uiLanguage,
1154
- })}`
1155
- : scheduledEntries.length > 0
1156
- ? "All time-based schedules are paused or idle."
1157
- : "No time-based schedules yet.", tone: nextUpcoming ? "ok" : "default" }), _jsx(OverviewMetricCard, { label: "Failing", value: _jsx("span", { className: "tabular-nums", children: failingCount }), detail: failingCount > 0
1158
- ? "Recent runs need attention."
1159
- : "All scheduled runs look healthy.", tone: failingCount > 0 ? "danger" : "ok" }), _jsx(OverviewMetricCard, { label: "Drafts", value: _jsx("span", { className: "tabular-nums", children: draftCount }), detail: draftCount > 0
1160
- ? "Still waiting to be finished."
1161
- : "Nothing half-built right now.", tone: draftCount > 0 ? "warning" : "default" })] })] }), _jsxs("div", { className: "rounded-xl border border-border/25 bg-bg/35 p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]", children: [_jsx("div", { className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-muted/70", children: "Next up" }), nextUpcoming?.schedule.nextRunAtMs ? (_jsxs("button", { type: "button", onClick: () => onSelectItem(nextUpcoming.item), className: "mt-3 flex w-full flex-col items-start gap-1 rounded-xl border border-border/20 bg-bg/30 px-3 py-3 text-left transition-colors hover:border-accent/30 hover:bg-accent/5", children: [_jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx("span", { className: "truncate text-sm font-semibold text-txt", children: getOverviewDisplayTitle(nextUpcoming.item) }), _jsx("span", { className: "rounded bg-bg/60 px-1.5 py-0.5 text-[10px] uppercase tracking-[0.12em] text-muted/70", children: getAutomationGroupLabel(nextUpcoming.item) })] }), _jsx("div", { className: "text-[11px] text-muted/70", children: scheduleLabel(nextUpcoming.schedule, t, uiLanguage) }), _jsxs("div", { className: "mt-2 flex w-full items-end justify-between gap-3", children: [_jsx("span", { className: "text-lg font-semibold text-txt", children: formatRelativeFuture(nextUpcoming.schedule.nextRunAtMs, t) }), _jsx("span", { className: "text-[11px] text-muted/70", children: formatDateTime(nextUpcoming.schedule.nextRunAtMs, {
1162
- fallback: "—",
1163
- locale: uiLanguage,
1164
- }) })] })] })) : (_jsxs("div", { className: "mt-3 rounded-xl border border-border/20 bg-bg/30 px-3 py-3", children: [_jsx("div", { className: "text-sm font-semibold text-txt", children: "No time-based run is queued" }), _jsx("div", { className: "mt-1 text-[11px] leading-snug text-muted/70", children: "Event-driven automations can still run. Add a schedule to a task or workflow if you want upcoming activity to show here." })] })), _jsxs("div", { className: "mt-4 space-y-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-[11px] text-muted/80", children: [_jsx(StatusDot, { tone: failingCount > 0 ? "danger" : "success", className: "shrink-0" }), _jsx("span", { children: failingCount > 0
1165
- ? `${failingCount} failing run${failingCount === 1 ? "" : "s"} need attention.`
1166
- : "No failing runs right now." })] }), _jsxs("div", { className: "flex items-center gap-2 text-[11px] text-muted/80", children: [_jsx(StatusDot, { tone: draftCount > 0 ? "warning" : "muted", className: "shrink-0" }), _jsx("span", { children: draftCount > 0
1167
- ? `${draftCount} draft${draftCount === 1 ? "" : "s"} still need to be finished.`
1168
- : "No drafts are waiting on you." })] }), _jsxs("div", { className: "flex items-center gap-2 text-[11px] text-muted/80", children: [_jsx(StatusDot, { tone: activeScheduleCount > 0 ? "success" : "muted", className: "shrink-0" }), _jsx("span", { children: activeScheduleCount > 0
1169
- ? `${activeScheduleCount} enabled schedule${activeScheduleCount === 1 ? "" : "s"} are live.`
1170
- : "No enabled time-based schedules yet." })] })] })] })] }) }), _jsxs("div", { className: "grid gap-4 xl:grid-cols-[minmax(0,1.15fr)_minmax(320px,0.85fr)]", children: [_jsxs("div", { className: "space-y-4", children: [_jsx(DetailSection, { title: "Needs attention", children: attentionEntries.length > 0 ? (_jsx("div", { className: "divide-y divide-border/20", children: attentionEntries.map((entry) => (_jsxs("button", { type: "button", onClick: () => onSelectItem(entry.item), className: "flex w-full items-start gap-2 px-3 py-2 text-left text-xs-tight hover:bg-bg-muted/40", children: [_jsx(StatusDot, { tone: entry.tone, className: "mt-1 shrink-0" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "truncate font-medium text-txt", children: entry.title }), _jsx("span", { className: "rounded bg-bg/50 px-1.5 py-0.5 text-[10px] uppercase tracking-[0.12em] text-muted/70", children: entry.groupLabel }), _jsx("span", { className: "rounded bg-bg/50 px-1.5 py-0.5 text-[10px] uppercase tracking-[0.12em] text-muted/70", children: entry.statusLabel }), _jsx("span", { className: "ml-auto text-muted/70 tabular-nums", children: entry.trailing })] }), _jsx("div", { className: "mt-0.5 text-[11px] text-muted/70", children: entry.meta }), _jsx("div", { className: `mt-1 line-clamp-2 text-[11px] ${entry.tone === "danger"
1171
- ? "text-danger/80"
1172
- : "text-muted/70"}`, children: entry.detail })] })] }, entry.key))) })) : (_jsx("div", { className: "px-3 py-4 text-xs-tight text-muted/70", children: "Everything looks healthy. No failures and no unfinished drafts are bubbling up right now." })) }), _jsx(DetailSection, { title: "Upcoming runs", action: _jsx("span", { className: "text-[10px] uppercase tracking-[0.12em] text-muted/60", children: activeScheduleCount > 0
1173
- ? `${activeScheduleCount} live`
1174
- : "none live" }), children: upcoming.length > 0 ? (_jsx("div", { className: "divide-y divide-border/20", children: upcoming.map(({ key, item, schedule }) => (_jsxs("button", { type: "button", onClick: () => onSelectItem(item), className: "flex w-full items-start gap-2 px-3 py-2 text-left text-xs-tight hover:bg-bg-muted/40", children: [_jsx(Clock3, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted/60", "aria-hidden": true }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "truncate font-medium text-txt", children: getOverviewDisplayTitle(item) }), _jsx("span", { className: "rounded bg-bg/50 px-1.5 py-0.5 text-[10px] uppercase tracking-[0.12em] text-muted/70", children: getAutomationGroupLabel(item) }), _jsx("span", { className: "ml-auto text-muted tabular-nums", children: schedule.nextRunAtMs
1175
- ? formatRelativeFuture(schedule.nextRunAtMs, t)
1176
- : "—" })] }), _jsx("div", { className: "mt-0.5 text-[11px] text-muted/70", children: scheduleLabel(schedule, t, uiLanguage) }), _jsx("div", { className: "mt-1 text-[11px] text-muted/60", children: formatDateTime(schedule.nextRunAtMs ?? null, {
1177
- fallback: "—",
1178
- locale: uiLanguage,
1179
- }) })] })] }, key))) })) : (_jsx("div", { className: "px-3 py-4 text-xs-tight text-muted/70", children: scheduledEntries.length > 0
1180
- ? "Nothing is queued right now. Enabled schedules will appear here when they have a next run."
1181
- : "No time-based schedules yet. Event-driven automations can still run without appearing in this list." })) }), _jsx(DetailSection, { title: "Recent runs", children: recent.length > 0 ? (_jsx("div", { className: "divide-y divide-border/20", children: recent.map(({ key, item, schedule }) => {
1182
- const tone = toneForLastStatus(schedule.lastStatus);
1183
- return (_jsxs("button", { type: "button", onClick: () => onSelectItem(item), className: "flex w-full items-start gap-2 px-3 py-2 text-left text-xs-tight hover:bg-bg-muted/40", children: [_jsx(StatusDot, { tone: tone, className: "mt-1 shrink-0" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "truncate font-medium text-txt", children: getOverviewDisplayTitle(item) }), _jsx("span", { className: "rounded bg-bg/50 px-1.5 py-0.5 text-[10px] uppercase tracking-[0.12em] text-muted/70", children: getAutomationGroupLabel(item) }), _jsx("span", { className: "ml-auto text-muted/70 tabular-nums", children: formatRelativePast(schedule.lastRunAtIso, t) })] }), _jsx("div", { className: "mt-0.5 text-[11px] text-muted/70", children: scheduleLabel(schedule, t, uiLanguage) }), _jsx("div", { className: "mt-1 text-[11px] text-muted/60", children: formatDateTime(schedule.lastRunAtIso, {
1184
- fallback: "—",
1185
- locale: uiLanguage,
1186
- }) })] })] }, key));
1187
- }) })) : (_jsx("div", { className: "px-3 py-4 text-xs-tight text-muted/70", children: scheduledEntries.length > 0
1188
- ? "No runs have completed yet."
1189
- : "Runs will show up here once a scheduled automation starts executing." })) })] }), _jsxs("div", { className: "space-y-4", children: [_jsx(DetailSection, { title: "Tasks", action: _jsx(Button, { variant: "ghost", size: "sm", className: "h-7 px-2 text-xs", onClick: onCreateTask, children: "New task" }), children: taskHighlights.length > 0 ? (_jsx("div", { className: "divide-y divide-border/20", children: taskHighlights.map((item) => (_jsxs("button", { type: "button", onClick: () => onSelectItem(item), className: "flex w-full items-start gap-2 px-3 py-2 text-left text-xs-tight hover:bg-bg-muted/40", children: [_jsx(StatusDot, { tone: item.enabled ? "success" : "muted", className: "mt-1 shrink-0" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "truncate font-medium text-txt", children: getOverviewDisplayTitle(item) }), item.schedules.length > 0 ? (_jsx("span", { className: "rounded bg-bg/50 px-1.5 py-0.5 text-[10px] uppercase tracking-[0.12em] text-muted/70", children: formatScheduleCount(item.schedules.length) })) : null, _jsx("span", { className: "ml-auto text-muted/70 tabular-nums", children: formatRelativePast(item.updatedAt, t) })] }), _jsx("div", { className: "mt-1 line-clamp-2 text-[11px] text-muted/70", children: item.description.trim() ||
1190
- (item.schedules.length > 0
1191
- ? scheduleLabel(item.schedules[0], t, uiLanguage)
1192
- : "Simple prompt automation.") })] })] }, item.id))) })) : (_jsx("div", { className: "px-3 py-4 text-xs-tight text-muted/70", children: "No real tasks yet. Start with a simple prompt automation here." })) }), _jsx(DetailSection, { title: "Workflows", action: _jsx(Button, { variant: "ghost", size: "sm", className: "h-7 px-2 text-xs", onClick: onCreateWorkflow, children: "New workflow" }), children: workflowHighlights.length > 0 ? (_jsx("div", { className: "divide-y divide-border/20", children: workflowHighlights.map((item) => (_jsxs("button", { type: "button", onClick: () => onSelectItem(item), className: "flex w-full items-start gap-2 px-3 py-2 text-left text-xs-tight hover:bg-bg-muted/40", children: [_jsx(StatusDot, { tone: item.enabled ? "success" : "muted", className: "mt-1 shrink-0" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "truncate font-medium text-txt", children: getOverviewDisplayTitle(item) }), _jsxs("span", { className: "rounded bg-bg/50 px-1.5 py-0.5 text-[10px] uppercase tracking-[0.12em] text-muted/70", children: [getWorkflowNodeCount(item), " nodes"] }), item.schedules.length > 0 ? (_jsx("span", { className: "rounded bg-bg/50 px-1.5 py-0.5 text-[10px] uppercase tracking-[0.12em] text-muted/70", children: formatScheduleCount(item.schedules.length) })) : null, _jsx("span", { className: "ml-auto text-muted/70 tabular-nums", children: formatRelativePast(item.updatedAt, t) })] }), _jsx("div", { className: "mt-1 line-clamp-2 text-[11px] text-muted/70", children: item.description.trim() ||
1193
- (item.schedules.length > 0
1194
- ? scheduleLabel(item.schedules[0], t, uiLanguage)
1195
- : "n8n workflow with no time-based schedule yet.") })] })] }, item.id))) })) : (_jsx("div", { className: "px-3 py-4 text-xs-tight text-muted/70", children: "No real workflows yet. Use a workflow when the automation needs multiple deterministic steps." })) }), _jsx(DetailSection, { title: "Recently changed", children: recentlyChanged.length > 0 ? (_jsx("div", { className: "divide-y divide-border/20", children: recentlyChanged.map((item) => (_jsxs("button", { type: "button", onClick: () => onSelectItem(item), className: "flex w-full items-start gap-2 px-3 py-2 text-left text-xs-tight hover:bg-bg-muted/40", children: [_jsx(StatusDot, { tone: item.isDraft
1196
- ? "warning"
1197
- : item.enabled
1198
- ? "success"
1199
- : "muted", className: "mt-1 shrink-0" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "truncate font-medium text-txt", children: getOverviewDisplayTitle(item) }), _jsx("span", { className: "rounded bg-bg/50 px-1.5 py-0.5 text-[10px] uppercase tracking-[0.12em] text-muted/70", children: item.isDraft
1200
- ? "Draft"
1201
- : getAutomationGroupLabel(item) }), _jsx("span", { className: "ml-auto text-muted/70 tabular-nums", children: formatRelativePast(item.updatedAt, t) })] }), _jsx("div", { className: "mt-1 line-clamp-2 text-[11px] text-muted/70", children: item.description.trim() ||
1202
- (item.type === "n8n_workflow"
1203
- ? `${getWorkflowNodeCount(item)} nodes`
1204
- : item.schedules.length > 0
1205
- ? scheduleLabel(item.schedules[0], t, uiLanguage)
1206
- : "Updated recently.") })] })] }, item.id))) })) : (_jsx("div", { className: "px-3 py-4 text-xs-tight text-muted/70", children: "Changes will appear here as automations are created or updated." })) })] })] })] }));
1196
+ return (_jsxs("div", { className: "space-y-4 px-1 pt-4", children: [_jsxs("section", { className: "grid gap-4 xl:grid-cols-[minmax(0,1.15fr)_minmax(320px,0.85fr)]", children: [_jsxs("div", { className: "rounded-xl border border-border/25 bg-[radial-gradient(circle_at_top_left,rgba(34,197,94,0.1),transparent_34%),radial-gradient(circle_at_top_right,rgba(56,189,248,0.12),transparent_30%),rgba(255,255,255,0.02)] px-4 py-4 sm:px-5", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "inline-flex items-center gap-2 rounded-full border border-border/25 bg-bg/40 px-2 py-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-muted/70", children: [_jsx(LayoutDashboard, { className: "h-3 w-3", "aria-hidden": true }), "Overview"] }), _jsx("h2", { className: "text-lg font-semibold text-txt", children: "Automations" })] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { variant: "default", size: "sm", onClick: onCreateTask, children: "New task" }), _jsx(Button, { variant: "outline", size: "sm", onClick: onCreateWorkflow, children: "New workflow" })] })] }), _jsxs("div", { className: "mt-4 grid gap-2 sm:grid-cols-2 xl:grid-cols-4", children: [_jsx(OverviewMetricCard, { label: "Live", value: _jsx("span", { className: "tabular-nums", children: activeCount }), detail: activeCount > 0 ? "Ready to run" : "Nothing enabled", tone: activeCount > 0 ? "ok" : "default" }), _jsx(OverviewMetricCard, { label: "Scheduled", value: _jsx("span", { className: "tabular-nums", children: activeScheduleCount }), detail: activeScheduleCount > 0
1197
+ ? "Time-based runs live"
1198
+ : "No live schedule", tone: activeScheduleCount > 0 ? "ok" : "default" }), _jsx(OverviewMetricCard, { label: "Attention", value: _jsx("span", { className: "tabular-nums", children: attentionEntries.length }), detail: attentionEntries.length > 0 ? "Failed runs" : "Nothing urgent", tone: attentionEntries.length > 0 ? "danger" : "ok" }), _jsx(OverviewMetricCard, { label: "Drafts", value: _jsx("span", { className: "tabular-nums", children: draftCount }), detail: draftCount > 0 ? "Still being shaped" : "Nothing in progress", tone: draftCount > 0 ? "warning" : "default" })] })] }), _jsx(DetailSection, { title: "Next up", className: "h-full", children: nextUpcoming?.schedule.nextRunAtMs ? (_jsxs("button", { type: "button", onClick: () => onSelectItem(nextUpcoming.item), className: "flex w-full flex-col gap-2 px-4 py-4 text-left transition-colors hover:bg-bg-muted/40", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(StatusDot, { tone: "success", className: "shrink-0" }), _jsx("span", { className: "truncate text-sm font-semibold text-txt", children: getOverviewDisplayTitle(nextUpcoming.item) }), _jsx("span", { className: "rounded bg-bg/50 px-1.5 py-0.5 text-[10px] uppercase tracking-[0.12em] text-muted/70", children: getAutomationGroupLabel(nextUpcoming.item) })] }), _jsx("div", { className: "pl-4 text-[11px] text-muted/70", children: scheduleLabel(nextUpcoming.schedule, t, uiLanguage) }), _jsxs("div", { className: "pl-4", children: [_jsx("div", { className: "text-lg font-semibold text-txt", children: formatRelativeFuture(nextUpcoming.schedule.nextRunAtMs, t) }), _jsx("div", { className: "mt-1 text-[11px] text-muted/70", children: formatDateTime(nextUpcoming.schedule.nextRunAtMs, {
1199
+ fallback: "",
1200
+ locale: uiLanguage,
1201
+ }) })] })] })) : (_jsxs("div", { className: "flex h-full min-h-[11.75rem] flex-col justify-between gap-3 px-4 py-4", children: [_jsx("div", { className: "space-y-1", children: _jsx("div", { className: "text-sm font-medium text-txt", children: "None queued." }) }), draftItems[0] ? (_jsx(Button, { variant: "outline", size: "sm", className: "w-fit", onClick: () => onSelectItem(draftItems[0]), children: "Continue draft" })) : null] })) })] }), attentionEntries.length > 0 ? (_jsx(DetailSection, { title: "Needs attention", children: _jsx("div", { className: "divide-y divide-border/20", children: attentionEntries.map((entry) => (_jsx(OverviewListItem, { onClick: () => onSelectItem(entry.item), title: entry.title, badge: entry.groupLabel, meta: entry.meta, detail: entry.detail, trailing: entry.trailing, tone: entry.tone }, entry.key))) }) })) : null, _jsxs("div", { className: "grid gap-4 xl:grid-cols-2", children: [_jsx(DetailSection, { title: "Upcoming runs", className: "h-full", children: upcoming.length > 0 ? (_jsx("div", { className: "divide-y divide-border/20", children: upcoming.map(({ key, item, schedule }) => (_jsx(OverviewListItem, { onClick: () => onSelectItem(item), title: getOverviewDisplayTitle(item), badge: getAutomationGroupLabel(item), meta: scheduleLabel(schedule, t, uiLanguage), detail: formatDateTime(schedule.nextRunAtMs ?? null, {
1202
+ fallback: "—",
1203
+ locale: uiLanguage,
1204
+ }), trailing: schedule.nextRunAtMs
1205
+ ? formatRelativeFuture(schedule.nextRunAtMs, t)
1206
+ : "—", tone: "success" }, key))) })) : (_jsx("div", { className: "px-3 py-4 text-xs-tight text-muted/70", children: scheduledEntries.length > 0 ? "None queued." : "No schedules." })) }), _jsx(DetailSection, { title: "Recent runs", className: "h-full", children: recent.length > 0 ? (_jsx("div", { className: "divide-y divide-border/20", children: recent.map(({ key, item, schedule }) => {
1207
+ const tone = toneForLastStatus(schedule.lastStatus);
1208
+ return (_jsx(OverviewListItem, { onClick: () => onSelectItem(item), title: getOverviewDisplayTitle(item), badge: getAutomationGroupLabel(item), meta: scheduleLabel(schedule, t, uiLanguage), detail: formatDateTime(schedule.lastRunAtIso, {
1209
+ fallback: "—",
1210
+ locale: uiLanguage,
1211
+ }), trailing: formatRelativePast(schedule.lastRunAtIso, t), tone: tone }, key));
1212
+ }) })) : (_jsx("div", { className: "px-3 py-4 text-xs-tight text-muted/70", children: "No runs yet." })) })] }), _jsxs("div", { className: "grid gap-4 xl:grid-cols-2", children: [_jsx(DetailSection, { title: "Tasks", className: "h-full", action: _jsx(Button, { variant: "ghost", size: "sm", className: "h-7 px-2 text-xs", onClick: onCreateTask, children: "New task" }), children: taskHighlights.length > 0 ? (_jsx("div", { className: "divide-y divide-border/20", children: taskHighlights.map((item) => (_jsx(OverviewListItem, { onClick: () => onSelectItem(item), title: getOverviewDisplayTitle(item), badge: item.isDraft
1213
+ ? "Draft"
1214
+ : item.schedules.length > 0
1215
+ ? formatScheduleCount(item.schedules.length)
1216
+ : undefined, meta: item.isDraft
1217
+ ? "In progress"
1218
+ : item.schedules.length > 0
1219
+ ? scheduleLabel(item.schedules[0], t, uiLanguage)
1220
+ : "Event or manual", trailing: formatRelativePast(item.updatedAt, t), tone: getAutomationStatusTone(item) }, item.id))) })) : (_jsx("div", { className: "px-3 py-4 text-xs-tight text-muted/70", children: "No tasks yet." })) }), _jsx(DetailSection, { title: "Workflows", className: "h-full", action: _jsx(Button, { variant: "ghost", size: "sm", className: "h-7 px-2 text-xs", onClick: onCreateWorkflow, children: "New workflow" }), children: workflowHighlights.length > 0 ? (_jsx("div", { className: "divide-y divide-border/20", children: workflowHighlights.map((item) => (_jsx(OverviewListItem, { onClick: () => onSelectItem(item), title: getOverviewDisplayTitle(item), badge: item.isDraft
1221
+ ? "Draft"
1222
+ : `${getWorkflowNodeCount(item)} nodes`, meta: item.isDraft
1223
+ ? "In progress"
1224
+ : item.schedules.length > 0
1225
+ ? scheduleLabel(item.schedules[0], t, uiLanguage)
1226
+ : "Event or manual", trailing: formatRelativePast(item.updatedAt, t), tone: getAutomationStatusTone(item) }, item.id))) })) : (_jsx("div", { className: "px-3 py-4 text-xs-tight text-muted/70", children: "No workflows yet." })) })] })] }));
1207
1227
  }
1208
- function AutomationDraftPane({ automation, onSeedPrompt, }) {
1228
+ function AutomationDraftPane({ automation, onSeedPrompt, onDeleteDraft, }) {
1209
1229
  const chatChrome = useAppWorkspaceChatChrome();
1210
1230
  const openSidebarDraftChat = useCallback((prompt) => {
1211
1231
  chatChrome?.openChat();
1212
1232
  onSeedPrompt(prompt);
1213
1233
  }, [chatChrome, onSeedPrompt]);
1214
- return (_jsxs("div", { className: "space-y-4 px-4 pt-6", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("h2", { className: "text-lg font-semibold text-txt", children: getAutomationDisplayTitle(automation) }), _jsx("p", { className: "max-w-2xl text-xs-tight text-muted/80", children: "Use the sidebar agent to turn this draft into a real task or workflow. Describe the trigger, schedule, and result you want, and the draft will materialize into the actual automation." })] }), _jsx(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5 px-3 text-sm", onClick: () => openSidebarDraftChat(DESCRIBE_WORKFLOW_PROMPT), children: DESCRIBE_WORKFLOW_PROMPT })] }), _jsx("div", { className: "grid gap-1.5 sm:grid-cols-2", children: AUTOMATION_DRAFT_EXAMPLES.map((example) => {
1234
+ return (_jsxs("div", { className: "space-y-4 px-4 pt-6", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("h2", { className: "text-lg font-semibold text-txt", children: getAutomationDisplayTitle(automation) }), _jsx("p", { className: "max-w-2xl text-xs-tight text-muted/80", children: "Use the sidebar agent to turn this draft into a task or workflow. Say what should start it and what result you want." })] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5 px-3 text-sm", onClick: () => openSidebarDraftChat(DESCRIBE_WORKFLOW_PROMPT), children: DESCRIBE_WORKFLOW_PROMPT }), _jsx(Button, { variant: "ghost", size: "sm", className: "h-8 px-3 text-sm text-danger hover:bg-danger/10 hover:text-danger", onClick: () => void onDeleteDraft(automation), children: "Delete draft" })] })] }), _jsx("div", { className: "grid gap-1.5 sm:grid-cols-2", children: AUTOMATION_DRAFT_EXAMPLES.map((example) => {
1215
1235
  const Icon = example.icon;
1216
1236
  return (_jsxs("button", { type: "button", onClick: () => openSidebarDraftChat(example.prompt), className: "group flex items-start gap-2 rounded-[var(--radius-sm)] border border-border/25 bg-bg/30 px-3 py-2 text-left transition-colors hover:border-accent/40 hover:bg-accent/5", children: [_jsx(Icon, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-accent/80", "aria-hidden": true }), _jsxs("div", { className: "min-w-0 flex-1 space-y-0.5", children: [_jsx("div", { className: "text-xs-tight font-semibold text-txt", children: example.label }), _jsx("div", { className: "line-clamp-2 text-[11px] leading-snug text-muted/70", children: example.prompt })] })] }, example.label));
1217
- }) }), _jsx("p", { className: "px-1 text-[11px] text-muted/60", children: "Keep the conversation in the sidebar agent. The draft will update here as it becomes a real task or workflow." })] }));
1237
+ }) }), _jsx("p", { className: "px-1 text-[11px] text-muted/60", children: "The draft updates here as the automation takes shape." })] }));
1218
1238
  }
1219
1239
  function IconAction({ icon, label, onClick, tone, disabled, ariaBusy, }) {
1220
1240
  const toneClass = tone === "warning"
@@ -1235,11 +1255,11 @@ function DetailStatusIndicator({ label, tone, dotOnly = false, }) {
1235
1255
  }
1236
1256
  return _jsx(StatusBadge, { label: label, variant: tone, withDot: true });
1237
1257
  }
1238
- function DetailStatsRow({ items, }) {
1239
- return (_jsx("dl", { className: "flex flex-wrap items-center gap-x-5 gap-y-1 px-1 text-xs-tight", children: items.map((item) => (_jsxs("div", { className: "flex items-baseline gap-1.5", children: [_jsx("dt", { className: "text-2xs font-semibold uppercase tracking-[0.12em] text-muted/70", children: item.label }), _jsx("dd", { className: "font-medium text-txt", children: item.value })] }, item.label))) }));
1258
+ function DetailSection({ title, action, children, className, }) {
1259
+ return (_jsxs("section", { className: `rounded-[var(--radius-sm)] border border-border/25 bg-bg/20 ${className ?? ""}`, children: [_jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-border/20 px-3 py-1.5", children: [_jsx("div", { className: "text-2xs font-semibold uppercase tracking-[0.14em] text-muted", children: title }), action] }), _jsx("div", { className: "py-1", children: children })] }));
1240
1260
  }
1241
- function DetailSection({ title, action, children, }) {
1242
- return (_jsxs("section", { className: "rounded-[var(--radius-sm)] border border-border/25 bg-bg/20", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-border/20 px-3 py-1.5", children: [_jsx("div", { className: "text-2xs font-semibold uppercase tracking-[0.14em] text-muted", children: title }), action] }), _jsx("div", { className: "py-1", children: children })] }));
1261
+ function DetailFactList({ items, }) {
1262
+ return (_jsx("dl", { className: "divide-y divide-border/20", children: items.map((item) => (_jsxs("div", { className: "flex items-start justify-between gap-3 px-3 py-2 text-xs-tight", children: [_jsx("dt", { className: "shrink-0 text-muted/70", children: item.label }), _jsx("dd", { className: "min-w-0 text-right text-txt", children: item.value })] }, item.label))) }));
1243
1263
  }
1244
1264
  function TriggerAutomationDetailPane({ automation, onPromoteToWorkflow, }) {
1245
1265
  const { t, uiLanguage, openEditTrigger, onRunSelectedTrigger, onToggleTriggerEnabled, loadTriggerRuns, triggerRunsById, setForm, setEditorOpen, setEditingId, setSelectedItemId, setSelectedItemKind, } = useAutomationsViewContext();
@@ -1265,7 +1285,13 @@ function TriggerAutomationDetailPane({ automation, onPromoteToWorkflow, }) {
1265
1285
  counts.failureCount += 1;
1266
1286
  return counts;
1267
1287
  }, { failureCount: 0, successCount: 0 });
1268
- return (_jsxs("div", { className: "space-y-3", children: [_jsx(DetailHeader, { icon: _jsx(Clock3, { className: "h-3.5 w-3.5", "aria-hidden": true }), title: getAutomationDisplayTitle(automation), description: automation.description, status: _jsx(DetailStatusIndicator, { label: trigger.enabled ? "Active" : "Paused", tone: trigger.enabled ? "success" : "muted", dotOnly: trigger.enabled }), actions: _jsxs(_Fragment, { children: [_jsx(IconAction, { label: trigger.enabled ? "Pause" : "Resume", onClick: () => void onToggleTriggerEnabled(trigger.id, trigger.enabled), icon: trigger.enabled ? (_jsx(Pause, { className: "h-3.5 w-3.5" })) : (_jsx(Play, { className: "h-3.5 w-3.5" })), tone: trigger.enabled ? "warning" : "ok" }), _jsx(IconAction, { label: t("triggersview.RunNow"), onClick: () => void onRunSelectedTrigger(trigger.id), icon: _jsx(Zap, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: t("triggersview.Edit"), onClick: () => openEditTrigger(trigger), icon: _jsx(EditIcon, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: t("heartbeatsview.duplicate"), onClick: () => {
1288
+ const nextRunLabel = trigger.nextRunAtMs
1289
+ ? formatRelativeFuture(trigger.nextRunAtMs, t)
1290
+ : "Event or manual";
1291
+ const whatRuns = trigger.kind === "workflow"
1292
+ ? trigger.workflowName || "Selected workflow"
1293
+ : trigger.instructions || "No prompt yet.";
1294
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(DetailHeader, { icon: _jsx(Clock3, { className: "h-3.5 w-3.5", "aria-hidden": true }), title: getAutomationDisplayTitle(automation), description: automation.description, status: _jsx(DetailStatusIndicator, { label: trigger.enabled ? "Active" : "Paused", tone: trigger.enabled ? "success" : "muted", dotOnly: trigger.enabled }), actions: _jsxs(_Fragment, { children: [_jsx(IconAction, { label: trigger.enabled ? "Pause" : "Resume", onClick: () => void onToggleTriggerEnabled(trigger.id, trigger.enabled), icon: trigger.enabled ? (_jsx(Pause, { className: "h-3.5 w-3.5" })) : (_jsx(Play, { className: "h-3.5 w-3.5" })), tone: trigger.enabled ? "warning" : "ok" }), _jsx(IconAction, { label: t("triggersview.RunNow"), onClick: () => void onRunSelectedTrigger(trigger.id), icon: _jsx(Zap, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: t("triggersview.Edit"), onClick: () => openEditTrigger(trigger), icon: _jsx(EditIcon, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: t("heartbeatsview.duplicate"), onClick: () => {
1269
1295
  setForm({
1270
1296
  ...formFromTrigger(trigger),
1271
1297
  displayName: `${trigger.displayName} (copy)`,
@@ -1274,36 +1300,43 @@ function TriggerAutomationDetailPane({ automation, onPromoteToWorkflow, }) {
1274
1300
  setEditingId(null);
1275
1301
  setSelectedItemId(null);
1276
1302
  setSelectedItemKind(null);
1277
- }, icon: _jsx(Copy, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: "Compile to Workflow", onClick: () => void onPromoteToWorkflow(automation), icon: _jsx(GitBranch, { className: "h-3.5 w-3.5" }) })] }) }), _jsx(DetailStatsRow, { items: [
1278
- {
1279
- label: "Schedule",
1280
- value: scheduleLabel(trigger, t, uiLanguage),
1281
- },
1282
- {
1283
- label: "Last run",
1284
- value: formatDateTime(trigger.lastRunAtIso, {
1285
- fallback: "—",
1303
+ }, icon: _jsx(Copy, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: "Compile to Workflow", onClick: () => void onPromoteToWorkflow(automation), icon: _jsx(GitBranch, { className: "h-3.5 w-3.5" }) })] }) }), _jsxs("div", { className: "grid gap-2 sm:grid-cols-2 xl:grid-cols-5", children: [_jsx(OverviewMetricCard, { label: "Runs", value: _jsx("span", { className: "tabular-nums", children: selectedRuns.length }), detail: `${successCount} successful · ${failureCount} failed`, tone: failureCount > 0 ? "danger" : successCount > 0 ? "ok" : "default" }), _jsx(OverviewMetricCard, { label: "Starts", value: getTriggerStartModeLabel(trigger), detail: scheduleLabel(trigger, t, uiLanguage) }), _jsx(OverviewMetricCard, { label: "When it fires", value: getTriggerWakeModeLabel(trigger), detail: trigger.enabled ? "Enabled" : "Paused", tone: trigger.enabled ? "ok" : "default" }), _jsx(OverviewMetricCard, { label: "Next run", value: nextRunLabel, detail: formatDateTime(trigger.nextRunAtMs, {
1304
+ fallback: "No time-based run queued",
1286
1305
  locale: uiLanguage,
1287
- }),
1288
- },
1289
- {
1290
- label: "Next run",
1291
- value: formatDateTime(trigger.nextRunAtMs, {
1292
- fallback: "—",
1306
+ }), tone: trigger.nextRunAtMs ? "ok" : "default" }), _jsx(OverviewMetricCard, { label: "Last run", value: formatRelativePast(trigger.lastRunAtIso, t), detail: formatDateTime(trigger.lastRunAtIso, {
1307
+ fallback: "Not run yet",
1293
1308
  locale: uiLanguage,
1294
- }),
1295
- },
1296
- {
1297
- label: "Runs",
1298
- value: (_jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx("span", { className: "text-txt tabular-nums", children: selectedRuns.length }), successCount > 0 && (_jsxs("span", { className: "text-ok", children: [successCount, "\u2713"] })), failureCount > 0 && (_jsxs("span", { className: "text-danger", children: [failureCount, "\u2717"] }))] })),
1299
- },
1300
- ] }), _jsx(DetailSection, { title: "Run history", action: _jsx(IconAction, { label: t("common.refresh"), onClick: () => void loadTriggerRuns(trigger.id), icon: _jsx(RefreshCw, { className: "h-3.5 w-3.5" }) }), children: !hasLoadedRuns ? (_jsxs("div", { className: "flex items-center gap-2 px-3 py-2 text-xs-tight text-muted/70", children: [_jsx("div", { className: "h-3 w-3 animate-spin rounded-full border-2 border-muted/30 border-t-muted/80" }), t("databaseview.Loading")] })) : selectedRuns.length === 0 ? (_jsx("div", { className: "px-3 py-2 text-xs-tight text-muted/60", children: t("heartbeatsview.noRunsYetMessage") })) : (_jsx("div", { className: "divide-y divide-border/20", children: selectedRuns.map((run) => (_jsxs("div", { className: "flex items-center gap-2 px-3 py-1.5 text-xs-tight", children: [_jsx(StatusBadge, { label: localizedExecutionStatus(run.status, t), variant: toneForLastStatus(run.status) }), _jsx("span", { className: "text-muted/70 tabular-nums", children: formatDateTime(run.startedAt, { locale: uiLanguage }) }), _jsx("span", { className: "text-muted/60", children: formatDurationMs(run.latencyMs, { t }) }), _jsx("span", { className: "ml-auto rounded bg-bg/40 px-1 py-0.5 font-mono text-[10px] text-muted/60", children: run.source }), run.error && (_jsx("span", { className: "basis-full whitespace-pre-wrap rounded border border-danger/20 bg-danger/10 px-2 py-1 font-mono text-[11px] text-danger/90", children: run.error }))] }, run.triggerRunId))) })) })] }));
1309
+ }) })] }), _jsx(DetailSection, { title: trigger.kind === "workflow" ? "Runs this workflow" : "Prompt", children: _jsx("div", { className: "min-h-[8rem] px-4 py-4 text-sm leading-relaxed text-muted/85 whitespace-pre-wrap", children: whatRuns }) }), _jsxs("div", { className: "grid gap-4 xl:grid-cols-[minmax(0,1fr)_minmax(320px,0.95fr)]", children: [_jsx(DetailSection, { title: "Run history", className: "h-full", action: _jsx(IconAction, { label: t("common.refresh"), onClick: () => void loadTriggerRuns(trigger.id), icon: _jsx(RefreshCw, { className: "h-3.5 w-3.5" }) }), children: !hasLoadedRuns ? (_jsxs("div", { className: "flex items-center gap-2 px-3 py-2 text-xs-tight text-muted/70", children: [_jsx("div", { className: "h-3 w-3 animate-spin rounded-full border-2 border-muted/30 border-t-muted/80" }), t("databaseview.Loading")] })) : selectedRuns.length === 0 ? (_jsx("div", { className: "px-3 py-3 text-xs-tight text-muted/60", children: "No runs yet." })) : (_jsx("div", { className: "divide-y divide-border/20", children: selectedRuns.map((run) => (_jsxs("div", { className: "flex flex-wrap items-center gap-2 px-3 py-2 text-xs-tight", children: [_jsx(StatusBadge, { label: localizedExecutionStatus(run.status, t), variant: toneForLastStatus(run.status) }), _jsx("span", { className: "text-muted/70 tabular-nums", children: formatDateTime(run.startedAt, { locale: uiLanguage }) }), _jsx("span", { className: "text-muted/60", children: formatDurationMs(run.latencyMs, { t }) }), _jsx("span", { className: "ml-auto rounded bg-bg/40 px-1 py-0.5 font-mono text-[10px] text-muted/60", children: run.source }), run.error ? (_jsx("div", { className: "basis-full whitespace-pre-wrap rounded border border-danger/20 bg-danger/10 px-2 py-1 font-mono text-[11px] text-danger/90", children: run.error })) : null] }, run.triggerRunId))) })) }), _jsx(DetailSection, { title: "Schedule & behavior", className: "h-full", children: _jsx(DetailFactList, { items: [
1310
+ {
1311
+ label: "Schedule",
1312
+ value: scheduleLabel(trigger, t, uiLanguage),
1313
+ },
1314
+ {
1315
+ label: "Trigger type",
1316
+ value: getTriggerStartModeLabel(trigger),
1317
+ },
1318
+ {
1319
+ label: "Wake mode",
1320
+ value: getTriggerWakeModeLabel(trigger),
1321
+ },
1322
+ {
1323
+ label: "Status",
1324
+ value: trigger.enabled ? "Enabled" : "Paused",
1325
+ },
1326
+ {
1327
+ label: "Max runs",
1328
+ value: trigger.maxRuns ? String(trigger.maxRuns) : "Unlimited",
1329
+ },
1330
+ ] }) })] })] }));
1301
1331
  }
1302
- function WorkflowAutomationDetailPane({ automation, n8nStatus, workflowFetchError, workflowBusyId, workflowOpsBusy, onDeleteWorkflow, onDuplicateWorkflow, onRefreshWorkflows, onStartLocalN8n, onToggleWorkflowActive, }) {
1332
+ function WorkflowAutomationDetailPane({ automation, n8nStatus, workflowFetchError, workflowBusyId, workflowOpsBusy, onDeleteDraft, onDeleteWorkflow, onDuplicateWorkflow, onGenerateWorkflow, onRefreshWorkflows, onScheduleWorkflow, onStartLocalN8n, onToggleWorkflowActive, }) {
1303
1333
  const { t, uiLanguage } = useApp();
1304
1334
  const chatChrome = useAppWorkspaceChatChrome();
1305
1335
  const [fullWorkflow, setFullWorkflow] = useState(automation.workflow ?? null);
1306
1336
  const [workflowLoading, setWorkflowLoading] = useState(false);
1337
+ const [workflowPrompt, setWorkflowPrompt] = useState("");
1338
+ const [workflowPromptError, setWorkflowPromptError] = useState(null);
1339
+ const [workflowPromptSaving, setWorkflowPromptSaving] = useState(false);
1307
1340
  const workflowGenerating = useWorkflowGenerationState(automation.workflowId);
1308
1341
  const busy = workflowOpsBusy ||
1309
1342
  (automation.workflowId != null && workflowBusyId === automation.workflowId);
@@ -1312,10 +1345,42 @@ function WorkflowAutomationDetailPane({ automation, n8nStatus, workflowFetchErro
1312
1345
  graphWorkflow?.nodes?.length ??
1313
1346
  getWorkflowNodeCount(automation);
1314
1347
  const workflowIsActive = graphWorkflow?.active ?? automation.enabled;
1348
+ const nextWorkflowRun = automation.schedules
1349
+ .map((schedule) => schedule.nextRunAtMs ?? 0)
1350
+ .filter((value) => value > 0)
1351
+ .sort((left, right) => left - right)[0];
1352
+ const workflowIdeas = AUTOMATION_DRAFT_EXAMPLES.filter((idea) => idea.kind === "workflow");
1353
+ const showWorkflowStarterIdeas = automation.isDraft || nodeCount === 0;
1354
+ const showWorkflowPromptBox = automation.isDraft || nodeCount === 0;
1315
1355
  const handleDescribeWorkflow = useCallback(() => {
1316
1356
  chatChrome?.openChat();
1317
1357
  prefillPageChat(DESCRIBE_WORKFLOW_PROMPT, { select: true });
1318
1358
  }, [chatChrome]);
1359
+ const handleUseWorkflowIdea = useCallback((idea) => {
1360
+ chatChrome?.openChat();
1361
+ prefillPageChat(idea.prompt, { select: true });
1362
+ }, [chatChrome]);
1363
+ const submitWorkflowPrompt = useCallback(async () => {
1364
+ const prompt = workflowPrompt.trim();
1365
+ if (!prompt) {
1366
+ setWorkflowPromptError("Describe what this workflow should do.");
1367
+ return;
1368
+ }
1369
+ setWorkflowPromptError(null);
1370
+ setWorkflowPromptSaving(true);
1371
+ try {
1372
+ await onGenerateWorkflow(automation, prompt);
1373
+ setWorkflowPrompt("");
1374
+ chatChrome?.openChat();
1375
+ prefillPageChat(prompt, { select: false });
1376
+ }
1377
+ catch (error) {
1378
+ setWorkflowPromptError(error instanceof Error ? error.message : "Failed to generate workflow.");
1379
+ }
1380
+ finally {
1381
+ setWorkflowPromptSaving(false);
1382
+ }
1383
+ }, [automation, chatChrome, onGenerateWorkflow, workflowPrompt]);
1319
1384
  useEffect(() => {
1320
1385
  let cancelled = false;
1321
1386
  setFullWorkflow(automation.workflow ?? null);
@@ -1349,10 +1414,7 @@ function WorkflowAutomationDetailPane({ automation, n8nStatus, workflowFetchErro
1349
1414
  automation.workflow,
1350
1415
  automation.workflowId,
1351
1416
  ]);
1352
- return (_jsxs("div", { className: "space-y-3", children: [_jsx(WorkflowRuntimeNotice, { status: n8nStatus, workflowFetchError: workflowFetchError, busy: busy, onRefresh: () => void onRefreshWorkflows(), onStartLocal: () => void onStartLocalN8n() }), _jsx(DetailHeader, { icon: _jsx(Workflow, { className: "h-3.5 w-3.5", "aria-hidden": true }), title: getAutomationDisplayTitle(automation), description: automation.description ||
1353
- (automation.isDraft
1354
- ? "Draft the workflow in chat and the graph will fill in as it is created."
1355
- : null), status: _jsx(DetailStatusIndicator, { label: automation.isDraft
1417
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(WorkflowRuntimeNotice, { status: n8nStatus, workflowFetchError: workflowFetchError, busy: busy, onRefresh: () => void onRefreshWorkflows(), onStartLocal: () => void onStartLocalN8n() }), _jsx(DetailHeader, { icon: _jsx(Workflow, { className: "h-3.5 w-3.5", "aria-hidden": true }), title: getAutomationDisplayTitle(automation), description: null, status: _jsx(DetailStatusIndicator, { label: automation.isDraft
1356
1418
  ? "Draft"
1357
1419
  : automation.enabled
1358
1420
  ? "Active"
@@ -1360,7 +1422,7 @@ function WorkflowAutomationDetailPane({ automation, n8nStatus, workflowFetchErro
1360
1422
  ? "warning"
1361
1423
  : automation.enabled
1362
1424
  ? "success"
1363
- : "muted", dotOnly: !automation.isDraft && automation.enabled }), actions: automation.workflowId ? (_jsxs(_Fragment, { children: [_jsx(IconAction, { label: busy
1425
+ : "muted", dotOnly: !automation.isDraft && automation.enabled }), actions: automation.isDraft ? (_jsx(IconAction, { label: "Delete draft", onClick: () => void onDeleteDraft(automation), icon: _jsx(Trash2, { className: "h-3.5 w-3.5" }), tone: "danger" })) : automation.workflowId ? (_jsxs(_Fragment, { children: [_jsx(IconAction, { label: busy
1364
1426
  ? t("automations.n8n.updating", {
1365
1427
  defaultValue: "Updating...",
1366
1428
  })
@@ -1370,31 +1432,68 @@ function WorkflowAutomationDetailPane({ automation, n8nStatus, workflowFetchErro
1370
1432
  })
1371
1433
  : t("automations.n8n.activate", {
1372
1434
  defaultValue: "Activate",
1373
- }), onClick: () => void onToggleWorkflowActive(automation), disabled: busy, ariaBusy: busy, icon: workflowIsActive ? (_jsx(Pause, { className: "h-3.5 w-3.5" })) : (_jsx(Play, { className: "h-3.5 w-3.5" })), tone: workflowIsActive ? "warning" : "ok" }), _jsx(IconAction, { label: "Duplicate workflow", onClick: () => void onDuplicateWorkflow(automation), disabled: busy, icon: _jsx(Copy, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: busy
1435
+ }), onClick: () => void onToggleWorkflowActive(automation), disabled: busy, ariaBusy: busy, icon: workflowIsActive ? (_jsx(Pause, { className: "h-3.5 w-3.5" })) : (_jsx(Play, { className: "h-3.5 w-3.5" })), tone: workflowIsActive ? "warning" : "ok" }), _jsx(IconAction, { label: "Schedule workflow", onClick: () => onScheduleWorkflow(automation), disabled: busy, icon: _jsx(Clock3, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: "Duplicate workflow", onClick: () => void onDuplicateWorkflow(automation), disabled: busy, icon: _jsx(Copy, { className: "h-3.5 w-3.5" }) }), _jsx(IconAction, { label: busy
1374
1436
  ? t("automations.n8n.updating", {
1375
1437
  defaultValue: "Updating...",
1376
1438
  })
1377
1439
  : t("automations.n8n.deleteWorkflow", {
1378
1440
  defaultValue: "Delete workflow",
1379
- }), onClick: () => void onDeleteWorkflow(automation), disabled: busy, ariaBusy: busy, icon: _jsx(Trash2, { className: "h-3.5 w-3.5" }), tone: "danger" })] })) : null }), _jsx(DetailStatsRow, { items: [
1380
- {
1381
- label: "ID",
1382
- value: (_jsx("span", { className: "break-all font-mono text-[10px]", children: automation.workflowId ?? automation.draftId ?? "—" })),
1383
- },
1384
- { label: "Nodes", value: nodeCount },
1385
- { label: "Schedules", value: automation.schedules.length },
1386
- {
1387
- label: "Updated",
1388
- value: formatDateTime(automation.updatedAt, {
1441
+ }), onClick: () => void onDeleteWorkflow(automation), disabled: busy, ariaBusy: busy, icon: _jsx(Trash2, { className: "h-3.5 w-3.5" }), tone: "danger" })] })) : null }), _jsxs("div", { className: "grid gap-2 sm:grid-cols-2 xl:grid-cols-5", children: [_jsx(OverviewMetricCard, { label: "Status", value: automation.isDraft
1442
+ ? "Draft"
1443
+ : workflowIsActive
1444
+ ? "Active"
1445
+ : "Paused", detail: automation.schedules.length > 0
1446
+ ? formatScheduleCount(automation.schedules.length)
1447
+ : "No schedule attached", tone: automation.isDraft ? "warning" : workflowIsActive ? "ok" : "default" }), _jsx(OverviewMetricCard, { label: "Nodes", value: _jsx("span", { className: "tabular-nums", children: nodeCount }), detail: nodeCount > 0 ? "Visible in the graph below" : "Still blank" }), _jsx(OverviewMetricCard, { label: "Starts", value: automation.schedules.length > 0
1448
+ ? formatScheduleCount(automation.schedules.length)
1449
+ : "Event or manual", detail: automation.schedules.length > 0
1450
+ ? scheduleLabel(automation.schedules[0], t, uiLanguage)
1451
+ : "No time-based schedule yet" }), _jsx(OverviewMetricCard, { label: "Next run", value: nextWorkflowRun
1452
+ ? formatRelativeFuture(nextWorkflowRun, t)
1453
+ : "Not queued", detail: nextWorkflowRun
1454
+ ? formatDateTime(nextWorkflowRun, {
1455
+ fallback: "—",
1456
+ locale: uiLanguage,
1457
+ })
1458
+ : "Trigger it from an event or attach a schedule", tone: nextWorkflowRun ? "ok" : "default" }), _jsx(OverviewMetricCard, { label: "Updated", value: formatRelativePast(automation.updatedAt, t), detail: formatDateTime(automation.updatedAt, {
1389
1459
  fallback: "—",
1390
1460
  locale: uiLanguage,
1391
- }),
1392
- },
1393
- {
1394
- label: "Backing",
1395
- value: automation.hasBackingWorkflow ? (_jsx("span", { className: "text-ok", children: "n8n" })) : (_jsx("span", { className: "text-warning", children: "room" })),
1396
- },
1397
- ] }), _jsx(DetailSection, { title: "Workflow editor", action: automation.isDraft || nodeCount === 0 ? (_jsx(Button, { variant: "ghost", size: "sm", className: "h-7 px-2 text-xs", onClick: handleDescribeWorkflow, children: DESCRIBE_WORKFLOW_PROMPT })) : undefined, children: _jsx("div", { className: "p-3", children: _jsx(WorkflowGraphViewer, { workflow: graphWorkflow, loading: workflowLoading, isGenerating: workflowGenerating, emptyStateActionLabel: DESCRIBE_WORKFLOW_PROMPT, emptyStateHelpText: "Use the sidebar agent to build or edit this workflow.", onEmptyStateAction: handleDescribeWorkflow, status: n8nStatus }) }) }), automation.schedules.length > 0 && (_jsx(DetailSection, { title: "Schedules", children: _jsx("div", { className: "divide-y divide-border/20", children: automation.schedules.map((schedule) => (_jsxs("div", { className: "flex items-center gap-2 px-3 py-1.5 text-xs-tight", children: [_jsx("span", { className: "truncate font-medium text-txt", children: schedule.displayName }), _jsx(DetailStatusIndicator, { label: schedule.enabled ? "Active" : "Paused", tone: schedule.enabled ? "success" : "muted", dotOnly: schedule.enabled }), _jsx("span", { className: "ml-auto text-muted", children: scheduleLabel(schedule, t, uiLanguage) })] }, schedule.id))) }) }))] }));
1461
+ }) })] }), showWorkflowPromptBox && (_jsx(DetailSection, { title: "Create workflow", children: _jsxs("div", { className: "p-3", children: [_jsxs("div", { className: "flex flex-col gap-2 rounded-full border border-border/30 bg-bg/50 p-1.5 shadow-sm sm:flex-row sm:items-center", children: [_jsx(Input, { "data-workflow-prompt-input": "true", value: workflowPrompt, onChange: (event) => setWorkflowPrompt(event.target.value), onKeyDown: (event) => {
1462
+ if (event.key === "Enter" && !event.shiftKey) {
1463
+ event.preventDefault();
1464
+ void submitWorkflowPrompt();
1465
+ }
1466
+ }, placeholder: WORKFLOW_PROMPT_PLACEHOLDER, className: "min-h-10 flex-1 border-0 bg-transparent px-4 shadow-none focus-visible:ring-0", autoFocus: automation.isDraft }), _jsx(Button, { variant: "default", size: "sm", className: "h-10 shrink-0 rounded-full px-5", disabled: workflowPromptSaving || busy, onClick: () => void submitWorkflowPrompt(), children: workflowPromptSaving ? "Generating..." : "Generate" })] }), workflowPromptError && (_jsx("div", { className: "mt-2 text-xs text-danger", children: workflowPromptError }))] }) })), _jsx(DetailSection, { title: "Workflow graph", action: automation.isDraft || nodeCount === 0 ? (_jsx(Button, { variant: "ghost", size: "sm", className: "h-7 px-2 text-xs", onClick: () => {
1467
+ const input = document.querySelector("[data-workflow-prompt-input='true']");
1468
+ input?.focus();
1469
+ if (!input)
1470
+ handleDescribeWorkflow();
1471
+ }, children: DESCRIBE_WORKFLOW_PROMPT })) : undefined, children: _jsx("div", { className: "p-3", children: _jsx(WorkflowGraphViewer, { workflow: graphWorkflow, loading: workflowLoading, isGenerating: workflowGenerating, emptyStateActionLabel: DESCRIBE_WORKFLOW_PROMPT, emptyStateHelpText: "Describe it above to generate the graph.", onEmptyStateAction: () => {
1472
+ const input = document.querySelector("[data-workflow-prompt-input='true']");
1473
+ input?.focus();
1474
+ if (!input)
1475
+ handleDescribeWorkflow();
1476
+ }, status: n8nStatus }) }) }), _jsxs("div", { className: "grid gap-4 xl:grid-cols-[minmax(0,1fr)_minmax(320px,0.95fr)]", children: [_jsx(DetailSection, { title: "Starts when", className: "h-full", children: automation.schedules.length > 0 ? (_jsx("div", { className: "divide-y divide-border/20", children: automation.schedules.map((schedule) => (_jsx(OverviewListItem, { title: schedule.displayName, meta: scheduleLabel(schedule, t, uiLanguage), detail: formatDateTime(schedule.nextRunAtMs ?? null, {
1477
+ fallback: "No next run queued",
1478
+ locale: uiLanguage,
1479
+ }), trailing: schedule.enabled ? "Live" : "Paused", tone: schedule.enabled ? "success" : "muted" }, schedule.id))) })) : (_jsx("div", { className: "px-4 py-4 text-xs-tight text-muted/70", children: "No schedule yet. Start it from an event or add one in the sidebar." })) }), showWorkflowStarterIdeas ? (_jsx(DetailSection, { title: "Starter ideas", className: "h-full", children: _jsx("div", { className: "p-2", children: _jsx(OverviewIdeaGrid, { ideas: workflowIdeas, onSelect: handleUseWorkflowIdea }) }) })) : (_jsx(DetailSection, { title: "Details", className: "h-full", children: _jsx(DetailFactList, { items: [
1480
+ {
1481
+ label: "Type",
1482
+ value: automation.isDraft ? "Draft" : "n8n workflow",
1483
+ },
1484
+ {
1485
+ label: "Nodes",
1486
+ value: String(nodeCount),
1487
+ },
1488
+ {
1489
+ label: "Schedules",
1490
+ value: String(automation.schedules.length),
1491
+ },
1492
+ {
1493
+ label: "Updated",
1494
+ value: formatRelativePast(automation.updatedAt, t),
1495
+ },
1496
+ ] }) }))] })] }));
1398
1497
  }
1399
1498
  function AutomationSidebarItem({ item, selected, onClick, onDoubleClick, }) {
1400
1499
  let Icon = Zap;
@@ -1441,72 +1540,95 @@ function AutomationSidebarItem({ item, selected, onClick, onDoubleClick, }) {
1441
1540
  function AutomationsSidebarChatGuideActions() {
1442
1541
  return (_jsx(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5 px-3 text-sm", onClick: () => prefillPageChat(DESCRIBE_WORKFLOW_PROMPT, { select: true }), children: DESCRIBE_WORKFLOW_PROMPT }));
1443
1542
  }
1444
- function AutomationsSidebarChat() {
1543
+ function AutomationsSidebarChat({ activeItem, }) {
1445
1544
  const { activeConversationId, conversations, t } = useApp();
1446
- const { resolvedSelectedItem, refreshAutomations } = useAutomationsViewContext();
1545
+ const { refreshAutomations } = useAutomationsViewContext();
1546
+ const [overviewVisible, setOverviewVisible] = useState(false);
1547
+ const scopedActiveItem = overviewVisible ? null : activeItem;
1548
+ useEffect(() => {
1549
+ const overviewWindow = window;
1550
+ setOverviewVisible(Boolean(overviewWindow.__miladyAutomationsOverviewVisible));
1551
+ const handleOverviewVisibility = (event) => {
1552
+ const detail = event
1553
+ .detail;
1554
+ setOverviewVisible(Boolean(detail?.visible));
1555
+ };
1556
+ window.addEventListener(AUTOMATIONS_OVERVIEW_VISIBILITY_EVENT, handleOverviewVisibility);
1557
+ return () => window.removeEventListener(AUTOMATIONS_OVERVIEW_VISIBILITY_EVENT, handleOverviewVisibility);
1558
+ }, []);
1447
1559
  const automationConversationAdapter = useMemo(() => {
1448
- if (!resolvedSelectedItem) {
1560
+ if (!scopedActiveItem) {
1449
1561
  return null;
1450
1562
  }
1451
- const bridgeConversationId = getAutomationBridgeIdForItem(resolvedSelectedItem, activeConversationId, conversations);
1452
- if (resolvedSelectedItem.type === "n8n_workflow") {
1453
- const metadata = resolvedSelectedItem.workflowId
1454
- ? buildWorkflowConversationMetadata(resolvedSelectedItem.workflowId, resolvedSelectedItem.title, bridgeConversationId)
1455
- : buildWorkflowDraftConversationMetadata(resolvedSelectedItem.draftId ?? resolvedSelectedItem.id, bridgeConversationId);
1563
+ const bridgeConversationId = getAutomationBridgeIdForItem(scopedActiveItem, activeConversationId, conversations);
1564
+ if (scopedActiveItem.type === "n8n_workflow") {
1565
+ const metadata = scopedActiveItem.workflowId
1566
+ ? buildWorkflowConversationMetadata(scopedActiveItem.workflowId, scopedActiveItem.title, bridgeConversationId)
1567
+ : buildWorkflowDraftConversationMetadata(scopedActiveItem.draftId ?? scopedActiveItem.id, bridgeConversationId);
1456
1568
  return {
1457
1569
  allowClear: false,
1458
1570
  buildRoutingMetadata: () => buildAutomationResponseRoutingMetadata(metadata),
1459
1571
  identityKey: JSON.stringify({
1460
1572
  metadata,
1461
- title: resolvedSelectedItem.title,
1573
+ title: scopedActiveItem.title,
1462
1574
  }),
1463
1575
  onAfterSend: () => void refreshAutomations(),
1464
1576
  resolveConversation: () => resolveAutomationConversation({
1465
- title: resolvedSelectedItem.title,
1577
+ title: scopedActiveItem.title,
1466
1578
  metadata,
1467
1579
  }),
1468
1580
  };
1469
1581
  }
1470
- if (resolvedSelectedItem.type === "automation_draft") {
1471
- const metadata = buildAutomationDraftConversationMetadata(resolvedSelectedItem.draftId ?? resolvedSelectedItem.id, bridgeConversationId);
1582
+ if (scopedActiveItem.type === "automation_draft") {
1583
+ const metadata = buildAutomationDraftConversationMetadata(scopedActiveItem.draftId ?? scopedActiveItem.id, bridgeConversationId);
1472
1584
  return {
1473
1585
  allowClear: false,
1474
1586
  buildRoutingMetadata: () => buildAutomationResponseRoutingMetadata(metadata),
1475
1587
  identityKey: JSON.stringify({
1476
1588
  metadata,
1477
- title: getAutomationDisplayTitle(resolvedSelectedItem),
1589
+ title: getAutomationDisplayTitle(scopedActiveItem),
1478
1590
  }),
1479
1591
  onAfterSend: () => void refreshAutomations(),
1480
1592
  resolveConversation: () => resolveAutomationConversation({
1481
- title: getAutomationDisplayTitle(resolvedSelectedItem),
1593
+ title: getAutomationDisplayTitle(scopedActiveItem),
1482
1594
  metadata,
1483
1595
  }),
1484
1596
  };
1485
1597
  }
1486
1598
  return null;
1487
1599
  }, [
1600
+ scopedActiveItem,
1488
1601
  activeConversationId,
1489
1602
  conversations,
1490
1603
  refreshAutomations,
1491
- resolvedSelectedItem,
1492
1604
  ]);
1493
- if (resolvedSelectedItem?.type === "n8n_workflow") {
1494
- return (_jsx(PageScopedChatPane, { scope: "page-automations", conversationAdapter: automationConversationAdapter ?? undefined, placeholderOverride: resolvedSelectedItem.isDraft
1605
+ if (scopedActiveItem?.type === "n8n_workflow") {
1606
+ return (_jsx(PageScopedChatPane, { scope: "page-automations", conversationAdapter: automationConversationAdapter ?? undefined, introOverride: {
1607
+ title: scopedActiveItem.isDraft ? "Workflow draft" : "Workflow",
1608
+ body: scopedActiveItem.isDraft
1609
+ ? "Describe the trigger and steps. The graph updates here."
1610
+ : "Ask to inspect, change, or troubleshoot this workflow.",
1611
+ actions: scopedActiveItem.isDraft ? (_jsx(AutomationsSidebarChatGuideActions, {})) : undefined,
1612
+ }, placeholderOverride: scopedActiveItem.isDraft
1495
1613
  ? DESCRIBE_WORKFLOW_PROMPT
1496
1614
  : t("automations.chat.placeholder"), systemAddendumOverride: WORKFLOW_SYSTEM_ADDENDUM }));
1497
1615
  }
1498
- if (resolvedSelectedItem?.type === "automation_draft") {
1499
- return (_jsx(PageScopedChatPane, { scope: "page-automations", conversationAdapter: automationConversationAdapter ?? undefined, placeholderOverride: "Describe your automation", systemAddendumOverride: AUTOMATION_DRAFT_SYSTEM_ADDENDUM }));
1616
+ if (scopedActiveItem?.type === "automation_draft") {
1617
+ return (_jsx(PageScopedChatPane, { scope: "page-automations", conversationAdapter: automationConversationAdapter ?? undefined, introOverride: {
1618
+ title: "Draft",
1619
+ body: "Describe the result and what should start it.",
1620
+ }, placeholderOverride: "Describe your automation", systemAddendumOverride: AUTOMATION_DRAFT_SYSTEM_ADDENDUM }));
1500
1621
  }
1501
1622
  return (_jsx(PageScopedChatPane, { scope: "page-automations", placeholderOverride: "Describe your workflow, task, or schedule", introOverride: {
1623
+ title: "Automations",
1624
+ body: "Create or inspect a task or workflow.",
1502
1625
  actions: _jsx(AutomationsSidebarChatGuideActions, {}),
1503
1626
  } }));
1504
1627
  }
1505
1628
  function AutomationsLayout() {
1506
1629
  const { activeConversationId, conversations } = useApp();
1507
- const chatChrome = useAppWorkspaceChatChrome();
1508
1630
  const ctx = useAutomationsViewContext();
1509
- const { closeEditor, editorEnabled, editingId, editingTaskId, editorOpen, editorMode, form, formError, loadTriggerRuns, modalTitle, onDeleteTrigger, onRunSelectedTrigger, onSubmitTrigger, onToggleTriggerEnabled, openCreateTrigger, openCreateTask, saveFormAsTemplate, selectedItemId, setEditingId, setEditorOpen, setField, setFilter, setForm, setFormError, setSelectedItemId, setSelectedItemKind, showDetailPane, showFirstRunEmptyState, resolvedSelectedItem, t, templateNotice, triggers, filteredItems, triggerRunsById, triggersSaving, automationNodes, combinedError, isLoading, n8nStatus, workflowFetchError, } = ctx;
1631
+ const { closeEditor, editorEnabled, editingId, editingTaskId, editorOpen, editorMode, form, formError, loadTriggerRuns, modalTitle, onDeleteTrigger, onRunSelectedTrigger, onSubmitTrigger, onToggleTriggerEnabled, openCreateTrigger, openCreateTask, saveFormAsTemplate, selectedItemId, setEditingId, setEditorOpen, setEditorMode, setField, setFilter, setForm, setFormError, setSelectedItemId, setSelectedItemKind, showDetailPane, showFirstRunEmptyState, resolvedSelectedItem, t, templateNotice, triggers, filteredItems, triggerRunsById, triggersSaving, automationNodes, combinedError, isLoading, n8nStatus, workflowFetchError, } = ctx;
1510
1632
  const [showDashboard, setShowDashboard] = useState(true);
1511
1633
  const [collapsedSections, setCollapsedSections] = useState(() => new Set(["agent-owned"]));
1512
1634
  const toggleSectionCollapsed = useCallback((key) => {
@@ -1527,6 +1649,19 @@ function AutomationsLayout() {
1527
1649
  const [templatesModalOpen, setTemplatesModalOpen] = useState(false);
1528
1650
  const [activeSubpage, setActiveSubpage] = useState(() => getAutomationSubpageFromPath(getNavigationPathFromWindow()));
1529
1651
  const visibleItems = filteredItems;
1652
+ useEffect(() => {
1653
+ if (!showDashboard)
1654
+ return;
1655
+ setSelectedItemId(null);
1656
+ setSelectedItemKind(null);
1657
+ }, [setSelectedItemId, setSelectedItemKind, showDashboard]);
1658
+ useEffect(() => {
1659
+ window.__miladyAutomationsOverviewVisible =
1660
+ showDashboard;
1661
+ window.dispatchEvent(new CustomEvent(AUTOMATIONS_OVERVIEW_VISIBILITY_EVENT, {
1662
+ detail: { visible: showDashboard },
1663
+ }));
1664
+ }, [showDashboard]);
1530
1665
  const syncSubpageFromLocation = useCallback(() => {
1531
1666
  const pathname = getNavigationPathFromWindow();
1532
1667
  const nextSubpage = getAutomationSubpageFromPath(pathname);
@@ -1707,10 +1842,54 @@ function AutomationsLayout() {
1707
1842
  }
1708
1843
  return data;
1709
1844
  }, [ctx, selectedItemId, setSelectedItemId, setSelectedItemKind]);
1845
+ const bindConversationToWorkflow = useCallback(async (conversation, workflow, bridgeConversationId) => {
1846
+ const reboundMetadata = buildWorkflowConversationMetadata(workflow.id, workflow.name, bridgeConversationId ?? undefined);
1847
+ const { conversation: reboundConversation } = await client.updateConversation(conversation.id, {
1848
+ title: workflow.name,
1849
+ metadata: reboundMetadata,
1850
+ });
1851
+ setActiveWorkflowConversation(reboundConversation);
1852
+ return reboundConversation;
1853
+ }, []);
1854
+ const selectWorkflowById = useCallback((workflowId) => {
1855
+ setShowDashboard(false);
1856
+ setSelectedItemId(`workflow:${workflowId}`);
1857
+ setSelectedItemKind("workflow");
1858
+ setEditorOpen(false);
1859
+ setEditingId(null);
1860
+ ctx.setEditingTaskId(null);
1861
+ }, [ctx, setEditingId, setEditorOpen, setSelectedItemId, setSelectedItemKind]);
1862
+ const generateWorkflowFromPrompt = useCallback(async ({ prompt, title, conversation, bridgeConversationId, workflowId, }) => {
1863
+ setWorkflowOpsBusy(true);
1864
+ setPageNotice(null);
1865
+ try {
1866
+ const workflow = await client.generateN8nWorkflow({
1867
+ prompt,
1868
+ ...(title?.trim() ? { name: title.trim() } : {}),
1869
+ ...(workflowId ? { workflowId } : {}),
1870
+ });
1871
+ if (conversation) {
1872
+ await bindConversationToWorkflow(conversation, workflow, bridgeConversationId);
1873
+ }
1874
+ await ctx.refreshAutomations();
1875
+ selectWorkflowById(workflow.id);
1876
+ return workflow;
1877
+ }
1878
+ finally {
1879
+ setWorkflowOpsBusy(false);
1880
+ }
1881
+ }, [bindConversationToWorkflow, ctx, selectWorkflowById]);
1710
1882
  const createWorkflowDraft = useCallback(async (options) => {
1711
1883
  setPageNotice(null);
1712
1884
  showAutomationsList();
1713
1885
  const draftId = createWorkflowDraftId();
1886
+ setShowDashboard(false);
1887
+ setFilter("all");
1888
+ setSelectedItemId(`workflow-draft:${draftId}`);
1889
+ setSelectedItemKind("workflow");
1890
+ setEditorOpen(false);
1891
+ setEditingId(null);
1892
+ ctx.setEditingTaskId(null);
1714
1893
  const bridgeConversationId = getAutomationBridgeIdForItem(resolvedSelectedItem, activeConversationId, conversations);
1715
1894
  const metadata = buildWorkflowDraftConversationMetadata(draftId, bridgeConversationId);
1716
1895
  try {
@@ -1720,25 +1899,18 @@ function AutomationsLayout() {
1720
1899
  });
1721
1900
  setActiveWorkflowConversation(conversation);
1722
1901
  if (options?.initialPrompt?.trim()) {
1723
- await client.sendConversationMessage(conversation.id, `[SYSTEM]${WORKFLOW_SYSTEM_ADDENDUM}[/SYSTEM]\n\n${options.initialPrompt.trim()}`, "DM", undefined, undefined, buildAutomationResponseRoutingMetadata(metadata));
1902
+ await generateWorkflowFromPrompt({
1903
+ prompt: options.initialPrompt.trim(),
1904
+ title: options.title?.trim() || undefined,
1905
+ conversation,
1906
+ bridgeConversationId,
1907
+ });
1908
+ return;
1724
1909
  }
1725
- const data = options?.initialPrompt
1726
- ? await refreshAutomationsWithDraftBinding(conversation)
1727
- : await ctx.refreshAutomations();
1910
+ const data = await ctx.refreshAutomations();
1728
1911
  const resolvedItem = findAutomationForConversation(data, conversation.id);
1729
- setShowDashboard(false);
1730
- setFilter("all");
1731
1912
  setSelectedItemId(resolvedItem?.id ?? `workflow-draft:${draftId}`);
1732
1913
  setSelectedItemKind("workflow");
1733
- setEditorOpen(false);
1734
- setEditingId(null);
1735
- ctx.setEditingTaskId(null);
1736
- if (!options?.initialPrompt?.trim()) {
1737
- window.requestAnimationFrame(() => {
1738
- chatChrome?.openChat();
1739
- prefillPageChat(DESCRIBE_WORKFLOW_PROMPT, { select: true });
1740
- });
1741
- }
1742
1914
  }
1743
1915
  catch (error) {
1744
1916
  setPageNotice(error instanceof Error
@@ -1750,15 +1922,14 @@ function AutomationsLayout() {
1750
1922
  conversations,
1751
1923
  ctx,
1752
1924
  findAutomationForConversation,
1753
- refreshAutomationsWithDraftBinding,
1925
+ generateWorkflowFromPrompt,
1754
1926
  resolvedSelectedItem,
1927
+ setFilter,
1755
1928
  setEditingId,
1756
1929
  setEditorOpen,
1757
- setFilter,
1758
1930
  setSelectedItemId,
1759
1931
  setSelectedItemKind,
1760
1932
  showAutomationsList,
1761
- chatChrome,
1762
1933
  ]);
1763
1934
  const promoteAutomationToWorkflow = useCallback(async (item) => {
1764
1935
  await createWorkflowDraft({
@@ -1780,7 +1951,7 @@ function AutomationsLayout() {
1780
1951
  showAutomationsList();
1781
1952
  openCreateTask();
1782
1953
  }, [openCreateTask, showAutomationsList]);
1783
- const openSeededScheduledTask = useCallback((idea) => {
1954
+ const openSeededTask = useCallback((idea) => {
1784
1955
  showAutomationsList();
1785
1956
  openCreateTrigger();
1786
1957
  setForm({
@@ -1844,6 +2015,51 @@ function AutomationsLayout() {
1844
2015
  setWorkflowBusyId(null);
1845
2016
  }
1846
2017
  }, [ctx, t]);
2018
+ const handleGenerateWorkflow = useCallback(async (item, prompt) => {
2019
+ const conversationId = item.room?.conversationId;
2020
+ const conversation = conversationId
2021
+ ? (conversations.find((candidate) => candidate.id === conversationId) ??
2022
+ null)
2023
+ : null;
2024
+ const title = item.title.trim() && item.title !== WORKFLOW_DRAFT_TITLE
2025
+ ? item.title
2026
+ : undefined;
2027
+ await generateWorkflowFromPrompt({
2028
+ prompt,
2029
+ title,
2030
+ conversation,
2031
+ bridgeConversationId: item.room?.terminalBridgeConversationId,
2032
+ workflowId: item.hasBackingWorkflow ? item.workflowId : null,
2033
+ });
2034
+ }, [conversations, generateWorkflowFromPrompt]);
2035
+ const handleScheduleWorkflow = useCallback((item) => {
2036
+ if (!item.workflowId) {
2037
+ setPageNotice("Generate the workflow before scheduling it.");
2038
+ return;
2039
+ }
2040
+ setForm({
2041
+ ...emptyForm,
2042
+ displayName: `Run ${item.title}`,
2043
+ kind: "workflow",
2044
+ workflowId: item.workflowId,
2045
+ workflowName: item.title,
2046
+ });
2047
+ setEditorMode("trigger");
2048
+ setEditingId(null);
2049
+ ctx.setEditingTaskId(null);
2050
+ setSelectedItemId(null);
2051
+ setSelectedItemKind(null);
2052
+ setEditorOpen(true);
2053
+ setShowDashboard(false);
2054
+ }, [
2055
+ ctx,
2056
+ setEditingId,
2057
+ setEditorMode,
2058
+ setEditorOpen,
2059
+ setForm,
2060
+ setSelectedItemId,
2061
+ setSelectedItemKind,
2062
+ ]);
1847
2063
  const handleDeleteWorkflow = useCallback(async (item) => {
1848
2064
  if (!item.workflowId) {
1849
2065
  return;
@@ -1852,10 +2068,12 @@ function AutomationsLayout() {
1852
2068
  title: t("automations.n8n.deleteWorkflow", {
1853
2069
  defaultValue: "Delete workflow",
1854
2070
  }),
1855
- message: t("automations.n8n.deleteConfirmWorkflow", {
1856
- defaultValue: 'Delete "{{name}}"? This cannot be undone.',
1857
- name: item.title,
1858
- }),
2071
+ message: item.schedules.length > 0
2072
+ ? `Delete "${item.title}" and ${item.schedules.length} attached schedule${item.schedules.length === 1 ? "" : "s"}? This cannot be undone.`
2073
+ : t("automations.n8n.deleteConfirmWorkflow", {
2074
+ defaultValue: 'Delete "{{name}}"? This cannot be undone.',
2075
+ name: item.title,
2076
+ }),
1859
2077
  confirmLabel: t("automations.n8n.deleteWorkflow", {
1860
2078
  defaultValue: "Delete workflow",
1861
2079
  }),
@@ -1868,6 +2086,7 @@ function AutomationsLayout() {
1868
2086
  setPageNotice(null);
1869
2087
  try {
1870
2088
  await client.deleteN8nWorkflow(item.workflowId);
2089
+ await Promise.all(item.schedules.map((schedule) => client.deleteTrigger(schedule.id)));
1871
2090
  await ctx.refreshAutomations();
1872
2091
  }
1873
2092
  catch (error) {
@@ -1888,24 +2107,57 @@ function AutomationsLayout() {
1888
2107
  setPageNotice(null);
1889
2108
  try {
1890
2109
  const workflow = await client.getN8nWorkflow(item.workflowId);
1891
- await createWorkflowDraft({
1892
- title: `${item.title} Copy`,
1893
- initialPrompt: buildWorkflowDuplicationPrompt({
1894
- ...item,
1895
- workflow,
1896
- }),
1897
- });
2110
+ const copy = await client.createN8nWorkflow(buildWorkflowCopyRequest(workflow, `${item.title} Copy`));
2111
+ await ctx.refreshAutomations();
2112
+ selectWorkflowById(copy.id);
1898
2113
  }
1899
2114
  catch (error) {
1900
2115
  setPageNotice(error instanceof Error
1901
2116
  ? error.message
1902
2117
  : "Failed to duplicate workflow.");
1903
2118
  }
1904
- }, [createWorkflowDraft]);
2119
+ }, [ctx, selectWorkflowById]);
2120
+ const handleDeleteDraft = useCallback(async (item) => {
2121
+ const conversationId = item.room?.conversationId;
2122
+ if (!conversationId) {
2123
+ setPageNotice("This draft is missing its automation room.");
2124
+ return;
2125
+ }
2126
+ const confirmed = await confirmDesktopAction({
2127
+ title: "Delete draft",
2128
+ message: `Delete "${getAutomationDisplayTitle(item)}"? This removes the draft room and its conversation.`,
2129
+ confirmLabel: "Delete draft",
2130
+ cancelLabel: t("common.cancel"),
2131
+ type: "warning",
2132
+ });
2133
+ if (!confirmed)
2134
+ return;
2135
+ setPageNotice(null);
2136
+ try {
2137
+ await client.deleteConversation(conversationId);
2138
+ if (selectedItemId === item.id) {
2139
+ setSelectedItemId(null);
2140
+ setSelectedItemKind(null);
2141
+ }
2142
+ if (activeWorkflowConversation?.id === conversationId) {
2143
+ setActiveWorkflowConversation(null);
2144
+ }
2145
+ await ctx.refreshAutomations();
2146
+ setShowDashboard(true);
2147
+ }
2148
+ catch (error) {
2149
+ setPageNotice(error instanceof Error ? error.message : "Failed to delete draft.");
2150
+ }
2151
+ }, [
2152
+ activeWorkflowConversation?.id,
2153
+ ctx,
2154
+ selectedItemId,
2155
+ setSelectedItemId,
2156
+ setSelectedItemKind,
2157
+ t,
2158
+ ]);
1905
2159
  const workflowItems = useMemo(() => visibleItems.filter((item) => item.type === "n8n_workflow"), [visibleItems]);
1906
- const taskItems = useMemo(() => visibleItems.filter((item) => item.type === "automation_draft" ||
1907
- item.trigger != null ||
1908
- (item.task != null && !item.system)), [visibleItems]);
2160
+ const taskItems = useMemo(() => visibleItems.filter((item) => item.type === "automation_draft" || item.trigger != null), [visibleItems]);
1909
2161
  const agentOwnedItems = useMemo(() => visibleItems.filter((item) => item.task != null && item.system), [visibleItems]);
1910
2162
  // Watch active drafts: while any unbound draft exists, poll the
1911
2163
  // automations list and try to rebind it to whatever the agent
@@ -1954,7 +2206,7 @@ function AutomationsLayout() {
1954
2206
  defaultValue: "Expand automations",
1955
2207
  }), bottomAction: _jsxs("button", { type: "button", onClick: () => showNodeCatalog(), "aria-pressed": nodeCatalogActive, title: nodeCatalogLabel, className: `inline-flex h-6 shrink-0 items-center gap-1.5 rounded-[var(--radius-sm)] bg-transparent px-1.5 text-2xs font-semibold uppercase tracking-[0.12em] transition-colors ${nodeCatalogActive ? "text-txt" : "text-muted hover:text-txt"}`, children: [_jsx(Grid3x3, { className: "h-3.5 w-3.5", "aria-hidden": true }), _jsx("span", { children: nodeCatalogLabel })] }), collapsedRailAction: _jsx(SidebarCollapsedActionButton, { "aria-label": "Create task or workflow", onClick: () => setCreateDialogMode("all"), children: _jsx(Plus, { className: "h-4 w-4" }) }), collapsedRailItems: visibleItems.map((item) => (_jsx(SidebarContent.RailItem, { "aria-label": getAutomationDisplayTitle(item), title: getAutomationDisplayTitle(item), active: item.id === selectedItemId, indicatorTone: getAutomationIndicatorTone(item), onClick: () => selectItem(item), children: railMonogram(getAutomationDisplayTitle(item)) }, item.id))), children: _jsx(SidebarScrollRegion, { className: "px-1 pb-2 pt-0", children: _jsxs(SidebarPanel, { className: "bg-transparent gap-0 p-0 shadow-none", children: [isLoading && (_jsxs("div", { className: "flex items-center gap-2 px-2 py-1.5 text-2xs text-muted", children: [_jsx("div", { className: "h-3 w-3 animate-spin rounded-full border-2 border-muted/30 border-t-muted/80" }), t("common.loading")] })), _jsxs("button", { type: "button", onClick: showOverview, "aria-current": showDashboard ? "page" : undefined, className: `mt-0.5 flex w-full items-center gap-2 rounded-[var(--radius-sm)] px-2 py-1 text-left text-xs-tight transition-colors ${showDashboard
1956
2208
  ? "bg-accent/15 text-txt"
1957
- : "text-txt hover:bg-bg-muted/50"}`, children: [_jsx(LayoutDashboard, { className: "h-3.5 w-3.5 shrink-0 text-muted/70", "aria-hidden": true }), _jsx("span", { className: "truncate", children: "Overview" })] }), _jsxs("div", { className: "mt-0.5 space-y-1", children: [_jsx(AutomationCollapsibleSection, { sectionKey: "tasks", label: "Tasks", icon: _jsx(FileText, { className: "h-3.5 w-3.5", "aria-hidden": true }), count: taskItems.length, collapsed: collapsedSections.has("tasks"), onToggleCollapsed: toggleSectionCollapsed, onAdd: () => setCreateDialogMode("tasks"), addLabel: "Create task", emptyLabel: "No tasks", children: taskItems.map(renderItem) }), _jsx(AutomationCollapsibleSection, { sectionKey: "workflows", label: "Workflows", icon: _jsx(Workflow, { className: "h-3.5 w-3.5", "aria-hidden": true }), count: workflowItems.length, collapsed: collapsedSections.has("workflows"), onToggleCollapsed: toggleSectionCollapsed, onAdd: () => void createWorkflowDraft(), addLabel: "Create workflow", emptyLabel: "No workflows", children: workflowItems.map(renderItem) }), _jsx(AutomationCollapsibleSection, { sectionKey: "agent-owned", label: "Agent Owned", icon: _jsx(SquareTerminal, { className: "h-3.5 w-3.5", "aria-hidden": true }), count: agentOwnedItems.length, collapsed: collapsedSections.has("agent-owned"), onToggleCollapsed: toggleSectionCollapsed, emptyLabel: "No agent-owned automations", children: agentOwnedItems.map(renderItem) })] })] }) }) }));
2209
+ : "text-txt hover:bg-bg-muted/50"}`, children: [_jsx(LayoutDashboard, { className: "h-3.5 w-3.5 shrink-0 text-muted/70", "aria-hidden": true }), _jsx("span", { className: "truncate", children: "Overview" })] }), _jsxs("div", { className: "mt-0.5 space-y-1", children: [_jsx(AutomationCollapsibleSection, { sectionKey: "tasks", label: "Tasks", icon: _jsx(FileText, { className: "h-3.5 w-3.5", "aria-hidden": true }), count: taskItems.length, collapsed: collapsedSections.has("tasks"), onToggleCollapsed: toggleSectionCollapsed, onAdd: handleZeroStateNewTask, addLabel: "Create task", emptyLabel: "No tasks", children: taskItems.map(renderItem) }), _jsx(AutomationCollapsibleSection, { sectionKey: "workflows", label: "Workflows", icon: _jsx(Workflow, { className: "h-3.5 w-3.5", "aria-hidden": true }), count: workflowItems.length, collapsed: collapsedSections.has("workflows"), onToggleCollapsed: toggleSectionCollapsed, onAdd: () => void createWorkflowDraft(), addLabel: "Create workflow", emptyLabel: "No workflows", children: workflowItems.map(renderItem) }), _jsx(AutomationCollapsibleSection, { sectionKey: "agent-owned", label: "Agent Owned", icon: _jsx(SquareTerminal, { className: "h-3.5 w-3.5", "aria-hidden": true }), count: agentOwnedItems.length, collapsed: collapsedSections.has("agent-owned"), onToggleCollapsed: toggleSectionCollapsed, emptyLabel: "No agent-owned automations", children: agentOwnedItems.map(renderItem) })] })] }) }) }));
1958
2210
  return (_jsxs(PageLayout, { className: "h-full bg-transparent", "data-testid": "automations-shell", sidebar: automationsSidebar, contentInnerClassName: "w-full", footer: _jsx(WidgetHost, { slot: "automations", className: "py-2" }), mobileSidebarLabel: mobileSidebarLabel, children: [_jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [activeSubpage === "node-catalog" ||
1959
2211
  (!showDashboard && showDetailPane) ? (_jsx("button", { type: "button", className: "mb-3 flex items-center gap-2 rounded-2xl border border-border/30 bg-bg/25 px-4 py-3 text-base font-medium text-muted hover:text-txt md:hidden", onClick: () => {
1960
2212
  if (activeSubpage === "node-catalog") {
@@ -1966,7 +2218,7 @@ function AutomationsLayout() {
1966
2218
  setEditorOpen(false);
1967
2219
  setEditingId(null);
1968
2220
  ctx.setEditingTaskId(null);
1969
- }, children: "\u2190 Back" })) : null, (pageNotice || combinedError) && (_jsx(PagePanel, { variant: "padded", className: "mb-4 border border-danger/20 bg-danger/5", children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("p", { className: "text-sm text-danger", children: pageNotice ?? combinedError }), pageNotice && (_jsx(Button, { variant: "ghost", size: "sm", className: "text-danger hover:bg-danger/10", onClick: () => setPageNotice(null), children: "Dismiss" }))] }) })), editorOpen || editingId || editingTaskId ? (editorMode === "task" || editingTaskId ? (_jsx(TaskForm, {})) : (_jsx(HeartbeatForm, { form: form, editingId: editingId, editorEnabled: editorEnabled, modalTitle: modalTitle, formError: formError, triggersSaving: triggersSaving, templateNotice: templateNotice, triggers: triggers, triggerRunsById: triggerRunsById, t: t, selectedTriggerId: editingId, setField: setField, setForm: setForm, setFormError: setFormError, closeEditor: closeEditor, onSubmit: onSubmitTrigger, onDelete: onDeleteTrigger, onRunSelectedTrigger: onRunSelectedTrigger, onToggleTriggerEnabled: onToggleTriggerEnabled, saveFormAsTemplate: saveFormAsTemplate, loadTriggerRuns: loadTriggerRuns, kickerLabelCreate: "New task", kickerLabelEdit: "Edit task", submitLabelCreate: "Create task", submitLabelEdit: "Save task" }))) : activeSubpage === "node-catalog" ? (_jsx(AutomationNodeCatalogPane, { nodes: automationNodes })) : showDashboard ? (_jsx(AutomationsDashboard, { items: ctx.allItems, onSelectItem: selectItem, onCreateTask: () => setCreateDialogMode("tasks"), onCreateWorkflow: () => void createWorkflowDraft(), onUseIdea: (idea) => {
2221
+ }, children: "\u2190 Back" })) : null, (pageNotice || combinedError) && (_jsx(PagePanel, { variant: "padded", className: "mb-4 border border-danger/20 bg-danger/5", children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("p", { className: "text-sm text-danger", children: pageNotice ?? combinedError }), pageNotice && (_jsx(Button, { variant: "ghost", size: "sm", className: "text-danger hover:bg-danger/10", onClick: () => setPageNotice(null), children: "Dismiss" }))] }) })), editorOpen || editingId || editingTaskId ? (editorMode === "task" || editingTaskId ? (_jsx(TaskForm, {})) : (_jsx(HeartbeatForm, { form: form, editingId: editingId, editorEnabled: editorEnabled, modalTitle: modalTitle, formError: formError, triggersSaving: triggersSaving, templateNotice: templateNotice, triggers: triggers, triggerRunsById: triggerRunsById, t: t, selectedTriggerId: editingId, setField: setField, setForm: setForm, setFormError: setFormError, closeEditor: closeEditor, onSubmit: onSubmitTrigger, onDelete: onDeleteTrigger, onRunSelectedTrigger: onRunSelectedTrigger, onToggleTriggerEnabled: onToggleTriggerEnabled, saveFormAsTemplate: saveFormAsTemplate, loadTriggerRuns: loadTriggerRuns, kickerLabelCreate: form.kind === "workflow" ? "New schedule" : "New task", kickerLabelEdit: form.kind === "workflow" ? "Edit schedule" : "Edit task", submitLabelCreate: form.kind === "workflow" ? "Create schedule" : "Create task", submitLabelEdit: form.kind === "workflow" ? "Save schedule" : "Save task" }))) : activeSubpage === "node-catalog" ? (_jsx(AutomationNodeCatalogPane, { nodes: automationNodes })) : showDashboard ? (_jsx(AutomationsDashboard, { items: ctx.allItems, onSelectItem: selectItem, onCreateTask: handleZeroStateNewTask, onCreateWorkflow: () => void createWorkflowDraft(), onUseIdea: (idea) => {
1970
2222
  if (idea.kind === "workflow") {
1971
2223
  void createWorkflowDraft({
1972
2224
  title: idea.label,
@@ -1974,17 +2226,14 @@ function AutomationsLayout() {
1974
2226
  });
1975
2227
  return;
1976
2228
  }
1977
- openSeededScheduledTask(idea);
1978
- } })) : resolvedSelectedItem?.type === "automation_draft" ? (_jsx(AutomationDraftPane, { automation: resolvedSelectedItem, onSeedPrompt: (prompt) => prefillPageChat(prompt, { select: true }) })) : resolvedSelectedItem?.type === "n8n_workflow" ? (_jsx(WorkflowAutomationDetailPane, { automation: resolvedSelectedItem, n8nStatus: n8nStatus, workflowFetchError: workflowFetchError, workflowBusyId: workflowBusyId, workflowOpsBusy: workflowOpsBusy, onDeleteWorkflow: handleDeleteWorkflow, onDuplicateWorkflow: handleDuplicateWorkflow, onRefreshWorkflows: handleRefreshWorkflows, onStartLocalN8n: handleStartLocalN8n, onToggleWorkflowActive: handleToggleWorkflowActive })) : resolvedSelectedItem?.trigger ? (_jsx(TriggerAutomationDetailPane, { automation: resolvedSelectedItem, onPromoteToWorkflow: promoteAutomationToWorkflow })) : resolvedSelectedItem?.task ? (_jsx(TaskAutomationDetailPane, { automation: resolvedSelectedItem, onPromoteToWorkflow: promoteAutomationToWorkflow })) : showFirstRunEmptyState ? (_jsx(AutomationsZeroState, { onBrowseTemplates: () => setTemplatesModalOpen(true), onNewTrigger: handleZeroStateNewTrigger, onNewTask: handleZeroStateNewTask })) : (_jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center px-8 py-10 text-center", children: _jsx("div", { className: "space-y-3", children: _jsx("h3", { className: "text-lg font-semibold text-txt-strong", children: "Select a task or workflow" }) }) }))] }), _jsx(CreateAutomationDialog, { open: createDialogMode !== null, mode: createDialogMode ?? "all", onOpenChange: (open) => {
2229
+ openSeededTask(idea);
2230
+ } })) : resolvedSelectedItem?.type === "automation_draft" ? (_jsx(AutomationDraftPane, { automation: resolvedSelectedItem, onSeedPrompt: (prompt) => prefillPageChat(prompt, { select: true }), onDeleteDraft: handleDeleteDraft })) : resolvedSelectedItem?.type === "n8n_workflow" ? (_jsx(WorkflowAutomationDetailPane, { automation: resolvedSelectedItem, n8nStatus: n8nStatus, workflowFetchError: workflowFetchError, workflowBusyId: workflowBusyId, workflowOpsBusy: workflowOpsBusy, onDeleteDraft: handleDeleteDraft, onDeleteWorkflow: handleDeleteWorkflow, onDuplicateWorkflow: handleDuplicateWorkflow, onGenerateWorkflow: handleGenerateWorkflow, onRefreshWorkflows: handleRefreshWorkflows, onScheduleWorkflow: handleScheduleWorkflow, onStartLocalN8n: handleStartLocalN8n, onToggleWorkflowActive: handleToggleWorkflowActive })) : resolvedSelectedItem?.trigger ? (_jsx(TriggerAutomationDetailPane, { automation: resolvedSelectedItem, onPromoteToWorkflow: promoteAutomationToWorkflow })) : resolvedSelectedItem?.task ? (_jsx(TaskAutomationDetailPane, { automation: resolvedSelectedItem, onPromoteToWorkflow: promoteAutomationToWorkflow })) : showFirstRunEmptyState ? (_jsx(AutomationsZeroState, { onBrowseTemplates: () => setTemplatesModalOpen(true), onNewTrigger: handleZeroStateNewTrigger, onNewTask: handleZeroStateNewTask })) : (_jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center px-8 py-10 text-center", children: _jsx("div", { className: "space-y-3", children: _jsx("h3", { className: "text-lg font-semibold text-txt-strong", children: "Select a task or workflow" }) }) }))] }), _jsx(CreateAutomationDialog, { open: createDialogMode !== null, onOpenChange: (open) => {
1979
2231
  if (!open) {
1980
2232
  setCreateDialogMode(null);
1981
2233
  }
1982
2234
  }, onCreateTask: () => {
1983
2235
  setCreateDialogMode(null);
1984
2236
  handleZeroStateNewTask();
1985
- }, onCreateScheduledTask: () => {
1986
- setCreateDialogMode(null);
1987
- handleZeroStateNewTrigger();
1988
2237
  }, onCreateWorkflow: () => {
1989
2238
  setCreateDialogMode(null);
1990
2239
  void createWorkflowDraft();
@@ -1999,5 +2248,5 @@ export function AutomationsView() {
1999
2248
  }
2000
2249
  export function AutomationsDesktopShell() {
2001
2250
  const controller = useAutomationsViewController();
2002
- return (_jsx(AutomationsViewContext.Provider, { value: controller, children: _jsx(AppWorkspaceChrome, { testId: "automations-workspace", chat: _jsx(AutomationsSidebarChat, {}), main: _jsx("div", { className: "flex flex-col flex-1 min-h-0 min-w-0 overflow-hidden", children: _jsx(AutomationsLayout, {}) }) }) }));
2251
+ return (_jsx(AutomationsViewContext.Provider, { value: controller, children: _jsx(AppWorkspaceChrome, { testId: "automations-workspace", chat: _jsx(AutomationsSidebarChat, { activeItem: controller.resolvedSelectedItem }), main: _jsx("div", { className: "flex flex-col flex-1 min-h-0 min-w-0 overflow-hidden", children: _jsx(AutomationsLayout, {}) }) }) }));
2003
2252
  }