@burdenoff/fluidgrids-fe-sdk 2026.525.1 → 2026.616.1

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.
@@ -6,7 +6,7 @@ import { waitForFluidGridsClient, getFluidGridsClient } from './chunk-5BDQNSP3.j
6
6
  import { config, getFluidGridsConfig } from './chunk-WWU2O2SR.js';
7
7
  import { deepClone, applyTemplateInputs } from './chunk-XQP33UJR.js';
8
8
  import * as React2 from 'react';
9
- import { memo, useState, useMemo, useCallback, useRef, useEffect, useContext } from 'react';
9
+ import { forwardRef, useState, useImperativeHandle, useRef, useEffect, useMemo, useCallback, memo, useContext } from 'react';
10
10
  import { gql as gql$1 } from '@apollo/client/core';
11
11
  import { useQuery, useMutation } from '@apollo/client/react';
12
12
  import JSZip from 'jszip';
@@ -14,10 +14,10 @@ import { useStore, useReactFlow, Position, getConnectedEdges, Handle, useNodeId
14
14
  import { nanoid } from 'nanoid/non-secure';
15
15
  import { useShallow } from 'zustand/shallow';
16
16
  import { cn } from '@burdenoff/fe-libs/utils';
17
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
18
- import * as AdaptiveCards from 'adaptivecards';
17
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
18
+ import { Textarea, Input, Button, Card, Dialog as Dialog$1, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuItem, DropdownMenuSeparator } from '@burdenoff/fe-libs/ui';
19
19
  import { Icon } from '@iconify/react';
20
- import { Button, Dialog as Dialog$1, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuItem, DropdownMenuSeparator } from '@burdenoff/fe-libs/ui';
20
+ import * as AdaptiveCards from 'adaptivecards';
21
21
  import { formatDistanceToNow } from 'date-fns';
22
22
  import { gql } from '@apollo/client';
23
23
  import * as Dialog from '@radix-ui/react-dialog';
@@ -143,51 +143,302 @@ function CustomHandle({
143
143
  }
144
144
  ) });
145
145
  }
