← back to gmx18617__pkm

Function bodies 23 total

All specs Real LLM only Function bodies
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: SY
POST 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 you
handleKeyDown 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,
  }
}