@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.
Files changed (227) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +209 -0
  3. package/bin/mark.js +27 -0
  4. package/out/renderer/assets/icon-DHo2CYph.svg +1 -0
  5. package/out/renderer/assets/index-G2fADWO-.js +4768 -0
  6. package/out/renderer/assets/index-f3mVse3c.css +1 -0
  7. package/out/renderer/assets/music-cover-CG4vi3kZ.png +0 -0
  8. package/out/renderer/assets/poppins-devanagari-400-normal-CJDn6rn8.woff2 +0 -0
  9. package/out/renderer/assets/poppins-devanagari-400-normal-CqVvlrh5.woff +0 -0
  10. package/out/renderer/assets/poppins-devanagari-500-normal-BIdkeU1p.woff2 +0 -0
  11. package/out/renderer/assets/poppins-devanagari-500-normal-DMPDjHtT.woff +0 -0
  12. package/out/renderer/assets/poppins-devanagari-600-normal-ClASKHrr.woff +0 -0
  13. package/out/renderer/assets/poppins-devanagari-600-normal-STEjXBNN.woff2 +0 -0
  14. package/out/renderer/assets/poppins-devanagari-700-normal-O-jipLrW.woff2 +0 -0
  15. package/out/renderer/assets/poppins-devanagari-700-normal-fHs-vx92.woff +0 -0
  16. package/out/renderer/assets/poppins-latin-400-normal-BOb3E3N0.woff +0 -0
  17. package/out/renderer/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
  18. package/out/renderer/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
  19. package/out/renderer/assets/poppins-latin-500-normal-DGXqpDMm.woff +0 -0
  20. package/out/renderer/assets/poppins-latin-600-normal-BJdTmd5m.woff +0 -0
  21. package/out/renderer/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
  22. package/out/renderer/assets/poppins-latin-700-normal-BVuQR_eA.woff +0 -0
  23. package/out/renderer/assets/poppins-latin-700-normal-Qrb0O0WB.woff2 +0 -0
  24. package/out/renderer/assets/poppins-latin-ext-400-normal-DaBSavcJ.woff +0 -0
  25. package/out/renderer/assets/poppins-latin-ext-400-normal-by3JarPu.woff2 +0 -0
  26. package/out/renderer/assets/poppins-latin-ext-500-normal-CK-6C4Hw.woff2 +0 -0
  27. package/out/renderer/assets/poppins-latin-ext-500-normal-CgAe2rWW.woff +0 -0
  28. package/out/renderer/assets/poppins-latin-ext-600-normal-CAhIAdZj.woff2 +0 -0
  29. package/out/renderer/assets/poppins-latin-ext-600-normal-Df5ffKXP.woff +0 -0
  30. package/out/renderer/assets/poppins-latin-ext-700-normal-DctTR6Tg.woff +0 -0
  31. package/out/renderer/assets/poppins-latin-ext-700-normal-cby-RkWa.woff2 +0 -0
  32. package/out/renderer/favicon.ico +0 -0
  33. package/out/renderer/icon-256.png +0 -0
  34. package/out/renderer/icon.svg +1 -0
  35. package/out/renderer/index.html +25 -0
  36. package/out/renderer/manifest.json +25 -0
  37. package/out/renderer/robot-simulator.html +1640 -0
  38. package/package.json +106 -0
  39. package/src/cli/index.js +332 -0
  40. package/src/cli/monitor.js +413 -0
  41. package/src/cli/theme.js +347 -0
  42. package/src/launcher/MarkTray.cs +227 -0
  43. package/src/main/browser-agent.js +1132 -0
  44. package/src/main/git-service.js +90 -0
  45. package/src/main/google/google-calendar.js +69 -0
  46. package/src/main/google/google-drive.js +263 -0
  47. package/src/main/google/google-gmail.js +131 -0
  48. package/src/main/google/google-service.js +241 -0
  49. package/src/main/node-tools.js +23 -0
  50. package/src/main/pc-agent-scripts/mouse-locker.ps1 +76 -0
  51. package/src/main/pc-agent-scripts/ocr-region.ps1 +185 -0
  52. package/src/main/pc-agent-scripts/pc-daemon.ps1 +726 -0
  53. package/src/main/pc-agent-scripts/pc-overlay.ps1 +253 -0
  54. package/src/main/pc-agent-scripts/pick-folder.ps1 +33 -0
  55. package/src/main/pc-agent-scripts/read-ui.ps1 +181 -0
  56. package/src/main/pc-agent-scripts/take-screenshot.ps1 +24 -0
  57. package/src/main/pc-agent-scripts/win-action.ps1 +380 -0
  58. package/src/main/plugins/plugin-loader.js +306 -0
  59. package/src/main/skills/skill-manager.js +265 -0
  60. package/src/main/syntax-validator.js +455 -0
  61. package/src/main/task-daemon.js +117 -0
  62. package/src/main/telegram/telegram-service.js +835 -0
  63. package/src/main/tools/browser-tools.js +516 -0
  64. package/src/main/tools/file-tools.js +1126 -0
  65. package/src/main/tools/git-tools.js +79 -0
  66. package/src/main/tools/google-tools.js +566 -0
  67. package/src/main/tools/index.js +36 -0
  68. package/src/main/tools/media-tools.js +42 -0
  69. package/src/main/tools/system-tools.js +295 -0
  70. package/src/main/tools/task-tools.js +73 -0
  71. package/src/main/tools/telegram-tools.js +101 -0
  72. package/src/renderer/index.html +24 -0
  73. package/src/renderer/public/favicon.ico +0 -0
  74. package/src/renderer/public/icon-256.png +0 -0
  75. package/src/renderer/public/icon.svg +1 -0
  76. package/src/renderer/public/manifest.json +25 -0
  77. package/src/renderer/public/robot-simulator.html +1640 -0
  78. package/src/renderer/src/App.jsx +304 -0
  79. package/src/renderer/src/api/ai/awareness.js +294 -0
  80. package/src/renderer/src/api/ai/chatSummarizer.js +79 -0
  81. package/src/renderer/src/api/ai/contextCompactor.js +129 -0
  82. package/src/renderer/src/api/ai/contextManager.js +1130 -0
  83. package/src/renderer/src/api/ai/core.js +167 -0
  84. package/src/renderer/src/api/ai/index.js +4 -0
  85. package/src/renderer/src/api/ai/memoryGroomer.js +101 -0
  86. package/src/renderer/src/api/ai/persona.js +115 -0
  87. package/src/renderer/src/api/ai/planning.js +317 -0
  88. package/src/renderer/src/api/ai/relationship.js +177 -0
  89. package/src/renderer/src/api/ai/skillSynthesizer.js +168 -0
  90. package/src/renderer/src/api/ai/tools.js +167 -0
  91. package/src/renderer/src/api/ai/utils.js +359 -0
  92. package/src/renderer/src/api/db.js +1013 -0
  93. package/src/renderer/src/api/oramaStore.js +592 -0
  94. package/src/renderer/src/api/ragPipeline.js +87 -0
  95. package/src/renderer/src/api/subagent/subagentExecutor.js +780 -0
  96. package/src/renderer/src/api/subagent/subagentPrompt.js +44 -0
  97. package/src/renderer/src/api/subagent/subagentStore.js +173 -0
  98. package/src/renderer/src/api/taskExecutor.js +18 -0
  99. package/src/renderer/src/api/taskStore.js +315 -0
  100. package/src/renderer/src/api/tools/core-tools.js +707 -0
  101. package/src/renderer/src/api/tools/index.js +151 -0
  102. package/src/renderer/src/api/turnPairMigrator.js +200 -0
  103. package/src/renderer/src/api/vectorMemory.js +136 -0
  104. package/src/renderer/src/api/wakeWord.js +101 -0
  105. package/src/renderer/src/api/web-bridge.js +885 -0
  106. package/src/renderer/src/api/webSpeech.js +178 -0
  107. package/src/renderer/src/api/workspaceRag.js +76 -0
  108. package/src/renderer/src/assets/electron.svg +10 -0
  109. package/src/renderer/src/assets/icon.svg +1 -0
  110. package/src/renderer/src/assets/main.css +589 -0
  111. package/src/renderer/src/assets/music-cover.png +0 -0
  112. package/src/renderer/src/assets/wavy-lines.svg +25 -0
  113. package/src/renderer/src/components/Chat/ApprovalBubble.jsx +127 -0
  114. package/src/renderer/src/components/Chat/CodeBlock.jsx +83 -0
  115. package/src/renderer/src/components/Chat/DurableTaskBubble.jsx +455 -0
  116. package/src/renderer/src/components/Chat/FileDiffModal.jsx +328 -0
  117. package/src/renderer/src/components/Chat/GeminiWidgets.jsx +82 -0
  118. package/src/renderer/src/components/Chat/MemoryFooterBubble.jsx +34 -0
  119. package/src/renderer/src/components/Chat/MessageBubble.jsx +459 -0
  120. package/src/renderer/src/components/Chat/PluginExecutionBubble.jsx +41 -0
  121. package/src/renderer/src/components/Chat/ThinkingBubble.jsx +316 -0
  122. package/src/renderer/src/components/Chat/YoutubeSearchBubble.jsx +43 -0
  123. package/src/renderer/src/components/Chat/YoutubeSummaryBubble.jsx +40 -0
  124. package/src/renderer/src/components/Chat/index.js +11 -0
  125. package/src/renderer/src/components/ChatList.jsx +434 -0
  126. package/src/renderer/src/components/GlobalCameraManager.jsx +67 -0
  127. package/src/renderer/src/components/Versions.jsx +15 -0
  128. package/src/renderer/src/components/YoutubeMusicPlayer.jsx +375 -0
  129. package/src/renderer/src/components/camera/CameraPreview.jsx +236 -0
  130. package/src/renderer/src/components/config/AiEngineSection.jsx +363 -0
  131. package/src/renderer/src/components/config/CameraSection.jsx +100 -0
  132. package/src/renderer/src/components/config/PermissionsSection.jsx +130 -0
  133. package/src/renderer/src/components/config/ShortcutSection.jsx +102 -0
  134. package/src/renderer/src/components/config/VoiceSection.jsx +110 -0
  135. package/src/renderer/src/components/config/index.js +5 -0
  136. package/src/renderer/src/components/core/Avatar.jsx +2197 -0
  137. package/src/renderer/src/components/core/ConfirmModal.jsx +80 -0
  138. package/src/renderer/src/components/core/DraggableHoloCard.jsx +125 -0
  139. package/src/renderer/src/components/core/FloatingMenu.jsx +272 -0
  140. package/src/renderer/src/components/core/HoloCard.jsx +64 -0
  141. package/src/renderer/src/components/core/InputBar.jsx +1346 -0
  142. package/src/renderer/src/components/core/ProcessPanel.jsx +216 -0
  143. package/src/renderer/src/components/core/ResponseArea.jsx +149 -0
  144. package/src/renderer/src/components/core/StatusIndicator.jsx +59 -0
  145. package/src/renderer/src/components/core/ThoughtNeuralFlow.jsx +168 -0
  146. package/src/renderer/src/components/core/ToolClustersDeck.jsx +210 -0
  147. package/src/renderer/src/components/core/native-skills.js +43 -0
  148. package/src/renderer/src/components/neural-core/EmotionalCortexLobe.jsx +359 -0
  149. package/src/renderer/src/components/neural-core/NodeDissectionModal.jsx +57 -0
  150. package/src/renderer/src/components/neural-core/ProceduralMatrixLobe.jsx +193 -0
  151. package/src/renderer/src/components/neural-core/ProceduralSkillModal.jsx +53 -0
  152. package/src/renderer/src/components/neural-core/ResetAiModal.jsx +198 -0
  153. package/src/renderer/src/components/neural-core/SynapticMemoryLobe.jsx +204 -0
  154. package/src/renderer/src/components/neural-core/traitMeta.js +102 -0
  155. package/src/renderer/src/components/subagent/AgentChatFeed.jsx +603 -0
  156. package/src/renderer/src/components/subagent/AgentInspector.jsx +224 -0
  157. package/src/renderer/src/components/subagent/AgentSidebar.jsx +216 -0
  158. package/src/renderer/src/components/subagent/SubagentIntercom.jsx +572 -0
  159. package/src/renderer/src/components/subagent/SubagentTopologyMap.jsx +423 -0
  160. package/src/renderer/src/contexts/ApprovalContext.jsx +425 -0
  161. package/src/renderer/src/contexts/ChatContext.jsx +61 -0
  162. package/src/renderer/src/contexts/YoutubeMusicContext.jsx +333 -0
  163. package/src/renderer/src/data/faqData.js +202 -0
  164. package/src/renderer/src/hooks/agent/executeAgentTool.js +895 -0
  165. package/src/renderer/src/hooks/agent/index.js +4 -0
  166. package/src/renderer/src/hooks/agent/useMarkMusic.js +75 -0
  167. package/src/renderer/src/hooks/agent/useMarkPlan.js +2739 -0
  168. package/src/renderer/src/hooks/agent/useMarkState.js +173 -0
  169. package/src/renderer/src/hooks/agent/useMarkYoutube.js +82 -0
  170. package/src/renderer/src/hooks/agent/useRelationalGrowth.js +201 -0
  171. package/src/renderer/src/hooks/telegram/useTelegramBot.js +63 -0
  172. package/src/renderer/src/hooks/useAwareness.js +289 -0
  173. package/src/renderer/src/hooks/useChatArchiver.js +52 -0
  174. package/src/renderer/src/hooks/useConfirm.jsx +68 -0
  175. package/src/renderer/src/hooks/useMarkAgent.js +454 -0
  176. package/src/renderer/src/hooks/useMemoryGroomer.js +152 -0
  177. package/src/renderer/src/hooks/useVAD.js +511 -0
  178. package/src/renderer/src/hooks/useWebSpeechWakeWord.js +122 -0
  179. package/src/renderer/src/main.jsx +12 -0
  180. package/src/renderer/src/pages/ChatStudio.jsx +922 -0
  181. package/src/renderer/src/pages/Configuration.jsx +360 -0
  182. package/src/renderer/src/pages/GoogleWorkspace.jsx +293 -0
  183. package/src/renderer/src/pages/Guidebook.jsx +917 -0
  184. package/src/renderer/src/pages/Knowledge.jsx +241 -0
  185. package/src/renderer/src/pages/MarkHome.jsx +491 -0
  186. package/src/renderer/src/pages/NeuralCore.jsx +786 -0
  187. package/src/renderer/src/pages/Plugins.jsx +630 -0
  188. package/src/renderer/src/pages/SkillEditor.jsx +380 -0
  189. package/src/renderer/src/pages/Skills.jsx +219 -0
  190. package/src/renderer/src/pages/Subagents.jsx +342 -0
  191. package/src/renderer/src/pages/TelegramBot.jsx +343 -0
  192. package/src/renderer/src/utils/chatContentParser.js +203 -0
  193. package/src/renderer/src/utils/toolClusters.js +201 -0
  194. package/src/server/bin/sha3_wasm_bg.wasm +0 -0
  195. package/src/server/config-manager.js +57 -0
  196. package/src/server/index.js +195 -0
  197. package/src/server/launcher.js +87 -0
  198. package/src/server/memory/db-store.js +758 -0
  199. package/src/server/memory/orama-store.js +177 -0
  200. package/src/server/memory/vector-engine.js +109 -0
  201. package/src/server/routes/ai.routes.js +280 -0
  202. package/src/server/routes/awareness.routes.js +62 -0
  203. package/src/server/routes/chat.routes.js +61 -0
  204. package/src/server/routes/config.routes.js +41 -0
  205. package/src/server/routes/index.js +35 -0
  206. package/src/server/routes/integrations.routes.js +116 -0
  207. package/src/server/routes/memory.routes.js +256 -0
  208. package/src/server/routes/plugins.routes.js +72 -0
  209. package/src/server/routes/skills.routes.js +139 -0
  210. package/src/server/routes/subagents.routes.js +70 -0
  211. package/src/server/routes/tasks.routes.js +175 -0
  212. package/src/server/routes/workspace.routes.js +127 -0
  213. package/src/server/services/ai/ai-utils.js +161 -0
  214. package/src/server/services/ai/openai-provider.js +757 -0
  215. package/src/server/services/ai/web-provider.js +729 -0
  216. package/src/server/services/ai-bridge.js +46 -0
  217. package/src/server/services/deepseek-web.js +956 -0
  218. package/src/server/services/gemini-web.js +170 -0
  219. package/src/server/services/notification-service.js +91 -0
  220. package/src/server/services/updater.js +125 -0
  221. package/src/server/tools/awareness-tracker.js +292 -0
  222. package/src/server/tools/group-tools.js +1164 -0
  223. package/src/server/tools/media-tools.js +244 -0
  224. package/src/server/tools/pc-agent.js +327 -0
  225. package/src/server/tools/screen-service.js +51 -0
  226. package/src/server/ws-hub.js +168 -0
  227. package/src/shared/parsers/mark-tag-parser.js +275 -0