146
- function isAdaptiveCard(data) {
147
- return data.type === "AdaptiveCard";
146
+
147
+ // src/modules/ui-blocks/registry.ts
148
+ var registry = /* @__PURE__ */ new Map();
149
+ function registerCustomBlock(name, component) {
150
+ if (registry.has(name)) {
151
+ console.warn(`ui-blocks: overwriting registered block "${name}"`);
152
+ }
153
+ registry.set(name, component);
148
154
  }
149
- function isCustomFormat(data) {
150
- const record = data;
151
- return record.type !== "AdaptiveCard" && "inputs" in record;
155
+ function getCustomBlock(name) {
156
+ return registry.get(name);
152
157
  }
153
- function expandTemplate(obj, data) {
154
- if (typeof obj === "string") {
155
- const singleVarMatch = obj.match(/^\$\{([^}]+)\}$/);
156
- if (singleVarMatch) {
157
- const key = singleVarMatch[1].trim();
158
- const value = data[key];
159
- if (value !== void 0 && value !== null) {
160
- if (typeof value === "boolean") {
161
- return value ? "true" : "false";
162
- }
163
- return value;
158
+ function listCustomBlocks() {
159
+ return [...registry.keys()];
160
+ }
161
+ function ExpressionEditor({
162
+ value,
163
+ onChange,
164
+ placeholder = "Enter expression...",
165
+ className,
166
+ rows = 4,
167
+ label,
168
+ helpText
169
+ }) {
170
+ const editorRef = useRef(null);
171
+ const [isFocused, setIsFocused] = useState(false);
172
+ const handleChange = useCallback(
173
+ (e) => {
174
+ onChange(e.target.value);
175
+ },
176
+ [onChange]
177
+ );
178
+ const insertTemplate = useCallback(
179
+ (template) => {
180
+ const textarea = editorRef.current;
181
+ if (!textarea) return;
182
+ const start = textarea.selectionStart;
183
+ const end = textarea.selectionEnd;
184
+ const newValue = value.substring(0, start) + template + value.substring(end);
185
+ onChange(newValue);
186
+ setTimeout(() => {
187
+ textarea.focus();
188
+ textarea.setSelectionRange(
189
+ start + template.length,
190
+ start + template.length
191
+ );
192
+ }, 0);
193
+ },
194
+ [value, onChange]
195
+ );
196
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
197
+ label && /* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-foreground", children: label }),
198
+ /* @__PURE__ */ jsxs(
199
+ Card,
200
+ {
201
+ className: cn(
202
+ "relative overflow-hidden transition-all",
203
+ isFocused && "ring-2 ring-primary ring-offset-1"
204
+ ),
205
+ children: [
206
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 px-2 py-1.5 border-b bg-muted/30", children: [
207
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground mr-2", children: "Insert:" }),
208
+ /* @__PURE__ */ jsx(
209
+ "button",
210
+ {
211
+ type: "button",
212
+ onClick: () => insertTemplate("{{input.}}"),
213
+ className: "text-xs px-2 py-0.5 rounded bg-primary/10 text-primary hover:bg-primary/20 transition-colors",
214
+ children: "{{input}}"
215
+ }
216
+ ),
217
+ /* @__PURE__ */ jsx(
218
+ "button",
219
+ {
220
+ type: "button",
221
+ onClick: () => insertTemplate("{{output.}}"),
222
+ className: "text-xs px-2 py-0.5 rounded bg-primary/10 text-primary hover:bg-primary/20 transition-colors",
223
+ children: "{{output}}"
224
+ }
225
+ ),
226
+ /* @__PURE__ */ jsx(
227
+ "button",
228
+ {
229
+ type: "button",
230
+ onClick: () => insertTemplate("{{$node['']}}"),
231
+ className: "text-xs px-2 py-0.5 rounded bg-primary/10 text-primary hover:bg-primary/20 transition-colors",
232
+ children: "{{$node}}"
233
+ }
234
+ )
235
+ ] }),
236
+ /* @__PURE__ */ jsx(
237
+ "textarea",
238
+ {
239
+ ref: editorRef,
240
+ value,
241
+ onChange: handleChange,
242
+ onFocus: () => setIsFocused(true),
243
+ onBlur: () => setIsFocused(false),
244
+ placeholder,
245
+ rows,
246
+ spellCheck: false,
247
+ className: cn(
248
+ "w-full p-3 font-mono text-sm bg-background resize-none",
249
+ "focus:outline-none",
250
+ "placeholder:text-muted-foreground/50"
251
+ )
252
+ }
253
+ )
254
+ ]
164
255
  }
165
- return "";
256
+ ),
257
+ helpText && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: helpText })
258
+ ] });
259
+ }
260
+ var ExpressionEditorBlock = forwardRef(function ExpressionEditorBlock2({ path, initialValue, options }, ref) {
261
+ const [draft, setDraft] = useState(
262
+ () => initialValue === void 0 || initialValue === null ? "" : String(initialValue)
263
+ );
264
+ useImperativeHandle(
265
+ ref,
266
+ () => ({ collectValues: () => ({ [path]: draft }) }),
267
+ [path, draft]
268
+ );
269
+ const language = typeof options.language === "string" ? options.language : void 0;
270
+ const helpText = typeof options.helpText === "string" ? options.helpText : void 0;
271
+ return /* @__PURE__ */ jsx(
272
+ ExpressionEditor,
273
+ {
274
+ value: draft,
275
+ onChange: setDraft,
276
+ label: typeof options.label === "string" ? options.label : void 0,
277
+ placeholder: typeof options.placeholder === "string" ? options.placeholder : void 0,
278
+ rows: typeof options.rows === "number" ? options.rows : void 0,
279
+ helpText: language ? helpText ? `${helpText} \xB7 ${language}` : language : helpText
166
280
  }
167
- return obj.replace(/\$\{([^}]+)\}/g, (_match, key) => {
168
- const trimmedKey = key.trim();
169
- const value = data[trimmedKey];
170
- if (value !== void 0 && value !== null) {
171
- return String(value);
281
+ );
282
+ });
283
+ function seedDraft(initialValue) {
284
+ if (initialValue === void 0 || initialValue === null) return "";
285
+ if (typeof initialValue === "string") return initialValue;
286
+ try {
287
+ return JSON.stringify(initialValue, null, 2);
288
+ } catch {
289
+ return "";
290
+ }
291
+ }
292
+ var JsonBlock = forwardRef(
293
+ function JsonBlock2({ path, initialValue, options }, ref) {
294
+ const [draft, setDraft] = useState(() => seedDraft(initialValue));
295
+ const [error, setError] = useState(null);
296
+ useImperativeHandle(
297
+ ref,
298
+ () => ({
299
+ collectValues() {
300
+ if (draft.trim() === "") return {};
301
+ try {
302
+ return { [path]: JSON.parse(draft) };
303
+ } catch {
304
+ return {};
305
+ }
306
+ }
307
+ }),
308
+ [path, draft]
309
+ );
310
+ const handleChange = (value) => {
311
+ setDraft(value);
312
+ if (value.trim() === "") {
313
+ setError(null);
314
+ return;
172
315
  }
173
- return "";
174
- });
316
+ try {
317
+ JSON.parse(value);
318
+ setError(null);
319
+ } catch (e) {
320
+ setError(e instanceof Error ? e.message : "Invalid JSON");
321
+ }
322
+ };
323
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
324
+ typeof options.label === "string" && /* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-foreground", children: options.label }),
325
+ /* @__PURE__ */ jsx(
326
+ Textarea,
327
+ {
328
+ value: draft,
329
+ onChange: (e) => handleChange(e.target.value),
330
+ rows: typeof options.rows === "number" ? options.rows : 6,
331
+ spellCheck: false,
332
+ className: "font-mono text-sm",
333
+ placeholder: typeof options.placeholder === "string" ? options.placeholder : "{}"
334
+ }
335
+ ),
336
+ error ? /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--color-status-error-text)]", children: error }) : typeof options.helpText === "string" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: options.helpText }) : null
337
+ ] });
175
338
  }
