foldkit 0.100.1 → 0.102.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 (217) hide show
  1. package/README.md +3 -2
  2. package/dist/canvas/view.d.ts +1 -1
  3. package/dist/canvas/view.d.ts.map +1 -1
  4. package/dist/canvas/view.js +5 -5
  5. package/dist/command/index.d.ts +71 -0
  6. package/dist/command/index.d.ts.map +1 -1
  7. package/dist/command/index.js +34 -1
  8. package/dist/command/public.d.ts +1 -1
  9. package/dist/command/public.d.ts.map +1 -1
  10. package/dist/command/public.js +1 -1
  11. package/dist/devTools/overlay.d.ts.map +1 -1
  12. package/dist/devTools/overlay.js +156 -149
  13. package/dist/dom/dom.d.ts +8 -11
  14. package/dist/dom/dom.d.ts.map +1 -1
  15. package/dist/dom/dom.js +8 -11
  16. package/dist/dom/elementMovement.d.ts +1 -3
  17. package/dist/dom/elementMovement.d.ts.map +1 -1
  18. package/dist/dom/elementMovement.js +1 -3
  19. package/dist/dom/inert.d.ts +2 -4
  20. package/dist/dom/inert.d.ts.map +1 -1
  21. package/dist/dom/inert.js +2 -4
  22. package/dist/dom/scrollLock.d.ts +2 -2
  23. package/dist/dom/scrollLock.js +2 -2
  24. package/dist/dom/waitForAnimation.d.ts +1 -1
  25. package/dist/dom/waitForAnimation.js +1 -1
  26. package/dist/html/boundary.d.ts +98 -0
  27. package/dist/html/boundary.d.ts.map +1 -0
  28. package/dist/html/boundary.js +176 -0
  29. package/dist/html/childAttribute.d.ts +44 -0
  30. package/dist/html/childAttribute.d.ts.map +1 -0
  31. package/dist/html/childAttribute.js +34 -0
  32. package/dist/html/index.d.ts +70 -23
  33. package/dist/html/index.d.ts.map +1 -1
  34. package/dist/html/index.js +639 -575
  35. package/dist/html/lazy.d.ts +12 -7
  36. package/dist/html/lazy.d.ts.map +1 -1
  37. package/dist/html/lazy.js +30 -11
  38. package/dist/html/public.d.ts +2 -2
  39. package/dist/html/public.d.ts.map +1 -1
  40. package/dist/html/public.js +1 -1
  41. package/dist/html/runtimeSingleton.d.ts +72 -0
  42. package/dist/html/runtimeSingleton.d.ts.map +1 -0
  43. package/dist/html/runtimeSingleton.js +112 -0
  44. package/dist/html/submodel.d.ts +98 -0
  45. package/dist/html/submodel.d.ts.map +1 -0
  46. package/dist/html/submodel.js +190 -0
  47. package/dist/index.d.ts +1 -0
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +1 -0
  50. package/dist/render/render.d.ts +1 -1
  51. package/dist/render/render.js +1 -1
  52. package/dist/runtime/messagePriority.d.ts +5 -1
  53. package/dist/runtime/messagePriority.d.ts.map +1 -1
  54. package/dist/runtime/messagePriority.js +25 -4
  55. package/dist/runtime/runtime.d.ts +11 -11
  56. package/dist/runtime/runtime.d.ts.map +1 -1
  57. package/dist/runtime/runtime.js +118 -63
  58. package/dist/runtime/subscription.d.ts +139 -19
  59. package/dist/runtime/subscription.d.ts.map +1 -1
  60. package/dist/runtime/subscription.js +90 -9
  61. package/dist/submodel/public.d.ts +4 -0
  62. package/dist/submodel/public.d.ts.map +1 -0
  63. package/dist/submodel/public.js +1 -0
  64. package/dist/submodel/submodel.d.ts +32 -0
  65. package/dist/submodel/submodel.d.ts.map +1 -0
  66. package/dist/submodel/submodel.js +1 -0
  67. package/dist/subscription/animationFrame.d.ts +23 -26
  68. package/dist/subscription/animationFrame.d.ts.map +1 -1
  69. package/dist/subscription/animationFrame.js +17 -18
  70. package/dist/subscription/public.d.ts +2 -2
  71. package/dist/subscription/public.d.ts.map +1 -1
  72. package/dist/subscription/public.js +1 -1
  73. package/dist/test/apps/disabledButton.d.ts +4 -5
  74. package/dist/test/apps/disabledButton.d.ts.map +1 -1
  75. package/dist/test/apps/disabledButton.js +16 -16
  76. package/dist/test/scene.d.ts +8 -8
  77. package/dist/test/scene.d.ts.map +1 -1
  78. package/dist/test/scene.js +25 -13
  79. package/dist/test/story.d.ts +15 -8
  80. package/dist/test/story.d.ts.map +1 -1
  81. package/dist/test/story.js +21 -9
  82. package/dist/ui/animation/index.d.ts +30 -14
  83. package/dist/ui/animation/index.d.ts.map +1 -1
  84. package/dist/ui/animation/index.js +9 -19
  85. package/dist/ui/animation/public.d.ts +2 -2
  86. package/dist/ui/animation/public.d.ts.map +1 -1
  87. package/dist/ui/animation/public.js +1 -1
  88. package/dist/ui/calendar/index.d.ts +199 -84
  89. package/dist/ui/calendar/index.d.ts.map +1 -1
  90. package/dist/ui/calendar/index.js +129 -140
  91. package/dist/ui/calendar/public.d.ts +2 -2
  92. package/dist/ui/calendar/public.d.ts.map +1 -1
  93. package/dist/ui/calendar/public.js +1 -1
  94. package/dist/ui/checkbox/index.d.ts +93 -21
  95. package/dist/ui/checkbox/index.d.ts.map +1 -1
  96. package/dist/ui/checkbox/index.js +62 -33
  97. package/dist/ui/checkbox/public.d.ts +2 -2
  98. package/dist/ui/checkbox/public.d.ts.map +1 -1
  99. package/dist/ui/checkbox/public.js +1 -1
  100. package/dist/ui/combobox/multi.d.ts +35 -91
  101. package/dist/ui/combobox/multi.d.ts.map +1 -1
  102. package/dist/ui/combobox/multi.js +34 -17
  103. package/dist/ui/combobox/multiPublic.d.ts +2 -2
  104. package/dist/ui/combobox/multiPublic.d.ts.map +1 -1
  105. package/dist/ui/combobox/multiPublic.js +1 -1
  106. package/dist/ui/combobox/public.d.ts +3 -3
  107. package/dist/ui/combobox/public.d.ts.map +1 -1
  108. package/dist/ui/combobox/public.js +2 -2
  109. package/dist/ui/combobox/shared.d.ts +56 -31
  110. package/dist/ui/combobox/shared.d.ts.map +1 -1
  111. package/dist/ui/combobox/shared.js +333 -322
  112. package/dist/ui/combobox/single.d.ts +46 -93
  113. package/dist/ui/combobox/single.d.ts.map +1 -1
  114. package/dist/ui/combobox/single.js +44 -17
  115. package/dist/ui/datePicker/index.d.ts +256 -48
  116. package/dist/ui/datePicker/index.d.ts.map +1 -1
  117. package/dist/ui/datePicker/index.js +149 -104
  118. package/dist/ui/datePicker/public.d.ts +2 -2
  119. package/dist/ui/datePicker/public.d.ts.map +1 -1
  120. package/dist/ui/datePicker/public.js +1 -1
  121. package/dist/ui/dialog/index.d.ts +95 -39
  122. package/dist/ui/dialog/index.d.ts.map +1 -1
  123. package/dist/ui/dialog/index.js +71 -62
  124. package/dist/ui/dialog/public.d.ts +2 -2
  125. package/dist/ui/dialog/public.d.ts.map +1 -1
  126. package/dist/ui/dialog/public.js +1 -1
  127. package/dist/ui/disclosure/index.d.ts +71 -31
  128. package/dist/ui/disclosure/index.d.ts.map +1 -1
  129. package/dist/ui/disclosure/index.js +57 -62
  130. package/dist/ui/disclosure/public.d.ts +2 -2
  131. package/dist/ui/disclosure/public.d.ts.map +1 -1
  132. package/dist/ui/disclosure/public.js +1 -1
  133. package/dist/ui/dragAndDrop/index.d.ts +385 -103
  134. package/dist/ui/dragAndDrop/index.d.ts.map +1 -1
  135. package/dist/ui/dragAndDrop/index.js +26 -31
  136. package/dist/ui/dragAndDrop/public.d.ts +1 -1
  137. package/dist/ui/dragAndDrop/public.d.ts.map +1 -1
  138. package/dist/ui/dragAndDrop/public.js +1 -1
  139. package/dist/ui/fileDrop/index.d.ts +42 -46
  140. package/dist/ui/fileDrop/index.d.ts.map +1 -1
  141. package/dist/ui/fileDrop/index.js +30 -46
  142. package/dist/ui/fileDrop/public.d.ts +2 -2
  143. package/dist/ui/fileDrop/public.d.ts.map +1 -1
  144. package/dist/ui/fileDrop/public.js +1 -1
  145. package/dist/ui/listbox/multi.d.ts +39 -84
  146. package/dist/ui/listbox/multi.d.ts.map +1 -1
  147. package/dist/ui/listbox/multi.js +38 -20
  148. package/dist/ui/listbox/multiPublic.d.ts +2 -2
  149. package/dist/ui/listbox/multiPublic.d.ts.map +1 -1
  150. package/dist/ui/listbox/multiPublic.js +1 -1
  151. package/dist/ui/listbox/public.d.ts +3 -3
  152. package/dist/ui/listbox/public.d.ts.map +1 -1
  153. package/dist/ui/listbox/public.js +2 -2
  154. package/dist/ui/listbox/shared.d.ts +71 -30
  155. package/dist/ui/listbox/shared.d.ts.map +1 -1
  156. package/dist/ui/listbox/shared.js +319 -296
  157. package/dist/ui/listbox/single.d.ts +57 -85
  158. package/dist/ui/listbox/single.d.ts.map +1 -1
  159. package/dist/ui/listbox/single.js +48 -24
  160. package/dist/ui/menu/index.d.ts +80 -36
  161. package/dist/ui/menu/index.d.ts.map +1 -1
  162. package/dist/ui/menu/index.js +117 -86
  163. package/dist/ui/menu/public.d.ts +2 -2
  164. package/dist/ui/menu/public.d.ts.map +1 -1
  165. package/dist/ui/menu/public.js +1 -1
  166. package/dist/ui/popover/index.d.ts +117 -44
  167. package/dist/ui/popover/index.d.ts.map +1 -1
  168. package/dist/ui/popover/index.js +88 -101
  169. package/dist/ui/popover/public.d.ts +2 -2
  170. package/dist/ui/popover/public.d.ts.map +1 -1
  171. package/dist/ui/popover/public.js +1 -1
  172. package/dist/ui/radioGroup/index.d.ts +122 -45
  173. package/dist/ui/radioGroup/index.d.ts.map +1 -1
  174. package/dist/ui/radioGroup/index.js +111 -72
  175. package/dist/ui/radioGroup/public.d.ts +2 -2
  176. package/dist/ui/radioGroup/public.d.ts.map +1 -1
  177. package/dist/ui/radioGroup/public.js +1 -1
  178. package/dist/ui/slider/index.d.ts +247 -103
  179. package/dist/ui/slider/index.d.ts.map +1 -1
  180. package/dist/ui/slider/index.js +52 -68
  181. package/dist/ui/slider/public.d.ts +2 -2
  182. package/dist/ui/slider/public.d.ts.map +1 -1
  183. package/dist/ui/slider/public.js +1 -1
  184. package/dist/ui/switch/index.d.ts +74 -21
  185. package/dist/ui/switch/index.d.ts.map +1 -1
  186. package/dist/ui/switch/index.js +62 -33
  187. package/dist/ui/switch/public.d.ts +2 -2
  188. package/dist/ui/switch/public.d.ts.map +1 -1
  189. package/dist/ui/switch/public.js +1 -1
  190. package/dist/ui/tabs/index.d.ts +107 -45
  191. package/dist/ui/tabs/index.d.ts.map +1 -1
  192. package/dist/ui/tabs/index.js +99 -81
  193. package/dist/ui/tabs/public.d.ts +2 -2
  194. package/dist/ui/tabs/public.d.ts.map +1 -1
  195. package/dist/ui/tabs/public.js +1 -1
  196. package/dist/ui/toast/index.d.ts +93 -109
  197. package/dist/ui/toast/index.d.ts.map +1 -1
  198. package/dist/ui/toast/index.js +16 -29
  199. package/dist/ui/toast/schema.d.ts +15 -4
  200. package/dist/ui/toast/schema.d.ts.map +1 -1
  201. package/dist/ui/toast/schema.js +11 -4
  202. package/dist/ui/toast/update.d.ts +36 -18
  203. package/dist/ui/toast/update.d.ts.map +1 -1
  204. package/dist/ui/toast/update.js +33 -14
  205. package/dist/ui/tooltip/index.d.ts +94 -42
  206. package/dist/ui/tooltip/index.d.ts.map +1 -1
  207. package/dist/ui/tooltip/index.js +64 -73
  208. package/dist/ui/tooltip/public.d.ts +2 -2
  209. package/dist/ui/tooltip/public.d.ts.map +1 -1
  210. package/dist/ui/tooltip/public.js +1 -1
  211. package/dist/ui/virtualList/index.d.ts +63 -80
  212. package/dist/ui/virtualList/index.d.ts.map +1 -1
  213. package/dist/ui/virtualList/index.js +22 -49
  214. package/dist/ui/virtualList/public.d.ts +2 -2
  215. package/dist/ui/virtualList/public.d.ts.map +1 -1
  216. package/dist/ui/virtualList/public.js +1 -1
  217. package/package.json +1 -1
