@ryanfw/prompt-orchestration-pipeline 0.5.0 → 0.7.0

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 (67) hide show
  1. package/README.md +1 -2
  2. package/package.json +1 -2
  3. package/src/api/validators/json.js +39 -0
  4. package/src/components/DAGGrid.jsx +392 -303
  5. package/src/components/JobCard.jsx +14 -12
  6. package/src/components/JobDetail.jsx +54 -51
  7. package/src/components/JobTable.jsx +72 -23
  8. package/src/components/Layout.jsx +145 -42
  9. package/src/components/LiveText.jsx +47 -0
  10. package/src/components/PageSubheader.jsx +75 -0
  11. package/src/components/TaskDetailSidebar.jsx +216 -0
  12. package/src/components/TimerText.jsx +82 -0
  13. package/src/components/UploadSeed.jsx +0 -70
  14. package/src/components/ui/Logo.jsx +16 -0
  15. package/src/components/ui/RestartJobModal.jsx +140 -0
  16. package/src/components/ui/toast.jsx +138 -0
  17. package/src/config/models.js +322 -0
  18. package/src/config/statuses.js +119 -0
  19. package/src/core/config.js +4 -34
  20. package/src/core/file-io.js +13 -28
  21. package/src/core/module-loader.js +54 -40
  22. package/src/core/pipeline-runner.js +65 -26
  23. package/src/core/status-writer.js +213 -58
  24. package/src/core/symlink-bridge.js +57 -0
  25. package/src/core/symlink-utils.js +94 -0
  26. package/src/core/task-runner.js +321 -437
  27. package/src/llm/index.js +258 -86
  28. package/src/pages/Code.jsx +351 -0
  29. package/src/pages/PipelineDetail.jsx +124 -15
  30. package/src/pages/PromptPipelineDashboard.jsx +20 -88
  31. package/src/providers/anthropic.js +83 -69
  32. package/src/providers/base.js +52 -0
  33. package/src/providers/deepseek.js +20 -21
  34. package/src/providers/gemini.js +226 -0
  35. package/src/providers/openai.js +36 -106
  36. package/src/providers/zhipu.js +136 -0
  37. package/src/ui/client/adapters/job-adapter.js +42 -28
  38. package/src/ui/client/api.js +134 -0
  39. package/src/ui/client/hooks/useJobDetailWithUpdates.js +65 -179
  40. package/src/ui/client/index.css +15 -0
  41. package/src/ui/client/index.html +2 -1
  42. package/src/ui/client/main.jsx +19 -14
  43. package/src/ui/client/time-store.js +161 -0
  44. package/src/ui/config-bridge.js +15 -24
  45. package/src/ui/config-bridge.node.js +15 -24
  46. package/src/ui/dist/assets/{index-CxcrauYR.js → index-DqkbzXZ1.js} +2132 -1086
  47. package/src/ui/dist/assets/style-DBF9NQGk.css +62 -0
  48. package/src/ui/dist/index.html +4 -3
  49. package/src/ui/job-reader.js +0 -108
  50. package/src/ui/public/favicon.svg +12 -0
  51. package/src/ui/server.js +252 -0
  52. package/src/ui/sse-enhancer.js +0 -1
  53. package/src/ui/transformers/list-transformer.js +32 -12
  54. package/src/ui/transformers/status-transformer.js +29 -42
  55. package/src/utils/dag.js +8 -4
  56. package/src/utils/duration.js +13 -19
  57. package/src/utils/formatters.js +27 -0
  58. package/src/utils/geometry-equality.js +83 -0
  59. package/src/utils/pipelines.js +5 -1
  60. package/src/utils/time-utils.js +40 -0
  61. package/src/utils/token-cost-calculator.js +294 -0
  62. package/src/utils/ui.jsx +18 -20
  63. package/src/components/ui/select.jsx +0 -27
  64. package/src/lib/utils.js +0 -6
  65. package/src/ui/client/hooks/useTicker.js +0 -26
  66. package/src/ui/config-bridge.browser.js +0 -149
  67. package/src/ui/dist/assets/style-D6K_oQ12.css +0 -62
