@toclocoinc/lattice-grid 1.53.0 → 1.55.0

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 (78) hide show
  1. package/README.md +6 -4
  2. package/docs/API.html +100 -36
  3. package/docs/api-detail.html +88 -14
  4. package/lattice-grid.d.ts +122 -15
  5. package/lattice-grid.esm.min.js +285 -66
  6. package/lattice-grid.min.cjs +285 -66
  7. package/lattice-grid.min.css +1 -1
  8. package/lattice-grid.min.js +285 -66
  9. package/modules/ai.esm.min.js +25 -4
  10. package/modules/ai.min.cjs +25 -4
  11. package/modules/ai.min.js +25 -4
  12. package/modules/angular.esm.min.js +2 -2
  13. package/modules/angular.min.cjs +2 -2
  14. package/modules/angular.min.js +2 -2
  15. package/modules/chart-alluvial.esm.min.js +1 -1
  16. package/modules/chart-arc.esm.min.js +1 -1
  17. package/modules/chart-bubblemap.esm.min.js +1 -1
  18. package/modules/chart-bump.esm.min.js +1 -1
  19. package/modules/chart-calendar.esm.min.js +1 -1
  20. package/modules/chart-decomposition.esm.min.js +1 -1
  21. package/modules/chart-diverging.esm.min.js +1 -1
  22. package/modules/chart-dumbbell.esm.min.js +1 -1
  23. package/modules/chart-fan.esm.min.js +1 -1
  24. package/modules/chart-hexbin.esm.min.js +1 -1
  25. package/modules/chart-hexmap.esm.min.js +1 -1
  26. package/modules/chart-icicle.esm.min.js +1 -1
  27. package/modules/chart-parallel.esm.min.js +1 -1
  28. package/modules/chart-ridgeline.esm.min.js +1 -1
  29. package/modules/chart-roc.esm.min.js +1 -1
  30. package/modules/chart-slope.esm.min.js +1 -1
  31. package/modules/chart-splom.esm.min.js +1 -1
  32. package/modules/chart-waffle.esm.min.js +1 -1
  33. package/modules/charts.esm.min.js +172 -43
  34. package/modules/charts.min.cjs +172 -43
  35. package/modules/charts.min.js +172 -43
  36. package/modules/data-router.esm.min.js +4 -4
  37. package/modules/data-router.min.cjs +4 -4
  38. package/modules/data-router.min.js +4 -4
  39. package/modules/devtools.esm.min.js +2 -2
  40. package/modules/devtools.min.cjs +2 -2
  41. package/modules/devtools.min.js +2 -2
  42. package/modules/dhtmlx-compat.esm.min.js +4 -4
  43. package/modules/dhtmlx-compat.min.cjs +4 -4
  44. package/modules/dhtmlx-compat.min.js +4 -4
  45. package/modules/gantt.esm.min.js +32 -6
  46. package/modules/gantt.min.cjs +32 -6
  47. package/modules/gantt.min.js +32 -6
  48. package/modules/htmx.esm.min.js +285 -66
  49. package/modules/htmx.min.cjs +285 -66
  50. package/modules/htmx.min.js +285 -66
  51. package/modules/kanban.esm.min.js +49 -9
  52. package/modules/kanban.min.cjs +49 -9
  53. package/modules/kanban.min.js +49 -9
  54. package/modules/kpi.esm.min.js +4213 -24
  55. package/modules/kpi.min.cjs +4213 -24
  56. package/modules/kpi.min.js +4213 -24
  57. package/modules/layout.esm.min.js +305 -23
  58. package/modules/layout.min.cjs +305 -23
  59. package/modules/layout.min.js +305 -23
  60. package/modules/mock-socket.esm.min.js +2 -2
  61. package/modules/mock-socket.min.cjs +2 -2
  62. package/modules/mock-socket.min.js +2 -2
  63. package/modules/react.esm.min.js +2 -2
  64. package/modules/react.min.cjs +2 -2
  65. package/modules/react.min.js +2 -2
  66. package/modules/svelte.esm.min.js +2 -2
  67. package/modules/svelte.min.cjs +2 -2
  68. package/modules/svelte.min.js +2 -2
  69. package/modules/tabs.esm.min.js +4 -4
  70. package/modules/tabs.min.cjs +4 -4
  71. package/modules/tabs.min.js +4 -4
  72. package/modules/vue.esm.min.js +2 -2
  73. package/modules/vue.min.cjs +2 -2
  74. package/modules/vue.min.js +2 -2
  75. package/modules/webcomponent.esm.min.js +285 -66
  76. package/modules/webcomponent.min.cjs +285 -66
  77. package/modules/webcomponent.min.js +285 -66
  78. package/package.json +1 -1
@@ -437,7 +437,7 @@
437
437
  <div class="shell">
438
438
  <aside class="rail">
439
439
  <p class="rail__brand">Lattice Grid</p>
440
- <p class="rail__sub">Developer guide · v1.53.0</p>
440
+ <p class="rail__sub">Developer guide · v1.55.0</p>
441
441
  <nav>
442
442
  <div class="rail__group">
443
443
  <span class="rail__label">Start here</span>
@@ -553,7 +553,7 @@
553
553
  <a href="API.html">reference tables</a> are the shorter version for when you already know.
554
554
  </p>
555
555
  <p class="chips">
556
- <span class="chip">Version 1.7.1</span>
556
+ <span class="chip">Version 1.55.0</span>
557
557
  <span class="chip">Zero dependencies</span>
558
558
  <span class="chip">No build step</span>
559
559
  </p>
@@ -585,6 +585,10 @@ createGrid(element, { locale: 'fr-FR', messages: FR_FR });</code></pre>
585
585
 
586
586
  <p><code>MESSAGE_KEYS</code> lists every key. <code>auditCatalogue(yours)</code> returns what is missing and what is not a real key, which is the quickest way to check a translation before shipping it.</p>
587
587
 
