create-foldkit-app 0.29.0 → 0.29.1

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 (68) hide show
  1. package/dist/templates/base/AGENTS.md +5 -112
  2. package/dist/templates/base/FOLDKIT.md +144 -0
  3. package/dist/templates/examples/api-cache/src/main.ts +36 -40
  4. package/dist/templates/examples/auth/src/main.ts +16 -15
  5. package/dist/templates/examples/auth/src/page/loggedIn/update.ts +10 -11
  6. package/dist/templates/examples/auth/src/page/loggedOut/page/login.ts +44 -55
  7. package/dist/templates/examples/auth/src/page/loggedOut/update.ts +8 -12
  8. package/dist/templates/examples/auth/src/update.ts +67 -73
  9. package/dist/templates/examples/canvas-art/src/main.ts +32 -31
  10. package/dist/templates/examples/charting/src/init.test.ts +6 -3
  11. package/dist/templates/examples/charting/src/init.ts +4 -4
  12. package/dist/templates/examples/charting/src/update.ts +34 -38
  13. package/dist/templates/examples/counter/src/main.ts +13 -13
  14. package/dist/templates/examples/counters/src/counter.ts +9 -8
  15. package/dist/templates/examples/counters/src/main.ts +20 -26
  16. package/dist/templates/examples/crash-view/src/main.ts +5 -3
  17. package/dist/templates/examples/embedding/src/main.ts +11 -9
  18. package/dist/templates/examples/form/src/main.ts +79 -89
  19. package/dist/templates/examples/generative-art/src/main.ts +6 -4
  20. package/dist/templates/examples/generative-art/src/update.ts +26 -23
  21. package/dist/templates/examples/interrupting-commands/src/main.ts +68 -66
  22. package/dist/templates/examples/job-application/src/main.ts +3 -4
  23. package/dist/templates/examples/job-application/src/step/attachments.ts +16 -19
  24. package/dist/templates/examples/job-application/src/step/coverLetter.ts +5 -5
  25. package/dist/templates/examples/job-application/src/step/education/education.ts +12 -17
  26. package/dist/templates/examples/job-application/src/step/education/entry.ts +29 -42
  27. package/dist/templates/examples/job-application/src/step/personalInfo.ts +35 -44
  28. package/dist/templates/examples/job-application/src/step/skills/entry.ts +15 -21
  29. package/dist/templates/examples/job-application/src/step/skills/skills.ts +12 -17
  30. package/dist/templates/examples/job-application/src/step/workHistory/entry.ts +41 -58
  31. package/dist/templates/examples/job-application/src/step/workHistory/workHistory.ts +12 -17
  32. package/dist/templates/examples/job-application/src/update.ts +26 -29
  33. package/dist/templates/examples/kanban/src/main.ts +3 -4
  34. package/dist/templates/examples/kanban/src/update.ts +31 -31
  35. package/dist/templates/examples/managed-resource-layer/src/main.ts +30 -34
  36. package/dist/templates/examples/map/src/main.ts +48 -56
  37. package/dist/templates/examples/pixel-art/src/comparison.bench.test.ts +34 -13
  38. package/dist/templates/examples/pixel-art/src/main.bench.ts +3 -3
  39. package/dist/templates/examples/pixel-art/src/main.ts +3 -4
  40. package/dist/templates/examples/pixel-art/src/update.ts +109 -100
  41. package/dist/templates/examples/query-sync/src/main.ts +27 -25
  42. package/dist/templates/examples/route-transitions/src/main.ts +46 -43
  43. package/dist/templates/examples/route-transitions/src/story.test.ts +11 -11
  44. package/dist/templates/examples/routing/src/main.ts +16 -14
  45. package/dist/templates/examples/routing/src/page/people.ts +23 -28
  46. package/dist/templates/examples/shopping-cart/src/main.ts +41 -42
  47. package/dist/templates/examples/shopping-cart/src/page/products.ts +28 -35
  48. package/dist/templates/examples/slow-warnings/src/main.ts +23 -32
  49. package/dist/templates/examples/snake/src/main.ts +135 -143
  50. package/dist/templates/examples/ssg/src/main.ts +20 -17
  51. package/dist/templates/examples/ssr/src/main.ts +20 -24
  52. package/dist/templates/examples/state-machine/src/main.ts +24 -11
  53. package/dist/templates/examples/state-machine/src/story.test.ts +2 -2
  54. package/dist/templates/examples/stopwatch/src/main.ts +42 -50
  55. package/dist/templates/examples/todo/src/main.ts +152 -160
  56. package/dist/templates/examples/ui-showcase/src/main.ts +53 -54
  57. package/dist/templates/examples/ui-showcase/src/scene.test.ts +2 -2
  58. package/dist/templates/examples/ui-showcase/src/story.test.ts +2 -2
  59. package/dist/templates/examples/ui-showcase/src/ui/init.ts +6 -7
  60. package/dist/templates/examples/ui-showcase/src/ui/update.ts +265 -336
  61. package/dist/templates/examples/view-transitions/src/main.ts +18 -17
  62. package/dist/templates/examples/weather/src/main.ts +33 -40
  63. package/dist/templates/examples/web-components/src/main.ts +14 -17
  64. package/dist/templates/examples/websocket-chat/src/main.ts +44 -50
  65. package/dist/templates/release.json +8 -8
  66. package/dist/templates/rendering/ssg/src/main.ts +20 -17
  67. package/dist/templates/rendering/ssr/src/main.ts +20 -24
  68. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { Array, Match as M, Option, pipe } from 'effect'
