@uipath/apollo-wind 2.57.0 → 2.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/components/ui/alert-dialog.cjs +7 -6
  2. package/dist/components/ui/alert-dialog.js +7 -6
  3. package/dist/components/ui/code-editors.shared.cjs +323 -0
  4. package/dist/components/ui/code-editors.shared.d.ts +936 -0
  5. package/dist/components/ui/code-editors.shared.js +264 -0
  6. package/dist/components/ui/dialog.cjs +2 -1
  7. package/dist/components/ui/dialog.js +2 -1
  8. package/dist/components/ui/field-actions/ai-assist-action.cjs +51 -7
  9. package/dist/components/ui/field-actions/ai-assist-action.d.ts +12 -3
  10. package/dist/components/ui/field-actions/ai-assist-action.js +52 -8
  11. package/dist/components/ui/field-actions/insert-variable-action.cjs +16 -15
  12. package/dist/components/ui/field-actions/insert-variable-action.d.ts +15 -4
  13. package/dist/components/ui/field-actions/insert-variable-action.js +16 -15
  14. package/dist/components/ui/field-addons/built-in-value-modes.cjs +84 -0
  15. package/dist/components/ui/field-addons/built-in-value-modes.d.ts +21 -0
  16. package/dist/components/ui/field-addons/built-in-value-modes.js +44 -0
  17. package/dist/components/ui/field-addons/{value-mode-menu.cjs → field-menu.cjs} +20 -25
  18. package/dist/components/ui/field-addons/{value-mode-menu.d.ts → field-menu.d.ts} +19 -20
  19. package/dist/components/ui/field-addons/{value-mode-menu.js → field-menu.js} +20 -25
  20. package/dist/components/ui/field-addons/index.cjs +12 -11
  21. package/dist/components/ui/field-addons/index.d.ts +3 -2
  22. package/dist/components/ui/field-addons/index.js +4 -3
  23. package/dist/components/ui/field-addons/value-mode-indicator.cjs +4 -4
  24. package/dist/components/ui/field-addons/value-mode-indicator.d.ts +3 -1
  25. package/dist/components/ui/field-addons/value-mode-indicator.js +5 -5
  26. package/dist/components/ui/field-addons/value-mode-strings.cjs +9 -27
  27. package/dist/components/ui/field-addons/value-mode-strings.d.ts +14 -16
  28. package/dist/components/ui/field-addons/value-mode-strings.js +8 -20
  29. package/dist/components/ui/index.cjs +24 -24
  30. package/dist/components/ui/lockable-value-field/components/field-header.cjs +20 -1
  31. package/dist/components/ui/lockable-value-field/components/field-header.d.ts +1 -1
  32. package/dist/components/ui/lockable-value-field/components/field-header.js +20 -1
  33. package/dist/components/ui/lockable-value-field/lockable-value-field.cjs +3 -3
  34. package/dist/components/ui/lockable-value-field/lockable-value-field.js +1 -1
  35. package/dist/index.cjs +14 -14
  36. package/dist/index.d.ts +6 -6
  37. package/dist/index.js +4 -4
  38. package/dist/lib/dialog-surface.cjs +36 -0
  39. package/dist/lib/dialog-surface.d.ts +1 -0
  40. package/dist/lib/dialog-surface.js +2 -0
  41. package/dist/styles.css +7 -9
  42. package/package.json +1 -1
