@parallelworks/ui 0.50.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 (240) hide show
  1. package/README.md +204 -18
  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 +4 -4
  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 +117 -56
  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 +51 -24
  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 +51 -17
  63. package/dist/components/DescriptionList.js +117 -31
  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 +41 -9
  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 +43 -12
  76. package/dist/components/MenuEntries.js +34 -19
  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 +79 -19
  89. package/dist/components/Provider.js +140 -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/RunStatusIcon.js +2 -2
  94. package/dist/components/Section.js +1 -1
  95. package/dist/components/Select.d.ts +4 -9
  96. package/dist/components/Select.js +24 -11
  97. package/dist/components/SettingModeOption.d.ts +1 -1
  98. package/dist/components/SettingModeOption.js +7 -1
  99. package/dist/components/SettingsCard.d.ts +5 -3
  100. package/dist/components/SettingsCard.js +27 -20
  101. package/dist/components/Slider.js +1 -1
  102. package/dist/components/StatusBadge.js +2 -2
  103. package/dist/components/StatusIcons.js +2 -2
  104. package/dist/components/Switch.js +5 -4
  105. package/dist/components/Table.d.ts +30 -20
  106. package/dist/components/Table.js +50 -73
  107. package/dist/components/Tabs.d.ts +3 -7
  108. package/dist/components/Tabs.js +6 -10
  109. package/dist/components/ThemeEditor.js +24 -14
  110. package/dist/components/Toast.d.ts +11 -3
  111. package/dist/components/Toast.js +47 -22
  112. package/dist/components/buttonClasses.js +2 -2
  113. package/dist/components/comboboxItems.d.ts +4 -0
  114. package/dist/components/comboboxItems.js +6 -1
  115. package/dist/components/dismiss.d.ts +12 -0
  116. package/dist/components/dismiss.js +19 -0
  117. package/dist/components/fieldBox.d.ts +4 -0
  118. package/dist/components/fieldBox.js +7 -3
  119. package/dist/components/floating.d.ts +3 -3
  120. package/dist/components/floating.js +1 -1
  121. package/dist/components/markdownCodeBreaks.js +3 -4
  122. package/dist/components/pageHeaderStore.d.ts +41 -0
  123. package/dist/components/pageHeaderStore.js +67 -0
  124. package/dist/components/pathBreaks.d.ts +8 -0
  125. package/dist/components/pathBreaks.js +28 -0
  126. package/dist/components/segmented.js +1 -1
  127. package/dist/components/shortcutKey.d.ts +2 -1
  128. package/dist/components/shortcutKey.js +19 -2
  129. package/dist/components/splitter.js +1 -1
  130. package/dist/components/tone.d.ts +5 -2
  131. package/dist/components/tone.js +15 -6
  132. package/dist/components/useCssIsDark.js +40 -18
  133. package/dist/components/useTheme.js +9 -10
  134. package/dist/dev.d.ts +9 -0
  135. package/dist/dev.js +14 -0
  136. package/dist/editor/Editor.d.ts +3 -2
  137. package/dist/editor/Editor.js +11 -2
  138. package/dist/editor/EditorField.d.ts +2 -1
  139. package/dist/editor/EditorField.js +11 -29
  140. package/dist/editor/FormCodePanel.d.ts +7 -3
  141. package/dist/editor/FormCodePanel.js +14 -7
  142. package/dist/editor/Monaco.d.ts +14 -5
  143. package/dist/editor/Monaco.js +163 -112
  144. package/dist/editor/index.d.ts +5 -5
  145. package/dist/editor/index.js +3 -3
  146. package/dist/editor/themes.d.ts +8 -5
  147. package/dist/editor/themes.js +58 -30
  148. package/dist/editor/yaml.d.ts +6 -2
  149. package/dist/editor/yaml.js +11 -2
  150. package/dist/file-explorer/FileExplorer.js +143 -124
  151. package/dist/file-explorer/FileExplorerContext.js +1 -1
  152. package/dist/file-explorer/FilePreview/StreamedBadge.js +3 -1
  153. package/dist/file-explorer/FilePreview/Viewer.js +2 -2
  154. package/dist/file-explorer/FilePreview/index.js +4 -4
  155. package/dist/file-explorer/FileRow.js +1 -1
  156. package/dist/file-explorer/FileRowsWindow.js +34 -3
  157. package/dist/file-explorer/FileTable.js +3 -1
  158. package/dist/file-explorer/TreeView.js +37 -14
  159. package/dist/file-explorer/helpers.d.ts +2 -1
  160. package/dist/file-explorer/helpers.js +21 -20
  161. package/dist/file-explorer/toasts.js +27 -19
  162. package/dist/file-explorer/treeRows.d.ts +6 -0
  163. package/dist/file-explorer/treeRows.js +19 -1
  164. package/dist/file-explorer/useNearEnd.js +3 -6
  165. package/dist/form/Fields/CheckboxGroupField.js +12 -5
  166. package/dist/form/Fields/DurationField.js +1 -1
  167. package/dist/form/Fields/LabelField.js +1 -1
  168. package/dist/form/Fields/ObjectField.d.ts +1 -1
  169. package/dist/form/Fields/ObjectField.js +3 -2
  170. package/dist/form/Fields/StringField.js +16 -29
  171. package/dist/form/Fields/TextAreaField.js +3 -4
  172. package/dist/form/Form.js +3 -8
  173. package/dist/form/Wizard/WizardNavigation.js +1 -4
  174. package/dist/form/Wizard/WizardStepIndicator.js +1 -1
  175. package/dist/form/Wizard/utils.js +1 -1
  176. package/dist/form/fieldFrame.d.ts +1 -1
  177. package/dist/form/fieldFrame.js +2 -2
  178. package/dist/form/types/fieldComponentTypes.d.ts +2 -1
  179. package/dist/graph/AnnotationBanner.d.ts +1 -2
  180. package/dist/graph/AnnotationBanner.js +30 -32
  181. package/dist/graph/Connectors.js +4 -1
  182. package/dist/graph/DependencyGraph.js +2 -2
  183. package/dist/graph/GraphEditor.js +12 -10
  184. package/dist/graph/GraphEditorDialogs.js +2 -2
  185. package/dist/graph/GraphNode.js +7 -4
  186. package/dist/graph/InputDialog.js +1 -1
  187. package/dist/graph/InputsEditor.js +14 -11
  188. package/dist/graph/MatrixGroup.js +9 -3
  189. package/dist/graph/ProblemsButton.js +2 -2
  190. package/dist/graph/Reveal.js +18 -10
  191. package/dist/graph/ShortcutsButton.d.ts +11 -2
  192. package/dist/graph/ShortcutsButton.js +15 -4
  193. package/dist/graph/TreeView.js +35 -18
  194. package/dist/graph/WorkflowSettingsDialog.js +1 -1
  195. package/dist/graph/editorChrome.js +1 -1
  196. package/dist/graph/editorFields.js +1 -1
  197. package/dist/graph/editorRows.js +16 -10
  198. package/dist/graph/editorStrings.d.ts +7 -0
  199. package/dist/graph/editorStrings.js +8 -1
  200. package/dist/graph/fieldProblems.js +1 -1
  201. package/dist/graph/inputRefs.js +1 -1
  202. package/dist/graph/settingsViews.js +11 -18
  203. package/dist/graph/stepWith.js +2 -2
  204. package/dist/icons.d.ts +2 -0
  205. package/dist/icons.js +4 -2
  206. package/dist/index.d.ts +12 -12
  207. package/dist/index.js +16 -16
  208. package/dist/list/ActionToolbar.d.ts +44 -8
  209. package/dist/list/ActionToolbar.js +301 -102
  210. package/dist/list/ListPage.d.ts +15 -23
  211. package/dist/list/ListPage.js +46 -45
  212. package/dist/list/ListTable.d.ts +32 -7
  213. package/dist/list/ListTable.js +48 -12
  214. package/dist/list/ListViewControls.js +2 -2
  215. package/dist/list/UserHoverCard.js +7 -15
  216. package/dist/list/index.d.ts +2 -2
  217. package/dist/list/index.js +2 -2
  218. package/dist/list/listSearch.js +12 -10
  219. package/dist/list/listView.d.ts +10 -1
  220. package/dist/list/listView.js +15 -1
  221. package/dist/list/rowMenu.d.ts +2 -2
  222. package/dist/logviewer/LogViewer.d.ts +4 -2
  223. package/dist/logviewer/LogViewer.js +88 -70
  224. package/dist/logviewer/logviewer.css +3 -2
  225. package/dist/reducedMotion.d.ts +5 -0
  226. package/dist/reducedMotion.js +20 -0
  227. package/dist/safeUrl.d.ts +1 -1
  228. package/dist/safeUrl.js +4 -2
  229. package/dist/styles/theme.css +24 -10
  230. package/dist/styles.css +1 -1
  231. package/dist/theme/derive.d.ts +1 -1
  232. package/dist/theme/derive.js +46 -21
  233. package/dist/theme/fit.js +10 -4
  234. package/package.json +1 -1
  235. package/dist/components/Breadcrumbs.d.ts +0 -37
  236. package/dist/components/Breadcrumbs.js +0 -90
  237. package/dist/components/CopyToClipboard.d.ts +0 -17
  238. package/dist/components/CopyToClipboard.js +0 -66
  239. package/dist/components/Sort.d.ts +0 -9
  240. package/dist/components/Sort.js +0 -42
