@n-uf/placement 26.11.9 → 26.11.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,8 @@ import {
4
4
 
5
5
  // src/panels/model.ts
6
6
  var SIDES = ["left", "right"];
7
+ var EDGES = ["top", "bottom"];
8
+ var BAND_SIZE = { min: 120, max: 480, natural: 200 };
7
9
  var IDENTITY_VIEW = { x: 0, y: 0, scale: 1 };
8
10
  var DEFAULT_PANELS_OPTIONS = {
9
11
  giveWay: { order: "oldest", by: "used" },
@@ -16,7 +18,10 @@ var DEFAULT_PANELS_OPTIONS = {
16
18
  rail: 36,
17
19
  segment: 36,
18
20
  railInset: 4,
19
- tabs: 28
21
+ tabs: 28,
22
+ bands: {},
23
+ pagesMinHeight: 240,
24
+ reserve: {}
20
25
  };
21
26
  var FLOAT_HEIGHT = 360;
22
27
  var FLOAT_MIN = { width: 160, height: 120 };
@@ -27,6 +32,10 @@ function whereIs(state, id) {
27
32
  const index = state.sides[side].findIndex((c) => c.panels.includes(id));
28
33
  if (index >= 0) return { kind: "column", side, index };
29
34
  }
35
+ for (const edge of EDGES) {
36
+ const index = state.bands?.[edge]?.panels.indexOf(id) ?? -1;
37
+ if (index >= 0) return { kind: "band", edge, index };
38
+ }
30
39
  const float2 = state.floats.find((f) => f.id === id);
31
40
  return float2 ? { kind: "float", float: float2 } : { kind: "none" };
32
41
  }
@@ -42,6 +51,21 @@ function floatSize(def) {
42
51
  const width = Math.max(FLOAT_MIN.width, def?.width.natural ?? FLOAT_MIN.width);
43
52
  return { min: FLOAT_MIN, natural: { width, height: FLOAT_HEIGHT } };
44
53
  }
54
+ var bandSize = (def) => ({ ...def?.band ?? BAND_SIZE });
55
+ function bandHeight(defs, band) {
56
+ if (!defs) return band.height;
57
+ const ds = band.panels.map((id) => bandSize(defOf(defs, id)));
58
+ const lo = Math.max(0, ...ds.map((d) => d.min));
59
+ const hi = Math.max(lo, Math.min(...ds.map((d) => d.max)));
60
+ return Math.min(Math.max(Number.isFinite(band.height) ? band.height : lo, lo), hi);
61
+ }
62
+ var withBand = (state, edge, band) => {
63
+ const { bands: _b, ...rest } = state;
64
+ const bands = { ...state.bands };
65
+ if (band && band.panels.length) bands[edge] = band;
66
+ else delete bands[edge];
67
+ return EDGES.some((e) => bands[e]) ? { ...rest, bands } : rest;
68
+ };
45
69
  function ownWidth(defs, state, id) {
46
70
  const def = defOf(defs, id);
47
71
  const w = whereIs(state, id);
@@ -63,10 +87,18 @@ function createPanels(defs, start) {
63
87
  return { id: f.id, rect: { x: f.x, y: f.y, width: f.width ?? n.width, height: f.height ?? n.height }, ...f.pinned ? { pinned: true } : {} };
64
88
  });
65
89
  const sides = { left: (start.left ?? []).map(column), right: (start.right ?? []).map(column) };
66
- const order = [...sides.left, ...sides.right].flatMap((c) => c.panels).concat(floats.map((f) => f.id));
90
+ const bands = {};
91
+ for (const edge of EDGES) {
92
+ const ids = start[edge] ?? [];
93
+ if (!ids.length) continue;
94
+ const natural = Math.max(0, ...ids.map((id) => bandSize(defOf(defs, id)).natural));
95
+ bands[edge] = { panels: [...ids], height: bandHeight(defs, { panels: ids, height: natural }) };
96
+ }
97
+ const order = [...sides.left, ...sides.right].flatMap((c) => c.panels).concat(EDGES.flatMap((e) => bands[e]?.panels ?? [])).concat(floats.map((f) => f.id));
67
98
  const clock = {};
68
99
  order.forEach((id, i) => clock[id] = { opened: i + 1, used: i + 1 });
69
- return { sides, floats, folded: [...start.folded ?? []], kept: [...start.kept ?? []], closed: [...start.closed ?? []], clock };
100
+ const s = { sides, floats, folded: [...start.folded ?? []], kept: [...start.kept ?? []], closed: [...start.closed ?? []], clock };
101
+ return EDGES.some((e) => bands[e]) ? { ...s, bands } : s;
70
102
  }
