flexdesk 0.16.1 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/LICENSE +0 -0
  3. package/README.md +2435 -42
  4. package/THIRD_PARTY_LICENSES.md +0 -0
  5. package/canvas.js +18 -0
  6. package/charts.js +21 -0
  7. package/core.js +14 -0
  8. package/css/base.css +1834 -75
  9. package/css/flexdesk.css +3165 -159
  10. package/css/overrides.css +16 -0
  11. package/css/reset.css +0 -0
  12. package/css/tokens.css +0 -0
  13. package/dist/canvas.js +6 -239
  14. package/dist/canvas.js.map +3 -3
  15. package/dist/charts.js +707 -18
  16. package/dist/charts.js.map +4 -4
  17. package/dist/chunk-3PHCPZHT.js +0 -0
  18. package/dist/chunk-3PHCPZHT.js.map +0 -0
  19. package/dist/chunk-4A7SJC3T.js +503 -0
  20. package/dist/chunk-4A7SJC3T.js.map +7 -0
  21. package/dist/{chunk-6JSOVNID.js → chunk-CVMCZZX2.js} +635 -10
  22. package/dist/chunk-CVMCZZX2.js.map +7 -0
  23. package/dist/chunk-D3COLZZ2.js +1311 -0
  24. package/dist/chunk-D3COLZZ2.js.map +7 -0
  25. package/dist/chunk-FOOS3T5L.js +0 -0
  26. package/dist/chunk-FOOS3T5L.js.map +0 -0
  27. package/dist/{chunk-SN4LSOAR.js → chunk-GOQLR5WR.js} +452 -2
  28. package/dist/chunk-GOQLR5WR.js.map +7 -0
  29. package/dist/{chunk-FL5KFNQH.js → chunk-HNU6MM2B.js} +52 -17
  30. package/dist/chunk-HNU6MM2B.js.map +7 -0
  31. package/dist/chunk-HYJYYEPK.js +375 -0
  32. package/dist/chunk-HYJYYEPK.js.map +7 -0
  33. package/dist/chunk-JKZLNKG6.js +2041 -0
  34. package/dist/chunk-JKZLNKG6.js.map +7 -0
  35. package/dist/chunk-JYWURG5T.js +0 -0
  36. package/dist/chunk-JYWURG5T.js.map +0 -0
  37. package/dist/{chunk-FBGP5KN5.js → chunk-LWV23YVE.js} +5073 -532
  38. package/dist/chunk-LWV23YVE.js.map +7 -0
  39. package/dist/chunk-N7HC3ADV.js +30 -0
  40. package/dist/chunk-N7HC3ADV.js.map +7 -0
  41. package/dist/{chunk-RQM5Z2MU.js → chunk-RMTRWWEO.js} +633 -311
  42. package/dist/chunk-RMTRWWEO.js.map +7 -0
  43. package/dist/{chunk-NLTOY57M.js → chunk-UURZ37SD.js} +28 -48
  44. package/dist/chunk-UURZ37SD.js.map +7 -0
  45. package/dist/{chunk-B5Q53LAJ.js → chunk-UWO43BZU.js} +2 -2
  46. package/dist/chunk-UWO43BZU.js.map +7 -0
  47. package/dist/chunk-WVFGV5FT.js +0 -0
  48. package/dist/chunk-WVFGV5FT.js.map +0 -0
  49. package/dist/core.js +170 -118
  50. package/dist/core.js.map +2 -2
  51. package/dist/editor.js +156 -70
  52. package/dist/editor.js.map +2 -2
  53. package/dist/flexdesk.css +3165 -159
  54. package/dist/flow.js +2789 -306
  55. package/dist/flow.js.map +4 -4
  56. package/dist/host.js +109 -3
  57. package/dist/host.js.map +3 -3
  58. package/dist/reset.css +0 -0
  59. package/dist/tile_registry-6WZPTUZV.js +0 -0
  60. package/dist/tile_registry-6WZPTUZV.js.map +0 -0
  61. package/dist/tiles.js +657 -234
  62. package/dist/tiles.js.map +4 -4
  63. package/dist/tokens.css +0 -0
  64. package/dist/widgets.js +3290 -2124
  65. package/dist/widgets.js.map +4 -4
  66. package/dist/wm.js +1227 -347
  67. package/dist/wm.js.map +4 -4
  68. package/editor.js +20 -0
  69. package/flow.js +21 -0
  70. package/host.js +14 -0
  71. package/package.json +12 -1
  72. package/src/canvas/canvas_adapter.js +5 -0
  73. package/src/canvas/edge_router.js +0 -0
  74. package/src/canvas/graph/connector_router.js +0 -0
  75. package/src/canvas/graph/history_service.js +0 -0
  76. package/src/canvas/graph/node_platform.js +0 -0
  77. package/src/canvas/index.js +1 -0
  78. package/src/canvas/layered_layout.js +244 -0
  79. package/src/charts/chart_types.js +0 -0
  80. package/src/charts/data_series_plot_window.js +0 -0
  81. package/src/charts/downsample.js +2 -2
  82. package/src/charts/plot_config.js +53 -29
  83. package/src/charts/plot_config_editor.js +627 -0
  84. package/src/charts/plot_grid.js +449 -0
  85. package/src/charts/plot_popout_window.js +103 -317
  86. package/src/charts/plotly_wrapper.js +22 -2
  87. package/src/charts/series_transforms.js +206 -0
  88. package/src/core/event_bus.js +0 -0
  89. package/src/core/logging.js +1 -1
  90. package/src/core/settings.js +105 -19
  91. package/src/core/state_guard.js +93 -51
  92. package/src/core/state_guard_config.js +85 -74
  93. package/src/core/state_machine.js +0 -0
  94. package/src/editor/editor_pane.js +3 -3
  95. package/src/editor/monaco_editor_factory.js +124 -60
  96. package/src/editor/monaco_loader.js +103 -28
  97. package/src/editor/notebook_search.js +0 -0
  98. package/src/editor/notebook_tab_bar.js +38 -52
  99. package/src/editor/notebook_undo_manager.js +0 -0
  100. package/src/editor/split_pane_container.js +6 -3
  101. package/src/flow/canvas_editor/connections.js +90 -0
  102. package/src/flow/canvas_editor/editor.js +2351 -153
  103. package/src/flow/canvas_editor/index.js +2 -1
  104. package/src/flow/canvas_editor/layout.js +119 -12
  105. package/src/flow/canvas_editor/strings.js +80 -0
  106. package/src/flow/kit/always_before.js +0 -0
  107. package/src/flow/kit/catalogue.js +97 -3
  108. package/src/flow/kit/chip_input.js +0 -0
  109. package/src/flow/kit/dom.js +0 -0
  110. package/src/flow/kit/findings.js +0 -0
  111. package/src/flow/kit/history.js +0 -0
  112. package/src/flow/kit/index.js +3 -3
  113. package/src/flow/kit/keys.js +7 -1
  114. package/src/flow/kit/logic_graph.js +97 -15
  115. package/src/flow/kit/references.js +0 -0
  116. package/src/flow/kit/settings_panel.js +0 -0
  117. package/src/flow/kit/settings_schema.js +2 -1
  118. package/src/flow/kit/step_picker.js +0 -0
  119. package/src/flow/kit/strings.js +3 -1
  120. package/src/flow/kit/value_picker.js +0 -0
  121. package/src/flow/kit/widgets.js +86 -109
  122. package/src/flow/lanes/editor.js +136 -14
  123. package/src/flow/lanes/index.js +3 -1
  124. package/src/flow/lanes/layout.js +0 -0
  125. package/src/flow/lanes/pipeline.js +44 -2
  126. package/src/flow/lanes/preview.js +0 -0
  127. package/src/flow/lanes/strings.js +1 -0
  128. package/src/flow/outline/editor.js +0 -0
  129. package/src/flow/outline/index.js +0 -0
  130. package/src/flow/outline/mapping.js +0 -0
  131. package/src/flow/outline/menu.js +0 -0
  132. package/src/flow/outline/operations.js +0 -0
  133. package/src/flow/outline/recognise.js +0 -0
  134. package/src/flow/outline/strings.js +0 -0
  135. package/src/help/help_modal.js +168 -42
  136. package/src/help/help_registry.js +12 -1
  137. package/src/host/external_links.js +0 -0
  138. package/src/host/file_picker.js +117 -0
  139. package/src/host/host.js +10 -2
  140. package/src/host/photino_host.js +9 -0
  141. package/src/host/photino_host.md +21 -3
  142. package/src/host/pywebview_host.js +10 -0
  143. package/src/tiles/chart_tiles.js +53 -245
  144. package/src/tiles/config_schema.js +8 -0
  145. package/src/tiles/figure_tile.js +322 -0
  146. package/src/tiles/layout_persistence.js +3 -3
  147. package/src/tiles/list_tiles.js +7 -7
  148. package/src/tiles/scope_picker.js +0 -0
  149. package/src/tiles/tile_base.js +1 -1
  150. package/src/tiles/tile_config_form.js +442 -0
  151. package/src/tiles/tile_grid.js +2 -0
  152. package/src/tiles/tile_registry.js +0 -0
  153. package/src/tiling/bottom_panel_tabs.js +67 -99
  154. package/src/tiling/command_palette.js +64 -19
  155. package/src/tiling/content_registry.js +4 -1
  156. package/src/tiling/desktops.js +11 -1
  157. package/src/tiling/entity_sources.js +0 -0
  158. package/src/tiling/keymap.js +403 -128
  159. package/src/tiling/kind_taxonomy.js +78 -0
  160. package/src/tiling/landing_table.js +0 -0
  161. package/src/tiling/loading_overlay.js +0 -0
  162. package/src/tiling/nav_panel.js +59 -10
  163. package/src/tiling/page_factory.js +5 -5
  164. package/src/tiling/panel_availability.js +49 -0
  165. package/src/tiling/panel_keys.js +0 -0
  166. package/src/tiling/shell.js +114 -12
  167. package/src/tiling/tab_strip.js +0 -0
  168. package/src/tiling/tile_breadcrumb.js +0 -0
  169. package/src/tiling/tile_renderer.js +105 -9
  170. package/src/tiling/tile_tab_menu.js +0 -0
  171. package/src/tiling/tile_tree.js +0 -0
  172. package/src/tiling/wm.js +562 -30
  173. package/src/tiling/zoom.js +0 -0
  174. package/src/ui/base/component_base.js +0 -0
  175. package/src/ui/base/controller_base.js +0 -0
  176. package/src/ui/base/page_base.js +0 -0
  177. package/src/ui/components/about_dialog.js +0 -0
  178. package/src/ui/components/action_dropdown.js +30 -2
  179. package/src/ui/components/alert_log.js +0 -0
  180. package/src/ui/components/alerts_pane.js +0 -0
  181. package/src/ui/components/attribute_list_editor.js +0 -0
  182. package/src/ui/components/autocomplete_field.js +0 -0
  183. package/src/ui/components/cell_editor_host.js +293 -0
  184. package/src/ui/components/cell_editors.js +480 -0
  185. package/src/ui/components/cell_grid.js +761 -0
  186. package/src/ui/components/cell_paste.js +243 -0
  187. package/src/ui/components/cell_selection.js +220 -0
  188. package/src/ui/components/choice_controls.js +351 -0
  189. package/src/ui/components/color_field.js +211 -0
  190. package/src/ui/components/color_list_field.js +210 -0
  191. package/src/ui/components/column_fit.js +0 -0
  192. package/src/ui/components/column_groups.js +314 -0
  193. package/src/ui/components/column_order.js +142 -0
  194. package/src/ui/components/computing_status_window.js +98 -25
  195. package/src/ui/components/confirm_dialog.js +81 -38
  196. package/src/ui/components/context_menu.js +2 -2
  197. package/src/ui/components/conversation_view.js +440 -0
  198. package/src/ui/components/crosstab.js +289 -0
  199. package/src/ui/components/data_table.js +696 -71
  200. package/src/ui/components/detail_header.js +22 -11
  201. package/src/ui/components/document_outline.js +397 -0
  202. package/src/ui/components/drag_reorder.js +0 -0
  203. package/src/ui/components/fact_table.js +0 -0
  204. package/src/ui/components/fill_handle.js +322 -0
  205. package/src/ui/components/gallery_picker.js +19 -8
  206. package/src/ui/components/header_actions.js +55 -0
  207. package/src/ui/components/inline_renamer.js +0 -0
  208. package/src/ui/components/job_progress.js +51 -0
  209. package/src/ui/components/managed_window.js +27 -4
  210. package/src/ui/components/menu_bar.js +745 -0
  211. package/src/ui/components/modal.js +449 -75
  212. package/src/ui/components/notification_history.js +3 -3
  213. package/src/ui/components/progress.js +483 -0
  214. package/src/ui/components/record_list.js +0 -0
  215. package/src/ui/components/row_activation.js +0 -0
  216. package/src/ui/components/section_stack.js +298 -0
  217. package/src/ui/components/series.js +173 -0
  218. package/src/ui/components/settings_view.js +634 -0
  219. package/src/ui/components/slide_out_panel.js +43 -6
  220. package/src/ui/components/slider_field.js +275 -18
  221. package/src/ui/components/sortable_list.js +0 -0
  222. package/src/ui/components/staged_edits.js +361 -0
  223. package/src/ui/components/staged_history.js +172 -0
  224. package/src/ui/components/table_filter_chips.js +0 -0
  225. package/src/ui/components/table_state_store.js +0 -0
  226. package/src/ui/components/table_structure.js +182 -0
  227. package/src/ui/components/table_values.js +0 -0
  228. package/src/ui/components/tabs.js +497 -0
  229. package/src/ui/components/tag_input.js +215 -0
  230. package/src/ui/components/toast.js +25 -8
  231. package/src/ui/components/toggle_field.js +175 -0
  232. package/src/ui/components/tree_view.js +55 -34
  233. package/src/ui/components/tsv.js +74 -0
  234. package/src/ui/components/window_taskbar.js +0 -0
  235. package/src/ui/components/wizard.js +563 -0
  236. package/src/ui/controllers/window_chrome_controller.js +2 -2
  237. package/src/ui/notification_center.js +375 -119
  238. package/src/ui/utils/auto_scrollbars.js +0 -0
  239. package/src/ui/utils/markdown.js +709 -0
  240. package/src/ui/utils/native_context_menu.js +0 -0
  241. package/src/ui/utils/overlay_scrollbar.js +16 -12
  242. package/src/ui/utils/raf_resize_observer.js +0 -0
  243. package/src/ui/utils/scroll_lock.js +0 -0
  244. package/src/ui/utils/strip_scroll.js +55 -0
  245. package/src/ui/utils/tooltip_service.js +0 -8
  246. package/tiles.js +27 -0
  247. package/widgets.js +69 -0
  248. package/wm.js +28 -0
  249. package/dist/chunk-6JSOVNID.js.map +0 -7
  250. package/dist/chunk-B5Q53LAJ.js.map +0 -7
  251. package/dist/chunk-FBGP5KN5.js.map +0 -7
  252. package/dist/chunk-FL5KFNQH.js.map +0 -7
  253. package/dist/chunk-NLTOY57M.js.map +0 -7
  254. package/dist/chunk-NNFTETK3.js +0 -633
  255. package/dist/chunk-NNFTETK3.js.map +0 -7
  256. package/dist/chunk-O5OHMWBB.js +0 -284
  257. package/dist/chunk-O5OHMWBB.js.map +0 -7
  258. package/dist/chunk-RQM5Z2MU.js.map +0 -7
  259. package/dist/chunk-SN4LSOAR.js.map +0 -7
  260. package/dist/chunk-W6FBJLPW.js +0 -367
  261. package/dist/chunk-W6FBJLPW.js.map +0 -7
  262. package/src/ui/controllers/panel_state_machine.js +0 -634
