@parallelworks/ui 0.51.0 → 0.52.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 (227) hide show
  1. package/README.md +155 -14
  2. package/dist/ai/agent/toolStrings.d.ts +2 -0
  3. package/dist/ai/agent/toolStrings.js +20 -18
  4. package/dist/ai/ai.css +12 -0
  5. package/dist/ai/components/AllocationSelector.js +5 -1
  6. package/dist/ai/components/AttachmentManager.js +51 -42
  7. package/dist/ai/components/AttachmentUpload.d.ts +4 -2
  8. package/dist/ai/components/AttachmentUpload.js +9 -9
  9. package/dist/ai/components/BlockedGroupBanner.js +8 -14
  10. package/dist/ai/components/BranchNavigator.js +11 -12
  11. package/dist/ai/components/ChatEmptyState.js +12 -19
  12. package/dist/ai/components/ChatInput.d.ts +4 -3
  13. package/dist/ai/components/ChatInput.js +27 -13
  14. package/dist/ai/components/ChatMessage.js +45 -121
  15. package/dist/ai/components/ChatMessageList.d.ts +5 -2
  16. package/dist/ai/components/ChatMessageList.js +89 -51
  17. package/dist/ai/components/ChatSidebar.js +11 -7
  18. package/dist/ai/components/ChatThread.js +1 -13
  19. package/dist/ai/components/DragOverlay.js +3 -3
  20. package/dist/ai/components/MessageAttachments.d.ts +10 -0
  21. package/dist/ai/components/MessageAttachments.js +78 -0
  22. package/dist/ai/components/ModelSelector.js +3 -3
  23. package/dist/ai/components/PastedText.js +2 -2
  24. package/dist/ai/components/ProviderIssueBanner.js +19 -23
  25. package/dist/ai/components/ShareDialog.js +2 -2
  26. package/dist/ai/components/agent/ActivityLine.d.ts +0 -2
  27. package/dist/ai/components/agent/ActivityLine.js +8 -51
  28. package/dist/ai/components/agent/AgentMessageParts.js +114 -53
  29. package/dist/ai/core/ChatProvider.d.ts +16 -5
  30. package/dist/ai/core/ChatProvider.js +41 -8
  31. package/dist/ai/core/config.js +4 -3
  32. package/dist/ai/index.d.ts +1 -1
  33. package/dist/ai/index.js +3 -3
  34. package/dist/ai/strings.d.ts +29 -3
  35. package/dist/ai/strings.js +81 -26
  36. package/dist/ai/types.d.ts +4 -0
  37. package/dist/ai/ui/Notice.d.ts +2 -2
  38. package/dist/ai/ui/Notice.js +29 -62
  39. package/dist/ai/ui/sidebar.d.ts +5 -3
  40. package/dist/ai/ui/sidebar.js +21 -9
  41. package/dist/components/ActionMenu.d.ts +1 -0
  42. package/dist/components/ActionMenu.js +6 -1
  43. package/dist/components/AppLink.d.ts +26 -0
  44. package/dist/components/AppLink.js +51 -0
  45. package/dist/components/Avatar.d.ts +2 -1
  46. package/dist/components/Avatar.js +4 -2
  47. package/dist/components/Button.d.ts +15 -5
  48. package/dist/components/Button.js +47 -40
  49. package/dist/components/Callout.d.ts +31 -7
  50. package/dist/components/Callout.js +50 -19
  51. package/dist/components/Card.d.ts +11 -6
  52. package/dist/components/Card.js +14 -8
  53. package/dist/components/Checkbox.js +2 -1
  54. package/dist/components/ColorInput.js +5 -3
  55. package/dist/components/Combobox.js +49 -19
  56. package/dist/components/ConfirmModal.d.ts +19 -8
  57. package/dist/components/ConfirmModal.js +31 -53
  58. package/dist/components/CopyButton.d.ts +38 -0
  59. package/dist/components/CopyButton.js +68 -0
  60. package/dist/components/CreateModal.js +118 -111
  61. package/dist/components/DatePicker.js +12 -8
  62. package/dist/components/DescriptionList.d.ts +25 -5
  63. package/dist/components/DescriptionList.js +56 -22
  64. package/dist/components/Disclosure.js +1 -1
  65. package/dist/components/Drawer.js +10 -16
  66. package/dist/components/Field.d.ts +15 -5
  67. package/dist/components/Field.js +40 -8
  68. package/dist/components/IconButton.d.ts +14 -5
  69. package/dist/components/IconButton.js +25 -14
  70. package/dist/components/InfoTip.d.ts +4 -1
  71. package/dist/components/InfoTip.js +2 -1
  72. package/dist/components/Input.d.ts +14 -8
  73. package/dist/components/Input.js +46 -31
  74. package/dist/components/Loader.js +1 -1
  75. package/dist/components/MenuEntries.d.ts +4 -0
  76. package/dist/components/MenuEntries.js +11 -12
  77. package/dist/components/Meter.js +5 -4
  78. package/dist/components/Modal.d.ts +28 -4
  79. package/dist/components/Modal.js +37 -14
  80. package/dist/components/Overview.d.ts +20 -12
  81. package/dist/components/Overview.js +14 -9
  82. package/dist/components/PageHeader.d.ts +47 -65
  83. package/dist/components/PageHeader.js +275 -153
  84. package/dist/components/PageHeaderAction.d.ts +67 -0
  85. package/dist/components/PageHeaderAction.js +107 -0
  86. package/dist/components/Pagination.js +6 -16
  87. package/dist/components/Popover.js +3 -2
  88. package/dist/components/Provider.d.ts +77 -19
  89. package/dist/components/Provider.js +139 -130
  90. package/dist/components/Radio.js +2 -1
  91. package/dist/components/RefreshButton.js +7 -8
  92. package/dist/components/RequiredMark.js +1 -1
  93. package/dist/components/Section.js +1 -1
  94. package/dist/components/Select.d.ts +4 -9
  95. package/dist/components/Select.js +24 -11
  96. package/dist/components/SettingModeOption.d.ts +1 -1
  97. package/dist/components/SettingModeOption.js +7 -1
  98. package/dist/components/SettingsCard.d.ts +5 -3
  99. package/dist/components/SettingsCard.js +27 -20
  100. package/dist/components/Slider.js +1 -1
  101. package/dist/components/StatusBadge.js +2 -2
  102. package/dist/components/Switch.js +5 -4
  103. package/dist/components/Table.d.ts +30 -20
  104. package/dist/components/Table.js +50 -73
  105. package/dist/components/Tabs.d.ts +3 -7
  106. package/dist/components/Tabs.js +6 -10
  107. package/dist/components/ThemeEditor.js +18 -8
  108. package/dist/components/Toast.d.ts +11 -3
  109. package/dist/components/Toast.js +45 -20
  110. package/dist/components/buttonClasses.js +1 -1
  111. package/dist/components/comboboxItems.d.ts +4 -0
  112. package/dist/components/comboboxItems.js +6 -1
  113. package/dist/components/dismiss.d.ts +12 -0
  114. package/dist/components/dismiss.js +19 -0
  115. package/dist/components/fieldBox.d.ts +4 -0
  116. package/dist/components/fieldBox.js +7 -3
  117. package/dist/components/floating.d.ts +3 -3
  118. package/dist/components/floating.js +1 -1
  119. package/dist/components/markdownCodeBreaks.js +3 -4
  120. package/dist/components/pageHeaderStore.d.ts +41 -0
  121. package/dist/components/pageHeaderStore.js +67 -0
  122. package/dist/components/pathBreaks.d.ts +8 -0
  123. package/dist/components/pathBreaks.js +28 -0
  124. package/dist/components/segmented.js +1 -1
  125. package/dist/components/shortcutKey.d.ts +2 -1
  126. package/dist/components/shortcutKey.js +19 -2
  127. package/dist/components/splitter.js +1 -1
  128. package/dist/components/useCssIsDark.js +40 -18
  129. package/dist/components/useTheme.js +9 -10
  130. package/dist/dev.d.ts +9 -0
  131. package/dist/dev.js +14 -0
  132. package/dist/editor/Editor.d.ts +3 -2
  133. package/dist/editor/Editor.js +11 -2
  134. package/dist/editor/EditorField.d.ts +2 -1
  135. package/dist/editor/EditorField.js +11 -29
  136. package/dist/editor/FormCodePanel.d.ts +7 -3
  137. package/dist/editor/FormCodePanel.js +14 -7
  138. package/dist/editor/Monaco.d.ts +14 -5
  139. package/dist/editor/Monaco.js +163 -112
  140. package/dist/editor/index.d.ts +5 -5
  141. package/dist/editor/index.js +3 -3
  142. package/dist/editor/themes.d.ts +8 -5
  143. package/dist/editor/themes.js +58 -30
  144. package/dist/editor/yaml.d.ts +6 -2
  145. package/dist/editor/yaml.js +11 -2
  146. package/dist/file-explorer/FileExplorer.js +143 -124
  147. package/dist/file-explorer/FileExplorerContext.js +1 -1
  148. package/dist/file-explorer/FilePreview/StreamedBadge.js +3 -1
  149. package/dist/file-explorer/FilePreview/Viewer.js +2 -2
  150. package/dist/file-explorer/FilePreview/index.js +4 -4
  151. package/dist/file-explorer/FileRow.js +1 -1
  152. package/dist/file-explorer/FileRowsWindow.js +34 -3
  153. package/dist/file-explorer/FileTable.js +3 -1
  154. package/dist/file-explorer/TreeView.js +37 -14
  155. package/dist/file-explorer/helpers.d.ts +2 -1
  156. package/dist/file-explorer/helpers.js +21 -20
  157. package/dist/file-explorer/toasts.js +27 -19
  158. package/dist/file-explorer/treeRows.d.ts +6 -0
  159. package/dist/file-explorer/treeRows.js +19 -1
  160. package/dist/file-explorer/useNearEnd.js +3 -6
  161. package/dist/form/Fields/CheckboxGroupField.js +12 -5
  162. package/dist/form/Fields/DurationField.js +1 -1
  163. package/dist/form/Fields/LabelField.js +1 -1
  164. package/dist/form/Fields/ObjectField.d.ts +1 -1
  165. package/dist/form/Fields/ObjectField.js +3 -2
  166. package/dist/form/Fields/StringField.js +16 -29
  167. package/dist/form/Fields/TextAreaField.js +3 -4
  168. package/dist/form/Form.js +3 -8
  169. package/dist/form/Wizard/WizardNavigation.js +0 -3
  170. package/dist/form/Wizard/utils.js +1 -1
  171. package/dist/form/fieldFrame.d.ts +1 -1
  172. package/dist/form/fieldFrame.js +2 -2
  173. package/dist/form/types/fieldComponentTypes.d.ts +2 -1
  174. package/dist/graph/AnnotationBanner.d.ts +1 -2
  175. package/dist/graph/AnnotationBanner.js +30 -32
  176. package/dist/graph/Connectors.js +4 -1
  177. package/dist/graph/DependencyGraph.js +2 -2
  178. package/dist/graph/GraphEditor.js +11 -9
  179. package/dist/graph/GraphEditorDialogs.js +1 -1
  180. package/dist/graph/GraphNode.js +7 -4
  181. package/dist/graph/InputDialog.js +1 -1
  182. package/dist/graph/InputsEditor.js +14 -11
  183. package/dist/graph/MatrixGroup.js +9 -3
  184. package/dist/graph/Reveal.js +18 -10
  185. package/dist/graph/ShortcutsButton.d.ts +11 -2
  186. package/dist/graph/ShortcutsButton.js +14 -3
  187. package/dist/graph/TreeView.js +35 -18
  188. package/dist/graph/WorkflowSettingsDialog.js +1 -1
  189. package/dist/graph/editorFields.js +1 -1
  190. package/dist/graph/editorRows.js +16 -10
  191. package/dist/graph/editorStrings.d.ts +7 -0
  192. package/dist/graph/editorStrings.js +8 -1
  193. package/dist/graph/inputRefs.js +1 -1
  194. package/dist/graph/settingsViews.js +11 -18
  195. package/dist/graph/stepWith.js +1 -1
  196. package/dist/icons.d.ts +2 -0
  197. package/dist/icons.js +4 -2
  198. package/dist/index.d.ts +12 -12
  199. package/dist/index.js +16 -16
  200. package/dist/list/ActionToolbar.d.ts +42 -6
  201. package/dist/list/ActionToolbar.js +297 -101
  202. package/dist/list/ListPage.d.ts +15 -23
  203. package/dist/list/ListPage.js +42 -43
  204. package/dist/list/ListTable.d.ts +32 -7
  205. package/dist/list/ListTable.js +48 -12
  206. package/dist/list/ListViewControls.js +1 -1
  207. package/dist/list/UserHoverCard.js +7 -15
  208. package/dist/list/index.d.ts +2 -2
  209. package/dist/list/index.js +2 -2
  210. package/dist/list/listSearch.js +12 -10
  211. package/dist/list/listView.d.ts +10 -1
  212. package/dist/list/listView.js +15 -1
  213. package/dist/logviewer/LogViewer.d.ts +4 -2
  214. package/dist/logviewer/LogViewer.js +87 -69
  215. package/dist/logviewer/logviewer.css +3 -2
  216. package/dist/reducedMotion.d.ts +5 -0
  217. package/dist/reducedMotion.js +20 -0
  218. package/dist/safeUrl.d.ts +1 -1
  219. package/dist/safeUrl.js +4 -2
  220. package/dist/styles.css +1 -1
  221. package/package.json +1 -1
  222. package/dist/components/Breadcrumbs.d.ts +0 -37
  223. package/dist/components/Breadcrumbs.js +0 -90
  224. package/dist/components/CopyToClipboard.d.ts +0 -17
  225. package/dist/components/CopyToClipboard.js +0 -66
  226. package/dist/components/Sort.d.ts +0 -9
  227. package/dist/components/Sort.js +0 -42
