@oomol-lab/open-flow 0.1.0-beta.79 → 0.1.0-beta.80

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 (32) hide show
  1. package/dist/browser/{FlowCanvas-BQGBcfoL.js → FlowCanvas-CrsiDHK8.js} +50 -51
  2. package/dist/browser/{createResourceDialog-DkovGogv.js → createResourceDialog-CS3u9ga9.js} +2 -2
  3. package/dist/browser/{dialog-CFFuRM8s.js → dialog-CFlvkliO.js} +7 -2
  4. package/dist/browser/{exportSvg-CYwyYiuH.js → exportPng-BOPfjeEi.js} +236 -38
  5. package/dist/browser/{flowChanges-CN2R-m1M.js → flowChanges-pFjPHU5k.js} +326 -203
  6. package/dist/browser/{flowWorkspace-CXVL1FNv.js → flowWorkspace-CRlqc403.js} +698 -480
  7. package/dist/browser/preview.css +153 -132
  8. package/dist/browser/preview.js +921 -643
  9. package/dist/browser/ui-logo.d.ts +1 -0
  10. package/dist/browser/ui.css +29 -4
  11. package/dist/browser/ui.js +4 -1
  12. package/dist/browser/workbench.css +153 -132
  13. package/dist/browser/workbench.js +9 -9
  14. package/dist/browser/{workbenchSelect-CkOS95gX.js → workbenchSelect-Ci0KCYv9.js} +1 -1
  15. package/dist/command/index.js +41 -41
  16. package/dist/common/control-api-conformance.js +49 -19
  17. package/dist/common/control-api.d.ts +26 -6
  18. package/dist/common/control-api.js +49 -19
  19. package/dist/common/control-requests.js +6 -2
  20. package/dist/common/flow-authoring.js +6 -2
  21. package/dist/common/flow-change.js +6 -2
  22. package/dist/common/flow-encoding.js +6 -2
  23. package/dist/common/flow-semantics.js +6 -2
  24. package/dist/common/flow-state.d.ts +47 -0
  25. package/dist/common/flow-state.js +7 -1
  26. package/dist/common/mcp.js +49 -18
  27. package/dist/common/scheduler.js +6 -2
  28. package/package.json +1 -1
  29. package/src/control/common/api.ts +24 -8
  30. package/src/control/common/connectorDecoders.ts +11 -30
  31. package/src/control/common/revisionDecoders.ts +14 -0
  32. package/src/state/common/state.ts +14 -0
@@ -3,13 +3,13 @@ import { A as DropdownMenuSeparator, C as DropdownMenuCheckboxItem, D as Dropdow
3
3
  import { i as compute, l as val } from "./dist-bO8nvHFH.js";
4
4
  import { c as useLang, l as useTranslate, o as I18nProvider, r as isPlainObject, s as useI18n, u as useVal } from "./dist-BFmpkX4P.js";
5
5
  import { A as string, Bn as transitionStatusMapping, Cr as cn, Dr as EMPTY_ARRAY, En as contains, Er as useRenderElement, Fr as formatErrorMessage, I as useCompositeListItem, In as useTransitionStatus, Ir as mergeProps, Ln as useOpenChangeComplete, Lr as useIsoLayoutEffect, Mn as ownerDocument, Mr as useButton, Nn as useLabelableId, Or as EMPTY_OBJECT, Qt as useStore, Sr as buttonVariants, Tn as activeElement, Tr as clsx, Un as useValueChanged, Wn as createChangeEventDetails, _r as fieldValidityMapping, a as createTooltipHandle, br as useControlled, c as TooltipPositioner, ct as CompositeList, d as TooltipRoot, dr as useFormContext, fr as useRegisterFieldControl, i as TooltipTrigger, jr as useMergedRefs, l as TooltipPortal, lt as inertValue, mr as useBaseUiId, mt as triggerOpenStateMapping, n as TooltipContent, nn as require_builtin_esm_external_require_react, o as TooltipArrow, p as timeZoneOffsetMinutes, pr as useFieldRootContext, qn as clearPress, r as TooltipProvider, rr as none, s as TooltipPopup, t as Tooltip, u as TooltipTrigger$1, ur as useLabelableContext, vr as visuallyHidden, wr as cva, xr as Button$1, yr as visuallyHiddenInput, zn as TransitionStatusDataAttributes, zr as useStableCallback } from "./tooltip-BDcVTNpu.js";
6
- import { $ as waitAutoContinueSchema, A as comparisonIssue, At as variableInputCompatible, Bt as SelectContent, Ct as limitDecisionQuestions, D as nodeSystemOutputPorts, Dt as matchesSchema, E as nodeErrorSchema, F as operatorsForType, Ft as skipDuplicatesConfigSchema, Gt as SelectValue, Ht as SelectItem, J as webhookResponseAllowsBody, K as webhookHeaderValuePort, L as unaryOperator, Lt as dequal, O as supportsNodeErrorHandling, Ot as portsAssignable, P as operandHandle, Q as workflowOutputsPort, R as valueType, Rt as Select$1, St as defaultDecisionQuestion, T as workflowToolNameIssue, Tt as webhookSupportsBody, U as decodeRevisionContent, Ut as SelectSeparator, Vt as SelectGroup, Wt as SelectTrigger, X as webhookResponseTimeout, Y as webhookResponseInputs, Z as webhookStatusCodePort, _t as isHttpHost, a as DialogFooter, at as httpHeaderSchema, b as nodeNameIssue, bt as decisionQuestionIssues, c as DialogTrigger, d as Field, et as defaultNodeRetry, f as FieldDescription, h as FieldLabel, i as DialogDescription, in as ChevronDown, kt as schemaObject, l as Icon, lt as httpRequestIssues, m as FieldGroup, nn as Label, nt as retryConfigurationSchema, o as DialogHeader, ot as httpQuerySchema, p as FieldError, pt as httpHeaderNameIssue, q as webhookHeadersPort, r as DialogContent, rn as Input, rt as supportsNodeRetry, s as DialogTitle, t as Dialog, tt as nodeRetryLimits, u as Spinner, ut as httpRequestTask, vt as decisionLimits, wt as webhookMethods, xt as decisionTask, zt as SelectChevron } from "./dialog-CFFuRM8s.js";
7
- import { C as toManifestHandleName, E as isSameViewport, S as toViewEdge, T as isMac, _ as timeZoneOffset, a as NodeHistoryContext, b as CanvasTooltip, c as defaultTriggerIcon, d as formatDuration, f as useDebouncedValue, g as timeZoneLongLabel, h as timeZoneLabel, i as setAddItemId, l as JSONViewer, m as selectableTimeZones, n as CompositeRoot, o as Textarea, p as IconStack, r as ACTIVE_COMPOSITE_ITEM, s as CanvasNodeIcon, t as FlowCanvas, u as collapseAllNested, v as cronDescription, w as toManifestNodeId, x as CanvasStore, y as E$1 } from "./FlowCanvas-BQGBcfoL.js";
8
- import { $n as triggerOutputDefinitions, A as schemaForEditor, An as EmptyTitle, B as datePickerValue, Cn as InputGroupAddon, Cr as workflowSelectionTask, D as defaultValueEditorSchema, Dn as EmptyDescription, Dr as controlErrorCode, E as supportsFeishuChatFilter, En as EmptyContent, Et as schemaMismatchMessage, F as calendarChange, Fn as createI18n, G as indexAddNodeOptions, Gn as executionReachableNodes, H as isDateFormat, I as calendarDate, In as FeishuEventPicker, Jn as webhookResponseDiagnostics, Jt as dequal$1, Kn as inputSourceCandidates, L as dateFromTimeZone, Ln as EventSourceSetup, M as getDefaultValue, N as isUnconstrainedSchema, Nn as SourceForm, O as editorComponent, On as EmptyHeader, Or as eventSourceKind, Ot as connectionCatalog, P as typeOfSchema, Pn as SourceSelect, Q as resourceValue, R as dateInTimeZone, Rn as errorNotice, Sn as InputGroup, Sr as workflowCapability, T as feishuResourceKind, Tn as Empty, Tt as diagnosticNodeId, Un as decodeResultList, V as dateTimeWall, X as observeResource, Xn as sourcePort, Xt as IdTooltip, Yn as sourceOutputLabel, Yt as WorkflowExportMenuItem, Z as resourceData, Zt as FlowSettingsButton, _n as selectors, _r as servers, _t as canCancelRun, an as valueEditor_module_default, at as getAppCatalogCache, cn as ComboboxPopup, cr as triggerConfigValues, dn as ComboboxList, dr as object$1, dt as designerGraph, er as isJsonObject, et as provider, fn as GroupCollectionProvider, fr as openApiFieldIssues, ft as semanticNodeIcon, gn as ComboboxRoot, gr as serverAddress, hn as ComboboxInput, hr as selectOperation, ht as providerIcon, in as ValueEditorFeedback, j as valueForEditor, jn as Badge, k as editorGroups, kn as EmptyMedia, ln as ComboboxPositioner, lr as inputValue, mn as ComboboxInputGroup, mr as securityOptions, mt as actionSummary, n as agentTool, nn as selectionMenuItemClass, on as ComboboxItemIndicator, pn as ComboboxTrigger, pr as openApiTask, pt as triggerNodeIcon, q as mapSource, qn as resolutionOutputPorts, rn as ValueControl, rt as decodeAppCatalogResponse, sn as ComboboxItem, sr as triggerConfigValue, tn as selectionMenuContentClass, un as ComboboxPortal, ur as listOperations, ut as loadJson, vn as useComboboxInputValueContext, vr as sameWorkflowInterface, vt as hasRunFilter, wn as InputGroupInput, wt as diagnosticMessage, xn as followWorkbenchLink, xr as workflowCandidateGroup, yn as useComboboxRootContext, yr as workflowAgentTool, yt as revisionView, z as datePickerChange } from "./flowChanges-CN2R-m1M.js";
6
+ import { $ as waitAutoContinueSchema, A as comparisonIssue, At as variableInputCompatible, Bt as SelectChevron, Ct as limitDecisionQuestions, D as nodeSystemOutputPorts, Dt as matchesSchema, E as nodeErrorSchema, F as operatorsForType, Gt as SelectTrigger, Ht as SelectGroup, It as skipDuplicatesConfigSchema, J as webhookResponseAllowsBody, K as webhookHeaderValuePort, Kt as SelectValue, L as unaryOperator, O as supportsNodeErrorHandling, Ot as portsAssignable, P as operandHandle, Q as workflowOutputsPort, R as valueType, Rt as dequal, St as defaultDecisionQuestion, T as workflowToolNameIssue, Tt as webhookSupportsBody, U as decodeRevisionContent, Ut as SelectItem, Vt as SelectContent, Wt as SelectSeparator, X as webhookResponseTimeout, Y as webhookResponseInputs, Z as webhookStatusCodePort, _t as isHttpHost, a as DialogFooter, an as ChevronDown, at as httpHeaderSchema, b as nodeNameIssue, bt as decisionQuestionIssues, c as DialogTrigger, d as Field, et as defaultNodeRetry, f as FieldDescription, h as FieldLabel, i as DialogDescription, in as Input, kt as schemaObject, l as Icon, lt as httpRequestIssues, m as FieldGroup, nt as retryConfigurationSchema, o as DialogHeader, ot as httpQuerySchema, p as FieldError, pt as httpHeaderNameIssue, q as webhookHeadersPort, r as DialogContent, rn as Label, rt as supportsNodeRetry, s as DialogTitle, t as Dialog, tt as nodeRetryLimits, u as Spinner, ut as httpRequestTask, vt as decisionLimits, wt as webhookMethods, xt as decisionTask, zt as Select$1 } from "./dialog-CFlvkliO.js";
7
+ import { C as toManifestHandleName, E as isSameViewport, S as toViewEdge, T as isMac, _ as timeZoneOffset, a as NodeHistoryContext, b as CanvasTooltip, c as defaultTriggerIcon, d as formatDuration, f as useDebouncedValue, g as timeZoneLongLabel, h as timeZoneLabel, i as setAddItemId, l as JSONViewer, m as selectableTimeZones, n as CompositeRoot, o as Textarea, p as IconStack, r as ACTIVE_COMPOSITE_ITEM, s as CanvasNodeIcon, t as FlowCanvas, u as collapseAllNested, v as cronDescription, w as toManifestNodeId, x as CanvasStore, y as E$1 } from "./FlowCanvas-CrsiDHK8.js";
8
+ import { $t as FlowSettingsButton, A as schemaForEditor, An as EmptyHeader, Ar as eventSourceKind, At as connectionCatalog, B as datePickerValue, Bn as errorNotice, Cn as followWorkbenchLink, Cr as workflowCandidateGroup, D as defaultValueEditorSchema, Dn as Empty, Dt as diagnosticNodeId, E as supportsFeishuChatFilter, En as InputGroupInput, Et as diagnosticMessage, F as calendarChange, Fn as SourceForm, G as indexAddNodeOptions, Gn as decodeResultList, H as isDateFormat, I as calendarDate, In as SourceSelect, Jn as inputSourceCandidates, L as dateFromTimeZone, Ln as createI18n, M as getDefaultValue, Mn as EmptyTitle, N as isUnconstrainedSchema, Nn as Badge, O as editorComponent, On as EmptyContent, Or as ApiError, Ot as schemaMismatchMessage, P as typeOfSchema, Q as resourceValue, Qn as sourcePort, Qt as IdTooltip, R as dateInTimeZone, Rn as FeishuEventPicker, T as feishuResourceKind, Tn as InputGroupAddon, Tr as workflowSelectionTask, V as dateTimeWall, X as observeResource, Xn as webhookResponseDiagnostics, Xt as dequal$1, Yn as resolutionOutputPorts, Z as resourceData, Zn as sourceOutputLabel, Zt as WorkflowExportMenuItem, _n as ComboboxInput, _r as selectOperation, _t as providerIcon, an as ValueControl, at as getAppCatalogCache, bn as useComboboxInputValueContext, br as sameWorkflowInterface, bt as hasRunFilter, cn as ComboboxItemIndicator, dn as ComboboxPositioner, dr as inputValue, dt as designerGraph, et as provider, fn as ComboboxPortal, fr as listOperations, ft as nodeIcon, gn as ComboboxInputGroup, gr as securityOptions, gt as actionSummary, hn as ComboboxTrigger, hr as openApiTask, ht as triggerNodeIcon, in as selectionMenuItemClass, j as valueForEditor, jn as EmptyMedia, k as editorGroups, kn as EmptyDescription, kr as controlErrorCode, ln as ComboboxItem, lr as triggerConfigValue, mn as GroupCollectionProvider, mr as openApiFieldIssues, mt as semanticNodeIcon, n as agentTool, nr as isJsonObject, on as ValueEditorFeedback, pn as ComboboxList, pr as object$1, pt as nodeTitle, q as mapSource, qn as executionReachableNodes, rn as selectionMenuContentClass, rt as decodeAppCatalogResponse, sn as valueEditor_module_default, tr as triggerOutputDefinitions, un as ComboboxPopup, ur as triggerConfigValues, ut as loadJson, vn as ComboboxRoot, vr as serverAddress, wn as InputGroup, wr as workflowCapability, xn as useComboboxRootContext, xr as workflowAgentTool, xt as revisionView, yn as selectors, yr as servers, yt as canCancelRun, z as datePickerChange, zn as EventSourceSetup } from "./flowChanges-pFjPHU5k.js";
9
9
  import { r as resolveUiLanguage } from "./languages-gYzoQqCi.js";
10
10
  import { a as renameObjectField, i as objectValue, n as isJsonValue, o as setObjectField, r as jsonDataTypes, s as valueType$1, t as initialValue } from "./value-BbFQZZ_V.js";
11
11
  import { n as compile, r as validate } from "./flowRunInputEditorStore-ys1-w9GW.js";
12
- import { t as WorkbenchSelect } from "./workbenchSelect-CkOS95gX.js";
12
+ import { t as WorkbenchSelect } from "./workbenchSelect-Ci0KCYv9.js";
13
13
  import * as React from "react";
14
14
  import reactExports, { Children, Fragment, Suspense, createContext, forwardRef, isValidElement, lazy, memo, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
15
15
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -5497,259 +5497,396 @@ function FlowNodeList({ nodes, onFocusNode, onSelect, groupTriggers = false }) {
5497
5497
  });
5498
5498
  }