@@ -1,53 +1,102 @@
1
1
  /**
2
2
  * Computing Status Window
3
3
  *
4
- * Lightweight ManagedWindow that shows a spinner and status message
5
- * while a long-running computation is in progress. Returns a handle
6
- * with update() and close() methods.
4
+ * Lightweight ManagedWindow that shows a spinner (or a progress bar) and a
5
+ * status message while a long-running computation is in progress. Returns a
6
+ * handle with update(), setProgress(), setStep(), complete(), cancelled(),
7
+ * close() and a `result` promise.
7
8
  *
8
9
  * Optionally shows a Cancel button when an onCancel callback is provided.
10
+ * Only one is open at a time: opening another closes the first, which then
11
+ * settles as cancelled (its onCancel is not called).
9
12
  */
10
13
 
11
14
  import { ManagedWindow } from './managed_window.js';
15
+ import { mountJobProgress } from './job_progress.js';
12
16
 
13
17
  let _activeWindow = null;
14
18
 
15
19
  /**
16
20
  * Show a computing status window.
21
+ *
22
+ * `result` settles once: `complete({ok, message})` gives `done` or `error`
23
+ * (the window stays, with a Close button, until closed), `cancelled()` gives
24
+ * `cancelled`, and so does closing the window while it runs (by its ×,
25
+ * Escape, `close()` or opening another). Closing never calls `onCancel`: only
26
+ * the Cancel button does. `progress` and `steps` are fixed when the window
27
+ * opens: `setProgress` throws if it was opened without `progress`, and
28
+ * `setStep` if it was opened without `steps`.
29
+ *
17
30
  * @param {Object} options
18
31
  * @param {string} [options.title='Computing...'] - Window title
19
- * @param {string} [options.message=''] - Status message below spinner
32
+ * @param {string} [options.message=''] - Status message below spinner (markup; `update` takes text)
20
33
  * @param {string} [options.icon='hourglass_top'] - Material icon name
21
34
  * @param {Function} [options.onCancel] - If provided, shows a Cancel button. Called once when clicked.
22
- * @returns {{ update(msg: string): void, close(): void }}
35
+ * @param {boolean|Object} [options.progress] - A ProgressBar in place of the spinner: `true` or its options.
36
+ * @param {Array} [options.steps] - StepProgress steps, shown above the message.
37
+ * @returns {{ update(msg: string): void, setProgress(next: number|Object): void,
38
+ * setStep(which: string|number, opts?: {pass?: number, passes?: number}): void,
39
+ * complete(result: {ok: boolean, message?: string}): void, cancelled(msg?: string): void,
40
+ * close(): void, result: Promise<{status: 'done'|'error'|'cancelled', message: string}> }}
23
41
  */