package/dist/dev.js ADDED
@@ -0,0 +1,14 @@
1
+ //#region src/dev.ts
2
+ /**
3
+ * Whether the host runs a development build, for checks that warn about misuse and cost too much
4
+ * to run in production.
5
+ *
6
+ * It reads `process.env.NODE_ENV`, which the host's bundler replaces, so a production build drops
7
+ * the checks and a development build keeps them. Vite's `DEV` flag would not do: this
8
+ * package's own library build replaces it with `false`, so no host would ever see the warning.
9
+ */
10
+ function isDev() {
11
+ return process.env.NODE_ENV !== "production";
12
+ }
13
+ //#endregion
14
+ export { isDev };
@@ -1,2 +1,3 @@
1
- import type { IEditorProps } from './Monaco.js';
2
- export default function Editor(props: IEditorProps): import("react").JSX.Element;
1
+ import type { EditorProps } from './Monaco.js';
2
+ /** A Monaco code editor, loaded on first render so Monaco stays out of the host's first bundle. */
3
+ export declare function Editor(props: EditorProps): import("react").JSX.Element;
@@ -3,11 +3,20 @@ import { jsx } from "react/jsx-runtime";
3
3
  import { Suspense, lazy } from "react";
4
4
  //#region src/editor/Editor.tsx
