@mazees/mark 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +29 -0
- package/README.md +209 -0
- package/bin/mark.js +27 -0
- package/out/renderer/assets/icon-DHo2CYph.svg +1 -0
- package/out/renderer/assets/index-G2fADWO-.js +4768 -0
- package/out/renderer/assets/index-f3mVse3c.css +1 -0
- package/out/renderer/assets/music-cover-CG4vi3kZ.png +0 -0
- package/out/renderer/assets/poppins-devanagari-400-normal-CJDn6rn8.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-400-normal-CqVvlrh5.woff +0 -0
- package/out/renderer/assets/poppins-devanagari-500-normal-BIdkeU1p.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-500-normal-DMPDjHtT.woff +0 -0
- package/out/renderer/assets/poppins-devanagari-600-normal-ClASKHrr.woff +0 -0
- package/out/renderer/assets/poppins-devanagari-600-normal-STEjXBNN.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-700-normal-O-jipLrW.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-700-normal-fHs-vx92.woff +0 -0
- package/out/renderer/assets/poppins-latin-400-normal-BOb3E3N0.woff +0 -0
- package/out/renderer/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-500-normal-DGXqpDMm.woff +0 -0
- package/out/renderer/assets/poppins-latin-600-normal-BJdTmd5m.woff +0 -0
- package/out/renderer/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-700-normal-BVuQR_eA.woff +0 -0
- package/out/renderer/assets/poppins-latin-700-normal-Qrb0O0WB.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-400-normal-DaBSavcJ.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-400-normal-by3JarPu.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-500-normal-CK-6C4Hw.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-500-normal-CgAe2rWW.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-600-normal-CAhIAdZj.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-600-normal-Df5ffKXP.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-700-normal-DctTR6Tg.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-700-normal-cby-RkWa.woff2 +0 -0
- package/out/renderer/favicon.ico +0 -0
- package/out/renderer/icon-256.png +0 -0
- package/out/renderer/icon.svg +1 -0
- package/out/renderer/index.html +25 -0
- package/out/renderer/manifest.json +25 -0
- package/out/renderer/robot-simulator.html +1640 -0
- package/package.json +106 -0
- package/src/cli/index.js +332 -0
- package/src/cli/monitor.js +413 -0
- package/src/cli/theme.js +347 -0
- package/src/launcher/MarkTray.cs +227 -0
- package/src/main/browser-agent.js +1132 -0
- package/src/main/git-service.js +90 -0
- package/src/main/google/google-calendar.js +69 -0
- package/src/main/google/google-drive.js +263 -0
- package/src/main/google/google-gmail.js +131 -0
- package/src/main/google/google-service.js +241 -0
- package/src/main/node-tools.js +23 -0
- package/src/main/pc-agent-scripts/mouse-locker.ps1 +76 -0
- package/src/main/pc-agent-scripts/ocr-region.ps1 +185 -0
- package/src/main/pc-agent-scripts/pc-daemon.ps1 +726 -0
- package/src/main/pc-agent-scripts/pc-overlay.ps1 +253 -0
- package/src/main/pc-agent-scripts/pick-folder.ps1 +33 -0
- package/src/main/pc-agent-scripts/read-ui.ps1 +181 -0
- package/src/main/pc-agent-scripts/take-screenshot.ps1 +24 -0
- package/src/main/pc-agent-scripts/win-action.ps1 +380 -0
- package/src/main/plugins/plugin-loader.js +306 -0
- package/src/main/skills/skill-manager.js +265 -0
- package/src/main/syntax-validator.js +455 -0
- package/src/main/task-daemon.js +117 -0
- package/src/main/telegram/telegram-service.js +835 -0
- package/src/main/tools/browser-tools.js +516 -0
- package/src/main/tools/file-tools.js +1126 -0
- package/src/main/tools/git-tools.js +79 -0
- package/src/main/tools/google-tools.js +566 -0
- package/src/main/tools/index.js +36 -0
- package/src/main/tools/media-tools.js +42 -0
- package/src/main/tools/system-tools.js +295 -0
- package/src/main/tools/task-tools.js +73 -0
- package/src/main/tools/telegram-tools.js +101 -0
- package/src/renderer/index.html +24 -0
- package/src/renderer/public/favicon.ico +0 -0
- package/src/renderer/public/icon-256.png +0 -0
- package/src/renderer/public/icon.svg +1 -0
- package/src/renderer/public/manifest.json +25 -0
- package/src/renderer/public/robot-simulator.html +1640 -0
- package/src/renderer/src/App.jsx +304 -0
- package/src/renderer/src/api/ai/awareness.js +294 -0
- package/src/renderer/src/api/ai/chatSummarizer.js +79 -0
- package/src/renderer/src/api/ai/contextCompactor.js +129 -0
- package/src/renderer/src/api/ai/contextManager.js +1130 -0
- package/src/renderer/src/api/ai/core.js +167 -0
- package/src/renderer/src/api/ai/index.js +4 -0
- package/src/renderer/src/api/ai/memoryGroomer.js +101 -0
- package/src/renderer/src/api/ai/persona.js +115 -0
- package/src/renderer/src/api/ai/planning.js +317 -0
- package/src/renderer/src/api/ai/relationship.js +177 -0
- package/src/renderer/src/api/ai/skillSynthesizer.js +168 -0
- package/src/renderer/src/api/ai/tools.js +167 -0
- package/src/renderer/src/api/ai/utils.js +359 -0
- package/src/renderer/src/api/db.js +1013 -0
- package/src/renderer/src/api/oramaStore.js +592 -0
- package/src/renderer/src/api/ragPipeline.js +87 -0
- package/src/renderer/src/api/subagent/subagentExecutor.js +780 -0
- package/src/renderer/src/api/subagent/subagentPrompt.js +44 -0
- package/src/renderer/src/api/subagent/subagentStore.js +173 -0
- package/src/renderer/src/api/taskExecutor.js +18 -0
- package/src/renderer/src/api/taskStore.js +315 -0
- package/src/renderer/src/api/tools/core-tools.js +707 -0
- package/src/renderer/src/api/tools/index.js +151 -0
- package/src/renderer/src/api/turnPairMigrator.js +200 -0
- package/src/renderer/src/api/vectorMemory.js +136 -0
- package/src/renderer/src/api/wakeWord.js +101 -0
- package/src/renderer/src/api/web-bridge.js +885 -0
- package/src/renderer/src/api/webSpeech.js +178 -0
- package/src/renderer/src/api/workspaceRag.js +76 -0
- package/src/renderer/src/assets/electron.svg +10 -0
- package/src/renderer/src/assets/icon.svg +1 -0
- package/src/renderer/src/assets/main.css +589 -0
- package/src/renderer/src/assets/music-cover.png +0 -0
- package/src/renderer/src/assets/wavy-lines.svg +25 -0
- package/src/renderer/src/components/Chat/ApprovalBubble.jsx +127 -0
- package/src/renderer/src/components/Chat/CodeBlock.jsx +83 -0
- package/src/renderer/src/components/Chat/DurableTaskBubble.jsx +455 -0
- package/src/renderer/src/components/Chat/FileDiffModal.jsx +328 -0
- package/src/renderer/src/components/Chat/GeminiWidgets.jsx +82 -0
- package/src/renderer/src/components/Chat/MemoryFooterBubble.jsx +34 -0
- package/src/renderer/src/components/Chat/MessageBubble.jsx +459 -0
- package/src/renderer/src/components/Chat/PluginExecutionBubble.jsx +41 -0
- package/src/renderer/src/components/Chat/ThinkingBubble.jsx +316 -0
- package/src/renderer/src/components/Chat/YoutubeSearchBubble.jsx +43 -0
- package/src/renderer/src/components/Chat/YoutubeSummaryBubble.jsx +40 -0
- package/src/renderer/src/components/Chat/index.js +11 -0
- package/src/renderer/src/components/ChatList.jsx +434 -0
- package/src/renderer/src/components/GlobalCameraManager.jsx +67 -0
- package/src/renderer/src/components/Versions.jsx +15 -0
- package/src/renderer/src/components/YoutubeMusicPlayer.jsx +375 -0
- package/src/renderer/src/components/camera/CameraPreview.jsx +236 -0
- package/src/renderer/src/components/config/AiEngineSection.jsx +363 -0
- package/src/renderer/src/components/config/CameraSection.jsx +100 -0
- package/src/renderer/src/components/config/PermissionsSection.jsx +130 -0
- package/src/renderer/src/components/config/ShortcutSection.jsx +102 -0
- package/src/renderer/src/components/config/VoiceSection.jsx +110 -0
- package/src/renderer/src/components/config/index.js +5 -0
- package/src/renderer/src/components/core/Avatar.jsx +2197 -0
- package/src/renderer/src/components/core/ConfirmModal.jsx +80 -0
- package/src/renderer/src/components/core/DraggableHoloCard.jsx +125 -0
- package/src/renderer/src/components/core/FloatingMenu.jsx +272 -0
- package/src/renderer/src/components/core/HoloCard.jsx +64 -0
- package/src/renderer/src/components/core/InputBar.jsx +1346 -0
- package/src/renderer/src/components/core/ProcessPanel.jsx +216 -0
- package/src/renderer/src/components/core/ResponseArea.jsx +149 -0
- package/src/renderer/src/components/core/StatusIndicator.jsx +59 -0
- package/src/renderer/src/components/core/ThoughtNeuralFlow.jsx +168 -0
- package/src/renderer/src/components/core/ToolClustersDeck.jsx +210 -0
- package/src/renderer/src/components/core/native-skills.js +43 -0
- package/src/renderer/src/components/neural-core/EmotionalCortexLobe.jsx +359 -0
- package/src/renderer/src/components/neural-core/NodeDissectionModal.jsx +57 -0
- package/src/renderer/src/components/neural-core/ProceduralMatrixLobe.jsx +193 -0
- package/src/renderer/src/components/neural-core/ProceduralSkillModal.jsx +53 -0
- package/src/renderer/src/components/neural-core/ResetAiModal.jsx +198 -0
- package/src/renderer/src/components/neural-core/SynapticMemoryLobe.jsx +204 -0
- package/src/renderer/src/components/neural-core/traitMeta.js +102 -0
- package/src/renderer/src/components/subagent/AgentChatFeed.jsx +603 -0
- package/src/renderer/src/components/subagent/AgentInspector.jsx +224 -0
- package/src/renderer/src/components/subagent/AgentSidebar.jsx +216 -0
- package/src/renderer/src/components/subagent/SubagentIntercom.jsx +572 -0
- package/src/renderer/src/components/subagent/SubagentTopologyMap.jsx +423 -0
- package/src/renderer/src/contexts/ApprovalContext.jsx +425 -0
- package/src/renderer/src/contexts/ChatContext.jsx +61 -0
- package/src/renderer/src/contexts/YoutubeMusicContext.jsx +333 -0
- package/src/renderer/src/data/faqData.js +202 -0
- package/src/renderer/src/hooks/agent/executeAgentTool.js +895 -0
- package/src/renderer/src/hooks/agent/index.js +4 -0
- package/src/renderer/src/hooks/agent/useMarkMusic.js +75 -0
- package/src/renderer/src/hooks/agent/useMarkPlan.js +2739 -0
- package/src/renderer/src/hooks/agent/useMarkState.js +173 -0
- package/src/renderer/src/hooks/agent/useMarkYoutube.js +82 -0
- package/src/renderer/src/hooks/agent/useRelationalGrowth.js +201 -0
- package/src/renderer/src/hooks/telegram/useTelegramBot.js +63 -0
- package/src/renderer/src/hooks/useAwareness.js +289 -0
- package/src/renderer/src/hooks/useChatArchiver.js +52 -0
- package/src/renderer/src/hooks/useConfirm.jsx +68 -0
- package/src/renderer/src/hooks/useMarkAgent.js +454 -0
- package/src/renderer/src/hooks/useMemoryGroomer.js +152 -0
- package/src/renderer/src/hooks/useVAD.js +511 -0
- package/src/renderer/src/hooks/useWebSpeechWakeWord.js +122 -0
- package/src/renderer/src/main.jsx +12 -0
- package/src/renderer/src/pages/ChatStudio.jsx +922 -0
- package/src/renderer/src/pages/Configuration.jsx +360 -0
- package/src/renderer/src/pages/GoogleWorkspace.jsx +293 -0
- package/src/renderer/src/pages/Guidebook.jsx +917 -0
- package/src/renderer/src/pages/Knowledge.jsx +241 -0
- package/src/renderer/src/pages/MarkHome.jsx +491 -0
- package/src/renderer/src/pages/NeuralCore.jsx +786 -0
- package/src/renderer/src/pages/Plugins.jsx +630 -0
- package/src/renderer/src/pages/SkillEditor.jsx +380 -0
- package/src/renderer/src/pages/Skills.jsx +219 -0
- package/src/renderer/src/pages/Subagents.jsx +342 -0
- package/src/renderer/src/pages/TelegramBot.jsx +343 -0
- package/src/renderer/src/utils/chatContentParser.js +203 -0
- package/src/renderer/src/utils/toolClusters.js +201 -0
- package/src/server/bin/sha3_wasm_bg.wasm +0 -0
- package/src/server/config-manager.js +57 -0
- package/src/server/index.js +195 -0
- package/src/server/launcher.js +87 -0
- package/src/server/memory/db-store.js +758 -0
- package/src/server/memory/orama-store.js +177 -0
- package/src/server/memory/vector-engine.js +109 -0
- package/src/server/routes/ai.routes.js +280 -0
- package/src/server/routes/awareness.routes.js +62 -0
- package/src/server/routes/chat.routes.js +61 -0
- package/src/server/routes/config.routes.js +41 -0
- package/src/server/routes/index.js +35 -0
- package/src/server/routes/integrations.routes.js +116 -0
- package/src/server/routes/memory.routes.js +256 -0
- package/src/server/routes/plugins.routes.js +72 -0
- package/src/server/routes/skills.routes.js +139 -0
- package/src/server/routes/subagents.routes.js +70 -0
- package/src/server/routes/tasks.routes.js +175 -0
- package/src/server/routes/workspace.routes.js +127 -0
- package/src/server/services/ai/ai-utils.js +161 -0
- package/src/server/services/ai/openai-provider.js +757 -0
- package/src/server/services/ai/web-provider.js +729 -0
- package/src/server/services/ai-bridge.js +46 -0
- package/src/server/services/deepseek-web.js +956 -0
- package/src/server/services/gemini-web.js +170 -0
- package/src/server/services/notification-service.js +91 -0
- package/src/server/services/updater.js +125 -0
- package/src/server/tools/awareness-tracker.js +292 -0
- package/src/server/tools/group-tools.js +1164 -0
- package/src/server/tools/media-tools.js +244 -0
- package/src/server/tools/pc-agent.js +327 -0
- package/src/server/tools/screen-service.js +51 -0
- package/src/server/ws-hub.js +168 -0
- package/src/shared/parsers/mark-tag-parser.js +275 -0
|
@@ -0,0 +1,786 @@
|
|
|
1
|
+
import { useState, useEffect, useRef, useCallback } from 'react'
|
|
2
|
+
import { useNavigate, useSearchParams } from 'react-router-dom'
|
|
3
|
+
import {
|
|
4
|
+
getRelationship,
|
|
5
|
+
getAllLearnedSkills,
|
|
6
|
+
deleteLearnedSkill,
|
|
7
|
+
getAllChatArchives,
|
|
8
|
+
getAllMemory,
|
|
9
|
+
getAllDocuments,
|
|
10
|
+
deleteMemory,
|
|
11
|
+
deleteChatArchive,
|
|
12
|
+
resetAiDatabase,
|
|
13
|
+
exportDatabaseDump,
|
|
14
|
+
restoreDatabaseDump
|
|
15
|
+
} from '../api/db'
|
|
16
|
+
import { webApi } from '../api/web-bridge'
|
|
17
|
+
import { useConfirm } from '../hooks/useConfirm'
|
|
18
|
+
import { useMemoryGroomer } from '../hooks/useMemoryGroomer'
|
|
19
|
+
import ConfirmModal from '../components/core/ConfirmModal'
|
|
20
|
+
import { EmotionalCortexLobe } from '../components/neural-core/EmotionalCortexLobe'
|
|
21
|
+
import { SynapticMemoryLobe } from '../components/neural-core/SynapticMemoryLobe'
|
|
22
|
+
import { ProceduralMatrixLobe } from '../components/neural-core/ProceduralMatrixLobe'
|
|
23
|
+
import { ProceduralSkillModal } from '../components/neural-core/ProceduralSkillModal'
|
|
24
|
+
import { NodeDissectionModal } from '../components/neural-core/NodeDissectionModal'
|
|
25
|
+
import { ResetAiModal } from '../components/neural-core/ResetAiModal'
|
|
26
|
+
import {
|
|
27
|
+
FaBrain,
|
|
28
|
+
FaNetworkWired,
|
|
29
|
+
FaGraduationCap,
|
|
30
|
+
FaFire,
|
|
31
|
+
FaLayerGroup,
|
|
32
|
+
FaBookOpen
|
|
33
|
+
} from 'react-icons/fa'
|
|
34
|
+
|
|
35
|
+
const NeuralCore = () => {
|
|
36
|
+
const navigate = useNavigate()
|
|
37
|
+
const [searchParams, setSearchParams] = useSearchParams()
|
|
38
|
+
const initialTab = searchParams.get('tab') || 'overview'
|
|
39
|
+
const [activeTab, setActiveTab] = useState(
|
|
40
|
+
initialTab === 'tri-lobe' || initialTab === 'overview'
|
|
41
|
+
? 'overview'
|
|
42
|
+
: initialTab === 'synaptic' || initialTab === 'memory'
|
|
43
|
+
? 'memory'
|
|
44
|
+
: initialTab === 'emotional' || initialTab === 'personality'
|
|
45
|
+
? 'personality'
|
|
46
|
+
: initialTab === 'procedural' || initialTab === 'skills'
|
|
47
|
+
? 'skills'
|
|
48
|
+
: initialTab === 'journal'
|
|
49
|
+
? 'journal'
|
|
50
|
+
: 'overview'
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
// Data States
|
|
54
|
+
const [traits, setTraits] = useState(null)
|
|
55
|
+
const [learnedSkills, setLearnedSkills] = useState([])
|
|
56
|
+
const [skillSearch, setSkillSearch] = useState('')
|
|
57
|
+
const [selectedSkill, setSelectedSkill] = useState(null)
|
|
58
|
+
const [loading, setLoading] = useState(true)
|
|
59
|
+
const [isResetModalOpen, setIsResetModalOpen] = useState(false)
|
|
60
|
+
const [isResettingAi, setIsResettingAi] = useState(false)
|
|
61
|
+
const [isExportingDb, setIsExportingDb] = useState(false)
|
|
62
|
+
const [isRestoringDb, setIsRestoringDb] = useState(false)
|
|
63
|
+
const [journalEntries, setJournalEntries] = useState([])
|
|
64
|
+
const fileInputRef = useRef(null)
|
|
65
|
+
const [toastMessage, setToastMessage] = useState(null)
|
|
66
|
+
|
|
67
|
+
const showToast = (message) => {
|
|
68
|
+
setToastMessage(message)
|
|
69
|
+
setTimeout(() => setToastMessage(null), 4000)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Memory Graph States
|
|
73
|
+
const [graphData, setGraphData] = useState({ nodes: [], links: [] })
|
|
74
|
+
const [selectedNode, setSelectedNode] = useState(null)
|
|
75
|
+
const [graphDimensions, setGraphDimensions] = useState({ width: 800, height: 500 })
|
|
76
|
+
const graphContainerRef = useRef(null)
|
|
77
|
+
const fgRef = useRef()
|
|
78
|
+
|
|
79
|
+
const { isGrooming, groomResult, triggerGrooming } = useMemoryGroomer(false)
|
|
80
|
+
const { confirm, ModalComponent } = useConfirm()
|
|
81
|
+
const [confirmModal, setConfirmModal] = useState({ isOpen: false, node: null })
|
|
82
|
+
|
|
83
|
+
// Sinkronkan URL param saat tab berubah
|
|
84
|
+
const handleTabChange = (tabId) => {
|
|
85
|
+
setActiveTab(tabId)
|
|
86
|
+
setSearchParams({ tab: tabId })
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Muat Data Pusat Otak
|
|
90
|
+
const loadBrainData = useCallback(async () => {
|
|
91
|
+
try {
|
|
92
|
+
setLoading(true)
|
|
93
|
+
|
|
94
|
+
// 1. Karakter & Status Hubungan (Owner)
|
|
95
|
+
const userTraits = await getRelationship('owner')
|
|
96
|
+
setTraits(userTraits)
|
|
97
|
+
|
|
98
|
+
// 2. Data Keahlian Hasil Belajar (Learned Skills)
|
|
99
|
+
const skills = await getAllLearnedSkills()
|
|
100
|
+
setLearnedSkills(skills || [])
|
|
101
|
+
|
|
102
|
+
let customDiskSkills = []
|
|
103
|
+
try {
|
|
104
|
+
if (webApi && typeof webApi.getSkills === 'function') {
|
|
105
|
+
customDiskSkills = (await webApi.getSkills()) || []
|
|
106
|
+
}
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.warn('[NeuralCore] Gagal mengambil custom skills dari disk:', err)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 3. Peta Memori (Chat, RAG, Dokumen)
|
|
112
|
+
const archives = await getAllChatArchives()
|
|
113
|
+
const explicitMemories = await getAllMemory()
|
|
114
|
+
const documents = await getAllDocuments()
|
|
115
|
+
|
|
116
|
+
const journals = (explicitMemories || [])
|
|
117
|
+
.filter((m) => m.type === 'private_journal' || m.type === 'journal')
|
|
118
|
+
.sort((a, b) => (b.created_at || 0) - (a.created_at || 0))
|
|
119
|
+
setJournalEntries(journals)
|
|
120
|
+
|
|
121
|
+
const nodes = []
|
|
122
|
+
const links = []
|
|
123
|
+
|
|
124
|
+
// 0. Node Pusat Otak (Warna Klasik Cyber: Neon Green)
|
|
125
|
+
const coreNodeId = 'core'
|
|
126
|
+
nodes.push({ id: coreNodeId, name: 'Pusat Otak Mark', group: 0, val: 25, color: '#00ff66' })
|
|
127
|
+
|
|
128
|
+
// 1. Kategori Utama Memori (Sub-Cores: Cyan, Pink, Orange, Purple)
|
|
129
|
+
nodes.push({
|
|
130
|
+
id: 'archives-root',
|
|
131
|
+
name: 'Riwayat Obrolan',
|
|
132
|
+
group: 1,
|
|
133
|
+
val: 15,
|
|
134
|
+
color: '#00e5ff'
|
|
135
|
+
})
|
|
136
|
+
nodes.push({
|
|
137
|
+
id: 'vector-root',
|
|
138
|
+
name: 'Basis Pengetahuan',
|
|
139
|
+
group: 1,
|
|
140
|
+
val: 15,
|
|
141
|
+
color: '#ff00aa'
|
|
142
|
+
})
|
|
143
|
+
nodes.push({ id: 'doc-root', name: 'Gudang Dokumen', group: 1, val: 15, color: '#ffaa00' })
|
|
144
|
+
nodes.push({
|
|
145
|
+
id: 'skills-root',
|
|
146
|
+
name: 'Skill Yang Dipelajari',
|
|
147
|
+
group: 1,
|
|
148
|
+
val: 15,
|
|
149
|
+
color: '#a855f7'
|
|
150
|
+
})
|
|
151
|
+
nodes.push({
|
|
152
|
+
id: 'custom-skills-root',
|
|
153
|
+
name: 'Custom User Skills',
|
|
154
|
+
group: 1,
|
|
155
|
+
val: 15,
|
|
156
|
+
color: '#38bdf8'
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
links.push({ source: coreNodeId, target: 'archives-root', color: 'rgba(255,255,255,0.3)' })
|
|
160
|
+
links.push({ source: coreNodeId, target: 'vector-root', color: 'rgba(255,255,255,0.3)' })
|
|
161
|
+
links.push({ source: coreNodeId, target: 'doc-root', color: 'rgba(255,255,255,0.3)' })
|
|
162
|
+
links.push({ source: coreNodeId, target: 'skills-root', color: 'rgba(255,255,255,0.3)' })
|
|
163
|
+
links.push({
|
|
164
|
+
source: coreNodeId,
|
|
165
|
+
target: 'custom-skills-root',
|
|
166
|
+
color: 'rgba(255,255,255,0.3)'
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// 2 & 3. Data Riwayat Obrolan
|
|
170
|
+
const topics = [...new Set(archives.map((a) => a.topic || 'Umum'))]
|
|
171
|
+
topics.forEach((topic) => {
|
|
172
|
+
nodes.push({ id: `topic-${topic}`, name: topic, group: 2, val: 10, color: '#00e5ff' })
|
|
173
|
+
links.push({
|
|
174
|
+
source: 'archives-root',
|
|
175
|
+
target: `topic-${topic}`,
|
|
176
|
+
color: 'rgba(255,255,255,0.1)'
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
archives.forEach((arc) => {
|
|
181
|
+
const topicId = `topic-${arc.topic || 'Umum'}`
|
|
182
|
+
nodes.push({
|
|
183
|
+
id: `arc-${arc.id}`,
|
|
184
|
+
name: arc.summary ? arc.summary.substring(0, 30) + '...' : 'Arsip Obrolan',
|
|
185
|
+
fullText: arc.summary,
|
|
186
|
+
date: new Date(arc.timestamp).toLocaleDateString('id-ID'),
|
|
187
|
+
group: 3,
|
|
188
|
+
val: 4,
|
|
189
|
+
color: '#a0a0a0',
|
|
190
|
+
typeLabel: 'Arsip Obrolan'
|
|
191
|
+
})
|
|
192
|
+
links.push({ source: topicId, target: `arc-${arc.id}`, color: 'rgba(255,255,255,0.1)' })
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
// 2 & 3. Data Memori Tersimpan
|
|
196
|
+
const memoryTypes = [...new Set(explicitMemories.map((m) => m.type || 'Lainnya'))]
|
|
197
|
+
memoryTypes.forEach((type) => {
|
|
198
|
+
nodes.push({
|
|
199
|
+
id: `type-${type}`,
|
|
200
|
+
name: type.toUpperCase(),
|
|
201
|
+
group: 2,
|
|
202
|
+
val: 10,
|
|
203
|
+
color: '#ff00aa'
|
|
204
|
+
})
|
|
205
|
+
links.push({
|
|
206
|
+
source: 'vector-root',
|
|
207
|
+
target: `type-${type}`,
|
|
208
|
+
color: 'rgba(255,255,255,0.1)'
|
|
209
|
+
})
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
explicitMemories.forEach((mem) => {
|
|
213
|
+
const typeId = `type-${mem.type || 'Lainnya'}`
|
|
214
|
+
nodes.push({
|
|
215
|
+
id: `mem-${mem.id}`,
|
|
216
|
+
name: mem.summary
|
|
217
|
+
? mem.summary
|
|
218
|
+
: mem.memory
|
|
219
|
+
? mem.memory.substring(0, 30) + '...'
|
|
220
|
+
: 'Memori Tersimpan',
|
|
221
|
+
fullText: mem.memory,
|
|
222
|
+
date: 'Memori RAG',
|
|
223
|
+
group: 3,
|
|
224
|
+
val: 5,
|
|
225
|
+
color: '#e0e0e0',
|
|
226
|
+
typeLabel: 'Memori Tersimpan'
|
|
227
|
+
})
|
|
228
|
+
links.push({ source: typeId, target: `mem-${mem.id}`, color: 'rgba(255,255,255,0.1)' })
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
// 2 & 3. Data Dokumen
|
|
232
|
+
const docNames = [...new Set(documents.map((d) => d.docName || 'Dokumen Tanpa Judul'))]
|
|
233
|
+
docNames.forEach((docName) => {
|
|
234
|
+
nodes.push({
|
|
235
|
+
id: `docGroup-${docName}`,
|
|
236
|
+
name: docName,
|
|
237
|
+
group: 2,
|
|
238
|
+
val: 12,
|
|
239
|
+
color: '#ffaa00'
|
|
240
|
+
})
|
|
241
|
+
links.push({
|
|
242
|
+
source: 'doc-root',
|
|
243
|
+
target: `docGroup-${docName}`,
|
|
244
|
+
color: 'rgba(255,255,255,0.1)'
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
documents.forEach((doc) => {
|
|
249
|
+
const docGroupId = `docGroup-${doc.docName || 'Dokumen Tanpa Judul'}`
|
|
250
|
+
nodes.push({
|
|
251
|
+
id: `doc-${doc.id}`,
|
|
252
|
+
name: `Bagian ${doc.chunkIndex}`,
|
|
253
|
+
fullText: doc.content,
|
|
254
|
+
date: doc.timestamp ? new Date(doc.timestamp).toLocaleDateString('id-ID') : 'Dokumen',
|
|
255
|
+
group: 3,
|
|
256
|
+
val: 4,
|
|
257
|
+
color: '#d0b080',
|
|
258
|
+
typeLabel: 'Potongan Dokumen'
|
|
259
|
+
})
|
|
260
|
+
links.push({ source: docGroupId, target: `doc-${doc.id}`, color: 'rgba(255,255,255,0.1)' })
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
// 2 & 3. Data Skill Yang Di Pelajari (Learned Skills)
|
|
264
|
+
skills.forEach((skill) => {
|
|
265
|
+
nodes.push({
|
|
266
|
+
id: `skill-${skill.id}`,
|
|
267
|
+
name: `/${skill.name}`,
|
|
268
|
+
fullText: skill.content,
|
|
269
|
+
description: skill.description,
|
|
270
|
+
date: new Date(skill.updatedAt || Date.now()).toLocaleDateString('id-ID'),
|
|
271
|
+
group: 3,
|
|
272
|
+
val: 5,
|
|
273
|
+
color: '#c084fc',
|
|
274
|
+
typeLabel: 'Skill Yang Di Pelajari'
|
|
275
|
+
})
|
|
276
|
+
links.push({
|
|
277
|
+
source: 'skills-root',
|
|
278
|
+
target: `skill-${skill.id}`,
|
|
279
|
+
color: 'rgba(255,255,255,0.1)'
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
// 4. Data Custom User Skills (Disk Documents/Mark Skills)
|
|
284
|
+
customDiskSkills.forEach((cSkill) => {
|
|
285
|
+
const cSkillId = `custom-skill-${cSkill.name}`
|
|
286
|
+
nodes.push({
|
|
287
|
+
id: cSkillId,
|
|
288
|
+
name: `/${cSkill.name}`,
|
|
289
|
+
fullText: cSkill.description || `Custom skill: ${cSkill.name}`,
|
|
290
|
+
description: cSkill.description,
|
|
291
|
+
date: 'Custom Disk Skill',
|
|
292
|
+
group: 3,
|
|
293
|
+
val: 5,
|
|
294
|
+
color: '#38bdf8',
|
|
295
|
+
typeLabel: 'Custom Skill'
|
|
296
|
+
})
|
|
297
|
+
links.push({
|
|
298
|
+
source: 'custom-skills-root',
|
|
299
|
+
target: cSkillId,
|
|
300
|
+
color: 'rgba(255,255,255,0.1)'
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
setGraphData({ nodes, links })
|
|
305
|
+
} catch (err) {
|
|
306
|
+
console.error('[NeuralCore] Error loading brain data:', err)
|
|
307
|
+
} finally {
|
|
308
|
+
setLoading(false)
|
|
309
|
+
}
|
|
310
|
+
}, [])
|
|
311
|
+
|
|
312
|
+
useEffect(() => {
|
|
313
|
+
loadBrainData()
|
|
314
|
+
const handleRelUpdated = () => {
|
|
315
|
+
loadBrainData()
|
|
316
|
+
}
|
|
317
|
+
window.addEventListener('relationship-updated', handleRelUpdated)
|
|
318
|
+
return () => window.removeEventListener('relationship-updated', handleRelUpdated)
|
|
319
|
+
}, [loadBrainData])
|
|
320
|
+
|
|
321
|
+
// Resize canvas graph
|
|
322
|
+
useEffect(() => {
|
|
323
|
+
const updateSize = () => {
|
|
324
|
+
if (graphContainerRef.current) {
|
|
325
|
+
setGraphDimensions({
|
|
326
|
+
width: graphContainerRef.current.clientWidth,
|
|
327
|
+
height: graphContainerRef.current.clientHeight
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
updateSize()
|
|
332
|
+
window.addEventListener('resize', updateSize)
|
|
333
|
+
const timer = setTimeout(updateSize, 300)
|
|
334
|
+
return () => {
|
|
335
|
+
window.removeEventListener('resize', updateSize)
|
|
336
|
+
clearTimeout(timer)
|
|
337
|
+
}
|
|
338
|
+
}, [activeTab])
|
|
339
|
+
|
|
340
|
+
// Reset Total AI (2-step Modal Handler)
|
|
341
|
+
const handleExecuteResetAi = async () => {
|
|
342
|
+
setIsResettingAi(true)
|
|
343
|
+
try {
|
|
344
|
+
const res = await resetAiDatabase()
|
|
345
|
+
setSelectedNode(null)
|
|
346
|
+
setSelectedSkill(null)
|
|
347
|
+
await loadBrainData()
|
|
348
|
+
showToast(
|
|
349
|
+
'Seluruh data memori, sesi percakapan, dan sifat Mark berhasil direset ke kondisi awal!'
|
|
350
|
+
)
|
|
351
|
+
setTimeout(() => {
|
|
352
|
+
window.location.reload()
|
|
353
|
+
}, 1500)
|
|
354
|
+
return res?.success !== false
|
|
355
|
+
} catch (err) {
|
|
356
|
+
console.error('[NeuralCore] Gagal mereset total AI:', err)
|
|
357
|
+
showToast('Gagal mereset AI: ' + err.message)
|
|
358
|
+
return false
|
|
359
|
+
} finally {
|
|
360
|
+
setIsResettingAi(false)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Backup Database (JSON)
|
|
365
|
+
const handleExportDatabase = async () => {
|
|
366
|
+
try {
|
|
367
|
+
setIsExportingDb(true)
|
|
368
|
+
let exportData
|
|
369
|
+
if (window.api && window.api.exportDatabase) {
|
|
370
|
+
exportData = await window.api.exportDatabase()
|
|
371
|
+
} else {
|
|
372
|
+
exportData = await exportDatabaseDump()
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (!exportData) {
|
|
376
|
+
throw new Error('Data export database kosong.')
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' })
|
|
380
|
+
const url = URL.createObjectURL(blob)
|
|
381
|
+
const a = document.createElement('a')
|
|
382
|
+
a.href = url
|
|
383
|
+
a.download = `mark-full-backup-${new Date().toISOString().slice(0, 10)}.json`
|
|
384
|
+
a.click()
|
|
385
|
+
URL.revokeObjectURL(url)
|
|
386
|
+
showToast('Database berhasil diekspor ke file JSON!')
|
|
387
|
+
} catch (err) {
|
|
388
|
+
console.error('[NeuralCore] Gagal export database:', err)
|
|
389
|
+
showToast('Gagal export database: ' + err.message)
|
|
390
|
+
} finally {
|
|
391
|
+
setIsExportingDb(false)
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Restore Database (JSON)
|
|
396
|
+
const handleFileRestoreSelect = async (e) => {
|
|
397
|
+
const file = e.target.files?.[0]
|
|
398
|
+
if (!file) return
|
|
399
|
+
|
|
400
|
+
const result = await confirm({
|
|
401
|
+
title: 'Restore Database?',
|
|
402
|
+
message:
|
|
403
|
+
'Data saat ini akan ditimpa dengan data backup dari file JSON yang kamu pilih. Lanjutkan?',
|
|
404
|
+
isError: true,
|
|
405
|
+
confirmText: 'Ya, Restore Sekarang'
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
if (!result.isConfirmed) {
|
|
409
|
+
if (fileInputRef.current) fileInputRef.current.value = ''
|
|
410
|
+
return
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
setIsRestoringDb(true)
|
|
414
|
+
try {
|
|
415
|
+
const text = await file.text()
|
|
416
|
+
const dumpData = JSON.parse(text)
|
|
417
|
+
|
|
418
|
+
if (window.api && window.api.restoreDatabase) {
|
|
419
|
+
await window.api.restoreDatabase(dumpData, true)
|
|
420
|
+
} else {
|
|
421
|
+
await restoreDatabaseDump(dumpData, true)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
showToast('Database MARK berhasil dipulihkan!')
|
|
425
|
+
setTimeout(() => {
|
|
426
|
+
window.location.reload()
|
|
427
|
+
}, 1500)
|
|
428
|
+
} catch (err) {
|
|
429
|
+
console.error('[NeuralCore] Gagal restore database:', err)
|
|
430
|
+
showToast('Gagal restore database: ' + err.message)
|
|
431
|
+
} finally {
|
|
432
|
+
setIsRestoringDb(false)
|
|
433
|
+
if (fileInputRef.current) fileInputRef.current.value = ''
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Hapus Skill Action
|
|
438
|
+
const handleDeleteSkill = async (skill) => {
|
|
439
|
+
const result = await confirm({
|
|
440
|
+
title: 'Hapus Skill Yang Di Pelajari?',
|
|
441
|
+
message: `Apakah kamu yakin ingin menghapus keahlian "/${skill.name}" dari memori Mark?`,
|
|
442
|
+
isError: true,
|
|
443
|
+
confirmText: 'Hapus Keahlian'
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
if (result.isConfirmed) {
|
|
447
|
+
await deleteLearnedSkill(skill.id)
|
|
448
|
+
if (selectedSkill?.id === skill.id) setSelectedSkill(null)
|
|
449
|
+
await loadBrainData()
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Hapus Titik Memori Action
|
|
454
|
+
const executeDeleteNode = async () => {
|
|
455
|
+
const node = confirmModal.node || selectedNode
|
|
456
|
+
if (!node) return
|
|
457
|
+
setConfirmModal({ isOpen: false, node: null })
|
|
458
|
+
|
|
459
|
+
try {
|
|
460
|
+
if (node.typeLabel === 'Memori Tersimpan' || node.typeLabel === 'Explicit Memory') {
|
|
461
|
+
const id = parseInt(String(node.id).split('-')[1])
|
|
462
|
+
await deleteMemory(id)
|
|
463
|
+
} else if (node.typeLabel === 'Arsip Obrolan' || node.typeLabel === 'Chat Archive') {
|
|
464
|
+
const id = parseInt(String(node.id).split('-')[1])
|
|
465
|
+
await deleteChatArchive(id)
|
|
466
|
+
} else if (
|
|
467
|
+
node.typeLabel === 'Skill Yang Di Pelajari' ||
|
|
468
|
+
node.typeLabel === 'Learned Skill'
|
|
469
|
+
) {
|
|
470
|
+
const id = String(node.id).replace('skill-', '')
|
|
471
|
+
await deleteLearnedSkill(id)
|
|
472
|
+
}
|
|
473
|
+
setSelectedNode(null)
|
|
474
|
+
await loadBrainData()
|
|
475
|
+
} catch (err) {
|
|
476
|
+
console.error('Gagal menghapus data memori:', err)
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return (
|
|
481
|
+
<div className="h-screen w-screen bg-base-300 text-base-content overflow-hidden relative font-['Poppins',sans-serif] flex flex-col select-none">
|
|
482
|
+
{/* ── BACKGROUND AMBIENCE (Tenang & Halus) ── */}
|
|
483
|
+
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top,oklch(var(--p)/0.04)_0%,transparent_60%)] pointer-events-none" />
|
|
484
|
+
|
|
485
|
+
{/* ── 1. TOP HEADER ── */}
|
|
486
|
+
<header className="h-16 px-6 border-b border-white/5 bg-base-200/70 backdrop-blur-xl flex items-center justify-between z-30 shrink-0">
|
|
487
|
+
<div className="flex items-center gap-4">
|
|
488
|
+
<button
|
|
489
|
+
type="button"
|
|
490
|
+
onClick={() => navigate('/')}
|
|
491
|
+
className="btn btn-ghost btn-sm btn-circle shrink-0"
|
|
492
|
+
style={{ WebkitAppRegion: 'no-drag' }}
|
|
493
|
+
title="Kembali ke Dashboard Utama"
|
|
494
|
+
>
|
|
495
|
+
<svg
|
|
496
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
497
|
+
width="1.2em"
|
|
498
|
+
height="1.2em"
|
|
499
|
+
fill="none"
|
|
500
|
+
viewBox="0 0 24 24"
|
|
501
|
+
stroke="currentColor"
|
|
502
|
+
strokeWidth={2}
|
|
503
|
+
>
|
|
504
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
|
|
505
|
+
</svg>
|
|
506
|
+
</button>
|
|
507
|
+
<div>
|
|
508
|
+
<div className="flex items-center gap-2">
|
|
509
|
+
<h1 className="text-sm font-bold tracking-wider uppercase font-mono text-base-content flex items-center gap-2">
|
|
510
|
+
<FaBrain className="text-primary" /> Pusat Otak Mark
|
|
511
|
+
</h1>
|
|
512
|
+
</div>
|
|
513
|
+
<p className="text-[10px] font-mono text-base-content/50 tracking-tight">
|
|
514
|
+
STATUS SIFAT, JARINGAN MEMORI, DAN Skill Yang Di Pelajari
|
|
515
|
+
</p>
|
|
516
|
+
</div>
|
|
517
|
+
</div>
|
|
518
|
+
|
|
519
|
+
{/* Mode Switcher Buttons (Palet Bersih & Seragam) */}
|
|
520
|
+
<div className="flex items-center gap-1.5 p-1 bg-base-300/60 border border-white/5 rounded-xl">
|
|
521
|
+
<button
|
|
522
|
+
type="button"
|
|
523
|
+
onClick={() => handleTabChange('overview')}
|
|
524
|
+
className={`px-3 py-1.5 rounded-lg text-xs font-mono font-semibold transition-all cursor-pointer flex items-center gap-1.5 ${
|
|
525
|
+
activeTab === 'overview'
|
|
526
|
+
? 'bg-primary/20 text-primary border border-primary/30 shadow-sm'
|
|
527
|
+
: 'text-base-content/60 hover:text-base-content hover:bg-base-200'
|
|
528
|
+
}`}
|
|
529
|
+
>
|
|
530
|
+
<FaLayerGroup size={11} /> Semua Bagian
|
|
531
|
+
</button>
|
|
532
|
+
<button
|
|
533
|
+
type="button"
|
|
534
|
+
onClick={() => handleTabChange('personality')}
|
|
535
|
+
className={`px-3 py-1.5 rounded-lg text-xs font-mono font-semibold transition-all cursor-pointer flex items-center gap-1.5 ${
|
|
536
|
+
activeTab === 'personality'
|
|
537
|
+
? 'bg-primary/20 text-primary border border-primary/30 shadow-sm'
|
|
538
|
+
: 'text-base-content/60 hover:text-base-content hover:bg-base-200'
|
|
539
|
+
}`}
|
|
540
|
+
>
|
|
541
|
+
<FaFire size={11} /> Karakter & Emosi
|
|
542
|
+
</button>
|
|
543
|
+
<button
|
|
544
|
+
type="button"
|
|
545
|
+
onClick={() => handleTabChange('memory')}
|
|
546
|
+
className={`px-3 py-1.5 rounded-lg text-xs font-mono font-semibold transition-all cursor-pointer flex items-center gap-1.5 ${
|
|
547
|
+
activeTab === 'memory'
|
|
548
|
+
? 'bg-primary/20 text-primary border border-primary/30 shadow-sm'
|
|
549
|
+
: 'text-base-content/60 hover:text-base-content hover:bg-base-200'
|
|
550
|
+
}`}
|
|
551
|
+
>
|
|
552
|
+
<FaNetworkWired size={11} /> Peta Memori
|
|
553
|
+
</button>
|
|
554
|
+
<button
|
|
555
|
+
type="button"
|
|
556
|
+
onClick={() => handleTabChange('skills')}
|
|
557
|
+
className={`px-3 py-1.5 rounded-lg text-xs font-mono font-semibold transition-all cursor-pointer flex items-center gap-1.5 ${
|
|
558
|
+
activeTab === 'skills'
|
|
559
|
+
? 'bg-primary/20 text-primary border border-primary/30 shadow-sm'
|
|
560
|
+
: 'text-base-content/60 hover:text-base-content hover:bg-base-200'
|
|
561
|
+
}`}
|
|
562
|
+
>
|
|
563
|
+
<FaGraduationCap size={11} /> Skill Yang Di Pelajari
|
|
564
|
+
</button>
|
|
565
|
+
<button
|
|
566
|
+
type="button"
|
|
567
|
+
onClick={() => handleTabChange('journal')}
|
|
568
|
+
className={`px-3 py-1.5 rounded-lg text-xs font-mono font-semibold transition-all cursor-pointer flex items-center gap-1.5 ${
|
|
569
|
+
activeTab === 'journal'
|
|
570
|
+
? 'bg-primary/20 text-primary border border-primary/30 shadow-sm'
|
|
571
|
+
: 'text-base-content/60 hover:text-base-content hover:bg-base-200'
|
|
572
|
+
}`}
|
|
573
|
+
>
|
|
574
|
+
<FaBookOpen size={11} /> Buku Harian
|
|
575
|
+
</button>
|
|
576
|
+
</div>
|
|
577
|
+
</header>
|
|
578
|
+
|
|
579
|
+
{/* ── 2. MAIN VIEWPORT ── */}
|
|
580
|
+
<main className="flex-1 min-h-0 relative z-10 p-4 md:p-6 overflow-hidden flex flex-col">
|
|
581
|
+
{loading ? (
|
|
582
|
+
<div className="flex-1 flex flex-col items-center justify-center gap-3">
|
|
583
|
+
<span className="loading loading-spinner loading-lg text-primary" />
|
|
584
|
+
<p className="font-mono text-xs text-base-content/50 tracking-widest uppercase">
|
|
585
|
+
Memuat Data Pikiran Mark...
|
|
586
|
+
</p>
|
|
587
|
+
</div>
|
|
588
|
+
) : (
|
|
589
|
+
<>
|
|
590
|
+
{/* MODE 1: RINGKASAN SEMUA BAGIAN */}
|
|
591
|
+
{activeTab === 'overview' && (
|
|
592
|
+
<div className="flex-1 grid grid-cols-1 lg:grid-cols-12 gap-4 min-h-0 overflow-y-auto lg:overflow-hidden custom-scrollbar">
|
|
593
|
+
<EmotionalCortexLobe
|
|
594
|
+
traits={traits}
|
|
595
|
+
onNavigateToFull={() => handleTabChange('personality')}
|
|
596
|
+
onResetAi={() => setIsResetModalOpen(true)}
|
|
597
|
+
onExportDatabase={handleExportDatabase}
|
|
598
|
+
onRestoreDatabase={() => fileInputRef.current?.click()}
|
|
599
|
+
isExportingDb={isExportingDb}
|
|
600
|
+
isRestoringDb={isRestoringDb}
|
|
601
|
+
/>
|
|
602
|
+
<SynapticMemoryLobe
|
|
603
|
+
graphData={graphData}
|
|
604
|
+
graphDimensions={graphDimensions}
|
|
605
|
+
graphContainerRef={graphContainerRef}
|
|
606
|
+
fgRef={fgRef}
|
|
607
|
+
groomResult={groomResult}
|
|
608
|
+
isGrooming={isGrooming}
|
|
609
|
+
triggerGrooming={triggerGrooming}
|
|
610
|
+
loadBrainData={loadBrainData}
|
|
611
|
+
onSelectNode={(node) => setSelectedNode(node)}
|
|
612
|
+
onNavigateToFull={() => handleTabChange('memory')}
|
|
613
|
+
/>
|
|
614
|
+
<ProceduralMatrixLobe
|
|
615
|
+
learnedSkills={learnedSkills}
|
|
616
|
+
skillSearch={skillSearch}
|
|
617
|
+
setSkillSearch={setSkillSearch}
|
|
618
|
+
onSelectSkill={(skill) => setSelectedSkill(skill)}
|
|
619
|
+
onDeleteSkill={handleDeleteSkill}
|
|
620
|
+
onNavigateToFull={() => handleTabChange('skills')}
|
|
621
|
+
/>
|
|
622
|
+
</div>
|
|
623
|
+
)}
|
|
624
|
+
|
|
625
|
+
{/* MODE 2: KARAKTER & EMOSI PENUH */}
|
|
626
|
+
{activeTab === 'personality' && (
|
|
627
|
+
<EmotionalCortexLobe
|
|
628
|
+
traits={traits}
|
|
629
|
+
isFullView={true}
|
|
630
|
+
onResetAi={() => setIsResetModalOpen(true)}
|
|
631
|
+
onExportDatabase={handleExportDatabase}
|
|
632
|
+
onRestoreDatabase={() => fileInputRef.current?.click()}
|
|
633
|
+
isExportingDb={isExportingDb}
|
|
634
|
+
isRestoringDb={isRestoringDb}
|
|
635
|
+
/>
|
|
636
|
+
)}
|
|
637
|
+
|
|
638
|
+
{/* MODE 3: PETA MEMORI PENUH */}
|
|
639
|
+
{activeTab === 'memory' && (
|
|
640
|
+
<SynapticMemoryLobe
|
|
641
|
+
graphData={graphData}
|
|
642
|
+
graphDimensions={graphDimensions}
|
|
643
|
+
graphContainerRef={graphContainerRef}
|
|
644
|
+
fgRef={fgRef}
|
|
645
|
+
groomResult={groomResult}
|
|
646
|
+
isGrooming={isGrooming}
|
|
647
|
+
triggerGrooming={triggerGrooming}
|
|
648
|
+
loadBrainData={loadBrainData}
|
|
649
|
+
onSelectNode={(node) => setSelectedNode(node)}
|
|
650
|
+
isFullView={true}
|
|
651
|
+
/>
|
|
652
|
+
)}
|
|
653
|
+
|
|
654
|
+
{/* MODE 4: GUDANG KEAHLIAN PENUH */}
|
|
655
|
+
{activeTab === 'skills' && (
|
|
656
|
+
<ProceduralMatrixLobe
|
|
657
|
+
learnedSkills={learnedSkills}
|
|
658
|
+
skillSearch={skillSearch}
|
|
659
|
+
setSkillSearch={setSkillSearch}
|
|
660
|
+
onSelectSkill={(skill) => setSelectedSkill(skill)}
|
|
661
|
+
onDeleteSkill={handleDeleteSkill}
|
|
662
|
+
isFullView={true}
|
|
663
|
+
/>
|
|
664
|
+
)}
|
|
665
|
+
|
|
666
|
+
{/* MODE 5: BUKU HARIAN MARK (PRIVATE JOURNAL) */}
|
|
667
|
+
{activeTab === 'journal' && (
|
|
668
|
+
<div className="flex-1 flex flex-col min-h-0 bg-base-200/40 border border-white/5 rounded-2xl p-6 overflow-hidden">
|
|
669
|
+
<div className="flex items-center justify-between pb-4 mb-4 border-b border-white/5 shrink-0">
|
|
670
|
+
<div>
|
|
671
|
+
<h2 className="text-sm font-bold font-mono tracking-wider uppercase text-white flex items-center gap-2">
|
|
672
|
+
<FaBookOpen className="text-primary" /> Buku Harian Refleksi Mark
|
|
673
|
+
</h2>
|
|
674
|
+
<p className="text-[11px] font-mono text-white/40 mt-0.5">
|
|
675
|
+
Catatan perenungan batin dan kesan pribadi Mark mengenai hari-harinya bersama
|
|
676
|
+
Anda.
|
|
677
|
+
</p>
|
|
678
|
+
</div>
|
|
679
|
+
<span className="text-xs font-mono px-2.5 py-1 bg-white/5 rounded-lg border border-white/5 text-white/60">
|
|
680
|
+
{journalEntries.length} Catatan
|
|
681
|
+
</span>
|
|
682
|
+
</div>
|
|
683
|
+
|
|
684
|
+
<div className="flex-1 min-h-0 overflow-y-auto space-y-3 pr-1 custom-scrollbar">
|
|
685
|
+
{journalEntries.length === 0 ? (
|
|
686
|
+
<div className="h-full flex flex-col items-center justify-center text-center p-8 text-white/30 font-mono text-xs">
|
|
687
|
+
<FaBookOpen className="w-8 h-8 mb-3 opacity-20 text-primary" />
|
|
688
|
+
<p>Belum ada catatan refleksi batin.</p>
|
|
689
|
+
<p className="text-[10px] text-white/20 mt-1 max-w-sm">
|
|
690
|
+
Mark menulis buku harian secara otomatis di tengah malam atau setelah sesi
|
|
691
|
+
kerja panjang.
|
|
692
|
+
</p>
|
|
693
|
+
</div>
|
|
694
|
+
) : (
|
|
695
|
+
journalEntries.map((item) => {
|
|
696
|
+
const dateStr = item.created_at
|
|
697
|
+
? new Date(item.created_at).toLocaleDateString('id-ID', {
|
|
698
|
+
weekday: 'long',
|
|
699
|
+
year: 'numeric',
|
|
700
|
+
month: 'long',
|
|
701
|
+
day: 'numeric'
|
|
702
|
+
})
|
|
703
|
+
: 'Waktu Tidak Tercatat'
|
|
704
|
+
const timeStr = item.created_at
|
|
705
|
+
? new Date(item.created_at).toLocaleTimeString('id-ID', {
|
|
706
|
+
hour: '2-digit',
|
|
707
|
+
minute: '2-digit'
|
|
708
|
+
})
|
|
709
|
+
: ''
|
|
710
|
+
|
|
711
|
+
return (
|
|
712
|
+
<div
|
|
713
|
+
key={item.id}
|
|
714
|
+
className="p-4 rounded-xl bg-base-300/60 border border-white/5 hover:border-primary/20 transition-all flex flex-col gap-2"
|
|
715
|
+
>
|
|
716
|
+
<div className="flex items-center justify-between">
|
|
717
|
+
<span className="text-[11px] font-mono font-bold text-primary tracking-wide">
|
|
718
|
+
{dateStr} {timeStr ? `• ${timeStr}` : ''}
|
|
719
|
+
</span>
|
|
720
|
+
<span className="text-[10px] font-mono text-white/30 uppercase tracking-wider">
|
|
721
|
+
{item.summary || 'Refleksi Harian'}
|
|
722
|
+
</span>
|
|
723
|
+
</div>
|
|
724
|
+
<p className="text-xs font-mono italic text-white/80 leading-relaxed whitespace-pre-wrap">
|
|
725
|
+
“{item.memory}”
|
|
726
|
+
</p>
|
|
727
|
+
</div>
|
|
728
|
+
)
|
|
729
|
+
})
|
|
730
|
+
)}
|
|
731
|
+
</div>
|
|
732
|
+
</div>
|
|
733
|
+
)}
|
|
734
|
+
</>
|
|
735
|
+
)}
|
|
736
|
+
</main>
|
|
737
|
+
|
|
738
|
+
{/* ── 3. MODALS & HIDDEN INPUTS ── */}
|
|
739
|
+
<input
|
|
740
|
+
type="file"
|
|
741
|
+
ref={fileInputRef}
|
|
742
|
+
onChange={handleFileRestoreSelect}
|
|
743
|
+
accept=".json"
|
|
744
|
+
className="hidden"
|
|
745
|
+
/>
|
|
746
|
+
|
|
747
|
+
<ProceduralSkillModal skill={selectedSkill} onClose={() => setSelectedSkill(null)} />
|
|
748
|
+
|
|
749
|
+
<NodeDissectionModal
|
|
750
|
+
node={selectedNode}
|
|
751
|
+
onClose={() => setSelectedNode(null)}
|
|
752
|
+
onDeleteNode={(node) => setConfirmModal({ isOpen: true, node })}
|
|
753
|
+
/>
|
|
754
|
+
|
|
755
|
+
<ResetAiModal
|
|
756
|
+
isOpen={isResetModalOpen}
|
|
757
|
+
onClose={() => setIsResetModalOpen(false)}
|
|
758
|
+
onConfirm={handleExecuteResetAi}
|
|
759
|
+
isResetting={isResettingAi}
|
|
760
|
+
/>
|
|
761
|
+
|
|
762
|
+
<ConfirmModal
|
|
763
|
+
isOpen={confirmModal.isOpen}
|
|
764
|
+
title="Hapus Data Memori?"
|
|
765
|
+
message={`Apakah kamu yakin ingin menghapus data memori ini secara permanen dari ingatan Mark?\n"${confirmModal.node?.name || ''}"`}
|
|
766
|
+
onConfirm={executeDeleteNode}
|
|
767
|
+
onCancel={() => setConfirmModal({ isOpen: false, node: null })}
|
|
768
|
+
confirmText="Hapus Permanen"
|
|
769
|
+
cancelText="Batal"
|
|
770
|
+
isError={true}
|
|
771
|
+
/>
|
|
772
|
+
<ModalComponent />
|
|
773
|
+
|
|
774
|
+
{/* ── TOAST NOTIFIKASI FEEDBACK ── */}
|
|
775
|
+
{toastMessage && (
|
|
776
|
+
<div className="toast toast-top toast-end z-[9999] animate-fade-in pointer-events-none">
|
|
777
|
+
<div className="alert alert-success shadow-2xl rounded-2xl border border-success/30 flex items-center gap-3 py-3 px-4 backdrop-blur-md bg-success/20 text-success-content">
|
|
778
|
+
<span className="text-xs font-mono font-medium">{toastMessage}</span>
|
|
779
|
+
</div>
|
|
780
|
+
</div>
|
|
781
|
+
)}
|
|
782
|
+
</div>
|
|
783
|
+
)
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
export default NeuralCore
|