@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,216 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
|
+
import { FaCheckCircle, FaListUl, FaBolt, FaCheck, FaChevronRight } from 'react-icons/fa'
|
|
3
|
+
|
|
4
|
+
const ProcessPanel = ({ processes = [], onDismiss, isEmbedded = false }) => {
|
|
5
|
+
const [renderedProcesses, setRenderedProcesses] = useState([])
|
|
6
|
+
const executingTaskCount = processes.filter(
|
|
7
|
+
(process) => process.type === 'planning' && process.status === 'active'
|
|
8
|
+
).length
|
|
9
|
+
|
|
10
|
+
// Sync rendered processes with delayed unmount
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
setRenderedProcesses((prev) => {
|
|
13
|
+
let next = prev.map((rp) => {
|
|
14
|
+
const updated = processes.find((p) => p.id === rp.id)
|
|
15
|
+
if (updated) return { ...updated, isExiting: false }
|
|
16
|
+
if (!rp.isExiting) return { ...rp, isExiting: true }
|
|
17
|
+
return rp
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
processes.forEach((p) => {
|
|
21
|
+
if (!prev.find((rp) => rp.id === p.id)) {
|
|
22
|
+
next.push({ ...p, isExiting: false })
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
return next
|
|
27
|
+
})
|
|
28
|
+
}, [processes])
|
|
29
|
+
|
|
30
|
+
// Clean up exiting processes after animation
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const hasExiting = renderedProcesses.some((p) => p.isExiting)
|
|
33
|
+
if (hasExiting) {
|
|
34
|
+
const timer = setTimeout(() => {
|
|
35
|
+
setRenderedProcesses((prev) => prev.filter((p) => !p.isExiting))
|
|
36
|
+
}, 250)
|
|
37
|
+
return () => clearTimeout(timer)
|
|
38
|
+
}
|
|
39
|
+
}, [renderedProcesses])
|
|
40
|
+
|
|
41
|
+
// Auto-dismiss logic for 'done' and 'failed' status
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const timers = []
|
|
44
|
+
processes.forEach((proc) => {
|
|
45
|
+
if (proc.status === 'done' || proc.status === 'completed' || proc.status === 'failed') {
|
|
46
|
+
const timeout = proc.type === 'planning' ? 1200 : 2500
|
|
47
|
+
const timer = setTimeout(() => {
|
|
48
|
+
if (onDismiss) onDismiss(proc.id)
|
|
49
|
+
}, timeout)
|
|
50
|
+
timers.push(timer)
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
return () => {
|
|
54
|
+
timers.forEach((t) => clearTimeout(t))
|
|
55
|
+
}
|
|
56
|
+
}, [processes, onDismiss])
|
|
57
|
+
|
|
58
|
+
if (!renderedProcesses || renderedProcesses.length === 0) return null
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className="w-full flex flex-col gap-2">
|
|
62
|
+
{renderedProcesses.map((proc) => {
|
|
63
|
+
if (proc.type === 'planning') {
|
|
64
|
+
const { steps, currentStep, reasoning } = proc.data || {}
|
|
65
|
+
const isDone = proc.status === 'done'
|
|
66
|
+
const isFailed = proc.status === 'failed'
|
|
67
|
+
const isPaused = proc.status === 'paused'
|
|
68
|
+
const executionTitle =
|
|
69
|
+
executingTaskCount === 1
|
|
70
|
+
? 'Executing 1 Task'
|
|
71
|
+
: `Executing ${Math.max(executingTaskCount, 1)} Tasks`
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div
|
|
75
|
+
key={proc.id}
|
|
76
|
+
className="bg-black/60 border border-primary/20 rounded-xl p-3 shadow-lg flex flex-col gap-1.5 animate-[holo-project-in_0.2s_ease-out_forwards]"
|
|
77
|
+
>
|
|
78
|
+
<div className="flex items-center justify-between border-b border-white/5 pb-1.5 text-xs font-mono font-bold">
|
|
79
|
+
<div className="flex items-center gap-1.5 text-primary">
|
|
80
|
+
{isDone ? (
|
|
81
|
+
<FaCheckCircle className="text-success" />
|
|
82
|
+
) : isFailed ? (
|
|
83
|
+
<FaBolt className="text-error" />
|
|
84
|
+
) : isPaused ? (
|
|
85
|
+
<FaBolt className="text-warning" />
|
|
86
|
+
) : (
|
|
87
|
+
<FaListUl className="text-primary animate-pulse" />
|
|
88
|
+
)}
|
|
89
|
+
<span>{isDone ? 'Task Completed' : isFailed ? 'Task Failed' : executionTitle}</span>
|
|
90
|
+
</div>
|
|
91
|
+
{onDismiss && (
|
|
92
|
+
<button
|
|
93
|
+
onClick={() => onDismiss(proc.id)}
|
|
94
|
+
className="text-white/40 hover:text-white text-xs cursor-pointer px-1"
|
|
95
|
+
>
|
|
96
|
+
✕
|
|
97
|
+
</button>
|
|
98
|
+
)}
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
{reasoning && (
|
|
102
|
+
<details className="group mt-1">
|
|
103
|
+
<summary className="text-[10px] cursor-pointer select-none flex items-center gap-1 opacity-50 hover:opacity-100 transition-opacity uppercase tracking-wider">
|
|
104
|
+
<FaChevronRight className="group-open:rotate-90 transition-transform text-[8px]" />
|
|
105
|
+
Proses Pemikiran
|
|
106
|
+
</summary>
|
|
107
|
+
<div className="text-[10px] opacity-70 border-l border-primary/30 pl-2 mt-1 mb-1 font-mono whitespace-pre-wrap">
|
|
108
|
+
{reasoning}
|
|
109
|
+
</div>
|
|
110
|
+
</details>
|
|
111
|
+
)}
|
|
112
|
+
|
|
113
|
+
{steps && steps.length > 0 && (
|
|
114
|
+
<div className="flex flex-col gap-1 mt-1">
|
|
115
|
+
{steps.map((step, idx) => {
|
|
116
|
+
let prefix = `${idx + 1}.`
|
|
117
|
+
let opacity = 'opacity-40 text-white/70'
|
|
118
|
+
let suffix = ''
|
|
119
|
+
|
|
120
|
+
if (idx < currentStep) {
|
|
121
|
+
prefix = <FaCheck className="inline" size={9} />
|
|
122
|
+
opacity = 'opacity-100 text-success font-bold'
|
|
123
|
+
} else if (idx === currentStep && !isDone) {
|
|
124
|
+
opacity = 'opacity-100 text-primary font-bold animate-pulse'
|
|
125
|
+
suffix = '...'
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const hasQuery = typeof step === 'object' && step.query
|
|
129
|
+
const taskTitle = typeof step === 'object' ? step.task : step
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<div key={idx} className={`flex items-start text-[10px] font-mono ${opacity}`}>
|
|
133
|
+
<span className="w-3.5 shrink-0 inline-block">{prefix}</span>
|
|
134
|
+
<div className="flex-1 min-w-0">
|
|
135
|
+
{hasQuery ? (
|
|
136
|
+
<details className="group/step outline-none">
|
|
137
|
+
<summary className="cursor-pointer select-none flex items-center hover:opacity-100 outline-none list-none [&::-webkit-details-marker]:hidden transition-opacity">
|
|
138
|
+
<FaChevronRight className="group-open/step:rotate-90 transition-transform text-[8px] mr-1 opacity-50 shrink-0" />
|
|
139
|
+
<span className="truncate">{taskTitle} {suffix}</span>
|
|
140
|
+
</summary>
|
|
141
|
+
<div className="mt-1 pl-2 opacity-80 text-[9px] border-l border-primary/30 ml-1 mb-1 break-words font-mono bg-black/40 p-1.5 rounded text-primary/90 max-h-32 overflow-y-auto">
|
|
142
|
+
{typeof step.query === 'object' ? JSON.stringify(step.query, null, 2) : step.query}
|
|
143
|
+
</div>
|
|
144
|
+
</details>
|
|
145
|
+
) : (
|
|
146
|
+
<div className="truncate">
|
|
147
|
+
{taskTitle} {suffix}
|
|
148
|
+
</div>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
)
|
|
153
|
+
})}
|
|
154
|
+
</div>
|
|
155
|
+
)}
|
|
156
|
+
</div>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (proc.type === 'plugin-execution') {
|
|
161
|
+
const hasQuery = !!proc.data?.query
|
|
162
|
+
return (
|
|
163
|
+
<div
|
|
164
|
+
key={proc.id}
|
|
165
|
+
className="bg-black/60 border border-primary/20 rounded-xl p-2.5 shadow-lg text-xs font-mono text-white/80 animate-[holo-project-in_0.2s_ease-out_forwards]"
|
|
166
|
+
>
|
|
167
|
+
<div className="flex items-center justify-between mb-1">
|
|
168
|
+
<div className="text-primary flex items-center gap-1 text-[11px] font-bold">
|
|
169
|
+
<FaBolt size={10} className="animate-pulse" /> {proc.data?.action}
|
|
170
|
+
</div>
|
|
171
|
+
{onDismiss && (
|
|
172
|
+
<button
|
|
173
|
+
onClick={() => onDismiss(proc.id)}
|
|
174
|
+
className="text-white/40 hover:text-white text-xs cursor-pointer px-1"
|
|
175
|
+
>
|
|
176
|
+
✕
|
|
177
|
+
</button>
|
|
178
|
+
)}
|
|
179
|
+
</div>
|
|
180
|
+
{hasQuery ? (
|
|
181
|
+
<details className="group/plugin outline-none">
|
|
182
|
+
<summary className="text-[10px] text-white/70 cursor-pointer select-none flex items-center hover:opacity-100 outline-none list-none [&::-webkit-details-marker]:hidden transition-opacity">
|
|
183
|
+
<FaChevronRight className="group-open/plugin:rotate-90 transition-transform text-[8px] mr-1 opacity-50 shrink-0" />
|
|
184
|
+
<span className="truncate">
|
|
185
|
+
Mengeksekusi: <span className="text-primary font-semibold">{proc.data?.action}</span>
|
|
186
|
+
</span>
|
|
187
|
+
</summary>
|
|
188
|
+
<div className="mt-1 pl-2 opacity-80 text-[9px] border-l border-primary/30 ml-1 mb-1 font-mono bg-black/40 p-1.5 rounded text-primary/90 max-h-28 overflow-y-auto whitespace-pre-wrap break-all">
|
|
189
|
+
{typeof proc.data.query === 'object'
|
|
190
|
+
? JSON.stringify(proc.data.query, null, 2)
|
|
191
|
+
: String(proc.data.query)}
|
|
192
|
+
</div>
|
|
193
|
+
</details>
|
|
194
|
+
) : (
|
|
195
|
+
<div className="text-[10px] text-white/70 truncate">
|
|
196
|
+
Mengeksekusi: <span className="text-primary">{proc.data?.action}</span>
|
|
197
|
+
</div>
|
|
198
|
+
)}
|
|
199
|
+
{proc.data?.result && (
|
|
200
|
+
<div className="mt-1 p-1.5 bg-primary/10 text-primary border border-primary/20 rounded text-[9px] font-mono whitespace-pre-wrap max-h-24 overflow-y-auto">
|
|
201
|
+
{typeof proc.data.result === 'object'
|
|
202
|
+
? JSON.stringify(proc.data.result, null, 2)
|
|
203
|
+
: String(proc.data.result)}
|
|
204
|
+
</div>
|
|
205
|
+
)}
|
|
206
|
+
</div>
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return null
|
|
211
|
+
})}
|
|
212
|
+
</div>
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export default ProcessPanel
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
|
+
import { FaLightbulb } from 'react-icons/fa'
|
|
3
|
+
import Markdown from 'react-markdown'
|
|
4
|
+
import remarkGfm from 'remark-gfm'
|
|
5
|
+
import rehypeExternalLinks from 'rehype-external-links'
|
|
6
|
+
import { CodeBlock } from '../Chat/CodeBlock'
|
|
7
|
+
import PluginExecutionBubble from '../Chat/PluginExecutionBubble'
|
|
8
|
+
|
|
9
|
+
const ResponseArea = ({ currentResponse }) => {
|
|
10
|
+
const [animState, setAnimState] = useState('idle') // 'fade-out', 'fade-in', 'idle'
|
|
11
|
+
const [displayResponse, setDisplayResponse] = useState(currentResponse)
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (currentResponse !== displayResponse) {
|
|
15
|
+
if (displayResponse) {
|
|
16
|
+
setAnimState('fade-out')
|
|
17
|
+
const timer = setTimeout(() => {
|
|
18
|
+
setDisplayResponse(currentResponse)
|
|
19
|
+
setAnimState('fade-in')
|
|
20
|
+
}, 150)
|
|
21
|
+
return () => clearTimeout(timer)
|
|
22
|
+
} else {
|
|
23
|
+
setDisplayResponse(currentResponse)
|
|
24
|
+
setAnimState('fade-in')
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}, [currentResponse, displayResponse])
|
|
28
|
+
|
|
29
|
+
if (!displayResponse) return null
|
|
30
|
+
|
|
31
|
+
const { text, type, pluginResult } = displayResponse
|
|
32
|
+
|
|
33
|
+
const animationClass =
|
|
34
|
+
animState === 'fade-out'
|
|
35
|
+
? 'animate-[response-fade-out_0.15s_ease-out_forwards]'
|
|
36
|
+
: animState === 'fade-in'
|
|
37
|
+
? 'animate-[response-fade-in_0.2s_ease-out_forwards]'
|
|
38
|
+
: ''
|
|
39
|
+
|
|
40
|
+
const markdownComponents = {
|
|
41
|
+
code({ node, inline, className, children, ...props }) {
|
|
42
|
+
const match = /language-(\w+)/.exec(className || '')
|
|
43
|
+
return !inline ? (
|
|
44
|
+
<CodeBlock match={match} children={children} />
|
|
45
|
+
) : (
|
|
46
|
+
<code
|
|
47
|
+
className={`px-1.5 py-0.5 rounded bg-white/10 text-primary font-mono text-xs ${className || ''}`}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
{children}
|
|
51
|
+
</code>
|
|
52
|
+
)
|
|
53
|
+
},
|
|
54
|
+
a: ({ node, ...props }) => {
|
|
55
|
+
let url = props.href || '#'
|
|
56
|
+
if (url !== '#' && !url.startsWith('http://') && !url.startsWith('https://')) {
|
|
57
|
+
url = 'https://' + url
|
|
58
|
+
}
|
|
59
|
+
return (
|
|
60
|
+
<a
|
|
61
|
+
{...props}
|
|
62
|
+
className="text-primary hover:underline"
|
|
63
|
+
onClick={(e) => {
|
|
64
|
+
e.preventDefault()
|
|
65
|
+
if (window.api && window.api.openExternal && url !== '#') {
|
|
66
|
+
window.api.openExternal(url)
|
|
67
|
+
}
|
|
68
|
+
}}
|
|
69
|
+
/>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const renderContent = () => {
|
|
75
|
+
if (displayResponse.isThinking) {
|
|
76
|
+
return (
|
|
77
|
+
<div className="w-full">
|
|
78
|
+
<div className="p-4 rounded-xl bg-white/[0.03] text-left text-xs md:text-sm font-mono leading-relaxed text-white/90">
|
|
79
|
+
<div className="flex flex-col items-center justify-center gap-3 py-3">
|
|
80
|
+
<div className="relative w-8 h-8 flex items-center justify-center text-primary">
|
|
81
|
+
<svg
|
|
82
|
+
viewBox="0 0 50 50"
|
|
83
|
+
className="w-full h-full animate-[spin_3s_linear_infinite]"
|
|
84
|
+
>
|
|
85
|
+
<circle
|
|
86
|
+
cx="25"
|
|
87
|
+
cy="25"
|
|
88
|
+
r="20"
|
|
89
|
+
fill="none"
|
|
90
|
+
stroke="currentColor"
|
|
91
|
+
strokeWidth="2"
|
|
92
|
+
strokeDasharray="30 15"
|
|
93
|
+
className="opacity-40"
|
|
94
|
+
/>
|
|
95
|
+
<circle
|
|
96
|
+
cx="25"
|
|
97
|
+
cy="25"
|
|
98
|
+
r="14"
|
|
99
|
+
fill="none"
|
|
100
|
+
stroke="currentColor"
|
|
101
|
+
strokeWidth="2"
|
|
102
|
+
strokeDasharray="20 10"
|
|
103
|
+
className="opacity-80 animate-[spin_2s_linear_infinite_reverse]"
|
|
104
|
+
style={{ transformOrigin: 'center' }}
|
|
105
|
+
/>
|
|
106
|
+
</svg>
|
|
107
|
+
<FaLightbulb className="absolute animate-pulse text-primary" size={10} />
|
|
108
|
+
</div>
|
|
109
|
+
<div className="text-[10px] uppercase tracking-widest text-primary/80 font-mono">
|
|
110
|
+
{text !== 'Bentar, mikir dlu...' && text !== 'Memproses...'
|
|
111
|
+
? text
|
|
112
|
+
: 'PROCESSING DATA...'}
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<div className="w-full">
|
|
122
|
+
<div className="p-3.5 rounded-xl bg-white/[0.03] text-left text-xs md:text-sm font-mono leading-relaxed text-white/90">
|
|
123
|
+
<Markdown
|
|
124
|
+
remarkPlugins={[remarkGfm]}
|
|
125
|
+
rehypePlugins={[[rehypeExternalLinks, { target: '_blank' }]]}
|
|
126
|
+
components={markdownComponents}
|
|
127
|
+
>
|
|
128
|
+
{text}
|
|
129
|
+
</Markdown>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<div className={`w-full flex flex-col gap-3 ${animationClass}`}>
|
|
137
|
+
{renderContent()}
|
|
138
|
+
|
|
139
|
+
{/* Plugin Execution Result Chip */}
|
|
140
|
+
{pluginResult && (
|
|
141
|
+
<div className="w-full">
|
|
142
|
+
<PluginExecutionBubble pluginExecution={pluginResult} />
|
|
143
|
+
</div>
|
|
144
|
+
)}
|
|
145
|
+
</div>
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export default React.memo(ResponseArea)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { FaCheckCircle, FaInfoCircle, FaExclamationTriangle, FaTimesCircle } from 'react-icons/fa';
|
|
3
|
+
|
|
4
|
+
const StatusIndicator = ({ notifications }) => {
|
|
5
|
+
const [activeToasts, setActiveToasts] = useState([]);
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (notifications && notifications.length > 0) {
|
|
9
|
+
const newNotifs = notifications.filter(n => !activeToasts.find(t => t.id === n.id));
|
|
10
|
+
if (newNotifs.length > 0) {
|
|
11
|
+
// Prepare toasts with types
|
|
12
|
+
const enhancedNotifs = newNotifs.map(notif => {
|
|
13
|
+
let alertType = 'alert-info';
|
|
14
|
+
let Icon = FaInfoCircle;
|
|
15
|
+
const typeStr = String(notif?.type || '').toLowerCase();
|
|
16
|
+
|
|
17
|
+
if (typeStr.includes('memory') || typeStr === 'plugin-done' || typeStr === 'success') {
|
|
18
|
+
alertType = 'alert-success';
|
|
19
|
+
Icon = FaCheckCircle;
|
|
20
|
+
} else if (typeStr === 'plugin-executing') {
|
|
21
|
+
alertType = 'alert-warning';
|
|
22
|
+
Icon = FaExclamationTriangle;
|
|
23
|
+
} else if (typeStr === 'error') {
|
|
24
|
+
alertType = 'alert-error';
|
|
25
|
+
Icon = FaTimesCircle;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return { ...notif, alertType, Icon };
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
setActiveToasts(prev => [...prev, ...enhancedNotifs]);
|
|
32
|
+
|
|
33
|
+
enhancedNotifs.forEach(notif => {
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
setActiveToasts(prev => prev.filter(t => t.id !== notif.id));
|
|
36
|
+
}, 3000);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}, [notifications]);
|
|
41
|
+
|
|
42
|
+
if (activeToasts.length === 0) return null;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div className="toast toast-top toast-end z-[9999] p-4">
|
|
46
|
+
{activeToasts.map(toast => (
|
|
47
|
+
<div
|
|
48
|
+
key={toast.id}
|
|
49
|
+
className={`alert ${toast.alertType} shadow-lg rounded-xl flex items-center gap-3 animate-fade-in`}
|
|
50
|
+
>
|
|
51
|
+
<toast.Icon className="w-5 h-5" />
|
|
52
|
+
<span className="text-sm font-medium pr-2">{toast.message}</span>
|
|
53
|
+
</div>
|
|
54
|
+
))}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default StatusIndicator;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { FaCheck, FaSpinner, FaBrain } from 'react-icons/fa';
|
|
3
|
+
|
|
4
|
+
const ThoughtNeuralFlow = ({ processes }) => {
|
|
5
|
+
const [displayedPlan, setDisplayedPlan] = useState(null);
|
|
6
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
let activePlan = processes.find(p => p.type === 'planning');
|
|
10
|
+
|
|
11
|
+
if (!activePlan && processes.length > 0) {
|
|
12
|
+
const runningProc = processes.find(p => p.status !== 'done');
|
|
13
|
+
if (runningProc) {
|
|
14
|
+
let taskName = runningProc.type === 'web-search' ? 'Mencari Data...' :
|
|
15
|
+
runningProc.type === 'plugin-execution' ? 'Eksekusi Plugin...' :
|
|
16
|
+
'Memproses...';
|
|
17
|
+
activePlan = {
|
|
18
|
+
status: runningProc.status,
|
|
19
|
+
data: {
|
|
20
|
+
plan: ['Analisis', taskName, 'Sintesis'],
|
|
21
|
+
currentStep: 1,
|
|
22
|
+
reasoning: 'Fast Track Execution'
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (activePlan) {
|
|
29
|
+
setDisplayedPlan(activePlan);
|
|
30
|
+
setIsVisible(true);
|
|
31
|
+
} else {
|
|
32
|
+
setIsVisible(false);
|
|
33
|
+
}
|
|
34
|
+
}, [processes]);
|
|
35
|
+
|
|
36
|
+
const plan = displayedPlan?.data?.plan || [];
|
|
37
|
+
const currentStep = displayedPlan?.data?.currentStep || 0;
|
|
38
|
+
const reasoning = displayedPlan?.data?.reasoning || '';
|
|
39
|
+
const isDone = displayedPlan?.status === 'done';
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div
|
|
43
|
+
className={`absolute inset-0 z-0 pointer-events-none flex items-center justify-center transition-all duration-500 ease-out
|
|
44
|
+
${isVisible ? 'opacity-100 scale-100' : 'opacity-0 scale-90'}
|
|
45
|
+
`}
|
|
46
|
+
>
|
|
47
|
+
{/* Reasoning text removed as per user request */}
|
|
48
|
+
|
|
49
|
+
{/* Nodes around the Orb */}
|
|
50
|
+
{plan.map((step, idx) => {
|
|
51
|
+
const isCompleted = idx < currentStep;
|
|
52
|
+
const isActive = idx === currentStep && !isDone;
|
|
53
|
+
const isPending = idx > currentStep;
|
|
54
|
+
const stepText = typeof step === 'object' ? step.task : step;
|
|
55
|
+
|
|
56
|
+
const totalNodes = plan.length;
|
|
57
|
+
const span = totalNodes > 3 ? 180 : 140; // Expand span if there are many nodes
|
|
58
|
+
const startAngle = -90 - (span / 2);
|
|
59
|
+
const stepAngle = totalNodes > 1 ? span / (totalNodes - 1) : 0;
|
|
60
|
+
|
|
61
|
+
const angleDeg = startAngle + (idx * stepAngle);
|
|
62
|
+
const angleRad = (angleDeg * Math.PI) / 180;
|
|
63
|
+
|
|
64
|
+
// Push nodes further away if there are many to avoid overlapping labels
|
|
65
|
+
// Use a zigzag pattern for radius if there are many nodes (alternating distances)
|
|
66
|
+
const baseRadius = totalNodes > 3 ? 170 : 150;
|
|
67
|
+
const radius = totalNodes > 3 ? baseRadius + (idx % 2 === 0 ? 0 : 40) : baseRadius;
|
|
68
|
+
|
|
69
|
+
const x = Math.cos(angleRad) * radius;
|
|
70
|
+
const y = Math.sin(angleRad) * radius;
|
|
71
|
+
|
|
72
|
+
// Holographic glass styles
|
|
73
|
+
let glassClass = 'from-base-content/10 to-base-content/5 border-base-content/20 text-base-content/40';
|
|
74
|
+
if (isCompleted) glassClass = 'from-primary/40 to-primary/10 border-primary/50 text-primary shadow-[inset_0_0_10px_oklch(var(--p)/0.2)]';
|
|
75
|
+
if (isActive) glassClass = 'from-secondary/50 to-secondary/20 border-secondary/60 text-secondary shadow-[inset_0_0_15px_oklch(var(--s)/0.4)] animate-pulse scale-110';
|
|
76
|
+
|
|
77
|
+
// Calculate line coordinates so they start from the surface of the Node and end at the surface of the Orb
|
|
78
|
+
// Node is at (x,y) relative to Orb(0,0).
|
|
79
|
+
// The SVG center (150,150) represents the Node.
|
|
80
|
+
const distance = radius;
|
|
81
|
+
const nodeClearance = 16; // Don't draw inside the 32px neuron cube
|
|
82
|
+
const orbClearance = 60; // Don't draw inside the 96px main orb
|
|
83
|
+
|
|
84
|
+
// Direction from Node to Orb is (-x, -y)
|
|
85
|
+
const dirX = -x / distance;
|
|
86
|
+
const dirY = -y / distance;
|
|
87
|
+
|
|
88
|
+
const startX = 150 + (dirX * nodeClearance);
|
|
89
|
+
const startY = 150 + (dirY * nodeClearance);
|
|
90
|
+
|
|
91
|
+
const endX = 150 + (dirX * (distance - orbClearance));
|
|
92
|
+
const endY = 150 + (dirY * (distance - orbClearance));
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div
|
|
96
|
+
key={idx}
|
|
97
|
+
className="absolute flex flex-col items-center"
|
|
98
|
+
style={{
|
|
99
|
+
transform: `translate(${x}px, ${y}px)`,
|
|
100
|
+
transition: 'all 0.5s cubic-bezier(0.4, 0, 0.2, 1)',
|
|
101
|
+
transitionDelay: `${idx * 100}ms`
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
{/* Draw SVG line connecting to center (Orb) */}
|
|
105
|
+
<svg className="absolute w-[300px] h-[300px] pointer-events-none overflow-visible" style={{ top: -150, left: -150 }}>
|
|
106
|
+
<line
|
|
107
|
+
x1={startX} y1={startY}
|
|
108
|
+
x2={endX} y2={endY}
|
|
109
|
+
stroke="currentColor"
|
|
110
|
+
className={isCompleted ? 'text-primary opacity-50' : isActive ? 'text-secondary opacity-80' : 'text-base-content opacity-20'}
|
|
111
|
+
strokeWidth="2"
|
|
112
|
+
strokeDasharray="4 4"
|
|
113
|
+
/>
|
|
114
|
+
</svg>
|
|
115
|
+
|
|
116
|
+
{/* The Node (3D Neuron) */}
|
|
117
|
+
<div className="relative w-8 h-8 z-10 perspective-1000">
|
|
118
|
+
<div
|
|
119
|
+
className="w-full h-full relative"
|
|
120
|
+
style={{
|
|
121
|
+
transformStyle: 'preserve-3d',
|
|
122
|
+
animation: isActive ? 'neuron-spin 4s linear infinite' : 'neuron-spin 10s linear infinite'
|
|
123
|
+
}}
|
|
124
|
+
>
|
|
125
|
+
{['front', 'back', 'right', 'left', 'top', 'bottom'].map((face) => {
|
|
126
|
+
let transform = '';
|
|
127
|
+
// 8 = 2rem = 32px -> half is 16px
|
|
128
|
+
if (face === 'front') transform = 'translateZ(16px)';
|
|
129
|
+
if (face === 'back') transform = 'rotateY(180deg) translateZ(16px)';
|
|
130
|
+
if (face === 'right') transform = 'rotateY(90deg) translateZ(16px)';
|
|
131
|
+
if (face === 'left') transform = 'rotateY(-90deg) translateZ(16px)';
|
|
132
|
+
if (face === 'top') transform = 'rotateX(90deg) translateZ(16px)';
|
|
133
|
+
if (face === 'bottom') transform = 'rotateX(-90deg) translateZ(16px)';
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<div
|
|
137
|
+
key={face}
|
|
138
|
+
className={`absolute inset-0 m-auto w-full h-full rounded-[4px] border backdrop-blur-sm bg-gradient-to-br flex items-center justify-center transition-all duration-700 ease-in-out ${glassClass}`}
|
|
139
|
+
style={{ transform }}
|
|
140
|
+
>
|
|
141
|
+
{face === 'front' && (
|
|
142
|
+
<div className="relative w-4 h-4 flex items-center justify-center">
|
|
143
|
+
<div className={`absolute inset-0 flex items-center justify-center transition-all duration-500 ${isCompleted ? 'opacity-100 scale-100 rotate-0' : 'opacity-0 scale-50 -rotate-90'}`}>
|
|
144
|
+
<FaCheck size={10} />
|
|
145
|
+
</div>
|
|
146
|
+
<div className={`absolute inset-0 flex items-center justify-center transition-all duration-500 ${isActive ? 'opacity-100 scale-100' : 'opacity-0 scale-50'}`}>
|
|
147
|
+
<FaSpinner className="animate-spin" size={10} />
|
|
148
|
+
</div>
|
|
149
|
+
<div className={`absolute inset-0 flex items-center justify-center transition-all duration-500 ${isPending ? 'opacity-100 scale-100' : 'opacity-0 scale-50'}`}>
|
|
150
|
+
<span className="text-[10px]">{idx + 1}</span>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
})}
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
{/* Text labels removed as per user request */}
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
})}
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export default ThoughtNeuralFlow;
|