@@ -0,0 +1,264 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import react from "@monaco-editor/react";
3
+ import { useEffect, useId, useRef, useState } from "react";
4
+ import { apolloCoreDarkCodeMirror, apolloCoreDarkHCCodeMirror, apolloCoreDarkHCMonaco, apolloCoreDarkMonaco, apolloCoreLightCodeMirror, apolloCoreLightHCCodeMirror, apolloCoreLightHCMonaco, apolloCoreLightMonaco, apolloFutureDarkCodeMirror, apolloFutureDarkMonaco, apolloFutureLightCodeMirror, apolloFutureLightMonaco } from "../../editor-themes/index.js";
5
+ const editorThemeConfigs = [
6
+ {
7
+ key: 'future-dark',
8
+ label: 'Future Dark',
9
+ monacoThemeName: 'apollo-future-dark',
10
+ monacoExport: 'apolloFutureDarkMonaco',
11
+ cmExport: 'apolloFutureDarkCodeMirror',
12
+ monacoThemeObj: apolloFutureDarkMonaco,
13
+ cmTokens: apolloFutureDarkCodeMirror,
14
+ isDark: true,
15
+ family: 'Future'
16
+ },
17
+ {
18
+ key: 'future-light',
19
+ label: 'Future Light',
20
+ monacoThemeName: 'apollo-future-light',
21
+ monacoExport: 'apolloFutureLightMonaco',
22
+ cmExport: 'apolloFutureLightCodeMirror',
23
+ monacoThemeObj: apolloFutureLightMonaco,
24
+ cmTokens: apolloFutureLightCodeMirror,
25
+ isDark: false,
26
+ family: 'Future'
27
+ },
28
+ {
29
+ key: 'dark',
30
+ label: 'Dark',
31
+ monacoThemeName: 'apollo-core-dark',
32
+ monacoExport: 'apolloCoreDarkMonaco',
33
+ cmExport: 'apolloCoreDarkCodeMirror',
34
+ monacoThemeObj: apolloCoreDarkMonaco,
35
+ cmTokens: apolloCoreDarkCodeMirror,
36
+ isDark: true,
37
+ family: 'Core'
38
+ },
39
+ {
40
+ key: 'light',
41
+ label: 'Light',
42
+ monacoThemeName: 'apollo-core-light',
43
+ monacoExport: 'apolloCoreLightMonaco',
44
+ cmExport: 'apolloCoreLightCodeMirror',
45
+ monacoThemeObj: apolloCoreLightMonaco,
46
+ cmTokens: apolloCoreLightCodeMirror,
47
+ isDark: false,
48
+ family: 'Core'
49
+ },
50
+ {
51
+ key: 'dark-hc',
52
+ label: 'Dark High Contrast',
53
+ monacoThemeName: 'apollo-core-dark-hc',
54
+ monacoExport: 'apolloCoreDarkHCMonaco',
55
+ cmExport: 'apolloCoreDarkHCCodeMirror',
56
+ monacoThemeObj: apolloCoreDarkHCMonaco,
57
+ cmTokens: apolloCoreDarkHCCodeMirror,
58
+ isDark: true,
59
+ family: 'Core HC'
60
+ },
61
+ {
62
+ key: 'light-hc',
63
+ label: 'Light High Contrast',
64
+ monacoThemeName: 'apollo-core-light-hc',
65
+ monacoExport: 'apolloCoreLightHCMonaco',
66
+ cmExport: 'apolloCoreLightHCCodeMirror',
67
+ monacoThemeObj: apolloCoreLightHCMonaco,
68
+ cmTokens: apolloCoreLightHCCodeMirror,
69
+ isDark: false,
70
+ family: 'Core HC'
71
+ }
72
+ ];
73
+ const monacoFullSample = `import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
74
+
75
+ type SortDirection = 'asc' | 'desc';
76
+
77
+ interface Column<T> {
78
+ key: keyof T;
79
+ header: string;
80
+ sortable?: boolean;
81
+ render?: (value: T[keyof T], row: T) => ReactNode;
82
+ }
83
+
84
+ interface TableProps<T> {
85
+ data: T[];
86
+ columns: Column<T>[];
87
+ pageSize?: number;
88
+ }
89
+
90
+ export function DataTable<T extends { id: string | number }>({
91
+ data,
92
+ columns,
93
+ pageSize = 10,
94
+ }: TableProps<T>) {
95
+ const [query, setQuery] = useState('');
96
+ const [sortKey, setSortKey] = useState<keyof T | null>(null);
97
+ const [sortDir, setSortDir] = useState<SortDirection>('asc');
98
+ const [page, setPage] = useState(1);
99
+
100
+ useEffect(() => { setPage(1); }, [query, sortKey, sortDir]);
101
+
102
+ const filtered = useMemo(() => {
103
+ if (!query.trim()) return data;
104
+ const q = query.toLowerCase();
105
+ return data.filter((row) =>
106
+ columns.some((col) => String(row[col.key]).toLowerCase().includes(q))
107
+ );
108
+ }, [data, columns, query]);
109
+
110
+ const sorted = useMemo(() => {
111
+ if (!sortKey) return filtered;
112
+ return [...filtered].sort((a, b) => {
113
+ const cmp = a[sortKey] < b[sortKey] ? -1 : a[sortKey] > b[sortKey] ? 1 : 0;
114
+ return sortDir === 'asc' ? cmp : -cmp;
115
+ });
116
+ }, [filtered, sortKey, sortDir]);
117
+
118
+ return (
119
+ <div className="flex flex-col gap-4">
120
+ <input
121
+ className="rounded-lg border border-border px-3 py-2 text-sm"
122
+ placeholder="Search..."
123
+ value={query}
124
+ onChange={(e) => setQuery(e.target.value)}
125
+ />
126
+ <table className="w-full text-sm">
127
+ <thead>
128
+ <tr className="border-b border-border">
129
+ {columns.map((col) => (
130
+ <th key={String(col.key)} className="px-4 py-2 text-left">
131
+ {col.header}
132
+ </th>
133
+ ))}
134
+ </tr>
135
+ </thead>
136
+ </table>
137
+ </div>
138
+ );
139
+ }`.trim();
140
+ const monacoSample = `interface WorkflowNode {
141
+ id: string;
142
+ type: 'action' | 'condition' | 'trigger';
143
+ executionStatus: 'NotExecuted' | 'InProgress' | 'Completed' | 'Failed';
144
+ }
145
+
146
+ function getNextNodes(node: WorkflowNode): string[] {
147
+ if (node.executionStatus === 'Completed') {
148
+ return node.type === 'condition'
149
+ ? ['true-branch', 'false-branch']
150
+ : ['next'];
151
+ }
152
+ return [];
153
+ }`.trim();
154
+ let monacoThemesRegistered = false;
155
+ function registerAllMonacoThemes(monaco) {
156
+ if (monacoThemesRegistered) return;
157
+ for (const cfg of editorThemeConfigs)monaco.editor.defineTheme(cfg.monacoThemeName, cfg.monacoThemeObj);
158
+ monacoThemesRegistered = true;
159
+ }
160
+ let demoDefaultsUsers = 0;
161
+ let savedTypescriptDefaults = null;
162
+ function acquireDemoTypescriptDefaults(monaco) {
163
+ demoDefaultsUsers += 1;
164
+ if (demoDefaultsUsers > 1) return;
165
+ const ts = monaco.languages.typescript.typescriptDefaults;
166
+ savedTypescriptDefaults = {
167
+ diagnostics: ts.getDiagnosticsOptions(),
168
+ compiler: ts.getCompilerOptions()
169
+ };
170
+ ts.setDiagnosticsOptions({
171
+ ...ts.getDiagnosticsOptions(),
172
+ noSemanticValidation: true
173
+ });
174
+ ts.setCompilerOptions({
175
+ ...ts.getCompilerOptions(),
176
+ jsx: monaco.languages.typescript.JsxEmit.Preserve
177
+ });
178
+ }
179
+ function releaseDemoTypescriptDefaults(monaco) {
180
+ demoDefaultsUsers -= 1;
181
+ if (demoDefaultsUsers > 0 || !savedTypescriptDefaults) return;
182
+ const ts = monaco.languages.typescript.typescriptDefaults;
183
+ ts.setDiagnosticsOptions(savedTypescriptDefaults.diagnostics);
184
+ ts.setCompilerOptions(savedTypescriptDefaults.compiler);
185
+ savedTypescriptDefaults = null;
186
+ }
187
+ function LiveMonacoEditor({ themeConfig, height = '220px', value, onChange, onMount, language = "typescript", options = {} }) {
188
+ const extension = 'json' === language ? 'json' : 'tsx';
189
+ const modelPath = `file:///editor-${useId().replace(/[^a-zA-Z0-9]/g, '')}.${extension}`;
190
+ const monacoRef = useRef(null);
191
+ useEffect(()=>()=>{
192
+ if (monacoRef.current) releaseDemoTypescriptDefaults(monacoRef.current);
193
+ monacoRef.current = null;
194
+ }, []);
195
+ return /*#__PURE__*/ jsx(react, {
196
+ height: height,
197
+ path: modelPath,
198
+ defaultLanguage: language,
199
+ ...onChange ? {
200
+ value: value ?? ''
201
+ } : {
202
+ defaultValue: value ?? monacoSample
203
+ },
204
+ theme: themeConfig.monacoThemeName,
205
+ beforeMount: (monaco)=>{
206
+ registerAllMonacoThemes(monaco);
207
+ if (!monacoRef.current) {
208
+ monacoRef.current = monaco;
209
+ acquireDemoTypescriptDefaults(monaco);
210
+ }
211
+ },
212
+ onChange: onChange,
213
+ onMount: onMount,
214
+ options: {
215
+ fontSize: 13,
216
+ lineHeight: 20,
217
+ minimap: {
218
+ enabled: false
219
+ },
220
+ scrollBeyondLastLine: false,
221
+ wordWrap: 'on',
222
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace',
223
+ padding: {
224
+ top: 16,
225
+ bottom: 16
226
+ },
227
+ lineNumbers: 'on',
228
+ glyphMargin: false,
229
+ folding: false,
230
+ renderLineHighlight: 'line',
231
+ hideCursorInOverviewRuler: true,
232
+ overviewRulerBorder: false,
233
+ scrollbar: {
234
+ vertical: 'auto',
235
+ horizontal: 'hidden',
236
+ alwaysConsumeMouseWheel: false
237
+ },
238
+ automaticLayout: true,
239
+ fixedOverflowWidgets: true,
240
+ ...options
241
+ }
242
+ });
243
+ }
244
+ const futureDarkConfig = editorThemeConfigs[0];
245
+ function getStorybookThemeConfig() {
246
+ const keys = editorThemeConfigs.map((c)=>c.key);
247
+ const found = Array.from(document.body.classList).find((c)=>keys.includes(c));
248
+ return editorThemeConfigs.find((c)=>c.key === found) ?? futureDarkConfig;
249
+ }
250
+ function useEditorThemeConfig() {
251
+ const [themeConfig, setThemeConfig] = useState(getStorybookThemeConfig);
252
+ useEffect(()=>{
253
+ const observer = new MutationObserver(()=>setThemeConfig(getStorybookThemeConfig()));
254
+ observer.observe(document.body, {
255
+ attributes: true,
256
+ attributeFilter: [
257
+ 'class'
258
+ ]
259
+ });
260
+ return ()=>observer.disconnect();
261
+ }, []);
262
+ return themeConfig;
263
+ }
264
+ export { LiveMonacoEditor, editorThemeConfigs, monacoFullSample, monacoSample, registerAllMonacoThemes, useEditorThemeConfig };
@@ -53,6 +53,7 @@ const external_lucide_react_namespaceObject = require("lucide-react");
53
53
  const external_react_namespaceObject = require("react");
