@n-uf/placement 26.11.4 → 26.11.6

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,41 @@
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.6
7
+
8
+ - `@n-uf/placement/targets`:
9
+ - `keyboardStops(targets, { home?, floats? })`: a group's places in one list in reading order (hosts title, top,
10
+ left, right, bottom; zones start, centre, end; each insertion point in order), with its own place as a `home`
11
+ stop when given and floating last only when asked. Each stop has the target id, a label, its place and the
12
+ arrangement a move there makes: one list for the arrow keys and a "Move to" menu, no parsing of target ids.
13
+ - Host targets carry their `slot`: the group's rect in the host once it lands there. `hostAfter(arrangement)`
14
+ says where the host is then, when it grows or appears; default: as it is now.
15
+ - `allows(group, place | "float")` in `hostTargets`' input: a place it refuses is no target. Its `place.index`
16
+ counts the groups there without the dragged one (as `insertGroup` does). For floating, the same rule decides
17
+ whether you add `floatTarget` at all: `...(allows(g, "float") ? [floatTarget(input)] : [])`.
18
+ - `@n-uf/placement/toolbar`:
19
+ - `HostSpec.keep`: a per-group keep priority (default 0). When room runs out, a lower keep collapses first;
20
+ among equal keeps the trailing end still goes first, so a primary group at the end can stay open. `HostSpec`
21
+ takes the group id type.
22
+ - `storeArrangement(arrangement)` / `readArrangement(data, groups?)`: a versioned stored form (`{ v: 1, hosts,
23
+ collapsed, floating }`). Reading parsed data gives `{ ok: true, arrangement }` or `{ ok: false, error }`, never a
24
+ throw: an unknown version, a missing host or zone, a value of the wrong kind, a group in two places, an unknown
25
+ group. An arrangement lacking some known groups reads fine; place those with `resetGroup` or `dockGroup`.
26
+ - New exports: the types `FloatTargetInput`, `Allows`, `KeyboardStop`, `KeyboardStopsOptions` and the constant
27
+ `HOST_READING_ORDER`. `ToolbarTarget.slot` is a required field, so hand-built targets (fixtures, mocks) add it.
28
+
29
+ ## 26.11.5
30
+
31
+ - `@n-uf/placement/targets`: `paneZone(rect, point, previous, options?)`, a pane's five drop zones: the centre
32
+ rectangle (34 % of each axis, at least 24 px, its edges snapped to the device-pixel grid) or one of four edge
33
+ trapezoids from the corners. The previous zone holds for 6 px past its boundary. `paneCentre` gives the centre's
34
+ rectangle (to draw the zones); `PANE_ZONE_DEFAULTS` and `PANE_EDGES` (the tie-break order) come with it. It fits
35
+ `@n-uf/motion/drag`'s new `placeOf(target, point, previous)`: the zone is the place, and a zone that takes no drop
36
+ answers null. Ported from hypr-tiling's engine.
37
+ - Fix: `sameArrangement` compares the collapsed groups and the floating groups' ids both ways, so a repeated group on
38
+ one side no longer passes for a different group on the other.
39
+ - The README lists all fifteen layout note codes; every code has a test.
40
+
6
41
  ## 26.11.4
7
42
 
8
43
  The first release: where things go, as pure functions over plain data. No DOM, no framework, no motion, no
package/README.md CHANGED
@@ -55,7 +55,25 @@ console.log(items.get("panel:3")!.look, notes.map((n) => n.code)); // header [ '
55
55
  Each construction is `{ id, title, idea, initial(docs, size?), layout(state, size, options) }`. A layout returns a
56
56
  `Placed` per item (its rect, look, layer, and whether it is hidden, has a minimum or may scroll off the stage) and
57
57
  notes saying how it gave way. A note's `code` (with its `side` and `items` where they apply) is the contract; its
58
- `text` is English for people, and its wording may change in any release.
58
+ `text` is English for people, and its wording may change in any release. The codes (`NoteCode`):
59
+
60
+ | Code | Says | Also |
61
+ |---|---|---|
62
+ | `bottom-folded` | the bottom dock folded to its header | |
63
+ | `dock-folded` | a side dock folded to its rail | `side` |
64
+ | `sections-folded` | sections folded to their headers by themselves, least recently used first | |
65
+ | `torn-floating` | panels torn off a dock float | `items` |
66
+ | `torn-shaded` | some torn-off panels shade because they don't fit | |
67
+ | `columns-railed` | the tiling tree's side columns folded to rails | `side` when only one |
68
+ | `tiles-float` | tiles that broke a minimum float, newest first | `items` |
69
+ | `tabs-folded` | a tabbed side folded to icon tabs | `side` |
70
+ | `drawer-over` | a drawer slides over the documents | `side` |
71
+ | `column-split` | a strip column split in two | `items` |
72
+ | `strip-scrolls` | the strip is wider than the stage | |
73
+ | `stacks-merged` | both palette stacks share one corner | |
74
+ | `palettes-shaded` | palettes shaded to their title bars | |
75
+ | `palettes-cover` | how much of the documents the palettes cover | |
76
+ | `docs-room` | how much room the palettes leave the documents | |
59
77
 
60
78
  ## Toolbar groups
61
79
 
@@ -66,8 +84,26 @@ const a = arrangement({ top: { start: ["file", "edit"], end: ["share"] } });
66
84
  const size = (_id: string, _vertical: boolean, collapsed: boolean) => ({ along: collapsed ? 30 : 120, across: 28 });
67
85
  const top = layoutHost(a.top, "top", { length: 320, thickness: 34, lead: 8, tail: 8 }, size);
68
86
  console.log(top.overflow); // [ 'share' ]
87
+
88
+ // A primary group keeps its full form longest: a higher keep folds later (equal keeps fold from the trailing end).
89
+ const kept = layoutHost(a.top, "top", { length: 320, thickness: 34, lead: 8, tail: 8, keep: { share: 1 } }, size);
90
+ console.log(kept.overflow); // [ 'edit' ]
69
91
  ```
70
92
 
93
+ An arrangement is stored as versioned plain data; reading it back never throws:
94
+
95
+ ```ts
96
+ import { arrangement, readArrangement, storeArrangement } from "@n-uf/placement/toolbar";
97
+
98
+ const saved = JSON.stringify(storeArrangement(arrangement({ top: { start: ["file", "edit"] } }))); // { "v": 1, "hosts": … }
99
+ console.log(readArrangement(JSON.parse(saved)).ok); // true
100
+ console.log(readArrangement({ v: 9 })); // { ok: false, error: 'unknown version 9 (this reads version 1)' }
101
+ ```
102
+
103
+ `readArrangement` takes parsed data, not the string. It accepts an arrangement that lacks some of your groups (one
104
+ stored before you added a group, say): place each missing group yourself, with `resetGroup(read, group, defaults)`
105
+ to put it where your default arrangement has it, or `dockGroup` for a group that floated.
106
+
71
107
  ## Drop targets
72
108
 
73
109
  ```ts
