Primitives / Time

Start with one: a timer

ts
import { Schema } from 'effect'
import type { HtmlBuilder } from 'foldkit/html'
import { Bundle } from 'foldkit-bundle'
import { Timer, TimerMessage } from 'foldkit-primitives/time'

const Page = Bundle.compose({}).pipe(
  Bundle.withChild('ticks', Timer, { args: { intervalMs: 1000 } }),
)
type Model = typeof Page.Model.Type
type Message = typeof Page.Message.Type
const { placements } = Page

const config = placements.complete({
  init: () => placements.initial({}),
  update: placements.update(model => ({ model })),
  view: (model: Model, h: HtmlBuilder<Message>) =>
    h.button(
      [h.OnClick(Page.Message.GotTicksMessage({ message: TimerMessage.Started() }))],
      [String(model.ticks.count)],
    ),
  subscriptions: placements.subscriptions(),
})

A timer starts stopped, and its stream is empty while stopped. Started runs it; the first tick comes one interval later, and only Ticked advances the count. Stopped pauses without resetting. An interval that is not positive and finite is rejected at placement.

Interval is the wall-clock sibling: Ticked { at } is stamped from Effect's clock and lastAt keeps it, for a view that renders a clock or an elapsed time. Timer counts, Interval records when.