foldkit-primitives 0.3.0 → 0.5.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 (51) hide show
  1. package/README.md +144 -723
  2. package/dist/device/index.d.mts.map +1 -1
  3. package/dist/device/index.mjs +95 -87
  4. package/dist/device/index.mjs.map +1 -1
  5. package/dist/dom/index.d.mts +71 -3
  6. package/dist/dom/index.d.mts.map +1 -1
  7. package/dist/dom/index.mjs +303 -6
  8. package/dist/dom/index.mjs.map +1 -1
  9. package/dist/editable-text-CKkS_mWt.d.mts +235 -0
  10. package/dist/editable-text-CKkS_mWt.d.mts.map +1 -0
  11. package/dist/events/index.d.mts +8 -1
  12. package/dist/events/index.d.mts.map +1 -1
  13. package/dist/events/index.mjs +32 -17
  14. package/dist/events/index.mjs.map +1 -1
  15. package/dist/follow-tab-stop-BSZsUrrk.mjs +805 -0
  16. package/dist/follow-tab-stop-BSZsUrrk.mjs.map +1 -0
  17. package/dist/index.d.mts +6 -6
  18. package/dist/index.mjs +7 -7
  19. package/dist/interaction/index.d.mts +247 -92
  20. package/dist/interaction/index.d.mts.map +1 -1
  21. package/dist/interaction/index.mjs +507 -224
  22. package/dist/interaction/index.mjs.map +1 -1
  23. package/dist/internal-DP-xLKbT.mjs +37 -0
  24. package/dist/internal-DP-xLKbT.mjs.map +1 -0
  25. package/dist/media/index.d.mts.map +1 -1
  26. package/dist/media/index.mjs +3 -3
  27. package/dist/media/index.mjs.map +1 -1
  28. package/dist/motion/index.mjs +1 -1
  29. package/dist/{motion-LfJYAMnP.mjs → motion-DGGBZ0wt.mjs} +14 -13
  30. package/dist/motion-DGGBZ0wt.mjs.map +1 -0
  31. package/dist/net/index.d.mts +69 -5
  32. package/dist/net/index.d.mts.map +1 -1
  33. package/dist/net/index.mjs +139 -56
  34. package/dist/net/index.mjs.map +1 -1
  35. package/dist/observers/index.d.mts +1 -1
  36. package/dist/observers/index.mjs +3 -0
  37. package/dist/observers/index.mjs.map +1 -1
  38. package/dist/state/index.d.mts +54 -12
  39. package/dist/state/index.d.mts.map +1 -1
  40. package/dist/state/index.mjs +198 -90
  41. package/dist/state/index.mjs.map +1 -1
  42. package/dist/time/index.d.mts +21 -1
  43. package/dist/time/index.d.mts.map +1 -1
  44. package/dist/time/index.mjs +48 -19
  45. package/dist/time/index.mjs.map +1 -1
  46. package/package.json +20 -9
  47. package/dist/motion-LfJYAMnP.mjs.map +0 -1
  48. package/dist/move-CZwHyTGk.mjs +0 -215
  49. package/dist/move-CZwHyTGk.mjs.map +0 -1
  50. package/dist/move-DNiMPcuf.d.mts +0 -58
  51. package/dist/move-DNiMPcuf.d.mts.map +0 -1
