flexdesk 0.17.18 → 0.18.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 (126) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +1 -0
  3. package/css/flexdesk.css +7547 -5891
  4. package/css/reset.css +1 -1
  5. package/css/tokens.css +30 -1
  6. package/dist/charts.js +83 -13
  7. package/dist/charts.js.map +2 -2
  8. package/dist/chunk-3BLIVOFD.js +778 -0
  9. package/dist/chunk-3BLIVOFD.js.map +7 -0
  10. package/dist/{chunk-7OL6F2AM.js → chunk-4X4SY3CP.js} +7 -5
  11. package/dist/chunk-4X4SY3CP.js.map +7 -0
  12. package/dist/chunk-DNJ3N4T4.js +82 -0
  13. package/dist/chunk-DNJ3N4T4.js.map +7 -0
  14. package/dist/{chunk-PGZ4EHSE.js → chunk-HL3IIHU3.js} +501 -359
  15. package/dist/chunk-HL3IIHU3.js.map +7 -0
  16. package/dist/{chunk-REJ5HLIM.js → chunk-HYJYYEPK.js} +2 -2
  17. package/dist/{chunk-7U4IB65H.js → chunk-LLBJ2SJH.js} +5 -3
  18. package/dist/{chunk-7U4IB65H.js.map → chunk-LLBJ2SJH.js.map} +1 -1
  19. package/dist/{chunk-XJNADRGR.js → chunk-NFZJ6P42.js} +188 -96
  20. package/dist/chunk-NFZJ6P42.js.map +7 -0
  21. package/dist/chunk-PAI2ZUWV.js +260 -0
  22. package/dist/chunk-PAI2ZUWV.js.map +7 -0
  23. package/dist/chunk-Q7BALEPX.js +527 -0
  24. package/dist/chunk-Q7BALEPX.js.map +7 -0
  25. package/dist/{chunk-2Z2ENHR3.js → chunk-QRBOIUU3.js} +7 -8
  26. package/dist/chunk-QRBOIUU3.js.map +7 -0
  27. package/dist/chunk-QXAOTGCC.js +40 -0
  28. package/dist/chunk-QXAOTGCC.js.map +7 -0
  29. package/dist/chunk-UKP22RUG.js +219 -0
  30. package/dist/chunk-UKP22RUG.js.map +7 -0
  31. package/dist/chunk-VLOAI3HS.js +43 -0
  32. package/dist/chunk-VLOAI3HS.js.map +7 -0
  33. package/dist/{chunk-WUFLGETN.js → chunk-WVFGV5FT.js} +2 -39
  34. package/dist/{chunk-WUFLGETN.js.map → chunk-WVFGV5FT.js.map} +3 -3
  35. package/dist/editor.js +3 -2
  36. package/dist/editor.js.map +1 -1
  37. package/dist/flexdesk.css +7547 -5891
  38. package/dist/flow.js +29 -97
  39. package/dist/flow.js.map +3 -3
  40. package/dist/git.js +1356 -0
  41. package/dist/git.js.map +7 -0
  42. package/dist/reset.css +1 -1
  43. package/dist/tiles.js +20 -15
  44. package/dist/tiles.js.map +2 -2
  45. package/dist/tokens.css +30 -1
  46. package/dist/widgets.js +4146 -1199
  47. package/dist/widgets.js.map +4 -4
  48. package/dist/wm.js +120 -32
  49. package/dist/wm.js.map +2 -2
  50. package/docs/README.md +9 -4
  51. package/docs/best-practices.md +2 -2
  52. package/docs/components/charts.md +40 -0
  53. package/docs/components/chat-shell.md +204 -0
  54. package/docs/components/conversation.md +10 -0
  55. package/docs/components/filter-builder.md +292 -0
  56. package/docs/components/forms-and-dialogs.md +30 -0
  57. package/docs/components/git.md +355 -0
  58. package/docs/components/input-fields.md +191 -2
  59. package/docs/components/keymap.md +8 -0
  60. package/docs/components/menus.md +86 -2
  61. package/docs/components/panels-and-desktops.md +16 -5
  62. package/docs/components/shell.md +108 -1
  63. package/docs/components/workspace-picker.md +193 -0
  64. package/docs/concepts/building-and-packaging.md +2 -2
  65. package/docs/concepts/packages-and-entry-points.md +14 -13
  66. package/docs/concepts/state-guard.md +3 -3
  67. package/docs/quickstart.md +1 -1
  68. package/git.js +13 -0
  69. package/package.json +6 -1
  70. package/src/charts/plot_config_editor.js +92 -6
  71. package/src/charts/plot_grid.js +6 -1
  72. package/src/flow/kit/dom.js +28 -119
  73. package/src/flow/kit/step_picker.js +1 -1
  74. package/src/flow/kit/value_picker.js +1 -1
  75. package/src/flow/outline/menu.js +1 -1
  76. package/src/git/diff_viewer.js +212 -0
  77. package/src/git/diff_window.js +51 -0
  78. package/src/git/git_graph.js +401 -0
  79. package/src/git/git_ops.js +61 -0
  80. package/src/git/git_panel.js +755 -0
  81. package/src/git/index.js +9 -0
  82. package/src/git/line_diff.js +202 -0
  83. package/src/tiles/tile_base.js +5 -4
  84. package/src/tiling/desktops.js +10 -8
  85. package/src/tiling/keymap.js +6 -5
  86. package/src/tiling/kind_taxonomy.js +15 -2
  87. package/src/tiling/shell.js +124 -17
  88. package/src/tiling/tile_renderer.js +2 -3
  89. package/src/ui/components/action_dropdown.js +4 -78
  90. package/src/ui/components/anchored_layer.js +255 -0
  91. package/src/ui/components/chat_shell.js +926 -0
  92. package/src/ui/components/chat_state_machine.js +223 -0
  93. package/src/ui/components/choice_controls.js +45 -2
  94. package/src/ui/components/color_field.js +3 -2
  95. package/src/ui/components/color_list_field.js +3 -2
  96. package/src/ui/components/combobox.js +422 -0
  97. package/src/ui/components/context_menu.js +8 -2
  98. package/src/ui/components/conversation_view.js +17 -2
  99. package/src/ui/components/data_table.js +2 -2
  100. package/src/ui/components/filter_builder.js +791 -0
  101. package/src/ui/components/filter_model.js +682 -0
  102. package/src/ui/components/inline_renamer.js +10 -5
  103. package/src/ui/components/managed_window.js +24 -47
  104. package/src/ui/components/modal.js +104 -117
  105. package/src/ui/components/option_list.js +160 -0
  106. package/src/ui/components/popover.js +94 -0
  107. package/src/ui/components/recent_items.js +169 -0
  108. package/src/ui/components/select.js +322 -0
  109. package/src/ui/components/tag_input.js +77 -8
  110. package/src/ui/components/workspace_picker.js +1253 -0
  111. package/src/ui/utils/fire_event.js +17 -0
  112. package/src/ui/utils/focusable.js +25 -0
  113. package/src/ui/utils/modal_host.js +27 -0
  114. package/src/ui/utils/tooltip_service.js +81 -14
  115. package/widgets.js +12 -2
  116. package/css/base.css +0 -10547
  117. package/css/overrides.css +0 -2596
  118. package/dist/chunk-2Z2ENHR3.js.map +0 -7
  119. package/dist/chunk-7JI7HHSY.js +0 -107
  120. package/dist/chunk-7JI7HHSY.js.map +0 -7
  121. package/dist/chunk-7OL6F2AM.js.map +0 -7
  122. package/dist/chunk-MIW7D6WC.js +0 -526
  123. package/dist/chunk-MIW7D6WC.js.map +0 -7
  124. package/dist/chunk-PGZ4EHSE.js.map +0 -7
  125. package/dist/chunk-XJNADRGR.js.map +0 -7
  126. /package/dist/{chunk-REJ5HLIM.js.map → chunk-HYJYYEPK.js.map} +0 -0
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Fire a DOM event on `target`, built by `target`'s own window.
3
+ *
4
+ * `dispatchEvent` takes only an event from the target's realm. A bare
5
+ * `new Event(...)` reads whatever global the running code sees, and that is a
6
+ * different realm from the element's in an iframe, and under jsdom on any Node
7
+ * with a global `Event` of its own: there it throws "parameter 1 is not of type
8
+ * 'Event'" in the middle of whatever fired it.
9
+ *
10
+ * @param {EventTarget & Node} target an element
11
+ * @param {string} type
12
+ * @param {CustomEventInit} [init]
13
+ */
14
+ export function fireEvent(target, type, init) {
15
+ const view = target.ownerDocument.defaultView;
16
+ target.dispatchEvent(new view.CustomEvent(type, init));
17
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Collect the focusable elements inside `root`, in DOM order: what a focus trap
3
+ * cycles through. Hidden and zero-size elements are skipped, since Tab visits only
4
+ * what the reader can see.
5
+ *
6
+ * @param {HTMLElement|null} root
7
+ * @returns {HTMLElement[]}
8
+ */
9
+ export function collectFocusable(root) {
10
+ if (!root) return [];
11
+ const sel = [
12
+ 'a[href]',
13
+ 'button:not([disabled])',
14
+ 'input:not([disabled]):not([type="hidden"])',
15
+ 'select:not([disabled])',
16
+ 'textarea:not([disabled])',
17
+ '[tabindex]:not([tabindex="-1"])',
18
+ ].join(',');
19
+ return Array.from(root.querySelectorAll(sel)).filter((el) => {
20
+ if (el.hidden) return false;
21
+ if (el.closest('[hidden]')) return false;
22
+ const r = el.getBoundingClientRect();
23
+ return r.width > 0 || r.height > 0;
24
+ });
25
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * C25. WHERE A DIALOG OR A FLOATING PANEL OPENS, when the application spans more
3
+ * than one window.
4
+ *
5
+ * Every modal is a `ManagedWindow` with no `container`, so it mounts into
6
+ * `document.body` — and "document" is the document the MODULE was loaded in. A
7
+ * consumer that opens a second browser window with `window.open` and builds its
8
+ * DOM from the opener's realm (which is how a pop-out grid works: same modules,
9
+ * same JS context, a different document) therefore gets its dialogs in the
10
+ * window it popped OUT of. The user presses "Add column" on their second monitor
11
+ * and a dialog appears on the first, behind whatever is there.
12
+ *
13
+ * Passing a container at each call site was the other option and it is worse:
14
+ * twenty-one of them across eleven files, every one of which would have to be
15
+ * given a document it has no other reason to know about, and any one missed is
16
+ * this bug again with no way to see it from here.
17
+ *
18
+ * So the HOST is ambient and the consumer sets it when its focus moves. Null —
19
+ * every consumer today — means `document.body`, exactly as before.
20
+ *
21
+ * A module of its own because modals and the anchored layers both ask it, and
22
+ * `openForm` draws its combobox on an anchored layer. `modal.js` re-exports both
23
+ * names: that is where they are public.
24
+ */
25
+ let host = null;
26
+ export function setModalHost(el) { host = el || null; }
27
+ export function modalHost() { return host; }
@@ -13,6 +13,10 @@ let currentTarget = null;
13
13
  let listenersAttached = false;
14
14
  let mouseCheckInterval = null;
15
15
  let lastMousePosition = { x: 0, y: 0 };
16
+ /** The target keyboard focus is showing a tooltip for: no pointer holds it up. */
17
+ let focusTarget = null;
18
+ /** The last press was a key. Focus that follows a pointer's, or a script's after one, shows nothing. */
19
+ let byKeyboard = false;
16
20
 
17
21
  /**
18
22
  * Create or get the tooltip DOM element.
@@ -140,14 +144,15 @@ function showTooltip(target) {
140
144
  const hasPlain = target.classList && target.classList.contains('twm-has-tooltip');
141
145
 
142
146
  if (!hasLatex && !hasPlain) return;
147
+ if (!target.getAttribute(hasLatex ? 'data-latex-tooltip' : 'data-tooltip')) return;
143
148
 
149
+ stopMouseCheck();
144
150
  currentTarget = target;
145
151
  const tooltip = createTooltipElement();
146
152
 
147
153
  if (hasLatex) {
148
154
  // Render LaTeX using KaTeX
149
155
  const latex = target.getAttribute('data-latex-tooltip');
150
- if (!latex) return;
151
156
 
152
157
  try {
153
158
  if (typeof katex !== 'undefined') {
@@ -166,7 +171,6 @@ function showTooltip(target) {
166
171
  } else if (hasPlain) {
167
172
  // Plain text tooltip with line breaks preserved
168
173
  const text = target.getAttribute('data-tooltip');
169
- if (!text) return;
170
174
 
171
175
  // Replace 
 with actual line breaks, preserve formatting
172
176
  const formattedText = text.replace(/
/g, '\n');
@@ -197,11 +201,13 @@ function showTooltip(target) {
197
201
 
198
202
  // Position and show
199
203
  requestAnimationFrame(() => {
204
+ // Gone in the frame's wait: focus passing through, or the pointer leaving.
205
+ if (currentTarget !== target) return;
200
206
  positionTooltip(target, tooltip);
201
207
  tooltip.classList.add('visible');
202
208
 
203
- // Start mouse position check for auto-hide
204
- startMouseCheck();
209
+ // Start mouse position check for auto-hide, unless focus is what holds it.
210
+ if (focusTarget !== target) startMouseCheck();
205
211
  });
206
212
  }
207
213
 
@@ -213,23 +219,51 @@ function hideTooltip() {
213
219
  tooltipElement.classList.remove('visible');
214
220
  }
215
221
  currentTarget = null;
222
+ focusTarget = null;
223
+ stopMouseCheck();
224
+ }
216
225
 
217
- // Clear mouse check interval
226
+ function stopMouseCheck() {
218
227
  if (mouseCheckInterval) {
219
228
  clearInterval(mouseCheckInterval);
220
229
  mouseCheckInterval = null;
221
230
  }
222
231
  }
223
232
 
233
+ /**
234
+ * The pointer has gone from what it showed, or a scroll took that away. What focus
235
+ * holds is not the pointer's to hide: it stays, or comes back from under the
236
+ * pointer's, as long as its target is in sight.
237
+ */
238
+ function pointerGone() {
239
+ const held = focusTarget;
240
+ if (held && held === currentTarget) return;
241
+ hideTooltip();
242
+ if (!held) return;
243
+ focusTarget = held;
244
+ showTooltip(held);
245
+ requestAnimationFrame(() => {
246
+ if (held === focusTarget && held === currentTarget) followFocus();
247
+ });
248
+ }
249
+
250
+ /**
251
+ * A scroll while focus holds the tooltip, the one the focus itself caused included:
252
+ * the tooltip goes with its target, and waits while the target is out of sight.
253
+ */
254
+ function followFocus() {
255
+ const { left, top, width, height } = focusTarget.getBoundingClientRect();
256
+ const inSight = focusTarget.contains(document.elementFromPoint(left + width / 2, top + height / 2));
257
+ tooltipElement.classList.toggle('visible', inSight);
258
+ if (inSight) positionTooltip(focusTarget, tooltipElement);
259
+ }
260
+
224
261
  /**
225
262
  * Start the mouse position check interval.
226
263
  * Hides tooltip if mouse is no longer over the target element.
227
264
  */
228
265
  function startMouseCheck() {
229
- // Clear any existing interval
230
- if (mouseCheckInterval) {
231
- clearInterval(mouseCheckInterval);
232
- }
266
+ stopMouseCheck();
233
267
 
234
268
  // Check every 3 seconds if mouse is still on the target element
235
269
  mouseCheckInterval = setInterval(() => {
@@ -249,11 +283,11 @@ function startMouseCheck() {
249
283
  elementUnderMouse.closest('.twm-has-tooltip, .has-latex-tooltip') === currentTarget;
250
284
 
251
285
  if (!isStillOnTarget) {
252
- hideTooltip();
286
+ pointerGone();
253
287
  }
254
288
  } else {
255
289
  // Mouse is outside the document
256
- hideTooltip();
290
+ pointerGone();
257
291
  }
258
292
  }, 3000);
259
293
  }
@@ -289,11 +323,44 @@ function attachListeners() {
289
323
  if (!(target instanceof Element)) return;
290
324
  const tooltipTarget = target.closest('.has-latex-tooltip, .twm-has-tooltip');
291
325
  if (!tooltipTarget) return;
292
- hideTooltip();
326
+ pointerGone();
327
+ }, true);
328
+
329
+ // On the window, ahead of a chord handler that stops the event. A chord is not
330
+ // a person walking the page by keyboard: it says nothing either way.
331
+ window.addEventListener('keydown', (event) => {
332
+ if (!event.ctrlKey && !event.metaKey && !event.altKey) byKeyboard = true;
333
+ }, true);
334
+ window.addEventListener('pointerdown', () => {
335
+ byKeyboard = false;
293
336
  }, true);