5
5
  var MonacoEditor = lazy(() => import("./Monaco.js"));
6
+ /** A Monaco code editor, loaded on first render so Monaco stays out of the host's first bundle. */
6
7
  function Editor(props) {
8
+ const { width = "100%", height = "100%" } = props;
7
9
  return /* @__PURE__ */ jsx(Suspense, {
8
- fallback: /* @__PURE__ */ jsx(Loader, {}),
10
+ fallback: /* @__PURE__ */ jsx("div", {
11
+ style: {
12
+ width,
13
+ height
14
+ },
15
+ className: "flex items-center justify-center",
16
+ children: /* @__PURE__ */ jsx(Loader, {})
17
+ }),
9
18
  children: /* @__PURE__ */ jsx(MonacoEditor, { ...props })
10
19
  });
11
20
  }
12
21
  //#endregion
13
- export { Editor as default };
22
+ export { Editor };
@@ -7,4 +7,5 @@ export interface IEditorField extends BaseField {
7
7
  language?: string;
8
8
  };
9
9
  }
10
- export default function EditorField(props: FieldComponentProps<IEditorField>): import("react").JSX.Element;
10
+ /** A form field that edits its value as code, in the language its `options` name (shell by default). */
11
+ export declare function EditorField(props: FieldComponentProps<IEditorField>): import("react").JSX.Element;
@@ -1,45 +1,27 @@
1
- import { cx } from "../cx.js";
2
1
  import { Field } from "../components/Field.js";
3
2
  import { fieldFrame } from "../form/fieldFrame.js";
4
3
  import { useFieldValue } from "../form/useFieldValue.js";
5
- import Editor from "./Editor.js";
4
+ import { Editor } from "./Editor.js";
6
5
  import { jsx } from "react/jsx-runtime";
7
- import { useMemo, useRef } from "react";
8
- import { useFormContext, useWatch } from "react-hook-form";
9
6
  //#region src/editor/EditorField.tsx
