import { Node } from 'prosemirror-model'; /** * Task extraction for the task overview block (issue #154): every * `task_item` of a document as a flat row — its stable id (#153, null for * lines not yet opened in an editor), checked state, the line's text * (mentions and date markers excluded — they become their own columns), * the mentioned users (#150), and the start/due dates (#152). */ export interface TaskRow { id: string | null; checked: boolean; text: string; mentions: { userId: string; username: string }[]; startDate: string | null; dueDate: string | null; } export function extractTaskRows(doc: Node): TaskRow[] { const rows: TaskRow[] = []; doc.descendants((node) => { if (node.type.name !== 'task_item') return; const row: TaskRow = { id: (node.attrs.id as string | null) ?? null, checked: node.attrs.checked === true, text: '', mentions: [], startDate: null, dueDate: null, }; // The line's own content is its first paragraph; nested task lists // produce their own rows via the outer descendants walk. const paragraph = node.firstChild; if (paragraph && paragraph.type.name === 'paragraph') { paragraph.forEach((child) => { if (child.isText) { row.text += child.text ?? ''; } else if (child.type.name === 'mention') { row.mentions.push({ userId: child.attrs.userId as string, username: child.attrs.username as string, }); } else if (child.type.name === 'date_marker') { if (child.attrs.kind === 'due') row.dueDate = child.attrs.date as string; else row.startDate = child.attrs.date as string; } else if (child.type.name === 'wikilink') { row.text += (child.attrs.displayText as string | null) ?? (child.attrs.targetSlug as string); } }); } row.text = row.text.replace(/\s+/g, ' ').trim(); rows.push(row); }); return rows; } /** Wire shapes of `GET /read/:pond/:slug/tasks` (issue #154). */ export interface TaskOverviewMention { id: string; username: string; displayName: string; } export interface TaskOverviewRow { /** Null for lines that never got an id — shown read-only. */ id: string | null; checked: boolean; text: string; mentions: TaskOverviewMention[]; startDate: string | null; dueDate: string | null; } export interface TaskOverviewPage { pageId: string; slug: string; title: string; tasks: TaskOverviewRow[]; }