@@ -88,7 +124,53 @@ console.log(drop.label, drop.slot.x, drop.slot.y); // Float here 580 290
88
124
  ```
89
125
 
90
126
  A toolbar group's targets come from `hostTargets` (a host's lines and empty zones, with its own place left out and
91
- returned as `own`, for the drag's home) and `floatTarget`.
127
+ returned as `own`, for the drag's home) and `floatTarget`. Each host target carries its `slot`, the group's rect
128
+ once it lands there; `allows(group, place)` leaves out places a group may not go (its `place.index` counts the
129
+ groups there without the dragged one, as `insertGroup` does). The same rule says whether a group may float: add
130
+ `floatTarget` only when `allows(group, "float")`. `keyboardStops` puts the targets in reading order (title, top,
131
+ left, right, bottom; start, centre, end) for the arrow keys and a "Move to" menu, with the group's own place as
132
+ `home`:
133
+
134
+ ```ts
135
+ import { floatTarget, hostTargets, keyboardStops } from "@n-uf/placement/targets";
136
+ import { arrangement, hostOf, type Place } from "@n-uf/placement/toolbar";
137
+
138
+ const a = arrangement({ top: { start: ["file", "edit"], end: ["share"] } });
139
+ const size = (_id: string, _vertical: boolean, collapsed: boolean) => ({ along: collapsed ? 30 : 120, across: 28 });
140
+ const allows = (g: string, place: Place | "float") => g !== "share" || (place !== "float" && place.host === "top"); // share stays in the top bar
141
+ const top = { origin: { x: 0, y: 40 }, spec: { length: 800, thickness: 34, lead: 8, tail: 8 } };
142
+ const { targets } = hostTargets({ group: "share", arrangement: a, host: "top", ...top, size, allows });
143
+ console.log(targets[0]!.slot); // { x: 8, y: 43, width: 120, height: 28 }
144
+ // Floating, by the same rule: share may not float, so it gets no float target.
145
+ const stage = { x: 0, y: 80, width: 800, height: 500 };
146
+ const all = [...targets, ...(allows("share", "float") ? [floatTarget({ group: "share", arrangement: a, stage, width: 120, height: 28 })] : [])];
147
+ console.log(all.length === targets.length); // true
148
+ const stops = keyboardStops(targets, { home: { place: hostOf(a, "share")!, arrangement: a } });
149
+ console.log(stops.map((s) => (s.home ? "home" : s.id))); // [ 'g:top:start:0', 'g:top:start:1', 'g:top:start:2', 'g:top:center:0', 'home' ]
150
+ ```
151
+
152
+ ## Pane zones
153
+
154
+ `paneZone(rect, point, previous, options?)` says which of a pane's five zones a point is in: the centre rectangle
155
+ (34 % of each axis, at least 24 px) or one of four edge trapezoids. The previous zone holds for 6 px past its
156
+ boundary. It fits `@n-uf/motion/drag`'s `placeOf(target, point, previous)`: one target per pane, the zone as the
157
+ place, and a null place for a zone that takes no drop.
158
+
159
+ ```ts
160
+ import { paneZone, type PaneZone } from "@n-uf/placement/targets";
161
+
162
+ const pane = { x: 0, y: 0, width: 200, height: 200 };
163
+ console.log(paneZone(pane, { x: 100, y: 100 }), paneZone(pane, { x: 190, y: 100 })); // center right
164
+ console.log(paneZone(pane, { x: 137, y: 100 }, "center")); // center: 3 px past the line, it holds
165
+
166
+ // As a placeOf: the pane's id and zone, none where this pane can't take a drop beside it.
167
+ const canSplit = false;
168
+ const placeOf = (t: { id: string; hit: typeof pane }, p: { x: number; y: number }, previous: string | null): string | null => {
169
+ const zone = paneZone(t.hit, p, (previous?.split(":")[1] as PaneZone | undefined) ?? null);
170
+ return zone === "center" || canSplit ? `${t.id}:${zone}` : null;
171
+ };
172
+ console.log(placeOf({ id: "a", hit: pane }, { x: 100, y: 100 }, null), placeOf({ id: "a", hit: pane }, { x: 10, y: 100 }, null)); // a:center null
173
+ ```
92
174
 
93
175
  ## License
94
176
 
@@ -51,15 +51,63 @@ var isEmptyHost = (a, h) => ZONES.every((zn) => a[h][zn].length === 0);
51
51
  var samePlace = (a, b) => a === null || b === null ? a === b : a.host === b.host && a.zone === b.zone && a.index === b.index;
52
52
  function sameArrangement(a, b) {
53
53
  const sameList = (x, y) => x.length === y.length && x.every((g, i) => g === y[i]);
54
- const sameSet = (x, y) => x.length === y.length && x.every((g) => y.includes(g));
54
+ const sameSet = (x, y) => x.length === y.length && x.every((g) => y.includes(g)) && y.every((g) => x.includes(g));
55
55
  if (!HOSTS.every((h) => ZONES.every((zn) => sameList(a[h][zn], b[h][zn])))) return false;
56
- if (!sameSet(a.collapsed, b.collapsed) || a.floating.length !== b.floating.length) return false;
56
+ if (!sameSet(a.collapsed, b.collapsed)) return false;
57
+ if (!sameSet(a.floating.map((f) => f.id), b.floating.map((f) => f.id))) return false;
57
58
  return a.floating.every((f) => {
58
59
  const o = b.floating.find((x) => x.id === f.id);
59
60
  return !!o && o.x === f.x && o.y === f.y && samePlace(o.from, f.from);
60
61
  });
61
62
  }
62
63
  var isOwnPlace = (a, g, place) => sameArrangement(insertGroup(a, g, place.host, place.zone, place.index), a);
64
+ function storeArrangement(a) {
65
+ const hosts = Object.fromEntries(HOSTS.map((h) => [h, Object.fromEntries(ZONES.map((zn) => [zn, [...a[h][zn]]]))]));
66
+ return { v: 1, hosts, collapsed: [...a.collapsed], floating: a.floating.map((f) => ({ id: f.id, x: f.x, y: f.y, from: f.from ? { ...f.from } : null })) };
67
+ }
68
+ function readArrangement(data, groups) {
69
+ const fail = (error) => ({ ok: false, error });
70
+ const isObject = (x) => typeof x === "object" && x !== null && !Array.isArray(x);
71
+ if (!isObject(data)) return fail("not an object");
72
+ if (data.v !== 1) return fail(`unknown version ${typeof data.v === "number" || typeof data.v === "string" ? String(data.v) : typeof data.v} (this reads version 1)`);
73
+ const seen = /* @__PURE__ */ new Set();
74
+ const ids = (x, where, placed) => {
75
+ if (!Array.isArray(x) || !x.every((g) => typeof g === "string")) return `${where} is not a list of group ids`;
76
+ for (const g of x) {
77
+ if (groups && !groups.includes(g)) return `${where} has an unknown group "${g}"`;
78
+ if (placed && seen.has(g)) return `group "${g}" is in two places`;
79
+ if (placed) seen.add(g);
80
+ }
81
+ return x;
82
+ };
83
+ if (!isObject(data.hosts)) return fail("hosts is missing");
84
+ const hosts = {};
85
+ for (const h of HOSTS) {
86
+ const zones = data.hosts[h];
87
+ if (!isObject(zones)) return fail(`host ${h} is missing`);
88
+ const out = {};
89
+ for (const zn of ZONES) {
90
+ const list = ids(zones[zn], `${h}.${zn}`, true);
91
+ if (typeof list === "string") return fail(list);
92
+ out[zn] = list;
93
+ }
94
+ hosts[h] = out;
95
+ }
96
+ const collapsed = ids(data.collapsed, "collapsed", false);
97
+ if (typeof collapsed === "string") return fail(collapsed);
98
+ if (!Array.isArray(data.floating)) return fail("floating is not a list");
99
+ const floating = [];
100
+ for (const f of data.floating) {
101
+ if (!isObject(f) || typeof f.id !== "string" || typeof f.x !== "number" || typeof f.y !== "number" || !Number.isFinite(f.x) || !Number.isFinite(f.y)) return fail("a floating group is not { id, x, y, from }");
102
+ const id = ids([f.id], "floating", true);
103
+ if (typeof id === "string") return fail(id);
104
+ const from = f.from;
105
+ const place = from === null || from === void 0 ? null : isObject(from) && HOSTS.includes(from.host) && ZONES.includes(from.zone) && Number.isInteger(from.index) && from.index >= 0 ? { host: from.host, zone: from.zone, index: from.index } : void 0;
106
+ if (place === void 0) return fail(`floating group "${f.id}" has a bad home`);
107
+ floating.push({ id: id[0], x: f.x, y: f.y, from: place });
108
+ }
109
+ return { ok: true, arrangement: { ...hosts, collapsed, floating } };
110
+ }
63
111
  function hostNeed(zones, collapsed, spec, size, v) {
64
112
  const lens = ZONES.map((zn) => zones[zn].reduce((s, g) => s + size(g, v, collapsed.includes(g)).along, 0) + GAP * Math.max(0, zones[zn].length - 1));
65
113
  const used = ZONES.filter((zn) => zones[zn].length > 0).length;
@@ -74,7 +122,12 @@ function layoutHost(zones, h, spec, size, byHand = []) {
74
122
  const len = (g) => size(g, v, collapsed.has(g)).along;
75
123
  const zoneLen = (l) => l.reduce((s, g) => s + len(g), 0) + GAP * Math.max(0, l.length - 1);
76
124
  const needNow = () => hostNeed(zones, [...collapsed], spec, size, v);
77
- for (const g of [...ZONES].reverse().flatMap((zn) => [...zones[zn]].reverse())) {
125
+ const keep = (g) => {
126
+ const k = spec.keep && Object.hasOwn(spec.keep, g) ? spec.keep[g] : 0;
127
+ return typeof k === "number" && Number.isFinite(k) ? k : 0;
128
+ };
129
+ const order = [...ZONES].reverse().flatMap((zn) => [...zones[zn]].reverse());
130
+ for (const g of order.sort((x, y) => keep(x) - keep(y))) {
78
131
  if (needNow() <= spec.length) break;
79
132
  if (collapsed.has(g)) continue;
80
133
  collapsed.add(g);
@@ -159,8 +212,10 @@ export {
159
212
  isEmptyHost,
160
213
  sameArrangement,
161
214
  isOwnPlace,
215
+ storeArrangement,
216
+ readArrangement,
162
217
  hostNeed,
163
218
  layoutHost,
164
219
  insertions
165
220
  };
166
- //# sourceMappingURL=chunk-PBH6TKET.js.map
221
+ //# sourceMappingURL=chunk-WLMJ6JGO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/toolbar/index.ts"],"sourcesContent":["/**\n * @n-uf/placement/toolbar: toolbar groups as a pure model. Commands come in groups, each sitting in one of five\n * hosts (the title bar, the top toolbar, a bottom toolbar, a left and a right side toolbar), in one of three zones\n * (start, centre, end; top, middle, bottom on a side), in order; or floating. A toolbar is a slot: it never moves\n * itself, its groups do. Group sizes come in as numbers; group ids are the consumer's strings.\n *\n * - A host lays its zones out along its length: start from the lead, end up to the tail, the centre centred on the\n * whole host; when the centre would hit the start or end zone it centres in the space between them.\n * - Toolbars never wrap: when room runs out, groups collapse to their handle and » from the trailing end. A group\n * collapsed by hand stays collapsed.\n * - A group drop lands in a zone before all, after all, or between two groups. An empty zone is a target of its\n * own. A group's own place is home: a drop there changes nothing.\n */\nimport type { Rect } from \"../geometry\";\n\nexport type HostId = \"title\" | \"top\" | \"bottom\" | \"left\" | \"right\";\nexport type ZoneId = \"start\" | \"center\" | \"end\";\nexport type Zones<G extends string = string> = Readonly<Record<ZoneId, readonly G[]>>;\n\n/** Where a group is docked: a host, a zone, and its index there. */\nexport interface Place {\n readonly host: HostId;\n readonly zone: ZoneId;\n readonly index: number;\n}\n\n/** A group floating over the content: its top-left corner, and where it was docked. */\nexport interface Floating<G extends string = string> {\n readonly id: G;\n readonly x: number;\n readonly y: number;\n readonly from: Place | null;\n}\n\nexport type Arrangement<G extends string = string> = Readonly<Record<HostId, Zones<G>>> & {\n /** Groups collapsed to their handle and » by hand. */\n readonly collapsed: readonly G[];\n readonly floating: readonly Floating<G>[];\n};\n\nexport const HOSTS: readonly HostId[] = [\"title\", \"top\", \"bottom\", \"left\", \"right\"];\nexport const ZONES: readonly ZoneId[] = [\"start\", \"center\", \"end\"];\n\nexport const HOST_LABEL: Readonly<Record<HostId, string>> = { title: \"Title bar\", top: \"Top bar\", bottom: \"Bottom bar\", left: \"Left edge\", right: \"Right edge\" };\nexport const zoneLabel = (h: HostId, z: ZoneId): string => (vertical(h) ? { start: \"top\", center: \"middle\", end: \"bottom\" } : { start: \"left\", center: \"centre\", end: \"right\" })[z];\n\n/** The side edges hold their groups in a column. */\nexport const vertical = (h: HostId): boolean => h === \"left\" || h === \"right\";\n\n/** Between two groups (the handle of the second is the separator). */\nexport const GAP = 12;\n/** Between zones. */\nexport const ZONE_GAP = 16;\n\n/** An arrangement from the zones each host has (the rest empty), nothing collapsed or floating. */\nexport function arrangement<G extends string>(p: Partial<Record<HostId, Partial<Record<ZoneId, readonly G[]>>>>): Arrangement<G> {\n const z = (q: Partial<Record<ZoneId, readonly G[]>> = {}): Zones<G> => ({ start: [], center: [], end: [], ...q });\n return { title: z(p.title), top: z(p.top), bottom: z(p.bottom), left: z(p.left), right: z(p.right), collapsed: [], floating: [] };\n}\n\n// --- changes -------------------------------------------------------------------------------------------------------\n\nconst mapHosts = <G extends string>(a: Arrangement<G>, f: (h: HostId, zone: ZoneId, list: readonly G[]) => readonly G[]): Arrangement<G> => ({\n ...a,\n ...(Object.fromEntries(HOSTS.map((h) => [h, Object.fromEntries(ZONES.map((zn) => [zn, f(h, zn, a[h][zn])]))])) as Record<HostId, Zones<G>>),\n});\n\n/** Where `g` is docked (null: floating or absent). */\nexport function hostOf<G extends string>(a: Arrangement<G>, g: G): Place | null {\n for (const h of HOSTS) for (const zn of ZONES) if (a[h][zn].includes(g)) return { host: h, zone: zn, index: a[h][zn].indexOf(g) };\n return null;\n}\n\n/** The arrangement with group `g` lifted out (of its host, or from floating). */\nexport const withoutGroup = <G extends string>(a: Arrangement<G>, g: G): Arrangement<G> => ({ ...mapHosts(a, (_h, _z, l) => l.filter((x) => x !== g)), floating: a.floating.filter((f) => f.id !== g) });\n\n/** The arrangement with `g` docked at place `i` of zone `zone` of host `h` (`i` counts the others). */\nexport function insertGroup<G extends string>(a: Arrangement<G>, g: G, h: HostId, zone: ZoneId, i: number): Arrangement<G> {\n return mapHosts(withoutGroup(a, g), (hh, zz, l) => (hh === h && zz === zone ? [...l.slice(0, i), g, ...l.slice(i)] : l));\n}\n\n/** `g` floating with its top-left corner at (x, y); it remembers where it was docked. */\nexport function floatGroup<G extends string>(a: Arrangement<G>, g: G, x: number, y: number): Arrangement<G> {\n const from = hostOf(a, g) ?? a.floating.find((f) => f.id === g)?.from ?? null;\n const out = withoutGroup(a, g);\n return { ...out, floating: [...out.floating, { id: g, x, y, from }] };\n}\n\n/** `g` at `place`, its index kept within the zone. */\nconst dockAt = <G extends string>(a: Arrangement<G>, g: G, place: Place): Arrangement<G> =>\n insertGroup(a, g, place.host, place.zone, Math.min(place.index, a[place.host][place.zone].filter((x) => x !== g).length));\n\n/** `g` docked again where it floated from, else where `home` (the default arrangement) has it. */\nexport function dockGroup<G extends string>(a: Arrangement<G>, g: G, home: Arrangement<G>): Arrangement<G> {\n const from = a.floating.find((f) => f.id === g)?.from ?? hostOf(home, g);\n return from ? dockAt(a, g, from) : a;\n}\n\n/** `g` back where `home` (the default arrangement) has it, expanded. */\nexport function resetGroup<G extends string>(a: Arrangement<G>, g: G, home: Arrangement<G>): Arrangement<G> {\n const place = hostOf(home, g);\n const out = place ? dockAt(a, g, place) : a;\n return { ...out, collapsed: out.collapsed.filter((x) => x !== g) };\n}\n\n/** `g` moved to host `h`: after its last group (in its last zone that has any), or into its start zone. */\nexport function moveToHost<G extends string>(a: Arrangement<G>, g: G, h: HostId): Arrangement<G> {\n const rest = withoutGroup(a, g);\n const zone = [...ZONES].reverse().find((zn) => rest[h][zn].length > 0) ?? \"start\";\n return insertGroup(a, g, h, zone, rest[h][zone].length);\n}\n\n/** Every group of host `from` moved to host `to`, zone by zone, after the groups there. */\nexport function moveBar<G extends string>(a: Arrangement<G>, from: HostId, to: HostId): Arrangement<G> {\n if (from === to) return a;\n return mapHosts(a, (h, zn, l) => (h === from ? [] : h === to ? [...l, ...a[from][zn]] : l));\n}\n\nexport const toggleCollapsed = <G extends string>(a: Arrangement<G>, g: G): Arrangement<G> => ({ ...a, collapsed: a.collapsed.includes(g) ? a.collapsed.filter((x) => x !== g) : [...a.collapsed, g] });\n\nexport const isEmptyHost = <G extends string>(a: Arrangement<G>, h: HostId): boolean => ZONES.every((zn) => a[h][zn].length === 0);\n\nconst samePlace = (a: Place | null, b: Place | null): boolean => (a === null || b === null ? a === b : a.host === b.host && a.zone === b.zone && a.index === b.index);\n\n/**\n * The same arrangement: the same groups in the same order in every zone of every host, the same groups collapsed,\n * and the same groups floating at the same corner with the same home (in any order: neither list's order means\n * anything). Compared field by field, so how the arrangement was written down (its keys' order) doesn't matter.\n */\nexport function sameArrangement<G extends string>(a: Arrangement<G>, b: Arrangement<G>): boolean {\n const sameList = (x: readonly G[], y: readonly G[]): boolean => x.length === y.length && x.every((g, i) => g === y[i]);\n // Both ways, so a repeated id on one side can't stand in for a missing one ([\"a\", \"a\"] is not [\"a\", \"b\"]).\n const sameSet = (x: readonly G[], y: readonly G[]): boolean => x.length === y.length && x.every((g) => y.includes(g)) && y.every((g) => x.includes(g));\n if (!HOSTS.every((h) => ZONES.every((zn) => sameList(a[h][zn], b[h][zn])))) return false;\n if (!sameSet(a.collapsed, b.collapsed)) return false;\n if (!sameSet(a.floating.map((f) => f.id), b.floating.map((f) => f.id))) return false;\n return a.floating.every((f) => {\n const o = b.floating.find((x) => x.id === f.id);\n return !!o && o.x === f.x && o.y === f.y && samePlace(o.from, f.from);\n });\n}\n\n/** Dropping `g` at `place` changes nothing: it is the group's own place, its home, never a target. */\nexport const isOwnPlace = <G extends string>(a: Arrangement<G>, g: G, place: Place): boolean => sameArrangement(insertGroup(a, g, place.host, place.zone, place.index), a);\n\n// --- stored arrangements -------------------------------------------------------------------------------------------\n\n/** An arrangement as stored: version 1. */\nexport interface StoredArrangement<G extends string = string> {\n readonly v: 1;\n readonly hosts: Readonly<Record<HostId, Readonly<Record<ZoneId, readonly G[]>>>>;\n readonly collapsed: readonly G[];\n readonly floating: readonly { readonly id: G; readonly x: number; readonly y: number; readonly from: Place | null }[];\n}\n\n/** What reading a stored arrangement gives: the arrangement, or why it couldn't be read. Never a throw. */\nexport type ArrangementRead<G extends string = string> = { readonly ok: true; readonly arrangement: Arrangement<G> } | { readonly ok: false; readonly error: string };\n\n/** The arrangement as plain data with its version, ready for `JSON.stringify`. */\nexport function storeArrangement<G extends string>(a: Arrangement<G>): StoredArrangement<G> {\n const hosts = Object.fromEntries(HOSTS.map((h) => [h, Object.fromEntries(ZONES.map((zn) => [zn, [...a[h][zn]]]))])) as unknown as Record<HostId, Record<ZoneId, G[]>>;\n return { v: 1, hosts, collapsed: [...a.collapsed], floating: a.floating.map((f) => ({ id: f.id, x: f.x, y: f.y, from: f.from ? { ...f.from } : null })) };\n}\n\n/**\n * A stored arrangement read back from parsed data (`JSON.parse`'s result, not the string). Anything else gives\n * `{ ok: false, error }`, never a throw: an unknown version, a missing host or zone, a value of the wrong kind, a\n * group in two places, or (with `groups`) a group the consumer doesn't have. A group the consumer has but the stored\n * arrangement lacks is no error: the consumer places it (`resetGroup` with its default arrangement, say).\n */\nexport function readArrangement<G extends string = string>(data: unknown, groups?: readonly G[]): ArrangementRead<G> {\n const fail = (error: string): ArrangementRead<G> => ({ ok: false, error });\n const isObject = (x: unknown): x is Record<string, unknown> => typeof x === \"object\" && x !== null && !Array.isArray(x);\n if (!isObject(data)) return fail(\"not an object\");\n // Named without serialising it: a BigInt or a circular value must not throw.\n if (data.v !== 1) return fail(`unknown version ${typeof data.v === \"number\" || typeof data.v === \"string\" ? String(data.v) : typeof data.v} (this reads version 1)`);\n const seen = new Set<string>();\n const ids = (x: unknown, where: string, placed: boolean): G[] | string => {\n if (!Array.isArray(x) || !x.every((g) => typeof g === \"string\")) return `${where} is not a list of group ids`;\n for (const g of x as string[]) {\n if (groups && !(groups as readonly string[]).includes(g)) return `${where} has an unknown group \"${g}\"`;\n if (placed && seen.has(g)) return `group \"${g}\" is in two places`;\n if (placed) seen.add(g);\n }\n return x as G[];\n };\n if (!isObject(data.hosts)) return fail(\"hosts is missing\");\n const hosts = {} as Record<HostId, Zones<G>>;\n for (const h of HOSTS) {\n const zones = data.hosts[h];\n if (!isObject(zones)) return fail(`host ${h} is missing`);\n const out = {} as Record<ZoneId, readonly G[]>;\n for (const zn of ZONES) {\n const list = ids(zones[zn], `${h}.${zn}`, true);\n if (typeof list === \"string\") return fail(list);\n out[zn] = list;\n }\n hosts[h] = out;\n }\n const collapsed = ids(data.collapsed, \"collapsed\", false);\n if (typeof collapsed === \"string\") return fail(collapsed);\n if (!Array.isArray(data.floating)) return fail(\"floating is not a list\");\n const floating: Floating<G>[] = [];\n for (const f of data.floating as unknown[]) {\n if (!isObject(f) || typeof f.id !== \"string\" || typeof f.x !== \"number\" || typeof f.y !== \"number\" || !Number.isFinite(f.x) || !Number.isFinite(f.y)) return fail(\"a floating group is not { id, x, y, from }\");\n const id = ids([f.id], \"floating\", true);\n if (typeof id === \"string\") return fail(id);\n const from = f.from;\n const place = from === null || from === undefined ? null : isObject(from) && HOSTS.includes(from.host as HostId) && ZONES.includes(from.zone as ZoneId) && Number.isInteger(from.index) && (from.index as number) >= 0 ? { host: from.host as HostId, zone: from.zone as ZoneId, index: from.index as number } : undefined;\n if (place === undefined) return fail(`floating group \"${f.id}\" has a bad home`);\n floating.push({ id: id[0]!, x: f.x, y: f.y, from: place });\n }\n return { ok: true, arrangement: { ...hosts, collapsed, floating } };\n}\n\n// --- a host's layout -----------------------------------------------------------------------------------------------\n\n/** A host's room: its length and thickness, and what its ends keep for themselves (window controls, a margin). */\nexport interface HostSpec<G extends string = string> {\n readonly length: number;\n readonly thickness: number;\n readonly lead: number;\n readonly tail: number;\n /**\n * The centre zone's minimum length and its margin on each side, counted in the need whether it holds groups or\n * not (an empty centre keeps its drop size). Unset: the centre counts only its groups.\n */\n readonly center?: { readonly min: number; readonly margin: number };\n /**\n * How long each group keeps its full form when room runs out (default 0): groups with a lower keep collapse\n * first, and among equal keeps the trailing end goes first. A primary group at the end keeps open with a keep of 1.\n */\n readonly keep?: Readonly<Partial<Record<G, number>>>;\n}\n\n/** A group's size in a host, expanded or collapsed: along it (`along`) and across it (`across`). */\nexport type GroupSize<G extends string = string> = (id: G, vertical: boolean, collapsed: boolean) => { along: number; across: number };\n\nexport interface HostLayout<G extends string = string> {\n /** Each group's rect in the host (x along, y across for a horizontal host; transposed on a side). */\n readonly slots: ReadonlyMap<G, Rect>;\n /** Groups shown collapsed (by hand, or because the room ran out). */\n readonly collapsed: readonly G[];\n /** Of those, the ones the room collapsed (from the trailing end). */\n readonly overflow: readonly G[];\n /** The length the groups and the ends take. */\n readonly need: number;\n}\n\n/** How long the host's groups need it to be, collapsing only those in `collapsed`. */\nexport function hostNeed<G extends string>(zones: Zones<G>, collapsed: readonly G[], spec: Pick<HostSpec, \"lead\" | \"tail\" | \"center\">, size: GroupSize<G>, v: boolean): number {\n const lens = ZONES.map((zn) => zones[zn].reduce((s, g) => s + size(g, v, collapsed.includes(g)).along, 0) + GAP * Math.max(0, zones[zn].length - 1));\n const used = ZONES.filter((zn) => zones[zn].length > 0).length;\n const c = spec.center;\n if (!c) return spec.lead + spec.tail + lens.reduce((s, x) => s + x, 0) + ZONE_GAP * Math.max(0, used - 1);\n // The centre always counts, at its minimum at least, and its margins keep it apart from the start and end zones.\n return spec.lead + spec.tail + lens[0]! + lens[2]! + Math.max(lens[1]!, c.min) + 2 * c.margin;\n}\n\n/**\n * Lay a host out: zones along its length; when they don't fit, groups collapse, the lowest keep first and, among\n * equal keeps, from the trailing end.\n */\nexport function layoutHost<G extends string>(zones: Zones<G>, h: HostId, spec: HostSpec<G>, size: GroupSize<G>, byHand: readonly G[] = []): HostLayout<G> {\n const v = vertical(h);\n const collapsed = new Set<G>(ZONES.flatMap((zn) => zones[zn]).filter((g) => byHand.includes(g)));\n const overflow: G[] = [];\n const len = (g: G): number => size(g, v, collapsed.has(g)).along;\n const zoneLen = (l: readonly G[]): number => l.reduce((s, g) => s + len(g), 0) + GAP * Math.max(0, l.length - 1);\n const needNow = (): number => hostNeed(zones, [...collapsed], spec, size, v);\n // The lowest keep first; among equal keeps the trailing end first: the end zone's last group, back to the start\n // zone's first (the sort is stable).\n // Own keys only (a group named \"toString\" mustn't read the prototype), and finite numbers only (NaN breaks the sort).\n const keep = (g: G): number => {\n const k = spec.keep && Object.hasOwn(spec.keep, g) ? spec.keep[g] : 0;\n return typeof k === \"number\" && Number.isFinite(k) ? k : 0;\n };\n const order = [...ZONES].reverse().flatMap((zn) => [...zones[zn]].reverse());\n for (const g of order.sort((x, y) => keep(x) - keep(y))) {\n if (needNow() <= spec.length) break;\n if (collapsed.has(g)) continue;\n collapsed.add(g);\n overflow.push(g);\n }\n const a0 = spec.lead;\n const b0 = spec.length - spec.tail;\n const S = zoneLen(zones.start);\n const C = zoneLen(zones.center);\n const E = zoneLen(zones.end);\n const lo = a0 + S + (S > 0 ? ZONE_GAP : 0);\n const hi = b0 - E - (E > 0 ? ZONE_GAP : 0);\n let c0 = spec.length / 2 - C / 2;\n if (c0 < lo || c0 + C > hi) c0 = (lo + hi) / 2 - C / 2;\n const slots = new Map<G, Rect>();\n const place = (l: readonly G[], from: number): void => {\n let at = from;\n for (const g of l) {\n const s = size(g, v, collapsed.has(g));\n const across = (spec.thickness - s.across) / 2;\n slots.set(g, v ? { x: across, y: at, width: s.across, height: s.along } : { x: at, y: across, width: s.along, height: s.across });\n at += s.along + GAP;\n }\n };\n place(zones.start, a0);\n place(zones.center, c0);\n place(zones.end, b0 - E);\n return { slots, collapsed: [...collapsed], overflow, need: needNow() };\n}\n\n// --- insertion points ----------------------------------------------------------------------------------------------\n\n/** A place a dragged group can go: a zone of a host and the index among the others there, and where it is. */\nexport interface Insertion extends Place {\n /** Along the host: the insertion line (between groups); for an empty zone, the zone's middle. */\n readonly at: number;\n /** The stretch of the host it owns (host coordinates). */\n readonly span: readonly [number, number];\n /** An empty zone: it owns all of its stretch, whatever is nearer (it wins while the pointer is inside it). */\n readonly empty: boolean;\n readonly label: string;\n}\n\n/**\n * The insertion points of a host laid out without the dragged group: in each zone before all, between two, after\n * all; each owns the stretch of the host nearest to it. An empty zone owns its own stretch: the host's first third\n * (start), middle third (centre) or last third (end), less what its neighbours' groups take. `label` names a group\n * in the \"between\" labels (default: its id).\n */\nexport function insertions<G extends string>(zones: Zones<G>, h: HostId, spec: HostSpec<G>, l: HostLayout<G>, label: (g: G) => string = (g) => g): Insertion[] {\n const v = vertical(h);\n const lo = (r: Rect): number => (v ? r.y : r.x);\n const hi = (r: Rect): number => (v ? r.y + r.height : r.x + r.width);\n const L = spec.length;\n const all = ZONES.flatMap((zn) => zones[zn].map((g) => l.slots.get(g)).filter((r): r is Rect => !!r));\n const first = all.length ? Math.min(...all.map(lo)) : L;\n const last = all.length ? Math.max(...all.map(hi)) : 0;\n const zoneEdge = (zn: ZoneId, end: \"lo\" | \"hi\"): number | null => {\n const rs = zones[zn].map((g) => l.slots.get(g)).filter((r): r is Rect => !!r);\n if (!rs.length) return null;\n return end === \"lo\" ? Math.min(...rs.map(lo)) : Math.max(...rs.map(hi));\n };\n const near: Omit<Insertion, \"span\">[] = [];\n const empties: Insertion[] = [];\n for (const zn of ZONES) {\n const name = `${HOST_LABEL[h]} · ${zoneLabel(h, zn)}`;\n const vis = zones[zn].map((g, i) => ({ g, i, r: l.slots.get(g)! }));\n if (!vis.length) {\n const sEnd = zoneEdge(\"start\", \"hi\");\n const eStart = zoneEdge(\"end\", \"lo\");\n const span: [number, number] =\n zn === \"start\" ? [0, Math.min(L / 3, first - GAP / 2)] : zn === \"end\" ? [Math.max((2 * L) / 3, last + GAP / 2), L] : [Math.max(L / 3, (sEnd ?? 0) + GAP / 2), Math.min((2 * L) / 3, (eStart ?? L) - GAP / 2)];\n if (span[1] - span[0] >= 8) empties.push({ host: h, zone: zn, index: 0, at: (span[0] + span[1]) / 2, span, empty: true, label: name });\n continue;\n }\n near.push({ host: h, zone: zn, index: vis[0]!.i, at: lo(vis[0]!.r) - GAP / 2, empty: false, label: `${name} · before all` });\n for (let k = 1; k < vis.length; k++) near.push({ host: h, zone: zn, index: vis[k]!.i, at: (hi(vis[k - 1]!.r) + lo(vis[k]!.r)) / 2, empty: false, label: `${name} · between ${label(vis[k - 1]!.g)} and ${label(vis[k]!.g)}` });\n near.push({ host: h, zone: zn, index: zones[zn].length, at: hi(vis.at(-1)!.r) + GAP / 2, empty: false, label: `${name} · after all` });\n }\n near.sort((a, b) => a.at - b.at);\n const lines = near.map((p, k) => ({ ...p, span: [k === 0 ? 0 : (near[k - 1]!.at + p.at) / 2, k === near.length - 1 ? L : (p.at + near[k + 1]!.at) / 2] as const }));\n return [...lines, ...empties];\n}\n"],"mappings":"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","names":[]}
package/dist/targets.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { R as Rect, P as Point, S as Size } from './geometry-mL-GTRti.js';
2
2
  import { D as DeskState, g as Layout, a as Construction, O as Options } from './constructions-VDK_mwmz.js';
