@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,459 @@
1
+ import React, { useState, useEffect } from 'react'
2
+ import Markdown from 'react-markdown'
3
+ import remarkGfm from 'remark-gfm'
4
+ import rehypeExternalLinks from 'rehype-external-links'
5
+ import { CodeBlock } from './CodeBlock'
6
+ import {
7
+ Brain,
8
+ ChevronRight,
9
+ ExternalLink,
10
+ Sparkles,
11
+ Activity,
12
+ Check,
13
+ XCircle,
14
+ Ban,
15
+ Terminal,
16
+ Loader2
17
+ } from 'lucide-react'
18
+ import { useApproval } from '../../contexts/ApprovalContext'
19
+ import { ApprovalBubble } from './ApprovalBubble'
20
+
21
+ export const MessageBubble = React.memo(
22
+ ({
23
+ isUser,
24
+ content,
25
+ reasoning,
26
+ sources = [],
27
+ executionSteps = null,
28
+ executedTools = [],
29
+ isPlanConclusion = false,
30
+ isLearned = false,
31
+ isThinking = false
32
+ }) => {
33
+ const { activeApproval } = useApproval()
34
+ const [isCopied, setIsCopied] = useState(false)
35
+ const [isProcessOpen, setIsProcessOpen] = useState(Boolean(isThinking))
36
+
37
+ useEffect(() => {
38
+ if (isThinking) {
39
+ setIsProcessOpen(true)
40
+ }
41
+ }, [isThinking])
42
+
43
+ const resolveImageUrl = (url) => {
44
+ if (!url || typeof url !== 'string') return ''
45
+ if (
46
+ url.startsWith('data:image/') ||
47
+ url.startsWith('http://') ||
48
+ url.startsWith('https://') ||
49
+ url.startsWith('/api/') ||
50
+ url.startsWith('blob:')
51
+ ) {
52
+ return url
53
+ }
54
+ if (url.includes('temp-uploads')) {
55
+ const filename = url.split(/[/\\]/).pop()
56
+ return `/api/chat/temp-file/${encodeURIComponent(filename)}`
57
+ }
58
+ return url
59
+ }
60
+
61
+ const extractContent = (val) => {
62
+ if (val == null) return { text: '', images: [] }
63
+ if (typeof val === 'string') {
64
+ if (
65
+ val.startsWith('data:image/') ||
66
+ val.startsWith('/api/chat/temp-file/') ||
67
+ (val.includes('temp-uploads') && /\.(png|jpe?g|webp|gif|bmp)$/i.test(val))
68
+ ) {
69
+ return { text: '', images: [resolveImageUrl(val)] }
70
+ }
71
+ return { text: val, images: [] }
72
+ }
73
+ if (Array.isArray(val)) {
74
+ const texts = []
75
+ const images = []
76
+ for (const item of val) {
77
+ if (!item) continue
78
+ if (typeof item === 'string') {
79
+ if (
80
+ item.startsWith('data:image/') ||
81
+ item.startsWith('/api/chat/temp-file/') ||
82
+ (item.includes('temp-uploads') && /\.(png|jpe?g|webp|gif|bmp)$/i.test(item))
83
+ ) {
84
+ images.push(resolveImageUrl(item))
85
+ } else {
86
+ texts.push(item)
87
+ }
88
+ } else if (item.type === 'text') {
89
+ if (item.text) texts.push(item.text)
90
+ } else if (item.type === 'image_url') {
91
+ const imgUrl =
92
+ item.image_url?.url ||
93
+ item.url ||
94
+ (typeof item.image_url === 'string' ? item.image_url : null)
95
+ if (imgUrl) images.push(resolveImageUrl(imgUrl))
96
+ } else if (item.image_url || item.url) {
97
+ const imgUrl = item.image_url?.url || item.image_url || item.url
98
+ if (typeof imgUrl === 'string') images.push(resolveImageUrl(imgUrl))
99
+ } else {
100
+ texts.push(JSON.stringify(item, null, 2))
101
+ }
102
+ }
103
+ return { text: texts.join('\n\n'), images }
104
+ }
105
+ if (typeof val === 'object') {
106
+ if (val.type === 'image_url') {
107
+ const imgUrl =
108
+ val.image_url?.url ||
109
+ val.url ||
110
+ (typeof val.image_url === 'string' ? val.image_url : null)
111
+ if (imgUrl) return { text: '', images: [resolveImageUrl(imgUrl)] }
112
+ }
113
+ return { text: JSON.stringify(val, null, 2), images: [] }
114
+ }
115
+ return { text: String(val), images: [] }
116
+ }
117
+
118
+ const { text: stringContent, images: attachedImages } = extractContent(content)
119
+
120
+ const handleCopy = () => {
121
+ const textToCopy = stringContent || ''
122
+ if (!textToCopy) return
123
+ navigator.clipboard.writeText(textToCopy)
124
+ setIsCopied(true)
125
+ setTimeout(() => setIsCopied(false), 2000)
126
+ }
127
+
128
+ return (
129
+ <div className="text-sm leading-relaxed custom-markdown flex flex-col gap-1 relative group">
130
+ {/* Plan Conclusion Header */}
131
+ {isPlanConclusion && (
132
+ <div className="flex items-center gap-1.5 text-[11px] font-bold text-primary uppercase tracking-wider mb-2 border-b border-primary/20 pb-1.5 w-max">
133
+ <Sparkles className="w-3.5 h-3.5" />
134
+ Kesimpulan Rencana
135
+ </div>
136
+ )}
137
+
138
+ {/* Executed Tools & Reasoning Activity Trace (Antigravity Style) */}
139
+ {((executedTools && executedTools.length > 0) || reasoning) && (
140
+ <div className="my-2.5 space-y-1.5 select-none">
141
+ {/* Thinking / Reasoning Accordion */}
142
+ {reasoning && (
143
+ <details className="group/thought outline-none">
144
+ <summary className="list-none flex items-center gap-2 cursor-pointer text-xs font-mono text-white/60 hover:text-white select-none py-0.5 transition-colors">
145
+ <Brain className="w-3.5 h-3.5 text-primary/80 shrink-0" />
146
+ <span className="font-medium text-white/80">Proses Pemikiran AI</span>
147
+ <ChevronRight className="w-3 h-3 text-white/40 transition-transform duration-150 group-open/thought:rotate-90 ml-auto shrink-0" />
148
+ </summary>
149
+ <div className="mt-1 pl-3 my-2 text-[11px] font-mono border-l-2 border-primary/30 text-white/80 leading-relaxed max-h-64 overflow-y-auto custom-scrollbar bg-base-300/30 p-2.5 rounded-lg select-text [&_p]:my-1 [&_p]:leading-relaxed [&_h1]:text-xs [&_h1]:font-bold [&_h1]:my-1.5 [&_h2]:text-[11px] [&_h2]:font-bold [&_h2]:my-1 [&_h3]:text-[11px] [&_h3]:font-bold [&_h3]:my-1 [&_ul]:my-1 [&_ul]:ml-3.5 [&_ol]:my-1 [&_ol]:ml-3.5 [&_li]:my-0.5 [&_code]:text-[10px] [&_pre]:my-1.5 [&_hr]:my-2 [&_hr]:border-white/10">
150
+ <Markdown remarkPlugins={[remarkGfm]}>
151
+ {typeof reasoning === 'string' ? reasoning : JSON.stringify(reasoning, null, 2)}
152
+ </Markdown>
153
+ </div>
154
+ </details>
155
+ )}
156
+
157
+ {/* Executed Tools Folded Process Accordion */}
158
+ {((executedTools && executedTools.length > 0) ||
159
+ (executionSteps && executionSteps.length > 0)) &&
160
+ (() => {
161
+ const toolsToRender =
162
+ executedTools && executedTools.length > 0 ? executedTools : executionSteps || []
163
+ return (
164
+ <details
165
+ className="group/process outline-none"
166
+ open={isProcessOpen}
167
+ onToggle={(e) => setIsProcessOpen(e.currentTarget.open)}
168
+ >
169
+ <summary className="list-none flex items-center gap-2 cursor-pointer text-xs font-mono text-white/70 hover:text-white select-none py-1 transition-colors">
170
+ <Terminal className="w-3.5 h-3.5 text-primary/80 shrink-0" />
171
+ <span className="font-semibold text-white/90">Proses</span>
172
+ <span className="text-[10px] text-white/40 font-normal">
173
+ ({toolsToRender.length} langkah)
174
+ </span>
175
+ <ChevronRight className="w-3.5 h-3.5 text-white/40 transition-transform duration-150 group-open/process:rotate-90 ml-auto shrink-0" />
176
+ </summary>
177
+ <div className="mt-1 pl-2.5 space-y-1 border-l-2 border-white/15 ml-1.5 my-1">
178
+ {toolsToRender.map((t, idx) => {
179
+ if (t.type === 'intervention') {
180
+ const interventionText = t.text || t.content || t.task || ''
181
+ return (
182
+ <div key={idx} className="flex items-start gap-2 py-0.5 text-xs">
183
+ <span className="font-bold text-primary font-mono shrink-0 select-none">
184
+ You :
185
+ </span>
186
+ <span className="text-white/90 font-sans select-text whitespace-pre-wrap leading-relaxed">
187
+ {interventionText}
188
+ </span>
189
+ </div>
190
+ )
191
+ }
192
+
193
+ if (t.type === 'narration' || (!t.tool && t.text)) {
194
+ return (
195
+ <div
196
+ key={idx}
197
+ className="flex flex-col py-1 pl-1 pr-2 text-xs text-white/85 leading-relaxed font-sans select-text"
198
+ >
199
+ <Markdown remarkPlugins={[remarkGfm]}>{t.text}</Markdown>
200
+ </div>
201
+ )
202
+ }
203
+
204
+ const isRunning = t.status === 'running'
205
+ const isStopped = t.status === 'stopped' || t.status === 'cancelled'
206
+ const isPendingApproval = Boolean(
207
+ activeApproval &&
208
+ activeApproval.status === 'pending' &&
209
+ isRunning &&
210
+ (activeApproval.tool === t.tool || !t.tool)
211
+ )
212
+ const hasQuery = t.query !== undefined && t.query !== null && t.query !== ''
213
+ const queryString =
214
+ typeof t.query === 'string' ? t.query : JSON.stringify(t.query, null, 2)
215
+ const textResult = String(t.fullResult || t.resultSummary || '')
216
+ const hasResult = Boolean(textResult.trim())
217
+ const isFailed =
218
+ t.status === 'failed' ||
219
+ t.status === 'error' ||
220
+ textResult.startsWith('[ERROR]') ||
221
+ textResult.includes(' crash:') ||
222
+ textResult.toLowerCase().includes(' gagal:')
223
+
224
+ const toolLabel = t.tool || t.task || 'tool'
225
+ const displayReason = t.reason || null
226
+ let shortSummary = ''
227
+ if (hasQuery) {
228
+ try {
229
+ const parsed = JSON.parse(queryString)
230
+ shortSummary =
231
+ parsed.command ||
232
+ parsed.path ||
233
+ parsed.query ||
234
+ parsed.prompt ||
235
+ parsed.skill_name ||
236
+ queryString
237
+ } catch (_) {
238
+ shortSummary = queryString
239
+ }
240
+ }
241
+
242
+ if (!hasQuery && !hasResult) {
243
+ return (
244
+ <div key={idx} className="flex flex-col py-0.5">
245
+ <div className="flex items-center gap-2 text-xs font-mono text-white/60">
246
+ {isPendingApproval ? (
247
+ <Loader2 className="w-3.5 h-3.5 animate-spin text-warning shrink-0" />
248
+ ) : isRunning ? (
249
+ <Loader2 className="w-3.5 h-3.5 animate-spin text-primary/80 shrink-0" />
250
+ ) : isStopped ? (
251
+ <Ban className="w-3.5 h-3.5 text-warning shrink-0" />
252
+ ) : isFailed ? (
253
+ <XCircle className="w-3.5 h-3.5 text-error shrink-0" />
254
+ ) : (
255
+ <Check className="w-3.5 h-3.5 text-success/80 shrink-0" />
256
+ )}
257
+ <span className="font-semibold text-white/90">
258
+ {displayReason || toolLabel}
259
+ </span>
260
+ {isPendingApproval ? (
261
+ <span className="text-[10px] text-warning font-normal animate-pulse">
262
+ (menunggu persetujuan...)
263
+ </span>
264
+ ) : isStopped ? (
265
+ <span className="text-[10px] text-warning/80 font-normal">
266
+ (dihentikan)
267
+ </span>
268
+ ) : null}
269
+ </div>
270
+ {isPendingApproval && (
271
+ <div className="pl-3 pr-1">
272
+ <ApprovalBubble
273
+ approvalId={activeApproval.id}
274
+ tool={activeApproval.tool}
275
+ query={activeApproval.query}
276
+ message={activeApproval.message}
277
+ status={activeApproval.status}
278
+ />
279
+ </div>
280
+ )}
281
+ </div>
282
+ )
283
+ }
284
+
285
+ return (
286
+ <div key={idx} className="flex flex-col py-0.5">
287
+ <details
288
+ className="group/toolitem outline-none text-xs font-mono"
289
+ open={isRunning}
290
+ >
291
+ <summary className="list-none flex items-center gap-2 cursor-pointer text-white/60 hover:text-white select-none py-0.5 transition-colors">
292
+ {isPendingApproval ? (
293
+ <Loader2 className="w-3.5 h-3.5 animate-spin text-warning shrink-0" />
294
+ ) : isRunning ? (
295
+ <Loader2 className="w-3.5 h-3.5 animate-spin text-primary/80 shrink-0" />
296
+ ) : isStopped ? (
297
+ <Ban className="w-3.5 h-3.5 text-warning shrink-0" />
298
+ ) : isFailed ? (
299
+ <XCircle className="w-3.5 h-3.5 text-error shrink-0" />
300
+ ) : (
301
+ <Check className="w-3.5 h-3.5 text-success/80 shrink-0" />
302
+ )}
303
+ <span className="font-semibold text-white/90">
304
+ {displayReason || toolLabel}
305
+ </span>
306
+ {isPendingApproval ? (
307
+ <span className="text-[10px] text-warning font-normal animate-pulse">
308
+ (menunggu persetujuan...)
309
+ </span>
310
+ ) : isStopped ? (
311
+ <span className="text-[10px] text-warning/80 font-normal">
312
+ (dihentikan)
313
+ </span>
314
+ ) : null}
315
+ {!displayReason && shortSummary && (
316
+ <span className="text-white/40 truncate max-w-md">
317
+ {String(shortSummary).slice(0, 80)}
318
+ </span>
319
+ )}
320
+ <ChevronRight className="w-3 h-3 text-white/40 transition-transform duration-150 group-open/toolitem:rotate-90 ml-auto shrink-0" />
321
+ </summary>
322
+ <div className="mt-1 pl-3 my-1.5 text-[11px] font-mono border-l-2 border-white/20 text-white/80 whitespace-pre-wrap break-all max-h-56 overflow-y-auto custom-scrollbar bg-base-300/40 p-2.5 rounded-lg space-y-1.5 select-text">
323
+ {t.tool && (
324
+ <div>
325
+ <div className="text-primary/70 font-semibold mb-0.5">
326
+ Tool:
327
+ </div>
328
+ <div className="text-white/90 font-mono text-[11px]">
329
+ {t.tool}
330
+ </div>
331
+ </div>
332
+ )}
333
+ {displayReason && (
334
+ <div>
335
+ <div className="text-info/80 font-semibold mb-0.5">Tujuan:</div>
336
+ <div className="text-white/90 font-sans">{displayReason}</div>
337
+ </div>
338
+ )}
339
+ {hasQuery && (
340
+ <div>
341
+ <div className="text-primary/70 font-semibold mb-0.5">
342
+ Input:
343
+ </div>
344
+ <div className="text-white/90">{queryString}</div>
345
+ </div>
346
+ )}
347
+ {hasResult && (
348
+ <div>
349
+ <div className="text-success/70 font-semibold mb-0.5">
350
+ Output:
351
+ </div>
352
+ <div className="text-white/80">{textResult}</div>
353
+ </div>
354
+ )}
355
+ {t.preview && (
356
+ <div className="mt-2 pt-1 border-t border-white/10">
357
+ <div className="text-primary/70 font-semibold mb-1">
358
+ Pratinjau Visual:
359
+ </div>
360
+ <div className="relative group/preview rounded-lg overflow-hidden border border-white/20 bg-black/40 max-w-sm">
361
+ <img
362
+ src={t.preview}
363
+ alt="Tool Visual Output"
364
+ className="max-h-48 w-auto object-contain cursor-pointer transition-transform duration-200 group-hover/preview:scale-105"
365
+ onClick={() => window.open(t.preview, '_blank')}
366
+ />
367
+ </div>
368
+ </div>
369
+ )}
370
+ </div>
371
+ </details>
372
+ {isPendingApproval && (
373
+ <div className="pl-3 pr-1">
374
+ <ApprovalBubble
375
+ approvalId={activeApproval.id}
376
+ tool={activeApproval.tool}
377
+ query={activeApproval.query}
378
+ message={activeApproval.message}
379
+ status={activeApproval.status}
380
+ />
381
+ </div>
382
+ )}
383
+ </div>
384
+ )
385
+ })}
386
+ </div>
387
+ </details>
388
+ )
389
+ })()}
390
+ </div>
391
+ )}
392
+
393
+ {/* Attached Images Preview Grid */}
394
+ {attachedImages && attachedImages.length > 0 && (
395
+ <div className="flex flex-wrap gap-2 my-1.5">
396
+ {attachedImages.map((imgSrc, idx) => (
397
+ <div
398
+ key={idx}
399
+ className="relative group/img rounded-xl overflow-hidden border border-white/20 shadow-md bg-black/40 max-w-xs"
400
+ >
401
+ <img
402
+ src={imgSrc}
403
+ alt={`Lampiran ${idx + 1}`}
404
+ className="max-h-56 w-auto object-contain cursor-pointer transition-transform duration-200 group-hover/img:scale-105"
405
+ onClick={() => {
406
+ const w = window.open('')
407
+ w?.document.write(
408
+ `<body style="margin:0;background:#0d1117;display:flex;align-items:center;justify-content:center;height:100vh;"><img src="${imgSrc}" style="max-width:95vw;max-height:95vh;border-radius:8px;object-contain;" /></body>`
409
+ )
410
+ }}
411
+ />
412
+ </div>
413
+ ))}
414
+ </div>
415
+ )}
416
+
417
+ {/* Markdown Content / Plain User Message */}
418
+ {stringContent &&
419
+ (isUser ? (
420
+ <div className="whitespace-pre-wrap leading-relaxed">{stringContent}</div>
421
+ ) : (
422
+ <div className="prose prose-sm max-w-none text-inherit prose-pre:p-0 prose-pre:bg-transparent prose-headings:text-inherit prose-strong:text-inherit">
423
+ <Markdown
424
+ remarkPlugins={[remarkGfm]}
425
+ rehypePlugins={[
426
+ [rehypeExternalLinks, { target: '_blank', rel: ['noopener', 'noreferrer'] }]
427
+ ]}
428
+ components={{
429
+ code: CodeBlock
430
+ }}
431
+ >
432
+ {stringContent}
433
+ </Markdown>
434
+ </div>
435
+ ))}
436
+
437
+ {/* Sources */}
438
+ {sources && sources.length > 0 && (
439
+ <div className="flex flex-wrap gap-2 mt-2 pt-2 border-t border-white/10">
440
+ <span className="text-[10px] font-bold opacity-50 w-full mb-1 uppercase tracking-wider">
441
+ Sumber & Referensi:
442
+ </span>
443
+ {sources.map((source, i) => (
444
+ <button
445
+ key={i}
446
+ onClick={() => window.api?.openExternal?.(source.link)}
447
+ className="btn btn-xs btn-neutral border border-primary/20 hover:border-primary/50 normal-case text-[10px] flex items-center gap-1.5 bg-base-300 transform transition hover:scale-105"
448
+ title={source.link}
449
+ >
450
+ <ExternalLink className="w-3 h-3 text-primary" />
451
+ <span className="truncate max-w-[150px]">{source.title || source.link}</span>
452
+ </button>
453
+ ))}
454
+ </div>
455
+ )}
456
+ </div>
457
+ )
458
+ }
459
+ )
@@ -0,0 +1,41 @@
1
+ import React from 'react'
2
+ import { FaCheck, FaChevronRight } from 'react-icons/fa'
3
+
4
+ export const PluginExecutionBubble = ({ pluginExecution }) => {
5
+ if (!pluginExecution) return null
6
+
7
+ return (
8
+ <div className="chat-bubble bg-transparent text-white p-0 shadow-none flex flex-col gap-1 mb-2">
9
+ <details className="group">
10
+ <summary className="text-xs cursor-pointer select-none flex items-center gap-1.5 opacity-60 hover:opacity-100 transition-opacity">
11
+ <FaChevronRight className="group-open:rotate-90 transition-transform text-[11px]" />
12
+ <span className="truncate max-w-[250px]">
13
+ Tool dieksekusi: {pluginExecution.action}
14
+ </span>
15
+ </summary>
16
+ <div className="pl-4 pt-1 flex flex-col gap-1 border-l border-white/20 ml-1.5 mt-1.5 mb-2">
17
+ <div className="flex items-start text-[11px] font-mono transition-opacity text-white">
18
+ <span className="opacity-100 text-success mr-1 font-bold inline-block w-3 text-center flex items-center justify-center">
19
+ <FaCheck size={10} />
20
+ </span>
21
+ <div className="flex-1 w-full overflow-hidden">
22
+ <div>
23
+ Mengeksekusi {pluginExecution.action}
24
+ {pluginExecution.query && (
25
+ <span className="opacity-50 ml-1 italic">: {pluginExecution.query}</span>
26
+ )}
27
+ </div>
28
+ {pluginExecution.result && (
29
+ <div className="mt-1.5 mb-1 opacity-80 text-[10px] text-info bg-info/10 p-2 rounded-md border border-info/20 whitespace-pre-wrap font-sans leading-relaxed break-words">
30
+ {pluginExecution.result}
31
+ </div>
32
+ )}
33
+ </div>
34
+ </div>
35
+ </div>
36
+ </details>
37
+ </div>
38
+ )
39
+ }
40
+
41
+ export default PluginExecutionBubble;