@arbidocs/blocks 0.4.73 → 0.4.74

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 (58) hide show
  1. package/dist/agents/index.cjs +253 -253
  2. package/dist/agents/index.js +3 -3
  3. package/dist/agents/workflows/WorkflowRunner.d.ts +20 -4
  4. package/dist/agents/workflows/WorkflowStepBar.d.ts +23 -0
  5. package/dist/agents/workflows/run.d.ts +1 -0
  6. package/dist/agents/workflows/types.d.ts +2 -0
  7. package/dist/chat/index.cjs +124 -124
  8. package/dist/chat/index.js +3 -3
  9. package/dist/{chunk-IUR565UW.cjs → chunk-5LP54CWR.cjs} +32 -32
  10. package/dist/chunk-5LP54CWR.cjs.map +1 -0
  11. package/dist/{chunk-3SFFTSOI.cjs → chunk-7CVF3DK2.cjs} +5 -5
  12. package/dist/{chunk-3SFFTSOI.cjs.map → chunk-7CVF3DK2.cjs.map} +1 -1
  13. package/dist/{chunk-V3L5DONY.cjs → chunk-B4SRYJLM.cjs} +31 -2
  14. package/dist/chunk-B4SRYJLM.cjs.map +1 -0
  15. package/dist/{chunk-WOYUXVVQ.cjs → chunk-HITAN7JF.cjs} +4 -4
  16. package/dist/{chunk-WOYUXVVQ.cjs.map → chunk-HITAN7JF.cjs.map} +1 -1
  17. package/dist/{chunk-3VQYJBOI.js → chunk-HOXKR26S.js} +31 -4
  18. package/dist/chunk-HOXKR26S.js.map +1 -0
  19. package/dist/{chunk-KJ3QEKOJ.cjs → chunk-IPGKTUF7.cjs} +7 -33
  20. package/dist/chunk-IPGKTUF7.cjs.map +1 -0
  21. package/dist/{chunk-QDNXEB3T.js → chunk-KOSGX3UK.js} +5 -5
  22. package/dist/chunk-KOSGX3UK.js.map +1 -0
  23. package/dist/{chunk-ORA2XE7U.js → chunk-MK6NJQGF.js} +3 -3
  24. package/dist/{chunk-ORA2XE7U.js.map → chunk-MK6NJQGF.js.map} +1 -1
  25. package/dist/{chunk-GTNUQBXI.cjs → chunk-P2RBE3FD.cjs} +254 -102
  26. package/dist/chunk-P2RBE3FD.cjs.map +1 -0
  27. package/dist/{chunk-G5DP27DU.js → chunk-UIWLDSTF.js} +3 -29
  28. package/dist/chunk-UIWLDSTF.js.map +1 -0
  29. package/dist/{chunk-VRPRAU3J.js → chunk-WJEKRSPH.js} +3 -3
  30. package/dist/{chunk-VRPRAU3J.js.map → chunk-WJEKRSPH.js.map} +1 -1
  31. package/dist/{chunk-2P3T23C7.js → chunk-YWYVIQ7T.js} +256 -104
  32. package/dist/chunk-YWYVIQ7T.js.map +1 -0
  33. package/dist/connect/index.cjs +95 -36
  34. package/dist/connect/index.cjs.map +1 -1
  35. package/dist/connect/index.js +80 -21
  36. package/dist/connect/index.js.map +1 -1
  37. package/dist/connection/ConnectedWorkflowRunner.d.ts +5 -1
  38. package/dist/connection/useWorkflowThread.d.ts +4 -0
  39. package/dist/index.cjs +382 -382
  40. package/dist/index.js +6 -6
  41. package/dist/studio/index.cjs +4 -4
  42. package/dist/studio/index.js +3 -3
  43. package/dist/workflow-canvas/StepInspector.d.ts +11 -0
  44. package/dist/workflow-canvas/WorkflowCanvas.d.ts +6 -2
  45. package/dist/workflow-canvas/index.cjs +200 -90
  46. package/dist/workflow-canvas/index.cjs.map +1 -1
  47. package/dist/workflow-canvas/index.js +168 -58
  48. package/dist/workflow-canvas/index.js.map +1 -1
  49. package/dist/workflow-canvas/viewport.d.ts +47 -0
  50. package/package.json +4 -4
  51. package/dist/chunk-2P3T23C7.js.map +0 -1
  52. package/dist/chunk-3VQYJBOI.js.map +0 -1
  53. package/dist/chunk-G5DP27DU.js.map +0 -1
  54. package/dist/chunk-GTNUQBXI.cjs.map +0 -1
  55. package/dist/chunk-IUR565UW.cjs.map +0 -1
  56. package/dist/chunk-KJ3QEKOJ.cjs.map +0 -1
  57. package/dist/chunk-QDNXEB3T.js.map +0 -1
  58. package/dist/chunk-V3L5DONY.cjs.map +0 -1
