@n-uf/placement 26.11.10 → 26.11.12
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 +42 -0
- package/README.md +107 -4
- package/dist/{chunk-7R5H2ABL.js → chunk-2H3CSPEH.js} +2 -2
- package/dist/{chunk-PJD5HKYX.js → chunk-CJA6LRHN.js} +45 -11
- package/dist/chunk-CJA6LRHN.js.map +1 -0
- package/dist/desk.d.ts +2 -1
- package/dist/desk.js +2 -2
- package/dist/panels.d.ts +3 -2
- package/dist/panels.js +2 -2
- package/dist/stack.d.ts +237 -0
- package/dist/stack.js +272 -0
- package/dist/stack.js.map +1 -0
- package/dist/target-D3PiLg44.d.ts +17 -0
- package/dist/{targets-CLV69zMw.d.ts → targets-B6eLC-52.d.ts} +32 -20
- package/dist/targets.d.ts +2 -1
- package/dist/targets.js +1 -1
- package/package.json +5 -1
- package/dist/chunk-PJD5HKYX.js.map +0 -1
- /package/dist/{chunk-7R5H2ABL.js.map → chunk-2H3CSPEH.js.map} +0 -0
package/dist/desk.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./chunk-
|
|
1
|
+
import "./chunk-2H3CSPEH.js";
|
|
2
2
|
import {
|
|
3
3
|
createPanels,
|
|
4
4
|
floatSize,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
panelDrop,
|
|
7
7
|
panelTargets,
|
|
8
8
|
showTabs
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-CJA6LRHN.js";
|
|
10
10
|
import "./chunk-QMTNB2C3.js";
|
|
11
11
|
|
|
12
12
|
// src/desk/index.ts
|
package/dist/panels.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { b as PanelsState, S as Side, E as Edge, C as Clock, a as PanelDef } from './targets-
|
|
2
|
-
export { B as BAND_SIZE, g as Band, h as BandOption, i as COLUMN_REACH, j as Column, D as DEFAULT_PANELS_OPTIONS, k as DockOptions, m as EDGES, F as FLOAT_HEIGHT, n as FLOAT_MIN, o as FloatAt, G as GiveWay, I as IDENTITY_VIEW, e as PanelDrop, q as PanelKind, r as PanelPlace, s as PanelStop, t as PanelStopsOptions, d as PanelTarget, c as PanelTargetInput, f as PanelsLayout, u as PanelsNote, v as PanelsNoteCode, P as PanelsOptions, w as PanelsStart, x as Place, y as PlacedBand, z as PlacedColumn, A as PlacedPanel, H as PlacedRail, R as RAIL_EDGE, J as Reserve, K as ReserveEdge, L as SIDES, V as View, W as Where, M as activate, N as bandHeight, O as bandSize, Q as close, T as columnWidth, U as createPanels, X as defOf, Y as dock, Z as dockBand, _ as float, $ as floatSize, a0 as focus, a1 as giveWayOrder, a2 as homePlace, a3 as keep, l as layoutPanels, a4 as liftPanel, a5 as open, a6 as ownWidth, p as panelDrop, a7 as panelStops, a8 as panelTargets, a9 as pin, aa as placeRank, ab as resizeBand, ac as resizeColumn, ad as resizeFloat, ae as samePlaces, af as showTabs, ag as tick, ah as toPage, ai as toView, aj as toggleFold, ak as whereIs } from './targets-
|
|
1
|
+
import { b as PanelsState, S as Side, E as Edge, C as Clock, a as PanelDef } from './targets-B6eLC-52.js';
|
|
2
|
+
export { B as BAND_SIZE, g as Band, h as BandOption, i as COLUMN_REACH, j as Column, D as DEFAULT_PANELS_OPTIONS, k as DockOptions, m as EDGES, F as FLOAT_HEIGHT, n as FLOAT_MIN, o as FloatAt, G as GiveWay, I as IDENTITY_VIEW, e as PanelDrop, q as PanelKind, r as PanelPlace, s as PanelStop, t as PanelStopsOptions, d as PanelTarget, c as PanelTargetInput, f as PanelsLayout, u as PanelsNote, v as PanelsNoteCode, P as PanelsOptions, w as PanelsStart, x as Place, y as PlacedBand, z as PlacedColumn, A as PlacedPanel, H as PlacedRail, R as RAIL_EDGE, J as Reserve, K as ReserveEdge, L as SIDES, V as View, W as Where, M as activate, N as bandHeight, O as bandSize, Q as close, T as columnWidth, U as createPanels, X as defOf, Y as dock, Z as dockBand, _ as float, $ as floatSize, a0 as focus, a1 as giveWayOrder, a2 as homePlace, a3 as keep, l as layoutPanels, a4 as liftPanel, a5 as open, a6 as ownWidth, p as panelDrop, a7 as panelStops, a8 as panelTargets, a9 as pin, aa as placeRank, ab as resizeBand, ac as resizeColumn, ad as resizeFloat, ae as samePlaces, af as showTabs, ag as tick, ah as toPage, ai as toView, aj as toggleFold, ak as whereIs } from './targets-B6eLC-52.js';
|
|
3
3
|
import { R as Rect } from './geometry-mL-GTRti.js';
|
|
4
|
+
import './target-D3PiLg44.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* The panels' stored form: `storePanels(state)` gives plain JSON-safe data (`{ v: 1, … }`); `readPanels(data, defs?)`
|
package/dist/panels.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readPanels,
|
|
3
3
|
storePanels
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2H3CSPEH.js";
|
|
5
5
|
import {
|
|
6
6
|
BAND_SIZE,
|
|
7
7
|
COLUMN_REACH,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
toView,
|
|
47
47
|
toggleFold,
|
|
48
48
|
whereIs
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-CJA6LRHN.js";
|
|
50
50
|
import "./chunk-QMTNB2C3.js";
|
|
51
51
|
export {
|
|
52
52
|
BAND_SIZE,
|
package/dist/stack.d.ts
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { R as Rect } from './geometry-mL-GTRti.js';
|
|
2
|
+
import { T as Target } from './target-D3PiLg44.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The edge stack's shapes. A stack is the items along one edge of a stage: a column (vertical, a side region) or a
|
|
6
|
+
* row (horizontal, a bar). An item is a panel or a control group; it declares its forms, largest first, and the
|
|
7
|
+
* solver (`fitStack`) picks one per item. Plain data in and out: no words, no time, no DOM.
|
|
8
|
+
*
|
|
9
|
+
* The form kinds are structural labels the app draws by; the solver reads only `menu` (a form with no room of its own)
|
|
10
|
+
* and, for the cross size, `open`. Panel and group kinds may be mixed on one item.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** `"column"` runs the stack down (its main size is a height, its cross size a width); `"row"` runs it across. */
|
|
14
|
+
type StackAxis = "column" | "row";
|
|
15
|
+
/** How a panel draws: open (header and body), a header only, a rail icon, or an entry in the » menu. */
|
|
16
|
+
type PanelFormKind = "open" | "header" | "rail" | "menu";
|
|
17
|
+
/** How a control group draws: a row of words, the words stacked, icons only, or an entry in the » menu. */
|
|
18
|
+
type GroupFormKind = "row" | "stacked" | "icons" | "menu";
|
|
19
|
+
type StackFormKind = PanelFormKind | GroupFormKind;
|
|
20
|
+
/** Along the stack: `min` is the least it can be drawn, `natural` what it asks for; `grows` takes spare length. */
|
|
21
|
+
interface MainSize {
|
|
22
|
+
readonly min: number;
|
|
23
|
+
readonly natural: number;
|
|
24
|
+
readonly grows?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** One way to draw an item. A `menu` form takes no room in the stack (the » entry stands for it). */
|
|
27
|
+
interface StackForm {
|
|
28
|
+
readonly kind: StackFormKind;
|
|
29
|
+
readonly main: MainSize;
|
|
30
|
+
/**
|
|
31
|
+
* Across the stack: `natural` is what this form asks for (it sets the stack's cross size when `setsCross`); `min`
|
|
32
|
+
* is the least it can be drawn across (default `natural`). A form that doesn't set the cross size takes its next
|
|
33
|
+
* form only when its `min` is over the stack's cross size, so a header stays in a column an open panel made
|
|
34
|
+
* narrow when it has a small `min`.
|
|
35
|
+
*/
|
|
36
|
+
readonly cross: {
|
|
37
|
+
readonly natural: number;
|
|
38
|
+
readonly min?: number;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Whether this form sets the stack's cross size (the column's width, the bar's height). Default: true for a
|
|
42
|
+
* panel's `open`, false for everything else (a group never sets a column's width).
|
|
43
|
+
*/
|
|
44
|
+
readonly setsCross?: boolean;
|
|
45
|
+
}
|
|
46
|
+
interface StackItem {
|
|
47
|
+
readonly id: string;
|
|
48
|
+
/** Largest first. The solver starts at `from` and steps down one at a time. An item with none is left out. */
|
|
49
|
+
readonly forms: readonly StackForm[];
|
|
50
|
+
/** How recently it was used: the smaller, the longer ago, the sooner it gives way. */
|
|
51
|
+
readonly used: number;
|
|
52
|
+
/** Kept: gives way after every item that isn't. */
|
|
53
|
+
readonly kept?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The first form the item may take, an index into `forms` (default 0; a panel folded by hand starts at its header).
|
|
56
|
+
* The solver never goes above it. `form` in the result and in the notes are indexes into `forms`, not into what is
|
|
57
|
+
* left after `from`.
|
|
58
|
+
*/
|
|
59
|
+
readonly from?: number;
|
|
60
|
+
}
|
|
61
|
+
/** The cross size a stack may take. With no form setting it, the stack is a `rail` wide (default {@link DEFAULT_RAIL}). */
|
|
62
|
+
interface CrossLimits {
|
|
63
|
+
readonly min: number;
|
|
64
|
+
readonly max: number;
|
|
65
|
+
readonly rail?: number;
|
|
66
|
+
}
|
|
67
|
+
interface StackInput {
|
|
68
|
+
readonly axis: StackAxis;
|
|
69
|
+
/** The stack's length along the axis. */
|
|
70
|
+
readonly length: number;
|
|
71
|
+
readonly cross: CrossLimits;
|
|
72
|
+
/** In order, from the start of the stack. */
|
|
73
|
+
readonly items: readonly StackItem[];
|
|
74
|
+
/** The » menu entry's size along the stack, taken only while an item is in the menu (default {@link DEFAULT_MENU}). */
|
|
75
|
+
readonly menu?: {
|
|
76
|
+
readonly main: number;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** The width of a stack nothing open holds, px. */
|
|
80
|
+
declare const DEFAULT_RAIL = 30;
|
|
81
|
+
/** The » menu entry's size along the stack, px. */
|
|
82
|
+
declare const DEFAULT_MENU = 28;
|
|
83
|
+
/** Whether a form sets the stack's cross size: its own say, else true for a panel's `open`. */
|
|
84
|
+
declare const formSetsCross: (f: StackForm) => boolean;
|
|
85
|
+
/** `input` with `item` inserted at index `at` (clamped to the items' range). */
|
|
86
|
+
declare function withItem(input: StackInput, item: StackItem, at: number): StackInput;
|
|
87
|
+
/** An item drawn: the form it took, its kind and its rect (stack-local px). One in the menu has the menu entry's rect. */
|
|
88
|
+
interface PlacedStackItem {
|
|
89
|
+
readonly id: string;
|
|
90
|
+
/** Index into the item's `forms`. */
|
|
91
|
+
readonly form: number;
|
|
92
|
+
readonly kind: StackFormKind;
|
|
93
|
+
readonly rect: Rect;
|
|
94
|
+
}
|
|
95
|
+
interface StackMenu {
|
|
96
|
+
/** The » entry's rect. */
|
|
97
|
+
readonly rect: Rect;
|
|
98
|
+
/** The items in it, in the stack's order: exactly the items whose kind is `menu`. */
|
|
99
|
+
readonly items: readonly string[];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Why a stack looks as it does:
|
|
103
|
+
* - `form-down`: an item took a smaller form than the one it started in (`from`, an index into its forms), by length
|
|
104
|
+
* or by cross size.
|
|
105
|
+
* - `to-menu`: an item is in the » menu.
|
|
106
|
+
* - `too-wide`: an item's form doesn't set the cross size and its `cross.min` is over the stack's. Cross size never
|
|
107
|
+
* puts an item in the menu, so it keeps its last drawn form and the app clips it.
|
|
108
|
+
* - `no-forms`: an item with no forms, left out of `items`.
|
|
109
|
+
* - `overflow`: no item can give more way and the stack is still `over` px too long (forms that don't end in a `menu`
|
|
110
|
+
* form can't give more; neither can a stack where going into the menu would only make it longer).
|
|
111
|
+
*/
|
|
112
|
+
type StackNote = {
|
|
113
|
+
readonly code: "form-down";
|
|
114
|
+
readonly id: string;
|
|
115
|
+
readonly from: number;
|
|
116
|
+
readonly to: number;
|
|
117
|
+
} | {
|
|
118
|
+
readonly code: "to-menu";
|
|
119
|
+
readonly id: string;
|
|
120
|
+
} | {
|
|
121
|
+
readonly code: "too-wide";
|
|
122
|
+
readonly id: string;
|
|
123
|
+
} | {
|
|
124
|
+
readonly code: "no-forms";
|
|
125
|
+
readonly id: string;
|
|
126
|
+
} | {
|
|
127
|
+
readonly code: "overflow";
|
|
128
|
+
readonly over: number;
|
|
129
|
+
};
|
|
130
|
+
type StackNoteCode = StackNote["code"];
|
|
131
|
+
interface StackFit {
|
|
132
|
+
readonly axis: StackAxis;
|
|
133
|
+
readonly length: number;
|
|
134
|
+
/** The stack's size across: the widest `open` (clamped to the limits), or the rail. */
|
|
135
|
+
readonly cross: number;
|
|
136
|
+
/** One per input item that has forms, in order. */
|
|
137
|
+
readonly items: readonly PlacedStackItem[];
|
|
138
|
+
/** The » entry, or null while no item is in the menu. */
|
|
139
|
+
readonly menu: StackMenu | null;
|
|
140
|
+
/** `no-forms` first, then `form-down` per item in the order they gave way, `to-menu`, `too-wide`, and `overflow`. */
|
|
141
|
+
readonly notes: readonly StackNote[];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
declare function fitStack(input: StackInput): StackFit;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The drop targets for dragging an item into a stack, in the shape @n-uf/motion/drag takes. `stackTargets(input,
|
|
148
|
+
* dragged)` gives one target for every gap of the stack without the dragged item (an item of the input with its id
|
|
149
|
+
* is lifted out first, so a reorder within a stack works): before each item and after the last, so an empty stack has
|
|
150
|
+
* one. An item with no forms has no place of its own, but its gaps count.
|
|
151
|
+
*
|
|
152
|
+
* - `id` `gap:<k>`, priority 2, `place` `{ kind: "gap", at: k }` (k counts the input's items, the dragged one lifted
|
|
153
|
+
* out, including any with no forms; it is not an index into `fit.items`, which leaves those out: look the dragged
|
|
154
|
+
* item up there by its id, as `slot` already does).
|
|
155
|
+
* - `hit`: the gap's zone along the axis, the stack split at its items' middles (an item in the menu has no place of
|
|
156
|
+
* its own: the gaps next to it split the stretch where the stack's drawn items end; with every item in the menu only
|
|
157
|
+
* the last gap, "at the end", has any length, and the others are a point at the start). Across, it is the larger of
|
|
158
|
+
* the stack's current size and the size the drop gives it, so the pointer over the preview is over the target.
|
|
159
|
+
* - `fit`: the full `fitStack` of the stack with the dragged item inserted at k, and `input` the stack that was
|
|
160
|
+
* solved. The preview is `slot`, the dragged item's rect in `fit`: exactly the rect it gets after the drop, because
|
|
161
|
+
* the drop is that same solve. `marker` is the line where its leading edge lands: a rect with no thickness across
|
|
162
|
+
* the stack's cross size (the app draws it with one).
|
|
163
|
+
*
|
|
164
|
+
* The dragged item counts as just used: it is newer than every item in the stack, so the others give way before it.
|
|
165
|
+
* A group lands in the form that fits; a panel that would set a wider column makes room by that solve too. A dragged
|
|
166
|
+
* item with no forms has no targets.
|
|
167
|
+
*/
|
|
168
|
+
|
|
169
|
+
type StackPlace = {
|
|
170
|
+
readonly kind: "gap";
|
|
171
|
+
readonly at: number;
|
|
172
|
+
};
|
|
173
|
+
interface StackTarget extends Target {
|
|
174
|
+
readonly priority: 2;
|
|
175
|
+
/** The item dragged. */
|
|
176
|
+
readonly item: string;
|
|
177
|
+
readonly place: StackPlace;
|
|
178
|
+
/** The line where the dragged item's leading edge lands. */
|
|
179
|
+
readonly marker: Rect;
|
|
180
|
+
/** The stack with the dragged item inserted: what a drop here makes. */
|
|
181
|
+
readonly input: StackInput;
|
|
182
|
+
/** Its fit: the preview and the drop are both its rect for the dragged item. */
|
|
183
|
+
readonly fit: StackFit;
|
|
184
|
+
}
|
|
185
|
+
declare function stackTargets(input: StackInput, dragged: StackItem): StackTarget[];
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The stack's stored form: `storeStack(state)` gives plain JSON-safe data (`{ v: 1, order, kept, closed, used, forms
|
|
189
|
+
* }`); `readStack(data, known?)` reads parsed data back and never throws. Anything but an object of version 1 is
|
|
190
|
+
* `{ ok: false, error }`. Inside one it is lenient, as the panels' reader is: an id that isn't a string, is repeated
|
|
191
|
+
* or (with `known`) isn't one the consumer has is dropped and listed in `dropped`; a `kept`, `closed`, `used` or
|
|
192
|
+
* `forms` entry for an id that isn't in `order` is dropped; a form that isn't a whole number from 1 up is dropped;
|
|
193
|
+
* the key `__proto__` in `forms` is dropped (the other keys, `constructor` and `prototype` included, are ids like any
|
|
194
|
+
* other: `forms` is an object with no prototype, written and read); a field this version doesn't know is skipped and
|
|
195
|
+
* reported as `"<field>"`. An item the consumer has that the stored order lacks is no error: the consumer places it.
|
|
196
|
+
*
|
|
197
|
+
* - `kept`: items kept open (they give way last).
|
|
198
|
+
* - `closed`: items the user closed; their place in `order` is kept for when they open.
|
|
199
|
+
* - `used`: the use order, oldest to newest, so a reload restores who gives way first (an item's `used` is its index in
|
|
200
|
+
* it; one the list lacks is older than all). An id listed twice counts at its last place: the newest use wins.
|
|
201
|
+
* - `forms`: the user's chosen form per item, the index of the largest form it may take (`StackItem.from`). 0, the
|
|
202
|
+
* first, is not stored.
|
|
203
|
+
*/
|
|
204
|
+
interface StackState {
|
|
205
|
+
/** The items' order along the stack. */
|
|
206
|
+
readonly order: readonly string[];
|
|
207
|
+
/** Items kept open: they give way last. */
|
|
208
|
+
readonly kept: readonly string[];
|
|
209
|
+
/** Items the user closed. */
|
|
210
|
+
readonly closed: readonly string[];
|
|
211
|
+
/** The use order, oldest first. */
|
|
212
|
+
readonly used: readonly string[];
|
|
213
|
+
/** The form index the user chose per item (only those that chose one; absent means the first). */
|
|
214
|
+
readonly forms: Readonly<Record<string, number>>;
|
|
215
|
+
}
|
|
216
|
+
interface StoredStack {
|
|
217
|
+
readonly v: 1;
|
|
218
|
+
readonly order: string[];
|
|
219
|
+
readonly kept: string[];
|
|
220
|
+
readonly closed: string[];
|
|
221
|
+
readonly used: string[];
|
|
222
|
+
readonly forms: Record<string, number>;
|
|
223
|
+
}
|
|
224
|
+
type StackRead = {
|
|
225
|
+
readonly ok: true;
|
|
226
|
+
readonly state: StackState;
|
|
227
|
+
readonly dropped: readonly string[];
|
|
228
|
+
} | {
|
|
229
|
+
readonly ok: false;
|
|
230
|
+
readonly error: string;
|
|
231
|
+
};
|
|
232
|
+
/** A state with these items in this order, none kept or closed, used in that order, every form the first. */
|
|
233
|
+
declare const createStack: (order: readonly string[]) => StackState;
|
|
234
|
+
declare function storeStack(state: StackState): StoredStack;
|
|
235
|
+
declare function readStack(data: unknown, known?: readonly string[]): StackRead;
|
|
236
|
+
|
|
237
|
+
export { type CrossLimits, DEFAULT_MENU, DEFAULT_RAIL, type GroupFormKind, type MainSize, type PanelFormKind, type PlacedStackItem, type StackAxis, type StackFit, type StackForm, type StackFormKind, type StackInput, type StackItem, type StackMenu, type StackNote, type StackNoteCode, type StackPlace, type StackRead, type StackState, type StackTarget, type StoredStack, createStack, fitStack, formSetsCross, readStack, stackTargets, storeStack, withItem };
|
package/dist/stack.js
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import {
|
|
2
|
+
rect
|
|
3
|
+
} from "./chunk-QMTNB2C3.js";
|
|
4
|
+
|
|
5
|
+
// src/stack/model.ts
|
|
6
|
+
var DEFAULT_RAIL = 30;
|
|
7
|
+
var DEFAULT_MENU = 28;
|
|
8
|
+
var finite = (x, fallback) => Number.isFinite(x) ? x : fallback;
|
|
9
|
+
function limitsOf(input) {
|
|
10
|
+
const min = Math.max(0, finite(input.cross.min, 0));
|
|
11
|
+
const max = Number.isNaN(input.cross.max) ? Infinity : input.cross.max;
|
|
12
|
+
return { min, max: Math.max(min, max), rail: Math.max(0, finite(input.cross.rail ?? DEFAULT_RAIL, DEFAULT_RAIL)) };
|
|
13
|
+
}
|
|
14
|
+
var menuMain = (input) => Math.max(0, finite(input.menu?.main ?? DEFAULT_MENU, DEFAULT_MENU));
|
|
15
|
+
var formSetsCross = (f) => f.setsCross ?? f.kind === "open";
|
|
16
|
+
function withItem(input, item, at) {
|
|
17
|
+
const k = Math.min(Math.max(0, Math.floor(at)), input.items.length);
|
|
18
|
+
return { ...input, items: [...input.items.slice(0, k), item, ...input.items.slice(k)] };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/stack/layout.ts
|
|
22
|
+
var EPS = 1e-6;
|
|
23
|
+
var isMenu = (f) => f.kind === "menu";
|
|
24
|
+
var finite2 = (x, fallback = 0) => Number.isFinite(x) ? x : fallback;
|
|
25
|
+
var mainOf = (f, which) => isMenu(f) ? 0 : Math.max(0, finite2(f.main[which]));
|
|
26
|
+
var crossMin = (f) => finite2(f.cross.min ?? f.cross.natural);
|
|
27
|
+
function victim(items, at) {
|
|
28
|
+
let best = -1;
|
|
29
|
+
for (let i = 0; i < items.length; i++) {
|
|
30
|
+
if (at[i] + 1 >= items[i].forms.length) continue;
|
|
31
|
+
if (best < 0) {
|
|
32
|
+
best = i;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const a = items[i];
|
|
36
|
+
const b = items[best];
|
|
37
|
+
const ka = a.kept ? 1 : 0;
|
|
38
|
+
const kb = b.kept ? 1 : 0;
|
|
39
|
+
if (ka !== kb ? ka < kb : a.used !== b.used ? a.used < b.used : true) best = i;
|
|
40
|
+
}
|
|
41
|
+
return best;
|
|
42
|
+
}
|
|
43
|
+
function fitStack(input) {
|
|
44
|
+
const length = Math.max(0, finite2(input.length));
|
|
45
|
+
const lim = limitsOf(input);
|
|
46
|
+
const items = input.items.filter((it) => it.forms.length > 0);
|
|
47
|
+
const start = items.map((it) => Math.min(Math.max(0, Math.floor(finite2(it.from ?? 0))), it.forms.length - 1));
|
|
48
|
+
const at = [...start];
|
|
49
|
+
const form = (i) => items[i].forms[at[i]];
|
|
50
|
+
const idx = items.map((_, i) => i);
|
|
51
|
+
const firstStep = [];
|
|
52
|
+
const menuOrder = [];
|
|
53
|
+
const step = (i) => {
|
|
54
|
+
if (!firstStep.includes(i)) firstStep.push(i);
|
|
55
|
+
at[i]++;
|
|
56
|
+
if (isMenu(form(i)) && !menuOrder.includes(i)) menuOrder.push(i);
|
|
57
|
+
};
|
|
58
|
+
for (const i of idx) if (isMenu(form(i))) menuOrder.push(i);
|
|
59
|
+
const crossOf = () => {
|
|
60
|
+
const setters = idx.filter((i) => !isMenu(form(i)) && formSetsCross(form(i)));
|
|
61
|
+
if (setters.length === 0) return lim.rail;
|
|
62
|
+
const want = Math.max(...setters.map((i) => finite2(form(i).cross.natural)));
|
|
63
|
+
return Math.min(Math.max(want, lim.min), lim.max);
|
|
64
|
+
};
|
|
65
|
+
const inMenu = () => idx.some((i) => isMenu(form(i)));
|
|
66
|
+
const minSum = () => idx.reduce((s, i) => s + mainOf(form(i), "min"), 0) + (inMenu() ? menuMain(input) : 0);
|
|
67
|
+
const canStep = (i) => at[i] + 1 < items[i].forms.length;
|
|
68
|
+
const tooWide = (i, cross2) => !isMenu(form(i)) && !formSetsCross(form(i)) && crossMin(form(i)) > cross2 + EPS;
|
|
69
|
+
let cross = crossOf();
|
|
70
|
+
let overflow = 0;
|
|
71
|
+
const guard = items.reduce((s, it) => s + it.forms.length, 0) + 1;
|
|
72
|
+
for (let n = 0; n <= guard; n++) {
|
|
73
|
+
cross = crossOf();
|
|
74
|
+
let stepped = false;
|
|
75
|
+
for (const i of idx) {
|
|
76
|
+
if (tooWide(i, cross) && canStep(i) && !isMenu(items[i].forms[at[i] + 1])) {
|
|
77
|
+
step(i);
|
|
78
|
+
stepped = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (stepped) continue;
|
|
82
|
+
const over = minSum() - length;
|
|
83
|
+
if (over <= EPS) break;
|
|
84
|
+
const v = victim(items, at);
|
|
85
|
+
if (v < 0) {
|
|
86
|
+
overflow = over;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
const intoMenu = isMenu(items[v].forms[at[v] + 1]);
|
|
90
|
+
const gain = mainOf(form(v), "min") - (inMenu() ? 0 : menuMain(input));
|
|
91
|
+
if (intoMenu && gain < 0 && idx.filter(canStep).length === 1) {
|
|
92
|
+
overflow = over;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
step(v);
|
|
96
|
+
}
|
|
97
|
+
cross = crossOf();
|
|
98
|
+
const menuSize = inMenu() ? menuMain(input) : 0;
|
|
99
|
+
const avail = Math.max(0, length - menuSize);
|
|
100
|
+
const drawn = idx.filter((i) => !isMenu(form(i)));
|
|
101
|
+
const min = (i) => mainOf(form(i), "min");
|
|
102
|
+
const nat = (i) => Math.max(min(i), mainOf(form(i), "natural"));
|
|
103
|
+
const sumMin = drawn.reduce((s, i) => s + min(i), 0);
|
|
104
|
+
const sumNat = drawn.reduce((s, i) => s + nat(i), 0);
|
|
105
|
+
const size = /* @__PURE__ */ new Map();
|
|
106
|
+
if (sumNat <= avail + EPS) {
|
|
107
|
+
const growers = drawn.filter((i) => form(i).main.grows);
|
|
108
|
+
const spare = Math.max(0, avail - sumNat);
|
|
109
|
+
const weight = growers.reduce((s, i) => s + nat(i), 0);
|
|
110
|
+
for (const i of drawn) {
|
|
111
|
+
const share = growers.includes(i) ? weight > EPS ? nat(i) / weight : 1 / growers.length : 0;
|
|
112
|
+
size.set(i, nat(i) + spare * share);
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
const t = avail > sumMin && sumNat > sumMin ? (avail - sumMin) / (sumNat - sumMin) : 0;
|
|
116
|
+
for (const i of drawn) size.set(i, min(i) + (nat(i) - min(i)) * t);
|
|
117
|
+
}
|
|
118
|
+
const rectAt = (from, main) => input.axis === "column" ? { x: 0, y: from, width: cross, height: main } : { x: from, y: 0, width: main, height: cross };
|
|
119
|
+
const rects = /* @__PURE__ */ new Map();
|
|
120
|
+
let cursor = 0;
|
|
121
|
+
for (const i of drawn) {
|
|
122
|
+
const s = size.get(i);
|
|
123
|
+
rects.set(i, rectAt(cursor, s));
|
|
124
|
+
cursor += s;
|
|
125
|
+
}
|
|
126
|
+
const menuRect = rectAt(Math.max(cursor, length - menuSize), menuSize);
|
|
127
|
+
const inside = idx.filter((i) => isMenu(form(i)));
|
|
128
|
+
const menu = inside.length > 0 ? { rect: menuRect, items: inside.map((i) => items[i].id) } : null;
|
|
129
|
+
const placed = idx.map((i) => ({ id: items[i].id, form: at[i], kind: form(i).kind, rect: isMenu(form(i)) ? menuRect : rects.get(i) }));
|
|
130
|
+
const notes = [];
|
|
131
|
+
for (const it of input.items) if (it.forms.length === 0) notes.push({ code: "no-forms", id: it.id });
|
|
132
|
+
for (const i of firstStep) if (at[i] > start[i]) notes.push({ code: "form-down", id: items[i].id, from: start[i], to: at[i] });
|
|
133
|
+
for (const i of menuOrder) if (isMenu(form(i))) notes.push({ code: "to-menu", id: items[i].id });
|
|
134
|
+
for (const i of idx) if (tooWide(i, cross)) notes.push({ code: "too-wide", id: items[i].id });
|
|
135
|
+
if (overflow > EPS) notes.push({ code: "overflow", over: overflow });
|
|
136
|
+
return { axis: input.axis, length, cross, items: placed, menu, notes };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/stack/targets.ts
|
|
140
|
+
function bounds(input, fit) {
|
|
141
|
+
const column = input.axis === "column";
|
|
142
|
+
const start = (r) => column ? r.y : r.x;
|
|
143
|
+
const size = (r) => column ? r.height : r.width;
|
|
144
|
+
const placed = new Map(fit.items.map((p) => [p.id, p]));
|
|
145
|
+
const b = [0];
|
|
146
|
+
let end = 0;
|
|
147
|
+
for (const it of input.items) {
|
|
148
|
+
const p = placed.get(it.id);
|
|
149
|
+
if (!p || p.kind === "menu") {
|
|
150
|
+
b.push(end);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
b.push(start(p.rect) + size(p.rect) / 2);
|
|
154
|
+
end = start(p.rect) + size(p.rect);
|
|
155
|
+
}
|
|
156
|
+
b.push(fit.length);
|
|
157
|
+
for (let i = 1; i < b.length; i++) b[i] = Math.min(Math.max(b[i], b[i - 1]), fit.length);
|
|
158
|
+
return b;
|
|
159
|
+
}
|
|
160
|
+
function stackTargets(input, dragged) {
|
|
161
|
+
if (dragged.forms.length === 0) return [];
|
|
162
|
+
const rest = { ...input, items: input.items.filter((it) => it.id !== dragged.id) };
|
|
163
|
+
const base = fitStack(rest);
|
|
164
|
+
const b = bounds(rest, base);
|
|
165
|
+
const column = rest.axis === "column";
|
|
166
|
+
const newest = rest.items.reduce((m, it) => Math.max(m, it.used), dragged.used) + 1;
|
|
167
|
+
const item = { ...dragged, used: newest };
|
|
168
|
+
const out = [];
|
|
169
|
+
for (let k = 0; k <= rest.items.length; k++) {
|
|
170
|
+
const solved = withItem(rest, item, k);
|
|
171
|
+
const fit = fitStack(solved);
|
|
172
|
+
const slot = fit.items.find((p) => p.id === dragged.id).rect;
|
|
173
|
+
const from = b[k];
|
|
174
|
+
const to = b[k + 1];
|
|
175
|
+
const across = Math.max(base.cross, fit.cross);
|
|
176
|
+
out.push({
|
|
177
|
+
id: `gap:${k}`,
|
|
178
|
+
priority: 2,
|
|
179
|
+
item: dragged.id,
|
|
180
|
+
place: { kind: "gap", at: k },
|
|
181
|
+
hit: column ? rect(0, from, across, to - from) : rect(from, 0, to - from, across),
|
|
182
|
+
slot,
|
|
183
|
+
marker: column ? rect(slot.x, slot.y, slot.width, 0) : rect(slot.x, slot.y, 0, slot.height),
|
|
184
|
+
input: solved,
|
|
185
|
+
fit
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
return out;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/stack/store.ts
|
|
192
|
+
var createStack = (order) => ({ order: [...order], kept: [], closed: [], used: [...order], forms: {} });
|
|
193
|
+
var isForm = (f) => typeof f === "number" && Number.isInteger(f) && f >= 1;
|
|
194
|
+
var uniq = (ids, order) => [...new Set(ids)].filter((id) => order.includes(id));
|
|
195
|
+
var uniqLast = (ids, order) => ids.filter((id, i) => order.includes(id) && ids.lastIndexOf(id) === i);
|
|
196
|
+
var noForms = () => /* @__PURE__ */ Object.create(null);
|
|
197
|
+
function storeStack(state) {
|
|
198
|
+
const forms = noForms();
|
|
199
|
+
for (const id of state.order) {
|
|
200
|
+
const f = state.forms[id];
|
|
201
|
+
if (id !== "__proto__" && Object.hasOwn(state.forms, id) && isForm(f)) forms[id] = f;
|
|
202
|
+
}
|
|
203
|
+
return { v: 1, order: [...state.order], kept: uniq(state.kept, state.order), closed: uniq(state.closed, state.order), used: uniqLast(state.used, state.order), forms };
|
|
204
|
+
}
|
|
205
|
+
var isObject = (x) => typeof x === "object" && x !== null && !Array.isArray(x);
|
|
206
|
+
function readStack(data, known) {
|
|
207
|
+
try {
|
|
208
|
+
return read(data, known);
|
|
209
|
+
} catch (e) {
|
|
210
|
+
return { ok: false, error: `unreadable: ${e instanceof Error ? e.message : typeof e}` };
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
var versionWords = (v) => typeof v === "number" ? String(v) : typeof v === "string" ? JSON.stringify(v.length > 24 ? `${v.slice(0, 24)}\u2026` : v) : typeof v;
|
|
214
|
+
function read(data, known) {
|
|
215
|
+
if (!isObject(data)) return { ok: false, error: "not an object" };
|
|
216
|
+
if (data.v !== 1) return { ok: false, error: `unknown version ${versionWords(data.v)} (this reads version 1)` };
|
|
217
|
+
if (!Array.isArray(data.order)) return { ok: false, error: "order is missing" };
|
|
218
|
+
const dropped = [];
|
|
219
|
+
const drop = (what) => {
|
|
220
|
+
if (!dropped.includes(what)) dropped.push(what);
|
|
221
|
+
};
|
|
222
|
+
for (const k of Object.keys(data)) if (!["v", "order", "kept", "closed", "used", "forms"].includes(k)) drop(k);
|
|
223
|
+
const order = [];
|
|
224
|
+
for (const x of data.order) {
|
|
225
|
+
if (typeof x !== "string") drop(`a ${typeof x} where an id goes`);
|
|
226
|
+
else if (known && !known.includes(x) || order.includes(x)) drop(x);
|
|
227
|
+
else order.push(x);
|
|
228
|
+
}
|
|
229
|
+
const list = (key) => {
|
|
230
|
+
const out = [];
|
|
231
|
+
const x = data[key];
|
|
232
|
+
if (x === void 0) return out;
|
|
233
|
+
if (!Array.isArray(x)) {
|
|
234
|
+
drop(key);
|
|
235
|
+
return out;
|
|
236
|
+
}
|
|
237
|
+
for (const id of x) {
|
|
238
|
+
if (typeof id === "string" && order.includes(id)) {
|
|
239
|
+
if (key === "used" && out.includes(id)) out.splice(out.indexOf(id), 1);
|
|
240
|
+
if (!out.includes(id)) out.push(id);
|
|
241
|
+
} else drop(typeof id === "string" ? `${key}:${id}` : `a ${typeof id} in ${key}`);
|
|
242
|
+
}
|
|
243
|
+
return out;
|
|
244
|
+
};
|
|
245
|
+
const kept = list("kept");
|
|
246
|
+
const closed = list("closed");
|
|
247
|
+
const used = list("used");
|
|
248
|
+
const forms = noForms();
|
|
249
|
+
if (data.forms !== void 0) {
|
|
250
|
+
if (!isObject(data.forms)) drop("forms");
|
|
251
|
+
else {
|
|
252
|
+
for (const id of Object.keys(data.forms)) {
|
|
253
|
+
const f = data.forms[id];
|
|
254
|
+
if (id === "__proto__" || !Object.hasOwn(data.forms, id) || !order.includes(id) || !isForm(f)) drop(`forms:${id}`);
|
|
255
|
+
else forms[id] = f;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return { ok: true, state: { order, kept, closed, used, forms }, dropped };
|
|
260
|
+
}
|
|
261
|
+
export {
|
|
262
|
+
DEFAULT_MENU,
|
|
263
|
+
DEFAULT_RAIL,
|
|
264
|
+
createStack,
|
|
265
|
+
fitStack,
|
|
266
|
+
formSetsCross,
|
|
267
|
+
readStack,
|
|
268
|
+
stackTargets,
|
|
269
|
+
storeStack,
|
|
270
|
+
withItem
|
|
271
|
+
};
|
|
272
|
+
//# sourceMappingURL=stack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stack/model.ts","../src/stack/layout.ts","../src/stack/targets.ts","../src/stack/store.ts"],"sourcesContent":["/**\n * The edge stack's shapes. A stack is the items along one edge of a stage: a column (vertical, a side region) or a\n * row (horizontal, a bar). An item is a panel or a control group; it declares its forms, largest first, and the\n * solver (`fitStack`) picks one per item. Plain data in and out: no words, no time, no DOM.\n *\n * The form kinds are structural labels the app draws by; the solver reads only `menu` (a form with no room of its own)\n * and, for the cross size, `open`. Panel and group kinds may be mixed on one item.\n */\nimport type { Rect } from \"../geometry\";\n\n/** `\"column\"` runs the stack down (its main size is a height, its cross size a width); `\"row\"` runs it across. */\nexport type StackAxis = \"column\" | \"row\";\n\n/** How a panel draws: open (header and body), a header only, a rail icon, or an entry in the » menu. */\nexport type PanelFormKind = \"open\" | \"header\" | \"rail\" | \"menu\";\n/** How a control group draws: a row of words, the words stacked, icons only, or an entry in the » menu. */\nexport type GroupFormKind = \"row\" | \"stacked\" | \"icons\" | \"menu\";\nexport type StackFormKind = PanelFormKind | GroupFormKind;\n\n/** Along the stack: `min` is the least it can be drawn, `natural` what it asks for; `grows` takes spare length. */\nexport interface MainSize {\n readonly min: number;\n readonly natural: number;\n readonly grows?: boolean;\n}\n\n/** One way to draw an item. A `menu` form takes no room in the stack (the » entry stands for it). */\nexport interface StackForm {\n readonly kind: StackFormKind;\n readonly main: MainSize;\n /**\n * Across the stack: `natural` is what this form asks for (it sets the stack's cross size when `setsCross`); `min`\n * is the least it can be drawn across (default `natural`). A form that doesn't set the cross size takes its next\n * form only when its `min` is over the stack's cross size, so a header stays in a column an open panel made\n * narrow when it has a small `min`.\n */\n readonly cross: { readonly natural: number; readonly min?: number };\n /**\n * Whether this form sets the stack's cross size (the column's width, the bar's height). Default: true for a\n * panel's `open`, false for everything else (a group never sets a column's width).\n */\n readonly setsCross?: boolean;\n}\n\nexport interface StackItem {\n readonly id: string;\n /** Largest first. The solver starts at `from` and steps down one at a time. An item with none is left out. */\n readonly forms: readonly StackForm[];\n /** How recently it was used: the smaller, the longer ago, the sooner it gives way. */\n readonly used: number;\n /** Kept: gives way after every item that isn't. */\n readonly kept?: boolean;\n /**\n * The first form the item may take, an index into `forms` (default 0; a panel folded by hand starts at its header).\n * The solver never goes above it. `form` in the result and in the notes are indexes into `forms`, not into what is\n * left after `from`.\n */\n readonly from?: number;\n}\n\n/** The cross size a stack may take. With no form setting it, the stack is a `rail` wide (default {@link DEFAULT_RAIL}). */\nexport interface CrossLimits {\n readonly min: number;\n readonly max: number;\n readonly rail?: number;\n}\n\nexport interface StackInput {\n readonly axis: StackAxis;\n /** The stack's length along the axis. */\n readonly length: number;\n readonly cross: CrossLimits;\n /** In order, from the start of the stack. */\n readonly items: readonly StackItem[];\n /** The » menu entry's size along the stack, taken only while an item is in the menu (default {@link DEFAULT_MENU}). */\n readonly menu?: { readonly main: number };\n}\n\n/** The width of a stack nothing open holds, px. */\nexport const DEFAULT_RAIL = 30;\n/** The » menu entry's size along the stack, px. */\nexport const DEFAULT_MENU = 28;\n\nconst finite = (x: number, fallback: number): number => (Number.isFinite(x) ? x : fallback);\n\n/**\n * The cross limits with a non-number replaced: `min` and `rail` that aren't finite become 0 and the default rail, a\n * `max` that is NaN becomes no limit (Infinity is one already), and `max` is never under `min`.\n */\nexport function limitsOf(input: StackInput): { readonly min: number; readonly max: number; readonly rail: number } {\n const min = Math.max(0, finite(input.cross.min, 0));\n const max = Number.isNaN(input.cross.max) ? Infinity : input.cross.max;\n return { min, max: Math.max(min, max), rail: Math.max(0, finite(input.cross.rail ?? DEFAULT_RAIL, DEFAULT_RAIL)) };\n}\nexport const menuMain = (input: StackInput): number => Math.max(0, finite(input.menu?.main ?? DEFAULT_MENU, DEFAULT_MENU));\n\n/** Whether a form sets the stack's cross size: its own say, else true for a panel's `open`. */\nexport const formSetsCross = (f: StackForm): boolean => f.setsCross ?? f.kind === \"open\";\n\n/** `input` with `item` inserted at index `at` (clamped to the items' range). */\nexport function withItem(input: StackInput, item: StackItem, at: number): StackInput {\n const k = Math.min(Math.max(0, Math.floor(at)), input.items.length);\n return { ...input, items: [...input.items.slice(0, k), item, ...input.items.slice(k)] };\n}\n\n/** An item drawn: the form it took, its kind and its rect (stack-local px). One in the menu has the menu entry's rect. */\nexport interface PlacedStackItem {\n readonly id: string;\n /** Index into the item's `forms`. */\n readonly form: number;\n readonly kind: StackFormKind;\n readonly rect: Rect;\n}\n\nexport interface StackMenu {\n /** The » entry's rect. */\n readonly rect: Rect;\n /** The items in it, in the stack's order: exactly the items whose kind is `menu`. */\n readonly items: readonly string[];\n}\n\n/**\n * Why a stack looks as it does:\n * - `form-down`: an item took a smaller form than the one it started in (`from`, an index into its forms), by length\n * or by cross size.\n * - `to-menu`: an item is in the » menu.\n * - `too-wide`: an item's form doesn't set the cross size and its `cross.min` is over the stack's. Cross size never\n * puts an item in the menu, so it keeps its last drawn form and the app clips it.\n * - `no-forms`: an item with no forms, left out of `items`.\n * - `overflow`: no item can give more way and the stack is still `over` px too long (forms that don't end in a `menu`\n * form can't give more; neither can a stack where going into the menu would only make it longer).\n */\nexport type StackNote =\n | { readonly code: \"form-down\"; readonly id: string; readonly from: number; readonly to: number }\n | { readonly code: \"to-menu\"; readonly id: string }\n | { readonly code: \"too-wide\"; readonly id: string }\n | { readonly code: \"no-forms\"; readonly id: string }\n | { readonly code: \"overflow\"; readonly over: number };\nexport type StackNoteCode = StackNote[\"code\"];\n\nexport interface StackFit {\n readonly axis: StackAxis;\n readonly length: number;\n /** The stack's size across: the widest `open` (clamped to the limits), or the rail. */\n readonly cross: number;\n /** One per input item that has forms, in order. */\n readonly items: readonly PlacedStackItem[];\n /** The » entry, or null while no item is in the menu. */\n readonly menu: StackMenu | null;\n /** `no-forms` first, then `form-down` per item in the order they gave way, `to-menu`, `too-wide`, and `overflow`. */\n readonly notes: readonly StackNote[];\n}\n","/**\n * `fitStack`: which form each item of a stack takes, and where it goes.\n *\n * 1. Every item starts in its largest form it may take (`forms[from]`, default the first).\n * 2. The stack's cross size is the largest `cross.natural` among the items whose form sets it (an open panel's, by\n * default), clamped to the limits; with none, the rail width.\n * 3. Cross-fit: an item whose form doesn't set the cross size and whose `cross.min` is over it steps down until it\n * fits (a group never sets a column's width, so a group alone becomes a rail of icons). Cross size never steps\n * an item into the menu: when only the menu is left it keeps its last drawn form and a `too-wide` note says so.\n * 4. Give-way: while the smallest sizes (`main.min`) don't fit the length, the item used longest ago steps down one\n * form; unkept before kept, the later in the stack first on a tie. The same item keeps giving way until its forms\n * end, then the next does. A `menu` form takes no room; the » entry does, while any item is in it (and an item\n * doesn't go into the menu when that would only make the stack longer and nothing else can give way).\n * Steps 2 to 4 repeat until nothing changes: a panel folding can narrow the column, which asks more of the groups.\n * 5. The room the items leave goes to the forms that grow, shared by their natural sizes. With none, the items keep\n * their natural sizes from the start of the stack. Between the minimum and natural sizes every item shrinks by the\n * same share of what it can give.\n *\n * The solver has no open or closed state: it lays out the items it is given, and a consumer closes a panel by not\n * passing it (or by starting it at a later form with `from`). An item with no forms is left out (`no-forms`). Pure:\n * same input, same fit.\n */\nimport type { Rect } from \"../geometry\";\nimport { formSetsCross, limitsOf, menuMain, type PlacedStackItem, type StackFit, type StackForm, type StackInput, type StackItem, type StackMenu, type StackNote } from \"./model\";\n\nconst EPS = 1e-6;\nconst isMenu = (f: StackForm): boolean => f.kind === \"menu\";\nconst finite = (x: number, fallback = 0): number => (Number.isFinite(x) ? x : fallback);\nconst mainOf = (f: StackForm, which: \"min\" | \"natural\"): number => (isMenu(f) ? 0 : Math.max(0, finite(f.main[which])));\n/** The least a form can be drawn across the stack. */\nconst crossMin = (f: StackForm): number => finite(f.cross.min ?? f.cross.natural);\n\n/** The item that gives way next: unkept before kept, used longest ago, the later in the stack on a tie. */\nfunction victim(items: readonly StackItem[], at: readonly number[]): number {\n let best = -1;\n for (let i = 0; i < items.length; i++) {\n if (at[i]! + 1 >= items[i]!.forms.length) continue;\n if (best < 0) {\n best = i;\n continue;\n }\n const a = items[i]!;\n const b = items[best]!;\n const ka = a.kept ? 1 : 0;\n const kb = b.kept ? 1 : 0;\n if (ka !== kb ? ka < kb : a.used !== b.used ? a.used < b.used : true) best = i;\n }\n return best;\n}\n\nexport function fitStack(input: StackInput): StackFit {\n const length = Math.max(0, finite(input.length));\n const lim = limitsOf(input);\n const items = input.items.filter((it) => it.forms.length > 0);\n const start = items.map((it) => Math.min(Math.max(0, Math.floor(finite(it.from ?? 0))), it.forms.length - 1));\n const at = [...start];\n const form = (i: number): StackForm => items[i]!.forms[at[i]!]!;\n const idx = items.map((_, i) => i);\n\n const firstStep: number[] = [];\n const menuOrder: number[] = [];\n const step = (i: number): void => {\n if (!firstStep.includes(i)) firstStep.push(i);\n at[i]!++;\n if (isMenu(form(i)) && !menuOrder.includes(i)) menuOrder.push(i);\n };\n // An item that starts in the menu is in it from the start.\n for (const i of idx) if (isMenu(form(i))) menuOrder.push(i);\n\n const crossOf = (): number => {\n const setters = idx.filter((i) => !isMenu(form(i)) && formSetsCross(form(i)));\n if (setters.length === 0) return lim.rail;\n const want = Math.max(...setters.map((i) => finite(form(i).cross.natural)));\n return Math.min(Math.max(want, lim.min), lim.max);\n };\n const inMenu = (): boolean => idx.some((i) => isMenu(form(i)));\n const minSum = (): number => idx.reduce((s, i) => s + mainOf(form(i), \"min\"), 0) + (inMenu() ? menuMain(input) : 0);\n const canStep = (i: number): boolean => at[i]! + 1 < items[i]!.forms.length;\n const tooWide = (i: number, cross: number): boolean => !isMenu(form(i)) && !formSetsCross(form(i)) && crossMin(form(i)) > cross + EPS;\n\n let cross = crossOf();\n let overflow = 0;\n const guard = items.reduce((s, it) => s + it.forms.length, 0) + 1;\n for (let n = 0; n <= guard; n++) {\n cross = crossOf();\n // Too wide for the stack: step down, but never into the menu (that is the length's call).\n let stepped = false;\n for (const i of idx) {\n if (tooWide(i, cross) && canStep(i) && !isMenu(items[i]!.forms[at[i]! + 1]!)) {\n step(i);\n stepped = true;\n }\n }\n if (stepped) continue;\n const over = minSum() - length;\n if (over <= EPS) break;\n const v = victim(items, at);\n if (v < 0) {\n overflow = over;\n break;\n }\n // Into the menu only when that helps, or when something else can still give way.\n const intoMenu = isMenu(items[v]!.forms[at[v]! + 1]!);\n const gain = mainOf(form(v), \"min\") - (inMenu() ? 0 : menuMain(input));\n if (intoMenu && gain < 0 && idx.filter(canStep).length === 1) {\n overflow = over;\n break;\n }\n step(v);\n }\n cross = crossOf();\n\n // Sizes along the stack.\n const menuSize = inMenu() ? menuMain(input) : 0;\n const avail = Math.max(0, length - menuSize);\n const drawn = idx.filter((i) => !isMenu(form(i)));\n const min = (i: number): number => mainOf(form(i), \"min\");\n const nat = (i: number): number => Math.max(min(i), mainOf(form(i), \"natural\"));\n const sumMin = drawn.reduce((s, i) => s + min(i), 0);\n const sumNat = drawn.reduce((s, i) => s + nat(i), 0);\n const size = new Map<number, number>();\n if (sumNat <= avail + EPS) {\n const growers = drawn.filter((i) => form(i).main.grows);\n const spare = Math.max(0, avail - sumNat);\n const weight = growers.reduce((s, i) => s + nat(i), 0);\n for (const i of drawn) {\n const share = growers.includes(i) ? (weight > EPS ? nat(i) / weight : 1 / growers.length) : 0;\n size.set(i, nat(i) + spare * share);\n }\n } else {\n const t = avail > sumMin && sumNat > sumMin ? (avail - sumMin) / (sumNat - sumMin) : 0;\n for (const i of drawn) size.set(i, min(i) + (nat(i) - min(i)) * t);\n }\n\n const rectAt = (from: number, main: number): Rect => (input.axis === \"column\" ? { x: 0, y: from, width: cross, height: main } : { x: from, y: 0, width: main, height: cross });\n const rects = new Map<number, Rect>();\n let cursor = 0;\n for (const i of drawn) {\n const s = size.get(i)!;\n rects.set(i, rectAt(cursor, s));\n cursor += s;\n }\n const menuRect = rectAt(Math.max(cursor, length - menuSize), menuSize);\n const inside = idx.filter((i) => isMenu(form(i)));\n const menu: StackMenu | null = inside.length > 0 ? { rect: menuRect, items: inside.map((i) => items[i]!.id) } : null;\n\n const placed: PlacedStackItem[] = idx.map((i) => ({ id: items[i]!.id, form: at[i]!, kind: form(i).kind, rect: isMenu(form(i)) ? menuRect : rects.get(i)! }));\n\n const notes: StackNote[] = [];\n for (const it of input.items) if (it.forms.length === 0) notes.push({ code: \"no-forms\", id: it.id });\n for (const i of firstStep) if (at[i]! > start[i]!) notes.push({ code: \"form-down\", id: items[i]!.id, from: start[i]!, to: at[i]! });\n for (const i of menuOrder) if (isMenu(form(i))) notes.push({ code: \"to-menu\", id: items[i]!.id });\n for (const i of idx) if (tooWide(i, cross)) notes.push({ code: \"too-wide\", id: items[i]!.id });\n if (overflow > EPS) notes.push({ code: \"overflow\", over: overflow });\n return { axis: input.axis, length, cross, items: placed, menu, notes };\n}\n","/**\n * The drop targets for dragging an item into a stack, in the shape @n-uf/motion/drag takes. `stackTargets(input,\n * dragged)` gives one target for every gap of the stack without the dragged item (an item of the input with its id\n * is lifted out first, so a reorder within a stack works): before each item and after the last, so an empty stack has\n * one. An item with no forms has no place of its own, but its gaps count.\n *\n * - `id` `gap:<k>`, priority 2, `place` `{ kind: \"gap\", at: k }` (k counts the input's items, the dragged one lifted\n * out, including any with no forms; it is not an index into `fit.items`, which leaves those out: look the dragged\n * item up there by its id, as `slot` already does).\n * - `hit`: the gap's zone along the axis, the stack split at its items' middles (an item in the menu has no place of\n * its own: the gaps next to it split the stretch where the stack's drawn items end; with every item in the menu only\n * the last gap, \"at the end\", has any length, and the others are a point at the start). Across, it is the larger of\n * the stack's current size and the size the drop gives it, so the pointer over the preview is over the target.\n * - `fit`: the full `fitStack` of the stack with the dragged item inserted at k, and `input` the stack that was\n * solved. The preview is `slot`, the dragged item's rect in `fit`: exactly the rect it gets after the drop, because\n * the drop is that same solve. `marker` is the line where its leading edge lands: a rect with no thickness across\n * the stack's cross size (the app draws it with one).\n *\n * The dragged item counts as just used: it is newer than every item in the stack, so the others give way before it.\n * A group lands in the form that fits; a panel that would set a wider column makes room by that solve too. A dragged\n * item with no forms has no targets.\n */\nimport { rect, type Rect } from \"../geometry\";\nimport type { Target } from \"../targets/target\";\nimport { fitStack } from \"./layout\";\nimport { withItem, type StackFit, type StackInput, type StackItem } from \"./model\";\n\nexport type StackPlace = { readonly kind: \"gap\"; readonly at: number };\n\nexport interface StackTarget extends Target {\n readonly priority: 2;\n /** The item dragged. */\n readonly item: string;\n readonly place: StackPlace;\n /** The line where the dragged item's leading edge lands. */\n readonly marker: Rect;\n /** The stack with the dragged item inserted: what a drop here makes. */\n readonly input: StackInput;\n /** Its fit: the preview and the drop are both its rect for the dragged item. */\n readonly fit: StackFit;\n}\n\n/** The gap boundaries along the axis: `b[0]` is the start, `b[k]` the middle of item k - 1, `b[n + 1]` the length. */\nfunction bounds(input: StackInput, fit: StackFit): number[] {\n const column = input.axis === \"column\";\n const start = (r: Rect): number => (column ? r.y : r.x);\n const size = (r: Rect): number => (column ? r.height : r.width);\n const placed = new Map(fit.items.map((p) => [p.id, p]));\n const b = [0];\n // Where the drawn items have ended so far: an item in the menu (or with no forms) sits there.\n let end = 0;\n for (const it of input.items) {\n const p = placed.get(it.id);\n if (!p || p.kind === \"menu\") {\n b.push(end);\n continue;\n }\n b.push(start(p.rect) + size(p.rect) / 2);\n end = start(p.rect) + size(p.rect);\n }\n b.push(fit.length);\n // Monotone, whatever the sizes did (a stack that overflows).\n for (let i = 1; i < b.length; i++) b[i] = Math.min(Math.max(b[i]!, b[i - 1]!), fit.length);\n return b;\n}\n\nexport function stackTargets(input: StackInput, dragged: StackItem): StackTarget[] {\n if (dragged.forms.length === 0) return [];\n const rest: StackInput = { ...input, items: input.items.filter((it) => it.id !== dragged.id) };\n const base = fitStack(rest);\n const b = bounds(rest, base);\n const column = rest.axis === \"column\";\n const newest = rest.items.reduce((m, it) => Math.max(m, it.used), dragged.used) + 1;\n const item: StackItem = { ...dragged, used: newest };\n const out: StackTarget[] = [];\n for (let k = 0; k <= rest.items.length; k++) {\n const solved = withItem(rest, item, k);\n const fit = fitStack(solved);\n const slot = fit.items.find((p) => p.id === dragged.id)!.rect;\n const from = b[k]!;\n const to = b[k + 1]!;\n const across = Math.max(base.cross, fit.cross);\n out.push({\n id: `gap:${k}`,\n priority: 2,\n item: dragged.id,\n place: { kind: \"gap\", at: k },\n hit: column ? rect(0, from, across, to - from) : rect(from, 0, to - from, across),\n slot,\n marker: column ? rect(slot.x, slot.y, slot.width, 0) : rect(slot.x, slot.y, 0, slot.height),\n input: solved,\n fit,\n });\n }\n return out;\n}\n","/**\n * The stack's stored form: `storeStack(state)` gives plain JSON-safe data (`{ v: 1, order, kept, closed, used, forms\n * }`); `readStack(data, known?)` reads parsed data back and never throws. Anything but an object of version 1 is\n * `{ ok: false, error }`. Inside one it is lenient, as the panels' reader is: an id that isn't a string, is repeated\n * or (with `known`) isn't one the consumer has is dropped and listed in `dropped`; a `kept`, `closed`, `used` or\n * `forms` entry for an id that isn't in `order` is dropped; a form that isn't a whole number from 1 up is dropped;\n * the key `__proto__` in `forms` is dropped (the other keys, `constructor` and `prototype` included, are ids like any\n * other: `forms` is an object with no prototype, written and read); a field this version doesn't know is skipped and\n * reported as `\"<field>\"`. An item the consumer has that the stored order lacks is no error: the consumer places it.\n *\n * - `kept`: items kept open (they give way last).\n * - `closed`: items the user closed; their place in `order` is kept for when they open.\n * - `used`: the use order, oldest to newest, so a reload restores who gives way first (an item's `used` is its index in\n * it; one the list lacks is older than all). An id listed twice counts at its last place: the newest use wins.\n * - `forms`: the user's chosen form per item, the index of the largest form it may take (`StackItem.from`). 0, the\n * first, is not stored.\n */\n\nexport interface StackState {\n /** The items' order along the stack. */\n readonly order: readonly string[];\n /** Items kept open: they give way last. */\n readonly kept: readonly string[];\n /** Items the user closed. */\n readonly closed: readonly string[];\n /** The use order, oldest first. */\n readonly used: readonly string[];\n /** The form index the user chose per item (only those that chose one; absent means the first). */\n readonly forms: Readonly<Record<string, number>>;\n}\n\nexport interface StoredStack {\n readonly v: 1;\n readonly order: string[];\n readonly kept: string[];\n readonly closed: string[];\n readonly used: string[];\n readonly forms: Record<string, number>;\n}\n\nexport type StackRead = { readonly ok: true; readonly state: StackState; readonly dropped: readonly string[] } | { readonly ok: false; readonly error: string };\n\n/** A state with these items in this order, none kept or closed, used in that order, every form the first. */\nexport const createStack = (order: readonly string[]): StackState => ({ order: [...order], kept: [], closed: [], used: [...order], forms: {} });\n\nconst isForm = (f: unknown): f is number => typeof f === \"number\" && Number.isInteger(f) && f >= 1;\n/** Ids that are in `order`, each once, at the first place it was listed. */\nconst uniq = (ids: readonly string[], order: readonly string[]): string[] => [...new Set(ids)].filter((id) => order.includes(id));\n/** Ids that are in `order`, each once, at the last place it was listed (the newest use wins). */\nconst uniqLast = (ids: readonly string[], order: readonly string[]): string[] => ids.filter((id, i) => order.includes(id) && ids.lastIndexOf(id) === i);\n/** Forms live in an object with no prototype, so no key (`constructor`, `prototype`) is more than a key; only `__proto__` is refused. */\nconst noForms = (): Record<string, number> => Object.create(null) as Record<string, number>;\n\nexport function storeStack(state: StackState): StoredStack {\n const forms = noForms();\n for (const id of state.order) {\n const f = state.forms[id];\n if (id !== \"__proto__\" && Object.hasOwn(state.forms, id) && isForm(f)) forms[id] = f;\n }\n return { v: 1, order: [...state.order], kept: uniq(state.kept, state.order), closed: uniq(state.closed, state.order), used: uniqLast(state.used, state.order), forms };\n}\n\nconst isObject = (x: unknown): x is Record<string, unknown> => typeof x === \"object\" && x !== null && !Array.isArray(x);\n\nexport function readStack(data: unknown, known?: readonly string[]): StackRead {\n try {\n return read(data, known);\n } catch (e) {\n // A getter that throws, a proxy: still no throw.\n return { ok: false, error: `unreadable: ${e instanceof Error ? e.message : typeof e}` };\n }\n}\n\nconst versionWords = (v: unknown): string => (typeof v === \"number\" ? String(v) : typeof v === \"string\" ? JSON.stringify(v.length > 24 ? `${v.slice(0, 24)}…` : v) : typeof v);\n\nfunction read(data: unknown, known?: readonly string[]): StackRead {\n if (!isObject(data)) return { ok: false, error: \"not an object\" };\n if (data.v !== 1) return { ok: false, error: `unknown version ${versionWords(data.v)} (this reads version 1)` };\n if (!Array.isArray(data.order)) return { ok: false, error: \"order is missing\" };\n const dropped: string[] = [];\n const drop = (what: string): void => {\n if (!dropped.includes(what)) dropped.push(what);\n };\n for (const k of Object.keys(data)) if (![\"v\", \"order\", \"kept\", \"closed\", \"used\", \"forms\"].includes(k)) drop(k);\n const order: string[] = [];\n for (const x of data.order as unknown[]) {\n if (typeof x !== \"string\") drop(`a ${typeof x} where an id goes`);\n else if ((known && !known.includes(x)) || order.includes(x)) drop(x);\n else order.push(x);\n }\n // A list of ids that are in `order`, each once: at the first place it was listed, or for `used` the last (the newest use wins).\n const list = (key: \"kept\" | \"closed\" | \"used\"): string[] => {\n const out: string[] = [];\n const x = data[key];\n if (x === undefined) return out;\n if (!Array.isArray(x)) {\n drop(key);\n return out;\n }\n for (const id of x as unknown[]) {\n if (typeof id === \"string\" && order.includes(id)) {\n if (key === \"used\" && out.includes(id)) out.splice(out.indexOf(id), 1);\n if (!out.includes(id)) out.push(id);\n } else drop(typeof id === \"string\" ? `${key}:${id}` : `a ${typeof id} in ${key}`);\n }\n return out;\n };\n const kept = list(\"kept\");\n const closed = list(\"closed\");\n const used = list(\"used\");\n const forms = noForms();\n if (data.forms !== undefined) {\n if (!isObject(data.forms)) drop(\"forms\");\n else {\n for (const id of Object.keys(data.forms)) {\n const f = data.forms[id];\n if (id === \"__proto__\" || !Object.hasOwn(data.forms, id) || !order.includes(id) || !isForm(f)) drop(`forms:${id}`);\n else forms[id] = f;\n }\n }\n }\n return { ok: true, state: { order, kept, closed, used, forms }, dropped };\n}\n"],"mappings":";;;;;AA+EO,IAAM,eAAe;AAErB,IAAM,eAAe;AAE5B,IAAM,SAAS,CAAC,GAAW,aAA8B,OAAO,SAAS,CAAC,IAAI,IAAI;AAM3E,SAAS,SAAS,OAA0F;AACjH,QAAM,MAAM,KAAK,IAAI,GAAG,OAAO,MAAM,MAAM,KAAK,CAAC,CAAC;AAClD,QAAM,MAAM,OAAO,MAAM,MAAM,MAAM,GAAG,IAAI,WAAW,MAAM,MAAM;AACnE,SAAO,EAAE,KAAK,KAAK,KAAK,IAAI,KAAK,GAAG,GAAG,MAAM,KAAK,IAAI,GAAG,OAAO,MAAM,MAAM,QAAQ,cAAc,YAAY,CAAC,EAAE;AACnH;AACO,IAAM,WAAW,CAAC,UAA8B,KAAK,IAAI,GAAG,OAAO,MAAM,MAAM,QAAQ,cAAc,YAAY,CAAC;AAGlH,IAAM,gBAAgB,CAAC,MAA0B,EAAE,aAAa,EAAE,SAAS;AAG3E,SAAS,SAAS,OAAmB,MAAiB,IAAwB;AACnF,QAAM,IAAI,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC,GAAG,MAAM,MAAM,MAAM;AAClE,SAAO,EAAE,GAAG,OAAO,OAAO,CAAC,GAAG,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,GAAG,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE;AACxF;;;AC9EA,IAAM,MAAM;AACZ,IAAM,SAAS,CAAC,MAA0B,EAAE,SAAS;AACrD,IAAMA,UAAS,CAAC,GAAW,WAAW,MAAe,OAAO,SAAS,CAAC,IAAI,IAAI;AAC9E,IAAM,SAAS,CAAC,GAAc,UAAsC,OAAO,CAAC,IAAI,IAAI,KAAK,IAAI,GAAGA,QAAO,EAAE,KAAK,KAAK,CAAC,CAAC;AAErH,IAAM,WAAW,CAAC,MAAyBA,QAAO,EAAE,MAAM,OAAO,EAAE,MAAM,OAAO;AAGhF,SAAS,OAAO,OAA6B,IAA+B;AAC1E,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,QAAI,GAAG,CAAC,IAAK,KAAK,MAAM,CAAC,EAAG,MAAM,OAAQ;AAC1C,QAAI,OAAO,GAAG;AACZ,aAAO;AACP;AAAA,IACF;AACA,UAAM,IAAI,MAAM,CAAC;AACjB,UAAM,IAAI,MAAM,IAAI;AACpB,UAAM,KAAK,EAAE,OAAO,IAAI;AACxB,UAAM,KAAK,EAAE,OAAO,IAAI;AACxB,QAAI,OAAO,KAAK,KAAK,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAM,QAAO;AAAA,EAC/E;AACA,SAAO;AACT;AAEO,SAAS,SAAS,OAA6B;AACpD,QAAM,SAAS,KAAK,IAAI,GAAGA,QAAO,MAAM,MAAM,CAAC;AAC/C,QAAM,MAAM,SAAS,KAAK;AAC1B,QAAM,QAAQ,MAAM,MAAM,OAAO,CAAC,OAAO,GAAG,MAAM,SAAS,CAAC;AAC5D,QAAM,QAAQ,MAAM,IAAI,CAAC,OAAO,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,MAAMA,QAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,MAAM,SAAS,CAAC,CAAC;AAC5G,QAAM,KAAK,CAAC,GAAG,KAAK;AACpB,QAAM,OAAO,CAAC,MAAyB,MAAM,CAAC,EAAG,MAAM,GAAG,CAAC,CAAE;AAC7D,QAAM,MAAM,MAAM,IAAI,CAAC,GAAG,MAAM,CAAC;AAEjC,QAAM,YAAsB,CAAC;AAC7B,QAAM,YAAsB,CAAC;AAC7B,QAAM,OAAO,CAAC,MAAoB;AAChC,QAAI,CAAC,UAAU,SAAS,CAAC,EAAG,WAAU,KAAK,CAAC;AAC5C,OAAG,CAAC;AACJ,QAAI,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,UAAU,SAAS,CAAC,EAAG,WAAU,KAAK,CAAC;AAAA,EACjE;AAEA,aAAW,KAAK,IAAK,KAAI,OAAO,KAAK,CAAC,CAAC,EAAG,WAAU,KAAK,CAAC;AAE1D,QAAM,UAAU,MAAc;AAC5B,UAAM,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,KAAK,CAAC,CAAC,KAAK,cAAc,KAAK,CAAC,CAAC,CAAC;AAC5E,QAAI,QAAQ,WAAW,EAAG,QAAO,IAAI;AACrC,UAAM,OAAO,KAAK,IAAI,GAAG,QAAQ,IAAI,CAAC,MAAMA,QAAO,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,CAAC;AAC1E,WAAO,KAAK,IAAI,KAAK,IAAI,MAAM,IAAI,GAAG,GAAG,IAAI,GAAG;AAAA,EAClD;AACA,QAAM,SAAS,MAAe,IAAI,KAAK,CAAC,MAAM,OAAO,KAAK,CAAC,CAAC,CAAC;AAC7D,QAAM,SAAS,MAAc,IAAI,OAAO,CAAC,GAAG,MAAM,IAAI,OAAO,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,KAAK,OAAO,IAAI,SAAS,KAAK,IAAI;AACjH,QAAM,UAAU,CAAC,MAAuB,GAAG,CAAC,IAAK,IAAI,MAAM,CAAC,EAAG,MAAM;AACrE,QAAM,UAAU,CAAC,GAAWC,WAA2B,CAAC,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,cAAc,KAAK,CAAC,CAAC,KAAK,SAAS,KAAK,CAAC,CAAC,IAAIA,SAAQ;AAElI,MAAI,QAAQ,QAAQ;AACpB,MAAI,WAAW;AACf,QAAM,QAAQ,MAAM,OAAO,CAAC,GAAG,OAAO,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI;AAChE,WAAS,IAAI,GAAG,KAAK,OAAO,KAAK;AAC/B,YAAQ,QAAQ;AAEhB,QAAI,UAAU;AACd,eAAW,KAAK,KAAK;AACnB,UAAI,QAAQ,GAAG,KAAK,KAAK,QAAQ,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,EAAG,MAAM,GAAG,CAAC,IAAK,CAAC,CAAE,GAAG;AAC5E,aAAK,CAAC;AACN,kBAAU;AAAA,MACZ;AAAA,IACF;AACA,QAAI,QAAS;AACb,UAAM,OAAO,OAAO,IAAI;AACxB,QAAI,QAAQ,IAAK;AACjB,UAAM,IAAI,OAAO,OAAO,EAAE;AAC1B,QAAI,IAAI,GAAG;AACT,iBAAW;AACX;AAAA,IACF;AAEA,UAAM,WAAW,OAAO,MAAM,CAAC,EAAG,MAAM,GAAG,CAAC,IAAK,CAAC,CAAE;AACpD,UAAM,OAAO,OAAO,KAAK,CAAC,GAAG,KAAK,KAAK,OAAO,IAAI,IAAI,SAAS,KAAK;AACpE,QAAI,YAAY,OAAO,KAAK,IAAI,OAAO,OAAO,EAAE,WAAW,GAAG;AAC5D,iBAAW;AACX;AAAA,IACF;AACA,SAAK,CAAC;AAAA,EACR;AACA,UAAQ,QAAQ;AAGhB,QAAM,WAAW,OAAO,IAAI,SAAS,KAAK,IAAI;AAC9C,QAAM,QAAQ,KAAK,IAAI,GAAG,SAAS,QAAQ;AAC3C,QAAM,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC;AAChD,QAAM,MAAM,CAAC,MAAsB,OAAO,KAAK,CAAC,GAAG,KAAK;AACxD,QAAM,MAAM,CAAC,MAAsB,KAAK,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,SAAS,CAAC;AAC9E,QAAM,SAAS,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC;AACnD,QAAM,SAAS,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC;AACnD,QAAM,OAAO,oBAAI,IAAoB;AACrC,MAAI,UAAU,QAAQ,KAAK;AACzB,UAAM,UAAU,MAAM,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,KAAK,KAAK;AACtD,UAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,MAAM;AACxC,UAAM,SAAS,QAAQ,OAAO,CAAC,GAAG,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC;AACrD,eAAW,KAAK,OAAO;AACrB,YAAM,QAAQ,QAAQ,SAAS,CAAC,IAAK,SAAS,MAAM,IAAI,CAAC,IAAI,SAAS,IAAI,QAAQ,SAAU;AAC5F,WAAK,IAAI,GAAG,IAAI,CAAC,IAAI,QAAQ,KAAK;AAAA,IACpC;AAAA,EACF,OAAO;AACL,UAAM,IAAI,QAAQ,UAAU,SAAS,UAAU,QAAQ,WAAW,SAAS,UAAU;AACrF,eAAW,KAAK,MAAO,MAAK,IAAI,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC;AAAA,EACnE;AAEA,QAAM,SAAS,CAAC,MAAc,SAAwB,MAAM,SAAS,WAAW,EAAE,GAAG,GAAG,GAAG,MAAM,OAAO,OAAO,QAAQ,KAAK,IAAI,EAAE,GAAG,MAAM,GAAG,GAAG,OAAO,MAAM,QAAQ,MAAM;AAC5K,QAAM,QAAQ,oBAAI,IAAkB;AACpC,MAAI,SAAS;AACb,aAAW,KAAK,OAAO;AACrB,UAAM,IAAI,KAAK,IAAI,CAAC;AACpB,UAAM,IAAI,GAAG,OAAO,QAAQ,CAAC,CAAC;AAC9B,cAAU;AAAA,EACZ;AACA,QAAM,WAAW,OAAO,KAAK,IAAI,QAAQ,SAAS,QAAQ,GAAG,QAAQ;AACrE,QAAM,SAAS,IAAI,OAAO,CAAC,MAAM,OAAO,KAAK,CAAC,CAAC,CAAC;AAChD,QAAM,OAAyB,OAAO,SAAS,IAAI,EAAE,MAAM,UAAU,OAAO,OAAO,IAAI,CAAC,MAAM,MAAM,CAAC,EAAG,EAAE,EAAE,IAAI;AAEhH,QAAM,SAA4B,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,MAAM,CAAC,EAAG,IAAI,MAAM,GAAG,CAAC,GAAI,MAAM,KAAK,CAAC,EAAE,MAAM,MAAM,OAAO,KAAK,CAAC,CAAC,IAAI,WAAW,MAAM,IAAI,CAAC,EAAG,EAAE;AAE3J,QAAM,QAAqB,CAAC;AAC5B,aAAW,MAAM,MAAM,MAAO,KAAI,GAAG,MAAM,WAAW,EAAG,OAAM,KAAK,EAAE,MAAM,YAAY,IAAI,GAAG,GAAG,CAAC;AACnG,aAAW,KAAK,UAAW,KAAI,GAAG,CAAC,IAAK,MAAM,CAAC,EAAI,OAAM,KAAK,EAAE,MAAM,aAAa,IAAI,MAAM,CAAC,EAAG,IAAI,MAAM,MAAM,CAAC,GAAI,IAAI,GAAG,CAAC,EAAG,CAAC;AAClI,aAAW,KAAK,UAAW,KAAI,OAAO,KAAK,CAAC,CAAC,EAAG,OAAM,KAAK,EAAE,MAAM,WAAW,IAAI,MAAM,CAAC,EAAG,GAAG,CAAC;AAChG,aAAW,KAAK,IAAK,KAAI,QAAQ,GAAG,KAAK,EAAG,OAAM,KAAK,EAAE,MAAM,YAAY,IAAI,MAAM,CAAC,EAAG,GAAG,CAAC;AAC7F,MAAI,WAAW,IAAK,OAAM,KAAK,EAAE,MAAM,YAAY,MAAM,SAAS,CAAC;AACnE,SAAO,EAAE,MAAM,MAAM,MAAM,QAAQ,OAAO,OAAO,QAAQ,MAAM,MAAM;AACvE;;;AChHA,SAAS,OAAO,OAAmB,KAAyB;AAC1D,QAAM,SAAS,MAAM,SAAS;AAC9B,QAAM,QAAQ,CAAC,MAAqB,SAAS,EAAE,IAAI,EAAE;AACrD,QAAM,OAAO,CAAC,MAAqB,SAAS,EAAE,SAAS,EAAE;AACzD,QAAM,SAAS,IAAI,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AACtD,QAAM,IAAI,CAAC,CAAC;AAEZ,MAAI,MAAM;AACV,aAAW,MAAM,MAAM,OAAO;AAC5B,UAAM,IAAI,OAAO,IAAI,GAAG,EAAE;AAC1B,QAAI,CAAC,KAAK,EAAE,SAAS,QAAQ;AAC3B,QAAE,KAAK,GAAG;AACV;AAAA,IACF;AACA,MAAE,KAAK,MAAM,EAAE,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC;AACvC,UAAM,MAAM,EAAE,IAAI,IAAI,KAAK,EAAE,IAAI;AAAA,EACnC;AACA,IAAE,KAAK,IAAI,MAAM;AAEjB,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,GAAE,CAAC,IAAI,KAAK,IAAI,KAAK,IAAI,EAAE,CAAC,GAAI,EAAE,IAAI,CAAC,CAAE,GAAG,IAAI,MAAM;AACzF,SAAO;AACT;AAEO,SAAS,aAAa,OAAmB,SAAmC;AACjF,MAAI,QAAQ,MAAM,WAAW,EAAG,QAAO,CAAC;AACxC,QAAM,OAAmB,EAAE,GAAG,OAAO,OAAO,MAAM,MAAM,OAAO,CAAC,OAAO,GAAG,OAAO,QAAQ,EAAE,EAAE;AAC7F,QAAM,OAAO,SAAS,IAAI;AAC1B,QAAM,IAAI,OAAO,MAAM,IAAI;AAC3B,QAAM,SAAS,KAAK,SAAS;AAC7B,QAAM,SAAS,KAAK,MAAM,OAAO,CAAC,GAAG,OAAO,KAAK,IAAI,GAAG,GAAG,IAAI,GAAG,QAAQ,IAAI,IAAI;AAClF,QAAM,OAAkB,EAAE,GAAG,SAAS,MAAM,OAAO;AACnD,QAAM,MAAqB,CAAC;AAC5B,WAAS,IAAI,GAAG,KAAK,KAAK,MAAM,QAAQ,KAAK;AAC3C,UAAM,SAAS,SAAS,MAAM,MAAM,CAAC;AACrC,UAAM,MAAM,SAAS,MAAM;AAC3B,UAAM,OAAO,IAAI,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ,EAAE,EAAG;AACzD,UAAM,OAAO,EAAE,CAAC;AAChB,UAAM,KAAK,EAAE,IAAI,CAAC;AAClB,UAAM,SAAS,KAAK,IAAI,KAAK,OAAO,IAAI,KAAK;AAC7C,QAAI,KAAK;AAAA,MACP,IAAI,OAAO,CAAC;AAAA,MACZ,UAAU;AAAA,MACV,MAAM,QAAQ;AAAA,MACd,OAAO,EAAE,MAAM,OAAO,IAAI,EAAE;AAAA,MAC5B,KAAK,SAAS,KAAK,GAAG,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,MAAM,GAAG,KAAK,MAAM,MAAM;AAAA,MAChF;AAAA,MACA,QAAQ,SAAS,KAAK,KAAK,GAAG,KAAK,GAAG,KAAK,OAAO,CAAC,IAAI,KAAK,KAAK,GAAG,KAAK,GAAG,GAAG,KAAK,MAAM;AAAA,MAC1F,OAAO;AAAA,MACP;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AACT;;;ACpDO,IAAM,cAAc,CAAC,WAA0C,EAAE,OAAO,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,MAAM,CAAC,GAAG,KAAK,GAAG,OAAO,CAAC,EAAE;AAE7I,IAAM,SAAS,CAAC,MAA4B,OAAO,MAAM,YAAY,OAAO,UAAU,CAAC,KAAK,KAAK;AAEjG,IAAM,OAAO,CAAC,KAAwB,UAAuC,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC,EAAE,OAAO,CAAC,OAAO,MAAM,SAAS,EAAE,CAAC;AAEhI,IAAM,WAAW,CAAC,KAAwB,UAAuC,IAAI,OAAO,CAAC,IAAI,MAAM,MAAM,SAAS,EAAE,KAAK,IAAI,YAAY,EAAE,MAAM,CAAC;AAEtJ,IAAM,UAAU,MAA8B,uBAAO,OAAO,IAAI;AAEzD,SAAS,WAAW,OAAgC;AACzD,QAAM,QAAQ,QAAQ;AACtB,aAAW,MAAM,MAAM,OAAO;AAC5B,UAAM,IAAI,MAAM,MAAM,EAAE;AACxB,QAAI,OAAO,eAAe,OAAO,OAAO,MAAM,OAAO,EAAE,KAAK,OAAO,CAAC,EAAG,OAAM,EAAE,IAAI;AAAA,EACrF;AACA,SAAO,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,MAAM,KAAK,GAAG,MAAM,KAAK,MAAM,MAAM,MAAM,KAAK,GAAG,QAAQ,KAAK,MAAM,QAAQ,MAAM,KAAK,GAAG,MAAM,SAAS,MAAM,MAAM,MAAM,KAAK,GAAG,MAAM;AACvK;AAEA,IAAM,WAAW,CAAC,MAA6C,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,CAAC;AAE/G,SAAS,UAAU,MAAe,OAAsC;AAC7E,MAAI;AACF,WAAO,KAAK,MAAM,KAAK;AAAA,EACzB,SAAS,GAAG;AAEV,WAAO,EAAE,IAAI,OAAO,OAAO,eAAe,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,GAAG;AAAA,EACxF;AACF;AAEA,IAAM,eAAe,CAAC,MAAwB,OAAO,MAAM,WAAW,OAAO,CAAC,IAAI,OAAO,MAAM,WAAW,KAAK,UAAU,EAAE,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,WAAM,CAAC,IAAI,OAAO;AAE5K,SAAS,KAAK,MAAe,OAAsC;AACjE,MAAI,CAAC,SAAS,IAAI,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO,gBAAgB;AAChE,MAAI,KAAK,MAAM,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO,mBAAmB,aAAa,KAAK,CAAC,CAAC,0BAA0B;AAC9G,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO,mBAAmB;AAC9E,QAAM,UAAoB,CAAC;AAC3B,QAAM,OAAO,CAAC,SAAuB;AACnC,QAAI,CAAC,QAAQ,SAAS,IAAI,EAAG,SAAQ,KAAK,IAAI;AAAA,EAChD;AACA,aAAW,KAAK,OAAO,KAAK,IAAI,EAAG,KAAI,CAAC,CAAC,KAAK,SAAS,QAAQ,UAAU,QAAQ,OAAO,EAAE,SAAS,CAAC,EAAG,MAAK,CAAC;AAC7G,QAAM,QAAkB,CAAC;AACzB,aAAW,KAAK,KAAK,OAAoB;AACvC,QAAI,OAAO,MAAM,SAAU,MAAK,KAAK,OAAO,CAAC,mBAAmB;AAAA,aACtD,SAAS,CAAC,MAAM,SAAS,CAAC,KAAM,MAAM,SAAS,CAAC,EAAG,MAAK,CAAC;AAAA,QAC9D,OAAM,KAAK,CAAC;AAAA,EACnB;AAEA,QAAM,OAAO,CAAC,QAA8C;AAC1D,UAAM,MAAgB,CAAC;AACvB,UAAM,IAAI,KAAK,GAAG;AAClB,QAAI,MAAM,OAAW,QAAO;AAC5B,QAAI,CAAC,MAAM,QAAQ,CAAC,GAAG;AACrB,WAAK,GAAG;AACR,aAAO;AAAA,IACT;AACA,eAAW,MAAM,GAAgB;AAC/B,UAAI,OAAO,OAAO,YAAY,MAAM,SAAS,EAAE,GAAG;AAChD,YAAI,QAAQ,UAAU,IAAI,SAAS,EAAE,EAAG,KAAI,OAAO,IAAI,QAAQ,EAAE,GAAG,CAAC;AACrE,YAAI,CAAC,IAAI,SAAS,EAAE,EAAG,KAAI,KAAK,EAAE;AAAA,MACpC,MAAO,MAAK,OAAO,OAAO,WAAW,GAAG,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,EAAE,OAAO,GAAG,EAAE;AAAA,IAClF;AACA,WAAO;AAAA,EACT;AACA,QAAM,OAAO,KAAK,MAAM;AACxB,QAAM,SAAS,KAAK,QAAQ;AAC5B,QAAM,OAAO,KAAK,MAAM;AACxB,QAAM,QAAQ,QAAQ;AACtB,MAAI,KAAK,UAAU,QAAW;AAC5B,QAAI,CAAC,SAAS,KAAK,KAAK,EAAG,MAAK,OAAO;AAAA,SAClC;AACH,iBAAW,MAAM,OAAO,KAAK,KAAK,KAAK,GAAG;AACxC,cAAM,IAAI,KAAK,MAAM,EAAE;AACvB,YAAI,OAAO,eAAe,CAAC,OAAO,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC,MAAM,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,EAAG,MAAK,SAAS,EAAE,EAAE;AAAA,YAC5G,OAAM,EAAE,IAAI;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AACA,SAAO,EAAE,IAAI,MAAM,OAAO,EAAE,OAAO,MAAM,QAAQ,MAAM,MAAM,GAAG,QAAQ;AAC1E;","names":["finite","cross"]}
|