@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,630 @@
1
+ import Editor from '@monaco-editor/react'
2
+ import { driver } from 'driver.js'
3
+ import { FaTimes, FaExclamationTriangle } from 'react-icons/fa'
4
+ import { useNavigate } from 'react-router-dom'
5
+ import 'driver.js/dist/driver.css'
6
+ import { useEffect, useState } from 'react'
7
+ import { useConfirm } from '../hooks/useConfirm'
8
+ import { webApi } from '../api/web-bridge'
9
+
10
+ export default function Plugins() {
11
+ const navigate = useNavigate()
12
+ const [plugins, setPlugins] = useState([])
13
+ const [isFormOpen, setIsFormOpen] = useState(false)
14
+ const [formData, setFormData] = useState({
15
+ name: '',
16
+ description: '',
17
+ actions: [{ name: '', description: '', triggerHint: '', code: '' }],
18
+ isEdit: false
19
+ })
20
+ const [formStatus, setFormStatus] = useState(null)
21
+ const [syntaxErrors, setSyntaxErrors] = useState([])
22
+ const { confirm, ModalComponent } = useConfirm()
23
+
24
+ useEffect(() => {
25
+ const errors = []
26
+ formData.actions.forEach((act, index) => {
27
+ if (act.code) {
28
+ try {
29
+ const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
30
+ new AsyncFunction('query', act.code)
31
+ errors[index] = null
32
+ } catch (err) {
33
+ errors[index] = err.message
34
+ }
35
+ } else {
36
+ errors[index] = null
37
+ }
38
+ })
39
+ setSyntaxErrors(errors)
40
+ }, [formData.actions])
41
+
42
+ const updateAction = (index, field, value) => {
43
+ const newActions = [...formData.actions]
44
+ newActions[index][field] = value
45
+ setFormData({ ...formData, actions: newActions })
46
+ }
47
+
48
+ const addAction = () => {
49
+ setFormData({
50
+ ...formData,
51
+ actions: [...formData.actions, { name: '', description: '', triggerHint: '', code: '' }]
52
+ })
53
+ }
54
+
55
+ const removeAction = async (index) => {
56
+ if (formData.actions.length <= 1) return
57
+ const result = await confirm({
58
+ title: 'Yakin mau hapus action ini?',
59
+ message: 'Code yang ada di action ini bakal hilang!',
60
+ isError: true,
61
+ confirmText: 'Ya, Hapus!',
62
+ cancelText: 'Batal'
63
+ })
64
+
65
+ if (result.isConfirmed) {
66
+ const newActions = formData.actions.filter((_, i) => i !== index)
67
+ setFormData({ ...formData, actions: newActions })
68
+ }
69
+ }
70
+
71
+ const loadData = async () => {
72
+ const data = await webApi.getPlugins()
73
+ setPlugins(data || [])
74
+ }
75
+
76
+ useEffect(() => {
77
+ loadData()
78
+ const unsub = webApi.onPluginsUpdated(() => {
79
+ loadData()
80
+ })
81
+ return () => {
82
+ if (typeof unsub === 'function') unsub()
83
+ }
84
+ }, [])
85
+
86
+ const handleOpenFolder = async () => {
87
+ await webApi.openPluginFolder()
88
+ }
89
+
90
+ const handleReload = async () => {
91
+ await webApi.reloadPlugins()
92
+ loadData()
93
+ }
94
+
95
+ const startTour = () => {
96
+ const driverObj = driver({
97
+ showProgress: true,
98
+ animate: true,
99
+ nextBtnText: 'Lanjut',
100
+ prevBtnText: 'Kembali',
101
+ doneBtnText: 'Siap Ngoding!',
102
+ steps: [
103
+ {
104
+ element: '#btn-tambah-plugin',
105
+ popover: {
106
+ title: '1. Buat Plugin',
107
+ description:
108
+ 'Klik tombol ini untuk membuat fungsi/plugin baru. Fitur baru yang kamu tulis di sini bakal otomatis dipahami oleh Mark.',
109
+ side: 'bottom',
110
+ align: 'start'
111
+ }
112
+ },
113
+ {
114
+ element: '#btn-buka-folder',
115
+ popover: {
116
+ title: '2. Folder Fisik',
117
+ description:
118
+ 'Plugin yang kamu buat disimpan dalam file .js sungguhan. Kamu bisa klik ini untuk ngedit file-filenya pakai VS Code secara manual.',
119
+ side: 'bottom'
120
+ }
121
+ },
122
+ {
123
+ element: '#btn-reload-plugin',
124
+ popover: {
125
+ title: '3. Reload Plugin',
126
+ description:
127
+ 'Kalau kamu abis ngedit file plugin secara manual lewat VS Code, tekan tombol ini supaya Mark memuat ulang versi terbarunya.',
128
+ side: 'bottom'
129
+ }
130
+ }
131
+ ]
132
+ })
133
+ driverObj.drive()
134
+ }
135
+
136
+ const openCreateForm = () => {
137
+ setFormData({
138
+ name: '',
139
+ description: '',
140
+ dependencies: '',
141
+ actions: [{ name: '', description: '', triggerHint: '', code: '' }],
142
+ isEdit: false
143
+ })
144
+ setFormStatus(null)
145
+ setIsFormOpen(true)
146
+ }
147
+
148
+ const openEditForm = (plugin) => {
149
+ setFormData({
150
+ name: plugin.name,
151
+ description: plugin.description,
152
+ dependencies: plugin.dependencies ? plugin.dependencies.join(', ') : '',
153
+ actions: plugin.actions.map((act) => ({
154
+ name: act.name,
155
+ description: act.description,
156
+ triggerHint: act.triggerHint,
157
+ code: act.code || '// Kode tidak ditemukan\nreturn "Hello";'
158
+ })),
159
+ isEdit: true
160
+ })
161
+ setFormStatus(null)
162
+ setIsFormOpen(true)
163
+ }
164
+
165
+ return (
166
+ <div className="h-screen bg-base-300 text-white overflow-hidden relative font-['Poppins',sans-serif]">
167
+ {/* Background Ambience */}
168
+ <ModalComponent />
169
+ <div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,oklch(var(--n))_0%,transparent_70%)] opacity-20 pointer-events-none" />
170
+ <div className="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/stardust.png')] opacity-10 pointer-events-none" />
171
+
172
+ {/* Main Content Area */}
173
+ <div className="relative z-10 w-full h-full overflow-y-auto custom-scrollbar">
174
+ <div className="max-w-5xl mx-auto px-4 py-8 pb-40 space-y-8">
175
+ <div className="flex items-center justify-between">
176
+ <div className="flex items-center gap-4">
177
+ <button
178
+ type="button"
179
+ onClick={() => navigate('/')}
180
+ className="btn btn-ghost btn-sm btn-circle shrink-0"
181
+ style={{ WebkitAppRegion: 'no-drag' }}
182
+ title="Kembali"
183
+ >
184
+ <svg
185
+ xmlns="http://www.w3.org/2000/svg"
186
+ width="1.2em"
187
+ height="1.2em"
188
+ fill="none"
189
+ viewBox="0 0 24 24"
190
+ stroke="currentColor"
191
+ strokeWidth={2}
192
+ >
193
+ <path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
194
+ </svg>
195
+ </button>
196
+ <div>
197
+ <h1 className="text-2xl font-bold">Plugin Manager</h1>
198
+ <p className="opacity-50 text-sm mt-1">Buat dan kelola custom skill lokal buat Mark.</p>
199
+ </div>
200
+ </div>
201
+ <div className="flex items-center gap-2">
202
+ <button
203
+ type="button"
204
+ onClick={startTour}
205
+ className="btn btn-ghost btn-sm btn-circle"
206
+ style={{ WebkitAppRegion: 'no-drag' }}
207
+ title="Tutorial Plugin"
208
+ >
209
+ <svg
210
+ xmlns="http://www.w3.org/2000/svg"
211
+ className="h-5 w-5"
212
+ fill="none"
213
+ viewBox="0 0 24 24"
214
+ stroke="currentColor"
215
+ >
216
+ <path
217
+ strokeLinecap="round"
218
+ strokeLinejoin="round"
219
+ strokeWidth={2}
220
+ d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
221
+ />
222
+ </svg>
223
+ </button>
224
+ <button
225
+ id="btn-tambah-plugin"
226
+ type="button"
227
+ onClick={openCreateForm}
228
+ className="btn btn-primary btn-sm"
229
+ style={{ WebkitAppRegion: 'no-drag' }}
230
+ >
231
+ + Tambah Plugin
232
+ </button>
233
+ <button
234
+ id="btn-buka-folder"
235
+ type="button"
236
+ onClick={handleOpenFolder}
237
+ className="btn btn-sm btn-outline border-base-content/20 hover:border-primary hover:bg-primary/10 hover:text-primary"
238
+ style={{ WebkitAppRegion: 'no-drag' }}
239
+ >
240
+ Buka Folder
241
+ </button>
242
+ <button
243
+ id="btn-reload-plugin"
244
+ type="button"
245
+ onClick={handleReload}
246
+ className="btn btn-sm btn-ghost btn-outline"
247
+ style={{ WebkitAppRegion: 'no-drag' }}
248
+ >
249
+ Reload
250
+ </button>
251
+ </div>
252
+ </div>
253
+
254
+ {isFormOpen && (
255
+ <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
256
+ <div className="bg-base-100 w-full max-w-5xl max-h-[90vh] rounded-2xl shadow-2xl flex flex-col overflow-hidden border border-base-content/10">
257
+ <div className="p-6 border-b border-base-content/10 flex justify-between items-center bg-base-200/50">
258
+ <h2 className="text-2xl font-bold">
259
+ {formData.isEdit ? 'Edit Plugin' : 'Buat Plugin Baru'}
260
+ </h2>
261
+ <button
262
+ className="btn btn-sm btn-circle btn-ghost"
263
+ onClick={() => setIsFormOpen(false)}
264
+ >
265
+ <FaTimes />
266
+ </button>
267
+ </div>
268
+
269
+ <div className="p-6 overflow-y-auto flex-1">
270
+ {formStatus && (
271
+ <div
272
+ className={`alert mb-6 py-3 shadow-sm ${formStatus.success ? 'alert-success' : 'alert-error'}`}
273
+ >
274
+ {formStatus.message}
275
+ </div>
276
+ )}
277
+
278
+ <div className="grid grid-cols-1 gap-6">
279
+ <div className="flex gap-4 p-4 bg-base-200/30 rounded-xl border border-base-content/10">
280
+ <div className="form-control flex-1">
281
+ <label className="label font-semibold">
282
+ <span className="label-text">Nama Plugin</span>
283
+ </label>
284
+ <input
285
+ type="text"
286
+ className="input input-bordered w-full bg-base-200/50 focus:bg-base-100 transition-colors"
287
+ placeholder="Cth: Kalkulator"
288
+ value={formData.name}
289
+ onChange={(e) => setFormData({ ...formData, name: e.target.value })}
290
+ disabled={formData.isEdit}
291
+ />
292
+ {formData.isEdit && (
293
+ <span className="text-xs opacity-50 mt-1">
294
+ Nama plugin tidak bisa diubah saat edit
295
+ </span>
296
+ )}
297
+ </div>
298
+ <div className="form-control flex-1">
299
+ <label className="label font-semibold">
300
+ <span className="label-text">Deskripsi Plugin</span>
301
+ </label>
302
+ <input
303
+ type="text"
304
+ className="input input-bordered w-full bg-base-200/50 focus:bg-base-100 transition-colors"
305
+ placeholder="Cth: Kumpulan alat matematika"
306
+ value={formData.description}
307
+ onChange={(e) =>
308
+ setFormData({ ...formData, description: e.target.value })
309
+ }
310
+ />
311
+ </div>
312
+ </div>
313
+ <div className="form-control w-full p-4 bg-base-200/30 rounded-xl border border-base-content/10">
314
+ <label className="label font-semibold">
315
+ <span className="label-text">Dependencies (Library NPM)</span>
316
+ <span className="label-text-alt opacity-70">Opsional</span>
317
+ </label>
318
+ <input
319
+ type="text"
320
+ className="input input-bordered w-full bg-base-200/50 focus:bg-base-100 transition-colors"
321
+ placeholder="Cth: axios, cheerio, moment"
322
+ value={formData.dependencies}
323
+ onChange={(e) => setFormData({ ...formData, dependencies: e.target.value })}
324
+ />
325
+ <span className="text-xs opacity-50 mt-2">
326
+ Pisahkan dengan koma. Library akan otomatis di-install (npm install) saat
327
+ disimpan.
328
+ </span>
329
+ </div>
330
+
331
+ <div className="flex items-center justify-between mt-4">
332
+ <h3 className="text-lg font-bold">Daftar Actions</h3>
333
+ <button className="btn btn-sm btn-outline btn-accent" onClick={addAction}>
334
+ + Tambah Action
335
+ </button>
336
+ </div>
337
+
338
+ {formData.actions.map((act, idx) => (
339
+ <div
340
+ key={idx}
341
+ className="relative p-6 bg-base-200/30 rounded-xl border border-base-content/10 grid grid-cols-1 md:grid-cols-2 gap-6 group"
342
+ >
343
+ {formData.actions.length > 1 && (
344
+ <button
345
+ className="btn btn-xs btn-error btn-outline transition-all absolute top-5 right-5 transition-opacity z-20"
346
+ onClick={() => removeAction(idx)}
347
+ title="Hapus Action"
348
+ >
349
+ DELETE
350
+ </button>
351
+ )}
352
+
353
+ <div className="space-y-4">
354
+ <div className="form-control w-full">
355
+ <label className="label font-semibold">
356
+ <span className="label-text">
357
+ Deskripsi Action{' '}
358
+ <span className="opacity-50 text-xs">#{idx + 1}</span>
359
+ </span>
360
+ </label>
361
+ <input
362
+ type="text"
363
+ className="input input-bordered w-full bg-base-200/50"
364
+ placeholder="Cth: Menjumlahkan dua angka"
365
+ value={act.description}
366
+ onChange={(e) => updateAction(idx, 'description', e.target.value)}
367
+ />
368
+ </div>
369
+ <div className="form-control w-full">
370
+ <label className="label font-semibold">
371
+ <span className="label-text">
372
+ Trigger Hint{' '}
373
+ <span className="opacity-50 font-normal">
374
+ (Kapan AI Pakai Ini?)
375
+ </span>
376
+ </span>
377
+ </label>
378
+ <input
379
+ type="text"
380
+ className="input input-bordered w-full bg-base-200/50"
381
+ placeholder="Cth: user ingin menambah angka"
382
+ value={act.triggerHint}
383
+ onChange={(e) => updateAction(idx, 'triggerHint', e.target.value)}
384
+ />
385
+ </div>
386
+ </div>
387
+
388
+ <div className="form-control w-full h-full flex flex-col">
389
+ <label className="label font-semibold flex justify-between items-end pb-1">
390
+ <span className="label-text flex items-center gap-2">
391
+ <svg
392
+ xmlns="http://www.w3.org/2000/svg"
393
+ width="16"
394
+ height="16"
395
+ viewBox="0 0 24 24"
396
+ fill="none"
397
+ stroke="currentColor"
398
+ strokeWidth="2"
399
+ strokeLinecap="round"
400
+ strokeLinejoin="round"
401
+ >
402
+ <polyline points="16 18 22 12 16 6"></polyline>
403
+ <polyline points="8 6 2 12 8 18"></polyline>
404
+ </svg>
405
+ Kode Node.js
406
+ </span>
407
+ </label>
408
+ <div className="relative mt-2 flex-1 rounded-xl overflow-hidden border border-neutral/50 bg-[#1e1e1e] shadow-inner focus-within:ring-2 focus-within:ring-primary/50 transition-all">
409
+ <div className="absolute top-0 left-0 w-full h-8 bg-[#2d2d2d] border-b border-white/5 flex items-center px-4 gap-1.5 z-10 shadow-sm">
410
+ <div className="w-2.5 h-2.5 rounded-full bg-[#ff5f56]"></div>
411
+ <div className="w-2.5 h-2.5 rounded-full bg-[#ffbd2e]"></div>
412
+ <div className="w-2.5 h-2.5 rounded-full bg-[#27c93f]"></div>
413
+ <span className="ml-2 text-xs font-mono text-white/40">
414
+ {act.name ? act.name : 'action'}.js
415
+ </span>
416
+ {syntaxErrors[idx] && (
417
+ <span
418
+ className="ml-auto text-[10px] text-error font-bold max-w-[150px] truncate"
419
+ title={syntaxErrors[idx]}
420
+ >
421
+ <FaExclamationTriangle className="inline mr-1" /> Error
422
+ </span>
423
+ )}
424
+ </div>
425
+
426
+ <div className="w-full h-full min-h-[200px] pt-10 pb-4 bg-[#1e1e1e] flex flex-col">
427
+ <div
428
+ className="px-4 pb-1 text-sm font-mono flex items-center flex-wrap"
429
+ style={{
430
+ fontFamily:
431
+ 'Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace'
432
+ }}
433
+ >
434
+ <span className="text-[#c678dd]">async function</span>
435
+ <input
436
+ type="text"
437
+ className="bg-transparent outline-none border-b border-dashed border-[#56b6c2]/50 focus:border-[#56b6c2] text-[#61afef] px-1 mx-2 w-32 focus:w-48 transition-all placeholder-[#61afef]/30"
438
+ placeholder="nama-action"
439
+ spellCheck="false"
440
+ value={act.name}
441
+ onChange={(e) => updateAction(idx, 'name', e.target.value)}
442
+ />
443
+ <span className="text-[#abb2bf]">(</span>
444
+ <span className="text-[#e06c75]">{`{ query }`}</span>
445
+ <span className="text-[#abb2bf]">) {`{`}</span>
446
+ </div>
447
+
448
+ <div className="flex-1 overflow-auto relative">
449
+ <Editor
450
+ height="150px"
451
+ defaultLanguage="javascript"
452
+ theme="vs-dark"
453
+ value={act.code}
454
+ onChange={(value) => updateAction(idx, 'code', value || '')}
455
+ options={{
456
+ minimap: { enabled: false },
457
+ scrollBeyondLastLine: false,
458
+ fontSize: 14,
459
+ fontFamily: 'Consolas, Monaco, monospace',
460
+ padding: { top: 16, bottom: 16 },
461
+ overviewRulerLanes: 0,
462
+ hideCursorInOverviewRuler: true,
463
+ scrollbar: { vertical: 'hidden', horizontal: 'hidden' },
464
+ lineNumbers: 'off',
465
+ glyphMargin: false,
466
+ folding: false,
467
+ lineDecorationsWidth: 0,
468
+ lineNumbersMinChars: 0
469
+ }}
470
+ className="border-l border-white/10 ml-4"
471
+ />
472
+ </div>
473
+
474
+ <div
475
+ className="px-4 pt-1 text-sm font-mono text-[#abb2bf]"
476
+ style={{
477
+ fontFamily:
478
+ 'Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace'
479
+ }}
480
+ >
481
+ {`}`}
482
+ </div>
483
+ </div>
484
+ </div>
485
+ </div>
486
+ </div>
487
+ ))}
488
+ </div>
489
+
490
+ <div className="modal-action mt-8 pt-4 border-t border-base-content/10">
491
+ <button
492
+ className="btn btn-ghost"
493
+ onClick={() => setIsFormOpen(false)}
494
+ disabled={formStatus?.loading}
495
+ >
496
+ Batal
497
+ </button>
498
+ <button
499
+ className="btn btn-primary px-8"
500
+ disabled={formStatus?.loading}
501
+ onClick={async () => {
502
+ setFormStatus({
503
+ loading: true,
504
+ message: 'Menyimpan dan menginstall library (jika ada)...'
505
+ })
506
+ if (!formData.name)
507
+ return setFormStatus({
508
+ success: false,
509
+ message: 'Nama Plugin wajib diisi'
510
+ })
511
+ for (let i = 0; i < formData.actions.length; i++) {
512
+ if (!formData.actions[i].name || !formData.actions[i].code) {
513
+ return setFormStatus({
514
+ success: false,
515
+ message: `Nama & Kode Action #${i + 1} wajib diisi`
516
+ })
517
+ }
518
+ if (syntaxErrors[i]) {
519
+ return setFormStatus({
520
+ success: false,
521
+ message: `Ada error syntax di Action #${i + 1} bro!`
522
+ })
523
+ }
524
+ }
525
+
526
+ const res = await webApi.createPlugin(formData)
527
+ if (res.success) {
528
+ setFormStatus({
529
+ success: true,
530
+ message: 'Plugin berhasil dibuat! Me-reload...'
531
+ })
532
+ setFormData({
533
+ name: '',
534
+ description: '',
535
+ actions: [{ name: '', description: '', triggerHint: '', code: '' }]
536
+ })
537
+ loadData()
538
+ setTimeout(() => setIsFormOpen(false), 1500)
539
+ } else {
540
+ setFormStatus({ success: false, message: res.error })
541
+ }
542
+ }}
543
+ >
544
+ {formStatus?.loading ? (
545
+ <span className="loading loading-spinner"></span>
546
+ ) : (
547
+ 'Simpan Plugin'
548
+ )}
549
+ </button>
550
+ </div>
551
+ </div>
552
+ </div>
553
+ </div>
554
+ )}
555
+
556
+ {plugins.length === 0 && !isFormOpen ? (
557
+ <div className="alert alert-info shadow-lg border border-info/20 bg-info/10 text-info">
558
+ Belum ada plugin yang terinstall. Buka folder atau klik Tambah Plugin.
559
+ </div>
560
+ ) : (
561
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
562
+ {plugins.map((pl, i) => (
563
+ <div
564
+ key={i}
565
+ className="card bg-base-200/50 shadow-xl border border-base-content/10 backdrop-blur-md flex flex-col"
566
+ >
567
+ <div className="card-body flex flex-col h-full">
568
+ <div className="flex justify-between items-start">
569
+ <h2
570
+ className={`card-title ${pl.isEnabled !== false ? 'text-primary' : 'text-base-content/50 line-through'}`}
571
+ >
572
+ {pl.name}{' '}
573
+ <span className="text-xs opacity-50 bg-base-300 px-2 py-1 rounded-full no-underline">
574
+ v{pl.version || '1.0.0'}
575
+ </span>
576
+ </h2>
577
+ <input
578
+ type="checkbox"
579
+ className="toggle toggle-primary toggle-sm mt-1"
580
+ checked={pl.isEnabled !== false}
581
+ onChange={async (e) => {
582
+ await webApi.togglePlugin(pl.name, e.target.checked)
583
+ loadData()
584
+ }}
585
+ />
586
+ </div>
587
+ <p
588
+ className={`text-sm flex-1 ${pl.isEnabled !== false ? 'opacity-80' : 'opacity-40'}`}
589
+ >
590
+ {pl.description}
591
+ </p>
592
+ <div className="mt-4">
593
+ <span className="text-xs font-bold uppercase opacity-50">
594
+ Available Actions:
595
+ </span>
596
+ <ul className="list-disc ml-4 text-xs mt-1 space-y-1">
597
+ {pl.actions?.map((act) => (
598
+ <li key={act.name}>
599
+ <span className="font-mono text-accent bg-accent/10 px-1 rounded">
600
+ {act.name}
601
+ </span>{' '}
602
+ <span className="opacity-70">- {act.description}</span>
603
+ </li>
604
+ ))}
605
+ </ul>
606
+ </div>
607
+ <div className="flex gap-2 w-full mt-6 pt-4 border-t border-base-content/10">
608
+ <button
609
+ className="btn btn-sm btn-outline flex-1"
610
+ onClick={() => webApi.openSpecificFolder(pl.folderPath)}
611
+ >
612
+ Buka Folder
613
+ </button>
614
+ <button
615
+ className="btn btn-sm btn-primary flex-1"
616
+ onClick={() => openEditForm(pl)}
617
+ >
618
+ Edit Plugin
619
+ </button>
620
+ </div>
621
+ </div>
622
+ </div>
623
+ ))}
624
+ </div>
625
+ )}
626
+ </div>
627
+ </div>
628
+ </div>
629
+ )
630
+ }