@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,193 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
FaGraduationCap,
|
|
4
|
+
FaSearch,
|
|
5
|
+
FaEye,
|
|
6
|
+
FaTrash,
|
|
7
|
+
FaBookOpen,
|
|
8
|
+
FaChevronRight
|
|
9
|
+
} from 'react-icons/fa'
|
|
10
|
+
|
|
11
|
+
export const ProceduralMatrixLobe = ({
|
|
12
|
+
learnedSkills = [],
|
|
13
|
+
skillSearch = '',
|
|
14
|
+
setSkillSearch,
|
|
15
|
+
onSelectSkill,
|
|
16
|
+
onDeleteSkill,
|
|
17
|
+
isFullView = false,
|
|
18
|
+
onNavigateToFull
|
|
19
|
+
}) => {
|
|
20
|
+
const filteredSkills = learnedSkills.filter(
|
|
21
|
+
(s) =>
|
|
22
|
+
s.name?.toLowerCase().includes(skillSearch.toLowerCase()) ||
|
|
23
|
+
(s.description && s.description.toLowerCase().includes(skillSearch.toLowerCase()))
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
if (isFullView) {
|
|
27
|
+
return (
|
|
28
|
+
<div className="flex-1 flex flex-col gap-4 min-h-0 overflow-hidden max-w-6xl mx-auto w-full pb-8">
|
|
29
|
+
{/* Header Vault Toolbar */}
|
|
30
|
+
<div className="p-4 bg-base-200/60 border border-white/5 rounded-2xl flex flex-wrap items-center justify-between gap-4 backdrop-blur-md shadow-lg">
|
|
31
|
+
<div>
|
|
32
|
+
<h2 className="text-sm font-bold font-mono text-base-content flex items-center gap-2 uppercase tracking-wider">
|
|
33
|
+
<FaGraduationCap className="text-primary" /> Learned Skill
|
|
34
|
+
</h2>
|
|
35
|
+
<p className="text-xs text-base-content/60 mt-0.5">
|
|
36
|
+
Skill yang dipelajari dan dirumuskan sendiri oleh Mark saat menyelesaikan tugas
|
|
37
|
+
sebelumnya.
|
|
38
|
+
</p>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
{/* Search Bar */}
|
|
42
|
+
<div className="relative w-full sm:w-72">
|
|
43
|
+
<FaSearch className="absolute left-3 top-1/2 -translate-y-1/2 text-base-content/40 text-xs" />
|
|
44
|
+
<input
|
|
45
|
+
type="text"
|
|
46
|
+
value={skillSearch}
|
|
47
|
+
onChange={(e) => setSkillSearch(e.target.value)}
|
|
48
|
+
placeholder="Cari keahlian atau panduan..."
|
|
49
|
+
className="w-full bg-base-300/60 border border-white/10 rounded-xl pl-9 pr-3 py-2 text-xs text-base-content placeholder:text-base-content/30 outline-none focus:border-primary/50 font-mono"
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
{/* Skills Card Grid */}
|
|
55
|
+
<div className="flex-1 overflow-y-auto pr-1 custom-scrollbar">
|
|
56
|
+
{filteredSkills.length === 0 ? (
|
|
57
|
+
<div className="h-64 flex flex-col items-center justify-center text-center p-6 bg-base-200/40 border border-white/5 rounded-2xl">
|
|
58
|
+
<FaGraduationCap className="text-4xl text-base-content/20 mb-3" />
|
|
59
|
+
<h3 className="text-sm font-bold font-mono text-base-content/80">
|
|
60
|
+
Belum Ada Keahlian Baru
|
|
61
|
+
</h3>
|
|
62
|
+
<p className="text-xs text-base-content/50 max-w-md mt-1 leading-relaxed">
|
|
63
|
+
Mark akan secara otomatis mencatat keahlian baru ke daftar ini ketika berhasil
|
|
64
|
+
menyelesaikan tugas teknis yang berulang atau rumit.
|
|
65
|
+
</p>
|
|
66
|
+
</div>
|
|
67
|
+
) : (
|
|
68
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
69
|
+
{filteredSkills.map((skill) => (
|
|
70
|
+
<div
|
|
71
|
+
key={skill.id}
|
|
72
|
+
className="p-5 bg-base-200/50 hover:bg-base-200/80 border border-white/5 hover:border-primary/30 rounded-2xl backdrop-blur-md transition-all flex flex-col justify-between group shadow-md"
|
|
73
|
+
>
|
|
74
|
+
<div className="flex flex-col gap-3 pb-3">
|
|
75
|
+
<div className="flex items-center justify-between">
|
|
76
|
+
<span className="font-mono font-bold text-sm text-base-content flex items-center gap-1.5 uppercase">
|
|
77
|
+
{skill.name}
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
<p className="text-xs text-base-content/70 leading-relaxed line-clamp-3">
|
|
81
|
+
{skill.description || 'Tidak ada deskripsi singkat.'}
|
|
82
|
+
</p>
|
|
83
|
+
<span className="text-[10px] font-mono text-base-content/40">
|
|
84
|
+
{new Date(skill.updatedAt || Date.now()).toLocaleDateString('id-ID', {
|
|
85
|
+
dateStyle: 'medium'
|
|
86
|
+
})}
|
|
87
|
+
</span>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div className="flex items-center justify-between pt-3 border-t border-white/5">
|
|
91
|
+
<button
|
|
92
|
+
type="button"
|
|
93
|
+
onClick={() => onSelectSkill(skill)}
|
|
94
|
+
className="px-3 py-1.5 bg-primary/10 hover:bg-primary/20 text-primary border border-primary/20 rounded-xl text-xs font-mono font-semibold transition-all flex items-center gap-1.5 cursor-pointer"
|
|
95
|
+
>
|
|
96
|
+
<FaEye size={12} /> Buka Panduan
|
|
97
|
+
</button>
|
|
98
|
+
<button
|
|
99
|
+
type="button"
|
|
100
|
+
onClick={() => onDeleteSkill(skill)}
|
|
101
|
+
className="p-2 text-base-content/40 hover:text-error hover:bg-error/10 rounded-xl transition-all cursor-pointer"
|
|
102
|
+
title="Hapus Skill"
|
|
103
|
+
>
|
|
104
|
+
<FaTrash size={12} />
|
|
105
|
+
</button>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
))}
|
|
109
|
+
</div>
|
|
110
|
+
)}
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Embedded Column
|
|
117
|
+
return (
|
|
118
|
+
<div className="lg:col-span-3 bg-base-200/50 backdrop-blur-md border border-white/5 rounded-2xl p-4 flex flex-col min-h-0 overflow-hidden shadow-xl">
|
|
119
|
+
<div className="flex items-center justify-between border-b border-white/5 pb-3 mb-3">
|
|
120
|
+
<div className="flex items-center gap-2 text-sm font-bold font-mono text-base-content">
|
|
121
|
+
<FaGraduationCap className="text-primary" /> Skill Yang Di Pelajari
|
|
122
|
+
</div>
|
|
123
|
+
{onNavigateToFull && (
|
|
124
|
+
<button
|
|
125
|
+
type="button"
|
|
126
|
+
onClick={onNavigateToFull}
|
|
127
|
+
className="text-[10px] font-mono text-base-content/50 hover:text-base-content flex items-center gap-1 cursor-pointer transition-colors"
|
|
128
|
+
>
|
|
129
|
+
Lihat Semua <FaChevronRight size={8} />
|
|
130
|
+
</button>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
{/* Search Mini */}
|
|
135
|
+
<div className="relative mb-3">
|
|
136
|
+
<FaSearch className="absolute left-3 top-1/2 -translate-y-1/2 text-base-content/30 text-xs" />
|
|
137
|
+
<input
|
|
138
|
+
type="text"
|
|
139
|
+
value={skillSearch}
|
|
140
|
+
onChange={(e) => setSkillSearch(e.target.value)}
|
|
141
|
+
placeholder="Cari keahlian..."
|
|
142
|
+
className="w-full bg-base-300/60 border border-white/10 rounded-xl pl-8 pr-3 py-1.5 text-xs text-base-content placeholder:text-base-content/30 outline-none focus:border-primary/50 font-mono"
|
|
143
|
+
/>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
{/* Skills List */}
|
|
147
|
+
<div className="flex-1 overflow-y-auto space-y-2 pr-1 custom-scrollbar">
|
|
148
|
+
{filteredSkills.length === 0 ? (
|
|
149
|
+
<div className="h-full flex flex-col items-center justify-center p-4 text-center text-base-content/40 text-xs">
|
|
150
|
+
<FaBookOpen className="text-2xl mb-2 text-base-content/20" />
|
|
151
|
+
<p>Belum ada keahlian yang dipelajari.</p>
|
|
152
|
+
</div>
|
|
153
|
+
) : (
|
|
154
|
+
filteredSkills.map((skill) => (
|
|
155
|
+
<div
|
|
156
|
+
key={skill.id}
|
|
157
|
+
className="p-2.5 bg-base-300/40 hover:bg-base-300/70 border border-white/5 hover:border-primary/20 rounded-xl transition-all flex flex-col gap-1 group"
|
|
158
|
+
>
|
|
159
|
+
<div className="flex items-center justify-between">
|
|
160
|
+
<span className="font-mono font-bold uppercase text-xs text-base-content/90">
|
|
161
|
+
{skill.name}
|
|
162
|
+
</span>
|
|
163
|
+
<div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
164
|
+
<button
|
|
165
|
+
type="button"
|
|
166
|
+
onClick={() => onSelectSkill(skill)}
|
|
167
|
+
className="p-1 text-base-content/60 hover:text-primary transition-colors"
|
|
168
|
+
title="Buka Detail Panduan"
|
|
169
|
+
>
|
|
170
|
+
<FaEye size={11} />
|
|
171
|
+
</button>
|
|
172
|
+
<button
|
|
173
|
+
type="button"
|
|
174
|
+
onClick={() => onDeleteSkill(skill)}
|
|
175
|
+
className="p-1 text-base-content/40 hover:text-error transition-colors"
|
|
176
|
+
title="Hapus"
|
|
177
|
+
>
|
|
178
|
+
<FaTrash size={10} />
|
|
179
|
+
</button>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
<p className="text-[11px] text-base-content/60 line-clamp-2 leading-tight">
|
|
183
|
+
{skill.description}
|
|
184
|
+
</p>
|
|
185
|
+
</div>
|
|
186
|
+
))
|
|
187
|
+
)}
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export default ProceduralMatrixLobe
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import ReactMarkdown from 'react-markdown'
|
|
3
|
+
import remarkGfm from 'remark-gfm'
|
|
4
|
+
import { FaGraduationCap, FaTimes } from 'react-icons/fa'
|
|
5
|
+
import rehypeExternalLinks from 'rehype-external-links'
|
|
6
|
+
import { CodeBlock } from '../Chat/CodeBlock'
|
|
7
|
+
|
|
8
|
+
export const ProceduralSkillModal = ({ skill, onClose }) => {
|
|
9
|
+
if (!skill) return null
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xl flex items-center justify-center p-4 animate-fade-in">
|
|
13
|
+
<div className="w-full max-w-3xl max-h-[85vh] bg-base-200 border border-white/10 rounded-2xl flex flex-col shadow-2xl overflow-hidden">
|
|
14
|
+
{/* Header */}
|
|
15
|
+
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between bg-base-300/60">
|
|
16
|
+
<div className="flex items-center gap-3">
|
|
17
|
+
<div className="w-8 h-8 rounded-xl bg-primary/10 border border-primary/20 flex items-center justify-center text-primary">
|
|
18
|
+
<FaGraduationCap size={15} />
|
|
19
|
+
</div>
|
|
20
|
+
<div>
|
|
21
|
+
<h3 className="font-mono font-bold text-sm text-base-content uppercase">
|
|
22
|
+
{skill.name}
|
|
23
|
+
</h3>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
<button
|
|
27
|
+
type="button"
|
|
28
|
+
onClick={onClose}
|
|
29
|
+
className="w-8 h-8 rounded-xl bg-base-300 hover:bg-base-100 flex items-center justify-center text-base-content/50 hover:text-base-content transition-all cursor-pointer"
|
|
30
|
+
>
|
|
31
|
+
<FaTimes size={13} />
|
|
32
|
+
</button>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div className="flex-1 overflow-y-auto p-6 font-sans text-sm text-base-content/90 custom-scrollbar leading-relaxed custom-markdown">
|
|
36
|
+
<ReactMarkdown
|
|
37
|
+
remarkPlugins={[remarkGfm]}
|
|
38
|
+
rehypePlugins={[
|
|
39
|
+
[rehypeExternalLinks, { target: '_blank', rel: ['noopener', 'noreferrer'] }]
|
|
40
|
+
]}
|
|
41
|
+
components={{
|
|
42
|
+
code: CodeBlock
|
|
43
|
+
}}
|
|
44
|
+
>
|
|
45
|
+
{skill.content || '*Tidak ada konten instruksi Markdown.*'}
|
|
46
|
+
</ReactMarkdown>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default ProceduralSkillModal
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react'
|
|
2
|
+
import { createPortal } from 'react-dom'
|
|
3
|
+
import { FaTrash, FaCheckCircle, FaShieldAlt } from 'react-icons/fa'
|
|
4
|
+
|
|
5
|
+
export const ResetAiModal = ({ isOpen, onClose, onConfirm, isResetting = false }) => {
|
|
6
|
+
const [step, setStep] = useState(1)
|
|
7
|
+
const [inputText, setInputText] = useState('')
|
|
8
|
+
const [isSuccess, setIsSuccess] = useState(false)
|
|
9
|
+
const CONFIRM_PHRASE = 'RESET MARK'
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const handleKeyDown = (e) => {
|
|
13
|
+
if (e.key === 'Escape' && isOpen && !isResetting) {
|
|
14
|
+
handleClose()
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (isOpen) {
|
|
18
|
+
window.addEventListener('keydown', handleKeyDown)
|
|
19
|
+
}
|
|
20
|
+
return () => window.removeEventListener('keydown', handleKeyDown)
|
|
21
|
+
}, [isOpen, isResetting])
|
|
22
|
+
|
|
23
|
+
if (!isOpen) return null
|
|
24
|
+
|
|
25
|
+
const handleClose = () => {
|
|
26
|
+
if (isResetting) return
|
|
27
|
+
setStep(1)
|
|
28
|
+
setInputText('')
|
|
29
|
+
setIsSuccess(false)
|
|
30
|
+
onClose?.()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const handleNextStep = () => {
|
|
34
|
+
setStep(2)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const handleExecuteReset = async () => {
|
|
38
|
+
if (inputText.trim() !== CONFIRM_PHRASE) return
|
|
39
|
+
const success = await onConfirm()
|
|
40
|
+
if (success !== false) {
|
|
41
|
+
setIsSuccess(true)
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
handleClose()
|
|
44
|
+
}, 1600)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const modalContent = (
|
|
49
|
+
<div className="modal modal-open z-[99999] fixed inset-0 flex items-center justify-center bg-black/70 backdrop-blur-md animate-[response-fade-in_0.15s_ease-out_forwards]">
|
|
50
|
+
<div className="modal-box relative bg-base-300 border border-white/10 shadow-2xl z-10 max-w-md">
|
|
51
|
+
{/* Success State */}
|
|
52
|
+
{isSuccess ? (
|
|
53
|
+
<div className="py-6 flex flex-col items-center justify-center text-center gap-3">
|
|
54
|
+
<div className="w-14 h-14 rounded-full bg-success/20 text-success flex items-center justify-center border border-success/30 shadow-lg">
|
|
55
|
+
<FaCheckCircle className="text-2xl" />
|
|
56
|
+
</div>
|
|
57
|
+
<div>
|
|
58
|
+
<h3 className="font-bold text-lg text-success">Reset AI Berhasil</h3>
|
|
59
|
+
<p className="py-2 text-sm opacity-80">
|
|
60
|
+
Seluruh memori, riwayat chat, dan sifat Mark telah dikembalikan ke kondisi awal.
|
|
61
|
+
</p>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
) : (
|
|
65
|
+
<>
|
|
66
|
+
{/* Header Title */}
|
|
67
|
+
<h3 className="font-bold text-lg text-error">Reset Total AI (Tahap {step}/2)</h3>
|
|
68
|
+
|
|
69
|
+
{/* Step 1: Warning Details */}
|
|
70
|
+
{step === 1 && (
|
|
71
|
+
<div className="py-3 space-y-3">
|
|
72
|
+
<p className="text-sm opacity-90">
|
|
73
|
+
Apakah Anda yakin ingin mereset seluruh data memori dan riwayat Mark?
|
|
74
|
+
</p>
|
|
75
|
+
<div className="p-3 bg-base-200/80 rounded-xl border border-white/5 space-y-1.5 text-xs">
|
|
76
|
+
<div className="text-error font-semibold flex items-center gap-1.5">
|
|
77
|
+
<FaTrash size={10} /> Data yang akan dihapus:
|
|
78
|
+
</div>
|
|
79
|
+
<ul className="list-disc list-inside opacity-70 space-y-0.5 pl-1">
|
|
80
|
+
<li>Seluruh riwayat obrolan & sesi percakapan</li>
|
|
81
|
+
<li>Seluruh memori eksplisit (Vector RAG)</li>
|
|
82
|
+
<li>Seluruh potongan dokumen tersimpan</li>
|
|
83
|
+
<li>Seluruh skill yang dipelajari (Learned Skills)</li>
|
|
84
|
+
<li>Status sifat & karakter (kembali ke default netral)</li>
|
|
85
|
+
<li>Riwayat tugas mandiri & sub-agen</li>
|
|
86
|
+
</ul>
|
|
87
|
+
<div className="pt-2 text-primary font-semibold flex items-center gap-1.5 border-t border-white/5">
|
|
88
|
+
<FaShieldAlt size={10} /> Data yang dipertahankan:
|
|
89
|
+
</div>
|
|
90
|
+
<p className="opacity-70 pl-1">
|
|
91
|
+
Pengaturan API Key, model provider, prompt, dan konfigurasi umum sistem tetap
|
|
92
|
+
aman.
|
|
93
|
+
</p>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div className="modal-action mt-4">
|
|
97
|
+
<button
|
|
98
|
+
type="button"
|
|
99
|
+
className="btn btn-ghost btn-sm"
|
|
100
|
+
onClick={(e) => {
|
|
101
|
+
e.preventDefault()
|
|
102
|
+
e.stopPropagation()
|
|
103
|
+
handleClose()
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
Batal
|
|
107
|
+
</button>
|
|
108
|
+
<button
|
|
109
|
+
type="button"
|
|
110
|
+
className="btn btn-error btn-sm shadow-md"
|
|
111
|
+
onClick={(e) => {
|
|
112
|
+
e.preventDefault()
|
|
113
|
+
e.stopPropagation()
|
|
114
|
+
handleNextStep()
|
|
115
|
+
}}
|
|
116
|
+
>
|
|
117
|
+
Lanjutkan ke Konfirmasi Terakhir
|
|
118
|
+
</button>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
)}
|
|
122
|
+
|
|
123
|
+
{/* Step 2: Final Verification via Typing */}
|
|
124
|
+
{step === 2 && (
|
|
125
|
+
<div className="py-3 space-y-3">
|
|
126
|
+
<p className="text-sm opacity-90">
|
|
127
|
+
Tindakan ini <strong className="text-error">tidak dapat dibatalkan</strong>. Untuk
|
|
128
|
+
mengonfirmasi penghapusan seluruh memori Mark, ketik teks di bawah:
|
|
129
|
+
</p>
|
|
130
|
+
|
|
131
|
+
<div className="p-3 bg-base-200 rounded-xl border border-error/20 text-center font-mono font-bold text-sm tracking-widest text-error select-all">
|
|
132
|
+
{CONFIRM_PHRASE}
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div>
|
|
136
|
+
<label className="block text-xs opacity-60 mb-1">
|
|
137
|
+
Ketik ulang kata konfirmasi:
|
|
138
|
+
</label>
|
|
139
|
+
<input
|
|
140
|
+
type="text"
|
|
141
|
+
autoFocus
|
|
142
|
+
value={inputText}
|
|
143
|
+
onChange={(e) => setInputText(e.target.value)}
|
|
144
|
+
placeholder="Ketik RESET MARK di sini"
|
|
145
|
+
className="w-full bg-base-200 border border-white/10 focus:border-error rounded-xl px-3 py-2 text-xs font-mono text-base-content outline-none transition-colors uppercase"
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<div className="modal-action mt-4">
|
|
150
|
+
<button
|
|
151
|
+
type="button"
|
|
152
|
+
disabled={isResetting}
|
|
153
|
+
className="btn btn-ghost btn-sm"
|
|
154
|
+
onClick={(e) => {
|
|
155
|
+
e.preventDefault()
|
|
156
|
+
e.stopPropagation()
|
|
157
|
+
setStep(1)
|
|
158
|
+
}}
|
|
159
|
+
>
|
|
160
|
+
Kembali
|
|
161
|
+
</button>
|
|
162
|
+
<button
|
|
163
|
+
type="button"
|
|
164
|
+
disabled={inputText.trim() !== CONFIRM_PHRASE || isResetting}
|
|
165
|
+
className="btn btn-error btn-sm shadow-md gap-1.5"
|
|
166
|
+
onClick={(e) => {
|
|
167
|
+
e.preventDefault()
|
|
168
|
+
e.stopPropagation()
|
|
169
|
+
handleExecuteReset()
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
172
|
+
{isResetting && <span className="loading loading-spinner loading-xs" />}
|
|
173
|
+
<span>{isResetting ? 'Mereset...' : 'Hapus & Reset AI'}</span>
|
|
174
|
+
</button>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
)}
|
|
178
|
+
</>
|
|
179
|
+
)}
|
|
180
|
+
</div>
|
|
181
|
+
<div
|
|
182
|
+
className="modal-backdrop fixed inset-0 cursor-pointer bg-transparent"
|
|
183
|
+
onClick={(e) => {
|
|
184
|
+
e.preventDefault()
|
|
185
|
+
e.stopPropagation()
|
|
186
|
+
handleClose()
|
|
187
|
+
}}
|
|
188
|
+
/>
|
|
189
|
+
</div>
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
if (typeof document !== 'undefined') {
|
|
193
|
+
return createPortal(modalContent, document.body)
|
|
194
|
+
}
|
|
195
|
+
return modalContent
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export default ResetAiModal
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import React, { useEffect } from 'react'
|
|
2
|
+
import ForceGraph2D from 'react-force-graph-2d'
|
|
3
|
+
import { FaNetworkWired, FaChevronRight } from 'react-icons/fa'
|
|
4
|
+
import { FiCheckCircle, FiRefreshCw } from 'react-icons/fi'
|
|
5
|
+
|
|
6
|
+
export const SynapticMemoryLobe = ({
|
|
7
|
+
graphData,
|
|
8
|
+
graphDimensions,
|
|
9
|
+
graphContainerRef,
|
|
10
|
+
fgRef,
|
|
11
|
+
groomResult,
|
|
12
|
+
isGrooming,
|
|
13
|
+
triggerGrooming,
|
|
14
|
+
loadBrainData,
|
|
15
|
+
onSelectNode,
|
|
16
|
+
isFullView = false,
|
|
17
|
+
onNavigateToFull
|
|
18
|
+
}) => {
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (fgRef.current) {
|
|
21
|
+
fgRef.current.d3Force('charge').strength(-150)
|
|
22
|
+
fgRef.current.d3Force('link').distance(40)
|
|
23
|
+
}
|
|
24
|
+
}, [graphData, fgRef])
|
|
25
|
+
|
|
26
|
+
if (isFullView) {
|
|
27
|
+
return (
|
|
28
|
+
<div
|
|
29
|
+
ref={graphContainerRef}
|
|
30
|
+
className="flex-1 w-full h-full bg-base-200/50 backdrop-blur-md border border-white/5 rounded-2xl relative overflow-hidden shadow-2xl flex flex-col"
|
|
31
|
+
>
|
|
32
|
+
{/* Memory Grooming Control Toolbar */}
|
|
33
|
+
<div className="absolute top-4 left-4 z-20 flex flex-wrap items-center gap-3 p-2.5 bg-base-300/90 backdrop-blur-md border border-white/10 rounded-xl text-xs shadow-lg">
|
|
34
|
+
<div className="flex items-center gap-2 px-2 text-base-content/70 font-mono text-[11px]">
|
|
35
|
+
<FiCheckCircle className="text-primary" />
|
|
36
|
+
<span>
|
|
37
|
+
Pembersih Memori:{' '}
|
|
38
|
+
{groomResult.lastChecked
|
|
39
|
+
? `Terakhir dirapikan ${new Date(groomResult.lastChecked).toLocaleTimeString([], {
|
|
40
|
+
hour: '2-digit',
|
|
41
|
+
minute: '2-digit'
|
|
42
|
+
})}`
|
|
43
|
+
: 'Siap merapikan data'}
|
|
44
|
+
</span>
|
|
45
|
+
</div>
|
|
46
|
+
{(groomResult.mergedCount > 0 || groomResult.deletedCount > 0) && (
|
|
47
|
+
<div className="flex items-center gap-2 pl-2 border-l border-white/10 font-mono text-[10px] text-base-content/70">
|
|
48
|
+
<span>{groomResult.mergedCount} digabung</span>
|
|
49
|
+
<span>{groomResult.deletedCount} dibersihkan</span>
|
|
50
|
+
</div>
|
|
51
|
+
)}
|
|
52
|
+
<button
|
|
53
|
+
type="button"
|
|
54
|
+
onClick={async () => {
|
|
55
|
+
await triggerGrooming(true)
|
|
56
|
+
await loadBrainData()
|
|
57
|
+
}}
|
|
58
|
+
disabled={isGrooming}
|
|
59
|
+
className="btn btn-xs btn-primary rounded-lg font-mono flex items-center gap-1.5"
|
|
60
|
+
>
|
|
61
|
+
<FiRefreshCw className={isGrooming ? 'animate-spin' : ''} />
|
|
62
|
+
<span>{isGrooming ? 'Sedang Merapikan...' : 'Rapikan & Bersihkan Duplikat'}</span>
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div className="flex-1 w-full h-full cursor-crosshair">
|
|
67
|
+
<ForceGraph2D
|
|
68
|
+
ref={fgRef}
|
|
69
|
+
width={graphDimensions.width}
|
|
70
|
+
height={graphDimensions.height}
|
|
71
|
+
graphData={graphData}
|
|
72
|
+
nodeLabel="name"
|
|
73
|
+
nodeColor={(node) => node.color}
|
|
74
|
+
nodeRelSize={4}
|
|
75
|
+
linkColor={() => 'rgba(255,255,255,0.15)'}
|
|
76
|
+
linkWidth={(link) => (link.source.id === 'core' || link.source === 'core' ? 2 : 1)}
|
|
77
|
+
linkCurvature={0.25}
|
|
78
|
+
linkDirectionalParticles={2}
|
|
79
|
+
linkDirectionalParticleWidth={1.5}
|
|
80
|
+
linkDirectionalParticleSpeed={0.005}
|
|
81
|
+
cooldownTicks={60}
|
|
82
|
+
onEngineStop={() => fgRef.current?.zoomToFit(400, 50)}
|
|
83
|
+
d3VelocityDecay={0.3}
|
|
84
|
+
onNodeClick={(node) => {
|
|
85
|
+
if (node.group === 3) {
|
|
86
|
+
onSelectNode(node)
|
|
87
|
+
fgRef.current?.centerAt(node.x, node.y, 1000)
|
|
88
|
+
fgRef.current?.zoom(3, 1000)
|
|
89
|
+
} else {
|
|
90
|
+
fgRef.current?.centerAt(node.x, node.y, 1000)
|
|
91
|
+
fgRef.current?.zoom(2.5, 1000)
|
|
92
|
+
}
|
|
93
|
+
}}
|
|
94
|
+
nodeCanvasObjectMode={() => 'after'}
|
|
95
|
+
nodeCanvasObject={(node, ctx, globalScale) => {
|
|
96
|
+
if (node.group === 0 || node.group === 1 || node.group === 2) {
|
|
97
|
+
const fontSize =
|
|
98
|
+
node.group === 0 ? 15 / globalScale : node.group === 1 ? 13 / globalScale : 10 / globalScale
|
|
99
|
+
if (globalScale > 0.5) {
|
|
100
|
+
ctx.font = `${fontSize}px Sans-Serif`
|
|
101
|
+
ctx.textAlign = 'center'
|
|
102
|
+
ctx.textBaseline = 'middle'
|
|
103
|
+
ctx.fillStyle = 'rgba(255,255,255,0.85)'
|
|
104
|
+
ctx.fillText(node.name, node.x, node.y + node.val + 8 / globalScale)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Embedded Column
|
|
115
|
+
return (
|
|
116
|
+
<div
|
|
117
|
+
ref={graphContainerRef}
|
|
118
|
+
className="lg:col-span-5 bg-base-200/50 backdrop-blur-md border border-white/5 rounded-2xl relative flex flex-col min-h-[380px] lg:min-h-0 overflow-hidden shadow-xl"
|
|
119
|
+
>
|
|
120
|
+
<div className="absolute top-3 left-4 z-20 flex items-center justify-between right-4 pointer-events-none">
|
|
121
|
+
<div className="flex items-center gap-2 text-xs font-bold font-mono text-base-content bg-base-300/80 backdrop-blur-md px-3 py-1.5 rounded-xl border border-white/10 shadow-md">
|
|
122
|
+
<FaNetworkWired className="text-primary" /> PETA JARINGAN MEMORI
|
|
123
|
+
</div>
|
|
124
|
+
{onNavigateToFull && (
|
|
125
|
+
<button
|
|
126
|
+
type="button"
|
|
127
|
+
onClick={onNavigateToFull}
|
|
128
|
+
className="pointer-events-auto text-[10px] font-mono text-base-content/60 hover:text-base-content bg-base-300/80 px-2.5 py-1.5 rounded-xl border border-white/10 flex items-center gap-1 shadow-md cursor-pointer transition-colors"
|
|
129
|
+
>
|
|
130
|
+
Layar Penuh <FaChevronRight size={8} />
|
|
131
|
+
</button>
|
|
132
|
+
)}
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
{/* Mini Trigger Bar */}
|
|
136
|
+
<div className="absolute bottom-3 left-3 right-3 z-20 flex items-center justify-between p-2.5 rounded-xl bg-base-300/90 backdrop-blur-md border border-white/10 text-xs text-base-content/80 shadow-md">
|
|
137
|
+
<div className="flex items-center gap-1.5 text-[11px] font-mono text-primary">
|
|
138
|
+
<FiCheckCircle className="text-primary" />
|
|
139
|
+
<span>{groomResult.lastChecked ? 'Memori Rapi' : 'Siap Dirapikan'}</span>
|
|
140
|
+
</div>
|
|
141
|
+
<button
|
|
142
|
+
type="button"
|
|
143
|
+
onClick={async () => {
|
|
144
|
+
await triggerGrooming(true)
|
|
145
|
+
await loadBrainData()
|
|
146
|
+
}}
|
|
147
|
+
disabled={isGrooming}
|
|
148
|
+
className="btn btn-xs btn-primary font-mono text-[10px] flex items-center gap-1"
|
|
149
|
+
>
|
|
150
|
+
<FiRefreshCw className={isGrooming ? 'animate-spin' : ''} />
|
|
151
|
+
<span>{isGrooming ? 'Merapikan...' : 'Rapikan Memori'}</span>
|
|
152
|
+
</button>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
{/* 2D Canvas ForceGraph */}
|
|
156
|
+
<div className="flex-1 w-full h-full cursor-crosshair">
|
|
157
|
+
<ForceGraph2D
|
|
158
|
+
ref={fgRef}
|
|
159
|
+
width={graphDimensions.width}
|
|
160
|
+
height={graphDimensions.height}
|
|
161
|
+
graphData={graphData}
|
|
162
|
+
nodeLabel="name"
|
|
163
|
+
nodeColor={(node) => node.color}
|
|
164
|
+
nodeRelSize={4}
|
|
165
|
+
linkColor={() => 'rgba(255,255,255,0.08)'}
|
|
166
|
+
linkWidth={(link) => (link.source.id === 'core' || link.source === 'core' ? 1.5 : 1)}
|
|
167
|
+
linkCurvature={0.2}
|
|
168
|
+
linkDirectionalParticles={1}
|
|
169
|
+
linkDirectionalParticleWidth={1.2}
|
|
170
|
+
linkDirectionalParticleSpeed={0.004}
|
|
171
|
+
cooldownTicks={60}
|
|
172
|
+
onEngineStop={() => fgRef.current?.zoomToFit(400, 50)}
|
|
173
|
+
d3VelocityDecay={0.3}
|
|
174
|
+
onNodeClick={(node) => {
|
|
175
|
+
if (node.group === 3) {
|
|
176
|
+
onSelectNode(node)
|
|
177
|
+
fgRef.current?.centerAt(node.x, node.y, 1000)
|
|
178
|
+
fgRef.current?.zoom(3, 1000)
|
|
179
|
+
} else {
|
|
180
|
+
fgRef.current?.centerAt(node.x, node.y, 1000)
|
|
181
|
+
fgRef.current?.zoom(2.5, 1000)
|
|
182
|
+
}
|
|
183
|
+
}}
|
|
184
|
+
nodeCanvasObjectMode={() => 'after'}
|
|
185
|
+
nodeCanvasObject={(node, ctx, globalScale) => {
|
|
186
|
+
if (node.group === 0 || node.group === 1 || node.group === 2) {
|
|
187
|
+
const fontSize =
|
|
188
|
+
node.group === 0 ? 15 / globalScale : node.group === 1 ? 13 / globalScale : 10 / globalScale
|
|
189
|
+
if (globalScale > 0.5) {
|
|
190
|
+
ctx.font = `${fontSize}px Sans-Serif`
|
|
191
|
+
ctx.textAlign = 'center'
|
|
192
|
+
ctx.textBaseline = 'middle'
|
|
193
|
+
ctx.fillStyle = 'rgba(255,255,255,0.85)'
|
|
194
|
+
ctx.fillText(node.name, node.x, node.y + node.val + 8 / globalScale)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}}
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export default SynapticMemoryLobe
|