2
- import { Command, Update } from 'foldkit'
2
+ import { Update } from 'foldkit'
3
3
  import { evo } from 'foldkit/struct'
4
4
 
5
5
  import { Menu, Tabs } from '@foldkit/ui'
@@ -26,8 +26,6 @@ const isApplicationComplete = (model: Model): boolean =>
26
26
  Education.isComplete(model.education) &&
27
27
  Skills.isComplete(model.skills)
28
28
 
29
- type UpdateReturn = readonly [Model, ReadonlyArray<Command.Command<Message>>]
30
-
31
29
  const toNextStep = (current: Step.Step): Step.Step =>
32
30
  pipe(
33
31
  Step.all,
@@ -94,7 +92,7 @@ const foldStepMenuOutMessage = M.type<Menu.OutMessage<Step.Step>>().pipe(
94
92
  M.tagsExhaustive({
95
93
  Selected:
96
94
  ({ value }) =>
97
- model => [evo(model, { currentStep: () => value }), []],
95
+ model => ({ model: evo(model, { currentStep: () => value }) }),
98
96
  }),
99
97
  )
100
98
 
@@ -111,7 +109,7 @@ const foldStepTabsOutMessage = M.type<Tabs.OutMessage<Step.Step>>().pipe(
111
109
  M.tagsExhaustive({
112
110
  Selected:
113
111
  ({ value }) =>
114
- model => [evo(model, { currentStep: () => value }), []],
112
+ model => ({ model: evo(model, { currentStep: () => value }) }),
115
113
  }),
116
114
  )
117
115
 
@@ -124,7 +122,7 @@ const foldStepTabs = Update.foldChild({
124
122
  })
125
123
 
126
124
  export const update = (model: Model, message: Message) =>
127
- Message.match<UpdateReturn>(message, {
125
+ Message.match<Update.Return<Model, Message>>(message, {
128
126
  GotPersonalInfoMessage: ({ message }) => foldPersonalInfo(model, message),
129
127
 
130
128
  GotWorkHistoryMessage: ({ message }) => foldWorkHistory(model, message),
@@ -141,18 +139,19 @@ export const update = (model: Model, message: Message) =>
141
139
 
142
140
  GotStepTabsMessage: ({ message }) => foldStepTabs(model, message),
143
141
 
144
- NavigatedToStep: ({ step }) => [
145
- evo(model, { currentStep: () => step }),
146
- [],
147
- ],
142
+ NavigatedToStep: ({ step }) => ({
143
+ model: evo(model, { currentStep: () => step }),
144
+ }),
145
+
146
+ ClickedNext: () => ({ model: evo(model, { currentStep: toNextStep }) }),
148
147
 
149
- ClickedNext: () => [evo(model, { currentStep: toNextStep }), []],
148
+ ClickedPrevious: () => ({
149
+ model: evo(model, { currentStep: toPreviousStep }),
150
+ }),
150
151
 
151
- ClickedPrevious: () => [evo(model, { currentStep: toPreviousStep }), []],
152
- ToggledPreview: () => [
153
- evo(model, { isPreviewVisible: isVisible => !isVisible }),
154
- [],
155
- ],
152
+ ToggledPreview: () => ({
153
+ model: evo(model, { isPreviewVisible: isVisible => !isVisible }),
154
+ }),
156
155
 
157
156
  ClickedSubmit: () => {
158
157
  const revealedModel = evo(model, {
@@ -163,21 +162,19 @@ export const update = (model: Model, message: Message) =>
163
162
  isSubmitAttempted: () => true,
164
163
  })
165
164
  if (isApplicationComplete(revealedModel)) {
166
- return [
167
- evo(revealedModel, { submission: () => Submitting() }),
168
- [SubmitApplication()],
169
- ]
165
+ return {
166
+ model: evo(revealedModel, { submission: () => Submitting() }),
167
+ commands: [SubmitApplication()],
168
+ }
170
169
  }
171
- return [revealedModel, []]
170
+ return { model: revealedModel }
172
171
  },
173
172
 
174
- SucceededSubmitApplication: () => [
175
- evo(model, { submission: () => SubmitSuccess() }),
176
- [],
177
- ],
173
+ SucceededSubmitApplication: () => ({
174
+ model: evo(model, { submission: () => SubmitSuccess() }),
175
+ }),
178
176
 
179
- FailedSubmitApplication: ({ error }) => [
180
- evo(model, { submission: () => SubmitError({ error }) }),
181
- [],
182
- ],
177
+ FailedSubmitApplication: ({ error }) => ({
178
+ model: evo(model, { submission: () => SubmitError({ error }) }),
179
+ }),
183
180
  })
@@ -41,16 +41,15 @@ export const init: Runtime.ApplicationInit<Model, Message, Flags> = flags => {
41
41
  onSome: ({ columns }) => columns,
42
42
  })
43
43
 
44
- return [
45
- {
44
+ return {
45
+ model: {
46
46
  columns,
47
47
  dragAndDrop: DragAndDrop.init({ id: 'kanban' }),
48
48
  maybeNewCardColumnId: Option.none(),
49
49
  newCardTitle: '',
50
50
  announcement: '',
51
51
  },
52
- [],
53
- ]
52
+ }
54
53
  }
55
54
 
56
55
  export { Message, Model, subscriptions, update, view }
@@ -1,5 +1,5 @@
1
1
  import { Array, Match as M, Option, String, pipe } from 'effect'
2
- import { Command, Update } from 'foldkit'
2
+ import { Update } from 'foldkit'
3
3
  import { evo } from 'foldkit/struct'
4
4
 
5
5
  import { DragAndDrop } from '@foldkit/ui'
@@ -9,7 +9,7 @@ import { Column } from './domain'
9
9
  import { Message } from './message'
10
10
  import type { Model } from './model'
11
11
 
12
- type UpdateReturn = readonly [Model, ReadonlyArray<Command.Command<Message>>]
12
+ type UpdateReturn = Update.Return<Model, Message>
13
13
 
14
14
  const findCardTitle = (
15
15
  columns: ReadonlyArray<Column.Column>,
@@ -106,22 +106,21 @@ const foldDragAndDropOutMessage: (
106
106
  toContainerId,
107
107
  toIndex,
108
108
  )
109
- return [
110
- evo(model, {
109
+ return {
110
+ model: evo(model, {
111
111
  columns: () => nextColumns,
112
112
  announcement: () =>
113
113
  screenReaderTextForDrop(previousModel, outMessage),
114
114
  }),
115
- [SaveBoard({ columns: nextColumns })],
116
- ]
115
+ commands: [SaveBoard({ columns: nextColumns })],
116
+ }
117
117
  },
118
- Cancelled: () => [
119
- evo(model, {
118
+ Cancelled: () => ({
119
+ model: evo(model, {
120
120
  announcement: () =>
121
121
  screenReaderTextForDrop(previousModel, outMessage),
122
122
  }),
123
- [],
124
- ],
123
+ }),
125
124
  })
126
125
 
127
126
  const foldDragAndDrop = (previousModel: Model) =>
@@ -142,29 +141,31 @@ export const update = (model: Model, message: Message) =>
142
141
  GotDragAndDropMessage: ({ message }) =>
143
142
  foldDragAndDrop(model)(model, message),
144
143
 
145
- ClickedAddCard: ({ columnId }) => [
146
- evo(model, {
144
+ ClickedAddCard: ({ columnId }) => ({
145
+ model: evo(model, {
147
146
  maybeNewCardColumnId: () => Option.some(columnId),
148
147
  newCardTitle: () => '',
149
148
  }),
150
- [FocusAddCardInput()],
151
- ],
149
+ commands: [FocusAddCardInput()],
150
+ }),
152
151
 
153
- ChangedNewCardTitle: ({ value }) => [
154
- evo(model, { newCardTitle: () => value }),
155
- [],
156
- ],
152
+ ChangedNewCardTitle: ({ value }) => ({
153
+ model: evo(model, { newCardTitle: () => value }),
154
+ }),
157
155
 
158
156
  SubmittedNewCard: () =>
159
157
  Option.match(model.maybeNewCardColumnId, {
160
- onNone: () => [model, []],
158
+ onNone: () => ({ model }),
161
159
  onSome: columnId => {
162
160
  const title = String.trim(model.newCardTitle)
163
161
  if (String.isEmpty(title)) {
164
- return [model, []]
162
+ return { model }
165
163
  }
166
164
 
167
- return [model, [GenerateCardId({ columnId: columnId, title: title })]]
165
+ return {
166
+ model,
167
+ commands: [GenerateCardId({ columnId, title })],
168
+ }
168
169
  },
169
170
  }),
170
171
 
@@ -181,25 +182,24 @@ export const update = (model: Model, message: Message) =>
181
182
  })
182
183
  })
183
184
 
184
- return [
185
- evo(model, {
185
+ return {
186
+ model: evo(model, {
186
187
  columns: () => nextColumns,
187
188
  maybeNewCardColumnId: () => Option.none(),
188
189
  newCardTitle: () => '',
189
190
  }),
190
- [SaveBoard({ columns: nextColumns })],
191
- ]
191
+ commands: [SaveBoard({ columns: nextColumns })],
192
+ }
192
193
  },
193
194
 
194
- CancelledNewCard: () => [
195
- evo(model, {
195
+ CancelledNewCard: () => ({
196
+ model: evo(model, {
196
197
  maybeNewCardColumnId: () => Option.none(),
197
198
  newCardTitle: () => '',
198
199
  }),
199
- [],
200
- ],
200
+ }),
201
201
 
202
- CompletedSaveBoard: () => [model, []],
202
+ CompletedSaveBoard: () => ({ model }),
203
203
 
204
- CompletedFocusAddCardInput: () => [model, []],
204
+ CompletedFocusAddCardInput: () => ({ model }),
205
205
  })
@@ -8,7 +8,7 @@ import {
8
8
  Option,
9
9
  Schema as S,
10
10
  } from 'effect'
11
- import { Command, ManagedResource, Runtime } from 'foldkit'
11
+ import { Command, ManagedResource, Runtime, type Update } from 'foldkit'
12
12
  import { Document, Html, HtmlBuilder } from 'foldkit/html'
13
13
  import { defineMessageUnion } from 'foldkit/message'
14
14
  import { ts } from 'foldkit/schema'
@@ -100,54 +100,50 @@ export const Compute = Command.define('Compute', {
100
100
 
101
101
  // UPDATE
102
102
 
103
- type UpdateReturn = readonly [
104
- Model,
105
- ReadonlyArray<Command.Command<Message, never, EngineService>>,
106
- ]
107
-
108
103
  export const update = (model: Model, message: Message) =>
109
- Message.match<UpdateReturn>(message, {
110
- ClickedStartEngine: () => [
111
- evo(model, { engine: () => EngineBooting() }),
112
- [],
113
- ],
104
+ Message.match<Update.Return<Model, Message, EngineService>>(message, {
105
+ ClickedStartEngine: () => ({
106
+ model: evo(model, { engine: () => EngineBooting() }),
107
+ }),
114
108
 
115
- ClickedStopEngine: () => [evo(model, { engine: () => EngineOff() }), []],
109
+ ClickedStopEngine: () => ({
110
+ model: evo(model, { engine: () => EngineOff() }),
111
+ }),
116
112
 
117
- StartedEngine: ({ engineId }) => [
118
- evo(model, { engine: () => EngineReady({ engineId }) }),
119
- [],
120
- ],
113
+ StartedEngine: ({ engineId }) => ({
114
+ model: evo(model, { engine: () => EngineReady({ engineId }) }),
115
+ }),
121
116
 
122
- StoppedEngine: () => [model, []],
117
+ StoppedEngine: () => ({ model }),
123
118
 
124
- FailedStartEngine: ({ reason }) => [
125
- evo(model, { engine: () => EngineFailed({ reason }) }),
126
- [],
127
- ],
119
+ FailedStartEngine: ({ reason }) => ({
120
+ model: evo(model, { engine: () => EngineFailed({ reason }) }),
121
+ }),
128
122
 
129
123
  ClickedCompute: () => {
130
124
  const nextComputeCount = Number.increment(model.computeCount)
131
- return [
132
- evo(model, { computeCount: () => nextComputeCount }),
133
- [Compute({ value: nextComputeCount })],
134
- ]
125
+ return {
126
+ model: evo(model, { computeCount: () => nextComputeCount }),
127
+ commands: [Compute({ value: nextComputeCount })],
128
+ }
135
129
  },
136
130
 
137
- CompletedCompute: ({ result }) => [
138
- evo(model, { maybeSquareResult: () => Option.some(result) }),
139
- [],
140
- ],
131
+ CompletedCompute: ({ result }) => ({
132
+ model: evo(model, { maybeSquareResult: () => Option.some(result) }),
133
+ }),
141
134
 
142
- SkippedCompute: () => [model, []],
135
+ SkippedCompute: () => ({ model }),
143
136
  })
144
137
 
145
138
  // INIT
146
139
 
147
- export const init: Runtime.ApplicationInit<Model, Message> = () => [
148
- { engine: EngineOff(), computeCount: 0, maybeSquareResult: Option.none() },
149
- [],
150
- ]
140
+ export const init: Runtime.ApplicationInit<Model, Message> = () => ({
141
+ model: {
142
+ engine: EngineOff(),
143
+ computeCount: 0,
144
+ maybeSquareResult: Option.none(),
145
+ },
146
+ })
151
147
 
152
148
  // MANAGED RESOURCE
153
149
 
@@ -11,7 +11,7 @@ import {
11
11
  Stream,
12
12
  String,
13
13
  } from 'effect'
14
- import { Command, Mount, Runtime, Subscription } from 'foldkit'
14
+ import { Command, Mount, Runtime, Subscription, type Update } from 'foldkit'
15
15
  import * as Dom from 'foldkit/dom'
16
16
  import type { Document, Html } from 'foldkit/html'
17
17
  import { HtmlBuilder } from 'foldkit/html'
@@ -205,8 +205,6 @@ export const UnlockBodyScroll = Command.define('UnlockBodyScroll', {
205
205
 
206
206
  // UPDATE
207
207
 
208
- type UpdateReturn = readonly [Model, ReadonlyArray<Command.Command<Message>>]
209
-
210
208
  const findLocation = (
211
209
  model: Model,
212
210
  locationId: string,
@@ -214,68 +212,63 @@ const findLocation = (
214
212
  Array.findFirst(model.locations, ({ id }) => Equal.equals(id, locationId))
215
213
 
216
214
  export const update = (model: Model, message: Message) =>
217
- Message.match<UpdateReturn>(message, {
218
- SucceededMountMap: ({ hostId }) => [
219
- evo(model, { maybeMapHostId: () => Option.some(hostId) }),
220
- [],
221
- ],
215
+ Message.match<Update.Return<Model, Message>>(message, {
216
+ SucceededMountMap: ({ hostId }) => ({
217
+ model: evo(model, { maybeMapHostId: () => Option.some(hostId) }),
218
+ }),
222
219
 
223
- FailedMountMap: ({ reason }) => [
224
- evo(model, { maybeMapError: () => Option.some(reason) }),
225
- [],
226
- ],
220
+ FailedMountMap: ({ reason }) => ({
221
+ model: evo(model, { maybeMapError: () => Option.some(reason) }),
222
+ }),
227
223
 
228
- MovedMap: ({ bounds }) => [
229
- evo(model, { maybeBounds: () => Option.some(bounds) }),
230
- [],
231
- ],
224
+ MovedMap: ({ bounds }) => ({
225
+ model: evo(model, { maybeBounds: () => Option.some(bounds) }),
226
+ }),
232
227
 
233
- ClickedMarker: ({ locationId }) => [
234
- evo(model, {
228
+ ClickedMarker: ({ locationId }) => ({
229
+ model: evo(model, {
235
230
  maybeSelectedLocationId: () => Option.some(locationId),
236
231
  }),
237
- [],
238
- ],
232
+ }),
239
233
 
240
234
  ClickedLocation: ({ locationId }) =>
241
235
  Option.match(findLocation(model, locationId), {
242
- onNone: () => [model, []],
243
- onSome: ({ lng, lat }) => [
244
- evo(model, {
236
+ onNone: () => ({ model }),
237
+ onSome: ({ lng, lat }) => ({
238
+ model: evo(model, {
245
239
  maybeSelectedLocationId: () => Option.some(locationId),
246
240
  }),
247
- [
241
+ commands: [
248
242
  FlyTo({
249
243
  maybeHostId: model.maybeMapHostId,
250
- lng: lng,
251
- lat: lat,
244
+ lng,
245
+ lat,
252
246
  zoom: SELECTED_LOCATION_ZOOM,
253
247
  }),
254
248
  ],
255
- ],
249
+ }),
256
250
  }),
257
251
 
258
- UpdatedSearchQuery: ({ value }) => [
259
- evo(model, { searchQuery: () => value }),
260
- [],
261
- ],
252
+ UpdatedSearchQuery: ({ value }) => ({
253
+ model: evo(model, { searchQuery: () => value }),
254
+ }),
262
255
 
263
- ClickedFindMe: () => [
264
- evo(model, { geolocateState: () => GeolocateLocating() }),
265
- [LockBodyScroll(), Geolocate()],
266
- ],
256
+ ClickedFindMe: () => ({
257
+ model: evo(model, { geolocateState: () => GeolocateLocating() }),
258
+ commands: [LockBodyScroll(), Geolocate()],
259
+ }),
267
260
 
268
- DismissedGeolocate: () => [
269
- evo(model, { geolocateState: () => GeolocateIdle() }),
270
- [UnlockBodyScroll()],
271
- ],
261
+ DismissedGeolocate: () => ({
262
+ model: evo(model, { geolocateState: () => GeolocateIdle() }),
263
+ commands: [UnlockBodyScroll()],
264
+ }),
272
265
 
273
- SucceededGeolocate: ({ lng, lat }) => [
274
- evo(model, {
266
+ SucceededGeolocate: ({ lng, lat }) => ({
267
+ model: evo(model, {
275
268
  maybeUserLocation: () => Option.some({ lng, lat }),
276
269
  geolocateState: () => GeolocateIdle(),
277
270
  }),
278
- [
271
+ commands: [
279
272
  UnlockBodyScroll(),
280
273
  FlyTo({
281
274
  maybeHostId: model.maybeMapHostId,
@@ -284,24 +277,23 @@ export const update = (model: Model, message: Message) =>
284
277
  zoom: USER_LOCATION_ZOOM,
285
278
  }),
286
279
  ],
287
- ],
280
+ }),
288
281
 
289
- FailedGeolocate: ({ reason }) => [
290
- evo(model, { geolocateState: () => GeolocateFailed({ reason }) }),
291
- [],
292
- ],
282
+ FailedGeolocate: ({ reason }) => ({
283
+ model: evo(model, { geolocateState: () => GeolocateFailed({ reason }) }),
284
+ }),
293
285
 
294
- SucceededFlyTo: () => [model, []],
295
- FailedFlyTo: () => [model, []],
296
- CompletedFocusSearchInput: () => [model, []],
297
- CompletedLockBodyScroll: () => [model, []],
298
- CompletedUnlockBodyScroll: () => [model, []],
286
+ SucceededFlyTo: () => ({ model }),
287
+ FailedFlyTo: () => ({ model }),
288
+ CompletedFocusSearchInput: () => ({ model }),
289
+ CompletedLockBodyScroll: () => ({ model }),
290
+ CompletedUnlockBodyScroll: () => ({ model }),
299
291
  })
300
292
 
301
293
  // INIT
302
294
 
303
- export const init: Runtime.ApplicationInit<Model, Message> = () => [
304
- {
295
+ export const init: Runtime.ApplicationInit<Model, Message> = () => ({
296
+ model: {
305
297
  locations: featuredLocations,
306
298
  searchQuery: '',
307
299
  maybeMapHostId: Option.none(),
@@ -311,8 +303,8 @@ export const init: Runtime.ApplicationInit<Model, Message> = () => [
311
303
  maybeUserLocation: Option.none(),
312
304
  geolocateState: GeolocateIdle(),
313
305
  },
314
- [FocusSearchInput()],
315
- ]
306
+ commands: [FocusSearchInput()],
307
+ })
316
308
 
317
309
  // MAP MOUNT
318
310
 
@@ -60,8 +60,9 @@ const buildFoldkitHistory = (steps: number): Model => {
60
60
  for (let i = 0; i < steps; i++) {
61
61
  const x = i % GRID_SIZE
62
62
  const y = Math.floor(i / GRID_SIZE) % GRID_SIZE
63
- ;[model] = update(model, Message.PressedCell({ x, y }))
64
- ;[model] = update(model, Message.ReleasedMouse())
63
+ const pressedCell = update(model, Message.PressedCell({ x, y }))
64
+ const releasedMouse = update(pressedCell.model, Message.ReleasedMouse())
65
+ model = releasedMouse.model
65
66
  }
66
67
  return model
67
68
  }
@@ -112,8 +113,11 @@ test('benchmark', () => {
112
113
  {
113
114
  name: 'Brush stroke',
114
115
  foldkit: () => {
115
- const [m] = update(foldkitModel, Message.PressedCell({ x: 5, y: 5 }))
116
- update(m, Message.ReleasedMouse())
116
+ const pressedCell = update(
117
+ foldkitModel,
118
+ Message.PressedCell({ x: 5, y: 5 }),
119
+ )
120
+ update(pressedCell.model, Message.ReleasedMouse())
117
121
  },
118
122
  react: () => {
119
123
  const s = reducer(reactState, { type: 'PressedCell', x: 5, y: 5 })
@@ -123,12 +127,27 @@ test('benchmark', () => {
123
127
  {
124
128
  name: 'Brush drag (5 cells)',
125
129
  foldkit: () => {
126
- let [m] = update(foldkitModel, Message.PressedCell({ x: 0, y: 0 }))
127
- ;[m] = update(m, Message.EnteredCell({ x: 1, y: 0 }))
128
- ;[m] = update(m, Message.EnteredCell({ x: 2, y: 0 }))
129
- ;[m] = update(m, Message.EnteredCell({ x: 3, y: 0 }))
130
- ;[m] = update(m, Message.EnteredCell({ x: 4, y: 0 }))
131
- update(m, Message.ReleasedMouse())
130
+ let brushUpdate = update(
131
+ foldkitModel,
132
+ Message.PressedCell({ x: 0, y: 0 }),
133
+ )
134
+ brushUpdate = update(
135
+ brushUpdate.model,
136
+ Message.EnteredCell({ x: 1, y: 0 }),
137
+ )
138
+ brushUpdate = update(
139
+ brushUpdate.model,
140
+ Message.EnteredCell({ x: 2, y: 0 }),
141
+ )
142
+ brushUpdate = update(
143
+ brushUpdate.model,
144
+ Message.EnteredCell({ x: 3, y: 0 }),
145
+ )
146
+ brushUpdate = update(
147
+ brushUpdate.model,
148
+ Message.EnteredCell({ x: 4, y: 0 }),
149
+ )
150
+ update(brushUpdate.model, Message.ReleasedMouse())
132
151
  },
133
152
  react: () => {
134
153
  let s = reducer(reactState, { type: 'PressedCell', x: 0, y: 0 })
@@ -162,12 +181,14 @@ test('benchmark', () => {
162
181
  {
163
182
  name: '5\u00d7 undo + 5\u00d7 redo',
164
183
  foldkit: () => {
165
- let m = foldkitWith20
184
+ let model = foldkitWith20
166
185
  for (let i = 0; i < 5; i++) {
167
- ;[m] = update(m, Message.ClickedUndo())
186
+ const undo = update(model, Message.ClickedUndo())
187
+ model = undo.model
168
188
  }
169
189
  for (let i = 0; i < 5; i++) {
170
- ;[m] = update(m, Message.ClickedRedo())
190
+ const redo = update(model, Message.ClickedRedo())
191
+ model = redo.model
171
192
  }
172
193
  },
173
194
  react: () => {
@@ -36,7 +36,7 @@ const dispatch = (
36
36
  ...messages: ReadonlyArray<Parameters<typeof update>[1]>
37
37
  ): Model =>
38
38
  messages.reduce<Model>(
39
- (currentModel, message) => update(currentModel, message)[0],
39
+ (currentModel, message) => update(currentModel, message).model,
40
40
  model,
41
41
  )
42
42
 
@@ -96,10 +96,10 @@ describe('update: undo/redo with history', () => {
96
96
  bench('5x undo then 5x redo', () => {
97
97
  let model = modelWith10Steps
98
98
  for (let i = 0; i < 5; i++) {
99
- model = update(model, Message.ClickedUndo())[0]
99
+ model = update(model, Message.ClickedUndo()).model
100
100
  }
101
101
  for (let i = 0; i < 5; i++) {
102
- model = update(model, Message.ClickedRedo())[0]
102
+ model = update(model, Message.ClickedRedo()).model
103
103
  }
104
104
  })
105
105
  })
@@ -46,8 +46,8 @@ export const flags: Effect.Effect<Flags> = Effect.gen(function* () {
46
46
 
47
47
  // INIT
48
48
 
49
- export const init: Runtime.ApplicationInit<Model, Message, Flags> = flags => [
50
- {
49
+ export const init: Runtime.ApplicationInit<Model, Message, Flags> = flags => ({
50
+ model: {
51
51
  grid: Option.match(flags.maybeSavedCanvas, {
52
52
  onNone: () => createEmptyGrid(DEFAULT_GRID_SIZE),
53
53
  onSome: ({ grid }) => grid,
@@ -79,7 +79,6 @@ export const init: Runtime.ApplicationInit<Model, Message, Flags> = flags => [
79
79
  gridSizeRadioGroup: RadioGroup.init({ id: GRID_SIZE_RADIO_GROUP_ID }),
80
80
  paletteRadioGroup: RadioGroup.init({ id: PALETTE_RADIO_GROUP_ID }),
81
81
  },
82
- [],
83
- ]
82
+ })
84
83
 
85
84
  export { Message, Model, subscriptions, update, view }