AgendaDocDebuggerTab.tsx6.3 KBView on GitHub import { useCedarStore } from '@/modules/store';
import { useTRPC } from '@/providers/query-provider';
import { useQuery } from '@tanstack/react-query';
import { JsonTreeView } from './JsonTreeView';
import { parseAgendaMarkdown } from '@/modules/agentCanvas/utils/agenda-markdown';
import { useSession } from '@/modules/auth/utils/auth-client';
import { buildDocPath } from '@/modules/files/store/buildDocPath';
import { format } from 'date-fns';
import { useMemo, useState } from 'react';
import { Check, Copy } from 'lucide-react';
interface AgendaDocTabProps {
onCopy: (text: string, id: string) => void;
copiedId: string | null;
}
export function AgendaDocDebuggerTab({ onCopy, copiedId }: AgendaDocTabProps) {
const trpc = useTRPC();
const currentDate = useCedarStore((state) => state.currentDate);
const todayKey=[redacted] ?? new Date(), 'yyyy-MM-dd');
const [selectedDate, setSelectedDate] = useState<string>(todayKey);
// List all stored agenda docs (for the date tabs)
const { data: listData } = useQuery(
trpc.documents.list.queryOptions(
{ documentType: 'agenda', limit: 30 },
{ staleTime: 60 * 1000 },
),
);
const dates = (listData ?? [])
.map((d) => d.path?.split('/').pop())
.filter((s): s is string => !!s && /^\d{4}-\d{2}-\d{2}$/.test(s))
.sort();
// If the selected date isn't in the list (e.g. before any doc exists), show today
const activeDate = dates.includes(selectedDate) ? selectedDate : (dates[0] ?? todayKey);
const { data: session } = useSession();
const userId = session?.user?.id ?? null;
const { data: agendaDoc, isLoading } = useQuery(
trpc.documents.getDoc.queryOptions(
{ documentType: 'agenda', path: buildDocPath.agenda(activeDate) },
{ enabled: !!userId, staleTime: 30 * 1000 },
),
);
// Editor state is local — TipTap holds the canonical JSON in-memory while
// the server stores the markdown mirror. Parse on the fly here so the
// debugger surfaces the same TipTap JSON the editor would render.
const editorJson = useMemo(() => {
if (!agendaDoc?.content) return null;
try {
return parseAgendaMarkdown(agendaDoc.content, { openingRule: true });
} catch (error) {
return { _parseError: String(error) };
}
}, [agendaDoc?.content]);
return (
<div className="flex h-full flex-col overflow-hidden">
{/* Date tabs */}
<div className="flex shrink-0 items-center gap-1 border-b px-2 py-1 overflow-x-auto">
{dates.length === 0 ? (
<span className="text-xs text-muted-foreground px-1">No agenda docs yet</span>
) : (
dates.map((d) => (
<button
key={d}
onClick={() => setSelectedDate(d)}
className={`shrink-0 rounded px-2 py-0.5 text-xs transition-colors ${
d === activeDate
? 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300 font-medium'
: 'text-muted-foreground hover:bg-muted'
}`}
>
{d}
</button>
))
)}
</div>
<div className="flex-1 overflow-auto p-2">
{isLoading ? (
<p className="px-2 py-4 text-center text-xs text-muted-foreground">Loading…</p>
) : agendaDoc == null ? (
<p className="px-2 py-4 text-center text-xs text-muted-foreground">
No agenda document for {activeDate}.
</p>
) : (
<div className="flex flex-col gap-3">
{/* Raw markdown */}
<div>
<div className="mb-1 flex items-center justify-between px-1">
<p className="text-xs font-medium text-muted-foreground">Raw markdown</p>
<button
onClick={() => onCopy(agendaDoc.content ?? '', `agenda-markdown-${activeDate}`)}
className="rounded p-0.5 transition-colors hover:bg-muted"
title="Copy raw markdown"
>
{copiedId === `agenda-markdown-${activeDate}` ? (
<Check className="h-3 w-3 text-green-600" />
) : (
<Copy className="h-3 w-3 text-muted-foreground" />
)}
</button>
</div>
<pre className="whitespace-pre-wrap rounded bg-muted p-2 text-xs leading-relaxed">
{agendaDoc.content}
</pre>
</div>
{/* Editor JSON — the TipTap JSON the editor renders, derived from
the markdown via the same parseAgendaMarkdown the editor uses. */}
{editorJson && (
<div>
<div className="mb-1 flex items-center justify-between px-1">
<p className="text-xs font-medium text-muted-foreground">Editor JSON</p>
<button
onClick={() =>
onCopy(JSON.stringify(editorJson, null, 2), `agenda-json-${activeDate}`)
}
className="rounded p-0.5 transition-colors hover:bg-muted"
title="Copy editor JSON"
>
{copiedId === `agenda-json-${activeDate}` ? (
<Check className="h-3 w-3 text-green-600" />
) : (
<Copy className="h-3 w-3 text-muted-foreground" />
)}
</button>
</div>
<JsonTreeView
data={editorJson}
onCopy={onCopy}
copiedId={copiedId}
defaultExpandDepth={3}
/>
</div>
)}
{/* Document metadata as JSON tree */}
<div>
<p className="mb-1 px-1 text-xs font-medium text-muted-foreground">Metadata</p>
<JsonTreeView
data={{
id: agendaDoc.id,
version: agendaDoc.version,
lastEditedBy: agendaDoc.lastEditedBy,
updatedAt: agendaDoc.updatedAt,
metadata: agendaDoc.metadata,
}}
onCopy={onCopy}
copiedId={copiedId}
defaultExpandDepth={2}
/>
</div>
</div>
)}
</div>
</div>
);
}