5499
5499
  //#endregion
5500
- //#region src/ui/browser/native-select.tsx
5501
- function NativeSelect({ className, size = "default", ...props }) {
5502
- return /* @__PURE__ */ jsxs("div", {
5503
- className: cn("group/native-select relative w-fit has-[select:disabled]:opacity-50", className),
5504
- "data-slot": "native-select-wrapper",
5505
- "data-size": size,
5506
- children: [/* @__PURE__ */ jsx("select", {
5507
- "data-slot": "native-select",
5508
- "data-size": size,
5509
- className: "h-8 w-full min-w-0 appearance-none rounded-[var(--ui-control-radius,calc(var(--ui-radius)_+_2px))] border border-input bg-[var(--ui-control-background,var(--ui-muted))] py-1 pr-8 pl-2.5 text-sm transition-colors outline-none select-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground data-[slot=native-select]:focus-visible:text-field-focus data-[slot=native-select]:focus-visible:aria-invalid:invalid-text-field-focus disabled:pointer-events-none disabled:cursor-not-allowed aria-invalid:border-destructive data-[size=sm]:h-7 data-[size=sm]:rounded-[var(--ui-control-radius,var(--ui-radius))] data-[size=sm]:py-0.5 dark:bg-[var(--ui-control-background,var(--ui-muted))] dark:hover:bg-input/50",
5510
- ...props
5511
- }), /* @__PURE__ */ jsx(ChevronDown, {
5512
- className: "pointer-events-none absolute top-1/2 right-2.5 size-4 -translate-y-1/2 text-muted-foreground select-none",
5513
- "aria-hidden": "true",
5514
- "data-slot": "native-select-icon"
5500
+ //#region src/ui/browser/helpButton.tsx
5501
+ /** Supplementary, non-interactive help that can also be opened by click. */
5502
+ function HelpButton({ label, children, container, size = 24 }) {
5503
+ const id = useId();
5504
+ const [tooltip] = useState(() => createTooltipHandle());
5505
+ return /* @__PURE__ */ jsxs(Tooltip, {
5506
+ handle: tooltip,
5507
+ children: [/* @__PURE__ */ jsx(TooltipTrigger, {
5508
+ id,
5509
+ handle: tooltip,
5510
+ closeOnClick: false,
5511
+ onClick: () => tooltip.open(id),
5512
+ render: /* @__PURE__ */ jsx(Button$1, {
5513
+ type: "button",
5514
+ variant: "ghost",
5515
+ size: "icon-xs",
5516
+ style: {
5517
+ width: size,
5518
+ height: size
5519
+ },
5520
+ "aria-label": label
5521
+ }),
5522
+ children: /* @__PURE__ */ jsx("i", {
5523
+ "aria-hidden": "true",
5524
+ className: "i-lucide-light:info text-muted-foreground",
5525
+ style: { fontSize: size * 5 / 8 }
5526
+ })
5527
+ }), /* @__PURE__ */ jsx(TooltipContent, {
5528
+ container,
5529
+ children
5515
5530
  })]
5516
5531
  });
5517
5532
  }
5518
- function NativeSelectOption({ className, ...props }) {
5519
- return /* @__PURE__ */ jsx("option", {
5520
- "data-slot": "native-select-option",
5521
- className: cn("bg-[Canvas] text-[CanvasText]", className),
5522
- ...props
5523
- });
5524
- }
5525
5533
  //#endregion
5526
5534
  //#region src/workbench/browser/runtime/editor/flowStatePanel.tsx
5527
- function StateValueRow({ item, client, flowId, scope, busy, onClear }) {
5535
+ function sourceName(space, nodes, t) {
5536
+ return space === "flow" ? t("flowState.shared") : nodes.find((node) => `node:${node.id}` === space)?.title ?? space;
5537
+ }
5538
+ function UpdatedTime({ value, container }) {
5539
+ const language = useLang();
5540
+ const delta = value - Date.now();
5541
+ const units = [
5542
+ ["year", 31536e6],
5543
+ ["month", 2592e6],
5544
+ ["day", 864e5],
5545
+ ["hour", 36e5],
5546
+ ["minute", 6e4],
5547
+ ["second", 1e3]
5548
+ ];
5549
+ const [unit, duration] = units.find(([, size]) => Math.abs(delta) >= size) ?? units[units.length - 1];
5550
+ const date = new Date(value);
5551
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
5552
+ render: /* @__PURE__ */ jsx("time", { dateTime: date.toISOString() }),
5553
+ children: new Intl.RelativeTimeFormat(language, { numeric: "auto" }).format(Math.round(delta / duration), unit)
5554
+ }), /* @__PURE__ */ jsx(TooltipContent, {
5555
+ container,
5556
+ children: date.toLocaleString(language)
5557
+ })] });
5558
+ }
5559
+ function Usage({ count, limit }) {
5560
+ const language = useLang();
5528
5561
  const t = useTranslate();
5529
- const [open, setOpen] = useState(false);
5530
- const [result, setResult] = useState();
5531
- const [error, setError] = useState();
5532
- useEffect(() => {
5533
- if (!open) return;
5534
- const controller = new AbortController();
5535
- setResult(void 0);
5536
- setError(void 0);
5537
- client.getFlowStateValues(flowId, scope, [item.key], controller.signal).then((response) => {
5538
- if (!controller.signal.aborted) setResult(response.values[0]);
5539
- }, (reason) => {
5540
- if (!controller.signal.aborted) setError(String(reason));
5541
- });
5542
- return () => controller.abort();
5543
- }, [
5544
- client,
5545
- flowId,
5546
- scope,
5547
- item.key,
5548
- item.updatedAt,
5549
- open
5550
- ]);
5551
- return /* @__PURE__ */ jsxs("div", {
5552
- className: "flex items-start justify-between gap-3 border-b border-border/60 py-2",
5553
- children: [/* @__PURE__ */ jsxs("div", {
5554
- className: "min-w-0 flex-1",
5555
- children: [/* @__PURE__ */ jsx("p", {
5556
- className: "m-0 break-all font-medium",
5557
- children: item.key
5558
- }), open && (result != null ? /* @__PURE__ */ jsx("pre", {
5559
- className: "max-h-40 overflow-auto whitespace-pre-wrap break-all text-xs",
5560
- children: JSON.stringify(result.value, null, 2)
5561
- }) : error != null ? /* @__PURE__ */ jsx("p", {
5562
- role: "alert",
5563
- className: "text-destructive text-sm",
5564
- children: error
5565
- }) : /* @__PURE__ */ jsx("p", {
5566
- role: "status",
5567
- className: "text-muted-foreground text-sm",
5568
- children: t("flowState.loading")
5569
- }))]
5570
- }), /* @__PURE__ */ jsxs("div", {
5571
- className: "flex shrink-0 gap-2",
5572
- children: [/* @__PURE__ */ jsx(Button$1, {
5573
- variant: "outline",
5574
- size: "sm",
5575
- disabled: busy,
5576
- "aria-expanded": open,
5577
- "aria-label": `${t(open ? "flowState.hideValue" : "flowState.showValue")}: ${item.key}`,
5578
- onClick: () => setOpen((value) => !value),
5579
- children: t(open ? "flowState.hideValue" : "flowState.showValue")
5580
- }), /* @__PURE__ */ jsx(Button$1, {
5581
- variant: "outline",
5582
- size: "sm",
5583
- disabled: busy,
5584
- onClick: (event) => onClear(item.key, event.currentTarget),
5585
- children: t("flowState.delete")
5586
- })]
5562
+ return /* @__PURE__ */ jsxs("span", {
5563
+ className: "inline-flex flex-col items-end whitespace-nowrap tabular-nums sm:inline",
5564
+ children: [/* @__PURE__ */ jsx("span", {
5565
+ className: "font-medium text-foreground",
5566
+ children: count?.toLocaleString(language) ?? "—"
5567
+ }), limit != null && /* @__PURE__ */ jsx("span", {
5568
+ className: "text-xs text-muted-foreground",
5569
+ children: t("flowState.limit", { limit: limit.toLocaleString(language) })
5587
5570
  })]
5588
5571
  });
5589
5572
  }
5590
- function FlowStatePanel({ value, client, flowId, nodeIds, busy, onClearValues, onClearSpace, space }) {
5573
+ function StateClearButton({ name, disabled, reason, onClear, container }) {
5574
+ const t = useTranslate();
5575
+ const label = t("flowState.clearSource", { name });
5576
+ const button = /* @__PURE__ */ jsx(Button$1, {
5577
+ variant: "ghost",
5578
+ size: "sm",
5579
+ "aria-label": label,
5580
+ disabled: disabled || reason != null,
5581
+ onClick: (event) => onClear(event.currentTarget),
5582
+ children: t("flowState.clear")
5583
+ });
5584
+ return reason == null ? button : /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
5585
+ render: /* @__PURE__ */ jsx("span", {
5586
+ role: "group",
5587
+ "aria-label": label,
5588
+ tabIndex: 0,
5589
+ className: "inline-flex"
5590
+ }),
5591
+ children: button
5592
+ }), /* @__PURE__ */ jsx(TooltipContent, {
5593
+ container,
5594
+ children: reason
5595
+ })] });
5596
+ }
5597
+ function StateDataRow({ name, quantity, updatedAt, status, source, icon, disabled, clearReason, onClear, container }) {
5598
+ return /* @__PURE__ */ jsxs("tr", {
5599
+ className: "border-b border-border/60 last:border-b-0",
5600
+ children: [
5601
+ /* @__PURE__ */ jsx("th", {
5602
+ scope: "row",
5603
+ className: "py-3 text-left align-middle font-normal [overflow-wrap:anywhere]",
5604
+ children: /* @__PURE__ */ jsxs("div", {
5605
+ className: "flex gap-2",
5606
+ children: [icon != null && /* @__PURE__ */ jsx("span", {
5607
+ "aria-hidden": "true",
5608
+ className: "flex h-5 w-4 shrink-0 items-center justify-center text-base text-muted-foreground",
5609
+ children: icon
5610
+ }), /* @__PURE__ */ jsxs("div", {
5611
+ className: "min-w-0 flex-1",
5612
+ children: [source == null ? name : /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
5613
+ render: /* @__PURE__ */ jsx("span", {}),
5614
+ children: name
5615
+ }), /* @__PURE__ */ jsx(TooltipContent, {
5616
+ container,
5617
+ children: source
5618
+ })] }), (status != null || updatedAt != null) && /* @__PURE__ */ jsx("div", {
5619
+ className: "mt-1 text-xs font-normal text-muted-foreground",
5620
+ children: status ?? (updatedAt != null && /* @__PURE__ */ jsx(UpdatedTime, {
5621
+ value: updatedAt,
5622
+ container
5623
+ }))
5624
+ })]
5625
+ })]
5626
+ })
5627
+ }),
5628
+ /* @__PURE__ */ jsx("td", {
5629
+ className: "w-px py-3 pl-3 text-right align-middle whitespace-nowrap tabular-nums",
5630
+ children: quantity
5631
+ }),
5632
+ /* @__PURE__ */ jsx("td", {
5633
+ className: "w-px py-3 pl-3 text-right align-middle whitespace-nowrap",
5634
+ children: onClear != null && /* @__PURE__ */ jsx(StateClearButton, {
5635
+ name,
5636
+ disabled,
5637
+ reason: clearReason,
5638
+ onClear,
5639
+ container
5640
+ })
5641
+ })
5642
+ ]
5643
+ });
5644
+ }
5645
+ function FlowStatePanel({ value, nodes, busy, loading = false, onClearValues, onClearHistory, onClearSpace, space, container }) {
5591
5646
  const t = useTranslate();
5592
- const count = value.spaces.reduce((total, item) => total + item.reservedCount, 0);
5593
- const spaces = space == null ? value.spaces : value.spaces.filter((item) => item.space === space);
5647
+ const language = useLang();
5648
+ const allSpaces = value?.spaces ?? [];
5649
+ const count = value == null ? void 0 : allSpaces.reduce((total, item) => total + item.reservedCount, 0);
5650
+ const spaces = allSpaces.filter((item) => space == null || item.space === space).toSorted((a, b) => b.reservedCount - a.reservedCount || (a.space < b.space ? -1 : a.space > b.space ? 1 : 0));
5651
+ const readStatus = loading ? t("flowState.loading") : value == null ? t("flowState.loadFailed") : void 0;
5652
+ const valuesDetail = readStatus ?? (value?.values.count === 0 ? t("flowState.emptyValues") : value?.values.updatedAt != null ? /* @__PURE__ */ jsx(UpdatedTime, {
5653
+ value: value.values.updatedAt,
5654
+ container
5655
+ }) : void 0);
5656
+ const historyDetail = readStatus ?? (spaces.length === 0 ? t("flowState.emptySpaces") : void 0);
5594
5657
  return /* @__PURE__ */ jsxs("div", {
5595
5658
  className: "flex flex-col gap-4",
5596
5659
  children: [space == null && /* @__PURE__ */ jsxs("section", {
5597
- className: "flex flex-col gap-2",
5660
+ "aria-label": t("flowState.values"),
5661
+ className: "flex flex-col gap-3 rounded-lg bg-muted/50 p-3",
5662
+ children: [/* @__PURE__ */ jsxs("div", {
5663
+ className: "flex items-center gap-3",
5664
+ children: [
5665
+ /* @__PURE__ */ jsxs("div", {
5666
+ className: "min-w-0 flex-1",
5667
+ children: [/* @__PURE__ */ jsx("h3", {
5668
+ className: "m-0 text-sm font-medium",
5669
+ children: t("flowState.values")
5670
+ }), valuesDetail != null && /* @__PURE__ */ jsx("p", {
5671
+ className: "mt-1 mb-0 text-xs text-muted-foreground",
5672
+ children: valuesDetail
5673
+ })]
5674
+ }),
5675
+ /* @__PURE__ */ jsx(Usage, {
5676
+ count: value?.values.count,
5677
+ limit: value?.limits.maxValues
5678
+ }),
5679
+ value != null && value.values.count > 0 && /* @__PURE__ */ jsx(StateClearButton, {
5680
+ name: t("flowState.values"),
5681
+ disabled: busy,
5682
+ onClear: onClearValues,
5683
+ container
5684
+ })
5685
+ ]
5686
+ }), value != null && value.values.count >= value.limits.maxValues * .8 && /* @__PURE__ */ jsx("p", {
5687
+ role: "status",
5688
+ className: "m-0 text-xs text-destructive",
5689
+ children: t("flowState.valuesNearLimit")
5690
+ })]
5691
+ }), /* @__PURE__ */ jsxs("section", {
5692
+ "aria-label": t("flowState.spaces"),
5693
+ className: "flex flex-col gap-3 rounded-lg bg-muted/50 p-3",
5598
5694
  children: [
5599
- /* @__PURE__ */ jsxs("div", {
5600
- className: "flex items-center justify-between gap-3",
5695
+ (space == null || spaces.length === 0) && /* @__PURE__ */ jsxs("div", {
5696
+ className: "flex items-center gap-3",
5601
5697
  children: [
5602
- /* @__PURE__ */ jsx("h3", {
5603
- className: "m-0 min-w-0 flex-1 text-sm font-medium",
5604
- children: t("flowState.values")
5698
+ /* @__PURE__ */ jsxs("div", {
5699
+ className: "min-w-0 flex-1",
5700
+ children: [/* @__PURE__ */ jsx("h3", {
5701
+ className: "m-0 text-sm font-medium",
5702
+ children: space == null ? t("flowState.spaces") : sourceName(space, nodes, t)
5703
+ }), historyDetail != null && /* @__PURE__ */ jsx("p", {
5704
+ className: "mt-1 mb-0 text-xs text-muted-foreground",
5705
+ children: historyDetail
5706
+ })]
5605
5707
  }),
5606
- /* @__PURE__ */ jsx("span", {
5607
- className: "text-xs text-muted-foreground",
5608
- children: t("flowState.entries", {
5609
- count: value.values.length.toLocaleString(),
5610
- limit: value.limits.maxValues.toLocaleString()
5611
- })
5708
+ space == null ? /* @__PURE__ */ jsx(Usage, {
5709
+ count,
5710
+ limit: value?.limits.maxIdentities
5711
+ }) : /* @__PURE__ */ jsx("span", {
5712
+ className: "text-sm tabular-nums",
5713
+ children: value == null ? "—" : t("flowState.records", { count: "0" })
5612
5714
  }),
5613
- value.values.length > 0 && /* @__PURE__ */ jsx(Button$1, {
5614
- variant: "outline",
5615
- size: "sm",
5715
+ space == null && allSpaces.length > 0 && /* @__PURE__ */ jsx(StateClearButton, {
5716
+ name: t("flowState.spaces"),
5616
5717
  disabled: busy,
5617
- onClick: (event) => onClearValues(void 0, event.currentTarget),
5618
- children: t("flowState.clearAll")
5718
+ reason: allSpaces.some((item) => item.pendingCount === 0) ? void 0 : t("flowState.clearBlocked"),
5719
+ onClear: onClearHistory,
5720
+ container
5619
5721
  })
5620
5722
  ]
5621
5723
  }),
5622
- !value.values.length && /* @__PURE__ */ jsx("p", {
5623
- className: "m-0 text-sm text-muted-foreground",
5624
- children: t("flowState.emptyValues")
5625
- }),
5626
- value.values.map((item) => /* @__PURE__ */ jsx(StateValueRow, {
5627
- item,
5628
- client,
5629
- flowId,
5630
- scope: value.scope,
5631
- busy,
5632
- onClear: onClearValues
5633
- }, item.key))
5634
- ]
5635
- }), /* @__PURE__ */ jsxs("section", {
5636
- className: "flex flex-col gap-2",
5637
- children: [
5638
- /* @__PURE__ */ jsxs("div", {
5639
- className: "flex items-center justify-between gap-3",
5640
- children: [/* @__PURE__ */ jsx("h3", {
5641
- className: "m-0 text-sm font-medium",
5642
- children: t("flowState.spaces")
5643
- }), /* @__PURE__ */ jsx("span", {
5644
- className: "text-xs text-muted-foreground",
5645
- children: t("flowState.entries", {
5646
- count: count.toLocaleString(),
5647
- limit: value.limits.maxIdentities.toLocaleString()
5648
- })
5649
- })]
5650
- }),
5651
- count >= value.limits.maxIdentities * .8 && /* @__PURE__ */ jsx("p", {
5724
+ value != null && count != null && count >= value.limits.maxIdentities * .8 && /* @__PURE__ */ jsx("p", {
5652
5725
  role: "status",
5653
- className: "m-0 text-destructive text-xs",
5726
+ className: "m-0 text-xs text-destructive",
5654
5727
  children: t("flowState.nearLimit")
5655
5728
  }),
5656
- !spaces.length && /* @__PURE__ */ jsx("p", {
5657
- className: "m-0 text-sm text-muted-foreground",
5658
- children: t("flowState.emptySpaces")
5659
- }),
5660
- spaces.map((item) => /* @__PURE__ */ jsxs("div", {
5661
- className: "flex items-start justify-between gap-3 border-b border-border/60 py-2",
5662
- children: [/* @__PURE__ */ jsxs("div", {
5663
- className: "min-w-0",
5664
- children: [/* @__PURE__ */ jsxs("p", {
5665
- className: "m-0 break-all font-medium",
5729
+ spaces.length > 0 && /* @__PURE__ */ jsxs("table", {
5730
+ "aria-label": t("flowState.source"),
5731
+ className: "w-full border-collapse text-sm",
5732
+ children: [/* @__PURE__ */ jsx("thead", {
5733
+ className: "sr-only",
5734
+ children: /* @__PURE__ */ jsxs("tr", {
5735
+ className: "text-xs text-muted-foreground",
5666
5736
  children: [
5667
- item.space,
5668
- " ",
5669
- item.space.startsWith("node:") && !nodeIds.includes(item.space.slice(5)) && /* @__PURE__ */ jsxs("span", {
5670
- className: "text-muted-foreground",
5671
- children: [
5672
- "(",
5673
- t("flowState.orphaned"),
5674
- ")"
5675
- ]
5737
+ /* @__PURE__ */ jsx("th", {
5738
+ scope: "col",
5739
+ className: "pb-2 text-left font-normal",
5740
+ children: t("flowState.source")
5741
+ }),
5742
+ /* @__PURE__ */ jsx("th", {
5743
+ scope: "col",
5744
+ className: "pb-2 pl-4 text-right font-normal",
5745
+ children: t("flowState.count")
5746
+ }),
5747
+ /* @__PURE__ */ jsx("th", {
5748
+ scope: "col",
5749
+ className: "pl-4",
5750
+ children: /* @__PURE__ */ jsx("span", {
5751
+ className: "sr-only",
5752
+ children: t("flowState.clear")
5753
+ })
5676
5754
  })
5677
5755
  ]
5678
- }), /* @__PURE__ */ jsxs("p", {
5679
- className: "m-0 text-xs",
5680
- children: [t("flowState.records", { count: item.count.toLocaleString() }), item.pendingCount > 0 && /* @__PURE__ */ jsxs(Fragment$1, { children: [" · ", t("flowState.pendingRecords", { count: item.pendingCount.toLocaleString() })] })]
5681
- })]
5682
- }), /* @__PURE__ */ jsx(Button$1, {
5683
- variant: "outline",
5684
- size: "sm",
5685
- disabled: busy || item.pendingCount > 0,
5686
- onClick: (event) => onClearSpace(item.space, event.currentTarget),
5687
- children: t("flowState.clear")
5756
+ })
5757
+ }), /* @__PURE__ */ jsx("tbody", { children: spaces.map((item) => {
5758
+ const node = nodes.find((entry) => `node:${entry.id}` === item.space);
5759
+ const fallback = /* @__PURE__ */ jsx("i", {
5760
+ "aria-hidden": "true",
5761
+ className: "i-lucide-light:workflow"
5762
+ });
5763
+ return /* @__PURE__ */ jsx(StateDataRow, {
5764
+ name: sourceName(item.space, nodes, t),
5765
+ source: item.space.startsWith("node:") && node == null ? `${item.space} · ${t("flowState.orphaned")}` : item.space,
5766
+ icon: item.space === "flow" ? /* @__PURE__ */ jsx("i", {
5767
+ "aria-hidden": "true",
5768
+ className: "i-lucide-light:database"
5769
+ }) : /* @__PURE__ */ jsx(ContentIcon, {
5770
+ src: node?.icon,
5771
+ className: "size-4",
5772
+ fallback: /* @__PURE__ */ jsx(ContentIcon, {
5773
+ src: node?.fallbackIcon,
5774
+ className: "size-4",
5775
+ fallback
5776
+ })
5777
+ }),
5778
+ quantity: item.reservedCount.toLocaleString(language),
5779
+ updatedAt: item.updatedAt,
5780
+ status: space != null ? readStatus : void 0,
5781
+ disabled: busy,
5782
+ clearReason: item.pendingCount > 0 ? t("flowState.pendingClear", { count: item.pendingCount.toLocaleString(language) }) : void 0,
5783
+ onClear: (trigger) => onClearSpace(item.space, trigger),
5784
+ container
5785
+ }, item.space);
5786
+ }) })]
5787
+ }),
5788
+ space != null && /* @__PURE__ */ jsxs("p", {
5789
+ className: "m-0 flex flex-wrap items-baseline justify-between gap-2 border-t border-border/60 pt-3 text-xs text-muted-foreground",
5790
+ children: [/* @__PURE__ */ jsx("span", { children: t("flowState.globalUsage") }), /* @__PURE__ */ jsx(Usage, {
5791
+ count,
5792
+ limit: value?.limits.maxIdentities
5688
5793
  })]
5689
- }, item.space))
5794
+ })
5690
5795
  ]
5691
5796
  })]
5692
5797
  });
5693
5798
  }
5694
- function FlowStateButton({ client, flowId, nodeIds, history, onClose }) {
5799
+ function FlowStateButton({ client, flowId, nodes, history, notify, onClose }) {
5695
5800
  const t = useTranslate();
5801
+ const language = useLang();
5696
5802
  const [container, setContainer] = useState(null);
5697
- const portal = useCallback((element) => {
5698
- setContainer(element?.closest(".open-flow-workbench") ?? element?.closest(".open-flow-theme") ?? null);
5699
- }, []);
5803
+ const portal = useCallback((element) => setContainer(element?.closest(".open-flow-workbench, .open-flow-theme") ?? null), []);
5700
5804
  const [open, setOpen] = useState(onClose != null);
5701
5805
  const [scope, setScope] = useState("draft");
5702
5806
  const space = history == null ? void 0 : `node:${history.nodeId}`;
5703
- const [data, setData] = useState();
5704
- const [error, setError] = useState();
5807
+ const [read, setRead] = useState({
5808
+ scope: "draft",
5809
+ loading: true
5810
+ });
5811
+ const data = read.scope === scope ? read.data : void 0;
5812
+ const loading = read.scope !== scope || read.loading;
5705
5813
  const [busy, setBusy] = useState(false);
5814
+ const pending = useRef(false);
5706
5815
  const [refresh, setRefresh] = useState(0);
5707
5816
  const [confirm, setConfirm] = useState();
5708
5817
  const cancelButton = useRef(null);
5818
+ const dialogRoot = useRef(null);
5709
5819
  const confirmTrigger = useRef();
5710
- useEffect(() => {
5711
- if (confirm != null) cancelButton.current?.focus();
5712
- }, [confirm]);
5713
- const cancelConfirm = () => {
5714
- setConfirm(void 0);
5715
- confirmTrigger.current?.focus();
5716
- };
5717
5820
  useEffect(() => {
5718
5821
  if (!open) return;
5719
- let active = true;
5720
- setData(void 0);
5721
- setError(void 0);
5722
- client.getFlowState(flowId, scope).then((result) => {
5723
- if (active) setData(result);
5822
+ const controller = new AbortController();
5823
+ setRead((previous) => ({
5824
+ scope,
5825
+ data: previous.scope === scope ? previous.data : void 0,
5826
+ loading: true
5827
+ }));
5828
+ client.getFlowStateSummary(flowId, scope, controller.signal).then((result) => {
5829
+ if (!controller.signal.aborted) setRead({
5830
+ scope,
5831
+ data: result,
5832
+ loading: false
5833
+ });
5724
5834
  }, (reason) => {
5725
- if (active) setError(String(reason));
5835
+ if (!controller.signal.aborted) setRead((previous) => ({
5836
+ ...previous,
5837
+ loading: false,
5838
+ error: errorNotice(reason, t).message
5839
+ }));
5726
5840
  });
5727
- return () => {
5728
- active = false;
5729
- };
5841
+ return () => controller.abort();
5730
5842
  }, [
5731
5843
  client,
5732
5844
  flowId,
5733
5845
  scope,
5734
5846
  refresh,
5735
5847
  open,
5736
- space
5848
+ t
5737
5849
  ]);
5738
5850
  const clear = async (target) => {
5851
+ if (pending.current) return;
5852
+ pending.current = true;
5739
5853
  setBusy(true);
5740
- setError(void 0);
5741
5854
  try {
5742
- if (target.space != null) await client.clearFlowDedupe(flowId, scope, target.space);
5743
- else await client.clearFlowState(flowId, scope, target.key);
5855
+ if (target.kind === "values") await client.clearFlowState(flowId, target.scope);
5856
+ else if (target.space != null) await client.clearFlowDedupe(flowId, target.scope, target.space);
5857
+ else await client.clearAllFlowDedupe(flowId, target.scope);
5858
+ confirmTrigger.current = dialogRoot.current ?? void 0;
5744
5859
  setConfirm(void 0);
5745
5860
  setRefresh((value) => value + 1);
5861
+ dialogRoot.current?.focus();
5746
5862
  } catch (reason) {
5747
- setError(String(reason));
5863
+ const conflict = target.kind === "dedupe" && reason instanceof ApiError && reason.code === controlErrorCode.flowConflict;
5864
+ notify(conflict ? {
5865
+ kind: "error",
5866
+ message: t("flowState.clearBlocked")
5867
+ } : errorNotice(reason, t));
5868
+ if (conflict) {
5869
+ confirmTrigger.current = dialogRoot.current ?? void 0;
5870
+ setConfirm(void 0);
5871
+ setRefresh((value) => value + 1);
5872
+ }
5748
5873
  } finally {
5874
+ pending.current = false;
5749
5875
  setBusy(false);
5750
5876
  }
5751
5877
  };
5752
- const request = (target, trigger) => {
5878
+ const request = (kind, targetSpace, trigger) => {
5879
+ if (pending.current || data == null) return;
5880
+ const targets = data.spaces.filter((item) => targetSpace == null || item.space === targetSpace);
5881
+ if (kind === "dedupe" && !targets.some((item) => item.pendingCount === 0)) return;
5882
+ const target = {
5883
+ kind,
5884
+ scope,
5885
+ space: targetSpace,
5886
+ name: kind === "values" ? t("flowState.values") : targetSpace == null ? t("flowState.spaces") : sourceName(targetSpace, nodes, t),
5887
+ count: kind === "values" ? data.values.count : targets.filter((item) => item.pendingCount === 0).reduce((total, item) => total + item.count, 0),
5888
+ pendingCount: kind === "values" ? 0 : targets.reduce((total, item) => total + item.pendingCount, 0)
5889
+ };
5753
5890
  confirmTrigger.current = trigger;
5754
5891
  if (scope === "live") setConfirm(target);
5755
5892
  else clear(target);
@@ -5759,13 +5896,20 @@ function FlowStateButton({ client, flowId, nodeIds, history, onClose }) {
5759
5896
  ref: portal,
5760
5897
  children: /* @__PURE__ */ jsxs(Dialog, {
5761
5898
  open,
5762
- onOpenChange: (next) => {
5763
- if (!busy) {
5764
- setOpen(next);
5765
- if (next) setScope("draft");
5766
- if (!next) onClose?.();
5767
- setConfirm(void 0);
5899
+ onOpenChange: (next, details) => {
5900
+ if (pending.current) {
5901
+ details.cancel();
5902
+ return;
5768
5903
  }
5904
+ setOpen(next);
5905
+ setConfirm(void 0);
5906
+ if (next) {
5907
+ setScope("draft");
5908
+ setRead({
5909
+ scope: "draft",
5910
+ loading: true
5911
+ });
5912
+ } else onClose?.();
5769
5913
  },
5770
5914
  children: [onClose == null && /* @__PURE__ */ jsxs(DialogTrigger, {
5771
5915
  render: /* @__PURE__ */ jsx(Button$1, {
@@ -5777,121 +5921,150 @@ function FlowStateButton({ client, flowId, nodeIds, history, onClose }) {
5777
5921
  className: "i-lucide-light:database"
5778
5922
  }), t(space == null ? "flowState.title" : "flowState.history")]
5779
5923
  }), /* @__PURE__ */ jsxs(DialogContent, {
5924
+ ref: dialogRoot,
5780
5925
  container,
5781
- className: "max-h-[80vh] overflow-auto",
5926
+ className: "flex h-[min(32rem,80dvh)] flex-col overflow-hidden sm:max-w-2xl",
5782
5927
  closeLabel: t("common.close"),
5783
5928
  children: [
5784
- /* @__PURE__ */ jsx(DialogTitle, { children: t(space == null ? "flowState.title" : "flowState.history") }),
5785
- /* @__PURE__ */ jsxs("div", {
5786
- className: "flex flex-wrap items-center gap-3",
5787
- children: [
5788
- /* @__PURE__ */ jsxs(NativeSelect, {
5929
+ /* @__PURE__ */ jsx(DialogTitle, {
5930
+ className: "break-words pr-8",
5931
+ children: space == null ? t("flowState.title") : t("flowState.sourceHistory", { name: sourceName(space, nodes, t) })
5932
+ }),
5933
+ /* @__PURE__ */ jsxs(Tabs, {
5934
+ value: scope,
5935
+ onValueChange: (value) => {
5936
+ if (!pending.current) {
5937
+ setScope(value);
5938
+ setConfirm(void 0);
5939
+ }
5940
+ },
5941
+ className: "min-h-0 flex-1 gap-4",
5942
+ children: [/* @__PURE__ */ jsxs("div", {
5943
+ className: "flex shrink-0 items-center justify-between gap-2",
5944
+ children: [/* @__PURE__ */ jsxs(TabsList, {
5789
5945
  "aria-label": t("flowState.scope"),
5790
- value: scope,
5791
- disabled: busy,
5792
- onChange: (event) => {
5793
- setScope(event.target.value);
5794
- setConfirm(void 0);
5795
- },
5796
- children: [/* @__PURE__ */ jsx(NativeSelectOption, {
5946
+ children: [/* @__PURE__ */ jsx(TabsTrigger, {
5797
5947
  value: "draft",
5948
+ disabled: busy,
5798
5949
  children: t("flowState.draft")
5799
- }), /* @__PURE__ */ jsx(NativeSelectOption, {
5950
+ }), /* @__PURE__ */ jsx(TabsTrigger, {
5800
5951
  value: "live",
5952
+ disabled: busy,
5801
5953
  children: t("flowState.live")
5802
5954
  })]
5803
- }),
5804
- /* @__PURE__ */ jsx(Button$1, {
5805
- variant: "outline",
5806
- size: "sm",
5807
- disabled: busy,
5808
- onClick: () => setRefresh((value) => value + 1),
5809
- children: t("flowState.refresh")
5810
- }),
5811
- /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
5812
- render: /* @__PURE__ */ jsx(Button$1, {
5813
- variant: "ghost",
5814
- size: "xs",
5815
- className: "ml-auto text-muted-foreground"
5816
- }),
5817
- children: t("flowState.rules")
5818
- }), /* @__PURE__ */ jsx(TooltipContent, {
5819
- container,
5820
- className: "max-w-xs",
5821
- children: /* @__PURE__ */ jsxs("div", {
5822
- className: "flex flex-col gap-2",
5823
- children: [
5824
- /* @__PURE__ */ jsx("p", {
5825
- className: "m-0",
5826
- children: t("flowState.isolation")
5827
- }),
5828
- /* @__PURE__ */ jsx("p", {
5829
- className: "m-0",
5830
- children: t("flowState.retention")
5831
- }),
5832
- data != null && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("p", {
5833
- className: "m-0",
5834
- children: t("flowState.valueLimit", { size: data.limits.maxValueBytes.toLocaleString() })
5835
- }), /* @__PURE__ */ jsx("p", {
5836
- className: "m-0",
5837
- children: t("flowState.sharedLimit", {
5838
- count: data.spaces.length,
5839
- limit: data.limits.maxSpaces
5955
+ }), /* @__PURE__ */ jsxs("div", {
5956
+ className: "flex shrink-0 items-center gap-2",
5957
+ children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
5958
+ render: /* @__PURE__ */ jsx(Button$1, {
5959
+ "aria-label": t("flowState.refresh"),
5960
+ variant: "ghost",
5961
+ size: "icon-sm",
5962
+ disabled: busy || loading,
5963
+ onClick: () => setRefresh((value) => value + 1)
5964
+ }),
5965
+ children: /* @__PURE__ */ jsx("i", {
5966
+ "aria-hidden": "true",
5967
+ className: "i-lucide-light:refresh-cw"
5968
+ })
5969
+ }), /* @__PURE__ */ jsx(TooltipContent, {
5970
+ container,
5971
+ children: t("flowState.refresh")
5972
+ })] }), /* @__PURE__ */ jsx(HelpButton, {
5973
+ label: t("flowState.rules"),
5974
+ container,
5975
+ size: 28,
5976
+ children: /* @__PURE__ */ jsxs("div", {
5977
+ className: "flex flex-col gap-2",
5978
+ children: [
5979
+ /* @__PURE__ */ jsx("p", {
5980
+ className: "m-0",
5981
+ children: t("flowState.valuesHelp", { name: t("flowState.values") })
5982
+ }),
5983
+ /* @__PURE__ */ jsx("p", {
5984
+ className: "m-0",
5985
+ children: t("flowState.historyHelp", { name: t("flowState.spaces") })
5986
+ }),
5987
+ /* @__PURE__ */ jsx("p", {
5988
+ className: "m-0",
5989
+ children: t("flowState.isolation")
5840
5990
  })
5841
- })] })
5842
- ]
5843
- })
5844
- })] })
5845
- ]
5846
- }),
5847
- error && /* @__PURE__ */ jsx("p", {
5848
- role: "alert",
5849
- className: "text-destructive",
5850
- children: error
5851
- }),
5852
- confirm && /* @__PURE__ */ jsxs("div", {
5853
- role: "alert",
5854
- className: "flex flex-col gap-2 rounded border border-border p-3",
5855
- children: [
5856
- /* @__PURE__ */ jsx("p", { children: t("flowState.confirmLive") }),
5857
- /* @__PURE__ */ jsx("p", {
5858
- className: "break-all",
5859
- children: confirm.space ?? confirm.key ?? t("flowState.values")
5860
- }),
5861
- /* @__PURE__ */ jsxs("div", {
5862
- className: "flex gap-2",
5863
- children: [/* @__PURE__ */ jsx(Button$1, {
5864
- disabled: busy,
5865
- variant: "destructive",
5866
- onClick: () => void clear(confirm),
5867
- children: t("flowState.clear")
5868
- }), /* @__PURE__ */ jsx(Button$1, {
5869
- ref: cancelButton,
5870
- disabled: busy,
5871
- variant: "outline",
5872
- onClick: cancelConfirm,
5873
- children: t("flowState.cancel")
5991
+ ]
5992
+ })
5993
+ })]
5994
+ })]
5995
+ }), /* @__PURE__ */ jsx(NativeScrollArea, {
5996
+ className: "min-h-0 flex-1 overflow-y-auto",
5997
+ children: /* @__PURE__ */ jsxs(TabsContent, {
5998
+ value: scope,
5999
+ "aria-busy": busy || loading,
6000
+ children: [read.scope === scope && read.error != null && /* @__PURE__ */ jsxs("div", {
6001
+ className: "mb-3 flex flex-wrap items-center gap-2",
6002
+ children: [/* @__PURE__ */ jsx("p", {
6003
+ role: "alert",
6004
+ className: "m-0 min-w-0 flex-1 text-sm text-destructive",
6005
+ children: read.error
6006
+ }), /* @__PURE__ */ jsx(Button$1, {
6007
+ variant: "outline",
6008
+ size: "sm",
6009
+ disabled: loading || busy,
6010
+ onClick: () => setRefresh((value) => value + 1),
6011
+ children: t("flowState.retry")
6012
+ })]
6013
+ }), /* @__PURE__ */ jsx(FlowStatePanel, {
6014
+ value: data,
6015
+ loading,
6016
+ space,
6017
+ nodes,
6018
+ container,
6019
+ busy: busy || loading,
6020
+ onClearValues: (trigger) => request("values", void 0, trigger),
6021
+ onClearHistory: (trigger) => request("dedupe", void 0, trigger),
6022
+ onClearSpace: (sourceSpace, trigger) => request("dedupe", sourceSpace, trigger)
5874
6023
  })]
5875
6024
  })
5876
- ]
6025
+ })]
5877
6026
  }),
5878
- data == null ? /* @__PURE__ */ jsx("p", {
5879
- role: "status",
5880
- children: error == null ? t("flowState.loading") : ""
5881
- }) : /* @__PURE__ */ jsx(FlowStatePanel, {
5882
- value: data,
5883
- client,
5884
- flowId,
5885
- space,
5886
- nodeIds,
5887
- busy,
5888
- onClearValues: (key, trigger) => request({ key }, trigger),
5889
- onClearSpace: (targetSpace, trigger) => request({ space: targetSpace }, trigger)
5890
- }, JSON.stringify([
5891
- flowId,
5892
- scope,
5893
- refresh
5894
- ]))
6027
+ /* @__PURE__ */ jsx(Dialog, {
6028
+ open: confirm != null,
6029
+ onOpenChange: (next, details) => {
6030
+ if (pending.current) details.cancel();
6031
+ else if (!next) setConfirm(void 0);
6032
+ },
6033
+ children: /* @__PURE__ */ jsxs(DialogContent, {
6034
+ container,
6035
+ initialFocus: cancelButton,
6036
+ finalFocus: () => confirmTrigger.current?.isConnected ? confirmTrigger.current : dialogRoot.current,
6037
+ showCloseButton: false,
6038
+ children: [
6039
+ /* @__PURE__ */ jsx(DialogTitle, { children: t("flowState.confirmTitle", { name: confirm?.name ?? "" }) }),
6040
+ /* @__PURE__ */ jsx(DialogDescription, { children: confirm && t(confirm.kind === "values" ? "flowState.confirmValues" : "flowState.confirmHistory", {
6041
+ count: confirm.count.toLocaleString(language),
6042
+ scope: t(confirm.scope === "draft" ? "flowState.draft" : "flowState.live")
6043
+ }) }),
6044
+ confirm != null && confirm.pendingCount > 0 && /* @__PURE__ */ jsx("p", {
6045
+ className: "m-0 text-sm text-muted-foreground",
6046
+ children: t("flowState.retainedHistory", { count: confirm.pendingCount.toLocaleString(language) })
6047
+ }),
6048
+ /* @__PURE__ */ jsxs("div", {
6049
+ className: "flex justify-end gap-2",
6050
+ children: [/* @__PURE__ */ jsx(Button$1, {
6051
+ ref: cancelButton,
6052
+ disabled: busy,
6053
+ variant: "outline",
6054
+ onClick: () => setConfirm(void 0),
6055
+ children: t("flowState.cancel")
6056
+ }), /* @__PURE__ */ jsx(Button$1, {
6057
+ disabled: busy,
6058
+ variant: "destructive",
6059
+ onClick: () => {
6060
+ if (confirm != null) clear(confirm);
6061
+ },
6062
+ children: t("flowState.confirmClear")
6063
+ })]
6064
+ })
6065
+ ]
6066
+ })
6067
+ })
5895
6068
  ]
5896
6069
  })]
5897
6070
  })
@@ -8527,47 +8700,54 @@ function ArrayValueFields(props) {
8527
8700
  label,
8528
8701
  children: (item, index, handle) => {
8529
8702
  const itemSchema = Array.isArray(source.items) ? source.items[index] ?? source.additionalItems ?? {} : source.items ?? {};
8703
+ const addLabel = `${t("valueEditor.addItem")} ${label}.${index}`;
8704
+ const removeLabel = `${t("valueEditor.remove")} ${label}.${index}`;
8530
8705
  return /* @__PURE__ */ jsx("div", {
8531
8706
  "data-array-field-content": true,
8532
8707
  children: child(index, itemSchema, item, (next, deletion) => onChange(setArrayItem(array, index, next), deletion), {
8533
8708
  arrayChild: true,
8534
8709
  header: /* @__PURE__ */ jsx(Fragment$1, {}),
8535
- disclosureContent: /* @__PURE__ */ jsxs("span", {
8710
+ disclosureContent: props.showIndices !== false ? /* @__PURE__ */ jsxs("span", {
8536
8711
  className: valueEditor_module_default.arrayIndex,
8537
8712
  children: [index, "."]
8538
- }),
8713
+ }) : void 0,
8714
+ ...props.showIndices === false ? { depth: props.depth ?? 0 } : {},
8539
8715
  leadingControl: handle || void 0,
8540
8716
  onDefinitionChange: props.onDefinitionChange && !Array.isArray(source.items) ? (items, nextValue, deletion) => props.onDefinitionChange({
8541
8717
  ...source,
8542
8718
  items
8543
8719
  }, array.map((entry, at) => at === index ? nextValue : entry), deletion) : void 0,
8544
8720
  hideOptions: true,
8545
- actions: !props.readOnly && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Button$1, {
8546
- type: "button",
8547
- variant: "ghost",
8548
- size: "icon-xs",
8549
- "aria-label": `${t("valueEditor.addItem")} ${label}.${index}`,
8550
- disabled: disabled || typeof source.maxItems === "number" && array.length >= source.maxItems,
8551
- onClick: () => onChange(array.toSpliced(index + 1, 0, initialValue(Array.isArray(source.items) ? source.items[index + 1] ?? source.additionalItems ?? {} : source.items ?? {}))),
8721
+ actions: !props.readOnly && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
8722
+ render: /* @__PURE__ */ jsx(Button$1, {
8723
+ type: "button",
8724
+ variant: "ghost",
8725
+ size: "icon-xs",
8726
+ "aria-label": addLabel,
8727
+ disabled: disabled || typeof source.maxItems === "number" && array.length >= source.maxItems,
8728
+ onClick: () => onChange(array.toSpliced(index + 1, 0, initialValue(Array.isArray(source.items) ? source.items[index + 1] ?? source.additionalItems ?? {} : source.items ?? {})))
8729
+ }),
8552
8730
  children: /* @__PURE__ */ jsx("i", {
8553
8731
  "aria-hidden": "true",
8554
8732
  className: "i-tabler-light:square-rounded-plus text-lg"
8555
8733
  })
8556
- }), /* @__PURE__ */ jsx(Button$1, {
8557
- type: "button",
8558
- variant: "ghost",
8559
- size: "icon-xs",
8560
- "aria-label": `${t("valueEditor.remove")} ${label}.${index}`,
8561
- disabled: disabled || typeof source.minItems === "number" && array.length <= source.minItems,
8562
- onClick: () => onChange(array.toSpliced(index, 1), {
8563
- target: "arrayItem",
8564
- name: `${label}.${index}`
8734
+ }), /* @__PURE__ */ jsx(TooltipContent, { children: addLabel })] }), /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
8735
+ render: /* @__PURE__ */ jsx(Button$1, {
8736
+ type: "button",
8737
+ variant: "ghost",
8738
+ size: "icon-xs",
8739
+ "aria-label": removeLabel,
8740
+ disabled: disabled || typeof source.minItems === "number" && array.length <= source.minItems,
8741
+ onClick: () => onChange(array.toSpliced(index, 1), {
8742
+ target: "arrayItem",
8743
+ name: `${label}.${index}`
8744
+ })
8565
8745
  }),
8566
8746
  children: /* @__PURE__ */ jsx("i", {
8567
8747
  "aria-hidden": "true",
8568
8748
  className: "i-tabler-light:square-rounded-minus text-lg"
8569
8749
  })
8570
- })] })
8750
+ }), /* @__PURE__ */ jsx(TooltipContent, { children: removeLabel })] })] })
8571
8751
  })
8572
8752
  });
8573
8753
  }
@@ -23441,6 +23621,32 @@ function isUnknownRecord(value) {
23441
23621
  return isPlainObject(value);
23442
23622
  }
23443
23623
  //#endregion
23624
+ //#region src/ui/browser/native-select.tsx
23625
+ function NativeSelect({ className, size = "default", ...props }) {
23626
+ return /* @__PURE__ */ jsxs("div", {
23627
+ className: cn("group/native-select relative w-fit has-[select:disabled]:opacity-50", className),
23628
+ "data-slot": "native-select-wrapper",
23629
+ "data-size": size,
23630
+ children: [/* @__PURE__ */ jsx("select", {
23631
+ "data-slot": "native-select",
23632
+ "data-size": size,
23633
+ className: "h-8 w-full min-w-0 appearance-none rounded-[var(--ui-control-radius,calc(var(--ui-radius)_+_2px))] border border-input bg-[var(--ui-control-background,var(--ui-muted))] py-1 pr-8 pl-2.5 text-sm transition-colors outline-none select-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground data-[slot=native-select]:focus-visible:text-field-focus data-[slot=native-select]:focus-visible:aria-invalid:invalid-text-field-focus disabled:pointer-events-none disabled:cursor-not-allowed aria-invalid:border-destructive data-[size=sm]:h-7 data-[size=sm]:rounded-[var(--ui-control-radius,var(--ui-radius))] data-[size=sm]:py-0.5 dark:bg-[var(--ui-control-background,var(--ui-muted))] dark:hover:bg-input/50",
23634
+ ...props
23635
+ }), /* @__PURE__ */ jsx(ChevronDown, {
23636
+ className: "pointer-events-none absolute top-1/2 right-2.5 size-4 -translate-y-1/2 text-muted-foreground select-none",
23637
+ "aria-hidden": "true",
23638
+ "data-slot": "native-select-icon"
23639
+ })]
23640
+ });
23641
+ }
23642
+ function NativeSelectOption({ className, ...props }) {
23643
+ return /* @__PURE__ */ jsx("option", {
23644
+ "data-slot": "native-select-option",
23645
+ className: cn("bg-[Canvas] text-[CanvasText]", className),
23646
+ ...props
23647
+ });
23648
+ }
23649
+ //#endregion
23444
23650
  //#region src/ui/browser/simple-code-editor.ts
23445
23651
  var import_lib = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports) => {
23446
23652
  var __assign = exports && exports.__assign || function() {
@@ -25208,37 +25414,6 @@ function FieldNullable({ name, checked, onChange }) {
25208
25414
  }), /* @__PURE__ */ jsx(TooltipContent, { children: t(checked ? "valueEditor.nullableDescription" : "valueEditor.notNullableDescription") })] });
25209
25415
  }
25210
25416
  //#endregion
25211
- //#region src/ui/browser/helpButton.tsx
25212
- /** Supplementary, non-interactive help that can also be opened by click. */
25213
- function HelpButton({ label, children, size = 24 }) {
25214
- const id = useId();
25215
- const [tooltip] = useState(() => createTooltipHandle());
25216
- return /* @__PURE__ */ jsxs(Tooltip, {
25217
- handle: tooltip,
25218
- children: [/* @__PURE__ */ jsx(TooltipTrigger, {
25219
- id,
25220
- handle: tooltip,
25221
- closeOnClick: false,
25222
- onClick: () => tooltip.open(id),
25223
- render: /* @__PURE__ */ jsx(Button$1, {
25224
- type: "button",
25225
- variant: "ghost",
25226
- size: "icon-xs",
25227
- style: {
25228
- width: size,
25229
- height: size
25230
- },
25231
- "aria-label": label
25232
- }),
25233
- children: /* @__PURE__ */ jsx("i", {
25234
- "aria-hidden": "true",
25235
- className: "i-lucide-light:info text-muted-foreground",
25236
- style: { fontSize: size * 5 / 8 }
25237
- })
25238
- }), /* @__PURE__ */ jsx(TooltipContent, { children })]
25239
- });
25240
- }
25241
- //#endregion
25242
25417
  //#region src/workbench/browser/runtime/editor/inspectorSection.tsx
25243
25418
  function InspectorSection({ title, children, className, contentInset = true, ...props }) {
25244
25419
  return /* @__PURE__ */ jsxs("section", {
@@ -29370,6 +29545,11 @@ function ResolutionDefinition({ disabled, selection, store }) {
29370
29545
  }
29371
29546
  //#endregion
29372
29547
  //#region src/workbench/browser/runtime/editor/skipDuplicatesSection.tsx
29548
+ var fieldsSchema = {
29549
+ type: "array",
29550
+ items: { type: "string" }
29551
+ };
29552
+ var ignoreDraft$1 = () => {};
29373
29553
  function SkipDuplicatesSection({ task, disabled, onSave, history }) {
29374
29554
  const t = useTranslate();
29375
29555
  const id = useId();
@@ -29392,21 +29572,14 @@ function SkipDuplicatesSection({ task, disabled, onSave, history }) {
29392
29572
  });
29393
29573
  }));
29394
29574
  const initial = task.executor.kind === "skip-duplicates" ? task.executor.fields : [];
29395
- const [fields, setFields] = useState(initial.length ? initial : [""]);
29396
- const inputs = useRef([]);
29397
- const focusField = useRef();
29398
- useEffect(() => {
29399
- if (focusField.current == null) return;
29400
- inputs.current[focusField.current]?.focus();
29401
- focusField.current = void 0;
29402
- }, [fields]);
29575
+ const [fields, setFields] = useState(initial);
29403
29576
  const [error, setError] = useState();
29404
29577
  useEffect(() => {
29405
29578
  const previous = changes.value;
29406
29579
  changes.sync(task.executor);
29407
29580
  if (previous.kind === "skip-duplicates" && changes.value.kind === "skip-duplicates" && !dequal(previous.fields, changes.value.fields)) {
29408
29581
  request.current?.abort();
29409
- setFields(changes.value.fields.length ? changes.value.fields : [""]);
29582
+ setFields(changes.value.fields);
29410
29583
  setError(void 0);
29411
29584
  }
29412
29585
  }, [changes, task.executor]);
@@ -29421,11 +29594,10 @@ function SkipDuplicatesSection({ task, disabled, onSave, history }) {
29421
29594
  request.current?.abort();
29422
29595
  const controller = new AbortController();
29423
29596
  request.current = controller;
29424
- const rows = next.length ? next : [""];
29425
- setFields(rows);
29597
+ setFields(next);
29426
29598
  await Promise.resolve();
29427
29599
  if (controller.signal.aborted || request.current !== controller) return;
29428
- const normalized = rows.map((field) => field.trim());
29600
+ const normalized = next.map((field) => field.trim());
29429
29601
  const parsed = skipDuplicatesConfigSchema.safeParse({ fields: normalized.filter(Boolean) });
29430
29602
  if (!parsed.success) {
29431
29603
  setError("invalid");
@@ -29447,87 +29619,60 @@ function SkipDuplicatesSection({ task, disabled, onSave, history }) {
29447
29619
  if (task.executor.kind !== "skip-duplicates") return null;
29448
29620
  return /* @__PURE__ */ jsx(InspectorSection, {
29449
29621
  title: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
29622
+ id: `${id}-label`,
29450
29623
  className: "min-w-0 flex-1",
29451
- children: t("flowState.nodeTitle")
29624
+ children: t("flowState.field")
29452
29625
  }), history] }),
29453
29626
  children: /* @__PURE__ */ jsxs("div", {
29454
29627
  className: "node-settings flex flex-col gap-3",
29455
- children: [
29456
- /* @__PURE__ */ jsxs(Field, { children: [
29457
- /* @__PURE__ */ jsx(FieldLabel, {
29458
- htmlFor: `${id}-field-0`,
29459
- children: t("flowState.field")
29460
- }),
29461
- fields.map((field, index) => /* @__PURE__ */ jsxs("div", {
29462
- className: "flex items-center gap-2",
29463
- children: [/* @__PURE__ */ jsx(Input, {
29464
- ref: (element) => {
29465
- inputs.current[index] = element;
29466
- },
29467
- id: `${id}-field-${index}`,
29468
- "aria-label": t("flowState.fieldIndex", { index: index + 1 }),
29469
- "aria-invalid": error === "invalid" || void 0,
29470
- "aria-describedby": error === "invalid" ? `${id}-error` : void 0,
29471
- disabled,
29472
- value: field,
29473
- placeholder: t(fields.length === 1 ? "flowState.fieldPlaceholder" : "flowState.pathPlaceholder"),
29474
- onChange: (event) => change(fields.map((value, at) => at === index ? event.target.value : value)),
29475
- onBlur: () => {
29476
- commit();
29477
- },
29478
- onKeyDown: (event) => {
29479
- if (event.key === "Enter") {
29480
- event.preventDefault();
29481
- event.currentTarget.blur();
29482
- }
29483
- }
29484
- }), !disabled && (fields.length > 1 || field !== "") && /* @__PURE__ */ jsx(Button$1, {
29485
- variant: "ghost",
29486
- size: "xs",
29487
- "aria-label": t("flowState.removeField", { index: index + 1 }),
29488
- onMouseDown: (event) => event.preventDefault(),
29489
- onClick: () => {
29490
- focusField.current = Math.max(0, index - 1);
29491
- commit(fields.filter((_, at) => at !== index));
29492
- },
29493
- children: t("flowState.delete")
29494
- })]
29495
- }, index)),
29496
- !disabled && /* @__PURE__ */ jsx("div", {
29497
- className: "flex",
29498
- children: /* @__PURE__ */ jsx(Button$1, {
29499
- variant: "ghost",
29500
- size: "sm",
29501
- onMouseDown: (event) => event.preventDefault(),
29502
- onClick: () => {
29503
- const empty = fields.findIndex((field) => field.trim() === "");
29504
- if (empty >= 0) {
29505
- inputs.current[empty]?.focus();
29506
- return;
29507
- }
29508
- focusField.current = fields.length;
29509
- change([...fields, ""]);
29510
- },
29511
- children: t("flowState.addField")
29512
- })
29513
- })
29514
- ] }),
29515
- error != null && /* @__PURE__ */ jsxs("div", {
29516
- role: "alert",
29517
- id: `${id}-error`,
29518
- children: [/* @__PURE__ */ jsx("p", { children: t(error === "invalid" ? "flowState.invalidFields" : "flowState.saveFailed") }), error === "save" && /* @__PURE__ */ jsx(Button$1, {
29628
+ children: [/* @__PURE__ */ jsxs(Field, {
29629
+ "aria-labelledby": `${id}-label`,
29630
+ "aria-invalid": error === "invalid" || void 0,
29631
+ "aria-describedby": `${id}-description${error === "invalid" ? ` ${id}-error` : ""}`,
29632
+ onBlur: (event) => {
29633
+ if (event.target instanceof HTMLInputElement) commit();
29634
+ },
29635
+ onKeyDown: (event) => {
29636
+ if (event.key === "Enter" && event.target instanceof HTMLInputElement) commit();
29637
+ },
29638
+ children: [/* @__PURE__ */ jsx(FieldDescription, {
29639
+ id: `${id}-description`,
29640
+ className: "text-xs",
29641
+ children: t("flowState.fieldDescription")
29642
+ }), /* @__PURE__ */ jsx(FieldTable, {
29643
+ layout: "values",
29644
+ typeColumn: false,
29645
+ columnHeadings: false,
29646
+ actionSlots: 2,
29647
+ standalone: true,
29648
+ children: /* @__PURE__ */ jsx(ArrayValueFields, {
29649
+ label: t("flowState.field"),
29650
+ showIndices: false,
29651
+ schema: fieldsSchema,
29652
+ value: fields,
29653
+ path: "/fields",
29519
29654
  disabled,
29520
- onClick: () => void commit(),
29521
- variant: "outline",
29522
- size: "sm",
29523
- children: t("flowState.retry")
29524
- })]
29525
- }),
29526
- /* @__PURE__ */ jsx("p", {
29527
- className: "m-0 text-muted-foreground text-xs",
29528
- children: t("flowState.warning")
29529
- })
29530
- ]
29655
+ readOnly: disabled,
29656
+ hideValueTools: true,
29657
+ onDraftIssue: ignoreDraft$1,
29658
+ onChange: (value) => {
29659
+ const next = value;
29660
+ change(next);
29661
+ if (next.length !== fields.length) commit(next);
29662
+ }
29663
+ })
29664
+ })]
29665
+ }), error != null && /* @__PURE__ */ jsxs("div", {
29666
+ role: "alert",
29667
+ id: `${id}-error`,
29668
+ children: [/* @__PURE__ */ jsx("p", { children: t(error === "invalid" ? "flowState.invalidFields" : "flowState.saveFailed") }), error === "save" && /* @__PURE__ */ jsx(Button$1, {
29669
+ disabled,
29670
+ onClick: () => void commit(),
29671
+ variant: "outline",
29672
+ size: "sm",
29673
+ children: t("flowState.retry")
29674
+ })]
29675
+ })]
29531
29676
  })
29532
29677
  });
29533
29678
  }
@@ -31515,7 +31660,13 @@ function NodeInspector({ onAddWorkflowOutputs, onAddResponseNode, webhookDeploym
31515
31660
  history: store != null && store.$.flowId.value != null ? /* @__PURE__ */ jsx(FlowStateButton, {
31516
31661
  client: store.state,
31517
31662
  flowId: store.$.flowId.value,
31518
- nodeIds: Object.keys(revision.graph()?.nodes ?? {}),
31663
+ nodes: selection?.kind === "task" ? [{
31664
+ id: selection.id,
31665
+ title: nodeTitle(selection, t),
31666
+ icon: sourceNodeIcons?.[selection.id] ?? semanticNodeIcon(selection, {}),
31667
+ fallbackIcon: nodeIcon(selection)
31668
+ }] : [],
31669
+ notify: store.notify,
31519
31670
  history: { nodeId: selection.id }
31520
31671
  }, selection.id) : void 0,
31521
31672
  onSave: (before, value) => store?.nodes.saveTaskSettings(selection.id, {
@@ -32204,7 +32355,7 @@ var WorkbenchCanvas = forwardRef(function WorkbenchCanvas({ nodePicker, annotati
32204
32355
  onDuplicate(positions, offset);
32205
32356
  },
32206
32357
  onMoveNodes,
32207
- onChangeDirection,
32358
+ onChangeDirection: disabled ? void 0 : onChangeDirection,
32208
32359
  onMoveViewport,
32209
32360
  subscribeCanvasCommands,
32210
32361
  onCopy: (nodeIds) => {
@@ -32573,19 +32724,39 @@ function PublicationActor({ actorId, store }) {
32573
32724
  /** Layout restoration deliberately preserves the current viewport and other graphs. */
32574
32725
  function changeRevisionCanvasSession(session, graphId, change) {
32575
32726
  const previous = session[graphId];
32576
- const next = change.kind === "move" ? {
32577
- ...previous,
32578
- positions: {
32579
- ...previous?.positions,
32580
- ...change.positions
32581
- }
32582
- } : change.kind === "viewport" ? {
32583
- ...previous,
32584
- viewport: change.viewport
32585
- } : {
32586
- ...previous,
32587
- positions: void 0
32588
- };
32727
+ let next;
32728
+ switch (change.kind) {
32729
+ case "move":
32730
+ next = {
32731
+ ...previous,
32732
+ positions: {
32733
+ ...previous?.positions,
32734
+ ...change.positions
32735
+ }
32736
+ };
32737
+ break;
32738
+ case "direction":
32739
+ next = {
32740
+ ...previous,
32741
+ direction: change.direction,
32742
+ positions: {
32743
+ ...previous?.positions,
32744
+ ...change.positions
32745
+ }
32746
+ };
32747
+ break;
32748
+ case "viewport":
32749
+ next = {
32750
+ ...previous,
32751
+ viewport: change.viewport
32752
+ };
32753
+ break;
32754
+ case "restore": next = {
32755
+ ...previous,
32756
+ direction: void 0,
32757
+ positions: void 0
32758
+ };
32759
+ }
32589
32760
  return {
32590
32761
  ...session,
32591
32762
  [graphId]: next
@@ -32647,22 +32818,20 @@ function RevisionCanvas({ draft, presentation, theme, interactiveMode$, label, b
32647
32818
  const session = sessions[graphId];
32648
32819
  const model = useMemo(() => ({
32649
32820
  ...baseline,
32821
+ direction: session?.direction ?? baseline.direction,
32650
32822
  viewport: session?.viewport ?? baseline.viewport,
32651
32823
  nodes: baseline.nodes.map((node) => ({
32652
32824
  ...node,
32653
32825
  position: session?.positions?.[node.id] ?? node.position
32654
32826
  }))
32655
32827
  }), [baseline, session]);
32656
- const moved = baseline.nodes.some((node) => {
32828
+ const layoutChanged = model.direction !== baseline.direction || baseline.nodes.some((node) => {
32657
32829
  const position = session?.positions?.[node.id];
32658
32830
  return position != null && (position.x !== node.position.x || position.y !== node.position.y);
32659
32831
  });
32660
32832
  const selection = selected.length === 1 ? revision.node(selected[0]) : void 0;
32661
32833
  const comment = selected.length === 1 ? model.nodes.find((node) => node.id === selected[0] && node.kind === "comment") : void 0;
32662
- const navigation = presentation == null || moved ? /* @__PURE__ */ jsxs(Fragment$1, { children: [presentation == null && /* @__PURE__ */ jsx("span", {
32663
- className: "revision-viewer-hint",
32664
- children: t("snapshot.missingLayout")
32665
- }), moved && /* @__PURE__ */ jsxs(Button$1, {
32834
+ const restoreLayout = layoutChanged ? /* @__PURE__ */ jsxs(Button$1, {
32666
32835
  className: "ml-auto",
32667
32836
  size: "xs",
32668
32837
  variant: "ghost",
@@ -32671,7 +32840,7 @@ function RevisionCanvas({ draft, presentation, theme, interactiveMode$, label, b
32671
32840
  "aria-hidden": "true",
32672
32841
  className: "i-lucide-light:rotate-ccw"
32673
32842
  }), t("snapshot.restore")]
32674
- })] }) : void 0;
32843
+ }) : void 0;
32675
32844
  return /* @__PURE__ */ jsxs("section", {
32676
32845
  ref: root,
32677
32846
  className: `revision-viewer${inspectorOpen ? " inspector-open" : ""}`,
@@ -32684,7 +32853,7 @@ function RevisionCanvas({ draft, presentation, theme, interactiveMode$, label, b
32684
32853
  "aria-hidden": "true",
32685
32854
  className: "i-lucide-light:lock-keyhole"
32686
32855
  }), label ?? t("snapshot.title")]
32687
- }), navigation]
32856
+ }), restoreLayout]
32688
32857
  }), /* @__PURE__ */ jsxs("div", {
32689
32858
  className: "revision-viewer-body",
32690
32859
  children: [/* @__PURE__ */ jsxs("div", {
@@ -32707,6 +32876,14 @@ function RevisionCanvas({ draft, presentation, theme, interactiveMode$, label, b
32707
32876
  kind: "move",
32708
32877
  positions
32709
32878
  })),
32879
+ onChangeDirection: (direction, positions) => {
32880
+ setSessions((current) => changeRevisionCanvasSession(current, graphId, {
32881
+ kind: "direction",
32882
+ direction,
32883
+ positions
32884
+ }));
32885
+ return true;
32886
+ },
32710
32887
  onMoveViewport: (viewport) => setSessions((current) => changeRevisionCanvasSession(current, graphId, {
32711
32888
  kind: "viewport",
32712
32889
  viewport
@@ -32714,7 +32891,7 @@ function RevisionCanvas({ draft, presentation, theme, interactiveMode$, label, b
32714
32891
  onSelectionChange: setSelected,
32715
32892
  onInspectSelection: () => setInspectorOpen((open) => !open),
32716
32893
  inspectorOpen,
32717
- toolbar: showHeader ? void 0 : navigation,
32894
+ toolbar: showHeader ? void 0 : restoreLayout,
32718
32895
  cornerTools: /* @__PURE__ */ jsx(WorkbenchInspectorToggle, {
32719
32896
  label: t("designer.toggleInspector"),
32720
32897
  open: inspectorOpen,
@@ -36125,17 +36302,33 @@ function WorkspaceNavigationIsland({ flowName, settings, flowsHref, ghost = fals
36125
36302
  });
36126
36303
  }
36127
36304
  //#endregion
36305
+ //#region src/workbench/browser/runtime/shell/pngExportPreference.ts
36306
+ var pngExportFramePreferenceKey = "open-flow:png-export-frame";
36307
+ function readPngExportFrame(preferences) {
36308
+ try {
36309
+ return preferences?.getItem(pngExportFramePreferenceKey) !== "false";
36310
+ } catch {
36311
+ return true;
36312
+ }
36313
+ }
36314
+ function writePngExportFrame(preferences, enabled) {
36315
+ try {
36316
+ preferences?.setItem(pngExportFramePreferenceKey, String(enabled));
36317
+ } catch {}
36318
+ }
36319
+ //#endregion
36128
36320
  //#region src/workbench/browser/runtime/shell/workspacePublishIsland.tsx
36129
36321
  var blockedReasonKey = {
36130
36322
  issues: "workspace.publishIssuesReason",
36131
36323
  busy: "workspace.publishActionInProgress",
36132
36324
  publishing: "workspace.publishInProgress"
36133
36325
  };
36134
- function WorkspacePublishIsland({ hasPublications, onOpenPublications, onExport, onExportSvg, onOpenRuns, onOpenState, onPublish, state }) {
36326
+ function WorkspacePublishIsland({ hasPublications, onOpenPublications, onExport, onExportPng, onOpenRuns, onOpenState, onPublish, preferences, state }) {
36135
36327
  const t = useTranslate();
36136
36328
  const [popupContainer, setPopupContainer] = useState(null);
36137
36329
  const [confirming, setConfirming] = useState(false);
36138
36330
  const [exporting, setExporting] = useState(false);
36331
+ const [showFrame, setShowFrame] = useState(() => readPngExportFrame(preferences));
36139
36332
  const cancelButton = useRef(null);
36140
36333
  const publishLabel = t(state == "publishing" ? "workspace.publishing" : "publication.publishDraft");
36141
36334
  const historyLabel = t(hasPublications === true ? "workspace.publications" : hasPublications === false ? "publication.historyEmpty" : "publication.loading");
@@ -36239,22 +36432,45 @@ function WorkspacePublishIsland({ hasPublications, onOpenPublications, onExport,
36239
36432
  disabled: state == "busy" || state == "publishing",
36240
36433
  onClick: onExport
36241
36434
  }),
36242
- onExportSvg != null && /* @__PURE__ */ jsxs(DropdownMenuItem, {
36435
+ onExportPng != null && /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [/* @__PURE__ */ jsxs(DropdownMenuSubTrigger, {
36243
36436
  className: "gap-2 px-2 py-[5px]",
36244
36437
  disabled: exporting,
36245
- onClick: async () => {
36246
- setExporting(true);
36247
- try {
36248
- await onExportSvg();
36249
- } finally {
36250
- setExporting(false);
36251
- }
36252
- },
36253
36438
  children: [/* @__PURE__ */ jsx("i", {
36254
36439
  "aria-hidden": "true",
36255
36440
  className: "i-lucide-light:download size-4 shrink-0"
36256
- }), t("resource.exportSvg")]
36257
- }),
36441
+ }), t("resource.exportPng")]
36442
+ }), /* @__PURE__ */ jsxs(DropdownMenuSubContent, {
36443
+ className: "min-w-32",
36444
+ container: popupContainer,
36445
+ children: [
36446
+ /* @__PURE__ */ jsx(DropdownMenuCheckboxItem, {
36447
+ checked: showFrame,
36448
+ className: "py-[5px] pl-2",
36449
+ closeOnClick: false,
36450
+ onCheckedChange: (checked) => {
36451
+ setShowFrame(checked);
36452
+ writePngExportFrame(preferences, checked);
36453
+ },
36454
+ children: t("resource.exportPngFrame")
36455
+ }),
36456
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, { className: "mx-2 bg-border/50" }),
36457
+ [false, true].map((dark) => /* @__PURE__ */ jsx(DropdownMenuItem, {
36458
+ className: "px-2 py-[5px]",
36459
+ onClick: async () => {
36460
+ setExporting(true);
36461
+ try {
36462
+ await onExportPng({
36463
+ dark,
36464
+ frame: showFrame
36465
+ });
36466
+ } finally {
36467
+ setExporting(false);
36468
+ }
36469
+ },
36470
+ children: t(dark ? "resource.exportPngDark" : "resource.exportPngLight")
36471
+ }, String(dark)))
36472
+ ]
36473
+ })] }),
36258
36474
  /* @__PURE__ */ jsxs(DropdownMenuItem, {
36259
36475
  className: "gap-2 px-2 py-[5px]",
36260
36476
  disabled: hasPublications !== true,
@@ -36297,8 +36513,8 @@ function WorkspacePublishIsland({ hasPublications, onOpenPublications, onExport,
36297
36513
  children: [/* @__PURE__ */ jsx(Spinner, {
36298
36514
  "aria-hidden": "true",
36299
36515
  className: "shrink-0"
36300
- }), /* @__PURE__ */ jsx(DialogTitle, { children: t("resource.exportSvgPending") })]
36301
- }), /* @__PURE__ */ jsx(DialogDescription, { children: t("resource.exportSvgPendingDescription") })]
36516
+ }), /* @__PURE__ */ jsx(DialogTitle, { children: t("resource.exportPngPending") })]
36517
+ }), /* @__PURE__ */ jsx(DialogDescription, { children: t("resource.exportPngPendingDescription") })]
36302
36518
  })
36303
36519
  })
36304
36520
  ]
@@ -36678,7 +36894,8 @@ function FlowEditor({ navigationIsland, onRun, onRunStarted, onCloseRuns, onConf
36678
36894
  flowId != null && stateTarget != null && /* @__PURE__ */ jsx(FlowStateButton, {
36679
36895
  client: store.state,
36680
36896
  flowId,
36681
- nodeIds: designer.nodes.map((node) => node.id),
36897
+ nodes: designer.nodes,
36898
+ notify: store.workspace.notify,
36682
36899
  history: stateTarget.history,
36683
36900
  onClose: () => setStateTarget(void 0)
36684
36901
  }, `${flowId}:${stateTarget.history?.nodeId ?? "all"}`),
@@ -36719,10 +36936,11 @@ function FlowEditor({ navigationIsland, onRun, onRunStarted, onCloseRuns, onConf
36719
36936
  },
36720
36937
  open: diagnosticsOpen
36721
36938
  }), /* @__PURE__ */ jsx(WorkspacePublishIsland, {
36722
- onExportSvg: connectorAccess.access?.mode != "selectable" || designer.nodes.length == 0 ? void 0 : () => store.exportSvg(language, {
36723
- dark: theme == "dark",
36939
+ onExportPng: connectorAccess.access?.mode != "selectable" || designer.nodes.length == 0 ? void 0 : (options) => store.exportPng(language, {
36940
+ ...options,
36724
36941
  ignoredNodeIds
36725
36942
  }),
36943
+ preferences: store.preferences,
36726
36944
  onExport: () => {
36727
36945
  const id = store.workspace.$.flowId.value;
36728
36946
  if (id != null) store.workspace.exportFlow(id);