feat(swift): implementeer artifact lifecycle management (E4.S2)

Epic 4 Story 2 compleet: Meerdere artifacts met open/close/switch werken!

E4.S2 - Artifact Lifecycle Management (3 SP)
- Store state: openArtifacts[], activeArtifactId
- Store actions met max 3 artifacts logica
- ArtifactArea refactor naar ArtifactContainer
- CommandCenter integration met nieuwe artifact system

Store Updates (stores/swift-store.ts):
- openArtifacts: Artifact[] — Array van open artifacts
- activeArtifactId: string | null — Currently active artifact
- Initial state: [], null

Store Actions:
```typescript
openArtifact(artifact: Omit<Artifact, 'id' | 'createdAt'>) {
  // Auto-generate ID + timestamp
  // Max 3 artifacts: remove oldest if at capacity
  // Set new artifact as active
}

closeArtifact(id: string) {
  // Filter out artifact
  // Auto-switch to last remaining if closing active
}

switchArtifact(id: string) {
  // Verify artifact exists
  // Set activeArtifactId
}

closeAllArtifacts() {
  // Clear all artifacts + active ID
}
```

Features:
- Max 3 artifacts enforced (oldest removed at capacity)
- Auto-switch when closing active artifact
- Console logging voor debugging
- ID generation met crypto.randomUUID()
- Timestamp tracking met createdAt

Components Updated:
- components/swift/artifacts/artifact-area.tsx (30 regels)
  - Simplified: alleen ArtifactContainer rendering
  - Props: openArtifacts, activeArtifactId, switchArtifact, closeArtifact

- components/swift/command-center/command-center.tsx
  - openArtifact() i.p.v. openBlock()
  - getArtifactTitle() voor dynamic titles
  - Escape: closeAllArtifacts() i.p.v. closeBlock()
  - Artifact opening met title generation

Integration Flow:
1. Chat: AI genereert action met artifact data
2. ChatPanel: setPendingAction(action)
3. CommandCenter: useEffect detecteert pendingAction
4. CommandCenter: getArtifactTitle(type, prefill)
5. CommandCenter: openArtifact({ type, prefill, title })
6. Store: Auto-generate ID, check max 3, add to array
7. ArtifactArea: Renders ArtifactContainer met artifacts
8. ArtifactContainer: Shows tabs (if >1), renders active block

Console Logging:
- "[Store] Max 3 artifacts reached, removing oldest"
- "[Store] Opening artifact: dagnotitie Dagnotitie - Jan"
- "[Store] Closing artifact: [id] New active: [id]"
- "[Store] Switching to artifact: [id]"
- "[Store] Cannot switch to artifact: [id] (not found)"

Build Status:
-  pnpm build succesvol (geen type errors)
- Alleen bekende warnings (Supabase realtime, useCallback)

Voortgang: 56 SP / 85 SP (66%) - 18/31 stories compleet, 2 geskipt

Next: E4.S3 (Slide-in animatie) - 200ms ease-out transitions

🤖 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
2025-12-27 16:33:56 +01:00
parent 855744c927
commit f6c422bb14
4 changed files with 155 additions and 86 deletions

View File

@@ -22,18 +22,19 @@ import { ContextBar } from './context-bar';
import { OfflineBanner } from './offline-banner';
import { ChatPanel } from '../chat/chat-panel';
import { ArtifactArea } from '../artifacts/artifact-area';
import { getArtifactTitle } from '../artifacts/artifact-container';
export function CommandCenter() {
const { closeBlock, activeBlock, openBlock, pendingAction, setPendingAction } = useSwiftStore();
const { closeAllArtifacts, openArtifacts, openArtifact, pendingAction, setPendingAction } = useSwiftStore();
const inputRef = useRef<HTMLInputElement>(null);
// Global keyboard shortcuts
const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
// Escape: close active block
if (e.key === 'Escape' && activeBlock) {
// Escape: close all artifacts
if (e.key === 'Escape' && openArtifacts.length > 0) {
e.preventDefault();
closeBlock();
closeAllArtifacts();
}
// Cmd/Ctrl + K: focus input (chat input in v3.0)
@@ -42,7 +43,7 @@ export function CommandCenter() {
inputRef.current?.focus();
}
},
[activeBlock, closeBlock]
[openArtifacts, closeAllArtifacts]
);
useEffect(() => {
@@ -50,7 +51,7 @@ export function CommandCenter() {
return () => window.removeEventListener('keydown', handleKeyDown);
}, [handleKeyDown]);
// E3.S6: Handle pending actions from chat (artifact opening)
// E3.S6 + E4.S2: Handle pending actions from chat (artifact opening)
useEffect(() => {
if (!pendingAction) return;
@@ -60,10 +61,17 @@ export function CommandCenter() {
if (pendingAction.artifact) {
const { type, prefill } = pendingAction.artifact;
console.log('[CommandCenter] Opening artifact:', type, 'with prefill:', prefill);
// Generate title for the artifact
const title = getArtifactTitle(type, prefill);
// Open the block with prefill data
openBlock(type, prefill);
console.log('[CommandCenter] Opening artifact:', type, title);
// Open the artifact (E4.S2 - new artifact system)
openArtifact({
type,
prefill,
title,
});
// Clear pending action after processing
setPendingAction(null);
@@ -71,7 +79,7 @@ export function CommandCenter() {
console.log('[CommandCenter] Action has no artifact, skipping');
setPendingAction(null);
}
}, [pendingAction, openBlock, setPendingAction]);
}, [pendingAction, openArtifact, setPendingAction]);
return (
<div className="flex flex-col h-screen overflow-hidden">