294
337
 
295
- // Hide on scroll
296
- document.addEventListener('scroll', hideTooltip, true);
338
+ // Keyboard focus shows what hovering shows. The focus a click leaves, or that
339
+ // its handler moves into a field, would keep a tooltip standing after the pointer
340
+ // has gone. `:focus-visible` cannot tell: it is true for any text field.
341
+ document.addEventListener('focusin', (event) => {
342
+ const target = event.target;
343
+ if (!(target instanceof Element) || !byKeyboard) return;
344
+ const tooltipTarget = target.closest('.has-latex-tooltip, .twm-has-tooltip');
345
+ if (!tooltipTarget) return;
346
+ focusTarget = tooltipTarget;
347
+ showTooltip(tooltipTarget);
348
+ }, true);
349
+
350
+ document.addEventListener('focusout', (event) => {
351
+ if (!focusTarget) return;
352
+ const next = event.relatedTarget;
353
+ if (next instanceof Node && focusTarget.contains(next)) return;
354
+ // A pointer's tooltip on another target is the pointer's to take down.
355
+ if (focusTarget === currentTarget) hideTooltip();
356
+ else focusTarget = null;
357
+ }, true);
358
+
359
+ // Hide on scroll, unless focus is what holds it.
360
+ document.addEventListener('scroll', () => {
361
+ if (focusTarget && focusTarget === currentTarget) followFocus();
362
+ else pointerGone();
363
+ }, true);
297
364
 