54
54
  const external_button_cjs_namespaceObject = require("./button.cjs");
55
55
  const external_portal_container_cjs_namespaceObject = require("./portal-container.cjs");
56
+ const dialog_surface_cjs_namespaceObject = require("../../lib/dialog-surface.cjs");
56
57
  const index_cjs_namespaceObject = require("../../lib/index.cjs");
57
58
  function Dialog({ ...props }) {
58
59
  return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(react_dialog_namespaceObject.Root, {
@@ -73,7 +74,7 @@ function DialogPortal({ ...props }) {
73
74
  ...props
74
75
  });
75
76
  }
76
- const dialogContentVariants = (0, external_class_variance_authority_namespaceObject.cva)('bg-surface-raised text-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 relative z-50 border border-border-subtle shadow-xl duration-200', {
77
+ const dialogContentVariants = (0, external_class_variance_authority_namespaceObject.cva)(`${dialog_surface_cjs_namespaceObject.dialogSurfaceClassName} data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 relative z-50 duration-200`, {
77
78
  variants: {
78
79
  variant: {
79
80
  default: 'grid w-full max-w-[calc(100%-2rem)] gap-4 rounded-2xl p-6 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 sm:max-w-lg',
@@ -6,6 +6,7 @@ import { Maximize2, Minimize2, XIcon } from "lucide-react";
6
6
  import { forwardRef, useState } from "react";
7
7
  import { Button } from "./button.js";
8
8
  import { useResolvedPortalContainer } from "./portal-container.js";
9
+ import { dialogSurfaceClassName } from "../../lib/dialog-surface.js";
9
10
  import { cn } from "../../lib/index.js";
10
11
  function Dialog({ ...props }) {
11
12
  return /*#__PURE__*/ jsx(Root, {
@@ -26,7 +27,7 @@ function DialogPortal({ ...props }) {
26
27
  ...props
27
28
  });
28
29
  }
29
- const dialogContentVariants = cva('bg-surface-raised text-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 relative z-50 border border-border-subtle shadow-xl duration-200', {
30
+ const dialogContentVariants = cva(`${dialogSurfaceClassName} data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 relative z-50 duration-200`, {
30
31
  variants: {
31
32
  variant: {
32
33
  default: 'grid w-full max-w-[calc(100%-2rem)] gap-4 rounded-2xl p-6 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 sm:max-w-lg',
@@ -40,16 +40,42 @@ const DEFAULT_AI_ASSIST_ACTION_STRINGS = {
40
40
  tooltip: 'Generate with AI',
41
41
  prompt: 'Describe what you want',
42
42
  promptPlaceholder: 'Display a value from the previous step',
43
- generate: 'Generate'
43
+ generate: 'Generate',
44
+ generating: 'Generating',
45
+ error: 'Could not generate a value. Try again.'
44
46
  };
45
- function AiAssistAction({ onGenerate, hint, strings }) {
47
+ function AiAssistAction({ onGenerate, hint, disabled, strings }) {
46
48
  const promptId = (0, external_react_namespaceObject.useId)();
49
+ const errorId = (0, external_react_namespaceObject.useId)();
47
50
  const [prompt, setPrompt] = (0, external_react_namespaceObject.useState)('');
51
+ const [open, setOpen] = (0, external_react_namespaceObject.useState)(false);
52
+ const [pending, setPending] = (0, external_react_namespaceObject.useState)(false);
53
+ const [failed, setFailed] = (0, external_react_namespaceObject.useState)(false);
48
54
  const text = {
49
55
  ...DEFAULT_AI_ASSIST_ACTION_STRINGS,
50
56
  ...strings
51
57
  };
58
+ const changeOpen = (next)=>{
59
+ setFailed(false);
60
+ setOpen(next);
61
+ };
62
+ const generate = async ()=>{
63
+ setFailed(false);
64
+ const result = onGenerate?.(prompt);
65
+ if (!isThenable(result)) return;
66
+ setPending(true);
67
+ try {
68
+ await result;
69
+ setOpen(false);
70
+ } catch {
71
+ setFailed(true);
72
+ } finally{
73
+ setPending(false);
74
+ }
75
+ };
52
76
  return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(external_popover_cjs_namespaceObject.Popover, {
77
+ open: open && !disabled,
78
+ onOpenChange: changeOpen,
53
79
  children: [
54
80
  /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(external_tooltip_cjs_namespaceObject.Tooltip, {
55
81
  children: [
@@ -60,7 +86,8 @@ function AiAssistAction({ onGenerate, hint, strings }) {
60
86
  children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("button", {
61
87
  type: "button",
62
88
  "aria-label": text.trigger,
63
- className: "grid size-7 place-items-center rounded-lg text-foreground-subtle transition hover:bg-surface-overlay hover:text-foreground",
89
+ disabled: disabled,
90
+ className: "grid size-7 place-items-center rounded-lg text-foreground-subtle transition hover:bg-surface-overlay hover:text-foreground disabled:pointer-events-none disabled:opacity-50",
64
91
  children: /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_lucide_react_namespaceObject.Sparkles, {
65
92
  size: 12
66
93
  })
@@ -90,6 +117,7 @@ function AiAssistAction({ onGenerate, hint, strings }) {
90
117
  value: prompt,
91
118
  onChange: (e)=>setPrompt(e.target.value),
92
119
  placeholder: text.promptPlaceholder,
120
+ "aria-describedby": failed ? errorId : void 0,
93
121
  className: "resize-none text-sm"
94
122
  })
95
123
  ]
@@ -98,18 +126,34 @@ function AiAssistAction({ onGenerate, hint, strings }) {
98
126
  className: "block text-[11px] text-foreground-subtle",
99
127
  children: hint
100
128
  }),
101
- /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_button_cjs_namespaceObject.Button, {
129
+ failed && /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("p", {
130
+ id: errorId,
131
+ role: "alert",
132
+ className: "text-xs text-error",
133
+ children: text.error
134
+ }),
135
+ /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(external_button_cjs_namespaceObject.Button, {
102
136
  size: "sm",
103
137
  className: "w-full",
104
- disabled: !onGenerate,
105
- onClick: ()=>onGenerate?.(prompt),
106
- children: text.generate
138
+ disabled: !onGenerate || pending,
139
+ "aria-busy": pending || void 0,
140
+ onClick: ()=>generate(),
141
+ children: [
142
+ pending && /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_lucide_react_namespaceObject.Loader2, {
143
+ className: "animate-spin",
144
+ "aria-hidden": "true"
145
+ }),
146
+ pending ? text.generating : text.generate
147
+ ]
107
148
  })
108
149
  ]
109
150
  })
110
151
  ]
111
152
  });
112
153
  }
154
+ function isThenable(value) {
155
+ return ('object' == typeof value || 'function' == typeof value) && null !== value && 'function' == typeof value.then;
156
+ }
113
157
  exports.AiAssistAction = __webpack_exports__.AiAssistAction;
114
158
  exports.DEFAULT_AI_ASSIST_ACTION_STRINGS = __webpack_exports__.DEFAULT_AI_ASSIST_ACTION_STRINGS;
115
159
  for(var __rspack_i in __webpack_exports__)if (-1 === [
@@ -5,11 +5,20 @@ export interface AiAssistActionStrings {
5
5
  prompt: string;
6
6
  promptPlaceholder: string;
7
7
  generate: string;
8
+ /** Replaces `generate` while a returned promise is pending. */
9
+ generating: string;
10
+ /** Shown when a returned promise rejects. */
11
+ error: string;
8
12
  }
9
13
  export declare const DEFAULT_AI_ASSIST_ACTION_STRINGS: AiAssistActionStrings;
10
14
  export interface AiAssistActionProps {
11
- /** Called with the entered prompt. Omitted, Generate is disabled. */
12
- onGenerate?: (prompt: string) => void;
15
+ /**
16
+ * Called with the entered prompt. Omitted, Generate is disabled. A returned promise keeps the
17
+ * prompt open and busy until it settles: it closes once the promise resolves and stays open,
18
+ * prompt intact and the error shown, if it rejects.
19
+ */
20
+ onGenerate?: (prompt: string) => unknown;
21
+ disabled?: boolean;
13
22
  /** A line under the prompt describing what will be generated, such as the value's type. */
14
23
  hint?: ReactNode;
15
24
  /** Overrides for any subset of the English strings. */
@@ -18,4 +27,4 @@ export interface AiAssistActionProps {
18
27
  /**
19
28
  * The AI-assist header action: a prompt in a popover. Requires an ancestor `TooltipProvider`.
20
29
  */
21
- export declare function AiAssistAction({ onGenerate, hint, strings }: AiAssistActionProps): import("react/jsx-runtime").JSX.Element;
30
+ export declare function AiAssistAction({ onGenerate, hint, disabled, strings }: AiAssistActionProps): import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { Sparkles } from "lucide-react";
2
+ import { Loader2, Sparkles } from "lucide-react";
3
3
  import { useId, useState } from "react";
4
4
  import { Button } from "../button.js";
5
5
  import { Label } from "../label.js";
@@ -11,16 +11,42 @@ const DEFAULT_AI_ASSIST_ACTION_STRINGS = {
11
11
  tooltip: 'Generate with AI',
12
12
  prompt: 'Describe what you want',
13
13
  promptPlaceholder: 'Display a value from the previous step',
14
- generate: 'Generate'
14
+ generate: 'Generate',
15
+ generating: 'Generating',
16
+ error: 'Could not generate a value. Try again.'
15
17
  };
16
- function AiAssistAction({ onGenerate, hint, strings }) {
18
+ function AiAssistAction({ onGenerate, hint, disabled, strings }) {
17
19
  const promptId = useId();
20
+ const errorId = useId();
18
21
  const [prompt, setPrompt] = useState('');
22
+ const [open, setOpen] = useState(false);
23
+ const [pending, setPending] = useState(false);
24
+ const [failed, setFailed] = useState(false);
19
25
  const text = {
20
26
  ...DEFAULT_AI_ASSIST_ACTION_STRINGS,
21
27
  ...strings
22
28
  };
29
+ const changeOpen = (next)=>{
30
+ setFailed(false);
31
+ setOpen(next);
32
+ };
33
+ const generate = async ()=>{
34
+ setFailed(false);
35
+ const result = onGenerate?.(prompt);
36
+ if (!isThenable(result)) return;
37
+ setPending(true);
38
+ try {
39
+ await result;
40
+ setOpen(false);
41
+ } catch {
42
+ setFailed(true);
43
+ } finally{
44
+ setPending(false);
45
+ }
46
+ };
23
47
  return /*#__PURE__*/ jsxs(Popover, {
48
+ open: open && !disabled,
49
+ onOpenChange: changeOpen,
24
50
  children: [
25
51
  /*#__PURE__*/ jsxs(Tooltip, {
26
52
  children: [
@@ -31,7 +57,8 @@ function AiAssistAction({ onGenerate, hint, strings }) {
31
57
  children: /*#__PURE__*/ jsx("button", {
32
58
  type: "button",
33
59
  "aria-label": text.trigger,
34
- className: "grid size-7 place-items-center rounded-lg text-foreground-subtle transition hover:bg-surface-overlay hover:text-foreground",
60
+ disabled: disabled,
61
+ className: "grid size-7 place-items-center rounded-lg text-foreground-subtle transition hover:bg-surface-overlay hover:text-foreground disabled:pointer-events-none disabled:opacity-50",
35
62
  children: /*#__PURE__*/ jsx(Sparkles, {
36
63
  size: 12
37
64
  })
@@ -61,6 +88,7 @@ function AiAssistAction({ onGenerate, hint, strings }) {
61
88
  value: prompt,
62
89
  onChange: (e)=>setPrompt(e.target.value),
63
90
  placeholder: text.promptPlaceholder,
91
+ "aria-describedby": failed ? errorId : void 0,
64
92
  className: "resize-none text-sm"
65
93
  })
66
94
  ]
@@ -69,16 +97,32 @@ function AiAssistAction({ onGenerate, hint, strings }) {
69
97
  className: "block text-[11px] text-foreground-subtle",
70
98
  children: hint
71
99
  }),
72
- /*#__PURE__*/ jsx(Button, {
100
+ failed && /*#__PURE__*/ jsx("p", {
101
+ id: errorId,
102
+ role: "alert",
103
+ className: "text-xs text-error",
104
+ children: text.error
105
+ }),
106
+ /*#__PURE__*/ jsxs(Button, {
73
107
  size: "sm",
74
108
  className: "w-full",
75
- disabled: !onGenerate,
76
- onClick: ()=>onGenerate?.(prompt),
77
- children: text.generate
109
+ disabled: !onGenerate || pending,
110
+ "aria-busy": pending || void 0,
111
+ onClick: ()=>generate(),
112
+ children: [
113
+ pending && /*#__PURE__*/ jsx(Loader2, {
114
+ className: "animate-spin",
115
+ "aria-hidden": "true"
116
+ }),
117
+ pending ? text.generating : text.generate
118
+ ]
78
119
  })
79
120
  ]
80
121
  })
81
122
  ]
82
123
  });
83
124
  }
125
+ function isThenable(value) {
126
+ return ('object' == typeof value || 'function' == typeof value) && null !== value && 'function' == typeof value.then;
127
+ }
84
128
  export { AiAssistAction, DEFAULT_AI_ASSIST_ACTION_STRINGS };
@@ -29,35 +29,36 @@ __webpack_require__.d(__webpack_exports__, {
29
29
  });
30
30
  const jsx_runtime_namespaceObject = require("react/jsx-runtime");
31
31
  const external_lucide_react_namespaceObject = require("lucide-react");
32
+ const external_react_namespaceObject = require("react");
32
33
  const index_cjs_namespaceObject = require("../variable-picker/index.cjs");
33
34
  const external_lib_index_cjs_namespaceObject = require("../../../lib/index.cjs");
34
35
  const external_collapse_cjs_namespaceObject = require("./collapse.cjs");
35
36
  const DEFAULT_INSERT_VARIABLE_ACTION_STRINGS = {
36
37
  label: 'Insert',
37
- ariaLabel: 'Insert variable'
38
+ ariaLabel: 'Insert variable',
39
+ searchPlaceholder: 'Search variables...',
40
+ empty: 'No variables found.',
41
+ replaceTitle: 'Replace the value?',
42
+ replaceDescription: 'The variable will replace the current value. This cannot be undone.',
43
+ replaceConfirm: 'Replace'
38
44
  };
39
45
  function InsertVariableAction({ variables, onInsert, disabled, compact, strings }) {
40
46
  const text = {
41
47
  ...DEFAULT_INSERT_VARIABLE_ACTION_STRINGS,
42
48
  ...strings
43
49
  };
44
- const inert = disabled || 0 === variables.length || !onInsert;
50
+ const lazy = 'function' == typeof variables;
51
+ const inert = disabled || !onInsert || !lazy && 0 === variables.length;
52
+ const [openedItems, setOpenedItems] = (0, external_react_namespaceObject.useState)([]);
45
53
  const collapsedTextClass = (0, external_lib_index_cjs_namespaceObject.cn)(external_collapse_cjs_namespaceObject.COLLAPSED_HIDDEN, compact && '!hidden');
46
54
  return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(index_cjs_namespaceObject.VariablePicker, {
47
55
  disabled: inert,
48
- items: [
49
- {
50
- id: 'vars',
51
- label: '$vars',
52
- type: 'object',
53
- children: variables.map((variable)=>({
54
- id: variable.value,
55
- label: variable.label,
56
- value: variable.value,
57
- type: 'string'
58
- }))
59
- }
60
- ],
56
+ items: lazy ? openedItems : variables,
57
+ placeholder: text.searchPlaceholder,
58
+ emptyText: text.empty,
59
+ onOpenChange: (open)=>{
60
+ if (open && lazy) setOpenedItems(variables());
61
+ },
61
62
  onSelect: (variable)=>{
62
63
  if (variable.value) onInsert?.(variable.value);
63
64
  },
@@ -1,15 +1,26 @@
1
+ import { type VariablePickerItem } from '../variable-picker';
1
2
  export interface InsertVariableActionStrings {
2
3
  /** Visible text, hidden when the action collapses to its icon. */
3
4
  label: string;
4
5
  /** Accessible name and hover title. */
5
6
  ariaLabel: string;
7
+ /** The picker's search box. */
8
+ searchPlaceholder: string;
9
+ /** The picker with nothing to show. */
10
+ empty: string;
11
+ /** Confirms replacing a value that cannot take the reference at a caret. */
12
+ replaceTitle: string;
13
+ replaceDescription: string;
14
+ replaceConfirm: string;
6
15
  }
7
16
  export declare const DEFAULT_INSERT_VARIABLE_ACTION_STRINGS: InsertVariableActionStrings;
8
17
  export interface InsertVariableActionProps {
9
- variables: {
10
- label: string;
11
- value: string;
12
- }[];
18
+ /**
19
+ * The variables to offer, rendered as supplied: the picker adds no root and no types. A function
20
+ * is called each time the picker opens, never during render, so a host can hand over a live
21
+ * source without re-rendering the field whenever it changes.
22
+ */
23
+ variables: VariablePickerItem[] | (() => VariablePickerItem[]);
13
24
  /** Called with the picked variable's value. Omitted, the action is disabled. */
14
25
  onInsert?: (value: string) => void;
15
26
  disabled?: boolean;
@@ -1,34 +1,35 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { Braces, ChevronDown } from "lucide-react";
3
+ import { useState } from "react";
3
4
  import { VariablePicker } from "../variable-picker/index.js";
4
5
  import { cn } from "../../../lib/index.js";
5
6
  import { COLLAPSED_HIDDEN, COLLAPSED_ICON_PADDING } from "./collapse.js";
6
7
  const DEFAULT_INSERT_VARIABLE_ACTION_STRINGS = {
7
8
  label: 'Insert',
8
- ariaLabel: 'Insert variable'
9
+ ariaLabel: 'Insert variable',
10
+ searchPlaceholder: 'Search variables...',
11
+ empty: 'No variables found.',
12
+ replaceTitle: 'Replace the value?',
13
+ replaceDescription: 'The variable will replace the current value. This cannot be undone.',
14
+ replaceConfirm: 'Replace'
9
15
  };
10
16
  function InsertVariableAction({ variables, onInsert, disabled, compact, strings }) {
11
17
  const text = {
12
18
  ...DEFAULT_INSERT_VARIABLE_ACTION_STRINGS,
13
19
  ...strings
14
20
  };
15
- const inert = disabled || 0 === variables.length || !onInsert;
21
+ const lazy = 'function' == typeof variables;
22
+ const inert = disabled || !onInsert || !lazy && 0 === variables.length;
23
+ const [openedItems, setOpenedItems] = useState([]);
16
24
  const collapsedTextClass = cn(COLLAPSED_HIDDEN, compact && '!hidden');
17
25
  return /*#__PURE__*/ jsx(VariablePicker, {
18
26
  disabled: inert,
19
- items: [
20
- {
21
- id: 'vars',
22
- label: '$vars',
23
- type: 'object',
24
- children: variables.map((variable)=>({
25
- id: variable.value,
26
- label: variable.label,
27
- value: variable.value,
28
- type: 'string'
29
- }))
30
- }
31
- ],
27
+ items: lazy ? openedItems : variables,
28
+ placeholder: text.searchPlaceholder,
29
+ emptyText: text.empty,
30
+ onOpenChange: (open)=>{
31
+ if (open && lazy) setOpenedItems(variables());
32
+ },
32
33
  onSelect: (variable)=>{
33
34
  if (variable.value) onInsert?.(variable.value);
34
35
  },