176
- if (Array.isArray(obj)) {
177
- return obj.map((item) => expandTemplate(item, data));
339
+ );
340
+ function seedRows(initialValue) {
341
+ if (initialValue !== null && typeof initialValue === "object" && !Array.isArray(initialValue)) {
342
+ return Object.entries(initialValue).map(
343
+ ([key, value]) => ({ key, value: String(value ?? "") })
344
+ );
178
345
  }
179
- if (obj !== null && typeof obj === "object") {
180
- const result = {};
181
- for (const [key, value] of Object.entries(obj)) {
182
- if (key === "defaultValue") {
183
- continue;
184
- }
185
- result[key] = expandTemplate(value, data);
186
- }
187
- return result;
346
+ return [];
347
+ }
348
+ var KeyValueBlock = forwardRef(
349
+ function KeyValueBlock2({ path, initialValue, options }, ref) {
350
+ const [rows, setRows] = useState(() => seedRows(initialValue));
351
+ useImperativeHandle(
352
+ ref,
353
+ () => ({
354
+ collectValues: () => ({
355
+ [path]: Object.fromEntries(
356
+ rows.filter((row) => row.key).map((row) => [row.key, row.value])
357
+ )
358
+ })
359
+ }),
360
+ [path, rows]
361
+ );
362
+ const updateRow = (index, patch) => {
363
+ setRows(
364
+ (prev) => prev.map((row, i) => i === index ? { ...row, ...patch } : row)
365
+ );
366
+ };
367
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
368
+ typeof options.label === "string" && /* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-foreground", children: options.label }),
369
+ rows.map((row, index) => (
370
+ // Index keys are fine here: rows are an ordered editable list with
371
+ // no stable identity beyond their position.
372
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
373
+ /* @__PURE__ */ jsx(
374
+ Input,
375
+ {
376
+ value: row.key,
377
+ onChange: (e) => updateRow(index, { key: e.target.value }),
378
+ placeholder: typeof options.keyPlaceholder === "string" ? options.keyPlaceholder : "Key",
379
+ className: "h-8 flex-1"
380
+ }
381
+ ),
382
+ /* @__PURE__ */ jsx(
383
+ Input,
384
+ {
385
+ value: row.value,
386
+ onChange: (e) => updateRow(index, { value: e.target.value }),
387
+ placeholder: typeof options.valuePlaceholder === "string" ? options.valuePlaceholder : "Value",
388
+ className: "h-8 flex-1"
389
+ }
390
+ ),
391
+ /* @__PURE__ */ jsx(
392
+ "button",
393
+ {
394
+ type: "button",
395
+ "aria-label": "Remove row",
396
+ onClick: () => setRows((prev) => prev.filter((_, i) => i !== index)),
397
+ className: "p-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors",
398
+ children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:x", className: "w-4 h-4" })
399
+ }
400
+ )
401
+ ] }, index)
402
+ )),
403
+ /* @__PURE__ */ jsxs(
404
+ Button,
405
+ {
406
+ type: "button",
407
+ variant: "outline",
408
+ size: "sm",
409
+ onClick: () => setRows((prev) => [...prev, { key: "", value: "" }]),
410
+ children: [
411
+ /* @__PURE__ */ jsx(Icon, { icon: "lucide:plus", className: "w-3.5 h-3.5 mr-1" }),
412
+ "Add"
413
+ ]
414
+ }
415
+ )
416
+ ] });
188
417
  }
189
- return obj;
418
+ );
419
+
420
+ // src/modules/ui-blocks/blocks/builtins.ts
421
+ var registered = false;
422
+ function ensureBuiltinBlocks() {
423
+ if (registered) return;
424
+ registered = true;
425
+ registerCustomBlock("expression-editor", ExpressionEditorBlock);
426
+ registerCustomBlock("key-value", KeyValueBlock);
427
+ registerCustomBlock("json", JsonBlock);
190
428
  }
