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>
  );
}