@@ -520,11 +520,11 @@ function requireReact_production() {
520
520
  react_production.useState = function(initialState) {
521
521
  return ReactSharedInternals.H.useState(initialState);
522
522
  };
523
- react_production.useSyncExternalStore = function(subscribe, getSnapshot, getServerSnapshot) {
523
+ react_production.useSyncExternalStore = function(subscribe2, getSnapshot2, getServerSnapshot2) {
524
524
  return ReactSharedInternals.H.useSyncExternalStore(
525
- subscribe,
526
- getSnapshot,
527
- getServerSnapshot
525
+ subscribe2,
526
+ getSnapshot2,
527
+ getServerSnapshot2
528
528
  );
529
529
  };
530
530
  react_production.useTransition = function() {
@@ -3180,15 +3180,15 @@ function requireReactDomClient_production() {
3180
3180
  return value;
3181
3181
  }
3182
3182
  var AbortControllerLocal = "undefined" !== typeof AbortController ? AbortController : function() {
3183
- var listeners = [], signal = this.signal = {
3183
+ var listeners2 = [], signal = this.signal = {
3184
3184
  aborted: false,
3185
3185
  addEventListener: function(type, listener) {
3186
- listeners.push(listener);
3186
+ listeners2.push(listener);
3187
3187
  }
3188
3188
  };
3189
3189
  this.abort = function() {
3190
3190
  signal.aborted = true;
3191
- listeners.forEach(function(listener) {
3191
+ listeners2.forEach(function(listener) {
3192
3192
  return listener();
3193
3193
  });
3194
3194
  };
@@ -3234,33 +3234,33 @@ function requireReactDomClient_production() {
3234
3234
  function pingEngtangledActionScope() {
3235
3235
  if (0 === --currentEntangledPendingCount && null !== currentEntangledListeners) {
3236
3236
  null !== currentEntangledActionThenable && (currentEntangledActionThenable.status = "fulfilled");
3237
- var listeners = currentEntangledListeners;
3237
+ var listeners2 = currentEntangledListeners;
3238
3238
  currentEntangledListeners = null;
3239
3239
  currentEntangledLane = 0;
3240
3240
  currentEntangledActionThenable = null;
3241
- for (var i2 = 0; i2 < listeners.length; i2++) (0, listeners[i2])();
3241
+ for (var i2 = 0; i2 < listeners2.length; i2++) (0, listeners2[i2])();
3242
3242
  }
3243
3243
  }
3244
3244
  function chainThenableValue(thenable, result) {
3245
- var listeners = [], thenableWithOverride = {
3245
+ var listeners2 = [], thenableWithOverride = {
3246
3246
  status: "pending",
3247
3247
  value: null,
3248
3248
  reason: null,
3249
3249
  then: function(resolve) {
3250
- listeners.push(resolve);
3250
+ listeners2.push(resolve);
3251
3251
  }
3252
3252
  };
3253
3253
  thenable.then(
3254
3254
  function() {
3255
3255
  thenableWithOverride.status = "fulfilled";
3256
3256
  thenableWithOverride.value = result;
3257
- for (var i2 = 0; i2 < listeners.length; i2++) (0, listeners[i2])(result);
3257
+ for (var i2 = 0; i2 < listeners2.length; i2++) (0, listeners2[i2])(result);
3258
3258
  },
3259
3259
  function(error) {
3260
3260
  thenableWithOverride.status = "rejected";
3261
3261
  thenableWithOverride.reason = error;
3262
- for (error = 0; error < listeners.length; error++)
3263
- (0, listeners[error])(void 0);
3262
+ for (error = 0; error < listeners2.length; error++)
3263
+ (0, listeners2[error])(void 0);
3264
3264
  }
3265
3265
  );
3266
3266
  return thenableWithOverride;
@@ -4327,22 +4327,22 @@ function requireReactDomClient_production() {
4327
4327
  }
4328
4328
  return [newState, dispatch];
4329
4329
  }
4330
- function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
4330
+ function updateSyncExternalStore(subscribe2, getSnapshot2, getServerSnapshot2) {
4331
4331
  var fiber = currentlyRenderingFiber, hook = updateWorkInProgressHook(), isHydrating$jscomp$0 = isHydrating;
4332
4332
  if (isHydrating$jscomp$0) {
4333
- if (void 0 === getServerSnapshot) throw Error(formatProdErrorMessage(407));
4334
- getServerSnapshot = getServerSnapshot();
4335
- } else getServerSnapshot = getSnapshot();
4333
+ if (void 0 === getServerSnapshot2) throw Error(formatProdErrorMessage(407));
4334
+ getServerSnapshot2 = getServerSnapshot2();
4335
+ } else getServerSnapshot2 = getSnapshot2();
4336
4336
  var snapshotChanged = !objectIs(
4337
4337
  (currentHook || hook).memoizedState,
4338
- getServerSnapshot
4338
+ getServerSnapshot2
4339
4339
  );
4340
- snapshotChanged && (hook.memoizedState = getServerSnapshot, didReceiveUpdate = true);
4340
+ snapshotChanged && (hook.memoizedState = getServerSnapshot2, didReceiveUpdate = true);
4341
4341
  hook = hook.queue;
4342
- updateEffect(subscribeToStore.bind(null, fiber, hook, subscribe), [
4343
- subscribe
4342
+ updateEffect(subscribeToStore.bind(null, fiber, hook, subscribe2), [
4343
+ subscribe2
4344
4344
  ]);
4345
- if (hook.getSnapshot !== getSnapshot || snapshotChanged || null !== workInProgressHook && workInProgressHook.memoizedState.tag & 1) {
4345
+ if (hook.getSnapshot !== getSnapshot2 || snapshotChanged || null !== workInProgressHook && workInProgressHook.memoizedState.tag & 1) {
4346
4346
  fiber.flags |= 2048;
4347
4347
  pushSimpleEffect(
4348
4348
  9,
@@ -4351,29 +4351,29 @@ function requireReactDomClient_production() {
4351
4351
  null,
4352
4352
  fiber,
4353
4353
  hook,
4354
- getServerSnapshot,
4355
- getSnapshot
4354
+ getServerSnapshot2,
4355
+ getSnapshot2
4356
4356
  ),
4357
4357
  null
4358
4358
  );
4359
4359
  if (null === workInProgressRoot) throw Error(formatProdErrorMessage(349));
4360
- isHydrating$jscomp$0 || 0 !== (renderLanes & 127) || pushStoreConsistencyCheck(fiber, getSnapshot, getServerSnapshot);
4360
+ isHydrating$jscomp$0 || 0 !== (renderLanes & 127) || pushStoreConsistencyCheck(fiber, getSnapshot2, getServerSnapshot2);
4361
4361
  }
4362
- return getServerSnapshot;
4362
+ return getServerSnapshot2;
4363
4363
  }
4364
- function pushStoreConsistencyCheck(fiber, getSnapshot, renderedSnapshot) {
4364
+ function pushStoreConsistencyCheck(fiber, getSnapshot2, renderedSnapshot) {
4365
4365
  fiber.flags |= 16384;
4366
- fiber = { getSnapshot, value: renderedSnapshot };
4367
- getSnapshot = currentlyRenderingFiber.updateQueue;
4368
- null === getSnapshot ? (getSnapshot = createFunctionComponentUpdateQueue(), currentlyRenderingFiber.updateQueue = getSnapshot, getSnapshot.stores = [fiber]) : (renderedSnapshot = getSnapshot.stores, null === renderedSnapshot ? getSnapshot.stores = [fiber] : renderedSnapshot.push(fiber));
4366
+ fiber = { getSnapshot: getSnapshot2, value: renderedSnapshot };
4367
+ getSnapshot2 = currentlyRenderingFiber.updateQueue;
4368
+ null === getSnapshot2 ? (getSnapshot2 = createFunctionComponentUpdateQueue(), currentlyRenderingFiber.updateQueue = getSnapshot2, getSnapshot2.stores = [fiber]) : (renderedSnapshot = getSnapshot2.stores, null === renderedSnapshot ? getSnapshot2.stores = [fiber] : renderedSnapshot.push(fiber));
4369
4369
  }
4370
- function updateStoreInstance(fiber, inst, nextSnapshot, getSnapshot) {
4370
+ function updateStoreInstance(fiber, inst, nextSnapshot, getSnapshot2) {
4371
4371
  inst.value = nextSnapshot;
4372
- inst.getSnapshot = getSnapshot;
4372
+ inst.getSnapshot = getSnapshot2;
4373
4373
  checkIfSnapshotChanged(inst) && forceStoreRerender(fiber);
4374
4374
  }
4375
- function subscribeToStore(fiber, inst, subscribe) {
4376
- return subscribe(function() {
4375
+ function subscribeToStore(fiber, inst, subscribe2) {
4376
+ return subscribe2(function() {
4377
4377
  checkIfSnapshotChanged(inst) && forceStoreRerender(fiber);
4378
4378
  });
4379
4379
  }
@@ -5092,23 +5092,23 @@ function requireReactDomClient_production() {
5092
5092
  mountWorkInProgressHook().memoizedState = stateHook;
5093
5093
  return [false, stateHook];
5094
5094
  },
5095
- useSyncExternalStore: function(subscribe, getSnapshot, getServerSnapshot) {
5095
+ useSyncExternalStore: function(subscribe2, getSnapshot2, getServerSnapshot2) {
5096
5096
  var fiber = currentlyRenderingFiber, hook = mountWorkInProgressHook();
5097
5097
  if (isHydrating) {
5098
- if (void 0 === getServerSnapshot)
5098
+ if (void 0 === getServerSnapshot2)
5099
5099
  throw Error(formatProdErrorMessage(407));
5100
- getServerSnapshot = getServerSnapshot();
5100
+ getServerSnapshot2 = getServerSnapshot2();
5101
5101
  } else {
5102
- getServerSnapshot = getSnapshot();
5102
+ getServerSnapshot2 = getSnapshot2();
5103
5103
  if (null === workInProgressRoot)
5104
5104
  throw Error(formatProdErrorMessage(349));
5105
- 0 !== (workInProgressRootRenderLanes & 127) || pushStoreConsistencyCheck(fiber, getSnapshot, getServerSnapshot);
5105
+ 0 !== (workInProgressRootRenderLanes & 127) || pushStoreConsistencyCheck(fiber, getSnapshot2, getServerSnapshot2);
5106
5106
  }
5107
- hook.memoizedState = getServerSnapshot;
5108
- var inst = { value: getServerSnapshot, getSnapshot };
5107
+ hook.memoizedState = getServerSnapshot2;
5108
+ var inst = { value: getServerSnapshot2, getSnapshot: getSnapshot2 };
5109
5109
  hook.queue = inst;
5110
- mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [
5111
- subscribe
5110
+ mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe2), [
5111
+ subscribe2
5112
5112
  ]);
5113
5113
  fiber.flags |= 2048;
5114
5114
  pushSimpleEffect(
@@ -5118,12 +5118,12 @@ function requireReactDomClient_production() {
5118
5118
  null,
5119
5119
  fiber,
5120
5120
  inst,
5121
- getServerSnapshot,
5122
- getSnapshot
5121
+ getServerSnapshot2,
5122
+ getSnapshot2
5123
5123
  ),
5124
5124
  null
5125
5125
  );
5126
- return getServerSnapshot;
5126
+ return getServerSnapshot2;
5127
5127
  },
5128
5128
  useId: function() {
5129
5129
  var hook = mountWorkInProgressHook(), identifierPrefix = workInProgressRoot.identifierPrefix;
@@ -8813,10 +8813,10 @@ function requireReactDomClient_production() {
8813
8813
  var tag = node.tag;
8814
8814
  if ((0 === tag || 11 === tag || 15 === tag) && node.flags & 16384 && (tag = node.updateQueue, null !== tag && (tag = tag.stores, null !== tag)))
8815
8815
  for (var i2 = 0; i2 < tag.length; i2++) {
8816
- var check = tag[i2], getSnapshot = check.getSnapshot;
8816
+ var check = tag[i2], getSnapshot2 = check.getSnapshot;
8817
8817
  check = check.value;
8818
8818
  try {
8819
- if (!objectIs(getSnapshot(), check)) return false;
8819
+ if (!objectIs(getSnapshot2(), check)) return false;
8820
8820
  } catch (error) {
8821
8821
  return false;
8822
8822
  }
@@ -10201,15 +10201,15 @@ function requireReactDomClient_production() {
10201
10201
  };
10202
10202
  }
10203
10203
  function accumulateTwoPhaseListeners(targetFiber, reactName) {
10204
- for (var captureName = reactName + "Capture", listeners = []; null !== targetFiber; ) {
10204
+ for (var captureName = reactName + "Capture", listeners2 = []; null !== targetFiber; ) {
10205
10205
  var _instance2 = targetFiber, stateNode = _instance2.stateNode;
10206
10206
  _instance2 = _instance2.tag;
10207
- 5 !== _instance2 && 26 !== _instance2 && 27 !== _instance2 || null === stateNode || (_instance2 = getListener(targetFiber, captureName), null != _instance2 && listeners.unshift(
10207
+ 5 !== _instance2 && 26 !== _instance2 && 27 !== _instance2 || null === stateNode || (_instance2 = getListener(targetFiber, captureName), null != _instance2 && listeners2.unshift(
10208
10208
  createDispatchListener(targetFiber, _instance2, stateNode)
10209
- ), _instance2 = getListener(targetFiber, reactName), null != _instance2 && listeners.push(
10209
+ ), _instance2 = getListener(targetFiber, reactName), null != _instance2 && listeners2.push(
10210
10210
  createDispatchListener(targetFiber, _instance2, stateNode)
10211
10211
  ));
10212
- if (3 === targetFiber.tag) return listeners;
10212
+ if (3 === targetFiber.tag) return listeners2;
10213
10213
  targetFiber = targetFiber.return;
10214
10214
  }
10215
10215
  return [];
@@ -10222,18 +10222,18 @@ function requireReactDomClient_production() {
10222
10222
  return inst ? inst : null;
10223
10223
  }
10224
10224
  function accumulateEnterLeaveListenersForEvent(dispatchQueue, event, target, common, inCapturePhase) {
10225
- for (var registrationName = event._reactName, listeners = []; null !== target && target !== common; ) {
10225
+ for (var registrationName = event._reactName, listeners2 = []; null !== target && target !== common; ) {
10226
10226
  var _instance3 = target, alternate = _instance3.alternate, stateNode = _instance3.stateNode;
10227
10227
  _instance3 = _instance3.tag;
10228
10228
  if (null !== alternate && alternate === common) break;
10229
- 5 !== _instance3 && 26 !== _instance3 && 27 !== _instance3 || null === stateNode || (alternate = stateNode, inCapturePhase ? (stateNode = getListener(target, registrationName), null != stateNode && listeners.unshift(
10229
+ 5 !== _instance3 && 26 !== _instance3 && 27 !== _instance3 || null === stateNode || (alternate = stateNode, inCapturePhase ? (stateNode = getListener(target, registrationName), null != stateNode && listeners2.unshift(
10230
10230
  createDispatchListener(target, stateNode, alternate)
10231
- )) : inCapturePhase || (stateNode = getListener(target, registrationName), null != stateNode && listeners.push(
10231
+ )) : inCapturePhase || (stateNode = getListener(target, registrationName), null != stateNode && listeners2.push(
10232
10232
  createDispatchListener(target, stateNode, alternate)
10233
10233
  )));
10234
10234
  target = target.return;
10235
10235
  }
10236
- 0 !== listeners.length && dispatchQueue.push({ event, listeners });
10236
+ 0 !== listeners2.length && dispatchQueue.push({ event, listeners: listeners2 });
10237
10237
  }
10238
10238
  var NORMALIZE_NEWLINES_REGEX = /\r\n?/g, NORMALIZE_NULL_AND_REPLACEMENT_REGEX = /\u0000|\uFFFD/g;
10239
10239
  function normalizeMarkupForTextOrAttribute(markup) {
@@ -12550,7 +12550,7 @@ function requireClient() {
12550
12550
  return client.exports;
12551
12551
  }
12552
12552
  var clientExports = requireClient();
12553
- const ReactDOM = /* @__PURE__ */ getDefaultExportFromCjs(clientExports);
12553
+ const ReactDOM$1 = /* @__PURE__ */ getDefaultExportFromCjs(clientExports);
12554
12554
  /**
12555
12555
  * react-router v7.9.4
12556
12556
  *
@@ -14637,6 +14637,7 @@ function useViewTransitionState(to, { relative } = {}) {
14637
14637
  return matchPath(path.pathname, nextPath) != null || matchPath(path.pathname, currentPath) != null;
14638
14638
  }
14639
14639
  var reactDomExports = requireReactDom();
14640
+ const ReactDOM = /* @__PURE__ */ getDefaultExportFromCjs(reactDomExports);
14640
14641
  function setRef(ref, value) {
14641
14642
  if (typeof ref === "function") {
14642
14643
  return ref(value);
@@ -15332,11 +15333,11 @@ function usePointerDownOutside(onPointerDownOutside, ownerDocument = globalThis?
15332
15333
  }
15333
15334
  isPointerInsideReactTreeRef.current = false;
15334
15335
  };
15335
- const timerId = window.setTimeout(() => {
15336
+ const timerId2 = window.setTimeout(() => {
15336
15337
  ownerDocument.addEventListener("pointerdown", handlePointerDown);
15337
15338
  }, 0);
15338
15339
  return () => {
15339
- window.clearTimeout(timerId);
15340
+ window.clearTimeout(timerId2);
15340
15341
  ownerDocument.removeEventListener("pointerdown", handlePointerDown);
15341
15342
  ownerDocument.removeEventListener("click", handleClickRef.current);
15342
15343
  };
@@ -15380,6 +15381,15 @@ function handleAndDispatchCustomEvent(name, handler, detail, { discrete }) {
15380
15381
  target.dispatchEvent(event);
15381
15382
  }
15382
15383
  }
15384
+ var PORTAL_NAME$1 = "Portal";
15385
+ var Portal$1 = reactExports.forwardRef((props, forwardedRef) => {
15386
+ const { container: containerProp, ...portalProps } = props;
15387
+ const [mounted, setMounted] = reactExports.useState(false);
15388
+ useLayoutEffect2(() => setMounted(true), []);
15389
+ const container = containerProp || mounted && globalThis?.document?.body;
15390
+ return container ? ReactDOM.createPortal(/* @__PURE__ */ jsxRuntimeExports.jsx(Primitive.div, { ...portalProps, ref: forwardedRef }), container) : null;
15391
+ });
15392
+ Portal$1.displayName = PORTAL_NAME$1;
15383
15393
  function useSize(element) {
15384
15394
  const [size2, setSize] = reactExports.useState(void 0);
15385
15395
  useLayoutEffect2(() => {
@@ -16007,7 +16017,7 @@ async function convertValueToCoords(state, options) {
16007
16017
  y: crossAxis * crossAxisMulti
16008
16018
  };
16009
16019
  }
16010
- const offset$2 = function(options) {
16020
+ const offset$3 = function(options) {
16011
16021
  if (options === void 0) {
16012
16022
  options = 0;
16013
16023
  }
@@ -16990,7 +17000,7 @@ function autoUpdate(reference, floating, update, options) {
16990
17000
  }
16991
17001
  };
16992
17002
  }
16993
- const offset$1 = offset$2;
17003
+ const offset$2 = offset$3;
16994
17004
  const shift$1 = shift$2;
16995
17005
  const flip$1 = flip$2;
16996
17006
  const size$1 = size$2;
@@ -17263,8 +17273,8 @@ const arrow$1 = (options) => {
17263
17273
  }
17264
17274
  };
17265
17275
  };
17266
- const offset = (options, deps) => ({
17267
- ...offset$1(options),
17276
+ const offset$1 = (options, deps) => ({
17277
+ ...offset$2(options),
17268
17278
  options: [options, deps]
17269
17279
  });
17270
17280
  const shift = (options, deps) => ({
@@ -17388,7 +17398,7 @@ var PopperContent = reactExports.forwardRef(
17388
17398
  reference: context.anchor
17389
17399
  },
17390
17400
  middleware: [
17391
- offset({ mainAxis: sideOffset + arrowHeight, alignmentAxis: alignOffset }),
17401
+ offset$1({ mainAxis: sideOffset + arrowHeight, alignmentAxis: alignOffset }),
17392
17402
  avoidCollisions && shift({
17393
17403
  mainAxis: true,
17394
17404
  crossAxis: false,
@@ -18867,6 +18877,12 @@ var PORTAL_NAME = "TooltipPortal";
18867
18877
  var [PortalProvider, usePortalContext] = createTooltipContext(PORTAL_NAME, {
18868
18878
  forceMount: void 0
18869
18879
  });
18880
+ var TooltipPortal = (props) => {
18881
+ const { __scopeTooltip, forceMount, children, container } = props;
18882
+ const context = useTooltipContext(PORTAL_NAME, __scopeTooltip);
18883
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(PortalProvider, { scope: __scopeTooltip, forceMount, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Presence, { present: forceMount || context.open, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Portal$1, { asChild: true, container, children }) }) });
18884
+ };
18885
+ TooltipPortal.displayName = PORTAL_NAME;
18870
18886
  var CONTENT_NAME = "TooltipContent";
18871
18887
  var TooltipContent = reactExports.forwardRef(
18872
18888
  (props, forwardedRef) => {
@@ -19135,7 +19151,9 @@ function getHullPresorted(points) {
19135
19151
  var Provider = TooltipProvider;
19136
19152
  var Root3 = Tooltip;
19137
19153
  var Trigger = TooltipTrigger;
19154
+ var Portal = TooltipPortal;
19138
19155
  var Content2 = TooltipContent;
19156
+ var Arrow2 = TooltipArrow;
19139
19157
  var classnames = { exports: {} };
19140
19158
  /*!
19141
19159
  Copyright (c) 2018 Jed Watson.
@@ -19201,23 +19219,23 @@ function requireClassnames() {
19201
19219
  }
19202
19220
  var classnamesExports = requireClassnames();
19203
19221
  const y = /* @__PURE__ */ getDefaultExportFromCjs(classnamesExports);
19204
- const o$8 = { asChild: { type: "boolean" } };
19222
+ const o$7 = { asChild: { type: "boolean" } };
19205
19223
  const t$7 = { width: { type: "string", className: "rt-r-w", customProperties: ["--width"], responsive: true }, minWidth: { type: "string", className: "rt-r-min-w", customProperties: ["--min-width"], responsive: true }, maxWidth: { type: "string", className: "rt-r-max-w", customProperties: ["--max-width"], responsive: true } };
19206
- const e$g = { height: { type: "string", className: "rt-r-h", customProperties: ["--height"], responsive: true }, minHeight: { type: "string", className: "rt-r-min-h", customProperties: ["--min-height"], responsive: true }, maxHeight: { type: "string", className: "rt-r-max-h", customProperties: ["--max-height"], responsive: true } };
19207
- const o$7 = ["gray", "gold", "bronze", "brown", "yellow", "amber", "orange", "tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "lime", "mint", "sky"], r$d = { color: { type: "enum", values: o$7, default: void 0 } }, s$5 = { color: { type: "enum", values: o$7, default: "" } };
19208
- const o$6 = { highContrast: { type: "boolean", className: "rt-high-contrast", default: void 0 } };
19209
- const e$f = ["normal", "start", "end", "both"], r$c = { trim: { type: "enum", className: "rt-r-lt", values: e$f, responsive: true } };
19210
- const e$e = ["left", "center", "right"], t$6 = { align: { type: "enum", className: "rt-r-ta", values: e$e, responsive: true } };
19211
- const e$d = ["wrap", "nowrap", "pretty", "balance"], r$b = { wrap: { type: "enum", className: "rt-r-tw", values: e$d, responsive: true } };
19212
- const e$c = { truncate: { type: "boolean", className: "rt-truncate" } };
19213
- const e$b = ["light", "regular", "medium", "bold"], t$5 = { weight: { type: "enum", className: "rt-r-weight", values: e$b, responsive: true } };
19214
- const m$6 = ["h1", "h2", "h3", "h4", "h5", "h6"], a$9 = ["1", "2", "3", "4", "5", "6", "7", "8", "9"], n$7 = { as: { type: "enum", values: m$6, default: "h1" }, ...o$8, size: { type: "enum", className: "rt-r-size", values: a$9, default: "6", responsive: true }, ...t$5, ...t$6, ...r$c, ...e$c, ...r$b, ...r$d, ...o$6 };
19215
- const e$a = ["initial", "xs", "sm", "md", "lg", "xl"];
19216
- function e$9(n2, r2) {
19224
+ const e$f = { height: { type: "string", className: "rt-r-h", customProperties: ["--height"], responsive: true }, minHeight: { type: "string", className: "rt-r-min-h", customProperties: ["--min-height"], responsive: true }, maxHeight: { type: "string", className: "rt-r-max-h", customProperties: ["--max-height"], responsive: true } };
19225
+ const o$6 = ["gray", "gold", "bronze", "brown", "yellow", "amber", "orange", "tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "lime", "mint", "sky"], r$c = { color: { type: "enum", values: o$6, default: void 0 } }, s$5 = { color: { type: "enum", values: o$6, default: "" } };
19226
+ const o$5 = { highContrast: { type: "boolean", className: "rt-high-contrast", default: void 0 } };
19227
+ const e$e = ["normal", "start", "end", "both"], r$b = { trim: { type: "enum", className: "rt-r-lt", values: e$e, responsive: true } };
19228
+ const e$d = ["left", "center", "right"], t$6 = { align: { type: "enum", className: "rt-r-ta", values: e$d, responsive: true } };
19229
+ const e$c = ["wrap", "nowrap", "pretty", "balance"], r$a = { wrap: { type: "enum", className: "rt-r-tw", values: e$c, responsive: true } };
19230
+ const e$b = { truncate: { type: "boolean", className: "rt-truncate" } };
19231
+ const e$a = ["light", "regular", "medium", "bold"], t$5 = { weight: { type: "enum", className: "rt-r-weight", values: e$a, responsive: true } };
19232
+ const m$6 = ["h1", "h2", "h3", "h4", "h5", "h6"], a$9 = ["1", "2", "3", "4", "5", "6", "7", "8", "9"], n$7 = { as: { type: "enum", values: m$6, default: "h1" }, ...o$7, size: { type: "enum", className: "rt-r-size", values: a$9, default: "6", responsive: true }, ...t$5, ...t$6, ...r$b, ...e$b, ...r$a, ...r$c, ...o$5 };
19233
+ const e$9 = ["initial", "xs", "sm", "md", "lg", "xl"];
19234
+ function e$8(n2, r2) {
19217
19235
  return Object.prototype.hasOwnProperty.call(n2, r2);
19218
19236
  }
19219
- function i$3(e2) {
19220
- return typeof e2 == "object" && Object.keys(e2).some((s2) => e$a.includes(s2));
19237
+ function i$4(e2) {
19238
+ return typeof e2 == "object" && Object.keys(e2).some((s2) => e$9.includes(s2));
19221
19239
  }
19222
19240
  function R$2({ className: r2, customProperties: n2, ...t2 }) {
19223
19241
  const p2 = g({ allowArbitraryValues: true, className: r2, ...t2 }), e2 = m$5({ customProperties: n2, ...t2 });
@@ -19227,10 +19245,10 @@ function g({ allowArbitraryValues: r2, value: n2, className: t2, propValues: p2,
19227
19245
  const s2 = [];
19228
19246
  if (n2) {
19229
19247
  if (typeof n2 == "string" && p2.includes(n2)) return l$3(t2, n2, e2);
19230
- if (i$3(n2)) {
19248
+ if (i$4(n2)) {
19231
19249
  const i2 = n2;
19232
19250
  for (const o2 in i2) {
19233
- if (!e$9(i2, o2) || !e$a.includes(o2)) continue;
19251
+ if (!e$8(i2, o2) || !e$9.includes(o2)) continue;
19234
19252
  const u2 = i2[o2];
19235
19253
  if (u2 !== void 0) {
19236
19254
  if (p2.includes(u2)) {
@@ -19254,10 +19272,10 @@ function l$3(r2, n2, t2) {
19254
19272
  function m$5({ customProperties: r2, value: n2, propValues: t2, parseValue: p2 = (e2) => e2 }) {
19255
19273
  let e2 = {};
19256
19274
  if (!(!n2 || typeof n2 == "string" && t2.includes(n2))) {
19257
- if (typeof n2 == "string" && (e2 = Object.fromEntries(r2.map((s2) => [s2, n2]))), i$3(n2)) {
19275
+ if (typeof n2 == "string" && (e2 = Object.fromEntries(r2.map((s2) => [s2, n2]))), i$4(n2)) {
19258
19276
  const s2 = n2;
19259
19277
  for (const i2 in s2) {
19260
- if (!e$9(s2, i2) || !e$a.includes(i2)) continue;
19278
+ if (!e$8(s2, i2) || !e$9.includes(i2)) continue;
19261
19279
  const o2 = s2[i2];
19262
19280
  if (!t2.includes(o2)) for (const u2 of r2) e2 = { [i2 === "initial" ? u2 : `${u2}-${i2}`]: o2, ...e2 };
19263
19281
  }
@@ -19283,11 +19301,11 @@ function v(r2, ...m2) {
19283
19301
  for (const n2 in f2) {
19284
19302
  let s2 = a2[n2];
19285
19303
  const e2 = f2[n2];
19286
- if (e2.default !== void 0 && s2 === void 0 && (s2 = e2.default), e2.type === "enum" && ![e2.default, ...e2.values].includes(s2) && !i$3(s2) && (s2 = e2.default), a2[n2] = s2, "className" in e2 && e2.className) {
19304
+ if (e2.default !== void 0 && s2 === void 0 && (s2 = e2.default), e2.type === "enum" && ![e2.default, ...e2.values].includes(s2) && !i$4(s2) && (s2 = e2.default), a2[n2] = s2, "className" in e2 && e2.className) {
19287
19305
  delete a2[n2];
19288
19306
  const u2 = "responsive" in e2;
19289
- if (!s2 || i$3(s2) && !u2) continue;
19290
- if (i$3(s2) && (e2.default !== void 0 && s2.initial === void 0 && (s2.initial = e2.default), e2.type === "enum" && ([e2.default, ...e2.values].includes(s2.initial) || (s2.initial = e2.default))), e2.type === "enum") {
19307
+ if (!s2 || i$4(s2) && !u2) continue;
19308
+ if (i$4(s2) && (e2.default !== void 0 && s2.initial === void 0 && (s2.initial = e2.default), e2.type === "enum" && ([e2.default, ...e2.values].includes(s2.initial) || (s2.initial = e2.default))), e2.type === "enum") {
19291
19309
  const i2 = g({ allowArbitraryValues: false, value: s2, className: e2.className, propValues: e2.values, parseValue: e2.parseValue });
19292
19310
  t2 = y(t2, i2);
19293
19311
  continue;
@@ -19305,18 +19323,18 @@ function v(r2, ...m2) {
19305
19323
  }
19306
19324
  return a2.className = y(t2, r2.className), a2.style = l$2(l2, r2.style), a2;
19307
19325
  }
19308
- const e$8 = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9"], r$a = { m: { type: "enum | string", values: e$8, responsive: true, className: "rt-r-m", customProperties: ["--m"] }, mx: { type: "enum | string", values: e$8, responsive: true, className: "rt-r-mx", customProperties: ["--ml", "--mr"] }, my: { type: "enum | string", values: e$8, responsive: true, className: "rt-r-my", customProperties: ["--mt", "--mb"] }, mt: { type: "enum | string", values: e$8, responsive: true, className: "rt-r-mt", customProperties: ["--mt"] }, mr: { type: "enum | string", values: e$8, responsive: true, className: "rt-r-mr", customProperties: ["--mr"] }, mb: { type: "enum | string", values: e$8, responsive: true, className: "rt-r-mb", customProperties: ["--mb"] }, ml: { type: "enum | string", values: e$8, responsive: true, className: "rt-r-ml", customProperties: ["--ml"] } };
19309
- const r$9 = reactExports.forwardRef((p2, t2) => {
19310
- const { children: e2, className: s2, asChild: a2, as: n2 = "h1", color: i2, ...m2 } = v(p2, n$7, r$a);
19326
+ const e$7 = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9"], r$9 = { m: { type: "enum | string", values: e$7, responsive: true, className: "rt-r-m", customProperties: ["--m"] }, mx: { type: "enum | string", values: e$7, responsive: true, className: "rt-r-mx", customProperties: ["--ml", "--mr"] }, my: { type: "enum | string", values: e$7, responsive: true, className: "rt-r-my", customProperties: ["--mt", "--mb"] }, mt: { type: "enum | string", values: e$7, responsive: true, className: "rt-r-mt", customProperties: ["--mt"] }, mr: { type: "enum | string", values: e$7, responsive: true, className: "rt-r-mr", customProperties: ["--mr"] }, mb: { type: "enum | string", values: e$7, responsive: true, className: "rt-r-mb", customProperties: ["--mb"] }, ml: { type: "enum | string", values: e$7, responsive: true, className: "rt-r-ml", customProperties: ["--ml"] } };
19327
+ const r$8 = reactExports.forwardRef((p2, t2) => {
19328
+ const { children: e2, className: s2, asChild: a2, as: n2 = "h1", color: i2, ...m2 } = v(p2, n$7, r$9);
19311
19329
  return reactExports.createElement(Slot, { "data-accent-color": i2, ...m2, ref: t2, className: y("rt-Heading", s2) }, a2 ? e2 : reactExports.createElement(n2, null, e2));
19312
19330
  });
19313
- r$9.displayName = "Heading";
19314
- const m$4 = ["span", "div", "label", "p"], a$8 = ["1", "2", "3", "4", "5", "6", "7", "8", "9"], n$6 = { as: { type: "enum", values: m$4, default: "span" }, ...o$8, size: { type: "enum", className: "rt-r-size", values: a$8, responsive: true }, ...t$5, ...t$6, ...r$c, ...e$c, ...r$b, ...r$d, ...o$6 };
19315
- const p$a = reactExports.forwardRef((t2, r2) => {
19316
- const { children: e2, className: s2, asChild: m2, as: a2 = "span", color: n2, ...P2 } = v(t2, n$6, r$a);
19331
+ r$8.displayName = "Heading";
19332
+ const m$4 = ["span", "div", "label", "p"], a$8 = ["1", "2", "3", "4", "5", "6", "7", "8", "9"], n$6 = { as: { type: "enum", values: m$4, default: "span" }, ...o$7, size: { type: "enum", className: "rt-r-size", values: a$8, responsive: true }, ...t$5, ...t$6, ...r$b, ...e$b, ...r$a, ...r$c, ...o$5 };
19333
+ const p$b = reactExports.forwardRef((t2, r2) => {
19334
+ const { children: e2, className: s2, asChild: m2, as: a2 = "span", color: n2, ...P2 } = v(t2, n$6, r$9);
19317
19335
  return reactExports.createElement(Slot, { "data-accent-color": n2, ...P2, ref: r2, className: y("rt-Text", s2) }, m2 ? e2 : reactExports.createElement(a2, null, e2));
19318
19336
  });
19319
- p$a.displayName = "Text";
19337
+ p$b.displayName = "Text";
19320
19338
  function a$7(e2) {
19321
19339
  switch (e2) {
19322
19340
  case "tomato":
@@ -19353,7 +19371,7 @@ function a$7(e2) {
19353
19371
  return "gray";
19354
19372
  }
19355
19373
  }
19356
- const e$7 = ["none", "small", "medium", "large", "full"], r$8 = { radius: { type: "enum", values: e$7, default: void 0 } };
19374
+ const e$6 = ["none", "small", "medium", "large", "full"], r$7 = { radius: { type: "enum", values: e$6, default: void 0 } };
19357
19375
  const s$4 = { hasBackground: { default: true }, appearance: { default: "inherit" }, accentColor: { default: "indigo" }, grayColor: { default: "auto" }, panelBackground: { default: "translucent" }, radius: { default: "medium" }, scaling: { default: "100%" } };
19358
19376
  const d$3 = () => {
19359
19377
  }, P$2 = reactExports.createContext(void 0);
@@ -19385,42 +19403,42 @@ function d$2(i2, e2) {
19385
19403
  const t2 = reactExports.Children.only(c2);
19386
19404
  return reactExports.cloneElement(t2, { children: typeof e2 == "function" ? e2(t2.props.children) : e2 });
19387
19405
  }
19388
- const e$6 = Slot;
19389
- const s$3 = ["div", "span"], o$5 = ["none", "inline", "inline-block", "block", "contents"], p$9 = { as: { type: "enum", values: s$3, default: "div" }, ...o$8, display: { type: "enum", className: "rt-r-display", values: o$5, responsive: true } };
19390
- const e$5 = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], p$8 = { p: { type: "enum | string", className: "rt-r-p", customProperties: ["--p"], values: e$5, responsive: true }, px: { type: "enum | string", className: "rt-r-px", customProperties: ["--pl", "--pr"], values: e$5, responsive: true }, py: { type: "enum | string", className: "rt-r-py", customProperties: ["--pt", "--pb"], values: e$5, responsive: true }, pt: { type: "enum | string", className: "rt-r-pt", customProperties: ["--pt"], values: e$5, responsive: true }, pr: { type: "enum | string", className: "rt-r-pr", customProperties: ["--pr"], values: e$5, responsive: true }, pb: { type: "enum | string", className: "rt-r-pb", customProperties: ["--pb"], values: e$5, responsive: true }, pl: { type: "enum | string", className: "rt-r-pl", customProperties: ["--pl"], values: e$5, responsive: true } };
19391
- const r$7 = ["visible", "hidden", "clip", "scroll", "auto"], i$2 = ["static", "relative", "absolute", "fixed", "sticky"], e$4 = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9"], p$7 = ["0", "1"], n$5 = ["0", "1"], u$3 = { ...p$8, ...t$7, ...e$g, position: { type: "enum", className: "rt-r-position", values: i$2, responsive: true }, inset: { type: "enum | string", className: "rt-r-inset", customProperties: ["--inset"], values: e$4, responsive: true }, top: { type: "enum | string", className: "rt-r-top", customProperties: ["--top"], values: e$4, responsive: true }, right: { type: "enum | string", className: "rt-r-right", customProperties: ["--right"], values: e$4, responsive: true }, bottom: { type: "enum | string", className: "rt-r-bottom", customProperties: ["--bottom"], values: e$4, responsive: true }, left: { type: "enum | string", className: "rt-r-left", customProperties: ["--left"], values: e$4, responsive: true }, overflow: { type: "enum", className: "rt-r-overflow", values: r$7, responsive: true }, overflowX: { type: "enum", className: "rt-r-ox", values: r$7, responsive: true }, overflowY: { type: "enum", className: "rt-r-oy", values: r$7, responsive: true }, flexBasis: { type: "string", className: "rt-r-fb", customProperties: ["--flex-basis"], responsive: true }, flexShrink: { type: "enum | string", className: "rt-r-fs", customProperties: ["--flex-shrink"], values: p$7, responsive: true }, flexGrow: { type: "enum | string", className: "rt-r-fg", customProperties: ["--flex-grow"], values: n$5, responsive: true }, gridArea: { type: "string", className: "rt-r-ga", customProperties: ["--grid-area"], responsive: true }, gridColumn: { type: "string", className: "rt-r-gc", customProperties: ["--grid-column"], responsive: true }, gridColumnStart: { type: "string", className: "rt-r-gcs", customProperties: ["--grid-column-start"], responsive: true }, gridColumnEnd: { type: "string", className: "rt-r-gce", customProperties: ["--grid-column-end"], responsive: true }, gridRow: { type: "string", className: "rt-r-gr", customProperties: ["--grid-row"], responsive: true }, gridRowStart: { type: "string", className: "rt-r-grs", customProperties: ["--grid-row-start"], responsive: true }, gridRowEnd: { type: "string", className: "rt-r-gre", customProperties: ["--grid-row-end"], responsive: true } };
19392
- const p$6 = reactExports.forwardRef((r2, s2) => {
19393
- const { className: t2, asChild: e2, as: m2 = "div", ...a2 } = v(r2, p$9, u$3, r$a);
19394
- return reactExports.createElement(e2 ? e$6 : m2, { ...a2, ref: s2, className: y("rt-Box", t2) });
19406
+ const e$5 = Slot;
19407
+ const s$3 = ["div", "span"], o$4 = ["none", "inline", "inline-block", "block", "contents"], p$a = { as: { type: "enum", values: s$3, default: "div" }, ...o$7, display: { type: "enum", className: "rt-r-display", values: o$4, responsive: true } };
19408
+ const e$4 = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], p$9 = { p: { type: "enum | string", className: "rt-r-p", customProperties: ["--p"], values: e$4, responsive: true }, px: { type: "enum | string", className: "rt-r-px", customProperties: ["--pl", "--pr"], values: e$4, responsive: true }, py: { type: "enum | string", className: "rt-r-py", customProperties: ["--pt", "--pb"], values: e$4, responsive: true }, pt: { type: "enum | string", className: "rt-r-pt", customProperties: ["--pt"], values: e$4, responsive: true }, pr: { type: "enum | string", className: "rt-r-pr", customProperties: ["--pr"], values: e$4, responsive: true }, pb: { type: "enum | string", className: "rt-r-pb", customProperties: ["--pb"], values: e$4, responsive: true }, pl: { type: "enum | string", className: "rt-r-pl", customProperties: ["--pl"], values: e$4, responsive: true } };
19409
+ const r$6 = ["visible", "hidden", "clip", "scroll", "auto"], i$3 = ["static", "relative", "absolute", "fixed", "sticky"], e$3 = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9"], p$8 = ["0", "1"], n$5 = ["0", "1"], u$3 = { ...p$9, ...t$7, ...e$f, position: { type: "enum", className: "rt-r-position", values: i$3, responsive: true }, inset: { type: "enum | string", className: "rt-r-inset", customProperties: ["--inset"], values: e$3, responsive: true }, top: { type: "enum | string", className: "rt-r-top", customProperties: ["--top"], values: e$3, responsive: true }, right: { type: "enum | string", className: "rt-r-right", customProperties: ["--right"], values: e$3, responsive: true }, bottom: { type: "enum | string", className: "rt-r-bottom", customProperties: ["--bottom"], values: e$3, responsive: true }, left: { type: "enum | string", className: "rt-r-left", customProperties: ["--left"], values: e$3, responsive: true }, overflow: { type: "enum", className: "rt-r-overflow", values: r$6, responsive: true }, overflowX: { type: "enum", className: "rt-r-ox", values: r$6, responsive: true }, overflowY: { type: "enum", className: "rt-r-oy", values: r$6, responsive: true }, flexBasis: { type: "string", className: "rt-r-fb", customProperties: ["--flex-basis"], responsive: true }, flexShrink: { type: "enum | string", className: "rt-r-fs", customProperties: ["--flex-shrink"], values: p$8, responsive: true }, flexGrow: { type: "enum | string", className: "rt-r-fg", customProperties: ["--flex-grow"], values: n$5, responsive: true }, gridArea: { type: "string", className: "rt-r-ga", customProperties: ["--grid-area"], responsive: true }, gridColumn: { type: "string", className: "rt-r-gc", customProperties: ["--grid-column"], responsive: true }, gridColumnStart: { type: "string", className: "rt-r-gcs", customProperties: ["--grid-column-start"], responsive: true }, gridColumnEnd: { type: "string", className: "rt-r-gce", customProperties: ["--grid-column-end"], responsive: true }, gridRow: { type: "string", className: "rt-r-gr", customProperties: ["--grid-row"], responsive: true }, gridRowStart: { type: "string", className: "rt-r-grs", customProperties: ["--grid-row-start"], responsive: true }, gridRowEnd: { type: "string", className: "rt-r-gre", customProperties: ["--grid-row-end"], responsive: true } };
19410
+ const p$7 = reactExports.forwardRef((r2, s2) => {
19411
+ const { className: t2, asChild: e2, as: m2 = "div", ...a2 } = v(r2, p$a, u$3, r$9);
19412
+ return reactExports.createElement(e2 ? e$5 : m2, { ...a2, ref: s2, className: y("rt-Box", t2) });
19395
19413
  });
19396
- p$6.displayName = "Box";
19397
- const t$4 = ["1", "2", "3", "4"], a$6 = ["classic", "solid", "soft", "surface", "outline", "ghost"], i$1 = { ...o$8, size: { type: "enum", className: "rt-r-size", values: t$4, default: "2", responsive: true }, variant: { type: "enum", className: "rt-variant", values: a$6, default: "solid" }, ...s$5, ...o$6, ...r$8, loading: { type: "boolean", className: "rt-loading", default: false } };
19398
- const e$3 = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], p$5 = { gap: { type: "enum | string", className: "rt-r-gap", customProperties: ["--gap"], values: e$3, responsive: true }, gapX: { type: "enum | string", className: "rt-r-cg", customProperties: ["--column-gap"], values: e$3, responsive: true }, gapY: { type: "enum | string", className: "rt-r-rg", customProperties: ["--row-gap"], values: e$3, responsive: true } };
19399
- const t$3 = ["div", "span"], p$4 = ["none", "inline-flex", "flex"], a$5 = ["row", "column", "row-reverse", "column-reverse"], o$4 = ["start", "center", "end", "baseline", "stretch"], n$4 = ["start", "center", "end", "between"], l$1 = ["nowrap", "wrap", "wrap-reverse"], u$2 = { as: { type: "enum", values: t$3, default: "div" }, ...o$8, display: { type: "enum", className: "rt-r-display", values: p$4, responsive: true }, direction: { type: "enum", className: "rt-r-fd", values: a$5, responsive: true }, align: { type: "enum", className: "rt-r-ai", values: o$4, responsive: true }, justify: { type: "enum", className: "rt-r-jc", values: n$4, parseValue: f$4, responsive: true }, wrap: { type: "enum", className: "rt-r-fw", values: l$1, responsive: true }, ...p$5 };
19400
- function f$4(e2) {
19414
+ p$7.displayName = "Box";
19415
+ const t$4 = ["1", "2", "3", "4"], a$6 = ["classic", "solid", "soft", "surface", "outline", "ghost"], i$2 = { ...o$7, size: { type: "enum", className: "rt-r-size", values: t$4, default: "2", responsive: true }, variant: { type: "enum", className: "rt-variant", values: a$6, default: "solid" }, ...s$5, ...o$5, ...r$7, loading: { type: "boolean", className: "rt-loading", default: false } };
19416
+ const e$2 = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], p$6 = { gap: { type: "enum | string", className: "rt-r-gap", customProperties: ["--gap"], values: e$2, responsive: true }, gapX: { type: "enum | string", className: "rt-r-cg", customProperties: ["--column-gap"], values: e$2, responsive: true }, gapY: { type: "enum | string", className: "rt-r-rg", customProperties: ["--row-gap"], values: e$2, responsive: true } };
19417
+ const t$3 = ["div", "span"], p$5 = ["none", "inline-flex", "flex"], a$5 = ["row", "column", "row-reverse", "column-reverse"], o$3 = ["start", "center", "end", "baseline", "stretch"], n$4 = ["start", "center", "end", "between"], l$1 = ["nowrap", "wrap", "wrap-reverse"], u$2 = { as: { type: "enum", values: t$3, default: "div" }, ...o$7, display: { type: "enum", className: "rt-r-display", values: p$5, responsive: true }, direction: { type: "enum", className: "rt-r-fd", values: a$5, responsive: true }, align: { type: "enum", className: "rt-r-ai", values: o$3, responsive: true }, justify: { type: "enum", className: "rt-r-jc", values: n$4, parseValue: f$5, responsive: true }, wrap: { type: "enum", className: "rt-r-fw", values: l$1, responsive: true }, ...p$6 };
19418
+ function f$5(e2) {
19401
19419
  return e2 === "between" ? "space-between" : e2;
19402
19420
  }
19403
- const p$3 = reactExports.forwardRef((r2, e2) => {
19404
- const { className: s2, asChild: t2, as: m2 = "div", ...l2 } = v(r2, u$2, u$3, r$a);
19405
- return reactExports.createElement(t2 ? e$6 : m2, { ...l2, ref: e2, className: y("rt-Flex", s2) });
19421
+ const p$4 = reactExports.forwardRef((r2, e2) => {
19422
+ const { className: s2, asChild: t2, as: m2 = "div", ...l2 } = v(r2, u$2, u$3, r$9);
19423
+ return reactExports.createElement(t2 ? e$5 : m2, { ...l2, ref: e2, className: y("rt-Flex", s2) });
19406
19424
  });
19407
- p$3.displayName = "Flex";
19408
- const e$2 = ["1", "2", "3"], s$2 = { size: { type: "enum", className: "rt-r-size", values: e$2, default: "2", responsive: true }, loading: { type: "boolean", default: true } };
19425
+ p$4.displayName = "Flex";
19426
+ const e$1 = ["1", "2", "3"], s$2 = { size: { type: "enum", className: "rt-r-size", values: e$1, default: "2", responsive: true }, loading: { type: "boolean", default: true } };
19409
19427
  const s$1 = reactExports.forwardRef((i2, o2) => {
19410
- const { className: a2, children: e2, loading: t2, ...m2 } = v(i2, s$2, r$a);
19428
+ const { className: a2, children: e2, loading: t2, ...m2 } = v(i2, s$2, r$9);
19411
19429
  if (!t2) return e2;
19412
19430
  const r2 = reactExports.createElement("span", { ...m2, ref: o2, className: y("rt-Spinner", a2) }, reactExports.createElement("span", { className: "rt-SpinnerLeaf" }), reactExports.createElement("span", { className: "rt-SpinnerLeaf" }), reactExports.createElement("span", { className: "rt-SpinnerLeaf" }), reactExports.createElement("span", { className: "rt-SpinnerLeaf" }), reactExports.createElement("span", { className: "rt-SpinnerLeaf" }), reactExports.createElement("span", { className: "rt-SpinnerLeaf" }), reactExports.createElement("span", { className: "rt-SpinnerLeaf" }), reactExports.createElement("span", { className: "rt-SpinnerLeaf" }));
19413
- return e2 === void 0 ? r2 : reactExports.createElement(p$3, { asChild: true, position: "relative", align: "center", justify: "center" }, reactExports.createElement("span", null, reactExports.createElement("span", { "aria-hidden": true, style: { display: "contents", visibility: "hidden" }, inert: void 0 }, e2), reactExports.createElement(p$3, { asChild: true, align: "center", justify: "center", position: "absolute", inset: "0" }, reactExports.createElement("span", null, r2))));
19431
+ return e2 === void 0 ? r2 : reactExports.createElement(p$4, { asChild: true, position: "relative", align: "center", justify: "center" }, reactExports.createElement("span", null, reactExports.createElement("span", { "aria-hidden": true, style: { display: "contents", visibility: "hidden" }, inert: void 0 }, e2), reactExports.createElement(p$4, { asChild: true, align: "center", justify: "center", position: "absolute", inset: "0" }, reactExports.createElement("span", null, r2))));
19414
19432
  });
19415
19433
  s$1.displayName = "Spinner";
19416
19434
  const d$1 = Root$3;
19417
19435
  function s(e2, t2) {
19418
19436
  if (e2 !== void 0) return typeof e2 == "string" ? t2(e2) : Object.fromEntries(Object.entries(e2).map(([n2, o2]) => [n2, t2(o2)]));
19419
19437
  }
19420
- function p$2(e2) {
19438
+ function p$3(e2) {
19421
19439
  return e2 === "3" ? "3" : "2";
19422
19440
  }
19423
- function r$6(e2) {
19441
+ function r$5(e2) {
19424
19442
  switch (e2) {
19425
19443
  case "1":
19426
19444
  return "1";
@@ -19432,15 +19450,15 @@ function r$6(e2) {
19432
19450
  }
19433
19451
  }
19434
19452
  const n$3 = reactExports.forwardRef((t2, p2) => {
19435
- const { size: i2 = i$1.size.default } = t2, { className: a2, children: e2, asChild: m2, color: d2, radius: l2, disabled: s$22 = t2.loading, ...u2 } = v(t2, i$1, r$a), f2 = m2 ? Slot : "button";
19436
- return reactExports.createElement(f2, { "data-disabled": s$22 || void 0, "data-accent-color": d2, "data-radius": l2, ...u2, ref: p2, className: y("rt-reset", "rt-BaseButton", a2), disabled: s$22 }, t2.loading ? reactExports.createElement(reactExports.Fragment, null, reactExports.createElement("span", { style: { display: "contents", visibility: "hidden" }, "aria-hidden": true }, e2), reactExports.createElement(d$1, null, e2), reactExports.createElement(p$3, { asChild: true, align: "center", justify: "center", position: "absolute", inset: "0" }, reactExports.createElement("span", null, reactExports.createElement(s$1, { size: s(i2, r$6) })))) : e2);
19453
+ const { size: i2 = i$2.size.default } = t2, { className: a2, children: e2, asChild: m2, color: d2, radius: l2, disabled: s$22 = t2.loading, ...u2 } = v(t2, i$2, r$9), f2 = m2 ? Slot : "button";
19454
+ return reactExports.createElement(f2, { "data-disabled": s$22 || void 0, "data-accent-color": d2, "data-radius": l2, ...u2, ref: p2, className: y("rt-reset", "rt-BaseButton", a2), disabled: s$22 }, t2.loading ? reactExports.createElement(reactExports.Fragment, null, reactExports.createElement("span", { style: { display: "contents", visibility: "hidden" }, "aria-hidden": true }, e2), reactExports.createElement(d$1, null, e2), reactExports.createElement(p$4, { asChild: true, align: "center", justify: "center", position: "absolute", inset: "0" }, reactExports.createElement("span", null, reactExports.createElement(s$1, { size: s(i2, r$5) })))) : e2);
19437
19455
  });
19438
19456
  n$3.displayName = "BaseButton";
19439
- const o$3 = reactExports.forwardRef(({ className: e2, ...n2 }, r2) => reactExports.createElement(n$3, { ...n2, ref: r2, className: y("rt-Button", e2) }));
19440
- o$3.displayName = "Button";
19441
- const t$2 = ["1", "2", "3"], r$5 = ["soft", "surface", "outline"], a$4 = { ...o$8, size: { type: "enum", className: "rt-r-size", values: t$2, default: "2", responsive: true }, variant: { type: "enum", className: "rt-variant", values: r$5, default: "soft" }, ...s$5, ...o$6 };
19457
+ const o$2 = reactExports.forwardRef(({ className: e2, ...n2 }, r2) => reactExports.createElement(n$3, { ...n2, ref: r2, className: y("rt-Button", e2) }));
19458
+ o$2.displayName = "Button";
19459
+ const t$2 = ["1", "2", "3"], r$4 = ["soft", "surface", "outline"], a$4 = { ...o$7, size: { type: "enum", className: "rt-r-size", values: t$2, default: "2", responsive: true }, variant: { type: "enum", className: "rt-variant", values: r$4, default: "soft" }, ...s$5, ...o$5 };
19442
19460
  const a$3 = reactExports.createContext({}), n$2 = reactExports.forwardRef((t2, l2) => {
19443
- const { size: e2 = a$4.size.default } = t2, { asChild: r2, children: C, className: i2, color: c2, ...f2 } = v(t2, a$4, r$a), P2 = r2 ? Slot : "div";
19461
+ const { size: e2 = a$4.size.default } = t2, { asChild: r2, children: C, className: i2, color: c2, ...f2 } = v(t2, a$4, r$9), P2 = r2 ? Slot : "div";
19444
19462
  return reactExports.createElement(P2, { "data-accent-color": c2, ...f2, className: y("rt-CalloutRoot", i2), ref: l2 }, reactExports.createElement(a$3.Provider, { value: reactExports.useMemo(() => ({ size: e2 }), [e2]) }, C));
19445
19463
  });
19446
19464
  n$2.displayName = "Callout.Root";
@@ -19448,21 +19466,21 @@ const m$3 = reactExports.forwardRef(({ className: t2, ...l2 }, e2) => reactExpor
19448
19466
  m$3.displayName = "Callout.Icon";
19449
19467
  const u$1 = reactExports.forwardRef(({ className: t2, ...l2 }, e2) => {
19450
19468
  const { size: r2 } = reactExports.useContext(a$3);
19451
- return reactExports.createElement(p$a, { as: "p", size: s(r2, p$2), ...l2, asChild: false, ref: e2, className: y("rt-CalloutText", t2) });
19469
+ return reactExports.createElement(p$b, { as: "p", size: s(r2, p$3), ...l2, asChild: false, ref: e2, className: y("rt-CalloutText", t2) });
19452
19470
  });
19453
19471
  u$1.displayName = "Callout.Text";
19454
- const e$1 = ["1", "2", "3", "4", "5"], r$4 = ["surface", "classic", "ghost"], a$2 = { ...o$8, size: { type: "enum", className: "rt-r-size", values: e$1, default: "1", responsive: true }, variant: { type: "enum", className: "rt-variant", values: r$4, default: "surface" } };
19455
- const o$2 = reactExports.forwardRef((p2, e2) => {
19456
- const { asChild: t2, className: s2, ...a2 } = v(p2, a$2, r$a), m2 = t2 ? Slot : "div";
19457
- return reactExports.createElement(m2, { ref: e2, ...a2, className: y("rt-reset", "rt-BaseCard", "rt-Card", s2) });
19472
+ const a$2 = ["1", "2", "3", "4", "5", "6", "7", "8", "9"], i$1 = ["solid", "soft", "outline", "ghost"], f$4 = { ...o$7, size: { type: "enum", className: "rt-r-size", values: a$2, responsive: true }, variant: { type: "enum", className: "rt-variant", values: i$1, default: "soft" }, ...t$5, ...s$5, ...o$5, ...e$b, ...r$a };
19473
+ const p$2 = reactExports.forwardRef((o2, t2) => {
19474
+ const { asChild: s2, className: m2, color: e2, ...d2 } = v(o2, f$4, r$9), n2 = o2.variant === "ghost" ? e2 || void 0 : e2, a2 = s2 ? Slot : "code";
19475
+ return reactExports.createElement(a2, { "data-accent-color": n2, ...d2, ref: t2, className: y("rt-reset", "rt-Code", m2) });
19458
19476
  });
19459
- o$2.displayName = "Card";
19460
- const r$3 = ["1", "2", "3"], t$1 = { ...o$8, size: { values: r$3, default: "1" }, ...r$8, scrollbars: { default: "both" } };
19477
+ p$2.displayName = "Code";
19478
+ const r$3 = ["1", "2", "3"], t$1 = { ...o$7, size: { values: r$3, default: "1" }, ...r$7, scrollbars: { default: "both" } };
19461
19479
  function a$1(r2) {
19462
19480
  const { m: t2, mx: m2, my: o2, mt: p2, mr: n2, mb: s2, ml: e2, ...i2 } = r2;
19463
19481
  return { m: t2, mx: m2, my: o2, mt: p2, mr: n2, mb: s2, ml: e2, rest: i2 };
19464
19482
  }
19465
- const r$2 = r$a.m.values;
19483
+ const r$2 = r$9.m.values;
19466
19484
  function S(s2) {
19467
19485
  const [e2, t2] = R$2({ className: "rt-r-m", customProperties: ["--margin"], propValues: r$2, value: s2.m }), [a2, o2] = R$2({ className: "rt-r-mx", customProperties: ["--margin-left", "--margin-right"], propValues: r$2, value: s2.mx }), [l2, i2] = R$2({ className: "rt-r-my", customProperties: ["--margin-top", "--margin-bottom"], propValues: r$2, value: s2.my }), [p2, u2] = R$2({ className: "rt-r-mt", customProperties: ["--margin-top"], propValues: r$2, value: s2.mt }), [n2, c2] = R$2({ className: "rt-r-mr", customProperties: ["--margin-right"], propValues: r$2, value: s2.mr }), [g2, P2] = R$2({ className: "rt-r-mb", customProperties: ["--margin-bottom"], propValues: r$2, value: s2.mb }), [N2, C] = R$2({ className: "rt-r-ml", customProperties: ["--margin-left"], propValues: r$2, value: s2.ml });
19468
19486
  return [y(e2, a2, l2, p2, n2, g2, N2), l$2(t2, o2, i2, u2, c2, P2, C)];
@@ -19472,23 +19490,23 @@ const c$1 = reactExports.forwardRef((n2, S$1) => {
19472
19490
  return reactExports.createElement(Root, { type: t2, scrollHideDelay: N2, className: y("rt-ScrollAreaRoot", u2, y$1), style: l$2(A2, v2), asChild: a2 }, d$2({ asChild: a2, children: d2 }, (g$1) => reactExports.createElement(reactExports.Fragment, null, reactExports.createElement(Viewport, { ...b2, ref: S$1, className: "rt-ScrollAreaViewport" }, g$1), reactExports.createElement("div", { className: "rt-ScrollAreaViewportFocusRing" }), l2 !== "vertical" ? reactExports.createElement(Scrollbar, { "data-radius": p2, orientation: "horizontal", className: y("rt-ScrollAreaScrollbar", g({ className: "rt-r-size", value: i2, propValues: t$1.size.values })) }, reactExports.createElement(Thumb, { className: "rt-ScrollAreaThumb" })) : null, l2 !== "horizontal" ? reactExports.createElement(Scrollbar, { "data-radius": p2, orientation: "vertical", className: y("rt-ScrollAreaScrollbar", g({ className: "rt-r-size", value: i2, propValues: t$1.size.values })) }, reactExports.createElement(Thumb, { className: "rt-ScrollAreaThumb" })) : null, l2 === "both" ? reactExports.createElement(Corner, { className: "rt-ScrollAreaCorner" }) : null)));
19473
19491
  });
19474
19492
  c$1.displayName = "ScrollArea";
19475
- const n$1 = ["1", "2", "3", "4", "5", "6", "7", "8", "9"], f$3 = ["auto", "always", "hover", "none"], m$2 = { ...o$8, size: { type: "enum", className: "rt-r-size", values: n$1, responsive: true }, ...t$5, ...r$c, ...e$c, ...r$b, underline: { type: "enum", className: "rt-underline", values: f$3, default: "auto" }, ...s$5, ...o$6 };
19493
+ const n$1 = ["1", "2", "3", "4", "5", "6", "7", "8", "9"], f$3 = ["auto", "always", "hover", "none"], m$2 = { ...o$7, size: { type: "enum", className: "rt-r-size", values: n$1, responsive: true }, ...t$5, ...r$b, ...e$b, ...r$a, underline: { type: "enum", className: "rt-underline", values: f$3, default: "auto" }, ...s$5, ...o$5 };
19476
19494
  const e = reactExports.forwardRef((p2, t2) => {
19477
19495
  const { children: r2, className: s2, color: n2, asChild: i2, ...m2 } = v(p2, m$2);
19478
- return reactExports.createElement(p$a, { ...m2, "data-accent-color": n2, ref: t2, asChild: true, className: y("rt-reset", "rt-Link", s2) }, i2 ? r2 : reactExports.createElement("a", null, r2));
19496
+ return reactExports.createElement(p$b, { ...m2, "data-accent-color": n2, ref: t2, asChild: true, className: y("rt-reset", "rt-Link", s2) }, i2 ? r2 : reactExports.createElement("a", null, r2));
19479
19497
  });
19480
19498
  e.displayName = "Link";
19481
- const r$1 = ["1", "2"], t = ["nowrap", "wrap", "wrap-reverse"], o$1 = ["start", "center", "end"], p$1 = { size: { type: "enum", className: "rt-r-size", values: r$1, default: "2", responsive: true }, wrap: { type: "enum", className: "rt-r-fw", values: t, responsive: true }, justify: { type: "enum", className: "rt-r-jc", values: o$1, responsive: true }, ...r$d, ...o$6 };
19499
+ const r$1 = ["1", "2"], t = ["nowrap", "wrap", "wrap-reverse"], o$1 = ["start", "center", "end"], p$1 = { size: { type: "enum", className: "rt-r-size", values: r$1, default: "2", responsive: true }, wrap: { type: "enum", className: "rt-r-fw", values: t, responsive: true }, justify: { type: "enum", className: "rt-r-jc", values: o$1, responsive: true }, ...r$c, ...o$5 };
19482
19500
  const r = ["1", "2", "3"], a = ["surface", "ghost"], o = ["auto", "fixed"], n = { size: { type: "enum", className: "rt-r-size", values: r, default: "2", responsive: true }, variant: { type: "enum", className: "rt-variant", values: a, default: "ghost" }, layout: { type: "enum", className: "rt-r-tl", values: o, responsive: true } }, i = ["start", "center", "end", "baseline"], u = { align: { type: "enum", className: "rt-r-va", values: i, parseValue: l, responsive: true } };
19483
19501
  function l(e2) {
19484
19502
  return { baseline: "baseline", start: "top", center: "middle", end: "bottom" }[e2];
19485
19503
  }
19486
- const p = ["start", "center", "end"], f$2 = { justify: { type: "enum", className: "rt-r-ta", values: p, parseValue: c, responsive: true }, ...t$7, ...p$8 };
19504
+ const p = ["start", "center", "end"], f$2 = { justify: { type: "enum", className: "rt-r-ta", values: p, parseValue: c, responsive: true }, ...t$7, ...p$9 };
19487
19505
  function c(e2) {
19488
19506
  return { start: "left", center: "center", end: "right" }[e2];
19489
19507
  }
19490
19508
  const m$1 = reactExports.forwardRef((o2, l2) => {
19491
- const { layout: a2, ...r2 } = n, { className: C, children: c2, layout: y$1, ...i2 } = v(o2, r2, r$a), w = g({ value: y$1, className: n.layout.className, propValues: n.layout.values });
19509
+ const { layout: a2, ...r2 } = n, { className: C, children: c2, layout: y$1, ...i2 } = v(o2, r2, r$9), w = g({ value: y$1, className: n.layout.className, propValues: n.layout.values });
19492
19510
  return reactExports.createElement("div", { ref: l2, className: y("rt-TableRoot", C), ...i2 }, reactExports.createElement(c$1, null, reactExports.createElement("table", { className: y("rt-TableRootTable", w) }, c2)));
19493
19511
  });
19494
19512
  m$1.displayName = "Table.Root";
@@ -19517,12 +19535,12 @@ const R = reactExports.forwardRef((o2, l2) => {
19517
19535
  });
19518
19536
  R.displayName = "Table.RowHeaderCell";
19519
19537
  const m = reactExports.forwardRef((t2, o2) => {
19520
- const { className: r2, ...e2 } = v(t2, r$a);
19538
+ const { className: r2, ...e2 } = v(t2, r$9);
19521
19539
  return reactExports.createElement(Root2, { ...e2, ref: o2, className: y("rt-TabsRoot", r2) });
19522
19540
  });
19523
19541
  m.displayName = "Tabs.Root";
19524
19542
  const b = reactExports.forwardRef((t2, o2) => {
19525
- const { className: r2, color: e2, ...n2 } = v(t2, p$1, r$a);
19543
+ const { className: r2, color: e2, ...n2 } = v(t2, p$1, r$9);
19526
19544
  return reactExports.createElement(List, { "data-accent-color": e2, ...n2, asChild: false, ref: o2, className: y("rt-BaseTabList", "rt-TabsList", r2) });
19527
19545
  });
19528
19546
  b.displayName = "Tabs.List";
@@ -19532,7 +19550,7 @@ const P = reactExports.forwardRef((t2, o2) => {
19532
19550
  });
19533
19551
  P.displayName = "Tabs.Trigger";
19534
19552
  const f = reactExports.forwardRef((t2, o2) => {
19535
- const { className: r2, ...e2 } = v(t2, r$a);
19553
+ const { className: r2, ...e2 } = v(t2, r$9);
19536
19554
  return reactExports.createElement(Content, { ...e2, ref: o2, className: y("rt-TabsContent", r2) });
19537
19555
  });
19538
19556
  f.displayName = "Tabs.Content";
@@ -19610,8 +19628,9 @@ function derivePipelineMetadata(source = {}) {
19610
19628
  const pipelineLabel = source?.pipelineLabel ?? (typeof pipelineSlugFromSource === "string" ? humanizePipelineSlug(pipelineSlugFromSource) : null);
19611
19629
  const pipelineObject = pipelineValue && typeof pipelineValue === "object" && !Array.isArray(pipelineValue) ? pipelineValue : null;
19612
19630
  const pipeline = pipelineObject ?? (typeof pipelineSlugFromSource === "string" ? pipelineSlugFromSource : null);
19631
+ const stringPipeline = typeof pipelineValue === "string" ? pipelineValue : null;
19613
19632
  return {
19614
- pipeline,
19633
+ pipeline: pipeline || stringPipeline,
19615
19634
  pipelineSlug: typeof pipelineSlugFromSource === "string" ? pipelineSlugFromSource : null,
19616
19635
  pipelineLabel: pipelineLabel || null
19617
19636
  };
@@ -19909,13 +19928,70 @@ function useJobListWithUpdates() {
19909
19928
  connectionStatus
19910
19929
  };
19911
19930
  }
19912
- const ALLOWED_STATES = /* @__PURE__ */ new Set(["pending", "running", "done", "failed"]);
19913
- function normalizeTaskState(raw) {
19931
+ const TaskState = Object.freeze({
19932
+ PENDING: "pending",
19933
+ RUNNING: "running",
19934
+ DONE: "done",
19935
+ FAILED: "failed"
19936
+ });
19937
+ const JobStatus = Object.freeze({
19938
+ PENDING: "pending",
19939
+ RUNNING: "running",
19940
+ FAILED: "failed",
19941
+ COMPLETE: "complete"
19942
+ });
19943
+ const JobLocation = Object.freeze({
19944
+ PENDING: "pending",
19945
+ CURRENT: "current",
19946
+ COMPLETE: "complete",
19947
+ REJECTED: "rejected"
19948
+ });
19949
+ new Set(Object.values(TaskState));
19950
+ new Set(Object.values(JobStatus));
19951
+ new Set(Object.values(JobLocation));
19952
+ function normalizeTaskState(state) {
19953
+ if (typeof state !== "string") {
19954
+ return TaskState.PENDING;
19955
+ }
19956
+ const normalized = state.toLowerCase().trim();
19957
+ switch (normalized) {
19958
+ case "error":
19959
+ return TaskState.FAILED;
19960
+ case "succeeded":
19961
+ return TaskState.DONE;
19962
+ case TaskState.PENDING:
19963
+ case TaskState.RUNNING:
19964
+ case TaskState.DONE:
19965
+ case TaskState.FAILED:
19966
+ return normalized;
19967
+ default:
19968
+ return TaskState.PENDING;
19969
+ }
19970
+ }
19971
+ function deriveJobStatusFromTasks(tasks) {
19972
+ if (!Array.isArray(tasks) || tasks.length === 0) {
19973
+ return JobStatus.PENDING;
19974
+ }
19975
+ const normalizedStates = tasks.map((task) => normalizeTaskState(task.state));
19976
+ if (normalizedStates.some((state) => state === TaskState.FAILED)) {
19977
+ return JobStatus.FAILED;
19978
+ }
19979
+ if (normalizedStates.some((state) => state === TaskState.RUNNING)) {
19980
+ return JobStatus.RUNNING;
19981
+ }
19982
+ if (normalizedStates.every((state) => state === TaskState.DONE)) {
19983
+ return JobStatus.COMPLETE;
19984
+ }
19985
+ return JobStatus.PENDING;
19986
+ }
19987
+ function normalizeTaskStateWithWarning(raw) {
19914
19988
  if (!raw || typeof raw !== "string")
19915
19989
  return { state: "pending", warning: "missing_state" };
19916
- const s2 = raw.toLowerCase();
19917
- if (ALLOWED_STATES.has(s2)) return { state: s2 };
19918
- return { state: "pending", warning: `unknown_state:${raw}` };
19990
+ const normalizedState = normalizeTaskState(raw);
19991
+ if (raw !== normalizedState) {
19992
+ return { state: normalizedState, warning: `unknown_state:${raw}` };
19993
+ }
19994
+ return { state: normalizedState };
19919
19995
  }
19920
19996
  function normalizeTasks(rawTasks) {
19921
19997
  if (!rawTasks) return { tasks: {}, warnings: [] };
@@ -19923,7 +19999,7 @@ function normalizeTasks(rawTasks) {
19923
19999
  if (typeof rawTasks === "object" && !Array.isArray(rawTasks)) {
19924
20000
  const tasks = {};
19925
20001
  Object.entries(rawTasks).forEach(([name, t2]) => {
19926
- const ns = normalizeTaskState(t2 && t2.state);
20002
+ const ns = normalizeTaskStateWithWarning(t2 && t2.state);
19927
20003
  if (ns.warning) warnings.push(`${name}:${ns.warning}`);
19928
20004
  const taskObj = {
19929
20005
  name,
@@ -19945,7 +20021,9 @@ function normalizeTasks(rawTasks) {
19945
20021
  logs: [],
19946
20022
  tmp: []
19947
20023
  },
19948
- artifacts: Array.isArray(t2 && t2.artifacts) ? t2.artifacts.slice() : void 0
20024
+ artifacts: Array.isArray(t2 && t2.artifacts) ? t2.artifacts.slice() : void 0,
20025
+ // Preserve tokenUsage if present
20026
+ ...t2 && t2.tokenUsage ? { tokenUsage: t2.tokenUsage } : {}
19949
20027
  };
19950
20028
  tasks[name] = taskObj;
19951
20029
  });
@@ -19955,7 +20033,7 @@ function normalizeTasks(rawTasks) {
19955
20033
  const tasks = {};
19956
20034
  rawTasks.forEach((t2, idx) => {
19957
20035
  const name = t2 && t2.name ? String(t2.name) : `task-${idx}`;
19958
- const ns = normalizeTaskState(t2 && t2.state);
20036
+ const ns = normalizeTaskStateWithWarning(t2 && t2.state);
19959
20037
  if (ns.warning) warnings.push(`${name}:${ns.warning}`);
19960
20038
  tasks[name] = {
19961
20039
  name,
@@ -19967,21 +20045,15 @@ function normalizeTasks(rawTasks) {
19967
20045
  // Preserve stage metadata for DAG visualization
19968
20046
  ...typeof t2?.currentStage === "string" && t2.currentStage.length > 0 ? { currentStage: t2.currentStage } : {},
19969
20047
  ...typeof t2?.failedStage === "string" && t2.failedStage.length > 0 ? { failedStage: t2.failedStage } : {},
19970
- artifacts: Array.isArray(t2 && t2.artifacts) ? t2.artifacts.slice() : void 0
20048
+ artifacts: Array.isArray(t2 && t2.artifacts) ? t2.artifacts.slice() : void 0,
20049
+ // Preserve tokenUsage if present
20050
+ ...t2 && t2.tokenUsage ? { tokenUsage: t2.tokenUsage } : {}
19971
20051
  };
19972
20052
  });
19973
20053
  return { tasks, warnings };
19974
20054
  }
19975
20055
  return { tasks: {}, warnings: ["invalid_tasks_shape"] };
19976
20056
  }
19977
- function deriveStatusFromTasks(tasks) {
19978
- const taskList = Object.values(tasks);
19979
- if (!Array.isArray(taskList) || taskList.length === 0) return "pending";
19980
- if (taskList.some((t2) => t2.state === "failed")) return "failed";
19981
- if (taskList.some((t2) => t2.state === "running")) return "running";
19982
- if (taskList.every((t2) => t2.state === "done")) return "complete";
19983
- return "pending";
19984
- }
19985
20057
  function computeJobSummaryStats(tasks) {
19986
20058
  const taskList = Object.values(tasks);
19987
20059
  const taskCount = taskList.length;
@@ -19989,14 +20061,14 @@ function computeJobSummaryStats(tasks) {
19989
20061
  (acc, t2) => acc + (t2.state === "done" ? 1 : 0),
19990
20062
  0
19991
20063
  );
19992
- const status = deriveStatusFromTasks(tasks);
20064
+ const status = deriveJobStatusFromTasks(Object.values(tasks));
19993
20065
  const progress = taskCount > 0 ? Math.round(doneCount / taskCount * 100) : 0;
19994
20066
  return { status, progress, doneCount, taskCount };
19995
20067
  }
19996
20068
  function adaptJobSummary(apiJob) {
19997
20069
  const id = apiJob.jobId;
19998
20070
  const name = apiJob.title || "";
19999
- const rawTasks = apiJob.tasksStatus;
20071
+ const rawTasks = apiJob.tasks;
20000
20072
  const location = apiJob.location;
20001
20073
  const current = apiJob.current;
20002
20074
  const currentStage = apiJob.currentStage;
@@ -20022,13 +20094,25 @@ function adaptJobSummary(apiJob) {
20022
20094
  const { pipeline, pipelineLabel } = derivePipelineMetadata(apiJob);
20023
20095
  if (pipeline != null) job.pipeline = pipeline;
20024
20096
  if (pipelineLabel != null) job.pipelineLabel = pipelineLabel;
20097
+ if (apiJob.costsSummary) {
20098
+ job.costsSummary = {
20099
+ totalTokens: apiJob.costsSummary.totalTokens || 0,
20100
+ totalInputTokens: apiJob.costsSummary.totalInputTokens || 0,
20101
+ totalOutputTokens: apiJob.costsSummary.totalOutputTokens || 0,
20102
+ totalCost: apiJob.costsSummary.totalCost || 0,
20103
+ totalInputCost: apiJob.costsSummary.totalInputCost || 0,
20104
+ totalOutputCost: apiJob.costsSummary.totalOutputCost || 0
20105
+ };
20106
+ job.totalCost = job.costsSummary.totalCost;
20107
+ job.totalTokens = job.costsSummary.totalTokens;
20108
+ }
20025
20109
  if (warnings.length > 0) job.__warnings = warnings;
20026
20110
  return job;
20027
20111
  }
20028
20112
  function adaptJobDetail(apiDetail) {
20029
20113
  const id = apiDetail.jobId;
20030
20114
  const name = apiDetail.title || "";
20031
- const rawTasks = apiDetail.tasksStatus;
20115
+ const rawTasks = apiDetail.tasks;
20032
20116
  const location = apiDetail.location;
20033
20117
  const current = apiDetail.current;
20034
20118
  const currentStage = apiDetail.currentStage;
@@ -20054,104 +20138,12 @@ function adaptJobDetail(apiDetail) {
20054
20138
  const { pipeline, pipelineLabel } = derivePipelineMetadata(apiDetail);
20055
20139
  if (pipeline != null) detail.pipeline = pipeline;
20056
20140
  if (pipelineLabel != null) detail.pipelineLabel = pipelineLabel;
20141
+ if (apiDetail.costs) {
20142
+ detail.costs = apiDetail.costs;
20143
+ }
20057
20144
  if (warnings.length > 0) detail.__warnings = warnings;
20058
20145
  return detail;
20059
20146
  }
20060
- function normalizeState(state) {
20061
- switch (state) {
20062
- case "done":
20063
- return "completed";
20064
- case "failed":
20065
- case "error":
20066
- return "error";
20067
- case "pending":
20068
- case "running":
20069
- case "current":
20070
- case "completed":
20071
- case "rejected":
20072
- return state;
20073
- default:
20074
- return state;
20075
- }
20076
- }
20077
- function taskDisplayDurationMs(task, now = Date.now()) {
20078
- const { state, startedAt, endedAt, executionTime, executionTimeMs } = task;
20079
- const normalizedState = normalizeState(state);
20080
- switch (normalizedState) {
20081
- case "pending":
20082
- return 0;
20083
- case "running":
20084
- case "current":
20085
- if (!startedAt) {
20086
- return 0;
20087
- }
20088
- const startTime = Date.parse(startedAt);
20089
- return Math.max(0, now - startTime);
20090
- case "completed":
20091
- const execTime = executionTimeMs != null ? executionTimeMs : executionTime;
20092
- if (typeof execTime === "number" && execTime >= 0) {
20093
- return execTime;
20094
- }
20095
- if (!startedAt) {
20096
- return 0;
20097
- }
20098
- const completedStartTime = Date.parse(startedAt);
20099
- const endTime = endedAt ? Date.parse(endedAt) : now;
20100
- return Math.max(0, endTime - completedStartTime);
20101
- case "rejected":
20102
- return 0;
20103
- default:
20104
- return 0;
20105
- }
20106
- }
20107
- function jobCumulativeDurationMs(job, now = Date.now()) {
20108
- const { tasks } = job;
20109
- if (!tasks) {
20110
- return 0;
20111
- }
20112
- let taskList;
20113
- if (Array.isArray(tasks)) {
20114
- taskList = tasks;
20115
- } else if (typeof tasks === "object") {
20116
- taskList = Object.values(tasks);
20117
- } else {
20118
- return 0;
20119
- }
20120
- return taskList.reduce((total, task) => {
20121
- return total + taskDisplayDurationMs(task, now);
20122
- }, 0);
20123
- }
20124
- function fmtDuration(ms) {
20125
- if (ms <= 0) return "0s";
20126
- const seconds = Math.floor(ms / 1e3);
20127
- const minutes = Math.floor(seconds / 60);
20128
- const hours = Math.floor(minutes / 60);
20129
- if (hours > 0) {
20130
- const remainingMinutes = minutes % 60;
20131
- const remainingSeconds = seconds % 60;
20132
- if (remainingSeconds > 0) {
20133
- return `${hours}h ${remainingMinutes}m ${remainingSeconds}s`;
20134
- } else {
20135
- return `${hours}h ${remainingMinutes}m`;
20136
- }
20137
- } else if (minutes > 0) {
20138
- return `${minutes}m ${seconds % 60}s`;
20139
- } else {
20140
- return `${seconds}s`;
20141
- }
20142
- }
20143
- function useTicker(intervalMs = 1e3) {
20144
- const [now, setNow] = reactExports.useState(() => Date.now());
20145
- reactExports.useEffect(() => {
20146
- const intervalId = setInterval(() => {
20147
- setNow(Date.now());
20148
- }, intervalMs);
20149
- return () => {
20150
- clearInterval(intervalId);
20151
- };
20152
- }, [intervalMs]);
20153
- return now;
20154
- }
20155
20147
  /**
20156
20148
  * @license lucide-react v0.544.0 - ISC
20157
20149
  *
@@ -20257,19 +20249,20 @@ const createLucideIcon = (iconName, iconNode) => {
20257
20249
  * This source code is licensed under the ISC license.
20258
20250
  * See the LICENSE file in the root directory of this source tree.
20259
20251
  */
20260
- const __iconNode$3 = [
20261
- ["path", { d: "m12 19-7-7 7-7", key: "1l729n" }],
20262
- ["path", { d: "M19 12H5", key: "x3x0zl" }]
20263
- ];
20264
- const ArrowLeft = createLucideIcon("arrow-left", __iconNode$3);
20252
+ const __iconNode$3 = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
20253
+ const ChevronRight = createLucideIcon("chevron-right", __iconNode$3);
20265
20254
  /**
20266
20255
  * @license lucide-react v0.544.0 - ISC
20267
20256
  *
20268
20257
  * This source code is licensed under the ISC license.
20269
20258
  * See the LICENSE file in the root directory of this source tree.
20270
20259
  */
20271
- const __iconNode$2 = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
20272
- const ChevronRight = createLucideIcon("chevron-right", __iconNode$2);
20260
+ const __iconNode$2 = [
20261
+ ["path", { d: "m18 16 4-4-4-4", key: "1inbqp" }],
20262
+ ["path", { d: "m6 8-4 4 4 4", key: "15zrgr" }],
20263
+ ["path", { d: "m14.5 4-5 16", key: "e7oirm" }]
20264
+ ];
20265
+ const CodeXml = createLucideIcon("code-xml", __iconNode$2);
20273
20266
  /**
20274
20267
  * @license lucide-react v0.544.0 - ISC
20275
20268
  *
@@ -20277,16 +20270,12 @@ const ChevronRight = createLucideIcon("chevron-right", __iconNode$2);
20277
20270
  * See the LICENSE file in the root directory of this source tree.
20278
20271
  */
20279
20272
  const __iconNode$1 = [
20280
- ["path", { d: "M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8", key: "5wwlr5" }],
20281
- [
20282
- "path",
20283
- {
20284
- d: "M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z",
20285
- key: "r6nss1"
20286
- }
20287
- ]
20273
+ ["path", { d: "M10 2h4", key: "n1abiw" }],
20274
+ ["path", { d: "M12 14v-4", key: "1evpnu" }],
20275
+ ["path", { d: "M4 13a8 8 0 0 1 8-7 8 8 0 1 1-5.3 14L4 17.6", key: "1ts96g" }],
20276
+ ["path", { d: "M9 17H4v5", key: "8t5av" }]
20288
20277
  ];
20289
- const House = createLucideIcon("house", __iconNode$1);
20278
+ const TimerReset = createLucideIcon("timer-reset", __iconNode$1);
20290
20279
  /**
20291
20280
  * @license lucide-react v0.544.0 - ISC
20292
20281
  *
@@ -20294,12 +20283,83 @@ const House = createLucideIcon("house", __iconNode$1);
20294
20283
  * See the LICENSE file in the root directory of this source tree.
20295
20284
  */
20296
20285
  const __iconNode = [
20297
- ["path", { d: "M10 2h4", key: "n1abiw" }],
20298
- ["path", { d: "M12 14v-4", key: "1evpnu" }],
20299
- ["path", { d: "M4 13a8 8 0 0 1 8-7 8 8 0 1 1-5.3 14L4 17.6", key: "1ts96g" }],
20300
- ["path", { d: "M9 17H4v5", key: "8t5av" }]
20286
+ ["path", { d: "M12 3v12", key: "1x0j5s" }],
20287
+ ["path", { d: "m17 8-5-5-5 5", key: "7q97r8" }],
20288
+ ["path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4", key: "ih7n3h" }]
20301
20289
  ];
20302
- const TimerReset = createLucideIcon("timer-reset", __iconNode);
20290
+ const Upload = createLucideIcon("upload", __iconNode);
20291
+ function normalizeState(state) {
20292
+ const canonicalState = normalizeTaskState(state);
20293
+ if (canonicalState === TaskState.DONE) {
20294
+ return "completed";
20295
+ }
20296
+ return canonicalState;
20297
+ }
20298
+ function taskDisplayDurationMs(task, now = Date.now()) {
20299
+ const { state, startedAt, endedAt, executionTime, executionTimeMs } = task;
20300
+ const normalizedState = normalizeState(state);
20301
+ switch (normalizedState) {
20302
+ case TaskState.PENDING:
20303
+ return 0;
20304
+ case TaskState.RUNNING:
20305
+ if (!startedAt) {
20306
+ return 0;
20307
+ }
20308
+ const startTime = Date.parse(startedAt);
20309
+ return Math.max(0, now - startTime);
20310
+ case "completed":
20311
+ const execTime = executionTimeMs != null ? executionTimeMs : executionTime;
20312
+ if (typeof execTime === "number" && execTime >= 0) {
20313
+ return execTime;
20314
+ }
20315
+ if (!startedAt) {
20316
+ return 0;
20317
+ }
20318
+ const completedStartTime = Date.parse(startedAt);
20319
+ const endTime = endedAt ? Date.parse(endedAt) : now;
20320
+ return Math.max(0, endTime - completedStartTime);
20321
+ case TaskState.FAILED:
20322
+ return 0;
20323
+ default:
20324
+ return 0;
20325
+ }
20326
+ }
20327
+ function jobCumulativeDurationMs(job, now = Date.now()) {
20328
+ const { tasks } = job;
20329
+ if (!tasks) {
20330
+ return 0;
20331
+ }
20332
+ let taskList;
20333
+ if (Array.isArray(tasks)) {
20334
+ taskList = tasks;
20335
+ } else if (typeof tasks === "object") {
20336
+ taskList = Object.values(tasks);
20337
+ } else {
20338
+ return 0;
20339
+ }
20340
+ return taskList.reduce((total, task) => {
20341
+ return total + taskDisplayDurationMs(task, now);
20342
+ }, 0);
20343
+ }
20344
+ function fmtDuration(ms) {
20345
+ if (ms <= 0) return "0s";
20346
+ const seconds = Math.floor(ms / 1e3);
20347
+ const minutes = Math.floor(seconds / 60);
20348
+ const hours = Math.floor(minutes / 60);
20349
+ if (hours > 0) {
20350
+ const remainingMinutes = minutes % 60;
20351
+ const remainingSeconds = seconds % 60;
20352
+ if (remainingSeconds > 0) {
20353
+ return `${hours}h ${remainingMinutes}m ${remainingSeconds}s`;
20354
+ } else {
20355
+ return `${hours}h ${remainingMinutes}m`;
20356
+ }
20357
+ } else if (minutes > 0) {
20358
+ return `${minutes}m ${seconds % 60}s`;
20359
+ } else {
20360
+ return `${seconds}s`;
20361
+ }
20362
+ }
20303
20363
  const countCompleted = (job) => {
20304
20364
  const list = Array.isArray(job?.tasks) ? job.tasks : Object.values(job?.tasks || {});
20305
20365
  return list.filter((t2) => t2?.state === "done" || t2?.state === "completed").length;
@@ -20321,42 +20381,209 @@ function Badge({ children, intent = "gray", className = "", ...props }) {
20321
20381
  }
20322
20382
  const statusBadge = (status) => {
20323
20383
  switch (status) {
20324
- case "running":
20325
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "info", "aria-label": "Running", children: "Running" });
20326
- case "failed":
20327
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "error", "aria-label": "Failed", children: "Failed" });
20328
- case "completed":
20329
- case "complete":
20330
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "success", "aria-label": "Completed", children: "Completed" });
20331
- case "pending":
20332
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "default", "aria-label": "Pending", children: "Pending" });
20384
+ case TaskState.RUNNING:
20385
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { intent: "blue", "aria-label": "Running", children: "Running" });
20386
+ case TaskState.FAILED:
20387
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { intent: "red", "aria-label": "Failed", children: "Failed" });
20388
+ case TaskState.DONE:
20389
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { intent: "green", "aria-label": "Completed", children: "Completed" });
20390
+ case TaskState.PENDING:
20391
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { intent: "gray", "aria-label": "Pending", children: "Pending" });
20333
20392
  default:
20334
20393
  return null;
20335
20394
  }
20336
20395
  };
20337
20396
  const progressClasses = (status) => {
20338
20397
  switch (status) {
20339
- case "running":
20398
+ case TaskState.RUNNING:
20340
20399
  return "bg-info/20 [&>div]:bg-info";
20341
- case "failed":
20400
+ case TaskState.FAILED:
20342
20401
  return "bg-destructive/20 [&>div]:bg-destructive";
20343
- case "completed":
20402
+ case TaskState.DONE:
20344
20403
  return "bg-success/20 [&>div]:bg-success";
20345
20404
  default:
20346
20405
  return "bg-muted [&>div]:bg-muted-foreground";
20347
20406
  }
20348
20407
  };
20349
- function JobTable({
20350
- jobs,
20351
- pipeline,
20352
- onOpenJob,
20353
- overallElapsed,
20354
- now
20408
+ const offset = Date.now() - performance.now();
20409
+ let currentNow = Math.floor(performance.now() + offset);
20410
+ const listeners = /* @__PURE__ */ new Set();
20411
+ const cadenceHints = /* @__PURE__ */ new Map();
20412
+ let timerId = null;
20413
+ let activeIntervalMs = 1e3;
20414
+ let isBackground = false;
20415
+ function subscribe(listener) {
20416
+ listeners.add(listener);
20417
+ if (listeners.size === 1) {
20418
+ startTimer();
20419
+ }
20420
+ return () => {
20421
+ listeners.delete(listener);
20422
+ if (listeners.size === 0) {
20423
+ stopTimer();
20424
+ }
20425
+ };
20426
+ }
20427
+ function getSnapshot() {
20428
+ return currentNow;
20429
+ }
20430
+ function getServerSnapshot() {
20431
+ return Date.now();
20432
+ }
20433
+ function addCadenceHint(id, ms) {
20434
+ cadenceHints.set(id, ms);
20435
+ recalculateInterval();
20436
+ }
20437
+ function removeCadenceHint(id) {
20438
+ cadenceHints.delete(id);
20439
+ recalculateInterval();
20440
+ }
20441
+ function recalculateInterval() {
20442
+ const minCadence = Math.min(...cadenceHints.values(), 1e3);
20443
+ const newIntervalMs = isBackground ? Math.max(minCadence, 6e4) : minCadence;
20444
+ if (newIntervalMs !== activeIntervalMs) {
20445
+ activeIntervalMs = newIntervalMs;
20446
+ if (listeners.size > 0) {
20447
+ stopTimer();
20448
+ startTimer();
20449
+ }
20450
+ }
20451
+ }
20452
+ function startTimer() {
20453
+ if (timerId !== null) return;
20454
+ const tick = () => {
20455
+ currentNow = Math.floor(performance.now() + offset);
20456
+ listeners.forEach((listener) => {
20457
+ try {
20458
+ listener();
20459
+ } catch (error) {
20460
+ console.error("Error in time store listener:", error);
20461
+ }
20462
+ });
20463
+ };
20464
+ if (activeIntervalMs >= 6e4) {
20465
+ const now = Date.now();
20466
+ const nextMinuteBoundary = Math.ceil(now / 6e4) * 6e4;
20467
+ const initialDelay = nextMinuteBoundary - now;
20468
+ timerId = setTimeout(() => {
20469
+ tick();
20470
+ timerId = setInterval(tick, 6e4);
20471
+ }, initialDelay);
20472
+ } else {
20473
+ timerId = setInterval(tick, activeIntervalMs);
20474
+ }
20475
+ }
20476
+ function stopTimer() {
20477
+ if (timerId !== null) {
20478
+ clearTimeout(timerId);
20479
+ clearInterval(timerId);
20480
+ timerId = null;
20481
+ }
20482
+ }
20483
+ function handleVisibilityChange() {
20484
+ const wasBackground = isBackground;
20485
+ isBackground = document.visibilityState === "hidden";
20486
+ if (wasBackground !== isBackground) {
20487
+ recalculateInterval();
20488
+ }
20489
+ }
20490
+ if (typeof document !== "undefined") {
20491
+ document.addEventListener("visibilitychange", handleVisibilityChange);
20492
+ }
20493
+ function TimerText({
20494
+ startMs,
20495
+ endMs = null,
20496
+ granularity = "second",
20497
+ format = fmtDuration,
20498
+ className
20355
20499
  }) {
20500
+ const id = reactExports.useId();
20501
+ const now = reactExports.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
20502
+ const [displayText, setDisplayText] = reactExports.useState(() => {
20503
+ if (!startMs) return "—";
20504
+ const initialEnd = endMs ?? Date.now();
20505
+ const elapsed = Math.max(0, initialEnd - startMs);
20506
+ return format(elapsed);
20507
+ });
20508
+ reactExports.useEffect(() => {
20509
+ if (!startMs) return;
20510
+ const cadenceMs = granularity === "second" ? 1e3 : 6e4;
20511
+ addCadenceHint(id, cadenceMs);
20512
+ return () => {
20513
+ removeCadenceHint(id);
20514
+ };
20515
+ }, [id, granularity, startMs]);
20516
+ reactExports.useEffect(() => {
20517
+ if (!startMs) return;
20518
+ if (endMs !== null) return;
20519
+ const elapsed = Math.max(0, now - startMs);
20520
+ setDisplayText(format(elapsed));
20521
+ }, [now, startMs, endMs, format]);
20522
+ reactExports.useEffect(() => {
20523
+ if (!startMs || endMs === null) return;
20524
+ const elapsed = Math.max(0, endMs - startMs);
20525
+ setDisplayText(format(elapsed));
20526
+ }, [startMs, endMs, format]);
20527
+ if (!startMs) {
20528
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className, children: "—" });
20529
+ }
20530
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className, children: displayText });
20531
+ }
20532
+ function LiveText({ compute, cadenceMs = 1e4, className }) {
20533
+ const id = reactExports.useId();
20534
+ const now = reactExports.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
20535
+ const [displayText, setDisplayText] = reactExports.useState(() => {
20536
+ return compute(Date.now());
20537
+ });
20538
+ reactExports.useEffect(() => {
20539
+ addCadenceHint(id, cadenceMs);
20540
+ return () => {
20541
+ removeCadenceHint(id);
20542
+ };
20543
+ }, [id, cadenceMs]);
20544
+ reactExports.useEffect(() => {
20545
+ setDisplayText(compute(now));
20546
+ }, [now, compute]);
20547
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className, children: displayText });
20548
+ }
20549
+ function toMilliseconds(timestamp) {
20550
+ if (timestamp === null || timestamp === void 0) {
20551
+ return null;
20552
+ }
20553
+ if (typeof timestamp === "number") {
20554
+ return isNaN(timestamp) ? null : timestamp;
20555
+ }
20556
+ if (typeof timestamp === "string") {
20557
+ const parsed = Date.parse(timestamp);
20558
+ return isNaN(parsed) ? null : parsed;
20559
+ }
20560
+ return null;
20561
+ }
20562
+ function taskToTimerProps(task) {
20563
+ return {
20564
+ startMs: toMilliseconds(task?.startedAt),
20565
+ endMs: toMilliseconds(task?.endedAt)
20566
+ };
20567
+ }
20568
+ function formatCurrency4$1(x) {
20569
+ if (typeof x !== "number" || x === 0) return "$0.0000";
20570
+ const formatted = x.toFixed(4);
20571
+ return `$${formatted.replace(/\.?0+$/, "")}`;
20572
+ }
20573
+ function formatTokensCompact$1(n2) {
20574
+ if (typeof n2 !== "number" || n2 === 0) return "0 tok";
20575
+ if (n2 >= 1e6) {
20576
+ return `${(n2 / 1e6).toFixed(1).replace(/\.0$/, "")}M tok`;
20577
+ } else if (n2 >= 1e3) {
20578
+ return `${(n2 / 1e3).toFixed(1).replace(/\.0$/, "")}k tok`;
20579
+ }
20580
+ return `${n2} tok`;
20581
+ }
20582
+ function JobTable({ jobs, pipeline, onOpenJob }) {
20356
20583
  if (jobs.length === 0) {
20357
- return /* @__PURE__ */ jsxRuntimeExports.jsx(p$6, { className: "p-6", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "2", className: "text-slate-600", children: "No jobs to show here yet." }) });
20584
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(p$7, { className: "p-6", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "2", className: "text-slate-600", children: "No jobs to show here yet." }) });
20358
20585
  }
20359
- return /* @__PURE__ */ jsxRuntimeExports.jsx(p$6, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(m$1, { radius: "none", children: [
20586
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(p$7, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(m$1, { radius: "none", children: [
20360
20587
  /* @__PURE__ */ jsxRuntimeExports.jsx(d, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(P$1, { children: [
20361
20588
  /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Job Name" }),
20362
20589
  /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Pipeline" }),
@@ -20364,11 +20591,12 @@ function JobTable({
20364
20591
  /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Current Task" }),
20365
20592
  /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Progress" }),
20366
20593
  /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Tasks" }),
20594
+ /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Cost" }),
20367
20595
  /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Duration" }),
20368
20596
  /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { className: "w-12" })
20369
20597
  ] }) }),
20370
20598
  /* @__PURE__ */ jsxRuntimeExports.jsx(b$1, { children: jobs.map((job) => {
20371
- const jobTitle = job.title || job.name;
20599
+ const jobTitle = job.name;
20372
20600
  const taskById = Array.isArray(job.tasks) ? Object.fromEntries(
20373
20601
  (job.tasks || []).map((t2) => {
20374
20602
  if (typeof t2 === "string") return [t2, { id: t2, name: t2 }];
@@ -20376,13 +20604,14 @@ function JobTable({
20376
20604
  })
20377
20605
  ) : job.tasks || {};
20378
20606
  const currentTask = job.current ? taskById[job.current] : void 0;
20379
- const currentElapsedMs = currentTask ? taskDisplayDurationMs(currentTask, now) : 0;
20380
20607
  const totalCompleted = countCompleted(job);
20381
20608
  const totalTasks = pipeline?.tasks?.length ?? (Array.isArray(job.tasks) ? job.tasks.length : Object.keys(job.tasks || {}).length);
20382
20609
  const progress = Number.isFinite(job.progress) ? Math.round(job.progress) : 0;
20383
- const duration = overallElapsed(job);
20384
20610
  const currentTaskName = currentTask ? currentTask.name ?? currentTask.id ?? job.current : void 0;
20385
20611
  const currentTaskConfig = job.current && (currentTask?.config || pipeline?.taskConfig?.[job.current]) || {};
20612
+ const costsSummary = job.costsSummary || {};
20613
+ const totalCost = job.totalCost || costsSummary.totalCost || 0;
20614
+ const totalTokens = job.totalTokens || costsSummary.totalTokens || 0;
20386
20615
  const hasValidId = Boolean(job.id);
20387
20616
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
20388
20617
  P$1,
@@ -20394,24 +20623,38 @@ function JobTable({
20394
20623
  "aria-label": hasValidId ? `Open ${jobTitle}` : `${jobTitle} - No valid job ID, cannot open details`,
20395
20624
  title: hasValidId ? void 0 : "This job cannot be opened because it lacks a valid ID",
20396
20625
  children: [
20397
- /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$3, { direction: "column", gap: "1", children: [
20398
- /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "2", weight: "medium", className: "text-slate-900", children: jobTitle }),
20399
- /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "1", className: "text-slate-500", children: job.id })
20626
+ /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$4, { direction: "column", gap: "1", children: [
20627
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "2", weight: "medium", className: "text-slate-900", children: jobTitle }),
20628
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "1", className: "text-slate-500", children: job.id })
20400
20629
  ] }) }),
20401
- /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$3, { direction: "column", gap: "1", children: [
20402
- /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "2", className: "text-slate-900", children: job.pipelineLabel || job.pipeline || "—" }),
20403
- job.pipelineLabel && job.pipeline && /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "1", className: "text-slate-500", children: job.pipeline })
20630
+ /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$4, { direction: "column", gap: "1", children: [
20631
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "2", className: "text-slate-900", children: job.pipelineLabel || job.pipeline || "—" }),
20632
+ job.pipelineLabel && job.pipeline && /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "1", className: "text-slate-500", children: job.pipeline })
20404
20633
  ] }) }),
20405
20634
  /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: statusBadge(job.status) }),
20406
- /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$3, { direction: "column", gap: "1", children: [
20407
- /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "2", className: "text-slate-700", children: currentTaskName ? currentTaskName : job.status === "completed" ? "—" : job.current ?? "—" }),
20408
- currentTask && /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "1", className: "text-slate-500", children: [
20635
+ /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$4, { direction: "column", gap: "1", children: [
20636
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "2", className: "text-slate-700", children: currentTaskName ? currentTaskName : job.status === "done" ? "—" : job.current ?? "—" }),
20637
+ currentTask && /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "1", className: "text-slate-500", children: [
20409
20638
  currentTaskConfig?.model || currentTask?.model,
20410
20639
  currentTaskConfig?.temperature != null || currentTask?.temperature != null ? `temp ${currentTaskConfig?.temperature ?? currentTask?.temperature}` : null,
20411
- currentElapsedMs > 0 ? fmtDuration(currentElapsedMs) : null
20412
- ].filter(Boolean).join(" · ") })
20640
+ (() => {
20641
+ const { startMs, endMs } = taskToTimerProps(currentTask);
20642
+ return startMs ? /* @__PURE__ */ jsxRuntimeExports.jsx(
20643
+ TimerText,
20644
+ {
20645
+ startMs,
20646
+ endMs,
20647
+ granularity: "second",
20648
+ className: "text-slate-500"
20649
+ }
20650
+ ) : null;
20651
+ })()
20652
+ ].filter(Boolean).map((item, index2) => /* @__PURE__ */ jsxRuntimeExports.jsxs(React.Fragment, { children: [
20653
+ typeof item === "string" ? item : item,
20654
+ index2 < 2 && " · "
20655
+ ] }, index2)) })
20413
20656
  ] }) }),
20414
- /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$3, { direction: "column", gap: "2", className: "w-32", children: [
20657
+ /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$4, { direction: "column", gap: "2", className: "w-32", children: [
20415
20658
  /* @__PURE__ */ jsxRuntimeExports.jsx(
20416
20659
  Progress,
20417
20660
  {
@@ -20420,22 +20663,33 @@ function JobTable({
20420
20663
  "aria-label": `Progress ${progress}%`
20421
20664
  }
20422
20665
  ),
20423
- /* @__PURE__ */ jsxRuntimeExports.jsxs(p$a, { size: "1", className: "text-slate-500", children: [
20666
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(p$b, { size: "1", className: "text-slate-500", children: [
20424
20667
  progress,
20425
20668
  "%"
20426
20669
  ] })
20427
20670
  ] }) }),
20428
- /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$a, { size: "2", className: "text-slate-700", children: [
20671
+ /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$b, { size: "2", className: "text-slate-700", children: [
20429
20672
  totalCompleted,
20430
20673
  " of ",
20431
20674
  totalTasks
20432
20675
  ] }) }),
20433
- /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$3, { align: "center", gap: "1", children: [
20676
+ /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$4, { direction: "column", gap: "1", children: [
20677
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "2", className: "text-slate-700", children: totalCost > 0 ? formatCurrency4$1(totalCost) : "—" }),
20678
+ totalTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "1", className: "text-slate-500", children: formatTokensCompact$1(totalTokens) })
20679
+ ] }) }),
20680
+ /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$4, { align: "center", gap: "1", children: [
20434
20681
  /* @__PURE__ */ jsxRuntimeExports.jsx(TimerReset, { className: "h-3 w-3 text-slate-500" }),
20435
- /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "2", className: "text-slate-700", children: fmtDuration(duration) })
20682
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
20683
+ LiveText,
20684
+ {
20685
+ cadenceMs: 1e4,
20686
+ compute: (now) => fmtDuration(jobCumulativeDurationMs(job, now)),
20687
+ className: "text-slate-700"
20688
+ }
20689
+ )
20436
20690
  ] }) }),
20437
20691
  /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
20438
- o$3,
20692
+ o$2,
20439
20693
  {
20440
20694
  variant: "ghost",
20441
20695
  size: "1",
@@ -20462,7 +20716,7 @@ function Button({
20462
20716
  const color = variant === "destructive" ? "red" : void 0;
20463
20717
  const combinedClassName = `transition-colors duration-200 ${className}`;
20464
20718
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
20465
- o$3,
20719
+ o$2,
20466
20720
  {
20467
20721
  variant: radixVariant,
20468
20722
  size: radixSize,
@@ -20472,34 +20726,93 @@ function Button({
20472
20726
  }
20473
20727
  );
20474
20728
  }
20475
- const normalizeUploadError = (err) => {
20476
- if (!err) return "Upload failed";
20477
- if (typeof err === "string") return err;
20478
- if (err instanceof Error) return err.message;
20479
- if (typeof err === "object") {
20480
- if ("message" in err && err.message) return String(err.message);
20481
- if ("error" in err && err.error) return String(err.error);
20729
+ const Logo = () => /* @__PURE__ */ jsxRuntimeExports.jsx(
20730
+ "svg",
20731
+ {
20732
+ xmlns: "http://www.w3.org/2000/svg",
20733
+ width: "100%",
20734
+ height: "100%",
20735
+ viewBox: "0 0 1200 1200",
20736
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
20737
+ "path",
20738
+ {
20739
+ fill: "#009966",
20740
+ d: "M406.13 988.31c-17.297 75.047-84.562 131.11-164.86 131.11-93.375 0-169.18-75.797-169.18-169.18s75.797-169.18 169.18-169.18 169.18 75.797 169.18 169.18v1.266h447.74v-167.9H671.63c-14.859 0-29.062-5.906-39.562-16.406s-16.406-24.703-16.406-39.562v-37.312h-317.16c-10.312 0-18.656-8.344-18.656-18.656V355.78h-147.94c-14.859 0-29.062-5.906-39.562-16.406s-16.406-24.75-16.406-39.562v-111.94c0-14.859 5.906-29.109 16.406-39.562 10.5-10.5 24.75-16.406 39.562-16.406h391.78c14.859 0 29.062 5.906 39.562 16.406s16.406 24.75 16.406 39.562v37.312h202.4c9.281-84.609 81.094-150.52 168.14-150.52 93.375 0 169.18 75.797 169.18 169.18s-75.797 169.18-169.18 169.18c-87.047 0-158.86-65.906-168.14-150.52h-202.4v37.312c0 14.859-5.906 29.062-16.406 39.562s-24.75 16.406-39.562 16.406h-206.53v297.24h298.5v-37.312c0-14.859 5.906-29.062 16.406-39.562s24.703-16.406 39.562-16.406h392.63c14.859 0 29.062 5.906 39.562 16.406s16.406 24.703 16.406 39.562v111.94c0 14.859-5.906 29.062-16.406 39.562s-24.75 16.406-39.562 16.406h-168.74v186.56c0 10.312-8.344 18.656-18.656 18.656h-466.4c-1.5 0-2.906-.187-4.312-.516zM225.19 262.45h18.656c10.312 0 18.656-8.344 18.656-18.656s-8.344-18.656-18.656-18.656H225.19c-10.312 0-18.656 8.344-18.656 18.656s8.344 18.656 18.656 18.656zm186.56 0h18.656c10.312 0 18.656-8.344 18.656-18.656s-8.344-18.656-18.656-18.656H411.75c-10.312 0-18.656 8.344-18.656 18.656s8.344 18.656 18.656 18.656zm-93.281 0h18.656c10.312 0 18.656-8.344 18.656-18.656s-8.344-18.656-18.656-18.656h-18.656c-10.312 0-18.656 8.344-18.656 18.656s8.344 18.656 18.656 18.656zm616.18 0h85.5c10.312 0 18.656-8.344 18.656-18.656s-8.344-18.656-18.656-18.656h-85.5l29.062-22.594c8.109-6.328 9.609-18.047 3.281-26.156s-18.047-9.609-26.156-3.281l-71.953 55.969a18.61 18.61 0 0 0 0 29.438l71.953 55.969c8.109 6.328 19.875 4.875 26.156-3.281 6.328-8.109 4.875-19.875-3.281-26.203l-29.062-22.594zm-779.95 696.66l50.391 50.391c7.266 7.313 19.078 7.313 26.391 0l100.73-100.73c7.266-7.266 7.266-19.078 0-26.391-7.266-7.266-19.078-7.266-26.391 0l-87.562 87.562-37.172-37.172c-7.266-7.266-19.078-7.266-26.391 0-7.266 7.266-7.266 19.078 0 26.391zm797.21-268.78h18.656c10.312 0 18.656-8.344 18.656-18.656s-8.344-18.656-18.656-18.656h-18.656c-10.312 0-18.656 8.344-18.656 18.656s8.344 18.656 18.656 18.656zm-186.56 0h18.656c10.312 0 18.656-8.344 18.656-18.656s-8.344-18.656-18.656-18.656h-18.656c-10.312 0-18.656 8.344-18.656 18.656s8.344 18.656 18.656 18.656zm93.281 0h18.656c10.312 0 18.656-8.344 18.656-18.656s-8.344-18.656-18.656-18.656H858.63c-10.312 0-18.656 8.344-18.656 18.656s8.344 18.656 18.656 18.656z",
20741
+ fillRule: "evenodd"
20742
+ }
20743
+ )
20482
20744
  }
20483
- return "Upload failed";
20484
- };
20485
- function UploadSeed({ onUploadSuccess }) {
20486
- const fileInputRef = React.useRef(null);
20487
- const [showSample, setShowSample] = reactExports.useState(false);
20488
- const [error, setError] = reactExports.useState(null);
20489
- const sampleSeed = {
20490
- name: "some-name",
20491
- pipeline: "content-generation",
20492
- data: {
20493
- type: "some-type",
20494
- contentType: "blog-post",
20495
- targetAudience: "software-developers",
20496
- tone: "professional-yet-accessible",
20497
- length: "1500-2000 words",
20498
- outputFormat: "blog-post"
20499
- }
20500
- };
20501
- const handleFileChange = async (event) => {
20502
- const files = event.target.files;
20745
+ );
20746
+ function PageSubheader({
20747
+ breadcrumbs = [],
20748
+ children,
20749
+ maxWidth = "max-w-7xl"
20750
+ }) {
20751
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
20752
+ p$7,
20753
+ {
20754
+ role: "region",
20755
+ "aria-label": "Page header",
20756
+ className: "border-b border-gray-300 bg-gray-1/60 backdrop-blur supports-[backdrop-filter]:bg-gray-1/40 mb-4",
20757
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
20758
+ p$4,
20759
+ {
20760
+ align: "center",
20761
+ justify: "between",
20762
+ className: `mx-auto w-full ${maxWidth} px-1.5 py-3`,
20763
+ gap: "4",
20764
+ wrap: "wrap",
20765
+ children: [
20766
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$4, { align: "center", gap: "3", className: "min-w-0 flex-1", children: breadcrumbs.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("nav", { "aria-label": "Breadcrumb", className: "shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx("ol", { className: "flex items-center gap-2 text-sm text-gray-11", children: breadcrumbs.map((crumb, index2) => {
20767
+ const isLast = index2 === breadcrumbs.length - 1;
20768
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(React.Fragment, { children: [
20769
+ index2 > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
20770
+ ChevronRight,
20771
+ {
20772
+ className: "h-4 w-4 text-gray-9",
20773
+ "aria-hidden": "true"
20774
+ }
20775
+ ),
20776
+ crumb.href ? /* @__PURE__ */ jsxRuntimeExports.jsx(
20777
+ Link,
20778
+ {
20779
+ to: crumb.href,
20780
+ className: "hover:text-gray-12 transition-colors underline-offset-4 hover:underline",
20781
+ children: crumb.label
20782
+ }
20783
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
20784
+ p$b,
20785
+ {
20786
+ as: "span",
20787
+ "aria-current": isLast ? "page" : void 0,
20788
+ className: isLast ? "text-gray-12 font-medium" : "",
20789
+ children: crumb.label
20790
+ }
20791
+ )
20792
+ ] }, index2);
20793
+ }) }) }) }),
20794
+ children && /* @__PURE__ */ jsxRuntimeExports.jsx(p$4, { align: "center", gap: "3", className: "shrink-0", children })
20795
+ ]
20796
+ }
20797
+ )
20798
+ }
20799
+ );
20800
+ }
20801
+ const normalizeUploadError = (err) => {
20802
+ if (!err) return "Upload failed";
20803
+ if (typeof err === "string") return err;
20804
+ if (err instanceof Error) return err.message;
20805
+ if (typeof err === "object") {
20806
+ if ("message" in err && err.message) return String(err.message);
20807
+ if ("error" in err && err.error) return String(err.error);
20808
+ }
20809
+ return "Upload failed";
20810
+ };
20811
+ function UploadSeed({ onUploadSuccess }) {
20812
+ const fileInputRef = React.useRef(null);
20813
+ const [error, setError] = reactExports.useState(null);
20814
+ const handleFileChange = async (event) => {
20815
+ const files = event.target.files;
20503
20816
  if (!files || files.length === 0) return;
20504
20817
  const file = files[0];
20505
20818
  try {
@@ -20558,7 +20871,7 @@ function UploadSeed({ onUploadSuccess }) {
20558
20871
  };
20559
20872
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { "data-testid": "upload-seed", className: "space-y-3", children: [
20560
20873
  error && /* @__PURE__ */ jsxRuntimeExports.jsx(
20561
- p$6,
20874
+ p$7,
20562
20875
  {
20563
20876
  role: "alert",
20564
20877
  "data-testid": "upload-error",
@@ -20631,80 +20944,62 @@ function UploadSeed({ onUploadSuccess }) {
20631
20944
  onChange: handleFileChange,
20632
20945
  "data-testid": "file-input"
20633
20946
  }
20634
- ),
20635
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-gray-200 rounded-lg overflow-hidden", children: [
20636
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
20637
- "button",
20638
- {
20639
- type: "button",
20640
- onClick: () => setShowSample(!showSample),
20641
- className: "w-full px-4 py-3 text-left bg-gray-50 hover:bg-gray-100 transition-colors flex items-center justify-between",
20642
- "aria-expanded": showSample,
20643
- "data-testid": "sample-toggle",
20644
- children: [
20645
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium text-gray-700", children: "Need help? View sample seed file structure" }),
20646
- /* @__PURE__ */ jsxRuntimeExports.jsx(
20647
- "svg",
20648
- {
20649
- className: `w-4 h-4 text-gray-500 transition-transform ${showSample ? "rotate-180" : ""}`,
20650
- fill: "none",
20651
- viewBox: "0 0 24 24",
20652
- stroke: "currentColor",
20653
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(
20654
- "path",
20655
- {
20656
- strokeLinecap: "round",
20657
- strokeLinejoin: "round",
20658
- strokeWidth: 2,
20659
- d: "M19 9l-7 7-7-7"
20660
- }
20661
- )
20662
- }
20663
- )
20664
- ]
20665
- }
20666
- ),
20667
- showSample && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-white border-t border-gray-200", children: [
20668
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
20669
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-600", children: "Use this structure as a reference for your seed file:" }),
20670
- /* @__PURE__ */ jsxRuntimeExports.jsx(
20671
- "button",
20672
- {
20673
- type: "button",
20674
- onClick: () => navigator.clipboard.writeText(
20675
- JSON.stringify(sampleSeed, null, 2)
20676
- ),
20677
- className: "text-xs bg-blue-100 text-blue-700 px-2 py-1 rounded hover:bg-blue-200 transition-colors",
20678
- "data-testid": "copy-sample",
20679
- children: "Copy"
20680
- }
20681
- )
20682
- ] }),
20683
- /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-xs bg-gray-50 p-3 rounded overflow-auto max-h-60", children: JSON.stringify(sampleSeed, null, 2) })
20684
- ] })
20685
- ] })
20947
+ )
20686
20948
  ] });
20687
20949
  }
20688
20950
  function Layout({
20689
20951
  children,
20690
20952
  title,
20953
+ pageTitle,
20954
+ breadcrumbs,
20691
20955
  actions,
20692
- showBackButton = false,
20693
20956
  backTo = "/",
20694
20957
  maxWidth = "max-w-7xl"
20695
20958
  }) {
20696
20959
  const navigate = useNavigate();
20697
20960
  const location = useLocation();
20961
+ const [isUploadOpen, setIsUploadOpen] = reactExports.useState(false);
20962
+ const [seedUploadSuccess, setSeedUploadSuccess] = reactExports.useState(null);
20963
+ const [seedUploadTimer, setSeedUploadTimer] = reactExports.useState(null);
20964
+ const uploadPanelRef = reactExports.useRef(null);
20698
20965
  const isActivePath = (path) => {
20699
- if (location.pathname === "/") return true;
20966
+ if (location.pathname.startsWith(path)) return true;
20700
20967
  return false;
20701
20968
  };
20702
- const handleBack = () => {
20703
- navigate(backTo);
20969
+ const toggleUploadPanel = () => {
20970
+ setIsUploadOpen(!isUploadOpen);
20971
+ };
20972
+ const handleSeedUploadSuccess = ({ jobName }) => {
20973
+ if (seedUploadTimer) {
20974
+ clearTimeout(seedUploadTimer);
20975
+ }
20976
+ setSeedUploadSuccess(jobName);
20977
+ const timer = setTimeout(() => {
20978
+ setSeedUploadSuccess(null);
20979
+ setSeedUploadTimer(null);
20980
+ }, 5e3);
20981
+ setSeedUploadTimer(timer);
20704
20982
  };
20705
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Provider, { delayDuration: 200, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$6, { className: "min-h-screen bg-gray-1", children: [
20983
+ reactExports.useEffect(() => {
20984
+ return () => {
20985
+ if (seedUploadTimer) {
20986
+ clearTimeout(seedUploadTimer);
20987
+ }
20988
+ };
20989
+ }, [seedUploadTimer]);
20990
+ reactExports.useEffect(() => {
20991
+ if (isUploadOpen && uploadPanelRef.current) {
20992
+ const uploadArea = uploadPanelRef.current.querySelector(
20993
+ '[data-testid="upload-area"]'
20994
+ );
20995
+ if (uploadArea) {
20996
+ uploadArea.focus();
20997
+ }
20998
+ }
20999
+ }, [isUploadOpen]);
21000
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Provider, { delayDuration: 200, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$7, { className: "min-h-screen bg-gray-1", children: [
20706
21001
  /* @__PURE__ */ jsxRuntimeExports.jsx(
20707
- p$6,
21002
+ p$7,
20708
21003
  {
20709
21004
  as: "a",
20710
21005
  href: "#main-content",
@@ -20713,40 +21008,55 @@ function Layout({
20713
21008
  }
20714
21009
  ),
20715
21010
  /* @__PURE__ */ jsxRuntimeExports.jsx(
20716
- p$6,
21011
+ p$7,
20717
21012
  {
20718
21013
  role: "banner",
20719
21014
  className: "sticky top-0 z-20 border-b border-gray-300 bg-gray-1/80 backdrop-blur supports-[backdrop-filter]:bg-gray-1/60",
20720
21015
  children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
20721
- p$3,
21016
+ p$4,
20722
21017
  {
20723
21018
  align: "center",
20724
21019
  justify: "between",
20725
21020
  className: `mx-auto w-full ${maxWidth} px-4 sm:px-6 lg:px-8 py-4`,
20726
21021
  gap: "4",
20727
21022
  children: [
20728
- /* @__PURE__ */ jsxRuntimeExports.jsxs(p$3, { align: "center", gap: "3", className: "min-w-0 flex-1", children: [
20729
- showBackButton && /* @__PURE__ */ jsxRuntimeExports.jsxs(Root3, { delayDuration: 200, children: [
20730
- /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
20731
- Button,
20732
- {
20733
- variant: "ghost",
20734
- size: "sm",
20735
- onClick: handleBack,
20736
- className: "shrink-0",
20737
- "aria-label": "Go back",
20738
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { className: "h-4 w-4" })
20739
- }
20740
- ) }),
20741
- /* @__PURE__ */ jsxRuntimeExports.jsx(Content2, { side: "bottom", sideOffset: 5, children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "2", children: "Go back" }) })
20742
- ] }),
21023
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(p$4, { align: "center", className: "min-w-0 flex-1", children: [
21024
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
21025
+ p$7,
21026
+ {
21027
+ asChild: true,
21028
+ className: "shrink-0",
21029
+ style: { width: "80px", height: "60px" },
21030
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
21031
+ Link,
21032
+ {
21033
+ to: "/",
21034
+ "aria-label": "Go to homepage",
21035
+ className: "rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500",
21036
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Logo, {})
21037
+ }
21038
+ )
21039
+ }
21040
+ ),
20743
21041
  /* @__PURE__ */ jsxRuntimeExports.jsx(
20744
- r$9,
21042
+ p$7,
20745
21043
  {
20746
- size: "5",
20747
- weight: "medium",
20748
- className: "text-gray-12 truncate",
20749
- children: title || "Prompt Pipeline"
21044
+ asChild: true,
21045
+ className: "shrink-0 cursor-pointer hover:bg-gray-3 rounded p-1 -m-1 transition-colors",
21046
+ onClick: () => navigate("/"),
21047
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
21048
+ r$8,
21049
+ {
21050
+ size: "6",
21051
+ weight: "medium",
21052
+ className: "text-gray-12 truncate",
21053
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
21054
+ "Prompt",
21055
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
21056
+ "Pipeline"
21057
+ ] })
21058
+ }
21059
+ )
20750
21060
  }
20751
21061
  )
20752
21062
  ] }),
@@ -20756,21 +21066,66 @@ function Layout({
20756
21066
  role: "navigation",
20757
21067
  "aria-label": "Main navigation",
20758
21068
  className: "hidden md:flex",
20759
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$3, { align: "center", gap: "6", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
21069
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$4, { align: "center", gap: "6", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
20760
21070
  e,
20761
21071
  {
20762
- href: "/",
20763
- className: `text-sm font-medium transition-colors hover:text-blue-600 ${isActivePath() ? "text-blue-600" : "text-gray-11 hover:text-gray-12"}`,
20764
- "aria-current": isActivePath() ? "page" : void 0,
20765
- children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$3, { align: "center", gap: "2", children: [
20766
- /* @__PURE__ */ jsxRuntimeExports.jsx(House, { className: "h-4 w-4" }),
20767
- "Dashboard"
21072
+ href: "/code",
21073
+ className: `text-sm font-medium transition-colors hover:text-blue-600 ${isActivePath("/code") ? "text-blue-600" : "text-gray-11 hover:text-gray-12"}`,
21074
+ "aria-current": isActivePath("/code") ? "page" : void 0,
21075
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$4, { align: "center", gap: "2", children: [
21076
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CodeXml, { className: "h-4 w-4" }),
21077
+ "Help"
20768
21078
  ] })
20769
21079
  }
20770
21080
  ) })
20771
21081
  }
20772
21082
  ),
20773
- actions && /* @__PURE__ */ jsxRuntimeExports.jsx(p$3, { align: "center", gap: "3", className: "shrink-0", children: actions })
21083
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(p$4, { align: "center", gap: "3", className: "shrink-0", children: [
21084
+ actions,
21085
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Root3, { delayDuration: 200, children: [
21086
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
21087
+ Button,
21088
+ {
21089
+ size: "sm",
21090
+ variant: "default",
21091
+ onClick: toggleUploadPanel,
21092
+ "aria-controls": "layout-upload-panel",
21093
+ "aria-expanded": isUploadOpen,
21094
+ children: [
21095
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { className: "h-4 w-4" }),
21096
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "2", className: "ml-2", children: "Upload Seed" })
21097
+ ]
21098
+ }
21099
+ ) }),
21100
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Content2, { side: "bottom", sideOffset: 5, children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "2", children: "Upload seed file" }) })
21101
+ ] })
21102
+ ] })
21103
+ ]
21104
+ }
21105
+ )
21106
+ }
21107
+ ),
21108
+ isUploadOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(
21109
+ p$7,
21110
+ {
21111
+ id: "layout-upload-panel",
21112
+ ref: uploadPanelRef,
21113
+ role: "region",
21114
+ "aria-label": "Upload seed file",
21115
+ className: "bg-blue-50",
21116
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
21117
+ p$4,
21118
+ {
21119
+ direction: "column",
21120
+ gap: "3",
21121
+ className: `mx-auto w-full ${maxWidth} px-4 sm:px-6 lg:px-8 py-4`,
21122
+ children: [
21123
+ seedUploadSuccess && /* @__PURE__ */ jsxRuntimeExports.jsx(p$7, { className: "rounded-md bg-green-50 p-3 border border-green-200", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$b, { size: "2", className: "text-green-800", children: [
21124
+ "Job ",
21125
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: seedUploadSuccess }),
21126
+ " created successfully"
21127
+ ] }) }),
21128
+ /* @__PURE__ */ jsxRuntimeExports.jsx(UploadSeed, { onUploadSuccess: handleSeedUploadSuccess })
20774
21129
  ]
20775
21130
  }
20776
21131
  )
@@ -20781,7 +21136,7 @@ function Layout({
20781
21136
  {
20782
21137
  id: "main-content",
20783
21138
  role: "main",
20784
- className: `mx-auto w-full ${maxWidth} px-4 sm:px-6 lg:px-8 py-6`,
21139
+ className: `mx-auto w-full ${maxWidth} px-4 sm:px-6 lg:px-8`,
20785
21140
  children
20786
21141
  }
20787
21142
  )
@@ -20799,36 +21154,32 @@ function PromptPipelineDashboard({ isConnected }) {
20799
21154
  return src.map(adaptJobSummary);
20800
21155
  }, [apiJobs, error]);
20801
21156
  const [activeTab, setActiveTab] = reactExports.useState("current");
20802
- const [seedUploadSuccess, setSeedUploadSuccess] = reactExports.useState(null);
20803
- const [seedUploadTimer, setSeedUploadTimer] = reactExports.useState(null);
20804
- const now = useTicker(1e4);
20805
21157
  const errorCount = reactExports.useMemo(
20806
- () => jobs.filter((j) => j.status === "failed").length,
21158
+ () => jobs.filter((j) => j.status === TaskState.FAILED).length,
20807
21159
  [jobs]
20808
21160
  );
20809
21161
  const currentCount = reactExports.useMemo(
20810
- () => jobs.filter((j) => j.status === "running").length,
21162
+ () => jobs.filter((j) => j.status === TaskState.RUNNING).length,
20811
21163
  [jobs]
20812
21164
  );
20813
21165
  const completedCount = reactExports.useMemo(
20814
- () => jobs.filter((j) => j.status === "complete").length,
21166
+ () => jobs.filter((j) => j.status === JobStatus.COMPLETE).length,
20815
21167
  [jobs]
20816
21168
  );
20817
21169
  const filteredJobs = reactExports.useMemo(() => {
20818
21170
  switch (activeTab) {
20819
21171
  case "current":
20820
- return jobs.filter((j) => j.status === "running");
21172
+ return jobs.filter((j) => j.status === TaskState.RUNNING);
20821
21173
  case "errors":
20822
- return jobs.filter((j) => j.status === "failed");
21174
+ return jobs.filter((j) => j.status === TaskState.FAILED);
20823
21175
  case "complete":
20824
- return jobs.filter((j) => j.status === "complete");
21176
+ return jobs.filter((j) => j.status === JobStatus.COMPLETE);
20825
21177
  default:
20826
21178
  return [];
20827
21179
  }
20828
21180
  }, [jobs, activeTab]);
20829
- const overallElapsed = (job) => jobCumulativeDurationMs(job, now);
20830
21181
  const runningJobs = reactExports.useMemo(
20831
- () => jobs.filter((j) => j.status === "running"),
21182
+ () => jobs.filter((j) => j.status === TaskState.RUNNING),
20832
21183
  [jobs]
20833
21184
  );
20834
21185
  const aggregateProgress = reactExports.useMemo(() => {
@@ -20843,94 +21194,76 @@ function PromptPipelineDashboard({ isConnected }) {
20843
21194
  console.warn(`Cannot open job "${job.name}" - no valid job ID available`);
20844
21195
  }
20845
21196
  };
20846
- const handleSeedUploadSuccess = ({ jobName }) => {
20847
- if (seedUploadTimer) {
20848
- clearTimeout(seedUploadTimer);
20849
- }
20850
- setSeedUploadSuccess(jobName);
20851
- const timer = setTimeout(() => {
20852
- setSeedUploadSuccess(null);
20853
- setSeedUploadTimer(null);
20854
- }, 5e3);
20855
- setSeedUploadTimer(timer);
20856
- };
20857
- reactExports.useEffect(() => {
20858
- return () => {
20859
- if (seedUploadTimer) {
20860
- clearTimeout(seedUploadTimer);
20861
- }
20862
- };
20863
- }, [seedUploadTimer]);
20864
- const headerActions = runningJobs.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(p$3, { align: "center", gap: "2", className: "text-gray-11", children: [
20865
- /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "1", weight: "medium", children: "Overall Progress" }),
21197
+ const headerActions = runningJobs.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(p$4, { align: "center", gap: "2", className: "text-gray-11", children: [
21198
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "1", weight: "medium", children: "Overall Progress" }),
20866
21199
  /* @__PURE__ */ jsxRuntimeExports.jsx(Progress, { value: aggregateProgress, className: "w-20" }),
20867
- /* @__PURE__ */ jsxRuntimeExports.jsxs(p$a, { size: "1", className: "text-gray-9", children: [
21200
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(p$b, { size: "1", className: "text-gray-9", children: [
20868
21201
  aggregateProgress,
20869
21202
  "%"
20870
21203
  ] })
20871
21204
  ] });
20872
21205
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Layout, { title: "Prompt Pipeline", actions: headerActions, children: [
20873
- /* @__PURE__ */ jsxRuntimeExports.jsx(o$2, { className: "mb-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$3, { direction: "column", gap: "3", children: [
20874
- /* @__PURE__ */ jsxRuntimeExports.jsx(r$9, { size: "4", weight: "medium", className: "text-gray-12", children: "Upload Seed File" }),
20875
- seedUploadSuccess && /* @__PURE__ */ jsxRuntimeExports.jsx(p$6, { className: "rounded-md bg-green-50 p-3 border border-green-200", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$a, { size: "2", className: "text-green-800", children: [
20876
- "Job ",
20877
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: seedUploadSuccess }),
20878
- " created successfully"
20879
- ] }) }),
20880
- /* @__PURE__ */ jsxRuntimeExports.jsx(UploadSeed, { onUploadSuccess: handleSeedUploadSuccess })
20881
- ] }) }),
20882
- error && /* @__PURE__ */ jsxRuntimeExports.jsx(p$6, { className: "mb-4 rounded-md bg-yellow-50 p-3 border border-yellow-200", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "2", className: "text-yellow-800", children: "Unable to load jobs from the server" }) }),
20883
- /* @__PURE__ */ jsxRuntimeExports.jsxs(m, { value: activeTab, onValueChange: setActiveTab, children: [
20884
- /* @__PURE__ */ jsxRuntimeExports.jsxs(b, { "aria-label": "Job filters", children: [
20885
- /* @__PURE__ */ jsxRuntimeExports.jsxs(P, { value: "current", children: [
20886
- "Current (",
20887
- currentCount,
20888
- ")"
20889
- ] }),
20890
- /* @__PURE__ */ jsxRuntimeExports.jsxs(P, { value: "errors", children: [
20891
- "Errors (",
20892
- errorCount,
20893
- ")"
20894
- ] }),
20895
- /* @__PURE__ */ jsxRuntimeExports.jsxs(P, { value: "complete", children: [
20896
- "Completed (",
20897
- completedCount,
20898
- ")"
20899
- ] })
20900
- ] }),
20901
- /* @__PURE__ */ jsxRuntimeExports.jsx(f, { value: "current", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
20902
- JobTable,
20903
- {
20904
- jobs: filteredJobs,
20905
- pipeline: null,
20906
- onOpenJob: openJob,
20907
- overallElapsed,
20908
- now
20909
- }
20910
- ) }),
20911
- /* @__PURE__ */ jsxRuntimeExports.jsx(f, { value: "errors", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
20912
- JobTable,
20913
- {
20914
- jobs: filteredJobs,
20915
- pipeline: null,
20916
- onOpenJob: openJob,
20917
- overallElapsed,
20918
- now
20919
- }
20920
- ) }),
20921
- /* @__PURE__ */ jsxRuntimeExports.jsx(f, { value: "complete", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
20922
- JobTable,
20923
- {
20924
- jobs: filteredJobs,
20925
- pipeline: null,
20926
- onOpenJob: openJob,
20927
- overallElapsed,
20928
- now
20929
- }
20930
- ) })
20931
- ] })
21206
+ error && /* @__PURE__ */ jsxRuntimeExports.jsx(p$7, { className: "mb-4 rounded-md bg-yellow-50 p-3 border border-yellow-200", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "2", className: "text-yellow-800", children: "Unable to load jobs from the server" }) }),
21207
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
21208
+ m,
21209
+ {
21210
+ value: activeTab,
21211
+ onValueChange: setActiveTab,
21212
+ className: "mt-4",
21213
+ children: [
21214
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(b, { "aria-label": "Job filters", children: [
21215
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(P, { value: "current", children: [
21216
+ "Current (",
21217
+ currentCount,
21218
+ ")"
21219
+ ] }),
21220
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(P, { value: "errors", children: [
21221
+ "Errors (",
21222
+ errorCount,
21223
+ ")"
21224
+ ] }),
21225
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(P, { value: "complete", children: [
21226
+ "Completed (",
21227
+ completedCount,
21228
+ ")"
21229
+ ] })
21230
+ ] }),
21231
+ /* @__PURE__ */ jsxRuntimeExports.jsx(f, { value: "current", children: /* @__PURE__ */ jsxRuntimeExports.jsx(JobTable, { jobs: filteredJobs, pipeline: null, onOpenJob: openJob }) }),
21232
+ /* @__PURE__ */ jsxRuntimeExports.jsx(f, { value: "errors", children: /* @__PURE__ */ jsxRuntimeExports.jsx(JobTable, { jobs: filteredJobs, pipeline: null, onOpenJob: openJob }) }),
21233
+ /* @__PURE__ */ jsxRuntimeExports.jsx(f, { value: "complete", children: /* @__PURE__ */ jsxRuntimeExports.jsx(JobTable, { jobs: filteredJobs, pipeline: null, onOpenJob: openJob }) })
21234
+ ]
21235
+ }
21236
+ )
20932
21237
  ] });
20933
21238
  }
21239
+ function areGeometriesEqual(prev, next, epsilon = 0.5) {
21240
+ if (prev === next) return true;
21241
+ if (!prev || !next) return false;
21242
+ if (prev.itemsLength !== next.itemsLength) return false;
21243
+ if (prev.effectiveCols !== next.effectiveCols) return false;
21244
+ if (!areOverlayBoxesEqual(prev.overlayBox, next.overlayBox, epsilon)) {
21245
+ return false;
21246
+ }
21247
+ const prevBoxes = prev.boxes;
21248
+ const nextBoxes = next.boxes;
21249
+ if (prevBoxes.length !== nextBoxes.length) return false;
21250
+ for (let i2 = 0; i2 < prevBoxes.length; i2++) {
21251
+ if (!areBoxesEqual(prevBoxes[i2], nextBoxes[i2], epsilon)) {
21252
+ return false;
21253
+ }
21254
+ }
21255
+ return true;
21256
+ }
21257
+ function areOverlayBoxesEqual(a2, b2, epsilon) {
21258
+ return areNumbersClose(a2.left, b2.left, epsilon) && areNumbersClose(a2.top, b2.top, epsilon) && areNumbersClose(a2.width, b2.width, epsilon) && areNumbersClose(a2.height, b2.height, epsilon) && areNumbersClose(a2.right, b2.right, epsilon) && areNumbersClose(a2.bottom, b2.bottom, epsilon);
21259
+ }
21260
+ function areBoxesEqual(a2, b2, epsilon) {
21261
+ if (!a2 || !b2) return a2 === b2;
21262
+ return areNumbersClose(a2.left, b2.left, epsilon) && areNumbersClose(a2.top, b2.top, epsilon) && areNumbersClose(a2.width, b2.width, epsilon) && areNumbersClose(a2.height, b2.height, epsilon) && areNumbersClose(a2.right, b2.right, epsilon) && areNumbersClose(a2.bottom, b2.bottom, epsilon) && areNumbersClose(a2.headerMidY, b2.headerMidY, epsilon);
21263
+ }
21264
+ function areNumbersClose(a2, b2, epsilon) {
21265
+ return Math.abs(a2 - b2) <= epsilon;
21266
+ }
20934
21267
  function TaskFilePane({
20935
21268
  isOpen,
20936
21269
  jobId,
@@ -21366,82 +21699,428 @@ function TaskFilePane({
21366
21699
  ] })
21367
21700
  ] });