@@ -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;
@@ -9,9 +9,8 @@ var fallbacks = {};
9
9
  /** A token as Monaco takes it: a 6-digit hex. Monaco can't read display-p3, so
10
10
  * a wide-gamut value is mapped back into sRGB; the default theme fills in for
11
11
  * a host that sets no tokens. */
12
- function token(name, mode) {
13
- const set = typeof window === "undefined" ? "" : getComputedStyle(document.documentElement).getPropertyValue(name).trim();
14
- const color = parseColor(expandShorthandHex(set));
12
+ function token(style, name, mode) {
13
+ const color = parseColor(expandShorthandHex(style?.getPropertyValue(name).trim() ?? ""));
15
14
  if (color) return toHex(color);
16
15
  fallbacks[mode] ??= deriveTheme(schemeFor(DEFAULT_THEME, mode));
17
16
  return fallbacks[mode][name];
@@ -37,27 +36,22 @@ var RULES = [
37
36
  ["delimiter", "--theme-code-text"],
38
37
  ["operators", "--theme-code-text"]
39
38
  ];
40
- function editorTheme(mode, disabled) {
41
- const c = (name) => token(name, mode);
42
- const foreground = disabled ? c("--theme-muted-text-color") : c("--theme-code-text");
43
- const background = disabled ? c("--theme-input-disabled-bg") : c("--theme-panel-bg");
39
+ function editorTheme(style, mode) {
40
+ const c = (name) => token(style, name, mode);
44
41
  return {
45
42
  base: mode === "dark" ? "vs-dark" : "vs",
46
43
  inherit: true,
47
- rules: disabled ? [{
48
- token: "",
49
- foreground: foreground.slice(1)
50
- }] : RULES.map(([name, t, fontStyle]) => ({
44
+ rules: RULES.map(([name, t, fontStyle]) => ({
51
45
  token: name,
52
46
  foreground: c(t).slice(1),
53
47
  ...fontStyle ? { fontStyle } : {}
54
48
  })),
55
49
  colors: {
56
- "editor.foreground": foreground,
57
- "editor.background": background,
50
+ "editor.foreground": c("--theme-code-text"),
51
+ "editor.background": c("--theme-panel-bg"),
58
52
  "editor.border": c("--theme-border"),
59
- "editorCursor.foreground": disabled ? background : c("--theme-app"),
60
- "editor.lineHighlightBackground": disabled ? background : c("--theme-hover"),
53
+ "editorCursor.foreground": c("--theme-app"),
54
+ "editor.lineHighlightBackground": c("--theme-hover"),
61
55
  "editorLineNumber.foreground": c("--theme-placeholder"),
62
56
  "editorLineNumber.activeForeground": c("--theme-muted-text-color"),
63
57
  "editor.selectionBackground": c("--theme-accent-subtle"),
@@ -65,26 +59,60 @@ function editorTheme(mode, disabled) {
65
59
  }
66
60
  };
67
61
  }
68
- /** Monaco themes read from the document's tokens: call again after the host applies another theme. */
62
+ var definedFor = /* @__PURE__ */ new WeakMap();
63
+ /**
64
+ * Defines the light and dark Monaco themes from the document's tokens. Call it again after the
65
+ * host applies other tokens; it returns whether anything changed.
66
+ */
69
67
  function defineEditorThemes(monacoEditor) {
70
- for (const mode of ["light", "dark"]) for (const disabled of [false, true]) monacoEditor.defineTheme(getThemeName(mode === "dark", disabled), editorTheme(mode, disabled));
68
+ const style = typeof window === "undefined" ? void 0 : getComputedStyle(document.documentElement);
69
+ const themes = ["light", "dark"].map((mode) => [mode, editorTheme(style, mode)]);
70
+ const snapshot = JSON.stringify(themes);
71
+ if (definedFor.get(monacoEditor) === snapshot) return false;
72
+ definedFor.set(monacoEditor, snapshot);
73
+ for (const [mode, data] of themes) monacoEditor.defineTheme(getThemeName(mode === "dark"), data);
74
+ return true;
71
75
  }
72
- /** Redefines the editor themes when the host applies other tokens to the document,
73
- * and sets `current()` again, so an open editor follows a theme being edited. */
76
+ var followers = /* @__PURE__ */ new Set();
77
+ var observer;
78
+ var frame = 0;
79
+ function flush() {
80
+ frame = 0;
81
+ for (const follow of followers) follow();
82
+ }
83
+ /** Redefines the editor themes when the host applies other tokens or another scheme to the
84
+ * document, and sets `current()` again, so an open editor follows a theme being edited. */
74
85
  function followThemeTokens(monacoEditor, current) {
75
86
  if (typeof MutationObserver === "undefined") return () => {};
76
- const observer = new MutationObserver(() => {
77
- defineEditorThemes(monacoEditor);
78
- monacoEditor.setTheme(current());
79
- });
80
- observer.observe(document.documentElement, {
81
- attributes: true,
82
- attributeFilter: ["style"]
83
- });
84
- return () => observer.disconnect();
87
+ const follow = () => {
88
+ if (defineEditorThemes(monacoEditor)) monacoEditor.setTheme(current());
89
+ };
90
+ followers.add(follow);
91
+ if (!observer) {
92
+ observer = new MutationObserver(() => {
93
+ frame ||= requestAnimationFrame(flush);
94
+ });
95
+ observer.observe(document.documentElement, {
96
+ attributes: true,
97
+ attributeFilter: [
98
+ "style",
99
+ "class",
100
+ "data-theme"
101
+ ]
102
+ });
103
+ }
104
+ return () => {
105
+ followers.delete(follow);
106
+ if (followers.size === 0) {
107
+ observer?.disconnect();
108
+ observer = void 0;
109
+ cancelAnimationFrame(frame);
110
+ frame = 0;
111
+ }
112
+ };
85
113
  }
86
- function getThemeName(isDark, disabled) {
87
- return `app-${isDark ? "dark" : "light"}${disabled ? "-disabled" : ""}`;
114
+ function getThemeName(isDark) {
115
+ return `app-${isDark ? "dark" : "light"}`;
88
116
  }
89
117
  //#endregion
90
118
  export { defineEditorThemes, expandShorthandHex, followThemeTokens, getThemeName };
@@ -7,7 +7,11 @@ export interface SchemasSettings {
7
7
  /** The schema itself, used instead of fetching `uri`. */
8
8
  schema?: object;
9
9
  }
10
- /** Adds JSON schemas that validate and complete YAML models whose URI matches. */
11
- export declare function configureEditorYaml(added: SchemasSettings[]): void;
10
+ /**
11
+ * Adds JSON schemas that validate and complete YAML models whose URI matches. A schema with the
12
+ * `uri` of one already added replaces it, so calling this again, as a reload or a second mount
13
+ * does, adds nothing twice. Returns a function that removes these schemas.
14
+ */
15
+ export declare function configureEditorYaml(added: SchemasSettings[]): () => void;
12
16
  /** Lets the loaded editor pick up schemas added before or after it loads. */
13
17
  export declare function onYamlSchemas(listener: (schemas: SchemasSettings[]) => void): void;
@@ -1,10 +1,19 @@
1
1
  //#region src/editor/yaml.ts
2
2
  var schemas = [];
3
3
  var apply;
4
- /** Adds JSON schemas that validate and complete YAML models whose URI matches. */
4
+ /**
5
+ * Adds JSON schemas that validate and complete YAML models whose URI matches. A schema with the
6
+ * `uri` of one already added replaces it, so calling this again, as a reload or a second mount
7
+ * does, adds nothing twice. Returns a function that removes these schemas.
8
+ */
5
9
  function configureEditorYaml(added) {
6
- schemas = [...schemas, ...added];
10
+ const uris = new Set(added.map((schema) => schema.uri));
11
+ schemas = [...schemas.filter((schema) => !uris.has(schema.uri)), ...added];
7
12
  apply?.(schemas);
13
+ return () => {
14
+ schemas = schemas.filter((schema) => !added.includes(schema));
15
+ apply?.(schemas);
16
+ };
8
17
  }
9
18
  /** Lets the loaded editor pick up schemas added before or after it loads. */
10
19
  function onYamlSchemas(listener) {