298
365
  // ESC key hides all tooltips application-wide (does not stop propagation)
299
366
  document.addEventListener('keydown', (event) => {
package/widgets.js CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * @flexdesk/widgets — The widget set: DataTable, modals, managed windows, toasts, trees,
3
- * context menus, menu bars, panels, section stacks, a step wizard, the help modal and the notification centre.
3
+ * context menus, menu bars, panels, section stacks, a rich select, a combobox, a popover, a step wizard, a workspace picker, a filter builder, an assistant chat shell, the help modal and the notification centre.
4
4
  *
5
5
  * GENERATED by scripts/gen_barrels.py. Do not hand-edit.
6
6
  *
7
- * 60 modules.
7
+ * 71 modules.
8
8
  */
9
9
 
10
10
  export * from './src/ui/base/component_base.js';
@@ -14,6 +14,7 @@ export * from './src/ui/components/about_dialog.js';
14
14
  export * from './src/ui/components/alert_log.js';
15
15
  export * from './src/ui/components/alerts_pane.js';
16
16
  export * from './src/ui/components/action_dropdown.js';
17
+ export * from './src/ui/components/anchored_layer.js';
17
18
  export * from './src/ui/components/attribute_list_editor.js';
18
19
  export * from './src/ui/components/autocomplete_field.js';
19
20
  export * from './src/ui/components/category_picker.js';
@@ -23,24 +24,32 @@ export * from './src/ui/components/cell_selection.js';
23
24
  export * from './src/ui/components/color_field.js';
24
25
  export * from './src/ui/components/color_list_field.js';
25
26
  export * from './src/ui/components/column_fit.js';
27
+ export * from './src/ui/components/combobox.js';
26
28
  export * from './src/ui/components/computing_status_window.js';
27
29
  export * from './src/ui/components/confirm_dialog.js';
28
30
  export * from './src/ui/components/context_menu.js';
31
+ export * from './src/ui/components/chat_shell.js';
32
+ export * from './src/ui/components/chat_state_machine.js';
29
33
  export * from './src/ui/components/conversation_view.js';
30
34
  export * from './src/ui/components/data_table.js';
31
35
  export * from './src/ui/components/detail_header.js';
32
36
  export * from './src/ui/components/document_outline.js';
33
37
  export * from './src/ui/components/drag_reorder.js';
34
38
  export * from './src/ui/components/fact_table.js';
39
+ export * from './src/ui/components/filter_builder.js';
40
+ export * from './src/ui/components/filter_model.js';
35
41
  export * from './src/ui/components/gallery_picker.js';
36
42
  export * from './src/ui/components/inline_renamer.js';
37
43
  export * from './src/ui/components/managed_window.js';
38
44
  export * from './src/ui/components/menu_bar.js';
39
45
  export * from './src/ui/components/modal.js';
40
46
  export * from './src/ui/components/notification_history.js';
47
+ export * from './src/ui/components/popover.js';
41
48
  export * from './src/ui/components/progress.js';
49
+ export * from './src/ui/components/recent_items.js';
42
50
  export * from './src/ui/components/record_list.js';
43
51
  export * from './src/ui/components/section_stack.js';
52
+ export * from './src/ui/components/select.js';
44
53
  export * from './src/ui/components/series.js';
45
54
  export * from './src/ui/components/settings_view.js';
46
55
  export * from './src/ui/components/slide_out_panel.js';
@@ -56,6 +65,7 @@ export * from './src/ui/components/tree_view.js';
56
65
  export * from './src/ui/components/tsv.js';
57
66
  export * from './src/ui/components/window_taskbar.js';
58
67
  export * from './src/ui/components/wizard.js';
68
+ export * from './src/ui/components/workspace_picker.js';
59
69
  export * from './src/ui/controllers/window_chrome_controller.js';
60
70
  export * from './src/ui/notification_center.js';
61
71
  export * from './src/ui/utils/auto_scrollbars.js';