@@ -0,0 +1,235 @@
1
+ import { Schema } from "effect";
2
+ import * as Mount from "foldkit/mount";
3
+ //#region src/dom/focus-scope.d.ts
4
+ /** Tabbable descendants in DOM order: focusable, visible, and not `tabindex="-1"`. */
5
+ declare const tabbableWithin: (root: Element) => ReadonlyArray<HTMLElement>;
6
+ declare const FocusScope: Mount.MountDefinitionWithArgs<"FocusScope", {
7
+ /** Keep Tab, Shift+Tab, and a stray focus inside. */
8
+ contain: Schema.Boolean;
9
+ /** Give focus back to what had it, on unmount. */
10
+ restore: Schema.Boolean;
11
+ /** A selector inside the element to focus first; else the first tabbable
12
+ * descendant, else the element itself when it is focusable. */
13
+ initialFocus: Schema.NullOr<Schema.String>;
14
+ }, never>;
15
+ //#endregion
16
+ //#region src/dom/layers.d.ts
17
+ declare const ScrollLock: Mount.MountDefinitionNoArgs<"ScrollLock", never>;
18
+ declare const HideOutside: Mount.MountDefinitionNoArgs<"HideOutside", never>;
19
+ //#endregion
20
+ //#region src/dom/move.d.ts
21
+ declare const MoveStarted: Schema.TaggedStruct<"MoveStarted", {
22
+ readonly pointerId: Schema.Number;
23
+ readonly pointerType: Schema.String;
24
+ }>;
25
+ type MoveStarted = typeof MoveStarted.Type;
26
+ declare const Moved: Schema.TaggedStruct<"Moved", {
27
+ readonly pointerId: Schema.Number;
28
+ /** From where the pointer went down, in CSS pixels. */
29
+ readonly deltaX: Schema.Number;
30
+ readonly deltaY: Schema.Number;
31
+ readonly pointerType: Schema.String;
32
+ }>;
33
+ type Moved = typeof Moved.Type;
34
+ declare const MoveEnded: Schema.TaggedStruct<"MoveEnded", {
35
+ readonly pointerId: Schema.Number;
36
+ /** `false` when the pointer was cancelled or capture was lost. */
37
+ readonly completed: Schema.Boolean;
38
+ }>;
39
+ type MoveEnded = typeof MoveEnded.Type;
40
+ type Fact = MoveStarted | Moved | MoveEnded;
41
+ declare const Move: Mount.MountDefinitionNoArgs<"Move", {
42
+ readonly _tag: "MoveStarted";
43
+ readonly pointerId: number;
44
+ readonly pointerType: string;
45
+ } | {
46
+ readonly _tag: "Moved";
47
+ readonly pointerId: number;
48
+ readonly deltaX: number;
49
+ readonly deltaY: number;
50
+ readonly pointerType: string;
51
+ } | {
52
+ readonly _tag: "MoveEnded";
53
+ readonly pointerId: number;
54
+ readonly completed: boolean;
55
+ }>;
56
+ //#endregion
57
+ //#region src/dom/targets.d.ts
58
+ declare const TargetHovered: Schema.TaggedStruct<"TargetHovered", {
59
+ /** The marked descendant under the pointer, or `null` when there is none. */
60
+ readonly id: Schema.NullOr<Schema.String>;
61
+ }>;
62
+ type TargetHovered = typeof TargetHovered.Type;
63
+ declare const TargetPressed: Schema.TaggedStruct<"TargetPressed", {
64
+ readonly id: Schema.String;
65
+ readonly shiftKey: Schema.Boolean;
66
+ readonly altKey: Schema.Boolean;
67
+ readonly ctrlKey: Schema.Boolean;
68
+ readonly metaKey: Schema.Boolean;
69
+ }>;
70
+ type TargetPressed = typeof TargetPressed.Type;
71
+ type TargetFact = TargetHovered | TargetPressed;
72
+ /** The id of the nearest marked element at or above `from`, inside `container`. */
73
+ declare const targetOf: (container: Element, from: EventTarget | null, attribute: string) => string | null;
74
+ declare const Targets: Mount.MountDefinitionWithArgs<"Targets", {
75
+ /** The attribute that marks a descendant, holding its id. */
76
+ attribute: Schema.String;
77
+ /** Prevent a press's default, such as a link navigating inside an editor's canvas. */
78
+ preventDefault: Schema.Boolean;
79
+ }, {
80
+ readonly _tag: "TargetHovered";
81
+ readonly id: string | null;
82
+ } | {
83
+ readonly _tag: "TargetPressed";
84
+ readonly id: string;
85
+ readonly shiftKey: boolean;
86
+ readonly altKey: boolean;
87
+ readonly ctrlKey: boolean;
88
+ readonly metaKey: boolean;
89
+ }>;
90
+ //#endregion
91
+ //#region src/dom/pointer-drag.d.ts
92
+ declare const DragZone: Schema.Literals<readonly ["before", "inside", "after"]>;
93
+ type DragZone = typeof DragZone.Type;
94
+ /** A marked descendant under the pointer, and the zone of it the pointer is in. */
95
+ declare const DragPlace: Schema.Struct<{
96
+ readonly id: Schema.String;
97
+ readonly zone: Schema.Literals<readonly ["before", "inside", "after"]>;
98
+ }>;
99
+ type DragPlace = typeof DragPlace.Type;
100
+ declare const DragStarted: Schema.TaggedStruct<"DragStarted", {
101
+ /** The marked descendant being dragged. */
102
+ readonly id: Schema.String;
103
+ }>;
104
+ type DragStarted = typeof DragStarted.Type;
105
+ declare const DraggedOver: Schema.TaggedStruct<"DraggedOver", {
106
+ /** Where the pointer is, or `null` over nothing marked, or over what is dragged. */
107
+ readonly over: Schema.NullOr<Schema.Struct<{
108
+ readonly id: Schema.String;
109
+ readonly zone: Schema.Literals<readonly ["before", "inside", "after"]>;
110
+ }>>;
111
+ /**
112
+ * For a drag onto `targets`: whether the pointer is inside the region they
113
+ * are in, so `over: null` with `region: true` is its empty space (an empty
114
+ * page, the space below the last node). Absent for a drag among its own.
115
+ */
116
+ readonly region: Schema.optionalKey<Schema.Boolean>;
117
+ }>;
118
+ type DraggedOver = typeof DraggedOver.Type;
119
+ declare const DragDropped: Schema.TaggedStruct<"DragDropped", {
120
+ readonly id: Schema.String;
121
+ readonly over: Schema.NullOr<Schema.Struct<{
122
+ readonly id: Schema.String;
123
+ readonly zone: Schema.Literals<readonly ["before", "inside", "after"]>;
124
+ }>>;
125
+ /** As `DraggedOver`'s: released inside the `targets` region. */
126
+ readonly region: Schema.optionalKey<Schema.Boolean>;
127
+ }>;
128
+ type DragDropped = typeof DragDropped.Type;
129
+ declare const DragCancelled: Schema.TaggedStruct<"DragCancelled", {
130
+ readonly id: Schema.String;
131
+ }>;
132
+ type DragCancelled = typeof DragCancelled.Type;
133
+ type DragFact = DragStarted | DraggedOver | DragDropped | DragCancelled;
134
+ /** How far, in CSS pixels, a press moves before it is a drag rather than a click. */
135
+ declare const DRAG_THRESHOLD = 4;
136
+ interface Box {
137
+ readonly top: number;
138
+ readonly height: number;
139
+ }
140
+ /** The zone of a box a vertical position falls in, by thirds. */
141
+ declare const zoneOf: (box: Box, y: number) => DragZone;
142
+ /**
143
+ * An element's box. An element drawn as `display: contents` has none, so its
144
+ * first child's stands for it, as an editor's node wrapper's does.
145
+ */
146
+ declare const boxOf: (element: Element) => Box;
147
+ declare const PointerDrag: Mount.MountDefinitionWithArgs<"PointerDrag", {
148
+ /** The attribute that marks a descendant, holding its id. */
149
+ attribute: Schema.String;
150
+ /**
151
+ * Where a drag may land, when not on the element's own marked descendants:
152
+ * the elements marked by `attribute` inside the one `within` selects, such
153
+ * as a palette's Blocks dragged onto a page. A drop's `over` is then one of
154
+ * those; what is dragged is still one of the element's own. `within` is
155
+ * looked for nearest first, under the element's closest ancestor that holds
156
+ * a match, so two editors on one page each drop onto their own.
157
+ */
158
+ targets: Schema.optionalKey<Schema.Struct<{
159
+ readonly attribute: Schema.String;
160
+ readonly within: Schema.String;
161
+ }>>;
162
+ }, {
163
+ readonly _tag: "DragStarted";
164
+ readonly id: string;
165
+ } | {
166
+ readonly _tag: "DraggedOver";
167
+ readonly over: {
168
+ readonly id: string;
169
+ readonly zone: "inside" | "before" | "after";
170
+ } | null;
171
+ readonly region?: boolean;
172
+ } | {
173
+ readonly _tag: "DragDropped";
174
+ readonly id: string;
175
+ readonly over: {
176
+ readonly id: string;
177
+ readonly zone: "inside" | "before" | "after";
178
+ } | null;
179
+ readonly region?: boolean;
180
+ } | {
181
+ readonly _tag: "DragCancelled";
182
+ readonly id: string;
183
+ }>;
184
+ //#endregion
185
+ //#region src/dom/editable-text.d.ts
186
+ declare const TextEdited: Schema.TaggedStruct<"TextEdited", {
187
+ /** The field, by its marking attribute's value. */
188
+ readonly field: Schema.String;
189
+ readonly text: Schema.String;
190
+ }>;
191
+ type TextEdited = typeof TextEdited.Type;
192
+ declare const TextCommitted: Schema.TaggedStruct<"TextCommitted", {
193
+ readonly field: Schema.String;
194
+ readonly text: Schema.String;
195
+ }>;
196
+ type TextCommitted = typeof TextCommitted.Type;
197
+ declare const TextCancelled: Schema.TaggedStruct<"TextCancelled", {
198
+ readonly field: Schema.String;
199
+ /** The text the field had when editing began, which the DOM shows again. */
200
+ readonly initial: Schema.String;
201
+ }>;
202
+ type TextCancelled = typeof TextCancelled.Type;
203
+ declare const EditAsked: Schema.TaggedStruct<"EditAsked", {
204
+ /** A marked field, not editable yet, that was double-clicked. */
205
+ readonly field: Schema.String;
206
+ }>;
207
+ type EditAsked = typeof EditAsked.Type;
208
+ type TextFact = TextEdited | TextCommitted | TextCancelled | EditAsked;
209
+ /**
210
+ * A field's text. A browser ends an edited element with a line break it adds
211
+ * itself, which is not the author's; in one line, the author's breaks are spaces.
212
+ */
213
+ declare const textOf: (element: HTMLElement) => string;
214
+ declare const EditableText: Mount.MountDefinitionWithArgs<"EditableText", {
215
+ /** The attribute that marks a field, holding its name. */
216
+ attribute: Schema.String;
217
+ }, {
218
+ readonly _tag: "TextEdited";
219
+ readonly field: string;
220
+ readonly text: string;
221
+ } | {
222
+ readonly _tag: "TextCommitted";
223
+ readonly field: string;
224
+ readonly text: string;
225
+ } | {
226
+ readonly _tag: "TextCancelled";
227
+ readonly field: string;
228
+ readonly initial: string;
229
+ } | {
230
+ readonly _tag: "EditAsked";
231
+ readonly field: string;
232
+ }>;
233
+ //#endregion
234
+ export { ScrollLock as A, targetOf as C, MoveStarted as D, MoveEnded as E, tabbableWithin as M, Moved as O, Targets as S, Move as T, boxOf as _, TextEdited as a, TargetHovered as b, DRAG_THRESHOLD as c, DragFact as d, DragPlace as f, PointerDrag as g, DraggedOver as h, TextCommitted as i, FocusScope as j, HideOutside as k, DragCancelled as l, DragZone as m, EditableText as n, TextFact as o, DragStarted as p, TextCancelled as r, textOf as s, EditAsked as t, DragDropped as u, zoneOf as v, Fact as w, TargetPressed as x, TargetFact as y };
235
+ //# sourceMappingURL=editable-text-CKkS_mWt.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editable-text-CKkS_mWt.d.mts","names":[],"sources":["../src/dom/focus-scope.ts","../src/dom/layers.ts","../src/dom/move.ts","../src/dom/targets.ts","../src/dom/pointer-drag.ts","../src/dom/editable-text.ts"],"mappings":";;;;cA0Ba,iBAAkB,MAAM,YAAU,cAAc;cAYhD,YAAU,MAAA;;;;;;;;;;;cCvBV,YAAU,MAAA;cAUV,aAAW,MAAA;;;cCfX,aAAW,OAAA;;;;KAIZ,qBAAqB,YAAY;cAEhC,OAAK,OAAA;;;;;;;KAON,eAAe,MAAM;cAEpB,WAAS,OAAA;;;;;KAKV,mBAAmB,UAAU;KAE7B,OAAO,cAAc,QAAQ;cAe5B,MAAI,MAAA;;;;;;;;;;;;;;;;;cClCJ,eAAa,OAAA;;;;KAId,uBAAuB,cAAc;cAEpC,eAAa,OAAA;;;;;;;KAOd,uBAAuB,cAAc;KAErC,aAAa,gBAAgB;;cAU5B,WAAY,WAAW,SAAS,MAAM,oBAAoB;cAM1D,SAAO,MAAA;;;;;;;;;;;;;;;;;;cC1BP,UAAQ,OAAA;KACT,kBAAkB,SAAS;;cAG1B,WAAS,OAAA;;;;KACV,mBAAmB,UAAU;cAE5B,aAAW,OAAA;;;;KAIZ,qBAAqB,YAAY;cAEhC,aAAW,OAAA;;;;;;;;;;;;;KAUZ,qBAAqB,YAAY;cAEhC,aAAW,OAAA;;;;;;;;;KAMZ,qBAAqB,YAAY;cAEhC,eAAa,OAAA;;;KACd,uBAAuB,cAAc;KAErC,WAAW,cAAc,cAAc,cAAc;;cAGpD;UAEH;WACC;WACA;;;cAIE,SAAU,KAAK,KAAK,cAAY;;;;;cAUhC,QAAS,SAAS,YAAU;cAgB5B,aAAW,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cClEX,YAAU,OAAA;;;;;KAKX,oBAAoB,WAAW;cAE9B,eAAa,OAAA;;;;KAId,uBAAuB,cAAc;cAEpC,eAAa,OAAA;;;;;KAKd,uBAAuB,cAAc;cAEpC,WAAS,OAAA;;;;KAIV,mBAAmB,UAAU;KAE7B,WAAW,aAAa,gBAAgB,gBAAgB;;;;;cASvD,SAAU,SAAS;cAoBnB,cAAY,MAAA"}
@@ -79,7 +79,14 @@ export type KeyboardMessage = typeof KeyboardMessage.Type;
79
79
  * empty instead of throwing. Lift with `Subscription.persistent`, mapping
