@gjermundgaraba-designs/pixel-paper 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/DESIGN.md +26 -5
  2. package/dist/src/commandPalette/commandPalette.d.ts +2 -2
  3. package/dist/src/commandPalette/commandPalette.d.ts.map +1 -1
  4. package/dist/src/commandPalette/commandPalette.js +66 -42
  5. package/dist/src/commandPalette/match.d.ts +15 -5
  6. package/dist/src/commandPalette/match.d.ts.map +1 -1
  7. package/dist/src/commandPalette/match.js +17 -2
  8. package/dist/src/drag/drag.d.ts +3 -0
  9. package/dist/src/drag/drag.d.ts.map +1 -1
  10. package/dist/src/drag/drag.js +14 -10
  11. package/dist/src/index.d.ts +1 -0
  12. package/dist/src/index.d.ts.map +1 -1
  13. package/dist/src/index.js +1 -0
  14. package/dist/src/reel/index.d.ts +4 -0
  15. package/dist/src/reel/index.d.ts.map +1 -0
  16. package/dist/src/reel/index.js +2 -0
  17. package/dist/src/reel/layout.d.ts +180 -0
  18. package/dist/src/reel/layout.d.ts.map +1 -0
  19. package/dist/src/reel/layout.js +370 -0
  20. package/dist/src/reel/reel.d.ts +1158 -0
  21. package/dist/src/reel/reel.d.ts.map +1 -0
  22. package/dist/src/reel/reel.js +922 -0
  23. package/dist/src/sortable/sortable.d.ts +30 -30
  24. package/dist/src/sortable/sortable.d.ts.map +1 -1
  25. package/dist/src/sortable/sortable.js +2 -10
  26. package/dist/src/toast/toast.d.ts +12 -12
  27. package/dist/src/window/window.d.ts +2 -0
  28. package/dist/src/window/window.d.ts.map +1 -1
  29. package/dist/src/window/window.js +2 -1
  30. package/package.json +1 -1
  31. package/src/commandPalette/commandPalette.ts +71 -24
  32. package/src/commandPalette/match.ts +35 -6
  33. package/src/drag/drag.ts +19 -4
  34. package/src/index.ts +1 -0
  35. package/src/pixel-paper.css +236 -2
  36. package/src/reel/index.ts +20 -0
  37. package/src/reel/layout.ts +602 -0
  38. package/src/reel/reel.ts +1597 -0
  39. package/src/sortable/sortable.ts +8 -16
  40. package/src/tokens.css +5 -0
  41. package/src/window/window.ts +4 -0