@@ -1,11 +1,12 @@
1
1
  "use client";
2
2
  import { UploadDropzone } from './chunk-EVUEIBNU.js';
3
3
  import { FoldSection } from './chunk-XD2M6LIG.js';
4
+ import { useElementSize } from './chunk-HOXKR26S.js';
4
5
  import { testIdOr, prefixedTestId } from './chunk-7F6CBP3Z.js';
5
- import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
6
+ import { useState, useRef, useEffect, useCallback, useMemo, useLayoutEffect } from 'react';
6
7
  import { cn, Card, Button, Label, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Switch, Input, Textarea } from '@arbidocs/react/ui';
7
8
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
- import { CheckCircle2, FileText, ArrowLeft, Loader2, ArrowRight, RotateCw, GitBranch, Check, AlertTriangle, MinusCircle } from 'lucide-react';
9
+ import { CheckCircle2, ArrowLeft, Loader2, ArrowRight, RotateCw, GitBranch, FileText, Check, AlertTriangle, MinusCircle } from 'lucide-react';
9
10
 
10
11
  // src/agents/workflows/types.ts
11
12
  var EMPTY_WORKFLOW_SCOPE = { folders: [], labels: [] };
@@ -597,7 +598,13 @@ function stepStatesFromActivity(events) {
597
598
  if (!status || typeof step !== "string") continue;
598
599
  const responseId = typeof e.detail?.response_id === "string" ? e.detail.response_id : void 0;
599
600
  const error = typeof e.detail?.error === "string" ? e.detail.error : null;
600
- states[step] = { status, responseId: responseId ?? states[step]?.responseId ?? null, error };
601
+ const startedAt = status === "running" ? e.created_at : states[step]?.startedAt;
602
+ states[step] = {
603
+ status,
604
+ responseId: responseId ?? states[step]?.responseId ?? null,
605
+ error,
606
+ ...startedAt ? { startedAt } : {}
607
+ };
601
608
  }
602
609
  return states;
603
610
  }
@@ -803,6 +810,15 @@ function useWorkflowView(options) {
803
810
  },
804
811
  [steps]
805
812
  );
813
+ const onward = useCallback(
814
+ (id) => {
815
+ const { steps: s, states: st } = optionsRef.current;
816
+ const i = s.findIndex((x) => x.id === id);
817
+ const open = s.slice(i + 1).find((x) => !["done", "skipped"].includes(st[x.id]?.status ?? "pending"));
818
+ return open?.id ?? following(id);
819
+ },
820
+ [following]
821
+ );
806
822
  const action = busy ? "wait" : nextAction(steps, states, current);
807
823
  const next = useCallback(async () => {
808
824
  const { steps: s, states: st, inputs: values } = optionsRef.current;
@@ -824,13 +840,13 @@ function useWorkflowView(options) {
824
840
  await optionsRef.current.complete(at);
825
841
  }
826
842
  setShowErrors(false);
827
- setCurrent(following(current) ?? current);
843
+ setCurrent(onward(at.id) ?? current);
828
844
  } catch (e) {
829
845
  setActionError(errorMessage(e));
830
846
  } finally {
831
847
  setBusy(false);
832
848
  }
833
- }, [current, busy, following]);
849
+ }, [current, busy, following, onward]);
834
850
  const goTo = useCallback((stepId) => {
835
851
  setShowErrors(false);
836
852
  setActionError(null);
@@ -856,9 +872,16 @@ function useWorkflowView(options) {
856
872
  }),
857
873
  []
858
874
  );
