Notes App
The reference VeloxKit application. A full-featured notes app with SQLite storage, full-text search, tagging, and a custom title bar.
Get the code
npx veloxkit-cli create my-notes --template notes
cd my-notes
npm run devOr clone directly:
git clone https://github.com/yourname/veloxkit-examples
cd veloxkit-examples/notes-app
npm install
npm run devFeatures
- Create, edit, delete notes with live auto-save
- Full-text search with result highlighting
- Pin notes to the top
- Custom transparent title bar (macOS & Windows)
- Keyboard shortcuts:
βNnew note,βFsearch,ββ«delete - Dark mode by default, follows system preference
Architecture
screens/
βββ _layout.tsx β sidebar + content layout
βββ index.tsx β redirects to /notes
βββ notes/
βββ index.tsx β notes list + search
βββ [id].tsx β note editor
src/
βββ main.ts
βββ db.ts β schema, migrations, query helpers
βββ hooks/
βββ useNotes.ts β useLiveQuery wrapperKey code
Database setup
// src/db.ts
import { db } from 'veloxkit'
export function initDatabase() {
db.migrate([
`CREATE TABLE notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL DEFAULT '',
body TEXT NOT NULL DEFAULT '',
pinned INTEGER NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
)`,
`CREATE VIRTUAL TABLE notes_fts USING fts5(
title, body, content=notes, content_rowid=id
)`,
// triggers for fts sync (see full source)
])
}
export function searchNotes(query: string) {
if (!query.trim()) {
return db.query<Note>(
'SELECT * FROM notes ORDER BY pinned DESC, updated_at DESC'
)
}
return db.query<Note>(
`SELECT notes.*
FROM notes_fts
JOIN notes ON notes.id = notes_fts.rowid
WHERE notes_fts MATCH ?
ORDER BY rank`,
[query]
)
}Auto-saving editor
// screens/notes/[id].tsx
import { db } from 'veloxkit'
import { useDebouncedCallback } from 'use-debounce'
export default function NoteDetail() {
const { id } = useParams()
const [note, setNote] = useState<Note | null>(null)
useEffect(() => {
setNote(db.query<Note>('SELECT * FROM notes WHERE id = ?', [id])[0])
}, [id])
const save = useDebouncedCallback((patch: Partial<Note>) => {
db.execute(
'UPDATE notes SET title=COALESCE(?,title), body=COALESCE(?,body), updated_at=unixepoch() WHERE id=?',
[patch.title ?? null, patch.body ?? null, id]
)
}, 300)
if (!note) return null
return (
<View style={{ flex: 1, padding: 24, gap: 12 }}>
<TextInput
value={note.title}
onChangeText={(title) => { setNote(n => n && { ...n, title }); save({ title }) }}
style={{ fontSize: 24, fontWeight: 700, color: '#F0F0F2' }}
placeholder="Title"
/>
<TextInput
value={note.body}
onChangeText={(body) => { setNote(n => n && { ...n, body }); save({ body }) }}
multiline
style={{ flex: 1, fontSize: 15, color: '#D0D0E0', lineHeight: 24 }}
placeholder="Start writing..."
/>
</View>
)
}veloxkit.config.ts
import { defineConfig } from 'veloxkit'
export default defineConfig({
name: 'velox-notes',
productName: 'Notes',
version: '0.1.0',
identifier: 'dev.veloxkit.examples.notes',
capabilities: ['db', 'fs', 'dialog', 'clipboard'],
window: {
titleBarStyle: 'transparent',
trafficLightsPosition: { x: 14, y: 14 },
width: 1100,
height: 720,
minWidth: 600,
minHeight: 400,
},
})