21368
21701
  }
21369
- const CATEGORY_KEYS = ["artifacts", "logs", "tmp"];
21370
- const LEGACY_KEY_SET = /* @__PURE__ */ new Set([
21371
- "input",
21372
- "inputs",
21373
- "output",
21374
- "outputs",
21375
- "legacyInput",
21376
- "legacyOutput",
21377
- "inputFiles",
21378
- "outputFiles"
21379
- ]);
21380
- function createEmptyTaskFiles() {
21381
- return { artifacts: [], logs: [], tmp: [] };
21382
- }
21383
- function coerceStringArray(value) {
21384
- if (!Array.isArray(value)) return [];
21385
- return value.filter((entry) => typeof entry === "string");
21386
- }
21387
- function reportUnsupportedKeys(keys) {
21388
- if (keys.length === 0) return;
21389
- const legacyKeys = keys.filter((key) => LEGACY_KEY_SET.has(key));
21390
- const otherKeys = keys.filter((key) => !LEGACY_KEY_SET.has(key));
21391
- if (legacyKeys.length > 0) {
21392
- console.warn(
21393
- `[task-files] Ignoring unsupported legacy keys: ${legacyKeys.join(", ")}`
21394
- );
21395
- }
21396
- if (otherKeys.length > 0) {
21397
- console.warn(
21398
- `[task-files] Ignoring unsupported task.files keys: ${otherKeys.join(", ")}`
21399
- );
21400
- }
21401
- }
21402
- function normalizeTaskFiles(candidate) {
21403
- const safeCandidate = candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : {};
21404
- const unsupportedKeys = Object.keys(safeCandidate).filter(
21405
- (key) => !CATEGORY_KEYS.includes(key)
21406
- );
21407
- reportUnsupportedKeys(unsupportedKeys);
21408
- return {
21409
- artifacts: coerceStringArray(safeCandidate.artifacts),
21410
- logs: coerceStringArray(safeCandidate.logs),
21411
- tmp: coerceStringArray(safeCandidate.tmp)
21412
- };
21413
- }
21414
- function ensureTaskFiles(task) {
21415
- const normalized = normalizeTaskFiles(task?.files);
21416
- if (task && typeof task === "object") {
21417
- task.files = normalized;
21418
- }
21419
- return normalized;
21420
- }
21421
- function matchesTaskIdentifier(task, taskId) {
21422
- if (!task || typeof task !== "object" || taskId == null) return false;
21423
- const target = String(taskId);
21424
- if (task.id != null && String(task.id) === target) return true;
21425
- if (task.name != null && String(task.name) === target) return true;
21426
- return false;
21427
- }
21428
- function findTaskCandidate(tasks, taskId) {
21429
- if (!tasks || taskId == null) return null;
21430
- if (Array.isArray(tasks)) {
21431
- if (typeof taskId === "number" && tasks[taskId]) {
21432
- const indexedTask = tasks[taskId];
21433
- if (indexedTask && typeof indexedTask === "object") {
21434
- return indexedTask;
21435
- }
21702
+ function TaskDetailSidebar({
21703
+ open,
21704
+ title,
21705
+ status,
21706
+ jobId,
21707
+ taskId,
21708
+ taskBody,
21709
+ filesByTypeForItem = () => ({ artifacts: [], logs: [], tmp: [] }),
21710
+ task,
21711
+ onClose,
21712
+ taskIndex
21713
+ // Add taskIndex for ID compatibility
21714
+ }) {
21715
+ const [filePaneType, setFilePaneType] = reactExports.useState("artifacts");
21716
+ const [filePaneOpen, setFilePaneOpen] = reactExports.useState(false);
21717
+ const [filePaneFilename, setFilePaneFilename] = reactExports.useState(null);
21718
+ const closeButtonRef = reactExports.useRef(null);
21719
+ const getHeaderClasses2 = (status2) => {
21720
+ switch (status2) {
21721
+ case TaskState.DONE:
21722
+ return "bg-green-50 border-green-200 text-green-700";
21723
+ case TaskState.RUNNING:
21724
+ return "bg-amber-50 border-amber-200 text-amber-700";
21725
+ case TaskState.FAILED:
21726
+ return "bg-pink-50 border-pink-200 text-pink-700";
21727
+ default:
21728
+ return "bg-gray-100 border-gray-200 text-gray-700";
21436
21729
  }
21437
- return tasks.find((task) => matchesTaskIdentifier(task, taskId)) ?? null;
21438
- }
21439
- if (typeof tasks === "object") {
21440
- const direct = tasks[taskId];
21441
- if (direct && typeof direct === "object") {
21442
- return direct;
21730
+ };
21731
+ reactExports.useEffect(() => {
21732
+ if (open && closeButtonRef.current) {
21733
+ closeButtonRef.current.focus();
21443
21734
  }
21444
- for (const task of Object.values(tasks)) {
21735
+ }, [open]);
21736
+ reactExports.useEffect(() => {
21737
+ if (open) {
21738
+ setFilePaneType("artifacts");
21739
+ setFilePaneOpen(false);
21740
+ setFilePaneFilename(null);
21741
+ }
21742
+ }, [open]);
21743
+ reactExports.useEffect(() => {
21744
+ setFilePaneFilename(null);
21745
+ setFilePaneOpen(false);
21746
+ }, [filePaneType]);
21747
+ const handleFileClick = (filename) => {
21748
+ setFilePaneFilename(filename);
21749
+ setFilePaneOpen(true);
21750
+ };
21751
+ const handleFilePaneClose = () => {
21752
+ setFilePaneOpen(false);
21753
+ setFilePaneFilename(null);
21754
+ };
21755
+ if (!open) {
21756
+ return null;
21757
+ }
21758
+ const filesForStep = filesByTypeForItem(task);
21759
+ const filesForTab = filesForStep[filePaneType] ?? [];
21760
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
21761
+ "aside",
21762
+ {
21763
+ role: "dialog",
21764
+ "aria-modal": "true",
21765
+ "aria-labelledby": `slide-over-title-${taskIndex}`,
21766
+ "aria-hidden": false,
21767
+ className: `fixed inset-y-0 right-0 z-[2000] w-full max-w-4xl bg-white border-l border-gray-200 transform transition-transform duration-300 ease-out translate-x-0`,
21768
+ children: [
21769
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
21770
+ "div",
21771
+ {
21772
+ className: `px-6 py-4 border-b flex items-center justify-between ${getHeaderClasses2(status)}`,
21773
+ children: [
21774
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
21775
+ "div",
21776
+ {
21777
+ id: `slide-over-title-${taskIndex}`,
21778
+ className: "text-lg font-semibold truncate",
21779
+ children: title
21780
+ }
21781
+ ),
21782
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
21783
+ "button",
21784
+ {
21785
+ ref: closeButtonRef,
21786
+ type: "button",
21787
+ "aria-label": "Close details",
21788
+ onClick: onClose,
21789
+ className: "rounded-md border border-gray-300 text-gray-700 hover:bg-gray-50 px-3 py-1.5 text-base",
21790
+ children: "×"
21791
+ }
21792
+ )
21793
+ ]
21794
+ }
21795
+ ),
21796
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 space-y-8 overflow-y-auto h-full", children: [
21797
+ status === TaskState.FAILED && taskBody && /* @__PURE__ */ jsxRuntimeExports.jsx("section", { "aria-label": "Error", children: /* @__PURE__ */ jsxRuntimeExports.jsx(n$2, { role: "alert", "aria-live": "assertive", children: /* @__PURE__ */ jsxRuntimeExports.jsx(u$1, { className: "whitespace-pre-wrap break-words", children: taskBody }) }) }),
21798
+ /* @__PURE__ */ jsxRuntimeExports.jsx("section", { className: "mt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-4", children: [
21799
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-base font-semibold text-gray-900", children: "Files" }),
21800
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center space-x-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex rounded-lg border border-gray-200 bg-gray-50 p-1", children: [
21801
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
21802
+ "button",
21803
+ {
21804
+ onClick: () => setFilePaneType("artifacts"),
21805
+ className: `px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${filePaneType === "artifacts" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600 hover:text-gray-900"}`,
21806
+ children: "Artifacts"
21807
+ }
21808
+ ),
21809
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
21810
+ "button",
21811
+ {
21812
+ onClick: () => setFilePaneType("logs"),
21813
+ className: `px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${filePaneType === "logs" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600 hover:text-gray-900"}`,
21814
+ children: "Logs"
21815
+ }
21816
+ ),
21817
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
21818
+ "button",
21819
+ {
21820
+ onClick: () => setFilePaneType("tmp"),
21821
+ className: `px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${filePaneType === "tmp" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600 hover:text-gray-900"}`,
21822
+ children: "Temp"
21823
+ }
21824
+ )
21825
+ ] }) })
21826
+ ] }) }),
21827
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
21828
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm text-gray-600", children: [
21829
+ filePaneType.charAt(0).toUpperCase() + filePaneType.slice(1),
21830
+ " files for ",
21831
+ taskId
21832
+ ] }),
21833
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1", children: filesForTab.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm text-gray-500 italic py-4 text-center", children: [
21834
+ "No ",
21835
+ filePaneType,
21836
+ " files available for this task"
21837
+ ] }) : filesForTab.map((name) => /* @__PURE__ */ jsxRuntimeExports.jsx(
21838
+ "div",
21839
+ {
21840
+ className: "flex items-center justify-between p-2 rounded border border-gray-200 hover:border-gray-300 hover:bg-gray-50 cursor-pointer transition-colors",
21841
+ onClick: () => handleFileClick(name),
21842
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center space-x-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: name }) })
21843
+ },
21844
+ `${filePaneType}-${name}`
21845
+ )) })
21846
+ ] }),
21847
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
21848
+ TaskFilePane,
21849
+ {
21850
+ isOpen: filePaneOpen,
21851
+ jobId,
21852
+ taskId,
21853
+ type: filePaneType,
21854
+ filename: filePaneFilename,
21855
+ onClose: handleFilePaneClose
21856
+ }
21857
+ )
21858
+ ] })
21859
+ ]
21860
+ }
21861
+ );
21862
+ }
21863
+ React.memo(TaskDetailSidebar);
21864
+ function RestartJobModal({
21865
+ open,
21866
+ onClose,
21867
+ onConfirm,
21868
+ jobId,
21869
+ taskId,
21870
+ isSubmitting = false
21871
+ }) {
21872
+ const modalRef = reactExports.useRef(null);
21873
+ reactExports.useEffect(() => {
21874
+ const handleKeyDown2 = (e2) => {
21875
+ if (e2.key === "Escape" && open) {
21876
+ e2.preventDefault();
21877
+ onClose();
21878
+ }
21879
+ };
21880
+ if (open) {
21881
+ document.addEventListener("keydown", handleKeyDown2);
21882
+ if (modalRef.current) {
21883
+ modalRef.current.focus();
21884
+ }
21885
+ return () => {
21886
+ document.removeEventListener("keydown", handleKeyDown2);
21887
+ };
21888
+ }
21889
+ }, [open, onClose]);
21890
+ const handleKeyDown = (e2) => {
21891
+ if (e2.key === "Enter" && !isSubmitting && open) {
21892
+ e2.preventDefault();
21893
+ onConfirm();
21894
+ }
21895
+ };
21896
+ if (!open) return null;
21897
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
21898
+ "div",
21899
+ {
21900
+ className: "fixed inset-0 z-50 flex items-center justify-center",
21901
+ "aria-hidden": !open,
21902
+ children: [
21903
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
21904
+ "div",
21905
+ {
21906
+ className: "absolute inset-0 bg-black/50",
21907
+ onClick: onClose,
21908
+ "aria-hidden": "true"
21909
+ }
21910
+ ),
21911
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
21912
+ "div",
21913
+ {
21914
+ ref: modalRef,
21915
+ role: "dialog",
21916
+ "aria-modal": "true",
21917
+ "aria-labelledby": "restart-modal-title",
21918
+ "aria-describedby": "restart-modal-description",
21919
+ className: "relative bg-white rounded-lg shadow-2xl border border-gray-200 max-w-lg w-full mx-4 outline-none",
21920
+ style: { minWidth: "320px", maxWidth: "560px" },
21921
+ tabIndex: -1,
21922
+ onKeyDown: handleKeyDown,
21923
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6", children: [
21924
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
21925
+ r$8,
21926
+ {
21927
+ id: "restart-modal-title",
21928
+ as: "h2",
21929
+ size: "5",
21930
+ className: "mb-4 text-gray-900",
21931
+ children: taskId ? `Restart from ${taskId}` : "Restart job (reset progress)"
21932
+ }
21933
+ ),
21934
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(p$7, { id: "restart-modal-description", className: "mb-6", children: [
21935
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { as: "p", className: "text-gray-700 mb-4", children: taskId ? `This will restart the job from the "${taskId}" task. Tasks before ${taskId} will remain completed, while ${taskId} and all subsequent tasks will be reset to pending. Files and artifacts are preserved. A new background run will start automatically. This cannot be undone.` : "This will clear the job's progress and active stage and reset all tasks to pending. Files and artifacts are preserved. A new background run will start automatically. This cannot be undone." }),
21936
+ taskId && /* @__PURE__ */ jsxRuntimeExports.jsxs(p$b, { as: "p", className: "text-sm text-gray-600 mb-3", children: [
21937
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Triggered from task:" }),
21938
+ " ",
21939
+ taskId
21940
+ ] }),
21941
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { as: "p", className: "text-sm text-gray-500 italic", children: "Note: Job must be in current lifecycle and not running." })
21942
+ ] }),
21943
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(p$4, { gap: "3", justify: "end", children: [
21944
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
21945
+ Button,
21946
+ {
21947
+ variant: "outline",
21948
+ onClick: onClose,
21949
+ disabled: isSubmitting,
21950
+ className: "min-w-[80px]",
21951
+ children: "Cancel"
21952
+ }
21953
+ ),
21954
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
21955
+ Button,
21956
+ {
21957
+ variant: "destructive",
21958
+ onClick: onConfirm,
21959
+ disabled: isSubmitting,
21960
+ className: "min-w-[80px]",
21961
+ children: isSubmitting ? "Restarting..." : "Restart"
21962
+ }
21963
+ )
21964
+ ] })
21965
+ ] })
21966
+ }
21967
+ )
21968
+ ]
21969
+ }
21970
+ ) });
21971
+ }
21972
+ async function restartJob(jobId, opts = {}) {
21973
+ const options = {
21974
+ clearTokenUsage: true,
21975
+ ...opts.options
21976
+ };
21977
+ const requestBody = opts.fromTask ? { fromTask: opts.fromTask, options } : { mode: "clean-slate", options };
21978
+ try {
21979
+ const response = await fetch(
21980
+ `/api/jobs/${encodeURIComponent(jobId)}/restart`,
21981
+ {
21982
+ method: "POST",
21983
+ headers: {
21984
+ "Content-Type": "application/json"
21985
+ },
21986
+ body: JSON.stringify(requestBody)
21987
+ }
21988
+ );
21989
+ if (!response.ok) {
21990
+ let errorData;
21991
+ try {
21992
+ errorData = await response.json();
21993
+ } catch {
21994
+ errorData = { message: response.statusText };
21995
+ }
21996
+ throw {
21997
+ code: errorData.code || getErrorCodeFromStatus(response.status),
21998
+ message: getRestartErrorMessage(errorData, response.status),
21999
+ status: response.status
22000
+ };
22001
+ }
22002
+ return await response.json();
22003
+ } catch (error) {
22004
+ if (error.code && error.message) {
22005
+ throw error;
22006
+ }
22007
+ throw {
22008
+ code: "network_error",
22009
+ message: error.message || "Failed to connect to server"
22010
+ };
22011
+ }
22012
+ }
22013
+ function getErrorCodeFromStatus(status) {
22014
+ switch (status) {
22015
+ case 404:
22016
+ return "job_not_found";
22017
+ case 409:
22018
+ return "conflict";
22019
+ case 500:
22020
+ return "spawn_failed";
22021
+ default:
22022
+ return "unknown_error";
22023
+ }
22024
+ }
22025
+ function getRestartErrorMessage(errorData, status) {
22026
+ if (status === 409) {
22027
+ if (errorData.code === "job_running") {
22028
+ return "Job is currently running; restart is unavailable.";
22029
+ }
22030
+ if (errorData.code === "unsupported_lifecycle") {
22031
+ return "Job must be in current to restart.";
22032
+ }
22033
+ if (errorData.message?.includes("job_running")) {
22034
+ return "Job is currently running; restart is unavailable.";
22035
+ }
22036
+ if (errorData.message?.includes("unsupported_lifecycle")) {
22037
+ return "Job must be in current to restart.";
22038
+ }
22039
+ }
22040
+ if (status === 404) {
22041
+ return "Job not found.";
22042
+ }
22043
+ if (status === 500) {
22044
+ return "Failed to start restart. Try again.";
22045
+ }
22046
+ return errorData.message || "Failed to restart job.";
22047
+ }
22048
+ const CATEGORY_KEYS = ["artifacts", "logs", "tmp"];
22049
+ const LEGACY_KEY_SET = /* @__PURE__ */ new Set([
22050
+ "input",
22051
+ "inputs",
22052
+ "output",
22053
+ "outputs",
22054
+ "legacyInput",
22055
+ "legacyOutput",
22056
+ "inputFiles",
22057
+ "outputFiles"
22058
+ ]);
22059
+ function createEmptyTaskFiles() {
22060
+ return { artifacts: [], logs: [], tmp: [] };
22061
+ }
22062
+ function coerceStringArray(value) {
22063
+ if (!Array.isArray(value)) return [];
22064
+ return value.filter((entry) => typeof entry === "string");
22065
+ }
22066
+ function reportUnsupportedKeys(keys) {
22067
+ if (keys.length === 0) return;
22068
+ const legacyKeys = keys.filter((key) => LEGACY_KEY_SET.has(key));
22069
+ const otherKeys = keys.filter((key) => !LEGACY_KEY_SET.has(key));
22070
+ if (legacyKeys.length > 0) {
22071
+ console.warn(
22072
+ `[task-files] Ignoring unsupported legacy keys: ${legacyKeys.join(", ")}`
22073
+ );
22074
+ }
22075
+ if (otherKeys.length > 0) {
22076
+ console.warn(
22077
+ `[task-files] Ignoring unsupported task.files keys: ${otherKeys.join(", ")}`
22078
+ );
22079
+ }
22080
+ }
22081
+ function normalizeTaskFiles(candidate) {
22082
+ const safeCandidate = candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : {};
22083
+ const unsupportedKeys = Object.keys(safeCandidate).filter(
22084
+ (key) => !CATEGORY_KEYS.includes(key)
22085
+ );
22086
+ reportUnsupportedKeys(unsupportedKeys);
22087
+ return {
22088
+ artifacts: coerceStringArray(safeCandidate.artifacts),
22089
+ logs: coerceStringArray(safeCandidate.logs),
22090
+ tmp: coerceStringArray(safeCandidate.tmp)
22091
+ };
22092
+ }
22093
+ function ensureTaskFiles(task) {
22094
+ const normalized = normalizeTaskFiles(task?.files);
22095
+ if (task && typeof task === "object") {
22096
+ task.files = normalized;
22097
+ }
22098
+ return normalized;
22099
+ }
22100
+ function matchesTaskIdentifier(task, taskId) {
22101
+ if (!task || typeof task !== "object" || taskId == null) return false;
22102
+ const target = String(taskId);
22103
+ if (task.id != null && String(task.id) === target) return true;
22104
+ if (task.name != null && String(task.name) === target) return true;
22105
+ return false;
22106
+ }
22107
+ function findTaskCandidate(tasks, taskId) {
22108
+ if (!tasks || taskId == null) return null;
22109
+ if (Array.isArray(tasks)) {
22110
+ if (typeof taskId === "number" && tasks[taskId]) {
22111
+ const indexedTask = tasks[taskId];
22112
+ if (indexedTask && typeof indexedTask === "object") {
22113
+ return indexedTask;
22114
+ }
22115
+ }
22116
+ return tasks.find((task) => matchesTaskIdentifier(task, taskId)) ?? null;
22117
+ }
22118
+ if (typeof tasks === "object") {
22119
+ const direct = tasks[taskId];
22120
+ if (direct && typeof direct === "object") {
22121
+ return direct;
22122
+ }
22123
+ for (const task of Object.values(tasks)) {
21445
22124
  if (matchesTaskIdentifier(task, taskId)) {
21446
22125
  return task;
21447
22126
  }
@@ -21467,6 +22146,10 @@ function getTaskFilesForTask(job, taskId) {
21467
22146
  console.debug("[getTaskFilesForTask] Task files result:", { taskId, result });
21468
22147
  return result;
21469
22148
  }
22149
+ const prefersReducedMotion = () => {
22150
+ if (typeof window === "undefined") return false;
22151
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
22152
+ };
21470
22153
  function upperFirst(s2) {
21471
22154
  return typeof s2 === "string" && s2.length > 0 ? s2.charAt(0).toUpperCase() + s2.slice(1) : s2;
21472
22155
  }
@@ -21489,26 +22172,136 @@ function formatStepName(item, idx) {
21489
22172
  const raw = item.title ?? item.id ?? `Step ${idx + 1}`;
21490
22173
  return upperFirst(item.title ? item.title : raw);
21491
22174
  }
21492
- const createDAGGridLogger = (jobId) => {
21493
- const prefix = `[DAGGrid:${jobId || "unknown"}]`;
21494
- return {
21495
- log: (message, data = null) => {
21496
- console.log(`${prefix} ${message}`, data ? data : "");
21497
- },
21498
- warn: (message, data = null) => {
21499
- console.warn(`${prefix} ${message}`, data ? data : "");
21500
- },
21501
- error: (message, data = null) => {
21502
- console.error(`${prefix} ${message}`, data ? data : "");
21503
- },
21504
- group: (label) => console.group(`${prefix} ${label}`),
21505
- groupEnd: () => console.groupEnd(),
21506
- table: (data, title) => {
21507
- console.log(`${prefix} ${title}:`);
21508
- console.table(data);
21509
- }
21510
- };
22175
+ const getHeaderClasses = (status) => {
22176
+ switch (status) {
22177
+ case TaskState.DONE:
22178
+ return "bg-green-50 border-green-200 text-green-700";
22179
+ case TaskState.RUNNING:
22180
+ return "bg-amber-50 border-amber-200 text-amber-700";
22181
+ case TaskState.FAILED:
22182
+ return "bg-pink-50 border-pink-200 text-pink-700";
22183
+ default:
22184
+ return "bg-gray-100 border-gray-200 text-gray-700";
22185
+ }
21511
22186
  };
22187
+ const canShowRestart = (status) => {
22188
+ return status === TaskState.FAILED || status === TaskState.DONE;
22189
+ };
22190
+ const TaskCard = reactExports.memo(function TaskCard2({
22191
+ item,
22192
+ idx,
22193
+ nodeRef,
22194
+ status,
22195
+ isActive,
22196
+ canRestart,
22197
+ isSubmitting,
22198
+ getRestartDisabledReason,
22199
+ onClick,
22200
+ onKeyDown,
22201
+ handleRestartClick
22202
+ }) {
22203
+ const { startMs, endMs } = taskToTimerProps(item);
22204
+ const reducedMotion = prefersReducedMotion();
22205
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
22206
+ "div",
22207
+ {
22208
+ ref: nodeRef,
22209
+ role: "listitem",
22210
+ "aria-current": isActive ? "step" : void 0,
22211
+ tabIndex: 0,
22212
+ onClick,
22213
+ onKeyDown,
22214
+ className: `cursor-pointer rounded-lg border border-gray-400 ${status === TaskState.PENDING ? "bg-gray-50" : "bg-white"} overflow-hidden flex flex-col ${reducedMotion ? "" : "transition-all duration-200 ease-in-out"} outline outline-2 outline-transparent hover:outline-gray-400/70 focus-visible:outline-blue-500/60`,
22215
+ children: [
22216
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
22217
+ "div",
22218
+ {
22219
+ "data-role": "card-header",
22220
+ className: `rounded-t-lg px-4 py-2 border-b flex items-center justify-between gap-3 ${reducedMotion ? "" : "transition-opacity duration-300 ease-in-out"} ${getHeaderClasses(status)}`,
22221
+ children: [
22222
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-medium truncate", children: formatStepName(item, idx) }),
22223
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2", children: status === TaskState.RUNNING ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
22224
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative h-4 w-4", "aria-label": "Active", children: [
22225
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Active" }),
22226
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute inset-0 rounded-full border-2 border-amber-200" }),
22227
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
22228
+ "span",
22229
+ {
22230
+ className: `absolute inset-0 rounded-full border-2 border-transparent border-t-amber-600 ${reducedMotion ? "" : "animate-spin"}`
22231
+ }
22232
+ )
22233
+ ] }),
22234
+ item.stage && /* @__PURE__ */ jsxRuntimeExports.jsx(
22235
+ "span",
22236
+ {
22237
+ className: "text-[11px] font-medium opacity-80 truncate uppercase tracking-wide",
22238
+ title: item.stage,
22239
+ children: formatStageLabel(item.stage)
22240
+ }
22241
+ ),
22242
+ startMs && /* @__PURE__ */ jsxRuntimeExports.jsx(
22243
+ TimerText,
22244
+ {
22245
+ startMs,
22246
+ granularity: "second",
22247
+ className: "text-[11px] opacity-80"
22248
+ }
22249
+ )
22250
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
22251
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
22252
+ "span",
22253
+ {
22254
+ className: `text-[11px] uppercase tracking-wide opacity-80${reducedMotion ? "" : " transition-opacity duration-200"}`,
22255
+ children: [
22256
+ status,
22257
+ status === TaskState.FAILED && item.stage && /* @__PURE__ */ jsxRuntimeExports.jsxs(
22258
+ "span",
22259
+ {
22260
+ className: "text-[11px] font-medium opacity-80 truncate ml-2 uppercase tracking-wide",
22261
+ title: item.stage,
22262
+ children: [
22263
+ "(",
22264
+ formatStageLabel(item.stage),
22265
+ ")"
22266
+ ]
22267
+ }
22268
+ )
22269
+ ]
22270
+ }
22271
+ ),
22272
+ status === TaskState.DONE && startMs && /* @__PURE__ */ jsxRuntimeExports.jsx(
22273
+ TimerText,
22274
+ {
22275
+ startMs,
22276
+ endMs: endMs || item.finishedAt,
22277
+ granularity: "minute",
22278
+ className: "text-[11px] opacity-80"
22279
+ }
22280
+ )
22281
+ ] }) })
22282
+ ]
22283
+ }
22284
+ ),
22285
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4", children: [
22286
+ item.subtitle && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm text-gray-600", children: item.subtitle }),
22287
+ item.body && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 text-sm text-gray-700", children: item.body }),
22288
+ canShowRestart(status) && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-3 pt-3 border-t border-gray-100", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
22289
+ Button,
22290
+ {
22291
+ variant: "outline",
22292
+ size: "sm",
22293
+ onClick: (e2) => handleRestartClick(e2, item.id),
22294
+ disabled: !canRestart || isSubmitting,
22295
+ className: "text-xs cursor-pointer disabled:cursor-not-allowed",
22296
+ title: !canRestart ? getRestartDisabledReason() : `Restart job from ${item.id}`,
22297
+ children: "Restart"
22298
+ }
22299
+ ) })
22300
+ ] })
22301
+ ]
22302
+ }
22303
+ );
22304
+ });
21512
22305
  function DAGGrid({
21513
22306
  items,
21514
22307
  cols = 3,
@@ -21517,28 +22310,19 @@ function DAGGrid({
21517
22310
  jobId,
21518
22311
  filesByTypeForItem = () => createEmptyTaskFiles()
21519
22312
  }) {
21520
- const logger = React.useMemo(() => createDAGGridLogger(jobId), [jobId]);
21521
22313
  const overlayRef = reactExports.useRef(null);
21522
22314
  const gridRef = reactExports.useRef(null);
21523
22315
  const nodeRefs = reactExports.useRef([]);
21524
22316
  const [lines, setLines] = reactExports.useState([]);
21525
22317
  const [effectiveCols, setEffectiveCols] = reactExports.useState(cols);
21526
- const [openIdx, setOpenIdx] = reactExports.useState(null);
21527
- const [selectedFile, setSelectedFile] = reactExports.useState(null);
21528
- const [filePaneOpen, setFilePaneOpen] = reactExports.useState(false);
21529
- const [filePaneType, setFilePaneType] = reactExports.useState("artifacts");
21530
- const [filePaneFilename, setFilePaneFilename] = reactExports.useState(null);
21531
- React.useEffect(() => {
21532
- logger.group("Component Render");
21533
- logger.log("Props received:", {
21534
- itemCount: items?.length,
21535
- cols,
21536
- activeIndex,
21537
- jobId
21538
- });
21539
- logger.log("Items data:", items);
21540
- logger.groupEnd();
21541
- }, [items, cols, activeIndex, jobId, logger]);
22318
+ const [openIdx, setOpenIdx] = reactExports.useState(-1);
22319
+ const [restartModalOpen, setRestartModalOpen] = reactExports.useState(false);
22320
+ const [restartTaskId, setRestartTaskId] = reactExports.useState(null);
22321
+ const [isSubmitting, setIsSubmitting] = reactExports.useState(false);
22322
+ const [alertMessage, setAlertMessage] = reactExports.useState(null);
22323
+ const [alertType, setAlertType] = reactExports.useState("info");
22324
+ const prevGeometryRef = reactExports.useRef(null);
22325
+ const rafRef = reactExports.useRef(null);
21542
22326
  nodeRefs.current = reactExports.useMemo(
21543
22327
  () => items.map((_, i2) => nodeRefs.current[i2] ?? reactExports.createRef()),
21544
22328
  [items.length]
@@ -21564,9 +22348,10 @@ function DAGGrid({
21564
22348
  const end = Math.min(start + effectiveCols, items.length);
21565
22349
  const slice = Array.from({ length: end - start }, (_, k) => start + k);
21566
22350
  const rowLen = slice.length;
21567
- const pad = Math.max(0, effectiveCols - rowLen);
21568
- if (r2 % 2 === 1) {
22351
+ const isReversedRow = r2 % 2 === 1;
22352
+ if (isReversedRow) {
21569
22353
  const reversed = slice.reverse();
22354
+ const pad = effectiveCols - rowLen;
21570
22355
  order.push(...Array(pad).fill(-1), ...reversed);
21571
22356
  } else {
21572
22357
  order.push(...slice);
@@ -21578,11 +22363,11 @@ function DAGGrid({
21578
22363
  if (typeof window === "undefined" || !overlayRef.current || items.length === 0) {
21579
22364
  return;
21580
22365
  }
21581
- let isComputing = false;
21582
22366
  const compute = () => {
21583
- if (isComputing) return;
21584
- isComputing = true;
21585
- try {
22367
+ if (rafRef.current) {
22368
+ cancelAnimationFrame(rafRef.current);
22369
+ }
22370
+ rafRef.current = requestAnimationFrame(() => {
21586
22371
  if (!overlayRef.current) return;
21587
22372
  const overlayBox = overlayRef.current.getBoundingClientRect();
21588
22373
  const boxes = nodeRefs.current.map((r2) => {
@@ -21604,6 +22389,17 @@ function DAGGrid({
21604
22389
  headerMidY
21605
22390
  };
21606
22391
  });
22392
+ const currentGeometry = {
22393
+ overlayBox,
22394
+ boxes: boxes.filter(Boolean),
22395
+ effectiveCols,
22396
+ itemsLength: items.length
22397
+ };
22398
+ const geometryChanged = !prevGeometryRef.current || !areGeometriesEqual(prevGeometryRef.current, currentGeometry);
22399
+ if (!geometryChanged) {
22400
+ rafRef.current = null;
22401
+ return;
22402
+ }
21607
22403
  const newLines = [];
21608
22404
  for (let i2 = 0; i2 < items.length - 1; i2++) {
21609
22405
  const a2 = boxes[i2];
@@ -21638,10 +22434,10 @@ function DAGGrid({
21638
22434
  });
21639
22435
  }
21640
22436
  }
22437
+ prevGeometryRef.current = currentGeometry;
21641
22438
  setLines(newLines);
21642
- } finally {
21643
- isComputing = false;
21644
- }
22439
+ rafRef.current = null;
22440
+ });
21645
22441
  };
21646
22442
  compute();
21647
22443
  let ro = null;
@@ -21658,66 +22454,147 @@ function DAGGrid({
21658
22454
  if (ro) ro.disconnect();
21659
22455
  window.removeEventListener("resize", handleResize);
21660
22456
  window.removeEventListener("scroll", handleScroll, true);
22457
+ if (rafRef.current) {
22458
+ cancelAnimationFrame(rafRef.current);
22459
+ }
21661
22460
  };
21662
22461
  }, [items, effectiveCols, visualOrder]);
21663
22462
  const getStatus = (index2) => {
21664
22463
  const item = items[index2];
21665
22464
  const s2 = item?.status;
21666
- if (s2 === "failed") return "failed";
21667
- if (s2 === "done") return "done";
21668
- if (s2 === "running") return "running";
22465
+ if (s2 === TaskState.FAILED) return TaskState.FAILED;
22466
+ if (s2 === TaskState.DONE) return TaskState.DONE;
22467
+ if (s2 === TaskState.RUNNING) return TaskState.RUNNING;
21669
22468
  if (typeof activeIndex === "number") {
21670
- if (index2 < activeIndex) return "done";
21671
- if (index2 === activeIndex) return "running";
21672
- return "pending";
21673
- }
21674
- return "pending";
21675
- };
21676
- const getHeaderClasses = (status) => {
21677
- switch (status) {
21678
- case "done":
21679
- return "bg-green-50 border-green-200 text-green-700";
21680
- case "running":
21681
- return "bg-amber-50 border-amber-200 text-amber-700";
21682
- case "failed":
21683
- return "bg-pink-50 border-pink-200 text-pink-700";
21684
- default:
21685
- return "bg-gray-100 border-gray-200 text-gray-700";
22469
+ if (index2 < activeIndex) return TaskState.DONE;
22470
+ if (index2 === activeIndex) return TaskState.RUNNING;
22471
+ return TaskState.PENDING;
21686
22472
  }
22473
+ return TaskState.PENDING;
21687
22474
  };
21688
22475
  React.useEffect(() => {
21689
22476
  const handleKeyDown = (e2) => {
21690
- if (e2.key === "Escape" && openIdx !== null) {
21691
- setOpenIdx(null);
21692
- setSelectedFile(null);
22477
+ if (e2.key === "Escape" && openIdx !== -1) {
22478
+ setOpenIdx(-1);
21693
22479
  }
21694
22480
  };
21695
- if (openIdx !== null) {
22481
+ if (openIdx !== -1) {
21696
22482
  document.addEventListener("keydown", handleKeyDown);
21697
22483
  return () => document.removeEventListener("keydown", handleKeyDown);
21698
22484
  }
21699
22485
  }, [openIdx]);
21700
- const closeButtonRef = reactExports.useRef(null);
21701
- React.useEffect(() => {
21702
- if (openIdx !== null && closeButtonRef.current) {
21703
- closeButtonRef.current.focus();
21704
- }
21705
- }, [openIdx]);
21706
- React.useEffect(() => {
21707
- setFilePaneFilename(null);
21708
- setFilePaneOpen(false);
21709
- }, [filePaneType]);
21710
- React.useEffect(() => {
21711
- if (openIdx === null) {
21712
- setFilePaneFilename(null);
21713
- setFilePaneOpen(false);
21714
- return;
22486
+ const handleRestartClick = (e2, taskId) => {
22487
+ e2.stopPropagation();
22488
+ setRestartTaskId(taskId);
22489
+ setRestartModalOpen(true);
22490
+ };
22491
+ const handleRestartConfirm = async () => {
22492
+ if (!jobId || isSubmitting) return;
22493
+ setIsSubmitting(true);
22494
+ setAlertMessage(null);
22495
+ try {
22496
+ const restartOptions = {};
22497
+ if (restartTaskId) {
22498
+ restartOptions.fromTask = restartTaskId;
22499
+ }
22500
+ await restartJob(jobId, restartOptions);
22501
+ const successMessage = restartTaskId ? `Restart requested from ${restartTaskId}. The job will start from that task in the background.` : "Restart requested. The job will reset to pending and start in the background.";
22502
+ setAlertMessage(successMessage);
22503
+ setAlertType("success");
22504
+ setRestartModalOpen(false);
22505
+ setRestartTaskId(null);
22506
+ } catch (error) {
22507
+ let message = "Failed to start restart. Try again.";
22508
+ let type = "error";
22509
+ switch (error.code) {
22510
+ case "job_running":
22511
+ message = "Job is currently running; restart is unavailable.";
22512
+ type = "warning";
22513
+ break;
22514
+ case "unsupported_lifecycle":
22515
+ message = "Job must be in current lifecycle to restart.";
22516
+ type = "warning";
22517
+ break;
22518
+ case "job_not_found":
22519
+ message = "Job not found.";
22520
+ type = "error";
22521
+ break;
22522
+ case "spawn_failed":
22523
+ message = "Failed to start restart. Try again.";
22524
+ type = "error";
22525
+ break;
22526
+ default:
22527
+ message = error.message || "An unexpected error occurred.";
22528
+ type = "error";
22529
+ }
22530
+ setAlertMessage(message);
22531
+ setAlertType(type);
22532
+ } finally {
22533
+ setIsSubmitting(false);
21715
22534
  }
21716
- setFilePaneType("artifacts");
21717
- setFilePaneFilename(null);
21718
- setFilePaneOpen(false);
21719
- }, [openIdx]);
22535
+ };
22536
+ const handleRestartCancel = () => {
22537
+ setRestartModalOpen(false);
22538
+ setRestartTaskId(null);
22539
+ };
22540
+ React.useEffect(() => {
22541
+ if (alertMessage) {
22542
+ const timer = setTimeout(() => {
22543
+ setAlertMessage(null);
22544
+ }, 5e3);
22545
+ return () => clearTimeout(timer);
22546
+ }
22547
+ }, [alertMessage]);
22548
+ const isRestartEnabled = React.useCallback(() => {
22549
+ const isJobRunning = items.some(
22550
+ (item) => item?.state === TaskState.RUNNING
22551
+ );
22552
+ const hasRunningTask = items.some(
22553
+ (item) => item?.status === TaskState.RUNNING
22554
+ );
22555
+ const jobLifecycle = items[0]?.lifecycle || "current";
22556
+ return jobLifecycle === "current" && !isJobRunning && !hasRunningTask;
22557
+ }, [items]);
22558
+ const getRestartDisabledReason = React.useCallback(() => {
22559
+ const isJobRunning = items.some(
22560
+ (item) => item?.state === TaskState.RUNNING
22561
+ );
22562
+ const hasRunningTask = items.some(
22563
+ (item) => item?.status === TaskState.RUNNING
22564
+ );
22565
+ const jobLifecycle = items[0]?.lifecycle || "current";
22566
+ if (isJobRunning || hasRunningTask) return "Job is currently running";
22567
+ if (jobLifecycle !== "current") return "Job must be in current lifecycle";
22568
+ return "";
22569
+ }, [items]);
21720
22570
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-full", role: "list", children: [
22571
+ alertMessage && /* @__PURE__ */ jsxRuntimeExports.jsx(
22572
+ "div",
22573
+ {
22574
+ className: `fixed top-4 right-4 z-[3000] max-w-sm p-4 rounded-lg shadow-lg border ${alertType === "success" ? "bg-green-50 border-green-200 text-green-800" : alertType === "error" ? "bg-red-50 border-red-200 text-red-800" : alertType === "warning" ? "bg-yellow-50 border-yellow-200 text-yellow-800" : "bg-blue-50 border-blue-200 text-blue-800"}`,
22575
+ role: "alert",
22576
+ "aria-live": "polite",
22577
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start", children: [
22578
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium", children: alertMessage }) }),
22579
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
22580
+ "button",
22581
+ {
22582
+ onClick: () => setAlertMessage(null),
22583
+ className: "ml-3 flex-shrink-0 inline-flex text-gray-400 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500",
22584
+ "aria-label": "Dismiss notification",
22585
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "h-4 w-4", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
22586
+ "path",
22587
+ {
22588
+ fillRule: "evenodd",
22589
+ d: "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",
22590
+ clipRule: "evenodd"
22591
+ }
22592
+ ) })
22593
+ }
22594
+ )
22595
+ ] })
22596
+ }
22597
+ ),
21721
22598
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
21722
22599
  "svg",
21723
22600
  {
@@ -21736,7 +22613,8 @@ function DAGGrid({
21736
22613
  markerHeight: "8",
21737
22614
  orient: "auto",
21738
22615
  markerUnits: "userSpaceOnUse",
21739
- children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: "#9ca3af" })
22616
+ className: "text-gray-400",
22617
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M 0 0 L 10 5 L 0 10 z" })
21740
22618
  }
21741
22619
  ) }),
21742
22620
  lines.map((line, idx) => /* @__PURE__ */ jsxRuntimeExports.jsx("g", { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -21745,9 +22623,9 @@ function DAGGrid({
21745
22623
  d: line.d,
21746
22624
  fill: "none",
21747
22625
  stroke: "currentColor",
21748
- strokeWidth: "3",
21749
- strokeLinecap: "round",
21750
- className: "text-gray-300",
22626
+ strokeWidth: "1",
22627
+ strokeLinecap: "square",
22628
+ className: "text-gray-400",
21751
22629
  strokeLinejoin: "round",
21752
22630
  markerEnd: "url(#arrow)"
21753
22631
  }
@@ -21774,195 +22652,58 @@ function DAGGrid({
21774
22652
  const item = items[idx];
21775
22653
  const status = getStatus(idx);
21776
22654
  const isActive = idx === activeIndex;
21777
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
21778
- "div",
22655
+ const canRestart = isRestartEnabled();
22656
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
22657
+ TaskCard,
21779
22658
  {
21780
- ref: nodeRefs.current[idx],
21781
- role: "listitem",
21782
- "aria-current": isActive ? "step" : void 0,
21783
- tabIndex: 0,
22659
+ idx,
22660
+ nodeRef: nodeRefs.current[idx],
22661
+ status,
22662
+ isActive,
22663
+ canRestart,
22664
+ isSubmitting,
22665
+ getRestartDisabledReason,
21784
22666
  onClick: () => {
21785
22667
  setOpenIdx(idx);
21786
- setSelectedFile(null);
21787
22668
  },
21788
22669
  onKeyDown: (e2) => {
21789
22670
  if (e2.key === "Enter" || e2.key === " ") {
21790
22671
  e2.preventDefault();
21791
22672
  setOpenIdx(idx);
21792
- setSelectedFile(null);
21793
22673
  }
21794
22674
  },
21795
- className: `cursor-pointer rounded-lg border border-gray-400 bg-white overflow-hidden flex flex-col transition outline outline-2 outline-transparent hover:outline-gray-400/70 focus-visible:outline-blue-500/60 ${cardClass}`,
21796
- children: [
21797
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
21798
- "div",
21799
- {
21800
- "data-role": "card-header",
21801
- className: `rounded-t-lg px-4 py-2 border-b flex items-center justify-between gap-3 ${getHeaderClasses(status)}`,
21802
- children: [
21803
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-medium truncate", children: formatStepName(item, idx) }),
21804
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2", children: status === "running" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
21805
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative h-4 w-4", "aria-label": "Running", children: [
21806
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Running" }),
21807
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute inset-0 rounded-full border-2 border-amber-200" }),
21808
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute inset-0 rounded-full border-2 border-transparent border-t-amber-600 animate-spin" })
21809
- ] }),
21810
- item.stage && /* @__PURE__ */ jsxRuntimeExports.jsx(
21811
- "span",
21812
- {
21813
- className: "text-[11px] font-medium opacity-80 truncate",
21814
- title: item.stage,
21815
- children: formatStageLabel(item.stage)
21816
- }
21817
- )
21818
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[11px] uppercase tracking-wide opacity-80", children: [
21819
- status,
21820
- status === "failed" && item.stage && /* @__PURE__ */ jsxRuntimeExports.jsxs(
21821
- "span",
21822
- {
21823
- className: "text-[11px] font-medium opacity-80 truncate ml-2",
21824
- title: item.stage,
21825
- children: [
21826
- "(",
21827
- formatStageLabel(item.stage),
21828
- ")"
21829
- ]
21830
- }
21831
- )
21832
- ] }) })
21833
- ]
21834
- }
21835
- ),
21836
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4", children: [
21837
- item.subtitle && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm text-gray-600", children: item.subtitle }),
21838
- item.body && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 text-sm text-gray-700", children: item.body })
21839
- ] })
21840
- ]
22675
+ handleRestartClick,
22676
+ item
21841
22677
  },