429
+
430
+ // src/modules/ui-blocks/path.ts
431
+ function getAtPath(obj, path) {
432
+ if (!obj || !path) return void 0;
433
+ let current = obj;
434
+ for (const key of path.split(".")) {
435
+ if (current === null || typeof current !== "object") return void 0;
436
+ current = current[key];
437
+ }
438
+ return current;
439
+ }
440
+
441
+ // src/modules/adaptive-cards/components/coerce.ts
191
442
  function coerceSubmittedDataFromCard(submittedData, cardData) {
192
443
  const fieldTypeMap = {};
193
444
  const integerFields = /* @__PURE__ */ new Set();
@@ -209,11 +460,32 @@ function coerceSubmittedDataFromCard(submittedData, cardData) {
209
460
  }
210
461
  }
211
462
  }
463
+ if (Array.isArray(element.actions)) {
464
+ extractFromActions(element.actions);
465
+ }
466
+ }
467
+ }
468
+ function extractFromActions(actions) {
469
+ for (const action of actions) {
470
+ const nested = action?.card;
471
+ if (nested && typeof nested === "object") {
472
+ if (Array.isArray(nested.body)) {
473
+ extractInputTypes(nested.body);
474
+ }
475
+ if (Array.isArray(nested.actions)) {
476
+ extractFromActions(nested.actions);
477
+ }
478
+ }
212
479
  }
213
480
  }
214
481
  if (Array.isArray(cardData.body)) {
215
482
  extractInputTypes(cardData.body);
216
483
  }
484
+ if (Array.isArray(cardData.actions)) {
485
+ extractFromActions(
486
+ cardData.actions
487
+ );
488
+ }
217
489
  const coercedData = {};
218
490
  for (const key in submittedData) {
219
491
  const value = submittedData[key];
@@ -237,6 +509,51 @@ function coerceSubmittedDataFromCard(submittedData, cardData) {
237
509
  }
238
510
  return coercedData;
239
511
  }
512
+ function isAdaptiveCard(data) {
513
+ return data.type === "AdaptiveCard";
514
+ }
515
+ function isCustomFormat(data) {
516
+ const record = data;
517
+ return record.type !== "AdaptiveCard" && "inputs" in record;
518
+ }
519
+ function expandTemplate(obj, data) {
520
+ if (typeof obj === "string") {
521
+ const singleVarMatch = obj.match(/^\$\{([^}]+)\}$/);
522
+ if (singleVarMatch) {
523
+ const key = singleVarMatch[1].trim();
524
+ const value = data[key];
525
+ if (value !== void 0 && value !== null) {
526
+ if (typeof value === "boolean") {
527
+ return value ? "true" : "false";
528
+ }
529
+ return value;
530
+ }
531
+ return "";
532
+ }
533
+ return obj.replace(/\$\{([^}]+)\}/g, (_match, key) => {
534
+ const trimmedKey = key.trim();
535
+ const value = data[trimmedKey];
536
+ if (value !== void 0 && value !== null) {
537
+ return String(value);
538
+ }
539
+ return "";
540
+ });
541
+ }
542
+ if (Array.isArray(obj)) {
543
+ return obj.map((item) => expandTemplate(item, data));
544
+ }
545
+ if (obj !== null && typeof obj === "object") {
546
+ const result = {};
547
+ for (const [key, value] of Object.entries(obj)) {
548
+ if (key === "defaultValue") {
549
+ continue;
550
+ }
551
+ result[key] = expandTemplate(value, data);
552
+ }
553
+ return result;
554
+ }
555
+ return obj;
556
+ }
240
557
  function applyCustomStyles(container) {
241
558
  const inputStyles = {
242
559
  width: "100%",
@@ -438,14 +755,11 @@ function applyCustomStyles(container) {
438
755
  });
439
756
  });
440
757
  }
