@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
@@ -3188,7 +3188,7 @@ var CanvasStore = class {
3188
3188
  if (this.$.editable.value) this.#callbacks.onIgnoreNodes?.(nodeIds, ignored);
3189
3189
  };
3190
3190
  this.changeDirection = (direction) => {
3191
- if (!this.$.editable.value || !this.canChangeDirection || this.$.changingDirection.value || this.$.direction.value === direction) return;
3191
+ if (!this.canChangeDirection || this.$.changingDirection.value || this.$.direction.value === direction) return;
3192
3192
  this.layoutComplete = false;
3193
3193
  this.layoutMeasurementAttempts = 0;
3194
3194
  this.$$.changingDirection.set(true);
@@ -18784,31 +18784,31 @@ function IconStack({ icons, count, limit = 3, size = "default", countClassName }
18784
18784
  });
18785
18785
  }
18786
18786
  var CanvasCard_module_default = {
18787
- card: "CanvasCard_card_5d8a7",
18788
- value: "CanvasCard_value_5d8a7",
18789
- preview: "CanvasCard_preview_5d8a7",
18790
- comment: "CanvasCard_comment_5d8a7",
18791
- selected: "CanvasCard_selected_5d8a7",
18792
- problem: "CanvasCard_problem_5d8a7",
18793
- runningBeam: "CanvasCard_runningBeam_5d8a7",
18794
- "running-beam": "CanvasCard_running-beam_5d8a7",
18795
- header: "CanvasCard_header_5d8a7",
18796
- icon: "CanvasCard_icon_5d8a7",
18797
- identity: "CanvasCard_identity_5d8a7",
18798
- indicator: "CanvasCard_indicator_5d8a7",
18799
- warning: "CanvasCard_warning_5d8a7",
18800
- danger: "CanvasCard_danger_5d8a7",
18801
- content: "CanvasCard_content_5d8a7",
18802
- branches: "CanvasCard_branches_5d8a7",
18803
- collapse: "CanvasCard_collapse_5d8a7",
18804
- collapseInner: "CanvasCard_collapseInner_5d8a7",
18805
- compactIdentity: "CanvasCard_compactIdentity_5d8a7",
18806
- compact: "CanvasCard_compact_5d8a7",
18807
- withBody: "CanvasCard_withBody_5d8a7",
18808
- compactContent: "CanvasCard_compactContent_5d8a7",
18809
- singleContent: "CanvasCard_singleContent_5d8a7",
18810
- subtitle: "CanvasCard_subtitle_5d8a7",
18811
- fill: "CanvasCard_fill_5d8a7"
18787
+ card: "CanvasCard_card_e92f9",
18788
+ value: "CanvasCard_value_e92f9",
18789
+ preview: "CanvasCard_preview_e92f9",
18790
+ comment: "CanvasCard_comment_e92f9",
18791
+ selected: "CanvasCard_selected_e92f9",
18792
+ problem: "CanvasCard_problem_e92f9",
18793
+ runningBeam: "CanvasCard_runningBeam_e92f9",
18794
+ "running-beam": "CanvasCard_running-beam_e92f9",
18795
+ header: "CanvasCard_header_e92f9",
18796
+ icon: "CanvasCard_icon_e92f9",
18797
+ identity: "CanvasCard_identity_e92f9",
18798
+ indicator: "CanvasCard_indicator_e92f9",
18799
+ warning: "CanvasCard_warning_e92f9",
18800
+ danger: "CanvasCard_danger_e92f9",
18801
+ content: "CanvasCard_content_e92f9",
18802
+ branches: "CanvasCard_branches_e92f9",
18803
+ collapse: "CanvasCard_collapse_e92f9",
18804
+ collapseInner: "CanvasCard_collapseInner_e92f9",
18805
+ compactIdentity: "CanvasCard_compactIdentity_e92f9",
18806
+ compact: "CanvasCard_compact_e92f9",
18807
+ withBody: "CanvasCard_withBody_e92f9",
18808
+ compactContent: "CanvasCard_compactContent_e92f9",
18809
+ singleContent: "CanvasCard_singleContent_e92f9",
18810
+ subtitle: "CanvasCard_subtitle_e92f9",
18811
+ fill: "CanvasCard_fill_e92f9"
18812
18812
  };
18813
18813
  //#endregion
18814
18814
  //#region src/ui/browser/hooks.ts