@@ -0,0 +1,342 @@
1
+ import React, { useState, useEffect } from 'react'
2
+ import { useNavigate } from 'react-router-dom'
3
+ import {
4
+ Bot,
5
+ ArrowLeft,
6
+ Plus,
7
+ Cpu,
8
+ Activity,
9
+ X,
10
+ LayoutGrid
11
+ } from 'lucide-react'
12
+ import AgentSidebar from '../components/subagent/AgentSidebar'
13
+ import AgentChatFeed from '../components/subagent/AgentChatFeed'
14
+ import AgentInspector from '../components/subagent/AgentInspector'
15
+ import SubagentTopologyMap from '../components/subagent/SubagentTopologyMap'
16
+ import { runSubagentTurn } from '../api/subagent/subagentExecutor'
17
+ import { subagentStore } from '../api/subagent/subagentStore'
18
+ import { useConfirm } from '../hooks/useConfirm'
19
+
20
+ export default function Subagents() {
21
+ const navigate = useNavigate()
22
+ const [selectedSubagentId, setSelectedSubagentId] = useState(null)
23
+ const [subagents, setSubagents] = useState([])
24
+ const [viewMode, setViewMode] = useState('workspace') // 'workspace' | 'topology'
25
+ const [isInspectorOpen, setIsInspectorOpen] = useState(true)
26
+
27
+ // Modal State untuk Spawn Manual
28
+ const [isSpawnModalOpen, setIsSpawnModalOpen] = useState(false)
29
+ const [newAgentName, setNewAgentName] = useState('')
30
+ const [newAgentRole, setNewAgentRole] = useState('')
31
+ const [newAgentGoal, setNewAgentGoal] = useState('')
32
+ const [isSpawning, setIsSpawning] = useState(false)
33
+
34
+ const loadSubagents = async () => {
35
+ try {
36
+ const list = await subagentStore.listSubagents('all')
37
+ setSubagents(list || [])
38
+ if (!selectedSubagentId && list && list.length > 0) {
39
+ setSelectedSubagentId(list[0].id)
40
+ }
41
+ } catch (err) {
42
+ console.error('[Subagents] Load error:', err)
43
+ }
44
+ }
45
+
46
+ useEffect(() => {
47
+ loadSubagents()
48
+ const interval = setInterval(loadSubagents, 1200)
49
+ return () => clearInterval(interval)
50
+ }, [selectedSubagentId])
51
+
52
+ const activeCount = subagents?.filter((s) => s.status === 'running').length || 0
53
+ const selectedAgent = subagents.find((s) => s.id === selectedSubagentId) || null
54
+
55
+ const handleSendMessageFromTopology = async (id, messageText) => {
56
+ try {
57
+ await runSubagentTurn(id, messageText, 'user')
58
+ await loadSubagents()
59
+ } catch (err) {
60
+ console.error('[Topology] Send error:', err)
61
+ }
62
+ }
63
+
64
+ const handleSpawnManual = async (e) => {
65
+ e.preventDefault()
66
+ if (!newAgentName.trim() || !newAgentGoal.trim() || isSpawning) return
67
+
68
+ setIsSpawning(true)
69
+ try {
70
+ const sub = await subagentStore.createSubagent({
71
+ name: newAgentName.trim(),
72
+ role: newAgentRole.trim() || 'Technical Specialist',
73
+ goal: newAgentGoal.trim(),
74
+ status: 'running'
75
+ })
76
+
77
+ // Jika subagent belum aktif berjalan sebelumnya, jalankan initial turn
78
+ if (!sub.wasRunning) {
79
+ runSubagentTurn(sub.id, newAgentGoal.trim(), 'user').catch((err) => {
80
+ console.error('[Subagent Spawn Error]', err)
81
+ })
82
+ }
83
+
84
+ setSelectedSubagentId(sub.id)
85
+ setIsSpawnModalOpen(false)
86
+ setNewAgentName('')
87
+ setNewAgentRole('')
88
+ setNewAgentGoal('')
89
+ await loadSubagents()
90
+ } catch (err) {
91
+ console.error('[Spawn Subagent Failed]', err)
92
+ } finally {
93
+ setIsSpawning(false)
94
+ }
95
+ }
96
+
97
+ const { confirm, ModalComponent } = useConfirm()
98
+
99
+ const handleDeleteSubagent = async (id) => {
100
+ const targetAgent = subagents.find((s) => s.id === id)
101
+ const agentName = targetAgent?.name || 'Sub-Agent'
102
+ const result = await confirm({
103
+ title: 'Hapus Sub-Agent',
104
+ message: `Apakah kamu yakin ingin menghapus sub-agent "${agentName}" beserta seluruh riwayat eksekusinya?`,
105
+ isError: true,
106
+ confirmText: 'Hapus',
107
+ cancelText: 'Batal'
108
+ })
109
+ if (result?.isConfirmed) {
110
+ // Update UI state seketika (optimistic update)
111
+ setSubagents((prev) => prev.filter((s) => s.id !== id))
112
+ if (selectedSubagentId === id) {
113
+ const remaining = subagents.filter((s) => s.id !== id)
114
+ setSelectedSubagentId(remaining.length > 0 ? remaining[0].id : null)
115
+ }
116
+ await subagentStore.deleteSubagent(id)
117
+ await loadSubagents()
118
+ }
119
+ }
120
+
121
+ return (
122
+ <div className="h-screen w-screen bg-base-300 text-base-content overflow-hidden flex flex-col font-['Poppins',sans-serif]">
123
+ {/* Top Navigation Bar */}
124
+ <header className="h-13 bg-base-200/90 border-b border-base-content/10 px-4 flex items-center justify-between shrink-0 select-none z-20 backdrop-blur-md">
125
+ <div className="flex items-center gap-3">
126
+ <button
127
+ type="button"
128
+ onClick={() => navigate('/')}
129
+ className="btn btn-ghost btn-sm btn-circle"
130
+ title="Kembali ke Beranda Mark"
131
+ >
132
+ <ArrowLeft className="w-4 h-4" />
133
+ </button>
134
+
135
+ <div className="flex items-center gap-2">
136
+ <div className="p-1.5 rounded-xl bg-primary/10 border border-primary/20 text-primary">
137
+ <Bot className="w-4 h-4" />
138
+ </div>
139
+ <div>
140
+ <div className="flex items-center gap-2">
141
+ <h1 className="text-xs font-bold font-mono uppercase tracking-wider text-base-content">
142
+ Agent Workspace
143
+ </h1>
144
+ <span className="text-[10px] text-base-content/40 font-mono">/ Mission Control</span>
145
+ {activeCount > 0 && (
146
+ <span className="badge badge-primary badge-xs gap-1 font-mono text-[9px] py-1.5">
147
+ <span className="w-1 h-1 rounded-full bg-base-100 animate-ping" />
148
+ {activeCount} active
149
+ </span>
150
+ )}
151
+ </div>
152
+ </div>
153
+ </div>
154
+ </div>
155
+
156
+ {/* Center / Right View Switcher */}
157
+ <div className="flex items-center gap-2.5">
158
+ <div className="flex items-center p-0.5 bg-base-300/80 rounded-xl border border-base-content/10 font-mono text-xs">
159
+ <button
160
+ type="button"
161
+ onClick={() => setViewMode('workspace')}
162
+ className={`px-3 py-1 rounded-lg text-xs font-medium transition-all flex items-center gap-1.5 ${
163
+ viewMode === 'workspace'
164
+ ? 'bg-primary text-primary-content shadow-sm font-semibold'
165
+ : 'text-base-content/60 hover:text-base-content'
166
+ }`}
167
+ title="Workspace Thread View (Hermes Style)"
168
+ >
169
+ <LayoutGrid className="w-3.5 h-3.5" /> Workspace
170
+ </button>
171
+ <button
172
+ type="button"
173
+ onClick={() => setViewMode('topology')}
174
+ className={`px-3 py-1 rounded-lg text-xs font-medium transition-all flex items-center gap-1.5 ${
175
+ viewMode === 'topology'
176
+ ? 'bg-primary text-primary-content shadow-sm font-semibold'
177
+ : 'text-base-content/60 hover:text-base-content'
178
+ }`}
179
+ title="Visual Constellation Map"
180
+ >
181
+ <Activity className="w-3.5 h-3.5" /> Topology
182
+ </button>
183
+ </div>
184
+
185
+ <button
186
+ type="button"
187
+ onClick={() => setIsSpawnModalOpen(true)}
188
+ className="btn btn-primary btn-xs rounded-xl gap-1 px-3 font-medium shadow-sm shadow-primary/20"
189
+ >
190
+ <Plus className="w-3.5 h-3.5" />
191
+ <span className="text-[11px]">New Agent</span>
192
+ </button>
193
+ </div>
194
+ </header>
195
+
196
+ {/* Main Container Area */}
197
+ <main className="flex-1 flex overflow-hidden relative">
198
+ {viewMode === 'topology' ? (
199
+ <div className="flex-1 p-4 overflow-hidden">
200
+ <SubagentTopologyMap
201
+ subagents={subagents}
202
+ selectedId={selectedSubagentId}
203
+ onSelectAgent={setSelectedSubagentId}
204
+ onOpenIntercom={(id) => {
205
+ setSelectedSubagentId(id)
206
+ setViewMode('workspace')
207
+ }}
208
+ onSendMessage={handleSendMessageFromTopology}
209
+ />
210
+ </div>
211
+ ) : (
212
+ <div className="flex-1 flex w-full h-full overflow-hidden">
213
+ {/* 1. Left Sidebar */}
214
+ <AgentSidebar
215
+ subagents={subagents}
216
+ selectedId={selectedSubagentId}
217
+ onSelectAgent={setSelectedSubagentId}
218
+ onOpenNewAgentModal={() => setIsSpawnModalOpen(true)}
219
+ onDeleteAgent={handleDeleteSubagent}
220
+ />
221
+
222
+ {/* 2. Center Panel */}
223
+ {selectedSubagentId ? (
224
+ <AgentChatFeed
225
+ subagentId={selectedSubagentId}
226
+ onOpenInspector={() => setIsInspectorOpen(!isInspectorOpen)}
227
+ onDeleteAgent={handleDeleteSubagent}
228
+ />
229
+ ) : (
230
+ <div className="flex-1 flex flex-col items-center justify-center p-8 text-base-content/30 gap-2.5 font-mono text-center">
231
+ <div className="p-4 bg-base-200 rounded-2xl border border-base-content/10">
232
+ <Bot className="w-10 h-10 stroke-[1.5]" />
233
+ </div>
234
+ <p className="text-xs font-semibold text-base-content/60">
235
+ Pilih atau buat sub-agent untuk memulai Agent Workspace
236
+ </p>
237
+ <button
238
+ type="button"
239
+ onClick={() => setIsSpawnModalOpen(true)}
240
+ className="btn btn-primary btn-xs rounded-xl mt-2"
241
+ >
242
+ <Plus className="w-3 h-3" /> Buat Sub-Agent Baru
243
+ </button>
244
+ </div>
245
+ )}
246
+
247
+ {/* 3. Right Panel: Agent Inspector */}
248
+ {isInspectorOpen && selectedAgent && (
249
+ <AgentInspector
250
+ subagent={selectedAgent}
251
+ onClose={() => setIsInspectorOpen(false)}
252
+ onDeleteAgent={handleDeleteSubagent}
253
+ />
254
+ )}
255
+ </div>
256
+ )}
257
+ </main>
258
+
259
+ {/* Modal Spawn Agent */}
260
+ {isSpawnModalOpen && (
261
+ <div className="modal modal-open bg-black/60 backdrop-blur-sm z-50">
262
+ <div className="modal-box bg-base-200 border border-base-content/10 rounded-2xl max-w-md shadow-2xl p-5 font-mono">
263
+ <div className="flex items-center justify-between mb-4">
264
+ <h3 className="font-semibold text-xs uppercase tracking-wider flex items-center gap-2 text-base-content/90">
265
+ <Cpu className="w-4 h-4 text-primary" /> Spawn Sub-Agent Baru
266
+ </h3>
267
+ <button
268
+ type="button"
269
+ onClick={() => setIsSpawnModalOpen(false)}
270
+ className="btn btn-ghost btn-xs btn-circle text-base-content/50 hover:text-base-content"
271
+ >
272
+ <X className="w-4 h-4" />
273
+ </button>
274
+ </div>
275
+ <form onSubmit={handleSpawnManual} className="space-y-3">
276
+ <div>
277
+ <label className="label text-[11px] font-medium py-1 text-base-content/70">
278
+ Nama Agen
279
+ </label>
280
+ <input
281
+ type="text"
282
+ placeholder="misal: Developer / Mr Tester / Researcher"
283
+ value={newAgentName}
284
+ onChange={(e) => setNewAgentName(e.target.value)}
285
+ className="input input-sm input-bordered w-full rounded-xl bg-base-100/60 text-xs focus:outline-none focus:border-primary/50"
286
+ required
287
+ />
288
+ </div>
289
+ <div>
290
+ <label className="label text-[11px] font-medium py-1 text-base-content/70">
291
+ Role / Spesialisasi
292
+ </label>
293
+ <input
294
+ type="text"
295
+ placeholder="misal: Software Engineer / QA Tester"
296
+ value={newAgentRole}
297
+ onChange={(e) => setNewAgentRole(e.target.value)}
298
+ className="input input-sm input-bordered w-full rounded-xl bg-base-100/60 text-xs focus:outline-none focus:border-primary/50"
299
+ required
300
+ />
301
+ </div>
302
+ <div>
303
+ <label className="label text-[11px] font-medium py-1 text-base-content/70">
304
+ Tujuan & Instruksi Awal
305
+ </label>
306
+ <textarea
307
+ placeholder="Deskripsikan misi teknis yang harus diselesaikan sub-agent..."
308
+ value={newAgentGoal}
309
+ onChange={(e) => setNewAgentGoal(e.target.value)}
310
+ className="textarea textarea-sm textarea-bordered w-full rounded-xl h-24 text-xs bg-base-100/60 focus:outline-none focus:border-primary/50"
311
+ required
312
+ />
313
+ </div>
314
+ <div className="modal-action pt-2 flex items-center justify-end gap-2">
315
+ <button
316
+ type="button"
317
+ onClick={() => setIsSpawnModalOpen(false)}
318
+ disabled={isSpawning}
319
+ className="btn btn-ghost btn-xs rounded-xl px-3"
320
+ >
321
+ Batal
322
+ </button>
323
+ <button
324
+ type="submit"
325
+ disabled={!newAgentName.trim() || !newAgentGoal.trim() || isSpawning}
326
+ className="btn btn-primary btn-xs rounded-xl px-4 font-medium shadow-sm shadow-primary/20"
327
+ >
328
+ {isSpawning ? (
329
+ <span className="loading loading-spinner loading-xs" />
330
+ ) : (
331
+ 'Mulai Eksekusi'
332
+ )}
333
+ </button>
334
+ </div>
335
+ </form>
336
+ </div>
337
+ </div>
338
+ )}
339
+ <ModalComponent />
340
+ </div>
341
+ )
342
+ }
@@ -0,0 +1,343 @@
1
+ import React, { useRef, useEffect, useState } from 'react'
2
+ import { useTelegramBot } from '../hooks/telegram/useTelegramBot'
3
+ import { FaTelegram, FaPlug, FaStop, FaCog, FaSave, FaTimes } from 'react-icons/fa'
4
+ import Markdown from 'react-markdown'
5
+ import remarkGfm from 'remark-gfm'
6
+ import rehypeExternalLinks from 'rehype-external-links'
7
+ import { CodeBlock } from '../components/Chat/CodeBlock'
8
+ import { useNavigate } from 'react-router-dom'
9
+ import { getAllConfig, saveConfiguration } from '../api/db'
10
+ import { webApi } from '../api/web-bridge'
11
+
12
+ const TelegramBot = () => {
13
+ const { status, messages, isThinking, currentSender, startBot, stopBot } = useTelegramBot()
14
+ const navigate = useNavigate()
15
+
16
+ const [tokenInput, setTokenInput] = useState('')
17
+ const [adminIdsInput, setAdminIdsInput] = useState('')
18
+ const [showConfigModal, setShowConfigModal] = useState(false)
19
+ const messagesEndRef = useRef(null)
20
+ const hasAutoConnectedRef = useRef(false)
21
+
22
+ const loadConfigData = async () => {
23
+ try {
24
+ const configs = await getAllConfig()
25
+ const config = configs[0] || {}
26
+ if (config.tgBotToken) {
27
+ setTokenInput(config.tgBotToken)
28
+ }
29
+ if (config.tgAdminIds) {
30
+ setAdminIdsInput(config.tgAdminIds)
31
+ }
32
+ return config
33
+ } catch (err) {
34
+ console.error('[TelegramBot] Error loading config:', err)
35
+ return {}
36
+ }
37
+ }
38
+
39
+ useEffect(() => {
40
+ loadConfigData().then(async (config) => {
41
+ if (config.tgBotToken && !hasAutoConnectedRef.current) {
42
+ hasAutoConnectedRef.current = true
43
+ const res = await webApi.tgGetStatus()
44
+ if (!res || res.status === 'disconnected') {
45
+ startBot(config.tgBotToken)
46
+ }
47
+ }
48
+ })
49
+ }, [])
50
+
51
+ useEffect(() => {
52
+ const timeout = setTimeout(() => {
53
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' })
54
+ }, 150)
55
+ return () => clearTimeout(timeout)
56
+ }, [messages, isThinking])
57
+
58
+ const handleSaveConfigAndConnect = async () => {
59
+ const token = tokenInput.trim()
60
+ const adminIds = adminIdsInput.trim()
61
+
62
+ if (!token) {
63
+ alert('Silakan masukkan Telegram Bot Token terlebih dahulu.')
64
+ return
65
+ }
66
+
67
+ try {
68
+ const configs = await getAllConfig()
69
+ const currentCfg = configs[0] || {}
70
+ const newCfg = { ...currentCfg, tgBotToken: token, tgAdminIds: adminIds }
71
+ await saveConfiguration(newCfg)
72
+ await webApi.syncConfig(newCfg)
73
+ setShowConfigModal(false)
74
+ startBot(token)
75
+ } catch (e) {
76
+ console.error('[TelegramBot] Gagal simpan pengaturan Telegram:', e)
77
+ alert('Gagal menyimpan pengaturan: ' + e.message)
78
+ }
79
+ }
80
+
81
+ return (
82
+ <div className="h-screen bg-base-300 text-base-content overflow-hidden relative font-['Poppins',sans-serif]">
83
+ {/* Background Ambience */}
84
+ <div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,oklch(var(--n))_0%,transparent_70%)] opacity-20 pointer-events-none" />
85
+ <div className="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/stardust.png')] opacity-10 pointer-events-none" />
86
+
87
+ {/* Main Content Area */}
88
+ <div className="relative z-10 w-full h-full overflow-y-auto custom-scrollbar flex flex-col">
89
+ <div className="max-w-4xl mx-auto px-4 py-8 pb-32 space-y-6 w-full flex-1 flex flex-col">
90
+ {/* Page Header */}
91
+ <div className="flex items-center justify-between">
92
+ <div className="flex items-center gap-4">
93
+ <button
94
+ type="button"
95
+ onClick={() => navigate('/')}
96
+ className="btn btn-ghost btn-sm btn-circle shrink-0"
97
+ style={{ WebkitAppRegion: 'no-drag' }}
98
+ title="Kembali"
99
+ >
100
+ <svg
101
+ xmlns="http://www.w3.org/2000/svg"
102
+ width="1.2em"
103
+ height="1.2em"
104
+ fill="none"
105
+ viewBox="0 0 24 24"
106
+ stroke="currentColor"
107
+ strokeWidth={2}
108
+ >
109
+ <path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
110
+ </svg>
111
+ </button>
112
+ <div>
113
+ <div className="flex items-center gap-2.5">
114
+ <h1 className="text-2xl font-bold">Telegram Bot Monitor</h1>
115
+ <div className="flex items-center gap-1.5 text-xs bg-base-200/80 px-2.5 py-0.5 rounded-full border border-base-content/10 font-mono">
116
+ <div
117
+ className={`w-2 h-2 rounded-full ${status === 'connected' ? 'bg-success' : status === 'connecting' ? 'bg-warning animate-pulse' : 'bg-error'}`}
118
+ />
119
+ <span className="capitalize text-xs">{status}</span>
120
+ </div>
121
+ </div>
122
+ <p className="opacity-50 text-sm mt-1">Kelola dan pantau interaksi Mark via Telegram.</p>
123
+ </div>
124
+ </div>
125
+ <div className="flex items-center gap-2">
126
+ <button
127
+ type="button"
128
+ onClick={() => {
129
+ loadConfigData()
130
+ setShowConfigModal(true)
131
+ }}
132
+ className="btn btn-sm btn-ghost gap-2 border border-base-content/10"
133
+ style={{ WebkitAppRegion: 'no-drag' }}
134
+ title="Buka Pengaturan Telegram Token & Admin"
135
+ >
136
+ <FaCog /> Pengaturan
137
+ </button>
138
+ {status === 'disconnected' && (
139
+ <button
140
+ type="button"
141
+ onClick={handleSaveConfigAndConnect}
142
+ className="btn btn-sm btn-info text-white gap-1.5"
143
+ style={{ WebkitAppRegion: 'no-drag' }}
144
+ >
145
+ <FaPlug /> Connect
146
+ </button>
147
+ )}
148
+ {(status === 'connected' || status === 'connecting') && (
149
+ <button
150
+ type="button"
151
+ onClick={stopBot}
152
+ className="btn btn-sm btn-outline btn-error gap-1.5"
153
+ style={{ WebkitAppRegion: 'no-drag' }}
154
+ >
155
+ <FaStop /> Disconnect
156
+ </button>
157
+ )}
158
+ </div>
159
+ </div>
160
+
161
+ {/* Content Area */}
162
+ <div className="flex-1 overflow-y-auto w-full flex flex-col gap-4">
163
+ {status === 'disconnected' && (
164
+ <div className="card bg-base-200 shadow-xl border border-white/10 p-6 max-w-md mx-auto my-auto text-center space-y-4">
165
+ <FaTelegram className="text-6xl text-info mx-auto" />
166
+ <h2 className="text-xl font-bold">Sambungkan Telegram Bot</h2>
167
+ <p className="text-sm opacity-70">
168
+ Masukkan API Bot Token dari <b>@BotFather</b> di Telegram.
169
+ </p>
170
+ <div className="space-y-3 text-left">
171
+ <div>
172
+ <label className="text-xs font-semibold opacity-70 block mb-1">Bot Token</label>
173
+ <input
174
+ type="password"
175
+ placeholder="123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ..."
176
+ value={tokenInput}
177
+ onChange={(e) => setTokenInput(e.target.value)}
178
+ className="input input-bordered w-full font-mono text-sm"
179
+ />
180
+ </div>
181
+ <div>
182
+ <label className="text-xs font-semibold opacity-70 block mb-1">
183
+ Telegram Admin Usernames
184
+ </label>
185
+ <input
186
+ type="text"
187
+ placeholder="@username1, @username2"
188
+ value={adminIdsInput}
189
+ onChange={(e) => setAdminIdsInput(e.target.value)}
190
+ className="input input-bordered w-full font-mono text-sm"
191
+ />
192
+ <span className="text-[10px] opacity-60 block mt-1">
193
+ Masukkan username Telegram (@username). Pisahkan dengan koma jika lebih dari satu.
194
+ </span>
195
+ </div>
196
+ </div>
197
+ <button onClick={handleSaveConfigAndConnect} className="btn btn-info text-white w-full">
198
+ <FaPlug /> Simpan & Hubungkan Bot
199
+ </button>
200
+ </div>
201
+ )}
202
+
203
+ {status === 'connected' && messages.length === 0 && (
204
+ <div className="flex-1 flex flex-col items-center justify-center opacity-40 select-none">
205
+ <FaTelegram className="text-6xl mb-4 text-info" />
206
+ <p className="text-lg font-semibold">Menunggu Pesan Masuk</p>
207
+ <p className="text-sm">Bot terhubung. Pantau aktivitas Telegram di sini.</p>
208
+ </div>
209
+ )}
210
+
211
+ {messages.map((msg, idx) => (
212
+ <div
213
+ key={idx}
214
+ className={`chat ${msg.type === 'outgoing' ? 'chat-end' : 'chat-start'} animate-fade-in`}
215
+ >
216
+ <div className="chat-header opacity-50 text-xs mb-1">
217
+ {msg.sender}
218
+ <time className="text-xs ml-2">{msg.time}</time>
219
+ </div>
220
+ <div
221
+ className={`chat-bubble flex flex-col gap-1 ${msg.type === 'outgoing' ? 'chat-bubble-info text-info-content' : 'bg-base-300 text-base-content'}`}
222
+ >
223
+ <div className="text-sm custom-markdown overflow-x-hidden">
224
+ <Markdown
225
+ remarkPlugins={[remarkGfm]}
226
+ rehypePlugins={[
227
+ [rehypeExternalLinks, { target: '_blank', rel: ['noopener', 'noreferrer'] }]
228
+ ]}
229
+ components={{
230
+ code: CodeBlock
231
+ }}
232
+ >
233
+ {msg.type === 'outgoing' ? msg.reply : msg.text}
234
+ </Markdown>
235
+ {msg.type === 'outgoing' && msg.toolsUsed && msg.toolsUsed.length > 0 && (
236
+ <div className="flex flex-wrap gap-1 mt-2">
237
+ {msg.toolsUsed.map((tool, i) => (
238
+ <span
239
+ key={i}
240
+ className="badge badge-sm text-white badge-accent badge-outline text-[10px] font-mono ml-auto"
241
+ >
242
+ {tool}
243
+ </span>
244
+ ))}
245
+ </div>
246
+ )}
247
+ </div>
248
+ </div>
249
+ </div>
250
+ ))}
251
+
252
+ {isThinking && (
253
+ <div className="chat chat-end animate-fade-in">
254
+ <div className="chat-header opacity-50 text-xs mb-1">
255
+ Mark sedang memproses pesan {currentSender}...
256
+ </div>
257
+ <div className="chat-bubble chat-bubble-info bg-info/20 text-info border border-info/30">
258
+ <span className="loading loading-dots loading-sm"></span>
259
+ </div>
260
+ </div>
261
+ )}
262
+
263
+ <div ref={messagesEndRef} />
264
+ </div>
265
+ </div>
266
+ </div>
267
+
268
+ {/* Telegram Configuration Modal */}
269
+ {showConfigModal && (
270
+ <div className="modal modal-open">
271
+ <div className="modal-box bg-base-200 border border-white/10 max-w-lg">
272
+ <div className="flex justify-between items-center mb-4">
273
+ <h3 className="font-bold text-lg flex items-center gap-2 text-info">
274
+ <FaTelegram /> Pengaturan Telegram Bot
275
+ </h3>
276
+ <button
277
+ onClick={() => setShowConfigModal(false)}
278
+ className="btn btn-sm btn-circle btn-ghost"
279
+ >
280
+ <FaTimes />
281
+ </button>
282
+ </div>
283
+
284
+ <div className="space-y-4 text-left">
285
+ <div className="form-control w-full">
286
+ <label className="label">
287
+ <span className="label-text font-semibold">Telegram Bot Token</span>
288
+ </label>
289
+ <input
290
+ type="password"
291
+ placeholder="123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ..."
292
+ value={tokenInput}
293
+ onChange={(e) => setTokenInput(e.target.value)}
294
+ className="input input-bordered w-full font-mono text-sm"
295
+ />
296
+ <span className="text-xs opacity-60 mt-1">
297
+ Dapatkan token dari akun resmi <b>@BotFather</b> di Telegram.
298
+ </span>
299
+ </div>
300
+
301
+ <div className="form-control w-full">
302
+ <label className="label">
303
+ <span className="label-text font-semibold">Telegram Admin Usernames</span>
304
+ </label>
305
+ <input
306
+ type="text"
307
+ placeholder="@username1, @username2"
308
+ value={adminIdsInput}
309
+ onChange={(e) => setAdminIdsInput(e.target.value)}
310
+ className="input input-bordered w-full font-mono text-sm"
311
+ />
312
+ <span className="text-xs opacity-60 mt-1">
313
+ Daftar username Telegram (@username) yang diizinkan mengontrol Mark. Pisahkan dengan koma.
314
+ </span>
315
+ </div>
316
+
317
+ <div className="alert alert-info bg-info/10 border-info/30 text-xs text-white">
318
+ <span>
319
+ Buka aplikasi Telegram & ketik <b>/start</b> ke bot kamu setidaknya 1x agar bot
320
+ bisa mencatat Chat ID kamu untuk mengirim balasan & notifikasi dari PC.
321
+ </span>
322
+ </div>
323
+ </div>
324
+
325
+ <div className="modal-action mt-6">
326
+ <button onClick={() => setShowConfigModal(false)} className="btn btn-ghost">
327
+ Batal
328
+ </button>
329
+ <button
330
+ onClick={handleSaveConfigAndConnect}
331
+ className="btn btn-info text-white gap-2"
332
+ >
333
+ <FaSave /> Simpan & Hubungkan
334
+ </button>
335
+ </div>
336
+ </div>
337
+ </div>
338
+ )}
339
+ </div>
340
+ )
341
+ }
342
+
343
+ export default TelegramBot