Function bodies 23 total
POST function · typescript · L18-L56 (39 LOC)app/api/briefing/route.ts
export async function POST(req: NextRequest) {
try {
const { items }: { items: Item[] } = await req.json()
const activeItems = items.filter(i => !i.completed)
if (activeItems.length === 0) {
return NextResponse.json({ briefing: "Your slate is clear. Nothing is sitting in any of your sections right now — a good moment to think about what you want to get ahead of." })
}
const summary = activeItems.map(item => {
const parts = [`[${item.section.toUpperCase()}] ${item.title}`]
if (item.notes) parts.push(`(${item.notes})`)
if (item.delegatedTo) parts.push(`→ delegated to ${item.delegatedTo}`)
if (item.dueDate) parts.push(`due ${item.dueDate}`)
if (item.effort) parts.push(`effort: ${item.effort}`)
if (item.context !== 'both') parts.push(`[${item.context}]`)
return parts.join(' ')
}).join('\n')
const message = await client.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 300,
system: SYPOST function · typescript · L13-L53 (41 LOC)app/api/inbound-email/route.ts
export async function POST(req: NextRequest) {
try {
const formData = await req.formData()
const sender = formData.get('sender') as string ?? ''
const subject = formData.get('subject') as string ?? '(no subject)'
const body = (
formData.get('stripped-text') ||
formData.get('body-plain') ||
''
) as string
// Build a rich capture string for Claude
const captureText = [
`Forwarded email`,
`From: ${sender}`,
`Subject: ${subject}`,
body.trim() ? `\n${body.slice(0, 2000)}` : '',
].filter(Boolean).join('\n')
const processed = await processText(captureText)
const now = new Date().toISOString()
const newItem: Item = {
id: nanoid(),
raw: captureText,
...processed,
completed: false,
createdAt: now,
updatedAt: now,
}
const { error } = await supabase.from('items').insert(itemToRow(newItem))
if (error) throw error
return NextResponse.json({ ok: true })
}POST function · typescript · L4-L14 (11 LOC)app/api/process/route.ts
export async function POST(req: NextRequest) {
try {
const { text } = await req.json()
if (!text?.trim()) return NextResponse.json({ error: 'No text provided' }, { status: 400 })
const processed = await processText(text)
return NextResponse.json(processed)
} catch (err) {
console.error('Process error:', err)
return NextResponse.json({ error: 'Failed to process input' }, { status: 500 })
}
}RootLayout function · typescript · L20-L34 (15 LOC)app/layout.tsx
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}formatDate function · typescript · L19-L21 (3 LOC)app/page.tsx
function formatDate(iso: string) {
return new Date(iso).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
}ItemCard function · typescript · L23-L177 (155 LOC)app/page.tsx
function ItemCard({ item, onMove, onDelete, onContextChange, onComplete, onUpdate }: {
item: Item
onMove: (id: string, section: Section) => void
onDelete: (id: string) => void
onContextChange: (id: string, context: Context) => void
onComplete: (id: string) => void
onUpdate: (id: string, title: string, notes: string) => void
}) {
const [open, setOpen] = useState(false)
const [editingTitle, setEditingTitle] = useState(false)
const [editingNotes, setEditingNotes] = useState(false)
const [titleVal, setTitleVal] = useState(item.title)
const [notesVal, setNotesVal] = useState(item.notes ?? '')
const titleRef = useRef<HTMLInputElement>(null)
const notesRef = useRef<HTMLTextAreaElement>(null)
useEffect(() => { if (editingTitle) titleRef.current?.focus() }, [editingTitle])
useEffect(() => { if (editingNotes) notesRef.current?.focus() }, [editingNotes])
// Keep local state in sync if item changes from another device
useEffect(() => { setTitleVal(item.title) },saveTitle function · typescript · L46-L53 (8 LOC)app/page.tsx
function saveTitle() {
setEditingTitle(false)
if (titleVal.trim() && titleVal.trim() !== item.title) {
onUpdate(item.id, titleVal.trim(), notesVal)
} else {
setTitleVal(item.title)
}
}Open data scored by Repobility · https://repobility.com
saveNotes function · typescript · L55-L58 (4 LOC)app/page.tsx
function saveNotes() {
setEditingNotes(false)
onUpdate(item.id, titleVal, notesVal)
}init function · typescript · L199-L224 (26 LOC)app/page.tsx
async function init() {
const { data, error } = await supabase
.from('items')
.select('*')
.order('created_at', { ascending: false })
if (error) { setLoadError(error.message); setLoading(false); return }
const dbItems = (data ?? []).map(rowToItem)
// Migrate any existing localStorage items
const stored = localStorage.getItem('pkm-items')
if (stored) {
const localItems: Item[] = JSON.parse(stored)
const dbIds = new Set(dbItems.map(i => i.id))
const toMigrate = localItems.filter(i => !dbIds.has(i.id))
if (toMigrate.length > 0) {
await supabase.from('items').insert(toMigrate.map(itemToRow))
dbItems.unshift(...toMigrate)
}
localStorage.removeItem('pkm-items')
}
setItems(dbItems)
setLoading(false)
}toggleSection function · typescript · L259-L265 (7 LOC)app/page.tsx
function toggleSection(section: Section) {
setCollapsedSections(prev => {
const next = new Set(prev)
next.has(section) ? next.delete(section) : next.add(section)
return next
})
}generateBriefing function · typescript · L269-L287 (19 LOC)app/page.tsx
async function generateBriefing(currentItems: Item[]) {
setBriefingLoading(true)
try {
const res = await fetch('/api/briefing', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ items: currentItems }),
})
if (!res.ok) throw new Error('Failed')
const { briefing } = await res.json()
setBriefing(briefing)
setShowBriefing(true)
localStorage.setItem('pkm-briefing', JSON.stringify({ date: todayKey, text: briefing }))
} catch {
setBriefing('')
} finally {
setBriefingLoading(false)
}
}handleCapture function · typescript · L304-L340 (37 LOC)app/page.tsx
async function handleCapture() {
if (!input.trim() || processing) return
setProcessing(true)
setError('')
try {
const res = await fetch('/api/process', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: input }),
})
if (!res.ok) throw new Error('Processing failed')
const processed = await res.json()
const newItem: Item = {
id: nanoid(),
raw: input,
...processed,
completed: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
}
const { error } = await supabase.from('items').insert(itemToRow(newItem))
if (error) throw error
setItems(prev => [newItem, ...prev])
setRecentlyCaptured(newItem.section)
setInput('')
textareaRef.current?.focus()
setTimeout(() => setRecentlyCaptured(null), 3000)
} catch {
setError('Something went wrong. Check youhandleKeyDown function · typescript · L342-L344 (3 LOC)app/page.tsx
function handleKeyDown(e: React.KeyboardEvent) {
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') handleCapture()
}moveItem function · typescript · L346-L350 (5 LOC)app/page.tsx
async function moveItem(id: string, section: Section) {
const updatedAt = new Date().toISOString()
setItems(prev => prev.map(i => i.id === id ? { ...i, section, updatedAt } : i))
await supabase.from('items').update({ section, updated_at: updatedAt }).eq('id', id)
}changeContext function · typescript · L352-L356 (5 LOC)app/page.tsx
async function changeContext(id: string, context: Context) {
const updatedAt = new Date().toISOString()
setItems(prev => prev.map(i => i.id === id ? { ...i, context, updatedAt } : i))
await supabase.from('items').update({ context, updated_at: updatedAt }).eq('id', id)
}Repobility (the analyzer behind this table) · https://repobility.com
deleteItem function · typescript · L358-L361 (4 LOC)app/page.tsx
async function deleteItem(id: string) {
setItems(prev => prev.filter(i => i.id !== id))
await supabase.from('items').delete().eq('id', id)
}completeItem function · typescript · L363-L371 (9 LOC)app/page.tsx
async function completeItem(id: string) {
const item = items.find(i => i.id === id)
if (!item) return
const completed = !item.completed
const completedAt = completed ? new Date().toISOString() : undefined
const updatedAt = new Date().toISOString()
setItems(prev => prev.map(i => i.id === id ? { ...i, completed, completedAt, updatedAt } : i))
await supabase.from('items').update({ completed, completed_at: completedAt ?? null, updated_at: updatedAt }).eq('id', id)
}updateItem function · typescript · L373-L377 (5 LOC)app/page.tsx
async function updateItem(id: string, title: string, notes: string) {
const updatedAt = new Date().toISOString()
setItems(prev => prev.map(i => i.id === id ? { ...i, title, notes: notes || undefined, updatedAt } : i))
await supabase.from('items').update({ title, notes: notes || null, updated_at: updatedAt }).eq('id', id)
}openSearch function · typescript · L397-L400 (4 LOC)app/page.tsx
function openSearch() {
setShowSearch(true)
setTimeout(() => searchRef.current?.focus(), 50)
}closeSearch function · typescript · L402-L405 (4 LOC)app/page.tsx
function closeSearch() {
setShowSearch(false)
setSearchQuery('')
}processText function · typescript · L50-L70 (21 LOC)lib/process.ts
export async function processText(text: string): Promise<ProcessedItem> {
const today = new Date().toISOString().slice(0, 10)
const message = await client.messages.create({
model: 'claude-haiku-4-5-20251001',
max_tokens: 512,
system: SYSTEM_PROMPT(today),
messages: [{ role: 'user', content: text }],
})
const content = message.content[0]
if (content.type !== 'text') throw new Error('Unexpected response type')
const rawText = content.text
.replace(/^```json\s*/i, '')
.replace(/^```\s*/i, '')
.replace(/```\s*$/i, '')
.trim()
return JSON.parse(rawText) as ProcessedItem
}rowToItem function · typescript · L10-L27 (18 LOC)lib/supabase.ts
export function rowToItem(row: Record<string, unknown>): Item {
return {
id: row.id as string,
raw: row.raw as string,
title: row.title as string,
notes: row.notes as string | undefined,
section: row.section as Item['section'],
type: row.type as Item['type'],
effort: row.effort as Item['effort'] | undefined,
context: row.context as Item['context'],
delegatedTo: row.delegated_to as string | undefined,
dueDate: row.due_date as string | undefined,
completed: row.completed as boolean,
completedAt: row.completed_at as string | undefined,
createdAt: row.created_at as string,
updatedAt: row.updated_at as string,
}
}itemToRow function · typescript · L30-L47 (18 LOC)lib/supabase.ts
export function itemToRow(item: Item) {
return {
id: item.id,
raw: item.raw,
title: item.title,
notes: item.notes ?? null,
section: item.section,
type: item.type,
effort: item.effort ?? null,
context: item.context,
delegated_to: item.delegatedTo ?? null,
due_date: item.dueDate ?? null,
completed: item.completed,
completed_at: item.completedAt ?? null,
created_at: item.createdAt,
updated_at: item.updatedAt,
}
}