588
+ <h3>Keys seeded in English, awaiting translation</h3>
589
+ <p>A key added after a catalogue was written ships in British English only until a translator supplies it; <code>Messages</code> merges every catalogue over the default, so the grid says it in English rather than showing the key. The shipped catalogues do not carry an English copy under the guise of a translation, and the build's completeness test lists exactly which keys are in this state. As of 1.55 the most recent additions are the strings that had shipped as template literals, untranslated in every locale (BACKLOG-0001106): the presence live region and roster note, <code>a11y.presence.refused</code>, <code>presence.someoneElse</code> and <code>presence.hidden</code>; the comment panel's changed-since note, <code>comments.valueMoved</code>; the facet band's accessible name, <code>facets.filter</code>; the heading tooltip that names a reduction, <code>header.totalOf</code>; the audit-mode tooltip, <code>diff.before</code> and <code>diff.empty</code>; the kanban list names, <code>kanban.columnCards</code> and <code>kanban.laneCards</code>, both plural objects selected by <code>count</code>; and the gantt lag label, <code>gantt.lag</code> and <code>gantt.lead</code>. Supply any of them in <code>messages</code> to translate it today.</p>
590
+ <p>The kanban board and the gantt view are modules and do not import the catalogue. A board bound to a grid, or a plan created with one, borrows that grid's <code>messages</code>; otherwise pass <code>messages</code> — a grid's own, or any object with <code>t(key, params)</code> — to <code>createKanban</code> or to <code>gantt.mount</code>. With neither they render the English.</p>
591
+
588
592
  <div class="why">
589
593
  <p><strong>The grid is checked in the other direction too.</strong> <code>auditCatalogue</code> tells you a catalogue is complete: that a translator covered every key. It cannot tell you the grid only ever renders text that came from a catalogue in the first place, and a string written into the source passes every test, because the tests assert on the English the grid happens to produce.</p>
590
594
  <p>So the build refuses one. Any literal reaching an element's text, or an announced attribute such as <code>aria-label</code>, <code>title</code> or <code>placeholder</code>, has to come from the catalogue. That is what stops a localised grid drifting back into English one plausible change at a time.</p>
@@ -668,8 +672,8 @@ createGrid(element, { direction: 'rtl' }); <span class="cmt">// or say so out
668
672
 
669
673
  <div class="example">
670
674
  <p class="example__label">jsDelivr, no npm install, no bundler</p>
671
- <pre><code>&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.7.1/lattice-grid.min.css"&gt;
672
- &lt;script src="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.7.1/lattice-grid.min.js"&gt;&lt;/script&gt;
675
+ <pre><code>&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1/lattice-grid.min.css"&gt;
676
+ &lt;script src="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1/lattice-grid.min.js"&gt;&lt;/script&gt;
673
677
 
674
678
  &lt;script&gt;
675
679
  <span class="kw">const</span> grid = LatticeGrid.createGrid(document.getElementById('grid'), config);
@@ -694,9 +698,10 @@ createGrid(element, { direction: 'rtl' }); <span class="cmt">// or say so out
694
698
 
695
699
  <div class="note">
696
700
  <p><strong>jsDelivr mirrors every version published to npm</strong> at
697
- <code>cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@&lt;version&gt;/&lt;file&gt;</code>: pin an
698
- exact version, e.g. <code>@1.7.1</code> rather than <code>@latest</code>, so a later release
699
- does not change what a page already in production loads. The same convention reaches a
701
+ <code>cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1/&lt;file&gt;</code>. <code>@1</code> pins
702
+ the major: a page in production picks up fixes within 1.x and never a breaking release, where
703
+ <code>@latest</code> would. To freeze a page on one exact build, replace <code>@1</code> with
704
+ the full version <code>getVersion()</code> reports. The same convention reaches a
700
705
  module: <code>.../modules/htmx.esm.min.js</code>, <code>.../modules/dhtmlx-compat.esm.min.js</code>,
701
706
  and so on. Type declarations resolve automatically through npm's own <code>types</code> field;
702
707
  for editor tooling against the CDN or a plain script tag, point your <code>tsconfig</code> at
@@ -1273,7 +1278,7 @@ off(); <span class="cmt">// every subscrip
1273
1278
  </p>
1274
1279
  <div class="example">
1275
1280
  <p class="example__label">Which version am I running?</p>
1276
- <pre><code>grid.getVersion(); <span class="cmt">// '1.7.1'</span>
1281
+ <pre><code>grid.getVersion(); <span class="cmt">// '1.55.0'</span>
1277
1282
  LatticeGrid.getVersion(); <span class="cmt">// the same, when you have no grid to hand</span></code></pre>
1278
1283
  </div>
1279
1284
  <p class="lead-in">
@@ -1354,6 +1359,31 @@ LatticeGrid.getVersion(); <span class="cmt">// the same, when you have no grid
1354
1359
  <code>grid.columns.fit()</code> distributes the viewport width across visible columns, and
1355
1360
  <code>autoSize</code> measures content.
1356
1361
  </p>
1362
+ <p>The width <code>fit()</code> distributes is the space the cells actually occupy: the body
1363
+ viewport&rsquo;s client width, read when you call it. When the grid has enough rows to
1364
+ scroll vertically, that width excludes the scrollbar, so the columns end flush with it
1365
+ instead of running under it; when there is no vertical scrollbar, it is the full inner
1366
+ width. Rows you passed to <code>createGrid</code> or <code>grid.rows.load()</code> before
1367
+ the call are counted, so calling it straight after either works.</p>
1368
+ <p>Every column the grid draws counts toward that width, not only the ones <code>fit()</code>
1369
+ sizes. It sizes your resizable columns. A column it does not size keeps its width, and that
1370
+ width comes off the target first: a column declared <code>resizable: false</code>, and the
1371
+ grid&rsquo;s own selection checkbox, detail expander, group and tree columns. Your resizable
1372
+ columns then share what is left in proportion to their current widths, within each
1373
+ <code>min</code> and <code>max</code>; a column held at a bound stays there and the others
1374
+ share the rest, so all the drawn columns together still come to the viewport width
1375
+ exactly. Under a pivot every column drawn is one the grid generates, so <code>fit()</code>
1376
+ has nothing to size and leaves the widths as they are.</p>
1377
+ <p>If what is left is less than those columns&rsquo; minimums, which happens when the columns
1378
+ <code>fit()</code> does not size already take the width, each resizable column is set to its
1379
+ <code>min</code> (40px when it declares none). None goes below its minimum, and none is
1380
+ squeezed to nothing: the grid scrolls horizontally instead, and a <code>[lattice]</code>
1381
+ warning in the console names the widths that ran out.</p>
1382
+ <p><code>fit()</code> is one-shot. It sets a fixed width on each column once, including a
1383
+ <code>flex</code> column, and does not follow the grid afterwards. If the width changes
1384
+ later, because the container is resized or because rows that arrive afterwards bring a
1385
+ vertical scrollbar in, call it again. A column that should keep tracking the width by
1386
+ itself wants <code>flex</code> instead of <code>fit()</code>.</p>
1357
1387
  <p>Both are also on the column menu: Move left, Move right, Move to start, Move to end, and
