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.
- package/CHANGELOG.md +27 -0
- package/LICENSE +0 -0
- package/README.md +0 -0
- package/THIRD_PARTY_LICENSES.md +0 -0
- package/canvas.js +0 -0
- package/charts.js +0 -0
- package/core.js +0 -0
- package/css/base.css +45 -3
- package/css/flexdesk.css +60 -3
- package/css/overrides.css +15 -0
- package/css/reset.css +0 -0
- package/dist/canvas.js +0 -0
- package/dist/canvas.js.map +0 -0
- package/dist/chunk-3PHCPZHT.js +0 -0
- package/dist/chunk-3PHCPZHT.js.map +0 -0
- package/dist/chunk-FOOS3T5L.js +0 -0
- package/dist/chunk-FOOS3T5L.js.map +0 -0
- package/dist/chunk-GOQLR5WR.js +0 -0
- package/dist/chunk-GOQLR5WR.js.map +0 -0
- package/dist/chunk-HNU6MM2B.js +0 -0
- package/dist/chunk-HNU6MM2B.js.map +0 -0
- package/dist/chunk-JKZLNKG6.js +0 -0
- package/dist/chunk-JKZLNKG6.js.map +0 -0
- package/dist/chunk-JYWURG5T.js +0 -0
- package/dist/chunk-JYWURG5T.js.map +0 -0
- package/dist/chunk-N7HC3ADV.js +0 -0
- package/dist/chunk-N7HC3ADV.js.map +0 -0
- package/dist/core.js +3 -18
- package/dist/core.js.map +2 -2
- package/dist/editor.js.map +0 -0
- package/dist/flexdesk.css +60 -3
- package/dist/flow.js.map +0 -0
- package/dist/host.js +0 -0
- package/dist/host.js.map +0 -0
- package/dist/reset.css +0 -0
- package/dist/tile_registry-6WZPTUZV.js +0 -0
- package/dist/tile_registry-6WZPTUZV.js.map +0 -0
- package/dist/widgets.js +27 -18
- package/dist/widgets.js.map +2 -2
- package/dist/wm.js +195 -28
- package/dist/wm.js.map +3 -3
- package/docs/README.md +1 -1
- package/docs/best-practices.md +1 -1
- package/docs/components/command-palette.md +0 -0
- package/docs/components/conversation.md +0 -0
- package/docs/components/data-table-grid.md +0 -0
- package/docs/components/data-table.md +0 -0
- package/docs/components/editor.md +0 -0
- package/docs/components/flow-canvas-editor.md +0 -0
- package/docs/components/flow-editors.md +0 -0
- package/docs/components/flow-lane-editor.md +0 -0
- package/docs/components/flow-outline-editor.md +0 -0
- package/docs/components/forms-and-dialogs.md +0 -0
- package/docs/components/input-fields.md +0 -0
- package/docs/components/keymap.md +66 -3
- package/docs/components/layout-widgets.md +32 -0
- package/docs/components/markdown-and-outline.md +0 -0
- package/docs/components/menus.md +9 -5
- package/docs/components/notifications.md +0 -0
- package/docs/components/panels-and-desktops.md +56 -0
- package/docs/components/record-list-and-fact-table.md +0 -0
- package/docs/components/settings-view.md +0 -0
- package/docs/components/shell.md +0 -0
- package/docs/components/slide-out-panel.md +0 -0
- package/docs/components/wizard.md +0 -0
- package/docs/concepts/building-and-packaging.md +0 -0
- package/docs/concepts/domain-neutrality.md +0 -0
- package/docs/concepts/host-port.md +0 -0
- package/docs/concepts/packages-and-entry-points.md +0 -0
- package/docs/concepts/peer-dependencies.md +0 -0
- package/docs/concepts/state-guard.md +65 -2
- package/docs/concepts/styling.md +24 -6
- package/docs/quickstart.md +0 -0
- package/editor.js +0 -0
- package/flow.js +0 -0
- package/host.js +0 -0
- package/package.json +2 -2
- package/src/canvas/canvas_adapter.js +0 -0
- package/src/canvas/edge_router.js +0 -0
- package/src/canvas/graph/connector_router.js +0 -0
- package/src/canvas/graph/history_service.js +0 -0
- package/src/canvas/graph/node_platform.js +0 -0
- package/src/canvas/index.js +0 -0
- package/src/canvas/layered_layout.js +0 -0
- package/src/charts/data_series_plot_window.js +0 -0
- package/src/charts/downsample.js +0 -0
- package/src/charts/plot_config.js +0 -0
- package/src/charts/plotly_wrapper.js +0 -0
- package/src/core/event_bus.js +0 -0
- package/src/core/logging.js +0 -0
- package/src/core/settings.js +0 -0
- package/src/core/state_guard.js +0 -0
- package/src/core/state_guard_config.js +19 -18
- package/src/core/state_machine.js +0 -0
- package/src/editor/editor_pane.js +0 -0
- package/src/editor/monaco_editor_factory.js +0 -0
- package/src/editor/monaco_loader.js +0 -0
- package/src/editor/notebook_search.js +0 -0
- package/src/editor/notebook_undo_manager.js +0 -0
- package/src/editor/split_pane_container.js +0 -0
- package/src/flow/canvas_editor/connections.js +0 -0
- package/src/flow/canvas_editor/editor.js +0 -0
- package/src/flow/canvas_editor/index.js +0 -0
- package/src/flow/canvas_editor/layout.js +0 -0
- package/src/flow/canvas_editor/strings.js +0 -0
- package/src/flow/kit/always_before.js +0 -0
- package/src/flow/kit/catalogue.js +0 -0
- package/src/flow/kit/chip_input.js +0 -0
- package/src/flow/kit/dom.js +0 -0
- package/src/flow/kit/findings.js +0 -0
- package/src/flow/kit/history.js +0 -0
- package/src/flow/kit/index.js +0 -0
- package/src/flow/kit/keys.js +0 -0
- package/src/flow/kit/logic_graph.js +0 -0
- package/src/flow/kit/references.js +0 -0
- package/src/flow/kit/settings_panel.js +0 -0
- package/src/flow/kit/settings_schema.js +0 -0
- package/src/flow/kit/step_picker.js +0 -0
- package/src/flow/kit/strings.js +0 -0
- package/src/flow/kit/value_picker.js +0 -0
- package/src/flow/kit/widgets.js +0 -0
- package/src/flow/lanes/editor.js +0 -0
- package/src/flow/lanes/index.js +0 -0
- package/src/flow/lanes/layout.js +0 -0
- package/src/flow/lanes/pipeline.js +0 -0
- package/src/flow/lanes/preview.js +0 -0
- package/src/flow/lanes/strings.js +0 -0
- package/src/flow/outline/editor.js +0 -0
- package/src/flow/outline/index.js +0 -0
- package/src/flow/outline/mapping.js +0 -0
- package/src/flow/outline/menu.js +0 -0
- package/src/flow/outline/operations.js +0 -0
- package/src/flow/outline/recognise.js +0 -0
- package/src/flow/outline/strings.js +0 -0
- package/src/help/help_modal.js +0 -0
- package/src/help/help_registry.js +0 -0
- package/src/host/external_links.js +0 -0
- package/src/host/file_picker.js +0 -0
- package/src/host/host.js +0 -0
- package/src/host/photino_host.js +0 -0
- package/src/host/photino_host.md +0 -0
- package/src/host/pywebview_host.js +0 -0
- package/src/tiles/chart_tiles.js +0 -0
- package/src/tiles/config_schema.js +0 -0
- package/src/tiles/figure_tile.js +0 -0
- package/src/tiles/layout_persistence.js +0 -0
- package/src/tiles/list_tiles.js +0 -0
- package/src/tiles/scope_picker.js +0 -0
- package/src/tiles/tile_config_form.js +0 -0
- package/src/tiles/tile_grid.js +0 -0
- package/src/tiles/tile_registry.js +0 -0
- package/src/tiling/bottom_panel_tabs.js +0 -0
- package/src/tiling/command_palette.js +0 -0
- package/src/tiling/content_registry.js +0 -0
- package/src/tiling/desktops.js +79 -16
- package/src/tiling/entity_sources.js +0 -0
- package/src/tiling/keymap.js +70 -2
- package/src/tiling/kind_taxonomy.js +0 -0
- package/src/tiling/landing_table.js +0 -0
- package/src/tiling/loading_overlay.js +0 -0
- package/src/tiling/nav_panel.js +0 -0
- package/src/tiling/page_factory.js +0 -0
- package/src/tiling/panel_availability.js +0 -0
- package/src/tiling/panel_keys.js +0 -0
- package/src/tiling/shell.js +9 -2
- package/src/tiling/tab_strip.js +0 -0
- package/src/tiling/tile_breadcrumb.js +0 -0
- package/src/tiling/tile_renderer.js +0 -0
- package/src/tiling/tile_tab_menu.js +0 -0
- package/src/tiling/tile_tree.js +63 -5
- package/src/tiling/wm.js +59 -6
- package/src/tiling/zoom.js +0 -0
- package/src/ui/base/component_base.js +0 -0
- package/src/ui/base/controller_base.js +0 -0
- package/src/ui/base/page_base.js +0 -0
- package/src/ui/components/about_dialog.js +0 -0
- package/src/ui/components/alert_log.js +0 -0
- package/src/ui/components/alerts_pane.js +0 -0
- package/src/ui/components/attribute_list_editor.js +0 -0
- package/src/ui/components/autocomplete_field.js +0 -0
- package/src/ui/components/cell_editor_host.js +0 -0
- package/src/ui/components/cell_editors.js +0 -0
- package/src/ui/components/cell_grid.js +0 -0
- package/src/ui/components/cell_paste.js +0 -0
- package/src/ui/components/cell_selection.js +0 -0
- package/src/ui/components/choice_controls.js +0 -0
- package/src/ui/components/color_field.js +0 -0
- package/src/ui/components/color_list_field.js +0 -0
- package/src/ui/components/column_fit.js +0 -0
- package/src/ui/components/column_groups.js +0 -0
- package/src/ui/components/column_order.js +0 -0
- package/src/ui/components/computing_status_window.js +0 -0
- package/src/ui/components/confirm_dialog.js +0 -0
- package/src/ui/components/conversation_view.js +0 -0
- package/src/ui/components/crosstab.js +0 -0
- package/src/ui/components/detail_header.js +0 -0
- package/src/ui/components/document_outline.js +0 -0
- package/src/ui/components/drag_reorder.js +0 -0
- package/src/ui/components/fact_table.js +0 -0
- package/src/ui/components/fill_handle.js +0 -0
- package/src/ui/components/gallery_picker.js +0 -0
- package/src/ui/components/header_actions.js +0 -0
- package/src/ui/components/inline_renamer.js +0 -0
- package/src/ui/components/job_progress.js +0 -0
- package/src/ui/components/menu_bar.js +0 -0
- package/src/ui/components/modal.js +0 -0
- package/src/ui/components/notification_history.js +0 -0
- package/src/ui/components/progress.js +0 -0
- package/src/ui/components/record_list.js +0 -0
- package/src/ui/components/row_activation.js +0 -0
- package/src/ui/components/section_stack.js +0 -0
- package/src/ui/components/series.js +0 -0
- package/src/ui/components/settings_view.js +0 -0
- package/src/ui/components/slide_out_panel.js +0 -0
- package/src/ui/components/slider_field.js +0 -0
- package/src/ui/components/sortable_list.js +0 -0
- package/src/ui/components/staged_edits.js +0 -0
- package/src/ui/components/staged_history.js +0 -0
- package/src/ui/components/table_filter_chips.js +0 -0
- package/src/ui/components/table_state_store.js +0 -0
- package/src/ui/components/table_structure.js +0 -0
- package/src/ui/components/table_values.js +0 -0
- package/src/ui/components/tabs.js +0 -0
- package/src/ui/components/tag_input.js +0 -0
- package/src/ui/components/toast.js +0 -0
- package/src/ui/components/toggle_field.js +0 -0
- package/src/ui/components/tree_view.js +41 -18
- package/src/ui/components/tsv.js +0 -0
- package/src/ui/components/window_taskbar.js +0 -0
- package/src/ui/components/wizard.js +0 -0
- package/src/ui/controllers/window_chrome_controller.js +0 -0
- package/src/ui/notification_center.js +0 -0
- package/src/ui/utils/auto_scrollbars.js +0 -0
- package/src/ui/utils/markdown.js +0 -0
- package/src/ui/utils/native_context_menu.js +0 -0
- package/src/ui/utils/overlay_scrollbar.js +0 -0
- package/src/ui/utils/raf_resize_observer.js +0 -0
- package/src/ui/utils/scroll_lock.js +0 -0
- package/src/ui/utils/strip_scroll.js +0 -0
- package/src/ui/utils/tooltip_service.js +0 -0
- package/tiles.js +0 -0
- package/widgets.js +0 -0
- 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
|
|
package/docs/best-practices.md
CHANGED
|
@@ -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
|
|
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,
|
|
46
|
-
|
|
47
|
-
|
|
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
|
package/docs/components/menus.md
CHANGED
|
@@ -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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
package/docs/components/shell.md
CHANGED
|
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
|
|
42
|
-
|
|
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
|
+
```
|
package/docs/concepts/styling.md
CHANGED
|
@@ -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` | `
|
|
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.
|
|
18
|
-
|
|
19
|
-
since `dist/`
|
|
20
|
-
|
|
21
|
-
|
|
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.
|
package/docs/quickstart.md
CHANGED
|
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.
|
|
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": "./
|
|
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
|
package/src/canvas/index.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/charts/downsample.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/core/event_bus.js
CHANGED
|
File without changes
|
package/src/core/logging.js
CHANGED
|
File without changes
|
package/src/core/settings.js
CHANGED
|
File without changes
|
package/src/core/state_guard.js
CHANGED
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
package/src/flow/kit/dom.js
CHANGED
|
File without changes
|
package/src/flow/kit/findings.js
CHANGED
|
File without changes
|
package/src/flow/kit/history.js
CHANGED
|
File without changes
|
package/src/flow/kit/index.js
CHANGED
|
File without changes
|
package/src/flow/kit/keys.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/flow/kit/strings.js
CHANGED
|
File without changes
|