441
- var AdaptiveCardRenderer = ({
442
- cardData,
443
- sampleData,
444
- onSubmit,
445
- onAction,
446
- className
447
- }) => {
758
+ var AdaptiveCardRenderer = forwardRef(function AdaptiveCardRenderer2({ cardData, sampleData, onSubmit, onAction, className }, ref) {
448
759
  const cardContainerRef = useRef(null);
760
+ const adaptiveCardInstanceRef = useRef(
761
+ null
762
+ );
449
763
  const initialSampleDataRef = useRef(sampleData);
450
764
  const hasRenderedRef = useRef(false);
451
765
  const onSubmitRef = useRef(onSubmit);
@@ -460,12 +774,16 @@ var AdaptiveCardRenderer = ({
460
774
  initialSampleDataRef.current = sampleData;
461
775
  }, [sampleData]);
462
776
  useEffect(() => {
463
- if (!cardData || !cardContainerRef.current) return;
777
+ if (!cardData || !cardContainerRef.current) {
778
+ adaptiveCardInstanceRef.current = null;
779
+ return;
780
+ }
464
781
  if (!isAdaptiveCard(cardData)) {
465
782
  console.warn(
466
783
  'AdaptiveCardRenderer: Invalid format. Expected type "AdaptiveCard"'
467
784
  );
468
785
  cardContainerRef.current.innerHTML = "";
786
+ adaptiveCardInstanceRef.current = null;
469
787
  return;
470
788
  }
471
789
  const adaptiveCard = new AdaptiveCards.AdaptiveCard();
@@ -503,6 +821,7 @@ var AdaptiveCardRenderer = ({
503
821
  hasRenderedRef.current = true;
504
822
  cardToRender = expandTemplate(cardData, mergedData);
505
823
  adaptiveCard.parse(cardToRender);
824
+ adaptiveCardInstanceRef.current = adaptiveCard;
506
825
  if (Object.keys(mergedData).length > 0) {
507
826
  const allInputs = adaptiveCard.getAllInputs();
508
827
  for (const input of allInputs) {
@@ -538,6 +857,23 @@ var AdaptiveCardRenderer = ({
538
857
  applyCustomStyles(cardContainerRef.current);
539
858
  }
540
859
  }, [cardData]);
860
+ useImperativeHandle(
861
+ ref,
862
+ () => ({
863
+ collectValues() {
864
+ const adaptiveCard = adaptiveCardInstanceRef.current;
865
+ if (!adaptiveCard || !cardData) return {};
866
+ const collected = {};
867
+ for (const input of adaptiveCard.getAllInputs()) {
868
+ if (input.id) {
869
+ collected[input.id] = input.value;
870
+ }
871
+ }
872
+ return coerceSubmittedDataFromCard(collected, cardData);
873
+ }
874
+ }),
875
+ [cardData]
876
+ );
541
877
  if (!cardData) {
542
878
  return null;
543
879
  }
@@ -558,8 +894,260 @@ var AdaptiveCardRenderer = ({
558
894
  className: cn("adaptive-card-container", className)
559
895
  }
560
896
  );
561
- };
897
+ });
562
898
  var components_default = AdaptiveCardRenderer;
899
+
900
+ // src/modules/ui-blocks/parse.ts
901
+ function isRecord(value) {
902
+ return typeof value === "object" && value !== null && !Array.isArray(value);
903
+ }
904
+ function validateUiSpec(raw) {
905
+ if (!isRecord(raw)) return "ui is not an object";
906
+ if (raw.version !== 1)
907
+ return `unsupported ui.version: ${String(raw.version)}`;
908
+ if (!Array.isArray(raw.blocks) || raw.blocks.length === 0) {
909
+ return "ui.blocks must be a non-empty array";
910
+ }
911
+ const seenIds = /* @__PURE__ */ new Set();
912
+ const blocks = [];
913
+ for (const [index, rawBlock] of raw.blocks.entries()) {
914
+ if (!isRecord(rawBlock)) return `ui.blocks[${index}] is not an object`;
915
+ const id = rawBlock.id;
916
+ if (typeof id !== "string" || id.length === 0) {
917
+ return `ui.blocks[${index}] is missing a non-empty string id`;
918
+ }
919
+ if (seenIds.has(id)) return `ui.blocks has duplicate id "${id}"`;
920
+ seenIds.add(id);
921
+ if (rawBlock.type === "adaptive_card") {
922
+ if (!isRecord(rawBlock.card)) {
923
+ return `ui.blocks[${index}] ("${id}") has no card object`;
924
+ }
925
+ blocks.push({ id, type: "adaptive_card", card: rawBlock.card });
926
+ } else if (rawBlock.type === "custom") {
927
+ if (typeof rawBlock.component !== "string" || !rawBlock.component) {
928
+ return `ui.blocks[${index}] ("${id}") has no component name`;
929
+ }
930
+ blocks.push({
931
+ id,
932
+ type: "custom",
933
+ component: rawBlock.component,
934
+ props: isRecord(rawBlock.props) ? rawBlock.props : void 0
935
+ });
936
+ } else {
937
+ return `ui.blocks[${index}] ("${id}") has unknown type "${String(rawBlock.type)}"`;
938
+ }
939
+ }
940
+ return { version: 1, blocks };
941
+ }
942
+ function resolveUiSpec(operation) {
943
+ if (!operation) return null;
944
+ if (operation.ui !== void 0) {
945
+ const result = validateUiSpec(operation.ui);
946
+ if (typeof result !== "string") {
947
+ return { spec: result, synthesized: false };
948
+ }
949
+ console.warn(
950
+ `ui-blocks: invalid ui declaration on operation "${operation.name ?? "?"}" (${result}); falling back to adaptive_card`
951
+ );
952
+ }
953
+ if (isRecord(operation.adaptive_card)) {
954
+ return {
955
+ spec: {
956
+ version: 1,
957
+ blocks: [
958
+ { id: "main", type: "adaptive_card", card: operation.adaptive_card }
959
+ ]
960
+ },
961
+ synthesized: true
962
+ };
963
+ }
964
+ return null;
965
+ }
966
+ function isSubmitAction(action) {
967
+ return isRecord(action) && action.type === "Action.Submit";
968
+ }
969
+ function stripFromActions(actions) {
970
+ return actions.filter((action) => !isSubmitAction(action)).map(
971
+ (action) => isRecord(action) && isRecord(action.card) ? { ...action, card: stripSubmitActions(action.card) } : action
972
+ );
973
+ }
974
+ function stripFromElements(elements) {
975
+ const result = [];
976
+ for (const element of elements) {
977
+ if (!isRecord(element)) {
978
+ result.push(element);
979
+ continue;
980
+ }
981
+ const el = { ...element };
982
+ if (el.type === "ActionSet" && Array.isArray(el.actions)) {
983
+ const remaining = stripFromActions(el.actions);
984
+ if (remaining.length === 0) continue;
985
+ el.actions = remaining;
986
+ }
987
+ if (Array.isArray(el.items)) {
988
+ el.items = stripFromElements(el.items);
989
+ }
990
+ if (Array.isArray(el.columns)) {
991
+ el.columns = el.columns.map(
992
+ (column) => isRecord(column) && Array.isArray(column.items) ? { ...column, items: stripFromElements(column.items) } : column
993
+ );
994
+ }
995
+ result.push(el);
996
+ }
997
+ return result;
998
+ }
999
+ function stripSubmitActions(card) {
1000
+ const result = { ...card };
1001
+ if (Array.isArray(result.actions)) {
1002
+ result.actions = stripFromActions(result.actions);
1003
+ }
1004
+ if (Array.isArray(result.body)) {
1005
+ result.body = stripFromElements(result.body);
1006
+ }
1007
+ return result;
1008
+ }
1009
+ function useStableJson(value) {
1010
+ const jsonRef = useRef(void 0);
1011
+ const stableRef = useRef(void 0);
1012
+ return useMemo(() => {
1013
+ const json = value === void 0 ? void 0 : JSON.stringify(value);
1014
+ if (json !== jsonRef.current) {
1015
+ jsonRef.current = json;
1016
+ stableRef.current = value;
1017
+ }
1018
+ return stableRef.current;
1019
+ }, [value]);
1020
+ }
1021
+ var AdaptiveCardBlock = forwardRef(function AdaptiveCardBlock2({ card, state, submitMode, onSubmit }, ref) {
1022
+ const rendererRef = useRef(null);
1023
+ const cardToRender = useMemo(
1024
+ () => submitMode === "panel" ? stripSubmitActions(card) : card,
1025
+ [card, submitMode]
1026
+ );
1027
+ const stableCard = useStableJson(cardToRender);
1028
+ useImperativeHandle(
1029
+ ref,
1030
+ () => ({
1031
+ collectValues() {
1032
+ return rendererRef.current?.collectValues() ?? {};
1033
+ }
1034
+ }),
1035
+ []
1036
+ );
1037
+ return /* @__PURE__ */ jsx(
1038
+ components_default,
1039
+ {
1040
+ ref: rendererRef,
1041
+ cardData: stableCard ?? null,
1042
+ sampleData: state,
1043
+ onSubmit: submitMode === "card" ? onSubmit : void 0
1044
+ }
1045
+ );
1046
+ });
1047
+ var UnknownBlock = forwardRef(
1048
+ function UnknownBlock2({ title, detail }, ref) {
1049
+ useImperativeHandle(ref, () => ({ collectValues: () => ({}) }), []);
1050
+ return /* @__PURE__ */ jsxs("div", { className: "p-3 border border-[var(--color-status-warning-border)] rounded bg-[var(--color-status-warning-bg-subtle)] text-sm", children: [
1051
+ /* @__PURE__ */ jsx("p", { className: "font-semibold text-[var(--color-status-warning-text)]", children: title }),
1052
+ detail && /* @__PURE__ */ jsx("p", { className: "text-[var(--color-status-warning-text)] mt-1 text-xs", children: detail })
1053
+ ] });
1054
+ }
1055
+ );
1056
+ ensureBuiltinBlocks();
1057
+ function BlocksRenderer({
1058
+ spec,
1059
+ synthesized,
1060
+ state,
1061
+ onApply
1062
+ }) {
1063
+ const handlesRef = useRef(/* @__PURE__ */ new Map());
1064
+ const refCallbacksRef = useRef(
1065
+ /* @__PURE__ */ new Map()
1066
+ );
1067
+ const handleRefFor = (blockId) => {
1068
+ let callback = refCallbacksRef.current.get(blockId);
1069
+ if (!callback) {
1070
+ callback = (handle) => {
1071
+ if (handle) {
1072
+ handlesRef.current.set(blockId, handle);
1073
+ } else {
1074
+ handlesRef.current.delete(blockId);
1075
+ }
1076
+ };
1077
+ refCallbacksRef.current.set(blockId, callback);
1078
+ }
1079
+ return callback;
1080
+ };
1081
+ const handleApply = useCallback(() => {
1082
+ const merged = {};
1083
+ for (const block of spec.blocks) {
1084
+ const handle = handlesRef.current.get(block.id);
1085
+ if (!handle) continue;
1086
+ for (const [rawPath, value] of Object.entries(handle.collectValues())) {
1087
+ const path = rawPath.replace(/^args\.state\./, "");
1088
+ if (path in merged) {
1089
+ console.warn(
1090
+ `ui-blocks: block "${block.id}" overwrites path "${path}" set by an earlier block`
1091
+ );
1092
+ }
1093
+ merged[path] = value;
1094
+ }
1095
+ }
1096
+ onApply(merged);
1097
+ }, [spec, onApply]);
1098
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
1099
+ spec.blocks.map((block) => {
1100
+ if (block.type === "adaptive_card") {
1101
+ return /* @__PURE__ */ jsx(
1102
+ AdaptiveCardBlock,
1103
+ {
1104
+ ref: handleRefFor(block.id),
1105
+ card: block.card,
1106
+ state,
1107
+ submitMode: synthesized ? "card" : "panel",
1108
+ onSubmit: synthesized ? onApply : void 0
1109
+ },
1110
+ block.id
1111
+ );
1112
+ }
1113
+ const Component = getCustomBlock(block.component);
1114
+ if (!Component) {
1115
+ return /* @__PURE__ */ jsx(
1116
+ UnknownBlock,
1117
+ {
1118
+ title: `Unknown block component "${block.component}"`,
1119
+ detail: "This block is not registered in this app. The rest of the panel works normally."
1120
+ },
1121
+ block.id
1122
+ );
1123
+ }
1124
+ const path = typeof block.props?.path === "string" ? block.props.path : "";
1125
+ if (!path) {
1126
+ return /* @__PURE__ */ jsx(
1127
+ UnknownBlock,
1128
+ {
1129
+ title: `Block "${block.id}" is missing required prop "path"`,
1130
+ detail: "The node manifest must declare which args.state field this block edits."
1131
+ },
1132
+ block.id
1133
+ );
1134
+ }
1135
+ return /* @__PURE__ */ jsx(
1136
+ Component,
1137
+ {
1138
+ ref: handleRefFor(block.id),
1139
+ blockId: block.id,
1140
+ path,
1141
+ initialValue: getAtPath(state, path) ?? block.props?.default,
1142
+ state,
1143
+ options: block.props ?? {}
1144
+ },
1145
+ block.id
1146
+ );
1147
+ }),
1148
+ !synthesized && /* @__PURE__ */ jsx(Button, { type: "button", onClick: handleApply, className: "self-start", children: "Apply" })
1149
+ ] });
1150
+ }
563
1151
  var PROVIDER_FIELDS = gql`
564
1152
  fragment ProviderFields on IntegrationProvider {
565
1153
  id
@@ -6704,8 +7292,11 @@ function NodePropertyPanel({
6704
7292
  }
6705
7293
  return [];
6706
7294
  }, [data?.nodeData?.operations, type]);
6707
- const currentOperation = operationsArray.find(
6708
- (op) => op.name === data?.operation_name
7295
+ const currentOperation = useMemo(
7296
+ () => operationsArray.find(
7297
+ (op) => op.name === data?.operation_name
7298
+ ),
7299
+ [operationsArray, data?.operation_name]
6709
7300
  );
6710
7301
  const connectionAdapters = currentOperation?.connectionAdapters || currentOperation?.credentialAdapters || data?.nodeData?.connectionAdapters || data?.nodeData?.credentialAdapters || [];
6711
7302
  const selectedConnections = useMemo(() => {
@@ -6760,19 +7351,10 @@ function NodePropertyPanel({
6760
7351
  }
6761
7352
  });
6762
7353
  };
6763
- const adaptiveTemplateRaw = currentOperation?.adaptive_card;
6764
- const adaptiveTemplateJsonRef = useRef(void 0);
6765
- const adaptiveTemplateStableRef = useRef(
6766
- void 0
7354
+ const resolvedUi = useMemo(
7355
+ () => resolveUiSpec(currentOperation),
7356
+ [currentOperation]
6767
7357
  );
6768
- const adaptiveTemplate = useMemo(() => {
6769
- const json = adaptiveTemplateRaw ? JSON.stringify(adaptiveTemplateRaw) : void 0;
6770
- if (json !== adaptiveTemplateJsonRef.current) {
6771
- adaptiveTemplateJsonRef.current = json;
6772
- adaptiveTemplateStableRef.current = adaptiveTemplateRaw;
6773
- }
6774
- return adaptiveTemplateStableRef.current;
6775
- }, [adaptiveTemplateRaw]);
6776
7358
  const sampleData = useMemo(() => {
6777
7359
  return data?.args?.state ?? {};
6778
7360
  }, [data?.args?.state]);
@@ -6836,13 +7418,15 @@ function NodePropertyPanel({
6836
7418
  ] }),
6837
7419
  /* @__PURE__ */ jsxs("div", { className: "flex pt-5 flex-col", children: [
6838
7420
  /* @__PURE__ */ jsx("div", { className: "text-xs text-card-foreground/60 font-semibold", children: "Properties" }),
6839
- adaptiveTemplate ? /* @__PURE__ */ jsx(
6840
- components_default,
7421
+ resolvedUi ? /* @__PURE__ */ jsx(
7422
+ BlocksRenderer,
6841
7423
  {
6842
- cardData: adaptiveTemplate,
6843
- sampleData,
6844
- onSubmit: submittedData
6845
- }
7424
+ spec: resolvedUi.spec,
7425
+ synthesized: resolvedUi.synthesized,
7426
+ state: sampleData,
7427
+ onApply: submittedData
7428
+ },
7429
+ `${id}:${data?.operation_name ?? ""}`
6846
7430
  ) : /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground py-2", children: "No Properties" })
6847
7431
  ] })
6848
7432
  ] });
@@ -8854,6 +9438,6 @@ function useImportWorkflowTemplate() {
8854
9438
  };
8855
9439
  }
8856
9440
 
8857
- export { AVAILABLE_NODES, ConfigSchemaForm, ConnectionFormDialog, ConnectionSelector, HeaderWithIcon, NODES, NODES_METADATA, NODE_CONFIGS, NODE_TYPES, NodePropertyPanel, ProviderPicker, createNodeWithDefaultData, ensureNodeConfigsLoaded, getNodeDetail, getNodeMetadata, getProviderForm, isNodeConfigsInitialized, onNodeTypesUpdate, registerWorkflowNodeTypes, useConnection, useConnectionDialog, useConnections, useConnectionsByProvider, useCreateConnection, useImportWorkflowTemplate, useInitiateOAuth, useInstallNode, useInstalledNodes, useNodeExecution, useProviderByKey, useProviderCatalog, useProviders, useRefreshAvailableNodes };
8858
- //# sourceMappingURL=chunk-QPZIBO3U.js.map
8859
- //# sourceMappingURL=chunk-QPZIBO3U.js.map
9441
+ export { AVAILABLE_NODES, ConfigSchemaForm, ConnectionFormDialog, ConnectionSelector, HeaderWithIcon, NODES, NODES_METADATA, NODE_CONFIGS, NODE_TYPES, NodePropertyPanel, ProviderPicker, createNodeWithDefaultData, ensureNodeConfigsLoaded, getNodeDetail, getNodeMetadata, getProviderForm, isNodeConfigsInitialized, listCustomBlocks, onNodeTypesUpdate, registerCustomBlock, registerWorkflowNodeTypes, resolveUiSpec, stripSubmitActions, useConnection, useConnectionDialog, useConnections, useConnectionsByProvider, useCreateConnection, useImportWorkflowTemplate, useInitiateOAuth, useInstallNode, useInstalledNodes, useNodeExecution, useProviderByKey, useProviderCatalog, useProviders, useRefreshAvailableNodes };
9442
+ //# sourceMappingURL=chunk-6FP5I5EG.js.map
9443
+ //# sourceMappingURL=chunk-6FP5I5EG.js.map