@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,127 @@
|
|
|
1
|
+
/* eslint-disable react/prop-types */
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
import { useApproval, extractToolTarget } from '../../contexts/ApprovalContext'
|
|
4
|
+
import FileDiffModal from './FileDiffModal'
|
|
5
|
+
|
|
6
|
+
const FILE_TOOLS = ['write-file', 'replace-content', 'replace-lines', 'delete-file']
|
|
7
|
+
|
|
8
|
+
export const ApprovalBubble = ({
|
|
9
|
+
approvalId,
|
|
10
|
+
tool = '',
|
|
11
|
+
message = '',
|
|
12
|
+
query = null,
|
|
13
|
+
status = 'pending'
|
|
14
|
+
}) => {
|
|
15
|
+
const { resolveApproval } = useApproval()
|
|
16
|
+
const [showDiffModal, setShowDiffModal] = useState(false)
|
|
17
|
+
|
|
18
|
+
// Hilang seketika setelah di-acc atau ditolak
|
|
19
|
+
if (status !== 'pending') {
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const targetInfo = extractToolTarget(tool, query)
|
|
24
|
+
const isFileTool = FILE_TOOLS.includes(tool)
|
|
25
|
+
const previewValue =
|
|
26
|
+
targetInfo.value ||
|
|
27
|
+
message ||
|
|
28
|
+
(typeof query === 'string' ? query : JSON.stringify(query, null, 2))
|
|
29
|
+
|
|
30
|
+
const handleDecision = (decisionType) => {
|
|
31
|
+
setShowDiffModal(false)
|
|
32
|
+
if (resolveApproval) {
|
|
33
|
+
resolveApproval(approvalId, decisionType)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div className="my-2 p-3 rounded-xl bg-base-300/50 border border-white/5 space-y-2.5 text-xs text-base-content select-text shadow-sm">
|
|
39
|
+
{/* Header persetujuan */}
|
|
40
|
+
<div className="flex items-center justify-between gap-2">
|
|
41
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
42
|
+
<span className="font-semibold text-white/90 text-xs tracking-wide">
|
|
43
|
+
Persetujuan Eksekusi
|
|
44
|
+
</span>
|
|
45
|
+
{tool && (
|
|
46
|
+
<span className="bg-warning/10 text-warning px-2 py-0.5 rounded text-[11px] font-mono font-medium">
|
|
47
|
+
{tool}
|
|
48
|
+
</span>
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
{isFileTool && (
|
|
53
|
+
<button
|
|
54
|
+
type="button"
|
|
55
|
+
onClick={() => setShowDiffModal(true)}
|
|
56
|
+
className="px-2.5 py-1 rounded-lg bg-primary/10 hover:bg-primary/20 text-primary font-mono text-xs transition-colors cursor-pointer"
|
|
57
|
+
>
|
|
58
|
+
Review
|
|
59
|
+
</button>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<p className="text-white/60 text-[11px]">
|
|
64
|
+
Mark meminta izin untuk mengeksekusi operasi berikut:
|
|
65
|
+
</p>
|
|
66
|
+
|
|
67
|
+
{/* Parameter / Command Preview */}
|
|
68
|
+
<div className="bg-black/30 rounded-lg p-2.5 font-mono text-[11px] text-white/90 space-y-1">
|
|
69
|
+
{targetInfo.value && (
|
|
70
|
+
<div className="text-[10px] uppercase font-bold text-warning/70 select-none">
|
|
71
|
+
{targetInfo.type === 'command' ? 'Perintah Shell' : 'Target Berkas'}
|
|
72
|
+
</div>
|
|
73
|
+
)}
|
|
74
|
+
<div className="text-white/90 whitespace-pre-wrap break-all leading-relaxed">
|
|
75
|
+
{previewValue}
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
{/* Action Buttons */}
|
|
80
|
+
<div className="flex flex-wrap items-center justify-between gap-2 pt-0.5">
|
|
81
|
+
<button
|
|
82
|
+
type="button"
|
|
83
|
+
onClick={() => handleDecision('reject')}
|
|
84
|
+
className="btn btn-ghost btn-xs text-error/80 hover:text-error hover:bg-error/10 rounded-lg px-3"
|
|
85
|
+
>
|
|
86
|
+
Tolak
|
|
87
|
+
</button>
|
|
88
|
+
<div className="flex flex-wrap items-center gap-1.5">
|
|
89
|
+
<button
|
|
90
|
+
type="button"
|
|
91
|
+
onClick={() => handleDecision('approve_session')}
|
|
92
|
+
className="btn btn-ghost btn-xs text-warning/80 hover:text-warning hover:bg-warning/10 rounded-lg px-2.5"
|
|
93
|
+
>
|
|
94
|
+
Izinkan Sesi Ini
|
|
95
|
+
</button>
|
|
96
|
+
<button
|
|
97
|
+
type="button"
|
|
98
|
+
onClick={() => handleDecision('approve_always')}
|
|
99
|
+
className="btn btn-ghost btn-xs text-white/40 hover:text-error hover:bg-error/10 rounded-lg px-2.5"
|
|
100
|
+
>
|
|
101
|
+
Izinkan Selamanya
|
|
102
|
+
</button>
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
onClick={() => handleDecision('approve_once')}
|
|
106
|
+
className="btn btn-primary btn-xs text-black font-semibold rounded-lg px-3 shadow-sm"
|
|
107
|
+
>
|
|
108
|
+
Izinkan Sekali
|
|
109
|
+
</button>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
{/* Floating Diff Modal */}
|
|
114
|
+
{isFileTool && (
|
|
115
|
+
<FileDiffModal
|
|
116
|
+
isOpen={showDiffModal}
|
|
117
|
+
onClose={() => setShowDiffModal(false)}
|
|
118
|
+
tool={tool}
|
|
119
|
+
query={query}
|
|
120
|
+
onResolve={handleDecision}
|
|
121
|
+
/>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export default ApprovalBubble
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
|
|
3
|
+
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
|
|
4
|
+
|
|
5
|
+
export const CodeBlock = React.memo(({ node, inline, className, children, ...props }) => {
|
|
6
|
+
const match = /language-(\w+)/.exec(className || '')
|
|
7
|
+
const [isCopied, setIsCopied] = useState(false)
|
|
8
|
+
|
|
9
|
+
const handleCopy = () => {
|
|
10
|
+
navigator.clipboard.writeText(String(children).replace(/\n$/, ''))
|
|
11
|
+
setIsCopied(true)
|
|
12
|
+
setTimeout(() => setIsCopied(false), 2000)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!inline && match) {
|
|
16
|
+
return (
|
|
17
|
+
<div className="relative group my-4 rounded-xl overflow-hidden border border-base-300 shadow-sm bg-base-200/50">
|
|
18
|
+
<div className="flex items-center justify-between px-4 py-1.5 bg-base-300/50 text-[10px] uppercase tracking-wider font-bold text-white/50 border-b border-base-300">
|
|
19
|
+
<span>{match[1]}</span>
|
|
20
|
+
<button
|
|
21
|
+
onClick={handleCopy}
|
|
22
|
+
className="flex items-center gap-1.5 hover:text-white transition-colors cursor-pointer"
|
|
23
|
+
>
|
|
24
|
+
{isCopied ? (
|
|
25
|
+
<>
|
|
26
|
+
<svg
|
|
27
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
28
|
+
width="12"
|
|
29
|
+
height="12"
|
|
30
|
+
viewBox="0 0 24 24"
|
|
31
|
+
fill="none"
|
|
32
|
+
stroke="currentColor"
|
|
33
|
+
strokeWidth="2"
|
|
34
|
+
strokeLinecap="round"
|
|
35
|
+
strokeLinejoin="round"
|
|
36
|
+
className="text-success"
|
|
37
|
+
>
|
|
38
|
+
<polyline points="20 6 9 17 4 12"></polyline>
|
|
39
|
+
</svg>
|
|
40
|
+
<span className="text-success">Copied!</span>
|
|
41
|
+
</>
|
|
42
|
+
) : (
|
|
43
|
+
<>
|
|
44
|
+
<svg
|
|
45
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
46
|
+
width="12"
|
|
47
|
+
height="12"
|
|
48
|
+
viewBox="0 0 24 24"
|
|
49
|
+
fill="none"
|
|
50
|
+
stroke="currentColor"
|
|
51
|
+
strokeWidth="2"
|
|
52
|
+
strokeLinecap="round"
|
|
53
|
+
strokeLinejoin="round"
|
|
54
|
+
>
|
|
55
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
56
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
57
|
+
</svg>
|
|
58
|
+
<span>Copy</span>
|
|
59
|
+
</>
|
|
60
|
+
)}
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
63
|
+
<SyntaxHighlighter
|
|
64
|
+
{...props}
|
|
65
|
+
style={oneDark}
|
|
66
|
+
language={match[1]}
|
|
67
|
+
PreTag="div"
|
|
68
|
+
className="!m-0 !bg-transparent text-[12px] no-scrollbar"
|
|
69
|
+
>
|
|
70
|
+
{String(children).replace(/\n$/, '')}
|
|
71
|
+
</SyntaxHighlighter>
|
|
72
|
+
</div>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
return (
|
|
76
|
+
<code
|
|
77
|
+
{...props}
|
|
78
|
+
className={`${className} bg-white/10 px-1.5 py-0.5 rounded-md text-[12px] font-mono`}
|
|
79
|
+
>
|
|
80
|
+
{children}
|
|
81
|
+
</code>
|
|
82
|
+
)
|
|
83
|
+
})
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
/* eslint-disable react/prop-types */
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
import Markdown from 'react-markdown'
|
|
4
|
+
import remarkGfm from 'remark-gfm'
|
|
5
|
+
import {
|
|
6
|
+
Check,
|
|
7
|
+
FolderOpen,
|
|
8
|
+
FileText,
|
|
9
|
+
Square,
|
|
10
|
+
ChevronRight,
|
|
11
|
+
XCircle,
|
|
12
|
+
Brain,
|
|
13
|
+
Terminal,
|
|
14
|
+
Ban,
|
|
15
|
+
Loader2
|
|
16
|
+
} from 'lucide-react'
|
|
17
|
+
import { useApproval } from '../../contexts/ApprovalContext'
|
|
18
|
+
import { ApprovalBubble } from './ApprovalBubble'
|
|
19
|
+
import { FileDiffModal } from './FileDiffModal'
|
|
20
|
+
|
|
21
|
+
export const DurableTaskBubble = ({
|
|
22
|
+
plan = [],
|
|
23
|
+
resolvedCurrentStep = 0,
|
|
24
|
+
reasoning = null,
|
|
25
|
+
taskId = null,
|
|
26
|
+
taskTitle = null,
|
|
27
|
+
taskStatus = null,
|
|
28
|
+
artifactRoot = null,
|
|
29
|
+
onStop = null,
|
|
30
|
+
executedTools = [],
|
|
31
|
+
activeLiveTools = null,
|
|
32
|
+
activeThinkingContent = null
|
|
33
|
+
}) => {
|
|
34
|
+
const [selectedArtifactPath, setSelectedArtifactPath] = useState(null)
|
|
35
|
+
const [isProcessOpen, setIsProcessOpen] = useState(true)
|
|
36
|
+
const { activeApproval } = useApproval() || {}
|
|
37
|
+
|
|
38
|
+
const totalSteps = plan.length
|
|
39
|
+
const completedCount = plan.filter(
|
|
40
|
+
(s, idx) =>
|
|
41
|
+
s.status === 'completed' || plan.slice(idx + 1).some((later) => later.status === 'completed')
|
|
42
|
+
).length
|
|
43
|
+
|
|
44
|
+
const isStopped = taskStatus === 'stopped' || taskStatus === 'cancelled'
|
|
45
|
+
const isFailed = taskStatus === 'failed'
|
|
46
|
+
const isAllDone =
|
|
47
|
+
taskStatus === 'completed' ||
|
|
48
|
+
(!isStopped && !isFailed && totalSteps > 0 && completedCount >= totalSteps)
|
|
49
|
+
const isRunning = !isAllDone && !isFailed && !isStopped
|
|
50
|
+
|
|
51
|
+
const toolsToDisplay =
|
|
52
|
+
activeLiveTools && activeLiveTools.length > 0 ? activeLiveTools : executedTools || []
|
|
53
|
+
const displayReasoning = reasoning || activeThinkingContent
|
|
54
|
+
|
|
55
|
+
const handleOpenFolder = (folderPath) => {
|
|
56
|
+
if (!folderPath) return
|
|
57
|
+
fetch('/api/tasks/ensure-dir', {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: { 'Content-Type': 'application/json' },
|
|
60
|
+
body: JSON.stringify({ dirPath: folderPath })
|
|
61
|
+
})
|
|
62
|
+
.then((r) => r.json())
|
|
63
|
+
.then((res) => {
|
|
64
|
+
const target = res?.data || folderPath
|
|
65
|
+
if (window.api?.executeNativeTool) {
|
|
66
|
+
window.api.executeNativeTool('open-folder', { path: target }).catch(() => {
|
|
67
|
+
window.api.executeNativeTool('open', target).catch(() => {})
|
|
68
|
+
})
|
|
69
|
+
} else if (window.api?.openPath) {
|
|
70
|
+
window.api.openPath(target)
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
.catch(() => {
|
|
74
|
+
if (window.api?.executeNativeTool) {
|
|
75
|
+
window.api.executeNativeTool('open-folder', { path: folderPath }).catch(() => {
|
|
76
|
+
window.api.executeNativeTool('open', folderPath).catch(() => {})
|
|
77
|
+
})
|
|
78
|
+
} else if (window.api?.openPath) {
|
|
79
|
+
window.api.openPath(folderPath)
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const renderToolItem = (t, idx) => {
|
|
85
|
+
const isToolRunning = t.status === 'running'
|
|
86
|
+
const isPendingApproval = Boolean(
|
|
87
|
+
activeApproval &&
|
|
88
|
+
activeApproval.status === 'pending' &&
|
|
89
|
+
isToolRunning &&
|
|
90
|
+
(activeApproval.tool === t.tool || !t.tool)
|
|
91
|
+
)
|
|
92
|
+
const hasQuery = t.query !== undefined && t.query !== null && t.query !== ''
|
|
93
|
+
const queryString = typeof t.query === 'string' ? t.query : JSON.stringify(t.query, null, 2)
|
|
94
|
+
const textResult = String(t.fullResult || t.resultSummary || t.result || '')
|
|
95
|
+
const hasResult = Boolean(textResult.trim())
|
|
96
|
+
const isStoppedTool = t.status === 'stopped' || t.status === 'cancelled'
|
|
97
|
+
const hasError =
|
|
98
|
+
!isStoppedTool &&
|
|
99
|
+
(t.status === 'failed' ||
|
|
100
|
+
t.status === 'error' ||
|
|
101
|
+
textResult.startsWith('[ERROR]') ||
|
|
102
|
+
textResult.includes(' crash:') ||
|
|
103
|
+
textResult.toLowerCase().includes(' gagal:'))
|
|
104
|
+
const isSuccessful =
|
|
105
|
+
!isStoppedTool &&
|
|
106
|
+
!hasError &&
|
|
107
|
+
(t.status === 'done' || t.status === 'success' || t.status !== 'error')
|
|
108
|
+
|
|
109
|
+
const StatusIcon = isStoppedTool ? Ban : isSuccessful ? Check : XCircle
|
|
110
|
+
const statusClass = isStoppedTool
|
|
111
|
+
? 'text-warning'
|
|
112
|
+
: isSuccessful
|
|
113
|
+
? 'text-success'
|
|
114
|
+
: 'text-error'
|
|
115
|
+
|
|
116
|
+
let parsedArgs = null
|
|
117
|
+
if (hasQuery) {
|
|
118
|
+
try {
|
|
119
|
+
parsedArgs =
|
|
120
|
+
typeof t.query === 'object' && t.query !== null ? t.query : JSON.parse(queryString)
|
|
121
|
+
} catch (_) {}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const toolLabel = t.tool || t.task || 'tool'
|
|
125
|
+
const displayReason =
|
|
126
|
+
t.reason ||
|
|
127
|
+
parsedArgs?.reason ||
|
|
128
|
+
parsedArgs?.description ||
|
|
129
|
+
parsedArgs?.task ||
|
|
130
|
+
parsedArgs?.title ||
|
|
131
|
+
null
|
|
132
|
+
|
|
133
|
+
let shortSummary = ''
|
|
134
|
+
if (parsedArgs && typeof parsedArgs === 'object') {
|
|
135
|
+
shortSummary =
|
|
136
|
+
parsedArgs.command ||
|
|
137
|
+
parsedArgs.path ||
|
|
138
|
+
parsedArgs.file ||
|
|
139
|
+
parsedArgs.filePath ||
|
|
140
|
+
parsedArgs.url ||
|
|
141
|
+
parsedArgs.query ||
|
|
142
|
+
parsedArgs.prompt ||
|
|
143
|
+
parsedArgs.message ||
|
|
144
|
+
parsedArgs.skill_name ||
|
|
145
|
+
parsedArgs.text ||
|
|
146
|
+
(parsedArgs.script ? parsedArgs.script.trim().replace(/\s+/g, ' ').slice(0, 80) : '') ||
|
|
147
|
+
''
|
|
148
|
+
}
|
|
149
|
+
if (!shortSummary && hasQuery) {
|
|
150
|
+
shortSummary = queryString
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!hasQuery && !hasResult) {
|
|
154
|
+
return (
|
|
155
|
+
<div key={idx} className="flex flex-col py-0.5">
|
|
156
|
+
<div className="flex items-center gap-2 text-xs font-mono text-white/60">
|
|
157
|
+
{isPendingApproval ? (
|
|
158
|
+
<Loader2 className="w-3.5 h-3.5 animate-spin text-warning shrink-0" />
|
|
159
|
+
) : isToolRunning ? (
|
|
160
|
+
<Loader2 className="w-3.5 h-3.5 animate-spin text-primary/80 shrink-0" />
|
|
161
|
+
) : isStoppedTool ? (
|
|
162
|
+
<Ban className="w-3.5 h-3.5 text-warning shrink-0" />
|
|
163
|
+
) : hasError ? (
|
|
164
|
+
<XCircle className="w-3.5 h-3.5 text-error shrink-0" />
|
|
165
|
+
) : (
|
|
166
|
+
<Check className="w-3.5 h-3.5 text-success/80 shrink-0" />
|
|
167
|
+
)}
|
|
168
|
+
<span className="font-semibold text-white/90">{displayReason || toolLabel}</span>
|
|
169
|
+
{isPendingApproval ? (
|
|
170
|
+
<span className="text-[10px] text-warning font-normal animate-pulse">
|
|
171
|
+
(menunggu persetujuan...)
|
|
172
|
+
</span>
|
|
173
|
+
) : isStoppedTool ? (
|
|
174
|
+
<span className="text-[10px] text-warning/80 font-normal">(dihentikan)</span>
|
|
175
|
+
) : null}
|
|
176
|
+
</div>
|
|
177
|
+
{isPendingApproval && (
|
|
178
|
+
<div className="pl-3 pr-1">
|
|
179
|
+
<ApprovalBubble
|
|
180
|
+
approvalId={activeApproval.id}
|
|
181
|
+
tool={activeApproval.tool}
|
|
182
|
+
query={activeApproval.query}
|
|
183
|
+
message={activeApproval.message}
|
|
184
|
+
status={activeApproval.status}
|
|
185
|
+
/>
|
|
186
|
+
</div>
|
|
187
|
+
)}
|
|
188
|
+
</div>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<div key={idx} className="flex flex-col py-0.5">
|
|
194
|
+
<details className="group/toolitem outline-none text-xs font-mono" open={isToolRunning}>
|
|
195
|
+
<summary className="list-none flex items-center gap-2 cursor-pointer text-white/60 hover:text-white select-none py-0.5 transition-colors">
|
|
196
|
+
{isPendingApproval ? (
|
|
197
|
+
<Loader2 className="w-3.5 h-3.5 animate-spin text-warning shrink-0" />
|
|
198
|
+
) : isToolRunning ? (
|
|
199
|
+
<Loader2 className="w-3.5 h-3.5 animate-spin text-primary/80 shrink-0" />
|
|
200
|
+
) : isStoppedTool ? (
|
|
201
|
+
<Ban className="w-3.5 h-3.5 text-warning shrink-0" />
|
|
202
|
+
) : hasError ? (
|
|
203
|
+
<XCircle className="w-3.5 h-3.5 text-error shrink-0" />
|
|
204
|
+
) : (
|
|
205
|
+
<Check className="w-3.5 h-3.5 text-success/80 shrink-0" />
|
|
206
|
+
)}
|
|
207
|
+
<span className="font-semibold text-white/90">{displayReason || toolLabel}</span>
|
|
208
|
+
{isPendingApproval ? (
|
|
209
|
+
<span className="text-[10px] text-warning font-normal animate-pulse">
|
|
210
|
+
(menunggu persetujuan...)
|
|
211
|
+
</span>
|
|
212
|
+
) : isStoppedTool ? (
|
|
213
|
+
<span className="text-[10px] text-warning/80 font-normal">(dihentikan)</span>
|
|
214
|
+
) : null}
|
|
215
|
+
{!displayReason && shortSummary && (
|
|
216
|
+
<span className="text-white/40 truncate max-w-md">
|
|
217
|
+
{String(shortSummary).slice(0, 80)}
|
|
218
|
+
</span>
|
|
219
|
+
)}
|
|
220
|
+
<ChevronRight className="w-3 h-3 text-white/40 transition-transform duration-150 group-open/toolitem:rotate-90 ml-auto shrink-0" />
|
|
221
|
+
</summary>
|
|
222
|
+
<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">
|
|
223
|
+
{t.tool && (
|
|
224
|
+
<div>
|
|
225
|
+
<div className="text-primary/70 font-semibold mb-0.5">Tool:</div>
|
|
226
|
+
<div className="text-white/90 font-mono text-[11px]">{t.tool}</div>
|
|
227
|
+
</div>
|
|
228
|
+
)}
|
|
229
|
+
{displayReason && (
|
|
230
|
+
<div>
|
|
231
|
+
<div className="text-info/80 font-semibold mb-0.5">Tujuan:</div>
|
|
232
|
+
<div className="text-white/90 font-sans">{displayReason}</div>
|
|
233
|
+
</div>
|
|
234
|
+
)}
|
|
235
|
+
{hasQuery && (
|
|
236
|
+
<div>
|
|
237
|
+
<div className="text-primary/70 font-semibold mb-0.5">Input:</div>
|
|
238
|
+
<div className="text-white/90">{queryString}</div>
|
|
239
|
+
</div>
|
|
240
|
+
)}
|
|
241
|
+
{hasResult && (
|
|
242
|
+
<div>
|
|
243
|
+
<div className="text-success/70 font-semibold mb-0.5">Output:</div>
|
|
244
|
+
<div className="text-white/80">{textResult}</div>
|
|
245
|
+
</div>
|
|
246
|
+
)}
|
|
247
|
+
</div>
|
|
248
|
+
</details>
|
|
249
|
+
{isPendingApproval && (
|
|
250
|
+
<div className="pl-3 pr-1 my-1">
|
|
251
|
+
<ApprovalBubble
|
|
252
|
+
approvalId={activeApproval.id}
|
|
253
|
+
tool={activeApproval.tool}
|
|
254
|
+
query={activeApproval.query}
|
|
255
|
+
message={activeApproval.message}
|
|
256
|
+
status={activeApproval.status}
|
|
257
|
+
/>
|
|
258
|
+
</div>
|
|
259
|
+
)}
|
|
260
|
+
</div>
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<div className="flex flex-col gap-2 w-full text-xs text-white/90 my-1 py-1">
|
|
266
|
+
{/* ── 1. HEADER ALUR KERJA MINIMALIS ── */}
|
|
267
|
+
<div className="flex items-center justify-between gap-2 pb-1.5 border-b border-white/10 text-xs select-none">
|
|
268
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
269
|
+
<span className="font-semibold text-primary">Workflow :</span>
|
|
270
|
+
<span className="text-white/90 font-medium truncate">{taskTitle || 'Task Workflow'}</span>
|
|
271
|
+
<span className="text-white/40 text-[11px] font-mono shrink-0">
|
|
272
|
+
({completedCount}/{totalSteps})
|
|
273
|
+
</span>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<div className="flex items-center gap-2 shrink-0">
|
|
277
|
+
<span
|
|
278
|
+
className={`text-[10px] font-mono px-1.5 py-0.5 rounded ${
|
|
279
|
+
isAllDone
|
|
280
|
+
? 'text-primary bg-primary/10'
|
|
281
|
+
: isStopped
|
|
282
|
+
? 'text-amber-400 bg-amber-400/10'
|
|
283
|
+
: isFailed
|
|
284
|
+
? 'text-rose-400 bg-rose-400/10'
|
|
285
|
+
: 'text-primary bg-primary/10'
|
|
286
|
+
}`}
|
|
287
|
+
>
|
|
288
|
+
{isAllDone ? 'Selesai' : isStopped ? 'Dihentikan' : isFailed ? 'Gagal' : 'Berjalan'}
|
|
289
|
+
</span>
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
{/* ── 2. DAFTAR CHECKBOX STEPS BIASA (FLAT MINIMALIS) ── */}
|
|
294
|
+
<div className="flex flex-col gap-1 py-0.5">
|
|
295
|
+
{plan.map((step, idx) => {
|
|
296
|
+
const hasLaterDone = plan.slice(idx + 1).some((later) => later.status === 'completed')
|
|
297
|
+
const isStepDone = step.status === 'completed' || hasLaterDone
|
|
298
|
+
const isStepRunning =
|
|
299
|
+
!isStepDone && isRunning && (step.status === 'running' || idx === resolvedCurrentStep)
|
|
300
|
+
|
|
301
|
+
let title = typeof step === 'string' ? step : step.title || step.task || ''
|
|
302
|
+
if (!title || /^((langkah|tahap|step)\s*\d+[:.-]?\s*)$/i.test(title.trim())) {
|
|
303
|
+
title = step.objective || step.deliverable || `Tahap ${idx + 1}`
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const artifactPath = typeof step === 'object' ? step.artifactPath : null
|
|
307
|
+
|
|
308
|
+
return (
|
|
309
|
+
<div
|
|
310
|
+
key={step.id || `step-${idx}`}
|
|
311
|
+
className="flex items-center justify-between gap-2.5 py-1 select-none"
|
|
312
|
+
>
|
|
313
|
+
<div className="flex items-center gap-2.5 min-w-0 flex-1">
|
|
314
|
+
{/* Checkbox Icon / Loading Spinner */}
|
|
315
|
+
{isStepDone ? (
|
|
316
|
+
<div className="w-3.5 h-3.5 rounded bg-primary/20 border border-primary flex items-center justify-center text-primary shrink-0">
|
|
317
|
+
<Check className="w-2.5 h-2.5 stroke-[3]" />
|
|
318
|
+
</div>
|
|
319
|
+
) : isStepRunning ? (
|
|
320
|
+
<div className="w-3.5 h-3.5 rounded flex items-center justify-center text-primary shrink-0">
|
|
321
|
+
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
|
322
|
+
</div>
|
|
323
|
+
) : (
|
|
324
|
+
<div className="w-3.5 h-3.5 rounded border border-white/25 shrink-0" />
|
|
325
|
+
)}
|
|
326
|
+
|
|
327
|
+
{/* Step Title & Deliverable Info */}
|
|
328
|
+
<div className="flex items-baseline gap-1.5 min-w-0 truncate">
|
|
329
|
+
<span
|
|
330
|
+
className={`text-xs truncate ${
|
|
331
|
+
isStepDone
|
|
332
|
+
? 'text-white/90'
|
|
333
|
+
: isStepRunning
|
|
334
|
+
? 'text-primary font-medium'
|
|
335
|
+
: 'text-white/70'
|
|
336
|
+
}`}
|
|
337
|
+
>
|
|
338
|
+
{idx + 1}. {title}
|
|
339
|
+
</span>
|
|
340
|
+
{isStepRunning && (
|
|
341
|
+
<span className="text-[10px] font-mono text-primary/80 animate-pulse shrink-0">
|
|
342
|
+
(sedang berjalan...)
|
|
343
|
+
</span>
|
|
344
|
+
)}
|
|
345
|
+
{step.deliverable && !isStepRunning && (
|
|
346
|
+
<span className="text-[10px] font-mono text-white/30 truncate hidden sm:inline">
|
|
347
|
+
({step.deliverable})
|
|
348
|
+
</span>
|
|
349
|
+
)}
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
|
|
353
|
+
{/* Action Button: Lihat Hasil */}
|
|
354
|
+
{isStepDone && artifactPath && (
|
|
355
|
+
<button
|
|
356
|
+
type="button"
|
|
357
|
+
onClick={() => setSelectedArtifactPath(artifactPath)}
|
|
358
|
+
className="btn btn-ghost btn-xs text-primary hover:underline gap-1 font-mono text-[10.5px] h-5 min-h-0 px-1.5 shrink-0"
|
|
359
|
+
title="Buka pratinjau artefak dokumen"
|
|
360
|
+
>
|
|
361
|
+
<FileText className="w-3 h-3 text-primary" />
|
|
362
|
+
<span>Lihat Hasil</span>
|
|
363
|
+
</button>
|
|
364
|
+
)}
|
|
365
|
+
</div>
|
|
366
|
+
)
|
|
367
|
+
})}
|
|
368
|
+
</div>
|
|
369
|
+
|
|
370
|
+
{/* ── 3. PROSES SAMA PERSIS SEPERTI BUBBLE BIASA ── */}
|
|
371
|
+
{((toolsToDisplay && toolsToDisplay.length > 0) || displayReasoning) && (
|
|
372
|
+
<div className="my-2 space-y-1.5 select-none pt-1.5 border-t border-white/10">
|
|
373
|
+
{/* Thinking / Reasoning Accordion */}
|
|
374
|
+
{displayReasoning && (
|
|
375
|
+
<details className="group/thought outline-none" open={isRunning}>
|
|
376
|
+
<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">
|
|
377
|
+
<Brain className="w-3.5 h-3.5 text-primary/80 shrink-0" />
|
|
378
|
+
<span className="font-medium text-white/80">Proses Pemikiran AI</span>
|
|
379
|
+
<ChevronRight className="w-3 h-3 text-white/40 transition-transform duration-150 group-open/thought:rotate-90 ml-auto shrink-0" />
|
|
380
|
+
</summary>
|
|
381
|
+
<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">
|
|
382
|
+
<Markdown remarkPlugins={[remarkGfm]}>
|
|
383
|
+
{typeof displayReasoning === 'string'
|
|
384
|
+
? displayReasoning
|
|
385
|
+
: JSON.stringify(displayReasoning, null, 2)}
|
|
386
|
+
</Markdown>
|
|
387
|
+
</div>
|
|
388
|
+
</details>
|
|
389
|
+
)}
|
|
390
|
+
|
|
391
|
+
{/* Executed Tools Folded Process Accordion */}
|
|
392
|
+
{toolsToDisplay && toolsToDisplay.length > 0 && (
|
|
393
|
+
<details
|
|
394
|
+
className="group/process outline-none"
|
|
395
|
+
open={isRunning || isProcessOpen}
|
|
396
|
+
onToggle={(e) => setIsProcessOpen(e.currentTarget.open)}
|
|
397
|
+
>
|
|
398
|
+
<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">
|
|
399
|
+
<Terminal className="w-3.5 h-3.5 text-primary/80 shrink-0" />
|
|
400
|
+
<span className="font-semibold text-white/90">Proses</span>
|
|
401
|
+
<span className="text-[10px] text-white/40 font-normal">
|
|
402
|
+
({toolsToDisplay.length} langkah)
|
|
403
|
+
</span>
|
|
404
|
+
<ChevronRight className="w-3.5 h-3.5 text-white/40 transition-transform duration-150 group-open/process:rotate-90 ml-auto shrink-0" />
|
|
405
|
+
</summary>
|
|
406
|
+
<div className="mt-1 pl-2.5 space-y-1 border-l-2 border-white/15 ml-1.5 my-1">
|
|
407
|
+
{toolsToDisplay.map((t, idx) => renderToolItem(t, idx))}
|
|
408
|
+
</div>
|
|
409
|
+
</details>
|
|
410
|
+
)}
|
|
411
|
+
</div>
|
|
412
|
+
)}
|
|
413
|
+
|
|
414
|
+
{/* Indikator Menunggu ketika masih berjalan tanpa tool/reasoning */}
|
|
415
|
+
{isRunning && (!toolsToDisplay || toolsToDisplay.length === 0) && !displayReasoning && (
|
|
416
|
+
<div className="flex items-center gap-2 text-xs text-primary font-medium py-1">
|
|
417
|
+
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
|
418
|
+
<span className="text-white/80 animate-pulse">
|
|
419
|
+
Mark sedang menganalisis & mengeksekusi...
|
|
420
|
+
</span>
|
|
421
|
+
</div>
|
|
422
|
+
)}
|
|
423
|
+
|
|
424
|
+
{/* ── 4. FOOTER FOLDER ARTEFAK ── */}
|
|
425
|
+
{artifactRoot && (
|
|
426
|
+
<div className="flex items-center justify-between pt-1 border-t border-white/5 text-[10px] font-mono text-white/40 select-none">
|
|
427
|
+
<span className="truncate max-w-[200px]">
|
|
428
|
+
Folder: {artifactRoot.split(/[\\/]/).pop()}
|
|
429
|
+
</span>
|
|
430
|
+
<button
|
|
431
|
+
type="button"
|
|
432
|
+
onClick={() => handleOpenFolder(artifactRoot)}
|
|
433
|
+
className="text-[10px] text-primary hover:underline flex items-center gap-1"
|
|
434
|
+
>
|
|
435
|
+
<FolderOpen className="w-3 h-3" />
|
|
436
|
+
<span>Buka Folder</span>
|
|
437
|
+
</button>
|
|
438
|
+
</div>
|
|
439
|
+
)}
|
|
440
|
+
|
|
441
|
+
{/* ── 5. MODAL PRATINJAU ARTEFAK DOKUMEN VIA FILEDIFFMODAL ── */}
|
|
442
|
+
{selectedArtifactPath && (
|
|
443
|
+
<FileDiffModal
|
|
444
|
+
isOpen={Boolean(selectedArtifactPath)}
|
|
445
|
+
onClose={() => setSelectedArtifactPath(null)}
|
|
446
|
+
tool="read-file"
|
|
447
|
+
query={{ path: selectedArtifactPath }}
|
|
448
|
+
/>
|
|
449
|
+
)}
|
|
450
|
+
</div>
|
|
451
|
+
)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export const PlanningBubble = DurableTaskBubble
|
|
455
|
+
export default DurableTaskBubble
|