feat(cortex): Epic 4 - Nudge MVP (Layer 3) complete

Proactive suggestions after successful actions - proof of concept.

E4.S1 - Protocol Rules (lib/cortex/nudge.ts)
- ProtocolRule and ProtocolCondition types
- Condition operators: equals, contains, matches, exists
- 2 hardcoded rules: wondzorg-controle, medicatie-controle

E4.S2 - evaluateNudge Function
- evaluateNudge(input) returns NudgeSuggestion[]
- checkCondition() helper for rule matching
- Priority sorting (high → medium → low)

E4.S3 - NudgeToast Component
- Countdown progress bar with auto-dismiss (5 min)
- Priority-based styling (low/medium/high)
- Accept and dismiss buttons
- Framer Motion animations

E4.S4 - Integration
- NudgeToast rendered in CommandCenter (fixed position)
- Trigger added in DagnotatieBlock after successful save
- Accept opens corresponding artifact with prefilled data

Demo flow:
1. Create dagnotitie with "wond" in content
2. Save → NudgeToast appears
3. Click "Ja, inplannen" → Appointment artifact opens

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
colinislit
2026-01-01 12:42:49 +01:00
parent 42d64761ec
commit 4b759c9b3d
6 changed files with 506 additions and 17 deletions

View File

@@ -17,16 +17,28 @@
*/
import { useEffect, useCallback, useRef } from 'react';
import { AnimatePresence } from 'framer-motion';
import { useCortexStore } from '@/stores/cortex-store';
import { ContextBar } from './context-bar';
import { OfflineBanner } from './offline-banner';
import { NudgeToast } from './nudge-toast';
import { ChatPanel } from '../chat/chat-panel';
import { ArtifactArea } from '../artifacts/artifact-area';
import { getArtifactTitle } from '../artifacts/artifact-container';
import { routeIntentToArtifact } from '@/lib/cortex/action-parser';
export function CommandCenter() {
const { closeAllArtifacts, openArtifacts, openArtifact, pendingAction, setPendingAction } = useCortexStore();
const {
closeAllArtifacts,
openArtifacts,
openArtifact,
pendingAction,
setPendingAction,
// Nudge state (E4)
suggestions,
acceptSuggestion,
dismissSuggestion,
} = useCortexStore();
const inputRef = useRef<HTMLInputElement>(null);
// Global keyboard shortcuts
@@ -118,6 +130,43 @@ export function CommandCenter() {
<ArtifactArea />
</div>
</div>
{/* Nudge Toast - E4 (fixed position, bottom-left above chat) */}
<AnimatePresence mode="wait">
{suggestions.length > 0 && (
<div className="fixed bottom-20 left-4 right-4 lg:left-4 lg:right-auto lg:w-[38%] z-50">
<NudgeToast
key={suggestions[0].id}
suggestion={suggestions[0]}
onAccept={(id) => {
const suggestion = suggestions.find((s) => s.id === id);
if (suggestion) {
// Route the suggested intent to an artifact
const artifact = routeIntentToArtifact(
suggestion.suggestion.intent,
suggestion.suggestion.entities,
1.0 // High confidence since user explicitly accepted
);
if (artifact) {
console.log('[CommandCenter] Opening artifact from nudge:', artifact.type);
openArtifact({
type: artifact.type,
prefill: artifact.prefill,
title: artifact.title,
});
}
}
acceptSuggestion(id);
}}
onDismiss={(id) => {
dismissSuggestion(id);
console.log('[CommandCenter] Nudge dismissed:', id);
}}
/>
</div>
)}
</AnimatePresence>
</div>
);
}