1358
1388
  a Width submenu, and bound to the keyboard with a heading focused: <kbd>Alt</kbd> with a
1359
1389
  left or right arrow resizes, <kbd>Shift</kbd> with one moves the column. Neither operation
@@ -1969,9 +1999,16 @@ grid.edit.setCells([
1969
1999
  <span class="cmt">// → the number of cells written</span></code></pre>
1970
2000
  </div>
1971
2001
  <p class="lead-in">
1972
- This is the full path, not a shortcut: it validates, emits <code>cell:changed</code>,
2002
+ This is the full path, not a shortcut: it validates, emits <code>cell:changed</code> per cell,
1973
2003
  re-sorts if the column is sorted on, records one undo entry, and returns <code>0</code> for a
1974
- column the user may not write.
2004
+ column the user may not write. It then announces the whole call <strong>once</strong> as
2005
+ <code>rows:changed</code> (<code>identified: true</code>, <code>edit: true</code>, the
2006
+ <code>updated</code> rows and the <code>columns</code> written), after the per-cell events, so
2007
+ a derived grid, a statistic tile or anything else that follows <code>rows:changed</code>
2008
+ re-reads once for a twenty-cell paste rather than twenty times. An editor commit, a fill, a
2009
+ paste, an undo, a redo and an optimistic rollback each announce themselves the same way, once
2010
+ per batch. Earlier releases announced nothing here, and a derived view of an edited grid went
2011
+ silently stale.
1975
2012
  </p>
1976
2013
 
1977
2014
  <h3>High-frequency updates</h3>
@@ -3306,7 +3343,17 @@ createGrid(right, {
3306
3343
  <code>refresh</code> to <code>live</code>, <code>manual</code> or a number of milliseconds to
3307
3344
  change the coalescing; <code>idle</code> is the default and settles to a frame.</p>
3308
3345
  <p><strong>Derived grids are read-only.</strong> There is one copy of the data and it lives in
3309
- the source. Write there and the derived grid follows.</p>
3346
+ the source. Write there and the derived grid follows: an edit through the source's editing
3347
+ API (<code>edit.setCells</code>, an inline commit, a fill, a paste, an undo, a redo or an
3348
+ optimistic rollback) is announced once per batch, after the per-cell
3349
+ <code>cell:changed</code> events, and the derived grid re-derives once for the batch. A
3350
+ grouped derivation patches the groups the edited rows belong to; a derivation with a
3351
+ <code>where</code>, an <code>unnest</code>, a producer, or an edit to a column the source
3352
+ filters on re-reads in full, at the cost measured above. <strong>Use <code>idle</code> when a
3353
+ derived child follows an editable grid.</strong> Under <code>live</code> the derivation runs
3354
+ synchronously inside the edit call, so a keystroke's commit on a 200k-row source pays the
3355
+ whole re-read before the editor closes; <code>idle</code> defers it to the next frame and
3356
+ folds a burst of edits into one.</p>
3310
3357
  <p><strong>The key comes for free.</strong> A derived grid keys on <code>__key</code>, which
3311
3358
  the source writes onto every row it produces: the group value, the profiled column, or the
3312
3359
  source row's own key when nothing is grouped. Set <code>rowKey</code> only to override it.</p>
@@ -4743,6 +4790,17 @@ createChart({
4743
4790
  <code>kind: 'count'</code> is refused with a warning rather than quietly given a second
4744
4791
  meaning. The x column has to be continuous and carry wall-clock times &mdash; a banded or
4745
4792
  categorical axis has no domain to roll.</p>
4793
+ <p><strong>A producer whose clock runs ahead.</strong> A reading stamped slightly ahead of the
4794
+ viewer&rsquo;s clock carries the end of the domain forward with it, so the newest mark is
4795
+ drawn. How far is bounded: <strong>a quarter of the span</strong> (15&nbsp;s on a
4796
+ 60&nbsp;s window). A reading further ahead than that is treated as a producer whose clock is
4797
+ wrong &mdash; it does not move the window, it is not drawn, and the chart warns once, naming
4798
+ the column, how many readings were left out and how far ahead they were. Without the bound,
4799
+ one device two minutes fast moved a one-minute window past every other device&rsquo;s recent
4800
+ readings and the chart drew a single dot (BACKLOG-0001123). If every reading in the window is
4801
+ that far ahead the chart shows its empty state, with the same warning.
4802
+ <code>chart.data().windowed</code> counts the readings dropped at either edge of the
4803
+ window. The fix for the warning is the producer&rsquo;s clock, not a wider window.</p>
4746
4804
  <p><strong>Idle costs nothing.</strong> Both halves advance on a plain interval &mdash; a
4747
4805
  quarter of the window, clamped to between 50&nbsp;ms and one second &mdash; and never on an
4748
4806
  animation frame. The source&rsquo;s wake returns after a single number comparison unless a
@@ -5357,6 +5415,17 @@ grid.annotate.use(null); <span class="cmt">// hand the grid
5357
5415
  <h3>Keyboard</h3>
5358
5416
  <p>Every operation is reachable without a pointer. Resizing and reordering a column were once
5359
5417
  drag-only; both now have key bindings and menu items, so nothing depends on dragging.</p>
5418
+ <p><strong>Entering the grid.</strong> The grid is one stop in the page's tab order. Pressing
5419
+ <kbd>Tab</kbd> into a grid that has not been used yet puts focus on the grid itself, and the
5420
+ grid draws a focus ring around its own edge at once, in the theme's focus colour, so a keyboard
5421
+ user can see where focus went before pressing anything else. In Windows High Contrast Mode
5422
+ the ring is drawn in the system text colour. A screen reader announces the grid (or tree grid),
5423
+ its row and column counts and whether it is read-only. The first arrow key, <kbd>Home</kbd>
5424
+ or <kbd>Ctrl</kbd>+<kbd>Home</kbd> moves focus to a cell, and the ring moves with it. From then
5425
+ on the cell you were on is the grid's tab stop: <kbd>Shift</kbd>+<kbd>Tab</kbd> from the first
5426
+ cell leaves the grid, and <kbd>Tab</kbd> back into the grid returns to that cell. The grid takes
5427
+ the tab stop back only when that cell is outside the rendered rows, and then draws its own
5428
+ ring again.</p>
5360
5429
  <div class="table-wrap">
5361
5430
  <table>
5362
5431
  <thead><tr><th>Keys</th><th>Does</th></tr></thead>
@@ -5706,10 +5775,11 @@ grid.edit.pasteInto(text); <span class="cmt">// Excel's t
5706
5775
  <tr><td class="sig">Delete / Backspace</td><td class="desc">Clear the selected cells.</td></tr>
5707
5776
  <tr><td class="sig">Enter</td><td class="desc">Start editing; commit and step down.</td></tr>
5708
5777
  <tr><td class="sig">Tab</td><td class="desc">Commit and step across.</td></tr>
5709
- <tr><td class="sig">Escape</td><td class="desc">Cancel the edit; restore a maximised grid once nothing else wants it.</td></tr>
5778
+ <tr><td class="sig">Escape</td><td class="desc">Cancel the edit; restore a maximised grid once nothing else wants it. Coming back from full screen, whether by Escape or the rail's restore button, puts focus back on the cell you were on, not on the page.</td></tr>
5710
5779
  <tr><td class="sig">Ctrl/Cmd + F</td><td class="desc">Open the <a href="#find">find bar</a>; in it, Enter / Shift+Enter step through the matches and Escape closes.</td></tr>
5711
5780
  <tr><td class="sig">Space</td><td class="desc">Toggle the row's selection.</td></tr>
5712
5781
  <tr><td class="sig">Home / End, Page Up / Down</td><td class="desc">Jump; with Ctrl, to the ends of the grid.</td></tr>
5782
+ <tr><td class="sig">Ctrl/Cmd + Alt + H</td><td class="desc">Move focus to the column heading. On a heading, ArrowLeft / ArrowRight move between headings, Enter or Space sort by the column (Shift to add it to the sort), Alt + arrows resize, Shift + arrows move the column, Alt + ArrowDown opens the column menu, and ArrowDown or Escape return you to the data. The full list is in the <a href="#accessibility-guide">accessibility guide</a>.</td></tr>
5713
5783
  </tbody>
5714
5784
  </table>
5715
5785
  </div>
@@ -5717,6 +5787,10 @@ grid.edit.pasteInto(text); <span class="cmt">// Excel's t
5717
5787
  <p>None of these fire while you are typing into an input, a filter box, an open editor, the
5718
5788
  view-name field. The grid checks where the keystroke came from before claiming it, which
5719
5789
  sounds obvious and is the sort of thing that is usually wrong.</p>
5790
+ <p>A key a heading handles acts once, on the heading: Enter sorts and does not also open an
5791
+ editor on a body cell, ArrowRight reaches the next heading and not a cell beneath it. A key
5792
+ the heading declines still travels, which is how Escape reaches a maximised grid from a
5793
+ heading.</p>
5720
5794
  </div>
5721
5795
 
5722
5796
  <h2 id="rules-guide">Conditional formatting</h2>
@@ -6991,7 +7065,7 @@ grid.import.apply(preview);</code></pre>
6991
7065
  <tr><td class="name">createKPI</td><td class="desc">A KPI / stat-tile view (module <code>kpi</code>) of a dataset as a panel of aggregate tiles — each tile a <code>sum</code>, <code>avg</code>, <code>min</code>, <code>max</code>, <code>count</code>, <code>countDistinct</code> or a <code>custom</code> reducer over the routed rows, with an optional <code>filter</code> predicate, number <code>format</code> (<code>number</code>/<code>currency</code>/<code>percent</code>/<code>compact</code>), a <code>baseline</code> for a delta, semantic threshold bands (<code>thresholds</code> with two cut points and a direction, or an explicit <code>bands</code> list, giving a <code>good</code>/<code>warn</code>/<code>critical</code> status kept separate from any accent), and an optional <code>sparkline</code> series. It is a dataset viewer like any other: it consumes data through the same keyed-diff <code>rows.apply({ add, update, remove })</code> contract a grid exposes, so a Data Router can <code>attach(value, kpi)</code> and drive a KPI panel beside a grid, a kanban and a chart off one feed. Updates are incremental — a delta adjusts each tile's running accumulator by only the rows it carries (an add contributes, a remove reverses, an update reverses-then-contributes), the two bounded exceptions being a <code>min</code>/<code>max</code> whose current extreme is removed (a rescan of that tile's own value multiset) and a <code>custom</code> reducer (recomputed over the filtered store, an arbitrary function having no inverse). Each tile is a labelled <code>&lt;figure&gt;</code>, focusable and keyboard-activatable, its value announced, the sparkline respecting <code>prefers-reduced-motion</code>; a <code>tile:click</code> event (also from the keyboard) lets a host drill down or filter a routed grid. Pass <code>null</code> as the element for a headless panel that computes the same tile model without a DOM. Not a dashboard layout engine (that is the parked dashboard generator) and no charting beyond the minimal sparkline (that is the charts module).</td></tr>
6992
7066
  <tr><td class="name">createAI</td><td class="desc">Create an AI narrative / insights controller (module <code>ai</code>) over a live grid. It produces a short, plain-language narrative of the grid's <em>computed</em> figures — a per-KPI / per-chart / per-column <code>explain</code>, or an <code>insights</code> panel over the current filtered view. The grid makes no AI call of its own: <code>createAI</code> imports no provider SDK, holds no key, and makes no network request; it calls one host callback, <code>ask({ system, messages, prompt, tools?, schema?, signal })</code> — your model, your key, your privacy decision — the same philosophy as the data adapters. Two grounding paths feed one guard: where the provider offers tool-use the model is given a curated read-only tool set (<code>getSchema</code>, <code>getProfile</code>, <code>getStatistics</code>, <code>getForecast</code>, <code>runQuery</code>) and the grid's own engine computes what it asks for; otherwise the module builds a facts packet from <code>grid.statistics</code>/the profile/forecasts/view counts and passes it in the prompt. Every figure in the narrative is reconciled against the values the engine produced this render — an ungrounded number is stripped before display (the number-reconciliation guard), so a hallucinated figure never reaches the user. The prompt is constrained to narrate-only; the layer is read-only and never mutates data. <code>redact</code> (a column id, a list, or a predicate) and <code>maxRows</code> bound what the module hands <code>ask()</code>, and the module sends nothing itself; an <code>ask()</code> error surfaces a friendly message and the grid stays fully usable, AI being additive rather than load-bearing. Complementary to <code>grid.ai</code> (the intent/plan skill layer): pass no <code>ask</code> and it adopts the grid's configured <code>ai.ask</code>, running the facts-packet path over it. Pass a headless grid for a DOM-free narrative; <code>facts(target)</code> returns the exact grounded packet without calling <code>ask()</code>. UMD global <code>LatticeGridAI</code>.</td></tr>
6993
7067
  <tr><td class="name">createTabs</td><td class="desc">Create a tabbed grid (module <code>tabs</code>): a <code>role="tablist"</code> strip above a stack of <code>role="tabpanel"</code> regions, each hosting its own, independently-configured <code>createGrid</code> instance — "configure each tab as per a normal grid" rather than one grid whose state is swapped (<code>ColumnModel#applyState</code> only repositions/hides/resizes existing columns by id; it carries no field, type or row data, so a state-swap only works when every tab shares one schema). <code>createGrid</code> is injected (<code>createTabs(el, { createGrid, tabs })</code>), the same pattern the React/Vue/Svelte adapters use, so the module imports no engine code and adds nothing to a page that does not load it. A tab that names <code>from: '&lt;tabId&gt;'</code> gets a <code>source: { mode: 'derived', from: &lt;the parent tab’s live grid&gt;, where, group, join, … }</code> wired for it automatically — reusing the shipped derived-source mechanism rather than a new config-inheritance one — and activating a derived tab materialises its whole ancestor chain first; a cyclic <code>from</code> graph is refused (naming the exact cycle) when <code>createTabs</code> is called, not at first click. A tab’s grid mounts on first activation and then stays alive, hidden, so its scroll/selection/filters/sort/grouping/expansion — and an open cell/row editor, left exactly as it was, uncommitted and undiscarded — survive a switch natively; <code>destroy()</code> tears every mounted tab down. The strip is a real tablist with <code>aria-selected</code>, a roving <code>tabindex</code>, and manual-activation keyboard handling (arrows/Home/End move focus, Enter/Space or a click activates). Events: <code>tab:changed</code>, a cancellable <code>beforeTabChange</code> paired with <code>tabChange:cancelled</code>. UMD global <code>LatticeGridTabs</code>.</td></tr>
6994
- <tr><td class="name">createLayout</td><td class="desc">Create a reconfigurable dashboard layout (module <code>layout</code>): a cell grid inside an element, and a set of windows on it that a user can move, resize and close by drag <em>or</em> by keyboard &mdash; the surface a customer would otherwise reach for GridStack to get. It is <strong>payload-agnostic</strong>: a window body is a <code>div</code> with an <code>id</code> that the module creates, sizes and never reads, so it imports no engine code at all (not even <code>createGrid</code>) and its own code is 11,202 bytes gzipped, measured against a 62,206-byte fixed bundle floor. <code>columns</code>/<code>rows</code> divide the element; <code>overflowX</code> and <code>overflowY</code> are <em>independent</em> axes, each <code>'static'</code> (tracks divide the container with <code>minmax(0, 1fr)</code>) or <code>'scroll'</code> (tracks take a fixed <code>columnWidth</code>/<code>rowHeight</code> and the canvas extends past the viewport, so a column keeps the size it asked for &mdash; measured: shrinking a 600px host to 300px leaves a 200px column at 200px). Spacing takes a real CSS length: a number of pixels, or <code>'200px'</code>, <code>'25%'</code>, <code>'1fr'</code>, <code>'2rem'</code>; anything else is refused by name and replaced by the default, because the value reaches an inline style. Windows are placed by 1-based <code>xPos</code>/<code>yPos</code>/<code>xSize</code>/<code>ySize</code>, or auto-placed in the first free cell; <code>chrome</code> defaults on, and <code>closable</code>/<code>movable</code>/<code>resizable</code> all default <em>off</em>, so a fixed dashboard is fixed without opting out. <code>compact: 'vertical'</code> pushes displaced windows down then pulls them up (<code>window:moved</code> carries both <code>to</code> and <code>landed</code>); <code>'none'</code> keeps every window where it is put. <code>closable</code>/<code>movable</code>/<code>resizable</code> each also take a <em>layout-level</em> default of the same name, which a window's own boolean overrides, and <code>setInteractive(true|false|{movable, resizable, closable})</code> changes that default at runtime &mdash; the &ldquo;Edit layout&rdquo; button &mdash; without destroying the layout or any payload in it, with <code>getInteractive()</code> reading it back. Locking always wins and unlocking never overrides an opt-out: <code>setInteractive(false)</code> pins a window that declared <code>movable: true</code>, while <code>setInteractive(true)</code> leaves a window that declared <code>movable: false</code> pinned. The config key and the method are deliberately different: <code>movable: false</code> in the config states the <em>default</em> for windows that declare nothing and takes nothing away from one that opted in, whereas <code>setInteractive(false)</code> is an <em>active lock</em>. <code>getInteractive()</code> is three-valued &mdash; <code>undefined</code> for unset, <code>true</code>, or <code>false</code> for a lock &mdash; and a key carrying <code>undefined</code> is treated as absent, so <code>setInteractive(getInteractive())</code> is a no-op in every state. It moves both halves of the enforcement, the rendered handles <em>and</em> the pointer and keyboard gesture checks, and fires no event because a mode is not an arrangement &mdash; <code>getLayout()</code> neither carries it nor restores it. A locked layout is not a read-only dashboard: what is inside a window is configured with that payload's own settings. Keyboard parity with the drag: a focusable handle per window running the kanban board's grab/move/drop/cancel model, with a polite live region announcing grabbed, every tentative position, dropped, cancelled and reverted. It owns exactly <strong>one</strong> <code>ResizeObserver</code> for the whole layout, over two targets, and tells payloads their new content box through <code>window:resized</code> &mdash; it never calls into a payload, because it cannot know what one is. Events: <code>window:moved</code>, <code>window:resized</code>, <code>window:closed</code>, <code>layout:changed</code>, the cancellable <code>beforeWindowMove</code>/<code>beforeWindowResize</code>/<code>beforeWindowClose</code> and their <code>*:cancelled</code> pairs; drag progress is not emitted per frame. <code>getLayout()</code>/<code>setLayout()</code> round-trip the arrangement as plain JSON, and <code>getState()</code>/<code>setState()</code> are the versioned pair. Closing a window does <strong>not</strong> destroy its payload &mdash; the container is handed back on <code>window:closed</code> and the host owns that lifecycle. UMD global <code>LatticeGridLayout</code>.</td></tr>
7068
+ <tr><td class="name">createLayout</td><td class="desc">Create a reconfigurable dashboard layout (module <code>layout</code>): a cell grid inside an element, and a set of windows on it that a user can move, resize and close by drag <em>or</em> by keyboard &mdash; the surface a customer would otherwise reach for GridStack to get. It is <strong>payload-agnostic</strong>: a window body is a <code>div</code> with an <code>id</code> that the module creates, sizes and never reads, so it imports no engine code at all (not even <code>createGrid</code>) and its own code is 12,890 bytes gzipped, measured against a 62,206-byte fixed bundle floor. <code>columns</code>/<code>rows</code> divide the element; <code>overflowX</code> and <code>overflowY</code> are <em>independent</em> axes, each <code>'static'</code> (tracks divide the container with <code>minmax(0, 1fr)</code>) or <code>'scroll'</code> (tracks take a fixed <code>columnWidth</code>/<code>rowHeight</code> and the canvas extends past the viewport, so a column keeps the size it asked for &mdash; measured: shrinking a 600px host to 300px leaves a 200px column at 200px). Spacing takes a real CSS length: a number of pixels, or <code>'200px'</code>, <code>'25%'</code>, <code>'1fr'</code>, <code>'2rem'</code>; anything else is refused by name and replaced by the default, because the value reaches an inline style. Windows are placed by 1-based <code>xPos</code>/<code>yPos</code>/<code>xSize</code>/<code>ySize</code>, or auto-placed in the first free cell; <code>chrome</code> defaults on, and <code>closable</code>/<code>movable</code>/<code>resizable</code> all default <em>off</em>, so a fixed dashboard is fixed without opting out. <code>compact: 'vertical'</code> pushes displaced windows down then pulls them up (<code>window:moved</code> carries both <code>to</code> and <code>landed</code>); <code>'none'</code> keeps every window where it is put. <code>closable</code>/<code>movable</code>/<code>resizable</code> each also take a <em>layout-level</em> default of the same name, which a window's own boolean overrides, and <code>setInteractive(true|false|{movable, resizable, closable})</code> changes that default at runtime &mdash; the &ldquo;Edit layout&rdquo; button &mdash; without destroying the layout or any payload in it, with <code>getInteractive()</code> reading it back. Locking always wins and unlocking never overrides an opt-out: <code>setInteractive(false)</code> pins a window that declared <code>movable: true</code>, while <code>setInteractive(true)</code> leaves a window that declared <code>movable: false</code> pinned. The config key and the method are deliberately different: <code>movable: false</code> in the config states the <em>default</em> for windows that declare nothing and takes nothing away from one that opted in, whereas <code>setInteractive(false)</code> is an <em>active lock</em>. <code>getInteractive()</code> is three-valued &mdash; <code>undefined</code> for unset, <code>true</code>, or <code>false</code> for a lock &mdash; and a key carrying <code>undefined</code> is treated as absent, so <code>setInteractive(getInteractive())</code> is a no-op in every state. It moves both halves of the enforcement, the rendered handles <em>and</em> the pointer and keyboard gesture checks, and fires no event because a mode is not an arrangement &mdash; <code>getLayout()</code> neither carries it nor restores it. A locked layout is not a read-only dashboard: what is inside a window is configured with that payload's own settings. <code>maximise(id)</code>, <code>minimise(id)</code> and <code>restore(id)</code> are the display modes, with <code>maximised()</code> and <code>minimised()</code> reading them back: maximise fills the <em>layout host</em> rather than the browser window (no <code>position: fixed</code>, whose containing block is the nearest ancestor with a <code>transform</code> or a <code>contain</code>; no reparenting; nothing that can disturb the page around the dashboard), hides the other windows, runs <strong>no compaction at all</strong> and keeps the payload container as the very same DOM node &mdash; and <kbd>Escape</kbd> restores it from anywhere inside the layout. <code>minimise</code> draws a window as a single row and hides its payload while its chrome stays to carry the way back, so the windows below pull up on screen; in the arrangement nothing moves at all, because the collapse is a projection of the dashboard rather than a change to it, so restoring gives back exactly the arrangement that was there in <em>any</em> order and with any number of other windows still collapsed. A <code>chrome: false</code> window is refused by name. Both controls are opt-in per window (<code>maximisable</code>, <code>minimisable</code>) with a layout-level default of the same name, and <code>setInteractive()</code> deliberately does not touch either: a mode is not an arrangement, so neither appears in <code>getLayout()</code>, which reports the underlying placement in both states. Keyboard parity with the drag: a focusable handle per window running the kanban board's grab/move/drop/cancel model, with a polite live region announcing grabbed, every tentative position, dropped, cancelled and reverted. It owns exactly <strong>one</strong> <code>ResizeObserver</code> for the whole layout, over two targets, and tells payloads their new content box through <code>window:resized</code> &mdash; it never calls into a payload, because it cannot know what one is. Events: <code>window:moved</code>, <code>window:resized</code>, <code>window:closed</code>, <code>layout:changed</code>, the cancellable <code>beforeWindowMove</code>/<code>beforeWindowResize</code>/<code>beforeWindowClose</code> and their <code>*:cancelled</code> pairs; drag progress is not emitted per frame. <code>getLayout()</code>/<code>setLayout()</code> round-trip the arrangement as plain JSON, and <code>getState()</code>/<code>setState()</code> are the versioned pair. Closing a window does <strong>not</strong> destroy its payload &mdash; the container is handed back on <code>window:closed</code> and the host owns that lifecycle. UMD global <code>LatticeGridLayout</code>.</td></tr>
6995
7069
  <tr><td class="name">createDevtools</td><td class="desc">Mount the devtools panel against a grid, including its accessibility checks.</td></tr>
6996
7070
  <tr><td class="name">createGantt</td><td class="desc">Create a project-planning Gantt controller (module <code>gantt</code>) over a task list and a dependency list. A CPM engine (<code>computeSchedule</code>) computes each task's early/late start and finish, its slack and the zero-float critical path, honouring the four link types (<code>LINK_TYPES</code>: FS/SS/FF/SF) with lag, and recomputes on every edit — emitting <code>schedule</code> or, on a dependency cycle or bad input, <code>error</code> (a code from <code>SCHEDULE_ERROR</code>). Milestones are zero-duration points; summary (WBS) tasks are derived from their children (earliest start, latest finish, weighted progress) rather than scheduled; <code>findViolations</code> flags a task placed earlier than its predecessors allow, and <code>toISODate</code> maps an engine day-number back to a calendar date.</td></tr>
6997
7071
  <tr><td class="name">createLatticeGridElement</td><td class="desc">Build the element class without registering it, for a custom registry.</td></tr>
@@ -7462,7 +7536,7 @@ grid.licence.state(); <span class="cmt">// 'licensed' | 'localhost' | 'trial'<
7462
7536
 
7463
7537
  <footer>
7464
7538
  <p>
7465
- Lattice Grid 1.7.1 · Copyright © 2026 TOCLOCO Inc. All rights reserved.
7539
+ Lattice Grid 1.55.0 · Copyright © 2026 TOCLOCO Inc. All rights reserved.
7466
7540
  Written against the shipped source. Where this guide and the code disagree, the code wins,
7467
7541
  please <a href="https://www.latticegrid.dev">tell us</a>.
7468
7542
  </p>
package/lattice-grid.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.53.0, type declarations
2
+ * Lattice Grid 1.55.0, type declarations
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -250,11 +250,6 @@ export interface NumberFormat {
250
250
  * leaves the remainder in English rather than showing raw keys.
251
251
  */
252
252
  messages?: Record<string, string | Record<string, string>>;
253
- /**
254
- * Writing direction. Omit to settle it from the element's own `dir` and then
255
- * from `locale`: `ar`, `he`, `fa` and the rest resolve to `rtl`.
256
- */
257
- direction?: 'ltr' | 'rtl';
258
253
  scale?: number;
259
254
  }
260
255
 
@@ -1747,6 +1742,14 @@ export interface GridConfig {
1747
1742
  /** Page the rows rather than scrolling them. */
1748
1743
  pagination?: PaginationConfig | boolean;
1749
1744
  locale?: string;
1745
+ /**
1746
+ * Writing direction. Omit it, or say `'auto'`, to settle it from the
1747
+ * element's own computed `dir` and then from `locale`: `ar`, `he`, `fa` and
1748
+ * the rest resolve to `rtl`. In a right-to-left grid the logical alignments
1749
+ * `start`/`end` mirror while the physical `left`/`right` do not (see
1750
+ * {@link Align}).
1751
+ */
1752
+ direction?: 'ltr' | 'rtl' | 'auto';
1750
1753
  /**
1751
1754
  * IANA zone every date column formats in, e.g. 'Europe/London' or 'UTC'.
1752
1755
  * Omit to use each viewer's own zone. A column's own `format.timeZone` wins.
@@ -4410,6 +4413,22 @@ export interface ColumnsApi {
4410
4413
  */
4411
4414
  decorate(id: string, decoration: DecorationName | DecorationSpec | null, opts?: { variant?: VariantSpec }): void;
4412
4415
  autoSize(ids?: string | string[]): void;
4416
+ /**
4417
+ * Size the visible resizable columns so that every column the grid draws,
4418
+ * together, exactly fills the width the cells occupy: the body viewport's
4419
+ * client width at the moment of the call, which excludes the vertical
4420
+ * scrollbar when the grid draws one and is the full inner width when it does
4421
+ * not. Columns it does not size keep their width and are taken out of that
4422
+ * width first: `resizable: false` columns and the grid's own selection
4423
+ * checkbox, detail expander, group and tree columns. The rest share what is
4424
+ * left in proportion to their current widths, within each `min`/`max`. If
4425
+ * that leaves less than their minimums, each is set to its minimum (never
4426
+ * below), the grid scrolls horizontally, and a `[lattice]` warning says so.
4427
+ * Rows given to `createGrid` or `rows.load()` before the call are counted.
4428
+ * One-shot: it sets fixed widths once (a `flex` column included) and does not
4429
+ * follow later changes; after a resize, or after rows arriving later bring a
4430
+ * vertical scrollbar in, call it again.
4431
+ */
4413
4432
  fit(): void;
4414
4433
  group(ids: string | string[]): void;
4415
4434
  pivot(ids: string | string[]): void;
@@ -5520,13 +5539,21 @@ export interface PaginationApi {
5520
5539
  /** What a cell-menu builder and a host item's `action` are handed. */
5521
5540
  export interface CellMenuParams {
5522
5541
  key: string;
5523
- colId: string;
5542
+ /**
5543
+ * The column under the pointer, or `null` when the row belongs to no column:
5544
+ * a right-click in the empty tail of a row beyond the last column
5545
+ * (BACKLOG-0001153), or on a group row, pivot group row or full-width row.
5546
+ * The grid-level menu stands in that case (BACKLOG-0001068).
5547
+ */
5548
+ colId: string | null;
5549
+ /** The cell's value; `undefined` when there is no column. */
5524
5550
  value: unknown;
5525
5551
  /** The row wrapper. */
5526
5552
  row: Row;
5527
5553
  /** Your original row object. */
5528
5554
  data: unknown;
5529
- column: ResolvedColumn;
5555
+ /** The resolved column; `undefined` when `colId` is `null`. */
5556
+ column: ResolvedColumn | undefined;
5530
5557
  index: number;
5531
5558
  grid: Grid;
5532
5559
  }
@@ -8715,14 +8742,17 @@ declare module 'lattice-grid/modules/kpi' {
8715
8742
  value: unknown;
8716
8743
  formatted: string;
8717
8744
  /**
8718
- * The tile's semantic band, or `unknown` when the panel holds no rows at
8719
- * all. `unknown` is decided from data presence before any threshold is
8745
+ * The tile's semantic band, or `unknown` when the tile measured nothing.
8746
+ * `unknown` is decided from data presence before any threshold is
8720
8747
  * consulted: an aggregation over nothing returns the identity of its
8721
8748
  * operation (`sum` and `count` return 0), and 0 is a number a threshold
8722
- * grades, so without it an empty panel would report as a healthy one. A
8723
- * tile whose `filter` matches none of the rows the panel *does* hold has
8724
- * measured a real zero and is banded normally. `null` means the tile has no
8725
- * thresholds or bands configured.
8749
+ * grades, so without it an empty panel would report as a healthy one.
8750
+ *
8751
+ * Two things make a tile `unknown`: the panel holds no rows at all, or the
8752
+ * tile's `field` names no column on the bound grid, so it never read a cell
8753
+ * to reduce over. A tile whose `filter` matches none of the rows the panel
8754
+ * *does* hold is neither — it has measured a real zero and is banded
8755
+ * normally. `null` means the tile has no thresholds or bands configured.
8726
8756
  */
8727
8757
  status: 'good' | 'warn' | 'critical' | 'unknown' | null;
8728
8758
  target?: number;
@@ -8746,6 +8776,14 @@ declare module 'lattice-grid/modules/kpi' {
8746
8776
  rows?: KPIRow[];
8747
8777
  grid?: unknown;
8748
8778
  rowKey?: string | ((row: KPIRow) => unknown);
8779
+ /**
8780
+ * Extra columns of the bound `grid` to project onto the rows a tile `filter`
8781
+ * sees, beyond the fields the tiles themselves declare. A grid-bound panel
8782
+ * hands a filter a projection, not a whole grid row, so a filter over a
8783
+ * column no tile names would otherwise read `undefined` and report a
8784
+ * confident zero. Ignored on a panel over a plain `rows` array.
8785
+ */
8786
+ fields?: string[];
8749
8787
  tiles?: KPITile[];
8750
8788
  columns?: number;
8751
8789
  ariaLabel?: string;
@@ -9348,6 +9386,21 @@ declare module 'lattice-grid/modules/layout' {
9348
9386
  movable?: boolean;
9349
9387
  /** Whether the window can be resized by drag or keyboard (default `false`). */
9350
9388
  resizable?: boolean;
9389
+ /**
9390
+ * Whether to offer a maximise control in the chrome (default `false`).
9391
+ *
9392
+ * Maximising fills the **layout host**, not the browser window, and hides
9393
+ * every other window for the duration. Escape restores it, unless a payload
9394
+ * has already claimed the key.
9395
+ */
9396
+ maximisable?: boolean;
9397
+ /**
9398
+ * Whether to offer a minimise control in the chrome (default `false`).
9399
+ *
9400
+ * A window with `chrome: false` cannot be minimised whatever this says:
9401
+ * there would be nothing left on screen to restore it with.
9402
+ */
9403
+ minimisable?: boolean;
9351
9404
  /** Padding inside the window; the layout's `padding` (default `'5px'`) otherwise. */
9352
9405
  padding?: number | string;
9353
9406
  /** The `id` given to the payload container (default `` `${id}-body` ``). */
@@ -9469,6 +9522,16 @@ declare module 'lattice-grid/modules/layout' {
9469
9522
  resizable?: boolean;
9470
9523
  /** The default `closable` for windows that declare none (default `false`); see `movable`. */
9471
9524
  closable?: boolean;
9525
+ /**
9526
+ * The default `maximisable` for windows that declare none (default `false`).
9527
+ *
9528
+ * Not touched by `setInteractive()`: a display mode neither moves nor resizes
9529
+ * a window in the arrangement, so a locked dashboard can still be blown up
9530
+ * to read.
9531
+ */
9532
+ maximisable?: boolean;
9533
+ /** The default `minimisable` for windows that declare none (default `false`); see `maximisable`. */
9534
+ minimisable?: boolean;
9472
9535
  /** The windows, in mount order. */
9473
9536
  windows?: LayoutWindow[];
9474
9537
  /** An arrangement to apply at mount, as produced by `getLayout()`. */
@@ -9513,7 +9576,51 @@ declare module 'lattice-grid/modules/layout' {
9513
9576
  move(id: string, to: Partial<LayoutPlacement>): boolean | Promise<boolean>;
9514
9577
  /** Close a window through `beforeWindowClose`; the payload is not destroyed. */
9515
9578
  close(id: string): boolean | Promise<boolean>;
9516
- /** The full current arrangement. */
9579
+ /**
9580
+ * Blow one window up to fill the layout host, hiding the rest.
9581
+ *
9582
+ * It fills the **host element**, not the browser window, so there is no
9583
+ * `position: fixed` (whose containing block is the nearest ancestor carrying
9584
+ * a `transform` or a `contain`, which is why the same rule fills the screen
9585
+ * on one page and lands in a 300px box on the next), no reparenting and
9586
+ * nothing that can disturb the page around the dashboard.
9587
+ *
9588
+ * **Nothing moves**: no compaction runs, no placement changes, and the
9589
+ * payload container is the same DOM node throughout. **Escape restores it**,
9590
+ * from anywhere inside the layout — a focused grid body cell or column
9591
+ * heading included — unless a payload has already claimed the key: an open
9592
+ * cell editor, filter menu or column menu closes first, and the next Escape
9593
+ * restores the window. Afterwards focus lands on the window's maximise
9594
+ * control. A minimised window is expanded first, and maximising a second
9595
+ * window restores the first.
9596
+ */
9597
+ maximise(id: string): boolean;
9598
+ /**
9599
+ * Collapse one window to a single row: its payload is hidden and its chrome
9600
+ * stays, carrying the control that brings it back.
9601
+ *
9602
+ * On screen it becomes one row and the windows below pull up into the space
9603
+ * under `compact: 'vertical'`. In the arrangement nothing moves at all — the
9604
+ * collapse is a projection of it — so `restore()` gives back exactly the
9605
+ * arrangement that was there, in **any** order and with any number of other
9606
+ * windows still collapsed.
9607
+ *
9608
+ * A window with `chrome: false` is refused, with a warning naming it.
9609
+ */
9610
+ minimise(id: string): boolean;
9611
+ /** Leave whichever display mode a window is in; `false` when it was in none. */
9612
+ restore(id: string): boolean;
9613
+ /** The id of the window filling the host, or `null`. At most one. */
9614
+ maximised(): string | null;
9615
+ /** The ids of every currently minimised window, in mount order. */
9616
+ minimised(): string[];
9617
+ /**
9618
+ * The full current arrangement.
9619
+ *
9620
+ * **A mode is not an arrangement**: this reports the *underlying* placement
9621
+ * of a maximised or minimised window — where it will be when restored — never
9622
+ * the geometry it is drawn at.
9623
+ */
9517
9624
  getLayout(): LayoutSnapshot;
9518
9625
  /** Restore an arrangement; never throws on garbage. */
9519
9626
  setLayout(incoming: LayoutSnapshot | LayoutWindow[]): number;