02examples

Natural-language event entry

Type "lunch with Sam tomorrow 1pm" into a box above the grid; the parser underlines what it understood and the event lands.

A time-blocking planner: one line of text above the grid. As you type, the parser claims the phrases it understands — a day, a time or range, a duration, a place, an email — and the box underlines each one in place. The row underneath reads the draft back: title, resolved when, and every claimed span with its kind. Enter commits it to the calendar and moves the view to that day.

Try the three phrases, or write your own.

liveNatural-language event entryparseQuickAddhighlightSegmentsquickAddPreviewuseKloqStore
loading

How it works

  • parseQuickAdd(text, now, { defaultDurationMin }) runs on every keystroke, in a useMemo keyed on the text. now is an argument, not something the parser reads, so the parse is deterministic; the planner passes a fresh new Date() each time so "tomorrow" is always tomorrow. The grammar is on Quick add.
  • highlightSegments(text, result.tokens) interleaves the input with its tokens. Concatenating the segments reproduces the input exactly, so the planner draws them in a transparent-text mirror under the real <input>, with matching padding and type metrics — the underline sits under the characters that were understood.
  • quickAddPreview(result, now) folds the result into one line: a title that is never blank, a formatted when, and chips for location and attendees.
  • The planner is a child of <Kloq>, which is what lets it call useKloqStore() and useKloq(). On Enter it builds a CalEvent the same way the built-in ⌘K quick add does — formatInstant for timed events, toDateOnlyString with an exclusive end for all-day ones (the parser's all-day end is inclusive) — then store.add(event) and k.goToDate(start). The event shapes are on Events.
  • A time with no day resolves to today, or tomorrow if that time has passed; a day with no time is an all-day event; a bare number needs at, from or a meridiem before it counts as a time, so "sprint 2 to 4" stays a title.

Take it further

kloq exports the same field it uses inside ⌘K as QuickAddInput, if you would rather not draw the mirror yourself. With a controlled events array the commit becomes onEventsChange([...events, event]) and the parser result can be sent to a server before it lands — see Controlled state.

on this page