875
+ const currentRef = useRef(current);
876
+ currentRef.current = current;
859
877
  const skip = useMemo(
860
- () => act((id) => optionsRef.current.skip(id)),
861
- [act]
878
+ () => act(async (id) => {
879
+ await optionsRef.current.skip(id);
880
+ if (currentRef.current !== id) return;
881
+ setShowErrors(false);
882
+ setCurrent(onward(id) ?? id);
883
+ }),
884
+ [act, onward]
862
885
  );
863
886
  const retry = useMemo(
864
887
  () => options.retry ? act((id) => optionsRef.current.retry?.(id) ?? Promise.resolve()) : void 0,
@@ -898,59 +921,38 @@ function useWorkflowView(options) {
898
921
  }
899
922
  };
900
923
  }
901
- var DEFAULT_WORKFLOW_RUNNER_LABELS = {
902
- back: "Back",
903
- next: "Continue",
904
- skip: "Skip",
905
- skipStep: "Skip this step",
906
- finish: "Finish",
907
- approve: "Approve",
908
- working: "Working\u2026",
909
- starting: "Starting\u2026",
910
- retry: "Retry",
911
- failed: "This step did not finish.",
912
- blocked: "Starts after {steps}.",
913
- awaitingReview: "Waiting for approval.",
914
- approved: "Approved.",
915
- skipped: "Skipped.",
916
- branch: "Runs alongside; its result joins the thread",
917
- complete: "Done",
918
- restart: "Start again",
919
- filesHeading: "Files ({count})",
920
- noFiles: "No files yet."
921
- };
922
924
  function PhaseMark({ phase, icon }) {
923
- if (phase === "done") return /* @__PURE__ */ jsx(Check, { className: "size-3.5" });
924
- if (phase === "running") return /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin" });
925
- if (phase === "failed") return /* @__PURE__ */ jsx(AlertTriangle, { className: "size-3.5" });
926
- if (phase === "skipped") return /* @__PURE__ */ jsx(MinusCircle, { className: "size-3.5" });
927
- return icon ? /* @__PURE__ */ jsx("span", { className: "flex size-3.5 items-center justify-center [&_svg]:size-3.5", children: icon }) : null;
925
+ if (phase === "done") return /* @__PURE__ */ jsx(Check, { className: "size-3.5 shrink-0" });
926
+ if (phase === "running") return /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 shrink-0 animate-spin" });
927
+ if (phase === "failed") return /* @__PURE__ */ jsx(AlertTriangle, { className: "size-3.5 shrink-0" });
928
+ if (phase === "skipped") return /* @__PURE__ */ jsx(MinusCircle, { className: "size-3.5 shrink-0" });
929
+ return icon ? /* @__PURE__ */ jsx("span", { className: "flex size-3.5 shrink-0 items-center justify-center [&_svg]:size-3.5", children: icon }) : null;
928
930
  }
