Skip to content

Commit ec55430

Browse files
authored
Merge pull request #4 from Eilodon/architecture-2.0
Architecture 2.0
2 parents b38efde + 533ea23 commit ec55430

28 files changed

Lines changed: 2585 additions & 2385 deletions

App.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { dbService } from './services/db';
44
import { useZenStore } from './store/zenStore';
55
import { ExtremeErrorBoundary } from './components/ExtremeErrorBoundary';
66
import { CryptoErrorBoundary } from './components/CryptoErrorBoundary';
7+
import { TestDashboard } from './test/TestDashboard';
78

89
export default function App() {
910
const { setHistory } = useZenStore();
@@ -15,8 +16,7 @@ export default function App() {
1516
const entries = await dbService.getAllEntries();
1617
setHistory(entries);
1718
} catch (error) {
18-
console.error("DB Load failed - this is normal if vault is locked:", error);
19-
// Don't throw error - it's normal when vault is locked
19+
console.error('Failed to load history:', error);
2020
}
2121
};
2222

@@ -27,6 +27,7 @@ export default function App() {
2727
<ExtremeErrorBoundary name="App-Level" severity="critical">
2828
<CryptoErrorBoundary>
2929
<MainView />
30+
<TestDashboard />
3031
</CryptoErrorBoundary>
3132
</ExtremeErrorBoundary>
3233
);

components/HistoryPanel.tsx

Lines changed: 114 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import React, { useState, useMemo } from 'react';
1+
import React, { useState, useMemo, useEffect } from 'react';
22
import { History, TrendingUp, X, Trash2, Brain, Map } from 'lucide-react';
33
import { ConversationEntry } from '../types';
44
import { dbService } from '../services/db';
5+
import { getWidthClass } from '../src/utils/progressUtils';
56

