@omega-flow/editor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,3133 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ActionNodeDetail: () => ActionNodeDetail,
34
+ ActionNodeView: () => ActionNodeView,
35
+ BaseNodeView: () => BaseNodeView,
36
+ CheckboxField: () => CheckboxField,
37
+ ConditionBuilder: () => ConditionBuilder,
38
+ ConditionBuilderDialog: () => ConditionBuilderDialog,
39
+ ConditionNodeDetail: () => ConditionNodeDetail,
40
+ ConditionNodeView: () => ConditionNodeView,
41
+ ControlPanel: () => ControlPanel,
42
+ DetailPanel: () => DetailPanel,
43
+ DurationField: () => DurationField,
44
+ ExitNodeDetail: () => ExitNodeDetail,
45
+ ExitNodeView: () => ExitNodeView,
46
+ Field: () => Field,
47
+ FieldGroup: () => FieldGroup,
48
+ JsonField: () => JsonField,
49
+ NodesPanel: () => NodesPanel,
50
+ NumberField: () => NumberField,
51
+ OptionsPanel: () => OptionsPanel,
52
+ SelectField: () => SelectField,
53
+ TextAreaField: () => TextAreaField,
54
+ TextField: () => TextField,
55
+ TranslationProvider: () => TranslationProvider,
56
+ TriggerNodeDetail: () => TriggerNodeDetail,
57
+ TriggerNodeView: () => TriggerNodeView,
58
+ TriggerOrTimeoutNodeDetail: () => TriggerOrTimeoutNodeDetail,
59
+ TriggerOrTimeoutNodeView: () => TriggerOrTimeoutNodeView,
60
+ WaitNodeDetail: () => WaitNodeDetail,
61
+ WaitNodeView: () => WaitNodeView,
62
+ WorkflowEditor: () => WorkflowEditor,
63
+ WorkflowEditorProvider: () => WorkflowEditorProvider,
64
+ createTranslationFunction: () => createTranslationFunction,
65
+ cssVar: () => cssVar,
66
+ defaultNodeTypes: () => defaultNodeTypes,
67
+ defaultOperators: () => defaultOperators,
68
+ defaultTranslations: () => defaultTranslations,
69
+ mergeNodeTypes: () => mergeNodeTypes,
70
+ themeVars: () => themeVars,
71
+ useDragAndDrop: () => useDragAndDrop,
72
+ useEdges: () => useEdges,
73
+ useNodeRegistry: () => useNodeRegistry,
74
+ useNodes: () => useNodes,
75
+ useSelectedNode: () => useSelectedNode,
76
+ useTranslation: () => useTranslation,
77
+ useWorkflowEditor: () => useWorkflowEditor,
78
+ useWorkflowEditorContext: () => useWorkflowEditorContext
79
+ });
80
+ module.exports = __toCommonJS(index_exports);
81
+
82
+ // src/components/WorkflowEditor.tsx
83
+ var import_react11 = require("@xyflow/react");
84
+
85
+ // src/context/WorkflowEditorContext.tsx
86
+ var import_react9 = require("react");
87
+ var import_react10 = require("@xyflow/react");
88
+
89
+ // src/nodes/views/BaseNodeView.tsx
90
+ var import_react = require("@xyflow/react");
91
+ var import_jsx_runtime = require("react/jsx-runtime");
92
+ var baseStyle = {
93
+ padding: "var(--of-spacing-4, 10px) 15px",
94
+ borderRadius: "var(--of-radius-lg, 8px)",
95
+ border: "2px solid",
96
+ backgroundColor: "var(--of-node-bg, #fff)",
97
+ minWidth: "150px",
98
+ fontSize: "var(--of-font-size-sm, 12px)",
99
+ fontFamily: "var(--of-font-family-base, system-ui, sans-serif)"
100
+ };
101
+ var headerStyle = {
102
+ display: "flex",
103
+ alignItems: "center",
104
+ gap: "var(--of-spacing-2, 6px)",
105
+ fontWeight: "var(--of-font-weight-semibold, 600)",
106
+ marginBottom: "var(--of-spacing-1, 4px)",
107
+ color: "var(--of-color-text-primary, #111827)"
108
+ };
109
+ var contentStyle = {
110
+ color: "var(--of-node-content-color, #666)",
111
+ fontSize: "var(--of-font-size-xs, 11px)"
112
+ };
113
+ var handleStyle = {
114
+ width: "10px",
115
+ height: "10px",
116
+ borderRadius: "50%"
117
+ };
118
+ function BaseNodeView({
119
+ label,
120
+ color = "#666",
121
+ icon,
122
+ sourceHandles = [],
123
+ targetHandles = [],
124
+ selected,
125
+ children
126
+ }) {
127
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
128
+ "div",
129
+ {
130
+ style: {
131
+ ...baseStyle,
132
+ borderColor: color,
133
+ boxShadow: selected ? `0 0 0 2px ${color}40` : "var(--of-node-shadow, 0 2px 4px rgba(0,0,0,0.1))"
134
+ },
135
+ children: [
136
+ targetHandles.map((handle, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
137
+ import_react.Handle,
138
+ {
139
+ type: "target",
140
+ position: import_react.Position.Top,
141
+ id: handle.id,
142
+ style: {
143
+ ...handleStyle,
144
+ backgroundColor: color,
145
+ left: `${(index + 1) / (targetHandles.length + 1) * 100}%`
146
+ }
147
+ },
148
+ handle.id
149
+ )),
150
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: headerStyle, children: [
151
+ icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color }, children: icon }),
152
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label })
153
+ ] }),
154
+ children && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: contentStyle, children }),
155
+ sourceHandles.map((handle, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
156
+ import_react.Handle,
157
+ {
158
+ type: "source",
159
+ position: import_react.Position.Bottom,
160
+ id: handle.id,
161
+ style: {
162
+ ...handleStyle,
163
+ backgroundColor: color,
164
+ left: `${(index + 1) / (sourceHandles.length + 1) * 100}%`
165
+ }
166
+ },
167
+ handle.id
168
+ ))
169
+ ]
170
+ }
171
+ );
172
+ }
173
+
174
+ // src/i18n/TranslationContext.tsx
175
+ var import_react2 = require("react");
176
+
177
+ // src/i18n/defaults.ts
178
+ var defaultTranslations = {
179
+ // ── Control Panel ──────────────────────────────────────────
180
+ "panels.control.title": "Workflow",
181
+ "panels.control.nameLabel": "Name",
182
+ "panels.control.namePlaceholder": "Workflow name",
183
+ "panels.control.saving": "Saving...",
184
+ "panels.control.savedSuccessfully": "Saved successfully",
185
+ "panels.control.failedToSave": "Failed to save",
186
+ "panels.control.unsavedChanges": "Unsaved changes",
187
+ // ── Detail Panel ───────────────────────────────────────────
188
+ "panels.detail.title": "Properties",
189
+ "panels.detail.emptyMessage": "Select a node to edit its properties",
190
+ "panels.detail.delete": "Delete",
191
+ "panels.detail.deleteTitle": "Delete node",
192
+ // ── Nodes Panel ────────────────────────────────────────────
193
+ "panels.nodes.title": "Nodes",
194
+ // ── Options Panel ──────────────────────────────────────────
195
+ "panels.options.title": "Options",
196
+ "panels.options.frequencyGroup": "Frequency",
197
+ "panels.options.frequencyTypeLabel": "Type",
198
+ "panels.options.frequencyTypeHint": "How often this workflow can run for a subject",
199
+ "panels.options.frequencyOneTime": "One time",
200
+ "panels.options.frequencyEveryRematch": "Every rematch",
201
+ "panels.options.intervalLabel": "Interval",
202
+ "panels.options.intervalHint": "Minimum time between runs",
203
+ // ── Node Views (canvas) ────────────────────────────────────
204
+ "nodes.trigger.label": "Trigger",
205
+ "nodes.trigger.noEvent": "No event set",
206
+ "nodes.action.label": "Action",
207
+ "nodes.action.noAction": "No action set",
208
+ "nodes.condition.label": "Condition",
209
+ "nodes.condition.noRules": "No rules",
210
+ "nodes.condition.ruleCount": "{{count}} rules",
211
+ "nodes.condition.ruleCountSingular": "1 rule",
212
+ "nodes.condition.handleTrue": "True",
213
+ "nodes.condition.handleFalse": "False",
214
+ "nodes.wait.label": "Wait",
215
+ "nodes.wait.noDuration": "No duration set",
216
+ "nodes.triggerOrTimeout.label": "Trigger or Timeout",
217
+ "nodes.triggerOrTimeout.notConfigured": "Not configured",
218
+ "nodes.triggerOrTimeout.eventFallback": "event",
219
+ "nodes.triggerOrTimeout.orDuration": "or {{duration}}",
220
+ "nodes.triggerOrTimeout.handleTrigger": "Trigger",
221
+ "nodes.triggerOrTimeout.handleTimeout": "Timeout",
222
+ "nodes.exit.label": "Exit",
223
+ "nodes.exit.endWorkflow": "End workflow",
224
+ // ── Node Details (property editors) ────────────────────────
225
+ "nodeDetails.trigger.group": "Trigger Configuration",
226
+ "nodeDetails.trigger.eventLabel": "Event Type",
227
+ "nodeDetails.trigger.eventPlaceholder": "e.g., user.signup, order.placed",
228
+ "nodeDetails.trigger.eventHint": "The event type that will start this workflow",
229
+ "nodeDetails.action.group": "Action Configuration",
230
+ "nodeDetails.action.nameLabel": "Action Name",
231
+ "nodeDetails.action.namePlaceholder": "e.g., sendEmail, createTask",
232
+ "nodeDetails.action.nameHint": "The action to perform when this node is reached",
233
+ "nodeDetails.action.paramsLabel": "Parameters",
234
+ "nodeDetails.action.paramsHint": "JSON object with action parameters",
235
+ "nodeDetails.condition.group": "Condition Configuration",
236
+ "nodeDetails.condition.conditionsLabel": "Conditions",
237
+ "nodeDetails.condition.conditionsHint": "JSON rules engine format. Use 'all' or 'any' arrays with conditions like: { fact: 'amount', operator: 'greaterThan', value: 100 }",
238
+ "nodeDetails.wait.group": "Wait Configuration",
239
+ "nodeDetails.wait.durationLabel": "Duration",
240
+ "nodeDetails.wait.durationHint": "How long to pause before continuing to the next node",
241
+ "nodeDetails.triggerOrTimeout.group": "Trigger or Timeout Configuration",
242
+ "nodeDetails.triggerOrTimeout.eventLabel": "Event Type",
243
+ "nodeDetails.triggerOrTimeout.eventPlaceholder": "e.g., payment.received",
244
+ "nodeDetails.triggerOrTimeout.eventHint": "The event type to wait for",
245
+ "nodeDetails.triggerOrTimeout.durationLabel": "Timeout Duration",
246
+ "nodeDetails.triggerOrTimeout.durationHint": "Max time to wait before timing out",
247
+ "nodeDetails.exit.group": "Exit Node",
248
+ "nodeDetails.exit.message": "This node ends the workflow. No configuration needed.",
249
+ // ── Default node type definitions (labels & descriptions) ──
250
+ "nodeTypes.trigger.label": "Trigger",
251
+ "nodeTypes.trigger.description": "Starts the workflow when a specific event occurs",
252
+ "nodeTypes.action.label": "Action",
253
+ "nodeTypes.action.description": "Performs an action and continues to the next node",
254
+ "nodeTypes.condition.label": "Condition",
255
+ "nodeTypes.condition.description": "Branches the workflow based on conditions",
256
+ "nodeTypes.wait.label": "Wait",
257
+ "nodeTypes.wait.description": "Pauses the workflow for a specified duration",
258
+ "nodeTypes.triggerOrTimeout.label": "Trigger or Timeout",
259
+ "nodeTypes.triggerOrTimeout.description": "Waits for an event or times out after a duration",
260
+ "nodeTypes.exit.label": "Exit",
261
+ "nodeTypes.exit.description": "Ends the workflow",
262
+ // ── Primitives / fields ────────────────────────────────────
263
+ "fields.json.invalidJson": "Invalid JSON",
264
+ "fields.duration.days": "d",
265
+ "fields.duration.hours": "h",
266
+ "fields.duration.minutes": "m",
267
+ // ── Condition Builder ────────────────────────────────────
268
+ "conditionBuilder.dialogTitle": "Condition Builder",
269
+ "conditionBuilder.topLevelLabel": "Match ANY of the following groups:",
270
+ "conditionBuilder.matchLabel": "Match",
271
+ "conditionBuilder.addGroup": "Add group",
272
+ "conditionBuilder.addCondition": "Add condition",
273
+ "conditionBuilder.removeGroup": "Remove",
274
+ "conditionBuilder.removeCondition": "Remove condition",
275
+ "conditionBuilder.selectProperty": "Select property...",
276
+ "conditionBuilder.selectOperator": "Select operator...",
277
+ "conditionBuilder.factPlaceholder": "fact name",
278
+ "conditionBuilder.valuePlaceholder": "value",
279
+ "conditionBuilder.emptyGroup": "No conditions yet. Add one below.",
280
+ "conditionBuilder.noConditions": "No conditions configured",
281
+ "conditionBuilder.ruleCountSingular": "1 condition",
282
+ "conditionBuilder.ruleCount": "{{count}} conditions",
283
+ "conditionBuilder.editButton": "Edit",
284
+ "conditionBuilder.apply": "Apply",
285
+ "conditionBuilder.cancel": "Cancel"
286
+ };
287
+
288
+ // src/i18n/TranslationContext.tsx
289
+ var import_jsx_runtime2 = require("react/jsx-runtime");
290
+ function createTranslationFunction(dictionary) {
291
+ return (key, params) => {
292
+ const template = dictionary[key] ?? key;
293
+ if (!params) return template;
294
+ return template.replace(
295
+ /\{\{(\w+)\}\}/g,
296
+ (_, k) => params[k] ?? `{{${k}}}`
297
+ );
298
+ };
299
+ }
300
+ var defaultT = createTranslationFunction(defaultTranslations);
301
+ var TranslationContext = (0, import_react2.createContext)(defaultT);
302
+ function TranslationProvider({
303
+ children,
304
+ translationFn,
305
+ translations
306
+ }) {
307
+ const t = (0, import_react2.useMemo)(() => {
308
+ if (translationFn) return translationFn;
309
+ if (translations) {
310
+ return createTranslationFunction({
311
+ ...defaultTranslations,
312
+ ...translations
313
+ });
314
+ }
315
+ return defaultT;
316
+ }, [translationFn, translations]);
317
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TranslationContext.Provider, { value: t, children });
318
+ }
319
+ function useTranslation() {
320
+ return (0, import_react2.useContext)(TranslationContext);
321
+ }
322
+
323
+ // src/nodes/views/TriggerNodeView.tsx
324
+ var import_jsx_runtime3 = require("react/jsx-runtime");
325
+ var TRIGGER_COLOR = "var(--of-node-trigger-color, #4CAF50)";
326
+ function TriggerNodeView({ id, data, selected }) {
327
+ const t = useTranslation();
328
+ const nodeData = data;
329
+ const params = nodeData.params;
330
+ const eventName = params?.event;
331
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
332
+ BaseNodeView,
333
+ {
334
+ id,
335
+ data: nodeData,
336
+ selected,
337
+ label: t("nodes.trigger.label"),
338
+ color: TRIGGER_COLOR,
339
+ icon: "\u25B6",
340
+ sourceHandles: [{ id: "output" }],
341
+ targetHandles: [],
342
+ children: eventName ? eventName : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("em", { children: t("nodes.trigger.noEvent") })
343
+ }
344
+ );
345
+ }
346
+
347
+ // src/nodes/views/ActionNodeView.tsx
348
+ var import_jsx_runtime4 = require("react/jsx-runtime");
349
+ var ACTION_COLOR = "var(--of-node-action-color, #2196F3)";
350
+ function ActionNodeView({ id, data, selected }) {
351
+ const t = useTranslation();
352
+ const nodeData = data;
353
+ const actionName = nodeData.action;
354
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
355
+ BaseNodeView,
356
+ {
357
+ id,
358
+ data: nodeData,
359
+ selected,
360
+ label: t("nodes.action.label"),
361
+ color: ACTION_COLOR,
362
+ icon: "\u26A1",
363
+ sourceHandles: [{ id: "output" }],
364
+ targetHandles: [{ id: "input" }],
365
+ children: actionName ? actionName : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("em", { children: t("nodes.action.noAction") })
366
+ }
367
+ );
368
+ }
369
+
370
+ // src/nodes/views/ConditionNodeView.tsx
371
+ var import_jsx_runtime5 = require("react/jsx-runtime");
372
+ var CONDITION_COLOR = "var(--of-node-condition-color, #FF9800)";
373
+ function ConditionNodeView({ id, data, selected }) {
374
+ const t = useTranslation();
375
+ const nodeData = data;
376
+ const conditions = nodeData.conditions;
377
+ const ruleCount = (conditions?.all?.length ?? 0) + (conditions?.any?.length ?? 0);
378
+ const ruleLabel = ruleCount === 0 ? null : ruleCount === 1 ? t("nodes.condition.ruleCountSingular") : t("nodes.condition.ruleCount", { count: String(ruleCount) });
379
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
380
+ BaseNodeView,
381
+ {
382
+ id,
383
+ data: nodeData,
384
+ selected,
385
+ label: t("nodes.condition.label"),
386
+ color: CONDITION_COLOR,
387
+ icon: "?",
388
+ sourceHandles: [
389
+ { id: "true", label: t("nodes.condition.handleTrue") },
390
+ { id: "false", label: t("nodes.condition.handleFalse") }
391
+ ],
392
+ targetHandles: [{ id: "input" }],
393
+ children: ruleLabel ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("em", { children: t("nodes.condition.noRules") })
394
+ }
395
+ );
396
+ }
397
+
398
+ // src/nodes/views/ExitNodeView.tsx
399
+ var import_jsx_runtime6 = require("react/jsx-runtime");
400
+ var EXIT_COLOR = "var(--of-node-exit-color, #F44336)";
401
+ function ExitNodeView({ id, data, selected }) {
402
+ const t = useTranslation();
403
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
404
+ BaseNodeView,
405
+ {
406
+ id,
407
+ data,
408
+ selected,
409
+ label: t("nodes.exit.label"),
410
+ color: EXIT_COLOR,
411
+ icon: "\u23F9",
412
+ sourceHandles: [],
413
+ targetHandles: [{ id: "input" }],
414
+ children: t("nodes.exit.endWorkflow")
415
+ }
416
+ );
417
+ }
418
+
419
+ // src/utils/duration.ts
420
+ var MS_PER_MINUTE = 6e4;
421
+ var MS_PER_HOUR = 36e5;
422
+ var MS_PER_DAY = 864e5;
423
+ function msToParts(ms) {
424
+ const days = Math.floor(ms / MS_PER_DAY);
425
+ const remainingAfterDays = ms % MS_PER_DAY;
426
+ const hours = Math.floor(remainingAfterDays / MS_PER_HOUR);
427
+ const remainingAfterHours = remainingAfterDays % MS_PER_HOUR;
428
+ const minutes = Math.floor(remainingAfterHours / MS_PER_MINUTE);
429
+ return { days, hours, minutes };
430
+ }
431
+ function partsToMs(parts) {
432
+ return parts.days * MS_PER_DAY + parts.hours * MS_PER_HOUR + parts.minutes * MS_PER_MINUTE;
433
+ }
434
+ function formatDuration(ms) {
435
+ if (ms === 0) return "0m";
436
+ const { days, hours, minutes } = msToParts(ms);
437
+ const parts = [];
438
+ if (days > 0) parts.push(`${days}d`);
439
+ if (hours > 0) parts.push(`${hours}h`);
440
+ if (minutes > 0) parts.push(`${minutes}m`);
441
+ return parts.length > 0 ? parts.join(" ") : "0m";
442
+ }
443
+
444
+ // src/nodes/views/WaitNodeView.tsx
445
+ var import_jsx_runtime7 = require("react/jsx-runtime");
446
+ var WAIT_COLOR = "var(--of-node-wait-color, #9C27B0)";
447
+ function WaitNodeView({ id, data, selected }) {
448
+ const t = useTranslation();
449
+ const nodeData = data;
450
+ const params = nodeData.params;
451
+ const duration = params?.duration;
452
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
453
+ BaseNodeView,
454
+ {
455
+ id,
456
+ data: nodeData,
457
+ selected,
458
+ label: t("nodes.wait.label"),
459
+ color: WAIT_COLOR,
460
+ icon: "\u23F1",
461
+ sourceHandles: [{ id: "output" }],
462
+ targetHandles: [{ id: "input" }],
463
+ children: duration != null ? formatDuration(duration) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("em", { children: t("nodes.wait.noDuration") })
464
+ }
465
+ );
466
+ }
467
+
468
+ // src/nodes/views/TriggerOrTimeoutNodeView.tsx
469
+ var import_jsx_runtime8 = require("react/jsx-runtime");
470
+ var TRIGGER_OR_TIMEOUT_COLOR = "var(--of-node-trigger-timeout-color, #607D8B)";
471
+ function TriggerOrTimeoutNodeView({ id, data, selected }) {
472
+ const t = useTranslation();
473
+ const nodeData = data;
474
+ const params = nodeData.params;
475
+ const eventName = params?.event;
476
+ const duration = params?.duration;
477
+ const description = [
478
+ eventName || t("nodes.triggerOrTimeout.eventFallback"),
479
+ duration != null ? t("nodes.triggerOrTimeout.orDuration", { duration: formatDuration(duration) }) : ""
480
+ ].filter(Boolean).join(" ");
481
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
482
+ BaseNodeView,
483
+ {
484
+ id,
485
+ data: nodeData,
486
+ selected,
487
+ label: t("nodes.triggerOrTimeout.label"),
488
+ color: TRIGGER_OR_TIMEOUT_COLOR,
489
+ icon: "\u23F0",
490
+ sourceHandles: [
491
+ { id: "trigger", label: t("nodes.triggerOrTimeout.handleTrigger") },
492
+ { id: "timeout", label: t("nodes.triggerOrTimeout.handleTimeout") }
493
+ ],
494
+ targetHandles: [{ id: "input" }],
495
+ children: description || /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("em", { children: t("nodes.triggerOrTimeout.notConfigured") })
496
+ }
497
+ );
498
+ }
499
+
500
+ // src/primitives/Field.tsx
501
+ var import_jsx_runtime9 = require("react/jsx-runtime");
502
+ var fieldStyle = {
503
+ display: "flex",
504
+ flexDirection: "column",
505
+ gap: "var(--of-spacing-1, 4px)",
506
+ marginBottom: "var(--of-spacing-5, 12px)"
507
+ };
508
+ var labelStyle = {
509
+ fontSize: "var(--of-field-label-size, 12px)",
510
+ fontWeight: "var(--of-font-weight-medium, 500)",
511
+ color: "var(--of-field-label-color, #374151)"
512
+ };
513
+ var errorStyle = {
514
+ fontSize: "var(--of-font-size-xs, 11px)",
515
+ color: "var(--of-field-error-color, #DC2626)"
516
+ };
517
+ var hintStyle = {
518
+ fontSize: "var(--of-font-size-xs, 11px)",
519
+ color: "var(--of-field-hint-color, #6B7280)"
520
+ };
521
+ function Field({ label, children, error, hint }) {
522
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: fieldStyle, children: [
523
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("label", { style: labelStyle, children: label }),
524
+ children,
525
+ error && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: errorStyle, children: error }),
526
+ hint && !error && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: hintStyle, children: hint })
527
+ ] });
528
+ }
529
+
530
+ // src/primitives/TextField.tsx
531
+ var import_jsx_runtime10 = require("react/jsx-runtime");
532
+ var inputStyle = {
533
+ padding: "var(--of-field-padding, 8px 10px)",
534
+ borderRadius: "var(--of-field-radius, 6px)",
535
+ border: "1px solid var(--of-field-border, #D1D5DB)",
536
+ fontSize: "var(--of-field-font-size, 13px)",
537
+ outline: "none",
538
+ transition: "border-color var(--of-transition-fast, 0.15s)",
539
+ color: "var(--of-color-text-primary, #111827)"
540
+ };
541
+ function TextField({
542
+ label,
543
+ value,
544
+ onChange,
545
+ placeholder,
546
+ disabled,
547
+ error,
548
+ hint
549
+ }) {
550
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Field, { label, error, hint, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
551
+ "input",
552
+ {
553
+ type: "text",
554
+ value,
555
+ onChange: (e) => onChange(e.target.value),
556
+ placeholder,
557
+ disabled,
558
+ style: {
559
+ ...inputStyle,
560
+ borderColor: error ? "var(--of-field-border-error, #DC2626)" : "var(--of-field-border, #D1D5DB)",
561
+ backgroundColor: disabled ? "var(--of-color-bg-disabled, #F3F4F6)" : "var(--of-field-bg, #fff)"
562
+ },
563
+ onFocus: (e) => {
564
+ if (!error)
565
+ e.target.style.borderColor = "var(--of-field-border-focus, #3B82F6)";
566
+ },
567
+ onBlur: (e) => {
568
+ if (!error)
569
+ e.target.style.borderColor = "var(--of-field-border, #D1D5DB)";
570
+ }
571
+ }
572
+ ) });
573
+ }
574
+
575
+ // src/primitives/NumberField.tsx
576
+ var import_jsx_runtime11 = require("react/jsx-runtime");
577
+ var inputStyle2 = {
578
+ padding: "var(--of-field-padding, 8px 10px)",
579
+ borderRadius: "var(--of-field-radius, 6px)",
580
+ border: "1px solid var(--of-field-border, #D1D5DB)",
581
+ fontSize: "var(--of-field-font-size, 13px)",
582
+ outline: "none",
583
+ transition: "border-color var(--of-transition-fast, 0.15s)",
584
+ color: "var(--of-color-text-primary, #111827)"
585
+ };
586
+ function NumberField({
587
+ label,
588
+ value,
589
+ onChange,
590
+ min,
591
+ max,
592
+ step,
593
+ disabled,
594
+ error,
595
+ hint
596
+ }) {
597
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Field, { label, error, hint, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
598
+ "input",
599
+ {
600
+ type: "number",
601
+ value,
602
+ onChange: (e) => onChange(parseFloat(e.target.value) || 0),
603
+ min,
604
+ max,
605
+ step,
606
+ disabled,
607
+ style: {
608
+ ...inputStyle2,
609
+ borderColor: error ? "var(--of-field-border-error, #DC2626)" : "var(--of-field-border, #D1D5DB)",
610
+ backgroundColor: disabled ? "var(--of-color-bg-disabled, #F3F4F6)" : "var(--of-field-bg, #fff)"
611
+ },
612
+ onFocus: (e) => {
613
+ if (!error)
614
+ e.target.style.borderColor = "var(--of-field-border-focus, #3B82F6)";
615
+ },
616
+ onBlur: (e) => {
617
+ if (!error)
618
+ e.target.style.borderColor = "var(--of-field-border, #D1D5DB)";
619
+ }
620
+ }
621
+ ) });
622
+ }
623
+
624
+ // src/primitives/SelectField.tsx
625
+ var import_jsx_runtime12 = require("react/jsx-runtime");
626
+ var selectStyle = {
627
+ padding: "var(--of-field-padding, 8px 10px)",
628
+ borderRadius: "var(--of-field-radius, 6px)",
629
+ border: "1px solid var(--of-field-border, #D1D5DB)",
630
+ fontSize: "var(--of-field-font-size, 13px)",
631
+ outline: "none",
632
+ transition: "border-color var(--of-transition-fast, 0.15s)",
633
+ backgroundColor: "var(--of-field-bg, #fff)",
634
+ color: "var(--of-color-text-primary, #111827)",
635
+ cursor: "pointer"
636
+ };
637
+ function SelectField({
638
+ label,
639
+ value,
640
+ options,
641
+ onChange,
642
+ placeholder,
643
+ disabled,
644
+ error,
645
+ hint
646
+ }) {
647
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Field, { label, error, hint, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
648
+ "select",
649
+ {
650
+ value,
651
+ onChange: (e) => onChange(e.target.value),
652
+ disabled,
653
+ style: {
654
+ ...selectStyle,
655
+ borderColor: error ? "var(--of-field-border-error, #DC2626)" : "var(--of-field-border, #D1D5DB)",
656
+ backgroundColor: disabled ? "var(--of-color-bg-disabled, #F3F4F6)" : "var(--of-field-bg, #fff)"
657
+ },
658
+ onFocus: (e) => {
659
+ if (!error)
660
+ e.target.style.borderColor = "var(--of-field-border-focus, #3B82F6)";
661
+ },
662
+ onBlur: (e) => {
663
+ if (!error)
664
+ e.target.style.borderColor = "var(--of-field-border, #D1D5DB)";
665
+ },
666
+ children: [
667
+ placeholder && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: "", disabled: true, children: placeholder }),
668
+ options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: option.value, children: option.label }, option.value))
669
+ ]
670
+ }
671
+ ) });
672
+ }
673
+
674
+ // src/primitives/CheckboxField.tsx
675
+ var import_jsx_runtime13 = require("react/jsx-runtime");
676
+ var containerStyle = {
677
+ display: "flex",
678
+ alignItems: "center",
679
+ gap: "var(--of-spacing-3, 8px)",
680
+ marginBottom: "var(--of-spacing-5, 12px)",
681
+ cursor: "pointer"
682
+ };
683
+ var checkboxStyle = {
684
+ width: "16px",
685
+ height: "16px",
686
+ cursor: "pointer"
687
+ };
688
+ var labelStyle2 = {
689
+ fontSize: "var(--of-field-font-size, 13px)",
690
+ color: "var(--of-color-text-secondary, #374151)",
691
+ cursor: "pointer"
692
+ };
693
+ function CheckboxField({
694
+ label,
695
+ checked,
696
+ onChange,
697
+ disabled
698
+ }) {
699
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
700
+ "label",
701
+ {
702
+ style: {
703
+ ...containerStyle,
704
+ cursor: disabled ? "not-allowed" : "pointer",
705
+ opacity: disabled ? 0.6 : 1
706
+ },
707
+ children: [
708
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
709
+ "input",
710
+ {
711
+ type: "checkbox",
712
+ checked,
713
+ onChange: (e) => onChange(e.target.checked),
714
+ disabled,
715
+ style: checkboxStyle
716
+ }
717
+ ),
718
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: labelStyle2, children: label })
719
+ ]
720
+ }
721
+ );
722
+ }
723
+
724
+ // src/primitives/TextAreaField.tsx
725
+ var import_jsx_runtime14 = require("react/jsx-runtime");
726
+ var textareaStyle = {
727
+ padding: "var(--of-field-padding, 8px 10px)",
728
+ borderRadius: "var(--of-field-radius, 6px)",
729
+ border: "1px solid var(--of-field-border, #D1D5DB)",
730
+ fontSize: "var(--of-field-font-size, 13px)",
731
+ color: "var(--of-color-text-primary, #111827)",
732
+ outline: "none",
733
+ transition: "border-color var(--of-transition-fast, 0.15s)",
734
+ resize: "vertical",
735
+ fontFamily: "inherit"
736
+ };
737
+ function TextAreaField({
738
+ label,
739
+ value,
740
+ onChange,
741
+ rows = 4,
742
+ placeholder,
743
+ disabled,
744
+ error,
745
+ hint
746
+ }) {
747
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Field, { label, error, hint, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
748
+ "textarea",
749
+ {
750
+ value,
751
+ onChange: (e) => onChange(e.target.value),
752
+ rows,
753
+ placeholder,
754
+ disabled,
755
+ style: {
756
+ ...textareaStyle,
757
+ borderColor: error ? "var(--of-field-border-error, #DC2626)" : "var(--of-field-border, #D1D5DB)",
758
+ backgroundColor: disabled ? "var(--of-color-bg-disabled, #F3F4F6)" : "var(--of-field-bg, #fff)"
759
+ },
760
+ onFocus: (e) => {
761
+ if (!error)
762
+ e.target.style.borderColor = "var(--of-field-border-focus, #3B82F6)";
763
+ },
764
+ onBlur: (e) => {
765
+ if (!error)
766
+ e.target.style.borderColor = "var(--of-field-border, #D1D5DB)";
767
+ }
768
+ }
769
+ ) });
770
+ }
771
+
772
+ // src/primitives/DurationField.tsx
773
+ var import_react3 = require("react");
774
+ var import_jsx_runtime15 = require("react/jsx-runtime");
775
+ var containerStyle2 = {
776
+ display: "flex",
777
+ gap: "var(--of-spacing-3, 8px)"
778
+ };
779
+ var unitGroupStyle = {
780
+ display: "flex",
781
+ alignItems: "center",
782
+ gap: "var(--of-spacing-2, 4px)"
783
+ };
784
+ var inputStyle3 = {
785
+ width: "60px",
786
+ padding: "var(--of-field-padding, 8px 10px)",
787
+ borderRadius: "var(--of-field-radius, 6px)",
788
+ border: "1px solid var(--of-field-border, #D1D5DB)",
789
+ fontSize: "var(--of-field-font-size, 13px)",
790
+ color: "var(--of-color-text-primary, #111827)",
791
+ outline: "none",
792
+ textAlign: "center"
793
+ };
794
+ var labelStyle3 = {
795
+ fontSize: "var(--of-field-font-size, 13px)",
796
+ color: "var(--of-color-text-secondary, #6B7280)"
797
+ };
798
+ function DurationField({
799
+ label,
800
+ value,
801
+ onChange,
802
+ disabled,
803
+ error,
804
+ hint
805
+ }) {
806
+ const t = useTranslation();
807
+ const initial = msToParts(value);
808
+ const [parts, setParts] = (0, import_react3.useState)(initial);
809
+ (0, import_react3.useEffect)(() => {
810
+ const converted = msToParts(value);
811
+ setParts(converted);
812
+ }, [value]);
813
+ const handlePartChange = (part, newValue) => {
814
+ const sanitizedValue = Math.max(0, Math.floor(newValue) || 0);
815
+ const newParts = { ...parts, [part]: sanitizedValue };
816
+ setParts(newParts);
817
+ onChange(partsToMs(newParts));
818
+ };
819
+ const getInputStyle = () => ({
820
+ ...inputStyle3,
821
+ borderColor: error ? "var(--of-field-border-error, #DC2626)" : "var(--of-field-border, #D1D5DB)",
822
+ backgroundColor: disabled ? "var(--of-color-bg-disabled, #F3F4F6)" : "var(--of-field-bg, #fff)"
823
+ });
824
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Field, { label, error, hint, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: containerStyle2, children: [
825
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: unitGroupStyle, children: [
826
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
827
+ "input",
828
+ {
829
+ type: "number",
830
+ value: parts.days,
831
+ onChange: (e) => handlePartChange("days", parseFloat(e.target.value)),
832
+ min: 0,
833
+ disabled,
834
+ style: getInputStyle()
835
+ }
836
+ ),
837
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: labelStyle3, children: t("fields.duration.days") })
838
+ ] }),
839
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: unitGroupStyle, children: [
840
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
841
+ "input",
842
+ {
843
+ type: "number",
844
+ value: parts.hours,
845
+ onChange: (e) => handlePartChange("hours", parseFloat(e.target.value)),
846
+ min: 0,
847
+ max: 23,
848
+ disabled,
849
+ style: getInputStyle()
850
+ }
851
+ ),
852
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: labelStyle3, children: t("fields.duration.hours") })
853
+ ] }),
854
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: unitGroupStyle, children: [
855
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
856
+ "input",
857
+ {
858
+ type: "number",
859
+ value: parts.minutes,
860
+ onChange: (e) => handlePartChange("minutes", parseFloat(e.target.value)),
861
+ min: 0,
862
+ max: 59,
863
+ disabled,
864
+ style: getInputStyle()
865
+ }
866
+ ),
867
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: labelStyle3, children: t("fields.duration.minutes") })
868
+ ] })
869
+ ] }) });
870
+ }
871
+
872
+ // src/primitives/JsonField.tsx
873
+ var import_react4 = require("react");
874
+ var import_jsx_runtime16 = require("react/jsx-runtime");
875
+ var textareaStyle2 = {
876
+ padding: "var(--of-field-padding, 8px 10px)",
877
+ borderRadius: "var(--of-field-radius, 6px)",
878
+ border: "1px solid var(--of-field-border, #D1D5DB)",
879
+ fontSize: "var(--of-font-size-sm, 12px)",
880
+ fontFamily: "var(--of-font-family-mono, monospace)",
881
+ color: "var(--of-color-text-primary, #111827)",
882
+ outline: "none",
883
+ transition: "border-color var(--of-transition-fast, 0.15s)",
884
+ resize: "vertical"
885
+ };
886
+ function JsonField({
887
+ label,
888
+ value,
889
+ onChange,
890
+ rows = 6,
891
+ disabled,
892
+ error: externalError,
893
+ hint
894
+ }) {
895
+ const t = useTranslation();
896
+ const [text, setText] = (0, import_react4.useState)(() => JSON.stringify(value, null, 2));
897
+ const [parseError, setParseError] = (0, import_react4.useState)(null);
898
+ (0, import_react4.useEffect)(() => {
899
+ setText(JSON.stringify(value, null, 2));
900
+ setParseError(null);
901
+ }, [value]);
902
+ const handleChange = (newText) => {
903
+ setText(newText);
904
+ try {
905
+ const parsed = JSON.parse(newText);
906
+ setParseError(null);
907
+ onChange(parsed);
908
+ } catch (e) {
909
+ setParseError(t("fields.json.invalidJson"));
910
+ }
911
+ };
912
+ const error = externalError || parseError;
913
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Field, { label, error: error ?? void 0, hint, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
914
+ "textarea",
915
+ {
916
+ value: text,
917
+ onChange: (e) => handleChange(e.target.value),
918
+ rows,
919
+ disabled,
920
+ style: {
921
+ ...textareaStyle2,
922
+ borderColor: error ? "var(--of-field-border-error, #DC2626)" : "var(--of-field-border, #D1D5DB)",
923
+ backgroundColor: disabled ? "var(--of-color-bg-disabled, #F3F4F6)" : "var(--of-field-bg, #fff)"
924
+ },
925
+ onFocus: (e) => {
926
+ if (!error)
927
+ e.target.style.borderColor = "var(--of-field-border-focus, #3B82F6)";
928
+ },
929
+ onBlur: (e) => {
930
+ if (!error)
931
+ e.target.style.borderColor = "var(--of-field-border, #D1D5DB)";
932
+ }
933
+ }
934
+ ) });
935
+ }
936
+
937
+ // src/primitives/FieldGroup.tsx
938
+ var import_react5 = require("react");
939
+ var import_jsx_runtime17 = require("react/jsx-runtime");
940
+ var groupStyle = {
941
+ marginBottom: "var(--of-spacing-6, 16px)"
942
+ };
943
+ var headerStyle2 = {
944
+ display: "flex",
945
+ alignItems: "center",
946
+ gap: "var(--of-spacing-2, 6px)",
947
+ marginBottom: "var(--of-spacing-5, 12px)",
948
+ fontSize: "var(--of-field-font-size, 13px)",
949
+ fontWeight: "var(--of-font-weight-semibold, 600)",
950
+ color: "var(--of-color-text-primary, #111827)"
951
+ };
952
+ var toggleStyle = {
953
+ cursor: "pointer",
954
+ userSelect: "none",
955
+ display: "flex",
956
+ alignItems: "center",
957
+ gap: "var(--of-spacing-2, 6px)"
958
+ };
959
+ var contentStyle2 = {
960
+ paddingLeft: "var(--of-spacing-1, 4px)"
961
+ };
962
+ function FieldGroup({
963
+ label,
964
+ children,
965
+ collapsible = false,
966
+ defaultCollapsed = false
967
+ }) {
968
+ const [collapsed, setCollapsed] = (0, import_react5.useState)(defaultCollapsed);
969
+ if (!label) {
970
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: groupStyle, children });
971
+ }
972
+ if (collapsible) {
973
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: groupStyle, children: [
974
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
975
+ "div",
976
+ {
977
+ style: { ...headerStyle2, ...toggleStyle },
978
+ onClick: () => setCollapsed(!collapsed),
979
+ children: [
980
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: { transform: collapsed ? "rotate(-90deg)" : "rotate(0)", transition: "transform var(--of-transition-fast, 0.15s)" }, children: "\u25BC" }),
981
+ label
982
+ ]
983
+ }
984
+ ),
985
+ !collapsed && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: contentStyle2, children })
986
+ ] });
987
+ }
988
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: groupStyle, children: [
989
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: headerStyle2, children: label }),
990
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: contentStyle2, children })
991
+ ] });
992
+ }
993
+
994
+ // src/primitives/condition-builder/ConditionBuilder.tsx
995
+ var import_react6 = __toESM(require("react"), 1);
996
+
997
+ // src/primitives/condition-builder/types.ts
998
+ function isPropertyGroup(item) {
999
+ return "children" in item && Array.isArray(item.children);
1000
+ }
1001
+
1002
+ // src/primitives/condition-builder/ConditionRow.tsx
1003
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1004
+ var rowStyle = {
1005
+ display: "flex",
1006
+ alignItems: "center",
1007
+ gap: "var(--of-spacing-2, 6px)",
1008
+ marginBottom: "var(--of-spacing-2, 6px)"
1009
+ };
1010
+ var selectStyle2 = {
1011
+ padding: "var(--of-spacing-2, 6px) var(--of-spacing-3, 8px)",
1012
+ borderRadius: "var(--of-field-radius, 6px)",
1013
+ border: "1px solid var(--of-field-border, #D1D5DB)",
1014
+ fontSize: "var(--of-font-size-sm, 12px)",
1015
+ outline: "none",
1016
+ backgroundColor: "var(--of-field-bg, #fff)",
1017
+ color: "var(--of-color-text-primary, #111827)",
1018
+ minWidth: 0
1019
+ };
1020
+ var factSelectStyle = {
1021
+ ...selectStyle2,
1022
+ flex: "1 1 30%"
1023
+ };
1024
+ var operatorSelectStyle = {
1025
+ ...selectStyle2,
1026
+ flex: "1 1 30%"
1027
+ };
1028
+ var inputStyle4 = {
1029
+ padding: "var(--of-spacing-2, 6px) var(--of-spacing-3, 8px)",
1030
+ borderRadius: "var(--of-field-radius, 6px)",
1031
+ border: "1px solid var(--of-field-border, #D1D5DB)",
1032
+ fontSize: "var(--of-font-size-sm, 12px)",
1033
+ outline: "none",
1034
+ backgroundColor: "var(--of-field-bg, #fff)",
1035
+ color: "var(--of-color-text-primary, #111827)",
1036
+ flex: "1 1 25%",
1037
+ minWidth: 0
1038
+ };
1039
+ var removeButtonStyle = {
1040
+ padding: "var(--of-spacing-1, 4px) var(--of-spacing-2, 6px)",
1041
+ border: "none",
1042
+ background: "none",
1043
+ color: "var(--of-color-text-muted, #9CA3AF)",
1044
+ cursor: "pointer",
1045
+ fontSize: "var(--of-font-size-lg, 14px)",
1046
+ lineHeight: 1,
1047
+ borderRadius: "var(--of-radius-sm, 4px)",
1048
+ flexShrink: 0
1049
+ };
1050
+ function parseValue(raw) {
1051
+ if (raw === "true") return true;
1052
+ if (raw === "false") return false;
1053
+ const num = Number(raw);
1054
+ if (raw !== "" && !isNaN(num)) return num;
1055
+ return raw;
1056
+ }
1057
+ function formatValue(value) {
1058
+ if (value === null || value === void 0) return "";
1059
+ return String(value);
1060
+ }
1061
+ function ConditionRow({
1062
+ rule,
1063
+ properties,
1064
+ operators,
1065
+ onChange,
1066
+ onRemove
1067
+ }) {
1068
+ const t = useTranslation();
1069
+ const hasProperties = properties.length > 0;
1070
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: rowStyle, children: [
1071
+ hasProperties ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1072
+ "select",
1073
+ {
1074
+ value: rule.fact,
1075
+ onChange: (e) => onChange({ ...rule, fact: e.target.value }),
1076
+ style: factSelectStyle,
1077
+ children: [
1078
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: "", children: t("conditionBuilder.selectProperty") }),
1079
+ properties.map(
1080
+ (item, idx) => isPropertyGroup(item) ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("optgroup", { label: item.label, children: item.children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: child.value, children: child.label }, child.value)) }, idx) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: item.value, children: item.label }, item.value)
1081
+ )
1082
+ ]
1083
+ }
1084
+ ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1085
+ "input",
1086
+ {
1087
+ type: "text",
1088
+ value: rule.fact,
1089
+ onChange: (e) => onChange({ ...rule, fact: e.target.value }),
1090
+ placeholder: t("conditionBuilder.factPlaceholder"),
1091
+ style: { ...inputStyle4, flex: "1 1 30%" }
1092
+ }
1093
+ ),
1094
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1095
+ "select",
1096
+ {
1097
+ value: rule.operator,
1098
+ onChange: (e) => onChange({ ...rule, operator: e.target.value }),
1099
+ style: operatorSelectStyle,
1100
+ children: [
1101
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: "", children: t("conditionBuilder.selectOperator") }),
1102
+ operators.map((op) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: op.value, children: op.label }, op.value))
1103
+ ]
1104
+ }
1105
+ ),
1106
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1107
+ "input",
1108
+ {
1109
+ type: "text",
1110
+ value: formatValue(rule.value),
1111
+ onChange: (e) => onChange({ ...rule, value: parseValue(e.target.value) }),
1112
+ placeholder: t("conditionBuilder.valuePlaceholder"),
1113
+ style: inputStyle4
1114
+ }
1115
+ ),
1116
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1117
+ "button",
1118
+ {
1119
+ type: "button",
1120
+ onClick: onRemove,
1121
+ style: removeButtonStyle,
1122
+ title: t("conditionBuilder.removeCondition"),
1123
+ onMouseEnter: (e) => e.currentTarget.style.color = "var(--of-color-status-error, #DC2626)",
1124
+ onMouseLeave: (e) => e.currentTarget.style.color = "var(--of-color-text-muted, #9CA3AF)",
1125
+ children: "\u2715"
1126
+ }
1127
+ )
1128
+ ] });
1129
+ }
1130
+
1131
+ // src/primitives/condition-builder/ConditionGroup.tsx
1132
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1133
+ var groupStyle2 = {
1134
+ border: "1px solid var(--of-color-border-secondary, #E5E7EB)",
1135
+ borderRadius: "var(--of-radius-md, 6px)",
1136
+ padding: "var(--of-spacing-4, 10px)",
1137
+ backgroundColor: "var(--of-color-bg-secondary, #F9FAFB)"
1138
+ };
1139
+ var groupHeaderStyle = {
1140
+ display: "flex",
1141
+ alignItems: "center",
1142
+ justifyContent: "space-between",
1143
+ marginBottom: "var(--of-spacing-3, 8px)"
1144
+ };
1145
+ var operatorToggleStyle = {
1146
+ display: "flex",
1147
+ alignItems: "center",
1148
+ gap: "var(--of-spacing-2, 6px)",
1149
+ fontSize: "var(--of-font-size-sm, 12px)",
1150
+ color: "var(--of-color-text-secondary, #374151)"
1151
+ };
1152
+ var toggleButtonBase = {
1153
+ padding: "var(--of-spacing-1, 4px) var(--of-spacing-3, 8px)",
1154
+ border: "1px solid var(--of-color-border-primary, #D1D5DB)",
1155
+ borderRadius: "var(--of-radius-sm, 4px)",
1156
+ fontSize: "var(--of-font-size-xs, 11px)",
1157
+ fontWeight: "var(--of-font-weight-semibold, 600)",
1158
+ cursor: "pointer",
1159
+ transition: "all var(--of-transition-fast, 0.15s)"
1160
+ };
1161
+ var removeGroupButtonStyle = {
1162
+ padding: "var(--of-spacing-1, 4px) var(--of-spacing-2, 6px)",
1163
+ border: "none",
1164
+ background: "none",
1165
+ color: "var(--of-color-text-muted, #9CA3AF)",
1166
+ cursor: "pointer",
1167
+ fontSize: "var(--of-font-size-sm, 12px)",
1168
+ borderRadius: "var(--of-radius-sm, 4px)"
1169
+ };
1170
+ var addConditionButtonStyle = {
1171
+ padding: "var(--of-spacing-1, 4px) var(--of-spacing-3, 8px)",
1172
+ border: "1px dashed var(--of-color-border-primary, #D1D5DB)",
1173
+ background: "none",
1174
+ color: "var(--of-color-text-tertiary, #6B7280)",
1175
+ cursor: "pointer",
1176
+ fontSize: "var(--of-font-size-xs, 11px)",
1177
+ borderRadius: "var(--of-radius-sm, 4px)",
1178
+ width: "100%",
1179
+ marginTop: "var(--of-spacing-2, 6px)"
1180
+ };
1181
+ var emptyStyle = {
1182
+ fontSize: "var(--of-font-size-xs, 11px)",
1183
+ color: "var(--of-color-text-muted, #9CA3AF)",
1184
+ textAlign: "center",
1185
+ padding: "var(--of-spacing-3, 8px) 0"
1186
+ };
1187
+ function createEmptyRule() {
1188
+ return { fact: "", operator: "equal", value: "" };
1189
+ }
1190
+ function ConditionGroupView({
1191
+ group,
1192
+ properties,
1193
+ operators,
1194
+ onChange,
1195
+ onRemove,
1196
+ canRemove
1197
+ }) {
1198
+ const t = useTranslation();
1199
+ const handleOperatorChange = (operator) => {
1200
+ onChange({ ...group, operator });
1201
+ };
1202
+ const handleRuleChange = (index, rule) => {
1203
+ const conditions = [...group.conditions];
1204
+ conditions[index] = rule;
1205
+ onChange({ ...group, conditions });
1206
+ };
1207
+ const handleRuleRemove = (index) => {
1208
+ const conditions = group.conditions.filter((_, i) => i !== index);
1209
+ onChange({ ...group, conditions });
1210
+ };
1211
+ const handleAddCondition = () => {
1212
+ onChange({
1213
+ ...group,
1214
+ conditions: [...group.conditions, createEmptyRule()]
1215
+ });
1216
+ };
1217
+ const isAnd = group.operator === "all";
1218
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: groupStyle2, children: [
1219
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: groupHeaderStyle, children: [
1220
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: operatorToggleStyle, children: [
1221
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { children: t("conditionBuilder.matchLabel") }),
1222
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1223
+ "button",
1224
+ {
1225
+ type: "button",
1226
+ onClick: () => handleOperatorChange("all"),
1227
+ style: {
1228
+ ...toggleButtonBase,
1229
+ backgroundColor: isAnd ? "var(--of-color-interactive-primary, #3B82F6)" : "var(--of-field-bg, #fff)",
1230
+ color: isAnd ? "var(--of-color-text-inverse, #fff)" : "var(--of-color-text-secondary, #374151)",
1231
+ borderColor: isAnd ? "var(--of-color-interactive-primary, #3B82F6)" : "var(--of-color-border-primary, #D1D5DB)"
1232
+ },
1233
+ children: "AND"
1234
+ }
1235
+ ),
1236
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1237
+ "button",
1238
+ {
1239
+ type: "button",
1240
+ onClick: () => handleOperatorChange("any"),
1241
+ style: {
1242
+ ...toggleButtonBase,
1243
+ backgroundColor: !isAnd ? "var(--of-color-interactive-primary, #3B82F6)" : "var(--of-field-bg, #fff)",
1244
+ color: !isAnd ? "var(--of-color-text-inverse, #fff)" : "var(--of-color-text-secondary, #374151)",
1245
+ borderColor: !isAnd ? "var(--of-color-interactive-primary, #3B82F6)" : "var(--of-color-border-primary, #D1D5DB)"
1246
+ },
1247
+ children: "OR"
1248
+ }
1249
+ )
1250
+ ] }),
1251
+ canRemove && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1252
+ "button",
1253
+ {
1254
+ type: "button",
1255
+ onClick: onRemove,
1256
+ style: removeGroupButtonStyle,
1257
+ title: t("conditionBuilder.removeGroup"),
1258
+ onMouseEnter: (e) => e.currentTarget.style.color = "var(--of-color-status-error, #DC2626)",
1259
+ onMouseLeave: (e) => e.currentTarget.style.color = "var(--of-color-text-muted, #9CA3AF)",
1260
+ children: t("conditionBuilder.removeGroup")
1261
+ }
1262
+ )
1263
+ ] }),
1264
+ group.conditions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: emptyStyle, children: t("conditionBuilder.emptyGroup") }) : group.conditions.map((rule, index) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1265
+ ConditionRow,
1266
+ {
1267
+ rule,
1268
+ properties,
1269
+ operators,
1270
+ onChange: (updated) => handleRuleChange(index, updated),
1271
+ onRemove: () => handleRuleRemove(index)
1272
+ },
1273
+ index
1274
+ )),
1275
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
1276
+ "button",
1277
+ {
1278
+ type: "button",
1279
+ onClick: handleAddCondition,
1280
+ style: addConditionButtonStyle,
1281
+ onMouseEnter: (e) => {
1282
+ e.currentTarget.style.borderColor = "var(--of-color-interactive-primary, #3B82F6)";
1283
+ e.currentTarget.style.color = "var(--of-color-interactive-primary, #3B82F6)";
1284
+ },
1285
+ onMouseLeave: (e) => {
1286
+ e.currentTarget.style.borderColor = "var(--of-color-border-primary, #D1D5DB)";
1287
+ e.currentTarget.style.color = "var(--of-color-text-tertiary, #6B7280)";
1288
+ },
1289
+ children: [
1290
+ "+ ",
1291
+ t("conditionBuilder.addCondition")
1292
+ ]
1293
+ }
1294
+ )
1295
+ ] });
1296
+ }
1297
+
1298
+ // src/primitives/condition-builder/operators.ts
1299
+ var defaultOperators = [
1300
+ { value: "equal", label: "equals" },
1301
+ { value: "notEqual", label: "does not equal" },
1302
+ { value: "greaterThan", label: "greater than" },
1303
+ { value: "greaterThanInclusive", label: "greater than or equal" },
1304
+ { value: "lessThan", label: "less than" },
1305
+ { value: "lessThanInclusive", label: "less than or equal" },
1306
+ { value: "in", label: "is in" },
1307
+ { value: "notIn", label: "is not in" },
1308
+ { value: "contains", label: "contains" },
1309
+ { value: "doesNotContain", label: "does not contain" }
1310
+ ];
1311
+
1312
+ // src/primitives/condition-builder/ConditionBuilder.tsx
1313
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1314
+ var containerStyle3 = {
1315
+ display: "flex",
1316
+ flexDirection: "column",
1317
+ gap: "var(--of-spacing-3, 8px)"
1318
+ };
1319
+ var topLabelStyle = {
1320
+ fontSize: "var(--of-font-size-sm, 12px)",
1321
+ fontWeight: "var(--of-font-weight-medium, 500)",
1322
+ color: "var(--of-color-text-secondary, #374151)",
1323
+ marginBottom: "var(--of-spacing-1, 4px)"
1324
+ };
1325
+ var orDividerStyle = {
1326
+ display: "flex",
1327
+ alignItems: "center",
1328
+ gap: "var(--of-spacing-3, 8px)",
1329
+ margin: "var(--of-spacing-1, 4px) 0"
1330
+ };
1331
+ var orLineStyle = {
1332
+ flex: 1,
1333
+ height: "1px",
1334
+ backgroundColor: "var(--of-color-border-secondary, #E5E7EB)"
1335
+ };
1336
+ var orTextStyle = {
1337
+ fontSize: "var(--of-font-size-xs, 11px)",
1338
+ fontWeight: "var(--of-font-weight-semibold, 600)",
1339
+ color: "var(--of-color-text-muted, #9CA3AF)",
1340
+ textTransform: "uppercase",
1341
+ letterSpacing: "0.5px"
1342
+ };
1343
+ var addGroupButtonStyle = {
1344
+ padding: "var(--of-spacing-3, 8px) var(--of-spacing-4, 10px)",
1345
+ border: "1px dashed var(--of-color-border-primary, #D1D5DB)",
1346
+ background: "none",
1347
+ color: "var(--of-color-text-tertiary, #6B7280)",
1348
+ cursor: "pointer",
1349
+ fontSize: "var(--of-font-size-sm, 12px)",
1350
+ borderRadius: "var(--of-radius-md, 6px)",
1351
+ marginTop: "var(--of-spacing-2, 6px)"
1352
+ };
1353
+ function normalize(value) {
1354
+ if (!value || !Array.isArray(value.groups) || value.groups.length === 0) {
1355
+ return { groups: [{ operator: "all", conditions: [] }] };
1356
+ }
1357
+ return value;
1358
+ }
1359
+ function ConditionBuilder({
1360
+ value,
1361
+ onChange,
1362
+ properties = [],
1363
+ operators = defaultOperators
1364
+ }) {
1365
+ const t = useTranslation();
1366
+ const current = normalize(value);
1367
+ const emit = (groups) => {
1368
+ onChange({ groups: groups.length > 0 ? groups : [{ operator: "all", conditions: [] }] });
1369
+ };
1370
+ const handleGroupChange = (index, group) => {
1371
+ const groups = [...current.groups];
1372
+ groups[index] = group;
1373
+ emit(groups);
1374
+ };
1375
+ const handleGroupRemove = (index) => {
1376
+ emit(current.groups.filter((_, i) => i !== index));
1377
+ };
1378
+ const handleAddGroup = () => {
1379
+ emit([
1380
+ ...current.groups,
1381
+ { operator: "all", conditions: [{ fact: "", operator: "equal", value: "" }] }
1382
+ ]);
1383
+ };
1384
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: containerStyle3, children: [
1385
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: topLabelStyle, children: t("conditionBuilder.topLevelLabel") }),
1386
+ current.groups.map((group, index) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react6.default.Fragment, { children: [
1387
+ index > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: orDividerStyle, children: [
1388
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: orLineStyle }),
1389
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: orTextStyle, children: "OR" }),
1390
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: orLineStyle })
1391
+ ] }),
1392
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1393
+ ConditionGroupView,
1394
+ {
1395
+ group,
1396
+ properties,
1397
+ operators,
1398
+ onChange: (updated) => handleGroupChange(index, updated),
1399
+ onRemove: () => handleGroupRemove(index),
1400
+ canRemove: current.groups.length > 1
1401
+ }
1402
+ )
1403
+ ] }, index)),
1404
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1405
+ "button",
1406
+ {
1407
+ type: "button",
1408
+ onClick: handleAddGroup,
1409
+ style: addGroupButtonStyle,
1410
+ onMouseEnter: (e) => {
1411
+ e.currentTarget.style.borderColor = "var(--of-color-interactive-primary, #3B82F6)";
1412
+ e.currentTarget.style.color = "var(--of-color-interactive-primary, #3B82F6)";
1413
+ },
1414
+ onMouseLeave: (e) => {
1415
+ e.currentTarget.style.borderColor = "var(--of-color-border-primary, #D1D5DB)";
1416
+ e.currentTarget.style.color = "var(--of-color-text-tertiary, #6B7280)";
1417
+ },
1418
+ children: [
1419
+ "+ ",
1420
+ t("conditionBuilder.addGroup")
1421
+ ]
1422
+ }
1423
+ )
1424
+ ] });
1425
+ }
1426
+
1427
+ // src/primitives/condition-builder/ConditionBuilderDialog.tsx
1428
+ var import_react7 = require("react");
1429
+ var import_react_dom = require("react-dom");
1430
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1431
+ var backdropStyle = {
1432
+ position: "fixed",
1433
+ inset: 0,
1434
+ backgroundColor: "rgba(0, 0, 0, 0.4)",
1435
+ display: "flex",
1436
+ alignItems: "center",
1437
+ justifyContent: "center",
1438
+ zIndex: 1e4,
1439
+ animation: "of-dialog-fade-in 0.15s ease-out"
1440
+ };
1441
+ var dialogStyle = {
1442
+ backgroundColor: "var(--of-panel-bg, #fff)",
1443
+ borderRadius: "var(--of-radius-lg, 8px)",
1444
+ boxShadow: "0 20px 60px rgba(0, 0, 0, 0.2)",
1445
+ width: "min(680px, calc(100vw - 48px))",
1446
+ maxHeight: "calc(100vh - 48px)",
1447
+ display: "flex",
1448
+ flexDirection: "column",
1449
+ overflow: "hidden",
1450
+ animation: "of-dialog-slide-in 0.15s ease-out"
1451
+ };
1452
+ var headerStyle3 = {
1453
+ display: "flex",
1454
+ alignItems: "center",
1455
+ justifyContent: "space-between",
1456
+ padding: "var(--of-spacing-6, 16px) var(--of-spacing-7, 20px)",
1457
+ borderBottom: "1px solid var(--of-color-border-secondary, #E5E7EB)",
1458
+ flexShrink: 0
1459
+ };
1460
+ var titleStyle = {
1461
+ fontSize: "var(--of-font-size-lg, 14px)",
1462
+ fontWeight: "var(--of-font-weight-semibold, 600)",
1463
+ color: "var(--of-color-text-primary, #111827)"
1464
+ };
1465
+ var closeButtonStyle = {
1466
+ padding: "var(--of-spacing-1, 4px) var(--of-spacing-2, 6px)",
1467
+ border: "none",
1468
+ background: "none",
1469
+ color: "var(--of-color-text-muted, #9CA3AF)",
1470
+ cursor: "pointer",
1471
+ fontSize: "18px",
1472
+ lineHeight: 1,
1473
+ borderRadius: "var(--of-radius-sm, 4px)"
1474
+ };
1475
+ var bodyStyle = {
1476
+ padding: "var(--of-spacing-7, 20px)",
1477
+ overflowY: "auto",
1478
+ flex: 1
1479
+ };
1480
+ var footerStyle = {
1481
+ display: "flex",
1482
+ justifyContent: "flex-end",
1483
+ gap: "var(--of-spacing-3, 8px)",
1484
+ padding: "var(--of-spacing-6, 16px) var(--of-spacing-7, 20px)",
1485
+ borderTop: "1px solid var(--of-color-border-secondary, #E5E7EB)",
1486
+ flexShrink: 0
1487
+ };
1488
+ var cancelButtonStyle = {
1489
+ padding: "var(--of-button-padding, 8px 16px)",
1490
+ border: "1px solid var(--of-color-border-primary, #D1D5DB)",
1491
+ borderRadius: "var(--of-button-radius, 6px)",
1492
+ backgroundColor: "var(--of-field-bg, #fff)",
1493
+ color: "var(--of-color-text-secondary, #374151)",
1494
+ cursor: "pointer",
1495
+ fontSize: "var(--of-font-size-sm, 12px)",
1496
+ fontWeight: "var(--of-font-weight-medium, 500)"
1497
+ };
1498
+ var applyButtonStyle = {
1499
+ padding: "var(--of-button-padding, 8px 16px)",
1500
+ border: "none",
1501
+ borderRadius: "var(--of-button-radius, 6px)",
1502
+ backgroundColor: "var(--of-button-primary-bg, #3B82F6)",
1503
+ color: "var(--of-button-primary-color, #fff)",
1504
+ cursor: "pointer",
1505
+ fontSize: "var(--of-font-size-sm, 12px)",
1506
+ fontWeight: "var(--of-font-weight-medium, 500)"
1507
+ };
1508
+ var stylesInjected = false;
1509
+ function injectStyles() {
1510
+ if (stylesInjected || typeof document === "undefined") return;
1511
+ stylesInjected = true;
1512
+ const style = document.createElement("style");
1513
+ style.textContent = `
1514
+ @keyframes of-dialog-fade-in {
1515
+ from { opacity: 0; }
1516
+ to { opacity: 1; }
1517
+ }
1518
+ @keyframes of-dialog-slide-in {
1519
+ from { opacity: 0; transform: translateY(-8px) scale(0.98); }
1520
+ to { opacity: 1; transform: translateY(0) scale(1); }
1521
+ }
1522
+ `;
1523
+ document.head.appendChild(style);
1524
+ }
1525
+ function ConditionBuilderDialog({
1526
+ open,
1527
+ onClose,
1528
+ value,
1529
+ onChange,
1530
+ properties,
1531
+ operators
1532
+ }) {
1533
+ const t = useTranslation();
1534
+ const [draft, setDraft] = (0, import_react7.useState)(value);
1535
+ const backdropRef = (0, import_react7.useRef)(null);
1536
+ (0, import_react7.useEffect)(() => {
1537
+ if (open) {
1538
+ setDraft(value);
1539
+ injectStyles();
1540
+ }
1541
+ }, [open, value]);
1542
+ (0, import_react7.useEffect)(() => {
1543
+ if (!open) return;
1544
+ const handleKeyDown = (e) => {
1545
+ if (e.key === "Escape") onClose();
1546
+ };
1547
+ document.addEventListener("keydown", handleKeyDown);
1548
+ return () => document.removeEventListener("keydown", handleKeyDown);
1549
+ }, [open, onClose]);
1550
+ const handleDraftChange = (0, import_react7.useCallback)((next) => {
1551
+ setDraft(next);
1552
+ }, []);
1553
+ const handleApply = () => {
1554
+ onChange(draft);
1555
+ onClose();
1556
+ };
1557
+ const handleBackdropClick = (e) => {
1558
+ if (e.target === backdropRef.current) {
1559
+ onClose();
1560
+ }
1561
+ };
1562
+ if (!open) return null;
1563
+ return (0, import_react_dom.createPortal)(
1564
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1565
+ "div",
1566
+ {
1567
+ ref: backdropRef,
1568
+ style: backdropStyle,
1569
+ onClick: handleBackdropClick,
1570
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: dialogStyle, role: "dialog", "aria-modal": "true", children: [
1571
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: headerStyle3, children: [
1572
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: titleStyle, children: t("conditionBuilder.dialogTitle") }),
1573
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1574
+ "button",
1575
+ {
1576
+ type: "button",
1577
+ onClick: onClose,
1578
+ style: closeButtonStyle,
1579
+ onMouseEnter: (e) => e.currentTarget.style.color = "var(--of-color-text-primary, #111827)",
1580
+ onMouseLeave: (e) => e.currentTarget.style.color = "var(--of-color-text-muted, #9CA3AF)",
1581
+ children: "\u2715"
1582
+ }
1583
+ )
1584
+ ] }),
1585
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: bodyStyle, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1586
+ ConditionBuilder,
1587
+ {
1588
+ value: draft,
1589
+ onChange: handleDraftChange,
1590
+ properties,
1591
+ operators
1592
+ }
1593
+ ) }),
1594
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: footerStyle, children: [
1595
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1596
+ "button",
1597
+ {
1598
+ type: "button",
1599
+ onClick: onClose,
1600
+ style: cancelButtonStyle,
1601
+ onMouseEnter: (e) => e.currentTarget.style.backgroundColor = "var(--of-color-bg-tertiary, #F3F4F6)",
1602
+ onMouseLeave: (e) => e.currentTarget.style.backgroundColor = "var(--of-field-bg, #fff)",
1603
+ children: t("conditionBuilder.cancel")
1604
+ }
1605
+ ),
1606
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1607
+ "button",
1608
+ {
1609
+ type: "button",
1610
+ onClick: handleApply,
1611
+ style: applyButtonStyle,
1612
+ onMouseEnter: (e) => e.currentTarget.style.backgroundColor = "var(--of-button-primary-bg-hover, #2563EB)",
1613
+ onMouseLeave: (e) => e.currentTarget.style.backgroundColor = "var(--of-button-primary-bg, #3B82F6)",
1614
+ children: t("conditionBuilder.apply")
1615
+ }
1616
+ )
1617
+ ] })
1618
+ ] })
1619
+ }
1620
+ ),
1621
+ document.body
1622
+ );
1623
+ }
1624
+
1625
+ // src/nodes/details/TriggerNodeDetail.tsx
1626
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1627
+ function TriggerNodeDetail({ node, onChange }) {
1628
+ const t = useTranslation();
1629
+ const data = node.data;
1630
+ const handleEventChange = (event) => {
1631
+ onChange({
1632
+ ...data,
1633
+ params: { ...data.params, event }
1634
+ });
1635
+ };
1636
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FieldGroup, { label: t("nodeDetails.trigger.group"), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1637
+ TextField,
1638
+ {
1639
+ label: t("nodeDetails.trigger.eventLabel"),
1640
+ value: data.params?.event ?? "",
1641
+ onChange: handleEventChange,
1642
+ placeholder: t("nodeDetails.trigger.eventPlaceholder"),
1643
+ hint: t("nodeDetails.trigger.eventHint")
1644
+ }
1645
+ ) });
1646
+ }
1647
+
1648
+ // src/nodes/details/ActionNodeDetail.tsx
1649
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1650
+ function ActionNodeDetail({ node, onChange }) {
1651
+ const t = useTranslation();
1652
+ const data = node.data;
1653
+ const handleActionChange = (action) => {
1654
+ onChange({
1655
+ ...data,
1656
+ action
1657
+ });
1658
+ };
1659
+ const handleParamsChange = (params) => {
1660
+ onChange({
1661
+ ...data,
1662
+ params
1663
+ });
1664
+ };
1665
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(FieldGroup, { label: t("nodeDetails.action.group"), children: [
1666
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1667
+ TextField,
1668
+ {
1669
+ label: t("nodeDetails.action.nameLabel"),
1670
+ value: data.action ?? "",
1671
+ onChange: handleActionChange,
1672
+ placeholder: t("nodeDetails.action.namePlaceholder"),
1673
+ hint: t("nodeDetails.action.nameHint")
1674
+ }
1675
+ ),
1676
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1677
+ JsonField,
1678
+ {
1679
+ label: t("nodeDetails.action.paramsLabel"),
1680
+ value: data.params ?? {},
1681
+ onChange: handleParamsChange,
1682
+ hint: t("nodeDetails.action.paramsHint"),
1683
+ rows: 4
1684
+ }
1685
+ )
1686
+ ] });
1687
+ }
1688
+
1689
+ // src/nodes/details/ConditionNodeDetail.tsx
1690
+ var import_react8 = require("react");
1691
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1692
+ var EMPTY_CONDITIONS = {
1693
+ groups: [{ operator: "all", conditions: [] }]
1694
+ };
1695
+ function countRules(conditions) {
1696
+ if (!conditions || !Array.isArray(conditions.groups)) return 0;
1697
+ return conditions.groups.reduce(
1698
+ (sum, group) => sum + (group.conditions?.length ?? 0),
1699
+ 0
1700
+ );
1701
+ }
1702
+ var openButtonStyle = {
1703
+ width: "100%",
1704
+ padding: "var(--of-spacing-3, 8px) var(--of-spacing-4, 10px)",
1705
+ border: "1px solid var(--of-color-border-primary, #D1D5DB)",
1706
+ borderRadius: "var(--of-field-radius, 6px)",
1707
+ backgroundColor: "var(--of-field-bg, #fff)",
1708
+ color: "var(--of-color-text-primary, #111827)",
1709
+ cursor: "pointer",
1710
+ fontSize: "var(--of-field-font-size, 13px)",
1711
+ textAlign: "left",
1712
+ display: "flex",
1713
+ alignItems: "center",
1714
+ justifyContent: "space-between"
1715
+ };
1716
+ var ruleCountStyle = {
1717
+ fontSize: "var(--of-font-size-xs, 11px)",
1718
+ color: "var(--of-color-text-muted, #9CA3AF)"
1719
+ };
1720
+ function ConditionNodeDetail({
1721
+ node,
1722
+ onChange,
1723
+ conditionProperties,
1724
+ conditionOperators
1725
+ }) {
1726
+ const t = useTranslation();
1727
+ const data = node.data;
1728
+ const [dialogOpen, setDialogOpen] = (0, import_react8.useState)(false);
1729
+ const conditions = data.conditions ?? EMPTY_CONDITIONS;
1730
+ const ruleCount = countRules(conditions);
1731
+ const handleConditionsChange = (updated) => {
1732
+ onChange({
1733
+ ...data,
1734
+ conditions: updated
1735
+ });
1736
+ };
1737
+ const ruleLabel = ruleCount === 0 ? t("conditionBuilder.noConditions") : ruleCount === 1 ? t("conditionBuilder.ruleCountSingular") : t("conditionBuilder.ruleCount", { count: String(ruleCount) });
1738
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(FieldGroup, { label: t("nodeDetails.condition.group"), children: [
1739
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { marginBottom: "var(--of-spacing-5, 12px)" }, children: [
1740
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1741
+ "label",
1742
+ {
1743
+ style: {
1744
+ display: "block",
1745
+ fontSize: "var(--of-field-label-size, 12px)",
1746
+ fontWeight: "var(--of-font-weight-medium, 500)",
1747
+ color: "var(--of-field-label-color, #374151)",
1748
+ marginBottom: "var(--of-spacing-1, 4px)"
1749
+ },
1750
+ children: t("nodeDetails.condition.conditionsLabel")
1751
+ }
1752
+ ),
1753
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
1754
+ "button",
1755
+ {
1756
+ type: "button",
1757
+ onClick: () => setDialogOpen(true),
1758
+ style: openButtonStyle,
1759
+ onMouseEnter: (e) => e.currentTarget.style.borderColor = "var(--of-field-border-focus, #3B82F6)",
1760
+ onMouseLeave: (e) => e.currentTarget.style.borderColor = "var(--of-color-border-primary, #D1D5DB)",
1761
+ children: [
1762
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: ruleLabel }),
1763
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: ruleCountStyle, children: t("conditionBuilder.editButton") })
1764
+ ]
1765
+ }
1766
+ )
1767
+ ] }),
1768
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1769
+ ConditionBuilderDialog,
1770
+ {
1771
+ open: dialogOpen,
1772
+ onClose: () => setDialogOpen(false),
1773
+ value: conditions,
1774
+ onChange: handleConditionsChange,
1775
+ properties: conditionProperties,
1776
+ operators: conditionOperators
1777
+ }
1778
+ )
1779
+ ] });
1780
+ }
1781
+
1782
+ // src/nodes/details/ExitNodeDetail.tsx
1783
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1784
+ function ExitNodeDetail(_props) {
1785
+ const t = useTranslation();
1786
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(FieldGroup, { label: t("nodeDetails.exit.group"), children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { style: { fontSize: "13px", color: "#6B7280", margin: 0 }, children: t("nodeDetails.exit.message") }) });
1787
+ }
1788
+
1789
+ // src/nodes/details/WaitNodeDetail.tsx
1790
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1791
+ function WaitNodeDetail({ node, onChange }) {
1792
+ const t = useTranslation();
1793
+ const data = node.data;
1794
+ const handleDurationChange = (duration) => {
1795
+ onChange({
1796
+ ...data,
1797
+ params: { ...data.params, duration }
1798
+ });
1799
+ };
1800
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(FieldGroup, { label: t("nodeDetails.wait.group"), children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1801
+ DurationField,
1802
+ {
1803
+ label: t("nodeDetails.wait.durationLabel"),
1804
+ value: data.params?.duration ?? 6e4,
1805
+ onChange: handleDurationChange,
1806
+ hint: t("nodeDetails.wait.durationHint")
1807
+ }
1808
+ ) });
1809
+ }
1810
+
1811
+ // src/nodes/details/TriggerOrTimeoutNodeDetail.tsx
1812
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1813
+ function TriggerOrTimeoutNodeDetail({ node, onChange }) {
1814
+ const t = useTranslation();
1815
+ const data = node.data;
1816
+ const handleEventChange = (event) => {
1817
+ onChange({
1818
+ ...data,
1819
+ params: { ...data.params, event }
1820
+ });
1821
+ };
1822
+ const handleDurationChange = (duration) => {
1823
+ onChange({
1824
+ ...data,
1825
+ params: { ...data.params, duration }
1826
+ });
1827
+ };
1828
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(FieldGroup, { label: t("nodeDetails.triggerOrTimeout.group"), children: [
1829
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1830
+ TextField,
1831
+ {
1832
+ label: t("nodeDetails.triggerOrTimeout.eventLabel"),
1833
+ value: data.params?.event ?? "",
1834
+ onChange: handleEventChange,
1835
+ placeholder: t("nodeDetails.triggerOrTimeout.eventPlaceholder"),
1836
+ hint: t("nodeDetails.triggerOrTimeout.eventHint")
1837
+ }
1838
+ ),
1839
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1840
+ DurationField,
1841
+ {
1842
+ label: t("nodeDetails.triggerOrTimeout.durationLabel"),
1843
+ value: data.params?.duration ?? 6e4,
1844
+ onChange: handleDurationChange,
1845
+ hint: t("nodeDetails.triggerOrTimeout.durationHint")
1846
+ }
1847
+ )
1848
+ ] });
1849
+ }
1850
+
1851
+ // src/nodes/icons/index.tsx
1852
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1853
+ function TriggerIcon({ size = 24 }) {
1854
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1855
+ "svg",
1856
+ {
1857
+ width: size,
1858
+ height: size,
1859
+ viewBox: "0 0 24 24",
1860
+ fill: "none",
1861
+ stroke: "#4CAF50",
1862
+ strokeWidth: "2",
1863
+ strokeLinecap: "round",
1864
+ strokeLinejoin: "round",
1865
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("polygon", { points: "13 2 3 14 12 14 11 22 21 10 12 10 13 2" })
1866
+ }
1867
+ );
1868
+ }
1869
+ function ActionIcon({ size = 24 }) {
1870
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1871
+ "svg",
1872
+ {
1873
+ width: size,
1874
+ height: size,
1875
+ viewBox: "0 0 24 24",
1876
+ fill: "none",
1877
+ stroke: "#2196F3",
1878
+ strokeWidth: "2",
1879
+ strokeLinecap: "round",
1880
+ strokeLinejoin: "round",
1881
+ children: [
1882
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1883
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("polygon", { points: "10 8 16 12 10 16 10 8", fill: "#2196F3" })
1884
+ ]
1885
+ }
1886
+ );
1887
+ }
1888
+ function ConditionIcon({ size = 24 }) {
1889
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1890
+ "svg",
1891
+ {
1892
+ width: size,
1893
+ height: size,
1894
+ viewBox: "0 0 24 24",
1895
+ fill: "none",
1896
+ stroke: "#FF9800",
1897
+ strokeWidth: "2",
1898
+ strokeLinecap: "round",
1899
+ strokeLinejoin: "round",
1900
+ children: [
1901
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M12 3L21 12L12 21L3 12L12 3Z" }),
1902
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M9 12L11 14L15 10" })
1903
+ ]
1904
+ }
1905
+ );
1906
+ }
1907
+ function WaitIcon({ size = 24 }) {
1908
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1909
+ "svg",
1910
+ {
1911
+ width: size,
1912
+ height: size,
1913
+ viewBox: "0 0 24 24",
1914
+ fill: "none",
1915
+ stroke: "#9C27B0",
1916
+ strokeWidth: "2",
1917
+ strokeLinecap: "round",
1918
+ strokeLinejoin: "round",
1919
+ children: [
1920
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1921
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("polyline", { points: "12 6 12 12 16 14" })
1922
+ ]
1923
+ }
1924
+ );
1925
+ }
1926
+ function TriggerOrTimeoutIcon({ size = 24 }) {
1927
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1928
+ "svg",
1929
+ {
1930
+ width: size,
1931
+ height: size,
1932
+ viewBox: "0 0 24 24",
1933
+ fill: "none",
1934
+ stroke: "#607D8B",
1935
+ strokeWidth: "2",
1936
+ strokeLinecap: "round",
1937
+ strokeLinejoin: "round",
1938
+ children: [
1939
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1940
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("polyline", { points: "12 6 12 12 16 14" }),
1941
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M17 2L19 6L15 6L17 2", fill: "#607D8B" })
1942
+ ]
1943
+ }
1944
+ );
1945
+ }
1946
+ function ExitIcon({ size = 24 }) {
1947
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1948
+ "svg",
1949
+ {
1950
+ width: size,
1951
+ height: size,
1952
+ viewBox: "0 0 24 24",
1953
+ fill: "none",
1954
+ stroke: "#F44336",
1955
+ strokeWidth: "2",
1956
+ strokeLinecap: "round",
1957
+ strokeLinejoin: "round",
1958
+ children: [
1959
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1960
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("rect", { x: "8", y: "8", width: "8", height: "8", fill: "#F44336" })
1961
+ ]
1962
+ }
1963
+ );
1964
+ }
1965
+
1966
+ // src/nodes/index.ts
1967
+ var defaultNodeTypes = [
1968
+ {
1969
+ type: "Trigger",
1970
+ label: "Trigger",
1971
+ description: "Starts the workflow when a specific event occurs",
1972
+ Icon: TriggerIcon,
1973
+ defaultData: { params: { event: "" } },
1974
+ ViewComponent: TriggerNodeView,
1975
+ DetailComponent: TriggerNodeDetail
1976
+ },
1977
+ {
1978
+ type: "Action",
1979
+ label: "Action",
1980
+ description: "Performs an action and continues to the next node",
1981
+ Icon: ActionIcon,
1982
+ defaultData: { action: "", params: {} },
1983
+ ViewComponent: ActionNodeView,
1984
+ DetailComponent: ActionNodeDetail
1985
+ },
1986
+ {
1987
+ type: "Condition",
1988
+ label: "Condition",
1989
+ description: "Branches the workflow based on conditions",
1990
+ Icon: ConditionIcon,
1991
+ defaultData: { conditions: { all: [] } },
1992
+ ViewComponent: ConditionNodeView,
1993
+ DetailComponent: ConditionNodeDetail
1994
+ },
1995
+ {
1996
+ type: "Wait",
1997
+ label: "Wait",
1998
+ description: "Pauses the workflow for a specified duration",
1999
+ Icon: WaitIcon,
2000
+ defaultData: { params: { duration: 6e4 } },
2001
+ ViewComponent: WaitNodeView,
2002
+ DetailComponent: WaitNodeDetail
2003
+ },
2004
+ {
2005
+ type: "TriggerOrTimeout",
2006
+ label: "Trigger or Timeout",
2007
+ description: "Waits for an event or times out after a duration",
2008
+ Icon: TriggerOrTimeoutIcon,
2009
+ defaultData: { params: { event: "", duration: 6e4 } },
2010
+ ViewComponent: TriggerOrTimeoutNodeView,
2011
+ DetailComponent: TriggerOrTimeoutNodeDetail
2012
+ },
2013
+ {
2014
+ type: "Exit",
2015
+ label: "Exit",
2016
+ description: "Ends the workflow",
2017
+ Icon: ExitIcon,
2018
+ defaultData: {},
2019
+ ViewComponent: ExitNodeView,
2020
+ DetailComponent: ExitNodeDetail
2021
+ }
2022
+ ];
2023
+ function mergeNodeTypes(base, overrides) {
2024
+ const map = new Map(base.map((t) => [t.type, t]));
2025
+ for (const override of overrides) {
2026
+ map.set(override.type, override);
2027
+ }
2028
+ return [...map.values()];
2029
+ }
2030
+
2031
+ // src/context/WorkflowEditorContext.tsx
2032
+ var import_jsx_runtime29 = require("react/jsx-runtime");
2033
+ function findSelectedNodeId(nodes) {
2034
+ return nodes.find((n) => n.selected)?.id ?? null;
2035
+ }
2036
+ function createInitialState(workflow, nodeTypes) {
2037
+ const nodeTypesMap = /* @__PURE__ */ new Map();
2038
+ const typesToUse = nodeTypes ?? defaultNodeTypes;
2039
+ typesToUse.forEach((def) => nodeTypesMap.set(def.type, def));
2040
+ if (workflow) {
2041
+ return {
2042
+ workflow,
2043
+ nodes: workflow.flow.nodes,
2044
+ edges: workflow.flow.edges,
2045
+ options: workflow.options,
2046
+ name: workflow.name,
2047
+ selectedNodeId: findSelectedNodeId(workflow.flow.nodes),
2048
+ isDirty: false,
2049
+ nodeTypes: nodeTypesMap
2050
+ };
2051
+ }
2052
+ return {
2053
+ workflow: null,
2054
+ nodes: [],
2055
+ edges: [],
2056
+ options: { frequency: { type: "one_time" } },
2057
+ name: "",
2058
+ selectedNodeId: null,
2059
+ isDirty: false,
2060
+ nodeTypes: nodeTypesMap
2061
+ };
2062
+ }
2063
+ function reducer(state, action) {
2064
+ switch (action.type) {
2065
+ case "LOAD_WORKFLOW": {
2066
+ const workflow = action.payload;
2067
+ return {
2068
+ ...state,
2069
+ workflow,
2070
+ nodes: workflow.flow.nodes,
2071
+ edges: workflow.flow.edges,
2072
+ options: workflow.options,
2073
+ name: workflow.name,
2074
+ selectedNodeId: findSelectedNodeId(workflow.flow.nodes),
2075
+ isDirty: false
2076
+ };
2077
+ }
2078
+ case "RESET_WORKFLOW": {
2079
+ if (state.workflow) {
2080
+ return {
2081
+ ...state,
2082
+ nodes: state.workflow.flow.nodes,
2083
+ edges: state.workflow.flow.edges,
2084
+ options: state.workflow.options,
2085
+ name: state.workflow.name,
2086
+ selectedNodeId: findSelectedNodeId(state.workflow.flow.nodes),
2087
+ isDirty: false
2088
+ };
2089
+ }
2090
+ return {
2091
+ ...state,
2092
+ nodes: [],
2093
+ edges: [],
2094
+ options: { frequency: { type: "one_time" } },
2095
+ name: "",
2096
+ selectedNodeId: null,
2097
+ isDirty: false
2098
+ };
2099
+ }
2100
+ case "SET_NODES":
2101
+ return { ...state, nodes: action.payload, isDirty: true };
2102
+ case "SET_EDGES":
2103
+ return { ...state, edges: action.payload, isDirty: true };
2104
+ case "ADD_NODE":
2105
+ return {
2106
+ ...state,
2107
+ nodes: [...state.nodes, action.payload],
2108
+ isDirty: true
2109
+ };
2110
+ case "UPDATE_NODE": {
2111
+ const { nodeId, data } = action.payload;
2112
+ return {
2113
+ ...state,
2114
+ nodes: state.nodes.map(
2115
+ (node) => node.id === nodeId ? { ...node, data: { ...node.data, ...data } } : node
2116
+ ),
2117
+ isDirty: true
2118
+ };
2119
+ }
2120
+ case "UPDATE_NODE_POSITION": {
2121
+ const { nodeId, position } = action.payload;
2122
+ return {
2123
+ ...state,
2124
+ nodes: state.nodes.map(
2125
+ (node) => node.id === nodeId ? { ...node, position } : node
2126
+ ),
2127
+ isDirty: true
2128
+ };
2129
+ }
2130
+ case "REMOVE_NODE": {
2131
+ const nodeId = action.payload;
2132
+ return {
2133
+ ...state,
2134
+ nodes: state.nodes.filter((node) => node.id !== nodeId),
2135
+ edges: state.edges.filter(
2136
+ (edge) => edge.source !== nodeId && edge.target !== nodeId
2137
+ ),
2138
+ selectedNodeId: state.selectedNodeId === nodeId ? null : state.selectedNodeId,
2139
+ isDirty: true
2140
+ };
2141
+ }
2142
+ case "SELECT_NODE":
2143
+ return { ...state, selectedNodeId: action.payload };
2144
+ case "ADD_EDGE":
2145
+ return {
2146
+ ...state,
2147
+ edges: [...state.edges, action.payload],
2148
+ isDirty: true
2149
+ };
2150
+ case "REMOVE_EDGE":
2151
+ return {
2152
+ ...state,
2153
+ edges: state.edges.filter((edge) => edge.id !== action.payload),
2154
+ isDirty: true
2155
+ };
2156
+ case "SET_NAME":
2157
+ return { ...state, name: action.payload, isDirty: true };
2158
+ case "SET_OPTIONS":
2159
+ return { ...state, options: action.payload, isDirty: true };
2160
+ case "REGISTER_NODE_TYPE": {
2161
+ const newNodeTypes = new Map(state.nodeTypes);
2162
+ newNodeTypes.set(action.payload.type, action.payload);
2163
+ return { ...state, nodeTypes: newNodeTypes };
2164
+ }
2165
+ case "MARK_CLEAN":
2166
+ return { ...state, isDirty: false };
2167
+ case "APPLY_NODE_CHANGES": {
2168
+ const newNodes = (0, import_react10.applyNodeChanges)(action.payload, state.nodes);
2169
+ let newSelectedId = state.selectedNodeId;
2170
+ for (const change of action.payload) {
2171
+ if (change.type === "select" && change.selected) {
2172
+ newSelectedId = change.id;
2173
+ }
2174
+ }
2175
+ for (const change of action.payload) {
2176
+ if (change.type === "select" && !change.selected && change.id === newSelectedId) {
2177
+ newSelectedId = null;
2178
+ } else if (change.type === "remove" && change.id === newSelectedId) {
2179
+ newSelectedId = null;
2180
+ }
2181
+ }
2182
+ const hasDirtyChange = action.payload.some(
2183
+ (c) => c.type === "position" || c.type === "dimensions" || c.type === "remove"
2184
+ );
2185
+ return {
2186
+ ...state,
2187
+ nodes: newNodes,
2188
+ selectedNodeId: newSelectedId,
2189
+ isDirty: state.isDirty || hasDirtyChange
2190
+ };
2191
+ }
2192
+ case "APPLY_EDGE_CHANGES": {
2193
+ const newEdges = (0, import_react10.applyEdgeChanges)(action.payload, state.edges);
2194
+ const hasDirtyChange = action.payload.some((c) => c.type === "remove");
2195
+ return {
2196
+ ...state,
2197
+ edges: newEdges,
2198
+ isDirty: state.isDirty || hasDirtyChange
2199
+ };
2200
+ }
2201
+ default:
2202
+ return state;
2203
+ }
2204
+ }
2205
+ var WorkflowEditorContext = (0, import_react9.createContext)(null);
2206
+ var nodeIdCounter = 0;
2207
+ function generateNodeId(type) {
2208
+ nodeIdCounter++;
2209
+ return `${type.toLowerCase()}-${Date.now()}-${nodeIdCounter}`;
2210
+ }
2211
+ function generateEdgeId(source, target) {
2212
+ return `edge-${source}-${target}-${Date.now()}`;
2213
+ }
2214
+ function WorkflowEditorProvider({
2215
+ children,
2216
+ workflow,
2217
+ nodeTypes,
2218
+ onWorkflowChange,
2219
+ onDirtyChange
2220
+ }) {
2221
+ const [state, dispatch] = (0, import_react9.useReducer)(
2222
+ reducer,
2223
+ { workflow, nodeTypes },
2224
+ ({ workflow: workflow2, nodeTypes: nodeTypes2 }) => createInitialState(workflow2, nodeTypes2)
2225
+ );
2226
+ (0, import_react9.useEffect)(() => {
2227
+ if (workflow) {
2228
+ dispatch({ type: "LOAD_WORKFLOW", payload: workflow });
2229
+ }
2230
+ }, [workflow]);
2231
+ (0, import_react9.useEffect)(() => {
2232
+ onDirtyChange?.(state.isDirty);
2233
+ }, [state.isDirty, onDirtyChange]);
2234
+ (0, import_react9.useEffect)(() => {
2235
+ if (state.isDirty && onWorkflowChange) {
2236
+ const currentWorkflow = getWorkflowImpl();
2237
+ onWorkflowChange(currentWorkflow);
2238
+ }
2239
+ }, [state.nodes, state.edges, state.options, state.name]);
2240
+ const getWorkflowImpl = (0, import_react9.useCallback)(() => {
2241
+ return {
2242
+ id: state.workflow?.id ?? "",
2243
+ name: state.name,
2244
+ flow: {
2245
+ nodes: state.nodes,
2246
+ edges: state.edges
2247
+ },
2248
+ options: state.options
2249
+ };
2250
+ }, [state.workflow?.id, state.name, state.nodes, state.edges, state.options]);
2251
+ const loadWorkflow = (0, import_react9.useCallback)((workflow2) => {
2252
+ dispatch({ type: "LOAD_WORKFLOW", payload: workflow2 });
2253
+ }, []);
2254
+ const resetWorkflow = (0, import_react9.useCallback)(() => {
2255
+ dispatch({ type: "RESET_WORKFLOW" });
2256
+ }, []);
2257
+ const addNode = (0, import_react9.useCallback)(
2258
+ (type, position) => {
2259
+ const nodeTypeDef = state.nodeTypes.get(type);
2260
+ if (!nodeTypeDef) {
2261
+ console.warn(`Unknown node type: ${type}`);
2262
+ return;
2263
+ }
2264
+ const newNode = {
2265
+ id: generateNodeId(type),
2266
+ type,
2267
+ position,
2268
+ data: { ...nodeTypeDef.defaultData }
2269
+ };
2270
+ dispatch({ type: "ADD_NODE", payload: newNode });
2271
+ },
2272
+ [state.nodeTypes]
2273
+ );
2274
+ const updateNode = (0, import_react9.useCallback)(
2275
+ (nodeId, data) => {
2276
+ dispatch({ type: "UPDATE_NODE", payload: { nodeId, data } });
2277
+ },
2278
+ []
2279
+ );
2280
+ const updateNodePosition = (0, import_react9.useCallback)(
2281
+ (nodeId, position) => {
2282
+ dispatch({ type: "UPDATE_NODE_POSITION", payload: { nodeId, position } });
2283
+ },
2284
+ []
2285
+ );
2286
+ const removeNode = (0, import_react9.useCallback)((nodeId) => {
2287
+ dispatch({ type: "REMOVE_NODE", payload: nodeId });
2288
+ }, []);
2289
+ const selectNode = (0, import_react9.useCallback)((nodeId) => {
2290
+ dispatch({ type: "SELECT_NODE", payload: nodeId });
2291
+ }, []);
2292
+ const addEdge = (0, import_react9.useCallback)(
2293
+ (connection) => {
2294
+ const newEdge = {
2295
+ id: generateEdgeId(connection.source, connection.target),
2296
+ source: connection.source,
2297
+ target: connection.target,
2298
+ sourceHandle: connection.sourceHandle,
2299
+ targetHandle: connection.targetHandle
2300
+ };
2301
+ dispatch({ type: "ADD_EDGE", payload: newEdge });
2302
+ },
2303
+ []
2304
+ );
2305
+ const removeEdge = (0, import_react9.useCallback)((edgeId) => {
2306
+ dispatch({ type: "REMOVE_EDGE", payload: edgeId });
2307
+ }, []);
2308
+ const setName = (0, import_react9.useCallback)((name) => {
2309
+ dispatch({ type: "SET_NAME", payload: name });
2310
+ }, []);
2311
+ const setOptions = (0, import_react9.useCallback)((options) => {
2312
+ dispatch({ type: "SET_OPTIONS", payload: options });
2313
+ }, []);
2314
+ const registerNodeType = (0, import_react9.useCallback)((definition) => {
2315
+ dispatch({ type: "REGISTER_NODE_TYPE", payload: definition });
2316
+ }, []);
2317
+ const getWorkflow = (0, import_react9.useCallback)(() => getWorkflowImpl(), [getWorkflowImpl]);
2318
+ const markClean = (0, import_react9.useCallback)(() => {
2319
+ dispatch({ type: "MARK_CLEAN" });
2320
+ }, []);
2321
+ const onNodesChange = (0, import_react9.useCallback)((changes) => {
2322
+ dispatch({ type: "APPLY_NODE_CHANGES", payload: changes });
2323
+ }, []);
2324
+ const onEdgesChange = (0, import_react9.useCallback)((changes) => {
2325
+ dispatch({ type: "APPLY_EDGE_CHANGES", payload: changes });
2326
+ }, []);
2327
+ const onConnect = (0, import_react9.useCallback)(
2328
+ (connection) => {
2329
+ const conn = connection;
2330
+ if (conn.source && conn.target) {
2331
+ addEdge({
2332
+ source: conn.source,
2333
+ target: conn.target,
2334
+ sourceHandle: conn.sourceHandle ?? void 0,
2335
+ targetHandle: conn.targetHandle ?? void 0
2336
+ });
2337
+ }
2338
+ },
2339
+ [addEdge]
2340
+ );
2341
+ const value = (0, import_react9.useMemo)(
2342
+ () => ({
2343
+ // State
2344
+ workflow: state.workflow,
2345
+ nodes: state.nodes,
2346
+ edges: state.edges,
2347
+ options: state.options,
2348
+ name: state.name,
2349
+ selectedNodeId: state.selectedNodeId,
2350
+ isDirty: state.isDirty,
2351
+ nodeTypes: state.nodeTypes,
2352
+ // Actions
2353
+ loadWorkflow,
2354
+ resetWorkflow,
2355
+ addNode,
2356
+ updateNode,
2357
+ updateNodePosition,
2358
+ removeNode,
2359
+ selectNode,
2360
+ addEdge,
2361
+ removeEdge,
2362
+ setName,
2363
+ setOptions,
2364
+ registerNodeType,
2365
+ getWorkflow,
2366
+ markClean,
2367
+ onNodesChange,
2368
+ onEdgesChange,
2369
+ onConnect
2370
+ }),
2371
+ [
2372
+ state,
2373
+ loadWorkflow,
2374
+ resetWorkflow,
2375
+ addNode,
2376
+ updateNode,
2377
+ updateNodePosition,
2378
+ removeNode,
2379
+ selectNode,
2380
+ addEdge,
2381
+ removeEdge,
2382
+ setName,
2383
+ setOptions,
2384
+ registerNodeType,
2385
+ getWorkflow,
2386
+ markClean,
2387
+ onNodesChange,
2388
+ onEdgesChange,
2389
+ onConnect
2390
+ ]
2391
+ );
2392
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(WorkflowEditorContext.Provider, { value, children });
2393
+ }
2394
+ function useWorkflowEditorContext() {
2395
+ const context = (0, import_react9.useContext)(WorkflowEditorContext);
2396
+ if (!context) {
2397
+ throw new Error(
2398
+ "useWorkflowEditorContext must be used within a WorkflowEditorProvider"
2399
+ );
2400
+ }
2401
+ return context;
2402
+ }
2403
+
2404
+ // src/components/WorkflowEditor.tsx
2405
+ var import_jsx_runtime30 = require("react/jsx-runtime");
2406
+ function WorkflowEditor({
2407
+ children,
2408
+ workflow,
2409
+ nodeTypes,
2410
+ onWorkflowChange,
2411
+ onDirtyChange,
2412
+ translationFn,
2413
+ translations
2414
+ }) {
2415
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react11.ReactFlowProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2416
+ TranslationProvider,
2417
+ {
2418
+ translationFn,
2419
+ translations,
2420
+ children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
2421
+ WorkflowEditorProvider,
2422
+ {
2423
+ workflow,
2424
+ nodeTypes,
2425
+ onWorkflowChange,
2426
+ onDirtyChange,
2427
+ children
2428
+ }
2429
+ )
2430
+ }
2431
+ ) });
2432
+ }
2433
+
2434
+ // src/hooks/useNodeRegistry.ts
2435
+ var import_react12 = require("react");
2436
+ function useNodeRegistry() {
2437
+ const context = useWorkflowEditorContext();
2438
+ const reactFlowNodeTypes = (0, import_react12.useMemo)(() => {
2439
+ const types = {};
2440
+ context.nodeTypes.forEach((def, type) => {
2441
+ types[type] = def.ViewComponent;
2442
+ });
2443
+ return types;
2444
+ }, [context.nodeTypes]);
2445
+ const nodeTypesList = (0, import_react12.useMemo)(
2446
+ () => Array.from(context.nodeTypes.values()),
2447
+ [context.nodeTypes]
2448
+ );
2449
+ return {
2450
+ /** Map of node type definitions */
2451
+ nodeTypes: context.nodeTypes,
2452
+ /** Array of node type definitions */
2453
+ nodeTypesList,
2454
+ /** ReactFlow-compatible nodeTypes object */
2455
+ reactFlowNodeTypes,
2456
+ /** Register a new node type */
2457
+ registerNodeType: context.registerNodeType,
2458
+ /** Get a specific node type definition */
2459
+ getNodeType: (type) => context.nodeTypes.get(type)
2460
+ };
2461
+ }
2462
+
2463
+ // src/hooks/useDragAndDrop.ts
2464
+ var import_react13 = require("react");
2465
+ var import_react14 = require("@xyflow/react");
2466
+ function useDragAndDrop() {
2467
+ const { addNode } = useWorkflowEditorContext();
2468
+ const reactFlowInstance = (0, import_react14.useReactFlow)();
2469
+ const onDragStart = (0, import_react13.useCallback)(
2470
+ (event, nodeType) => {
2471
+ event.dataTransfer.setData("application/reactflow", nodeType);
2472
+ event.dataTransfer.effectAllowed = "move";
2473
+ },
2474
+ []
2475
+ );
2476
+ const onDragOver = (0, import_react13.useCallback)((event) => {
2477
+ event.preventDefault();
2478
+ event.dataTransfer.dropEffect = "move";
2479
+ }, []);
2480
+ const onDrop = (0, import_react13.useCallback)(
2481
+ (event) => {
2482
+ event.preventDefault();
2483
+ const type = event.dataTransfer.getData("application/reactflow");
2484
+ if (!type) return;
2485
+ const position = reactFlowInstance.screenToFlowPosition({
2486
+ x: event.clientX,
2487
+ y: event.clientY
2488
+ });
2489
+ addNode(type, position);
2490
+ },
2491
+ [addNode, reactFlowInstance]
2492
+ );
2493
+ return {
2494
+ onDragStart,
2495
+ onDragOver,
2496
+ onDrop
2497
+ };
2498
+ }
2499
+
2500
+ // src/components/NodesPanel.tsx
2501
+ var import_jsx_runtime31 = require("react/jsx-runtime");
2502
+ var panelStyle = {
2503
+ backgroundColor: "var(--of-panel-bg, #fff)",
2504
+ borderRadius: "var(--of-panel-radius, 8px)",
2505
+ padding: "var(--of-panel-padding, 12px)",
2506
+ boxShadow: "var(--of-panel-shadow, 0 2px 8px rgba(0,0,0,0.1))",
2507
+ minWidth: "180px"
2508
+ };
2509
+ var titleStyle2 = {
2510
+ fontSize: "var(--of-panel-title-size, 12px)",
2511
+ fontWeight: "var(--of-font-weight-semibold, 600)",
2512
+ color: "var(--of-panel-title-color, #374151)",
2513
+ marginBottom: "var(--of-spacing-5, 12px)",
2514
+ textTransform: "uppercase",
2515
+ letterSpacing: "0.5px"
2516
+ };
2517
+ var itemStyle = {
2518
+ display: "flex",
2519
+ alignItems: "center",
2520
+ gap: "var(--of-spacing-3, 8px)",
2521
+ padding: "var(--of-spacing-3, 8px) var(--of-spacing-4, 10px)",
2522
+ borderRadius: "var(--of-radius-md, 6px)",
2523
+ cursor: "grab",
2524
+ marginBottom: "var(--of-spacing-1, 4px)",
2525
+ border: "1px solid var(--of-color-border-secondary, #E5E7EB)",
2526
+ backgroundColor: "var(--of-color-bg-primary, #fff)",
2527
+ transition: "all var(--of-transition-fast, 0.15s)"
2528
+ };
2529
+ var itemIconStyle = {
2530
+ width: "24px",
2531
+ height: "24px",
2532
+ display: "flex",
2533
+ alignItems: "center",
2534
+ justifyContent: "center",
2535
+ flexShrink: 0
2536
+ };
2537
+ var itemTextStyle = {
2538
+ flex: 1
2539
+ };
2540
+ var itemLabelStyle = {
2541
+ fontSize: "var(--of-field-font-size, 13px)",
2542
+ fontWeight: "var(--of-font-weight-medium, 500)",
2543
+ color: "var(--of-color-text-primary, #111827)"
2544
+ };
2545
+ var itemDescStyle = {
2546
+ fontSize: "var(--of-font-size-xs, 11px)",
2547
+ color: "var(--of-color-text-tertiary, #6B7280)",
2548
+ marginTop: "2px"
2549
+ };
2550
+ function NodeItem({
2551
+ nodeType,
2552
+ onDragStart,
2553
+ showDescription = true
2554
+ }) {
2555
+ const IconComponent = nodeType.Icon;
2556
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
2557
+ "div",
2558
+ {
2559
+ style: itemStyle,
2560
+ draggable: true,
2561
+ onDragStart: (e) => onDragStart(e, nodeType.type),
2562
+ onMouseEnter: (e) => {
2563
+ e.currentTarget.style.borderColor = "var(--of-color-border-focus, #3B82F6)";
2564
+ e.currentTarget.style.backgroundColor = "var(--of-color-bg-secondary, #F9FAFB)";
2565
+ },
2566
+ onMouseLeave: (e) => {
2567
+ e.currentTarget.style.borderColor = "var(--of-color-border-secondary, #E5E7EB)";
2568
+ e.currentTarget.style.backgroundColor = "var(--of-color-bg-primary, #fff)";
2569
+ },
2570
+ children: [
2571
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { style: itemIconStyle, children: IconComponent ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(IconComponent, { size: 24 }) : null }),
2572
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { style: itemTextStyle, children: [
2573
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { style: itemLabelStyle, children: nodeType.label }),
2574
+ showDescription && nodeType.description && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { style: itemDescStyle, children: nodeType.description })
2575
+ ] })
2576
+ ]
2577
+ }
2578
+ );
2579
+ }
2580
+ function NodesPanel({
2581
+ className,
2582
+ showDescriptions = true,
2583
+ filter,
2584
+ renderItem
2585
+ }) {
2586
+ const { nodeTypesList } = useNodeRegistry();
2587
+ const { onDragStart } = useDragAndDrop();
2588
+ const t = useTranslation();
2589
+ const filteredNodeTypes = filter ? nodeTypesList.filter(filter) : nodeTypesList;
2590
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { style: panelStyle, className, children: [
2591
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { style: titleStyle2, children: t("panels.nodes.title") }),
2592
+ filteredNodeTypes.map(
2593
+ (nodeType) => renderItem ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
2594
+ "div",
2595
+ {
2596
+ draggable: true,
2597
+ onDragStart: (e) => onDragStart(e, nodeType.type),
2598
+ children: renderItem(nodeType)
2599
+ },
2600
+ nodeType.type
2601
+ ) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
2602
+ NodeItem,
2603
+ {
2604
+ nodeType,
2605
+ onDragStart,
2606
+ showDescription: showDescriptions
2607
+ },
2608
+ nodeType.type
2609
+ )
2610
+ )
2611
+ ] });
2612
+ }
2613
+
2614
+ // src/hooks/useSelectedNode.ts
2615
+ function useSelectedNode() {
2616
+ const context = useWorkflowEditorContext();
2617
+ const selectedNode = context.selectedNodeId ? context.nodes.find((n) => n.id === context.selectedNodeId) ?? null : null;
2618
+ const nodeType = selectedNode?.type ? context.nodeTypes.get(selectedNode.type) : void 0;
2619
+ return {
2620
+ /** The currently selected node, or null if none */
2621
+ selectedNode,
2622
+ /** The ID of the selected node */
2623
+ selectedNodeId: context.selectedNodeId,
2624
+ /** The node type definition for the selected node */
2625
+ nodeType,
2626
+ /** Select a node by ID */
2627
+ selectNode: context.selectNode,
2628
+ /** Update the selected node's data */
2629
+ updateSelectedNode: (data) => {
2630
+ if (context.selectedNodeId) {
2631
+ context.updateNode(context.selectedNodeId, data);
2632
+ }
2633
+ },
2634
+ /** Remove the selected node */
2635
+ removeSelectedNode: () => {
2636
+ if (context.selectedNodeId) {
2637
+ context.removeNode(context.selectedNodeId);
2638
+ }
2639
+ }
2640
+ };
2641
+ }
2642
+
2643
+ // src/components/DetailPanel.tsx
2644
+ var import_jsx_runtime32 = require("react/jsx-runtime");
2645
+ var panelStyle2 = {
2646
+ backgroundColor: "var(--of-panel-bg, #fff)",
2647
+ borderRadius: "var(--of-panel-radius, 8px)",
2648
+ padding: "var(--of-panel-padding, 12px)",
2649
+ boxShadow: "var(--of-panel-shadow, 0 2px 8px rgba(0,0,0,0.1))",
2650
+ minWidth: "280px",
2651
+ maxWidth: "320px"
2652
+ };
2653
+ var titleStyle3 = {
2654
+ fontSize: "var(--of-panel-title-size, 12px)",
2655
+ fontWeight: "var(--of-font-weight-semibold, 600)",
2656
+ color: "var(--of-panel-title-color, #374151)",
2657
+ marginBottom: "var(--of-spacing-5, 12px)",
2658
+ textTransform: "uppercase",
2659
+ letterSpacing: "0.5px"
2660
+ };
2661
+ var emptyStyle2 = {
2662
+ fontSize: "var(--of-field-font-size, 13px)",
2663
+ color: "var(--of-color-text-tertiary, #6B7280)",
2664
+ textAlign: "center",
2665
+ padding: "var(--of-spacing-7, 20px) 0"
2666
+ };
2667
+ var headerStyle4 = {
2668
+ display: "flex",
2669
+ alignItems: "center",
2670
+ gap: "var(--of-spacing-3, 8px)",
2671
+ marginBottom: "var(--of-spacing-6, 16px)",
2672
+ paddingBottom: "var(--of-spacing-5, 12px)",
2673
+ borderBottom: "1px solid var(--of-color-border-secondary, #E5E7EB)"
2674
+ };
2675
+ var nodeTypeStyle = {
2676
+ fontSize: "var(--of-font-size-lg, 14px)",
2677
+ fontWeight: "var(--of-font-weight-semibold, 600)",
2678
+ color: "var(--of-color-text-primary, #111827)"
2679
+ };
2680
+ var nodeIdStyle = {
2681
+ fontSize: "var(--of-font-size-xs, 11px)",
2682
+ color: "var(--of-color-text-muted, #9CA3AF)",
2683
+ fontFamily: "var(--of-font-family-mono, monospace)"
2684
+ };
2685
+ var deleteButtonStyle = {
2686
+ marginLeft: "auto",
2687
+ padding: "var(--of-spacing-1, 4px) var(--of-spacing-3, 8px)",
2688
+ fontSize: "var(--of-font-size-xs, 11px)",
2689
+ color: "var(--of-button-danger-color, #DC2626)",
2690
+ backgroundColor: "var(--of-button-danger-bg, #FEE2E2)",
2691
+ border: "none",
2692
+ borderRadius: "var(--of-radius-sm, 4px)",
2693
+ cursor: "pointer"
2694
+ };
2695
+ function DetailPanel({
2696
+ className,
2697
+ emptyMessage,
2698
+ showNodeType = true,
2699
+ showNodeId = false
2700
+ }) {
2701
+ const { selectedNode, nodeType, updateSelectedNode, removeSelectedNode } = useSelectedNode();
2702
+ const t = useTranslation();
2703
+ if (!selectedNode || !nodeType) {
2704
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { style: panelStyle2, className, children: [
2705
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { style: titleStyle3, children: t("panels.detail.title") }),
2706
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { style: emptyStyle2, children: emptyMessage ?? t("panels.detail.emptyMessage") })
2707
+ ] });
2708
+ }
2709
+ const DetailComponent = nodeType.DetailComponent;
2710
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { style: panelStyle2, className, children: [
2711
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { style: titleStyle3, children: t("panels.detail.title") }),
2712
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { style: headerStyle4, children: [
2713
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { children: [
2714
+ showNodeType && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { style: nodeTypeStyle, children: nodeType.label }),
2715
+ showNodeId && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { style: nodeIdStyle, children: selectedNode.id })
2716
+ ] }),
2717
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
2718
+ "button",
2719
+ {
2720
+ style: deleteButtonStyle,
2721
+ onClick: removeSelectedNode,
2722
+ title: t("panels.detail.deleteTitle"),
2723
+ children: t("panels.detail.delete")
2724
+ }
2725
+ )
2726
+ ] }),
2727
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
2728
+ DetailComponent,
2729
+ {
2730
+ node: selectedNode,
2731
+ onChange: (data) => updateSelectedNode(data)
2732
+ }
2733
+ )
2734
+ ] });
2735
+ }
2736
+
2737
+ // src/components/OptionsPanel.tsx
2738
+ var import_jsx_runtime33 = require("react/jsx-runtime");
2739
+ var panelStyle3 = {
2740
+ backgroundColor: "var(--of-panel-bg, #fff)",
2741
+ borderRadius: "var(--of-panel-radius, 8px)",
2742
+ padding: "var(--of-panel-padding, 12px)",
2743
+ boxShadow: "var(--of-panel-shadow, 0 2px 8px rgba(0,0,0,0.1))",
2744
+ minWidth: "220px"
2745
+ };
2746
+ var titleStyle4 = {
2747
+ fontSize: "var(--of-panel-title-size, 12px)",
2748
+ fontWeight: "var(--of-font-weight-semibold, 600)",
2749
+ color: "var(--of-panel-title-color, #374151)",
2750
+ marginBottom: "var(--of-spacing-5, 12px)",
2751
+ textTransform: "uppercase",
2752
+ letterSpacing: "0.5px"
2753
+ };
2754
+ function OptionsPanel({
2755
+ className,
2756
+ showFrequency = true,
2757
+ customOptions
2758
+ }) {
2759
+ const { options, setOptions } = useWorkflowEditorContext();
2760
+ const t = useTranslation();
2761
+ const frequencyOptions = [
2762
+ { value: "one_time", label: t("panels.options.frequencyOneTime") },
2763
+ { value: "every_rematch", label: t("panels.options.frequencyEveryRematch") }
2764
+ ];
2765
+ const handleFrequencyTypeChange = (type) => {
2766
+ const newFrequency = {
2767
+ type
2768
+ };
2769
+ if (type === "every_rematch") {
2770
+ newFrequency.interval = options.frequency?.interval ?? 3600;
2771
+ }
2772
+ setOptions({ ...options, frequency: newFrequency });
2773
+ };
2774
+ const handleIntervalChange = (ms) => {
2775
+ if (options.frequency) {
2776
+ const intervalInSeconds = Math.round(ms / 1e3);
2777
+ setOptions({
2778
+ ...options,
2779
+ frequency: { ...options.frequency, interval: intervalInSeconds }
2780
+ });
2781
+ }
2782
+ };
2783
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { style: panelStyle3, className, children: [
2784
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { style: titleStyle4, children: t("panels.options.title") }),
2785
+ showFrequency && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(FieldGroup, { label: t("panels.options.frequencyGroup"), children: [
2786
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2787
+ SelectField,
2788
+ {
2789
+ label: t("panels.options.frequencyTypeLabel"),
2790
+ value: options.frequency?.type ?? "one_time",
2791
+ options: frequencyOptions,
2792
+ onChange: handleFrequencyTypeChange,
2793
+ hint: t("panels.options.frequencyTypeHint")
2794
+ }
2795
+ ),
2796
+ options.frequency?.type === "every_rematch" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2797
+ DurationField,
2798
+ {
2799
+ label: t("panels.options.intervalLabel"),
2800
+ value: (options.frequency?.interval ?? 3600) * 1e3,
2801
+ onChange: handleIntervalChange,
2802
+ hint: t("panels.options.intervalHint")
2803
+ }
2804
+ )
2805
+ ] }),
2806
+ customOptions
2807
+ ] });
2808
+ }
2809
+
2810
+ // src/components/ControlPanel.tsx
2811
+ var import_react15 = require("react");
2812
+ var import_jsx_runtime34 = require("react/jsx-runtime");
2813
+ var panelStyle4 = {
2814
+ backgroundColor: "var(--of-panel-bg, #fff)",
2815
+ borderRadius: "var(--of-panel-radius, 8px)",
2816
+ padding: "var(--of-panel-padding, 12px)",
2817
+ boxShadow: "var(--of-panel-shadow, 0 2px 8px rgba(0,0,0,0.1))",
2818
+ minWidth: "220px"
2819
+ };
2820
+ var titleStyle5 = {
2821
+ fontSize: "var(--of-panel-title-size, 12px)",
2822
+ fontWeight: "var(--of-font-weight-semibold, 600)",
2823
+ color: "var(--of-panel-title-color, #374151)",
2824
+ marginBottom: "var(--of-spacing-5, 12px)",
2825
+ textTransform: "uppercase",
2826
+ letterSpacing: "0.5px"
2827
+ };
2828
+ var actionsStyle = {
2829
+ display: "flex",
2830
+ gap: "var(--of-spacing-3, 8px)",
2831
+ marginTop: "var(--of-spacing-5, 12px)"
2832
+ };
2833
+ var buttonStyle = {
2834
+ padding: "var(--of-button-padding, 8px 16px)",
2835
+ fontSize: "var(--of-field-font-size, 13px)",
2836
+ fontWeight: "var(--of-font-weight-medium, 500)",
2837
+ borderRadius: "var(--of-button-radius, 6px)",
2838
+ cursor: "pointer",
2839
+ border: "none",
2840
+ transition: "all var(--of-transition-fast, 0.15s)"
2841
+ };
2842
+ var saveButtonStyle = {
2843
+ ...buttonStyle,
2844
+ backgroundColor: "var(--of-button-primary-bg, #3B82F6)",
2845
+ color: "var(--of-button-primary-color, #fff)"
2846
+ };
2847
+ var saveButtonDisabledStyle = {
2848
+ ...saveButtonStyle,
2849
+ backgroundColor: "var(--of-button-primary-bg-disabled, #93C5FD)",
2850
+ cursor: "not-allowed"
2851
+ };
2852
+ var statusStyle = {
2853
+ fontSize: "var(--of-font-size-xs, 11px)",
2854
+ color: "var(--of-color-text-tertiary, #6B7280)",
2855
+ marginTop: "var(--of-spacing-3, 8px)"
2856
+ };
2857
+ function ControlPanel({
2858
+ className,
2859
+ showName = true,
2860
+ showSaveButton = true,
2861
+ saveButtonLabel,
2862
+ onSave,
2863
+ renderActions
2864
+ }) {
2865
+ const { name, setName, isDirty, getWorkflow, markClean } = useWorkflowEditorContext();
2866
+ const t = useTranslation();
2867
+ const [isSaving, setIsSaving] = (0, import_react15.useState)(false);
2868
+ const [saveStatus, setSaveStatus] = (0, import_react15.useState)(null);
2869
+ const handleSave = async () => {
2870
+ if (!onSave || isSaving) return;
2871
+ setIsSaving(true);
2872
+ setSaveStatus(null);
2873
+ try {
2874
+ await onSave();
2875
+ markClean();
2876
+ setSaveStatus("saved");
2877
+ setTimeout(() => setSaveStatus(null), 2e3);
2878
+ } catch (error) {
2879
+ console.error("Failed to save workflow:", error);
2880
+ setSaveStatus("error");
2881
+ } finally {
2882
+ setIsSaving(false);
2883
+ }
2884
+ };
2885
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { style: panelStyle4, className, children: [
2886
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { style: titleStyle5, children: t("panels.control.title") }),
2887
+ showName && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2888
+ TextField,
2889
+ {
2890
+ label: t("panels.control.nameLabel"),
2891
+ value: name,
2892
+ onChange: setName,
2893
+ placeholder: t("panels.control.namePlaceholder")
2894
+ }
2895
+ ),
2896
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { style: actionsStyle, children: [
2897
+ showSaveButton && onSave && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2898
+ "button",
2899
+ {
2900
+ style: isSaving || !isDirty ? saveButtonDisabledStyle : saveButtonStyle,
2901
+ onClick: handleSave,
2902
+ disabled: isSaving || !isDirty,
2903
+ children: isSaving ? t("panels.control.saving") : saveButtonLabel ?? "Save"
2904
+ }
2905
+ ),
2906
+ renderActions?.({ isDirty, workflow: getWorkflow() })
2907
+ ] }),
2908
+ saveStatus && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2909
+ "div",
2910
+ {
2911
+ style: {
2912
+ ...statusStyle,
2913
+ color: saveStatus === "saved" ? "var(--of-color-status-success, #059669)" : "var(--of-color-status-error, #DC2626)"
2914
+ },
2915
+ children: saveStatus === "saved" ? t("panels.control.savedSuccessfully") : t("panels.control.failedToSave")
2916
+ }
2917
+ ),
2918
+ isDirty && !saveStatus && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { style: statusStyle, children: t("panels.control.unsavedChanges") })
2919
+ ] });
2920
+ }
2921
+
2922
+ // src/hooks/useWorkflowEditor.ts
2923
+ function useWorkflowEditor() {
2924
+ const context = useWorkflowEditorContext();
2925
+ return {
2926
+ // State
2927
+ workflow: context.getWorkflow(),
2928
+ nodes: context.nodes,
2929
+ edges: context.edges,
2930
+ options: context.options,
2931
+ name: context.name,
2932
+ isDirty: context.isDirty,
2933
+ selectedNode: context.selectedNodeId ? context.nodes.find((n) => n.id === context.selectedNodeId) ?? null : null,
2934
+ selectedNodeId: context.selectedNodeId,
2935
+ // Actions
2936
+ loadWorkflow: context.loadWorkflow,
2937
+ resetWorkflow: context.resetWorkflow,
2938
+ addNode: context.addNode,
2939
+ updateNode: context.updateNode,
2940
+ removeNode: context.removeNode,
2941
+ selectNode: context.selectNode,
2942
+ addEdge: context.addEdge,
2943
+ removeEdge: context.removeEdge,
2944
+ setName: context.setName,
2945
+ setOptions: context.setOptions,
2946
+ getWorkflow: context.getWorkflow,
2947
+ markClean: context.markClean
2948
+ };
2949
+ }
2950
+
2951
+ // src/hooks/useNodes.ts
2952
+ function useNodes() {
2953
+ const context = useWorkflowEditorContext();
2954
+ return {
2955
+ nodes: context.nodes,
2956
+ onNodesChange: context.onNodesChange,
2957
+ addNode: context.addNode,
2958
+ updateNode: context.updateNode,
2959
+ removeNode: context.removeNode
2960
+ };
2961
+ }
2962
+
2963
+ // src/hooks/useEdges.ts
2964
+ function useEdges() {
2965
+ const context = useWorkflowEditorContext();
2966
+ return {
2967
+ edges: context.edges,
2968
+ onEdgesChange: context.onEdgesChange,
2969
+ onConnect: context.onConnect,
2970
+ addEdge: context.addEdge,
2971
+ removeEdge: context.removeEdge
2972
+ };
2973
+ }
2974
+
2975
+ // src/styles/index.ts
2976
+ function cssVar(name, fallback) {
2977
+ return fallback ? `var(${name}, ${fallback})` : `var(${name})`;
2978
+ }
2979
+ var themeVars = {
2980
+ // Colors
2981
+ color: {
2982
+ bgPrimary: "var(--of-color-bg-primary, #fff)",
2983
+ bgSecondary: "var(--of-color-bg-secondary, #F9FAFB)",
2984
+ bgTertiary: "var(--of-color-bg-tertiary, #F3F4F6)",
2985
+ bgDisabled: "var(--of-color-bg-disabled, #F3F4F6)",
2986
+ textPrimary: "var(--of-color-text-primary, #111827)",
2987
+ textSecondary: "var(--of-color-text-secondary, #374151)",
2988
+ textTertiary: "var(--of-color-text-tertiary, #6B7280)",
2989
+ textMuted: "var(--of-color-text-muted, #9CA3AF)",
2990
+ textInverse: "var(--of-color-text-inverse, #fff)",
2991
+ borderPrimary: "var(--of-color-border-primary, #D1D5DB)",
2992
+ borderSecondary: "var(--of-color-border-secondary, #E5E7EB)",
2993
+ borderFocus: "var(--of-color-border-focus, #3B82F6)",
2994
+ interactivePrimary: "var(--of-color-interactive-primary, #3B82F6)",
2995
+ interactivePrimaryHover: "var(--of-color-interactive-primary-hover, #2563EB)",
2996
+ interactivePrimaryDisabled: "var(--of-color-interactive-primary-disabled, #93C5FD)",
2997
+ statusSuccess: "var(--of-color-status-success, #059669)",
2998
+ statusError: "var(--of-color-status-error, #DC2626)",
2999
+ statusErrorBg: "var(--of-color-status-error-bg, #FEE2E2)",
3000
+ statusWarning: "var(--of-color-status-warning, #FF9800)"
3001
+ },
3002
+ // Node colors
3003
+ node: {
3004
+ trigger: "var(--of-node-trigger-color, #4CAF50)",
3005
+ action: "var(--of-node-action-color, #2196F3)",
3006
+ condition: "var(--of-node-condition-color, #FF9800)",
3007
+ exit: "var(--of-node-exit-color, #F44336)",
3008
+ wait: "var(--of-node-wait-color, #9C27B0)",
3009
+ triggerTimeout: "var(--of-node-trigger-timeout-color, #607D8B)",
3010
+ bg: "var(--of-node-bg, #fff)",
3011
+ contentColor: "var(--of-node-content-color, #666)",
3012
+ shadow: "var(--of-node-shadow, 0 2px 4px rgba(0, 0, 0, 0.1))"
3013
+ },
3014
+ // Spacing
3015
+ spacing: {
3016
+ 1: "var(--of-spacing-1, 4px)",
3017
+ 2: "var(--of-spacing-2, 6px)",
3018
+ 3: "var(--of-spacing-3, 8px)",
3019
+ 4: "var(--of-spacing-4, 10px)",
3020
+ 5: "var(--of-spacing-5, 12px)",
3021
+ 6: "var(--of-spacing-6, 16px)",
3022
+ 7: "var(--of-spacing-7, 20px)"
3023
+ },
3024
+ // Typography
3025
+ font: {
3026
+ sizeXs: "var(--of-font-size-xs, 11px)",
3027
+ sizeSm: "var(--of-font-size-sm, 12px)",
3028
+ sizeMd: "var(--of-font-size-md, 13px)",
3029
+ sizeLg: "var(--of-font-size-lg, 14px)",
3030
+ weightNormal: "var(--of-font-weight-normal, 400)",
3031
+ weightMedium: "var(--of-font-weight-medium, 500)",
3032
+ weightSemibold: "var(--of-font-weight-semibold, 600)",
3033
+ familyBase: "var(--of-font-family-base, system-ui, sans-serif)",
3034
+ familyMono: "var(--of-font-family-mono, monospace)"
3035
+ },
3036
+ // Border radius
3037
+ radius: {
3038
+ sm: "var(--of-radius-sm, 4px)",
3039
+ md: "var(--of-radius-md, 6px)",
3040
+ lg: "var(--of-radius-lg, 8px)"
3041
+ },
3042
+ // Shadows
3043
+ shadow: {
3044
+ panel: "var(--of-shadow-panel, 0 2px 8px rgba(0, 0, 0, 0.1))",
3045
+ node: "var(--of-shadow-node, 0 2px 4px rgba(0, 0, 0, 0.1))"
3046
+ },
3047
+ // Transitions
3048
+ transition: {
3049
+ fast: "var(--of-transition-fast, 0.15s)",
3050
+ normal: "var(--of-transition-normal, 0.2s)"
3051
+ },
3052
+ // Component-specific
3053
+ panel: {
3054
+ bg: "var(--of-panel-bg, #fff)",
3055
+ shadow: "var(--of-panel-shadow, 0 2px 8px rgba(0, 0, 0, 0.1))",
3056
+ radius: "var(--of-panel-radius, 8px)",
3057
+ padding: "var(--of-panel-padding, 12px)",
3058
+ titleColor: "var(--of-panel-title-color, #374151)",
3059
+ titleSize: "var(--of-panel-title-size, 12px)"
3060
+ },
3061
+ field: {
3062
+ bg: "var(--of-field-bg, #fff)",
3063
+ border: "var(--of-field-border, #D1D5DB)",
3064
+ borderFocus: "var(--of-field-border-focus, #3B82F6)",
3065
+ borderError: "var(--of-field-border-error, #DC2626)",
3066
+ radius: "var(--of-field-radius, 6px)",
3067
+ padding: "var(--of-field-padding, 8px 10px)",
3068
+ fontSize: "var(--of-field-font-size, 13px)",
3069
+ labelColor: "var(--of-field-label-color, #374151)",
3070
+ labelSize: "var(--of-field-label-size, 12px)",
3071
+ hintColor: "var(--of-field-hint-color, #6B7280)",
3072
+ errorColor: "var(--of-field-error-color, #DC2626)"
3073
+ },
3074
+ button: {
3075
+ primaryBg: "var(--of-button-primary-bg, #3B82F6)",
3076
+ primaryColor: "var(--of-button-primary-color, #fff)",
3077
+ primaryBgHover: "var(--of-button-primary-bg-hover, #2563EB)",
3078
+ primaryBgDisabled: "var(--of-button-primary-bg-disabled, #93C5FD)",
3079
+ dangerBg: "var(--of-button-danger-bg, #FEE2E2)",
3080
+ dangerColor: "var(--of-button-danger-color, #DC2626)",
3081
+ radius: "var(--of-button-radius, 6px)",
3082
+ padding: "var(--of-button-padding, 8px 16px)"
3083
+ }
3084
+ };
3085
+ // Annotate the CommonJS export names for ESM import in node:
3086
+ 0 && (module.exports = {
3087
+ ActionNodeDetail,
3088
+ ActionNodeView,
3089
+ BaseNodeView,
3090
+ CheckboxField,
3091
+ ConditionBuilder,
3092
+ ConditionBuilderDialog,
3093
+ ConditionNodeDetail,
3094
+ ConditionNodeView,
3095
+ ControlPanel,
3096
+ DetailPanel,
3097
+ DurationField,
3098
+ ExitNodeDetail,
3099
+ ExitNodeView,
3100
+ Field,
3101
+ FieldGroup,
3102
+ JsonField,
3103
+ NodesPanel,
3104
+ NumberField,
3105
+ OptionsPanel,
3106
+ SelectField,
3107
+ TextAreaField,
3108
+ TextField,
3109
+ TranslationProvider,
3110
+ TriggerNodeDetail,
3111
+ TriggerNodeView,
3112
+ TriggerOrTimeoutNodeDetail,
3113
+ TriggerOrTimeoutNodeView,
3114
+ WaitNodeDetail,
3115
+ WaitNodeView,
3116
+ WorkflowEditor,
3117
+ WorkflowEditorProvider,
3118
+ createTranslationFunction,
3119
+ cssVar,
3120
+ defaultNodeTypes,
3121
+ defaultOperators,
3122
+ defaultTranslations,
3123
+ mergeNodeTypes,
3124
+ themeVars,
3125
+ useDragAndDrop,
3126
+ useEdges,
3127
+ useNodeRegistry,
3128
+ useNodes,
3129
+ useSelectedNode,
3130
+ useTranslation,
3131
+ useWorkflowEditor,
3132
+ useWorkflowEditorContext
3133
+ });