929
- function WorkflowRunner({
930
- workflow,
931
+ var PILL = "flex min-w-0 max-w-full shrink-0 items-center gap-2 whitespace-nowrap rounded-full border px-2.5 py-1.5 text-xs font-medium";
932
+ var LABEL = "min-w-0 max-w-[12rem] truncate";
933
+ function WorkflowStepBar({
931
934
  run,
932
- renderAgentOutput,
933
- renderReview,
934
- onUpload,
935
- uploading = false,
936
- files,
937
- renderUpload,
938
- renderStepNote,
939
- renderComplete,
940
- renderScope,
941
- renderDocuments,
942
- renderIcon,
943
- labels,
944
- testIdPrefix = "workflow-run",
945
- className
935
+ levels,
936
+ doneTitle,
937
+ doneIcon,
938
+ icon,
939
+ tid
946
940
  }) {
947
- const l = { ...DEFAULT_WORKFLOW_RUNNER_LABELS, ...labels };
948
- const { steps, step, isComplete, action } = run;
949
- const completion = workflow.run.kind === "steps" ? workflow.run.completion : void 0;
950
- const tid = (suffix) => `${testIdPrefix}-${suffix}`;
951
- const levels = stepLevels(steps);
952
- const crowded = levels.length > 6;
953
- const icon = (key) => key && renderIcon ? renderIcon(key) : void 0;
941
+ const box = useRef(null);
942
+ const measure = useRef(null);
943
+ const row = useRef(null);
944
+ const { width } = useElementSize(box);
945
+ const [compact3, setCompact] = useState(false);
946
+ const titles = levels.map((l) => l.map((s) => s.title).join("\0")).join("") + doneTitle;
947
+ useLayoutEffect(() => {
948
+ const needed = measure.current?.scrollWidth ?? 0;
949
+ setCompact(width > 0 && needed > width);
950
+ }, [width, titles]);
951
+ useLayoutEffect(() => {
952
+ if (!compact3) return;
953
+ const el = row.current?.querySelector('[aria-current="step"]');
954
+ el?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
955
+ }, [compact3, run.current]);
954
956
  const pill = (s) => {
955
957
  const phase = run.phaseOf(s.id);
956
958
  const current = s.id === run.current;
@@ -961,7 +963,8 @@ function WorkflowRunner({
961
963
  onClick: () => run.goTo(s.id),
962
964
  disabled: run.busy,
963
965
  className: cn(
964
- "flex shrink-0 items-center gap-2 whitespace-nowrap rounded-full border px-2.5 py-1.5 text-xs font-medium transition-colors disabled:cursor-default",
966
+ PILL,
967
+ "transition-colors disabled:cursor-default",
965
968
  current ? "border-primary bg-primary/10 text-primary" : phase === "failed" ? "border-destructive/40 text-destructive" : phase === "done" || phase === "skipped" || phase === "running" ? "border-primary/30 bg-primary/5 text-primary" : "border-border text-muted-foreground"
966
969
  ),
967
970
  "aria-current": current ? "step" : void 0,
@@ -971,47 +974,65 @@ function WorkflowRunner({
971
974
  "data-testid": tid(`step-${s.id}`),
972
975
  children: [
973
976
  /* @__PURE__ */ jsx(PhaseMark, { phase, icon: icon(s.icon) }),
974
- /* @__PURE__ */ jsx("span", { className: cn(current ? "inline" : crowded ? "hidden" : "hidden @4xl:inline"), children: s.title })
977
+ (current || !compact3) && /* @__PURE__ */ jsx("span", { className: LABEL, "data-testid": tid(`step-${s.id}-label`), children: s.title })
975
978
  ]
976
979
  },
977
980
  s.id
978
981
  );
979
982
  };
980
- const nextLabel = action === "skip" ? l.skip : action === "finish" ? l.finish : action === "complete" && step?.kind === "review" ? l.approve : action === "wait" && (run.isRunning || run.busy) ? l.working : (step && !run.isLocked(step.id) ? step.nextLabel : void 0) ?? l.next;
981
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-6", className), "data-testid": testIdPrefix, children: [
982
- /* @__PURE__ */ jsx("div", { className: "@container", children: /* @__PURE__ */ jsx(
983
+ const done = run.isComplete;
984
+ return /* @__PURE__ */ jsxs("div", { ref: box, className: "relative min-w-0", children: [
985
+ /* @__PURE__ */ jsx(
986
+ "div",
987
+ {
988
+ ref: measure,
989
+ "aria-hidden": true,
990
+ className: "pointer-events-none invisible absolute left-0 top-0 flex w-max items-center gap-1.5",
991
+ children: [...levels, [{ id: "__done", title: doneTitle }]].map((level, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
992
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col items-start gap-1", children: level.map((s) => /* @__PURE__ */ jsxs("div", { className: PILL, children: [
993
+ /* @__PURE__ */ jsx("span", { className: "size-3.5 shrink-0" }),
994
+ /* @__PURE__ */ jsx(
995
+ "span",
996
+ {
997
+ className: cn(LABEL, "before:content-[attr(data-label)]"),
998
+ "data-label": s.title
999
+ }
1000
+ )
1001
+ ] }, s.id)) }),
1002
+ i < levels.length && /* @__PURE__ */ jsx("div", { className: "w-2" })
1003
+ ] }, i))
1004
+ }
1005
+ ),
1006
+ /* @__PURE__ */ jsx(
983
1007
  "ol",
984
1008
  {
985
- className: cn("flex w-full items-center gap-1.5", crowded && "overflow-x-auto pb-1"),
1009
+ ref: row,
1010
+ className: cn("flex w-full items-center gap-1.5", compact3 && "overflow-x-auto pb-1"),
1011
+ "data-compact": compact3 || void 0,
986
1012
  "data-testid": tid("steps"),
987
1013
  children: [...levels, null].map((level, i) => /* @__PURE__ */ jsxs(
988
1014
  "li",
989
1015
  {
990
1016
  className: cn(
991
- "flex items-center gap-1.5",
992
- i < levels.length && (crowded ? "flex-none" : "min-w-0 flex-1 @4xl:flex-none")
1017
+ "flex min-w-0 items-center gap-1.5",
1018
+ i < levels.length && !compact3 && "flex-1",
1019
+ compact3 && "flex-none"
993
1020
  ),
994
1021
  children: [
995
- level ? /* @__PURE__ */ jsx("div", { className: "flex flex-col items-start gap-1", children: level.map(pill) }) : /* @__PURE__ */ jsxs(
1022
+ level ? /* @__PURE__ */ jsx("div", { className: "flex min-w-0 flex-col items-start gap-1", children: level.map(pill) }) : /* @__PURE__ */ jsxs(
996
1023
  "div",
997
1024
  {
998
1025
  className: cn(
999
- "flex shrink-0 items-center gap-2 whitespace-nowrap rounded-full border px-2.5 py-1.5 text-xs font-medium",
1000
- isComplete ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground"
1026
+ PILL,
1027
+ done ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground"
1001
1028
  ),
1002
- "aria-current": isComplete ? "step" : void 0,
1029
+ "aria-current": done ? "step" : void 0,
1030
+ "aria-label": doneTitle,
1031
+ title: doneTitle,
1003
1032
  "data-testid": tid("step-done"),
1004
1033
  children: [
1005
- /* @__PURE__ */ jsx(PhaseMark, { phase: "ready", icon: icon(completion?.icon) }),
1006
- /* @__PURE__ */ jsx(
1007
- "span",
1008
- {
1009
- className: cn(
1010
- isComplete ? "inline" : crowded ? "hidden" : "hidden @4xl:inline"
1011
- ),
1012
- children: completion?.title ?? l.complete
1013
- }
1014
- )
1034
+ /* @__PURE__ */ jsx(PhaseMark, { phase: "ready", icon: doneIcon }),
1035
+ (done || !compact3) && /* @__PURE__ */ jsx("span", { className: LABEL, children: doneTitle })
1015
1036
  ]
1016
1037
  }
1017
1038
  ),
@@ -1020,7 +1041,7 @@ function WorkflowRunner({
1020
1041
  {
1021
1042
  className: cn(
1022
1043
  "h-px",
1023
- crowded ? "w-3 flex-none" : "min-w-2 flex-1 @4xl:w-4 @4xl:flex-none",
1044
+ compact3 ? "w-3 flex-none" : "min-w-2 flex-1",
1024
1045
  level?.every((s) => ["done", "skipped"].includes(run.phaseOf(s.id))) ? "bg-primary/40" : "bg-border"
1025
1046
  )
1026
1047
  }
@@ -1030,7 +1051,76 @@ function WorkflowRunner({
1030
1051
  level ? level.map((s) => s.id).join("+") : "done"
1031
1052
  ))
1032
1053
  }
1033
- ) }),
1054
+ )
1055
+ ] });
1056
+ }
1057
+ var DEFAULT_WORKFLOW_RUNNER_LABELS = {
1058
+ back: "Back",
1059
+ next: "Continue",
1060
+ skip: "Skip",
1061
+ skipStep: "Skip this step",
1062
+ finish: "Finish",
1063
+ approve: "Approve",
1064
+ working: "Working\u2026",
1065
+ stepWorking: "{step}\u2026 {elapsed}",
1066
+ starting: "Starting\u2026",
1067
+ retry: "Retry",
1068
+ failed: "This step did not finish.",
1069
+ blocked: "Starts after {steps}.",
1070
+ awaitingReview: "Waiting for approval.",
1071
+ approved: "Approved.",
1072
+ skipped: "Skipped.",
1073
+ branch: "Runs alongside; its result joins the thread",
1074
+ complete: "Done",
1075
+ restart: "Start again",
1076
+ filesHeading: "Already in the workspace ({count})",
1077
+ addedHeading: "Added for this step ({count})",
1078
+ noFiles: "No files yet.",
1079
+ fileReading: "Reading\u2026",
1080
+ fileReady: "Ready",
1081
+ fileFailed: "Could not be read",
1082
+ readingFiles: "Reading {current} of {count} files\u2026"
1083
+ };
1084
+ function WorkflowRunner({
1085
+ workflow,
1086
+ run,
1087
+ renderAgentOutput,
1088
+ renderReview,
1089
+ onUpload,
1090
+ uploading = false,
1091
+ files,
1092
+ renderUpload,
1093
+ renderStepNote,
1094
+ renderComplete,
1095
+ renderScope,
1096
+ renderDocuments,
1097
+ renderIcon,
1098
+ labels,
1099
+ testIdPrefix = "workflow-run",
1100
+ className
1101
+ }) {
1102
+ const l = { ...DEFAULT_WORKFLOW_RUNNER_LABELS, ...labels };
1103
+ const { steps, step, isComplete, action } = run;
1104
+ const completion = workflow.run.kind === "steps" ? workflow.run.completion : void 0;
1105
+ const tid = (suffix) => `${testIdPrefix}-${suffix}`;
1106
+ const levels = stepLevels(steps);
1107
+ const icon = (key) => key && renderIcon ? renderIcon(key) : void 0;
1108
+ const added = step?.kind === "upload" ? (files ?? []).filter((f) => f.addedFor === step.id) : [];
1109
+ const unread = added.filter((f) => f.status === "processing").length;
1110
+ const reading = action === "complete" && unread > 0;
1111
+ const nextLabel = reading ? l.readingFiles.replace("{current}", String(Math.min(added.length - unread + 1, added.length))).replace("{count}", String(added.length)) : action === "skip" ? l.skip : action === "finish" ? l.finish : action === "complete" && step?.kind === "review" ? l.approve : action === "wait" && (run.isRunning || run.busy) ? l.working : (step && !run.isLocked(step.id) ? step.nextLabel : void 0) ?? l.next;
1112
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-6", className), "data-testid": testIdPrefix, children: [
1113
+ /* @__PURE__ */ jsx(
1114
+ WorkflowStepBar,
1115
+ {
1116
+ run,
1117
+ levels,
1118
+ doneTitle: completion?.title ?? l.complete,
1119
+ doneIcon: icon(completion?.icon),
1120
+ icon,
1121
+ tid
1122
+ }
1123
+ ),
1034
1124
  isComplete ? renderComplete?.(run) ?? /* @__PURE__ */ jsxs(Card, { className: "mx-auto max-w-lg p-8 text-center", "data-testid": tid("done"), children: [
1035
1125
  /* @__PURE__ */ jsx(CheckCircle2, { className: "mx-auto size-12 text-primary" }),
1036
1126
  /* @__PURE__ */ jsx("h2", { className: "mt-4 text-xl font-semibold", children: completion?.title ?? l.complete }),
@@ -1049,7 +1139,7 @@ function WorkflowRunner({
1049
1139
  "{steps}",
1050
1140
  stepDependencies(steps, step.id).map((d) => steps.find((s) => s.id === d)?.title ?? d).join(", ")
1051
1141
  ) }) }) : step.kind === "form" ? /* @__PURE__ */ jsxs(Card, { className: "max-w-2xl space-y-4 p-6", "data-testid": tid(step.id), children: [
1052
- step.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: step.description }),
1142
+ /* @__PURE__ */ jsx(StepHeading, { step, icon: icon(step.icon), testId: tid(`${step.id}-title`) }),
1053
1143
  /* @__PURE__ */ jsx("fieldset", { disabled: run.isLocked(step.id), className: "contents", children: /* @__PURE__ */ jsx(
1054
1144
  SchemaForm,
1055
1145
  {
@@ -1071,7 +1161,7 @@ function WorkflowRunner({
1071
1161
  /* @__PURE__ */ jsx(
1072
1162
  UploadDropzone,
1073
1163
  {
1074
- onFiles: (picked) => void onUpload?.(picked),
1164
+ onFiles: (picked) => void onUpload?.(picked, step),
1075
1165
  uploading,
1076
1166
  disabled: !onUpload,
1077
1167
  accept: step.accept,
@@ -1081,21 +1171,15 @@ function WorkflowRunner({
1081
1171
  testId: tid(`${step.id}-drop`)
1082
1172
  }
1083
1173
  ),
1084
- files && /* @__PURE__ */ jsxs("div", { className: "space-y-1", "data-testid": tid(`${step.id}-files`), children: [
1085
- /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: l.filesHeading.replace("{count}", String(files.length)) }),
1086
- files.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: l.noFiles }),
1087
- files.slice(0, 6).map((f) => /* @__PURE__ */ jsxs(
1088
- "div",
1089
- {
1090
- className: "flex items-center gap-2 text-sm text-muted-foreground",
1091
- children: [
1092
- /* @__PURE__ */ jsx(FileText, { className: "size-3.5 text-primary" }),
1093
- /* @__PURE__ */ jsx("span", { className: "truncate", children: f.name })
1094
- ]
1095
- },
1096
- f.id
1097
- ))
1098
- ] })
1174
+ files && /* @__PURE__ */ jsx(
1175
+ UploadFiles,
1176
+ {
1177
+ added,
1178
+ others: files.filter((f) => f.addedFor !== step.id),
1179
+ l,
1180
+ testId: tid(`${step.id}-files`)
1181
+ }
1182
+ )
1099
1183
  ] }),
1100
1184
  run.actionError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: run.actionError }),
1101
1185
  renderStepNote?.(step, run)
@@ -1149,11 +1233,11 @@ function WorkflowRunner({
1149
1233
  Button,
1150
1234
  {
1151
1235
  onClick: () => void run.next(),
1152
- disabled: action === "wait",
1236
+ disabled: action === "wait" || reading,
1153
1237
  "data-testid": tid("next"),
1154
1238
  children: [
1155
1239
  nextLabel,
1156
- action === "wait" && (run.isRunning || run.busy) ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(ArrowRight, { className: "size-4" })
1240
+ reading || action === "wait" && (run.isRunning || run.busy) ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(ArrowRight, { className: "size-4" })
1157
1241
  ]
1158
1242
  }
1159
1243
  )
@@ -1161,15 +1245,83 @@ function WorkflowRunner({
1161
1245
  ] })
1162
1246
  ] });
1163
1247
  }
