fix: wire conversation panel to DAG nodes with toggle support

- Add MessageSquare icon to GraphNodeCard; prop-thread onConversationOpen
  and selectedTaskId through WorkflowGraph node data (no useUrlState
  inside ReactFlow nodes — avoids context/timing issues)
- Fix ContextualRightPanel: check taskId before epicId so clicking the
  conversation icon always opens ThreadDrawer even when an epic filter
  is active
- setEpicId now clears task from URL so selecting an epic resets any
  open conversation thread
- handleGraphSelect toggles: second click on same node calls setTaskId(null)
  closing the right panel
- Add onSelect to WorkflowGraph flowModel deps to prevent stale callbacks
- Fix ContextualRightPanel onClose no-ops: wired to setTaskId(null) /
  setSwarmId(null) so back button works
- Right panel always visible (removed panel==='open' gate in UnifiedShell)
- SmartDag task grid: horizontal scroll, fixed-width cards, hideClosed=true
- Add <Suspense> in page.tsx for useSearchParams compatibility
- Enable dolt auto-start in .beads/config.yaml
- Add 14 static analysis tests (graph-node-conversation.test.tsx)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ZenchantLive 2026-03-01 13:51:46 -08:00
parent cb83fd92a9
commit 861ae89491
11 changed files with 1279 additions and 1174 deletions

View file

@ -63,9 +63,14 @@ export function UnifiedShell({
const selectedIssue = taskId ? issues.find((issue) => issue.id === taskId) ?? null : null;
const handleGraphSelect = useMemo(() => (id: string) => {
setTaskId(id);
setCustomRightPanel(null); // Reset when switching context
}, [setTaskId]);
// Toggle: clicking the same node again closes the conversation panel
if (taskId === id) {
setTaskId(null);
} else {
setTaskId(id);
}
setCustomRightPanel(null);
}, [taskId, setTaskId]);
const handleCardSelect = useMemo(() => (id: string) => {
if (view === 'social') {
@ -203,17 +208,15 @@ export function UnifiedShell({
{renderMiddleContent()}
</div>
{/* RESIZE HANDLE: Right (only when panel open) */}
{panel === 'open' && <ResizeHandle direction="right" onResize={handleRightResize} />}
{/* RESIZE HANDLE: Right */}
<ResizeHandle direction="right" onResize={handleRightResize} />
{/* RIGHT PANEL */}
{panel === 'open' && (
<div style={{ width: rightWidth }} className="flex-shrink-0 overflow-hidden">
<RightPanel isOpen={true}>
{renderRightPanelContent()}
</RightPanel>
</div>
)}
{/* RIGHT PANEL: always visible, content adapts to selection */}
<div style={{ width: rightWidth }} className="flex-shrink-0 overflow-hidden">
<RightPanel isOpen={true}>
{renderRightPanelContent()}
</RightPanel>
</div>
</div>
{/* THREAD DRAWER: Popup overlay when a task is selected */}