21842
22678
  item.id ?? idx
21843
22679
  );
21844
22680
  })
21845
22681
  }
21846
22682
  ),
22683
+ openIdx !== -1 && /* @__PURE__ */ jsxRuntimeExports.jsx(
22684
+ TaskDetailSidebar,
22685
+ {
22686
+ open: openIdx !== -1,
22687
+ title: formatStepName(items[openIdx], openIdx),
22688
+ status: getStatus(openIdx),
22689
+ jobId,
22690
+ taskId: items[openIdx]?.id || `task-${openIdx}`,
22691
+ taskBody: items[openIdx]?.body || null,
22692
+ filesByTypeForItem,
22693
+ task: items[openIdx],
22694
+ taskIndex: openIdx,
22695
+ onClose: () => setOpenIdx(-1)
22696
+ }
22697
+ ),
21847
22698
  /* @__PURE__ */ jsxRuntimeExports.jsx(
21848
- "aside",
22699
+ RestartJobModal,
21849
22700
  {
21850
- role: "dialog",
21851
- "aria-modal": "true",
21852
- "aria-labelledby": `slide-over-title-${openIdx}`,
21853
- "aria-hidden": openIdx === null,
21854
- className: `fixed inset-y-0 right-0 z-[2000] w-full max-w-4xl bg-white border-l border-gray-200 transform transition-transform duration-300 ease-out ${openIdx !== null ? "translate-x-0" : "translate-x-full"}`,
21855
- children: openIdx !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
21856
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
21857
- "div",
21858
- {
21859
- className: `px-6 py-4 border-b flex items-center justify-between ${getHeaderClasses(getStatus(openIdx))}`,
21860
- children: [
21861
- /* @__PURE__ */ jsxRuntimeExports.jsx(
21862
- "div",
21863
- {
21864
- id: `slide-over-title-${openIdx}`,
21865
- className: "text-lg font-semibold truncate",
21866
- children: formatStepName(items[openIdx], openIdx)
21867
- }
21868
- ),
21869
- /* @__PURE__ */ jsxRuntimeExports.jsx(
21870
- "button",
21871
- {
21872
- ref: closeButtonRef,
21873
- type: "button",
21874
- "aria-label": "Close details",
21875
- onClick: () => {
21876
- setOpenIdx(null);
21877
- setSelectedFile(null);
21878
- },
21879
- className: "rounded-md border border-gray-300 text-gray-700 hover:bg-gray-50 px-3 py-1.5 text-base",
21880
- children: "×"
21881
- }
21882
- )
21883
- ]
21884
- }
21885
- ),
21886
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 space-y-8 overflow-y-auto h-full", children: [
21887
- items[openIdx]?.status === "failed" && items[openIdx]?.body && /* @__PURE__ */ jsxRuntimeExports.jsx("section", { "aria-label": "Error", children: /* @__PURE__ */ jsxRuntimeExports.jsx(n$2, { role: "alert", "aria-live": "assertive", children: /* @__PURE__ */ jsxRuntimeExports.jsx(u$1, { className: "whitespace-pre-wrap break-words", children: items[openIdx].body }) }) }),
21888
- /* @__PURE__ */ jsxRuntimeExports.jsx("section", { className: "mt-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-4", children: [
21889
- /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-base font-semibold text-gray-900", children: "Files" }),
21890
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center space-x-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex rounded-lg border border-gray-200 bg-gray-50 p-1", children: [
21891
- /* @__PURE__ */ jsxRuntimeExports.jsx(
21892
- "button",
21893
- {
21894
- onClick: () => setFilePaneType("artifacts"),
21895
- className: `px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${filePaneType === "artifacts" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600 hover:text-gray-900"}`,
21896
- children: "Artifacts"
21897
- }
21898
- ),
21899
- /* @__PURE__ */ jsxRuntimeExports.jsx(
21900
- "button",
21901
- {
21902
- onClick: () => setFilePaneType("logs"),
21903
- className: `px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${filePaneType === "logs" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600 hover:text-gray-900"}`,
21904
- children: "Logs"
21905
- }
21906
- ),
21907
- /* @__PURE__ */ jsxRuntimeExports.jsx(
21908
- "button",
21909
- {
21910
- onClick: () => setFilePaneType("tmp"),
21911
- className: `px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${filePaneType === "tmp" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600 hover:text-gray-900"}`,
21912
- children: "Temp"
21913
- }
21914
- )
21915
- ] }) })
21916
- ] }) }),
21917
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
21918
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm text-gray-600", children: [
21919
- filePaneType.charAt(0).toUpperCase() + filePaneType.slice(1),
21920
- " ",
21921
- "files for ",
21922
- items[openIdx]?.id || `Task ${openIdx + 1}`
21923
- ] }),
21924
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1", children: (() => {
21925
- const filesForStep = filesByTypeForItem(items[openIdx]);
21926
- const filesForTab = filesForStep[filePaneType] ?? [];
21927
- if (filesForTab.length === 0) {
21928
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-sm text-gray-500 italic py-4 text-center", children: [
21929
- "No ",
21930
- filePaneType,
21931
- " files available for this task"
21932
- ] });
21933
- }
21934
- return filesForTab.map((name) => {
21935
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
21936
- "div",
21937
- {
21938
- className: "flex items-center justify-between p-2 rounded border border-gray-200 hover:border-gray-300 hover:bg-gray-50 cursor-pointer transition-colors",
21939
- onClick: () => {
21940
- setFilePaneFilename(name);
21941
- setFilePaneOpen(true);
21942
- },
21943
- children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center space-x-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-700", children: name }) })
21944
- },
21945
- `${filePaneType}-${name}`
21946
- );
21947
- });
21948
- })() })
21949
- ] }),
21950
- /* @__PURE__ */ jsxRuntimeExports.jsx(
21951
- TaskFilePane,
21952
- {
21953
- isOpen: filePaneOpen,
21954
- jobId,
21955
- taskId: items[openIdx]?.id || `task-${openIdx}`,
21956
- type: filePaneType,
21957
- filename: filePaneFilename,
21958
- onClose: () => {
21959
- setFilePaneOpen(false);
21960
- setFilePaneFilename(null);
21961
- }
21962
- }
21963
- )
21964
- ] })
21965
- ] })
22701
+ open: restartModalOpen,
22702
+ onClose: handleRestartCancel,
22703
+ onConfirm: handleRestartConfirm,
22704
+ jobId,
22705
+ taskId: restartTaskId,
22706
+ isSubmitting
21966
22707
  }
