flexdesk 0.17.14 → 0.17.16

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 (47) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/css/base.css +237 -8
  3. package/css/flexdesk.css +344 -17
  4. package/css/overrides.css +6 -0
  5. package/dist/charts.js +104 -34
  6. package/dist/charts.js.map +3 -3
  7. package/dist/{chunk-2PM6ZMNS.js → chunk-2Z2ENHR3.js} +2 -2
  8. package/dist/{chunk-IG74S3ZH.js → chunk-7JI7HHSY.js} +2 -2
  9. package/dist/{chunk-TLIV6QSU.js → chunk-7OL6F2AM.js} +3 -3
  10. package/dist/{chunk-MADMGRX2.js → chunk-PGZ4EHSE.js} +156 -9
  11. package/dist/chunk-PGZ4EHSE.js.map +7 -0
  12. package/dist/{chunk-ZMH3OC7H.js → chunk-XJNADRGR.js} +4 -2
  13. package/dist/chunk-XJNADRGR.js.map +7 -0
  14. package/dist/flexdesk.css +344 -17
  15. package/dist/flow.js +3 -3
  16. package/dist/tiles.js +84 -9
  17. package/dist/tiles.js.map +2 -2
  18. package/dist/widgets.js +448 -5
  19. package/dist/widgets.js.map +4 -4
  20. package/dist/wm.js +110 -19
  21. package/dist/wm.js.map +2 -2
  22. package/docs/README.md +1 -1
  23. package/docs/components/charts.md +70 -3
  24. package/docs/components/forms-and-dialogs.md +80 -2
  25. package/docs/components/menus.md +85 -1
  26. package/docs/components/shell.md +52 -0
  27. package/docs/components/slide-out-panel.md +15 -0
  28. package/docs/components/tiles.md +37 -0
  29. package/package.json +1 -1
  30. package/src/charts/plot_config_editor.js +125 -29
  31. package/src/tiles/tile_base.js +48 -2
  32. package/src/tiles/tile_grid.js +42 -1
  33. package/src/tiling/kind_taxonomy.js +45 -0
  34. package/src/tiling/tile_breadcrumb.js +12 -4
  35. package/src/tiling/wm.js +86 -11
  36. package/src/ui/components/category_picker.js +556 -0
  37. package/src/ui/components/computing_status_window.js +3 -0
  38. package/src/ui/components/confirm_dialog.js +3 -0
  39. package/src/ui/components/managed_window.js +204 -9
  40. package/src/ui/components/modal.js +11 -2
  41. package/src/ui/components/wizard.js +4 -1
  42. package/widgets.js +2 -1
  43. package/dist/chunk-MADMGRX2.js.map +0 -7
  44. package/dist/chunk-ZMH3OC7H.js.map +0 -7
  45. /package/dist/{chunk-2PM6ZMNS.js.map → chunk-2Z2ENHR3.js.map} +0 -0
  46. /package/dist/{chunk-IG74S3ZH.js.map → chunk-7JI7HHSY.js.map} +0 -0
  47. /package/dist/{chunk-TLIV6QSU.js.map → chunk-7OL6F2AM.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.17.16 (2026-10-27)
4
+
5
+ A patch release. Two new widgets and options; the other entries change what
6
+ embedders see (listed under "Changed behaviour"). Each entry links to its
7
+ section of the docs.
8
+
9
+ ### Added
10
+
11
+ - [A categorised picker: `CategoryPicker`](docs/components/menus.md#a-categorised-picker-categorypicker-01716): a popover anchored to a trigger, like ActionDropdown, with a search field over categories (colour dot, count), the active category's items (icon or short badge) and a detail pane (description, optional monospace preview, an Add button). Typing filters across categories; arrows, Enter, Tab (cycling field and Add), double-click and Esc work as documented; IME composition is respected; ARIA combobox/listbox with `aria-activedescendant`. Exported from `@flexdesk/widgets` (STORY-0126).
12
+ - [A kind that lives in a window: `windowOnly` and `window`](docs/components/shell.md#a-kind-that-lives-in-a-window-windowonly-and-window-01716): a taxonomy kind can declare `window: { width, height }` and `windowOnly: true`. A window-only kind opens in a managed window from every open path, its window never docks into a tile, and a restored layout drops its tile tabs with one warning; `taxonomy.isWindowOnly` and `windowSize` answer (STORY-0127).
13
+ - [Tile title double-click and configuring marks](docs/components/tiles.md#a-double-click-on-the-title-strip-configures-the-tile-01716): a double-click on a tile's title strip does what its cogwheel does (`configOnDoubleClick`, default on); `grid.setConfiguring(tileId, on)` / `grid.isConfiguring(tileId)` keep any number of tiles marked as being configured (`tile--configuring`), across `setLayout` (STORY-0128).
14
+ - `ManagedWindow` options `persistGeometry` (default true) and `focusOnOpen` (default true; `'window'` focuses the frame), `ManagedWindow.configure({ storageKey, maxSavedWindows })`, `DEFAULT_MAX_SAVED_WINDOWS` (50) and `win.isClosed` (BUG-0053).
15
+ - The plot config editor's `extraSeriesFields` may return an array of field specs `{ label, control | render, hint? }` that the editor lays out on its own label column, and receives `ctx.field(label, control, { hint })` (BUG-0052).
16
+
17
+ ### Changed behaviour
18
+
19
+ - [Windows: closing, focus and Esc](docs/components/forms-and-dialogs.md#windows-closing-focus-and-esc-01716): a closed `ManagedWindow` leaves the registry and releases its content and callbacks (`get(id)` answers null; build a new window to reopen). Saved geometry keeps the 50 most recently used ids (window-manager-minted `twm-mw-*` entries are purged). A new window takes focus (its first field) and gives it back on close; the computing status window does not take focus, and the run-progress dialog focuses its frame, not Stop. Esc closes a modal on top wherever focus is, otherwise the window holding focus, otherwise the topmost modal, and never when the key was already handled (`defaultPrevented`) or composing (BUG-0053).
20
+ - [A plot config series is one block](docs/components/charts.md#a-plot-config-series-is-one-block-and-an-embedders-fields-share-it-01716): each series is a header line (variable, label, remove) and fields on the editor's label column (colour as ColorField, width, dash, stack group, extra fields, overlay). `.twm-plot-config__series-main` is gone; new classes `__series-head`, `__series-fields`, `__slot`, `__hint`, `__field--hinted`. The config objects read and written are unchanged (BUG-0052).
21
+
22
+ ### Removed or renamed exports
23
+
24
+ None. New exports: `CategoryPicker`, `DEFAULT_MAX_SAVED_WINDOWS`. flexdesk.css gains the category picker's classes, `.tile--configuring` and the plot config classes above, and drops `.twm-plot-config__series-main`; it defines no new custom property besides the picker's `--twm-category-picker-color`.
25
+
26
+ ## 0.17.15 (2026-10-26)
27
+
28
+ A patch release with no API change. Its entries change what embedders see: a top-level tile loses its one-segment breadcrumb strip, every `openForm` row lays its inputs out anew, and a slide-out panel no longer crosses what lies right of its host. Each entry links to its section of the docs.
29
+
30
+ ### Added and changed
31
+
32
+ - [A tile breadcrumb of one segment is not drawn](docs/components/shell.md#a-tile-breadcrumb-of-one-segment-is-not-drawn-01715): while `mountTileBreadcrumb`'s path is a single segment or none (a top-nav page with no root crumb, trail or entity; an `appGlobal` kind), the strip is `hidden` and the page shell's `.twm-page-shell__breadcrumb` slot takes no room, so a top-level page no longer shows its name twice. `refresh()` shows it again at two segments; longer paths and `renderBreadcrumb` are unchanged (STORY-0124).
33
+ - [The inputs of a row are equally wide](docs/components/forms-and-dialogs.md#the-inputs-of-a-row-are-equally-wide-01715): in an `openForm` `{ row }` each field's basis is its label (the first one's, the 130px label column) and the free space is shared equally, so every input of a row has the same width (up to each control's own padding); until 0.17.14 the first input was squeezed by the label column. A form whose widest row has three or more fields opens at `170 + 230 × fields` px: at least 640px, otherwise at most 90% of the window (BUG-0050).
34
+ - [A slide-out panel is never drawn outside its box](docs/components/slide-out-panel.md#never-drawn-outside-its-box-01715): `SlideOutPanel` no longer slides by a transform; a `clip-path` alone (200ms ease-out) reveals it from its right edge, so opening or closing it no longer crosses the dock panel right of its host (BUG-0051).
35
+
36
+ ### Removed or renamed exports
37
+
38
+ None. Compared with 0.17.14, across every entry point and the package
39
+ `exports` map, no export or option was added, removed or renamed. flexdesk.css
40
+ has no new class and defines no new custom property.
41
+
3
42
  ## 0.17.14 (2026-10-26)
4
43
 
5
44
  A patch release. Everything below is additive except one changed default (see
package/css/base.css CHANGED
@@ -3344,20 +3344,25 @@ body.twm-window-maximized .twm-resize-handle {
3344
3344
  }
3345
3345
 
3346
3346
  /* Selected — set by `_onConfigClick()` while the config dialog is open, and
3347
- cleared from every sibling by the same handler. It reads `--accent-lighter`
3347
+ cleared from every sibling by the same handler. `--configuring` is the same
3348
+ look set by the embedder through `setConfiguring()` (0.17.16), one per open
3349
+ config window, and no handler clears it. It reads `--accent-lighter`
3348
3350
  (BUG-0045): `--accent-bright` is a border colour on this background and not
3349
3351
  a text one. It was #78b9ff, which no mix of `--accent-bright` with white
3350
3352
  gives exactly; `--accent-lighter` (about #73b6e3) is the nearest. */
3351
- .tile.tile--selected {
3353
+ .tile.tile--selected,
3354
+ .tile.tile--configuring {
3352
3355
  border-color: var(--accent-lighter);
3353
3356
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-lighter) 30%, transparent), var(--shadow-md);
3354
3357
  }
3355
3358
 
3356
- .tile.tile--selected .tile-header {
3359
+ .tile.tile--selected .tile-header,
3360
+ .tile.tile--configuring .tile-header {
3357
3361
  background: color-mix(in srgb, var(--accent-lighter) 10%, transparent);
3358
3362
  }
3359
3363
 
3360
- .tile.tile--selected .tile-title {
3364
+ .tile.tile--selected .tile-title,
3365
+ .tile.tile--configuring .tile-title {
3361
3366
  color: var(--accent-lighter);
3362
3367
  }
3363
3368
 
@@ -3775,6 +3780,215 @@ body.twm-window-maximized .twm-resize-handle {
3775
3780
  text-overflow: ellipsis;
3776
3781
  }
3777
3782
 
3783
+ /* ══ CATEGORY PICKER (STORY-0126) ══════════════════════════════════════
3784
+ `CategoryPicker` (category_picker.js): a search field over three columns —
3785
+ categories, the active category's items, the highlighted item's detail.
3786
+ Placed by `ActionDropdown.position` (inline top/left/max-height), so it is
3787
+ `position: fixed` on --z-over-windows: over the managed window it opened
3788
+ from. It shrinks to the inline max-height and its columns scroll inside.
3789
+ A category's colour arrives as `--twm-category-picker-color` on its rows. */
3790
+ .twm-category-picker {
3791
+ position: fixed;
3792
+ z-index: var(--z-over-windows);
3793
+ display: flex;
3794
+ flex-direction: column;
3795
+ box-sizing: border-box;
3796
+ width: 640px;
3797
+ max-width: calc(100vw - 2 * var(--space-md));
3798
+ height: 440px;
3799
+ background: var(--surface-1);
3800
+ border: 1px solid var(--border-strong);
3801
+ border-radius: var(--radius-lg);
3802
+ box-shadow: var(--shadow-lg);
3803
+ color: var(--text-primary);
3804
+ font-family: inherit;
3805
+ font-size: var(--font-size-md);
3806
+ overflow: hidden;
3807
+ }
3808
+ .twm-category-picker[hidden] {
3809
+ display: none;
3810
+ }
3811
+ .twm-category-picker__search {
3812
+ flex: none;
3813
+ padding: var(--space-md);
3814
+ border-bottom: 1px solid var(--border-default);
3815
+ }
3816
+ .twm-category-picker__input {
3817
+ width: 100%;
3818
+ box-sizing: border-box;
3819
+ padding: var(--space-sm) var(--space-md);
3820
+ background: var(--surface-0);
3821
+ border: 1px solid var(--border-strong);
3822
+ border-radius: var(--radius-sm);
3823
+ color: var(--text-primary);
3824
+ font-family: inherit;
3825
+ font-size: var(--font-size-md);
3826
+ }
3827
+ .twm-category-picker__input:focus {
3828
+ outline: none;
3829
+ border-color: var(--accent-bright);
3830
+ }
3831
+ .twm-category-picker__body {
3832
+ flex: 1 1 auto;
3833
+ min-height: 0;
3834
+ display: flex;
3835
+ }
3836
+ .twm-category-picker__column {
3837
+ flex: none;
3838
+ box-sizing: border-box;
3839
+ padding: var(--space-sm) 0;
3840
+ border-right: 1px solid var(--border-default);
3841
+ overflow-y: auto;
3842
+ }
3843
+ .twm-category-picker__categories {
3844
+ width: 170px;
3845
+ }
3846
+ .twm-category-picker__items {
3847
+ width: 220px;
3848
+ }
3849
+ .twm-category-picker__category,
3850
+ .twm-category-picker__item {
3851
+ display: flex;
3852
+ align-items: center;
3853
+ gap: var(--space-md);
3854
+ width: 100%;
3855
+ box-sizing: border-box;
3856
+ padding: var(--space-sm) var(--space-lg);
3857
+ background: transparent;
3858
+ border: none;
3859
+ color: var(--text-primary);
3860
+ font-family: inherit;
3861
+ font-size: var(--font-size-md);
3862
+ text-align: left;
3863
+ cursor: pointer;
3864
+ }
3865
+ .twm-category-picker__category:hover,
3866
+ .twm-category-picker__item:hover {
3867
+ background: var(--state-hover);
3868
+ }
3869
+ .twm-category-picker__category--active,
3870
+ .twm-category-picker__item--highlighted,
3871
+ .twm-category-picker__category--active:hover,
3872
+ .twm-category-picker__item--highlighted:hover {
3873
+ background: var(--state-selected);
3874
+ }
3875
+ /* The column the arrow keys move in marks its row with an accent edge. */
3876
+ .twm-category-picker__column--current .twm-category-picker__category--active,
3877
+ .twm-category-picker__column--current .twm-category-picker__item--highlighted {
3878
+ box-shadow: inset 2px 0 0 var(--accent-bright);
3879
+ }
3880
+ .twm-category-picker__category--empty {
3881
+ color: var(--text-dim);
3882
+ }
3883
+ .twm-category-picker__dot {
3884
+ flex: none;
3885
+ width: 8px;
3886
+ height: 8px;
3887
+ border-radius: var(--radius-circle);
3888
+ /* background-color, not the shorthand: the colour is the caller's, and a
3889
+ url() in the shorthand would be fetched */
3890
+ background-color: var(--twm-category-picker-color, var(--text-muted));
3891
+ }
3892
+ .twm-category-picker__category-label,
3893
+ .twm-category-picker__item-label {
3894
+ flex: 1 1 auto;
3895
+ min-width: 0;
3896
+ overflow: hidden;
3897
+ white-space: nowrap;
3898
+ text-overflow: ellipsis;
3899
+ }
3900
+ .twm-category-picker__count {
3901
+ flex: none;
3902
+ color: var(--text-muted);
3903
+ font-size: var(--font-size-xs);
3904
+ font-variant-numeric: tabular-nums;
3905
+ }
3906
+ .twm-category-picker__badge,
3907
+ .twm-category-picker__item .twm-category-picker__icon {
3908
+ flex: none;
3909
+ display: flex;
3910
+ align-items: center;
3911
+ justify-content: center;
3912
+ width: 22px;
3913
+ height: 22px;
3914
+ box-sizing: border-box;
3915
+ color: var(--twm-category-picker-color, var(--text-muted));
3916
+ }
3917
+ .twm-category-picker__badge {
3918
+ border: 1px solid var(--twm-category-picker-color, var(--text-muted));
3919
+ border-radius: var(--radius-sm);
3920
+ font-family: var(--font-family-mono);
3921
+ font-size: var(--font-size-xs);
3922
+ font-weight: var(--font-weight-semibold);
3923
+ overflow: hidden;
3924
+ white-space: nowrap;
3925
+ }
3926
+ .twm-category-picker__item .twm-category-picker__icon {
3927
+ font-size: 18px;
3928
+ }
3929
+ /* One category's hits while a query is set: role="group", named by its header. */
3930
+ .twm-category-picker__hits {
3931
+ display: flex;
3932
+ flex-direction: column;
3933
+ }
3934
+ .twm-category-picker__group {
3935
+ display: flex;
3936
+ align-items: center;
3937
+ gap: var(--space-sm);
3938
+ padding: var(--space-md) var(--space-lg) var(--space-xs);
3939
+ color: var(--text-muted);
3940
+ font-size: var(--font-size-xs);
3941
+ text-transform: uppercase;
3942
+ letter-spacing: 0.06em;
3943
+ }
3944
+ .twm-category-picker__empty,
3945
+ .twm-category-picker__detail-empty {
3946
+ padding: var(--space-md) var(--space-lg);
3947
+ color: var(--text-muted);
3948
+ }
3949
+ .twm-category-picker__detail {
3950
+ flex: 1 1 auto;
3951
+ min-width: 0;
3952
+ display: flex;
3953
+ flex-direction: column;
3954
+ gap: var(--space-md);
3955
+ padding: var(--space-xl);
3956
+ overflow-y: auto;
3957
+ }
3958
+ .twm-category-picker__title {
3959
+ color: var(--text-bright);
3960
+ font-size: var(--font-size-xl);
3961
+ font-weight: var(--font-weight-semibold);
3962
+ }
3963
+ .twm-category-picker__description {
3964
+ color: var(--text-secondary);
3965
+ line-height: var(--line-height-normal);
3966
+ }
3967
+ .twm-category-picker__preview-label {
3968
+ margin-top: var(--space-sm);
3969
+ color: var(--text-muted);
3970
+ font-size: var(--font-size-xs);
3971
+ text-transform: uppercase;
3972
+ letter-spacing: 0.06em;
3973
+ }
3974
+ .twm-category-picker__preview {
3975
+ margin: 0;
3976
+ padding: var(--space-md);
3977
+ background: var(--surface-0);
3978
+ border: 1px solid var(--border-default);
3979
+ border-radius: var(--radius-sm);
3980
+ color: var(--text-body);
3981
+ font-family: var(--font-family-mono);
3982
+ font-size: var(--font-size-sm);
3983
+ white-space: pre-wrap;
3984
+ overflow-wrap: anywhere;
3985
+ }
3986
+ .twm-category-picker__add {
3987
+ margin-top: auto;
3988
+ align-self: flex-end;
3989
+ }
3990
+ /* ══ end CATEGORY PICKER ══ */
3991
+
3778
3992
  /* ── from code.css ───────────────────────── */
3779
3993
  /* Code language tabs */
3780
3994
  .twm-code-tabs-header {
@@ -3934,6 +4148,12 @@ body.twm-window-maximized .twm-resize-handle {
3934
4148
  .twm-managed-window:focus-within {
3935
4149
  border-color: #505050;
3936
4150
  }
4151
+ /* BUG-0053. The frame is focusable (tabindex -1) so a press on it keeps focus
4152
+ in the window; the focus-within border above already shows that, so the
4153
+ frame draws no outline of its own. */
4154
+ .twm-managed-window:focus {
4155
+ outline: none;
4156
+ }
3937
4157
  /* Minimize animation class — targets taskbar item via CSS custom properties */
3938
4158
  .twm-managed-window--minimizing {
3939
4159
  transform: translate(var(--mw-target-x, 0px), var(--mw-target-y, 100vh)) scale(var(--mw-target-scale, 0.1));
@@ -4824,14 +5044,23 @@ button.twm-about-dialog__btn--close:focus {
4824
5044
  min-width: min(400px, var(--twm-slide-out-host-w, 400px));
4825
5045
  max-width: var(--twm-slide-out-host-w, none);
4826
5046
 
4827
- transform: translateX(100%);
4828
- transition: transform 200ms ease-out, visibility 0s linear 200ms;
5047
+ /* Never drawn outside its final box (BUG-0051). While open the panel
5048
+ * is `position: fixed` at its host's right edge, where the host's
5049
+ * `overflow: hidden` no longer clips it, and its slide from
5050
+ * translateX(100%) was drawn across whatever lies right of the host (a
5051
+ * dock panel). It does not move any more: a clip alone carries the
5052
+ * motion, revealing it from its right edge leftwards, so no frame can
5053
+ * paint outside its box (a transform and a clip animated together can
5054
+ * drift apart, the transform often running on the compositor). The left
5055
+ * inset of -16px keeps the box shadow. */
5056
+ clip-path: inset(0 0 0 100%);
5057
+ transition: clip-path 200ms ease-out, visibility 0s linear 200ms;
4829
5058
  visibility: hidden;
4830
5059
  }
4831
5060
  .twm-slide-out-panel--open {
4832
- transform: translateX(0);
5061
+ clip-path: inset(0 0 0 -16px);
4833
5062
  visibility: visible;
4834
- transition: transform 200ms ease-out, visibility 0s linear 0s;
5063
+ transition: clip-path 200ms ease-out, visibility 0s linear 0s;
4835
5064
  }
4836
5065
  /* Header bar */
4837
5066
  .twm-slide-out-panel__header {