80
80
  * into the parent's Message.
81
81
  */
82
- export declare const keyboardEvents: () => Stream.Stream<KeyboardMessage>;
82
+ export declare const keyboardEvents: (options?: {
83
+ /**
84
+ * Whether to cancel a press's default (the page scrolling under an arrow
85
+ * key, the browser's own shortcut), decided in the listener, since a
86
+ * default can only be cancelled before the event finishes dispatching.
87
+ */
88
+ readonly preventDefault?: (press: KeyPress) => boolean;
89
+ }) => Stream.Stream<KeyboardMessage>;
83
90
  //#endregion
84
91
  //#region src/events/pointer.d.ts
85
92
  export declare const PointerMessage: import("foldkit/message").MessageUnion<{
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/events/active-element.ts","../../src/events/hotkeys.ts","../../src/events/idle.ts","../../src/events/keyboard.ts","../../src/events/pointer.ts","../../src/events/scroll.ts","../../src/events/visibility.ts","../../src/events/window-size.ts"],"mappings":";;;;qBASa,gDAAoB;;;;;;YAGrB,8BAA8B,qBAAqB;;;;;;qBAOlD,2BAA0B,OAAO,OAAO;;;;;;;;;;;;;;;iBCNpC;WACN;WACA;WACA;WACA;WACA;WACA;;qBAkCE,cAAe,iBAAiB,OAAO;;;qBCvCvC,WAAS,OAAA;;;YACV,mBAAmB,UAAU;qBAE5B,uCAAW;;;;YACZ,qBAAqB,YAAY;qBAchC,MAAI,OAAA;;;;;;;;;;;qBCvBJ,2CAAe;;;;;;;;;;;;;YAWhB,yBAAyB,gBAAgB;;;;;;qBAOxC,sBAAqB,OAAO,OAAO;;;qBClBnC,0CAAc;;;;;;YAGf,wBAAwB,eAAe;;;;;;qBAOtC,qBAAoB,OAAO,OAAO;;;qBCVlC,yCAAa;;;;;;YAGd,uBAAuB,cAAc;;;;;;;qBAQpC,oBAAmB,OAAO,OAAO;;;qBCTjC,iBAAe,OAAA;;;YAChB,yBAAyB,gBAAgB;qBAExC,6CAAiB;;sBAAjB,OAAA;;;YACD,2BAA2B,kBAAkB;qBAiB5C,YAAU,OAAA;;;;;;;;qBCpBV,iBAAe,OAAA;;;;YAChB,yBAAyB,gBAAgB;qBAExC,6CAAiB;;;;;;YAGlB,2BAA2B,kBAAkB;qBAmC5C,YAAU,OAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/events/active-element.ts","../../src/events/hotkeys.ts","../../src/events/idle.ts","../../src/events/keyboard.ts","../../src/events/pointer.ts","../../src/events/scroll.ts","../../src/events/visibility.ts","../../src/events/window-size.ts"],"mappings":";;;;qBASa,gDAAoB;;;;;;YAGrB,8BAA8B,qBAAqB;;;;;;qBAOlD,2BAA0B,OAAO,OAAO;;;;;;;;;;;;;;;iBCNpC;WACN;WACA;WACA;WACA;WACA;WACA;;qBAkCE,cAAe,iBAAiB,OAAO;;;qBCvCvC,WAAS,OAAA;;;YACV,mBAAmB,UAAU;qBAE5B,uCAAW;;;;YACZ,qBAAqB,YAAY;qBAchC,MAAI,OAAA;;;;;;;;;;;qBCtBJ,2CAAe;;;;;;;;;;;;;YAWhB,yBAAyB,gBAAgB;;;;;;qBAOxC,iBACX;;;;;;WAMW,kBAAkB,OAAO;MAEnC,OAAO,OAAO;;;qBC5BJ,0CAAc;;;;;;YAGf,wBAAwB,eAAe;;;;;;qBAOtC,qBAAoB,OAAO,OAAO;;;qBCVlC,yCAAa;;;;;;YAGd,uBAAuB,cAAc;;;;;;;qBAQpC,oBAAmB,OAAO,OAAO;;;qBCTjC,iBAAe,OAAA;;;YAChB,yBAAyB,gBAAgB;qBAExC,6CAAiB;;sBAAjB,OAAA;;;YACD,2BAA2B,kBAAkB;qBAiB5C,YAAU,OAAA;;;;;;;;qBCnBV,iBAAe,OAAA;;;;YAChB,yBAAyB,gBAAgB;qBAExC,6CAAiB;;;;;;YAGlB,2BAA2B,kBAAkB;qBAmC5C,YAAU,OAAA"}
@@ -1,3 +1,4 @@
1
+ import { i as unlessSame } from "../internal-DP-xLKbT.mjs";
1
2
  import { a as modalityOfKey, i as Modality, n as InputModalityMessage, r as InputModalityModel, t as InputModality } from "../input-modality-m6v7__Xs.mjs";
2
3
  import { Effect, Queue, Schema, Stream } from "effect";
3
4
  import { defineMessageUnion } from "foldkit/message";
@@ -104,14 +105,14 @@ const Idle = Bundle.make("Idle", {
104
105
  args: Schema.Struct({ timeoutMs: Schema.Number.pipe(Schema.check(Schema.isGreaterThan(0)), Schema.check(Schema.isFinite())) }),
105
106
  init: () => ({ model: { idle: false } }),
106
107
  update: (model, message) => IdleMessage.match(message, {
107
- BecameIdle: () => ({ model: {
108
+ BecameIdle: () => model.idle ? { model } : { model: {
108
109
  ...model,
109
110
  idle: true
110
- } }),
111
- BecameActive: () => ({ model: {
111
+ } },
112
+ BecameActive: () => model.idle ? { model: {
112
113
  ...model,
113
114
  idle: false
114
- } })
115
+ } } : { model }
115
116
  }),
116
117
  subscriptions: ({ timeoutMs }) => Subscription.make()((entry) => ({ watch: entry({ idle: Schema.Boolean }, {
117
118
  modelToDependencies: (model) => ({ idle: model.idle }),
@@ -142,18 +143,32 @@ const KeyboardMessage = defineMessageUnion({
142
143
  * empty instead of throwing. Lift with `Subscription.persistent`, mapping
143
144
  * into the parent's Message.
144
145
  */
145
- const keyboardEvents = () => {
146
+ const keyboardEvents = (options = {}) => {
146
147
  if (typeof window === "undefined") return Stream.empty;
147
- const downs = Stream.fromEventListener(window, "keydown").pipe(Stream.map((event) => KeyboardMessage.Pressed({
148
- key: event.key,
149
- repeat: event.repeat,
150
- ctrl: event.ctrlKey,
151
- shift: event.shiftKey,
152
- alt: event.altKey,
153
- meta: event.metaKey
148
+ return Stream.callback((queue) => Effect.acquireRelease(Effect.sync(() => {
149
+ const down = (event) => {
150
+ const press = KeyboardMessage.Pressed({
151
+ key: event.key,
152
+ repeat: event.repeat,
153
+ ctrl: event.ctrlKey,
154
+ shift: event.shiftKey,
155
+ alt: event.altKey,
156
+ meta: event.metaKey
157
+ });
158
+ if (options.preventDefault?.(press) === true) event.preventDefault();
159
+ Queue.offerUnsafe(queue, press);
160
+ };
161
+ const up = (event) => Queue.offerUnsafe(queue, KeyboardMessage.Released({ key: event.key }));
162
+ window.addEventListener("keydown", down);
163
+ window.addEventListener("keyup", up);
164
+ return {
165
+ down,
166
+ up
167
+ };
168
+ }), ({ down, up }) => Effect.sync(() => {
169
+ window.removeEventListener("keydown", down);
170
+ window.removeEventListener("keyup", up);
154
171
  })));
155
- const ups = Stream.fromEventListener(window, "keyup").pipe(Stream.map((event) => KeyboardMessage.Released({ key: event.key })));
156
- return Stream.mergeAll([downs, ups], { concurrency: "unbounded" });
157
172
  };
158
173
  //#endregion
159
174
  //#region src/events/pointer.ts
@@ -223,7 +238,7 @@ const Visibility = Bundle.make("Visibility", {
223
238
  Model: VisibilityModel,
224
239
  Message: VisibilityMessage,
225
240
  init: () => ({ model: { visible: isVisible() } }),
226
- update: (model, message) => ({ model: { visible: message.visible } }),
241
+ update: (model, message) => message.visible === model.visible ? { model } : { model: { visible: message.visible } },
227
242
  subscriptions: () => Subscription.make()(() => ({ changes: Subscription.persistent(visibilityStream()) }))
228
243
  });
229
244
  //#endregion
@@ -276,10 +291,10 @@ const WindowSize = Bundle.make("WindowSize", {
276
291
  width: 0,
277
292
  height: 0
278
293
  } }),
279
- update: (model, message) => ({ model: {
294
+ update: (model, message) => ({ model: unlessSame(model, {
280
295
  width: message.width,
281
296
  height: message.height
282
- } }),
297
+ }) }),
283
298
  subscriptions: () => Subscription.make()(() => ({ changes: Subscription.persistent(sizeStream()) }))
284
299
  });
285
300
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/events/active-element.ts","../../src/events/hotkeys.ts","../../src/events/idle.ts","../../src/events/keyboard.ts","../../src/events/pointer.ts","../../src/events/scroll.ts","../../src/events/visibility.ts","../../src/events/window-size.ts"],"sourcesContent":["/**\n * Focused element as an entry — not a bundle, because elements are not\n * Model facts: this reports the active element's tag and id (both\n * serializable), and the parent stores what matters. Starts with the\n * current answer, then follows `focusin`/`focusout`.\n */\nimport { Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\n\nexport const ActiveElementMessage = defineMessageUnion({\n Changed: { tag: Schema.String, id: Schema.NullOr(Schema.String) },\n})\nexport type ActiveElementMessage = typeof ActiveElementMessage.Type\n\n/**\n * Focus changes on the document. Without a document the stream is empty\n * instead of throwing. Lift with `Subscription.persistent`, mapping into\n * the parent's Message.\n */\nexport const activeElementEvents = (): Stream.Stream<ActiveElementMessage> => {\n if (typeof document === 'undefined') return Stream.empty\n const read = (): ActiveElementMessage => {\n const element = document.activeElement as Element | null | undefined\n return ActiveElementMessage.Changed({\n tag: element?.tagName ?? '',\n id: element?.id !== undefined && element.id !== '' ? element.id : null,\n })\n }\n const changes: Stream.Stream<ActiveElementMessage> = Stream.mergeAll(\n [\n Stream.fromEventListener(document, 'focusin').pipe(Stream.map(read)),\n Stream.fromEventListener(document, 'focusout').pipe(Stream.map(read)),\n ],\n { concurrency: 'unbounded' },\n )\n return Stream.concat(Stream.make(read()), changes)\n}\n","/**\n * Hotkey matching as a pure function over a `Pressed` payload: `matchHotkey`\n * answers whether a press is the shortcut, so `update` stays a table of\n * chords instead of a nest of modifier checks. Patterns read like\n * `\"ctrl+shift+k\"`: modifiers first (any order), the key last,\n * case-insensitive, with `cmd`/`command` and `option` as the Mac names for\n * meta and alt. Matching is exact — unlisted modifiers must be up — and\n * auto-repeat never matches, so holding a chord fires once. `space` (or a\n * bare space) means the space key; `esc` means escape. A pattern with\n * no key, or two keys, matches nothing: predicates return false, they do\n * not throw on the hot path.\n */\n\nexport interface KeyPress {\n readonly key: string\n readonly repeat: boolean\n readonly ctrl: boolean\n readonly shift: boolean\n readonly alt: boolean\n readonly meta: boolean\n}\n\nconst modifierOf = (part: string): 'ctrl' | 'shift' | 'alt' | 'meta' | null => {\n switch (part) {\n case 'ctrl':\n case 'control':\n return 'ctrl'\n case 'shift':\n return 'shift'\n case 'alt':\n case 'option':\n return 'alt'\n case 'meta':\n case 'cmd':\n case 'command':\n return 'meta'\n default:\n return null\n }\n}\n\nconst keyNameOf = (part: string): string => {\n switch (part) {\n case 'spacebar':\n case ' ':\n return 'space'\n case 'esc':\n return 'escape'\n default:\n return part\n }\n}\n\nexport const matchHotkey = (pattern: string, press: KeyPress): boolean => {\n if (press.repeat) return false\n const trimmed = pattern\n .split('+')\n .map(part => part.trim().toLowerCase())\n .filter(part => part !== '')\n // A bare whitespace pattern means the space key; an empty pattern is malformed.\n const parts = trimmed.length === 0 && pattern.length > 0 ? ['space'] : trimmed\n const keys = parts.filter(part => modifierOf(part) === null)\n if (keys.length !== 1) return false\n if (keyNameOf(keys[0]!) !== keyNameOf(press.key.toLowerCase())) return false\n const required: Record<'ctrl' | 'shift' | 'alt' | 'meta', boolean> = {\n ctrl: false,\n shift: false,\n alt: false,\n meta: false,\n }\n for (const part of parts) {\n const modifier = modifierOf(part)\n if (modifier !== null) required[modifier] = true\n }\n return (\n required.ctrl === press.ctrl &&\n required.shift === press.shift &&\n required.alt === press.alt &&\n required.meta === press.meta\n )\n}\n","/**\n * Idleness as a bundle: `idle` in the Model, flipped by activity and\n * silence. While active, activity debounced past `timeoutMs` settles to\n * `BecameIdle`; while idle, the first activity wakes to `BecameActive` and\n * the dependency flip restarts the watch. Starts active — a lurker idles\n * when the timeout elapses. The clock is Effect's, so tests drive it with\n * TestClock.\n */\nimport { Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport type * as Update from 'foldkit/update'\nimport { Bundle } from 'foldkit-bundle'\n\nexport const IdleModel = Schema.Struct({ idle: Schema.Boolean })\nexport type IdleModel = typeof IdleModel.Type\n\nexport const IdleMessage = defineMessageUnion({ BecameIdle: {}, BecameActive: {} })\nexport type IdleMessage = typeof IdleMessage.Type\n\nconst activity = (): Stream.Stream<unknown> => {\n if (typeof window === 'undefined') return Stream.empty\n // One stream per sense; the merge fans into the debounce below.\n const senses: Array<Stream.Stream<unknown>> = [\n Stream.fromEventListener(window, 'mousemove'),\n Stream.fromEventListener(window, 'keydown'),\n Stream.fromEventListener(window, 'pointerdown'),\n Stream.fromEventListener(window, 'scroll'),\n ]\n return Stream.mergeAll(senses, { concurrency: 'unbounded' })\n}\n\nexport const Idle = Bundle.make('Idle', {\n Model: IdleModel,\n Message: IdleMessage,\n args: Schema.Struct({\n timeoutMs: Schema.Number.pipe(\n Schema.check(Schema.isGreaterThan(0)),\n Schema.check(Schema.isFinite()),\n ),\n }),\n init: () => ({ model: { idle: false } }),\n update: (model, message) =>\n IdleMessage.match<Update.ReturnWithOutMessage<IdleModel, IdleMessage, never>>(message, {\n BecameIdle: () => ({ model: { ...model, idle: true } }),\n BecameActive: () => ({ model: { ...model, idle: false } }),\n }),\n subscriptions: ({ timeoutMs }): Subscription.Subscriptions<IdleModel, IdleMessage> =>\n Subscription.make<IdleModel, IdleMessage>()(entry => ({\n watch: entry(\n { idle: Schema.Boolean },\n {\n modelToDependencies: model => ({ idle: model.idle }),\n dependenciesToStream: ({ idle }) =>\n idle\n ? activity().pipe(\n Stream.take(1),\n Stream.map(() => IdleMessage.BecameActive()),\n )\n : // Seeded with subscribe time as last-known-alive: silence from\n // here still settles, so a lurker idles without moving first.\n Stream.concat(Stream.make(null), activity()).pipe(\n Stream.debounce(timeoutMs),\n Stream.map(() => IdleMessage.BecameIdle()),\n ),\n },\n ),\n })),\n})\n","/**\n * Keyboard as an entry — not a bundle, because the parent owns whatever key\n * state it keeps (a held-keys set, a shortcut map, a focused field): this\n * reports presses and releases, including auto-repeat, and the parent\n * stores what matters. Hotkey matching stays application policy.\n */\nimport { Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\n\nexport const KeyboardMessage = defineMessageUnion({\n Pressed: {\n key: Schema.String,\n repeat: Schema.Boolean,\n ctrl: Schema.Boolean,\n shift: Schema.Boolean,\n alt: Schema.Boolean,\n meta: Schema.Boolean,\n },\n Released: { key: Schema.String },\n})\nexport type KeyboardMessage = typeof KeyboardMessage.Type\n\n/**\n * Key presses and releases on the window. Without a window the stream is\n * empty instead of throwing. Lift with `Subscription.persistent`, mapping\n * into the parent's Message.\n */\nexport const keyboardEvents = (): Stream.Stream<KeyboardMessage> => {\n if (typeof window === 'undefined') return Stream.empty\n const downs: Stream.Stream<KeyboardMessage> = Stream.fromEventListener<KeyboardEvent>(\n window,\n 'keydown',\n ).pipe(\n Stream.map(event =>\n KeyboardMessage.Pressed({\n key: event.key,\n repeat: event.repeat,\n ctrl: event.ctrlKey,\n shift: event.shiftKey,\n alt: event.altKey,\n meta: event.metaKey,\n }),\n ),\n )\n const ups: Stream.Stream<KeyboardMessage> = Stream.fromEventListener<KeyboardEvent>(\n window,\n 'keyup',\n ).pipe(Stream.map(event => KeyboardMessage.Released({ key: event.key })))\n return Stream.mergeAll([downs, ups], { concurrency: 'unbounded' })\n}\n","/**\n * Pointer position as an entry — not a bundle, because the parent owns what\n * it keeps (a cursor, a drag anchor, hover state): this reports moves, and\n * the parent stores what matters. Element-scoped pointer needs are Mounts,\n * not this window-level entry.\n */\nimport { Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\n\nexport const PointerMessage = defineMessageUnion({\n Moved: { x: Schema.Number, y: Schema.Number },\n})\nexport type PointerMessage = typeof PointerMessage.Type\n\n/**\n * Pointer moves on the window. Without a window the stream is empty instead\n * of throwing. Lift with `Subscription.persistent`, mapping into the\n * parent's Message.\n */\nexport const pointerEvents = (): Stream.Stream<PointerMessage> => {\n if (typeof window === 'undefined') return Stream.empty\n return Stream.fromEventListener<PointerEvent>(window, 'pointermove').pipe(\n Stream.map(event => PointerMessage.Moved({ x: event.clientX, y: event.clientY })),\n )\n}\n","/**\n * Window scroll position as an entry — not a bundle, because the parent\n * owns what it keeps (a stuck header, a back-to-top gate, restore-on-back):\n * this reports positions, and the parent stores what matters.\n * Element-scoped scrolling needs a Mount, not this window-level entry.\n */\nimport { Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\n\nexport const ScrollMessage = defineMessageUnion({\n Scrolled: { x: Schema.Number, y: Schema.Number },\n})\nexport type ScrollMessage = typeof ScrollMessage.Type\n\n/**\n * Scrolls on the window, in the capture phase so container scrolls (which\n * do not bubble) report too. Without a window the stream is empty instead\n * of throwing. Lift with `Subscription.persistent`, mapping into the\n * parent's Message.\n */\nexport const scrollEvents = (): Stream.Stream<ScrollMessage> => {\n if (typeof window === 'undefined') return Stream.empty\n return Stream.fromEventListener(window, 'scroll', { capture: true }).pipe(\n Stream.map(() => ScrollMessage.Scrolled({ x: window.scrollX, y: window.scrollY })),\n )\n}\n","/**\n * Page visibility as a bundle: `visible` in the Model, read from the\n * document at startup and kept current by `visibilitychange`. A hidden page\n * is the application's cue to pause polling, animations, and realtime\n * streams; the decision stays in application `update`, not here.\n */\nimport { Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\n\nexport const VisibilityModel = Schema.Struct({ visible: Schema.Boolean })\nexport type VisibilityModel = typeof VisibilityModel.Type\n\nexport const VisibilityMessage = defineMessageUnion({ Changed: { visible: Schema.Boolean } })\nexport type VisibilityMessage = typeof VisibilityMessage.Type\n\nconst isVisible = (): boolean =>\n typeof document === 'undefined' || document.visibilityState !== 'hidden'\n\nconst visibilityStream = (): Stream.Stream<VisibilityMessage> => {\n if (typeof document === 'undefined' || typeof document.addEventListener !== 'function') {\n return Stream.empty\n }\n return Stream.concat(\n Stream.make(VisibilityMessage.Changed({ visible: isVisible() })),\n Stream.fromEventListener(document, 'visibilitychange').pipe(\n Stream.map(() => VisibilityMessage.Changed({ visible: isVisible() })),\n ),\n )\n}\n\nexport const Visibility = Bundle.make('Visibility', {\n Model: VisibilityModel,\n Message: VisibilityMessage,\n // No document (SSR): assume visible; the stream corrects on subscribe.\n init: () => ({ model: { visible: isVisible() } }),\n update: (model, message) => ({ model: { visible: message.visible } }),\n subscriptions: (): Subscription.Subscriptions<VisibilityModel, VisibilityMessage> =>\n Subscription.make<VisibilityModel, VisibilityMessage>()(() => ({\n changes: Subscription.persistent(visibilityStream()),\n })),\n})\n","/**\n * Window size as a bundle: `{ width, height }` in the Model, read from the\n * window at startup and kept current by one `resize` listener. Breakpoints\n * owns its own listener and derives names; place this one when views need\n * the raw pixels. Placing both doubles resize listeners — pick the one the\n * view reads.\n */\nimport { Effect, Queue, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\n\nexport const WindowSizeModel = Schema.Struct({ width: Schema.Number, height: Schema.Number })\nexport type WindowSizeModel = typeof WindowSizeModel.Type\n\nexport const WindowSizeMessage = defineMessageUnion({\n Changed: { width: Schema.Number, height: Schema.Number },\n})\nexport type WindowSizeMessage = typeof WindowSizeMessage.Type\n\nconst readSize = (): { readonly width: number; readonly height: number } => ({\n width: window.innerWidth,\n height: window.innerHeight,\n})\n\nconst sizeStream = (): Stream.Stream<WindowSizeMessage> => {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return Stream.empty\n }\n return Stream.unwrap(\n Effect.gen(function* () {\n const queue = yield* Queue.unbounded<WindowSizeMessage>()\n const onResize = () => {\n const size = readSize()\n Effect.runFork(\n Queue.offer(queue, WindowSizeMessage.Changed({ width: size.width, height: size.height })),\n )\n }\n window.addEventListener('resize', onResize)\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => {\n window.removeEventListener('resize', onResize)\n }),\n )\n const size = readSize()\n return Stream.concat(\n Stream.make(WindowSizeMessage.Changed({ width: size.width, height: size.height })),\n Stream.fromQueue(queue),\n )\n }),\n )\n}\n\nexport const WindowSize = Bundle.make('WindowSize', {\n Model: WindowSizeModel,\n Message: WindowSizeMessage,\n init: () => ({ model: { width: 0, height: 0 } }),\n update: (model, message) => ({ model: { width: message.width, height: message.height } }),\n subscriptions: (): Subscription.Subscriptions<WindowSizeModel, WindowSizeMessage> =>\n Subscription.make<WindowSizeModel, WindowSizeMessage>()(() => ({\n changes: Subscription.persistent(sizeStream()),\n })),\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/events/active-element.ts","../../src/events/hotkeys.ts","../../src/events/idle.ts","../../src/events/keyboard.ts","../../src/events/pointer.ts","../../src/events/scroll.ts","../../src/events/visibility.ts","../../src/events/window-size.ts"],"sourcesContent":["/**\n * Focused element as an entry — not a bundle, because elements are not\n * Model facts: this reports the active element's tag and id (both\n * serializable), and the parent stores what matters. Starts with the\n * current answer, then follows `focusin`/`focusout`.\n */\nimport { Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\n\nexport const ActiveElementMessage = defineMessageUnion({\n Changed: { tag: Schema.String, id: Schema.NullOr(Schema.String) },\n})\nexport type ActiveElementMessage = typeof ActiveElementMessage.Type\n\n/**\n * Focus changes on the document. Without a document the stream is empty\n * instead of throwing. Lift with `Subscription.persistent`, mapping into\n * the parent's Message.\n */\nexport const activeElementEvents = (): Stream.Stream<ActiveElementMessage> => {\n if (typeof document === 'undefined') return Stream.empty\n const read = (): ActiveElementMessage => {\n const element = document.activeElement as Element | null | undefined\n return ActiveElementMessage.Changed({\n tag: element?.tagName ?? '',\n id: element?.id !== undefined && element.id !== '' ? element.id : null,\n })\n }\n const changes: Stream.Stream<ActiveElementMessage> = Stream.mergeAll(\n [\n Stream.fromEventListener(document, 'focusin').pipe(Stream.map(read)),\n Stream.fromEventListener(document, 'focusout').pipe(Stream.map(read)),\n ],\n { concurrency: 'unbounded' },\n )\n return Stream.concat(Stream.make(read()), changes)\n}\n","/**\n * Hotkey matching as a pure function over a `Pressed` payload: `matchHotkey`\n * answers whether a press is the shortcut, so `update` stays a table of\n * chords instead of a nest of modifier checks. Patterns read like\n * `\"ctrl+shift+k\"`: modifiers first (any order), the key last,\n * case-insensitive, with `cmd`/`command` and `option` as the Mac names for\n * meta and alt. Matching is exact — unlisted modifiers must be up — and\n * auto-repeat never matches, so holding a chord fires once. `space` (or a\n * bare space) means the space key; `esc` means escape. A pattern with\n * no key, or two keys, matches nothing: predicates return false, they do\n * not throw on the hot path.\n */\n\nexport interface KeyPress {\n readonly key: string\n readonly repeat: boolean\n readonly ctrl: boolean\n readonly shift: boolean\n readonly alt: boolean\n readonly meta: boolean\n}\n\nconst modifierOf = (part: string): 'ctrl' | 'shift' | 'alt' | 'meta' | null => {\n switch (part) {\n case 'ctrl':\n case 'control':\n return 'ctrl'\n case 'shift':\n return 'shift'\n case 'alt':\n case 'option':\n return 'alt'\n case 'meta':\n case 'cmd':\n case 'command':\n return 'meta'\n default:\n return null\n }\n}\n\nconst keyNameOf = (part: string): string => {\n switch (part) {\n case 'spacebar':\n case ' ':\n return 'space'\n case 'esc':\n return 'escape'\n default:\n return part\n }\n}\n\nexport const matchHotkey = (pattern: string, press: KeyPress): boolean => {\n if (press.repeat) return false\n const trimmed = pattern\n .split('+')\n .map(part => part.trim().toLowerCase())\n .filter(part => part !== '')\n // A bare whitespace pattern means the space key; an empty pattern is malformed.\n const parts = trimmed.length === 0 && pattern.length > 0 ? ['space'] : trimmed\n const keys = parts.filter(part => modifierOf(part) === null)\n if (keys.length !== 1) return false\n if (keyNameOf(keys[0]!) !== keyNameOf(press.key.toLowerCase())) return false\n const required: Record<'ctrl' | 'shift' | 'alt' | 'meta', boolean> = {\n ctrl: false,\n shift: false,\n alt: false,\n meta: false,\n }\n for (const part of parts) {\n const modifier = modifierOf(part)\n if (modifier !== null) required[modifier] = true\n }\n return (\n required.ctrl === press.ctrl &&\n required.shift === press.shift &&\n required.alt === press.alt &&\n required.meta === press.meta\n )\n}\n","/**\n * Idleness as a bundle: `idle` in the Model, flipped by activity and\n * silence. While active, activity debounced past `timeoutMs` settles to\n * `BecameIdle`; while idle, the first activity wakes to `BecameActive` and\n * the dependency flip restarts the watch. Starts active — a lurker idles\n * when the timeout elapses. The clock is Effect's, so tests drive it with\n * TestClock.\n */\nimport { Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport type * as Update from 'foldkit/update'\nimport { Bundle } from 'foldkit-bundle'\n\nexport const IdleModel = Schema.Struct({ idle: Schema.Boolean })\nexport type IdleModel = typeof IdleModel.Type\n\nexport const IdleMessage = defineMessageUnion({ BecameIdle: {}, BecameActive: {} })\nexport type IdleMessage = typeof IdleMessage.Type\n\nconst activity = (): Stream.Stream<unknown> => {\n if (typeof window === 'undefined') return Stream.empty\n // One stream per sense; the merge fans into the debounce below.\n const senses: Array<Stream.Stream<unknown>> = [\n Stream.fromEventListener(window, 'mousemove'),\n Stream.fromEventListener(window, 'keydown'),\n Stream.fromEventListener(window, 'pointerdown'),\n Stream.fromEventListener(window, 'scroll'),\n ]\n return Stream.mergeAll(senses, { concurrency: 'unbounded' })\n}\n\nexport const Idle = Bundle.make('Idle', {\n Model: IdleModel,\n Message: IdleMessage,\n args: Schema.Struct({\n timeoutMs: Schema.Number.pipe(\n Schema.check(Schema.isGreaterThan(0)),\n Schema.check(Schema.isFinite()),\n ),\n }),\n init: () => ({ model: { idle: false } }),\n update: (model, message) =>\n IdleMessage.match<Update.ReturnWithOutMessage<IdleModel, IdleMessage, never>>(message, {\n // One already in flight when the watch restarts can repeat the state.\n BecameIdle: () => (model.idle ? { model } : { model: { ...model, idle: true } }),\n BecameActive: () => (model.idle ? { model: { ...model, idle: false } } : { model }),\n }),\n subscriptions: ({ timeoutMs }): Subscription.Subscriptions<IdleModel, IdleMessage> =>\n Subscription.make<IdleModel, IdleMessage>()(entry => ({\n watch: entry(\n { idle: Schema.Boolean },\n {\n modelToDependencies: model => ({ idle: model.idle }),\n dependenciesToStream: ({ idle }) =>\n idle\n ? activity().pipe(\n Stream.take(1),\n Stream.map(() => IdleMessage.BecameActive()),\n )\n : // Seeded with subscribe time as last-known-alive: silence from\n // here still settles, so a lurker idles without moving first.\n Stream.concat(Stream.make(null), activity()).pipe(\n Stream.debounce(timeoutMs),\n Stream.map(() => IdleMessage.BecameIdle()),\n ),\n },\n ),\n })),\n})\n","/**\n * Keyboard as an entry — not a bundle, because the parent owns whatever key\n * state it keeps (a held-keys set, a shortcut map, a focused field): this\n * reports presses and releases, including auto-repeat, and the parent\n * stores what matters. Hotkey matching stays application policy.\n */\nimport { Effect, Queue, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport type { KeyPress } from './hotkeys.js'\n\nexport const KeyboardMessage = defineMessageUnion({\n Pressed: {\n key: Schema.String,\n repeat: Schema.Boolean,\n ctrl: Schema.Boolean,\n shift: Schema.Boolean,\n alt: Schema.Boolean,\n meta: Schema.Boolean,\n },\n Released: { key: Schema.String },\n})\nexport type KeyboardMessage = typeof KeyboardMessage.Type\n\n/**\n * Key presses and releases on the window. Without a window the stream is\n * empty instead of throwing. Lift with `Subscription.persistent`, mapping\n * into the parent's Message.\n */\nexport const keyboardEvents = (\n options: {\n /**\n * Whether to cancel a press's default (the page scrolling under an arrow\n * key, the browser's own shortcut), decided in the listener, since a\n * default can only be cancelled before the event finishes dispatching.\n */\n readonly preventDefault?: (press: KeyPress) => boolean\n } = {},\n): Stream.Stream<KeyboardMessage> => {\n if (typeof window === 'undefined') return Stream.empty\n return Stream.callback<KeyboardMessage>(queue =>\n Effect.acquireRelease(\n Effect.sync(() => {\n const down = (event: KeyboardEvent) => {\n const press = KeyboardMessage.Pressed({\n key: event.key,\n repeat: event.repeat,\n ctrl: event.ctrlKey,\n shift: event.shiftKey,\n alt: event.altKey,\n meta: event.metaKey,\n })\n if (options.preventDefault?.(press) === true) event.preventDefault()\n Queue.offerUnsafe(queue, press)\n }\n const up = (event: KeyboardEvent) =>\n Queue.offerUnsafe(queue, KeyboardMessage.Released({ key: event.key }))\n window.addEventListener('keydown', down)\n window.addEventListener('keyup', up)\n return { down, up }\n }),\n ({ down, up }) =>\n Effect.sync(() => {\n window.removeEventListener('keydown', down)\n window.removeEventListener('keyup', up)\n }),\n ),\n )\n}\n","/**\n * Pointer position as an entry — not a bundle, because the parent owns what\n * it keeps (a cursor, a drag anchor, hover state): this reports moves, and\n * the parent stores what matters. Element-scoped pointer needs are Mounts,\n * not this window-level entry.\n */\nimport { Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\n\nexport const PointerMessage = defineMessageUnion({\n Moved: { x: Schema.Number, y: Schema.Number },\n})\nexport type PointerMessage = typeof PointerMessage.Type\n\n/**\n * Pointer moves on the window. Without a window the stream is empty instead\n * of throwing. Lift with `Subscription.persistent`, mapping into the\n * parent's Message.\n */\nexport const pointerEvents = (): Stream.Stream<PointerMessage> => {\n if (typeof window === 'undefined') return Stream.empty\n return Stream.fromEventListener<PointerEvent>(window, 'pointermove').pipe(\n Stream.map(event => PointerMessage.Moved({ x: event.clientX, y: event.clientY })),\n )\n}\n","/**\n * Window scroll position as an entry — not a bundle, because the parent\n * owns what it keeps (a stuck header, a back-to-top gate, restore-on-back):\n * this reports positions, and the parent stores what matters.\n * Element-scoped scrolling needs a Mount, not this window-level entry.\n */\nimport { Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\n\nexport const ScrollMessage = defineMessageUnion({\n Scrolled: { x: Schema.Number, y: Schema.Number },\n})\nexport type ScrollMessage = typeof ScrollMessage.Type\n\n/**\n * Scrolls on the window, in the capture phase so container scrolls (which\n * do not bubble) report too. Without a window the stream is empty instead\n * of throwing. Lift with `Subscription.persistent`, mapping into the\n * parent's Message.\n */\nexport const scrollEvents = (): Stream.Stream<ScrollMessage> => {\n if (typeof window === 'undefined') return Stream.empty\n return Stream.fromEventListener(window, 'scroll', { capture: true }).pipe(\n Stream.map(() => ScrollMessage.Scrolled({ x: window.scrollX, y: window.scrollY })),\n )\n}\n","/**\n * Page visibility as a bundle: `visible` in the Model, read from the\n * document at startup and kept current by `visibilitychange`. A hidden page\n * is the application's cue to pause polling, animations, and realtime\n * streams; the decision stays in application `update`, not here.\n */\nimport { Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\n\nexport const VisibilityModel = Schema.Struct({ visible: Schema.Boolean })\nexport type VisibilityModel = typeof VisibilityModel.Type\n\nexport const VisibilityMessage = defineMessageUnion({ Changed: { visible: Schema.Boolean } })\nexport type VisibilityMessage = typeof VisibilityMessage.Type\n\nconst isVisible = (): boolean =>\n typeof document === 'undefined' || document.visibilityState !== 'hidden'\n\nconst visibilityStream = (): Stream.Stream<VisibilityMessage> => {\n if (typeof document === 'undefined' || typeof document.addEventListener !== 'function') {\n return Stream.empty\n }\n return Stream.concat(\n Stream.make(VisibilityMessage.Changed({ visible: isVisible() })),\n Stream.fromEventListener(document, 'visibilitychange').pipe(\n Stream.map(() => VisibilityMessage.Changed({ visible: isVisible() })),\n ),\n )\n}\n\nexport const Visibility = Bundle.make('Visibility', {\n Model: VisibilityModel,\n Message: VisibilityMessage,\n // No document (SSR): assume visible; the stream corrects on subscribe.\n init: () => ({ model: { visible: isVisible() } }),\n // The stream opens with the state `init` already read.\n update: (model, message) =>\n message.visible === model.visible ? { model } : { model: { visible: message.visible } },\n subscriptions: (): Subscription.Subscriptions<VisibilityModel, VisibilityMessage> =>\n Subscription.make<VisibilityModel, VisibilityMessage>()(() => ({\n changes: Subscription.persistent(visibilityStream()),\n })),\n})\n","/**\n * Window size as a bundle: `{ width, height }` in the Model, read from the\n * window at startup and kept current by one `resize` listener. Breakpoints\n * owns its own listener and derives names; place this one when views need\n * the raw pixels. Placing both doubles resize listeners — pick the one the\n * view reads.\n */\nimport { Effect, Queue, Schema, Stream } from 'effect'\nimport { defineMessageUnion } from 'foldkit/message'\nimport * as Subscription from 'foldkit/subscription'\nimport { Bundle } from 'foldkit-bundle'\nimport { unlessSame } from '../internal.js'\n\nexport const WindowSizeModel = Schema.Struct({ width: Schema.Number, height: Schema.Number })\nexport type WindowSizeModel = typeof WindowSizeModel.Type\n\nexport const WindowSizeMessage = defineMessageUnion({\n Changed: { width: Schema.Number, height: Schema.Number },\n})\nexport type WindowSizeMessage = typeof WindowSizeMessage.Type\n\nconst readSize = (): { readonly width: number; readonly height: number } => ({\n width: window.innerWidth,\n height: window.innerHeight,\n})\n\nconst sizeStream = (): Stream.Stream<WindowSizeMessage> => {\n if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {\n return Stream.empty\n }\n return Stream.unwrap(\n Effect.gen(function* () {\n const queue = yield* Queue.unbounded<WindowSizeMessage>()\n const onResize = () => {\n const size = readSize()\n Effect.runFork(\n Queue.offer(queue, WindowSizeMessage.Changed({ width: size.width, height: size.height })),\n )\n }\n window.addEventListener('resize', onResize)\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => {\n window.removeEventListener('resize', onResize)\n }),\n )\n const size = readSize()\n return Stream.concat(\n Stream.make(WindowSizeMessage.Changed({ width: size.width, height: size.height })),\n Stream.fromQueue(queue),\n )\n }),\n )\n}\n\nexport const WindowSize = Bundle.make('WindowSize', {\n Model: WindowSizeModel,\n Message: WindowSizeMessage,\n init: () => ({ model: { width: 0, height: 0 } }),\n update: (model, message) => ({\n model: unlessSame(model, { width: message.width, height: message.height }),\n }),\n subscriptions: (): Subscription.Subscriptions<WindowSizeModel, WindowSizeMessage> =>\n Subscription.make<WindowSizeModel, WindowSizeMessage>()(() => ({\n changes: Subscription.persistent(sizeStream()),\n })),\n})\n"],"mappings":"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"}