24
- export function showComputingWindow({ title = 'Computing...', message = '', icon = 'hourglass_top', onCancel } = {}) {
42
+ export function showComputingWindow({
43
+ title = 'Computing...', message = '', icon = 'hourglass_top', onCancel,
44
+ progress = null, steps = null,
45
+ } = {}) {
25
46
  if (_activeWindow) {
26
47
  _activeWindow.close();
27
48
  _activeWindow = null;
28
49
  }
29
50
 
51
+ let finished = false;
52
+ let settle;
53
+ const result = new Promise((resolve) => { settle = resolve; });
54
+
30
55
  const content = document.createElement('div');
31
56
  content.className = 'twm-computing-status';
32
- content.innerHTML = `
33
- <div class="twm-computing-status__spinner"></div>
34
- <div class="twm-computing-status__message">${message}</div>
35
- `;
57
+ const spinnerEl = document.createElement('div');
58
+ spinnerEl.className = 'twm-computing-status__spinner';
59
+ const msgEl = document.createElement('div');
60
+ msgEl.className = 'twm-computing-status__message';
61
+ // The opening message is markup, as it always was; every later one is text.
62
+ msgEl.innerHTML = message;
63
+ content.appendChild(spinnerEl);
64
+ const job = mountJobProgress(content, { progress, steps, spinner: spinnerEl });
65
+ content.appendChild(msgEl);
36
66
 
67
+ let cancelBtn = null;
37
68
  if (typeof onCancel === 'function') {
38
- const btn = document.createElement('button');
39
- btn.className = 'twm-computing-status__cancel-btn';
40
- btn.textContent = 'Cancel';
41
- btn.addEventListener('click', () => {
42
- btn.disabled = true;
43
- const msgEl = content.querySelector('.twm-computing-status__message');
44
- if (msgEl) msgEl.textContent = 'Cancelling...';
69
+ cancelBtn = document.createElement('button');
70
+ cancelBtn.className = 'twm-computing-status__cancel-btn';
71
+ cancelBtn.textContent = 'Cancel';
72
+ cancelBtn.addEventListener('click', () => {
73
+ cancelBtn.disabled = true;
74
+ msgEl.textContent = 'Cancelling...';
45
75
  onCancel();
46
76
  }, { once: true });
47
- content.appendChild(btn);
77
+ content.appendChild(cancelBtn);
48
78
  }
49
79
 
50
- _activeWindow = new ManagedWindow({
80
+ const closeBtn = document.createElement('button');
81
+ closeBtn.type = 'button';
82
+ closeBtn.className = 'twm-computing-status__cancel-btn twm-computing-status__close-btn';
83
+ closeBtn.textContent = 'Close';
84
+ closeBtn.hidden = true;
85
+ content.appendChild(closeBtn);
86
+
87
+ const finish = (status, text) => {
88
+ finished = true;
89
+ spinnerEl.hidden = true;
90
+ if (cancelBtn) cancelBtn.hidden = true;
91
+ msgEl.textContent = text;
92
+ msgEl.classList.toggle('is-ok', status === 'done');
93
+ msgEl.classList.toggle('is-err', status === 'error');
94
+ msgEl.classList.toggle('is-cancelled', status === 'cancelled');
95
+ job.finish(status);
96
+ settle({ status, message: text });
97
+ };
98
+
99
+ const win = new ManagedWindow({
51
100
  id: 'computing-status-window',
52
101
  title,
53
102
  icon,
@@ -55,21 +104,45 @@ export function showComputingWindow({ title = 'Computing...', message = '', icon
55
104
  minWidth: 320,
56
105
  minHeight: 140,
57
106
  defaultWidth: 380,
58
- defaultHeight: onCancel ? 190 : 160,
107
+ defaultHeight: (onCancel ? 190 : 160) + (job.bar ? 30 : 0) + (job.steps ? 40 : 0),
59
108
  canMinimize: true,
60
109
  canMaximize: false,
61
110
  canResize: false,
62
111
  canDrag: true,
63
112
  modal: false,
64
- onClose: () => { _activeWindow = null; },
113
+ onClose: () => {
114
+ if (_activeWindow === win) _activeWindow = null;
115
+ if (finished) return;
116
+ finish('cancelled', 'Cancelled.');
117
+ },
65
118
  });
66
-
67
- _activeWindow.show();
119
+ closeBtn.addEventListener('click', () => win.close());
120
+ _activeWindow = win;
121
+ win.show();
68
122
 
69
123
  return {
124
+ result,
70
125
  update(msg) {
71
- const el = content.querySelector('.twm-computing-status__message');
72
- if (el) el.textContent = msg;
126
+ if (finished) return;
127
+ msgEl.textContent = msg;
128
+ },
129
+ setProgress(next) {
130
+ if (finished) return;
131
+ job.setProgress(next);
132
+ },
133
+ setStep(which, opts) {
134
+ if (finished) return;
135
+ job.setStep(which, opts);
136
+ },
137
+ complete(res) {
138
+ if (finished) return;
139
+ finish(res?.ok ? 'done' : 'error', res?.message || '');
140
+ closeBtn.hidden = false;
141
+ },
142
+ cancelled(msg) {
143
+ if (finished) return;
144
+ finish('cancelled', msg || 'Cancelled.');
145
+ closeBtn.hidden = false;
73
146
  },
74
147
  close() {
75
148
  if (_activeWindow) {
@@ -11,12 +11,14 @@
11
11
  * action, square frame, Tab focus trap).
12
12
  *
13
13
  * The one custom case is `showRunProgressDialog`, which exposes an
14
- * imperative `update / complete / cancelled / close` API and so
14
+ * imperative `update / setProgress / setStep / complete / cancelled /
15
+ * close` API and a `result` promise, and so
15
16
  * stays as a direct `ManagedWindow` build — but it still benefits
16
17
  * from the focus trap + chrome by virtue of `modal: true`.
17
18
  */
18
19
 
19
20
  import { ManagedWindow } from './managed_window.js';
21
+ import { mountJobProgress } from './job_progress.js';
20
22
  import {
21
23
  openConfirm, openForm, openModal,
22
24
  } from './modal.js';
@@ -65,10 +67,12 @@ export async function showDeleteConfirmDialog(options = {}) {
65
67
  itemType = 'item',
66
68
  additionalMessage = '',
67
69
  } = options;
70
+ // `openConfirm` renders the message as text, so a user-named item
71
+ // (`<b>x</b>`, an unclosed `<`) is shown exactly as it was typed.
68
72
  let message = `Delete ${itemType}`;
69
73
  if (itemName) message += ` "${itemName}"`;
70
74
  message += '?';
71
- if (additionalMessage) message += `<br><br>${escapeHtml(additionalMessage)}`;
75
+ if (additionalMessage) message += `\n\n${additionalMessage}`;
72
76
  return openConfirm({
73
77
  title: `Delete ${capitalize(itemType)}`,
74
78
  message,
@@ -189,41 +193,73 @@ function capitalize(str) {
189
193
  }
190
194
 
191
195
 
192
- // ─── showRunProgressDialog (unchanged custom build) ─────────────────
196
+ // ─── showRunProgressDialog (custom build) ───────────────────────────
193
197
  //
194
- // This one exposes an imperative API (`update`, `complete`, `cancelled`,
195
- // `close`) that the openModal/openForm/openConfirm helpers don't model
196
- // — they're one-shot promise-returning. Keep the direct ManagedWindow
197
- // build, but it still gets the TUI chrome + focus trap for free via
198
- // `modal: true`.
198
+ // This one exposes an imperative API (`update`, `setProgress`, `setStep`,
199
+ // `complete`, `cancelled`, `close`) that the openModal/openForm/openConfirm
200
+ // helpers don't model — they're one-shot promise-returning. Keep the direct
201
+ // ManagedWindow build, but it still gets the TUI chrome + focus trap for free
202
+ // via `modal: true`.
199
203
 
200
204
  /**
201
- * Show a run progress dialog with spinner, live message updates, and
202
- * final status. Returns a control object — keep this build instead of
203
- * delegating so callers retain the imperative update / complete API.
204
- * @returns {{ update(msg: string): void, complete(result: {ok: boolean, message: string}): void, cancelled(msg?: string): void, close(): void }}
205
+ * @typedef {Object} RunProgressResult
206
+ * @property {'done'|'error'|'cancelled'} status
207
+ * @property {string} message
208
+ */
209
+
210
+ /**
211
+ * Show a run progress dialog with a spinner (or a bar), live message
212
+ * updates, optional phases, and a final status.
213
+ *
214
+ * `progress` (`true` or ProgressBar options) shows a determinate bar in place
215
+ * of the spinner; `steps` (StepProgress steps) shows the phases above it.
216
+ * Both are fixed when the dialog opens: `setProgress` throws if it was opened
217
+ * without `progress`, and `setStep` if it was opened without `steps`.
218
+ * `result` settles once, with the final state: `complete({ok})` gives `done`
219
+ * or `error`, `cancelled()` gives `cancelled`, and so does closing the dialog
220
+ * while it runs (by its ×, Escape or `close()`). Closing never calls
221
+ * `onCancel`: only the Stop button does.
222
+ *
223
+ * @param {Object} [options]
224
+ * @param {string} [options.title='Running Pipeline']
225
+ * @param {string} [options.message='Starting…']
226
+ * @param {Function} [options.onCancel] - Shows a Stop button.
227
+ * @param {boolean|Object} [options.progress] - A ProgressBar, `true` or its options.
228
+ * @param {Array} [options.steps] - StepProgress steps.
229
+ * @returns {{ update(msg: string): void, setProgress(next: number|Object): void,
230
+ * setStep(which: string|number, opts?: {pass?: number, passes?: number}): void,
231
+ * complete(result: {ok: boolean, message: string}): void, cancelled(msg?: string): void,
232
+ * close(): void, result: Promise<RunProgressResult> }}
205
233
  */
206
234
  export function showRunProgressDialog(options = {}) {
207
235
  const {
208
236
  title = 'Running Pipeline',
209
237
  message = 'Starting…',
210
238
  onCancel,
239
+ progress = null,
240
+ steps = null,
211
241
  } = options;
212
242
 
213
243
  const dialogId = `run-progress-${++dialogIdCounter}`;
214
244
  let finished = false;
245
+ let settle;
246
+ const result = new Promise((resolve) => { settle = resolve; });
215
247
 
216
248
  const contentEl = document.createElement('div');
217
249
  contentEl.className = 'twm-confirm-dialog__content run-progress';
218
250
 
219
251
  const bodyEl = document.createElement('div');
220
252
  bodyEl.className = 'twm-run-progress__body';
221
- bodyEl.innerHTML = `
222
- <div class="twm-run-progress__spinner"></div>
223
- <div class="twm-run-progress__message">${escapeHtml(message)}</div>
224
- `;
253
+ const spinnerEl = document.createElement('div');
254
+ spinnerEl.className = 'twm-run-progress__spinner';
255
+ const messageEl = document.createElement('div');
256
+ messageEl.className = 'twm-run-progress__message';
257
+ messageEl.textContent = message;
258
+ bodyEl.append(spinnerEl, messageEl);
225
259
  contentEl.appendChild(bodyEl);
226
260
 
261
+ const job = mountJobProgress(contentEl, { progress, steps, spinner: spinnerEl });
262
+
227
263
  let stopBtn = null;
228
264
  if (typeof onCancel === 'function') {
229
265
  const stopActionsEl = document.createElement('div');
@@ -253,6 +289,17 @@ export function showRunProgressDialog(options = {}) {
253
289
  actionsEl.appendChild(closeBtn);
254
290
  contentEl.appendChild(actionsEl);
255
291
 
292
+ const finish = (status, text, cls) => {
293
+ finished = true;
294
+ spinnerEl.style.display = 'none';
295
+ if (stopBtn) stopBtn.parentElement.style.display = 'none';
296
+ messageEl.textContent = text;
297
+ messageEl.className = `twm-run-progress__message ${cls}`;
298
+ job.finish(status);
299
+ actionsEl.style.display = '';
300
+ settle({ status, message: text });
301
+ };
302
+
256
303
  const dialogWindow = new ManagedWindow({
257
304
  id: dialogId,
258
305
  title,
@@ -261,44 +308,40 @@ export function showRunProgressDialog(options = {}) {
261
308
  minWidth: 320,
262
309
  minHeight: 120,
263
310
  defaultWidth: 420,
264
- defaultHeight: 200,
311
+ defaultHeight: 200 + (job.bar ? 40 : 0) + (job.steps ? 40 : 0),
265
312
  canMinimize: false,
266
313
  canMaximize: false,
267
314
  canResize: false,
268
315
  canDrag: false,
269
316
  modal: true,
317
+ onClose: () => {
318
+ if (finished) return;
319
+ finish('cancelled', 'Cancelled.', 'is-cancelled');
320
+ },
270
321
  });
271
322
  dialogWindow.show();
272
323
 
273
- const messageEl = bodyEl.querySelector('.twm-run-progress__message');
274
- const spinnerEl = bodyEl.querySelector('.twm-run-progress__spinner');
275
-
276
324
  return {
325
+ result,
277
326
  update(msg) {
278
- if (finished || !messageEl) return;
327
+ if (finished) return;
279
328
  messageEl.textContent = msg;
280
329
  },
281
- complete(result) {
330
+ setProgress(next) {
331
+ if (finished) return;
332
+ job.setProgress(next);
333
+ },
334
+ setStep(which, opts) {
335
+ if (finished) return;
336
+ job.setStep(which, opts);
337
+ },
338
+ complete(res) {
282
339
  if (finished) return;
283
- finished = true;
284
- if (spinnerEl) spinnerEl.style.display = 'none';
285
- if (stopBtn) stopBtn.parentElement.style.display = 'none';
286
- if (messageEl) {
287
- messageEl.textContent = result?.message || '';
288
- messageEl.className = `twm-run-progress__message ${result?.ok ? 'is-ok' : 'is-err'}`;
289
- }
290
- actionsEl.style.display = '';
340
+ finish(res?.ok ? 'done' : 'error', res?.message || '', res?.ok ? 'is-ok' : 'is-err');
291
341
  },
292
342
  cancelled(msg) {
293
343
  if (finished) return;
294
- finished = true;
295
- if (spinnerEl) spinnerEl.style.display = 'none';
296
- if (stopBtn) stopBtn.parentElement.style.display = 'none';
297
- if (messageEl) {
298
- messageEl.textContent = msg || 'Cancelled.';
299
- messageEl.className = 'twm-run-progress__message is-cancelled';
300
- }
301
- actionsEl.style.display = '';
344
+ finish('cancelled', msg || 'Cancelled.', 'is-cancelled');
302
345
  },
303
346
  close() {
304
347
  try { dialogWindow.close(); } catch { /* already closed */ }
@@ -1,6 +1,6 @@
1
1
  /**
2
- * context_menu.js — small popup menu that matches Ecosim's `.context-menu`
3
- * idiom (see main_new.css line 9713 and ui/js/ui/components/data_table.js).
2
+ * context_menu.js — small popup menu in the `.context-menu` idiom the
3
+ * DataTable's own right-click menu uses (./data_table.js).
4
4
  *
5
5
  * Usage:
6
6
  * showContextMenu(x, y, [