create-foldkit-app 0.31.0 → 0.31.1-canary.4e7d8d4010be
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.
|
@@ -60,7 +60,7 @@ When composing one of those results, bind the whole result to a value named afte
|
|
|
60
60
|
|
|
61
61
|
Pass optional Commands directly to APIs that accept them: `Command.mapMessages(homeInit.commands, toParentMessage)`. Use `result.commands ?? []` only when the next operation requires a concrete array for spreading, concatenating, execution, or an assertion.
|
|
62
62
|
|
|
63
|
-
`Update.Return<Model, Message>`
|
|
63
|
+
Use `Update.Return<Model, Message>` when an update cannot emit an OutMessage. It prevents a result containing an OutMessage from entering code that would keep only its Model and Commands. A result with no `outMessage` can still be used where `Update.ReturnWithOutMessage<Model, Message, OutMessage>` is expected. The missing field means that update emitted no OutMessage.
|
|
64
64
|
|
|
65
65
|
Manual unpacking of a child result usually means the site should use `Update.foldChild` or `Update.foldChildStep`.
|
|
66
66
|
|
|
@@ -68,7 +68,7 @@ Use `Update.combine` when a later Step should receive the Model produced by an e
|
|
|
68
68
|
|
|
69
69
|
When the OutMessage is already known while constructing a new result, include it directly: `{ model, commands, outMessage }`. Use `Update.withOutMessage` when attaching an OutMessage to an existing plain return or when the value has the type `OutMessage | undefined`. Pipe an existing return into the helper: `pipe(dialogClose, Update.withOutMessage(outMessage))`. When constructing the plain return in the same expression, pass it first: `Update.withOutMessage({ model, commands }, outMessage)`.
|
|
70
70
|
|
|
71
|
-
Add `toParentOutMessage` only when at least one child OutMessage
|
|
71
|
+
Add `toParentOutMessage` only when at least one child OutMessage should continue to the current Submodel's parent. For partial forwarding, match every child variant and return `undefined` for the variants that stop here. Omit `toParentOutMessage` when every variant stops here. `foldOutMessage` still handles each variant locally, including variants that continue upward. Never write `toParentOutMessage: () => undefined`.
|
|
72
72
|
|
|
73
73
|
Use `evo()` from `foldkit/struct` for immutable model updates. Never spread or `Object.assign`.
|
|
74
74
|
|
|
@@ -109,7 +109,7 @@ Scene runs at any level, since a page's own `update`/`view` pair drops into `sce
|
|
|
109
109
|
## Code Style
|
|
110
110
|
|
|
111
111
|
- Encode state in discriminated unions, not booleans or nullable fields. `Idle | Loading | Error | Ok`, not `isLoading: boolean`. Make impossible states unrepresentable.
|
|
112
|
-
- Use `Option` for absence in the Model and domain values instead of `null` or `undefined`. Foldkit return records are the exception: omit `commands` and `outMessage` when absent. A partial `toParentOutMessage` mapper returns `undefined` for each
|
|
112
|
+
- Use `Option` for absence in the Model and domain values instead of `null` or `undefined`. Foldkit return records are the exception: omit `commands` and `outMessage` when absent. A partial `toParentOutMessage` mapper returns `undefined` for each child variant that stops at this Submodel. Prefix Option-typed values with `maybe*`. Match with `Option.match`; don't unwrap with `Option.map(...)` + `Option.getOrElse(...)` when you can just match.
|
|
113
113
|
- Use Effect modules over native methods in `pipe` chains (`Array.map`, `String.startsWith`, `Array.findFirst`). Native methods are fine when calling directly on a named variable.
|
|
114
114
|
- Never cast Schema values with `as Type`. Use the callable constructor: `Message.SucceededLogin({ sessionId })`, not `{ _tag: 'SucceededLogin', sessionId } as Message`.
|
|
115
115
|
- Always `Array.isArrayEmpty` / `Array.isArrayNonEmpty` (not `.length === 0` / `.length > 0`). Use `Array.match` when handling both empty and non-empty cases.
|
|
@@ -13,53 +13,51 @@ import { formatInteger } from './format'
|
|
|
13
13
|
|
|
14
14
|
export const CHART_HOST_ID = 'charting-chart'
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
element =>
|
|
24
|
-
Effect.gen(function* () {
|
|
25
|
-
if (!(element instanceof HTMLElement)) {
|
|
26
|
-
return Message.FailedMountChart({
|
|
27
|
-
reason: 'Chart host is not an HTMLElement.',
|
|
28
|
-
})
|
|
29
|
-
}
|
|
16
|
+
const mountChart = (element: Element, hostId: string) =>
|
|
17
|
+
Effect.gen(function* () {
|
|
18
|
+
if (!(element instanceof HTMLElement)) {
|
|
19
|
+
return Message.FailedMountChart({
|
|
20
|
+
reason: 'Chart host is not an HTMLElement.',
|
|
21
|
+
})
|
|
22
|
+
}
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}),
|
|
49
|
-
({ resizeObserver, onWindowResize }) =>
|
|
50
|
-
Effect.sync(() => {
|
|
51
|
-
resizeObserver.disconnect()
|
|
52
|
-
window.removeEventListener('resize', onWindowResize)
|
|
53
|
-
removeChart(hostId)
|
|
54
|
-
}),
|
|
55
|
-
).pipe(
|
|
56
|
-
Effect.map(() => Message.SucceededMountChart({ hostId })),
|
|
57
|
-
Effect.catch(error =>
|
|
58
|
-
Effect.succeed(Message.FailedMountChart({ reason: error.message })),
|
|
59
|
-
),
|
|
60
|
-
)
|
|
24
|
+
return yield* Effect.acquireRelease(
|
|
25
|
+
Effect.try({
|
|
26
|
+
try: () => {
|
|
27
|
+
const chart = echarts.init(element, undefined, {
|
|
28
|
+
renderer: 'canvas',
|
|
29
|
+
})
|
|
30
|
+
const resizeObserver = new ResizeObserver(() => chart.resize())
|
|
31
|
+
resizeObserver.observe(element)
|
|
32
|
+
const onWindowResize = () => chart.resize()
|
|
33
|
+
window.addEventListener('resize', onWindowResize)
|
|
34
|
+
setChart(hostId, chart)
|
|
35
|
+
return { resizeObserver, onWindowResize }
|
|
36
|
+
},
|
|
37
|
+
catch: error =>
|
|
38
|
+
error instanceof Error
|
|
39
|
+
? error
|
|
40
|
+
: new Error(`Failed to mount chart: ${error}`),
|
|
61
41
|
}),
|
|
62
|
-
)
|
|
42
|
+
({ resizeObserver, onWindowResize }) =>
|
|
43
|
+
Effect.sync(() => {
|
|
44
|
+
resizeObserver.disconnect()
|
|
45
|
+
window.removeEventListener('resize', onWindowResize)
|
|
46
|
+
removeChart(hostId)
|
|
47
|
+
}),
|
|
48
|
+
).pipe(
|
|
49
|
+
Effect.map(() => Message.SucceededMountChart({ hostId })),
|
|
50
|
+
Effect.catch(error =>
|
|
51
|
+
Effect.succeed(Message.FailedMountChart({ reason: error.message })),
|
|
52
|
+
),
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
export const MountChart = Mount.define('MountChart', {
|
|
57
|
+
args: { hostId: S.String },
|
|
58
|
+
messages: [Message.SucceededMountChart, Message.FailedMountChart],
|
|
59
|
+
execute: ({ element, hostId }) => mountChart(element, hostId),
|
|
60
|
+
})
|
|
63
61
|
|
|
64
62
|
export const chartPanelView = (
|
|
65
63
|
model: Model,
|
|
@@ -310,61 +310,58 @@ export const init: Runtime.ApplicationInit<Model, Message> = () => ({
|
|
|
310
310
|
|
|
311
311
|
// MAP MOUNT
|
|
312
312
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
)
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
313
|
+
const mountMap = (element: Element, hostId: string) =>
|
|
314
|
+
Effect.gen(function* () {
|
|
315
|
+
if (!(element instanceof HTMLElement)) {
|
|
316
|
+
return Message.FailedMountMap({
|
|
317
|
+
reason: 'Map host is not an HTMLElement.',
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return yield* Effect.gen(function* () {
|
|
322
|
+
yield* Effect.acquireRelease(
|
|
323
|
+
Effect.gen(function* () {
|
|
324
|
+
const maplibre = yield* Effect.tryPromise(() => import('maplibre-gl'))
|
|
325
|
+
const map = new maplibre.Map({
|
|
326
|
+
container: element,
|
|
327
|
+
style: 'https://demotiles.maplibre.org/style.json',
|
|
328
|
+
center: [0, 20],
|
|
329
|
+
zoom: INITIAL_MAP_ZOOM,
|
|
325
330
|
})
|
|
326
|
-
}
|
|
327
|
-
return yield* Effect.gen(function* () {
|
|
328
|
-
yield* Effect.acquireRelease(
|
|
329
|
-
Effect.gen(function* () {
|
|
330
|
-
const maplibre = yield* Effect.tryPromise(
|
|
331
|
-
() => import('maplibre-gl'),
|
|
332
|
-
)
|
|
333
|
-
const map = new maplibre.Map({
|
|
334
|
-
container: element,
|
|
335
|
-
style: 'https://demotiles.maplibre.org/style.json',
|
|
336
|
-
center: [0, 20],
|
|
337
|
-
zoom: INITIAL_MAP_ZOOM,
|
|
338
|
-
})
|
|
339
|
-
|
|
340
|
-
Array.forEach(featuredLocations, ({ id, lng, lat }) => {
|
|
341
|
-
const markerElement = document.createElement('button')
|
|
342
|
-
markerElement.setAttribute('data-location-id', id)
|
|
343
|
-
markerElement.setAttribute('aria-label', `Marker: ${id}`)
|
|
344
|
-
markerElement.className = markerStyle
|
|
345
|
-
new maplibre.Marker({ element: markerElement })
|
|
346
|
-
.setLngLat([lng, lat])
|
|
347
|
-
.addTo(map)
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
setMap(hostId, map)
|
|
351
|
-
return map
|
|
352
|
-
}),
|
|
353
|
-
() => Effect.sync(() => removeMap(hostId)),
|
|
354
|
-
)
|
|
355
331
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
)
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
332
|
+
Array.forEach(featuredLocations, ({ id, lng, lat }) => {
|
|
333
|
+
const markerElement = document.createElement('button')
|
|
334
|
+
markerElement.setAttribute('data-location-id', id)
|
|
335
|
+
markerElement.setAttribute('aria-label', `Marker: ${id}`)
|
|
336
|
+
markerElement.className = markerStyle
|
|
337
|
+
new maplibre.Marker({ element: markerElement })
|
|
338
|
+
.setLngLat([lng, lat])
|
|
339
|
+
.addTo(map)
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
setMap(hostId, map)
|
|
343
|
+
return map
|
|
344
|
+
}),
|
|
345
|
+
() => Effect.sync(() => removeMap(hostId)),
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
return Message.SucceededMountMap({ hostId })
|
|
349
|
+
}).pipe(
|
|
350
|
+
Effect.catch(error =>
|
|
351
|
+
Effect.succeed(
|
|
352
|
+
Message.FailedMountMap({
|
|
353
|
+
reason: error instanceof Error ? error.message : `${error}`,
|
|
354
|
+
}),
|
|
355
|
+
),
|
|
356
|
+
),
|
|
357
|
+
)
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
export const MountMap = Mount.define('MountMap', {
|
|
361
|
+
args: { hostId: S.String },
|
|
362
|
+
messages: [Message.SucceededMountMap, Message.FailedMountMap],
|
|
363
|
+
execute: ({ element, hostId }) => mountMap(element, hostId),
|
|
364
|
+
})
|
|
368
365
|
|
|
369
366
|
// SUBSCRIPTIONS
|
|
370
367
|
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"channel": "
|
|
4
|
-
"sourceCommit": "
|
|
3
|
+
"channel": "canary",
|
|
4
|
+
"sourceCommit": "4e7d8d4010be5a84dd37d1dc48bb97b4f4e599f3",
|
|
5
5
|
"packages": {
|
|
6
|
-
"create-foldkit-app": "0.31.
|
|
7
|
-
"@foldkit/devtools": "0.
|
|
8
|
-
"@foldkit/devtools-mcp": "0.19.
|
|
9
|
-
"foldkit": "0.
|
|
10
|
-
"@foldkit/markdown": "0.8.0",
|
|
11
|
-
"@foldkit/oxlint-plugin": "0.
|
|
12
|
-
"@foldkit/ui": "0.
|
|
13
|
-
"@foldkit/vite-plugin": "0.19.0"
|
|
6
|
+
"create-foldkit-app": "0.31.1-canary.4e7d8d4010be",
|
|
7
|
+
"@foldkit/devtools": "0.154.0-canary.4e7d8d4010be",
|
|
8
|
+
"@foldkit/devtools-mcp": "0.19.1-canary.4e7d8d4010be",
|
|
9
|
+
"foldkit": "0.154.0-canary.4e7d8d4010be",
|
|
10
|
+
"@foldkit/markdown": "0.8.0-canary.4e7d8d4010be",
|
|
11
|
+
"@foldkit/oxlint-plugin": "0.10.0-canary.4e7d8d4010be",
|
|
12
|
+
"@foldkit/ui": "0.154.0-canary.4e7d8d4010be",
|
|
13
|
+
"@foldkit/vite-plugin": "0.19.0-canary.4e7d8d4010be"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@foldkit/devtools": "0.
|
|
17
|
-
"@foldkit/vite-plugin": "0.19.0",
|
|
18
|
-
"@foldkit/devtools-mcp": "0.19.
|
|
19
|
-
"@foldkit/oxlint-plugin": "0.
|
|
16
|
+
"@foldkit/devtools": "0.154.0-canary.4e7d8d4010be",
|
|
17
|
+
"@foldkit/vite-plugin": "0.19.0-canary.4e7d8d4010be",
|
|
18
|
+
"@foldkit/devtools-mcp": "0.19.1-canary.4e7d8d4010be",
|
|
19
|
+
"@foldkit/oxlint-plugin": "0.10.0-canary.4e7d8d4010be",
|
|
20
20
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
21
21
|
"@types/node": "^25.9.3",
|
|
22
22
|
"happy-dom": "^20.10.4",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-foldkit-app",
|
|
3
|
-
"version": "0.31.
|
|
3
|
+
"version": "0.31.1-canary.4e7d8d4010be",
|
|
4
4
|
"description": "Create Foldkit applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"url": "https://github.com/foldkit/foldkit.git",
|
|
39
39
|
"directory": "packages/create-foldkit-app"
|
|
40
40
|
},
|
|
41
|
+
"homepage": "https://foldkit.dev/get-started/getting-started",
|
|
41
42
|
"publishConfig": {
|
|
42
43
|
"access": "public"
|
|
43
44
|
},
|