KQ-1348 · NOTES/APP
Notes App
A complete notes screen built around block-editor, pin-press and bin-lid: tags whose current pill travels on snap, a list whose pinned note lifts on snap and glides to the top — under a Pinned heading or floated over the rest — while the rows between close the gap on glide, search that washes the words it found and grows from its magnifier on snap, and an editor that cross-fades in from 4px on glide with the note's own title and tags over the blocks. Deleting drops the note in the bin until its undo ring drains. Below 640px the list and the note become a stack whose pushes slide on glide and whose left edge takes a 1:1 back-swipe that commits by projection; the list is one tab stop with Up, Down, Home and End, slash searches, and Escape clears the search or goes back.
All notes7 notes
Edited 09:12 · 82 words
Moves it to the bin, with 4 seconds to undo.- #field
- #survey
North basin survey, day 2
Fieldline · shared with the survey team
8 blocks · 82 words
Alt+Up or Alt+Down moves the block. Type / for block types. Select text for formatting, or press Alt+F10 to reach it.
Down from the search reaches the notes; Up and Down move between them and Enter opens one. Slash searches from anywhere.
all notes · 7 notes · 2 pinned
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/notes-app
Props
- pin
- "section" | "float" | "off"
- default "section"
- Where pinned notes go: under a Pinned heading, floated to the top of one list with a pin glyph, or pinning is off (no pin button, plain recency order).
- search
- "field" | "expand" | "off"
- default "field"
- How search lives over the list: an open field, a magnifier that grows into the field and folds back when emptied and left, or no search.
- density
- "compact" | "cozy" | "roomy"
- default "cozy"
- Row height and how much each row says: title and date, one snippet line, or two lines and the tags.
- notes / defaultNotes
- NoteRecord[]
- default defaultNotes
- Every note: title, block-editor blocks, tags, pinned and when it was last edited.
- onNotesChange
- (notes: NoteRecord[]) => void
- default —
- Fires from the edit, pin, tag, creation or deletion that changed the notes, with all of them.
- note / defaultNote
- string | null
- default the most recently edited
- The open note's id.
- onNoteChange
- (id: string | null) => void
- default —
- Fires from the row, the new note or the deletion that changed the open note.
- tag / defaultTag
- string | null
- default null
- The tag the list is filtered to, or null for every note.
- onTagChange
- (tag: string | null) => void
- default —
- Fires from the sidebar row or chip that changed the filter.
- query / defaultQuery
- string
- default ""
- The search text; it matches titles, every block and tags.
- onQueryChange
- (query: string) => void
- default —
- Fires with every edit of the search field, Clear and Escape.
- now
- Date | number
- default defaultNotesNow
- The moment dates are read against, and what an edit is stamped with. Never read from the clock during render.
- zoneOffset
- number
- default 0
- Minutes east of UTC that times are shown in, so server and browser agree.
- onCreate
- (note: NoteRecord) => void
- default —
- A new note was made; it is already in the list and open with its title focused.
- onDelete
- (note: NoteRecord) => void
- default —
- A note was thrown away, once its undo window ran out.
- onPinChange
- (id: string, pinned: boolean) => void
- default —
- The editor's pin was pressed.
- status
- "ready" | "loading" | "error"
- default "ready"
- Loading draws placeholder rows and lines; error offers Try again.
- onRetry
- () => void
- default —
- Try again was pressed in the error state.
- label
- string
- default "Notes"
- The screen's accessible name.
- sound
- boolean
- default false
- Play a tick for list steps and tag picks and a click for new notes, tags and screen moves; the composed pieces play their own. Off unless asked for.
- disabled
- boolean
- default false
- Read only: nothing can be written, pinned, tagged or deleted.
- className
- string
- default —
- Classes for the root. It is 560px tall by default; pass a height class to change it.
| Prop | Type | Default | Description |
|---|---|---|---|
| pin | "section" | "float" | "off" | "section" | Where pinned notes go: under a Pinned heading, floated to the top of one list with a pin glyph, or pinning is off (no pin button, plain recency order). |
| search | "field" | "expand" | "off" | "field" | How search lives over the list: an open field, a magnifier that grows into the field and folds back when emptied and left, or no search. |
| density | "compact" | "cozy" | "roomy" | "cozy" | Row height and how much each row says: title and date, one snippet line, or two lines and the tags. |
| notes / defaultNotes | NoteRecord[] | defaultNotes | Every note: title, block-editor blocks, tags, pinned and when it was last edited. |
| onNotesChange | (notes: NoteRecord[]) => void | — | Fires from the edit, pin, tag, creation or deletion that changed the notes, with all of them. |
| note / defaultNote | string | null | the most recently edited | The open note's id. |
| onNoteChange | (id: string | null) => void | — | Fires from the row, the new note or the deletion that changed the open note. |
| tag / defaultTag | string | null | null | The tag the list is filtered to, or null for every note. |
| onTagChange | (tag: string | null) => void | — | Fires from the sidebar row or chip that changed the filter. |
| query / defaultQuery | string | "" | The search text; it matches titles, every block and tags. |
| onQueryChange | (query: string) => void | — | Fires with every edit of the search field, Clear and Escape. |
| now | Date | number | defaultNotesNow | The moment dates are read against, and what an edit is stamped with. Never read from the clock during render. |
| zoneOffset | number | 0 | Minutes east of UTC that times are shown in, so server and browser agree. |
| onCreate | (note: NoteRecord) => void | — | A new note was made; it is already in the list and open with its title focused. |
| onDelete | (note: NoteRecord) => void | — | A note was thrown away, once its undo window ran out. |
| onPinChange | (id: string, pinned: boolean) => void | — | The editor's pin was pressed. |
| status | "ready" | "loading" | "error" | "ready" | Loading draws placeholder rows and lines; error offers Try again. |
| onRetry | () => void | — | Try again was pressed in the error state. |
| label | string | "Notes" | The screen's accessible name. |
| sound | boolean | false | Play a tick for list steps and tag picks and a click for new notes, tags and screen moves; the composed pieces play their own. Off unless asked for. |
| disabled | boolean | false | Read only: nothing can be written, pinned, tagged or deleted. |
| className | string | — | Classes for the root. It is 560px tall by default; pass a height class to change it. |