3
- import { Arrangement, HostId, HostSpec, GroupSize, Place } from './toolbar.js';
3
+ import { Place, Arrangement, HostId, HostSpec, GroupSize } from './toolbar.js';
4
4
 
5
5
  /**
6
6
  * A drop target in the shape @n-uf/motion/drag takes: an id, a priority (1 most precise: a rail or a tab strip;
@@ -73,7 +73,7 @@ declare function deskDrop(input: DeskTargetInput, target: DeskTarget, p: Point,
73
73
  /**
74
74
  * Toolbar group targets: a host's insertion points (lines between groups, priority 2) and empty zones (priority 1),
75
75
  * and floating over the content (priority 3). A group's own place is home, never a target: its stretch comes back
76
- * in `own`, for the consumer to join to the drag's home.
76
+ * in `own`, for the consumer to join to the drag's home. A place `allows` forbids is no host target either.
77
77
  */
78
78
 
79
79
  /** How far a line's hit reaches beyond the host's thickness on each side; an empty zone's. */
@@ -87,9 +87,17 @@ interface ToolbarTarget<G extends string = string> extends Target {
87
87
  readonly vertical: boolean;
88
88
  /** The insertion line (3 px, inset 4 px from the host's sides) in stage coordinates; for an empty zone, at its middle. */
89
89
  readonly line: Rect;
90
+ /** Where the group lands, in stage coordinates: its rect in the host once the drop is made. */
91
+ readonly slot: Rect;
90
92
  /** The arrangement a drop here makes. */
91
93
  readonly arrangement: Arrangement<G>;
92
94
  }