1164
- function StepHeading({ step, icon }) {
1248
+ function UploadFiles({
1249
+ added,
1250
+ others,
1251
+ l,
1252
+ testId
1253
+ }) {
1254
+ const heading = "text-xs font-semibold uppercase tracking-wide text-muted-foreground";
1255
+ const row = (f) => /* @__PURE__ */ jsxs(
1256
+ "div",
1257
+ {
1258
+ className: "flex items-center gap-2 text-sm text-muted-foreground",
1259
+ "data-status": f.status,
1260
+ "data-testid": `${testId}-file-${f.id}`,
1261
+ children: [
1262
+ /* @__PURE__ */ jsx(FileText, { className: "size-3.5 shrink-0 text-primary" }),
1263
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", title: f.name, children: f.name }),
1264
+ f.status === "processing" && /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-1 text-xs", children: [
1265
+ /* @__PURE__ */ jsx(Loader2, { className: "size-3 animate-spin" }),
1266
+ " ",
1267
+ l.fileReading
1268
+ ] }),
1269
+ f.status === "ready" && /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-1 text-xs text-primary", children: [
1270
+ /* @__PURE__ */ jsx(Check, { className: "size-3" }),
1271
+ " ",
1272
+ l.fileReady
1273
+ ] }),
1274
+ f.status === "failed" && /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-1 text-xs text-destructive", children: [
1275
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "size-3" }),
1276
+ " ",
1277
+ l.fileFailed
1278
+ ] })
1279
+ ]
1280
+ },
1281
+ f.id
1282
+ );
1283
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", "data-testid": testId, children: [
1284
+ added.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-1", "data-testid": `${testId}-added`, children: [
1285
+ /* @__PURE__ */ jsx("p", { className: heading, children: l.addedHeading.replace("{count}", String(added.length)) }),
1286
+ added.map(row)
1287
+ ] }),
1288
+ added.length === 0 && others.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: l.noFiles }),
1289
+ others.length > 0 && /* @__PURE__ */ jsxs("details", { className: "space-y-1", "data-testid": `${testId}-others`, children: [
1290
+ /* @__PURE__ */ jsx("summary", { className: cn(heading, "cursor-pointer select-none"), children: l.filesHeading.replace("{count}", String(others.length)) }),
1291
+ /* @__PURE__ */ jsx("div", { className: "mt-1 max-h-48 space-y-1 overflow-y-auto", children: others.map(row) })
1292
+ ] })
1293
+ ] });
1294
+ }
1295
+ function StepHeading({
1296
+ step,
1297
+ icon,
1298
+ testId
1299
+ }) {
1165
1300
  return /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
1166
- /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-medium", children: [
1301
+ /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-medium", "data-testid": testId, children: [
1167
1302
  icon && /* @__PURE__ */ jsx("span", { className: "flex size-4 items-center justify-center text-primary [&_svg]:size-4", children: icon }),
1168
1303
  step.title
1169
1304
  ] }),
