flexdesk 0.17.13 → 0.17.14

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 (243) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +0 -0
  3. package/README.md +0 -0
  4. package/THIRD_PARTY_LICENSES.md +0 -0
  5. package/canvas.js +0 -0
  6. package/charts.js +0 -0
  7. package/core.js +0 -0
  8. package/css/base.css +45 -3
  9. package/css/flexdesk.css +60 -3
  10. package/css/overrides.css +15 -0
  11. package/css/reset.css +0 -0
  12. package/dist/canvas.js +0 -0
  13. package/dist/canvas.js.map +0 -0
  14. package/dist/chunk-3PHCPZHT.js +0 -0
  15. package/dist/chunk-3PHCPZHT.js.map +0 -0
  16. package/dist/chunk-FOOS3T5L.js +0 -0
  17. package/dist/chunk-FOOS3T5L.js.map +0 -0
  18. package/dist/chunk-GOQLR5WR.js +0 -0
  19. package/dist/chunk-GOQLR5WR.js.map +0 -0
  20. package/dist/chunk-HNU6MM2B.js +0 -0
  21. package/dist/chunk-HNU6MM2B.js.map +0 -0
  22. package/dist/chunk-JKZLNKG6.js +0 -0
  23. package/dist/chunk-JKZLNKG6.js.map +0 -0
  24. package/dist/chunk-JYWURG5T.js +0 -0
  25. package/dist/chunk-JYWURG5T.js.map +0 -0
  26. package/dist/chunk-N7HC3ADV.js +0 -0
  27. package/dist/chunk-N7HC3ADV.js.map +0 -0
  28. package/dist/core.js +3 -18
  29. package/dist/core.js.map +2 -2
  30. package/dist/editor.js.map +0 -0
  31. package/dist/flexdesk.css +60 -3
  32. package/dist/flow.js.map +0 -0
  33. package/dist/host.js +0 -0
  34. package/dist/host.js.map +0 -0
  35. package/dist/reset.css +0 -0
  36. package/dist/tile_registry-6WZPTUZV.js +0 -0
  37. package/dist/tile_registry-6WZPTUZV.js.map +0 -0
  38. package/dist/widgets.js +27 -18
  39. package/dist/widgets.js.map +2 -2
  40. package/dist/wm.js +195 -28
  41. package/dist/wm.js.map +3 -3
  42. package/docs/README.md +1 -1
  43. package/docs/best-practices.md +1 -1
  44. package/docs/components/command-palette.md +0 -0
  45. package/docs/components/conversation.md +0 -0
  46. package/docs/components/data-table-grid.md +0 -0
  47. package/docs/components/data-table.md +0 -0
  48. package/docs/components/editor.md +0 -0
  49. package/docs/components/flow-canvas-editor.md +0 -0
  50. package/docs/components/flow-editors.md +0 -0
  51. package/docs/components/flow-lane-editor.md +0 -0
  52. package/docs/components/flow-outline-editor.md +0 -0
  53. package/docs/components/forms-and-dialogs.md +0 -0
  54. package/docs/components/input-fields.md +0 -0
  55. package/docs/components/keymap.md +66 -3
  56. package/docs/components/layout-widgets.md +32 -0
  57. package/docs/components/markdown-and-outline.md +0 -0
  58. package/docs/components/menus.md +9 -5
  59. package/docs/components/notifications.md +0 -0
  60. package/docs/components/panels-and-desktops.md +56 -0
  61. package/docs/components/record-list-and-fact-table.md +0 -0
  62. package/docs/components/settings-view.md +0 -0
  63. package/docs/components/shell.md +0 -0
  64. package/docs/components/slide-out-panel.md +0 -0
  65. package/docs/components/wizard.md +0 -0
  66. package/docs/concepts/building-and-packaging.md +0 -0
  67. package/docs/concepts/domain-neutrality.md +0 -0
  68. package/docs/concepts/host-port.md +0 -0
  69. package/docs/concepts/packages-and-entry-points.md +0 -0
  70. package/docs/concepts/peer-dependencies.md +0 -0
  71. package/docs/concepts/state-guard.md +65 -2
  72. package/docs/concepts/styling.md +24 -6
  73. package/docs/quickstart.md +0 -0
  74. package/editor.js +0 -0
  75. package/flow.js +0 -0
  76. package/host.js +0 -0
  77. package/package.json +2 -2
  78. package/src/canvas/canvas_adapter.js +0 -0
  79. package/src/canvas/edge_router.js +0 -0
  80. package/src/canvas/graph/connector_router.js +0 -0
  81. package/src/canvas/graph/history_service.js +0 -0
  82. package/src/canvas/graph/node_platform.js +0 -0
  83. package/src/canvas/index.js +0 -0
  84. package/src/canvas/layered_layout.js +0 -0
  85. package/src/charts/data_series_plot_window.js +0 -0
  86. package/src/charts/downsample.js +0 -0
  87. package/src/charts/plot_config.js +0 -0
  88. package/src/charts/plotly_wrapper.js +0 -0
  89. package/src/core/event_bus.js +0 -0
  90. package/src/core/logging.js +0 -0
  91. package/src/core/settings.js +0 -0
  92. package/src/core/state_guard.js +0 -0
  93. package/src/core/state_guard_config.js +19 -18
  94. package/src/core/state_machine.js +0 -0
  95. package/src/editor/editor_pane.js +0 -0
  96. package/src/editor/monaco_editor_factory.js +0 -0
  97. package/src/editor/monaco_loader.js +0 -0
  98. package/src/editor/notebook_search.js +0 -0
  99. package/src/editor/notebook_undo_manager.js +0 -0
  100. package/src/editor/split_pane_container.js +0 -0
  101. package/src/flow/canvas_editor/connections.js +0 -0
  102. package/src/flow/canvas_editor/editor.js +0 -0
  103. package/src/flow/canvas_editor/index.js +0 -0
  104. package/src/flow/canvas_editor/layout.js +0 -0
  105. package/src/flow/canvas_editor/strings.js +0 -0
  106. package/src/flow/kit/always_before.js +0 -0
  107. package/src/flow/kit/catalogue.js +0 -0
  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 +0 -0
  113. package/src/flow/kit/keys.js +0 -0
  114. package/src/flow/kit/logic_graph.js +0 -0
  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 +0 -0
  118. package/src/flow/kit/step_picker.js +0 -0
  119. package/src/flow/kit/strings.js +0 -0
  120. package/src/flow/kit/value_picker.js +0 -0
  121. package/src/flow/kit/widgets.js +0 -0
  122. package/src/flow/lanes/editor.js +0 -0
  123. package/src/flow/lanes/index.js +0 -0
  124. package/src/flow/lanes/layout.js +0 -0
  125. package/src/flow/lanes/pipeline.js +0 -0
  126. package/src/flow/lanes/preview.js +0 -0
  127. package/src/flow/lanes/strings.js +0 -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 +0 -0
  136. package/src/help/help_registry.js +0 -0
  137. package/src/host/external_links.js +0 -0
  138. package/src/host/file_picker.js +0 -0
  139. package/src/host/host.js +0 -0
  140. package/src/host/photino_host.js +0 -0
  141. package/src/host/photino_host.md +0 -0
  142. package/src/host/pywebview_host.js +0 -0
  143. package/src/tiles/chart_tiles.js +0 -0
  144. package/src/tiles/config_schema.js +0 -0
  145. package/src/tiles/figure_tile.js +0 -0
  146. package/src/tiles/layout_persistence.js +0 -0
  147. package/src/tiles/list_tiles.js +0 -0
  148. package/src/tiles/scope_picker.js +0 -0
  149. package/src/tiles/tile_config_form.js +0 -0
  150. package/src/tiles/tile_grid.js +0 -0
  151. package/src/tiles/tile_registry.js +0 -0
  152. package/src/tiling/bottom_panel_tabs.js +0 -0
  153. package/src/tiling/command_palette.js +0 -0
  154. package/src/tiling/content_registry.js +0 -0
  155. package/src/tiling/desktops.js +79 -16
  156. package/src/tiling/entity_sources.js +0 -0
  157. package/src/tiling/keymap.js +70 -2
  158. package/src/tiling/kind_taxonomy.js +0 -0
  159. package/src/tiling/landing_table.js +0 -0
  160. package/src/tiling/loading_overlay.js +0 -0
  161. package/src/tiling/nav_panel.js +0 -0
  162. package/src/tiling/page_factory.js +0 -0
  163. package/src/tiling/panel_availability.js +0 -0
  164. package/src/tiling/panel_keys.js +0 -0
  165. package/src/tiling/shell.js +9 -2
  166. package/src/tiling/tab_strip.js +0 -0
  167. package/src/tiling/tile_breadcrumb.js +0 -0
  168. package/src/tiling/tile_renderer.js +0 -0
  169. package/src/tiling/tile_tab_menu.js +0 -0
  170. package/src/tiling/tile_tree.js +63 -5
  171. package/src/tiling/wm.js +59 -6
  172. package/src/tiling/zoom.js +0 -0
  173. package/src/ui/base/component_base.js +0 -0
  174. package/src/ui/base/controller_base.js +0 -0
  175. package/src/ui/base/page_base.js +0 -0
  176. package/src/ui/components/about_dialog.js +0 -0
  177. package/src/ui/components/alert_log.js +0 -0
  178. package/src/ui/components/alerts_pane.js +0 -0
  179. package/src/ui/components/attribute_list_editor.js +0 -0
  180. package/src/ui/components/autocomplete_field.js +0 -0
  181. package/src/ui/components/cell_editor_host.js +0 -0
  182. package/src/ui/components/cell_editors.js +0 -0
  183. package/src/ui/components/cell_grid.js +0 -0
  184. package/src/ui/components/cell_paste.js +0 -0
  185. package/src/ui/components/cell_selection.js +0 -0
  186. package/src/ui/components/choice_controls.js +0 -0
  187. package/src/ui/components/color_field.js +0 -0
  188. package/src/ui/components/color_list_field.js +0 -0
  189. package/src/ui/components/column_fit.js +0 -0
  190. package/src/ui/components/column_groups.js +0 -0
  191. package/src/ui/components/column_order.js +0 -0
  192. package/src/ui/components/computing_status_window.js +0 -0
  193. package/src/ui/components/confirm_dialog.js +0 -0
  194. package/src/ui/components/conversation_view.js +0 -0
  195. package/src/ui/components/crosstab.js +0 -0
  196. package/src/ui/components/detail_header.js +0 -0
  197. package/src/ui/components/document_outline.js +0 -0
  198. package/src/ui/components/drag_reorder.js +0 -0
  199. package/src/ui/components/fact_table.js +0 -0
  200. package/src/ui/components/fill_handle.js +0 -0
  201. package/src/ui/components/gallery_picker.js +0 -0
  202. package/src/ui/components/header_actions.js +0 -0
  203. package/src/ui/components/inline_renamer.js +0 -0
  204. package/src/ui/components/job_progress.js +0 -0
  205. package/src/ui/components/menu_bar.js +0 -0
  206. package/src/ui/components/modal.js +0 -0
  207. package/src/ui/components/notification_history.js +0 -0
  208. package/src/ui/components/progress.js +0 -0
  209. package/src/ui/components/record_list.js +0 -0
  210. package/src/ui/components/row_activation.js +0 -0
  211. package/src/ui/components/section_stack.js +0 -0
  212. package/src/ui/components/series.js +0 -0
  213. package/src/ui/components/settings_view.js +0 -0
  214. package/src/ui/components/slide_out_panel.js +0 -0
  215. package/src/ui/components/slider_field.js +0 -0
  216. package/src/ui/components/sortable_list.js +0 -0
  217. package/src/ui/components/staged_edits.js +0 -0
  218. package/src/ui/components/staged_history.js +0 -0
  219. package/src/ui/components/table_filter_chips.js +0 -0
  220. package/src/ui/components/table_state_store.js +0 -0
  221. package/src/ui/components/table_structure.js +0 -0
  222. package/src/ui/components/table_values.js +0 -0
  223. package/src/ui/components/tabs.js +0 -0
  224. package/src/ui/components/tag_input.js +0 -0
  225. package/src/ui/components/toast.js +0 -0
  226. package/src/ui/components/toggle_field.js +0 -0
  227. package/src/ui/components/tree_view.js +41 -18
  228. package/src/ui/components/tsv.js +0 -0
  229. package/src/ui/components/window_taskbar.js +0 -0
  230. package/src/ui/components/wizard.js +0 -0
  231. package/src/ui/controllers/window_chrome_controller.js +0 -0
  232. package/src/ui/notification_center.js +0 -0
  233. package/src/ui/utils/auto_scrollbars.js +0 -0
  234. package/src/ui/utils/markdown.js +0 -0
  235. package/src/ui/utils/native_context_menu.js +0 -0
  236. package/src/ui/utils/overlay_scrollbar.js +0 -0
  237. package/src/ui/utils/raf_resize_observer.js +0 -0
  238. package/src/ui/utils/scroll_lock.js +0 -0
  239. package/src/ui/utils/strip_scroll.js +0 -0
  240. package/src/ui/utils/tooltip_service.js +0 -0
  241. package/tiles.js +0 -0
  242. package/widgets.js +0 -0
  243. package/wm.js +0 -0