67
interface Props {
78
history: ConversationEntry[];
@@ -11,6 +12,25 @@ interface Props {
1112
export const HistoryPanel: React.FC<Props> = ({ history, onClear }) => {
1213
const [isOpen, setIsOpen] = useState(false);
1314

15+
// Handle click outside to close
16+
const handleBackdropClick = (e: React.MouseEvent) => {
17+
if (e.target === e.currentTarget) {
18+
setIsOpen(false);
19+
}
20+
};
21+
22+
// Handle ESC key
23+
useEffect(() => {
24+
const handleEsc = (e: KeyboardEvent) => {
25+
if (e.key === 'Escape' && isOpen) {
26+
setIsOpen(false);
27+
}
28+
};
29+
30+
window.addEventListener('keydown', handleEsc);
31+
return () => window.removeEventListener('keydown', handleEsc);
32+
}, [isOpen]);
33+
1434
// Memoize the heavy analysis logic so it doesn't run on every render
1535
const analysis = useMemo(() => {
1636
if (history.length === 0) return null;
@@ -28,48 +48,37 @@ export const HistoryPanel: React.FC<Props> = ({ history, onClear }) => {
2848
const avgEmotionalRegulation = displayData.reduce((sum, e) => sum + safeMetric(e, 'emotional_regulation'), 0) / (displayData.length || 1);
2949

3050
// DETERMINE MINDFULNESS PROFILE (clinically based)
31-
let profileTitle = "Developing Awareness";
32-
let description = "Bạn đang xây dựng nền tảng chánh niệm.";
33-
34-
if (avgPresentMomentAwareness > 0.7 && avgAttentionStability > 0.7) {
35-
profileTitle = "Strong Practice"; // High discipline
36-
description = "Bạn đã phát triển khả năng định tâm vững chãi.";
37-
} else if (avgEmotionalRegulation > 0.7) {
38-
profileTitle = "Emotional Balance"; // High regulation
39-
description = "Bạn điều tiết cảm xúc một cách khéo léo.";
40-
} else if (avgAttentionStability > 0.8) {
41-
profileTitle = "Focused Attention"; // High attention
42-
description = "Bạn có khả năng tập trung ổn định tốt.";
51+
let profileTitle = "Beginning";
52+
let description = "Starting your mindfulness journey";
53+
54+
if (avgPresentMomentAwareness >= 0.7 && avgEmotionalRegulation >= 0.7 && avgAttentionStability >= 0.7) {
55+
profileTitle = "Advanced";
56+
description = "Deep mindfulness practice established";
57+
} else if (avgPresentMomentAwareness >= 0.5 && avgEmotionalRegulation >= 0.5 && avgAttentionStability >= 0.5) {
58+
profileTitle = "Intermediate";
59+
description = "Building consistent mindfulness habits";
60+
} else if (avgPresentMomentAwareness >= 0.3 || avgEmotionalRegulation >= 0.3 || avgAttentionStability >= 0.3) {
61+
profileTitle = "Developing";
62+
description = "Early progress in mindfulness practice";
4363
}
4464

4565
return {
46-
displayData,
66+
profileTitle,
67+
description,
4768
avgAttentionStability,
4869
avgPresentMomentAwareness,
4970
avgEmotionalRegulation,
50-
profileTitle,
51-
description
71+
displayData
5272
};
5373
}, [history]);
5474

5575
if (!analysis) return null;
5676

57-
const { displayData, avgAttentionStability, avgPresentMomentAwareness, avgEmotionalRegulation, profileTitle, description } = analysis;
77+
const { profileTitle, description, avgAttentionStability, avgPresentMomentAwareness, avgEmotionalRegulation, displayData } = analysis;
5878

59-
const emotionColor = {
60-
anxious: 'border-orange-400 bg-orange-50',
61-
sad: 'border-blue-400 bg-blue-50',
62-
joyful: 'border-yellow-400 bg-yellow-50',
63-
calm: 'border-emerald-400 bg-emerald-50',
64-
neutral: 'border-stone-300 bg-stone-50'
65-
};
66-
67-
const handleClear = async () => {
68-
if (window.confirm('Xóa toàn bộ lịch sử? (Không thể hoàn tác)')) {
69-
await dbService.clearAll();
70-
onClear();
71-
setIsOpen(false);
72-
}
79+
const handleClear = () => {
80+
onClear();
81+
setIsOpen(false);
7382
};
7483

7584
return (
@@ -83,71 +92,83 @@ export const HistoryPanel: React.FC<Props> = ({ history, onClear }) => {
8392
</button>
8493

8594
{isOpen && (
86-
<div className="absolute top-20 right-4 z-50 w-80 max-h-[70vh] flex flex-col bg-white/95 backdrop-blur-xl rounded-2xl shadow-2xl border border-stone-100 animate-[fadeIn_0.3s_ease-out]">
87-
{/* Header */}
88-
<div className="bg-gradient-to-r from-stone-800 to-stone-900 text-amber-50 p-4 rounded-t-2xl flex items-center justify-between shadow-sm">
89-
<div className="flex items-center gap-2">
90-
<Brain size={18} className="text-amber-400" />
91-
<h3 className="font-bold text-sm tracking-wide uppercase">Mindfulness Profile</h3>
95+
<div
96+
className="fixed inset-0 z-50 flex items-start justify-center pt-20 bg-black/40 backdrop-blur-sm p-4"
97+
onClick={handleBackdropClick}
98+
>
99+
<div
100+
className="w-full max-w-md bg-white/95 backdrop-blur-xl rounded-2xl shadow-2xl border border-stone-100 animate-[scaleIn_0.3s_ease-out] max-h-[70vh] overflow-hidden"
101+
onClick={(e) => e.stopPropagation()}
102+
>
103+
{/* Header */}
104+
<div className="bg-gradient-to-r from-stone-800 to-stone-900 text-amber-50 p-4 flex items-center justify-between shadow-sm">
105+
<div className="flex items-center gap-2">
106+
<Brain size={18} className="text-amber-400" />
107+
<h3 className="font-bold text-sm tracking-wide uppercase">Mindfulness Profile</h3>
108+
</div>
109+
<button
110+
onClick={() => setIsOpen(false)}
111+
className="hover:bg-white/10 rounded-full p-1 transition-colors"
112+
aria-label="Close history"
113+
>
114+
<X size={18} />
115+
</button>
92116
</div>
93-
<button onClick={() => setIsOpen(false)} className="hover:bg-white/10 rounded-full p-1 transition-colors">
94-
<X size={18} />
95-
</button>
96-
</div>
97-
98-
{/* Mindfulness Profile (Clinical Metrics) */}
99-
<div className="bg-stone-50 p-5 border-b border-stone-200">
100-
<div className="text-center mb-3">
101-
<span className="text-[10px] uppercase tracking-[0.2em] text-stone-400">Practice Level</span>
102-
<h4 className="text-xl font-serif font-bold text-stone-800 mt-1">{profileTitle}</h4>
103-
<p className="text-xs text-stone-500 italic mt-1">{description}</p>
104-
</div>
105-
106-
{/* Mindfulness Metrics Bar Chart */}
107-
<div className="space-y-2 mt-4">
108-
<DnaBar label="Present Moment" value={avgPresentMomentAwareness} color="bg-emerald-500" />
109-
<DnaBar label="Emotion Regulation" value={avgEmotionalRegulation} color="bg-purple-500" />
110-
<DnaBar label="Attention" value={avgAttentionStability} color="bg-blue-500" />
111-
</div>
112-
</div>
113117

114-
{/* History list */}
115-
<div className="overflow-y-auto p-4 space-y-3 flex-1 custom-scrollbar">
116-
<div className="flex items-center gap-2 mb-2 text-stone-400">
117-
<TrendingUp size={12} />
118-
<span className="text-[10px] uppercase font-bold tracking-wider">Journey Log</span>
118+
{/* Mindfulness Profile (Clinical Metrics) */}
119+
<div className="bg-stone-50 p-5 border-b border-stone-200">
120+
<div className="text-center mb-3">
121+
<span className="text-[10px] uppercase tracking-[0.2em] text-stone-400">Practice Level</span>
122+
<h4 className="text-xl font-serif font-bold text-stone-800 mt-1">{profileTitle}</h4>
123+
<p className="text-xs text-stone-500 italic mt-1">{description}</p>
124+
</div>
125+
126+
{/* Mindfulness Metrics Bar Chart */}
127+
<div className="space-y-2 mt-4">
128+
<DnaBar label="Present Moment" value={avgPresentMomentAwareness} color="bg-emerald-500" />
129+
<DnaBar label="Emotion Regulation" value={avgEmotionalRegulation} color="bg-purple-500" />
130+
<DnaBar label="Attention" value={avgAttentionStability} color="bg-blue-500" />
131+
</div>
119132
</div>
120-
121-
{[...displayData].reverse().map((entry) => {
122-
const date = new Date(entry.timestamp);
123-
const isToday = date.toDateString() === new Date().toDateString();
124-
const timeStr = isToday
125-
? date.toLocaleTimeString('vi-VN', { hour: '2-digit', minute: '2-digit' })
126-
: date.toLocaleDateString('vi-VN', { month: 'numeric', day: 'numeric' });
127-
128-
return (
129-
<div key={entry.id} className={`border-l-[3px] ${emotionColor[entry.emotion] || emotionColor.neutral} rounded-r-lg p-3 transition-all hover:bg-stone-50`}>
130-
<div className="flex items-center justify-between mb-1">
131-
<span className="text-[10px] font-bold text-stone-600 uppercase tracking-widest">{entry.emotion}</span>
132-
<span className="text-[10px] text-stone-400 font-mono">{timeStr}</span>
133-
</div>
134-
<div className="flex gap-3 text-[10px] font-medium opacity-80">
135-
<span className="text-blue-600">Attn: {Math.round((entry.mindfulness_metrics?.attention_stability || 0) * 100)}</span>
136-
<span className="text-purple-600">Reg: {Math.round((entry.mindfulness_metrics?.emotional_regulation || 0) * 100)}</span>
133+
134+
{/* History list */}
135+
<div className="overflow-y-auto p-4 space-y-3 flex-1 custom-scrollbar">
136+
<div className="flex items-center gap-2 mb-2 text-stone-400">
137+
<TrendingUp size={12} />
138+
<span className="text-[10px] uppercase font-bold tracking-wider">Journey Log</span>
139+
</div>
140+
{displayData.map((entry, idx) => {
141+
const timeStr = new Date(entry.timestamp).toLocaleTimeString('en-US', {
142+
hour: '2-digit',
143+
minute: '2-digit'
144+
});
145+
return (
146+
<div
147+
key={entry.id}
148+
className="bg-white border border-stone-200 rounded-lg p-3 shadow-sm hover:shadow-md transition-shadow"
149+
>
150+
<div className="flex items-center justify-between mb-1">
151+
<span className="text-[10px] font-bold text-stone-600 uppercase tracking-widest">{entry.emotion}</span>
152+
<span className="text-[10px] text-stone-400 font-mono">{timeStr}</span>
153+
</div>
154+
<div className="flex gap-3 text-[10px] font-medium opacity-80">
155+
<span className="text-blue-600">Attn: {Math.round((entry.mindfulness_metrics?.attention_stability || 0) * 100)}</span>
156+
<span className="text-purple-600">Reg: {Math.round((entry.mindfulness_metrics?.emotional_regulation || 0) * 100)}</span>
157+
</div>
137158
</div>
138-
</div>
139-
);
140-
})}
141-
</div>
159+
);
160+
})}
161+
</div>
142162

143-
{/* Clear button */}
144-
<div className="border-t border-stone-100 p-3 bg-stone-50/50 rounded-b-2xl">
145-
<button
146-
onClick={handleClear}
147-
className="w-full py-2 text-xs font-medium text-stone-500 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors flex items-center justify-center gap-2"
148-
>
149-
<Trash2 size={14} /> Xóa lịch sử
150-
</button>
163+
{/* Clear button */}
164+
<div className="border-t border-stone-100 p-3 bg-stone-50/50 rounded-b-2xl">
165+
<button
166+
onClick={handleClear}
167+
className="w-full py-2 text-xs font-medium text-stone-500 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors flex items-center justify-center gap-2"
168+
>
169+
<Trash2 size={14} /> Clear History
170+
</button>
171+
</div>
151172
</div>
152173
</div>
153174
)}
@@ -160,9 +181,8 @@ const DnaBar = ({ label, value, color }: { label: string, value: number, color:
160181
<span className="text-[10px] font-bold text-stone-400 w-16 text-right">{label}</span>
161182
<div className="flex-1 h-1.5 bg-stone-200 rounded-full overflow-hidden">
162183
<div
163-
className={`h-full ${color} rounded-full`}
164-
style={{ width: `${value * 100}%` }}
184+
className={`h-full ${color} rounded-full dna-bar-fill ${getWidthClass(value * 100)}`}
165185
/>
166186
</div>
167187
</div>
168-
);
188+
);

components/NarrativeMemory.tsx

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
22
import { BookOpen, TrendingUp, Target, AlertTriangle, X } from 'lucide-react';
33
import { ConversationMemory } from '../types';
44
import { ConversationMemoryService } from '../services/conversationMemoryService';
5+
import { getWidthClass } from '../src/utils/progressUtils';
56

67
interface Props {
78
isOpen: boolean;
@@ -19,6 +20,27 @@ export const NarrativeMemory: React.FC<Props> = ({ isOpen, onClose, language })
1920
}
2021
}, [isOpen]);
2122

23+
// Handle click outside to close
24+
const handleBackdropClick = (e: React.MouseEvent) => {
25+
if (e.target === e.currentTarget) {
26+
onClose();
27+
}
28+
};
29+
30+
// Handle ESC key
31+
useEffect(() => {
32+
const handleEsc = (e: KeyboardEvent) => {
33+
if (e.key === 'Escape') {
34+
onClose();
35+
}
36+
};
37+
38+
if (isOpen) {
39+
window.addEventListener('keydown', handleEsc);
40+
}
41+
return () => window.removeEventListener('keydown', handleEsc);
42+
}, [onClose, isOpen]);
43+
2244
const loadMemory = async () => {
2345
setLoading(true);
2446
try {
@@ -55,21 +77,31 @@ export const NarrativeMemory: React.FC<Props> = ({ isOpen, onClose, language })
5577
};
5678

5779
return (
58-
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4">
59-
<div className="bg-white rounded-2xl shadow-2xl max-w-2xl w-full max-h-[85vh] overflow-y-auto">
80+
<div
81+
className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md p-4"
82+
onClick={handleBackdropClick}
83+
>
84+
<div
85+
className="bg-white/95 backdrop-blur-xl rounded-3xl shadow-2xl max-w-2xl w-full max-h-[85vh] overflow-y-auto border border-white/20 relative animate-[scaleIn_0.3s_ease-out]"
86+
onClick={(e) => e.stopPropagation()}
87+
>
6088
{/* Header */}
61-
<div className="bg-gradient-to-r from-indigo-600 to-purple-600 text-white p-6 rounded-t-2xl sticky top-0 z-10">
62-
<div className="flex items-center justify-between">
63-
<div className="flex items-center gap-3">
64-
<BookOpen size={24} />
65-
<h2 className="text-2xl font-bold">{text.title}</h2>
89+
<div className="bg-gradient-to-r from-indigo-600 to-purple-600 text-white p-6 rounded-t-3xl relative overflow-hidden">
90+
<div className="absolute inset-0 bg-gradient-to-br from-white/10 to-transparent"></div>
91+
<div className="relative z-10">
92+
<div className="flex items-center justify-between">
93+
<div className="flex items-center gap-3">
94+
<BookOpen size={24} />
95+
<h2 className="text-2xl font-bold">{text.title}</h2>
96+
</div>
97+
<button
98+
onClick={onClose}
99+
className="p-2 rounded-full hover:bg-white/20 transition-all backdrop-blur-sm"
100+
aria-label="Close memory"
101+
>
102+
<X size={20} />
103+
</button>
66104
</div>
67-
<button
68-
onClick={onClose}
69-
className="p-2 rounded-full hover:bg-white/20 transition-colors"
70-
>
71-
<X size={20} />
72-
</button>
73105
</div>
74106
</div>
75107

@@ -174,8 +206,7 @@ export const NarrativeMemory: React.FC<Props> = ({ isOpen, onClose, language })
174206
</div>
175207
<div className="w-full bg-stone-200 rounded-full h-1.5">
176208
<div
177-
className="h-full bg-amber-500 rounded-full transition-all"
178-
style={{ width: `${intensity * 100}%` }}
209+
className={`h-full bg-amber-500 rounded-full progress-bar-fill ${getWidthClass(intensity * 100)}`}
179210
/>
180211
</div>
181212
</div>

0 commit comments

Comments
 (0)