7
+ /** A form field that edits its value as code, in the language its `options` name (shell by default). */
10
8
  function EditorField(props) {
11
9
  const { field, label, disabled, onChange } = props;
12
- const fieldName = field.name ?? "";
13
- const [value, change] = useFieldValue(fieldName);
14
- const touchedRef = useRef(false);
15
- const handleChange = (val) => {
16
- touchedRef.current = true;
17
- onChange?.(val);
18
- change(val);
19
- };
20
- const { control } = useFormContext();
21
- const resetKey = useWatch({
22
- control,
23
- name: "editorKeyPostfix"
24
- });
25
- const editorKeyPostfix = typeof resetKey === "string" ? resetKey : "";
26
- const postfixKey = useMemo(() => {
27
- const randomKey = Math.random().toString(36).substring(7);
28
- return editorKeyPostfix || randomKey;
29
- }, [editorKeyPostfix]);
10
+ const [value, change] = useFieldValue(field.name ?? "");
30
11
  return /* @__PURE__ */ jsx(Field, {
31
12
  ...fieldFrame(props),
32
13
  children: /* @__PURE__ */ jsx(Editor, {
33
14
  ariaLabel: label,
34
15
  height: "200px",
35
- path: fieldName,
36
- ...touchedRef.current || value === void 0 ? {} : { value },
37
- className: cx("bg-(--theme-input-bg) w-11 border focus:border rounded text-[10px] leading-4 pl-[6px]", disabled && "opacity-75"),
38
- readOnly: disabled,
39
- onChange: handleChange,
16
+ value: value ?? "",
17
+ disabled,
18
+ onChange: (text) => {
19
+ onChange?.(text);
20
+ change(text);
21
+ },
40
22
  language: field.options?.language || "shell"
41
- }, fieldName + postfixKey)
23
+ })
42
24
  });
43
25
  }
44
26
  //#endregion
45
- export { EditorField as default };
27
+ export { EditorField };
@@ -1,8 +1,12 @@
1
- declare function FormCodePanel({ formJSON, data, mutate, height, disabled, }: {
1
+ export interface FormCodePanelProps {
2
+ /** The form's input definitions, which say which values are sensitive or hidden. */
2
3
  formJSON: Record<string, unknown>;
4
+ /** The form's values, shown as JSON. */
3
5
  data: Record<string, unknown>;
6
+ /** Called with the values each time the JSON parses as an object. */
4
7
  mutate: (values: Record<string, unknown>) => void;
5
8
  height?: string;
6
9
  disabled?: boolean;
7
- }): import("react").JSX.Element;
8
- export default FormCodePanel;
10
+ }
11
+ /** A form's values as editable JSON, with sensitive and hidden fields left out unless asked for. */
12
+ export declare function FormCodePanel({ formJSON, data, mutate, height, disabled, }: FormCodePanelProps): import("react").JSX.Element;
@@ -5,7 +5,7 @@ import { useStrings, useWorkflowEngine } from "../components/Provider.js";
5
5
  import { useCopy } from "../components/useCopy.js";
6
6
  import { Switch } from "../components/Switch.js";
7
7
  import { flattenGroups, impureSetValueFromPath } from "../form/lib.js";
8
- import Editor from "./Editor.js";
8
+ import { Editor } from "./Editor.js";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { useRef, useState } from "react";
11
11
  //#region src/editor/FormCodePanel.tsx
@@ -68,6 +68,7 @@ var populateFiltered = (filteredFields, data) => {
68
68
  });
69
69
  return data;
70
70
  };