package/docs/README.md CHANGED
@@ -29,7 +29,7 @@ FlexDesk is nine entry points that share their code, so you load only what you u
29
29
  `@flexdesk/wm`
30
30
 
31
31
  - [Shell and navigation](components/shell.md): `createShell`, kinds, navigating between them, what a pane tells its chrome.
32
- - [Panels and desktops](components/panels-and-desktops.md): which panels a page offers, panel sizes, `mountNavPanel`, the desktop bar.
32
+ - [Panels and desktops](components/panels-and-desktops.md): which panels a page offers, panel sizes, `mountNavPanel`, the desktop bar, a saved layout that cannot be read.
33
33
  - [Command palette](components/command-palette.md): grouped results, drill-down, disabled rows, prefix-only sources.
34
34
  - [Keys, Back and tab layout](components/keymap.md): rebinding the window manager's keys, Back and the breadcrumb, zoom, tab layout.
35
35
 
@@ -36,7 +36,7 @@ The rules the reference pages state, gathered in one place. Each item says what
36
36
 
37
37
  **14. Restyle through the tokens.** Every rule reads its values from `tokens.css`, so swapping that one file restyles the shell. Library classes are `twm-` prefixed, and that prefix is stable; the editor components and the help modal keep the class names they were extracted with. Supply the icon font, or accept that icons show their text names. See [theming and CSS](concepts/styling.md#styling).
38
38
 
39
- **15. Run the state guard strictly in your own code.** `StateGuardService` defaults to `mode: 'warn'`. FlexDesk's own widgets run clean under `'strict'`, so choose strict for your code and add your application's keys on top of `DEFAULT_STATE_GUARD_CONFIG`. See [state guard](concepts/state-guard.md#the-state-guard-who-wrote-that-attribute-0170) and [the default allow-list](concepts/state-guard.md#the-defaults-cover-flexdesks-own-keys-0170).
39
+ **15. Run the state guard strictly in your own code.** `StateGuardService` defaults to `mode: 'warn'`. FlexDesk's own widgets run clean under `'strict'`, so choose strict for your code and add your application's keys on top of `DEFAULT_STATE_GUARD_CONFIG`: a key you write that no FlexDesk widget does is yours to list. The defaults also still allow some keys that no widget writes, which a strict guard lets through unless you filter them out ([what 0.18.0 removed and what stayed](concepts/state-guard.md#thirteen-application-keys-leave-the-defaults-0180)). See [state guard](concepts/state-guard.md#the-state-guard-who-wrote-that-attribute-0170) and [the default allow-list](concepts/state-guard.md#the-defaults-cover-flexdesks-own-keys-0170).
40
40
 
41
41
  **16. Dispose what you create.** `shell.dispose()` removes the `?` shortcut and the desktop bar along with the rest of the shell. A `NotificationCenter`'s `dispose()` hands the toast helpers back. Menu bars, tables and lists have `dispose()` too.
42
42
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -4,6 +4,67 @@
4
4
 
5
5
  The window manager binds a set of keyboard chords: F1 to F12, Backspace, Ctrl+K, Ctrl+Tab, Ctrl/Shift+Arrows, the Alt chords, `?` and Esc. `keymap.bindings` takes any of them back or rebinds it. The same page covers what depends on those keys and on how a user moves around: what Back and the breadcrumb do, the content zoom control (`chrome.zoom`), and the shell options `backToOpenList`, `floatActiveTab` and `tabLayout`.
6
6
 
7
+ ## The digit chords follow the key's position, not its character (0.17.14)
8
+
9
+ Alt+1..9 switch to desktop N and Alt+Shift+1..9 move the focused tile to desktop
10
+ N. The keymap now names the digit row by the key's position, `event.code`
11
+ (`Digit1`..`Digit9`), which neither Shift nor the keyboard layout changes, and
12
+ no longer by the character the key types, `event.key`. Through 0.17.13 it read the
13
+ character, so a real Alt+Shift+2 never matched: Shift turns the 2 into `@` on a
14
+ US keyboard and `"` on a German one. Moving a tile worked from the tile's
15
+ context menu but not from the keyboard, while a synthetic event with
16
+ `key: '2'` did match, which hid it. The chord ids are unchanged, so `bindings`
17
+ spelled `Alt+Shift+2` takes back or rebinds the chord whatever the layout.
18
+
19
+ On every keyboard below, Alt with the key at position 2 now switches to desktop
20
+ 2 and Alt+Shift with it moves the tile there (a Mac in a text field is the one
21
+ exception, below):
22
+
23
+ | Keyboard | That key types | Through 0.17.13 |
24
+ |---|---|---|
25
+ | US | `2`, with Shift `@` | Alt+2 switched; Alt+Shift+2 did nothing |
26
+ | German | `2`, with Shift `"` | the same |
27
+ | French AZERTY | `é`, with Shift `2` | Alt+2 did nothing; Alt+Shift+2 moved the tile |
28
+ | Mac, Option held | `™`, with Shift `€` | neither did anything, outside a text field |
29
+
30
+ Alt+Shift+N for the desktop you are already on does nothing, and a panel (the
31
+ Navigator, say) cannot be moved to another desktop. The tile menu never offered
32
+ either, and `wm.moveFocusedToDesktop` refuses both itself, so the keyboard
33
+ cannot do what the menu will not.
34
+
35
+ Four kinds of press are still read by the character, `event.key`, as before:
36
+
37
+ - **An event with no position.** A synthetic `KeyboardEvent` that sets `key` and
38
+ no `code` is read by `key`, so a test or a script that dispatches
39
+ `{ key: '2', altKey: true }` keeps working. So does the numpad, whose `code` is
40
+ `Numpad2`: with NumLock on its `key` is the digit and it is a digit chord, and
41
+ with NumLock off its `key` is `ArrowDown` and it is the arrow chord.
42
+ - **AltGr.** Windows reports AltGr as Ctrl+Alt, and a German, French or Spanish
43
+ layout types `{ [ ] @ # |` on its digit row with it. Those presses are not
44
+ digit chords: nothing switches, and the character is typed, in a field too.
45
+ - **Option on a Mac, in a text field.** Option changes the character on every
46
+ Mac layout, and a programmer types on the digit row with it: on a German
47
+ keyboard Option+5, 6, 7, 8 and 9 type `[ ] | { }` and Option+Shift+7 types
48
+ `\`, on a Spanish or Swedish one Option+2 types `@`, on a US one `™`. Such a
49
+ press is an Alt press with no Ctrl and no AltGr state, so nothing in the
50
+ event tells it from Alt+2 on Windows, and the keymap goes by the platform
51
+ instead (`navigator.userAgentData.platform`, or `navigator.platform` where
52
+ the browser has no such property, naming a Mac, an iPhone or an iPad). With
53
+ focus in an input, a textarea, a select or a `contenteditable="true"`
54
+ element that press is typing: nothing switches and the character is typed.
55
+ With focus anywhere else nothing is typed, so it is a chord by position like
56
+ every other press, and Option+2 switches to desktop 2 on any Mac keyboard. To
57
+ switch desktops by key while typing on a Mac, leave the field first. Windows
58
+ and Linux have no such limit: Alt types no character there, so the digit
59
+ chords work in a field.
60
+ - **Letters.** Alt+H, V, T, N, W and F, and the Alt+letter swallowing, are still
61
+ named by the character. Letter keys move between layouts (the key that types
62
+ `w` on AZERTY sits where QWERTY has `z`), so a position would bind the wrong
63
+ key there. Option+letter on a Mac types a symbol (Option+H is `˙`), which is
64
+ not a letter, so those chords do not fire on a Mac.
65
+
66
+ `tests/keymap_digit_row.test.mjs` pins it.
67
+
7
68
  ## Your keys, not the window manager's: `keymap` (0.17.0)
8
69
 
9
70
  The window manager binds F1..F12, Backspace, Ctrl+K, Ctrl+Tab, Ctrl/Shift+Arrows,
@@ -42,9 +103,11 @@ throw a `TypeError`, so a binding you meant to change never stays in force
42
103
  unnoticed.
43
104
 
44
105
  **A key press is read as loosely as before.** With no bindings every key does
45
- what it did in 0.16, and a binding takes over every key press its chord was
46
- read as. Backspace ignores Shift and Alt, so `{ Backspace: false }` also hands
47
- Shift+Backspace and Alt+Backspace to the page. Alt+T/N/W/F ignore Shift. With
106
+ what it did in 0.16, apart from the digit row, which is read by position (see
107
+ [the digit chords](#the-digit-chords-follow-the-keys-position-not-its-character-01714)),
108
+ and a binding takes over every key press its chord was read as. Backspace
109
+ ignores Shift and Alt, so `{ Backspace: false }` also hands Shift+Backspace and
110
+ Alt+Backspace to the page. Alt+T/N/W/F ignore Shift. With
48
111
  Alt held, Ctrl and Meta are ignored, so Alt+Ctrl+H is `Alt+H`. Ctrl+K and
49
112
  Ctrl+Tab also match with Meta held, and Ctrl wins when both are held:
50
113
  Ctrl+Meta+K is `Ctrl+K`, and Meta+K is `Meta+K` only without Ctrl. Esc and `?`
@@ -199,3 +199,35 @@ runs on the bottom panel built over it. The bottom panel behaves as it did:
199
199
  re-shows its pane, fires `onSelect` and is remembered, as `select` does. A
200
200
  remembered tab that is already in front when `remember.read` answers is shown
201
201
  again: its pane hears `onShow` and `onSelect` fires, and nothing is written back.
202
+
203
+ ## Tree items with row actions (0.17.14)
204
+
205
+ `createTreeItem` takes `actions`, in the shape `createTreeNode` takes them:
206
+
207
+ ```js
208
+ const item = createTreeItem({
209
+ id: path, label, icon: 'description', badge: { text: '65' },
210
+ actions: {
211
+ rename: { icon: 'edit', title: 'Rename', onClick: (id) => startRename(id) },
212
+ delete: { icon: 'delete', title: 'Delete', onClick: (id) => confirmDelete(id) },
213
+ },
214
+ });
215
+ ```
216
+
217
+ - Each entry is a `.twm-tree-node__action-btn` (`type="button"`,
218
+ `data-action`, its `title`, a Material Symbols icon) in a
219
+ `.twm-tree-item__actions` slot, the row's last child, after the label, the
220
+ subtitle and the badge.
221
+ - A click on an entry with `onClick` calls `onClick(id, event)` and stops
222
+ there, so it does not select the item; a double-click on it does not reach
223
+ TreeView's `onItemDoubleClick` either.
224
+ - The slot is one line at the row's end, shown on hover, on focus within the
225
+ row and on the selected item. Elsewhere it is transparent and takes no
226
+ pointer, but its buttons stay in the tab order, so Tab reaches them and
227
+ shows them. It takes no height of its own (`height: 0`, the buttons centred
228
+ on the label line), so a row is as tall with actions as without.
229
+ - `createTreeNode`'s action buttons, built by the same code, are now
230
+ `type="button"` too: inside a form they no longer submit it.
231
+ - Without `actions` the item's markup is what it was. Until 0.17.13 an item
232
+ had no actions, and an app that added its own slot got no styles for it
233
+ (STORY-0121).
File without changes
@@ -133,11 +133,15 @@ const shell = await createShell({
133
133
  - **The host gives up the row's height.** `body.twm-body` is a flex column,
134
134
  and the row is a fixed-size item in it (`flex: 0 0 auto`). The host still
135
135
  takes whatever is left, so it is shorter by the row's height and nothing is
136
- drawn under the row. The row's height is the bar's (24px) plus its padding.
137
- - **It is styled with FlexDesk tokens.** It is a flex row with
138
- `padding: var(--space-xxs) var(--space-xs)`, background `var(--surface-1)`,
139
- text `var(--text-body)` and a bottom border `var(--surface-0)`, the same
140
- border as the top bar. The `.twm-menubar` inside it keeps its own styles.
136
+ drawn under the row. The row's height is the bar's (24px) plus its padding
137
+ and its two 1px borders.
138
+ - **It is a band of its own, on FlexDesk tokens (0.17.14).** It is a flex row
139
+ with `padding: var(--space-xxs) var(--space-xs)`, background
140
+ `var(--surface-0)` (darker than the top bar), text `var(--text-body)` and a
141
+ `1px solid var(--border-strong)` border above and below, so it is set off
142
+ from the top bar and from the tiles under it. Until 0.17.13 it had
143
+ `var(--surface-1)` and only a `var(--surface-0)` bottom border, and barely
144
+ stood out (STORY-0122). The `.twm-menubar` inside it keeps its own styles.
141
145
  - **It is not part of the top bar.** It is a sibling of `.twm-global-top-bar`,
142
146
  not inside it. A window-drag region bound to the top bar
143
147
  (`WindowChromeController`'s `topBar`, which defaults to
File without changes
@@ -10,6 +10,7 @@ The shell's left, right and bottom panels, and its virtual desktops. A kind says
10
10
  - [Panels keep the size you dragged them to (0.17.0)](#panels-keep-the-size-you-dragged-them-to-0170)
11
11
  - [A navigation panel you drive yourself: `mountNavPanel` (0.17.0)](#a-navigation-panel-you-drive-yourself-mountnavpanel-0170)
12
12
  - [Your desktops beside the app's (0.10.0)](#your-desktops-beside-the-apps-0100)
13
+ - [A saved layout that cannot be read (0.17.14)](#a-saved-layout-that-cannot-be-read-01714)
13
14
 
14
15
  ## Panels a page does not offer (0.17.0)
15
16
 
@@ -125,3 +126,58 @@ screen. With neither set anywhere, the bar is what it always was.
125
126
  main pane; it used to replace that pane with the active tab alone. A target still
126
127
  holding only its seed is replaced, as before. `dispose()` removes the bar, so a
127
128
  rebuilt shell paints a fresh one.
129
+
130
+ ## A saved layout that cannot be read (0.17.14)
131
+
132
+ The shell saves its layout through the host's `state` under the key `desktops`
133
+ and reads it back when it boots. That file can be truncated or edited by hand.
134
+ Text that is not JSON was always handled: the host logs `[host] bad JSON for
135
+ state key desktops` and answers `null`. A layout that is valid JSON but not a
136
+ layout (a split whose `sizes` is a number, a `null` desktop, a split with no
137
+ `children`) used to make `createShell` throw, on every start, until the file was
138
+ deleted by hand, so an app had to vet the file before booting. Others were let
139
+ through half understood: a desktop with no `tree` came back as an empty one.
140
+
141
+ Now such a layout is ignored. `createShell` resolves, the shell opens its
142
+ default layout exactly as it does when nothing was saved, and one warning names
143
+ the state key and the first thing wrong. The warning reads:
144
+
145
+ ```
146
+ [wm] bad layout for state key desktops (opening the default layout): desktops[0].tree.root.children[1].sizes: expected 2 non-negative numbers, one per child, found 5
147
+ ```
148
+
149
+ The warning goes to the `logger` you pass to `createShell`, else to
150
+ `console.warn`, once per start. It is a single string, like the keymap's, so it
151
+ arrives whole at any logger: a `LoggingService` (or one of its `scoped()`
152
+ loggers) records that line as the message, with the key and the cause in it.
153
+
154
+ - **All or nothing.** One bad desktop sets the whole layout aside; the shell does
155
+ not guess which parts to keep. Nothing of the bad layout is drawn, not even
156
+ the tiles before the damage.
157
+ - **The file is left where it is.** Deleting it would destroy the only copy of
158
+ what the user was trying to restore. The next time the shell saves the layout
159
+ (any change to it) that save replaces the file, and until then every start
160
+ warns again.
161
+ - **What is unreadable** is a shape `serialize()` could not have written:
162
+ `desktops` not a non-empty array; a desktop that is not an object, or has no
163
+ `tree` (a `tree` of `null` is a desktop with nothing on it, and is fine); a
164
+ `panels` or `panelShares` that is not an object; a node that is not a `leaf`
165
+ or a `split`; a split without a `dir` of `h` or `v`, without children, or
166
+ whose `sizes` are not one non-negative number per child; a tab without a
167
+ string `kind`; and a leaf's `tabs`, `pageTabs` or `activeTabIdx` of the wrong
168
+ type.
169
+ - **What still loads.** Values that are only drawn or coerced are taken as they
170
+ are: a title (a content factory can return a number), a desktop's `label`,
171
+ `group` and `fixed`, a panel's flag, a tab's props. A layout saved by an
172
+ earlier release loads too: the keys that came later (`panelShares`, `group`,
173
+ `fixed`, a leaf's `pageTabs`) may be missing, and so may a desktop's `id`,
174
+ `label` and `panels`, and a leaf's `tabs`. Keys it does not know are ignored, a
175
+ tab of a kind with no factory registered is the usual placeholder tile, and an
176
+ `activeIdx` past the last desktop is clamped.
177
+
178
+ `DESKTOPS_KEY` (`'desktops'`) names the key, and `DesktopManager.deserialize`
179
+ throws the `TypeError` above for a blob that cannot be read, which is what
180
+ `wm.load()` catches; `wm.load()` itself does not throw for one.
181
+
182
+ `tests/saved_layout_unreadable.test.mjs` boots a real shell over each of 78
183
+ shapes of broken layout and over the readable ones.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -38,8 +38,10 @@ global; choose strict mode in your own code.
38
38
 
39
39
  `DEFAULT_STATE_GUARD_CONFIG` includes every data key FlexDesk's own widgets
40
40
  write, so the shell, dialogs, forms, tooltips, tiles and flow editors run under
41
- `mode: 'strict'` with no violation. The list also carries some keys that
42
- FlexDesk itself does not write. Add your application's keys on top:
41
+ `mode: 'strict'` with no violation. The list also carries keys that FlexDesk
42
+ itself does not write, and it has not been audited key by key
43
+ ([what 0.18.0 took out and what stayed](#thirteen-application-keys-leave-the-defaults-0180)).
44
+ A key that names your application's concepts is yours to add, on top:
43
45
 
44
46
  ```js
45
47
  const guard = new StateGuardService({ eventBus, logger, config: {
@@ -56,3 +58,64 @@ capital letter becomes `-` plus its lower-case form. So `rowID` is `row-i-d`
56
58
  (what `dataset.rowID` writes), not `rowid`; to allow `data-id`, list `id`.
57
59
  Before 0.17.0 a camelCase entry never matched, and some entries named keys no
58
60
  element carries.
61
+
62
+ ### Thirteen application keys leave the defaults (0.18.0)
63
+
64
+ **Breaking for an application that writes one of these keys under a strict
65
+ guard.** `DEFAULT_STATE_GUARD_CONFIG.allowedDatasetKeys` no longer lists
66
+ thirteen keys that belonged to one embedding application, not to FlexDesk. No
67
+ FlexDesk widget writes any of them, so nothing FlexDesk draws changes; what
68
+ changes is that a write of one is now on no allow-list. Before 0.18.0 the
69
+ defaults let it through silently, for every application that used them.
70
+
71
+ | Group, as the list labelled it | Keys removed |
72
+ |---|---|
73
+ | Simulation/scenario state | `scenario-id`, `run-id`, `run-state` |
74
+ | Scenario editor | `mirror-input`, `mirror-input-bound`, `mirror-change-bound`, `override-type` |
75
+ | Calibration page | `info-for` |
76
+ | DSL generator | `dsl-state`, `dsl-section-registered`, `dsl-scrollbars-installed`, `dsl-tabs-bound`, `dsl-controls-bound` |
77
+
78
+ Those thirteen are the whole change. Every key FlexDesk writes is still
79
+ allowed, and so is every other key those groups held: the generic words
80
+ (`status`, `selected`, `active`, `disabled`, `expanded`, `collapsed`,
81
+ `visible`, `hidden`, `source`, `bound`, `field`, `key`, `column`) and the rest.
82
+
83
+ **What stays although FlexDesk does not write it.** The defaults are meant to
84
+ include every key a FlexDesk widget writes, and are not limited to them: many
85
+ listed keys are written by no widget, and the list has not been audited key by
86
+ key. Some look like one application's (`series-id`, `dataset-id`, `func-name`
87
+ and `connector-id` among them) and were not part of this change. A strict guard
88
+ built from the defaults therefore lets a write of any such key through without
89
+ a word. If your application does not write one, leave it out of the list you
90
+ pass, spelled kebab-case as the defaults spell it:
91
+
92
+ ```js
93
+ const NOT_MINE = new Set(['series-id', 'dataset-id']);
94
+ const guard = new StateGuardService({ eventBus, logger, config: {
95
+ ...DEFAULT_STATE_GUARD_CONFIG,
96
+ mode: 'strict',
97
+ allowedDatasetKeys: DEFAULT_STATE_GUARD_CONFIG.allowedDatasetKeys.filter((k) => !NOT_MINE.has(k)),
98
+ } });
99
+ ```
100
+
101
+ **Who is affected.** An application whose guard takes its `allowedDatasetKeys`
102
+ from the defaults, by spreading `DEFAULT_STATE_GUARD_CONFIG.allowedDatasetKeys`
103
+ or by leaving the option out, and that writes or removes one of the thirteen
104
+ keys (as `data-scenario-id`, or `dataset.scenarioId`: they are one key). In
105
+ `strict` the call throws
106
+ `StateGuard blocked setAttribute on "data-scenario-id" from …`; in the default
107
+ `warn` mode it is logged and emitted as `state-guard:violation`, on every
108
+ call. An application that passes its own complete list, or that allows the key
109
+ to one module through `allow()` or `moduleOverrides`, is not affected.
110
+
111
+ **To restore a key**, list it on top of the defaults, as for any key of your
112
+ own (the spelling does not matter: `scenarioId` and `scenario-id` are the same
113
+ key):
114
+
115
+ ```js
116
+ const guard = new StateGuardService({ eventBus, logger, config: {
117
+ ...DEFAULT_STATE_GUARD_CONFIG,
118
+ mode: 'strict',
119
+ allowedDatasetKeys: [...DEFAULT_STATE_GUARD_CONFIG.allowedDatasetKeys, 'scenario-id', 'run-id', 'run-state'],
120
+ } });
121
+ ```
@@ -8,17 +8,20 @@ Three stylesheets, and that is the whole CSS contract:
8
8
 
9
9
  | | Specifier | Resolves to | |
10
10
  |---|---|---|---|
11
- | `dist/tokens.css` | `flexdesk/css/tokens` | `css/tokens.css` | Every rule reads `var(--…)` from here. Swap this one file and the shell restyles. |
11
+ | `dist/tokens.css` | `flexdesk/css/tokens` | `dist/tokens.css` | Every rule reads `var(--…)` from here. Swap this one file and the shell restyles. |
12
12
  | `dist/reset.css` | `flexdesk/css/reset` | `dist/reset.css` | The reset. |
13
13
  | `dist/flexdesk.css` | `flexdesk/css` | `dist/flexdesk.css` | The library. Every class is `twm-` prefixed (the original working name; the prefix is stable and not worth a breaking rename). |
14
14
 
15
15
  A bundler, an import-map generator or `require.resolve` reaches each one by its
16
16
  specifier, through the package's `exports` map, which closes every other path
17
- into the package. The map sends `flexdesk/css/tokens` to the source
18
- `css/tokens.css`, not `dist/tokens.css`. The two are identical in a release,
19
- since `dist/` is rebuilt in the release commit, but can differ on a working
20
- checkout. `flexdesk/css/reset` is missing from 0.16.1 and earlier,
21
- where it fails with `ERR_PACKAGE_PATH_NOT_EXPORTED`.
17
+ into the package. All three resolve to the built copy under `dist/`, the file
18
+ the first column names. A specifier therefore never serves a source file that
19
+ has moved on since `dist/` was last rebuilt, which a working checkout or a git
20
+ dependency can hold. In 0.17.13 and earlier `flexdesk/css/tokens` resolved to
21
+ the source `css/tokens.css` instead. The two files are identical in a release,
22
+ since `dist/` is rebuilt in the release commit, so an installed release serves
23
+ the same bytes as before. `flexdesk/css/reset` is missing from 0.16.1 and
24
+ earlier, where it fails with `ERR_PACKAGE_PATH_NOT_EXPORTED`.
22
25
 
23
26
  `createDetailHeader` follows the prefix too: a badge is a `.twm-detail-header__badge`
24
27
  pill (a badge's `className` is added beside it, to recolour it), and an action is a
@@ -179,3 +182,18 @@ Still literals, because they are not the accent: the colours listed in the
179
182
  `COLOR_PALETTE`, `#1f77b4` as the colour pickers' default), and the Monaco
180
183
  editor theme's selection colours, which Monaco takes as hex values and not
181
184
  as CSS.
185
+
186
+ ## Spinners turn without keyframes of your own (0.17.14)
187
+
188
+ The tile's loading overlay, the DataTable's loading overlay, the computing
189
+ status window and the run-progress dialog each draw a ring with one coloured
190
+ arc and turn it. They turn with `flexdesk.css` alone: it defines their
191
+ `@keyframes` (`twm-tile-loading-spin`, `data-table__spin`, `computing-spin` and
192
+ `etl-spin`), each a full turn.
193
+
194
+ Up to and including 0.17.13 the stylesheet named those four animations and
195
+ defined none of them. CSS treats an animation with no `@keyframes` as no
196
+ animation, so the rings were drawn and stood still, and an embedder had to
197
+ define the same four names itself to make them move. You can delete those definitions. If you keep
198
+ one, `@keyframes` names are global and the later definition wins, which
199
+ changes nothing unless yours is not a full turn.
File without changes
package/editor.js CHANGED
File without changes
package/flow.js CHANGED
File without changes
package/host.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flexdesk",
3
- "version": "0.17.13",
3
+ "version": "0.17.14",
4
4
  "description": "A tiling window manager and widget set for desktop-class web applications. Zero runtime dependencies.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -52,7 +52,7 @@
52
52
  "development": "./widgets.js"
53
53
  },
54
54
  "./css": "./dist/flexdesk.css",
55
- "./css/tokens": "./css/tokens.css",
55
+ "./css/tokens": "./dist/tokens.css",
56
56
  "./css/reset": "./dist/reset.css",
57
57
  "./charts": {
58
58
  "import": "./dist/charts.js",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -23,6 +23,22 @@ const DEFAULT_ALLOWED_ATTRIBUTES = Object.freeze([
23
23
  const DEFAULT_ALLOWED_ATTRIBUTE_PREFIXES = Object.freeze([
24
24
  ]);
25
25
 
26
+ // What belongs here: the keys FlexDesk's own widgets write (the last group,
27
+ // which tests/state_guard_defaults.test.mjs keeps complete by scanning src/),
28
+ // the third-party attributes Plotly sets, and generic UI words. What does not:
29
+ // one application's own keys. Whether `data-order-id` is a legitimate write is
30
+ // that application's call, and it says so by adding the key (see
31
+ // docs/concepts/state-guard.md). A key listed here for one embedder's sake that
32
+ // no widget writes is a key every OTHER embedder's strict guard lets through
33
+ // without a word, which is the one thing a strict guard is there to stop.
34
+ //
35
+ // The list does not yet meet that, and it has not been audited key by key:
36
+ // many keys no widget writes are still here, some of them carried over from one
37
+ // application's markup (series-id, dataset-id, func-name and connector-id are
38
+ // examples). Thirteen that name one application's concepts were taken out. The
39
+ // test pins nine of the rest as still allowed, so dropping one is a deliberate
40
+ // edit, and the docs tell an embedder that a strict guard built from the
41
+ // defaults lets a write of such a key through. Do not add another like them.
26
42
  const DEFAULT_ALLOWED_DATASET_KEYS = Object.freeze([
27
43
  'testid',
28
44
  'phase',
@@ -70,11 +86,8 @@ const DEFAULT_ALLOWED_DATASET_KEYS = Object.freeze([
70
86
  'node-name',
71
87
  'lang',
72
88
  'playback-control',
73
- // Simulation/scenario state keys
89
+ // Generic state words
74
90
  'status',
75
- 'scenario-id',
76
- 'run-id',
77
- 'run-state',
78
91
  'selected',
79
92
  'active',
80
93
  'disabled',
@@ -90,13 +103,10 @@ const DEFAULT_ALLOWED_DATASET_KEYS = Object.freeze([
90
103
  'input-index',
91
104
  'output-index',
92
105
  'port-index',
93
- // Scenario editor keys
106
+ // Identifier and binding keys
94
107
  'source',
95
108
  'series-id',
96
109
  'dataset-id',
97
- 'mirror-input-bound',
98
- 'mirror-change-bound',
99
- 'mirror-input',
100
110
  'bound',
101
111
  'field',
102
112
  'key',
@@ -105,14 +115,11 @@ const DEFAULT_ALLOWED_DATASET_KEYS = Object.freeze([
105
115
  'tab-id',
106
116
  'param-id',
107
117
  'target-id',
108
- 'override-type',
109
118
  // Tree view component keys
110
119
  'item-id',
111
120
  'category-id',
112
- // Calibration page keys
113
- 'info-for',
114
- 'column',
115
121
  // Data page keys
122
+ 'column',
116
123
  'readonly',
117
124
  'id',
118
125
  'action',
@@ -131,12 +138,6 @@ const DEFAULT_ALLOWED_DATASET_KEYS = Object.freeze([
131
138
  'label',
132
139
  'title',
133
140
  'description',
134
- // DSL generator keys
135
- 'dsl-state',
136
- 'dsl-section-registered',
137
- 'dsl-scrollbars-installed',
138
- 'dsl-tabs-bound',
139
- 'dsl-controls-bound',
140
141
  // Inline highlighter keys
141
142
  'inline-hl',
142
143
  // Overlay scrollbar keys (UI-only, not state storage)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes