@n-uf/placement 26.11.8 → 26.11.10

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
@@ -3,6 +3,61 @@
3
3
  Versions are calendar versions, `YY.M.R` (year, month of the release, counter within the month), shared with
4
4
  `@n-uf/motion`: both packages carry the same version, and a release bumps both.
5
5
 
6
+ ## 26.11.10
7
+
8
+ - `@n-uf/placement/panels`, bands and reserved room (all opt-in; a state and options without them lay out as before,
9
+ and the store stays v1):
10
+ - Bands: a top and a bottom dock (`options.bands`, `{ top?, bottom? }`, each `{ mode: "reserved" | "overlay",
11
+ span: "between" | "full" }`). Its panels sit side by side and share its width (`kind` `"band"`, `edge`, `index`);
12
+ a stored height clamped to `PanelDef.band` (default `BAND_SIZE`, min 120, max 480, natural 200). It folds as a
13
+ whole, to one bar. `layout.bands` (`PlacedBand`). New: `Edge`, `EDGES`, `Place`, `Band`, `BandOption`,
14
+ `PanelsState.bands`, `PanelsStart.top` / `bottom`, `dockBand`, `resizeBand`, `bandSize`, `bandHeight`;
15
+ `open(state, id, { edge, height? })`; `Where` and `PanelPlace` gain a `band` kind; `PanelDef.sides` may name
16
+ edges.
17
+ - Height give-way for bands: `options.pagesMinHeight` (default 240). Reserved bands shrink, then fold to a bar,
18
+ unkept before kept, policy order, bottom first on a tie, until the pages keep the min; then the overlay bands,
19
+ together, until they leave the min of the pages uncovered. Notes `band-shrunk`, `band-folded`, `kept-folded` with
20
+ `edge`; `pages-short` when the pages' rect is still under the min (overlay bands never cause it); `band-off` for a
21
+ band on an edge the options don't enable.
22
+ - Reserved room: `options.reserve` (`{ top?, right?, bottom?, left? }`) keeps px clear along the pages' edges for an
23
+ app's toolbar; `layout.reserved` has the rects. `pagesMin` and `pagesMinHeight` count after it. An overlay band
24
+ lies next to the reserve on its edge, on the pages' side, never over it.
25
+ - Targets `band:<edge>:<i>` and `band:<edge>` (an empty enabled edge). The stored form carries `bands` (an optional
26
+ v1 field).
27
+ - Typing: `PanelKind`, `PanelPlace`, `Where` and `PanelsNote` gain members, and `@n-uf/placement/desk`'s `PresetId`
28
+ gains `"docksFull"` (so `PRESETS` has a fourth key), so an exhaustive `switch` over them, or a `Record` keyed by
29
+ `PresetId`, in a consumer needs the new cases.
30
+ - `@n-uf/placement/desk`: `docks` and `dockTear` get a bottom band (reserved, between the columns); a new preset
31
+ `docksFull` (the bottom band full width). `DeskHint.bottom` starts the band; else the last def whose `sides` names
32
+ `"bottom"`. `navigator` has no band. Because the band is enabled, `docks` and `dockTear` now always offer a
33
+ `band:bottom` drop strip along the pages' bottom, even when the band is empty (a drop there used to be a float in
34
+ `dockTear`, and no drop in `docks`).
35
+ - Known limit: with `pagesMinHeight` below about 100 px on a very short stage, a column can be shorter than its title
36
+ bars, which then run past it (column bars were always a fixed `options.bar` tall). Keep the default 240, or at least
37
+ a few bars' height, when bands are on.
38
+
39
+ ## 26.11.9
40
+
41
+ - `@n-uf/placement/panels`:
42
+ - Tabs: `Column.show` (`"stack"`, the default, or `"tabs"`) and `Column.active`. A tabbed column draws a strip
43
+ `options.tabs` tall (new chrome size, default 28; `PlacedColumn.strip`); its active panel takes the rest and the
44
+ others are `hidden`, each with its `tab` rect in the strip. It folds as a whole: its active panel folded shows only
45
+ the strip, every panel folded goes to the rail (or rides). `showTabs(state, side, index, on)`, `activate(state, id)`
46
+ (a use). A drop `into` a tabbed column adds a tab at the end and shows it, and so does `open` on a panel in one; a
47
+ panel that leaves takes its `active` mark with it. With no `active` (or one that isn't there), the column shows its
48
+ first panel that isn't closed.
49
+ - Height give-way, opt-in: `PanelDef.height` (`{ min, natural }`). In a stack column where an open section has one,
50
+ sections get their natural heights and the last open one the rest (never under its min); sections without one
51
+ share what is left, never under a bar's height. The others' natural heights give way toward their mins first;
52
+ still short of room, open sections fold to bars in give-way policy order (kept last, never the last open one, never
53
+ one whose min is no more than a bar), note `section-folded`. It never writes `folded`. Columns without `height`
54
+ defs are unchanged.
55
+ - The stored form carries a column's `show` and `active` (optional v1 fields); an `active` that isn't in its column
56
+ is dropped as `"column.active:<id>"`, an unknown `show` as `"column.show"`.
57
+ - New entry `@n-uf/placement/desk`: presets as data on `./panels` (`docks`, `dockTear`, `navigator`; `PRESETS`,
58
+ `presetLayout`, `presetTargets`, `presetDrop`, `presetInput`). It imports only `./panels`' public index (a guard test
59
+ holds it to that) and carries no titles, words or toolbar wiring. `./dock` is unchanged.
60
+
6
61
  ## 26.11.8
7
62
 
8
63
  - `@n-uf/placement/panels`:
package/README.md CHANGED
@@ -34,9 +34,11 @@ without importing each other: a target list from `@n-uf/placement/targets` is ex
34
34
  - **`/targets`**: drop targets (`{ id, priority, hit, slot?, label?, free? }`) for the desk's constructions,
35
35
  toolbar hosts (each with its `slot`, and `allows` to leave out places a group may not take) and pane zones; and
36
36
  `keyboardStops`, one ordered list of places for the arrow keys and a "Move to" menu.
37
- - **`/panels`**: tool panels with string ids: full-height columns per side, each with its own width; folding to a
38
- title bar or a rail; give-way by age when the pages run out of room; resizable floats, some pinned to the view;
39
- their drop targets; and a stored form.
37
+ - **`/panels`**: tool panels with string ids: full-height columns per side, each with its own width, shown as a
38
+ stack of sections or as tabs; folding to a title bar or a rail; give-way by age when the pages run out of room (and,
39
+ opt-in, when a column runs out of height); resizable floats, some pinned to the view; their drop targets; and a
40
+ stored form.
41
+ - **`/desk`**: desk presets (`docks`, `dockTear`, `navigator`) as data on `/panels`: options and a start state.
40
42
 
41
43
  Rendering stays in the app: it draws the rects, measures its groups, and feeds pointer samples to a drag intent.
42
44
 
@@ -210,9 +212,96 @@ gain its room), and its stored place and width are untouched, so unfolding puts
210
212
  every column on a side is folded does the side show its rail. Give-way's folds (`column-folded`, `kept-folded`) go to
211
213
  the rail in both modes.
212
214
 
215
+ **Tabs**: a column with `show: "tabs"` shows one panel at a time, its `active` one (default, or when `active` isn't
216
+ one of them: the first panel in the column that isn't closed), under a strip
217
+ `options.tabs` tall (default 28). The strip is `PlacedColumn.strip`, and each panel's `tab` is its header's rect in it
218
+ (panel order, equal widths); the others are `hidden`. A tabbed column folds as a whole: with its active panel in
219
+ `folded` it shows only its strip (the active panel is a `"bar"` there); with every panel folded it goes to the rail,
220
+ or rides, as a stack column does. `showTabs(state, side, index, on)` switches a column; `activate(state, id)` shows a
221
+ tab and counts as a use. A drop `into` a tabbed column adds a tab at the end and shows it, and `open` on a panel in a
222
+ tabbed column shows its tab too.
223
+
224
+ **Section heights** (opt-in): a `PanelDef.height` (`{ min, natural }`) gives a section of a stack column its natural
225
+ height; the column's last open section takes the rest, never under its min, and sections without one share what is
226
+ left equally, never under a bar's height (`options.bar`). The others' natural heights give way toward their mins
227
+ first. When the open sections' mins and the bars still don't fit, open sections fold to bars by the give-way policy
228
+ (kept last, never the last open one, and never one whose min is no more than a bar, since folding it gains nothing),
229
+ each with a `section-folded` note; that never writes `folded`, so a taller stage unfolds them. Columns without
230
+ `height` defs lay out as before.
231
+
213
232
  **Changes** are pure: `open`, `close`, `focus`, `toggleFold`, `keep`, `dock(state, id, side, index, { into?, width? })`,
214
233
  `float`, `pin(state, id, on, view?, shown?)` (pass the layout's rect as `shown` so a clamped float pins where it
215
- shows), `resizeColumn`, `resizeFloat`.
234
+ shows), `resizeColumn`, `resizeFloat`, `showTabs`, `activate`, and for bands `dockBand`, `resizeBand`.
235
+
236
+ ### Bands
237
+
238
+ A band is a horizontal dock along the stage's top or bottom edge (opt-in: `options.bands`, default none). Its panels
239
+ sit side by side, left to right, and share its width equally; it has one stored height, clamped to its panels' limits
240
+ (`PanelDef.band`, default `BAND_SIZE`: min 120, max 480, natural 200; the largest min and the smallest max win, as a
241
+ column's width does). Each enabled edge says how its band lies:
242
+
243
+ - `mode: "reserved"` shortens the pages; `"overlay"` lies over the pages' upper or lower part (layer 5, over columns'
244
+ layer 0 and under floats) and leaves the pages' rect as it is.
245
+ - `span: "full"` takes its height from the stage first, across the whole width, and the columns and rails stand on it
246
+ (or hang from it); `"between"` runs between the innermost open column (or rail) on each side.
247
+
248
+ A band's open panels are `kind` `"band"` (with `edge` and `index`); `layout.bands` has each band as laid out
249
+ (`PlacedBand`: `rect`, `height`, `min`, `max`, `folded`, `shrunk`, `kept`, `mode`, `span`, `panels`). It folds as a
250
+ whole: `toggleFold` on any of its panels folds or unfolds them all, and a folded band is one bar, `options.bar` tall,
251
+ each panel a `"bar"` side by side. `keep` works on band panels.
252
+
253
+ **Height give-way**: when the pages would be shorter than `pagesMinHeight` (default 240; counted after the top and
254
+ bottom reserves), reserved bands shrink toward their min, then fold to a bar, unkept before kept, in the give-way
255
+ policy's order, bottom before top on a tie (`band-shrunk`, `band-folded`, or `kept-folded` with `edge`); they leave the
256
+ pages room for the overlay bands' bars at least. Then the overlay bands give way the same way, together, until they leave
257
+ `pagesMinHeight` of the pages uncovered, so a top and a bottom one never meet. Give-way never writes `folded`, so a
258
+ taller stage brings a band back. If the pages' rect is still under the min: `pages-short` (overlay bands don't change
259
+ the pages' rect, so they never cause it; how far one covers the pages is its own `band-shrunk` or `band-folded`).
260
+ Columns never give way by height to a band.
261
+
262
+ `dockBand(state, id, edge, index, defs?)` puts a panel into a band (0 the leftmost, past the end the rightmost; a new
263
+ band takes the panel's natural band height); `open(state, id, { edge, height? })` puts a panel placed nowhere at a
264
+ band's right end; `liftPanel`, `dock` and `float` take a panel out, and an emptied band goes from the state.
265
+ `createPanels` takes `top` and `bottom` (the clock counts left, right, top, bottom, floats). A band in the state on an
266
+ edge the options don't enable is left out of `items` (`band-off`). A def's `sides` may name edges: without `sides` a
267
+ panel may go to every enabled edge; with them, only where they say. The drop targets add `band:<edge>:<i>` (the slots
268
+ at a band's ends and between its panels, the band split at its panels' middles) and, on an empty enabled edge,
269
+ `band:<edge>` (a strip `2 × bar` tall along it inside the pages); a drop's `slot` is the panel's share of the band.
270
+
271
+ ```ts
272
+ import { createPanels, layoutPanels, panelTargets, type PanelDef } from "@n-uf/placement/panels";
273
+
274
+ const defs: PanelDef[] = [
275
+ { id: "files", width: { min: 180, natural: 220, max: 360 } },
276
+ { id: "history", width: { min: 200, natural: 240, max: 360 }, band: { min: 100, max: 400, natural: 180 } },
277
+ ];
278
+ const state = createPanels(defs, { left: [["files"]], bottom: ["history"] });
279
+ const options = { bands: { bottom: { mode: "reserved", span: "between" } } } as const;
280
+ const tall = layoutPanels(defs, state, { width: 1200, height: 700 }, options);
281
+ console.log(tall.bands.bottom!.rect, tall.pages); // { x: 220, y: 520, width: 980, height: 180 } { x: 220, y: 0, width: 980, height: 520 }
282
+ const short = layoutPanels(defs, state, { width: 1200, height: 300 }, options);
283
+ console.log(short.notes.map((n) => n.code), short.items.get("history")!.kind); // [ 'band-folded' ] bar
284
+ const targets = panelTargets({ defs, state, id: "files", stage: { width: 1200, height: 700 }, options });
285
+ console.log(targets.filter((t) => t.place.kind === "band").map((t) => t.id)); // [ 'band:bottom:0', 'band:bottom:1' ]
286
+ ```
287
+
288
+ ### Reserved room
289
+
290
+ `options.reserve` (`{ top?, right?, bottom?, left? }`, px, default none) keeps room clear along the pages' edges for a
291
+ toolbar the app draws; `./toolbar` stays the toolbar's model, and placement neither draws nor names it. The reserve
292
+ comes off the pages' area after the columns and bands: `layout.reserved.top` is the pages' full width and `reserve.top`
293
+ tall at their top (bottom likewise), and the left and right reserves are as tall as what the top and bottom reserves
294
+ (and overlay bands) leave. `pagesMin` counts after the left and right reserves, `pagesMinHeight` after the top and
295
+ bottom ones; a reserve never shrinks or folds. An overlay band lies next to the reserve on its edge, on the pages' side, never over it.
296
+
297
+ ```ts
298
+ // The defs and state above; without band options the band is off (a `band-off` note).
299
+ const l = layoutPanels(defs, state, { width: 1200, height: 700 }, { reserve: { top: 40 } });
300
+ console.log(l.reserved.top, l.pages); // { x: 220, y: 0, width: 980, height: 40 } { x: 220, y: 40, width: 980, height: 660 }
301
+ ```
302
+
303
+ Without band options, no `bands` in the state and no reserve, every layout, target and note is what it was before
304
+ bands.
216
305
 
217
306
  **Drop targets**: `panelTargets({ defs, state, id, stage, options?, allows? })` gives `column:<side>:<i>` (a new
218
307
  column as wide as the panel's own width), `into:<side>:<i>` (stack into a column), `rail:<side>` (dock folded) and
@@ -243,17 +332,64 @@ console.log(stops.map((s) => (s.home ? "home" : s.id))); // [ 'rail:left', 'colu
243
332
  parsed data back and never throws. Anything but an object of version 1 is `{ ok: false, error }`; inside one, unknown
244
333
  or repeated ids and malformed entries are dropped and listed in `dropped`, and widths and float sizes are clamped to
245
334
  the defs. Version 1 only gains optional fields: a field this reader doesn't know is skipped and listed in `dropped`
246
- too (`"theme"`, `"sides.bottom"`, `"column.<field>"`, `"float.<field>"`, `"clock.<id>"`). The note codes
247
- (`PanelsNoteCode`):
335
+ too (`"theme"`, `"sides.bottom"`, `"column.<field>"`, `"float.<field>"`, `"clock.<id>"`). A column's `show` and
336
+ `active` are carried; an `active` that isn't one of the column's panels is dropped as `"column.active:<id>"`. `bands`
337
+ is carried too, only when the state has one: an unknown edge is dropped as `"bands.<key>"`, a band without a panels
338
+ list or a numeric height (or whose panels are all dropped) as `"bands.<edge>"`, an unknown field in one as
339
+ `"band.<field>"`, and heights are clamped to the defs. The note codes (`PanelsNoteCode`):
248
340
 
249
341
  | Code | Says | Also |
250
342
  |---|---|---|
251
343
  | `column-shrunk` | a column narrowed toward its minimum | `side`, `column`, `items`, `from`, `to` |
252
344
  | `column-folded` | a column folded to its side's rail | `side`, `column`, `items` |
253
345
  | `kept-folded` | a kept column folded: the pages needed the room | `side`, `column`, `items` |
346
+ | `section-folded` | a section of a stack column folded to a bar: the column was short | `side`, `column`, `items` |
254
347
  | `pages-under-min` | the pages are under `pagesMin` with every column folded | `width`, `min` |
255
348
  | `float-resized` | a float shrank to fit the view | `items` |
256
349
  | `float-clamped` | a float moved to stay in the view | `items` |
350
+ | `band-shrunk` | a band lowered toward its min: the pages needed the height | `edge`, `items`, `from`, `to` |
351
+ | `band-folded` | a band folded to a bar: the pages needed the height | `edge`, `items` |
352
+ | `kept-folded` | (on a band) a kept band folded, last | `edge`, `items` |
353
+ | `band-off` | a band on an edge the options don't enable: its panels aren't laid out | `edge`, `items` |
354
+ | `pages-short` | the pages' rect is under `pagesMinHeight` with every reserved band folded (or by the reserves alone) | `height`, `min` |
355
+
356
+ ## Desk presets
357
+
358
+ `@n-uf/placement/desk` holds desk presets as data on `./panels`: each is panels options and a start state. It imports
359
+ only the panels entry, carries no titles or words and wires no toolbar; the app names the presets and draws them.
360
+
361
+ | Preset | Columns, bands and floats |
362
+ |---|---|
363
+ | `docks` | one stack column per side, sections at their natural heights when the defs give `height`, folding to the rail; a bottom band between the columns (reserved), folding to a bar on a short stage; no floats |
364
+ | `docksFull` | as `docks`, with the bottom band full width: the columns stand on it |
365
+ | `dockTear` | as `docks` (with its bottom band), and panels may float (tear-off and dock-back); one panel floats at the start |
366
+ | `navigator` | a tabbed column on the left, a stack column on the right; no band, no floats |
367
+
368
+ The bottom band starts with the hint's `bottom` panels (`start(defs, { bottom: [...] })`), else with the last def whose
369
+ `sides` names `"bottom"`; a def without `sides` may be dropped into the band but doesn't start there. `presetTargets`
370
+ and `presetDrop` include the band's targets.
371
+
372
+ ```ts
373
+ import { PRESETS, presetLayout, presetTargets } from "@n-uf/placement/desk";
374
+ import type { PanelDef } from "@n-uf/placement/panels";
375
+
376
+ const defs: PanelDef[] = [
377
+ { id: "files", width: { min: 200, natural: 240, max: 360 }, height: { min: 80, natural: 160 } },
378
+ { id: "outline", width: { min: 200, natural: 240, max: 360 }, height: { min: 80, natural: 200 } },
379
+ { id: "history", width: { min: 200, natural: 240, max: 360 } },
380
+ ];
381
+ const nav = PRESETS.navigator;
382
+ const state = nav.start(defs, { left: ["files", "outline"] });
383
+ const layout = presetLayout(nav, defs, state, { width: 1200, height: 600 });
384
+ console.log(layout.columns[0]!.strip, layout.items.get("outline")!.hidden); // { x: 0, y: 0, width: 240, height: 28 } true
385
+ const targets = presetTargets(nav, { defs, state, id: "history", stage: { width: 1200, height: 600 } });
386
+ console.log(targets.some((t) => t.id === "float")); // false
387
+ ```
388
+
389
+ `presetLayout(preset, defs, state, stage, options?)` is `layoutPanels` with the preset's options under the caller's;
390
+ `presetTargets(preset, input)` and `presetDrop(preset, input, target, point, grab)` are `panelTargets` and `panelDrop`
391
+ the same way, offering only the places the preset allows (`presetInput` gives that input). `./dock` stays as it is for
392
+ now.
257
393
 
258
394
  ## Pane zones
259
395
 
@@ -0,0 +1,172 @@
1
+ import {
2
+ EDGES,
3
+ SIDES,
4
+ bandSize,
5
+ defOf,
6
+ floatSize
7
+ } from "./chunk-PJD5HKYX.js";
8
+
9
+ // src/panels/store.ts
10
+ function storePanels(state) {
11
+ const column = (c) => ({
12
+ panels: [...c.panels],
13
+ width: c.width,
14
+ ...c.show === "tabs" ? { show: "tabs" } : {},
15
+ ...c.active !== void 0 ? { active: c.active } : {}
16
+ });
17
+ const sides = Object.fromEntries(SIDES.map((s) => [s, state.sides[s].map(column)]));
18
+ const bands = {};
19
+ for (const e of EDGES) {
20
+ const b = state.bands?.[e];
21
+ if (b) bands[e] = { panels: [...b.panels], height: b.height };
22
+ }
23
+ return {
24
+ v: 1,
25
+ sides,
26
+ ...EDGES.some((e) => bands[e]) ? { bands } : {},
27
+ floats: state.floats.map((f) => ({ id: f.id, rect: { x: f.rect.x, y: f.rect.y, width: f.rect.width, height: f.rect.height }, pinned: !!f.pinned })),
28
+ folded: [...state.folded],
29
+ kept: [...state.kept],
30
+ closed: [...state.closed],
31
+ clock: Object.fromEntries(Object.entries(state.clock).map(([k, c]) => [k, { opened: c.opened, used: c.used }]))
32
+ };
33
+ }
34
+ var isObject = (x) => typeof x === "object" && x !== null && !Array.isArray(x);
35
+ var finite = (x) => typeof x === "number" && Number.isFinite(x);
36
+ function readPanels(data, defs) {
37
+ try {
38
+ return read(data, defs);
39
+ } catch (e) {
40
+ return { ok: false, error: `unreadable: ${e instanceof Error ? e.message : typeof e}` };
41
+ }
42
+ }
43
+ function read(data, defs) {
44
+ if (!isObject(data)) return { ok: false, error: "not an object" };
45
+ if (data.v !== 1) return { ok: false, error: `unknown version ${typeof data.v === "number" || typeof data.v === "string" ? String(data.v) : typeof data.v} (this reads version 1)` };
46
+ if (!isObject(data.sides)) return { ok: false, error: "sides is missing" };
47
+ const dropped = [];
48
+ const drop = (what) => {
49
+ if (!dropped.includes(what)) dropped.push(what);
50
+ };
51
+ const seen = /* @__PURE__ */ new Set();
52
+ const take = (x) => {
53
+ if (typeof x !== "string") {
54
+ drop(`a ${typeof x} where an id goes`);
55
+ return null;
56
+ }
57
+ if (defs && !defOf(defs, x) || seen.has(x)) {
58
+ drop(x);
59
+ return null;
60
+ }
61
+ seen.add(x);
62
+ return x;
63
+ };
64
+ const extra = (o, known, prefix) => {
65
+ for (const k of Object.keys(o)) if (!known.includes(k)) drop(`${prefix}${k}`);
66
+ };
67
+ extra(data, ["v", "sides", "bands", "floats", "folded", "kept", "closed", "clock"], "");
68
+ extra(data.sides, SIDES, "sides.");
69
+ const sides = { left: [], right: [] };
70
+ for (const side of SIDES) {
71
+ const cols = data.sides[side];
72
+ if (cols === void 0) continue;
73
+ if (!Array.isArray(cols)) {
74
+ drop(`sides.${side}`);
75
+ continue;
76
+ }
77
+ for (const c of cols) {
78
+ if (!isObject(c) || !Array.isArray(c.panels)) {
79
+ drop(`a column on the ${side}`);
80
+ continue;
81
+ }
82
+ extra(c, ["panels", "width", "show", "active"], "column.");
83
+ const panels = c.panels.map(take).filter((x) => x !== null);
84
+ if (panels.length === 0) continue;
85
+ const ds = defs ? panels.map((id) => defOf(defs, id)) : [];
86
+ const lo = Math.max(0, ...ds.map((d) => d.width.min));
87
+ const hi = Math.max(lo, Math.min(...ds.map((d) => d.width.max)));
88
+ const natural = ds.length ? Math.max(...ds.map((d) => d.width.natural)) : 0;
89
+ const width = finite(c.width) ? c.width : natural;
90
+ if (c.show !== void 0 && c.show !== "tabs" && c.show !== "stack") drop("column.show");
91
+ if (c.active !== void 0 && !(typeof c.active === "string" && panels.includes(c.active))) drop(`column.active:${typeof c.active === "string" ? c.active : typeof c.active}`);
92
+ const tabs = c.show === "tabs" ? { show: "tabs" } : {};
93
+ const active = typeof c.active === "string" && panels.includes(c.active) ? { active: c.active } : {};
94
+ sides[side].push({ panels, width: defs ? Math.min(Math.max(width, lo), hi) : Math.max(0, width), ...tabs, ...active });
95
+ }
96
+ }
97
+ const bands = {};
98
+ if (data.bands !== void 0) {
99
+ if (!isObject(data.bands)) drop("bands");
100
+ else {
101
+ extra(data.bands, EDGES, "bands.");
102
+ for (const e of EDGES) {
103
+ const b = data.bands[e];
104
+ if (b === void 0) continue;
105
+ if (!isObject(b) || !Array.isArray(b.panels) || !finite(b.height)) {
106
+ drop(`bands.${e}`);
107
+ continue;
108
+ }
109
+ extra(b, ["panels", "height"], "band.");
110
+ const panels = b.panels.map(take).filter((x) => x !== null);
111
+ if (panels.length === 0) {
112
+ drop(`bands.${e}`);
113
+ continue;
114
+ }
115
+ const ds = defs ? panels.map((id) => bandSize(defOf(defs, id))) : [];
116
+ const lo = Math.max(0, ...ds.map((d) => d.min));
117
+ const hi = Math.max(lo, Math.min(...ds.map((d) => d.max)));
118
+ bands[e] = { panels, height: defs ? Math.min(Math.max(b.height, lo), hi) : Math.max(0, b.height) };
119
+ }
120
+ }
121
+ }
122
+ const floats = [];
123
+ for (const f of Array.isArray(data.floats) ? data.floats : []) {
124
+ const r = isObject(f) ? f.rect : void 0;
125
+ if (!isObject(f) || !isObject(r) || !finite(r.x) || !finite(r.y) || !finite(r.width) || !finite(r.height)) {
126
+ drop(isObject(f) && typeof f.id === "string" ? f.id : "a float");
127
+ continue;
128
+ }
129
+ extra(f, ["id", "rect", "pinned"], "float.");
130
+ const id = take(f.id);
131
+ if (id === null) continue;
132
+ const min = defs ? floatSize(defOf(defs, id)).min : { width: 0, height: 0 };
133
+ floats.push({ id, rect: { x: r.x, y: r.y, width: Math.max(min.width, r.width), height: Math.max(min.height, r.height) }, ...f.pinned === true ? { pinned: true } : {} });
134
+ }
135
+ if (data.floats !== void 0 && !Array.isArray(data.floats)) drop("floats");
136
+ const placed = (key) => {
137
+ const x = data[key];
138
+ if (x === void 0) return [];
139
+ if (!Array.isArray(x)) {
140
+ drop(key);
141
+ return [];
142
+ }
143
+ const out = [];
144
+ for (const id of x) {
145
+ if (typeof id === "string" && seen.has(id)) {
146
+ if (!out.includes(id)) out.push(id);
147
+ } else drop(typeof id === "string" ? id : `a ${typeof id} in ${key}`);
148
+ }
149
+ return out;
150
+ };
151
+ const floating = new Set(floats.map((f) => f.id));
152
+ const kept = placed("kept").filter((id) => {
153
+ if (floating.has(id)) drop(id);
154
+ return !floating.has(id);
155
+ });
156
+ const clock = {};
157
+ if (isObject(data.clock)) {
158
+ for (const id of Object.keys(data.clock)) if (!seen.has(id)) drop(`clock.${id}`);
159
+ for (const id of seen) {
160
+ const c = data.clock[id];
161
+ clock[id] = isObject(c) && finite(c.opened) && finite(c.used) ? { opened: c.opened, used: c.used } : { opened: 0, used: 0 };
162
+ }
163
+ }
164
+ const state = { sides, floats, folded: placed("folded"), kept, closed: placed("closed"), clock };
165
+ return { ok: true, state: EDGES.some((e) => bands[e]) ? { ...state, bands } : state, dropped };
166
+ }
167
+
168
+ export {
169
+ storePanels,
170
+ readPanels
171
+ };
172
+ //# sourceMappingURL=chunk-7R5H2ABL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/panels/store.ts"],"sourcesContent":["/**\n * The panels' stored form: `storePanels(state)` gives plain JSON-safe data (`{ v: 1, … }`); `readPanels(data, defs?)`\n * reads parsed data back and never throws. As with the toolbar's `readArrangement`: anything but an object of\n * version 1 is `{ ok: false, error }`. Inside one, it is lenient: an unknown id (with `defs`), a repeated one or a\n * malformed entry is dropped and listed in `dropped`; widths and float sizes are clamped to the defs. A panel the\n * consumer has but the stored state lacks is no error: the consumer places it (`open` with a place, say).\n *\n * Version 1 only gains optional fields. A field this reader doesn't know (a newer writer's, say) is no error either:\n * it is skipped and reported in `dropped` (`\"<field>\"` at the top, `\"sides.<key>\"`, `\"column.<field>\"`,\n * `\"float.<field>\"`; a clock entry for a panel that isn't placed as `\"clock.<id>\"`), so a consumer can tell that\n * something was left out. A column's `show` (\"tabs\") and `active` are such optional fields: an unknown `show` is\n * dropped as `\"column.show\"`, an `active` that isn't one of the column's panels as `\"column.active:<id>\"`.\n *\n * `bands` (26.11.10) is such an optional field too: `{ top?, bottom? }`, each `{ panels, height }`. An unknown edge is\n * dropped as `\"bands.<key>\"`, a band that isn't an object with a panels list and a numeric height (or whose panels are\n * all dropped) as `\"bands.<edge>\"`, an unknown field in one as `\"band.<field>\"`. Heights are clamped to the defs.\n */\nimport type { Rect } from \"../geometry\";\nimport { EDGES, SIDES, bandSize, defOf, floatSize, type Band, type Clock, type Column, type Edge, type FloatAt, type PanelDef, type PanelsState, type Side } from \"./model\";\n\nexport interface StoredPanels {\n readonly v: 1;\n /** A column's `show` and `active` only when it is tabbed / has one (optional fields of v1). */\n readonly sides: Record<Side, { panels: string[]; width: number; show?: \"tabs\"; active?: string }[]>;\n /** The bands, only when the state has one (an optional field of v1). */\n readonly bands?: Partial<Record<Edge, { panels: string[]; height: number }>>;\n readonly floats: { id: string; rect: Rect; pinned: boolean }[];\n readonly folded: string[];\n readonly kept: string[];\n readonly closed: string[];\n readonly clock: Record<string, Clock>;\n}\n\nexport type PanelsRead = { readonly ok: true; readonly state: PanelsState; readonly dropped: readonly string[] } | { readonly ok: false; readonly error: string };\n\nexport function storePanels(state: PanelsState): StoredPanels {\n const column = (c: Column): StoredPanels[\"sides\"][\"left\"][number] => ({\n panels: [...c.panels],\n width: c.width,\n ...(c.show === \"tabs\" ? { show: \"tabs\" as const } : {}),\n ...(c.active !== undefined ? { active: c.active } : {}),\n });\n const sides = Object.fromEntries(SIDES.map((s) => [s, state.sides[s].map(column)])) as StoredPanels[\"sides\"];\n const bands: NonNullable<StoredPanels[\"bands\"]> = {};\n for (const e of EDGES) {\n const b = state.bands?.[e];\n if (b) bands[e] = { panels: [...b.panels], height: b.height };\n }\n return {\n v: 1,\n sides,\n ...(EDGES.some((e) => bands[e]) ? { bands } : {}),\n floats: state.floats.map((f) => ({ id: f.id, rect: { x: f.rect.x, y: f.rect.y, width: f.rect.width, height: f.rect.height }, pinned: !!f.pinned })),\n folded: [...state.folded],\n kept: [...state.kept],\n closed: [...state.closed],\n clock: Object.fromEntries(Object.entries(state.clock).map(([k, c]) => [k, { opened: c.opened, used: c.used }])),\n };\n}\n\nconst isObject = (x: unknown): x is Record<string, unknown> => typeof x === \"object\" && x !== null && !Array.isArray(x);\nconst finite = (x: unknown): x is number => typeof x === \"number\" && Number.isFinite(x);\n\nexport function readPanels(data: unknown, defs?: readonly PanelDef[]): PanelsRead {\n try {\n return read(data, defs);\n } catch (e) {\n // A getter that throws, a proxy: still no throw.\n return { ok: false, error: `unreadable: ${e instanceof Error ? e.message : typeof e}` };\n }\n}\n\nfunction read(data: unknown, defs?: readonly PanelDef[]): PanelsRead {\n if (!isObject(data)) return { ok: false, error: \"not an object\" };\n // Named without serialising it: a BigInt or a circular value must not throw.\n if (data.v !== 1) return { ok: false, error: `unknown version ${typeof data.v === \"number\" || typeof data.v === \"string\" ? String(data.v) : typeof data.v} (this reads version 1)` };\n if (!isObject(data.sides)) return { ok: false, error: \"sides is missing\" };\n const dropped: string[] = [];\n const drop = (what: string): void => {\n if (!dropped.includes(what)) dropped.push(what);\n };\n const seen = new Set<string>();\n // An id the consumer has, not placed yet.\n const take = (x: unknown): string | null => {\n if (typeof x !== \"string\") {\n drop(`a ${typeof x} where an id goes`);\n return null;\n }\n if ((defs && !defOf(defs, x)) || seen.has(x)) {\n drop(x);\n return null;\n }\n seen.add(x);\n return x;\n };\n // Fields this version doesn't know: skipped, and reported.\n const extra = (o: Record<string, unknown>, known: readonly string[], prefix: string): void => {\n for (const k of Object.keys(o)) if (!known.includes(k)) drop(`${prefix}${k}`);\n };\n extra(data, [\"v\", \"sides\", \"bands\", \"floats\", \"folded\", \"kept\", \"closed\", \"clock\"], \"\");\n extra(data.sides, SIDES, \"sides.\");\n const sides = { left: [] as Column[], right: [] as Column[] };\n for (const side of SIDES) {\n const cols = data.sides[side];\n if (cols === undefined) continue;\n if (!Array.isArray(cols)) {\n drop(`sides.${side}`);\n continue;\n }\n for (const c of cols as unknown[]) {\n if (!isObject(c) || !Array.isArray(c.panels)) {\n drop(`a column on the ${side}`);\n continue;\n }\n extra(c, [\"panels\", \"width\", \"show\", \"active\"], \"column.\");\n const panels = (c.panels as unknown[]).map(take).filter((x): x is string => x !== null);\n if (panels.length === 0) continue;\n const ds = defs ? panels.map((id) => defOf(defs, id)!) : [];\n const lo = Math.max(0, ...ds.map((d) => d.width.min));\n const hi = Math.max(lo, Math.min(...ds.map((d) => d.width.max)));\n const natural = ds.length ? Math.max(...ds.map((d) => d.width.natural)) : 0;\n const width = finite(c.width) ? c.width : natural;\n // `show`: \"tabs\" or \"stack\" (a stack is the default, so not kept); `active`: a panel of this column.\n if (c.show !== undefined && c.show !== \"tabs\" && c.show !== \"stack\") drop(\"column.show\");\n if (c.active !== undefined && !(typeof c.active === \"string\" && panels.includes(c.active))) drop(`column.active:${typeof c.active === \"string\" ? c.active : typeof c.active}`);\n const tabs = c.show === \"tabs\" ? { show: \"tabs\" as const } : {};\n const active = typeof c.active === \"string\" && panels.includes(c.active) ? { active: c.active } : {};\n sides[side].push({ panels, width: defs ? Math.min(Math.max(width, lo), hi) : Math.max(0, width), ...tabs, ...active });\n }\n }\n const bands: Partial<Record<Edge, Band>> = {};\n if (data.bands !== undefined) {\n if (!isObject(data.bands)) drop(\"bands\");\n else {\n extra(data.bands, EDGES, \"bands.\");\n for (const e of EDGES) {\n const b = data.bands[e];\n if (b === undefined) continue;\n if (!isObject(b) || !Array.isArray(b.panels) || !finite(b.height)) {\n drop(`bands.${e}`);\n continue;\n }\n extra(b, [\"panels\", \"height\"], \"band.\");\n const panels = (b.panels as unknown[]).map(take).filter((x): x is string => x !== null);\n if (panels.length === 0) {\n drop(`bands.${e}`);\n continue;\n }\n const ds = defs ? panels.map((id) => bandSize(defOf(defs, id))) : [];\n const lo = Math.max(0, ...ds.map((d) => d.min));\n const hi = Math.max(lo, Math.min(...ds.map((d) => d.max)));\n bands[e] = { panels, height: defs ? Math.min(Math.max(b.height, lo), hi) : Math.max(0, b.height) };\n }\n }\n }\n const floats: FloatAt[] = [];\n for (const f of Array.isArray(data.floats) ? (data.floats as unknown[]) : []) {\n const r = isObject(f) ? f.rect : undefined;\n if (!isObject(f) || !isObject(r) || !finite(r.x) || !finite(r.y) || !finite(r.width) || !finite(r.height)) {\n drop(isObject(f) && typeof f.id === \"string\" ? f.id : \"a float\");\n continue;\n }\n extra(f, [\"id\", \"rect\", \"pinned\"], \"float.\");\n const id = take(f.id);\n if (id === null) continue;\n const min = defs ? floatSize(defOf(defs, id)).min : { width: 0, height: 0 };\n floats.push({ id, rect: { x: r.x, y: r.y, width: Math.max(min.width, r.width), height: Math.max(min.height, r.height) }, ...(f.pinned === true ? { pinned: true } : {}) });\n }\n if (data.floats !== undefined && !Array.isArray(data.floats)) drop(\"floats\");\n // The lists keep only ids that are placed (a closed panel keeps its place).\n const placed = (key: \"folded\" | \"kept\" | \"closed\"): string[] => {\n const x = data[key];\n if (x === undefined) return [];\n if (!Array.isArray(x)) {\n drop(key);\n return [];\n }\n const out: string[] = [];\n for (const id of x as unknown[]) {\n if (typeof id === \"string\" && seen.has(id)) {\n if (!out.includes(id)) out.push(id);\n } else drop(typeof id === \"string\" ? id : `a ${typeof id} in ${key}`);\n }\n return out;\n };\n const floating = new Set(floats.map((f) => f.id));\n // Only a docked panel is kept open.\n const kept = placed(\"kept\").filter((id) => {\n if (floating.has(id)) drop(id);\n return !floating.has(id);\n });\n const clock: Record<string, Clock> = {};\n if (isObject(data.clock)) {\n for (const id of Object.keys(data.clock)) if (!seen.has(id)) drop(`clock.${id}`);\n for (const id of seen) {\n const c = data.clock[id];\n clock[id] = isObject(c) && finite(c.opened) && finite(c.used) ? { opened: c.opened, used: c.used } : { opened: 0, used: 0 };\n }\n }\n const state: PanelsState = { sides, floats, folded: placed(\"folded\"), kept, closed: placed(\"closed\"), clock };\n return { ok: true, state: EDGES.some((e) => bands[e]) ? { ...state, bands } : state, dropped };\n}\n"],"mappings":"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","names":[]}