95
+ /**
96
+ * Where a group may go: a host's place, or floating. `hostTargets` leaves out a place it refuses; for floating, the
97
+ * consumer adds `floatTarget` only when `allows(group, "float")`, so one rule covers both. `place.index` counts the
98
+ * groups in that zone without the dragged one (as `insertGroup` does): 0 is before all of the others.
99
+ */
100
+ type Allows<G extends string> = (group: G, place: Place | "float") => boolean;
93
101
  interface HostTargetsInput<G extends string> {
94
102
  readonly group: G;
95
103
  /** The arrangement as it is, the group still in its place. */
@@ -97,10 +105,20 @@ interface HostTargetsInput<G extends string> {
97
105
  readonly host: HostId;
98
106
  /** The host's top-left corner in stage coordinates. */
99
107
  readonly origin: Point;
100
- readonly spec: HostSpec;
108
+ readonly spec: HostSpec<G>;
101
109
  readonly size: GroupSize<G>;
102
110
  /** A group's name in the labels (default: its id). */
103
111
  readonly label?: (g: G) => string;
112
+ /** Where the group may go (default: anywhere). A place it may not go is left out. */
113
+ readonly allows?: Allows<G>;
114
+ /**
115
+ * Where this host is, and how long, once a drop has made `arrangement` (a host may grow or appear when it gets a
116
+ * group); default: where it is now. The targets' `slot`s are laid out there.
117
+ */
118
+ readonly hostAfter?: (arrangement: Arrangement<G>) => {
119
+ readonly origin: Point;
120
+ readonly spec: HostSpec<G>;
121
+ };
104
122
  }
105
123
  /** The targets a host offers a dragged group, zone by zone in order, and the stretches that are its own place. */
106
124
  declare function hostTargets<G extends string>(input: HostTargetsInput<G>): {
@@ -113,13 +131,104 @@ interface ToolbarFloatTarget<G extends string = string> extends Target {
113
131
  /** The group's top-left corner relative to the stage, and the arrangement, for a drop with the pointer at `p`. */
114
132
  readonly make: (p: Point, grab: Point) => Arrangement<G>;
115
133
  }
116
- /** Floating over the content (`stage`, in stage coordinates): the group's top-left corner kept on the stage. */
117
- declare function floatTarget<G extends string>(input: {
134
+ interface FloatTargetInput<G extends string> {
118
135
  readonly group: G;
119
136
  readonly arrangement: Arrangement<G>;
137
+ /** The content the group floats over, in stage coordinates. */
120
138
  readonly stage: Rect;
139
+ /** The group's size as it floats. */
140
+ readonly width: number;
141
+ readonly height: number;
142
+ }
143
+ /**
144
+ * Floating over the content (`stage`, in stage coordinates): the group's top-left corner kept on the stage. A
145
+ * group that may not float simply gets no float target: add it only when your rule allows it, with the same
146
+ * `allows` you give `hostTargets`: `...(allows(group, "float") ? [floatTarget(input)] : [])`.
147
+ */
148
+ declare function floatTarget<G extends string>(input: FloatTargetInput<G>): ToolbarFloatTarget<G>;
149
+
150
+ /**
151
+ * Keyboard stops: every place a toolbar group can go, in one list in reading order, for the arrow keys and a
152
+ * "Move to" menu alike. A consumer steps through the list; it never parses target ids.
153
+ */
154
+
155
+ /** The hosts in reading order: the title bar, the top bar, the left edge, the right edge, the bottom bar. */
156
+ declare const HOST_READING_ORDER: readonly HostId[];
157
+ interface KeyboardStop<G extends string = string> {
158
+ /** The target's id ("home" for the group's own place). */
159
+ readonly id: string;
160
+ readonly label: string;
161
+ /** Where in a host it is; null for floating. */
162
+ readonly place: Place | null;
163
+ /** The arrangement a move here makes (the current one for home). */
164
+ readonly arrangement: Arrangement<G>;
165
+ /** The group's own place: where the keyboard starts, and a move there changes nothing. */
166
+ readonly home: boolean;
167
+ }
168
+ interface KeyboardStopsOptions<G extends string> {
169
+ /** The group's own place, as a stop of its own in its reading-order position (the targets leave it out). */
170
+ readonly home?: {
171
+ readonly place: Place;
172
+ readonly arrangement: Arrangement<G>;
173
+ readonly label?: string;
174
+ };
175
+ /**
176
+ * Floating targets as stops at the end (default: left out). `at` is where the pointer would drop it and `grab`
177
+ * where it holds the group (default: the float target's middle, the group's top-left corner).
178
+ */
179
+ readonly floats?: boolean | {
180
+ readonly at: Point;
181
+ readonly grab?: Point;
182
+ };
183
+ }
184
+ /**
185
+ * The places a group can go, in reading order: hosts title → top → left → right → bottom, zones start → centre →
186
+ * end, then each insertion point in order; the group's own place among them when `home` is given; floating targets
187
+ * at the end only when `floats` asks for them. One list serves the arrow keys and a "Move to" menu.
188
+ */
189
+ declare function keyboardStops<G extends string>(targets: readonly (ToolbarTarget<G> | ToolbarFloatTarget<G>)[], options?: KeyboardStopsOptions<G>): KeyboardStop<G>[];
190
+
191
+ /**
192
+ * A pane's drop zones: a centre rectangle (put it in the pane's place, merge or swap) and four edge trapezoids (put
193
+ * it beside the pane on that side), formed by the diagonals from each pane corner to the nearest corner of the
194
+ * centre. The five zones tile the pane with no gaps and no overlaps.
195
+ *
196
+ * Meant for `@n-uf/motion/drag`'s `placeOf(target, point, previous)`: one target per pane, its place the zone under
197
+ * the pointer, the previous zone held for `hysteresis` px past its boundary so a pointer resting on a boundary
198
+ * doesn't flicker. A zone that takes no drop is the consumer's to answer with a null place.
199
+ */
200
+
201
+ type PaneZone = "center" | "top" | "right" | "bottom" | "left";
202
+ /** The edge zones in their tie-break order: at an exact diagonal the first wins (a corner resolves to `top`). */
203
+ declare const PANE_EDGES: readonly Exclude<PaneZone, "center">[];
204
+ interface PaneZoneOptions {
205
+ /** The centre rectangle's share of each pane axis (0.05 to 0.95); `centreX` / `centreY` override one axis. */
206
+ readonly centre?: number;
207
+ readonly centreX?: number;
208
+ readonly centreY?: number;
209
+ /** The least the centre spans on either axis, px (a small pane keeps a usable centre). */
210
+ readonly centreMin?: number;
211
+ /** How far past its boundary the previous zone holds, px. */
212
+ readonly hysteresis?: number;
213
+ /** Device pixels per px: the centre's edges snap to that grid, as the browser draws them. */
214
+ readonly pixelRatio?: number;
215
+ }
216
+ declare const PANE_ZONE_DEFAULTS: Required<Omit<PaneZoneOptions, "centreX" | "centreY">>;
217
+ /** The centre rectangle of a pane of `size`, in pane-local px (origin at the pane's top-left corner). */
218
+ interface PaneCentre {
121
219
  readonly width: number;
122
220
  readonly height: number;
123
- }): ToolbarFloatTarget<G>;
221
+ readonly left: number;
222
+ readonly top: number;
223
+ readonly right: number;
224
+ readonly bottom: number;
225
+ }
226
+ /** Where the centre rectangle of a pane of `size` lies, by these options. */
227
+ declare function paneCentre(size: Size, options?: PaneZoneOptions): PaneCentre;
228
+ /**
229
+ * The zone of `rect` (a pane, in the same coordinates as `point`) the point is in. With a `previous` zone, the point
230
+ * has to be `hysteresis` px past that zone's boundary before the zone changes: no time-based debounce, only geometry.
231
+ */
232
+ declare function paneZone(rect: Rect, point: Point, previous?: PaneZone | null, options?: PaneZoneOptions): PaneZone;
124
233
 
125
- export { type DeskDrop, type DeskTarget, type DeskTargetInput, type HostTargetsInput, LINE_REACH, type Made, SNAP, type Target, type ToolbarFloatTarget, type ToolbarTarget, ZONE_REACH, deskDrop, deskTargets, floatTarget, hostTargets };
234
+ export { type Allows, type DeskDrop, type DeskTarget, type DeskTargetInput, type FloatTargetInput, HOST_READING_ORDER, type HostTargetsInput, type KeyboardStop, type KeyboardStopsOptions, LINE_REACH, type Made, PANE_EDGES, PANE_ZONE_DEFAULTS, type PaneCentre, type PaneZone, type PaneZoneOptions, SNAP, type Target, type ToolbarFloatTarget, type ToolbarTarget, ZONE_REACH, deskDrop, deskTargets, floatTarget, hostTargets, keyboardStops, paneCentre, paneZone };
package/dist/targets.js CHANGED
@@ -30,6 +30,7 @@ import {
30
30
  rect
31
31
  } from "./chunk-QMTNB2C3.js";
32
32
  import {
33
+ HOST_LABEL,
33
34
  ZONES,
34
35
  floatGroup,
35
36
  insertGroup,
@@ -37,8 +38,9 @@ import {
37
38
  isOwnPlace,
38
39
  layoutHost,
39
40
  vertical,
40
- withoutGroup
41
- } from "./chunk-PBH6TKET.js";
41
+ withoutGroup,
42
+ zoneLabel
43
+ } from "./chunk-WLMJ6JGO.js";
42
44
 
43
45
  // src/targets/desk.ts
44
46
  var SNAP = 18;
@@ -321,6 +323,11 @@ var LINE_REACH = 12;
321
323
  var ZONE_REACH = 4;
322
324
  function hostTargets(input) {
323
325
  const { group, arrangement: a, host: h, origin: o, spec } = input;
326
+ const slotIn = (after) => {
327
+ const at = input.hostAfter?.(after) ?? { origin: o, spec };
328
+ const r = layoutHost(after[h], h, at.spec, input.size, after.collapsed).slots.get(group);
329
+ return rect(at.origin.x + r.x, at.origin.y + r.y, r.width, r.height);
330
+ };
324
331
  const a0 = withoutGroup(a, group);
325
332
  const l = layoutHost(a0[h], h, spec, input.size, a0.collapsed);
326
333
  const v = vertical(h);
@@ -336,6 +343,8 @@ function hostTargets(input) {
336
343
  own.push(across(s0, s1, LINE_REACH));
337
344
  continue;
338
345
  }
346
+ if (input.allows && !input.allows(group, place)) continue;
347
+ const after = insertGroup(a, group, h, p.zone, p.index);
339
348
  targets.push({
340
349
  id: `g:${h}:${p.zone}:${p.index}`,
341
350
  priority: p.empty ? 1 : 2,
@@ -345,7 +354,8 @@ function hostTargets(input) {
345
354
  place,
346
355
  vertical: v,
347
356
  line: v ? rect(o.x + 4, o.y + p.at - 1.5, T - 8, 3) : rect(o.x + p.at - 1.5, o.y + 4, 3, T - 8),
348
- arrangement: insertGroup(a, group, h, p.zone, p.index)
357
+ slot: slotIn(after),
358
+ arrangement: after
349
359
  });
350
360
  }
351
361
  return { targets, own };
@@ -367,13 +377,98 @@ function floatTarget(input) {
367
377
  }
368
378
  };
369
379
  }
380
+
381
+ // src/targets/stops.ts
382
+ var HOST_READING_ORDER = ["title", "top", "left", "right", "bottom"];
383
+ var isFloat = (t) => t.kind === "float";
384
+ var rank = (p) => HOST_READING_ORDER.indexOf(p.host) * 3e3 + ZONES.indexOf(p.zone) * 1e3 + p.index;
385
+ function keyboardStops(targets, options = {}) {
386
+ const placed = [];
387
+ const floating = [];
388
+ for (const t of targets) {
389
+ if (!isFloat(t)) {
390
+ placed.push({ id: t.id, label: t.label, place: t.place, arrangement: t.arrangement, home: false });
391
+ continue;
392
+ }
393
+ if (!options.floats) continue;
394
+ const at = options.floats === true ? centre(t.hit) : options.floats.at;
395
+ const grab = options.floats === true ? { x: 0, y: 0 } : options.floats.grab ?? { x: 0, y: 0 };
396
+ floating.push({ id: t.id, label: t.label, place: null, arrangement: t.make(at, grab), home: false });
397
+ }
398
+ const h = options.home;
399
+ if (h) placed.push({ id: "home", label: h.label ?? `${HOST_LABEL[h.place.host]} \xB7 ${zoneLabel(h.place.host, h.place.zone)} \xB7 current place`, place: h.place, arrangement: h.arrangement, home: true });
400
+ placed.sort((a, b) => rank(a.place) - rank(b.place));
401
+ return [...placed, ...floating];
402
+ }
403
+
404
+ // src/targets/zones.ts
405
+ var PANE_EDGES = ["top", "right", "bottom", "left"];
406
+ var PANE_ZONE_DEFAULTS = { centre: 0.34, centreMin: 24, hysteresis: 6, pixelRatio: 1 };
407
+ var clamp2 = (v, lo, hi) => Math.min(Math.max(v, lo), hi);
408
+ var snap = (v, ratio) => Math.round(v * ratio) / ratio;
409
+ function paneCentre(size, options = {}) {
410
+ const o = { ...PANE_ZONE_DEFAULTS, ...options };
411
+ const width = Math.max(0, size.width);
412
+ const height = Math.max(0, size.height);
413
+ const ratio = o.pixelRatio > 0 ? o.pixelRatio : 1;
414
+ const rx = clamp2(o.centreX ?? o.centre, 0.05, 0.95);
415
+ const ry = clamp2(o.centreY ?? o.centre, 0.05, 0.95);
416
+ const cw = Math.min(width, Math.max(width * rx, Math.min(o.centreMin, width)));
417
+ const ch = Math.min(height, Math.max(height * ry, Math.min(o.centreMin, height)));
418
+ return { width, height, left: snap((width - cw) / 2, ratio), top: snap((height - ch) / 2, ratio), right: snap((width + cw) / 2, ratio), bottom: snap((height + ch) / 2, ratio) };
419
+ }
420
+ function depths(p, c) {
421
+ return [
422
+ { zone: "top", depth: (c.top - p.y) / Math.max(c.top, 1) },
423
+ { zone: "right", depth: (p.x - c.right) / Math.max(c.width - c.right, 1) },
424
+ { zone: "bottom", depth: (p.y - c.bottom) / Math.max(c.height - c.bottom, 1) },
425
+ { zone: "left", depth: (c.left - p.x) / Math.max(c.left, 1) }
426
+ ];
427
+ }
428
+ function rawZone(p, c) {
429
+ if (p.x >= c.left && p.x <= c.right && p.y >= c.top && p.y <= c.bottom) return "center";
430
+ let best = "center";
431
+ let deepest = 0;
432
+ for (const d of depths(p, c)) {
433
+ if (d.depth > deepest) {
434
+ deepest = d.depth;
435
+ best = d.zone;
436
+ }
437
+ }
438
+ return best;
439
+ }
440
+ function toward(p, zone, by, c) {
441
+ if (zone === "left") return { x: p.x - by, y: p.y };
442
+ if (zone === "right") return { x: p.x + by, y: p.y };
443
+ if (zone === "top") return { x: p.x, y: p.y - by };
444
+ if (zone === "bottom") return { x: p.x, y: p.y + by };
445
+ const dx = c.width / 2 - p.x;
446
+ const dy = c.height / 2 - p.y;
447
+ const len = Math.hypot(dx, dy);
448
+ if (len <= by || len === 0) return { x: c.width / 2, y: c.height / 2 };
449
+ return { x: p.x + dx / len * by, y: p.y + dy / len * by };
450
+ }
451
+ function paneZone(rect2, point, previous = null, options = {}) {
452
+ const c = paneCentre(rect2, options);
453
+ const p = { x: point.x - rect2.x, y: point.y - rect2.y };
454
+ const raw = rawZone(p, c);
455
+ const by = Math.max(0, options.hysteresis ?? PANE_ZONE_DEFAULTS.hysteresis);
456
+ if (previous === null || by === 0 || previous === raw) return raw;
457
+ return rawZone(toward(p, previous, by, c), c) === previous ? previous : raw;
458
+ }
370
459
  export {
460
+ HOST_READING_ORDER,
371
461
  LINE_REACH,
462
+ PANE_EDGES,
463
+ PANE_ZONE_DEFAULTS,
372
464
  SNAP,
373
465
  ZONE_REACH,
374
466
  deskDrop,
375
467
  deskTargets,
376
468
  floatTarget,
377
- hostTargets
469
+ hostTargets,
470
+ keyboardStops,
471
+ paneCentre,
472
+ paneZone
378
473
  };
379
474
  //# sourceMappingURL=targets.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/targets/desk.ts","../src/targets/toolbar.ts"],"sourcesContent":["/**\n * Each desk construction's drop targets, as pure functions: from the state with the dragged item lifted out, a list\n * of zones with a priority, each able to make the state with the item dropped there.\n *\n * - Edge docks (`docks`, `docksFull`, `dockTear`): a rail (priority 1) takes the panel at the end of its dock (a\n * folded dock keeps it in the rail); a slot between sections (2) and the bottom dock (2) take it at that place;\n * with tear-off, the documents area (3) tears it off to float there.\n * - Tiling tree (`tree`): each slot an insertion produces (2), a side column's row or a dwindle region of the\n * centre; an empty side column's edge (2).\n * - Strip (`strip`): between two columns (2) makes a new column; a gap inside a column (2) puts it there.\n * - Navigator, drawers (`navigator`, `drawers`): a tab strip (1) at the tab under the pointer, a folded side's rail\n * (1), a side's panel (3); the panel becomes the side's showing one.\n * - Palettes (`floatOver`, `floatAround`): a corner stack's column (2) at the place under the pointer; anywhere\n * else in the area (3), snapped to the area's edges (over the documents, also to other palettes' edges).\n */\nimport { centre, overlap, rect, type Point, type Rect, type Size } from \"../geometry\";\nimport type { Construction } from \"../dock/constructions\";\nimport { BAR, G, RAIL, TITLE, idOf, isDocKey, isPanelKey, panelKey, touch, type DeskState, type Layout, type Options, type Side } from \"../dock/model\";\nimport {\n DOCK,\n OVER_MARGIN,\n PALETTE_NARROW,\n TORN_W,\n TREE_COLUMN,\n dockGeometry,\n paletteArea,\n paletteWidth,\n stripColumns,\n stripGeometry,\n treeArea,\n treeFloats,\n treeRails,\n treeShown,\n treeTiles,\n} from \"../dock/parts\";\nimport type { Target } from \"./target\";\n\n/** A target's drop for a pointer: the state with the item there, a label, and a key (same key, same place). */\nexport interface Made {\n readonly state: DeskState;\n readonly label: string;\n readonly key: string;\n}\n\nexport interface DeskTarget extends Target {\n /** The drop with the pointer at `p`, holding the item at `grab` from its top-left corner. */\n readonly make: (p: Point, grab: Point) => Made;\n /** Where the keyboard stops in this target, in order (default: its zone's centre). */\n readonly stops?: readonly Point[];\n}\n\n/** A made drop, laid out. */\nexport interface DeskDrop extends Made {\n readonly target: string;\n readonly layout: Layout;\n /** Where the item lands. */\n readonly slot: Rect;\n /** It lands hidden (folded into a rail). */\n readonly hidden: boolean;\n /** It floats where it is dropped: no room is made for it. */\n readonly free: boolean;\n}\n\nexport interface DeskTargetInput {\n readonly c: Construction;\n /** The state with the dragged item lifted out (`liftOut`). */\n readonly s0: DeskState;\n readonly key: string;\n readonly size: Size;\n readonly opt: Options;\n}\n\n/** How close (stage px) a free drop has to come to an edge to snap to it. */\nexport const SNAP = 18;\n\nconst insert = <T>(list: readonly T[], i: number, x: T): T[] => [...list.slice(0, i), x, ...list.slice(i)];\nconst clamp = (v: number, lo: number, hi: number): number => Math.min(Math.max(v, lo), Math.max(lo, hi));\nconst Cap = (s: Side): string => (s === \"left\" ? \"Left\" : \"Right\");\nconst fixed = (state: DeskState, label: string) => (): Made => ({ state, label, key: label });\n\n/** Zones `count + 1` slots split between items at `mids` (their centres), from `lo` to `hi`: slot i's span. */\nconst slotSpan = (mids: readonly number[], lo: number, hi: number, i: number): [number, number] => [i === 0 ? lo : mids[i - 1]!, i === mids.length ? hi : mids[i]!];\n\n// --- edge docks ----------------------------------------------------------------------------------------------------\n\nfunction dockTargets({ c, s0, key, size, opt }: DeskTargetInput): DeskTarget[] {\n const cat = c.desk.catalog;\n const out: DeskTarget[] = [];\n const id = idOf(key);\n const mode = c.id === \"docksFull\" ? \"full\" : \"between\";\n const d = dockGeometry(cat, s0, size, opt, mode);\n const l0 = c.layout(s0, size, opt);\n const W = size.width;\n const H = size.height;\n for (const side of [\"left\", \"right\"] as const) {\n const railed = l0.items.get(`rail:${side}`);\n if (!railed) continue;\n const label = `${Cap(side)} rail: into the ${side} dock`;\n out.push({ id: `rail:${side}`, priority: 1, hit: railed.rect, make: fixed(touch({ ...s0, [side]: [...s0[side], id] }, id), label) });\n if (side === \"left\" ? !d.lOpen : !d.rOpen) continue;\n // The dock's slots: each section's halves belong to the slots above and below it.\n const shown = side === \"left\" ? s0.left : d.right;\n const mids = shown.map((p) => centre(l0.items.get(panelKey(p))!.rect).y);\n const x = side === \"left\" ? RAIL : W - RAIL - DOCK;\n for (let i = 0; i <= shown.length; i++) {\n const [y0, y1] = slotSpan(mids, 0, d.h, i);\n // Past the side list (the bottom dock is off and its panels show here): into the bottom list.\n const state = i <= s0[side].length ? { ...s0, [side]: insert(s0[side], i, id) } : { ...s0, bottom: insert(s0.bottom, i - s0[side].length, id) };\n out.push({ id: `dock:${side}:${i}`, priority: 2, hit: rect(x, y0, DOCK, y1 - y0), make: fixed(touch(state, id), `${Cap(side)} dock · slot ${i + 1}`) });\n }\n }\n if (opt.bottom !== \"off\") {\n const b = d.bottomRect ?? rect(d.x0, H - 40 - G, d.x1 - d.x0, 40);\n const mids = s0.bottom.map((p) => centre(l0.items.get(panelKey(p))!.rect).x);\n for (let i = 0; i <= s0.bottom.length; i++) {\n const [x0, x1] = slotSpan(mids, b.x, b.x + b.width, i);\n out.push({ id: `bottom:${i}`, priority: 2, hit: rect(x0, b.y - 24, x1 - x0, H - b.y + 24), make: fixed(touch({ ...s0, bottom: insert(s0.bottom, i, id) }, id), `Bottom dock · slot ${i + 1}`) });\n }\n }\n if (c.id === \"dockTear\") {\n // Torn off, floating where it is dropped; snapped to the documents' edges, kept on the stage.\n out.push({\n id: \"float\",\n priority: 3,\n free: true,\n hit: rect(d.x0, 0, d.x1 - d.x0, H),\n make: (p, grab) => {\n let x = p.x - grab.x;\n let y = p.y - grab.y;\n if (Math.abs(x - d.x0) < SNAP) x = d.x0;\n if (Math.abs(x + TORN_W - d.x1) < SNAP) x = d.x1 - TORN_W;\n if (Math.abs(y - G) < SNAP) y = G;\n x = clamp(x, 0, W - TORN_W);\n y = clamp(y, 0, H - Math.min(cat.natural(id), H));\n return { state: touch({ ...s0, torn: [...s0.torn, { id, at: { x, y } }] }, id), label: \"Float here\", key: `float:${Math.round(x)},${Math.round(y)}` };\n },\n });\n }\n return out;\n}\n\n// --- tiling tree ---------------------------------------------------------------------------------------------------\n\n/**\n * The tree's targets are the slots an insertion produces: inserting at place i of a side column gives row i of its\n * n + 1 rows, inserting at place i of the centre gives dwindle's region i of n + 1. Those regions tile the column and\n * the centre, so a zone is where the item lands (unless that makes the newest tile float, which reshapes the rest);\n * the label says where it lands in the tree as laid out, and what floats.\n */\nfunction treeTargets({ c, s0, key, size }: DeskTargetInput): DeskTarget[] {\n const cat = c.desk.catalog;\n const out: DeskTarget[] = [];\n const area = treeArea(size, s0);\n // The tree as tiled: a column folded to its rail offers its rail instead of its rows.\n const t = treeShown(s0);\n const rails = treeRails(s0);\n // A side column is narrower than a document's minimum: a document dropped into one would only float.\n const sides = !isDocKey(key);\n for (const [side, group] of [[\"left\", \"L\"], [\"right\", \"R\"]] as const) {\n if (!sides || !rails[side]) continue;\n const hit = rect(side === \"left\" ? 0 : size.width - RAIL, BAR, RAIL, size.height - BAR);\n out.push({ id: `rail:${side}`, priority: 1, hit, make: fixed({ ...s0, tree: { ...s0.tree, [group]: [...s0.tree[group], key] } }, `${Cap(side)} rail: into the ${side} column`) });\n }\n if (sides && s0.tree.L.length === 0) out.push({ id: \"edge:L\", priority: 2, hit: rect(area.x, area.y, 30, area.height), make: fixed({ ...s0, tree: { ...s0.tree, L: [key] } }, \"New left column\") });\n if (sides && s0.tree.R.length === 0) out.push({ id: \"edge:R\", priority: 2, hit: rect(area.x + area.width - 30, area.y, 30, area.height), make: fixed({ ...s0, tree: { ...s0.tree, R: [key] } }, \"New right column\") });\n const x0 = area.x + (t.L.length ? TREE_COLUMN + G : 0);\n const x1 = area.x + area.width - (t.R.length ? TREE_COLUMN + G : 0);\n const middle = rect(x0, area.y, x1 - x0, area.height);\n const NAME = { L: \"Left column\", R: \"Right column\", C: \"Centre\" } as const;\n const seen: { label: string; hit: Rect }[] = [];\n const sameZone = (a: Rect, b: Rect): boolean => Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y), Math.abs(a.width - b.width), Math.abs(a.height - b.height)) < 0.5;\n for (const group of [\"L\", \"R\", \"C\"] as const) {\n const list = t[group];\n if (group !== \"C\" && list.length === 0) continue;\n if (group !== \"C\" && !sides) continue;\n for (let i = 0; i <= list.length; i++) {\n const state = { ...s0, tree: { ...s0.tree, [group]: insert(list, i, key) } };\n const shown = treeShown(state);\n // The slot as laid out once the tiles that break a minimum have floated (as laid out without them if it floats).\n const tiled = treeFloats(cat, shown, area);\n const zone = treeTiles(cat, tiled.floats.includes(key) ? shown : tiled.tree, area).get(key)!;\n const label = `${NAME[group]}, ${landing(c, shown, area, key, group, middle)}`;\n // Places that come out the same (around a floating tile) are one target: the keyboard stops there once.\n if (seen.some((o) => o.label === label && sameZone(o.hit, zone))) continue;\n seen.push({ label, hit: zone });\n out.push({ id: `tile:${group}:${i}`, priority: 2, hit: zone, make: fixed(state, label) });\n }\n }\n return out;\n}\n\n/**\n * Where `key` lands in the tree as laid out (after the newest tiles that break a minimum float): its row of its\n * column, or its region of the centre; or that it floats, or what floats because of it.\n */\nfunction landing(c: Construction, tree: DeskState[\"tree\"], area: Rect, key: string, group: \"L\" | \"R\" | \"C\", middle: Rect): string {\n const cat = c.desk.catalog;\n const { tree: tiled, floats } = treeFloats(cat, tree, area);\n if (floats.includes(key)) return \"it floats there (too small)\";\n const others = floats.length ? `; ${floats.map(cat.title).join(\", \")} ${floats.length > 1 ? \"float\" : \"floats\"}` : \"\";\n if (group !== \"C\") return `row ${tiled[group].indexOf(key) + 1} of ${tiled[group].length}${others}`;\n const x0 = tiled.L.length ? middle.x : area.x;\n const x1 = tiled.R.length ? middle.x + middle.width : area.x + area.width;\n return `${regionName(treeTiles(cat, tiled, area).get(key)!, rect(x0, area.y, x1 - x0, area.height))}${others}`;\n}\n\n/** Where a region lies in `whole`, in words: \"all of it\", \"left half\", \"top right\"… */\nfunction regionName(r: Rect, whole: Rect): string {\n const near = (a: number, b: number): boolean => Math.abs(a - b) < 1;\n const h = near(r.width, whole.width) ? \"\" : near(r.x, whole.x) ? \"left\" : near(r.x + r.width, whole.x + whole.width) ? \"right\" : \"middle\";\n const v = near(r.height, whole.height) ? \"\" : near(r.y, whole.y) ? \"top\" : near(r.y + r.height, whole.y + whole.height) ? \"bottom\" : \"middle\";\n if (!h && !v) return \"all of it\";\n if (!v) return `${h} part`;\n if (!h) return `${v} part`;\n return `${v} ${h}`;\n}\n\n// --- scrolling strip -----------------------------------------------------------------------------------------------\n\nfunction stripTargets({ c, s0, key, size, opt }: DeskTargetInput): DeskTarget[] {\n const cat = c.desk.catalog;\n const out: DeskTarget[] = [];\n const g = stripGeometry(cat, s0, size);\n const l0 = c.layout(s0, size, opt);\n // The laid-out columns, each with the state column it comes from and where in it it starts.\n const parts: { owner: number; start: number; keys: string[] }[] = [];\n s0.cols.forEach((col, owner) => {\n let start = 0;\n for (const keys of stripColumns(cat, [col], g.h)) {\n parts.push({ owner, start, keys });\n start += keys.length;\n }\n });\n const newColumn = (at: number, label: string): Made => ({ state: { ...s0, cols: insert(s0.cols, at, [key]) }, label, key: label });\n if (parts.length === 0) return [{ id: \"strip\", priority: 3, hit: rect(0, g.y, size.width, g.h), make: () => newColumn(0, \"The strip\") }];\n // Between columns: a band across each boundary where a state column starts or ends.\n for (let k = 0; k <= parts.length; k++) {\n if (k < parts.length && parts[k]!.start !== 0) continue;\n const x = k < parts.length ? g.xs[k]! - G / 2 : g.xs[parts.length - 1]! + g.widths[parts.length - 1]! + G / 2;\n const at = k < parts.length ? parts[k]!.owner : s0.cols.length;\n const label = k === 0 ? \"New column at the start\" : k === parts.length ? \"New column at the end\" : `New column before ${cat.title(parts[k]!.keys[0]!)}`;\n out.push({ id: `between:${at}`, priority: 2, hit: rect(x - 28, g.y, 56, g.h), make: () => newColumn(at, label) });\n }\n // Inside a column: each item's halves belong to the gaps above and below it.\n parts.forEach((part, k) => {\n const mids = part.keys.map((pk) => centre(l0.items.get(pk)!.rect).y);\n for (let j = 0; j <= part.keys.length; j++) {\n const [y0, y1] = slotSpan(mids, g.y, g.y + g.h, j);\n const at = part.start + j;\n const cols = s0.cols.map((col, ci) => (ci === part.owner ? insert(col, at, key) : col));\n const label = `Into the column with ${cat.title(s0.cols[part.owner]![0]!)} · ${at + 1}`;\n out.push({ id: `into:${part.owner}:${at}`, priority: 2, hit: rect(g.xs[k]!, y0, g.widths[k]!, y1 - y0), make: fixed({ ...s0, cols }, label) });\n }\n });\n return out;\n}\n\n// --- navigator and drawers -----------------------------------------------------------------------------------------\n\nfunction tabTargets({ c, s0, key, size, opt }: DeskTargetInput): DeskTarget[] {\n const out: DeskTarget[] = [];\n const id = idOf(key);\n const l0 = c.layout(s0, size, opt);\n const onSide = (side: Side, i: number): DeskState => touch({ ...s0, [side]: insert(s0[side], i, id), active: { ...s0.active, [side]: id } }, id);\n for (const side of [\"left\", \"right\"] as const) {\n const list = s0[side];\n const tabs = l0.items.get(`tabs:${side}`);\n const railed = l0.items.get(`rail:${side}`);\n if (tabs && !tabs.hidden) {\n const r = tabs.rect;\n const tw = r.width / (list.length + 1);\n out.push({\n id: `tabs:${side}`,\n priority: 1,\n hit: rect(r.x, r.y - 4, r.width, r.height + 8),\n make: (p) => {\n const i = clamp(Math.floor((p.x - r.x) / tw), 0, list.length);\n const label = `${Cap(side)} tab strip · tab ${i + 1}`;\n return { state: onSide(side, i), label, key: label };\n },\n stops: Array.from({ length: list.length + 1 }, (_, i) => ({ x: r.x + tw * (i + 0.5), y: r.y + r.height / 2 })),\n });\n const panel = list.map((p) => l0.items.get(panelKey(p))).find((pl) => pl && !pl.hidden);\n if (panel) out.push({ id: `side:${side}`, priority: 3, hit: panel.rect, make: fixed(onSide(side, list.length), `${Cap(side)} side`) });\n }\n if (railed && !railed.hidden) out.push({ id: `rail:${side}`, priority: 1, hit: railed.rect, make: fixed(onSide(side, list.length), `${Cap(side)} rail`) });\n if (list.length === 0 && (!railed || railed.hidden)) {\n const hit = side === \"left\" ? rect(0, 0, 40, size.height) : rect(size.width - 40, 0, 40, size.height);\n out.push({ id: `edge:${side}`, priority: 2, hit, make: fixed(onSide(side, 0), `${Cap(side)} side`) });\n }\n }\n return out;\n}\n\n// --- palettes ------------------------------------------------------------------------------------------------------\n\nfunction paletteTargets({ c, s0, key, size, opt }: DeskTargetInput): DeskTarget[] {\n const cat = c.desk.catalog;\n const out: DeskTarget[] = [];\n const id = idOf(key);\n const { area } = paletteArea(size, opt);\n const m = c.id === \"floatOver\" ? OVER_MARGIN : 0;\n const pw = paletteWidth(size.width);\n const ph = s0.folded.includes(id) ? TITLE : cat.natural(id);\n const narrow = size.width < PALETTE_NARROW;\n const anchor = { left: area.x + m, right: area.x + area.width - m - pw };\n const l0 = c.layout(s0, size, opt);\n const rectOf = (pid: number): Rect => l0.items.get(panelKey(pid))!.rect;\n const tb = l0.items.get(\"toolbar\");\n const pillRect = tb?.look === \"pill\" ? tb.rect : null;\n // A corner stack's column, down to just under its last palette: it joins the stack at the place under the pointer.\n for (const side of narrow ? ([\"right\"] as const) : ([\"left\", \"right\"] as const)) {\n const ids = narrow ? [...s0.stacks.left, ...s0.stacks.right] : [...s0.stacks[side]];\n const bottom = ids.length ? Math.max(...ids.map((pid) => rectOf(pid).y + rectOf(pid).height)) : area.y + m;\n const at = (y: number): number => ids.filter((pid) => centre(rectOf(pid)).y < y).length;\n const join = (i: number): Made => {\n let stacks = s0.stacks;\n if (!narrow) stacks = { ...stacks, [side]: insert(stacks[side], i, id) };\n else if (i <= s0.stacks.left.length) stacks = { ...stacks, left: insert(stacks.left, i, id) };\n else stacks = { ...stacks, right: insert(stacks.right, i - s0.stacks.left.length, id) };\n const label = `${narrow ? \"The\" : Cap(side)} corner stack · ${i + 1}`;\n return { state: touch({ ...s0, stacks }, id), label, key: label };\n };\n out.push({\n id: `stack:${side}`,\n priority: 2,\n hit: rect(anchor[side], area.y, pw, bottom + 30 - area.y),\n make: (p) => join(at(p.y)),\n stops: [area.y + m, ...ids.map((pid) => rectOf(pid).y + rectOf(pid).height + G / 2)].map((y) => ({ x: anchor[side] + pw / 2, y })),\n });\n }\n // Anywhere: snapped to the area's edges (over the documents also to other palettes' edges), kept inside the area.\n out.push({\n id: \"free\",\n priority: 3,\n free: true,\n hit: area,\n make: (p, grab) => {\n let x = p.x - grab.x;\n let y = p.y - grab.y;\n const snaps: string[] = [];\n if (Math.abs(x - anchor.left) < SNAP) [x, snaps[0]] = [anchor.left, \"left edge\"];\n else if (Math.abs(x - anchor.right) < SNAP) [x, snaps[0]] = [anchor.right, \"right edge\"];\n if (Math.abs(y - (area.y + m)) < SNAP) [y, snaps[1]] = [area.y + m, \"top\"];\n else if (Math.abs(y + ph - (area.y + area.height - m)) < SNAP) [y, snaps[1]] = [area.y + area.height - m - ph, \"bottom\"];\n if (c.id === \"floatOver\") {\n for (const [k, pl] of l0.items) {\n if (!isPanelKey(k) || pl.hidden) continue;\n const o = pl.rect;\n if (snaps[0] === undefined && Math.abs(x - o.x) < SNAP) [x, snaps[0]] = [o.x, `${cat.title(k)}'s edge`];\n if (snaps[1] === undefined && Math.abs(y - (o.y + o.height + G)) < SNAP && x < o.x + o.width && x + pw > o.x) [y, snaps[1]] = [o.y + o.height + G, `under ${cat.title(k)}`];\n }\n }\n x = clamp(x, area.x, area.x + area.width - pw);\n y = clamp(y, area.y, area.y + area.height - ph);\n // Never under a floating pill: it moves clear, below a pill at the top, above one at the bottom.\n if (pillRect && overlap(rect(x, y, pw, ph), pillRect) > 0) {\n y = pillRect.y < size.height / 2 ? pillRect.y + pillRect.height + G : pillRect.y - G - ph;\n y = clamp(y, 0, size.height - ph);\n snaps[1] = \"clear of the toolbar\";\n }\n const how = snaps.filter(Boolean).join(\", \");\n return { state: touch({ ...s0, free: [...s0.free, { id, at: { x, y } }] }, id), label: how ? `Here · snapped to the ${how}` : \"Here\", key: `free:${Math.round(x)},${Math.round(y)}` };\n },\n });\n return out;\n}\n\n/** The drop targets for dragging `key` in construction `c`, in a fixed order (the keyboard's). */\nexport function deskTargets(input: DeskTargetInput): DeskTarget[] {\n switch (input.c.id) {\n case \"docks\":\n case \"docksFull\":\n case \"dockTear\":\n return dockTargets(input);\n case \"tree\":\n return treeTargets(input);\n case \"strip\":\n return stripTargets(input);\n case \"navigator\":\n case \"drawers\":\n return tabTargets(input);\n case \"floatOver\":\n case \"floatAround\":\n return paletteTargets(input);\n }\n}\n\n/** A target's drop for a pointer at `p` holding the item at `grab`, laid out: where the item lands. */\nexport function deskDrop(input: DeskTargetInput, target: DeskTarget, p: Point, grab: Point): DeskDrop {\n const made = target.make(p, grab);\n const layout = input.c.layout(made.state, input.size, input.opt);\n const placed = layout.items.get(input.key)!;\n return { ...made, key: `${target.id}|${made.key}`, target: target.id, layout, slot: placed.rect, hidden: !!placed.hidden, free: !!target.free };\n}\n","/**\n * Toolbar group targets: a host's insertion points (lines between groups, priority 2) and empty zones (priority 1),\n * and floating over the content (priority 3). A group's own place is home, never a target: its stretch comes back\n * in `own`, for the consumer to join to the drag's home.\n */\nimport { rect, type Point, type Rect } from \"../geometry\";\nimport { ZONES, floatGroup, insertGroup, insertions, isOwnPlace, layoutHost, vertical, withoutGroup, type Arrangement, type GroupSize, type HostId, type HostSpec, type Place } from \"../toolbar\";\nimport type { Target } from \"./target\";\n\n/** How far a line's hit reaches beyond the host's thickness on each side; an empty zone's. */\nexport const LINE_REACH = 12;\nexport const ZONE_REACH = 4;\n\nexport interface ToolbarTarget<G extends string = string> extends Target {\n readonly label: string;\n readonly kind: \"line\" | \"zone\";\n readonly place: Place;\n /** It lands in a side edge's column. */\n readonly vertical: boolean;\n /** The insertion line (3 px, inset 4 px from the host's sides) in stage coordinates; for an empty zone, at its middle. */\n readonly line: Rect;\n /** The arrangement a drop here makes. */\n readonly arrangement: Arrangement<G>;\n}\n\nexport interface HostTargetsInput<G extends string> {\n readonly group: G;\n /** The arrangement as it is, the group still in its place. */\n readonly arrangement: Arrangement<G>;\n readonly host: HostId;\n /** The host's top-left corner in stage coordinates. */\n readonly origin: Point;\n readonly spec: HostSpec;\n readonly size: GroupSize<G>;\n /** A group's name in the labels (default: its id). */\n readonly label?: (g: G) => string;\n}\n\n/** The targets a host offers a dragged group, zone by zone in order, and the stretches that are its own place. */\nexport function hostTargets<G extends string>(input: HostTargetsInput<G>): { targets: ToolbarTarget<G>[]; own: Rect[] } {\n const { group, arrangement: a, host: h, origin: o, spec } = input;\n const a0 = withoutGroup(a, group);\n const l = layoutHost(a0[h], h, spec, input.size, a0.collapsed);\n const v = vertical(h);\n const T = spec.thickness;\n const across = (s0: number, s1: number, reach: number): Rect => (v ? rect(o.x - reach, o.y + s0, T + 2 * reach, s1 - s0) : rect(o.x + s0, o.y - reach, s1 - s0, T + 2 * reach));\n const targets: ToolbarTarget<G>[] = [];\n const own: Rect[] = [];\n const pts = insertions(a0[h], h, spec, l, input.label).sort((p, q) => ZONES.indexOf(p.zone) - ZONES.indexOf(q.zone) || p.index - q.index);\n for (const p of pts) {\n const [s0, s1] = p.span;\n const place: Place = { host: h, zone: p.zone, index: p.index };\n if (isOwnPlace(a, group, place)) {\n own.push(across(s0, s1, LINE_REACH));\n continue;\n }\n targets.push({\n id: `g:${h}:${p.zone}:${p.index}`,\n priority: p.empty ? 1 : 2,\n hit: p.empty ? across(s0, s1, ZONE_REACH) : across(s0, s1, LINE_REACH),\n label: p.label,\n kind: p.empty ? \"zone\" : \"line\",\n place,\n vertical: v,\n line: v ? rect(o.x + 4, o.y + p.at - 1.5, T - 8, 3) : rect(o.x + p.at - 1.5, o.y + 4, 3, T - 8),\n arrangement: insertGroup(a, group, h, p.zone, p.index),\n });\n }\n return { targets, own };\n}\n\nexport interface ToolbarFloatTarget<G extends string = string> extends Target {\n readonly label: string;\n readonly kind: \"float\";\n /** The group's top-left corner relative to the stage, and the arrangement, for a drop with the pointer at `p`. */\n readonly make: (p: Point, grab: Point) => Arrangement<G>;\n}\n\n/** Floating over the content (`stage`, in stage coordinates): the group's top-left corner kept on the stage. */\nexport function floatTarget<G extends string>(input: { readonly group: G; readonly arrangement: Arrangement<G>; readonly stage: Rect; readonly width: number; readonly height: number }): ToolbarFloatTarget<G> {\n const { group, arrangement, stage } = input;\n return {\n id: \"g:float\",\n priority: 3,\n free: true,\n hit: stage,\n label: \"Float here\",\n kind: \"float\",\n // Kept on the stage; a group larger than the stage keeps its top-left corner on the stage's.\n make: (p, grab) => {\n const x = Math.max(0, Math.min(p.x - grab.x - stage.x, stage.width - input.width));\n const y = Math.max(0, Math.min(p.y - grab.y - stage.y, stage.height - input.height));\n return floatGroup(arrangement, group, x, y);\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/targets/desk.ts","../src/targets/toolbar.ts","../src/targets/stops.ts","../src/targets/zones.ts"],"sourcesContent":["/**\n * Each desk construction's drop targets, as pure functions: from the state with the dragged item lifted out, a list\n * of zones with a priority, each able to make the state with the item dropped there.\n *\n * - Edge docks (`docks`, `docksFull`, `dockTear`): a rail (priority 1) takes the panel at the end of its dock (a\n * folded dock keeps it in the rail); a slot between sections (2) and the bottom dock (2) take it at that place;\n * with tear-off, the documents area (3) tears it off to float there.\n * - Tiling tree (`tree`): each slot an insertion produces (2), a side column's row or a dwindle region of the\n * centre; an empty side column's edge (2).\n * - Strip (`strip`): between two columns (2) makes a new column; a gap inside a column (2) puts it there.\n * - Navigator, drawers (`navigator`, `drawers`): a tab strip (1) at the tab under the pointer, a folded side's rail\n * (1), a side's panel (3); the panel becomes the side's showing one.\n * - Palettes (`floatOver`, `floatAround`): a corner stack's column (2) at the place under the pointer; anywhere\n * else in the area (3), snapped to the area's edges (over the documents, also to other palettes' edges).\n */\nimport { centre, overlap, rect, type Point, type Rect, type Size } from \"../geometry\";\nimport type { Construction } from \"../dock/constructions\";\nimport { BAR, G, RAIL, TITLE, idOf, isDocKey, isPanelKey, panelKey, touch, type DeskState, type Layout, type Options, type Side } from \"../dock/model\";\nimport {\n DOCK,\n OVER_MARGIN,\n PALETTE_NARROW,\n TORN_W,\n TREE_COLUMN,\n dockGeometry,\n paletteArea,\n paletteWidth,\n stripColumns,\n stripGeometry,\n treeArea,\n treeFloats,\n treeRails,\n treeShown,\n treeTiles,\n} from \"../dock/parts\";\nimport type { Target } from \"./target\";\n\n/** A target's drop for a pointer: the state with the item there, a label, and a key (same key, same place). */\nexport interface Made {\n readonly state: DeskState;\n readonly label: string;\n readonly key: string;\n}\n\nexport interface DeskTarget extends Target {\n /** The drop with the pointer at `p`, holding the item at `grab` from its top-left corner. */\n readonly make: (p: Point, grab: Point) => Made;\n /** Where the keyboard stops in this target, in order (default: its zone's centre). */\n readonly stops?: readonly Point[];\n}\n\n/** A made drop, laid out. */\nexport interface DeskDrop extends Made {\n readonly target: string;\n readonly layout: Layout;\n /** Where the item lands. */\n readonly slot: Rect;\n /** It lands hidden (folded into a rail). */\n readonly hidden: boolean;\n /** It floats where it is dropped: no room is made for it. */\n readonly free: boolean;\n}\n\nexport interface DeskTargetInput {\n readonly c: Construction;\n /** The state with the dragged item lifted out (`liftOut`). */\n readonly s0: DeskState;\n readonly key: string;\n readonly size: Size;\n readonly opt: Options;\n}\n\n/** How close (stage px) a free drop has to come to an edge to snap to it. */\nexport const SNAP = 18;\n\nconst insert = <T>(list: readonly T[], i: number, x: T): T[] => [...list.slice(0, i), x, ...list.slice(i)];\nconst clamp = (v: number, lo: number, hi: number): number => Math.min(Math.max(v, lo), Math.max(lo, hi));\nconst Cap = (s: Side): string => (s === \"left\" ? \"Left\" : \"Right\");\nconst fixed = (state: DeskState, label: string) => (): Made => ({ state, label, key: label });\n\n/** Zones `count + 1` slots split between items at `mids` (their centres), from `lo` to `hi`: slot i's span. */\nconst slotSpan = (mids: readonly number[], lo: number, hi: number, i: number): [number, number] => [i === 0 ? lo : mids[i - 1]!, i === mids.length ? hi : mids[i]!];\n\n// --- edge docks ----------------------------------------------------------------------------------------------------\n\nfunction dockTargets({ c, s0, key, size, opt }: DeskTargetInput): DeskTarget[] {\n const cat = c.desk.catalog;\n const out: DeskTarget[] = [];\n const id = idOf(key);\n const mode = c.id === \"docksFull\" ? \"full\" : \"between\";\n const d = dockGeometry(cat, s0, size, opt, mode);\n const l0 = c.layout(s0, size, opt);\n const W = size.width;\n const H = size.height;\n for (const side of [\"left\", \"right\"] as const) {\n const railed = l0.items.get(`rail:${side}`);\n if (!railed) continue;\n const label = `${Cap(side)} rail: into the ${side} dock`;\n out.push({ id: `rail:${side}`, priority: 1, hit: railed.rect, make: fixed(touch({ ...s0, [side]: [...s0[side], id] }, id), label) });\n if (side === \"left\" ? !d.lOpen : !d.rOpen) continue;\n // The dock's slots: each section's halves belong to the slots above and below it.\n const shown = side === \"left\" ? s0.left : d.right;\n const mids = shown.map((p) => centre(l0.items.get(panelKey(p))!.rect).y);\n const x = side === \"left\" ? RAIL : W - RAIL - DOCK;\n for (let i = 0; i <= shown.length; i++) {\n const [y0, y1] = slotSpan(mids, 0, d.h, i);\n // Past the side list (the bottom dock is off and its panels show here): into the bottom list.\n const state = i <= s0[side].length ? { ...s0, [side]: insert(s0[side], i, id) } : { ...s0, bottom: insert(s0.bottom, i - s0[side].length, id) };\n out.push({ id: `dock:${side}:${i}`, priority: 2, hit: rect(x, y0, DOCK, y1 - y0), make: fixed(touch(state, id), `${Cap(side)} dock · slot ${i + 1}`) });\n }\n }\n if (opt.bottom !== \"off\") {\n const b = d.bottomRect ?? rect(d.x0, H - 40 - G, d.x1 - d.x0, 40);\n const mids = s0.bottom.map((p) => centre(l0.items.get(panelKey(p))!.rect).x);\n for (let i = 0; i <= s0.bottom.length; i++) {\n const [x0, x1] = slotSpan(mids, b.x, b.x + b.width, i);\n out.push({ id: `bottom:${i}`, priority: 2, hit: rect(x0, b.y - 24, x1 - x0, H - b.y + 24), make: fixed(touch({ ...s0, bottom: insert(s0.bottom, i, id) }, id), `Bottom dock · slot ${i + 1}`) });\n }\n }\n if (c.id === \"dockTear\") {\n // Torn off, floating where it is dropped; snapped to the documents' edges, kept on the stage.\n out.push({\n id: \"float\",\n priority: 3,\n free: true,\n hit: rect(d.x0, 0, d.x1 - d.x0, H),\n make: (p, grab) => {\n let x = p.x - grab.x;\n let y = p.y - grab.y;\n if (Math.abs(x - d.x0) < SNAP) x = d.x0;\n if (Math.abs(x + TORN_W - d.x1) < SNAP) x = d.x1 - TORN_W;\n if (Math.abs(y - G) < SNAP) y = G;\n x = clamp(x, 0, W - TORN_W);\n y = clamp(y, 0, H - Math.min(cat.natural(id), H));\n return { state: touch({ ...s0, torn: [...s0.torn, { id, at: { x, y } }] }, id), label: \"Float here\", key: `float:${Math.round(x)},${Math.round(y)}` };\n },\n });\n }\n return out;\n}\n\n// --- tiling tree ---------------------------------------------------------------------------------------------------\n\n/**\n * The tree's targets are the slots an insertion produces: inserting at place i of a side column gives row i of its\n * n + 1 rows, inserting at place i of the centre gives dwindle's region i of n + 1. Those regions tile the column and\n * the centre, so a zone is where the item lands (unless that makes the newest tile float, which reshapes the rest);\n * the label says where it lands in the tree as laid out, and what floats.\n */\nfunction treeTargets({ c, s0, key, size }: DeskTargetInput): DeskTarget[] {\n const cat = c.desk.catalog;\n const out: DeskTarget[] = [];\n const area = treeArea(size, s0);\n // The tree as tiled: a column folded to its rail offers its rail instead of its rows.\n const t = treeShown(s0);\n const rails = treeRails(s0);\n // A side column is narrower than a document's minimum: a document dropped into one would only float.\n const sides = !isDocKey(key);\n for (const [side, group] of [[\"left\", \"L\"], [\"right\", \"R\"]] as const) {\n if (!sides || !rails[side]) continue;\n const hit = rect(side === \"left\" ? 0 : size.width - RAIL, BAR, RAIL, size.height - BAR);\n out.push({ id: `rail:${side}`, priority: 1, hit, make: fixed({ ...s0, tree: { ...s0.tree, [group]: [...s0.tree[group], key] } }, `${Cap(side)} rail: into the ${side} column`) });\n }\n if (sides && s0.tree.L.length === 0) out.push({ id: \"edge:L\", priority: 2, hit: rect(area.x, area.y, 30, area.height), make: fixed({ ...s0, tree: { ...s0.tree, L: [key] } }, \"New left column\") });\n if (sides && s0.tree.R.length === 0) out.push({ id: \"edge:R\", priority: 2, hit: rect(area.x + area.width - 30, area.y, 30, area.height), make: fixed({ ...s0, tree: { ...s0.tree, R: [key] } }, \"New right column\") });\n const x0 = area.x + (t.L.length ? TREE_COLUMN + G : 0);\n const x1 = area.x + area.width - (t.R.length ? TREE_COLUMN + G : 0);\n const middle = rect(x0, area.y, x1 - x0, area.height);\n const NAME = { L: \"Left column\", R: \"Right column\", C: \"Centre\" } as const;\n const seen: { label: string; hit: Rect }[] = [];\n const sameZone = (a: Rect, b: Rect): boolean => Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y), Math.abs(a.width - b.width), Math.abs(a.height - b.height)) < 0.5;\n for (const group of [\"L\", \"R\", \"C\"] as const) {\n const list = t[group];\n if (group !== \"C\" && list.length === 0) continue;\n if (group !== \"C\" && !sides) continue;\n for (let i = 0; i <= list.length; i++) {\n const state = { ...s0, tree: { ...s0.tree, [group]: insert(list, i, key) } };\n const shown = treeShown(state);\n // The slot as laid out once the tiles that break a minimum have floated (as laid out without them if it floats).\n const tiled = treeFloats(cat, shown, area);\n const zone = treeTiles(cat, tiled.floats.includes(key) ? shown : tiled.tree, area).get(key)!;\n const label = `${NAME[group]}, ${landing(c, shown, area, key, group, middle)}`;\n // Places that come out the same (around a floating tile) are one target: the keyboard stops there once.\n if (seen.some((o) => o.label === label && sameZone(o.hit, zone))) continue;\n seen.push({ label, hit: zone });\n out.push({ id: `tile:${group}:${i}`, priority: 2, hit: zone, make: fixed(state, label) });\n }\n }\n return out;\n}\n\n/**\n * Where `key` lands in the tree as laid out (after the newest tiles that break a minimum float): its row of its\n * column, or its region of the centre; or that it floats, or what floats because of it.\n */\nfunction landing(c: Construction, tree: DeskState[\"tree\"], area: Rect, key: string, group: \"L\" | \"R\" | \"C\", middle: Rect): string {\n const cat = c.desk.catalog;\n const { tree: tiled, floats } = treeFloats(cat, tree, area);\n if (floats.includes(key)) return \"it floats there (too small)\";\n const others = floats.length ? `; ${floats.map(cat.title).join(\", \")} ${floats.length > 1 ? \"float\" : \"floats\"}` : \"\";\n if (group !== \"C\") return `row ${tiled[group].indexOf(key) + 1} of ${tiled[group].length}${others}`;\n const x0 = tiled.L.length ? middle.x : area.x;\n const x1 = tiled.R.length ? middle.x + middle.width : area.x + area.width;\n return `${regionName(treeTiles(cat, tiled, area).get(key)!, rect(x0, area.y, x1 - x0, area.height))}${others}`;\n}\n\n/** Where a region lies in `whole`, in words: \"all of it\", \"left half\", \"top right\"… */\nfunction regionName(r: Rect, whole: Rect): string {\n const near = (a: number, b: number): boolean => Math.abs(a - b) < 1;\n const h = near(r.width, whole.width) ? \"\" : near(r.x, whole.x) ? \"left\" : near(r.x + r.width, whole.x + whole.width) ? \"right\" : \"middle\";\n const v = near(r.height, whole.height) ? \"\" : near(r.y, whole.y) ? \"top\" : near(r.y + r.height, whole.y + whole.height) ? \"bottom\" : \"middle\";\n if (!h && !v) return \"all of it\";\n if (!v) return `${h} part`;\n if (!h) return `${v} part`;\n return `${v} ${h}`;\n}\n\n// --- scrolling strip -----------------------------------------------------------------------------------------------\n\nfunction stripTargets({ c, s0, key, size, opt }: DeskTargetInput): DeskTarget[] {\n const cat = c.desk.catalog;\n const out: DeskTarget[] = [];\n const g = stripGeometry(cat, s0, size);\n const l0 = c.layout(s0, size, opt);\n // The laid-out columns, each with the state column it comes from and where in it it starts.\n const parts: { owner: number; start: number; keys: string[] }[] = [];\n s0.cols.forEach((col, owner) => {\n let start = 0;\n for (const keys of stripColumns(cat, [col], g.h)) {\n parts.push({ owner, start, keys });\n start += keys.length;\n }\n });\n const newColumn = (at: number, label: string): Made => ({ state: { ...s0, cols: insert(s0.cols, at, [key]) }, label, key: label });\n if (parts.length === 0) return [{ id: \"strip\", priority: 3, hit: rect(0, g.y, size.width, g.h), make: () => newColumn(0, \"The strip\") }];\n // Between columns: a band across each boundary where a state column starts or ends.\n for (let k = 0; k <= parts.length; k++) {\n if (k < parts.length && parts[k]!.start !== 0) continue;\n const x = k < parts.length ? g.xs[k]! - G / 2 : g.xs[parts.length - 1]! + g.widths[parts.length - 1]! + G / 2;\n const at = k < parts.length ? parts[k]!.owner : s0.cols.length;\n const label = k === 0 ? \"New column at the start\" : k === parts.length ? \"New column at the end\" : `New column before ${cat.title(parts[k]!.keys[0]!)}`;\n out.push({ id: `between:${at}`, priority: 2, hit: rect(x - 28, g.y, 56, g.h), make: () => newColumn(at, label) });\n }\n // Inside a column: each item's halves belong to the gaps above and below it.\n parts.forEach((part, k) => {\n const mids = part.keys.map((pk) => centre(l0.items.get(pk)!.rect).y);\n for (let j = 0; j <= part.keys.length; j++) {\n const [y0, y1] = slotSpan(mids, g.y, g.y + g.h, j);\n const at = part.start + j;\n const cols = s0.cols.map((col, ci) => (ci === part.owner ? insert(col, at, key) : col));\n const label = `Into the column with ${cat.title(s0.cols[part.owner]![0]!)} · ${at + 1}`;\n out.push({ id: `into:${part.owner}:${at}`, priority: 2, hit: rect(g.xs[k]!, y0, g.widths[k]!, y1 - y0), make: fixed({ ...s0, cols }, label) });\n }\n });\n return out;\n}\n\n// --- navigator and drawers -----------------------------------------------------------------------------------------\n\nfunction tabTargets({ c, s0, key, size, opt }: DeskTargetInput): DeskTarget[] {\n const out: DeskTarget[] = [];\n const id = idOf(key);\n const l0 = c.layout(s0, size, opt);\n const onSide = (side: Side, i: number): DeskState => touch({ ...s0, [side]: insert(s0[side], i, id), active: { ...s0.active, [side]: id } }, id);\n for (const side of [\"left\", \"right\"] as const) {\n const list = s0[side];\n const tabs = l0.items.get(`tabs:${side}`);\n const railed = l0.items.get(`rail:${side}`);\n if (tabs && !tabs.hidden) {\n const r = tabs.rect;\n const tw = r.width / (list.length + 1);\n out.push({\n id: `tabs:${side}`,\n priority: 1,\n hit: rect(r.x, r.y - 4, r.width, r.height + 8),\n make: (p) => {\n const i = clamp(Math.floor((p.x - r.x) / tw), 0, list.length);\n const label = `${Cap(side)} tab strip · tab ${i + 1}`;\n return { state: onSide(side, i), label, key: label };\n },\n stops: Array.from({ length: list.length + 1 }, (_, i) => ({ x: r.x + tw * (i + 0.5), y: r.y + r.height / 2 })),\n });\n const panel = list.map((p) => l0.items.get(panelKey(p))).find((pl) => pl && !pl.hidden);\n if (panel) out.push({ id: `side:${side}`, priority: 3, hit: panel.rect, make: fixed(onSide(side, list.length), `${Cap(side)} side`) });\n }\n if (railed && !railed.hidden) out.push({ id: `rail:${side}`, priority: 1, hit: railed.rect, make: fixed(onSide(side, list.length), `${Cap(side)} rail`) });\n if (list.length === 0 && (!railed || railed.hidden)) {\n const hit = side === \"left\" ? rect(0, 0, 40, size.height) : rect(size.width - 40, 0, 40, size.height);\n out.push({ id: `edge:${side}`, priority: 2, hit, make: fixed(onSide(side, 0), `${Cap(side)} side`) });\n }\n }\n return out;\n}\n\n// --- palettes ------------------------------------------------------------------------------------------------------\n\nfunction paletteTargets({ c, s0, key, size, opt }: DeskTargetInput): DeskTarget[] {\n const cat = c.desk.catalog;\n const out: DeskTarget[] = [];\n const id = idOf(key);\n const { area } = paletteArea(size, opt);\n const m = c.id === \"floatOver\" ? OVER_MARGIN : 0;\n const pw = paletteWidth(size.width);\n const ph = s0.folded.includes(id) ? TITLE : cat.natural(id);\n const narrow = size.width < PALETTE_NARROW;\n const anchor = { left: area.x + m, right: area.x + area.width - m - pw };\n const l0 = c.layout(s0, size, opt);\n const rectOf = (pid: number): Rect => l0.items.get(panelKey(pid))!.rect;\n const tb = l0.items.get(\"toolbar\");\n const pillRect = tb?.look === \"pill\" ? tb.rect : null;\n // A corner stack's column, down to just under its last palette: it joins the stack at the place under the pointer.\n for (const side of narrow ? ([\"right\"] as const) : ([\"left\", \"right\"] as const)) {\n const ids = narrow ? [...s0.stacks.left, ...s0.stacks.right] : [...s0.stacks[side]];\n const bottom = ids.length ? Math.max(...ids.map((pid) => rectOf(pid).y + rectOf(pid).height)) : area.y + m;\n const at = (y: number): number => ids.filter((pid) => centre(rectOf(pid)).y < y).length;\n const join = (i: number): Made => {\n let stacks = s0.stacks;\n if (!narrow) stacks = { ...stacks, [side]: insert(stacks[side], i, id) };\n else if (i <= s0.stacks.left.length) stacks = { ...stacks, left: insert(stacks.left, i, id) };\n else stacks = { ...stacks, right: insert(stacks.right, i - s0.stacks.left.length, id) };\n const label = `${narrow ? \"The\" : Cap(side)} corner stack · ${i + 1}`;\n return { state: touch({ ...s0, stacks }, id), label, key: label };\n };\n out.push({\n id: `stack:${side}`,\n priority: 2,\n hit: rect(anchor[side], area.y, pw, bottom + 30 - area.y),\n make: (p) => join(at(p.y)),\n stops: [area.y + m, ...ids.map((pid) => rectOf(pid).y + rectOf(pid).height + G / 2)].map((y) => ({ x: anchor[side] + pw / 2, y })),\n });\n }\n // Anywhere: snapped to the area's edges (over the documents also to other palettes' edges), kept inside the area.\n out.push({\n id: \"free\",\n priority: 3,\n free: true,\n hit: area,\n make: (p, grab) => {\n let x = p.x - grab.x;\n let y = p.y - grab.y;\n const snaps: string[] = [];\n if (Math.abs(x - anchor.left) < SNAP) [x, snaps[0]] = [anchor.left, \"left edge\"];\n else if (Math.abs(x - anchor.right) < SNAP) [x, snaps[0]] = [anchor.right, \"right edge\"];\n if (Math.abs(y - (area.y + m)) < SNAP) [y, snaps[1]] = [area.y + m, \"top\"];\n else if (Math.abs(y + ph - (area.y + area.height - m)) < SNAP) [y, snaps[1]] = [area.y + area.height - m - ph, \"bottom\"];\n if (c.id === \"floatOver\") {\n for (const [k, pl] of l0.items) {\n if (!isPanelKey(k) || pl.hidden) continue;\n const o = pl.rect;\n if (snaps[0] === undefined && Math.abs(x - o.x) < SNAP) [x, snaps[0]] = [o.x, `${cat.title(k)}'s edge`];\n if (snaps[1] === undefined && Math.abs(y - (o.y + o.height + G)) < SNAP && x < o.x + o.width && x + pw > o.x) [y, snaps[1]] = [o.y + o.height + G, `under ${cat.title(k)}`];\n }\n }\n x = clamp(x, area.x, area.x + area.width - pw);\n y = clamp(y, area.y, area.y + area.height - ph);\n // Never under a floating pill: it moves clear, below a pill at the top, above one at the bottom.\n if (pillRect && overlap(rect(x, y, pw, ph), pillRect) > 0) {\n y = pillRect.y < size.height / 2 ? pillRect.y + pillRect.height + G : pillRect.y - G - ph;\n y = clamp(y, 0, size.height - ph);\n snaps[1] = \"clear of the toolbar\";\n }\n const how = snaps.filter(Boolean).join(\", \");\n return { state: touch({ ...s0, free: [...s0.free, { id, at: { x, y } }] }, id), label: how ? `Here · snapped to the ${how}` : \"Here\", key: `free:${Math.round(x)},${Math.round(y)}` };\n },\n });\n return out;\n}\n\n/** The drop targets for dragging `key` in construction `c`, in a fixed order (the keyboard's). */\nexport function deskTargets(input: DeskTargetInput): DeskTarget[] {\n switch (input.c.id) {\n case \"docks\":\n case \"docksFull\":\n case \"dockTear\":\n return dockTargets(input);\n case \"tree\":\n return treeTargets(input);\n case \"strip\":\n return stripTargets(input);\n case \"navigator\":\n case \"drawers\":\n return tabTargets(input);\n case \"floatOver\":\n case \"floatAround\":\n return paletteTargets(input);\n }\n}\n\n/** A target's drop for a pointer at `p` holding the item at `grab`, laid out: where the item lands. */\nexport function deskDrop(input: DeskTargetInput, target: DeskTarget, p: Point, grab: Point): DeskDrop {\n const made = target.make(p, grab);\n const layout = input.c.layout(made.state, input.size, input.opt);\n const placed = layout.items.get(input.key)!;\n return { ...made, key: `${target.id}|${made.key}`, target: target.id, layout, slot: placed.rect, hidden: !!placed.hidden, free: !!target.free };\n}\n","/**\n * Toolbar group targets: a host's insertion points (lines between groups, priority 2) and empty zones (priority 1),\n * and floating over the content (priority 3). A group's own place is home, never a target: its stretch comes back\n * in `own`, for the consumer to join to the drag's home. A place `allows` forbids is no host target either.\n */\nimport { rect, type Point, type Rect } from \"../geometry\";\nimport { ZONES, floatGroup, insertGroup, insertions, isOwnPlace, layoutHost, vertical, withoutGroup, type Arrangement, type GroupSize, type HostId, type HostSpec, type Place } from \"../toolbar\";\nimport type { Target } from \"./target\";\n\n/** How far a line's hit reaches beyond the host's thickness on each side; an empty zone's. */\nexport const LINE_REACH = 12;\nexport const ZONE_REACH = 4;\n\nexport interface ToolbarTarget<G extends string = string> extends Target {\n readonly label: string;\n readonly kind: \"line\" | \"zone\";\n readonly place: Place;\n /** It lands in a side edge's column. */\n readonly vertical: boolean;\n /** The insertion line (3 px, inset 4 px from the host's sides) in stage coordinates; for an empty zone, at its middle. */\n readonly line: Rect;\n /** Where the group lands, in stage coordinates: its rect in the host once the drop is made. */\n readonly slot: Rect;\n /** The arrangement a drop here makes. */\n readonly arrangement: Arrangement<G>;\n}\n\n/**\n * Where a group may go: a host's place, or floating. `hostTargets` leaves out a place it refuses; for floating, the\n * consumer adds `floatTarget` only when `allows(group, \"float\")`, so one rule covers both. `place.index` counts the\n * groups in that zone without the dragged one (as `insertGroup` does): 0 is before all of the others.\n */\nexport type Allows<G extends string> = (group: G, place: Place | \"float\") => boolean;\n\nexport interface HostTargetsInput<G extends string> {\n readonly group: G;\n /** The arrangement as it is, the group still in its place. */\n readonly arrangement: Arrangement<G>;\n readonly host: HostId;\n /** The host's top-left corner in stage coordinates. */\n readonly origin: Point;\n readonly spec: HostSpec<G>;\n readonly size: GroupSize<G>;\n /** A group's name in the labels (default: its id). */\n readonly label?: (g: G) => string;\n /** Where the group may go (default: anywhere). A place it may not go is left out. */\n readonly allows?: Allows<G>;\n /**\n * Where this host is, and how long, once a drop has made `arrangement` (a host may grow or appear when it gets a\n * group); default: where it is now. The targets' `slot`s are laid out there.\n */\n readonly hostAfter?: (arrangement: Arrangement<G>) => { readonly origin: Point; readonly spec: HostSpec<G> };\n}\n\n/** The targets a host offers a dragged group, zone by zone in order, and the stretches that are its own place. */\nexport function hostTargets<G extends string>(input: HostTargetsInput<G>): { targets: ToolbarTarget<G>[]; own: Rect[] } {\n const { group, arrangement: a, host: h, origin: o, spec } = input;\n const slotIn = (after: Arrangement<G>): Rect => {\n const at = input.hostAfter?.(after) ?? { origin: o, spec };\n const r = layoutHost(after[h], h, at.spec, input.size, after.collapsed).slots.get(group)!;\n return rect(at.origin.x + r.x, at.origin.y + r.y, r.width, r.height);\n };\n const a0 = withoutGroup(a, group);\n const l = layoutHost(a0[h], h, spec, input.size, a0.collapsed);\n const v = vertical(h);\n const T = spec.thickness;\n const across = (s0: number, s1: number, reach: number): Rect => (v ? rect(o.x - reach, o.y + s0, T + 2 * reach, s1 - s0) : rect(o.x + s0, o.y - reach, s1 - s0, T + 2 * reach));\n const targets: ToolbarTarget<G>[] = [];\n const own: Rect[] = [];\n const pts = insertions(a0[h], h, spec, l, input.label).sort((p, q) => ZONES.indexOf(p.zone) - ZONES.indexOf(q.zone) || p.index - q.index);\n for (const p of pts) {\n const [s0, s1] = p.span;\n const place: Place = { host: h, zone: p.zone, index: p.index };\n if (isOwnPlace(a, group, place)) {\n own.push(across(s0, s1, LINE_REACH));\n continue;\n }\n if (input.allows && !input.allows(group, place)) continue;\n const after = insertGroup(a, group, h, p.zone, p.index);\n targets.push({\n id: `g:${h}:${p.zone}:${p.index}`,\n priority: p.empty ? 1 : 2,\n hit: p.empty ? across(s0, s1, ZONE_REACH) : across(s0, s1, LINE_REACH),\n label: p.label,\n kind: p.empty ? \"zone\" : \"line\",\n place,\n vertical: v,\n line: v ? rect(o.x + 4, o.y + p.at - 1.5, T - 8, 3) : rect(o.x + p.at - 1.5, o.y + 4, 3, T - 8),\n slot: slotIn(after),\n arrangement: after,\n });\n }\n return { targets, own };\n}\n\nexport interface ToolbarFloatTarget<G extends string = string> extends Target {\n readonly label: string;\n readonly kind: \"float\";\n /** The group's top-left corner relative to the stage, and the arrangement, for a drop with the pointer at `p`. */\n readonly make: (p: Point, grab: Point) => Arrangement<G>;\n}\n\nexport interface FloatTargetInput<G extends string> {\n readonly group: G;\n readonly arrangement: Arrangement<G>;\n /** The content the group floats over, in stage coordinates. */\n readonly stage: Rect;\n /** The group's size as it floats. */\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * Floating over the content (`stage`, in stage coordinates): the group's top-left corner kept on the stage. A\n * group that may not float simply gets no float target: add it only when your rule allows it, with the same\n * `allows` you give `hostTargets`: `...(allows(group, \"float\") ? [floatTarget(input)] : [])`.\n */\nexport function floatTarget<G extends string>(input: FloatTargetInput<G>): ToolbarFloatTarget<G> {\n const { group, arrangement, stage } = input;\n return {\n id: \"g:float\",\n priority: 3,\n free: true,\n hit: stage,\n label: \"Float here\",\n kind: \"float\",\n // Kept on the stage; a group larger than the stage keeps its top-left corner on the stage's.\n make: (p, grab) => {\n const x = Math.max(0, Math.min(p.x - grab.x - stage.x, stage.width - input.width));\n const y = Math.max(0, Math.min(p.y - grab.y - stage.y, stage.height - input.height));\n return floatGroup(arrangement, group, x, y);\n },\n };\n}\n","/**\n * Keyboard stops: every place a toolbar group can go, in one list in reading order, for the arrow keys and a\n * \"Move to\" menu alike. A consumer steps through the list; it never parses target ids.\n */\nimport { centre, type Point } from \"../geometry\";\nimport { HOST_LABEL, ZONES, zoneLabel, type Arrangement, type HostId, type Place } from \"../toolbar\";\nimport type { ToolbarFloatTarget, ToolbarTarget } from \"./toolbar\";\n\n/** The hosts in reading order: the title bar, the top bar, the left edge, the right edge, the bottom bar. */\nexport const HOST_READING_ORDER: readonly HostId[] = [\"title\", \"top\", \"left\", \"right\", \"bottom\"];\n\nexport interface KeyboardStop<G extends string = string> {\n /** The target's id (\"home\" for the group's own place). */\n readonly id: string;\n readonly label: string;\n /** Where in a host it is; null for floating. */\n readonly place: Place | null;\n /** The arrangement a move here makes (the current one for home). */\n readonly arrangement: Arrangement<G>;\n /** The group's own place: where the keyboard starts, and a move there changes nothing. */\n readonly home: boolean;\n}\n\nexport interface KeyboardStopsOptions<G extends string> {\n /** The group's own place, as a stop of its own in its reading-order position (the targets leave it out). */\n readonly home?: { readonly place: Place; readonly arrangement: Arrangement<G>; readonly label?: string };\n /**\n * Floating targets as stops at the end (default: left out). `at` is where the pointer would drop it and `grab`\n * where it holds the group (default: the float target's middle, the group's top-left corner).\n */\n readonly floats?: boolean | { readonly at: Point; readonly grab?: Point };\n}\n\nconst isFloat = <G extends string>(t: ToolbarTarget<G> | ToolbarFloatTarget<G>): t is ToolbarFloatTarget<G> => t.kind === \"float\";\n\n/** Reading order of a place: host, then zone, then index. */\nconst rank = (p: Place): number => HOST_READING_ORDER.indexOf(p.host) * 3000 + ZONES.indexOf(p.zone) * 1000 + p.index;\n\n/**\n * The places a group can go, in reading order: hosts title → top → left → right → bottom, zones start → centre →\n * end, then each insertion point in order; the group's own place among them when `home` is given; floating targets\n * at the end only when `floats` asks for them. One list serves the arrow keys and a \"Move to\" menu.\n */\nexport function keyboardStops<G extends string>(targets: readonly (ToolbarTarget<G> | ToolbarFloatTarget<G>)[], options: KeyboardStopsOptions<G> = {}): KeyboardStop<G>[] {\n const placed: KeyboardStop<G>[] = [];\n const floating: KeyboardStop<G>[] = [];\n for (const t of targets) {\n if (!isFloat(t)) {\n placed.push({ id: t.id, label: t.label, place: t.place, arrangement: t.arrangement, home: false });\n continue;\n }\n if (!options.floats) continue;\n const at = options.floats === true ? centre(t.hit) : options.floats.at;\n const grab = options.floats === true ? { x: 0, y: 0 } : (options.floats.grab ?? { x: 0, y: 0 });\n floating.push({ id: t.id, label: t.label, place: null, arrangement: t.make(at, grab), home: false });\n }\n const h = options.home;\n if (h) placed.push({ id: \"home\", label: h.label ?? `${HOST_LABEL[h.place.host]} · ${zoneLabel(h.place.host, h.place.zone)} · current place`, place: h.place, arrangement: h.arrangement, home: true });\n placed.sort((a, b) => rank(a.place!) - rank(b.place!));\n return [...placed, ...floating];\n}\n","/**\n * A pane's drop zones: a centre rectangle (put it in the pane's place, merge or swap) and four edge trapezoids (put\n * it beside the pane on that side), formed by the diagonals from each pane corner to the nearest corner of the\n * centre. The five zones tile the pane with no gaps and no overlaps.\n *\n * Meant for `@n-uf/motion/drag`'s `placeOf(target, point, previous)`: one target per pane, its place the zone under\n * the pointer, the previous zone held for `hysteresis` px past its boundary so a pointer resting on a boundary\n * doesn't flicker. A zone that takes no drop is the consumer's to answer with a null place.\n */\nimport type { Point, Rect, Size } from \"../geometry\";\n\nexport type PaneZone = \"center\" | \"top\" | \"right\" | \"bottom\" | \"left\";\n\n/** The edge zones in their tie-break order: at an exact diagonal the first wins (a corner resolves to `top`). */\nexport const PANE_EDGES: readonly Exclude<PaneZone, \"center\">[] = [\"top\", \"right\", \"bottom\", \"left\"];\n\nexport interface PaneZoneOptions {\n /** The centre rectangle's share of each pane axis (0.05 to 0.95); `centreX` / `centreY` override one axis. */\n readonly centre?: number;\n readonly centreX?: number;\n readonly centreY?: number;\n /** The least the centre spans on either axis, px (a small pane keeps a usable centre). */\n readonly centreMin?: number;\n /** How far past its boundary the previous zone holds, px. */\n readonly hysteresis?: number;\n /** Device pixels per px: the centre's edges snap to that grid, as the browser draws them. */\n readonly pixelRatio?: number;\n}\n\nexport const PANE_ZONE_DEFAULTS: Required<Omit<PaneZoneOptions, \"centreX\" | \"centreY\">> = { centre: 0.34, centreMin: 24, hysteresis: 6, pixelRatio: 1 };\n\n/** The centre rectangle of a pane of `size`, in pane-local px (origin at the pane's top-left corner). */\nexport interface PaneCentre {\n readonly width: number;\n readonly height: number;\n readonly left: number;\n readonly top: number;\n readonly right: number;\n readonly bottom: number;\n}\n\nconst clamp = (v: number, lo: number, hi: number): number => Math.min(Math.max(v, lo), hi);\nconst snap = (v: number, ratio: number): number => Math.round(v * ratio) / ratio;\n\n/** Where the centre rectangle of a pane of `size` lies, by these options. */\nexport function paneCentre(size: Size, options: PaneZoneOptions = {}): PaneCentre {\n const o = { ...PANE_ZONE_DEFAULTS, ...options };\n const width = Math.max(0, size.width);\n const height = Math.max(0, size.height);\n const ratio = o.pixelRatio > 0 ? o.pixelRatio : 1;\n const rx = clamp(o.centreX ?? o.centre, 0.05, 0.95);\n const ry = clamp(o.centreY ?? o.centre, 0.05, 0.95);\n const cw = Math.min(width, Math.max(width * rx, Math.min(o.centreMin, width)));\n const ch = Math.min(height, Math.max(height * ry, Math.min(o.centreMin, height)));\n return { width, height, left: snap((width - cw) / 2, ratio), top: snap((height - ch) / 2, ratio), right: snap((width + cw) / 2, ratio), bottom: snap((height + ch) / 2, ratio) };\n}\n\n/** How deep past the centre a point is toward each edge, as a share of that edge's band (positive: in that band). */\nfunction depths(p: Point, c: PaneCentre): { zone: Exclude<PaneZone, \"center\">; depth: number }[] {\n return [\n { zone: \"top\", depth: (c.top - p.y) / Math.max(c.top, 1) },\n { zone: \"right\", depth: (p.x - c.right) / Math.max(c.width - c.right, 1) },\n { zone: \"bottom\", depth: (p.y - c.bottom) / Math.max(c.height - c.bottom, 1) },\n { zone: \"left\", depth: (c.left - p.x) / Math.max(c.left, 1) },\n ];\n}\n\n/** The zone a pane-local point is in, by position alone. */\nfunction rawZone(p: Point, c: PaneCentre): PaneZone {\n if (p.x >= c.left && p.x <= c.right && p.y >= c.top && p.y <= c.bottom) return \"center\";\n let best: PaneZone = \"center\";\n let deepest = 0;\n for (const d of depths(p, c)) {\n if (d.depth > deepest) {\n deepest = d.depth;\n best = d.zone;\n }\n }\n return best;\n}\n\n/** `p` moved `by` px into `zone` (toward the pane's middle for the centre). */\nfunction toward(p: Point, zone: PaneZone, by: number, c: PaneCentre): Point {\n if (zone === \"left\") return { x: p.x - by, y: p.y };\n if (zone === \"right\") return { x: p.x + by, y: p.y };\n if (zone === \"top\") return { x: p.x, y: p.y - by };\n if (zone === \"bottom\") return { x: p.x, y: p.y + by };\n const dx = c.width / 2 - p.x;\n const dy = c.height / 2 - p.y;\n const len = Math.hypot(dx, dy);\n if (len <= by || len === 0) return { x: c.width / 2, y: c.height / 2 };\n return { x: p.x + (dx / len) * by, y: p.y + (dy / len) * by };\n}\n\n/**\n * The zone of `rect` (a pane, in the same coordinates as `point`) the point is in. With a `previous` zone, the point\n * has to be `hysteresis` px past that zone's boundary before the zone changes: no time-based debounce, only geometry.\n */\nexport function paneZone(rect: Rect, point: Point, previous: PaneZone | null = null, options: PaneZoneOptions = {}): PaneZone {\n const c = paneCentre(rect, options);\n const p = { x: point.x - rect.x, y: point.y - rect.y };\n const raw = rawZone(p, c);\n const by = Math.max(0, options.hysteresis ?? PANE_ZONE_DEFAULTS.hysteresis);\n if (previous === null || by === 0 || previous === raw) return raw;\n return rawZone(toward(p, previous, by, c), c) === previous ? previous : raw;\n}\n"],"mappings":"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","names":["clamp","rect"]}
package/dist/toolbar.d.ts CHANGED
@@ -73,8 +73,37 @@ declare const isEmptyHost: <G extends string>(a: Arrangement<G>, h: HostId) => b
73
73
  declare function sameArrangement<G extends string>(a: Arrangement<G>, b: Arrangement<G>): boolean;
74
74
  /** Dropping `g` at `place` changes nothing: it is the group's own place, its home, never a target. */
75
75
  declare const isOwnPlace: <G extends string>(a: Arrangement<G>, g: G, place: Place) => boolean;
76
+ /** An arrangement as stored: version 1. */
77
+ interface StoredArrangement<G extends string = string> {
78
+ readonly v: 1;
79
+ readonly hosts: Readonly<Record<HostId, Readonly<Record<ZoneId, readonly G[]>>>>;
80
+ readonly collapsed: readonly G[];
81
+ readonly floating: readonly {
82
+ readonly id: G;
83
+ readonly x: number;
84
+ readonly y: number;
85
+ readonly from: Place | null;
86
+ }[];
87
+ }
88
+ /** What reading a stored arrangement gives: the arrangement, or why it couldn't be read. Never a throw. */
89
+ type ArrangementRead<G extends string = string> = {
90
+ readonly ok: true;
91
+ readonly arrangement: Arrangement<G>;
92
+ } | {
93
+ readonly ok: false;
94
+ readonly error: string;
95
+ };
96
+ /** The arrangement as plain data with its version, ready for `JSON.stringify`. */
97
+ declare function storeArrangement<G extends string>(a: Arrangement<G>): StoredArrangement<G>;
98
+ /**
99
+ * A stored arrangement read back from parsed data (`JSON.parse`'s result, not the string). Anything else gives
100
+ * `{ ok: false, error }`, never a throw: an unknown version, a missing host or zone, a value of the wrong kind, a
101
+ * group in two places, or (with `groups`) a group the consumer doesn't have. A group the consumer has but the stored
102
+ * arrangement lacks is no error: the consumer places it (`resetGroup` with its default arrangement, say).
103
+ */
104
+ declare function readArrangement<G extends string = string>(data: unknown, groups?: readonly G[]): ArrangementRead<G>;
76
105
  /** A host's room: its length and thickness, and what its ends keep for themselves (window controls, a margin). */
77
- interface HostSpec {
106
+ interface HostSpec<G extends string = string> {
78
107
  readonly length: number;
79
108
  readonly thickness: number;
80
109
  readonly lead: number;
@@ -87,6 +116,11 @@ interface HostSpec {
87
116
  readonly min: number;
88
117
  readonly margin: number;
89
118
  };
119
+ /**
120
+ * How long each group keeps its full form when room runs out (default 0): groups with a lower keep collapse
121
+ * first, and among equal keeps the trailing end goes first. A primary group at the end keeps open with a keep of 1.
122
+ */
123
+ readonly keep?: Readonly<Partial<Record<G, number>>>;
90
124
  }
91
125
  /** A group's size in a host, expanded or collapsed: along it (`along`) and across it (`across`). */
92
126
  type GroupSize<G extends string = string> = (id: G, vertical: boolean, collapsed: boolean) => {
@@ -105,8 +139,11 @@ interface HostLayout<G extends string = string> {
105
139
  }
106
140
  /** How long the host's groups need it to be, collapsing only those in `collapsed`. */
107
141
  declare function hostNeed<G extends string>(zones: Zones<G>, collapsed: readonly G[], spec: Pick<HostSpec, "lead" | "tail" | "center">, size: GroupSize<G>, v: boolean): number;
108
- /** Lay a host out: zones along its length; when they don't fit, groups collapse from the trailing end. */
109
- declare function layoutHost<G extends string>(zones: Zones<G>, h: HostId, spec: HostSpec, size: GroupSize<G>, byHand?: readonly G[]): HostLayout<G>;
142
+ /**
143
+ * Lay a host out: zones along its length; when they don't fit, groups collapse, the lowest keep first and, among
144
+ * equal keeps, from the trailing end.
145
+ */
146
+ declare function layoutHost<G extends string>(zones: Zones<G>, h: HostId, spec: HostSpec<G>, size: GroupSize<G>, byHand?: readonly G[]): HostLayout<G>;
110
147
  /** A place a dragged group can go: a zone of a host and the index among the others there, and where it is. */
111
148
  interface Insertion extends Place {
112
149
  /** Along the host: the insertion line (between groups); for an empty zone, the zone's middle. */
@@ -123,6 +160,6 @@ interface Insertion extends Place {
123
160
  * (start), middle third (centre) or last third (end), less what its neighbours' groups take. `label` names a group
124
161
  * in the "between" labels (default: its id).
125
162
  */
126
- declare function insertions<G extends string>(zones: Zones<G>, h: HostId, spec: HostSpec, l: HostLayout<G>, label?: (g: G) => string): Insertion[];
163
+ declare function insertions<G extends string>(zones: Zones<G>, h: HostId, spec: HostSpec<G>, l: HostLayout<G>, label?: (g: G) => string): Insertion[];
127
164
 
128
- export { type Arrangement, type Floating, GAP, type GroupSize, HOSTS, HOST_LABEL, type HostId, type HostLayout, type HostSpec, type Insertion, type Place, ZONES, ZONE_GAP, type ZoneId, type Zones, arrangement, dockGroup, floatGroup, hostNeed, hostOf, insertGroup, insertions, isEmptyHost, isOwnPlace, layoutHost, moveBar, moveToHost, resetGroup, sameArrangement, toggleCollapsed, vertical, withoutGroup, zoneLabel };
165
+ export { type Arrangement, type ArrangementRead, type Floating, GAP, type GroupSize, HOSTS, HOST_LABEL, type HostId, type HostLayout, type HostSpec, type Insertion, type Place, type StoredArrangement, ZONES, ZONE_GAP, type ZoneId, type Zones, arrangement, dockGroup, floatGroup, hostNeed, hostOf, insertGroup, insertions, isEmptyHost, isOwnPlace, layoutHost, moveBar, moveToHost, readArrangement, resetGroup, sameArrangement, storeArrangement, toggleCollapsed, vertical, withoutGroup, zoneLabel };
package/dist/toolbar.js CHANGED
@@ -16,13 +16,15 @@ import {
16
16
  layoutHost,
17
17
  moveBar,
18
18
  moveToHost,
19
+ readArrangement,
19
20
  resetGroup,
20
21
  sameArrangement,
22
+ storeArrangement,
21
23
  toggleCollapsed,
22
24
  vertical,
23
25
  withoutGroup,
24
26
  zoneLabel
25
- } from "./chunk-PBH6TKET.js";
27
+ } from "./chunk-WLMJ6JGO.js";
26
28
  export {
27
29
  GAP,
28
30
  HOSTS,
@@ -41,8 +43,10 @@ export {
41
43
  layoutHost,
42
44
  moveBar,
43
45
  moveToHost,
46
+ readArrangement,
44
47
  resetGroup,
45
48
  sameArrangement,
49
+ storeArrangement,
46
50
  toggleCollapsed,
47
51
  vertical,
48
52
  withoutGroup,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@n-uf/placement",
3
- "version": "26.11.4",
3
+ "version": "26.11.6",
4
4
  "description": "Where things go: desk docks, rails and palettes, toolbar groups, and the drop targets for each. Pure state in, rects out. No DOM, no framework, no motion.",
5
5
  "keywords": [
6
6
  "layout",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/toolbar/index.ts"],"sourcesContent":["/**\n * @n-uf/placement/toolbar: toolbar groups as a pure model. Commands come in groups, each sitting in one of five\n * hosts (the title bar, the top toolbar, a bottom toolbar, a left and a right side toolbar), in one of three zones\n * (start, centre, end; top, middle, bottom on a side), in order; or floating. A toolbar is a slot: it never moves\n * itself, its groups do. Group sizes come in as numbers; group ids are the consumer's strings.\n *\n * - A host lays its zones out along its length: start from the lead, end up to the tail, the centre centred on the\n * whole host; when the centre would hit the start or end zone it centres in the space between them.\n * - Toolbars never wrap: when room runs out, groups collapse to their handle and » from the trailing end. A group\n * collapsed by hand stays collapsed.\n * - A group drop lands in a zone before all, after all, or between two groups. An empty zone is a target of its\n * own. A group's own place is home: a drop there changes nothing.\n */\nimport type { Rect } from \"../geometry\";\n\nexport type HostId = \"title\" | \"top\" | \"bottom\" | \"left\" | \"right\";\nexport type ZoneId = \"start\" | \"center\" | \"end\";\nexport type Zones<G extends string = string> = Readonly<Record<ZoneId, readonly G[]>>;\n\n/** Where a group is docked: a host, a zone, and its index there. */\nexport interface Place {\n readonly host: HostId;\n readonly zone: ZoneId;\n readonly index: number;\n}\n\n/** A group floating over the content: its top-left corner, and where it was docked. */\nexport interface Floating<G extends string = string> {\n readonly id: G;\n readonly x: number;\n readonly y: number;\n readonly from: Place | null;\n}\n\nexport type Arrangement<G extends string = string> = Readonly<Record<HostId, Zones<G>>> & {\n /** Groups collapsed to their handle and » by hand. */\n readonly collapsed: readonly G[];\n readonly floating: readonly Floating<G>[];\n};\n\nexport const HOSTS: readonly HostId[] = [\"title\", \"top\", \"bottom\", \"left\", \"right\"];\nexport const ZONES: readonly ZoneId[] = [\"start\", \"center\", \"end\"];\n\nexport const HOST_LABEL: Readonly<Record<HostId, string>> = { title: \"Title bar\", top: \"Top bar\", bottom: \"Bottom bar\", left: \"Left edge\", right: \"Right edge\" };\nexport const zoneLabel = (h: HostId, z: ZoneId): string => (vertical(h) ? { start: \"top\", center: \"middle\", end: \"bottom\" } : { start: \"left\", center: \"centre\", end: \"right\" })[z];\n\n/** The side edges hold their groups in a column. */\nexport const vertical = (h: HostId): boolean => h === \"left\" || h === \"right\";\n\n/** Between two groups (the handle of the second is the separator). */\nexport const GAP = 12;\n/** Between zones. */\nexport const ZONE_GAP = 16;\n\n/** An arrangement from the zones each host has (the rest empty), nothing collapsed or floating. */\nexport function arrangement<G extends string>(p: Partial<Record<HostId, Partial<Record<ZoneId, readonly G[]>>>>): Arrangement<G> {\n const z = (q: Partial<Record<ZoneId, readonly G[]>> = {}): Zones<G> => ({ start: [], center: [], end: [], ...q });\n return { title: z(p.title), top: z(p.top), bottom: z(p.bottom), left: z(p.left), right: z(p.right), collapsed: [], floating: [] };\n}\n\n// --- changes -------------------------------------------------------------------------------------------------------\n\nconst mapHosts = <G extends string>(a: Arrangement<G>, f: (h: HostId, zone: ZoneId, list: readonly G[]) => readonly G[]): Arrangement<G> => ({\n ...a,\n ...(Object.fromEntries(HOSTS.map((h) => [h, Object.fromEntries(ZONES.map((zn) => [zn, f(h, zn, a[h][zn])]))])) as Record<HostId, Zones<G>>),\n});\n\n/** Where `g` is docked (null: floating or absent). */\nexport function hostOf<G extends string>(a: Arrangement<G>, g: G): Place | null {\n for (const h of HOSTS) for (const zn of ZONES) if (a[h][zn].includes(g)) return { host: h, zone: zn, index: a[h][zn].indexOf(g) };\n return null;\n}\n\n/** The arrangement with group `g` lifted out (of its host, or from floating). */\nexport const withoutGroup = <G extends string>(a: Arrangement<G>, g: G): Arrangement<G> => ({ ...mapHosts(a, (_h, _z, l) => l.filter((x) => x !== g)), floating: a.floating.filter((f) => f.id !== g) });\n\n/** The arrangement with `g` docked at place `i` of zone `zone` of host `h` (`i` counts the others). */\nexport function insertGroup<G extends string>(a: Arrangement<G>, g: G, h: HostId, zone: ZoneId, i: number): Arrangement<G> {\n return mapHosts(withoutGroup(a, g), (hh, zz, l) => (hh === h && zz === zone ? [...l.slice(0, i), g, ...l.slice(i)] : l));\n}\n\n/** `g` floating with its top-left corner at (x, y); it remembers where it was docked. */\nexport function floatGroup<G extends string>(a: Arrangement<G>, g: G, x: number, y: number): Arrangement<G> {\n const from = hostOf(a, g) ?? a.floating.find((f) => f.id === g)?.from ?? null;\n const out = withoutGroup(a, g);\n return { ...out, floating: [...out.floating, { id: g, x, y, from }] };\n}\n\n/** `g` at `place`, its index kept within the zone. */\nconst dockAt = <G extends string>(a: Arrangement<G>, g: G, place: Place): Arrangement<G> =>\n insertGroup(a, g, place.host, place.zone, Math.min(place.index, a[place.host][place.zone].filter((x) => x !== g).length));\n\n/** `g` docked again where it floated from, else where `home` (the default arrangement) has it. */\nexport function dockGroup<G extends string>(a: Arrangement<G>, g: G, home: Arrangement<G>): Arrangement<G> {\n const from = a.floating.find((f) => f.id === g)?.from ?? hostOf(home, g);\n return from ? dockAt(a, g, from) : a;\n}\n\n/** `g` back where `home` (the default arrangement) has it, expanded. */\nexport function resetGroup<G extends string>(a: Arrangement<G>, g: G, home: Arrangement<G>): Arrangement<G> {\n const place = hostOf(home, g);\n const out = place ? dockAt(a, g, place) : a;\n return { ...out, collapsed: out.collapsed.filter((x) => x !== g) };\n}\n\n/** `g` moved to host `h`: after its last group (in its last zone that has any), or into its start zone. */\nexport function moveToHost<G extends string>(a: Arrangement<G>, g: G, h: HostId): Arrangement<G> {\n const rest = withoutGroup(a, g);\n const zone = [...ZONES].reverse().find((zn) => rest[h][zn].length > 0) ?? \"start\";\n return insertGroup(a, g, h, zone, rest[h][zone].length);\n}\n\n/** Every group of host `from` moved to host `to`, zone by zone, after the groups there. */\nexport function moveBar<G extends string>(a: Arrangement<G>, from: HostId, to: HostId): Arrangement<G> {\n if (from === to) return a;\n return mapHosts(a, (h, zn, l) => (h === from ? [] : h === to ? [...l, ...a[from][zn]] : l));\n}\n\nexport const toggleCollapsed = <G extends string>(a: Arrangement<G>, g: G): Arrangement<G> => ({ ...a, collapsed: a.collapsed.includes(g) ? a.collapsed.filter((x) => x !== g) : [...a.collapsed, g] });\n\nexport const isEmptyHost = <G extends string>(a: Arrangement<G>, h: HostId): boolean => ZONES.every((zn) => a[h][zn].length === 0);\n\nconst samePlace = (a: Place | null, b: Place | null): boolean => (a === null || b === null ? a === b : a.host === b.host && a.zone === b.zone && a.index === b.index);\n\n/**\n * The same arrangement: the same groups in the same order in every zone of every host, the same groups collapsed,\n * and the same groups floating at the same corner with the same home (in any order: neither list's order means\n * anything). Compared field by field, so how the arrangement was written down (its keys' order) doesn't matter.\n */\nexport function sameArrangement<G extends string>(a: Arrangement<G>, b: Arrangement<G>): boolean {\n const sameList = (x: readonly G[], y: readonly G[]): boolean => x.length === y.length && x.every((g, i) => g === y[i]);\n const sameSet = (x: readonly G[], y: readonly G[]): boolean => x.length === y.length && x.every((g) => y.includes(g));\n if (!HOSTS.every((h) => ZONES.every((zn) => sameList(a[h][zn], b[h][zn])))) return false;\n if (!sameSet(a.collapsed, b.collapsed) || a.floating.length !== b.floating.length) return false;\n return a.floating.every((f) => {\n const o = b.floating.find((x) => x.id === f.id);\n return !!o && o.x === f.x && o.y === f.y && samePlace(o.from, f.from);\n });\n}\n\n/** Dropping `g` at `place` changes nothing: it is the group's own place, its home, never a target. */\nexport const isOwnPlace = <G extends string>(a: Arrangement<G>, g: G, place: Place): boolean => sameArrangement(insertGroup(a, g, place.host, place.zone, place.index), a);\n\n// --- a host's layout -----------------------------------------------------------------------------------------------\n\n/** A host's room: its length and thickness, and what its ends keep for themselves (window controls, a margin). */\nexport interface HostSpec {\n readonly length: number;\n readonly thickness: number;\n readonly lead: number;\n readonly tail: number;\n /**\n * The centre zone's minimum length and its margin on each side, counted in the need whether it holds groups or\n * not (an empty centre keeps its drop size). Unset: the centre counts only its groups.\n */\n readonly center?: { readonly min: number; readonly margin: number };\n}\n\n/** A group's size in a host, expanded or collapsed: along it (`along`) and across it (`across`). */\nexport type GroupSize<G extends string = string> = (id: G, vertical: boolean, collapsed: boolean) => { along: number; across: number };\n\nexport interface HostLayout<G extends string = string> {\n /** Each group's rect in the host (x along, y across for a horizontal host; transposed on a side). */\n readonly slots: ReadonlyMap<G, Rect>;\n /** Groups shown collapsed (by hand, or because the room ran out). */\n readonly collapsed: readonly G[];\n /** Of those, the ones the room collapsed (from the trailing end). */\n readonly overflow: readonly G[];\n /** The length the groups and the ends take. */\n readonly need: number;\n}\n\n/** How long the host's groups need it to be, collapsing only those in `collapsed`. */\nexport function hostNeed<G extends string>(zones: Zones<G>, collapsed: readonly G[], spec: Pick<HostSpec, \"lead\" | \"tail\" | \"center\">, size: GroupSize<G>, v: boolean): number {\n const lens = ZONES.map((zn) => zones[zn].reduce((s, g) => s + size(g, v, collapsed.includes(g)).along, 0) + GAP * Math.max(0, zones[zn].length - 1));\n const used = ZONES.filter((zn) => zones[zn].length > 0).length;\n const c = spec.center;\n if (!c) return spec.lead + spec.tail + lens.reduce((s, x) => s + x, 0) + ZONE_GAP * Math.max(0, used - 1);\n // The centre always counts, at its minimum at least, and its margins keep it apart from the start and end zones.\n return spec.lead + spec.tail + lens[0]! + lens[2]! + Math.max(lens[1]!, c.min) + 2 * c.margin;\n}\n\n/** Lay a host out: zones along its length; when they don't fit, groups collapse from the trailing end. */\nexport function layoutHost<G extends string>(zones: Zones<G>, h: HostId, spec: HostSpec, size: GroupSize<G>, byHand: readonly G[] = []): HostLayout<G> {\n const v = vertical(h);\n const collapsed = new Set<G>(ZONES.flatMap((zn) => zones[zn]).filter((g) => byHand.includes(g)));\n const overflow: G[] = [];\n const len = (g: G): number => size(g, v, collapsed.has(g)).along;\n const zoneLen = (l: readonly G[]): number => l.reduce((s, g) => s + len(g), 0) + GAP * Math.max(0, l.length - 1);\n const needNow = (): number => hostNeed(zones, [...collapsed], spec, size, v);\n // The trailing end first: the end zone's last group, back to the start zone's first.\n for (const g of [...ZONES].reverse().flatMap((zn) => [...zones[zn]].reverse())) {\n if (needNow() <= spec.length) break;\n if (collapsed.has(g)) continue;\n collapsed.add(g);\n overflow.push(g);\n }\n const a0 = spec.lead;\n const b0 = spec.length - spec.tail;\n const S = zoneLen(zones.start);\n const C = zoneLen(zones.center);\n const E = zoneLen(zones.end);\n const lo = a0 + S + (S > 0 ? ZONE_GAP : 0);\n const hi = b0 - E - (E > 0 ? ZONE_GAP : 0);\n let c0 = spec.length / 2 - C / 2;\n if (c0 < lo || c0 + C > hi) c0 = (lo + hi) / 2 - C / 2;\n const slots = new Map<G, Rect>();\n const place = (l: readonly G[], from: number): void => {\n let at = from;\n for (const g of l) {\n const s = size(g, v, collapsed.has(g));\n const across = (spec.thickness - s.across) / 2;\n slots.set(g, v ? { x: across, y: at, width: s.across, height: s.along } : { x: at, y: across, width: s.along, height: s.across });\n at += s.along + GAP;\n }\n };\n place(zones.start, a0);\n place(zones.center, c0);\n place(zones.end, b0 - E);\n return { slots, collapsed: [...collapsed], overflow, need: needNow() };\n}\n\n// --- insertion points ----------------------------------------------------------------------------------------------\n\n/** A place a dragged group can go: a zone of a host and the index among the others there, and where it is. */\nexport interface Insertion extends Place {\n /** Along the host: the insertion line (between groups); for an empty zone, the zone's middle. */\n readonly at: number;\n /** The stretch of the host it owns (host coordinates). */\n readonly span: readonly [number, number];\n /** An empty zone: it owns all of its stretch, whatever is nearer (it wins while the pointer is inside it). */\n readonly empty: boolean;\n readonly label: string;\n}\n\n/**\n * The insertion points of a host laid out without the dragged group: in each zone before all, between two, after\n * all; each owns the stretch of the host nearest to it. An empty zone owns its own stretch: the host's first third\n * (start), middle third (centre) or last third (end), less what its neighbours' groups take. `label` names a group\n * in the \"between\" labels (default: its id).\n */\nexport function insertions<G extends string>(zones: Zones<G>, h: HostId, spec: HostSpec, l: HostLayout<G>, label: (g: G) => string = (g) => g): Insertion[] {\n const v = vertical(h);\n const lo = (r: Rect): number => (v ? r.y : r.x);\n const hi = (r: Rect): number => (v ? r.y + r.height : r.x + r.width);\n const L = spec.length;\n const all = ZONES.flatMap((zn) => zones[zn].map((g) => l.slots.get(g)).filter((r): r is Rect => !!r));\n const first = all.length ? Math.min(...all.map(lo)) : L;\n const last = all.length ? Math.max(...all.map(hi)) : 0;\n const zoneEdge = (zn: ZoneId, end: \"lo\" | \"hi\"): number | null => {\n const rs = zones[zn].map((g) => l.slots.get(g)).filter((r): r is Rect => !!r);\n if (!rs.length) return null;\n return end === \"lo\" ? Math.min(...rs.map(lo)) : Math.max(...rs.map(hi));\n };\n const near: Omit<Insertion, \"span\">[] = [];\n const empties: Insertion[] = [];\n for (const zn of ZONES) {\n const name = `${HOST_LABEL[h]} · ${zoneLabel(h, zn)}`;\n const vis = zones[zn].map((g, i) => ({ g, i, r: l.slots.get(g)! }));\n if (!vis.length) {\n const sEnd = zoneEdge(\"start\", \"hi\");\n const eStart = zoneEdge(\"end\", \"lo\");\n const span: [number, number] =\n zn === \"start\" ? [0, Math.min(L / 3, first - GAP / 2)] : zn === \"end\" ? [Math.max((2 * L) / 3, last + GAP / 2), L] : [Math.max(L / 3, (sEnd ?? 0) + GAP / 2), Math.min((2 * L) / 3, (eStart ?? L) - GAP / 2)];\n if (span[1] - span[0] >= 8) empties.push({ host: h, zone: zn, index: 0, at: (span[0] + span[1]) / 2, span, empty: true, label: name });\n continue;\n }\n near.push({ host: h, zone: zn, index: vis[0]!.i, at: lo(vis[0]!.r) - GAP / 2, empty: false, label: `${name} · before all` });\n for (let k = 1; k < vis.length; k++) near.push({ host: h, zone: zn, index: vis[k]!.i, at: (hi(vis[k - 1]!.r) + lo(vis[k]!.r)) / 2, empty: false, label: `${name} · between ${label(vis[k - 1]!.g)} and ${label(vis[k]!.g)}` });\n near.push({ host: h, zone: zn, index: zones[zn].length, at: hi(vis.at(-1)!.r) + GAP / 2, empty: false, label: `${name} · after all` });\n }\n near.sort((a, b) => a.at - b.at);\n const lines = near.map((p, k) => ({ ...p, span: [k === 0 ? 0 : (near[k - 1]!.at + p.at) / 2, k === near.length - 1 ? L : (p.at + near[k + 1]!.at) / 2] as const }));\n return [...lines, ...empties];\n}\n"],"mappings":"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","names":[]}