1170
1305
  step.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: step.description })
1171
1306
  ] });
1172
1307
  }
1308
+ function formatElapsed(ms) {
1309
+ const total = Math.max(0, Math.floor(ms / 1e3));
1310
+ const h = Math.floor(total / 3600);
1311
+ const m = Math.floor(total % 3600 / 60);
1312
+ const sec = String(total % 60).padStart(2, "0");
1313
+ return h ? `${h}:${String(m).padStart(2, "0")}:${sec}` : `${m}:${sec}`;
1314
+ }
1315
+ function StepWorking({ title, since, label }) {
1316
+ const seen = useRef(Date.now());
1317
+ const start = since && Date.parse(since) || seen.current;
1318
+ const [now, setNow] = useState(() => Date.now());
1319
+ useEffect(() => {
1320
+ const t = setInterval(() => setNow(Date.now()), 1e3);
1321
+ return () => clearInterval(t);
1322
+ }, []);
1323
+ return /* @__PURE__ */ jsx("span", { children: label.replace("{step}", title).replace("{elapsed}", formatElapsed(now - start)) });
1324
+ }
1173
1325
  function AgentStepBody({
1174
1326
  step,
1175
1327
  run,
@@ -1213,7 +1365,7 @@ function AgentStepBody({
1213
1365
  children: [
1214
1366
  /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
1215
1367
  " ",
1216
- phase === "ready" ? l.starting : l.working
1368
+ phase === "ready" ? l.starting : /* @__PURE__ */ jsx(StepWorking, { title: step.title, since: state?.startedAt, label: l.stepWorking })
1217
1369
  ]
1218
1370
  }
1219
1371
  ),
@@ -1246,5 +1398,5 @@ function AgentStepBody({
1246
1398
  }
1247
1399
 
1248
1400
  export { DEFAULT_SCHEMA_FORM_LABELS, DEFAULT_WORKFLOW_RUNNER_LABELS, EMPTY_WORKFLOW_SCOPE, SKILL_PROMPT_FALLBACK_PLACEHOLDER, SKILL_PROMPT_LABEL, SchemaForm, WorkflowRunner, buildLaunch, composeStepPrompt, composeWorkflowPrompt, defaultInputs, dependsOn, fieldToFrontmatter, fillRunText, fillStepInputs, fromPlanSteps, initialRunInputs, inputsFromContext, isEmptyWorkflowScope, isPersonStep, isRunFinished, isSteppedWorkflow, isWorkflowScope, layoutFromPlanSteps, nextAction, orderSteps, promptFieldName, reviewInstructions, runFields, runThreadTitle, skillToWorkflow, stepDependencies, stepLevels, stepPhase, stepStatesFromActivity, toPlanSteps, useWorkflowView, validateInputs, validateStep, validateWorkflowGraph, workflowAccepts, workflowFromFrontmatter, workflowSteps, workflowsAccepting, workflowsForHost };
1249
- //# sourceMappingURL=chunk-2P3T23C7.js.map
1250
- //# sourceMappingURL=chunk-2P3T23C7.js.map
1401
+ //# sourceMappingURL=chunk-YWYVIQ7T.js.map
1402
+ //# sourceMappingURL=chunk-YWYVIQ7T.js.map