@@ -21375,6 +21375,7 @@ var NodeLayout = /* @__PURE__ */ memo(({ canvasStore, nodeStore, visible }) => {
21375
21375
  modelNode?.kind === "trigger" && /* @__PURE__ */ jsx(Zap, {
21376
21376
  "aria-hidden": "true",
21377
21377
  className: NodeLayout_module_default.triggerIcon,
21378
+ "data-canvas-bounds": true,
21378
21379
  strokeWidth: 1.5
21379
21380
  }),
21380
21381
  /* @__PURE__ */ jsxs("div", {
@@ -21594,27 +21595,27 @@ var fitViewOptions = {
21594
21595
  maxZoom: 1
21595
21596
  };
21596
21597
  var ReactFlowContainer_module_default = {
21597
- container: "ReactFlowContainer_container_40f4d",
21598
- flow: "ReactFlowContainer_flow_40f4d",
21599
- contextMenu: "ReactFlowContainer_contextMenu_40f4d",
21600
- movingLayout: "ReactFlowContainer_movingLayout_40f4d",
21601
- layoutPending: "ReactFlowContainer_layoutPending_40f4d",
21602
- dock: "ReactFlowContainer_dock_40f4d",
21603
- viewDock: "ReactFlowContainer_viewDock_40f4d",
21604
- expandedViewControls: "ReactFlowContainer_expandedViewControls_40f4d",
21605
- viewOptionButtons: "ReactFlowContainer_viewOptionButtons_40f4d",
21606
- compactViewControls: "ReactFlowContainer_compactViewControls_40f4d",
21607
- createDock: "ReactFlowContainer_createDock_40f4d",
21608
- zoomValue: "ReactFlowContainer_zoomValue_40f4d",
21609
- viewChoiceTrigger: "ReactFlowContainer_viewChoiceTrigger_40f4d",
21610
- viewChoicePanel: "ReactFlowContainer_viewChoicePanel_40f4d",
21611
- viewChoices: "ReactFlowContainer_viewChoices_40f4d",
21612
- viewChoice: "ReactFlowContainer_viewChoice_40f4d",
21613
- dockActions: "ReactFlowContainer_dockActions_40f4d",
21614
- miniMap: "ReactFlowContainer_miniMap_40f4d",
21615
- viewOptions: "ReactFlowContainer_viewOptions_40f4d",
21616
- viewOptionsClip: "ReactFlowContainer_viewOptionsClip_40f4d",
21617
- viewSeparator: "ReactFlowContainer_viewSeparator_40f4d"
21598
+ container: "ReactFlowContainer_container_18f86",
21599
+ flow: "ReactFlowContainer_flow_18f86",
21600
+ contextMenu: "ReactFlowContainer_contextMenu_18f86",
21601
+ movingLayout: "ReactFlowContainer_movingLayout_18f86",
21602
+ layoutPending: "ReactFlowContainer_layoutPending_18f86",
21603
+ dock: "ReactFlowContainer_dock_18f86",
21604
+ viewDock: "ReactFlowContainer_viewDock_18f86",
21605
+ expandedViewControls: "ReactFlowContainer_expandedViewControls_18f86",
21606
+ viewOptionButtons: "ReactFlowContainer_viewOptionButtons_18f86",
21607
+ compactViewControls: "ReactFlowContainer_compactViewControls_18f86",
21608
+ createDock: "ReactFlowContainer_createDock_18f86",
21609
+ zoomValue: "ReactFlowContainer_zoomValue_18f86",
21610
+ viewChoiceTrigger: "ReactFlowContainer_viewChoiceTrigger_18f86",
21611
+ viewChoicePanel: "ReactFlowContainer_viewChoicePanel_18f86",
21612
+ viewChoices: "ReactFlowContainer_viewChoices_18f86",
21613
+ viewChoice: "ReactFlowContainer_viewChoice_18f86",
21614
+ dockActions: "ReactFlowContainer_dockActions_18f86",
21615
+ miniMap: "ReactFlowContainer_miniMap_18f86",
21616
+ viewOptions: "ReactFlowContainer_viewOptions_18f86",
21617
+ viewOptionsClip: "ReactFlowContainer_viewOptionsClip_18f86",
21618
+ viewSeparator: "ReactFlowContainer_viewSeparator_18f86"
21618
21619
  };
21619
21620
  //#endregion
21620
21621
  //#region src/canvas/browser/addItemDrag.ts
@@ -22491,14 +22492,13 @@ function CanvasOptionsMenu({ onRelayout, miniMapExpanded$, interactiveMode$, dir
22491
22492
  interactiveMode$ != null && /* @__PURE__ */ jsx(CanvasInteractiveModeMenu, { interactiveMode$ })
22492
22493
  ] });
22493
22494
  }
22494
- function CanvasViewControls({ readOnly = false, direction, changingDirection, onChangeDirection, interactiveMode$, miniMapExpanded$, maxZoomReached, minZoomReached, onFitView, onRelayout, onZoomIn, onZoomOut, onZoomReset, zoom }) {
22495
+ function CanvasViewControls({ direction, changingDirection, onChangeDirection, interactiveMode$, miniMapExpanded$, maxZoomReached, minZoomReached, onFitView, onRelayout, onZoomIn, onZoomOut, onZoomReset, zoom }) {
22495
22496
  const t = useTranslate();
22496
22497
  const [optionsExpanded, setOptionsExpanded] = useState(false);
22497
22498
  const [popupContainer, setPopupContainer] = useState(null);
22498
22499
  return /* @__PURE__ */ jsxs(Panel, {
22499
22500
  position: "bottom-left",
22500
22501
  className: cn$1("open-flow-control-island open-flow-control-island-compact", ReactFlowContainer_module_default.dock, ReactFlowContainer_module_default.viewDock),
22501
- "data-readonly": readOnly,
22502
22502
  "data-canvas-control-scope": true,
22503
22503
  "data-tooltip-toolbar": true,
22504
22504
  ref: setPopupContainer,
@@ -23034,7 +23034,6 @@ var FlowControls = /*#__PURE__*/ memo((props) => {
23034
23034
  variant: BackgroundVariant.Dots
23035
23035
  }),
23036
23036
  /* @__PURE__ */ jsx(CanvasViewControls, {
23037
- readOnly: !props.editable,
23038
23037
  interactiveMode$: props.interactiveMode$,
23039
23038
  direction: props.direction,
23040
23039
  changingDirection: props.changingDirection,
@@ -23750,7 +23749,7 @@ var FlowCanvas = ({ flowCanvasStore, annotationContext, onActivateSelection, onS
23750
23749
  editable,
23751
23750
  direction,
23752
23751
  changingDirection,
23753
- onChangeDirection: editable && flowCanvasStore.canChangeDirection ? flowCanvasStore.changeDirection : void 0,
23752
+ onChangeDirection: flowCanvasStore.canChangeDirection ? flowCanvasStore.changeDirection : void 0,
23754
23753
  nodesDraggable: nodesDraggable ?? editable,
23755
23754
  className,
23756
23755
  i18n: flowCanvasStore.i18n,
@@ -1,7 +1,7 @@
1
1
  import { l as useTranslate } from "./dist-BFmpkX4P.js";
2
2
  import { xr as Button } from "./tooltip-BDcVTNpu.js";
3
- import { a as DialogFooter, d as Field, f as FieldDescription, h as FieldLabel, i as DialogDescription, l as Icon, m as FieldGroup, n as DialogClose, o as DialogHeader, p as FieldError, r as DialogContent, rn as Input, s as DialogTitle, t as Dialog, u as Spinner } from "./dialog-CFFuRM8s.js";
4
- import { t as WorkbenchSelect } from "./workbenchSelect-CkOS95gX.js";
3
+ import { a as DialogFooter, d as Field, f as FieldDescription, h as FieldLabel, i as DialogDescription, in as Input, l as Icon, m as FieldGroup, n as DialogClose, o as DialogHeader, p as FieldError, r as DialogContent, s as DialogTitle, t as Dialog, u as Spinner } from "./dialog-CFlvkliO.js";
4
+ import { t as WorkbenchSelect } from "./workbenchSelect-Ci0KCYv9.js";
5
5
  import { useEffect, useRef, useState } from "react";
6
6
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
7
  //#region src/workbench/browser/runtime/shell/createResourceDialog.tsx
@@ -2286,7 +2286,7 @@ var keySchema = string().min(1).refine((value) => new TextEncoder().encode(value
2286
2286
  strictObject({
2287
2287
  scope: _enum(["draft", "live"]),
2288
2288
  values: literal(false).optional()
2289
- });
2289
+ }).omit({ values: true });
2290
2290
  strictObject({
2291
2291
  scope: _enum(["draft", "live"]),
2292
2292
  keys: array(keySchema).min(1).max(stateReadLimits.maxKeys).refine((keys) => new Set(keys).size === keys.length, "State keys must be distinct.")
@@ -2299,6 +2299,7 @@ strictObject({
2299
2299
  scope: _enum(["draft", "live"]),
2300
2300
  space: string().regex(/^(flow|node:.+)$/)
2301
2301
  });
2302
+ strictObject({ scope: _enum(["draft", "live"]) });
2302
2303
  var skipDuplicatesConfigSchema = strictObject({ fields: array(string().refine((field) => field.length > 0 && field.split(".").every((part) => part.length > 0), "Use a nonempty dot-separated field path.")).refine((fields) => new Set(fields).size === fields.length, "Identity fields must be distinct.").default([]) });
2303
2304
  var skipDuplicatesExecutorSchema = skipDuplicatesConfigSchema.extend({ kind: literal("skip-duplicates") });
2304
2305
  function skipDuplicatesTask(config = skipDuplicatesConfigSchema.parse({}), name = "Skip Duplicates") {
@@ -2343,6 +2344,10 @@ var flowStateViewSchema = object({
2343
2344
  updatedAt: number()
2344
2345
  }))
2345
2346
  });
2347
+ var flowStateSummarySchema = flowStateViewSchema.extend({ values: strictObject({
2348
+ count: number().int().nonnegative(),
2349
+ updatedAt: number().nullable()
2350
+ }) });
2346
2351
  var flowStateValuesSchema = object({
2347
2352
  version: literal(1),
2348
2353
  scope: _enum(["draft", "live"]),
@@ -9360,4 +9365,4 @@ function DialogDescription({ className, ...props }) {
9360
9365
  });
9361
9366
  }
9362
9367
  //#endregion
9363
- export { waitAutoContinueSchema as $, findItemIndex as $t, comparisonIssue as A, variableInputCompatible as At, authoringDefinitionSchemas as B, SelectContent as Bt, upgradeManualTrigger as C, limitDecisionQuestions as Ct, nodeSystemOutputPorts as D, matchesSchema as Dt, nodeErrorSchema as E, comparePorts as Et, operatorsForType as F, skipDuplicatesConfigSchema as Ft, workflowAgentToolSchema as G, SelectValue as Gt, connectorCapabilitySchema as H, SelectItem as Ht, otherwiseOutput as I, skipDuplicatesTask as It, webhookResponseAllowsBody as J, isGroupedItems as Jt, webhookHeaderValuePort as K, resolveAriaLabelledBy as Kt, unaryOperator as L, dequal as Lt, mapConditionSources as M, format as Mt, nodeInputMappings as N, flowStateValuesSchema as Nt, supportsNodeErrorHandling as O, portsAssignable as Ot, operandHandle as P, flowStateViewSchema as Pt, workflowOutputsPort as Q, defaultItemEquality as Qt, valueType as R, Select as Rt, resourceNameIssue as S, defaultDecisionQuestion as St, workflowToolNameIssue as T, webhookSupportsBody as Tt, decodeRevisionContent as U, SelectSeparator as Ut, connectorAgentToolSchema as V, SelectGroup as Vt, httpCapabilitySchema as W, SelectTrigger as Wt, webhookResponseTimeout as X, stringifyAsValue as Xt, webhookResponseInputs as Y, stringifyAsLabel as Yt, webhookStatusCodePort as Z, compareItemEquality as Zt, applyFlowChanges as _, isHttpHost as _t, DialogFooter as a, httpHeaderSchema as at, nodeNameIssue as b, decisionQuestionIssues as bt, DialogTrigger as c, httpRequestInputs as ct, Field as d, withHttpParameterFields as dt, removeItem as en, defaultNodeRetry as et, FieldDescription as f, withHttpParameterSource as ft, FlowChangeError as g, renamedPort as gt, FieldLabel as h, renameOutputMapping as ht, DialogDescription as i, ChevronDown as in, checkJsonDepth as it, conditionInputPorts as j, isSchemaKeyword as jt, clearConditionBindings as k, schemaObject as kt, Icon as l, httpRequestIssues as lt, FieldGroup as m, remapInputMappings as mt, DialogClose as n, Label as nn, retryConfigurationSchema as nt, DialogHeader as o, httpQuerySchema as ot, FieldError as p, httpHeaderNameIssue as pt, webhookHeadersPort as q, hasNullItemLabel as qt, DialogContent as r, Input as rn, supportsNodeRetry as rt, DialogTitle as s, httpRequestConfigSchema as st, Dialog as t, selectedValueIncludes as tn, nodeRetryLimits as tt, Spinner as u, httpRequestTask as ut, isTriggerNode as v, decisionLimits as vt, upgradeRevisionContent as w, webhookMethods as wt, normalizeNodeName as x, decisionTask as xt, nextNodeName as y, decisionModel as yt, agentToolFields as z, SelectChevron as zt };
9368
+ export { waitAutoContinueSchema as $, defaultItemEquality as $t, comparisonIssue as A, variableInputCompatible as At, authoringDefinitionSchemas as B, SelectChevron as Bt, upgradeManualTrigger as C, limitDecisionQuestions as Ct, nodeSystemOutputPorts as D, matchesSchema as Dt, nodeErrorSchema as E, comparePorts as Et, operatorsForType as F, flowStateViewSchema as Ft, workflowAgentToolSchema as G, SelectTrigger as Gt, connectorCapabilitySchema as H, SelectGroup as Ht, otherwiseOutput as I, skipDuplicatesConfigSchema as It, webhookResponseAllowsBody as J, hasNullItemLabel as Jt, webhookHeaderValuePort as K, SelectValue as Kt, unaryOperator as L, skipDuplicatesTask as Lt, mapConditionSources as M, format as Mt, nodeInputMappings as N, flowStateSummarySchema as Nt, supportsNodeErrorHandling as O, portsAssignable as Ot, operandHandle as P, flowStateValuesSchema as Pt, workflowOutputsPort as Q, compareItemEquality as Qt, valueType as R, dequal as Rt, resourceNameIssue as S, defaultDecisionQuestion as St, workflowToolNameIssue as T, webhookSupportsBody as Tt, decodeRevisionContent as U, SelectItem as Ut, connectorAgentToolSchema as V, SelectContent as Vt, httpCapabilitySchema as W, SelectSeparator as Wt, webhookResponseTimeout as X, stringifyAsLabel as Xt, webhookResponseInputs as Y, isGroupedItems as Yt, webhookStatusCodePort as Z, stringifyAsValue as Zt, applyFlowChanges as _, isHttpHost as _t, DialogFooter as a, ChevronDown as an, httpHeaderSchema as at, nodeNameIssue as b, decisionQuestionIssues as bt, DialogTrigger as c, httpRequestInputs as ct, Field as d, withHttpParameterFields as dt, findItemIndex as en, defaultNodeRetry as et, FieldDescription as f, withHttpParameterSource as ft, FlowChangeError as g, renamedPort as gt, FieldLabel as h, renameOutputMapping as ht, DialogDescription as i, Input as in, checkJsonDepth as it, conditionInputPorts as j, isSchemaKeyword as jt, clearConditionBindings as k, schemaObject as kt, Icon as l, httpRequestIssues as lt, FieldGroup as m, remapInputMappings as mt, DialogClose as n, selectedValueIncludes as nn, retryConfigurationSchema as nt, DialogHeader as o, httpQuerySchema as ot, FieldError as p, httpHeaderNameIssue as pt, webhookHeadersPort as q, resolveAriaLabelledBy as qt, DialogContent as r, Label as rn, supportsNodeRetry as rt, DialogTitle as s, httpRequestConfigSchema as st, Dialog as t, removeItem as tn, nodeRetryLimits as tt, Spinner as u, httpRequestTask as ut, isTriggerNode as v, decisionLimits as vt, upgradeRevisionContent as w, webhookMethods as wt, normalizeNodeName as x, decisionTask as xt, nextNodeName as y, decisionModel as yt, agentToolFields as z, Select as zt };
@@ -1,6 +1,7 @@
1
1
  import { n as IconImageCrossOriginContext, o as IconifyProvider } from "./ContentIcon-CMn0c-kl.js";
2
- import { t as FlowCanvas, x as CanvasStore } from "./FlowCanvas-BQGBcfoL.js";
2
+ import { t as FlowCanvas, x as CanvasStore } from "./FlowCanvas-CrsiDHK8.js";
3
3
  import { createElement } from "react";
4
+ import "react/jsx-runtime";
4
5
  import { createRoot } from "react-dom/client";
5
6
  //#region ../../node_modules/.bun/html-to-image@1.11.11/node_modules/html-to-image/es/util.js
6
7
  function resolveUrl(url, baseUrl) {
@@ -547,30 +548,188 @@ async function toSvg(node, options = {}) {
547
548
  return await nodeToDataURL(clonedNode, width, height);
548
549
  }
549
550
  //#endregion
550
- //#region src/canvas/browser/graph/FlowCanvas/exportSvg.ts
551
+ //#region ../../assets/logo/open-flow-dark.svg
552
+ var open_flow_dark_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='100'%20height='100'%20viewBox='0%200%20100%20100'%20role='img'%20aria-label='Open%20Flow'%3e%3cpath%20fill='%23e0e2e6'%20d='M32%2016H68Q84%2016%2084%2032V36H68C46%2036%2054%2056%2032%2056H16V32Q16%2016%2032%2016Z'/%3e%3cpath%20fill='%238c919a'%20d='M16%2068H32C54%2068%2046%2048%2068%2048H84V68Q84%2084%2068%2084H32Q16%2084%2016%2068Z'/%3e%3c/svg%3e";
553
+ //#endregion
554
+ //#region ../../assets/logo/open-flow-light.svg
555
+ var open_flow_light_default = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='100'%20height='100'%20viewBox='0%200%20100%20100'%20role='img'%20aria-label='Open%20Flow'%3e%3cpath%20fill='%2344474c'%20d='M32%2016H68Q84%2016%2084%2032V36H68C46%2036%2054%2056%2032%2056H16V32Q16%2016%2032%2016Z'/%3e%3cpath%20fill='%2393979f'%20d='M16%2068H32C54%2068%2046%2048%2068%2048H84V68Q84%2084%2068%2084H32Q16%2084%2016%2068Z'/%3e%3c/svg%3e";
556
+ //#endregion
557
+ //#region src/ui/browser/logo.tsx
558
+ function openFlowLogoSource(theme) {
559
+ return theme === "dark" ? open_flow_dark_default : open_flow_light_default;
560
+ }
561
+ //#endregion
562
+ //#region src/canvas/browser/graph/FlowCanvas/exportPng.ts
551
563
  var padding = 64;
564
+ var maximumDimension = 16384;
565
+ var maximumPixels = 32e6;
566
+ var framePadding = 12;
567
+ var frameHeaderHeight = 40;
568
+ var frameTitleInset = 20;
569
+ var frameInset = 8;
570
+ var frameRadius = 16;
571
+ function drawFrame(context, image, logo, width, height, background, frame) {
572
+ const x = framePadding;
573
+ const y = framePadding;
574
+ const frameWidth = width - 24;
575
+ const frameHeight = height - 24;
576
+ const contentX = 20;
577
+ const contentY = 52;
578
+ const contentWidth = frameWidth - 16;
579
+ const contentHeight = frameHeight - frameHeaderHeight - frameInset;
580
+ context.save();
581
+ context.fillStyle = frame.background;
582
+ context.beginPath();
583
+ context.roundRect(x, y, frameWidth, frameHeight, frameRadius);
584
+ context.fill();
585
+ context.save();
586
+ context.beginPath();
587
+ context.roundRect(contentX, contentY, contentWidth, contentHeight, 8);
588
+ context.clip();
589
+ context.fillStyle = background;
590
+ context.fillRect(contentX, contentY, contentWidth, contentHeight);
591
+ context.drawImage(image, contentX, contentY, contentWidth, contentHeight);
592
+ context.restore();
593
+ context.strokeStyle = frame.border;
594
+ context.beginPath();
595
+ context.roundRect(20.5, 52.5, contentWidth - 1, contentHeight - 1, 7.5);
596
+ context.stroke();
597
+ context.drawImage(logo, 24, 22, 20, 20);
598
+ context.fillStyle = frame.foreground;
599
+ context.textBaseline = "middle";
600
+ context.font = `600 14px ${frame.fontFamily}`;
601
+ const wordmarkX = 52;
602
+ context.fillText("Open Flow", wordmarkX, 32);
603
+ const availableWidth = x + frameWidth - frameTitleInset - (wordmarkX + context.measureText("Open Flow").width + 24);
604
+ context.font = `400 12px ${frame.fontFamily}`;
605
+ if (availableWidth > 0) {
606
+ let title = frame.title.replace(/\s+/g, " ");
607
+ if (context.measureText(title).width > availableWidth) {
608
+ const characters = Array.from(title);
609
+ while (characters.length && context.measureText(characters.join("") + "…").width > availableWidth) characters.pop();
610
+ title = characters.join("") + "…";
611
+ }
612
+ context.textAlign = "right";
613
+ context.fillText(title, x + frameWidth - frameTitleInset, 32);
614
+ }
615
+ context.strokeStyle = frame.border;
616
+ context.beginPath();
617
+ context.roundRect(12.5, 12.5, frameWidth - 1, frameHeight - 1, 15.5);
618
+ context.stroke();
619
+ context.restore();
620
+ }
621
+ var imagePlaceholder = "data:image/svg+xml;charset=utf-8," + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"160\" height=\"100\" viewBox=\"0 0 160 100\"><rect width=\"160\" height=\"100\" rx=\"6\" fill=\"#eeeeee\"/><text x=\"80\" y=\"50\" text-anchor=\"middle\" dominant-baseline=\"central\" font-family=\"sans-serif\" font-size=\"24\" fill=\"#777777\">?</text></svg>");
552
622
  var nextFrame = () => new Promise((resolve) => requestAnimationFrame(() => resolve()));
553
- async function waitFor(ready) {
554
- const started = performance.now();
623
+ async function waitFor(ready, deadline) {
555
624
  while (!ready()) {
556
- if (performance.now() - started > 15e3) throw new Error("Timed out rendering the workflow image.");
625
+ if (performance.now() > deadline) throw new Error("Timed out rendering the workflow image.");
557
626
  await nextFrame();
558
627
  }
559
628
  }
629
+ async function decodeImage(image, timeout) {
630
+ let timer;
631
+ try {
632
+ return await Promise.race([image.decode().then(() => true, () => false), new Promise((resolve) => {
633
+ timer = setTimeout(() => resolve(false), Math.max(0, timeout));
634
+ })]);
635
+ } finally {
636
+ clearTimeout(timer);
637
+ }
638
+ }
639
+ /** Image failures can mount another image or start loading a bundled fallback icon. */
640
+ async function prepareImages(host, deadline) {
641
+ let hasImagePlaceholders = false;
642
+ while (true) {
643
+ for (const image of host.querySelectorAll("img")) image.loading = "eager";
644
+ await waitFor(() => host.querySelector("[data-render-pending]") == null, deadline);
645
+ const images = [...host.querySelectorAll("img")].map((image) => ({
646
+ image,
647
+ source: image.src
648
+ }));
649
+ const decoded = await Promise.all(images.map(({ image }) => {
650
+ image.loading = "eager";
651
+ return decodeImage(image, Math.min(5e3, deadline - performance.now()));
652
+ }));
653
+ await nextFrame();
654
+ await nextFrame();
655
+ let replaced = false;
656
+ images.forEach(({ image, source }, index) => {
657
+ if (decoded[index] || !host.contains(image) || image.src !== source) return;
658
+ if (source === imagePlaceholder) throw new Error("Unable to load the image placeholder.");
659
+ image.removeAttribute("srcset");
660
+ image.src = imagePlaceholder;
661
+ hasImagePlaceholders = replaced = true;
662
+ });
663
+ const current = [...host.querySelectorAll("img")];
664
+ if (!replaced && host.querySelector("[data-render-pending]") == null && current.length === images.length && current.every((image, index) => image === images[index].image && image.src === images[index].source)) return hasImagePlaceholders;
665
+ if (performance.now() > deadline) throw new Error("Timed out preparing workflow images.");
666
+ }
667
+ }
668
+ async function renderPng(dataUrl, width, height, background, frame) {
669
+ const ratio = Math.min(2, maximumDimension / width, maximumDimension / height, Math.sqrt(maximumPixels / (width * height)));
670
+ let outputWidth = Math.max(1, Math.floor(width * ratio));
671
+ let outputHeight = Math.max(1, Math.floor(height * ratio));
672
+ const image = new Image();
673
+ const logo = frame ? new Image() : void 0;
674
+ image.decoding = "async";
675
+ image.src = dataUrl;
676
+ try {
677
+ await image.decode();
678
+ if (logo && frame) {
679
+ logo.src = frame.logo;
680
+ await logo.decode();
681
+ }
682
+ for (let attempt = 0; attempt < 3; attempt++) {
683
+ const canvas = document.createElement("canvas");
684
+ try {
685
+ canvas.width = outputWidth;
686
+ canvas.height = outputHeight;
687
+ const context = canvas.getContext("2d", { alpha: false });
688
+ if (!context) throw new Error("Unable to allocate the workflow image.");
689
+ context.fillStyle = frame?.outerBackground ?? background;
690
+ context.fillRect(0, 0, outputWidth, outputHeight);
691
+ if (frame && logo) {
692
+ context.scale(outputWidth / width, outputHeight / height);
693
+ drawFrame(context, image, logo, width, height, background, frame);
694
+ } else context.drawImage(image, 0, 0, outputWidth, outputHeight);
695
+ if (context.isContextLost?.()) throw new Error("The workflow image context was lost.");
696
+ const blob = await new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
697
+ if (!blob || context.isContextLost?.()) throw new Error("Unable to encode the workflow image.");
698
+ return {
699
+ blob,
700
+ width: outputWidth,
701
+ height: outputHeight
702
+ };
703
+ } catch (error) {
704
+ if (attempt === 2 || outputWidth === 1 && outputHeight === 1) throw error;
705
+ outputWidth = Math.max(1, Math.floor(outputWidth / 2));
706
+ outputHeight = Math.max(1, Math.floor(outputHeight / 2));
707
+ } finally {
708
+ canvas.width = canvas.height = 0;
709
+ }
710
+ }
711
+ throw new Error("Unable to render the workflow image.");
712
+ } finally {
713
+ image.src = "";
714
+ if (logo) logo.src = "";
715
+ }
716
+ }
560
717
  /** Render the real canvas at full detail without moving or changing the user's canvas. */
561
- async function flowToSvg(model, title, language = "en", options = {}) {
718
+ async function captureFlowImage(model, language, options) {
719
+ const contentPadding = options.frame ? padding / 2 : padding;
562
720
  const positions = model.nodes.map((node) => node.position);
563
721
  const minX = Math.min(0, ...positions.map((position) => position.x));
564
722
  const minY = Math.min(0, ...positions.map((position) => position.y));
565
723
  const snapshot = {
566
724
  ...model,
567
725
  viewport: {
568
- x: padding - minX,
569
- y: padding - minY,
726
+ x: contentPadding - minX,
727
+ y: contentPadding - minY,
570
728
  zoom: 1
571
729
  }
572
730
  };
573
731
  const host = document.createElement("div");
732
+ host.lang = language;
574
733
  Object.assign(host.style, {
575
734
  position: "fixed",
576
735
  left: "-100000px",
@@ -596,16 +755,13 @@ async function flowToSvg(model, title, language = "en", options = {}) {
596
755
  layoutMotion: false,
597
756
  nodesDraggable: false
598
757
  }))));
599
- await waitFor(() => store.$.initialized.value && store.$.rfNodes.value.every((node) => !!node.measured?.width && !!node.measured.height));
600
- await waitFor(() => host.querySelector("[data-render-pending]") == null);
601
- for (const image of host.querySelectorAll("img")) image.loading = "eager";
602
- await Promise.all([...host.querySelectorAll("img")].map((image) => image.decode().catch(() => {})));
603
- await document.fonts.ready;
604
- await nextFrame();
605
- await nextFrame();
758
+ const deadline = performance.now() + 15e3;
759
+ await waitFor(() => store.$.initialized.value && store.$.rfNodes.value.every((node) => !!node.measured?.width && !!node.measured.height), deadline);
760
+ let hasImagePlaceholders = await prepareImages(host, deadline);
761
+ await waitFor(() => document.fonts.status === "loaded", deadline);
606
762
  const viewport = host.querySelector(".react-flow__viewport");
607
763
  const origin = viewport.getBoundingClientRect();
608
- const bounds = [...viewport.querySelectorAll(".react-flow__node")].map((node) => {
764
+ const bounds = [...viewport.querySelectorAll(".react-flow__node, [data-canvas-bounds]")].map((node) => {
609
765
  const rect = node.getBoundingClientRect();
610
766
  return {
611
767
  x: rect.left - origin.left,
@@ -619,41 +775,83 @@ async function flowToSvg(model, title, language = "en", options = {}) {
619
775
  const top = bounds.length ? Math.min(...bounds.map((rect) => rect.y)) : 0;
620
776
  const right = bounds.length ? Math.max(...bounds.map((rect) => rect.x + rect.width)) : 320;
621
777
  const bottom = bounds.length ? Math.max(...bounds.map((rect) => rect.y + rect.height)) : 120;
778
+ const width = Math.ceil(right - left + contentPadding * 2);
779
+ const height = Math.ceil(bottom - top + contentPadding * 2);
780
+ const background = getComputedStyle(host.querySelector(".react-flow")).backgroundColor;
622
781
  const dataUrl = await toSvg(viewport, {
623
- width: Math.ceil(right - left + 128),
624
- height: Math.ceil(bottom - top + 128),
782
+ width,
783
+ height,
784
+ imagePlaceholder,
625
785
  preferredFontFormat: "woff2",
626
786
  includeQueryParams: true,
627
787
  style: {
628
- transform: `translate(${padding - left}px, ${padding - top}px)`,
788
+ transform: `translate(${contentPadding - left}px, ${contentPadding - top}px)`,
629
789
  transformOrigin: "0 0"
630
790
  }
631
791
  });
632
- const svg = new DOMParser().parseFromString(decodeURIComponent(dataUrl.slice(dataUrl.indexOf(",") + 1)), "image/svg+xml");
633
- const background = svg.createElementNS("http://www.w3.org/2000/svg", "rect");
634
- background.setAttribute("width", "100%");
635
- background.setAttribute("height", "100%");
636
- background.setAttribute("fill", getComputedStyle(host.querySelector(".react-flow")).backgroundColor);
637
- const label = svg.createElementNS("http://www.w3.org/2000/svg", "title");
638
- label.textContent = title;
639
- svg.documentElement.prepend(label, background);
640
- return `<?xml version="1.0" encoding="UTF-8"?>\n${new XMLSerializer().serializeToString(svg)}`;
792
+ hasImagePlaceholders ||= dataUrl.includes(encodeURIComponent(imagePlaceholder));
793
+ let frame;
794
+ if (options.frame) {
795
+ const palette = document.createElement("div");
796
+ palette.className = "open-flow-theme";
797
+ palette.dataset.theme = options.dark ? "dark" : "light";
798
+ Object.assign(palette.style, {
799
+ backgroundColor: "var(--ui-background)",
800
+ color: "var(--ui-foreground)",
801
+ borderColor: "var(--ui-border)"
802
+ });
803
+ host.append(palette);
804
+ const style = getComputedStyle(palette);
805
+ const outerBackground = style.backgroundColor;
806
+ if (options.dark) palette.style.backgroundColor = "var(--ui-card)";
807
+ frame = {
808
+ title: options.title ?? "",
809
+ logo: openFlowLogoSource(options.dark ? "dark" : "light"),
810
+ background: getComputedStyle(palette).backgroundColor,
811
+ outerBackground,
812
+ foreground: style.color,
813
+ border: style.borderColor,
814
+ fontFamily: getComputedStyle(viewport).fontFamily || "sans-serif"
815
+ };
816
+ }
817
+ return {
818
+ dataUrl,
819
+ width: width + (frame ? 40 : 0),
820
+ height: height + (frame ? 72 : 0),
821
+ background,
822
+ frame,
823
+ hasImagePlaceholders
824
+ };
641
825
  } finally {
642
826
  root.unmount();
643
827
  store.dispose();
644
828
  host.remove();
645
829
  }
646
830
  }
647
- async function downloadFlowSvg(model, title, language = "en", options = {}) {
648
- const source = await flowToSvg(model, title, language, options);
649
- const url = URL.createObjectURL(new Blob([source], { type: "image/svg+xml;charset=utf-8" }));
831
+ async function flowToPng(model, language = "en", options = {}) {
832
+ const { dataUrl, width, height, background, frame, hasImagePlaceholders } = await captureFlowImage(model, language, options);
833
+ return {
834
+ ...await renderPng(dataUrl, width, height, background, frame),
835
+ hasImagePlaceholders
836
+ };
837
+ }
838
+ async function downloadFlowPng(model, title, language = "en", options = {}) {
839
+ const result = await flowToPng(model, language, {
840
+ ...options,
841
+ title
842
+ });
843
+ const url = URL.createObjectURL(result.blob);
650
844
  const link = document.createElement("a");
651
- link.download = `${title.replace(/[<>:"/\\|?*\u0000-\u001f]/g, "_").replace(/[. ]+$/g, "") || "workflow"}.svg`;
652
- link.href = url;
653
- document.body.append(link);
654
- link.click();
655
- link.remove();
656
- setTimeout(() => URL.revokeObjectURL(url), 0);
845
+ try {
846
+ link.download = `${title.replace(/[<>:"/\\|?*\u0000-\u001f]/g, "_").replace(/[. ]+$/g, "") || "workflow"}.png`;
847
+ link.href = url;
848
+ document.body.append(link);
849
+ link.click();
850
+ } finally {
851
+ link.remove();
852
+ setTimeout(() => URL.revokeObjectURL(url), 0);
853
+ }
854
+ return result;
657
855
  }
658
856
  //#endregion
659
- export { downloadFlowSvg };
857
+ export { downloadFlowPng };