flexdesk 0.17.11 → 0.17.13

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 CHANGED
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.17.13 (2026-10-26)
4
+
5
+ A patch release: everything below is additive. Every fix is documented in a
6
+ section of the docs headed "(0.17.13)"; each entry links to it.
7
+
8
+ ### Added and changed
9
+
10
+ - [The menu bar in its own row under the top bar](docs/components/menus.md#the-menu-bar-in-its-own-row-under-the-top-bar-01713): a shell's menu bar can sit on a second line, as a full-width strip directly under the top bar. Put `chrome.menuBar` in a `.twm-global-menu-bar` that is a child of `body.twm-body`, between `.twm-global-top-bar` and the host. The row is a fixed-size item in the body's flex column (`flex: 0 0 auto`), so the host is shorter by the row's height and nothing is drawn under it. It is styled with FlexDesk tokens (`padding: var(--space-xxs) var(--space-xs)`, background `var(--surface-1)`, text `var(--text-body)`, a bottom border `var(--surface-0)` like the top bar's), and the `.twm-menubar` inside it keeps its own styles. It is a sibling of the top bar, not part of it: a window-drag region bound to the top bar (`WindowChromeController`'s `topBar`) does not include it, so a press on the row never drags the window, and it sets no `z-index`, so the top bar's dropdowns are drawn over it. A `ManagedWindow` on `document.body` (no `container`) is clamped, centred and maximised below the top bar and the row. A modal's backdrop starts below the top bar, so the row, like the top bar, stays usable while a modal is open. A shell without the row looks exactly as before; the row works only inside `body.twm-body` (STORY-0119).
11
+
12
+ ### Removed or renamed exports
13
+
14
+ None. Compared with 0.17.12, across every entry point (`core`, `widgets`, `wm`,
15
+ `host`, `canvas`, `flow`, `charts`, `tiles`, `editor`) and the package
16
+ `exports` map, no export was added, removed or renamed, and no option was
17
+ removed or renamed. Not an export but worth knowing: flexdesk.css has one new
18
+ class, `.twm-global-menu-bar`, and defines no new custom property.
19
+
20
+ ## 0.17.12 (2026-10-26)
21
+
22
+ A patch release: everything below is additive. Every fix is documented in a
23
+ section of the docs headed "(0.17.12)"; each entry links to it.
24
+
25
+ ### Added and changed
26
+
27
+ - [An empty cell can name what is missing: `emptyMessage` as a function](docs/components/charts.md#an-empty-cell-can-name-what-is-missing-emptymessage-as-a-function-01712): `renderPlotGrid`, `PlotPopoutWindow` and `openPlotPopoutWindow` take `emptyMessage` as a string, as before, or as a function `(subplot, results, index) => string`. It is called each time a cell is drawn empty, with that cell's subplot, the grid's results at that moment (as the grid was given them, so null or undefined when there are none) and the cell's index in the layout: on the first draw, on every `update()` (new `results`, a new `plotConfig`, `overlays` or `hiddenSeries`), on a resize that draws a deferred cell, and when a subplot fails to draw, so the message follows results that stream in or change without rebuilding the grid. The plot window hands its `emptyMessage` to its grid, so the function is also called on the window's refresh and stream events and `updateConfig()`; `openPlotPopoutWindow` forwards it. A slot with no subplot still shows `noSubplotMessage`, the Data tab's `tableEmptyMessage` stays a string, and a string `emptyMessage` is shown exactly as before (STORY-0117).
28
+
29
+ ### Removed or renamed exports
30
+
31
+ None. Compared with 0.17.11, across every entry point (`core`, `widgets`, `wm`,
32
+ `host`, `canvas`, `flow`, `charts`, `tiles`, `editor`) and the package
33
+ `exports` map, no export was added, removed or renamed, and no option was
34
+ removed or renamed: `emptyMessage` accepts a function as well as a string.
35
+
3
36
  ## 0.17.11 (2026-10-25)
4
37
 
5
38
  A patch release: everything below is additive. Every fix is documented in a
package/css/base.css CHANGED
@@ -1275,6 +1275,25 @@ body.twm-dt-col-resizing {
1275
1275
  .twm-global-top-bar, .twm-global-top-bar .twm-bar-left, .twm-global-top-bar .twm-bar-center, .twm-global-top-bar .twm-bar-right {
1276
1276
  min-width: 0; /* allow flex children to shrink */
1277
1277
  }
1278
+ /* Global menu row (STORY-0119, 0.17.13): a shell's `chrome.menuBar` in a
1279
+ * full-width strip of its own, directly under .twm-global-top-bar in
1280
+ * body.twm-body's column (host.css), so the host below shrinks by its height.
1281
+ * It is a sibling of the top bar, not inside it: a window-drag region bound
1282
+ * to the top bar does not include it. No z-index, so it is no stacking
1283
+ * context: the top bar's dropdowns draw over it, and the .twm-menubar inside
1284
+ * keeps its own styles, its menus (position: fixed, --z-over-windows) drawing
1285
+ * over the page. Its height is the bar's plus the padding. */
1286
+ .twm-global-menu-bar {
1287
+ display: flex;
1288
+ align-items: center;
1289
+ width: 100%;
1290
+ box-sizing: border-box;
1291
+ padding: var(--space-xxs) var(--space-xs);
1292
+ background-color: var(--surface-1);
1293
+ color: var(--text-body);
1294
+ border-bottom: 1px solid var(--surface-0);
1295
+ overflow: visible; /* the bar's menus extend below */
1296
+ }
1278
1297
  .twm-bar-center {
1279
1298
  white-space: nowrap;
1280
1299
  overflow: hidden;
package/css/flexdesk.css CHANGED
@@ -1695,6 +1695,25 @@ body.twm-dt-col-resizing {
1695
1695
  .twm-global-top-bar, .twm-global-top-bar .twm-bar-left, .twm-global-top-bar .twm-bar-center, .twm-global-top-bar .twm-bar-right {
1696
1696
  min-width: 0; /* allow flex children to shrink */
1697
1697
  }
1698
+ /* Global menu row (STORY-0119, 0.17.13): a shell's `chrome.menuBar` in a
1699
+ * full-width strip of its own, directly under .twm-global-top-bar in
1700
+ * body.twm-body's column (host.css), so the host below shrinks by its height.
1701
+ * It is a sibling of the top bar, not inside it: a window-drag region bound
1702
+ * to the top bar does not include it. No z-index, so it is no stacking
1703
+ * context: the top bar's dropdowns draw over it, and the .twm-menubar inside
1704
+ * keeps its own styles, its menus (position: fixed, --z-over-windows) drawing
1705
+ * over the page. Its height is the bar's plus the padding. */
1706
+ .twm-global-menu-bar {
1707
+ display: flex;
1708
+ align-items: center;
1709
+ width: 100%;
1710
+ box-sizing: border-box;
1711
+ padding: var(--space-xxs) var(--space-xs);
1712
+ background-color: var(--surface-1);
1713
+ color: var(--text-body);
1714
+ border-bottom: 1px solid var(--surface-0);
1715
+ overflow: visible; /* the bar's menus extend below */
1716
+ }
1698
1717
  .twm-bar-center {
1699
1718
  white-space: nowrap;
1700
1719
  overflow: hidden;
@@ -8528,8 +8547,9 @@ body.twm-dragging * { user-select: none; }
8528
8547
  * the WM host fills the remaining space.
8529
8548
  */
8530
8549
 
8531
- /* Body is a flex column: top bar, WM host (fill), bottom bar. We
8532
- * override the legacy `position: fixed` on both bars so they become
8550
+ /* Body is a flex column: top bar, menu row (when there is one), WM host
8551
+ * (fill), bottom bar. We override the legacy `position: fixed` on both
8552
+ * bars so they become
8533
8553
  * deterministic flex items and the host's height is exactly the
8534
8554
  * leftover space — no math, no overlap, no off-by-N pixels. Their
8535
8555
  * `z-index` stays put for dropdown stacking. */
@@ -8547,6 +8567,12 @@ body.twm-body > .twm-global-bottom-bar {
8547
8567
  inset: auto !important;
8548
8568
  flex: 0 0 auto;
8549
8569
  }
8570
+ /* The menu row (STORY-0119) sits between the top bar and the host, in
8571
+ * normal flow: a fixed-size strip, so the host's leftover space is less by
8572
+ * exactly its height. */
8573
+ body.twm-body > .twm-global-menu-bar {
8574
+ flex: 0 0 auto;
8575
+ }
8550
8576
  body.twm-body > .twm-host {
8551
8577
  flex: 1 1 auto;
8552
8578
  min-height: 0;
package/css/overrides.css CHANGED
@@ -1797,8 +1797,9 @@ body.twm-dragging * { user-select: none; }
1797
1797
  * the WM host fills the remaining space.
1798
1798
  */
1799
1799
 
1800
- /* Body is a flex column: top bar, WM host (fill), bottom bar. We
1801
- * override the legacy `position: fixed` on both bars so they become
1800
+ /* Body is a flex column: top bar, menu row (when there is one), WM host
1801
+ * (fill), bottom bar. We override the legacy `position: fixed` on both
1802
+ * bars so they become
1802
1803
  * deterministic flex items and the host's height is exactly the
1803
1804
  * leftover space — no math, no overlap, no off-by-N pixels. Their
1804
1805
  * `z-index` stays put for dropdown stacking. */
@@ -1816,6 +1817,12 @@ body.twm-body > .twm-global-bottom-bar {
1816
1817
  inset: auto !important;
1817
1818
  flex: 0 0 auto;
1818
1819
  }
1820
+ /* The menu row (STORY-0119) sits between the top bar and the host, in
1821
+ * normal flow: a fixed-size strip, so the host's leftover space is less by
1822
+ * exactly its height. */
1823
+ body.twm-body > .twm-global-menu-bar {
1824
+ flex: 0 0 auto;
1825
+ }
1819
1826
  body.twm-body > .twm-host {
1820
1827
  flex: 1 1 auto;
1821
1828
  min-height: 0;
package/dist/charts.js CHANGED
@@ -55,17 +55,17 @@ import {
55
55
  strideIndices,
56
56
  uid,
57
57
  updateChart
58
- } from "./chunk-2YTM3GJE.js";
58
+ } from "./chunk-TLIV6QSU.js";
59
59
  import {
60
60
  AutocompleteField,
61
61
  createRafResizeObserver
62
- } from "./chunk-NIJEJAZ7.js";
62
+ } from "./chunk-2PM6ZMNS.js";
63
63
  import {
64
64
  ColorField,
65
65
  ManagedWindow,
66
66
  ToggleField,
67
67
  selectBox
68
- } from "./chunk-CVMCZZX2.js";
68
+ } from "./chunk-MADMGRX2.js";
69
69
  import "./chunk-HNU6MM2B.js";
70
70
  import "./chunk-N7HC3ADV.js";
71
71
  import "./chunk-WUFLGETN.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  isRowActivation,
3
3
  isRowControl
4
- } from "./chunk-CVMCZZX2.js";
4
+ } from "./chunk-MADMGRX2.js";
5
5
  import {
6
6
  focusMenuItem,
7
7
  moveMenuFocus,
@@ -10745,4 +10745,4 @@ export {
10745
10745
  resolvePaging,
10746
10746
  DataTable
10747
10747
  };
10748
- //# sourceMappingURL=chunk-NIJEJAZ7.js.map
10748
+ //# sourceMappingURL=chunk-2PM6ZMNS.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  modalHost
3
- } from "./chunk-BE2PU3LL.js";
3
+ } from "./chunk-ZMH3OC7H.js";
4
4
  import {
5
5
  focusMenuItem,
6
6
  moveMenuFocus,
@@ -104,4 +104,4 @@ export {
104
104
  showContextMenu,
105
105
  hideContextMenu
106
106
  };
107
- //# sourceMappingURL=chunk-F35XSQWA.js.map
107
+ //# sourceMappingURL=chunk-IG74S3ZH.js.map
@@ -9,6 +9,10 @@ var BASE_Z_INDEX = 6e3;
9
9
  var MAX_Z_INDEX = 6999;
10
10
  var TOP_BAR_HEIGHT = 35;
11
11
  var BOTTOM_BAR_HEIGHT = 22;
12
+ function _topInset() {
13
+ const row = typeof document !== "undefined" ? document.querySelector("body > .twm-global-menu-bar") : null;
14
+ return TOP_BAR_HEIGHT + (row ? row.getBoundingClientRect().height : 0);
15
+ }
12
16
  var SNAP_EDGE = 12;
13
17
  var DEFAULT_ICON = "web_asset";
14
18
  var _zIndexCounter = 0;
@@ -631,6 +635,10 @@ var ManagedWindow = class {
631
635
  * width = window.innerWidth
632
636
  * height = window.innerHeight - TOP_BAR_HEIGHT - BOTTOM_BAR_HEIGHT
633
637
  *
638
+ * (STORY-0119: TOP_BAR_HEIGHT here is `_topInset()`, which is
639
+ * TOP_BAR_HEIGHT on a page without a menu row and adds the row's height
640
+ * on a page with one.)
641
+ *
634
642
  * so, substituting into the clamps below:
635
643
  *
636
644
  * maxWidth = width = window.innerWidth ✓
@@ -659,11 +667,12 @@ var ManagedWindow = class {
659
667
  height: this.container.clientHeight
660
668
  };
661
669
  }
670
+ const top = _topInset();
662
671
  return {
663
672
  minX: 0,
664
- minY: TOP_BAR_HEIGHT,
673
+ minY: top,
665
674
  width: window.innerWidth,
666
- height: window.innerHeight - TOP_BAR_HEIGHT - BOTTOM_BAR_HEIGHT
675
+ height: window.innerHeight - top - BOTTOM_BAR_HEIGHT
667
676
  };
668
677
  }
669
678
  _applyPosition() {
@@ -890,7 +899,8 @@ var ManagedWindow = class {
890
899
  }
891
900
  } else {
892
901
  this.x = Math.max(0, (maxWidth - this.width) / 2);
893
- this.y = Math.max(TOP_BAR_HEIGHT, TOP_BAR_HEIGHT + (maxHeight - this.height) / 2);
902
+ const top = _topInset();
903
+ this.y = Math.max(top, top + (maxHeight - this.height) / 2);
894
904
  }
895
905
  }
896
906
  _saveCurrentState() {
@@ -1139,7 +1149,8 @@ var ManagedWindow = class {
1139
1149
  const dy = e.clientY - state.startY;
1140
1150
  const dir = state.direction;
1141
1151
  const maxWidth = window.innerWidth;
1142
- const maxHeight = window.innerHeight - TOP_BAR_HEIGHT - BOTTOM_BAR_HEIGHT;
1152
+ const top = _topInset();
1153
+ const maxHeight = window.innerHeight - top - BOTTOM_BAR_HEIGHT;
1143
1154
  let newX = state.startWinX;
1144
1155
  let newY = state.startWinY;
1145
1156
  let newW = state.startWidth;
@@ -1154,12 +1165,12 @@ var ManagedWindow = class {
1154
1165
  newW = state.startWidth - (newX - state.startWinX);
1155
1166
  }
1156
1167
  if (dir.includes("s")) {
1157
- newH = Math.max(this.minHeight, Math.min(state.startHeight + dy, maxHeight - (newY - TOP_BAR_HEIGHT)));
1168
+ newH = Math.max(this.minHeight, Math.min(state.startHeight + dy, maxHeight - (newY - top)));
1158
1169
  }
1159
1170
  if (dir.includes("n")) {
1160
1171
  const maxDy = state.startHeight - this.minHeight;
1161
1172
  const actualDy = Math.min(dy, maxDy);
1162
- newY = Math.max(TOP_BAR_HEIGHT, state.startWinY + actualDy);
1173
+ newY = Math.max(top, state.startWinY + actualDy);
1163
1174
  newH = state.startHeight - (newY - state.startWinY);
1164
1175
  }
1165
1176
  this.x = newX;
@@ -1899,4 +1910,4 @@ export {
1899
1910
  endsTextSelection,
1900
1911
  isRowActivation
1901
1912
  };
1902
- //# sourceMappingURL=chunk-CVMCZZX2.js.map
1913
+ //# sourceMappingURL=chunk-MADMGRX2.js.map