@@ -0,0 +1,922 @@
1
+ import { brandViewResult as __foldkitBrandViewResult } from 'foldkit/brand'
2
+ import { Array, Effect, Equal, Match, Number, Option, Order, Schema, Stream, String } from "effect";
3
+ import * as Command from "foldkit/command";
4
+ import { defineMessageUnion } from "foldkit/message";
5
+ import * as Mount from "foldkit/mount";
6
+ import * as Render from "foldkit/render";
7
+ import { defineTaggedUnion } from "foldkit/schema";
8
+ import { modifyFields } from "foldkit/struct";
9
+ import { defineView } from "foldkit/submodel";
10
+ import * as Subscription from "foldkit/subscription";
11
+ import * as Announcer from "../announcer/announcer.js";
12
+ import * as Button from "../button/button.js";
13
+ import { LEFT_BUTTON, escapeKeys, markDragging, measureLift, pressingPointer, } from "../drag/drag.js";
14
+ import * as Glyph from "../glyph/glyph.js";
15
+ import * as IconButton from "../iconButton/iconButton.js";
16
+ import { prefixKeys } from "../subscription/prefixKeys.js";
17
+ import { OutMessage, Resize, Splitter, Step, Subject, Target, WHERE_IT_WAS, apply, columnOf, columnOfTarget, fill, firstPlace, isPresent, isSplitterPresent, isTargetPresent, landingWords, nextPreset, originOf, placeAt, placeOf, placeWords, requestOf, resizeOf, steppedTarget, subjectWords, targetAtPoint, whereWords, widthMark, widthWords, } from "./layout.js";
18
+ // MODEL
19
+ /** The words a reel says. `{pane}` is a pane's label, `{subject}` what is held (a pane, or a
20
+ * column by its first pane), `{where}` a place and `{column}` a column's number. */
21
+ const COPY = {
22
+ paneGrip: "Move {pane}",
23
+ columnGrip: "Move column {column}",
24
+ gripHelp: "Press space to pick it up. Arrow keys move it, space drops it, escape or tab puts it back.",
25
+ widthLabel: "Width of column {column}",
26
+ heightLabel: "Height of {pane}",
27
+ focused: "{pane}, {where}.",
28
+ moved: "{subject} moved to {where}.",
29
+ widened: "Column {column} takes {width}.",
30
+ pickedUp: "Picked up {subject}.",
31
+ pickedUpByKeyboard: "Picked up {subject}. Arrow keys move it, space drops it, escape or tab puts it back.",
32
+ over: "{subject}, {where}.",
33
+ dropped: "{subject} dropped at {where}.",
34
+ putBack: "{subject} put back where it was.",
35
+ cancelled: "Move cancelled. {subject} put back where it was.",
36
+ gone: "Move cancelled.",
37
+ };
38
+ /** The focused pane, with where it last sat, so focus finds a neighbour when it goes. */
39
+ const Focus = Schema.Struct({ paneId: Schema.String, column: Schema.Number, row: Schema.Number });
40
+ /** How something is held: by a pointer, pressed at (`pressX`, `pressY`) and now at (`x`,
41
+ * `y`), carrying it once it has moved past the threshold; or by the keyboard, on its grip. */
42
+ const By = defineTaggedUnion({
43
+ Pointer: {
44
+ pointerId: Schema.Number,
45
+ pressX: Schema.Number,
46
+ pressY: Schema.Number,
47
+ x: Schema.Number,
48
+ y: Schema.Number,
49
+ isCarrying: Schema.Boolean,
50
+ },
51
+ Keyboard: {},
52
+ });
53
+ /** What is held, how, and where it would land: none while a pointer is outside the reel. */
54
+ const Held = Schema.Struct({
55
+ subject: Subject,
56
+ by: By,
57
+ maybeTarget: Schema.Option(Target),
58
+ });
59
+ /** A splitter a pointer drags, pressed at (`pressX`, `pressY`), with the resize it previews. */
60
+ const Resizing = Schema.Struct({
61
+ splitter: Splitter,
62
+ pointerId: Schema.Number,
63
+ pressX: Schema.Number,
64
+ pressY: Schema.Number,
65
+ maybeResize: Schema.Option(Resize),
66
+ });
67
+ /** Schema for a reel: its id, the focused pane, what is held and a splitter being dragged. All
68
+ * of it is this client's; the layout is the application's. */
69
+ export const Model = Schema.Struct({
70
+ id: Schema.String,
71
+ maybeFocus: Schema.Option(Focus),
72
+ maybeHeld: Schema.Option(Held),
73
+ maybeResizing: Schema.Option(Resizing),
74
+ });
75
+ // MESSAGE
76
+ const PointerAt = Schema.Struct({
77
+ clientX: Schema.Number,
78
+ clientY: Schema.Number,
79
+ maybeTarget: Schema.Option(Target),
80
+ });
81
+ /** Union of all Messages a reel can produce. */
82
+ export const Message = defineMessageUnion({
83
+ PressedGrip: {
84
+ subject: Subject,
85
+ pointerId: Schema.Number,
86
+ clientX: Schema.Number,
87
+ clientY: Schema.Number,
88
+ },
89
+ PressedGripKey: { subject: Subject },
90
+ MovedPointer: PointerAt.fields,
91
+ ReleasedPointer: PointerAt.fields,
92
+ CancelledPointer: {},
93
+ SteppedHeld: { step: Step },
94
+ DroppedHeld: {},
95
+ LeftGrip: {},
96
+ PressedEscape: {},
97
+ FocusedPane: { paneId: Schema.String },
98
+ PressedSplitter: {
99
+ splitter: Splitter,
100
+ pointerId: Schema.Number,
101
+ clientX: Schema.Number,
102
+ clientY: Schema.Number,
103
+ },
104
+ MovedSplitter: { share: Schema.Number },
105
+ ReleasedSplitter: { share: Schema.Number },
106
+ CancelledSplitter: {},
107
+ PressedSplitterKey: { splitter: Splitter, share: Schema.Number },
108
+ CompletedAnnounceReel: {},
109
+ CompletedFocusPane: {},
110
+ CompletedRestoreFocus: {},
111
+ CompletedFocusGrip: {},
112
+ CompletedScrollIntoView: {},
113
+ CompletedSizeLiftedPane: {},
114
+ });
115
+ /** Creates a reel focused on its first pane, holding nothing, at rest. */
116
+ export const init = (config) => (__foldkitBrandViewResult(({
117
+ id: config.id,
118
+ maybeFocus: Option.none(),
119
+ maybeHeld: Option.none(),
120
+ maybeResizing: Option.none(),
121
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#init"));
122
+ // ACCESSORS
123
+ const focusedPlace = (model, layout) => __foldkitBrandViewResult((Option.orElse(Option.flatMap(model.maybeFocus, (focus) => __foldkitBrandViewResult((Option.orElse(placeOf(layout, focus.paneId), () => {
124
+ const column = Number.clamp(focus.column, { minimum: 0, maximum: layout.length - 1 });
125
+ const rows = Option.match(Array.get(layout, column), {
126
+ onNone: () => __foldkitBrandViewResult((0), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone"),
127
+ onSome: (found) => __foldkitBrandViewResult((found.panes.length), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome"),
128
+ });
129
+ return __foldkitBrandViewResult((placeAt(layout, column, Number.clamp(focus.row, { minimum: 0, maximum: rows - 1 }))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~2");
130
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous")), () => __foldkitBrandViewResult((firstPlace(layout)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~3"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#focusedPlace");
131
+ /** The pane focus is on: the one last focused, or, when it has gone from `layout`, the pane
132
+ * now nearest its place; the first pane before any is focused. None for an empty layout. */
133
+ export const focusedPaneId = (model, layout) => __foldkitBrandViewResult((Option.map(focusedPlace(model, layout), ({ pane }) => __foldkitBrandViewResult((pane.id), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~4"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#focusedPaneId");
134
+ const isHolding = (model) => __foldkitBrandViewResult((Option.isSome(model.maybeHeld)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#isHolding");
135
+ /** Whether `held` is carried: by the keyboard, or by a pointer past the threshold. */
136
+ const isCarried = (held) => __foldkitBrandViewResult((By.match(held.by, { Pointer: ({ isCarrying }) => __foldkitBrandViewResult((isCarrying), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Pointer"), Keyboard: () => __foldkitBrandViewResult((true), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Keyboard") })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#isCarried");
137
+ // DOM HOOKS
138
+ const paneHook = (model, paneId) => __foldkitBrandViewResult((`${model.id}/pane/${paneId}`), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#paneHook");
139
+ const columnHook = (model, columnId) => __foldkitBrandViewResult((`${model.id}/column/${columnId}`), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#columnHook");
140
+ const subjectHook = (model, subject) => __foldkitBrandViewResult((Subject.match(subject, {
141
+ Pane: ({ paneId }) => __foldkitBrandViewResult((paneHook(model, paneId)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Pane"),
142
+ Column: ({ columnId }) => __foldkitBrandViewResult((columnHook(model, columnId)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Column"),
143
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#subjectHook");
144
+ const scrollerOf = (reelHook) => __foldkitBrandViewResult((Option.fromNullishOr(document.querySelector(`[data-reel="${CSS.escape(reelHook)}"] > .reel-view`))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#scrollerOf");
145
+ const paneElement = (hook) => __foldkitBrandViewResult((Option.fromNullishOr(document.querySelector(`[data-reel-pane="${CSS.escape(hook)}"]`))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#paneElement");
146
+ /** Puts the keyboard in `pane`: on its control marked `data-reel-focus` (a terminal's input),
147
+ * or on the pane itself, without scrolling the page. */
148
+ const focusInto = (pane) => {
149
+ Option.getOrElse(Option.fromNullishOr(pane.querySelector("[data-reel-focus]")), () => __foldkitBrandViewResult((pane), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~5")).focus({ preventScroll: true });
150
+ };
151
+ // COMMAND
152
+ /** Says a focus move, a move or a step of a drag through the page's live region (as
153
+ * `Announcer.Announce` does). */
154
+ export const AnnounceReel = Command.define("AnnounceReel", {
155
+ args: { text: Schema.String },
156
+ messages: [Message.CompletedAnnounceReel],
157
+ execute: ({ text }) => __foldkitBrandViewResult((Announcer.announce(text).pipe(Effect.as(Message.CompletedAnnounceReel()))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#execute"),
158
+ });
159
+ /** Focuses the pane `paneHook` names, unless focus is already inside it: the control in it
160
+ * marked `data-reel-focus` (a terminal's input), or the pane itself. It does not scroll;
161
+ * ScrollIntoView does. */
162
+ export const FocusPane = Command.define("FocusPane", {
163
+ args: { paneHook: Schema.String },
164
+ messages: [Message.CompletedFocusPane],
165
+ execute: ({ paneHook: hook }) => __foldkitBrandViewResult((Effect.gen(function* () {
166
+ yield* Render.afterCommit;
167
+ yield* Effect.sync(() => {
168
+ Option.map(Option.filter(paneElement(hook), (pane) => __foldkitBrandViewResult((!pane.contains(document.activeElement)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~8")), focusInto);
169
+ });
170
+ return __foldkitBrandViewResult((Message.CompletedFocusPane()), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~6");
171
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#execute~2"),
172
+ });
173
+ /** Focuses the pane `paneHook` names only where focus was lost (it left with the pane that
174
+ * held it), so focus the reader put somewhere else stays there. */
175
+ export const RestoreFocus = Command.define("RestoreFocus", {
176
+ args: { paneHook: Schema.String },
177
+ messages: [Message.CompletedRestoreFocus],
178
+ execute: ({ paneHook: hook }) => __foldkitBrandViewResult((Effect.gen(function* () {
179
+ yield* Render.afterCommit;
180
+ yield* Effect.sync(() => {
181
+ const active = document.activeElement;
182
+ if (active === null || active === document.body) {
183
+ Option.map(paneElement(hook), focusInto);
184
+ }
185
+ });
186
+ return __foldkitBrandViewResult((Message.CompletedRestoreFocus()), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~9");
187
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#execute~3"),
188
+ });
189
+ /** Puts focus back on a column's grip once the render that moved its column is in the page:
190
+ * a column's element moves with it, which takes focus off its grip. */
191
+ export const FocusGrip = Command.define("FocusGrip", {
192
+ args: { gripHook: Schema.String },
193
+ messages: [Message.CompletedFocusGrip],
194
+ execute: ({ gripHook }) => __foldkitBrandViewResult((Effect.gen(function* () {
195
+ yield* Render.afterCommit;
196
+ yield* Effect.sync(() => {
197
+ document
198
+ .querySelector(`[data-reel-grip="${CSS.escape(gripHook)}"]`)
199
+ ?.focus({ preventScroll: true });
200
+ });
201
+ return __foldkitBrandViewResult((Message.CompletedFocusGrip()), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~11");
202
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#execute~4"),
203
+ });
204
+ /** Scrolls the pane or column `hook` names into view, by the least distance either way, its
205
+ * gutter kept by the stylesheet's `scroll-margin`, once the render that placed it is in the
206
+ * page. It jumps: motion is stepped or instant, and a follow is instant. */
207
+ export const ScrollIntoView = Command.define("ScrollIntoView", {
208
+ args: { hook: Schema.String },
209
+ messages: [Message.CompletedScrollIntoView],
210
+ execute: ({ hook }) => __foldkitBrandViewResult((Effect.gen(function* () {
211
+ yield* Render.afterCommit;
212
+ yield* Effect.sync(() => {
213
+ document
214
+ .querySelector(`[data-reel-pane="${CSS.escape(hook)}"], [data-reel-hook="${CSS.escape(hook)}"]`)
215
+ ?.scrollIntoView({ block: "nearest", inline: "nearest" });
216
+ });
217
+ return __foldkitBrandViewResult((Message.CompletedScrollIntoView()), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~13");
218
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#execute~5"),
219
+ });
220
+ /** How far a pointer moves from its press before it carries what it pressed. */
221
+ const CARRY_THRESHOLD = 5;
222
+ const withHeld = (model, maybeHeld) => __foldkitBrandViewResult((modifyFields(model, { maybeHeld: () => __foldkitBrandViewResult((maybeHeld), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#maybeHeld") })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#withHeld");
223
+ const withResizing = (model, maybeResizing) => __foldkitBrandViewResult((modifyFields(model, { maybeResizing: () => __foldkitBrandViewResult((maybeResizing), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#maybeResizing") })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#withResizing");
224
+ const focusOn = (model, place) => __foldkitBrandViewResult((modifyFields(model, {
225
+ maybeFocus: () => __foldkitBrandViewResult((Option.some({ paneId: place.pane.id, column: place.columnIndex, row: place.row })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#maybeFocus"),
226
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#focusOn");
227
+ const announce = (text) => __foldkitBrandViewResult((AnnounceReel({ text })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#announce");
228
+ const heldWords = (layout, held) => __foldkitBrandViewResult((String.capitalize(Option.getOrElse(subjectWords(layout, held.subject), () => __foldkitBrandViewResult((""), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~15")))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#heldWords");
229
+ const scrollToTarget = (model, layout, target) => __foldkitBrandViewResult((Array.fromOption(Option.map(columnOfTarget(layout, target), (columnId) => __foldkitBrandViewResult((ScrollIntoView({ hook: columnHook(model, columnId) })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~16")))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#scrollToTarget");
230
+ /** Lets go of what is held: dropped at its target when it is carried and the drop changes
231
+ * something (reported as the request), put back otherwise, said in `putBack`'s words. A
232
+ * column dropped by the keyboard gets its grip's focus back. */
233
+ const letGo = (model, layout, held, putBack) => {
234
+ const released = withHeld(model, Option.none());
235
+ if (!isCarried(held)) {
236
+ return __foldkitBrandViewResult(({ model: released }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#letGo");
237
+ }
238
+ const subject = heldWords(layout, held);
239
+ const maybeRequest = Option.flatMap(held.maybeTarget, (target) => __foldkitBrandViewResult((requestOf(layout, held.subject, target)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~17"));
240
+ // a column that moves takes focus off its grip; one put back never left its place
241
+ const refocus = held.subject._tag === "Column" && held.by._tag === "Keyboard"
242
+ ? [FocusGrip({ gripHook: subjectHook(model, held.subject) })]
243
+ : [];
244
+ return __foldkitBrandViewResult((Option.match(maybeRequest, {
245
+ onNone: () => (__foldkitBrandViewResult(({
246
+ model: released,
247
+ commands: [announce(fill(putBack, { subject }))],
248
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~2")),
249
+ onSome: (request) => (__foldkitBrandViewResult(({
250
+ model: released,
251
+ commands: [
252
+ ...refocus,
253
+ announce(fill(COPY.dropped, {
254
+ subject,
255
+ where: Option.getOrElse(whereWords(apply(layout, request), held.subject), () => __foldkitBrandViewResult((WHERE_IT_WAS), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~18")),
256
+ })),
257
+ ],
258
+ outMessage: request,
259
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~2")),
260
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#letGo");
261
+ };
262
+ const cancel = (model, layout, held) => __foldkitBrandViewResult((letGo(model, layout, { ...held, maybeTarget: Option.none() }, COPY.cancelled)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#cancel");
263
+ const lift = (model, layout, subject, by) => __foldkitBrandViewResult((Option.match(Option.filter(originOf(layout, subject), () => __foldkitBrandViewResult((!isHolding(model) && Option.isNone(model.maybeResizing)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~19")), {
264
+ onNone: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~3")),
265
+ onSome: (origin) => {
266
+ const held = { subject, by, maybeTarget: Option.some(origin) };
267
+ return __foldkitBrandViewResult(({
268
+ model: withHeld(model, Option.some(held)),
269
+ commands: by._tag === "Keyboard"
270
+ ? [announce(fill(COPY.pickedUpByKeyboard, { subject: heldWords(layout, held) }))]
271
+ : [],
272
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~3");
273
+ },
274
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#lift");
275
+ const movePointer = (model, layout, held, pointer) => __foldkitBrandViewResult((By.match(held.by, {
276
+ Keyboard: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Keyboard~2")),
277
+ Pointer: (by) => {
278
+ const isCarrying = by.isCarrying ||
279
+ Math.hypot(pointer.clientX - by.pressX, pointer.clientY - by.pressY) > CARRY_THRESHOLD;
280
+ const next = {
281
+ ...held,
282
+ by: By.Pointer({ ...by, x: pointer.clientX, y: pointer.clientY, isCarrying }),
283
+ maybeTarget: isCarrying ? pointer.maybeTarget : held.maybeTarget,
284
+ };
285
+ return __foldkitBrandViewResult(({
286
+ model: withHeld(model, Option.some(next)),
287
+ commands: isCarrying && !by.isCarrying
288
+ ? [announce(fill(COPY.pickedUp, { subject: heldWords(layout, held) }))]
289
+ : [],
290
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Pointer~2");
291
+ },
292
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#movePointer");
293
+ /** A keyboard step of what is held, resolved from the layout, with the reel scrolled to the
294
+ * column it lands by. */
295
+ const stepHeld = (model, layout, held, step) => __foldkitBrandViewResult((Option.match(Option.filter(held.maybeTarget, () => __foldkitBrandViewResult((held.by._tag === "Keyboard"), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~20")), {
296
+ onNone: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~4")),
297
+ onSome: (target) => {
298
+ const next = steppedTarget(layout, held.subject, target, step);
299
+ if (Equal.equals(next, target)) {
300
+ return __foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~4");
301
+ }
302
+ return __foldkitBrandViewResult(({
303
+ model: withHeld(model, Option.some({ ...held, maybeTarget: Option.some(next) })),
304
+ commands: [
305
+ announce(fill(COPY.over, {
306
+ subject: heldWords(layout, held),
307
+ where: landingWords(layout, held.subject, next),
308
+ })),
309
+ ...scrollToTarget(model, layout, next),
310
+ ],
311
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~4");
312
+ },
313
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#stepHeld");
314
+ const whileHeld = (model, f) => __foldkitBrandViewResult((Option.match(model.maybeHeld, { onNone: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~5")), onSome: f })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#whileHeld");
315
+ const whileHeldBy = (model, by, f) => __foldkitBrandViewResult((whileHeld(model, (held) => (__foldkitBrandViewResult((held.by._tag === by ? f(held) : { model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~21")))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#whileHeldBy");
316
+ /** The resize a splitter dragged `share` of its scale asks for: previewed while it moves, and
317
+ * asked for when it is let go. */
318
+ const resizeSplitter = (model, layout, share, isLetGo) => __foldkitBrandViewResult((Option.match(model.maybeResizing, {
319
+ onNone: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~6")),
320
+ onSome: (resizing) => {
321
+ const maybeResize = resizeOf(layout, resizing.splitter, share);
322
+ if (!isLetGo) {
323
+ return __foldkitBrandViewResult(({ model: withResizing(model, Option.some({ ...resizing, maybeResize })) }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~5");
324
+ }
325
+ return __foldkitBrandViewResult((Option.match(maybeResize, {
326
+ onNone: () => (__foldkitBrandViewResult(({ model: withResizing(model, Option.none()) }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~7")),
327
+ onSome: (resize) => (__foldkitBrandViewResult(({ model: withResizing(model, Option.none()), outMessage: resize }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~6")),
328
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~5");
329
+ },
330
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#resizeSplitter");
331
+ /** Processes a reel Message against the layout as the application holds it now, and returns
332
+ * the next Model, Commands and OutMessage. */
333
+ export const update = (model, message, context) => {
334
+ const { layout } = context;
335
+ return __foldkitBrandViewResult((Message.match(message, {
336
+ PressedGrip: ({ subject, pointerId, clientX, clientY }) => __foldkitBrandViewResult((lift(model, layout, subject, By.Pointer({
337
+ pointerId,
338
+ pressX: clientX,
339
+ pressY: clientY,
340
+ x: clientX,
341
+ y: clientY,
342
+ isCarrying: false,
343
+ }))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#PressedGrip"),
344
+ PressedGripKey: ({ subject }) => __foldkitBrandViewResult((lift(model, layout, subject, By.Keyboard())), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#PressedGripKey"),
345
+ MovedPointer: (pointer) => __foldkitBrandViewResult((whileHeldBy(model, "Pointer", (held) => __foldkitBrandViewResult((movePointer(model, layout, held, pointer)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~22"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#MovedPointer"),
346
+ ReleasedPointer: (pointer) => __foldkitBrandViewResult((whileHeldBy(model, "Pointer", (held) => __foldkitBrandViewResult((letGo(model, layout, isCarried(held) ? { ...held, maybeTarget: pointer.maybeTarget } : held, COPY.putBack)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~23"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#ReleasedPointer"),
347
+ CancelledPointer: () => __foldkitBrandViewResult((whileHeldBy(model, "Pointer", (held) => __foldkitBrandViewResult((letGo(model, layout, { ...held, maybeTarget: Option.none() }, COPY.putBack)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~24"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#CancelledPointer"),
348
+ SteppedHeld: ({ step }) => __foldkitBrandViewResult((whileHeldBy(model, "Keyboard", (held) => __foldkitBrandViewResult((stepHeld(model, layout, held, step)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~25"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#SteppedHeld"),
349
+ DroppedHeld: () => __foldkitBrandViewResult((whileHeldBy(model, "Keyboard", (held) => __foldkitBrandViewResult((letGo(model, layout, held, COPY.putBack)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~26"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#DroppedHeld"),
350
+ LeftGrip: () => __foldkitBrandViewResult((whileHeldBy(model, "Keyboard", (held) => __foldkitBrandViewResult((cancel(model, layout, held)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~27"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#LeftGrip"),
351
+ PressedEscape: () => __foldkitBrandViewResult((Option.isSome(model.maybeResizing)
352
+ ? { model: withResizing(model, Option.none()) }
353
+ : whileHeld(model, (held) => __foldkitBrandViewResult((cancel(model, layout, held)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~28"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#PressedEscape"),
354
+ FocusedPane: ({ paneId }) => __foldkitBrandViewResult((Option.match(Option.filter(placeOf(layout, paneId), () => __foldkitBrandViewResult((!Option.exists(model.maybeFocus, (focus) => __foldkitBrandViewResult((focus.paneId === paneId), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~30"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~29")), {
355
+ onNone: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~8")),
356
+ onSome: (place) => (__foldkitBrandViewResult(({ model: focusOn(model, place) }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~7")),
357
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#FocusedPane"),
358
+ PressedSplitter: ({ splitter, pointerId, clientX, clientY }) => __foldkitBrandViewResult((isHolding(model) || Option.isSome(model.maybeResizing) || !isSplitterPresent(layout, splitter)
359
+ ? { model }
360
+ : {
361
+ model: withResizing(model, Option.some({
362
+ splitter,
363
+ pointerId,
364
+ pressX: clientX,
365
+ pressY: clientY,
366
+ maybeResize: Option.none(),
367
+ })),
368
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#PressedSplitter"),
369
+ MovedSplitter: ({ share }) => __foldkitBrandViewResult((resizeSplitter(model, layout, share, false)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#MovedSplitter"),
370
+ ReleasedSplitter: ({ share }) => __foldkitBrandViewResult((resizeSplitter(model, layout, share, true)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#ReleasedSplitter"),
371
+ CancelledSplitter: () => (__foldkitBrandViewResult(({ model: withResizing(model, Option.none()) }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#CancelledSplitter")),
372
+ PressedSplitterKey: ({ splitter, share }) => __foldkitBrandViewResult((Option.match(resizeOf(layout, splitter, share), {
373
+ onNone: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~9")),
374
+ onSome: (resize) => (__foldkitBrandViewResult(({ model, outMessage: resize }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~8")),
375
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#PressedSplitterKey"),
376
+ CompletedAnnounceReel: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#CompletedAnnounceReel")),
377
+ CompletedFocusPane: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#CompletedFocusPane")),
378
+ CompletedRestoreFocus: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#CompletedRestoreFocus")),
379
+ CompletedFocusGrip: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#CompletedFocusGrip")),
380
+ CompletedScrollIntoView: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#CompletedScrollIntoView")),
381
+ CompletedSizeLiftedPane: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#CompletedSizeLiftedPane")),
382
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#update");
383
+ };
384
+ // CAPABILITIES
385
+ /**
386
+ * Brings the reel in line with a layout that changed: call it whenever the application changes
387
+ * the layout, by a request it applied, another client's or one of its own (a pane closed). The
388
+ * focused pane's place is read again; when it has gone, focus moves to the pane now nearest
389
+ * where it was, and the keyboard follows where it went with the pane. A drag whose pane or
390
+ * column has gone is cancelled, and one whose target's column has gone aims where it started;
391
+ * a splitter whose column or pane has gone lets go.
392
+ */
393
+ export const reconcile = (model, context) => {
394
+ const { layout } = context;
395
+ const isFocusGone = Option.exists(model.maybeFocus, (focus) => __foldkitBrandViewResult((Option.isNone(placeOf(layout, focus.paneId))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~31"));
396
+ const maybePlace = Option.filter(focusedPlace(model, layout), () => __foldkitBrandViewResult((Option.isSome(model.maybeFocus)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~32"));
397
+ const focused = Option.match(maybePlace, {
398
+ onNone: () => __foldkitBrandViewResult((model), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~10"),
399
+ onSome: (place) => __foldkitBrandViewResult((focusOn(model, place)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~9"),
400
+ });
401
+ const restore = Array.fromOption(Option.map(Option.filter(maybePlace, () => __foldkitBrandViewResult((isFocusGone), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~33")), (place) => __foldkitBrandViewResult((RestoreFocus({ paneHook: paneHook(model, place.pane.id) })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~34")));
402
+ const settled = Option.exists(model.maybeResizing, ({ splitter }) => __foldkitBrandViewResult((isSplitterPresent(layout, splitter)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~35"))
403
+ ? focused
404
+ : withResizing(focused, Option.none());
405
+ return __foldkitBrandViewResult((Option.match(settled.maybeHeld, {
406
+ onNone: () => (__foldkitBrandViewResult(({ model: settled, commands: restore }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~11")),
407
+ onSome: (held) => {
408
+ if (!isPresent(layout, held.subject)) {
409
+ return __foldkitBrandViewResult(({
410
+ model: withHeld(settled, Option.none()),
411
+ commands: [...restore, announce(COPY.gone)],
412
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~10");
413
+ }
414
+ const maybeTarget = Option.orElse(Option.filter(held.maybeTarget, (target) => __foldkitBrandViewResult((isTargetPresent(layout, target)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~36")), () => (__foldkitBrandViewResult((held.by._tag === "Keyboard" ? originOf(layout, held.subject) : Option.none()), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~37")));
415
+ return __foldkitBrandViewResult(({
416
+ model: withHeld(settled, Option.some({ ...held, maybeTarget })),
417
+ commands: restore,
418
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~10");
419
+ },
420
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#reconcile");
421
+ };
422
+ /** What the application's keys ask of the reel. `Focus*`: focus the pane beside the focused
423
+ * one. `MovePane*`: move the focused pane up or down its column, or into the column beside it
424
+ * (past the first or last column, into a column of its own). `MoveColumn*`: move its column.
425
+ * `CycleWidth`: its column's width to the next preset. `PullOut`: the pane into a column of
426
+ * its own, right of its column. */
427
+ export const Action = Schema.Literals([
428
+ "FocusLeft",
429
+ "FocusRight",
430
+ "FocusUp",
431
+ "FocusDown",
432
+ "MovePaneLeft",
433
+ "MovePaneRight",
434
+ "MovePaneUp",
435
+ "MovePaneDown",
436
+ "MoveColumnLeft",
437
+ "MoveColumnRight",
438
+ "CycleWidth",
439
+ "PullOut",
440
+ ]);
441
+ const focusStep = (model, layout, place) => (__foldkitBrandViewResult(({
442
+ model: focusOn(model, place),
443
+ commands: [
444
+ FocusPane({ paneHook: paneHook(model, place.pane.id) }),
445
+ ScrollIntoView({ hook: paneHook(model, place.pane.id) }),
446
+ announce(fill(COPY.focused, { pane: place.pane.label, where: placeWords(layout, place) })),
447
+ ],
448
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#focusStep"));
449
+ /** Asks for `request` with focus kept on the pane it moves, at its new place, the reel
450
+ * scrolled to it and the move said; nothing where it would change nothing. */
451
+ const requestStep = (model, layout, place, subject, maybeRequest) => __foldkitBrandViewResult((Option.match(Option.filter(maybeRequest, (request) => __foldkitBrandViewResult((!Equal.equals(apply(layout, request), layout)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~38")), {
452
+ onNone: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~12")),
453
+ onSome: (request) => {
454
+ const next = apply(layout, request);
455
+ const landing = Option.getOrElse(placeOf(next, place.pane.id), () => __foldkitBrandViewResult((place), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~39"));
456
+ const words = Option.getOrElse(subjectWords(layout, subject), () => __foldkitBrandViewResult((place.pane.label), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~40"));
457
+ const where = Option.getOrElse(whereWords(next, subject), () => __foldkitBrandViewResult((WHERE_IT_WAS), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~41"));
458
+ return __foldkitBrandViewResult(({
459
+ model: focusOn(model, landing),
460
+ commands: [
461
+ ScrollIntoView({ hook: paneHook(model, place.pane.id) }),
462
+ announce(fill(COPY.moved, { subject: String.capitalize(words), where })),
463
+ ],
464
+ outMessage: request,
465
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~11");
466
+ },
467
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#requestStep");
468
+ const columnIdAt = (layout, index) => __foldkitBrandViewResult((Option.map(Array.get(layout, index), ({ id }) => __foldkitBrandViewResult((id), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~42"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#columnIdAt");
469
+ /**
470
+ * Does what the application's key for `action` asks, from the focused pane, against the
471
+ * layout as the application holds it: focus moves focus the pane and scroll the reel to it;
472
+ * moves report their request as the OutMessage, which the application applies, with focus
473
+ * kept on the pane. Each is said through the live region. Nothing happens while something is
474
+ * held, or where there is nowhere to go.
475
+ */
476
+ export const perform = (model, action, context) => {
477
+ const { layout } = context;
478
+ return __foldkitBrandViewResult((Option.match(Option.filter(focusedPlace(model, layout), () => __foldkitBrandViewResult((!isHolding(model)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~43")), {
479
+ onNone: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~13")),
480
+ onSome: (place) => {
481
+ const { column, columnIndex, row, pane } = place;
482
+ const rows = column.panes.length;
483
+ const paneId = pane.id;
484
+ const ask = (maybeRequest, subject = Subject.Pane({ paneId })) => __foldkitBrandViewResult((requestStep(model, layout, place, subject, maybeRequest)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#ask");
485
+ const focusAt = (toColumn, toRow) => __foldkitBrandViewResult((Option.match(Option.flatMap(Array.get(layout, toColumn), (found) => __foldkitBrandViewResult((placeAt(layout, toColumn, Number.clamp(toRow, { minimum: 0, maximum: found.panes.length - 1 }))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~44")), {
486
+ onNone: () => (__foldkitBrandViewResult(({ model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~14")),
487
+ onSome: (next) => __foldkitBrandViewResult((next.pane.id === paneId ? { model } : focusStep(model, layout, next)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~13"),
488
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#focusAt");
489
+ const sideways = (toColumn, edge) => __foldkitBrandViewResult((ask(Option.match(Array.get(layout, toColumn), {
490
+ onSome: (found) => __foldkitBrandViewResult((Option.some(OutMessage.MovedPane({
491
+ paneId,
492
+ toColumnId: found.id,
493
+ toIndex: Math.min(row, found.panes.length),
494
+ }))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~14"),
495
+ onNone: () => __foldkitBrandViewResult((Option.liftPredicate(OutMessage.PulledOutPane({ paneId, maybeBeforeColumnId: edge }), () => __foldkitBrandViewResult((rows > 1), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~45"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~15"),
496
+ }))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#sideways");
497
+ const moveColumn = (maybeBefore) => __foldkitBrandViewResult((ask(Option.map(maybeBefore, (maybeBeforeColumnId) => __foldkitBrandViewResult((OutMessage.MovedColumn({ columnId: column.id, maybeBeforeColumnId })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~46")), Subject.Column({ columnId: column.id }))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#moveColumn");
498
+ return __foldkitBrandViewResult((Match.value(action).pipe(Match.withReturnType(), Match.when("FocusLeft", () => __foldkitBrandViewResult((focusAt(columnIndex - 1, row)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~47")), Match.when("FocusRight", () => __foldkitBrandViewResult((focusAt(columnIndex + 1, row)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~48")), Match.when("FocusUp", () => (__foldkitBrandViewResult((row > 0 ? focusAt(columnIndex, row - 1) : { model }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~49"))), Match.when("FocusDown", () => __foldkitBrandViewResult((focusAt(columnIndex, row + 1)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~50")), Match.when("MovePaneUp", () => __foldkitBrandViewResult((ask(Option.some(OutMessage.MovedPane({ paneId, toColumnId: column.id, toIndex: row - 1 })))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~51")), Match.when("MovePaneDown", () => __foldkitBrandViewResult((ask(Option.some(OutMessage.MovedPane({ paneId, toColumnId: column.id, toIndex: row + 1 })))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~52")), Match.when("MovePaneLeft", () => __foldkitBrandViewResult((sideways(columnIndex - 1, columnIdAt(layout, 0))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~53")), Match.when("MovePaneRight", () => __foldkitBrandViewResult((sideways(columnIndex + 1, Option.none())), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~54")), Match.when("MoveColumnLeft", () => __foldkitBrandViewResult((moveColumn(Option.map(columnIdAt(layout, columnIndex - 1), (before) => __foldkitBrandViewResult((Option.some(before)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~56")))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~55")), Match.when("MoveColumnRight", () => __foldkitBrandViewResult((moveColumn(Option.map(columnIdAt(layout, columnIndex + 1), () => __foldkitBrandViewResult((columnIdAt(layout, columnIndex + 2)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~58")))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~57")), Match.when("CycleWidth", () => {
499
+ const width = nextPreset(column.width);
500
+ return __foldkitBrandViewResult(({
501
+ model,
502
+ commands: [
503
+ ScrollIntoView({ hook: paneHook(model, paneId) }),
504
+ announce(fill(COPY.widened, { column: columnIndex + 1, width: widthWords(width) })),
505
+ ],
506
+ outMessage: OutMessage.ResizedColumn({ columnId: column.id, width }),
507
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~59");
508
+ }), Match.when("PullOut", () => __foldkitBrandViewResult((ask(Option.liftPredicate(OutMessage.PulledOutPane({
509
+ paneId,
510
+ maybeBeforeColumnId: columnIdAt(layout, columnIndex + 1),
511
+ }), () => __foldkitBrandViewResult((rows > 1), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~61")))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~60")), Match.exhaustive)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~12");
512
+ },
513
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#perform");
514
+ };
515
+ // SUBSCRIPTION
516
+ /** How near the reel's edges a carried pane scrolls it, how fast, and the longest frame it
517
+ * counts, so a tab coming back from the background does not jump. */
518
+ const EDGE_SCROLL = 40;
519
+ const EDGE_SCROLL_PIXELS_PER_MILLI = 0.8;
520
+ const LONGEST_FRAME_MILLIS = 50;
521
+ /** A column as the pointer reads it from the page: its edges, and the middles of its slots
522
+ * but the held pane's. */
523
+ const columnBoxOf = (element, subject) => {
524
+ const rect = element.getBoundingClientRect();
525
+ const heldId = Subject.match(subject, {
526
+ Pane: ({ paneId }) => __foldkitBrandViewResult((Option.some(paneId)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Pane~2"),
527
+ Column: () => __foldkitBrandViewResult((Option.none()), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Column~2"),
528
+ });
529
+ return __foldkitBrandViewResult(({
530
+ id: element.getAttribute("data-reel-column") ?? "",
531
+ left: rect.left,
532
+ right: rect.right,
533
+ slotMiddles: Array.map(Array.filter(Array.fromIterable(element.querySelectorAll("[data-reel-slot]")), (slot) => __foldkitBrandViewResult((!Option.contains(heldId, slot.getAttribute("data-reel-slot") ?? "")), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~62")), (slot) => {
534
+ const slotRect = slot.getBoundingClientRect();
535
+ return __foldkitBrandViewResult((slotRect.top + slotRect.height / 2), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~63");
536
+ }),
537
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#columnBoxOf");
538
+ };
539
+ /** Where a pointer at (`clientX`, `clientY`) would drop `subject` in the reel `reelHook`
540
+ * names, read from where its columns and their slots sit: none outside the reel. */
541
+ const pointerTarget = (reelHook, subject, clientX, clientY) => __foldkitBrandViewResult((Option.map(Option.filter(scrollerOf(reelHook), (view) => {
542
+ const rect = view.getBoundingClientRect();
543
+ return (__foldkitBrandViewResult((clientX >= rect.left &&
544
+ clientX <= rect.right &&
545
+ clientY >= rect.top &&
546
+ clientY <= rect.bottom), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~64"));
547
+ }), (view) => __foldkitBrandViewResult((targetAtPoint(subject, Array.map(Array.fromIterable(view.querySelectorAll("[data-reel-column]")), (element) => __foldkitBrandViewResult((columnBoxOf(element, subject)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~66")), clientX, clientY)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~65"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#pointerTarget");
548
+ const PointerHold = Schema.Struct({
549
+ reelHook: Schema.String,
550
+ subject: Subject,
551
+ pointerId: Schema.Number,
552
+ });
553
+ const pointerAt = (hold, event) => (__foldkitBrandViewResult(({
554
+ clientX: event.clientX,
555
+ clientY: event.clientY,
556
+ maybeTarget: pointerTarget(hold.reelHook, hold.subject, event.clientX, event.clientY),
557
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#pointerAt"));
558
+ /** The pressing pointer's moves, release and cancel; another pointer's are not the reel's. */
559
+ const pointerMessages = (hold) => __foldkitBrandViewResult((Stream.map(pressingPointer(hold.pointerId), (event) => __foldkitBrandViewResult((Match.value(event.type).pipe(Match.when("pointermove", () => __foldkitBrandViewResult((Message.MovedPointer(pointerAt(hold, event))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~68")), Match.when("pointerup", () => __foldkitBrandViewResult((Message.ReleasedPointer(pointerAt(hold, event))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~69")), Match.orElse(() => __foldkitBrandViewResult((Message.CancelledPointer()), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~70")))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~67"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#pointerMessages");
560
+ /** Which way a pointer at `at` scrolls a box from `low` to `high`: towards the edge it is near. */
561
+ const towards = (at, low, high) => {
562
+ if (at < low + EDGE_SCROLL) {
563
+ return __foldkitBrandViewResult((-1), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#towards");
564
+ }
565
+ else if (at > high - EDGE_SCROLL) {
566
+ return __foldkitBrandViewResult((1), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#towards");
567
+ }
568
+ else {
569
+ return __foldkitBrandViewResult((0), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#towards");
570
+ }
571
+ };
572
+ /** While a pointer carries a pane or a column near the reel's edges, the reel scrolls that way,
573
+ * by the time each frame took, so a drop can reach a column or a place out of view. */
574
+ const edgeScroll = (hold) => __foldkitBrandViewResult((Stream.callback(() => __foldkitBrandViewResult((Effect.acquireRelease(Effect.sync(() => {
575
+ const state = {
576
+ maybeAt: Option.none(),
577
+ frame: 0,
578
+ last: performance.now(),
579
+ };
580
+ const onMove = (event) => {
581
+ if (event.pointerId === hold.pointerId) {
582
+ state.maybeAt = Option.some({ x: event.clientX, y: event.clientY });
583
+ }
584
+ };
585
+ const step = (now) => {
586
+ const distance = Math.min(now - state.last, LONGEST_FRAME_MILLIS) * EDGE_SCROLL_PIXELS_PER_MILLI;
587
+ state.last = now;
588
+ Option.map(Option.all({ view: scrollerOf(hold.reelHook), at: state.maybeAt }), ({ view, at }) => {
589
+ const rect = view.getBoundingClientRect();
590
+ view.scrollLeft += towards(at.x, rect.left, rect.right) * distance;
591
+ view.scrollTop += towards(at.y, rect.top, rect.bottom) * distance;
592
+ });
593
+ state.frame = requestAnimationFrame(step);
594
+ };
595
+ document.addEventListener("pointermove", onMove);
596
+ state.frame = requestAnimationFrame(step);
597
+ return __foldkitBrandViewResult(({ state, onMove }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~72");
598
+ }), ({ state, onMove }) => __foldkitBrandViewResult((Effect.sync(() => {
599
+ cancelAnimationFrame(state.frame);
600
+ document.removeEventListener("pointermove", onMove);
601
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~74")).pipe(Effect.flatMap(() => __foldkitBrandViewResult((Effect.never), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~76")))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~71"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#edgeScroll");
602
+ const SplitterHold = Schema.Struct({
603
+ reelHook: Schema.String,
604
+ splitter: Splitter,
605
+ pointerId: Schema.Number,
606
+ pressX: Schema.Number,
607
+ pressY: Schema.Number,
608
+ });
609
+ const heightOf = (element) => __foldkitBrandViewResult((element.getBoundingClientRect().height), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#heightOf");
610
+ /** The pixels a splitter's share is counted in, read when its drag begins: the reel's width
611
+ * less a gutter for a width (a column is `width × (view − gutter) − gutter` wide), its
612
+ * column's panes' height for a pane splitter. */
613
+ const scaleOf = (hold) => __foldkitBrandViewResult((Option.getOrElse(Option.map(scrollerOf(hold.reelHook), (view) => __foldkitBrandViewResult((Splitter.match(hold.splitter, {
614
+ Width: () => __foldkitBrandViewResult((view.clientWidth -
615
+ Option.getOrElse(Option.map(Option.fromNullishOr(view.querySelector(".reel-gap")), (gap) => __foldkitBrandViewResult((gap.getBoundingClientRect().width), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~78")), () => __foldkitBrandViewResult((0), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~79"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Width"),
616
+ Split: ({ abovePaneId }) => __foldkitBrandViewResult((Number.sumAll(Array.map(Array.fromIterable(view
617
+ .querySelector(`[data-reel-slot="${CSS.escape(abovePaneId)}"]`)
618
+ ?.closest(".reel-column")
619
+ ?.querySelectorAll("[data-reel-slot]") ?? []), heightOf))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Split"),
620
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~77")), () => __foldkitBrandViewResult((0), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~80"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#scaleOf");
621
+ /** The dragging pointer's moves, release and cancel, as shares of the splitter's scale. */
622
+ const splitterMessages = (hold) => __foldkitBrandViewResult((Stream.suspend(() => {
623
+ const scale = scaleOf(hold);
624
+ const shareOf = (event) => {
625
+ const moved = hold.splitter._tag === "Width" ? event.clientX - hold.pressX : event.clientY - hold.pressY;
626
+ return __foldkitBrandViewResult((scale > 0 ? moved / scale : 0), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#shareOf");
627
+ };
628
+ return __foldkitBrandViewResult((Stream.map(pressingPointer(hold.pointerId), (event) => __foldkitBrandViewResult((Match.value(event.type).pipe(Match.when("pointermove", () => __foldkitBrandViewResult((Message.MovedSplitter({ share: shareOf(event) })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~83")), Match.when("pointerup", () => __foldkitBrandViewResult((Message.ReleasedSplitter({ share: shareOf(event) })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~84")), Match.orElse(() => __foldkitBrandViewResult((Message.CancelledSplitter()), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~85")))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~82"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~81");
629
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#splitterMessages");
630
+ const pointerHoldOf = (model, isCarryingOnly) => __foldkitBrandViewResult((Option.flatMap(model.maybeHeld, ({ subject, by }) => __foldkitBrandViewResult((By.match(by, {
631
+ Keyboard: () => __foldkitBrandViewResult((Option.none()), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Keyboard~3"),
632
+ Pointer: ({ pointerId, isCarrying }) => __foldkitBrandViewResult((isCarryingOnly && !isCarrying
633
+ ? Option.none()
634
+ : Option.some({ reelHook: model.id, subject, pointerId })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Pointer~3"),
635
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~86"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#pointerHoldOf");
636
+ const reelSubscriptions = Subscription.make()((entry) => (__foldkitBrandViewResult(({
637
+ pointer: entry({ maybeHold: Schema.Option(PointerHold) }, {
638
+ modelToDependencies: (model) => (__foldkitBrandViewResult(({ maybeHold: pointerHoldOf(model, false) }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#modelToDependencies")),
639
+ dependenciesToStream: ({ maybeHold }) => __foldkitBrandViewResult((Option.match(maybeHold, {
640
+ onNone: () => __foldkitBrandViewResult((Stream.empty), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~16"),
641
+ onSome: (hold) => __foldkitBrandViewResult((Stream.merge(pointerMessages(hold), markDragging)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~15"),
642
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#dependenciesToStream"),
643
+ }),
644
+ edge: entry({ maybeHold: Schema.Option(PointerHold) }, {
645
+ modelToDependencies: (model) => (__foldkitBrandViewResult(({ maybeHold: pointerHoldOf(model, true) }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#modelToDependencies~2")),
646
+ dependenciesToStream: ({ maybeHold }) => __foldkitBrandViewResult((Option.match(maybeHold, { onNone: () => __foldkitBrandViewResult((Stream.empty), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~17"), onSome: edgeScroll })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#dependenciesToStream~2"),
647
+ }),
648
+ resize: entry({ maybeHold: Schema.Option(SplitterHold) }, {
649
+ modelToDependencies: (model) => (__foldkitBrandViewResult(({
650
+ maybeHold: Option.map(model.maybeResizing, ({ splitter, pointerId, pressX, pressY }) => (__foldkitBrandViewResult(({
651
+ reelHook: model.id,
652
+ splitter,
653
+ pointerId,
654
+ pressX,
655
+ pressY,
656
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~88"))),
657
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#modelToDependencies~3")),
658
+ dependenciesToStream: ({ maybeHold }) => __foldkitBrandViewResult((Option.match(maybeHold, {
659
+ onNone: () => __foldkitBrandViewResult((Stream.empty), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~18"),
660
+ onSome: (hold) => __foldkitBrandViewResult((Stream.merge(splitterMessages(hold), markDragging)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~16"),
661
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#dependenciesToStream~3"),
662
+ }),
663
+ escape: entry({ isHolding: Schema.Boolean }, {
664
+ modelToDependencies: (model) => (__foldkitBrandViewResult(({
665
+ isHolding: isHolding(model) || Option.isSome(model.maybeResizing),
666
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#modelToDependencies~4")),
667
+ dependenciesToStream: ({ isHolding: isHoldingNow }) => __foldkitBrandViewResult((isHoldingNow ? escapeKeys(Message.PressedEscape()) : Stream.empty), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#dependenciesToStream~4"),
668
+ }),
669
+ }), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~87")));
670
+ /** A held pane or column follows the pointer (the reel scrolling near its edges), a dragged
671
+ * splitter resizes, and Escape puts either back, before a dialog or the page sees it, from
672
+ * listeners that run only while something is held. Lift them into the application's
673
+ * Subscriptions, once per reel, behind a key prefix of the parent's: `subscriptions("tiles")`
674
+ * is keyed `tilesPointer`, `tilesEdge`, `tilesResize` and `tilesEscape`. */
675
+ export const subscriptions = (keyPrefix) => __foldkitBrandViewResult((prefixKeys(keyPrefix, reelSubscriptions)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#subscriptions");
676
+ // MOUNT
677
+ /** Sizes the copy that follows the pointer to what it lifts, and keeps the pointer where it
678
+ * pressed. Measures the source once, when the copy appears. */
679
+ export const SizeLiftedPane = Mount.define("SizeLiftedPane", {
680
+ args: { sourceSelector: Schema.String, pressX: Schema.Number, pressY: Schema.Number },
681
+ messages: [Message.CompletedSizeLiftedPane],
682
+ execute: ({ element, sourceSelector, pressX, pressY }) => __foldkitBrandViewResult((Effect.sync(() => {
683
+ measureLift(element, sourceSelector, pressX, pressY);
684
+ return __foldkitBrandViewResult((Message.CompletedSizeLiftedPane()), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~89");
685
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#execute~6"),
686
+ });
687
+ const helpId = (model) => __foldkitBrandViewResult((`${model.id}-grip-help`), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#helpId");
688
+ /** The share of the reel or the column a splitter's arrow key moves. */
689
+ const SPLITTER_STEP = 0.05;
690
+ const HELD_STEPS = {
691
+ ArrowUp: "Up",
692
+ ArrowDown: "Down",
693
+ ArrowLeft: "Left",
694
+ ArrowRight: "Right",
695
+ };
696
+ const isPlain = (modifiers) => __foldkitBrandViewResult((!modifiers.shiftKey && !modifiers.ctrlKey && !modifiers.altKey && !modifiers.metaKey), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#isPlain");
697
+ const isDropKey = (key) => __foldkitBrandViewResult((key === " " || key === "Enter"), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#isDropKey");
698
+ /** The anchor that pins a pane to its slot. Pane ids are the application's, so the name is
699
+ * the pane's place in the id order, which the slot and the pane read from one list. */
700
+ const anchorName = (index) => __foldkitBrandViewResult((`--reel-${index}`), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anchorName");
701
+ /** The panes in their layer's order: by id, which no move changes. */
702
+ const byId = Order.mapInput(Order.String, (pane) => __foldkitBrandViewResult((pane.id), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~90"));
703
+ /** Where a carried drop would land, marked: none where it would change nothing. */
704
+ const markOf = (model, layout) => __foldkitBrandViewResult((Option.flatMap(Option.filter(model.maybeHeld, isCarried), (held) => __foldkitBrandViewResult((Option.filter(held.maybeTarget, (target) => __foldkitBrandViewResult((Option.isSome(requestOf(layout, held.subject, target))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~92"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~91"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#markOf");
705
+ const heldPanesOf = (model, layout) => __foldkitBrandViewResult((new Set(Option.match(Option.filter(model.maybeHeld, isCarried), {
706
+ onNone: () => __foldkitBrandViewResult(([]), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~19"),
707
+ onSome: ({ subject }) => __foldkitBrandViewResult((Subject.match(subject, {
708
+ Pane: ({ paneId }) => __foldkitBrandViewResult(([paneId]), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Pane~3"),
709
+ Column: ({ columnId }) => __foldkitBrandViewResult((Option.match(columnOf(layout, columnId), {
710
+ onNone: () => __foldkitBrandViewResult(([]), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~20"),
711
+ onSome: (column) => __foldkitBrandViewResult((Array.map(column.panes, (pane) => __foldkitBrandViewResult((pane.id), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~93"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~18"),
712
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Column~3"),
713
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~17"),
714
+ }))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#heldPanesOf");
715
+ const gripView = (model, subject, label, h) => {
716
+ const isIdleNow = !isHolding(model) && Option.isNone(model.maybeResizing);
717
+ const maybeHeldHere = Option.filter(model.maybeHeld, (held) => __foldkitBrandViewResult((Equal.equals(held.subject, subject)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~94"));
718
+ const isHeld = Option.exists(maybeHeldHere, isCarried);
719
+ const isKeyboardHeld = Option.exists(maybeHeldHere, (held) => __foldkitBrandViewResult((held.by._tag === "Keyboard"), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~95"));
720
+ // the keyboard holds an item on its grip: the keys are the grip's own, and focus leaving the
721
+ // grip puts the item back, so a field reached by Tab types as it should
722
+ const onGripKey = h.OnKeyDownPreventDefault((key, modifiers) => {
723
+ if (!isPlain(modifiers)) {
724
+ return __foldkitBrandViewResult((Option.none()), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~96");
725
+ }
726
+ else if (isKeyboardHeld) {
727
+ return __foldkitBrandViewResult((isDropKey(key)
728
+ ? Option.some(Message.DroppedHeld())
729
+ : Option.map(Option.fromNullishOr(HELD_STEPS[key]), (step) => __foldkitBrandViewResult((Message.SteppedHeld({ step })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~97"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~96");
730
+ }
731
+ else {
732
+ return __foldkitBrandViewResult((Option.liftPredicate(Message.PressedGripKey({ subject }), () => __foldkitBrandViewResult((isIdleNow && isDropKey(key)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~98"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~96");
733
+ }
734
+ });
735
+ const onGripPress = h.OnPointerDown((_pointerType, button, _screenX, _screenY, _time, clientX, clientY, pointerId) => __foldkitBrandViewResult((Option.liftPredicate(Message.PressedGrip({ subject, pointerId, clientX, clientY }), () => __foldkitBrandViewResult((isIdleNow && button === LEFT_BUTTON), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~100"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~99"));
736
+ return __foldkitBrandViewResult((IconButton.view({
737
+ glyph: "grip",
738
+ label,
739
+ isQuiet: true,
740
+ attributes: [
741
+ h.DataAttribute("reel-grip", subjectHook(model, subject)),
742
+ h.AriaRoleDescription("draggable"),
743
+ h.AriaDescribedBy(helpId(model)),
744
+ Button.pressedAttribute(isHeld, h),
745
+ onGripKey,
746
+ onGripPress,
747
+ ...(isKeyboardHeld ? [h.OnBlur(Message.LeftGrip())] : []),
748
+ ],
749
+ }, h)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#gripView");
750
+ };
751
+ /** A splitter's handlers: a pointer drags it, and its arrows (the side ones for a width, up
752
+ * and down for a height), unmodified, step it; a modified arrow is the application's. */
753
+ const splitterAttributes = (model, splitter, h) => {
754
+ const [less, more] = splitter._tag === "Width" ? ["ArrowLeft", "ArrowRight"] : ["ArrowUp", "ArrowDown"];
755
+ return __foldkitBrandViewResult(([
756
+ h.OnPointerDown((_pointerType, button, _screenX, _screenY, _time, clientX, clientY, pointerId) => __foldkitBrandViewResult((Option.liftPredicate(Message.PressedSplitter({ splitter, pointerId, clientX, clientY }), () => __foldkitBrandViewResult((button === LEFT_BUTTON && !isHolding(model) && Option.isNone(model.maybeResizing)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~102"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~101")),
757
+ h.OnKeyDownPreventDefault((key, modifiers) => __foldkitBrandViewResult((Option.liftPredicate(Message.PressedSplitterKey({
758
+ splitter,
759
+ share: key === more ? SPLITTER_STEP : -SPLITTER_STEP,
760
+ }), () => __foldkitBrandViewResult((isPlain(modifiers) && (key === less || key === more)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~104"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~103")),
761
+ ]), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#splitterAttributes");
762
+ };
763
+ const dropClass = (info, column, paneId) => __foldkitBrandViewResult((Option.match(Option.flatMap(info.maybeMark, (mark) => {
764
+ if (mark._tag !== "Into" || mark.columnId !== column.id) {
765
+ return __foldkitBrandViewResult((Option.none()), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~105");
766
+ }
767
+ const others = Array.filter(column.panes, (pane) => __foldkitBrandViewResult((!info.heldPaneIds.has(pane.id)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~106"));
768
+ return __foldkitBrandViewResult((Option.orElse(Option.map(Option.filter(Array.get(others, mark.index), (pane) => __foldkitBrandViewResult((pane.id === paneId), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~107")), () => __foldkitBrandViewResult((" drop-before"), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~108")), () => __foldkitBrandViewResult((Option.map(Option.filter(Array.last(others), (pane) => __foldkitBrandViewResult((mark.index >= others.length && pane.id === paneId), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~110")), () => __foldkitBrandViewResult((" drop-after"), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~111"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~109"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~105");
769
+ }), { onNone: () => __foldkitBrandViewResult((""), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~21"), onSome: (mark) => __foldkitBrandViewResult((mark), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~19") })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#dropClass");
770
+ const isGapMarked = (info, maybeBeforeColumnId) => __foldkitBrandViewResult((Option.exists(info.maybeMark, (mark) => __foldkitBrandViewResult((mark._tag === "Gap" && Equal.equals(mark.maybeBeforeColumnId, maybeBeforeColumnId)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~112"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#isGapMarked");
771
+ const slotView = (info, anchors, column, pane, h) => __foldkitBrandViewResult((h.keyed("div")(pane.id, [
772
+ h.Class(`reel-slot${dropClass(info, column, pane.id)}`),
773
+ h.DataAttribute("reel-slot", pane.id),
774
+ h.Style({ "--reel-weight": `${pane.weight}`, "--reel-anchor": anchors.get(pane.id) ?? "none" }),
775
+ ])), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#slotView");
776
+ const splitView = (model, above, share, h) => __foldkitBrandViewResult((h.keyed("div")(`split:${above.id}`, [
777
+ h.Class("reel-split"),
778
+ h.Role("separator"),
779
+ h.AriaOrientation("horizontal"),
780
+ h.AriaLabel(fill(COPY.heightLabel, { pane: above.label })),
781
+ h.AriaValuemin(0),
782
+ h.AriaValuemax(100),
783
+ h.AriaValuenow(Math.round(share * 100)),
784
+ h.Tabindex(0),
785
+ h.DataAttribute("reel-split", above.id),
786
+ ...splitterAttributes(model, Splitter.Split({ abovePaneId: above.id }), h),
787
+ ])), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#splitView");
788
+ const columnView = (info, anchors, column, index, h) => {
789
+ const { model, layout } = info;
790
+ const total = Number.sumAll(Array.map(column.panes, (pane) => __foldkitBrandViewResult((pane.weight), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~113")));
791
+ const stack = Array.flatMap(column.panes, (pane, row) => __foldkitBrandViewResult(([
792
+ ...Array.fromOption(Option.map(Array.get(column.panes, row - 1), (above) => __foldkitBrandViewResult((splitView(model, above, total > 0 ? above.weight / total : 0, h)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~115"))),
793
+ slotView(info, anchors, column, pane, h),
794
+ ]), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~114"));
795
+ const maybeNextId = Option.map(Array.get(layout, index + 1), ({ id }) => __foldkitBrandViewResult((id), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~116"));
796
+ return __foldkitBrandViewResult(([
797
+ h.keyed("div")(`column:${column.id}`, [
798
+ h.Class("reel-column"),
799
+ h.DataAttribute("reel-column", column.id),
800
+ h.DataAttribute("reel-hook", columnHook(model, column.id)),
801
+ h.Style({ "--reel-width": `${column.width}` }),
802
+ ], [
803
+ h.div([h.Class("reel-column-head")], [
804
+ gripView(model, Subject.Column({ columnId: column.id }), fill(COPY.columnGrip, { column: index + 1 }), h),
805
+ h.span([h.Class("reel-width"), h.AriaHidden(true)], [widthMark(column.width)]),
806
+ ]),
807
+ ...stack,
808
+ ]),
809
+ h.keyed("div")(`resize:${column.id}`, [
810
+ h.Class(`reel-gap reel-resize${isGapMarked(info, maybeNextId) ? " drop-here" : ""}`),
811
+ h.Role("separator"),
812
+ h.AriaOrientation("vertical"),
813
+ h.AriaLabel(fill(COPY.widthLabel, { column: index + 1 })),
814
+ h.AriaValuemin(0),
815
+ h.AriaValuemax(100),
816
+ h.AriaValuenow(Math.round(column.width * 100)),
817
+ h.AriaValuetext(widthWords(column.width)),
818
+ h.Tabindex(0),
819
+ h.DataAttribute("reel-resize", column.id),
820
+ ...splitterAttributes(model, Splitter.Width({ columnId: column.id }), h),
821
+ ]),
822
+ ]), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#columnView");
823
+ };
824
+ const paneLayerView = (info, panes, anchors, h) => {
825
+ const { model } = info;
826
+ // NOTE: the panes keep one order in the page, by id, whatever the layout: a move changes
827
+ // only where each is anchored, so no pane's element is taken out and put back, which would
828
+ // drop focus, a terminal's scroll and whatever a Mount holds inside it.
829
+ return __foldkitBrandViewResult((h.div([h.Class("reel-panes")], Array.map(panes, (pane) => {
830
+ const isFocused = Option.contains(info.maybeFocusedId, pane.id);
831
+ const isHeld = info.heldPaneIds.has(pane.id);
832
+ return __foldkitBrandViewResult((h.keyed("div")(pane.id, [
833
+ h.Class(`reel-pane${isFocused ? " focused" : ""}${isHeld ? " drag-dim" : ""}`),
834
+ h.DataAttribute("reel-pane", paneHook(model, pane.id)),
835
+ h.Style({ "--reel-anchor": anchors.get(pane.id) ?? "none" }),
836
+ h.Tabindex(-1),
837
+ h.OnFocusEnter(Message.FocusedPane({ paneId: pane.id })),
838
+ ], [
839
+ info.toPaneView(pane, {
840
+ grip: gripView(model, Subject.Pane({ paneId: pane.id }), fill(COPY.paneGrip, { pane: pane.label }), h),
841
+ id: `${model.id}-${pane.id}`,
842
+ }),
843
+ ])), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~117");
844
+ }))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#paneLayerView");
845
+ };
846
+ /** The copy a pointer carries: the held panes' title bars, drawn by the reel from their
847
+ * labels, so whatever a pane holds (a terminal, a canvas) is never built twice. */
848
+ const liftedCopyView = (info, h) => {
849
+ const { model, layout } = info;
850
+ return __foldkitBrandViewResult((Array.fromOption(Option.flatMap(model.maybeHeld, (held) => __foldkitBrandViewResult((By.match(held.by, {
851
+ Keyboard: () => __foldkitBrandViewResult((Option.none()), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Keyboard~4"),
852
+ Pointer: ({ isCarrying, pressX, pressY, x, y }) => __foldkitBrandViewResult((Option.liftPredicate(h.div([
853
+ h.Class("reel-lift"),
854
+ h.Style({ transform: `translate3d(${x}px, ${y}px, 0)` }),
855
+ h.AriaHidden(true),
856
+ h.Inert(true),
857
+ h.OnMount(SizeLiftedPane({
858
+ sourceSelector: Subject.match(held.subject, {
859
+ Pane: () => __foldkitBrandViewResult((`[data-reel-pane="${CSS.escape(subjectHook(model, held.subject))}"]`), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Pane~4"),
860
+ Column: () => __foldkitBrandViewResult((`[data-reel-hook="${CSS.escape(subjectHook(model, held.subject))}"]`), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Column~4"),
861
+ }),
862
+ pressX,
863
+ pressY,
864
+ })),
865
+ ], [
866
+ h.div([h.Class("reel-lifted")], Array.map(Array.filter(Array.flatMap(layout, (column) => __foldkitBrandViewResult((column.panes), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~119")), (pane) => __foldkitBrandViewResult((info.heldPaneIds.has(pane.id)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~120")), (pane) => __foldkitBrandViewResult((h.div([h.Class("panel window")], [
867
+ h.div([h.Class("panel-tb")], [
868
+ h.span([h.Class("panel-lead")], [
869
+ h.span([h.Class("iconbtn quiet")], [Glyph.view({ name: "grip" }, h)]),
870
+ ]),
871
+ h.h2([], [pane.label]),
872
+ ]),
873
+ ])), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~121"))),
874
+ ]), () => __foldkitBrandViewResult((isCarrying), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~122"))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#Pointer~4"),
875
+ })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~118")))), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#liftedCopyView");
876
+ };
877
+ /** The layout as it prints, with a dragged splitter's resize shown before it is asked for. */
878
+ const previewed = (model, layout) => __foldkitBrandViewResult((Option.match(Option.flatMap(model.maybeResizing, ({ maybeResize }) => __foldkitBrandViewResult((maybeResize), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~123")), { onNone: () => __foldkitBrandViewResult((layout), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onNone~22"), onSome: (resize) => __foldkitBrandViewResult((apply(layout, resize)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#onSome~20") })), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#previewed");
879
+ /**
880
+ * A row of columns wider than the view when it must be, each a stack of panes, scrolled
881
+ * sideways, and down where a column is taller than the view: `div.reel` holding the scroller
882
+ * `div.reel-view`, whose `div.reel-track` lays out the columns (`div.reel-column`, each a head
883
+ * with its grip and width, then a slot per pane with a splitter between) with a splitter after
884
+ * each, and the panes themselves in `div.reel-panes`, each pinned to its slot by anchor
885
+ * positioning. The reel takes its height from whatever holds it. The layout is the parent's;
886
+ * each pane renders through `toPaneView`.
887
+ *
888
+ * A pane's grip, or a column's, lifts it under a pointer or the keyboard: Space or Enter picks
889
+ * it up, the arrows move it (left and right through the gaps and columns, up and down among a
890
+ * column's panes), Space drops it, and Escape or Tab puts it back. A drop reports what it asks
891
+ * of the layout as the OutMessage. Lift `subscriptions(keyPrefix)` into the application's, or
892
+ * nothing follows the pointer, and call `reconcile` whenever the layout changes.
893
+ */
894
+ export const view = defineView((model, viewInputs, h) => {
895
+ const { label, toPaneView } = viewInputs;
896
+ const layout = previewed(model, viewInputs.layout);
897
+ const panes = Array.sort(Array.flatMap(layout, (column) => __foldkitBrandViewResult((column.panes), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~125")), byId);
898
+ const anchors = new Map(Array.map(panes, (pane, index) => __foldkitBrandViewResult(([pane.id, anchorName(index)]), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~126")));
899
+ const info = {
900
+ model,
901
+ layout,
902
+ toPaneView,
903
+ maybeFocusedId: focusedPaneId(model, layout),
904
+ maybeMark: markOf(model, layout),
905
+ heldPaneIds: heldPanesOf(model, layout),
906
+ };
907
+ return __foldkitBrandViewResult((h.div([h.Class("reel"), h.Role("group"), h.AriaLabel(label), h.DataAttribute("reel", model.id)], [
908
+ h.div([h.Class("reel-view")], [
909
+ h.div([h.Class("reel-track")], [
910
+ h.keyed("div")("gap:start", [
911
+ h.Class(`reel-gap${isGapMarked(info, Option.map(Array.head(layout), ({ id }) => __foldkitBrandViewResult((id), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~127")))
912
+ ? " drop-here"
913
+ : ""}`),
914
+ ]),
915
+ ...Array.flatMap(layout, (column, index) => __foldkitBrandViewResult((columnView(info, anchors, column, index, h)), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~128")),
916
+ paneLayerView(info, panes, anchors, h),
917
+ ]),
918
+ ]),
919
+ h.p([h.Id(helpId(model)), h.Class("sr-only")], [COPY.gripHelp]),
920
+ ...liftedCopyView(info, h),
921
+ ])), "@gjermundgaraba-designs/pixel-paper/src/reel/reel.js#anonymous~124");
922
+ });