71
103
  function liftPanel(state, id) {
72
104
  const out = (c) => {
@@ -74,11 +106,22 @@ function liftPanel(state, id) {
74
106
  return { ...c.active === id ? rest : c, panels: without(c.panels, id) };
75
107
  };
76
108
  const lift = (cols) => cols.map(out).filter((c) => c.panels.length > 0);
77
- return { ...state, sides: { left: lift(state.sides.left), right: lift(state.sides.right) }, floats: state.floats.filter((f) => f.id !== id) };
109
+ let s = { ...state, sides: { left: lift(state.sides.left), right: lift(state.sides.right) }, floats: state.floats.filter((f) => f.id !== id) };
110
+ for (const edge of EDGES) {
111
+ const b = s.bands?.[edge];
112
+ if (b?.panels.includes(id)) s = withBand(s, edge, { ...b, panels: without(b.panels, id) });
113
+ }
114
+ return s;
78
115
  }
79
116
  function open(state, id, at) {
80
117
  let s = { ...state, closed: without(state.closed, id) };
81
- if (at && whereIs(s, id).kind === "none") s = { ...s, sides: { ...s.sides, [at.side]: [...s.sides[at.side], { panels: [id], width: at.width }] } };
118
+ if (at && whereIs(s, id).kind === "none") {
119
+ if ("edge" in at) {
120
+ const b = s.bands?.[at.edge];
121
+ const panels = [...b?.panels ?? [], id];
122
+ s = withBand({ ...s, folded: s.folded.filter((x) => !panels.includes(x)) }, at.edge, { panels, height: b?.height ?? at.height ?? BAND_SIZE.natural });
123
+ } else s = { ...s, sides: { ...s.sides, [at.side]: [...s.sides[at.side], { panels: [id], width: at.width }] } };
124
+ }
82
125
  const w = whereIs(s, id);
83
126
  if (w.kind === "column" && s.sides[w.side][w.index].show === "tabs") s = { ...s, sides: { ...s.sides, [w.side]: s.sides[w.side].map((c, i) => i === w.index ? { ...c, active: id } : c) } };
84
127
  return bump(s, id, true);
@@ -90,12 +133,37 @@ function focus(state, id) {
90
133
  return bump(s, id, false);
91
134
  }
92
135
  function toggleFold(state, id) {
136
+ const w = whereIs(state, id);
137
+ if (w.kind === "band") {
138
+ const ids = state.bands[w.edge].panels;
139
+ const shown = ids.filter((x) => !state.closed.includes(x));
140
+ if ((shown.length ? shown : ids).every((x) => state.folded.includes(x))) return bump({ ...state, folded: state.folded.filter((x) => !ids.includes(x)) }, id, false);
141
+ return { ...state, folded: [...state.folded.filter((x) => !ids.includes(x)), ...ids] };
142
+ }
93
143
  if (state.folded.includes(id)) return bump({ ...state, folded: without(state.folded, id) }, id, false);
94
144
  return { ...state, folded: [...state.folded, id] };
95
145
  }
96
146
  function keep(state, id, on) {
97
147
  if (!on) return { ...state, kept: without(state.kept, id) };
98
- return whereIs(state, id).kind === "column" ? { ...state, kept: withOnce(state.kept, id) } : state;
148
+ const k = whereIs(state, id).kind;
149
+ return k === "column" || k === "band" ? { ...state, kept: withOnce(state.kept, id) } : state;
150
+ }
151
+ function dockBand(state, id, edge, index, defs) {
152
+ const s = liftPanel(state, id);
153
+ const b = s.bands?.[edge];
154
+ const panels = [...b?.panels ?? []];
155
+ panels.splice(Math.max(0, Math.min(index, panels.length)), 0, id);
156
+ const height = b?.height ?? bandSize(defs ? defOf(defs, id) : void 0).natural;
157
+ const s1 = withBand(s, edge, { panels, height });
158
+ return bump({ ...s1, folded: s1.folded.filter((x) => !panels.includes(x)), closed: without(s1.closed, id) }, id, false);
159
+ }
160
+ function resizeBand(state, edge, height) {
161
+ const b = state.bands?.[edge];
162
+ if (!b || !Number.isFinite(height)) return state;
163
+ const t = tick(state);
164
+ const clock = { ...state.clock };
165
+ for (const id of b.panels) clock[id] = { opened: clock[id]?.opened ?? 0, used: t };
166
+ return { ...withBand(state, edge, { ...b, height: Math.max(0, height) }), clock };
99
167
  }
100
168
  function dock(state, id, side, index, options = {}) {
101
169
  const w = whereIs(state, id);
@@ -164,8 +232,12 @@ function samePlaces(a, b, defs) {
164
232
  (side) => s.sides[side].map((c) => `${c.panels.join(",")}@${Math.round(columnWidth(defs, c))}${c.show === "tabs" ? `/tabs:${c.active ?? c.panels[0]}` : ""}`).join("|")
165
233
  ).join("\u2016");
166
234
  const fl = (s) => s.floats.map((f) => `${f.id}@${Math.round(f.rect.x)},${Math.round(f.rect.y)},${Math.round(f.rect.width)},${Math.round(f.rect.height)}${f.pinned ? "p" : ""}`).join("|");
235
+ const bs = (s) => EDGES.map((e) => {
236
+ const b2 = s.bands?.[e];
237
+ return b2 ? `${b2.panels.join(",")}@${Math.round(bandHeight(defs, b2))}` : "";
238
+ }).join("\u2016");
167
239
  const set = (l) => [...l].sort().join(",");
168
- return cols(a) === cols(b) && fl(a) === fl(b) && set(a.folded) === set(b.folded) && set(a.kept) === set(b.kept) && set(a.closed) === set(b.closed);
240
+ return cols(a) === cols(b) && bs(a) === bs(b) && fl(a) === fl(b) && set(a.folded) === set(b.folded) && set(a.kept) === set(b.kept) && set(a.closed) === set(b.closed);
169
241
  }
170
242
 
171
243
  // src/panels/layout.ts
@@ -182,6 +254,67 @@ function layoutPanels(defs, state, stage, options = {}) {
182
254
  const kept = new Set(state.kept);
183
255
  const by = opt.giveWay.by;
184
256
  const ride = opt.fold === "ride";
257
+ const px = (v) => typeof v === "number" && Number.isFinite(v) ? Math.max(0, v) : 0;
258
+ const reserveOpt = opt.reserve ?? {};
259
+ const bandOpts = opt.bands ?? {};
260
+ const minH = typeof opt.pagesMinHeight === "number" ? opt.pagesMinHeight : DEFAULT_PANELS_OPTIONS.pagesMinHeight;
261
+ const res = { top: px(reserveOpt.top), right: px(reserveOpt.right), bottom: px(reserveOpt.bottom), left: px(reserveOpt.left) };
262
+ const bandsIn = [];
263
+ const offNotes = [];
264
+ for (const edge of EDGES) {
265
+ const b = state.bands?.[edge];
266
+ if (!b) continue;
267
+ const panels = b.panels.filter((id) => !closed.has(id));
268
+ if (panels.length === 0) continue;
269
+ const o = bandOpts[edge];
270
+ if (!o) {
271
+ offNotes.push({ code: "band-off", edge, items: panels });
272
+ continue;
273
+ }
274
+ const ds = panels.map((id) => bandSize(defOf(defs, id)));
275
+ const min = Math.max(0, ...ds.map((d) => d.min));
276
+ const max = Math.max(min, Math.min(...ds.map((d) => d.max)));
277
+ const height = clamp(Number.isFinite(b.height) ? b.height : min, min, max);
278
+ const age = Math.max(...panels.map((id) => state.clock[id]?.[by] ?? 0));
279
+ bandsIn.push({ edge, panels, at: panels.map((id) => b.panels.indexOf(id)), min, max, height, hand: panels.every((id) => folded.has(id)), kept: panels.some((id) => kept.has(id)), age, mode: o.mode, span: o.span });
280
+ }
281
+ const bOrder = bandsIn.filter((b) => !b.hand).sort((a, b) => Number(a.kept) - Number(b.kept) || (opt.giveWay.order === "oldest" ? a.age - b.age : b.age - a.age) || EDGES.indexOf(b.edge) - EDGES.indexOf(a.edge));
282
+ const gh = { auto: /* @__PURE__ */ new Set(), heights: /* @__PURE__ */ new Map() };
283
+ const fitH = (list, room) => {
284
+ const order2 = bOrder.filter((b) => list.includes(b));
285
+ const hands = list.length - order2.length;
286
+ const fit2 = (k) => {
287
+ const auto = new Set(order2.slice(0, k));
288
+ const heights = new Map(order2.filter((b) => !auto.has(b)).map((b) => [b, b.height]));
289
+ let excess = [...heights.values()].reduce((a, b) => a + b, 0) + (hands + auto.size) * opt.bar - room;
290
+ for (const b of order2) {
291
+ if (excess <= 0 || auto.has(b)) continue;
292
+ const take = Math.min(excess, b.height - b.min);
293
+ if (take > 0) {
294
+ heights.set(b, b.height - take);
295
+ excess -= take;
296
+ }
297
+ }
298
+ return { auto, heights, excess };
299
+ };
300
+ let g2 = fit2(0);
301
+ for (let k = 1; g2.excess > 0 && k <= order2.length; k++) g2 = fit2(k);
302
+ for (const b of g2.auto) gh.auto.add(b);
303
+ for (const [b, h] of g2.heights) gh.heights.set(b, h);
304
+ };
305
+ const reservedIn = bandsIn.filter((b) => b.mode === "reserved");
306
+ const overlayIn = bandsIn.filter((b) => b.mode === "overlay");
307
+ fitH(reservedIn, H - res.top - res.bottom - Math.max(minH, overlayIn.length * opt.bar));
308
+ const bandH = (b) => b ? gh.heights.get(b) ?? opt.bar : 0;
309
+ const pagesH = H - res.top - res.bottom - reservedIn.reduce((a, b) => a + bandH(b), 0);
310
+ fitH(overlayIn, pagesH - minH);
311
+ const topB = bandsIn.find((b) => b.edge === "top");
312
+ const botB = bandsIn.find((b) => b.edge === "bottom");
313
+ const hT = bandH(topB);
314
+ const hB = bandH(botB);
315
+ const colTop = topB?.span === "full" ? (topB.mode === "overlay" ? res.top : 0) + hT : 0;
316
+ const colBot = botB?.span === "full" ? H - (botB.mode === "overlay" ? res.bottom : 0) - hB : H;
317
+ const CH = Math.max(0, colBot - colTop);
185
318
  const cols = [];
186
319
  for (const side of SIDES) {
187
320
  state.sides[side].forEach((c, index) => {
@@ -261,7 +394,7 @@ function layoutPanels(defs, state, stage, options = {}) {
261
394
  const open2 = (side) => cols.some((c) => c.side === side && !c.hand && !auto.has(c));
262
395
  const railed = (side) => cols.some((c) => c.side === side && (auto.has(c) || c.hand && !(ride && open2(side))));
263
396
  const widths = new Map(order.filter((c) => !auto.has(c)).map((c) => [c, c.width]));
264
- let excess = [...widths.values()].reduce((a, b) => a + b, 0) + SIDES.filter(railed).length * opt.rail + opt.pagesMin - W;
397
+ let excess = [...widths.values()].reduce((a, b) => a + b, 0) + SIDES.filter(railed).length * opt.rail + opt.pagesMin + res.left + res.right - W;
265
398
  for (const c of order) {
266
399
  if (excess <= 0 || auto.has(c)) continue;
267
400
  const take = Math.min(excess, c.width - c.min);
@@ -292,8 +425,8 @@ function layoutPanels(defs, state, stage, options = {}) {
292
425
  const riders = host ? mine.filter((c) => c.hand) : [];
293
426
  const inRail = mine.filter((c) => c.hand && !riders.includes(c) || g.auto.has(c));
294
427
  if (inRail.length) {
295
- const r = side === "left" ? rect(0, 0, opt.rail, H) : rect(W - opt.rail, 0, opt.rail, H);
296
- const segments = inRail.flatMap((c) => c.panels).map((id, i) => ({ id, rect: rect(r.x, opt.railInset + i * opt.segment, opt.rail, opt.segment) }));
428
+ const r = side === "left" ? rect(0, colTop, opt.rail, CH) : rect(W - opt.rail, colTop, opt.rail, CH);
429
+ const segments = inRail.flatMap((c) => c.panels).map((id, i) => ({ id, rect: rect(r.x, colTop + opt.railInset + i * opt.segment, opt.rail, opt.segment) }));
297
430
  rails[side] = { side, rect: r, segments };
298
431
  for (const s of segments) {
299
432
  const c = inRail.find((x) => x.panels.includes(s.id));
@@ -307,11 +440,11 @@ function layoutPanels(defs, state, stage, options = {}) {
307
440
  const w = g.widths.get(c);
308
441
  if (w === void 0 || riders.includes(c)) continue;
309
442
  if (side === "left") {
310
- at.set(c, rect(x0, 0, w, H));
443
+ at.set(c, rect(x0, colTop, w, CH));
311
444
  x0 += w;
312
445
  } else {
313
446
  x1 -= w;
314
- at.set(c, rect(x1, 0, w, H));
447
+ at.set(c, rect(x1, colTop, w, CH));
315
448
  }
316
449
  }
317
450
  const riding = riders.flatMap((c) => c.panels.map((id) => ({ id, c })));
@@ -324,13 +457,13 @@ function layoutPanels(defs, state, stage, options = {}) {
324
457
  const hr = at.get(host);
325
458
  const first = riding.findIndex((x) => x.c === c);
326
459
  c.panels.forEach(
327
- (id, i) => items.set(id, { rect: rect(hr.x, (first + i) * opt.bar, hr.width, opt.bar), kind: "bar", layer: 0, hidden: false, folded: "hand", kept: kept.has(id), pinned: false, floating: false, side, column: c.index, rides: host.index })
460
+ (id, i) => items.set(id, { rect: rect(hr.x, colTop + (first + i) * opt.bar, hr.width, opt.bar), kind: "bar", layer: 0, hidden: false, folded: "hand", kept: kept.has(id), pinned: false, floating: false, side, column: c.index, rides: host.index })
328
461
  );
329
- placedCols.push({ ...base, rect: rect(hr.x, first * opt.bar, hr.width, c.panels.length * opt.bar), shrunk: false, rides: host.index });
462
+ placedCols.push({ ...base, rect: rect(hr.x, colTop + first * opt.bar, hr.width, c.panels.length * opt.bar), shrunk: false, rides: host.index });
330
463
  continue;
331
464
  }
332
- const r = at.get(c) ?? rect(side === "left" ? x0 : x1, 0, 0, H);
333
- const y0 = c === host ? top : 0;
465
+ const r = at.get(c) ?? rect(side === "left" ? x0 : x1, colTop, 0, CH);
466
+ const y0 = colTop + (c === host ? top : 0);
334
467
  const tabbed = c.show === "tabs";
335
468
  const strip = rect(r.x, y0, r.width, opt.tabs);
336
469
  const shown = tabbed && w !== void 0 ? { strip, active: c.active } : {};
@@ -340,7 +473,7 @@ function layoutPanels(defs, state, stage, options = {}) {
340
473
  items.set(id, { rect: to, kind, layer: 0, hidden: more.hidden ?? false, folded: f, kept: kept.has(id), pinned: false, floating: false, side, column: c.index, ...more.tab ? { tab: more.tab } : {} });
341
474
  };
342
475
  if (tabbed) {
343
- const body = rect(r.x, y0 + opt.tabs, r.width, Math.max(0, H - y0 - opt.tabs));
476
+ const body = rect(r.x, y0 + opt.tabs, r.width, Math.max(0, colBot - y0 - opt.tabs));
344
477
  const shade = folded.has(c.active);
345
478
  const tw = r.width / c.panels.length;
346
479
  c.panels.forEach((id, i) => {
@@ -350,7 +483,7 @@ function layoutPanels(defs, state, stage, options = {}) {
350
483
  });
351
484
  continue;
352
485
  }
353
- const hs = sectionHeights(c, H - y0);
486
+ const hs = sectionHeights(c, colBot - y0);
354
487
  let y = y0;
355
488
  for (const id of c.panels) {
356
489
  const s = hs.get(id);
@@ -360,8 +493,39 @@ function layoutPanels(defs, state, stage, options = {}) {
360
493
  for (const id of c.panels) if (hs.get(id).folded === "auto") notes.push({ code: "section-folded", side, column: c.index, items: [id] });
361
494
  }
362
495
  }
363
- const pages = rect(x0, 0, Math.max(0, x1 - x0), H);
496
+ for (const b of bOrder) {
497
+ const h = gh.heights.get(b);
498
+ if (h !== void 0 && h < b.height) notes.push({ code: "band-shrunk", edge: b.edge, items: b.panels, from: b.height, to: h });
499
+ }
500
+ for (const b of bOrder) if (gh.auto.has(b)) notes.push({ code: b.kept ? "kept-folded" : "band-folded", edge: b.edge, items: b.panels });
501
+ const pw = Math.max(0, x1 - x0);
502
+ const pTop = topB?.mode === "reserved" ? hT : 0;
503
+ const pBot = botB?.mode === "reserved" ? H - hB : H;
504
+ const reserved = {};
505
+ const iTop = pTop + res.top + (topB?.mode === "overlay" ? hT : 0);
506
+ const iBot = pBot - res.bottom - (botB?.mode === "overlay" ? hB : 0);
507
+ if (res.top > 0) reserved.top = rect(x0, pTop, pw, res.top);
508
+ if (res.right > 0) reserved.right = rect(x0 + pw - res.right, iTop, res.right, Math.max(0, iBot - iTop));
509
+ if (res.bottom > 0) reserved.bottom = rect(x0, pBot - res.bottom, pw, res.bottom);
510
+ if (res.left > 0) reserved.left = rect(x0, iTop, res.left, Math.max(0, iBot - iTop));
511
+ const pages = rect(x0 + res.left, pTop + res.top, Math.max(0, pw - res.left - res.right), Math.max(0, pBot - res.bottom - pTop - res.top));
364
512
  if (pages.width < opt.pagesMin) notes.push({ code: "pages-under-min", width: pages.width, min: opt.pagesMin });
513
+ if ((bandsIn.length || res.top + res.bottom > 0) && pages.height < minH - 1e-9) notes.push({ code: "pages-short", height: pages.height, min: minH });
514
+ const bands = {};
515
+ for (const b of bandsIn) {
516
+ const h = bandH(b);
517
+ const full = b.span === "full";
518
+ const over = b.mode === "overlay";
519
+ const y = b.edge === "top" ? over ? res.top : 0 : over ? H - res.bottom - h : H - h;
520
+ const r = full ? rect(0, y, W, h) : rect(x0, y, pw, h);
521
+ const f = b.hand ? "hand" : gh.auto.has(b) ? "auto" : null;
522
+ const hh = gh.heights.get(b);
523
+ bands[b.edge] = { edge: b.edge, rect: r, height: b.height, min: b.min, max: b.max, folded: f, shrunk: hh !== void 0 && hh < b.height, kept: b.kept, mode: b.mode, span: b.span, panels: b.panels };
524
+ const share = r.width / b.panels.length;
525
+ b.panels.forEach(
526
+ (id, i) => items.set(id, { rect: rect(r.x + i * share, r.y, share, h), kind: f ? "bar" : "band", layer: over ? 5 : 0, hidden: false, folded: f, kept: kept.has(id), pinned: false, floating: false, edge: b.edge, index: b.at[i] })
527
+ );
528
+ }
365
529
  state.floats.forEach((f, i) => {
366
530
  if (closed.has(f.id)) return;
367
531
  const shaded = folded.has(f.id);
@@ -390,7 +554,8 @@ function layoutPanels(defs, state, stage, options = {}) {
390
554
  ...clamped ? { clamped: true } : {}
391
555
  });
392
556
  });
393
- return { items, columns: placedCols, rails, notes, pages };
557
+ notes.push(...offNotes);
558
+ return { items, columns: placedCols, rails, notes, pages, bands, reserved };
394
559
  }
395
560
 
396
561
  // src/panels/targets.ts
@@ -400,6 +565,8 @@ function placeRank(place) {
400
565
  switch (place.kind) {
401
566
  case "float":
402
567
  return 3e3;
568
+ case "band":
569
+ return (place.edge === "top" ? 2e3 : 2500) + Math.min(place.index, 499);
403
570
  case "rail":
404
571
  return place.side === "left" ? 0 : 1999;
405
572
  case "column":
@@ -412,6 +579,7 @@ function homePlace(state, id) {
412
579
  const w = whereIs(state, id);
413
580
  if (w.kind === "float") return { kind: "float" };
414
581
  if (w.kind === "none") return null;
582
+ if (w.kind === "band") return { kind: "band", edge: w.edge, index: w.index };
415
583
  return state.sides[w.side][w.index].panels.length === 1 ? { kind: "column", side: w.side, index: w.index } : { kind: "into", side: w.side, index: w.index };
416
584
  }
417
585
  var cut = (x0, x1, lo, hi) => [Math.max(x0, lo), Math.min(x1, hi)];
@@ -425,19 +593,38 @@ function panelTargets(input) {
425
593
  const W = stage.width;
426
594
  const H = stage.height;
427
595
  const R = COLUMN_REACH;
596
+ const bandOpts = opt.bands ?? {};
597
+ const reserve = opt.reserve ?? {};
598
+ const px = (v) => typeof v === "number" && Number.isFinite(v) ? Math.max(0, v) : 0;
428
599
  const out = [];
600
+ const idOf = (place) => {
601
+ switch (place.kind) {
602
+ case "float":
603
+ return "float";
604
+ case "rail":
605
+ return `rail:${place.side}`;
606
+ case "band":
607
+ return s0.bands?.[place.edge] ? `band:${place.edge}:${place.index}` : `band:${place.edge}`;
608
+ default:
609
+ return `${place.kind}:${place.side}:${place.index}`;
610
+ }
611
+ };
429
612
  const add = (place, priority, hit, make, free = false) => {
430
613
  if (hit.width <= 0 || hit.height <= 0) return;
431
614
  if (input.allows && !input.allows(id, place)) return;
432
615
  if (place.kind !== "float" && samePlaces(make({ x: 0, y: 0 }, { x: 0, y: 0 }), state, defs)) return;
433
- out.push({ id: place.kind === "float" ? "float" : place.kind === "rail" ? `rail:${place.side}` : `${place.kind}:${place.side}:${place.index}`, priority, hit, panel: id, place, order: placeRank(place), make, ...free ? { free: true } : {} });
616
+ out.push({ id: idOf(place), priority, hit, panel: id, place, order: placeRank(place), make, ...free ? { free: true } : {} });
434
617
  };
618
+ const fullTop = l0.bands.top?.span === "full" ? l0.bands.top.rect : null;
619
+ const fullBot = l0.bands.bottom?.span === "full" ? l0.bands.bottom.rect : null;
620
+ const top = fullTop ? fullTop.y + fullTop.height : 0;
621
+ const CH = Math.max(0, (fullBot ? fullBot.y : H) - top);
435
622
  for (const side of SIDES) {
436
623
  if (def?.sides && !def.sides.includes(side)) continue;
437
624
  const left = side === "left";
438
625
  const rail = l0.rails[side];
439
626
  const edge = rail ? rail.rect.width : RAIL_EDGE;
440
- const railHit = rail ? rail.rect : left ? rect(0, 0, RAIL_EDGE, H) : rect(W - RAIL_EDGE, 0, RAIL_EDGE, H);
627
+ const railHit = rail ? rail.rect : left ? rect(0, top, RAIL_EDGE, CH) : rect(W - RAIL_EDGE, top, RAIL_EDGE, CH);
441
628
  add({ kind: "rail", side }, 1, railHit, () => {
442
629
  const s = dock(state, id, side, 0, { width });
443
630
  return { ...s, folded: [...s.folded.filter((x) => x !== id), id] };
@@ -447,9 +634,9 @@ function panelTargets(input) {
447
634
  const hi = left ? W : W - edge;
448
635
  const band = (x, outer, i) => {
449
636
  const [a, b] = outer ? left ? cut(x, x + 2 * R, lo, hi) : cut(x - 2 * R, x, lo, hi) : cut(x - R, x + R, lo, hi);
450
- add({ kind: "column", side, index: i }, 2, rect(a, 0, b - a, H), () => dock(state, id, side, i, { width }));
637
+ add({ kind: "column", side, index: i }, 2, rect(a, top, b - a, CH), () => dock(state, id, side, i, { width }));
451
638
  };
452
- if (open2.length === 0) band(left ? l0.pages.x : l0.pages.x + l0.pages.width, true, s0.sides[side].length);
639
+ if (open2.length === 0) band(left ? l0.pages.x - px(reserve.left) : l0.pages.x + l0.pages.width + px(reserve.right), true, s0.sides[side].length);
453
640
  open2.forEach((c, k) => {
454
641
  band(left ? c.rect.x : c.rect.x + c.rect.width, k === 0, c.index);
455
642
  add({ kind: "into", side, index: c.index }, 2, c.rect, () => dock(state, id, side, c.index, { into: true }));
@@ -457,6 +644,28 @@ function panelTargets(input) {
457
644
  const last = open2.at(-1);
458
645
  if (last) band(left ? last.rect.x + last.rect.width : last.rect.x, false, last.index + 1);
459
646
  }
647
+ const home = whereIs(state, id);
648
+ for (const e of EDGES) {
649
+ if (!bandOpts[e] || def?.sides && !def.sides.includes(e)) continue;
650
+ const pb = l0.bands[e];
651
+ const list = s0.bands?.[e]?.panels ?? [];
652
+ const make = (i) => () => dockBand(state, id, e, i, defs);
653
+ if (!pb) {
654
+ if (home.kind === "band" && home.edge === e && !s0.bands?.[e]) continue;
655
+ const p = l0.pages;
656
+ const h = Math.min(2 * opt.bar, p.height);
657
+ add({ kind: "band", edge: e, index: list.length }, 2, rect(p.x, e === "top" ? p.y : p.y + p.height - h, p.width, h), make(list.length));
658
+ continue;
659
+ }
660
+ const m = pb.panels.length;
661
+ const share = pb.rect.width / m;
662
+ for (let k = 0; k <= m; k++) {
663
+ const i = k < m ? list.indexOf(pb.panels[k]) : list.length;
664
+ const a = pb.rect.x + Math.max(0, k - 0.5) * share;
665
+ const b = pb.rect.x + Math.min(m, k + 0.5) * share;
666
+ add({ kind: "band", edge: e, index: i }, 2, rect(a, pb.rect.y, b - a, pb.rect.height), make(i));
667
+ }
668
+ }
460
669
  if (def?.floats !== false) {
461
670
  const was = state.floats.find((f) => f.id === id);
462
671
  const size = was ? { width: was.rect.width, height: was.rect.height } : floatSize(def).natural;
@@ -504,6 +713,8 @@ function panelStops(targets, options = {}) {
504
713
 
505
714
  export {
506
715
  SIDES,
716
+ EDGES,
717
+ BAND_SIZE,
507
718
  IDENTITY_VIEW,
508
719
  DEFAULT_PANELS_OPTIONS,
509
720
  FLOAT_HEIGHT,
@@ -512,6 +723,8 @@ export {
512
723
  tick,
513
724
  defOf,
514
725
  floatSize,
726
+ bandSize,
727
+ bandHeight,
515
728
  ownWidth,
516
729
  createPanels,
517
730
  liftPanel,
@@ -520,6 +733,8 @@ export {
520
733
  focus,
521
734
  toggleFold,
522
735
  keep,
736
+ dockBand,
737
+ resizeBand,
523
738
  dock,
524
739
  showTabs,
525
740
  activate,
@@ -541,4 +756,4 @@ export {
541
756
  panelDrop,
542
757
  panelStops
543
758
  };
544
- //# sourceMappingURL=chunk-PWQWD2CS.js.map
759
+ //# sourceMappingURL=chunk-PJD5HKYX.js.map