@@ -1,23 +1,143 @@
1
- import { type Equivalence, type Schema, type Stream } from 'effect';
2
- type ResolveMessage<T> = T extends Schema.Top ? Schema.Schema.Type<T> : T;
3
- /** A reactive binding between Model state and a long-running stream of Messages. */
4
- export type Subscription<Model, Message, StreamDeps, Resources = never> = {
5
- readonly modelToDependencies: (model: Model) => StreamDeps;
6
- readonly equivalence?: Equivalence.Equivalence<StreamDeps>;
7
- readonly dependenciesToStream: (deps: StreamDeps, readDependencies: () => StreamDeps) => Stream.Stream<ResolveMessage<Message>, never, Resources>;
1
+ import { type Equivalence, Schema, Stream } from 'effect';
2
+ type SubscriptionBrand = {
3
+ readonly __subscription: never;
8
4
  };
9
- type SubscriptionConfig<Model, Message, StreamDeps, Resources = never> = {
10
- readonly schema: Schema.Schema<StreamDeps>;
11
- } & Subscription<Model, Message, StreamDeps, Resources>;
12
- /** A record of named subscription configurations, keyed by dependency field name. */
13
- export type Subscriptions<Model, Message, SubscriptionDependencies extends Schema.Struct<any>, Resources = never> = {
14
- readonly [K in keyof Schema.Schema.Type<SubscriptionDependencies>]: SubscriptionConfig<Model, Message, Schema.Schema.Type<SubscriptionDependencies>[K], Resources>;
5
+ type DependenciesSchema<Dependencies> = Schema.Schema<Dependencies> & {
6
+ readonly fields: Schema.Struct.Fields;
15
7
  };
16
- /** Creates type-safe subscription configurations from a dependency schema. */
17
- export declare const makeSubscriptions: <SubscriptionDependencies extends Schema.Struct<any>>(SubscriptionDependencies: SubscriptionDependencies) => <Model, Message, Resources = never>(configs: { readonly [K in keyof Schema.Schema.Type<SubscriptionDependencies>]: {
18
- readonly modelToDependencies: (model: Model) => Schema.Schema.Type<SubscriptionDependencies>[K];
19
- readonly equivalence?: Equivalence.Equivalence<Schema.Schema.Type<SubscriptionDependencies>[K]> | undefined;
20
- readonly dependenciesToStream: (deps: Schema.Schema.Type<SubscriptionDependencies>[K], readDependencies: () => Schema.Schema.Type<SubscriptionDependencies>[K]) => Stream.Stream<ResolveMessage<Message>, never, Resources>;
21
- }; }) => Subscriptions<Model, Message, SubscriptionDependencies, Resources>;
8
+ type EntryWithoutKeepAlive<Model, Message, Dependencies, Services> = {
9
+ readonly dependenciesSchema: DependenciesSchema<Dependencies>;
10
+ readonly modelToDependencies: (model: Model) => Dependencies;
11
+ readonly keepAliveEquivalence?: never;
12
+ readonly dependenciesToStream: (dependencies: Dependencies) => Stream.Stream<Message, never, Services>;
13
+ };
14
+ type EntryWithKeepAlive<Model, Message, Dependencies, Services> = {
15
+ readonly dependenciesSchema: DependenciesSchema<Dependencies>;
16
+ readonly modelToDependencies: (model: Model) => Dependencies;
17
+ readonly keepAliveEquivalence: Equivalence.Equivalence<Dependencies>;
18
+ readonly dependenciesToStream: (dependencies: Dependencies, readDependencies: () => Dependencies) => Stream.Stream<Message, never, Services>;
19
+ };
20
+ type Entry<Model, Message, Dependencies, Services = never> = EntryWithoutKeepAlive<Model, Message, Dependencies, Services> | EntryWithKeepAlive<Model, Message, Dependencies, Services>;
21
+ /**
22
+ * A single subscription entry produced by `Subscription.make`,
23
+ * `Subscription.lift`, or `Subscription.aggregate`. The brand field is
24
+ * `never`, so application code cannot manually construct a `Subscription`
25
+ * value: it must go through one of those constructors (or a helper like
26
+ * `Subscription.persistent` that returns an entry shape, then through
27
+ * `make`).
28
+ *
29
+ * Two variants by `keepAliveEquivalence` presence:
30
+ *
31
+ * - Without `keepAliveEquivalence` (the common case), every Model change recomputes
32
+ * the dependencies. Equivalent dependencies leave the Stream alone; any
33
+ * change tears it down and restarts. `dependenciesToStream` takes a single
34
+ * argument: the latest dependencies.
35
+ * - With `keepAliveEquivalence` (an escape hatch), Model changes that the
36
+ * equivalence treats as equal leave the Stream running, but the running
37
+ * Stream can still read the latest dependencies via the second
38
+ * `readDependencies` argument. Use this when the Stream needs mid-flight
39
+ * access to data that changes often but shouldn't trigger restarts
40
+ * (Foldkit UI's `Ui.DragAndDrop.autoScroll` reading the latest pointer
41
+ * `clientY` each rAF tick is the canonical example).
42
+ *
43
+ * `dependenciesSchema` must be a `Schema.Struct` so every dependency is
44
+ * explicitly named at the schema level.
45
+ */
46
+ export type Subscription<Model, Message, Dependencies, Services = never> = Entry<Model, Message, Dependencies, Services> & SubscriptionBrand;
47
+ /** A record of named Subscriptions keyed by dependency field name. */
48
+ export type Subscriptions<Model, Message, Services = never> = Readonly<Record<string, Subscription<Model, Message, any, Services>>>;
49
+ /**
50
+ * Callbacks for a subscription entry without `keepAliveEquivalence`. Dependencies
51
+ * are inferred from the field map passed to `entry`.
52
+ */
53
+ type EntryCallbacksWithoutKeepAlive<Model, Message, Dependencies, Services> = {
54
+ readonly modelToDependencies: (model: Model) => Dependencies;
55
+ readonly keepAliveEquivalence?: never;
56
+ readonly dependenciesToStream: (dependencies: Dependencies) => Stream.Stream<Message, never, Services>;
57
+ };
58
+ /**
59
+ * Callbacks for a subscription entry with `keepAliveEquivalence`. Dependencies
60
+ * are inferred from the field map passed to `entry`.
61
+ */
62
+ type EntryCallbacksWithKeepAlive<Model, Message, Dependencies, Services> = {
63
+ readonly modelToDependencies: (model: Model) => Dependencies;
64
+ readonly keepAliveEquivalence: Equivalence.Equivalence<Dependencies>;
65
+ readonly dependenciesToStream: (dependencies: Dependencies, readDependencies: () => Dependencies) => Stream.Stream<Message, never, Services>;
66
+ };
67
+ /**
68
+ * Builds a single subscription entry from a field map and callbacks.
69
+ *
70
+ * The field map is the same shape you would pass to `S.Struct`. Reading the
71
+ * schema as a positional argument (rather than a property on the entry
72
+ * literal) lets TypeScript fully resolve the `Dependencies` type before
73
+ * contextually typing `modelToDependencies` and `dependenciesToStream`, so
74
+ * destructuring patterns like `({ maybeMapHostId })` are inferred correctly
75
+ * even when the field schemas use transforms (e.g. `S.Option`).
76
+ *
77
+ * Two overloads, one per `keepAliveEquivalence` presence:
78
+ *
79
+ * - Without `keepAliveEquivalence`, `dependenciesToStream` takes a single
80
+ * `dependencies` argument.
81
+ * - With `keepAliveEquivalence`, `dependenciesToStream` also receives a
82
+ * `readDependencies` thunk for accessing the latest value while the Stream
83
+ * stays running across Model changes the equivalence accepts as equal.
84
+ */
85
+ export type EntryBuilder<Model, Message, Services> = <const Fields extends Schema.Struct.Fields, Callbacks extends EntryCallbacksWithoutKeepAlive<Model, Message, Schema.Struct.Type<Fields>, Services> | EntryCallbacksWithKeepAlive<Model, Message, Schema.Struct.Type<Fields>, Services>>(fields: Fields, callbacks: Callbacks) => Callbacks extends {
86
+ readonly keepAliveEquivalence: Equivalence.Equivalence<any>;
87
+ } ? EntryWithKeepAlive<Model, Message, Schema.Struct.Type<Fields>, Services> : EntryWithoutKeepAlive<Model, Message, Schema.Struct.Type<Fields>, Services>;
88
+ /**
89
+ * Declares a Subscriptions record. The Model, Message, and optional Services
90
+ * generics are provided up front; the entries record follows, built from
91
+ * calls to the `entry` builder passed into the inner function.
92
+ *
93
+ * Reach for `Subscription.aggregate` to combine multiple records, and
94
+ * `Subscription.lift` to translate a child Submodel's record into a parent
95
+ * context.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * Subscription.make<Model, Message>()(entry => ({
100
+ * tick: entry(
101
+ * { isRunning: S.Boolean },
102
+ * {
103
+ * modelToDependencies: model => ({ isRunning: model.isRunning }),
104
+ * dependenciesToStream: ({ isRunning }) =>
105
+ * Stream.when(..., Effect.sync(() => isRunning)),
106
+ * },
107
+ * ),
108
+ * }))
109
+ * ```
110
+ */
111
+ export declare const make: <Model, Message, Services = never>() => <Entries extends Readonly<Record<string, Entry<Model, Message, any, Services>>>>(build: (entry: EntryBuilder<Model, Message, Services>) => Entries) => { readonly [K in keyof Entries]: Entries[K] & SubscriptionBrand; };
112
+ /**
113
+ * Combines multiple Subscriptions records into one. Throws on duplicate
114
+ * keys so a misconfigured aggregate fails loudly at startup rather than
115
+ * silently overriding.
116
+ */
117
+ export declare const aggregate: <Model, Message, Services = never>() => (...records: ReadonlyArray<Subscriptions<Model, Message, Services>>) => Subscriptions<Model, Message, Services>;
118
+ /**
119
+ * Wraps a Stream as a Subscription entry whose lifecycle is independent of
120
+ * the Model. The Stream runs for the lifetime of the Subscriptions record;
121
+ * no Model change tears it down or restarts it. Use for any Stream whose
122
+ * work doesn't depend on Model state, such as system theme listeners,
123
+ * viewport width observers, or route-independent timers.
124
+ *
125
+ * Returns an entry shape, not a branded Subscription. Pass it into `make`
126
+ * as an entry value.
127
+ */
128
+ export declare const persistent: <Message, Services = never>(stream: Stream.Stream<Message, never, Services>) => EntryWithoutKeepAlive<unknown, Message, Record<string, never>, Services>;
129
+ type ChildModelOf<ChildSubscriptions> = ChildSubscriptions[keyof ChildSubscriptions] extends Subscription<infer ChildModel, any, any, any> ? ChildModel : never;
130
+ type ChildMessageOf<ChildSubscriptions> = ChildSubscriptions[keyof ChildSubscriptions] extends Subscription<any, infer ChildMessage, any, any> ? ChildMessage : never;
131
+ /**
132
+ * Lifts a record of child Subscriptions into a parent's Model and Message
133
+ * context, applying a Model accessor and a Message wrapper uniformly to
134
+ * every entry. Per-entry dependency types, schemas, and `keepAliveEquivalence`
135
+ * settings are preserved; each lifted entry's variant (with or without
136
+ * `readDependencies`) matches its source entry's.
137
+ */
138
+ export declare const lift: <Subscriptions extends Readonly<Record<string, Subscription<any, any, any, any>>>>(subscriptions: Subscriptions) => <ParentModel, ParentMessage>(config: {
139
+ readonly toChildModel: (parentModel: ParentModel) => ChildModelOf<Subscriptions>;
140
+ readonly toParentMessage: (message: ChildMessageOf<Subscriptions>) => ParentMessage;
141
+ }) => { readonly [K in keyof Subscriptions]: Subscriptions[K] extends Subscription<any, any, infer Dependencies, infer Services> ? Subscription<ParentModel, ParentMessage, Dependencies, Services> : never; };
22
142
  export {};
23
143
  //# sourceMappingURL=subscription.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../src/runtime/subscription.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAU,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE3E,KAAK,cAAc,CAAC,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAEzE,oFAAoF;AACpF,MAAM,MAAM,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,GAAG,KAAK,IAAI;IACxE,QAAQ,CAAC,mBAAmB,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,UAAU,CAAA;IAC1D,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,WAAW,CAAC,UAAU,CAAC,CAAA;IAC1D,QAAQ,CAAC,oBAAoB,EAAE,CAC7B,IAAI,EAAE,UAAU,EAChB,gBAAgB,EAAE,MAAM,UAAU,KAC/B,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;CAC9D,CAAA;AAED,KAAK,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,GAAG,KAAK,IAAI;IACvE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;CAC3C,GAAG,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;AAEvD,qFAAqF;AACrF,MAAM,MAAM,aAAa,CACvB,KAAK,EACL,OAAO,EACP,wBAAwB,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EACnD,SAAS,GAAG,KAAK,IACf;IACF,QAAQ,EAAE,CAAC,IAAI,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,GAAG,kBAAkB,CACpF,KAAK,EACL,OAAO,EACP,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,EAC/C,SAAS,CACV;CACF,CAAA;AAED,8EAA8E;AAC9E,eAAO,MAAM,iBAAiB,GAC3B,wBAAwB,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAClD,0BAA0B,wBAAwB,MAEnD,KAAK,EAAE,OAAO,EAAE,SAAS,GAAG,KAAK,EAAE,SAAS,EAC3C,QAAQ,EAAE,CAAC,IAAI,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,GAAG;IAClE,QAAQ,CAAC,mBAAmB,EAAE,CAC5B,KAAK,EAAE,KAAK,KACT,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAA;IACpD,QAAQ,CAAC,WAAW,CAAC,EACjB,WAAW,CAAC,WAAW,CACrB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAChD,GACD,SAAS,CAAA;IACb,QAAQ,CAAC,oBAAoB,EAAE,CAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,EACrD,gBAAgB,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,KACpE,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;CAC9D,GACF,KAAG,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,wBAAwB,EAAE,SAAS,CAelE,CAAA"}
1
+ {"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../src/runtime/subscription.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,92 @@
1
- import { Record } from 'effect';
2
- /** Creates type-safe subscription configurations from a dependency schema. */
3
- export const makeSubscriptions = (SubscriptionDependencies) => (configs) =>
1
+ import { Record, Schema, Stream } from 'effect';
2
+ /**
3
+ * Declares a Subscriptions record. The Model, Message, and optional Services
4
+ * generics are provided up front; the entries record follows, built from
5
+ * calls to the `entry` builder passed into the inner function.
6
+ *
7
+ * Reach for `Subscription.aggregate` to combine multiple records, and
8
+ * `Subscription.lift` to translate a child Submodel's record into a parent
9
+ * context.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * Subscription.make<Model, Message>()(entry => ({
14
+ * tick: entry(
15
+ * { isRunning: S.Boolean },
16
+ * {
17
+ * modelToDependencies: model => ({ isRunning: model.isRunning }),
18
+ * dependenciesToStream: ({ isRunning }) =>
19
+ * Stream.when(..., Effect.sync(() => isRunning)),
20
+ * },
21
+ * ),
22
+ * }))
23
+ * ```
24
+ */
25
+ export const make = () => (build) => {
26
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
27
+ const entryBuilder = ((fields, callbacks) => ({
28
+ dependenciesSchema: Schema.Struct(fields),
29
+ ...callbacks,
30
+ }));
31
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
32
+ return build(entryBuilder);
33
+ };
34
+ /**
35
+ * Combines multiple Subscriptions records into one. Throws on duplicate
36
+ * keys so a misconfigured aggregate fails loudly at startup rather than
37
+ * silently overriding.
38
+ */
39
+ export const aggregate = () => (...records) => {
40
+ const result = {};
41
+ for (const record of records) {
42
+ for (const key of Object.keys(record)) {
43
+ if (key in result) {
44
+ throw new Error(`Subscription.aggregate: duplicate key "${key}" across records`);
45
+ }
46
+ result[key] = record[key];
47
+ }
48
+ }
49
+ return result;
50
+ };
51
+ /**
52
+ * Wraps a Stream as a Subscription entry whose lifecycle is independent of
53
+ * the Model. The Stream runs for the lifetime of the Subscriptions record;
54
+ * no Model change tears it down or restarts it. Use for any Stream whose
55
+ * work doesn't depend on Model state, such as system theme listeners,
56
+ * viewport width observers, or route-independent timers.
57
+ *
58
+ * Returns an entry shape, not a branded Subscription. Pass it into `make`
59
+ * as an entry value.
60
+ */
61
+ export const persistent = (stream) => ({
62
+ dependenciesSchema: Schema.Struct({}),
63
+ modelToDependencies: () => ({}),
64
+ dependenciesToStream: () => stream,
65
+ });
66
+ /**
67
+ * Lifts a record of child Subscriptions into a parent's Model and Message
68
+ * context, applying a Model accessor and a Message wrapper uniformly to
69
+ * every entry. Per-entry dependency types, schemas, and `keepAliveEquivalence`
70
+ * settings are preserved; each lifted entry's variant (with or without
71
+ * `readDependencies`) matches its source entry's.
72
+ */
73
+ export const lift = (subscriptions) => (config) =>
4
74
  /* eslint-disable @typescript-eslint/consistent-type-assertions */
5
- Record.map(configs, ({ modelToDependencies, equivalence, dependenciesToStream }, key) => ({
6
- schema: SubscriptionDependencies.fields[key],
7
- modelToDependencies,
8
- equivalence,
9
- dependenciesToStream,
10
- }));
75
+ Record.map(subscriptions, subscription => {
76
+ const modelToDependencies = (parentModel) => subscription.modelToDependencies(config.toChildModel(parentModel));
77
+ const wrapStream = (stream) => Stream.map(stream, config.toParentMessage);
78
+ if (subscription.keepAliveEquivalence !== undefined) {
79
+ return {
80
+ dependenciesSchema: subscription.dependenciesSchema,
81
+ modelToDependencies,
82
+ keepAliveEquivalence: subscription.keepAliveEquivalence,
83
+ dependenciesToStream: (dependencies, readDependencies) => wrapStream(subscription.dependenciesToStream(dependencies, readDependencies)),
84
+ };
85
+ }
86
+ return {
87
+ dependenciesSchema: subscription.dependenciesSchema,
88
+ modelToDependencies,
89
+ dependenciesToStream: (dependencies) => wrapStream(subscription.dependenciesToStream(dependencies)),
90
+ };
91
+ });
11
92
  /* eslint-enable @typescript-eslint/consistent-type-assertions */
@@ -0,0 +1,4 @@
1
+ export { defineView } from '../html/submodel.js';
2
+ export type { SubmodelConfig as Config, SubmodelView as View, } from '../html/submodel.js';
3
+ export type { Reflect, Reflect2 } from './submodel.js';
4
+ //# sourceMappingURL=public.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/submodel/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,YAAY,EACV,cAAc,IAAI,MAAM,EACxB,YAAY,IAAI,IAAI,GACrB,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1 @@
1
+ export { defineView } from '../html/submodel.js';
@@ -0,0 +1,32 @@
1
+ /** Data-first / data-last signature for a `reflect*` setter built with
2
+ * `Function.dual`.
3
+ *
4
+ * A `reflect*` helper conforms a Submodel to a value that originated
5
+ * outside it (a URL, a server push, restored storage, a sibling field),
6
+ * without emitting an OutMessage. It is the inbound complement to
7
+ * OutMessage's outbound direction: the world is the source of truth, so
8
+ * the Submodel mirrors it silently and never announces the change back.
9
+ *
10
+ * Being dual, it reads two ways. Data-first sets the field and returns the
11
+ * model; data-last returns `(model) => model`, which slots point-free into
12
+ * an `evo` callback:
13
+ *
14
+ * ```ts
15
+ * // data-first
16
+ * const next = ColorListbox.reflectSelectedItem(model.colors, fromUrl)
17
+ * // data-last, point-free in evo
18
+ * evo(model, { colors: ColorListbox.reflectSelectedItem(fromUrl) })
19
+ * ```
20
+ */
21
+ export type Reflect<Model, Value> = {
22
+ (model: Model, value: Value): Model;
23
+ (value: Value): (model: Model) => Model;
24
+ };
25
+ /** Two-argument variant of {@link Reflect}, for setters that resolve a
26
+ * value against a companion argument (e.g. `Tabs.reflectSelectedTab(value,
27
+ * options)`, which finds the value's index in `options`). */
28
+ export type Reflect2<Model, A, B> = {
29
+ (model: Model, a: A, b: B): Model;
30
+ (a: A, b: B): (model: Model) => Model;
31
+ };
32
+ //# sourceMappingURL=submodel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"submodel.d.ts","sourceRoot":"","sources":["../../src/submodel/submodel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,OAAO,CAAC,KAAK,EAAE,KAAK,IAAI;IAClC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,CAAA;IACnC,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,KAAK,CAAA;CACxC,CAAA;AAED;;8DAE8D;AAC9D,MAAM,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,IAAI;IAClC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,CAAA;IACjC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,KAAK,CAAA;CACtC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,4 @@
1
- import { Stream } from 'effect';
1
+ import { Schema as S, Stream } from 'effect';
2
2
  /**
3
3
  * Configuration for the `animationFrame` Subscription helper.
4
4
  *
@@ -13,41 +13,38 @@ export type AnimationFrameConfig<Model, Message> = Readonly<{
13
13
  toMessage: (deltaTime: number) => Message;
14
14
  }>;
15
15
  /**
16
- * The subscription record returned by `animationFrame`. Shape matches the
17
- * `{ modelToDependencies, dependenciesToStream }` form expected by
18
- * `Subscription.makeSubscriptions` field configs.
19
- */
20
- export type AnimationFrameSubscription<Model, Message> = Readonly<{
21
- modelToDependencies: (model: Model) => boolean;
22
- dependenciesToStream: (isActive: boolean) => Stream.Stream<Message>;
23
- }>;
24
- /**
25
- * Build a Subscription field config that emits a Message on every
16
+ * Build a Subscription that emits a Message on every
26
17
  * `requestAnimationFrame` tick, with the inter-frame delta in milliseconds.
27
18
  *
28
- * Pair with `S.Boolean` in the `SubscriptionDependencies` schema:
29
- *
30
19
  * @example
31
20
  * ```typescript
32
- * const SubscriptionDependencies = S.Struct({
33
- * frame: S.Boolean,
34
- * })
35
- *
36
- * const subscriptions = Subscription.makeSubscriptions(SubscriptionDependencies)<
37
- * Model,
38
- * Message
39
- * >({
21
+ * const subscriptions = Subscription.make<Model, Message>()(_entry => ({
40
22
  * frame: Subscription.animationFrame({
41
23
  * isActive: model => model.isPlaying,
42
24
  * toMessage: deltaTime => Tick({ deltaTime }),
43
25
  * }),
44
- * })
26
+ * }))
45
27
  * ```
46
28
  *
47
29
  * The browser pauses `requestAnimationFrame` when the tab is hidden, so
48
- * `deltaTime` may spike on the first frame after the tab regains focus.
49
- * Clamp it in the update function if your simulation is sensitive to large
50
- * jumps.
30
+ * `deltaTime` may spike to several seconds on the first frame after the
31
+ * tab regains focus. If your `update` function multiplies `deltaTime`
32
+ * against motion or physics, cap it to a reasonable maximum (32ms is
33
+ * typical) before using it. Otherwise a multi-second `deltaTime` can send
34
+ * moving objects flying across the screen in one frame.
35
+ *
36
+ * Returns an entry shape, not a branded Subscription. Pass it into
37
+ * `Subscription.make` as an entry value.
51
38
  */
52
- export declare const animationFrame: <Model, Message>(config: AnimationFrameConfig<Model, Message>) => AnimationFrameSubscription<Model, Message>;
39
+ export declare const animationFrame: <Model, Message>(config: AnimationFrameConfig<Model, Message>) => {
40
+ dependenciesSchema: S.Struct<{
41
+ readonly isActive: S.Boolean;
42
+ }>;
43
+ modelToDependencies: (model: Model) => {
44
+ isActive: boolean;
45
+ };
46
+ dependenciesToStream: ({ isActive }: {
47
+ readonly isActive: boolean;
48
+ }) => Stream.Stream<Message, never, never>;
49
+ };
53
50
  //# sourceMappingURL=animationFrame.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"animationFrame.d.ts","sourceRoot":"","sources":["../../src/subscription/animationFrame.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE9C;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,CAAC,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;IAC1D,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAA;IACnC,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAA;CAC1C,CAAC,CAAA;AAEF;;;;GAIG;AACH,MAAM,MAAM,0BAA0B,CAAC,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;IAChE,mBAAmB,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAA;IAC9C,oBAAoB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;CACpE,CAAC,CAAA;AA2BF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,EAAE,OAAO,EAC3C,QAAQ,oBAAoB,CAAC,KAAK,EAAE,OAAO,CAAC,KAC3C,0BAA0B,CAAC,KAAK,EAAE,OAAO,CAO1C,CAAA"}
1
+ {"version":3,"file":"animationFrame.d.ts","sourceRoot":"","sources":["../../src/subscription/animationFrame.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE3D;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,CAAC,KAAK,EAAE,OAAO,IAAI,QAAQ,CAAC;IAC1D,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAA;IACnC,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAA;CAC1C,CAAC,CAAA;AA2BF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,EAAE,OAAO,EAC3C,QAAQ,oBAAoB,CAAC,KAAK,EAAE,OAAO,CAAC;;;;iCAGf,KAAK;;;yCAGG;QAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;KAAE;CAKnE,CAAA"}
@@ -1,4 +1,4 @@
1
- import { Effect, Queue, Stream } from 'effect';
1
+ import { Effect, Queue, Schema as S, Stream } from 'effect';
2
2
  const makeAnimationFrameStream = (toMessage) => Stream.callback(queue => Effect.acquireRelease(Effect.sync(() => {
3
3
  const state = {
4
4
  frameId: 0,
@@ -14,34 +14,33 @@ const makeAnimationFrameStream = (toMessage) => Stream.callback(queue => Effect.
14
14
  return state;
15
15
  }), state => Effect.sync(() => cancelAnimationFrame(state.frameId))).pipe(Effect.flatMap(() => Effect.never)));
16
16
  /**
17
- * Build a Subscription field config that emits a Message on every
17
+ * Build a Subscription that emits a Message on every
18
18
  * `requestAnimationFrame` tick, with the inter-frame delta in milliseconds.
19
19
  *
20
- * Pair with `S.Boolean` in the `SubscriptionDependencies` schema:
21
- *
22
20
  * @example
23
21
  * ```typescript
24
- * const SubscriptionDependencies = S.Struct({
25
- * frame: S.Boolean,
26
- * })
27
- *
28
- * const subscriptions = Subscription.makeSubscriptions(SubscriptionDependencies)<
29
- * Model,
30
- * Message
31
- * >({
22
+ * const subscriptions = Subscription.make<Model, Message>()(_entry => ({
32
23
  * frame: Subscription.animationFrame({
33
24
  * isActive: model => model.isPlaying,
34
25
  * toMessage: deltaTime => Tick({ deltaTime }),
35
26
  * }),
36
- * })
27
+ * }))
37
28
  * ```
38
29
  *
39
30
  * The browser pauses `requestAnimationFrame` when the tab is hidden, so
40
- * `deltaTime` may spike on the first frame after the tab regains focus.
41
- * Clamp it in the update function if your simulation is sensitive to large
42
- * jumps.
31
+ * `deltaTime` may spike to several seconds on the first frame after the
32
+ * tab regains focus. If your `update` function multiplies `deltaTime`
33
+ * against motion or physics, cap it to a reasonable maximum (32ms is
34
+ * typical) before using it. Otherwise a multi-second `deltaTime` can send
35
+ * moving objects flying across the screen in one frame.
36
+ *
37
+ * Returns an entry shape, not a branded Subscription. Pass it into
38
+ * `Subscription.make` as an entry value.
43
39
  */
44
40
  export const animationFrame = (config) => ({
45
- modelToDependencies: model => config.isActive(model),
46
- dependenciesToStream: isActive => Stream.when(makeAnimationFrameStream(config.toMessage), Effect.sync(() => isActive)),
41
+ dependenciesSchema: S.Struct({ isActive: S.Boolean }),
42
+ modelToDependencies: (model) => ({
43
+ isActive: config.isActive(model),
44
+ }),
45
+ dependenciesToStream: ({ isActive }) => Stream.when(makeAnimationFrameStream(config.toMessage), Effect.sync(() => isActive)),
47
46
  });
@@ -1,5 +1,5 @@
1
- export { makeSubscriptions } from '../runtime/subscription.js';
1
+ export { aggregate, lift, make, persistent } from '../runtime/subscription.js';
2
2
  export type { Subscription, Subscriptions } from '../runtime/subscription.js';
3
3
  export { animationFrame } from './animationFrame.js';
4
- export type { AnimationFrameConfig, AnimationFrameSubscription, } from './animationFrame.js';
4
+ export type { AnimationFrameConfig } from './animationFrame.js';
5
5
  //# sourceMappingURL=public.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/subscription/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAE9D,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAE7E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,YAAY,EACV,oBAAoB,EACpB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAA"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/subscription/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AAE9E,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAE7E,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA"}
@@ -1,2 +1,2 @@
1
- export { makeSubscriptions } from '../runtime/subscription.js';
1
+ export { aggregate, lift, make, persistent } from '../runtime/subscription.js';
2
2
  export { animationFrame } from './animationFrame.js';
@@ -19,21 +19,20 @@ export type Model = typeof Model.Type;
19
19
  export declare const ClickedToggle: import("../../schema/index.js").CallableTaggedStruct<"ClickedToggle", {}>;
20
20
  export declare const ClickedSubmit: import("../../schema/index.js").CallableTaggedStruct<"ClickedSubmit", {}>;
21
21
  export declare const GotDialogMessage: import("../../schema/index.js").CallableTaggedStruct<"GotDialogMessage", {
22
- message: S.Union<[import("../../schema/index.js").CallableTaggedStruct<"Opened", {}>, import("../../schema/index.js").CallableTaggedStruct<"Closed", {}>, import("../../schema/index.js").CallableTaggedStruct<"CompletedShowDialog", {}>, import("../../schema/index.js").CallableTaggedStruct<"CompletedCloseDialog", {}>, import("../../schema/index.js").CallableTaggedStruct<"GotAnimationMessage", {
22
+ message: S.Union<[import("../../schema/index.js").CallableTaggedStruct<"RequestedOpen", {}>, import("../../schema/index.js").CallableTaggedStruct<"RequestedClose", {}>, import("../../schema/index.js").CallableTaggedStruct<"CompletedShowDialog", {}>, import("../../schema/index.js").CallableTaggedStruct<"CompletedCloseDialog", {}>, import("../../schema/index.js").CallableTaggedStruct<"GotAnimationMessage", {
23
23
  message: S.Union<[import("../../schema/index.js").CallableTaggedStruct<"Showed", {}>, import("../../schema/index.js").CallableTaggedStruct<"Hid", {}>, import("../../schema/index.js").CallableTaggedStruct<"AdvancedAnimationFrame", {}>, import("../../schema/index.js").CallableTaggedStruct<"EndedAnimation", {}>]>;
24
24
  }>]>;
25
25
  }>;
26
26
  export declare const Message: S.Union<readonly [import("../../schema/index.js").CallableTaggedStruct<"ClickedToggle", {}>, import("../../schema/index.js").CallableTaggedStruct<"ClickedSubmit", {}>, import("../../schema/index.js").CallableTaggedStruct<"GotDialogMessage", {
27
- message: S.Union<[import("../../schema/index.js").CallableTaggedStruct<"Opened", {}>, import("../../schema/index.js").CallableTaggedStruct<"Closed", {}>, import("../../schema/index.js").CallableTaggedStruct<"CompletedShowDialog", {}>, import("../../schema/index.js").CallableTaggedStruct<"CompletedCloseDialog", {}>, import("../../schema/index.js").CallableTaggedStruct<"GotAnimationMessage", {
27
+ message: S.Union<[import("../../schema/index.js").CallableTaggedStruct<"RequestedOpen", {}>, import("../../schema/index.js").CallableTaggedStruct<"RequestedClose", {}>, import("../../schema/index.js").CallableTaggedStruct<"CompletedShowDialog", {}>, import("../../schema/index.js").CallableTaggedStruct<"CompletedCloseDialog", {}>, import("../../schema/index.js").CallableTaggedStruct<"GotAnimationMessage", {
28
28
  message: S.Union<[import("../../schema/index.js").CallableTaggedStruct<"Showed", {}>, import("../../schema/index.js").CallableTaggedStruct<"Hid", {}>, import("../../schema/index.js").CallableTaggedStruct<"AdvancedAnimationFrame", {}>, import("../../schema/index.js").CallableTaggedStruct<"EndedAnimation", {}>]>;
29
29
  }>]>;
30
30
  }>]>;
31
31
  export type Message = typeof Message.Type;
32
32
  export declare const initialModel: Model;
33
33
  export declare const update: (model: Model, message: Message) => readonly [Model, ReadonlyArray<Command.Command<Message>>];
34
- /** Plain view no dialog wrapper. */
34
+ /** Plain view, no dialog wrapper. */
35
35
  export declare const view: (model: Model) => Html;
36
- /** View with submit button inside a dialog's panelContent. */
36
+ /** View with submit button inside a dialog's panel. */
37
37
  export declare const viewWithDialog: (model: Model) => Html;
38
- export declare const viewWithLazyDialog: (model: Model) => Html;
39
38
  //# sourceMappingURL=disabledButton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"disabledButton.d.ts","sourceRoot":"","sources":["../../../src/test/apps/disabledButton.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAExD,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,qBAAqB,CAAA;AAMrD,eAAO,MAAM,KAAK;;;;;;;;;;;;;EAGhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,eAAO,MAAM,aAAa,2EAAqB,CAAA;AAC/C,eAAO,MAAM,aAAa,2EAAqB,CAAA;AAC/C,eAAO,MAAM,gBAAgB;;;;EAE3B,CAAA;AAEF,eAAO,MAAM,OAAO;;;;IAA4D,CAAA;AAChF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,eAAO,MAAM,YAAY,EAAE,KAG1B,CAAA;AAID,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAyBxD,CAAA;AAgBH,sCAAsC;AACtC,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,KAAG,IAUnC,CAAA;AAED,8DAA8D;AAC9D,eAAO,MAAM,cAAc,GAAI,OAAO,KAAK,KAAG,IAe7C,CAAA;AAKD,eAAO,MAAM,kBAAkB,GAAI,OAAO,KAAK,KAAG,IAejD,CAAA"}
1
+ {"version":3,"file":"disabledButton.d.ts","sourceRoot":"","sources":["../../../src/test/apps/disabledButton.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,IAAI,CAAC,EAAE,MAAM,QAAQ,CAAA;AAEhD,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAE,KAAK,IAAI,EAAQ,MAAM,qBAAqB,CAAA;AAMrD,eAAO,MAAM,KAAK;;;;;;;;;;;;;EAGhB,CAAA;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAIrC,eAAO,MAAM,aAAa,2EAAqB,CAAA;AAC/C,eAAO,MAAM,aAAa,2EAAqB,CAAA;AAC/C,eAAO,MAAM,gBAAgB;;;;EAE3B,CAAA;AAEF,eAAO,MAAM,OAAO;;;;IAA4D,CAAA;AAChF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAIzC,eAAO,MAAM,YAAY,EAAE,KAG1B,CAAA;AAID,eAAO,MAAM,MAAM,GACjB,OAAO,KAAK,EACZ,SAAS,OAAO,KACf,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAqBxD,CAAA;AAgBH,qCAAqC;AACrC,eAAO,MAAM,IAAI,GAAI,OAAO,KAAK,KAAG,IAUnC,CAAA;AAED,uDAAuD;AACvD,eAAO,MAAM,cAAc,GAAI,OAAO,KAAK,KAAG,IA2B7C,CAAA"}
@@ -1,4 +1,4 @@
1
- import { Effect, Match as M, Schema as S } from 'effect';
1
+ import { Match as M, Schema as S } from 'effect';
2
2
  import * as Command from '../../command/index.js';
3
3
  import { html } from '../../html/index.js';
4
4
  import { m } from '../../message/index.js';
@@ -28,7 +28,7 @@ export const update = (model, message) => M.value(message).pipe(M.withReturnType
28
28
  const [nextDialog, commands] = Dialog.update(model.dialog, dialogMessage);
29
29
  return [
30
30
  { ...model, dialog: nextDialog },
31
- commands.map(Command.mapEffect(Effect.map(dialogMessage => GotDialogMessage({ message: dialogMessage })))),
31
+ Command.mapMessages(commands, dialogMessage => GotDialogMessage({ message: dialogMessage })),
32
32
  ];
33
33
  },
34
34
  }));
@@ -40,7 +40,7 @@ const submitButton = (isEnabled) => {
40
40
  ...(isEnabled ? [h.OnClick(ClickedSubmit())] : [h.Disabled(true)]),
41
41
  ], ['Submit']);
42
42
  };
43
- /** Plain view no dialog wrapper. */
43
+ /** Plain view, no dialog wrapper. */
44
44
  export const view = (model) => {
45
45
  const h = html();
46
46
  return h.div([], [
@@ -48,24 +48,24 @@ export const view = (model) => {
48
48
  submitButton(model.isEnabled),
49
49
  ]);
50
50
  };
51
- /** View with submit button inside a dialog's panelContent. */
51
+ /** View with submit button inside a dialog's panel. */
52
52
  export const viewWithDialog = (model) => {
53
53
  const h = html();
54
54
  return h.div([], [
55
55
  h.button([h.OnClick(ClickedToggle())], ['Toggle']),
56
- Dialog.view({
56
+ h.submodel({
57
+ slotId: model.dialog.id,
57
58
  model: model.dialog,
58
- toParentMessage: (dialogMessage) => GotDialogMessage({ message: dialogMessage }),
59
- panelContent: submitButton(model.isEnabled),
59
+ view: Dialog.view,
60
+ viewInputs: {
61
+ toView: ({ dialog, backdrop, panel, isVisible }) => h.dialog([...dialog], isVisible
62
+ ? [
63
+ h.div([...backdrop], []),
64
+ h.div([...panel], [submitButton(model.isEnabled)]),
65
+ ]
66
+ : []),
67
+ },
68
+ toParentMessage: message => GotDialogMessage({ message }),
60
69
  }),
61
70
  ]);
62
71
  };
63
- /** View using Dialog.lazy with panelContent passed dynamically. */
64
- const lazyDialogView = Dialog.lazy({});
65
- export const viewWithLazyDialog = (model) => {
66
- const h = html();
67
- return h.div([], [
68
- h.button([h.OnClick(ClickedToggle())], ['Toggle']),
69
- lazyDialogView(model.dialog, (dialogMessage) => GotDialogMessage({ message: dialogMessage }), submitButton(model.isEnabled)),
70
- ]);
71
- };