21967
22708
  )
21968
22709
  ] });
@@ -22003,7 +22744,7 @@ function computeDagItems(job, pipeline) {
22003
22744
  const jobTask = jobTasks[taskId];
22004
22745
  return {
22005
22746
  id: taskId,
22006
- status: jobTask ? jobTask.state : "pending",
22747
+ status: jobTask ? jobTask.state : TaskState.PENDING,
22007
22748
  source: "pipeline",
22008
22749
  stage: computeTaskStage(job, taskId)
22009
22750
  };
@@ -22026,28 +22767,35 @@ function computeDagItems(job, pipeline) {
22026
22767
  function computeActiveIndex(items) {
22027
22768
  if (!items || items.length === 0) return 0;
22028
22769
  const firstRunningIndex = items.findIndex(
22029
- (item) => item.status === "running"
22770
+ (item) => item.status === TaskState.RUNNING
22030
22771
  );
22031
22772
  if (firstRunningIndex !== -1) return firstRunningIndex;
22032
- const firstFailedIndex = items.findIndex((item) => item.status === "failed");
22773
+ const firstFailedIndex = items.findIndex(
22774
+ (item) => item.status === TaskState.FAILED
22775
+ );
22033
22776
  if (firstFailedIndex !== -1) return firstFailedIndex;
22034
22777
  let lastDoneIndex = -1;
22035
22778
  items.forEach((item, index2) => {
22036
- if (item.status === "done") lastDoneIndex = index2;
22779
+ if (item.status === TaskState.DONE) lastDoneIndex = index2;
22037
22780
  });
22038
22781
  if (lastDoneIndex !== -1) return lastDoneIndex;
22039
22782
  return 0;
22040
22783
  }
22041
- function JobDetail({ job, pipeline, onClose, onResume }) {
22042
- const now = useTicker(1e3);
22043
- const [resumeFrom, setResumeFrom] = reactExports.useState(
22044
- pipeline?.tasks?.[0] ? typeof pipeline.tasks[0] === "string" ? pipeline.tasks[0] : pipeline.tasks[0].id ?? pipeline.tasks[0].name ?? "" : ""
22045
- );
22046
- reactExports.useEffect(() => {
22047
- setResumeFrom(
22048
- pipeline?.tasks?.[0] ? typeof pipeline.tasks[0] === "string" ? pipeline.tasks[0] : pipeline.tasks[0].id ?? pipeline.tasks[0].name ?? "" : ""
22049
- );
22050
- }, [job.id, pipeline?.tasks?.length]);
22784
+ function formatCurrency4(x) {
22785
+ if (typeof x !== "number" || x === 0) return "$0.0000";
22786
+ const formatted = x.toFixed(4);
22787
+ return `$${formatted.replace(/\.?0+$/, "")}`;
22788
+ }
22789
+ function formatTokensCompact(n2) {
22790
+ if (typeof n2 !== "number" || n2 === 0) return "0 tok";
22791
+ if (n2 >= 1e6) {
22792
+ return `${(n2 / 1e6).toFixed(1).replace(/\.0$/, "")}M tokens`;
22793
+ } else if (n2 >= 1e3) {
22794
+ return `${(n2 / 1e3).toFixed(1).replace(/\.0$/, "")}k tokens`;
22795
+ }
22796
+ return `${n2} tokens`;
22797
+ }
22798
+ function JobDetail({ job, pipeline }) {
22051
22799
  const taskById = React.useMemo(() => {
22052
22800
  const tasks = job?.tasks;
22053
22801
  let result;
@@ -22082,18 +22830,9 @@ function JobDetail({ job, pipeline, onClose, onResume }) {
22082
22830
  }
22083
22831
  return result;
22084
22832
  }, [pipeline, job?.tasks]);
22085
- const dagItems = React.useMemo(() => {
22833
+ const stableDagItems = React.useMemo(() => {
22086
22834
  const rawDagItems = computeDagItems(job, computedPipeline);
22087
- const processedItems = rawDagItems.map((item, index2) => {
22088
- {
22089
- console.debug("[JobDetail] computed DAG item", {
22090
- id: item.id,
22091
- status: item.status,
22092
- stage: item.stage,
22093
- jobHasTasks: !!job?.tasks,
22094
- taskKeys: job?.tasks ? Object.keys(job.tasks) : null
22095
- });
22096
- }
22835
+ return rawDagItems.map((item, index2) => {
22097
22836
  const task = taskById[item.id];
22098
22837
  const taskConfig = task?.config || {};
22099
22838
  const subtitleParts = [];
@@ -22103,30 +22842,41 @@ function JobDetail({ job, pipeline, onClose, onResume }) {
22103
22842
  if (taskConfig?.temperature != null) {
22104
22843
  subtitleParts.push(`temp ${taskConfig.temperature}`);
22105
22844
  }
22106
- if (task?.attempts != null) {
22107
- subtitleParts.push(`${task.attempts} attempts`);
22108
- }
22109
22845
  if (task?.refinementAttempts != null) {
22110
22846
  subtitleParts.push(`${task.refinementAttempts} refinements`);
22111
22847
  }
22112
- if (task?.startedAt) {
22113
- const durationMs = taskDisplayDurationMs(task, now);
22114
- if (durationMs > 0) {
22115
- subtitleParts.push(fmtDuration(durationMs));
22116
- }
22848
+ const taskBreakdown = job?.costs?.taskBreakdown?.[item.id]?.summary || {};
22849
+ if (taskBreakdown.totalTokens > 0) {
22850
+ subtitleParts.push(formatTokensCompact(taskBreakdown.totalTokens));
22851
+ }
22852
+ if (taskBreakdown.totalCost > 0) {
22853
+ subtitleParts.push(formatCurrency4(taskBreakdown.totalCost));
22117
22854
  }
22118
22855
  const errorMsg = task?.error?.message;
22119
- const body = item.status === "failed" && errorMsg ? errorMsg : null;
22120
- const resultItem = {
22856
+ const body = (item.status === "failed" || item.status === "error") && errorMsg ? errorMsg : null;
22857
+ return {
22121
22858
  ...item,
22122
22859
  title: typeof item.id === "string" ? item.id : item.id?.name || item.id?.id || `Task ${item.id}`,
22123
22860
  subtitle: subtitleParts.length > 0 ? subtitleParts.join(" · ") : null,
22124
- body
22861
+ body,
22862
+ startedAt: task?.startedAt,
22863
+ endedAt: task?.endedAt
22125
22864
  };
22126
- return resultItem;
22127
22865
  });
22128
- return processedItems;
22129
- }, [job, computedPipeline, taskById, now]);
22866
+ }, [job, computedPipeline, taskById]);
22867
+ const prevDagItemsRef = React.useRef([]);
22868
+ const dagItems = React.useMemo(() => {
22869
+ const prevItems = prevDagItemsRef.current;
22870
+ const newItems = stableDagItems.map((item, index2) => {
22871
+ const prevItem = prevItems[index2];
22872
+ if (prevItem && prevItem.id === item.id && prevItem.status === item.status && prevItem.stage === item.stage && prevItem.title === item.title && prevItem.subtitle === item.subtitle && prevItem.body === item.body) {
22873
+ return prevItem;
22874
+ }
22875
+ return item;
22876
+ });
22877
+ prevDagItemsRef.current = newItems;
22878
+ return newItems;
22879
+ }, [stableDagItems]);
22130
22880
  const activeIndex = React.useMemo(() => {
22131
22881
  const index2 = computeActiveIndex(dagItems);
22132
22882
  return index2;
@@ -22149,40 +22899,6 @@ function JobDetail({ job, pipeline, onClose, onResume }) {
22149
22899
  ) });
22150
22900
  }
22151
22901
  const REFRESH_DEBOUNCE_MS = 200;
22152
- const createHookLogger = (jobId) => {
22153
- const prefix = `[useJobDetailWithUpdates:${jobId || "unknown"}]`;
22154
- return {
22155
- log: (message, data = null) => {
22156
- console.log(`${prefix} ${message}`, data ? data : "");
22157
- },
22158
- warn: (message, data = null) => {
22159
- console.warn(`${prefix} ${message}`, data ? data : "");
22160
- },
22161
- error: (message, data = null) => {
22162
- console.error(`${prefix} ${message}`, data ? data : "");
22163
- },
22164
- group: (label) => console.group(`${prefix} ${label}`),
22165
- groupEnd: () => console.groupEnd(),
22166
- table: (data, title) => {
22167
- console.log(`${prefix} ${title}:`);
22168
- console.table(data);
22169
- },
22170
- sse: (eventType, eventData) => {
22171
- console.log(
22172
- `%c${prefix} SSE Event: ${eventType}`,
22173
- "color: #0066cc; font-weight: bold;",
22174
- eventData
22175
- );
22176
- },
22177
- state: (stateName, value) => {
22178
- console.log(
22179
- `%c${prefix} State Change: ${stateName}`,
22180
- "color: #006600; font-weight: bold;",
22181
- value
22182
- );
22183
- }
22184
- };
22185
- };
22186
22902
  async function fetchJobDetail(jobId, { signal } = {}) {
22187
22903
  const response = await fetch(`/api/jobs/${jobId}`, { signal });
22188
22904
  if (!response.ok) {
@@ -22267,90 +22983,63 @@ function matchesJobTasksStatusPath(path, jobId) {
22267
22983
  }
22268
22984
  }
22269
22985
  function useJobDetailWithUpdates(jobId) {
22270
- const logger = reactExports.useMemo(() => createHookLogger(jobId), [jobId]);
22271
22986
  const [data, setData] = reactExports.useState(null);
22272
22987
  const [loading, setLoading] = reactExports.useState(true);
22273
22988
  const [error, setError] = reactExports.useState(null);
22274
22989
  const [connectionStatus, setConnectionStatus] = reactExports.useState("disconnected");
22990
+ const [isRefreshing, setIsRefreshing] = reactExports.useState(false);
22991
+ const [isPending, startTransition] = reactExports.useTransition();
22992
+ const [isHydrated, setIsHydrated] = reactExports.useState(false);
22275
22993
  const esRef = reactExports.useRef(null);
22276
22994
  const reconnectTimer = reactExports.useRef(null);
22277
22995
  const hydratedRef = reactExports.useRef(false);
22278
22996
  const eventQueue = reactExports.useRef([]);
22279
22997
  const mountedRef = reactExports.useRef(true);
22280
22998
  const refetchTimerRef = reactExports.useRef(null);
22281
- reactExports.useEffect(() => {
22282
- logger.group("Hook Initialization");
22283
- logger.log("Job ID:", jobId);
22284
- logger.log("Initial state:", { data, loading, error, connectionStatus });
22285
- logger.groupEnd();
22286
- }, [jobId, logger]);
22287
- reactExports.useEffect(() => {
22288
- logger.state("data", data);
22289
- }, [data, logger]);
22290
- reactExports.useEffect(() => {
22291
- logger.state("loading", loading);
22292
- }, [loading, logger]);
22293
- reactExports.useEffect(() => {
22294
- logger.state("error", error);
22295
- }, [error, logger]);
22296
- reactExports.useEffect(() => {
22297
- logger.state("connectionStatus", connectionStatus);
22298
- }, [connectionStatus, logger]);
22299
22999
  const scheduleDebouncedRefetch = reactExports.useCallback(
22300
23000
  (context = {}) => {
22301
- logger.group("Debounced Refetch Request");
22302
- logger.log("Request context:", context);
22303
- logger.log("Scheduling debounced refetch");
22304
23001
  if (refetchTimerRef.current) {
22305
- logger.log("Clearing existing refetch timer");
22306
23002
  clearTimeout(refetchTimerRef.current);
22307
23003
  }
22308
23004
  refetchTimerRef.current = setTimeout(async () => {
22309
23005
  if (!mountedRef.current || !hydratedRef.current) {
22310
- logger.warn(
22311
- "Refetch aborted - component not mounted or not hydrated",
22312
- { mounted: mountedRef.current, hydrated: hydratedRef.current }
22313
- );
22314
- logger.groupEnd();
22315
23006
  return;
22316
23007
  }
22317
- logger.log("Executing debounced refetch");
22318
- logger.log("Refetch jobId:", jobId);
22319
23008
  const abortController = new AbortController();
22320
23009
  try {
23010
+ startTransition(() => {
23011
+ setIsRefreshing(true);
23012
+ });
22321
23013
  const jobData = await fetchJobDetail(jobId, {
22322
23014
  signal: abortController.signal
22323
23015
  });
22324
- logger.log("Refetch response received");
22325
- logger.log("Refetch job data preview:", {
22326
- status: jobData?.status,
22327
- hasTasks: !!jobData?.tasks,
22328
- taskKeys: jobData?.tasks ? Object.keys(jobData.tasks) : [],
22329
- hasTasksStatus: !!jobData?.tasksStatus
22330
- });
22331
23016
  if (mountedRef.current) {
22332
- logger.log("Refetch successful, updating data");
22333
- setData(jobData);
22334
- setError(null);
22335
- } else {
22336
- logger.warn("Refetch completed but component is unmounted");
23017
+ startTransition(() => {
23018
+ setData(jobData);
23019
+ setError(null);
23020
+ setIsRefreshing(false);
23021
+ });
22337
23022
  }
22338
23023
  } catch (err) {
22339
- logger.error("Failed to refetch job detail:", err);
22340
- if (mountedRef.current) setError(err.message);
23024
+ if (mountedRef.current) {
23025
+ startTransition(() => {
23026
+ setError(err.message);
23027
+ setIsRefreshing(false);
23028
+ });
23029
+ }
22341
23030
  } finally {
22342
23031
  refetchTimerRef.current = null;
22343
- logger.groupEnd();
22344
23032
  }
22345
23033
  }, REFRESH_DEBOUNCE_MS);
22346
23034
  },
22347
- [jobId, logger]
23035
+ [jobId]
22348
23036
  );
22349
23037
  reactExports.useEffect(() => {
22350
23038
  setData(null);
22351
23039
  setLoading(true);
22352
23040
  setError(null);
22353
23041
  setConnectionStatus("disconnected");
23042
+ setIsHydrated(false);
22354
23043
  hydratedRef.current = false;
22355
23044
  eventQueue.current = [];
22356
23045
  if (refetchTimerRef.current) {
@@ -22361,92 +23050,80 @@ function useJobDetailWithUpdates(jobId) {
22361
23050
  reactExports.useEffect(() => {
22362
23051
  if (!jobId || !mountedRef.current) return;
22363
23052
  const doFetch = async () => {
22364
- logger.group("Initial Data Fetch");
22365
23053
  try {
22366
- setLoading(true);
23054
+ if (!hydratedRef.current) {
23055
+ setLoading(true);
23056
+ }
22367
23057
  setError(null);
22368
- logger.log("Starting initial job data fetch");
22369
23058
  const jobData = await fetchJobDetail(jobId);
22370
- logger.log("Initial fetch successful", jobData);
22371
23059
  let finalData = jobData;
22372
23060
  let queuedNeedsRefetch = false;
22373
23061
  if (eventQueue.current.length > 0) {
22374
- logger.log(`Processing ${eventQueue.current.length} queued events`);
22375
23062
  for (const ev of eventQueue.current) {
22376
- logger.log("Processing queued event:", ev);
22377
23063
  if (ev.type === "state:change") {
22378
23064
  const d2 = ev.payload && (ev.payload.data || ev.payload) || {};
22379
23065
  if (typeof d2.path === "string" && matchesJobTasksStatusPath(d2.path, jobId)) {
22380
- logger.log(
22381
- "Queued state:change matches tasks-status path, scheduling refetch"
22382
- );
22383
23066
  queuedNeedsRefetch = true;
22384
23067
  continue;
22385
23068
  }
22386
23069
  }
22387
23070
  finalData = applyJobEvent(finalData, ev, jobId);
22388
- logger.log("Applied queued event, result:", finalData);
22389
23071
  }
22390
23072
  eventQueue.current = [];
22391
23073
  }
22392
23074
  if (mountedRef.current) {
22393
- logger.log("Updating state with final data");
22394
- setData(finalData);
22395
- setError(null);
22396
- hydratedRef.current = true;
22397
- logger.log("Component hydrated");
23075
+ startTransition(() => {
23076
+ setData(finalData);
23077
+ setError(null);
23078
+ const wasHydrated = hydratedRef.current;
23079
+ hydratedRef.current = true;
23080
+ if (!wasHydrated) {
23081
+ setIsHydrated(true);
23082
+ setLoading(false);
23083
+ }
23084
+ });
22398
23085
  if (queuedNeedsRefetch) {
22399
- logger.log("Scheduling refetch for queued path changes");
22400
23086
  scheduleDebouncedRefetch();
22401
23087
  }
22402
23088
  }
22403
23089
  } catch (err) {
22404
- logger.error("Failed to fetch job detail:", err);
22405
23090
  if (mountedRef.current) {
22406
- setError(err.message);
22407
- setData(null);
23091
+ startTransition(() => {
23092
+ setError(err.message);
23093
+ setData(null);
23094
+ if (!hydratedRef.current) {
23095
+ setLoading(false);
23096
+ }
23097
+ });
22408
23098
  }
22409
23099
  } finally {
22410
- if (mountedRef.current) {
23100
+ if (mountedRef.current && !hydratedRef.current) {
22411
23101
  setLoading(false);
22412
23102
  }
22413
- logger.groupEnd();
22414
23103
  }
22415
23104
  };
22416
23105
  doFetch();
22417
- }, [jobId, scheduleDebouncedRefetch, logger]);
23106
+ }, [jobId, scheduleDebouncedRefetch]);
22418
23107
  reactExports.useEffect(() => {
22419
23108
  if (!jobId) {
22420
- logger.log("SSE setup skipped - no jobId available", {
22421
- hasJobId: !!jobId,
22422
- hasExistingEs: !!esRef.current,
22423
- isMounted: mountedRef.current
22424
- });
22425
23109
  return void 0;
22426
23110
  }
22427
23111
  if (esRef.current) {
22428
- logger.log("Closing existing EventSource before reinitializing");
22429
23112
  try {
22430
23113
  esRef.current.close();
22431
23114
  } catch (err) {
22432
- logger.warn("Error closing existing EventSource during reinit", err);
22433
23115
  }
22434
23116
  esRef.current = null;
22435
23117
  }
22436
- logger.group("SSE Connection Setup");
22437
- logger.log("Setting up SSE connection for job:", jobId);
22438
23118
  const attachListeners = (es) => {
22439
23119
  const onOpen = () => {
22440
- logger.log("SSE connection opened");
22441
23120
  if (mountedRef.current) {
22442
23121
  setConnectionStatus("connected");
22443
23122
  }
22444
23123
  };
22445
23124
  const onError = () => {
22446
- logger.warn("SSE connection error");
22447
23125
  try {
22448
23126
  const rs = esRef.current?.readyState;
22449
- logger.log("SSE readyState:", rs);
22450
23127
  if (rs === 0) {
22451
23128
  if (mountedRef.current) setConnectionStatus("disconnected");
22452
23129
  } else if (rs === 1) {
@@ -22457,11 +23134,9 @@ function useJobDetailWithUpdates(jobId) {
22457
23134
  if (mountedRef.current) setConnectionStatus("disconnected");
22458
23135
  }
22459
23136
  } catch (err) {
22460
- logger.error("Error getting readyState:", err);
22461
23137
  if (mountedRef.current) setConnectionStatus("disconnected");
22462
23138
  }
22463
23139
  if (esRef.current && esRef.current.readyState === 2 && mountedRef.current) {
22464
- logger.log("Scheduling SSE reconnection");
22465
23140
  if (reconnectTimer.current) clearTimeout(reconnectTimer.current);
22466
23141
  reconnectTimer.current = setTimeout(() => {
22467
23142
  if (!mountedRef.current) return;
@@ -22471,7 +23146,6 @@ function useJobDetailWithUpdates(jobId) {
22471
23146
  } catch (e2) {
22472
23147
  }
22473
23148
  const eventsUrl = jobId ? `/api/events?jobId=${encodeURIComponent(jobId)}` : "/api/events";
22474
- logger.log("Creating new EventSource for reconnection");
22475
23149
  const newEs = new EventSource(eventsUrl);
22476
23150
  newEs.addEventListener("open", onOpen);
22477
23151
  newEs.addEventListener("job:updated", onJobUpdated);
@@ -22482,7 +23156,7 @@ function useJobDetailWithUpdates(jobId) {
22482
23156
  newEs.addEventListener("error", onError);
22483
23157
  esRef.current = newEs;
22484
23158
  } catch (err) {
22485
- logger.error("Failed to reconnect SSE:", err);
23159
+ console.error("Failed to reconnect SSE:", err);
22486
23160
  }
22487
23161
  }, 2e3);
22488
23162
  }
@@ -22491,64 +23165,37 @@ function useJobDetailWithUpdates(jobId) {
22491
23165
  try {
22492
23166
  const payload = evt && evt.data ? JSON.parse(evt.data) : null;
22493
23167
  const eventObj = { type, payload };
22494
- logger.sse(type, payload);
22495
23168
  if (payload && payload.jobId && payload.jobId !== jobId) {
22496
- logger.log(
22497
- `Ignoring event for different job: ${payload.jobId} (current: ${jobId})`
22498
- );
22499
23169
  return;
22500
23170
  }
22501
23171
  if (!hydratedRef.current) {
22502
- logger.log(`Queueing event until hydration: ${type}`);
22503
23172
  eventQueue.current = (eventQueue.current || []).concat(eventObj);
22504
23173
  return;
22505
23174
  }
22506
23175
  if (type === "state:change") {
22507
23176
  const d2 = payload && (payload.data || payload) || {};
22508
- logger.log("Processing state:change event:", d2);
22509
23177
  if (typeof d2.path === "string" && matchesJobTasksStatusPath(d2.path, jobId)) {
22510
- logger.log(
22511
- `state:change matches tasks-status path: ${d2.path}, scheduling refetch`
22512
- );
22513
23178
  scheduleDebouncedRefetch({
22514
23179
  reason: "state:change",
22515
23180
  path: d2.path
22516
23181
  });
22517
23182
  return;
22518
- } else {
22519
- logger.log(
22520
- `state:change does not match tasks-status path: ${d2.path}`
22521
- );
22522
23183
  }
22523
23184
  }
22524
- setData((prev) => {
22525
- logger.group("Applying SSE event to state");
22526
- logger.log("Previous state snapshot:", {
22527
- hasTasks: !!prev?.tasks,
22528
- taskKeys: prev?.tasks ? Object.keys(prev.tasks) : [],
22529
- status: prev?.status
22530
- });
22531
- logger.log("Incoming event payload:", payload);
22532
- const next = applyJobEvent(prev, eventObj, jobId);
22533
- try {
22534
- if (JSON.stringify(prev) === JSON.stringify(next)) {
22535
- logger.log("Event application resulted in no state change");
22536
- logger.groupEnd();
22537
- return prev;
23185
+ startTransition(() => {
23186
+ setData((prev) => {
23187
+ const next = applyJobEvent(prev, eventObj, jobId);
23188
+ try {
23189
+ if (JSON.stringify(prev) === JSON.stringify(next)) {
23190
+ return prev;
23191
+ }
23192
+ } catch (e2) {
23193
+ console.error("Error comparing states:", e2);
22538
23194
  }
22539
- } catch (e2) {
22540
- logger.error("Error comparing states:", e2);
22541
- }
22542
- logger.log("Event applied, state updated", {
22543
- hasTasks: !!next?.tasks,
22544
- taskKeys: next?.tasks ? Object.keys(next.tasks) : [],
22545
- status: next?.status
23195
+ return next;
22546
23196
  });
22547
- logger.groupEnd();
22548
- return next;
22549
23197
  });
22550
23198
  } catch (err) {
22551
- logger.error("Failed to handle SSE event:", err);
22552
23199
  console.error("Failed to handle SSE event:", err);
22553
23200
  }
22554
23201
  };
@@ -22557,7 +23204,6 @@ function useJobDetailWithUpdates(jobId) {
22557
23204
  const onJobRemoved = (evt) => handleIncomingEvent("job:removed", evt);
22558
23205
  const onStatusChanged = (evt) => handleIncomingEvent("status:changed", evt);
22559
23206
  const onStateChange = (evt) => handleIncomingEvent("state:change", evt);
22560
- logger.log("Attaching SSE event listeners");
22561
23207
  es.addEventListener("open", onOpen);
22562
23208
  es.addEventListener("job:updated", onJobUpdated);
22563
23209
  es.addEventListener("job:created", onJobCreated);
@@ -22566,14 +23212,11 @@ function useJobDetailWithUpdates(jobId) {
22566
23212
  es.addEventListener("state:change", onStateChange);
22567
23213
  es.addEventListener("error", onError);
22568
23214
  if (es.readyState === 1 && mountedRef.current) {
22569
- logger.log("SSE already open, setting connected");
22570
23215
  setConnectionStatus("connected");
22571
23216
  } else if (es.readyState === 0 && mountedRef.current) {
22572
- logger.log("SSE connecting, setting disconnected");
22573
23217
  setConnectionStatus("disconnected");
22574
23218
  }
22575
23219
  return () => {
22576
- logger.log("Cleaning up SSE connection");
22577
23220
  try {
22578
23221
  es.removeEventListener("open", onOpen);
22579
23222
  es.removeEventListener("job:updated", onJobUpdated);
@@ -22583,9 +23226,8 @@ function useJobDetailWithUpdates(jobId) {
22583
23226
  es.removeEventListener("state:change", onStateChange);
22584
23227
  es.removeEventListener("error", onError);
22585
23228
  es.close();
22586
- logger.log("SSE connection closed");
22587
23229
  } catch (err) {
22588
- logger.error("Error during SSE cleanup:", err);
23230
+ console.error("Error during SSE cleanup:", err);
22589
23231
  }
22590
23232
  if (reconnectTimer.current) {
22591
23233
  clearTimeout(reconnectTimer.current);
@@ -22596,21 +23238,18 @@ function useJobDetailWithUpdates(jobId) {
22596
23238
  };
22597
23239
  try {
22598
23240
  const eventsUrl = jobId ? `/api/events?jobId=${encodeURIComponent(jobId)}` : "/api/events";
22599
- logger.log(`Creating EventSource with URL: ${eventsUrl}`);
22600
23241
  const es = new EventSource(eventsUrl);
22601
23242
  esRef.current = es;
22602
23243
  const cleanup = attachListeners(es);
22603
- logger.groupEnd();
22604
23244
  return cleanup;
22605
23245
  } catch (err) {
22606
- logger.error("Failed to create SSE connection:", err);
23246
+ console.error("Failed to create SSE connection:", err);
22607
23247
  if (mountedRef.current) {
22608
23248
  setConnectionStatus("error");
22609
23249
  }
22610
- logger.groupEnd();
22611
23250
  return void 0;
22612
23251
  }
22613
- }, [jobId, scheduleDebouncedRefetch, logger]);
23252
+ }, [jobId, scheduleDebouncedRefetch]);
22614
23253
  reactExports.useEffect(() => {
22615
23254
  mountedRef.current = true;
22616
23255
  return () => {
@@ -22636,26 +23275,77 @@ function useJobDetailWithUpdates(jobId) {
22636
23275
  data,
22637
23276
  loading,
22638
23277
  error,
22639
- connectionStatus
23278
+ connectionStatus,
23279
+ isRefreshing,
23280
+ isTransitioning: isPending,
23281
+ isHydrated
22640
23282
  };
22641
23283
  }
22642
23284
  function PipelineDetail() {
22643
23285
  const { jobId } = useParams();
22644
23286
  if (jobId === void 0 || jobId === null) {
22645
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Layout, { title: "Pipeline Details", showBackButton: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$3, { align: "center", justify: "center", className: "min-h-64", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$6, { className: "text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "5", weight: "medium", color: "red", className: "mb-2", children: "No job ID provided" }) }) }) });
23287
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
23288
+ Layout,
23289
+ {
23290
+ pageTitle: "Pipeline Details",
23291
+ breadcrumbs: [{ label: "Home", href: "/" }],
23292
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$4, { align: "center", justify: "center", className: "min-h-64", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$7, { className: "text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "5", weight: "medium", color: "red", className: "mb-2", children: "No job ID provided" }) }) })
23293
+ }
23294
+ );
22646
23295
  }
22647
- const { data: job, loading, error } = useJobDetailWithUpdates(jobId);
22648
- if (loading) {
22649
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Layout, { title: "Pipeline Details", showBackButton: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$3, { align: "center", justify: "center", className: "min-h-64", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$6, { className: "text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "5", weight: "medium", className: "mb-2", children: "Loading job details..." }) }) }) });
23296
+ const {
23297
+ data: job,
23298
+ loading,
23299
+ error,
23300
+ isRefreshing,
23301
+ isHydrated
23302
+ } = useJobDetailWithUpdates(jobId);
23303
+ const showLoadingScreen = loading && !isHydrated;
23304
+ if (showLoadingScreen) {
23305
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
23306
+ Layout,
23307
+ {
23308
+ pageTitle: "Pipeline Details",
23309
+ breadcrumbs: [
23310
+ { label: "Home", href: "/" },
23311
+ {
23312
+ label: job && job?.pipelineLabel ? job.pipelineLabel : "Pipeline Details"
23313
+ }
23314
+ ],
23315
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$4, { align: "center", justify: "center", className: "min-h-64", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$7, { className: "text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "5", weight: "medium", className: "mb-2", children: "Loading job details..." }) }) })
23316
+ }
23317
+ );
22650
23318
  }
22651
23319
  if (error) {
22652
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Layout, { title: "Pipeline Details", showBackButton: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$3, { align: "center", justify: "center", className: "min-h-64", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$6, { className: "text-center", children: [
22653
- /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "5", weight: "medium", color: "red", className: "mb-2", children: "Failed to load job details" }),
22654
- /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "2", color: "gray", className: "mt-2", children: error })
22655
- ] }) }) });
23320
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
23321
+ Layout,
23322
+ {
23323
+ pageTitle: "Pipeline Details",
23324
+ breadcrumbs: [
23325
+ { label: "Home", href: "/" },
23326
+ {
23327
+ label: job && job?.pipelineLabel ? job.pipelineLabel : "Pipeline Details"
23328
+ }
23329
+ ],
23330
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$4, { align: "center", justify: "center", className: "min-h-64", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$7, { className: "text-center", children: [
23331
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "5", weight: "medium", color: "red", className: "mb-2", children: "Failed to load job details" }),
23332
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "2", color: "gray", className: "mt-2", children: error })
23333
+ ] }) })
23334
+ }
23335
+ );
22656
23336
  }
22657
23337
  if (!job) {
22658
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Layout, { title: "Pipeline Details", showBackButton: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$3, { align: "center", justify: "center", className: "min-h-64", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$6, { className: "text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$a, { size: "5", weight: "medium", className: "mb-2", children: "Job not found" }) }) }) });
23338
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
23339
+ Layout,
23340
+ {
23341
+ pageTitle: "Pipeline Details",
23342
+ breadcrumbs: [
23343
+ { label: "Home", href: "/" },
23344
+ { label: job.pipelineLabel || "Pipeline Details" }
23345
+ ],
23346
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$4, { align: "center", justify: "center", className: "min-h-64", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$7, { className: "text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "5", weight: "medium", className: "mb-2", children: "Job not found" }) }) })
23347
+ }
23348
+ );
22659
23349
  }
22660
23350
  const pipeline = job?.pipeline || (() => {
22661
23351
  if (!job?.tasks) return { tasks: [] };
@@ -22667,25 +23357,380 @@ function PipelineDetail() {
22667
23357
  }
22668
23358
  return { tasks: pipelineTasks };
22669
23359
  })();
22670
- const headerActions = /* @__PURE__ */ jsxRuntimeExports.jsxs(p$3, { align: "center", gap: "3", className: "shrink-0", children: [
22671
- /* @__PURE__ */ jsxRuntimeExports.jsxs(p$a, { size: "2", color: "gray", children: [
23360
+ const pageTitle = job.name || "Pipeline Details";
23361
+ const breadcrumbs = [
23362
+ { label: "Home", href: "/" },
23363
+ {
23364
+ label: job && job?.pipelineLabel ? job.pipelineLabel : "Pipeline Details"
23365
+ },
23366
+ ...job.name ? [{ label: job.name }] : []
23367
+ ];
23368
+ const totalCost = job?.totalCost || job?.costs?.summary?.totalCost || 0;
23369
+ const totalTokens = job?.totalTokens || job?.costs?.summary?.totalTokens || 0;
23370
+ const totalInputTokens = job?.costs?.summary?.totalInputTokens || 0;
23371
+ const totalOutputTokens = job?.costs?.summary?.totalOutputTokens || 0;
23372
+ const costIndicator = /* @__PURE__ */ jsxRuntimeExports.jsxs(p$b, { size: "2", color: "gray", children: [
23373
+ "Cost: ",
23374
+ totalCost > 0 ? formatCurrency4(totalCost) : "—"
23375
+ ] });
23376
+ const costIndicatorWithTooltip = totalCost > 0 && totalTokens > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(Provider, { delayDuration: 100, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Root3, { children: [
23377
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
23378
+ p$7,
23379
+ {
23380
+ className: "cursor-help border-b border-dotted border-gray-400 hover:border-gray-600 transition-colors",
23381
+ "aria-label": `Total cost: ${formatCurrency4(totalCost)}, ${formatTokensCompact(totalTokens)}`,
23382
+ children: costIndicator
23383
+ }
23384
+ ) }),
23385
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Portal, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
23386
+ Content2,
23387
+ {
23388
+ className: "bg-gray-900 text-white px-2 py-1 rounded text-xs max-w-xs",
23389
+ sideOffset: 5,
23390
+ children: [
23391
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
23392
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-semibold", children: formatTokensCompact(totalTokens) }),
23393
+ totalInputTokens > 0 && totalOutputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-gray-300", children: [
23394
+ "Input: ",
23395
+ formatTokensCompact(totalInputTokens),
23396
+ " • Output:",
23397
+ " ",
23398
+ formatTokensCompact(totalOutputTokens)
23399
+ ] })
23400
+ ] }),
23401
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Arrow2, { className: "fill-gray-900" })
23402
+ ]
23403
+ }
23404
+ ) })
23405
+ ] }) }) : costIndicator;
23406
+ const subheaderRightContent = /* @__PURE__ */ jsxRuntimeExports.jsxs(p$4, { align: "center", gap: "3", className: "shrink-0 flex-wrap", children: [
23407
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(p$b, { size: "2", color: "gray", children: [
22672
23408
  "ID: ",
22673
23409
  job.id || jobId
22674
23410
  ] }),
23411
+ costIndicatorWithTooltip,
22675
23412
  statusBadge(job.status)
22676
23413
  ] });
23414
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Layout, { pageTitle, breadcrumbs, children: [
23415
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(PageSubheader, { breadcrumbs, maxWidth: "max-w-7xl", children: [
23416
+ subheaderRightContent,
23417
+ isRefreshing && /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { size: "2", color: "blue", className: "ml-3 animate-pulse", children: "Refreshing..." })
23418
+ ] }),
23419
+ /* @__PURE__ */ jsxRuntimeExports.jsx(JobDetail, { job, pipeline })
23420
+ ] });
23421
+ }
23422
+ const ioFunctions = [
23423
+ {
23424
+ name: "writeArtifact",
23425
+ description: "Write an artifact file",
23426
+ params: 'name: string, content: string, options?: { mode?: "replace"|"append"=replace }',
23427
+ returns: "Promise<string>",
23428
+ notes: "Writes to {workDir}/files/artifacts; updates tasks-status.json"
23429
+ },
23430
+ {
23431
+ name: "writeLog",
23432
+ description: "Write a log file",
23433
+ params: 'name: string, content: string, options?: { mode?: "append"|"replace"=append }',
23434
+ returns: "Promise<string>",
23435
+ notes: "Writes to {workDir}/files/logs; default append; updates tasks-status.json"
23436
+ },
23437
+ {
23438
+ name: "writeTmp",
23439
+ description: "Write a temporary file",
23440
+ params: 'name: string, content: string, options?: { mode?: "replace"|"append"=replace }',
23441
+ returns: "Promise<string>",
23442
+ notes: "Writes to {workDir}/files/tmp; updates tasks-status.json"
23443
+ },
23444
+ {
23445
+ name: "readArtifact",
23446
+ description: "Read an artifact file",
23447
+ params: "name: string",
23448
+ returns: "Promise<string>",
23449
+ notes: "Reads from {workDir}/files/artifacts"
23450
+ },
23451
+ {
23452
+ name: "readLog",
23453
+ description: "Read a log file",
23454
+ params: "name: string",
23455
+ returns: "Promise<string>",
23456
+ notes: "Reads from {workDir}/files/logs"
23457
+ },
23458
+ {
23459
+ name: "readTmp",
23460
+ description: "Read a temporary file",
23461
+ params: "name: string",
23462
+ returns: "Promise<string>",
23463
+ notes: "Reads from {workDir}/files/tmp"
23464
+ },
23465
+ {
23466
+ name: "getTaskDir",
23467
+ description: "Get the task directory path",
23468
+ params: "",
23469
+ returns: "string",
23470
+ notes: "Returns {workDir}/tasks/{taskName}"
23471
+ },
23472
+ {
23473
+ name: "getCurrentStage",
23474
+ description: "Get the current stage name",
23475
+ params: "",
23476
+ returns: "string",
23477
+ notes: "Calls injected getStage()"
23478
+ }
23479
+ ];
23480
+ const sampleSeed = {
23481
+ name: "some-name",
23482
+ pipeline: "content-generation",
23483
+ data: {
23484
+ type: "some-type",
23485
+ contentType: "blog-post",
23486
+ targetAudience: "software-developers",
23487
+ tone: "professional-yet-accessible",
23488
+ length: "1500-2000 words",
23489
+ outputFormat: "blog-post"
23490
+ }
23491
+ };
23492
+ function CodePage() {
23493
+ const [llmFunctions, setLlmFunctions] = reactExports.useState(null);
23494
+ reactExports.useEffect(() => {
23495
+ fetch("/api/llm/functions").then((res) => res.json()).then(setLlmFunctions).catch(console.error);
23496
+ }, []);
23497
+ const breadcrumbs = [{ label: "Home", href: "/" }, { label: "Code" }];
23498
+ const handleCopySeed = () => {
23499
+ navigator.clipboard.writeText(JSON.stringify(sampleSeed, null, 2));
23500
+ };
23501
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Layout, { children: [
23502
+ /* @__PURE__ */ jsxRuntimeExports.jsx(PageSubheader, { breadcrumbs }),
23503
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(p$7, { children: [
23504
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(p$7, { mb: "8", children: [
23505
+ /* @__PURE__ */ jsxRuntimeExports.jsx(r$8, { size: "6", mb: "4", children: "Seed File Example" }),
23506
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { as: "p", mb: "3", size: "2", children: "A seed file is a JSON object used to start a new pipeline job. It defines the job name, the pipeline to run, and any contextual data the pipeline requires to begin." }),
23507
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { as: "p", mb: "3", size: "2", weight: "bold", children: "Required fields:" }),
23508
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "list-disc list-inside mb-4 space-y-1", children: [
23509
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: "text-sm text-gray-700", children: [
23510
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { as: "span", weight: "bold", children: "name" }),
23511
+ " ",
23512
+ "(string): Human-friendly title; non-empty, printable only, ≤120 chars; must be unique."
23513
+ ] }),
23514
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: "text-sm text-gray-700", children: [
23515
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { as: "span", weight: "bold", children: "pipeline" }),
23516
+ " ",
23517
+ "(string): Pipeline slug defined in your registry (e.g., content-generation)."
23518
+ ] }),
23519
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: "text-sm text-gray-700", children: [
23520
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { as: "span", weight: "bold", children: "data" }),
23521
+ " ",
23522
+ "(object): Required but flexible; include any arbitrary keys your pipeline tasks expect."
23523
+ ] })
23524
+ ] }),
23525
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$7, { mb: "3", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
23526
+ Button,
23527
+ {
23528
+ size: "1",
23529
+ onClick: handleCopySeed,
23530
+ "data-testid": "copy-seed-example",
23531
+ children: "Copy"
23532
+ }
23533
+ ) }),
23534
+ /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-xs bg-gray-50 p-3 rounded overflow-auto max-h-60 border border-gray-200", children: JSON.stringify(sampleSeed, null, 2) })
23535
+ ] }),
23536
+ /* @__PURE__ */ jsxRuntimeExports.jsx(r$8, { size: "6", mb: "4", children: "Pipeline Task IO API" }),
23537
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$7, { overflowX: "auto", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(m$1, { children: [
23538
+ /* @__PURE__ */ jsxRuntimeExports.jsx(d, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(P$1, { children: [
23539
+ /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Function" }),
23540
+ /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Parameters" }),
23541
+ /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Returns" }),
23542
+ /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Notes" })
23543
+ ] }) }),
23544
+ /* @__PURE__ */ jsxRuntimeExports.jsx(b$1, { children: ioFunctions.map((fn) => /* @__PURE__ */ jsxRuntimeExports.jsxs(P$1, { children: [
23545
+ /* @__PURE__ */ jsxRuntimeExports.jsx(R, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$2, { size: "3", children: [
23546
+ "io.",
23547
+ fn.name
23548
+ ] }) }),
23549
+ /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", children: fn.params || "—" }) }),
23550
+ /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", children: fn.returns }) }),
23551
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(T, { children: [
23552
+ fn.description,
23553
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
23554
+ fn.notes
23555
+ ] })
23556
+ ] }, fn.name)) })
23557
+ ] }) }),
23558
+ /* @__PURE__ */ jsxRuntimeExports.jsx(r$8, { size: "6", mt: "8", mb: "4", children: "Pipeline Task LLM API" }),
23559
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(p$7, { mb: "4", children: [
23560
+ /* @__PURE__ */ jsxRuntimeExports.jsx(r$8, { size: "4", mb: "2", children: "Arguments" }),
23561
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", mb: "4", children: `{
23562
+ messages: Array<{role: "system"|"user"|"assistant", content: string }>,
23563
+ temperature?: number,
23564
+ maxTokens?: number,
23565
+ responseFormat?: "json" | { type: "json_object" | { type: "json_schema", name: string, json_schema: object } },
23566
+ stop?: string | string[],
23567
+ topP?: number,
23568
+ frequencyPenalty?: number,
23569
+ presencePenalty?: number,
23570
+ seed?: number,
23571
+ provider?: string,
23572
+ model?: string,
23573
+ metadata?: object,
23574
+ maxRetries?: number
23575
+ }` }),
23576
+ /* @__PURE__ */ jsxRuntimeExports.jsx(r$8, { size: "4", mb: "2", children: "Returns" }),
23577
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", children: `Promise<{ content: any, usage?: object, raw?: any }>` })
23578
+ ] }),
23579
+ llmFunctions && /* @__PURE__ */ jsxRuntimeExports.jsx(p$7, { overflowX: "auto", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(m$1, { children: [
23580
+ /* @__PURE__ */ jsxRuntimeExports.jsx(d, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(P$1, { children: [
23581
+ /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Function" }),
23582
+ /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Model" })
23583
+ ] }) }),
23584
+ /* @__PURE__ */ jsxRuntimeExports.jsx(b$1, { children: Object.entries(llmFunctions).map(
23585
+ ([provider, functions]) => functions.map((fn) => /* @__PURE__ */ jsxRuntimeExports.jsxs(P$1, { children: [
23586
+ /* @__PURE__ */ jsxRuntimeExports.jsx(R, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", children: fn.fullPath }) }),
23587
+ /* @__PURE__ */ jsxRuntimeExports.jsx(T, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", children: fn.model }) })
23588
+ ] }, fn.fullPath))
23589
+ ) })
23590
+ ] }) }),
23591
+ /* @__PURE__ */ jsxRuntimeExports.jsx(r$8, { size: "6", mt: "8", mb: "4", children: "Validation API" }),
23592
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { as: "p", mb: "3", size: "2", children: "Schema validation helper available to task stages via validators." }),
23593
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(p$7, { mb: "4", children: [
23594
+ /* @__PURE__ */ jsxRuntimeExports.jsx(r$8, { size: "4", mb: "2", children: "Function Signature" }),
23595
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", children: `validateWithSchema(schema: object, data: object | string): { valid: true } | { valid: false, errors: AjvError[] }` }),
23596
+ /* @__PURE__ */ jsxRuntimeExports.jsx(r$8, { size: "4", mb: "2", mt: "4", children: "Behavior" }),
23597
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "list-disc list-inside mb-4 space-y-1", children: [
23598
+ /* @__PURE__ */ jsxRuntimeExports.jsx("li", { className: "text-sm text-gray-700", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$b, { as: "span", children: [
23599
+ "Parses string data to JSON; on parse failure returns",
23600
+ " ",
23601
+ `{ valid:false, errors:[{ keyword:"type", message:"must be a valid JSON object (string parsing failed)"} ]`
23602
+ ] }) }),
23603
+ /* @__PURE__ */ jsxRuntimeExports.jsx("li", { className: "text-sm text-gray-700", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$b, { as: "span", children: [
23604
+ "Uses Ajv(",
23605
+ `{ allErrors: true, strict: false }`,
23606
+ "); compiles provided schema"
23607
+ ] }) }),
23608
+ /* @__PURE__ */ jsxRuntimeExports.jsx("li", { className: "text-sm text-gray-700", children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { as: "span", children: "Returns AJV errors array when invalid" }) })
23609
+ ] }),
23610
+ /* @__PURE__ */ jsxRuntimeExports.jsx(r$8, { size: "4", mb: "2", children: "Source" }),
23611
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", children: "src/api/validators/json.js" }),
23612
+ /* @__PURE__ */ jsxRuntimeExports.jsx(r$8, { size: "4", mb: "2", mt: "4", children: "Usage Example" }),
23613
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", children: `export const validateStructure = async ({
23614
+ io,
23615
+ flags,
23616
+ validators: { validateWithSchema },
23617
+ }) => {
23618
+ const content = await io.readArtifact("research-output.json");
23619
+ const result = validateWithSchema(researchJsonSchema, content);
23620
+
23621
+ if (!result.valid) {
23622
+ console.warn("[Research:validateStructure] Validation failed", result.errors);
23623
+ return { output: {}, flags: { ...flags, validationFailed: true } };
23624
+ }
23625
+ return { output: {}, flags };
23626
+ };` })
23627
+ ] }),
23628
+ /* @__PURE__ */ jsxRuntimeExports.jsx(r$8, { size: "6", mt: "8", mb: "4", children: "Environment Configuration" }),
23629
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(p$7, { mb: "4", children: [
23630
+ /* @__PURE__ */ jsxRuntimeExports.jsx(r$8, { size: "4", mb: "2", children: "Example .env Configuration" }),
23631
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$7, { overflowX: "auto", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(m$1, { children: [
23632
+ /* @__PURE__ */ jsxRuntimeExports.jsx(d, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(P$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(f$1, { children: "Environment Variable" }) }) }),
23633
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(b$1, { children: [
23634
+ /* @__PURE__ */ jsxRuntimeExports.jsx(P$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(R, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", children: "OPENAI_API_KEY=" }) }) }),
23635
+ /* @__PURE__ */ jsxRuntimeExports.jsx(P$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(R, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", children: "DEEPSEEK_API_KEY=" }) }) }),
23636
+ /* @__PURE__ */ jsxRuntimeExports.jsx(P$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(R, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", children: "GEMINI_API_KEY=" }) }) }),
23637
+ /* @__PURE__ */ jsxRuntimeExports.jsx(P$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(R, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", children: "ANTHROPIC_API_KEY=" }) }) }),
23638
+ /* @__PURE__ */ jsxRuntimeExports.jsx(P$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(R, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(p$2, { size: "3", children: "ZHIPU_API_KEY=" }) }) })
23639
+ ] })
23640
+ ] }) })
23641
+ ] })
23642
+ ] })
23643
+ ] });
23644
+ }
23645
+ const ToastContext = reactExports.createContext();
23646
+ function ToastProvider({ children }) {
23647
+ const [toasts, setToasts] = reactExports.useState([]);
23648
+ const addToast = reactExports.useCallback((message, options = {}) => {
23649
+ const toast = {
23650
+ id: Date.now() + Math.random(),
23651
+ message,
23652
+ type: options.type || "info",
23653
+ duration: options.duration || 5e3
23654
+ };
23655
+ setToasts((prev) => [...prev, toast]);
23656
+ if (toast.duration > 0) {
23657
+ setTimeout(() => {
23658
+ removeToast(toast.id);
23659
+ }, toast.duration);
23660
+ }
23661
+ return toast.id;
23662
+ }, []);
23663
+ const removeToast = reactExports.useCallback((id) => {
23664
+ setToasts((prev) => prev.filter((toast) => toast.id !== id));
23665
+ }, []);
23666
+ const value = {
23667
+ addToast,
23668
+ removeToast,
23669
+ success: (message, options) => addToast(message, { ...options, type: "success" }),
23670
+ error: (message, options) => addToast(message, { ...options, type: "error" }),
23671
+ warning: (message, options) => addToast(message, { ...options, type: "warning" }),
23672
+ info: (message, options) => addToast(message, { ...options, type: "info" })
23673
+ };
23674
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(ToastContext.Provider, { value, children: [
23675
+ children,
23676
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ToastContainer, { toasts, onRemove: removeToast })
23677
+ ] });
23678
+ }
23679
+ function ToastContainer({ toasts, onRemove }) {
23680
+ if (toasts.length === 0) return null;
23681
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed top-4 right-4 z-50 space-y-2", children: toasts.map((toast) => /* @__PURE__ */ jsxRuntimeExports.jsx(ToastItem, { toast, onRemove }, toast.id)) });
23682
+ }
23683
+ function ToastItem({ toast, onRemove }) {
23684
+ const getToastStyles = (type) => {
23685
+ switch (type) {
23686
+ case "success":
23687
+ return "bg-green-50 border-green-200 text-green-800";
23688
+ case "error":
23689
+ return "bg-red-50 border-red-200 text-red-800";
23690
+ case "warning":
23691
+ return "bg-yellow-50 border-yellow-200 text-yellow-800";
23692
+ default:
23693
+ return "bg-blue-50 border-blue-200 text-blue-800";
23694
+ }
23695
+ };
23696
+ const getIcon = (type) => {
23697
+ switch (type) {
23698
+ case "success":
23699
+ return "✓";
23700
+ case "error":
23701
+ return "✕";
23702
+ case "warning":
23703
+ return "⚠";
23704
+ default:
23705
+ return "ℹ";
23706
+ }
23707
+ };
22677
23708
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
22678
- Layout,
23709
+ p$7,
22679
23710
  {
22680
- title: job.name || "Pipeline Details",
22681
- showBackButton: true,
22682
- actions: headerActions,
22683
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(JobDetail, { job, pipeline })
23711
+ className: `relative flex items-start p-4 border rounded-lg shadow-lg max-w-sm ${getToastStyles(
23712
+ toast.type
23713
+ )}`,
23714
+ role: "alert",
23715
+ "aria-live": "polite",
23716
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(p$4, { gap: "3", align: "start", children: [
23717
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { className: "flex-shrink-0 text-lg font-semibold", children: getIcon(toast.type) }),
23718
+ /* @__PURE__ */ jsxRuntimeExports.jsx(p$b, { className: "text-sm font-medium flex-1", children: toast.message }),
23719
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
23720
+ "button",
23721
+ {
23722
+ onClick: () => onRemove(toast.id),
23723
+ className: "flex-shrink-0 ml-4 text-sm opacity-60 hover:opacity-100 focus:outline-none focus:opacity-100",
23724
+ "aria-label": "Dismiss notification",
23725
+ children: "✕"
23726
+ }
23727
+ )
23728
+ ] })
22684
23729
  }
22685
23730
  );
22686
23731
  }
22687
- ReactDOM.createRoot(document.getElementById("root")).render(
22688
- /* @__PURE__ */ jsxRuntimeExports.jsx(React.StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
23732
+ ReactDOM$1.createRoot(document.getElementById("root")).render(
23733
+ /* @__PURE__ */ jsxRuntimeExports.jsx(React.StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(ToastProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
22689
23734
  R$1,
22690
23735
  {
22691
23736
  accentColor: "iris",
@@ -22695,8 +23740,9 @@ ReactDOM.createRoot(document.getElementById("root")).render(
22695
23740
  radius: "full",
22696
23741
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(BrowserRouter, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Routes, { children: [
22697
23742
  /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { path: "/", element: /* @__PURE__ */ jsxRuntimeExports.jsx(PromptPipelineDashboard, {}) }),
22698
- /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { path: "/pipeline/:jobId", element: /* @__PURE__ */ jsxRuntimeExports.jsx(PipelineDetail, {}) })
23743
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { path: "/pipeline/:jobId", element: /* @__PURE__ */ jsxRuntimeExports.jsx(PipelineDetail, {}) }),
23744
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Route, { path: "/code", element: /* @__PURE__ */ jsxRuntimeExports.jsx(CodePage, {}) })
22699
23745
  ] }) })
22700
23746
  }
22701
- ) })
23747
+ ) }) })
22702
23748
  );