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
@@ -8,7 +8,7 @@ import {
8
8
  flow,
9
9
  pipe,
10
10
  } from 'effect'
11
- import { Command, Runtime, Subscription } from 'foldkit'
11
+ import { Command, Runtime, Subscription, type Update } from 'foldkit'
12
12
  import { Document, Html, HtmlBuilder } from 'foldkit/html'
13
13
  import { defineMessageUnion } from 'foldkit/message'
14
14
  import { evo } from 'foldkit/struct'
@@ -64,62 +64,54 @@ export const DetermineTickTime = Command.define('DetermineTickTime', {
64
64
  // UPDATE
65
65
 
66
66
  export const update = (model: Model, message: Message) =>
67
- Message.match<readonly [Model, ReadonlyArray<Command.Command<Message>>]>(
68
- message,
69
- {
70
- ClickedStart: () => [
71
- model,
72
- [DetermineStartTime({ elapsedMs: model.elapsedMs })],
73
- ],
74
-
75
- CompletedDetermineStartTime: ({ startTime }) => [
76
- evo(model, {
77
- isRunning: () => true,
78
- startTime: () => startTime,
79
- }),
80
- [],
81
- ],
82
-
83
- ClickedStop: () => [
84
- evo(model, {
85
- isRunning: () => false,
86
- }),
87
- [],
88
- ],
89
-
90
- ClickedReset: () => [
91
- evo(model, {
92
- elapsedMs: () => 0,
93
- isRunning: () => false,
94
- startTime: () => 0,
95
- }),
96
- [],
97
- ],
98
-
99
- Ticked: () => [
100
- model,
101
- [DetermineTickTime({ startTime: model.startTime })],
102
- ],
103
-
104
- CompletedDetermineTickTime: ({ elapsedMs }) => [
105
- evo(model, {
106
- elapsedMs: () => elapsedMs,
107
- }),
108
- [],
109
- ],
110
- },
111
- )
67
+ Message.match<Update.Return<Model, Message>>(message, {
68
+ ClickedStart: () => ({
69
+ model,
70
+ commands: [DetermineStartTime({ elapsedMs: model.elapsedMs })],
71
+ }),
72
+
73
+ CompletedDetermineStartTime: ({ startTime }) => ({
74
+ model: evo(model, {
75
+ isRunning: () => true,
76
+ startTime: () => startTime,
77
+ }),
78
+ }),
79
+
80
+ ClickedStop: () => ({
81
+ model: evo(model, {
82
+ isRunning: () => false,
83
+ }),
84
+ }),
85
+
86
+ ClickedReset: () => ({
87
+ model: evo(model, {
88
+ elapsedMs: () => 0,
89
+ isRunning: () => false,
90
+ startTime: () => 0,
91
+ }),
92
+ }),
93
+
94
+ Ticked: () => ({
95
+ model,
96
+ commands: [DetermineTickTime({ startTime: model.startTime })],
97
+ }),
98
+
99
+ CompletedDetermineTickTime: ({ elapsedMs }) => ({
100
+ model: evo(model, {
101
+ elapsedMs: () => elapsedMs,
102
+ }),
103
+ }),
104
+ })
112
105
 
113
106
  // INIT
114
107
 
115
- export const init: Runtime.ApplicationInit<Model, Message> = () => [
116
- {
108
+ export const init: Runtime.ApplicationInit<Model, Message> = () => ({
109
+ model: {
117
110
  elapsedMs: 0,
118
111
  isRunning: false,
119
112
  startTime: 0,
120
113
  },
121
- [],
122
- ]
114
+ })
123
115
 
124
116
  // SUBSCRIPTION
125
117
 
@@ -10,7 +10,7 @@ import {
10
10
  String,
11
11
  } from 'effect'
12
12
  import { KeyValueStore } from 'effect/unstable/persistence'
13
- import { Command, Runtime } from 'foldkit'
13
+ import { Command, Runtime, type Update } from 'foldkit'
14
14
  import { Document, Html, HtmlBuilder } from 'foldkit/html'
15
15
  import { defineMessageUnion } from 'foldkit/message'
16
16
  import { ts } from 'foldkit/schema'
@@ -90,198 +90,190 @@ export type Flags = typeof Flags.Type
90
90
 
91
91
  // INIT
92
92
 
93
- export const init: Runtime.ApplicationInit<Model, Message, Flags> = flags => [
94
- {
93
+ export const init: Runtime.ApplicationInit<Model, Message, Flags> = flags => ({
94
+ model: {
95
95
  todos: Option.getOrElse(flags.todos, () => []),
96
96
  newTodoText: '',
97
97
  filter: 'All',
98
98
  editing: NotEditing(),
99
99
  },
100
- [],
101
- ]
100
+ })
102
101
 
103
102
  // UPDATE
104
103
 
104
+ type UpdateReturn = Update.Return<Model, Message>
105
+
105
106
  export const update = (model: Model, message: Message) =>
106
- Message.match<readonly [Model, ReadonlyArray<Command.Command<Message>>]>(
107
- message,
108
- {
109
- UpdatedNewTodo: ({ text }) => [
110
- evo(model, {
111
- newTodoText: () => text,
112
- }),
113
- [],
114
- ],
107
+ Message.match<UpdateReturn>(message, {
108
+ UpdatedNewTodo: ({ text }) => ({
109
+ model: evo(model, {
110
+ newTodoText: () => text,
111
+ }),
112
+ }),
115
113
 
116
- UpdatedEditingTodo: ({ text }) => [
117
- evo(model, {
118
- editing: () =>
119
- M.value(model.editing).pipe(
120
- M.tagsExhaustive({
121
- NotEditing: () => model.editing,
122
- Editing: ({ id }) => Editing({ id, text }),
123
- }),
124
- ),
125
- }),
126
- [],
127
- ],
114
+ UpdatedEditingTodo: ({ text }) => ({
115
+ model: evo(model, {
116
+ editing: () =>
117
+ M.value(model.editing).pipe(
118
+ M.tagsExhaustive({
119
+ NotEditing: () => model.editing,
120
+ Editing: ({ id }) => Editing({ id, text }),
121
+ }),
122
+ ),
123
+ }),
124
+ }),
128
125
 
129
- AddedTodo: () => {
130
- if (String.isEmpty(String.trim(model.newTodoText))) {
131
- return [model, []]
132
- }
126
+ AddedTodo: () => {
127
+ if (String.isEmpty(String.trim(model.newTodoText))) {
128
+ return { model }
129
+ }
133
130
 
134
- return [model, [GenerateTodo({ text: String.trim(model.newTodoText) })]]
135
- },
131
+ return {
132
+ model,
133
+ commands: [GenerateTodo({ text: String.trim(model.newTodoText) })],
134
+ }
135
+ },
136
136
 
137
- CompletedGenerateTodo: ({ id, timestamp, text }) => {
138
- const newTodo: Todo = {
139
- id,
140
- text,
141
- completed: false,
142
- createdAt: timestamp,
143
- }
137
+ CompletedGenerateTodo: ({ id, timestamp, text }) => {
138
+ const newTodo: Todo = {
139
+ id,
140
+ text,
141
+ completed: false,
142
+ createdAt: timestamp,
143
+ }
144
144
 
145
- const updatedTodos = [...model.todos, newTodo]
145
+ const updatedTodos = [...model.todos, newTodo]
146
146
 
147
- return [
148
- evo(model, {
149
- todos: () => updatedTodos,
150
- newTodoText: () => '',
151
- }),
152
- [SaveTodos({ todos: updatedTodos })],
153
- ]
154
- },
147
+ return {
148
+ model: evo(model, {
149
+ todos: () => updatedTodos,
150
+ newTodoText: () => '',
151
+ }),
152
+ commands: [SaveTodos({ todos: updatedTodos })],
153
+ }
154
+ },
155
155
 
156
- DeletedTodo: ({ id }) => {
157
- const updatedTodos = Array.filter(model.todos, todo => todo.id !== id)
156
+ DeletedTodo: ({ id }) => {
157
+ const updatedTodos = Array.filter(model.todos, todo => todo.id !== id)
158
158
 
159
- return [
160
- evo(model, {
161
- todos: () => updatedTodos,
162
- }),
163
- [SaveTodos({ todos: updatedTodos })],
164
- ]
165
- },
159
+ return {
160
+ model: evo(model, {
161
+ todos: () => updatedTodos,
162
+ }),
163
+ commands: [SaveTodos({ todos: updatedTodos })],
164
+ }
165
+ },
166
166
 
167
- ToggledTodo: ({ id }) => {
168
- const updatedTodos = Array.map(model.todos, todo =>
169
- todo.id === id
170
- ? evo(todo, { completed: completed => !completed })
171
- : todo,
172
- )
167
+ ToggledTodo: ({ id }) => {
168
+ const updatedTodos = Array.map(model.todos, todo =>
169
+ todo.id === id
170
+ ? evo(todo, { completed: completed => !completed })
171
+ : todo,
172
+ )
173
173
 
174
- return [
175
- evo(model, {
176
- todos: () => updatedTodos,
177
- }),
178
- [SaveTodos({ todos: updatedTodos })],
179
- ]
180
- },
174
+ return {
175
+ model: evo(model, {
176
+ todos: () => updatedTodos,
177
+ }),
178
+ commands: [SaveTodos({ todos: updatedTodos })],
179
+ }
180
+ },
181
181
 
182
- StartedEditing: ({ id }) => {
183
- const maybeTodo = Array.findFirst(model.todos, todo => todo.id === id)
184
- return [
185
- evo(model, {
186
- editing: () =>
187
- Editing({
188
- id,
189
- text: Option.match(maybeTodo, {
190
- onNone: () => '',
191
- onSome: todo => todo.text,
192
- }),
182
+ StartedEditing: ({ id }) => {
183
+ const maybeTodo = Array.findFirst(model.todos, todo => todo.id === id)
184
+ return {
185
+ model: evo(model, {
186
+ editing: () =>
187
+ Editing({
188
+ id,
189
+ text: Option.match(maybeTodo, {
190
+ onNone: () => '',
191
+ onSome: todo => todo.text,
193
192
  }),
194
- }),
195
- [],
196
- ]
197
- },
198
-
199
- SavedEdit: () =>
200
- M.value(model.editing).pipe(
201
- M.withReturnType<
202
- readonly [Model, ReadonlyArray<Command.Command<Message>>]
203
- >(),
204
- M.tagsExhaustive({
205
- NotEditing: () => [model, []],
206
-
207
- Editing: ({ id, text }) => {
208
- if (String.isEmpty(String.trim(text))) {
209
- return [
210
- evo(model, {
211
- editing: () => NotEditing(),
212
- }),
213
- [],
214
- ]
215
- }
193
+ }),
194
+ }),
195
+ }
196
+ },
216
197
 
217
- const updatedTodos = Array.map(model.todos, todo =>
218
- todo.id === id
219
- ? evo(todo, { text: () => String.trim(text) })
220
- : todo,
221
- )
198
+ SavedEdit: () =>
199
+ M.value(model.editing).pipe(
200
+ M.withReturnType<UpdateReturn>(),
201
+ M.tagsExhaustive({
202
+ NotEditing: () => ({ model }),
222
203
 
223
- return [
224
- evo(model, {
225
- todos: () => updatedTodos,
204
+ Editing: ({ id, text }) => {
205
+ if (String.isEmpty(String.trim(text))) {
206
+ return {
207
+ model: evo(model, {
226
208
  editing: () => NotEditing(),
227
209
  }),
228
- [SaveTodos({ todos: updatedTodos })],
229
- ]
230
- },
231
- }),
232
- ),
233
-
234
- CancelledEdit: () => [
235
- evo(model, {
236
- editing: () => NotEditing(),
210
+ }
211
+ }
212
+
213
+ const updatedTodos = Array.map(model.todos, todo =>
214
+ todo.id === id
215
+ ? evo(todo, { text: () => String.trim(text) })
216
+ : todo,
217
+ )
218
+
219
+ return {
220
+ model: evo(model, {
221
+ todos: () => updatedTodos,
222
+ editing: () => NotEditing(),
223
+ }),
224
+ commands: [SaveTodos({ todos: updatedTodos })],
225
+ }
226
+ },
237
227
  }),
238
- [],
239
- ],
240
-
241
- ToggledAll: () => {
242
- const allCompleted = Array.every(model.todos, todo => todo.completed)
243
- const updatedTodos = Array.map(model.todos, todo =>
244
- evo(todo, {
245
- completed: () => !allCompleted,
246
- }),
247
- )
248
-
249
- return [
250
- evo(model, {
251
- todos: () => updatedTodos,
252
- }),
253
- [SaveTodos({ todos: updatedTodos })],
254
- ]
255
- },
256
-
257
- ClearedCompleted: () => {
258
- const updatedTodos = Array.filter(model.todos, todo => !todo.completed)
228
+ ),
259
229
 
260
- return [
261
- evo(model, {
262
- todos: () => updatedTodos,
263
- }),
264
- [SaveTodos({ todos: updatedTodos })],
265
- ]
266
- },
230
+ CancelledEdit: () => ({
231
+ model: evo(model, {
232
+ editing: () => NotEditing(),
233
+ }),
234
+ }),
267
235
 
268
- SelectedFilter: ({ filter }) => [
269
- evo(model, {
270
- filter: () => filter,
236
+ ToggledAll: () => {
237
+ const allCompleted = Array.every(model.todos, todo => todo.completed)
238
+ const updatedTodos = Array.map(model.todos, todo =>
239
+ evo(todo, {
240
+ completed: () => !allCompleted,
271
241
  }),
272
- [],
273
- ],
242
+ )
274
243
 
275
- SucceededSaveTodos: ({ todos }) => [
276
- evo(model, {
277
- todos: () => todos,
244
+ return {
245
+ model: evo(model, {
246
+ todos: () => updatedTodos,
278
247
  }),
279
- [],
280
- ],
248
+ commands: [SaveTodos({ todos: updatedTodos })],
249
+ }
250
+ },
251
+
252
+ ClearedCompleted: () => {
253
+ const updatedTodos = Array.filter(model.todos, todo => !todo.completed)
281
254
 
282
- FailedSaveTodos: () => [model, []],
255
+ return {
256
+ model: evo(model, {
257
+ todos: () => updatedTodos,
258
+ }),
259
+ commands: [SaveTodos({ todos: updatedTodos })],
260
+ }
283
261
  },
284
- )
262
+
263
+ SelectedFilter: ({ filter }) => ({
264
+ model: evo(model, {
265
+ filter: () => filter,
266
+ }),
267
+ }),
268
+
269
+ SucceededSaveTodos: ({ todos }) => ({
270
+ model: evo(model, {
271
+ todos: () => todos,
272
+ }),
273
+ }),
274
+
275
+ FailedSaveTodos: () => ({ model }),
276
+ })
285
277
 
286
278
  // COMMAND
287
279
 
@@ -211,17 +211,17 @@ export const init: Runtime.RoutingApplicationInit<Model, Message, Flags> = (
211
211
  flags: Flags,
212
212
  url: Url,
213
213
  ) => {
214
- const [initialUiModel, uiCommands] = uiInit(flags.today)
214
+ const uiInit_ = uiInit(flags.today)
215
215
 
216
- return [
217
- {
216
+ return {
217
+ model: {
218
218
  route: urlToAppRoute(url),
219
- uiModel: initialUiModel,
219
+ uiModel: uiInit_.model,
220
220
  },
221
- Command.mapMessages(uiCommands, message =>
221
+ commands: Command.mapMessages(uiInit_.commands, message =>
222
222
  Message.GotUiMessage({ message }),
223
223
  ),
224
- ]
224
+ }
225
225
  }
226
226
 
227
227
  // UPDATE
@@ -241,58 +241,57 @@ const foldUi = Update.foldChild({
241
241
  toParentMessage: toUiMessage,
242
242
  })
243
243
 
244
+ const foldMobileMenuDialogOutMessage = M.type<Dialog.OutMessage>().pipe(
245
+ M.withReturnType<Update.Step<Model, Message>>(),
246
+ M.tagsExhaustive({
247
+ Opened: () => model => ({ model }),
248
+ Closed: () => model => ({ model }),
249
+ }),
250
+ )
251
+
252
+ const foldMobileMenuDialogClose = Update.foldChildStep({
253
+ update: Dialog.close,
254
+ read: (model: Model) => Option.some(model.uiModel.mobileMenuDialog),
255
+ write: (model, nextMobileMenuDialog) =>
256
+ evo(model, {
257
+ uiModel: evo({ mobileMenuDialog: () => nextMobileMenuDialog }),
258
+ }),
259
+ toParentMessage: toMobileMenuDialogMessage,
260
+ foldOutMessage: foldMobileMenuDialogOutMessage,
261
+ })
262
+
263
+ type UpdateReturn = Update.Return<Model, Message>
264
+
244
265
  export const update = (model: Model, message: Message) =>
245
- Message.match<readonly [Model, ReadonlyArray<Command.Command<Message>>]>(
246
- message,
247
- {
248
- CompletedNavigateInternal: () => [model, []],
249
- CompletedLoadExternal: () => [model, []],
250
-
251
- ClickedLink: ({ request }) =>
252
- M.value(request).pipe(
253
- M.tagsExhaustive({
254
- Internal: ({
255
- url,
256
- }): [
257
- Model,
258
- ReadonlyArray<
259
- Command.Command<typeof Message.CompletedNavigateInternal>
260
- >,
261
- ] => [model, [NavigateInternal({ url: urlToString(url) })]],
262
- External: ({
263
- href,
264
- }): [
265
- Model,
266
- ReadonlyArray<
267
- Command.Command<typeof Message.CompletedLoadExternal>
268
- >,
269
- ] => [model, [LoadExternal({ href })]],
266
+ Message.match<UpdateReturn>(message, {
267
+ CompletedNavigateInternal: () => ({ model }),
268
+ CompletedLoadExternal: () => ({ model }),
269
+
270
+ ClickedLink: ({ request }) =>
271
+ M.value(request).pipe(
272
+ M.withReturnType<UpdateReturn>(),
273
+ M.tagsExhaustive({
274
+ Internal: ({ url }) => ({
275
+ model,
276
+ commands: [NavigateInternal({ url: urlToString(url) })],
270
277
  }),
271
- ),
272
-
273
- ChangedUrl: ({ url }) => {
274
- const [nextMobileMenuDialog, mobileMenuDialogCommands] = Dialog.close(
275
- model.uiModel.mobileMenuDialog,
276
- )
277
-
278
- return [
279
- evo(model, {
280
- route: () => urlToAppRoute(url),
281
- uiModel: uiModel =>
282
- evo(uiModel, {
283
- mobileMenuDialog: () => nextMobileMenuDialog,
284
- }),
278
+ External: ({ href }) => ({
279
+ model,
280
+ commands: [LoadExternal({ href })],
285
281
  }),
286
- Command.mapMessages(
287
- mobileMenuDialogCommands,
288
- toMobileMenuDialogMessage,
289
- ),
290
- ]
291
- },
282
+ }),
283
+ ),
292
284
 
293
- GotUiMessage: ({ message }) => foldUi(model, message),
294
- },
295
- )
285
+ ChangedUrl: ({ url }) =>
286
+ Update.combine(model, [
287
+ stepModel => ({
288
+ model: evo(stepModel, { route: () => urlToAppRoute(url) }),
289
+ }),
290
+ foldMobileMenuDialogClose,
291
+ ]),
292
+
293
+ GotUiMessage: ({ message }) => foldUi(model, message),
294
+ })
296
295
 
297
296
  // VIEW
298
297
 
@@ -22,11 +22,11 @@ import {
22
22
  import { uiInit } from './ui/init'
23
23
 
24
24
  const today = Calendar.make(2026, 4, 16)
25
- const [initialUiModel] = uiInit(today)
25
+ const uiInit_ = uiInit(today)
26
26
 
27
27
  const modelForRoute = (route: Model['route']): Model => ({
28
28
  route,
29
- uiModel: initialUiModel,
29
+ uiModel: uiInit_.model,
30
30
  })
31
31
 
32
32
  const homeModel = modelForRoute(HomeRoute())
@@ -11,11 +11,11 @@ import { HomeRoute, Message, type Model, update } from './main'
11
11
  import { uiInit } from './ui/init'
12
12
 
13
13
  const today = Calendar.make(2026, 4, 16)
14
- const [initialUiModel] = uiInit(today)
14
+ const uiInit_ = uiInit(today)
15
15
 
16
16
  const initialModel: Model = {
17
17
  route: HomeRoute(),
18
- uiModel: initialUiModel,
18
+ uiModel: uiInit_.model,
19
19
  }
20
20
 
21
21
  const urlOrThrow = (raw: string) =>
@@ -1,5 +1,5 @@
1
1
  import { Option } from 'effect'
2
- import { Calendar, Command } from 'foldkit'
2
+ import { Calendar, type Update } from 'foldkit'
3
3
 
4
4
  import {
5
5
  Animation,
@@ -23,10 +23,10 @@ import type { UiMessage } from './message'
23
23
  import type { UiModel } from './model'
24
24
  import { Toast } from './toast'
25
25
 
26
- export const uiInit = (
27
- today: Calendar.CalendarDate,
28
- ): [UiModel, ReadonlyArray<Command.Command<UiMessage>>] => [
29
- {
26
+ type InitReturn = Update.Return<UiModel, UiMessage>
27
+
28
+ export const uiInit = (today: Calendar.CalendarDate): InitReturn => ({
29
+ model: {
30
30
  mobileMenuDialog: Dialog.init({ id: 'mobile-menu' }),
31
31
  buttonClickCount: 0,
32
32
  inputDemoValue: '',
@@ -181,5 +181,4 @@ export const uiInit = (
181
181
  rowHeightPx: 56,
182
182
  }),
183
183
  },
184
- [],
185
- ]
184
+ })