71
+ /** A form's values as editable JSON, with sensitive and hidden fields left out unless asked for. */
71
72
  function FormCodePanel({ formJSON, data, mutate, height = "600px", disabled = false }) {
72
73
  const engine = useWorkflowEngine();
73
74
  const { common: t } = useStrings();
@@ -85,12 +86,14 @@ function FormCodePanel({ formJSON, data, mutate, height = "600px", disabled = fa
85
86
  const filteredFieldsRef = useRef(filteredFields);
86
87
  filteredFieldsRef.current = filteredFields;
87
88
  const json = JSON.stringify(displayObj, null, 2);
89
+ const typedRef = useRef(null);
90
+ const shown = typedRef.current?.json === json ? typedRef.current.text : json;
88
91
  const handleCopy = () => void copy(json, { quiet: true });
89
92
  return /* @__PURE__ */ jsxs("div", {
90
93
  style: { height },
91
94
  className: "flex w-full flex-col gap-2",
92
95
  children: [/* @__PURE__ */ jsxs("div", {
93
- className: cx("flex min-h-0 flex-1 flex-col overflow-hidden rounded-md border", jsonHasErrors ? "border-red-500" : "border-(--theme-border)"),
96
+ className: cx("flex min-h-0 flex-1 flex-col overflow-hidden rounded-md border", jsonHasErrors ? "border-(--theme-error)" : "border-(--theme-border)"),
94
97
  children: [/* @__PURE__ */ jsxs("div", {
95
98
  className: "bg-(--theme-muted-panel-bg) text-(--theme-muted-text-color) flex shrink-0 items-center gap-3 border-b border-(--theme-border) px-3 py-2",
96
99
  children: [hasHiddenFields && /* @__PURE__ */ jsx(Tooltip, {
@@ -111,13 +114,13 @@ function FormCodePanel({ formJSON, data, mutate, height = "600px", disabled = fa
111
114
  onClick: handleCopy,
112
115
  "aria-label": copied ? t.copied : t.copy,
113
116
  className: "ml-auto flex shrink-0 items-center gap-1.5 rounded-md border border-(--theme-border) px-2.5 py-1.5 text-sm text-(--theme-app) transition-colors duration-150 hover:bg-(--theme-hover) focus:outline-none focus-visible:ring-2 focus-visible:ring-(--theme-link)",
114
- children: [copied ? /* @__PURE__ */ jsx(CheckIcon, { className: "text-green-500" }) : /* @__PURE__ */ jsx(CopyIcon, { className: "text-(--theme-muted-text-color)" }), copied ? t.copied : t.copy]
117
+ children: [copied ? /* @__PURE__ */ jsx(CheckIcon, { className: "text-(--theme-success)" }) : /* @__PURE__ */ jsx(CopyIcon, { className: "text-(--theme-muted-text-color)" }), copied ? t.copied : t.copy]
115
118
  })]
116
119
  }), /* @__PURE__ */ jsx("div", {
117
120
  className: "min-h-0 flex-1",
118
121
  children: /* @__PURE__ */ jsx(Editor, {
119
122
  height: "100%",
120
- value: json,
123
+ value: shown,
121
124
  onValidate: isValidJson,
122
125
  className: "!rounded-none !border-0",
123
126
  minimap: { enabled: false },
@@ -140,7 +143,11 @@ function FormCodePanel({ formJSON, data, mutate, height = "600px", disabled = fa
140
143
  try {
141
144
  const parsed = JSON.parse(e);
142
145
  if (!isObject(parsed)) return;
143
- mutate(showHiddenRef.current ? parsed : populateFiltered(filteredFieldsRef.current, parsed));
146
+ typedRef.current = {
147
+ text: e,
148
+ json: JSON.stringify(parsed, null, 2)
149
+ };
150
+ mutate(showHiddenRef.current ? parsed : populateFiltered(filteredFieldsRef.current, structuredClone(parsed)));
144
151
  } catch {}
145
152
  },
146
153
  language: "json",
@@ -148,10 +155,10 @@ function FormCodePanel({ formJSON, data, mutate, height = "600px", disabled = fa
148
155
  })
149
156
  })]
150
157
  }), jsonHasErrors && /* @__PURE__ */ jsx("p", {
151
- className: "shrink-0 text-sm text-red-500",
158
+ className: "shrink-0 text-sm text-(--theme-error-text)",
152
159
  children: t.jsonHasErrors
153
160
  })]
154
161
  });
155
162
  }
156
163
  //#endregion
157
- export { FormCodePanel as default };
164
+ export { FormCodePanel };
@@ -1,19 +1,28 @@
1
1
  import * as monaco from 'monaco-editor';
2
2
  import { type NestedWorkflowText } from './nestedText.js';
3
- export type ISchemaError = monaco.editor.IMarker;
3
+ /** A problem Monaco or the YAML service found in the text, as `onValidate` reports it. */
4
+ export type ValidationMarker = monaco.editor.IMarker;
4
5
  /** A problem to mark on one line of the editor. */
5
6
  export interface EditorMarker {
6
7
  line: number;
7
8
  message: string;
8
9
  }
9
- export interface IEditorProps extends monaco.editor.IStandaloneEditorConstructionOptions {
10
+ export interface EditorProps extends monaco.editor.IStandaloneEditorConstructionOptions {
10
11
  className?: string;
12
+ /** Shows the text greyed out and read-only, as a disabled field does. */
11
13
  disabled?: boolean | undefined;
14
+ /** The text as the person edits it, with carriage returns removed. */
12
15
  onChange?: (value: string) => void;
13
- onValidate?: (errors: ISchemaError[]) => void;
16
+ /** Every marker on the text, each time they change: the schema's, the lint's and the host's. */
17
+ onValidate?: (markers: ValidationMarker[]) => void;
14
18
  width?: string | number;
15
19
  height?: string | number;
16
- path?: 'file:///workflow.yaml' | string;
20
+ /**
21
+ * The model's URI. A YAML schema registered with `configureEditorYaml` applies where its
22
+ * `fileMatch` matches this path, and `revealLines` finds the editor by it. Each editor gets one
23
+ * of its own when left out. It is read once: give the editor a `key` to edit another path.
24
+ */
25
+ path?: string;
17
26
  /** Apply outside `value` changes as undoable edits instead of resetting the undo history. */
18
27
  preserveUndo?: boolean;
19
28
  /** Checks a whole workflow for what the schema can't, such as needs read but never listed. */
@@ -23,4 +32,4 @@ export interface IEditorProps extends monaco.editor.IStandaloneEditorConstructio
23
32
  /** Set when the text is one job or step, for completions that touch the rest of the workflow. */
24
33
  nested?: NestedWorkflowText;
25
34
  }
26
- export default function MonacoEditor({ className, disabled, onChange, onValidate, width, height, path, preserveUndo, lint, markers, nested, ...options }: IEditorProps): import("react").JSX.Element;
35
+ export default function MonacoEditor({ className, disabled, onChange, onValidate, width, height, path: pathProp, preserveUndo, lint, markers, nested, value, language, ...options }: EditorProps): import("react").JSX.Element;
@@ -1,4 +1,5 @@
1
1
  import { cx } from "../cx.js";
2
+ import { useStrings } from "../components/Provider.js";
2
3
  import { useCssIsDark } from "../components/useCssIsDark.js";
3
4
  import { HOST_MARKER_OWNER, LINT_OWNER } from "./lintOwner.js";
4
5
  import { lintContext, useLintEditing, useLintSources } from "./lintContext.js";
@@ -9,7 +10,7 @@ import { setupMonacoWorkers } from "./workers.js";
9
10
  import { holdListeners, modelFor } from "./editorModel.js";
10
11
  import { ensureYamlLanguage } from "./yamlLanguage.js";
11
12
  import { jsx } from "react/jsx-runtime";
12
- import { useEffect, useLayoutEffect, useRef } from "react";
13
+ import { useEffect, useId, useLayoutEffect, useRef } from "react";
13
14
  import * as monaco from "monaco-editor";
14
15
  //#region src/editor/Monaco.tsx
15
16
  setupMonacoWorkers();
@@ -38,25 +39,166 @@ function editorText(value) {
38
39
  return value.replace(/\r/g, "");
39
40
  }
40
41
  var noop = () => {};
41
- function MonacoEditor({ className, disabled, onChange = noop, onValidate = noop, width = "100%", height = "100%", path = "file:///default", preserveUndo = false, lint = false, markers, nested, ...options }) {
42
+ var DISABLED_BACKGROUNDS = [
43
+ "--vscode-editor-background",
44
+ "--vscode-editorGutter-background",
45
+ "--vscode-editorStickyScroll-background",
46
+ "--vscode-minimap-background",
47
+ "--vscode-editorOverviewRuler-background"
48
+ ];
49
+ function minimalEdit(model, from, to) {
50
+ let start = 0;
51
+ const shortest = Math.min(from.length, to.length);
52
+ while (start < shortest && from[start] === to[start]) start++;
53
+ let end = 0;
54
+ while (end < shortest - start && from[from.length - 1 - end] === to[to.length - 1 - end]) end++;
55
+ const a = model.getPositionAt(start);
56
+ const b = model.getPositionAt(from.length - end);
57
+ return {
58
+ range: new monaco.Range(a.lineNumber, a.column, b.lineNumber, b.column),
59
+ text: to.slice(start, to.length - end)
60
+ };
61
+ }
62
+ function MonacoEditor({ className, disabled, onChange = noop, onValidate = noop, width = "100%", height = "100%", path: pathProp, preserveUndo = false, lint = false, markers, nested, value, language, ...options }) {
63
+ const { common: t } = useStrings();
64
+ const ownPath = `file:///editor/${useId().replace(/[^\w-]/g, "")}`;
65
+ const path = useRef(pathProp ?? ownPath).current;
42
66
  const isDark = useCssIsDark();
67
+ const isDarkRef = useRef(isDark);
68
+ isDarkRef.current = isDark;
43
69
  const readOnly = Boolean(disabled || options.readOnly);
70
+ const containerRef = useRef(null);
44
71
  const monacoRef = useRef(null);
45
72
  const isSyncingValueRef = useRef(false);
46
73
  const lintSources = useLintSources();
47
74
  const lintSourcesRef = useRef(lintSources);
48
75
  lintSourcesRef.current = lintSources;
49
76
  const runLintRef = useRef(null);
50
- const unregisterRevealRef = useRef(null);
51
- const listenersRef = useRef([]);
52
77
  const nestedRef = useRef(nested);
53
78
  nestedRef.current = nested;
54
79
  const isNested = nested !== void 0;
80
+ const onChangeRef = useRef(onChange);
81
+ onChangeRef.current = onChange;
82
+ const onValidateRef = useRef(onValidate);
83
+ onValidateRef.current = onValidate;
84
+ const markersRef = useRef(markers);
85
+ markersRef.current = markers;
86
+ const editorOptions = {
87
+ ariaLabel: t.codeEditor,
88
+ readOnlyMessage: { value: t.readOnlyText },
89
+ ...options,
90
+ readOnly,
91
+ renderLineHighlight: disabled ? "none" : options.renderLineHighlight ?? "line"
92
+ };
93
+ const optionsKey = JSON.stringify(editorOptions);
94
+ const optionsRef = useRef(editorOptions);
95
+ optionsRef.current = editorOptions;
96
+ const initialRef = useRef({
97
+ value,
98
+ language
99
+ });
55
100
  const lintEditing = useLintEditing(lint);
56
101
  const lintEditingRef = useRef(lintEditing);
57
102
  lintEditingRef.current = lintEditing;
58
103
  const lintRef = useRef(lint);
59
104
  lintRef.current = lint;
105
+ useLayoutEffect(() => {
106
+ const container = containerRef.current;
107
+ if (!container) return;
108
+ defineEditorThemes(monaco.editor);
109
+ monaco.editor.setTheme(getThemeName(isDarkRef.current));
110
+ const { value: initial, language: initialLanguage } = initialRef.current;
111
+ const model = modelFor(monaco.Uri.parse(path), initial ?? "", initialLanguage);
112
+ const editor = monaco.editor.create(container, {
113
+ automaticLayout: true,
114
+ model,
115
+ ...optionsRef.current,
116
+ fixedOverflowWidgets: true,
117
+ suggest: {
118
+ preview: true,
119
+ showWords: false,
120
+ showInlineDetails: true
121
+ }
122
+ });
123
+ setMarkers(model, HOST_MARKER_OWNER, (markersRef.current ?? []).map((marker) => lintMarker(model, marker)));
124
+ setMarkers(model, LINT_OWNER, []);
125
+ const runLint = () => {
126
+ const editing = lintEditingRef.current;
127
+ if (!lintRef.current || !editing || model.isDisposed()) return;
128
+ const schema = monaco.editor.getModelMarkers({ resource: model.uri }).filter((marker) => marker.owner !== "workflowlint" && marker.owner !== "workflowmarkers" && String(marker.code ?? "") !== SCHEMA_UNREADABLE);
129
+ const source = model.getValue();
130
+ setMarkers(model, LINT_OWNER, schema.length > 0 ? [] : editing.lintWorkflow(source, lintContext(editing, source, lintSourcesRef.current, runLint)).map((problem) => lintMarker(model, problem)));
131
+ };
132
+ runLintRef.current = runLint;
133
+ let lintTimer;
134
+ const validate = () => {
135
+ runLint();
136
+ onValidateRef.current(monaco.editor.getModelMarkers({ resource: model.uri }));
137
+ };
138
+ const modelListeners = [
139
+ model.onDidChangeContent(() => {
140
+ if (lintRef.current) {
141
+ window.clearTimeout(lintTimer);
142
+ lintTimer = window.setTimeout(runLint, 300);
143
+ }
144
+ if (isSyncingValueRef.current) return;
145
+ onChangeRef.current(editorText(editor.getValue()));
146
+ }),
147
+ monaco.editor.onDidChangeMarkers((resources) => {
148
+ const uri = model.uri.toString();
149
+ if (resources.some((resource) => resource.toString() === uri)) validate();
150
+ }),
151
+ { dispose: () => window.clearTimeout(lintTimer) }
152
+ ];
153
+ holdListeners(model, modelListeners);
154
+ validate();
155
+ const unregisterReveal = registerRevealer(path, (start, end) => {
156
+ const last = Math.min(Math.max(start, end), model.getLineCount());
157
+ editor.revealLinesInCenterIfOutsideViewport(start, last);
158
+ editor.setPosition({
159
+ lineNumber: start,
160
+ column: model.getLineFirstNonWhitespaceColumn(start) || 1
161
+ });
162
+ const flash = editor.createDecorationsCollection([{
163
+ range: new monaco.Range(start, 1, last, 1),
164
+ options: {
165
+ isWholeLine: true,
166
+ className: "bg-(--theme-accent)/20"
167
+ }
168
+ }]);
169
+ window.setTimeout(() => flash.clear(), 1500);
170
+ });
171
+ const unfollow = followThemeTokens(monaco.editor, () => getThemeName(isDarkRef.current));
172
+ ensureYamlLanguage();
173
+ monacoRef.current = editor;
174
+ return () => {
175
+ unfollow();
176
+ unregisterReveal();
177
+ for (const listener of modelListeners) listener.dispose();
178
+ editor.dispose();
179
+ monacoRef.current = null;
180
+ runLintRef.current = null;
181
+ if (!model.isDisposed() && model.getLanguageId() !== "yaml") model.dispose();
182
+ };
183
+ }, [path]);
184
+ useEffect(() => {
185
+ if (monacoRef.current) {
186
+ defineEditorThemes(monaco.editor);
187
+ monaco.editor.setTheme(getThemeName(isDark));
188
+ }
189
+ }, [isDark]);
190
+ useEffect(() => {
191
+ const node = monacoRef.current?.getDomNode();
192
+ for (const name of DISABLED_BACKGROUNDS) if (disabled) node?.style.setProperty(name, "var(--theme-input-disabled-bg)");
193
+ else node?.style.removeProperty(name);
194
+ }, [disabled]);
195
+ useEffect(() => {
196
+ monacoRef.current?.updateOptions(optionsRef.current);
197
+ }, [optionsKey]);
198
+ useEffect(() => {
199
+ const model = monacoRef.current?.getModel();
200
+ if (model && language && model.getLanguageId() !== language) monaco.editor.setModelLanguage(model, language);
201
+ }, [language]);
60
202
  useEffect(() => {
61
203
  const model = monacoRef.current?.getModel();
62
204
  if (!lint && model) setMarkers(model, LINT_OWNER, []);
@@ -70,126 +212,35 @@ function MonacoEditor({ className, disabled, onChange = noop, onValidate = noop,
70
212
  if (!isNested) return;
71
213
  return publishNestedText(monaco.Uri.parse(path).toString(), nestedRef);
72
214
  }, [path, isNested]);
73
- const themeName = useRef(getThemeName(isDark, disabled));
74
- themeName.current = getThemeName(isDark, disabled);
75
- useEffect(() => {
76
- if (!monacoRef.current) return;
77
- defineEditorThemes(monaco.editor);
78
- monaco.editor.setTheme(getThemeName(isDark, disabled));
79
- monacoRef.current.updateOptions({ readOnly });
80
- }, [
81
- disabled,
82
- isDark,
83
- readOnly
84
- ]);
85
- useEffect(() => followThemeTokens(monaco.editor, () => themeName.current), []);
86
215
  useLayoutEffect(() => {
87
- const model = monacoRef.current?.getModel();
88
- if (!model || options.value === void 0 || editorText(model.getValue()) === editorText(options.value)) return;
216
+ const editor = monacoRef.current;
217
+ const model = editor?.getModel();
218
+ if (!editor || !model || value === void 0) return;
219
+ const current = model.getValue();
220
+ if (editorText(current) === editorText(value)) return;
89
221
  isSyncingValueRef.current = true;
90
222
  try {
91
223
  if (preserveUndo) {
92
- const editor = monacoRef.current;
93
- editor?.pushUndoStop();
94
- model.pushEditOperations([], [{
95
- range: model.getFullModelRange(),
96
- text: options.value
97
- }], () => null);
98
- editor?.pushUndoStop();
99
- } else model.setValue(options.value);
224
+ editor.pushUndoStop();
225
+ model.pushEditOperations([], [minimalEdit(model, current, value)], () => null);
226
+ editor.pushUndoStop();
227
+ } else {
228
+ const view = editor.saveViewState();
229
+ model.setValue(value);
230
+ if (view) editor.restoreViewState(view);
231
+ }
100
232
  } finally {
101
233
  isSyncingValueRef.current = false;
102
234
  }
103
- }, [options.value, preserveUndo]);
104
- useEffect(() => {
105
- return () => {
106
- unregisterRevealRef.current?.();
107
- for (const listener of listenersRef.current) listener.dispose();
108
- monacoRef.current?.dispose();
109
- monacoRef.current = null;
110
- };
111
- }, []);
112
- /** Strips CR (\r) — Windows line endings break bash scripts. */
113
- const onChangeWrapper = (value) => {
114
- value = editorText(value);
115
- onChange(value);
116
- };
235
+ }, [value, preserveUndo]);
117
236
  return /* @__PURE__ */ jsx("div", {
237
+ ref: containerRef,
118
238
  style: {
119
239
  width,
120
240
  height
121
241
  },
122
- className: cx("relative border rounded", className),
123
- ref: (ref) => {
124
- if (ref && !monacoRef.current) {
125
- defineEditorThemes(monaco.editor);
126
- monaco.editor.setTheme(getThemeName(isDark, disabled));
127
- const model = modelFor(monaco.Uri.parse(path), options.value ?? "", options.language);
128
- const editor = monaco.editor.create(ref, {
129
- automaticLayout: true,
130
- model,
131
- ...options,
132
- readOnly,
133
- fixedOverflowWidgets: true,
134
- suggest: {
135
- preview: true,
136
- showWords: false,
137
- showInlineDetails: true
138
- }
139
- });
140
- setMarkers(model, HOST_MARKER_OWNER, (markers ?? []).map((marker) => lintMarker(model, marker)));
141
- setMarkers(model, LINT_OWNER, []);
142
- const runLint = () => {
143
- const editing = lintEditingRef.current;
144
- if (!lintRef.current || !editing || model.isDisposed()) return;
145
- const schema = monaco.editor.getModelMarkers({ resource: model.uri }).filter((marker) => marker.owner !== "workflowlint" && marker.owner !== "workflowmarkers" && String(marker.code ?? "") !== SCHEMA_UNREADABLE);
146
- const source = model.getValue();
147
- setMarkers(model, LINT_OWNER, schema.length > 0 ? [] : editing.lintWorkflow(source, lintContext(editing, source, lintSourcesRef.current, runLint)).map((problem) => lintMarker(model, problem)));
148
- };
149
- runLintRef.current = runLint;
150
- let lintTimer;
151
- const validate = () => {
152
- runLint();
153
- onValidate(monaco.editor.getModelMarkers({ resource: model.uri }));
154
- };
155
- const modelListeners = [
156
- model.onDidChangeContent(() => {
157
- if (lintRef.current) {
158
- window.clearTimeout(lintTimer);
159
- lintTimer = window.setTimeout(runLint, 300);
160
- }
161
- if (isSyncingValueRef.current) return;
162
- onChangeWrapper(editor.getValue());
163
- }),
164
- monaco.editor.onDidChangeMarkers((resources) => {
165
- const uri = model.uri.toString();
166
- if (resources.some((resource) => resource.toString() === uri)) validate();
167
- }),
168
- { dispose: () => window.clearTimeout(lintTimer) }
169
- ];
170
- holdListeners(model, modelListeners);
171
- listenersRef.current = modelListeners;
172
- validate();
173
- unregisterRevealRef.current = registerRevealer(path, (start, end) => {
174
- const last = Math.min(Math.max(start, end), model.getLineCount());
175
- editor.revealLinesInCenterIfOutsideViewport(start, last);
176
- editor.setPosition({
177
- lineNumber: start,
178
- column: model.getLineFirstNonWhitespaceColumn(start) || 1
179
- });
180
- const flash = editor.createDecorationsCollection([{
181
- range: new monaco.Range(start, 1, last, 1),
182
- options: {
183
- isWholeLine: true,
184
- className: "bg-(--theme-accent)/20"
185
- }
186
- }]);
187
- window.setTimeout(() => flash.clear(), 1500);
188
- });
189
- ensureYamlLanguage();
190
- monacoRef.current = editor;
191
- }
192
- }
242
+ "data-disabled": disabled ? "" : void 0,
243
+ className: cx("relative rounded border border-(--theme-border)", "focus-within:border-(--theme-accent) focus-within:ring-1 focus-within:ring-(--theme-accent)", "data-disabled:[&_.cursors-layer]:hidden data-disabled:[&_.view-lines]:opacity-60", className)
193
244
  });
194
245
  }
195
246
  //#endregion
@@ -1,11 +1,11 @@
1
- export { default as Editor } from './Editor.js';
2
- export { default as EditorField, type IEditorField } from './EditorField.js';
3
- export { default as FormCodePanel } from './FormCodePanel.js';
1
+ export { Editor } from './Editor.js';
2
+ export { EditorField, type IEditorField } from './EditorField.js';
3
+ export { FormCodePanel, type FormCodePanelProps } from './FormCodePanel.js';
4
4
  export { HOST_MARKER_OWNER, LINT_OWNER } from './lintOwner.js';
5
- export type { IEditorProps, ISchemaError } from './Monaco.js';
5
+ export type { EditorMarker, EditorProps, ValidationMarker } from './Monaco.js';
6
6
  export { type NestedWorkflowText, nestedWorkflowText } from './nestedText.js';
7
7
  export { revealLines } from './reveal.js';
8
8
  export { INPUT_YAML_PATH, JOB_YAML_PATH, type JsonSchema, SETTINGS_YAML_PATH, STEP_YAML_PATH, settingsSchemas, } from './settingsYaml.js';
9
9
  export { defineEditorThemes, followThemeTokens, getThemeName } from './themes.js';
10
10
  export { setupMonacoWorkers } from './workers.js';
11
- export { configureEditorYaml } from './yaml.js';
11
+ export { configureEditorYaml, type SchemasSettings } from './yaml.js';
@@ -1,8 +1,8 @@
1
1
  import { HOST_MARKER_OWNER, LINT_OWNER } from "./lintOwner.js";
2
2
  import { INPUT_YAML_PATH, JOB_YAML_PATH, SETTINGS_YAML_PATH, STEP_YAML_PATH, settingsSchemas } from "./settingsYaml.js";
3
- import Editor from "./Editor.js";
4
- import EditorField from "./EditorField.js";
5
- import FormCodePanel from "./FormCodePanel.js";
3
+ import { Editor } from "./Editor.js";
4
+ import { EditorField } from "./EditorField.js";
5
+ import { FormCodePanel } from "./FormCodePanel.js";
6
6
  import { nestedWorkflowText } from "./nestedText.js";
7
7
  import { revealLines } from "./reveal.js";
8
8
  import { defineEditorThemes, followThemeTokens, getThemeName } from "./themes.js";
@@ -1,8 +1,11 @@
1
1
  import type * as monaco from 'monaco-editor';
2
2
  export declare function expandShorthandHex(color: string): string;
3
- /** Monaco themes read from the document's tokens: call again after the host applies another theme. */
4
- export declare function defineEditorThemes(monacoEditor: typeof monaco.editor): void;
5
- /** Redefines the editor themes when the host applies other tokens to the document,
6
- * and sets `current()` again, so an open editor follows a theme being edited. */
3
+ /**
4
+ * Defines the light and dark Monaco themes from the document's tokens. Call it again after the
5
+ * host applies other tokens; it returns whether anything changed.
6
+ */
7
+ export declare function defineEditorThemes(monacoEditor: typeof monaco.editor): boolean;
8
+ /** Redefines the editor themes when the host applies other tokens or another scheme to the
9
+ * document, and sets `current()` again, so an open editor follows a theme being edited. */
7
10
  export declare function followThemeTokens(monacoEditor: typeof monaco.editor, current: () => string): () => void;
8
- export declare function getThemeName(isDark: boolean, disabled?: boolean): string;
11
+ export declare function getThemeName(isDark: boolean): string;