@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,363 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import { FaCog } from 'react-icons/fa'
|
|
3
|
+
import { AlertTriangle } from 'lucide-react'
|
|
4
|
+
|
|
5
|
+
export const AiEngineSection = ({ config, setConfig }) => {
|
|
6
|
+
const [showCustomKey, setShowCustomKey] = useState(false)
|
|
7
|
+
const [showDeepseekToken, setShowDeepseekToken] = useState(false)
|
|
8
|
+
|
|
9
|
+
const handleAiProviderChange = (provider) =>
|
|
10
|
+
setConfig((prev) => ({ ...prev, aiProvider: provider }))
|
|
11
|
+
|
|
12
|
+
const handleModelChange = (e) => setConfig((prev) => ({ ...prev, model: e.target.value }))
|
|
13
|
+
|
|
14
|
+
const handleCustomEndpointChange = (e) =>
|
|
15
|
+
setConfig((prev) => ({ ...prev, customEndpoint: e.target.value }))
|
|
16
|
+
|
|
17
|
+
const handleCustomApiKeyChange = (e) =>
|
|
18
|
+
setConfig((prev) => ({ ...prev, customApiKey: e.target.value }))
|
|
19
|
+
|
|
20
|
+
const handleAwarenessEnabledChange = (e) =>
|
|
21
|
+
setConfig((prev) => ({ ...prev, awarenessEnabled: e.target.checked }))
|
|
22
|
+
|
|
23
|
+
const handlePersonalityChange = (e) =>
|
|
24
|
+
setConfig((prev) => ({ ...prev, personality: e.target.value }))
|
|
25
|
+
|
|
26
|
+
const handleTemperatureChange = (e) =>
|
|
27
|
+
setConfig((prev) => ({ ...prev, temperature: e.target.value }))
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<section className="space-y-5">
|
|
31
|
+
<h2 className="text-base font-bold uppercase tracking-wider opacity-70">AI Engine & Tools</h2>
|
|
32
|
+
|
|
33
|
+
{/* AI Provider Selector */}
|
|
34
|
+
<div id="tour-ai-provider" className="space-y-1.5">
|
|
35
|
+
<p className="text-sm font-semibold">AI Provider</p>
|
|
36
|
+
<select
|
|
37
|
+
className="select select-bordered w-full font-medium"
|
|
38
|
+
value={config.aiProvider || 'gemini-web'}
|
|
39
|
+
onChange={(e) => handleAiProviderChange(e.target.value)}
|
|
40
|
+
>
|
|
41
|
+
<option value="gemini-web">Gemini Web (Gratis)</option>
|
|
42
|
+
<option value="deepseek-web">DeepSeek Web (Gratis)</option>
|
|
43
|
+
<option value="custom">Custom API (OpenAI-Compatible)</option>
|
|
44
|
+
<option value="lm-studio">LM Studio (Local Offline)</option>
|
|
45
|
+
</select>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
{config.aiProvider === 'deepseek-web' ? (
|
|
49
|
+
<div className="space-y-4">
|
|
50
|
+
{/* Risk Warning Notice */}
|
|
51
|
+
<div className="p-3 bg-warning/10 border border-warning/30 rounded-xl text-warning text-xs flex items-start gap-2.5 leading-relaxed">
|
|
52
|
+
<AlertTriangle className="w-4 h-4 shrink-0 mt-0.5 text-warning" />
|
|
53
|
+
<div>
|
|
54
|
+
<p className="font-semibold text-warning">Peringatan Risiko:</p>
|
|
55
|
+
<p className="text-white/80 mt-0.5">
|
|
56
|
+
Provider ini menggunakan sesi web/OAuth yang tidak dilisensikan secara resmi untuk
|
|
57
|
+
penggunaan proxy/router pihak ketiga. Akun dapat dibatasi atau diblokir (banned)
|
|
58
|
+
sementara oleh DeepSeek. Gunakan atas risiko sendiri.
|
|
59
|
+
</p>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div className="space-y-1.5">
|
|
64
|
+
<div className="flex justify-between items-center">
|
|
65
|
+
<p className="text-sm font-semibold">DeepSeek User Token (Bearer)</p>
|
|
66
|
+
<button
|
|
67
|
+
type="button"
|
|
68
|
+
onClick={async () => {
|
|
69
|
+
try {
|
|
70
|
+
const text = await navigator.clipboard.readText()
|
|
71
|
+
if (text) setConfig((prev) => ({ ...prev, deepseekUserToken: text.trim() }))
|
|
72
|
+
} catch (_) {}
|
|
73
|
+
}}
|
|
74
|
+
className="text-xs text-primary hover:underline cursor-pointer"
|
|
75
|
+
>
|
|
76
|
+
Paste dari Clipboard
|
|
77
|
+
</button>
|
|
78
|
+
</div>
|
|
79
|
+
<div className="relative w-full">
|
|
80
|
+
<input
|
|
81
|
+
type={showDeepseekToken ? 'text' : 'password'}
|
|
82
|
+
placeholder="eyJhbGciOi..."
|
|
83
|
+
className="input input-bordered w-full font-mono text-xs pr-10"
|
|
84
|
+
value={config.deepseekUserToken || ''}
|
|
85
|
+
onChange={(e) =>
|
|
86
|
+
setConfig((prev) => ({ ...prev, deepseekUserToken: e.target.value }))
|
|
87
|
+
}
|
|
88
|
+
/>
|
|
89
|
+
<button
|
|
90
|
+
type="button"
|
|
91
|
+
className="absolute right-3 top-1/2 -translate-y-1/2 opacity-50 hover:opacity-100 cursor-pointer"
|
|
92
|
+
onClick={() => setShowDeepseekToken(!showDeepseekToken)}
|
|
93
|
+
title={showDeepseekToken ? 'Sembunyikan Token' : 'Tampilkan Token'}
|
|
94
|
+
>
|
|
95
|
+
{showDeepseekToken ? (
|
|
96
|
+
<svg
|
|
97
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
98
|
+
width="16"
|
|
99
|
+
height="16"
|
|
100
|
+
viewBox="0 0 24 24"
|
|
101
|
+
fill="none"
|
|
102
|
+
stroke="currentColor"
|
|
103
|
+
strokeWidth="2"
|
|
104
|
+
strokeLinecap="round"
|
|
105
|
+
strokeLinejoin="round"
|
|
106
|
+
>
|
|
107
|
+
<path d="M9.88 9.88a3 3 0 1 0 4.24 4.24" />
|
|
108
|
+
<path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" />
|
|
109
|
+
<path d="M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" />
|
|
110
|
+
<line x1="2" x2="22" y1="2" y2="22" />
|
|
111
|
+
</svg>
|
|
112
|
+
) : (
|
|
113
|
+
<svg
|
|
114
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
115
|
+
width="16"
|
|
116
|
+
height="16"
|
|
117
|
+
viewBox="0 0 24 24"
|
|
118
|
+
fill="none"
|
|
119
|
+
stroke="currentColor"
|
|
120
|
+
strokeWidth="2"
|
|
121
|
+
strokeLinecap="round"
|
|
122
|
+
strokeLinejoin="round"
|
|
123
|
+
>
|
|
124
|
+
<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" />
|
|
125
|
+
<circle cx="12" cy="12" r="3" />
|
|
126
|
+
</svg>
|
|
127
|
+
)}
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
{/* Panduan Cara Mendapatkan Token */}
|
|
132
|
+
<div className="p-3 bg-base-200/60 rounded-xl border border-white/5 space-y-2 text-xs">
|
|
133
|
+
<div className="flex items-center gap-1.5 font-semibold text-primary">
|
|
134
|
+
<FaCog className="w-3.5 h-3.5" />
|
|
135
|
+
<span>Cara Mendapatkan User Token DeepSeek:</span>
|
|
136
|
+
</div>
|
|
137
|
+
<ol className="list-decimal list-inside space-y-1 text-white/70">
|
|
138
|
+
<li>
|
|
139
|
+
Buka dan login ke akunmu di{' '}
|
|
140
|
+
<a
|
|
141
|
+
href="https://chat.deepseek.com"
|
|
142
|
+
target="_blank"
|
|
143
|
+
rel="noreferrer"
|
|
144
|
+
className="text-primary underline"
|
|
145
|
+
>
|
|
146
|
+
chat.deepseek.com
|
|
147
|
+
</a>{' '}
|
|
148
|
+
di browser.
|
|
149
|
+
</li>
|
|
150
|
+
<li>
|
|
151
|
+
Tekan tombol keyboard <strong>F12</strong> (atau klik kanan > Inspect) lalu
|
|
152
|
+
buka tab <strong>Console</strong>.
|
|
153
|
+
</li>
|
|
154
|
+
<li>Copy dan paste baris kode berikut ke dalam Console lalu tekan Enter:</li>
|
|
155
|
+
</ol>
|
|
156
|
+
<div className="flex items-center justify-between bg-black/50 p-2 rounded-lg border border-white/10 font-mono text-[11px] text-success">
|
|
157
|
+
<code className="truncate mr-2">
|
|
158
|
+
JSON.parse(localStorage.getItem('userToken')).value
|
|
159
|
+
</code>
|
|
160
|
+
<button
|
|
161
|
+
type="button"
|
|
162
|
+
onClick={() =>
|
|
163
|
+
navigator.clipboard.writeText(
|
|
164
|
+
"JSON.parse(localStorage.getItem('userToken')).value"
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
className="btn btn-xs btn-ghost text-white/50 hover:text-white shrink-0"
|
|
168
|
+
title="Salin snippet"
|
|
169
|
+
>
|
|
170
|
+
Salin Kode
|
|
171
|
+
</button>
|
|
172
|
+
</div>
|
|
173
|
+
<p className="text-[11px] text-white/40">
|
|
174
|
+
Salin nilai token yang muncul (tanpa tanda kutip), lalu tempelkan ke kolom input di
|
|
175
|
+
atas. Token ini otomatis memecahkan challenge anti-bot WASM DeepSeek secara lokal!
|
|
176
|
+
</p>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
) : config.aiProvider === 'gemini-web' || !config.aiProvider ? (
|
|
181
|
+
<div className="space-y-4">
|
|
182
|
+
<div className="space-y-1.5">
|
|
183
|
+
<p className="text-sm font-semibold">Model Gemini</p>
|
|
184
|
+
<select
|
|
185
|
+
className="select select-bordered w-full"
|
|
186
|
+
value={config.geminiWebModel || 'gemini-3.6-flash'}
|
|
187
|
+
onChange={(e) => setConfig((prev) => ({ ...prev, geminiWebModel: e.target.value }))}
|
|
188
|
+
>
|
|
189
|
+
<option value="gemini-3.6-flash">gemini-3.6-flash (Model Utama Terbaru)</option>
|
|
190
|
+
<option value="gemini-3.5-flash">gemini-3.5-flash (Stabil & Seimbang)</option>
|
|
191
|
+
<option value="gemini-3.5-flash-thinking">
|
|
192
|
+
gemini-3.5-flash-thinking (Penalaran Mendalam)
|
|
193
|
+
</option>
|
|
194
|
+
<option value="gemini-3.5-flash-thinking-lite">
|
|
195
|
+
gemini-3.5-flash-thinking-lite (Penalaran Cepat)
|
|
196
|
+
</option>
|
|
197
|
+
<option value="gemini-auto">gemini-auto (Otomatis Server)</option>
|
|
198
|
+
<option value="gemini-flash-lite">gemini-flash-lite (Super Cepat)</option>
|
|
199
|
+
</select>
|
|
200
|
+
<p className="text-xs opacity-50 mt-1">
|
|
201
|
+
Provider bawaan tanpa API Key. Membutuhkan koneksi internet (tidak mendukung input
|
|
202
|
+
gambar).
|
|
203
|
+
</p>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
) : config.aiProvider === 'custom' ? (
|
|
207
|
+
<div className="space-y-4">
|
|
208
|
+
<div className="space-y-1.5">
|
|
209
|
+
<p className="text-sm font-semibold">Custom Endpoint URL</p>
|
|
210
|
+
<input
|
|
211
|
+
type="text"
|
|
212
|
+
placeholder="Contoh: https://api.openai.com/v1/chat/completions"
|
|
213
|
+
className={`input input-bordered w-full ${config.customEndpoint && !config.customEndpoint.trim().endsWith('/chat/completions') ? 'input-error' : ''}`}
|
|
214
|
+
value={config.customEndpoint || ''}
|
|
215
|
+
onChange={handleCustomEndpointChange}
|
|
216
|
+
/>
|
|
217
|
+
{config.customEndpoint &&
|
|
218
|
+
!config.customEndpoint.trim().endsWith('/chat/completions') ? (
|
|
219
|
+
<p className="text-xs text-error mt-1 font-medium">
|
|
220
|
+
URL endpoint tidak memenuhi standar format OpenAI-Compatible.
|
|
221
|
+
</p>
|
|
222
|
+
) : (
|
|
223
|
+
<p className="text-xs opacity-50 mt-1">
|
|
224
|
+
Pastikan Endpoint mendukung standar format <strong>OpenAI-Compatible</strong>.
|
|
225
|
+
</p>
|
|
226
|
+
)}
|
|
227
|
+
</div>
|
|
228
|
+
<div className="space-y-1.5">
|
|
229
|
+
<p className="text-sm font-semibold">Custom Model ID</p>
|
|
230
|
+
<input
|
|
231
|
+
type="text"
|
|
232
|
+
placeholder="Contoh: gpt-4o-mini"
|
|
233
|
+
className="input input-bordered w-full"
|
|
234
|
+
value={config.customModel || ''}
|
|
235
|
+
onChange={(e) => setConfig((prev) => ({ ...prev, customModel: e.target.value }))}
|
|
236
|
+
/>
|
|
237
|
+
</div>
|
|
238
|
+
<div className="space-y-1.5">
|
|
239
|
+
<p className="text-sm font-semibold">Custom API Key</p>
|
|
240
|
+
<div className="relative w-full">
|
|
241
|
+
<input
|
|
242
|
+
type={showCustomKey ? 'text' : 'password'}
|
|
243
|
+
placeholder="Masukkan API Key (jika diperlukan)"
|
|
244
|
+
className="input input-bordered w-full pr-10"
|
|
245
|
+
value={config.customApiKey || ''}
|
|
246
|
+
onChange={handleCustomApiKeyChange}
|
|
247
|
+
/>
|
|
248
|
+
<button
|
|
249
|
+
type="button"
|
|
250
|
+
className="absolute right-3 top-1/2 -translate-y-1/2 opacity-50 hover:opacity-100 cursor-pointer"
|
|
251
|
+
onClick={() => setShowCustomKey(!showCustomKey)}
|
|
252
|
+
title={showCustomKey ? 'Sembunyikan API Key' : 'Tampilkan API Key'}
|
|
253
|
+
>
|
|
254
|
+
{showCustomKey ? (
|
|
255
|
+
<svg
|
|
256
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
257
|
+
width="16"
|
|
258
|
+
height="16"
|
|
259
|
+
viewBox="0 0 24 24"
|
|
260
|
+
fill="none"
|
|
261
|
+
stroke="currentColor"
|
|
262
|
+
strokeWidth="2"
|
|
263
|
+
strokeLinecap="round"
|
|
264
|
+
strokeLinejoin="round"
|
|
265
|
+
>
|
|
266
|
+
<path d="M9.88 9.88a3 3 0 1 0 4.24 4.24" />
|
|
267
|
+
<path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" />
|
|
268
|
+
<path d="M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" />
|
|
269
|
+
<line x1="2" x2="22" y1="2" y2="22" />
|
|
270
|
+
</svg>
|
|
271
|
+
) : (
|
|
272
|
+
<svg
|
|
273
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
274
|
+
width="16"
|
|
275
|
+
height="16"
|
|
276
|
+
viewBox="0 0 24 24"
|
|
277
|
+
fill="none"
|
|
278
|
+
stroke="currentColor"
|
|
279
|
+
strokeWidth="2"
|
|
280
|
+
strokeLinecap="round"
|
|
281
|
+
strokeLinejoin="round"
|
|
282
|
+
>
|
|
283
|
+
<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" />
|
|
284
|
+
<circle cx="12" cy="12" r="3" />
|
|
285
|
+
</svg>
|
|
286
|
+
)}
|
|
287
|
+
</button>
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
291
|
+
) : (
|
|
292
|
+
<div className="space-y-1.5">
|
|
293
|
+
<p className="text-sm font-semibold">Model Selector (LM Studio)</p>
|
|
294
|
+
<input
|
|
295
|
+
type="text"
|
|
296
|
+
placeholder="Contoh: google/gemma-3-4b"
|
|
297
|
+
className="input input-bordered w-full"
|
|
298
|
+
value={config.model || ''}
|
|
299
|
+
onChange={handleModelChange}
|
|
300
|
+
/>
|
|
301
|
+
<p className="text-xs opacity-40">
|
|
302
|
+
Nama model yang aktif di LM Studio. Pastikan sudah ter-load.
|
|
303
|
+
</p>
|
|
304
|
+
</div>
|
|
305
|
+
)}
|
|
306
|
+
|
|
307
|
+
{/* Awareness Engine Toggle */}
|
|
308
|
+
<div className="space-y-1.5 p-2 -mx-2 rounded-lg bg-base-200">
|
|
309
|
+
<div className="flex items-center justify-between">
|
|
310
|
+
<div>
|
|
311
|
+
<p className="text-sm font-semibold">Awareness Engine</p>
|
|
312
|
+
<p className="text-xs opacity-50 mt-1">
|
|
313
|
+
Mengizinkan Mark membaca log sistem/aktivitas dan memulai obrolan secara proaktif di
|
|
314
|
+
latar belakang.
|
|
315
|
+
</p>
|
|
316
|
+
</div>
|
|
317
|
+
<input
|
|
318
|
+
type="checkbox"
|
|
319
|
+
className="toggle toggle-primary"
|
|
320
|
+
checked={config.awarenessEnabled !== false}
|
|
321
|
+
onChange={handleAwarenessEnabledChange}
|
|
322
|
+
/>
|
|
323
|
+
</div>
|
|
324
|
+
</div>
|
|
325
|
+
|
|
326
|
+
{/* System Persona */}
|
|
327
|
+
<div id="tour-persona" className="space-y-1.5 p-2 -mx-2 rounded-lg">
|
|
328
|
+
<p className="text-sm font-semibold">Gaya Bicara dan Kepribadian</p>
|
|
329
|
+
<textarea
|
|
330
|
+
className="textarea w-full h-72 leading-relaxed no-scrollbar resize-none"
|
|
331
|
+
placeholder="Deskripsikan kepribadian Mark..."
|
|
332
|
+
value={config.personality}
|
|
333
|
+
onChange={handlePersonalityChange}
|
|
334
|
+
/>
|
|
335
|
+
</div>
|
|
336
|
+
|
|
337
|
+
{/* Temperature Slider */}
|
|
338
|
+
<div id="tour-temperature" className="space-y-2 p-2 -mx-2 rounded-lg">
|
|
339
|
+
<div className="flex items-center justify-between">
|
|
340
|
+
<p className="text-sm font-semibold">Temperature</p>
|
|
341
|
+
<span className="font-mono text-sm text-primary font-bold">{config.temperature}</span>
|
|
342
|
+
</div>
|
|
343
|
+
<input
|
|
344
|
+
type="range"
|
|
345
|
+
min="0"
|
|
346
|
+
max="1"
|
|
347
|
+
step="0.1"
|
|
348
|
+
value={config.temperature}
|
|
349
|
+
className="range range-primary range-xs w-full"
|
|
350
|
+
onChange={handleTemperatureChange}
|
|
351
|
+
/>
|
|
352
|
+
<div className="flex justify-between px-2.5 mt-2 text-xs">
|
|
353
|
+
<span>0</span>
|
|
354
|
+
<span>0.2</span>
|
|
355
|
+
<span>0.4</span>
|
|
356
|
+
<span>0.6</span>
|
|
357
|
+
<span>0.8</span>
|
|
358
|
+
<span>1.0</span>
|
|
359
|
+
</div>
|
|
360
|
+
</div>
|
|
361
|
+
</section>
|
|
362
|
+
)
|
|
363
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react'
|
|
2
|
+
|
|
3
|
+
export const ConfigCameraPreview = ({ deviceId, enabled }) => {
|
|
4
|
+
const videoRef = useRef(null)
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (!enabled) return
|
|
8
|
+
let stream = null
|
|
9
|
+
let isMounted = true
|
|
10
|
+
const startCamera = async () => {
|
|
11
|
+
try {
|
|
12
|
+
const constraints = {
|
|
13
|
+
video: deviceId && deviceId !== 'default' ? { deviceId: { exact: deviceId } } : true
|
|
14
|
+
}
|
|
15
|
+
stream = await navigator.mediaDevices.getUserMedia(constraints)
|
|
16
|
+
if (videoRef.current && isMounted) {
|
|
17
|
+
videoRef.current.srcObject = stream
|
|
18
|
+
videoRef.current.play().catch((e) => console.error(e))
|
|
19
|
+
} else {
|
|
20
|
+
stream.getTracks().forEach((t) => t.stop())
|
|
21
|
+
}
|
|
22
|
+
} catch (err) {
|
|
23
|
+
console.error('Preview camera error:', err)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
startCamera()
|
|
27
|
+
return () => {
|
|
28
|
+
isMounted = false
|
|
29
|
+
if (stream) stream.getTracks().forEach((t) => t.stop())
|
|
30
|
+
}
|
|
31
|
+
}, [deviceId, enabled])
|
|
32
|
+
|
|
33
|
+
if (!enabled) return null
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className="mt-4 rounded-xl overflow-hidden border border-white/10 bg-black/50 aspect-video relative flex items-center justify-center shadow-inner">
|
|
37
|
+
<video ref={videoRef} autoPlay playsInline muted className="w-full h-full object-cover" />
|
|
38
|
+
<div className="absolute top-2 left-2 flex items-center gap-2 px-2 py-1 bg-black/60 rounded text-xs font-mono text-white backdrop-blur-md">
|
|
39
|
+
<span className="w-2 h-2 rounded-full bg-red-500 animate-pulse"></span>
|
|
40
|
+
Live Preview
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const CameraSection = ({ config, setConfig, videoDevices }) => {
|
|
47
|
+
const handleCameraEnabledChange = (e) =>
|
|
48
|
+
setConfig((prev) => ({ ...prev, cameraEnabled: e.target.checked }))
|
|
49
|
+
|
|
50
|
+
const handleCameraDeviceIdChange = (e) =>
|
|
51
|
+
setConfig((prev) => ({ ...prev, cameraDeviceId: e.target.value }))
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div className="space-y-6 p-2 -mx-2 rounded-lg">
|
|
55
|
+
<h2 className="text-base font-bold uppercase tracking-wider opacity-70 mb-5 flex items-center gap-2">
|
|
56
|
+
Kamera
|
|
57
|
+
</h2>
|
|
58
|
+
|
|
59
|
+
<div className="form-control">
|
|
60
|
+
<label className="label cursor-pointer p-0">
|
|
61
|
+
<span className="label-text text-sm font-semibold">Aktifkan Kamera AI</span>
|
|
62
|
+
<input
|
|
63
|
+
type="checkbox"
|
|
64
|
+
className="toggle toggle-primary"
|
|
65
|
+
checked={config.cameraEnabled !== false}
|
|
66
|
+
onChange={handleCameraEnabledChange}
|
|
67
|
+
/>
|
|
68
|
+
</label>
|
|
69
|
+
<span className="text-xs opacity-50 mt-2 block">
|
|
70
|
+
Mengizinkan Mark menggunakan kamera (jika diminta) untuk melihat dunia fisik.
|
|
71
|
+
</span>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{config.cameraEnabled !== false && (
|
|
75
|
+
<div className="space-y-1.5">
|
|
76
|
+
<p className="text-sm font-semibold">Perangkat Kamera</p>
|
|
77
|
+
<select
|
|
78
|
+
className="select select-bordered w-full"
|
|
79
|
+
value={config.cameraDeviceId || 'default'}
|
|
80
|
+
onChange={handleCameraDeviceIdChange}
|
|
81
|
+
>
|
|
82
|
+
<option value="default">Default System Camera</option>
|
|
83
|
+
{videoDevices.map((cam) => (
|
|
84
|
+
<option key={cam.deviceId} value={cam.deviceId}>
|
|
85
|
+
{cam.label || `Camera ${cam.deviceId.substring(0, 5)}...`}
|
|
86
|
+
</option>
|
|
87
|
+
))}
|
|
88
|
+
</select>
|
|
89
|
+
</div>
|
|
90
|
+
)}
|
|
91
|
+
|
|
92
|
+
{config.cameraEnabled !== false && (
|
|
93
|
+
<ConfigCameraPreview
|
|
94
|
+
deviceId={config.cameraDeviceId}
|
|
95
|
+
enabled={config.cameraEnabled !== false}
|
|
96
|
+
/>
|
|
97
|
+
)}
|
|
98
|
+
</div>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react'
|
|
2
|
+
import { ShieldCheck, Terminal, Folder, Trash2, ShieldAlert } from 'lucide-react'
|
|
3
|
+
import { getAlwaysAllowedPaths, removeAlwaysAllowedPath } from '../../api/db'
|
|
4
|
+
|
|
5
|
+
export const PermissionsSection = () => {
|
|
6
|
+
const [paths, setPaths] = useState([])
|
|
7
|
+
const [isDeleting, setIsDeleting] = useState(null)
|
|
8
|
+
|
|
9
|
+
const loadPaths = async () => {
|
|
10
|
+
try {
|
|
11
|
+
const list = await getAlwaysAllowedPaths()
|
|
12
|
+
setPaths(Array.isArray(list) ? list : [])
|
|
13
|
+
} catch (_) {
|
|
14
|
+
setPaths([])
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
loadPaths()
|
|
20
|
+
|
|
21
|
+
const handleConfigUpdated = (e) => {
|
|
22
|
+
if (Array.isArray(e.detail?.alwaysAllowedPaths)) {
|
|
23
|
+
setPaths(e.detail.alwaysAllowedPaths)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
window.addEventListener('config-updated', handleConfigUpdated)
|
|
27
|
+
return () => window.removeEventListener('config-updated', handleConfigUpdated)
|
|
28
|
+
}, [])
|
|
29
|
+
|
|
30
|
+
const handleDelete = async (target) => {
|
|
31
|
+
setIsDeleting(target)
|
|
32
|
+
try {
|
|
33
|
+
const updated = await removeAlwaysAllowedPath(target)
|
|
34
|
+
setPaths(Array.isArray(updated) ? updated : [])
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.error('Gagal menghapus izin whitelist:', e)
|
|
37
|
+
} finally {
|
|
38
|
+
setIsDeleting(null)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<section className="space-y-4 p-2 -mx-2 rounded-lg">
|
|
44
|
+
<div>
|
|
45
|
+
<h2 className="text-base font-bold uppercase tracking-wider opacity-70 flex items-center gap-2">
|
|
46
|
+
<ShieldAlert className="w-4 h-4 text-warning" />
|
|
47
|
+
Keamanan & Izin Tool (Whitelist)
|
|
48
|
+
</h2>
|
|
49
|
+
<p className="text-xs text-white/50 mt-1">
|
|
50
|
+
Daftar folder dan perintah shell yang diizinkan selamanya tanpa memunculkan permintaan
|
|
51
|
+
konfirmasi.
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div className="space-y-2">
|
|
56
|
+
{paths.length === 0 ? (
|
|
57
|
+
<div className="flex flex-col items-center justify-center p-6 bg-base-200/50 rounded-xl border border-white/5 text-center space-y-2">
|
|
58
|
+
<ShieldCheck className="w-8 h-8 text-white/30" />
|
|
59
|
+
<p className="text-xs font-medium text-white/60">
|
|
60
|
+
Belum ada izin permanen yang tersimpan.
|
|
61
|
+
</p>
|
|
62
|
+
<p className="text-[11px] text-white/40 max-w-sm">
|
|
63
|
+
Ketika Mark meminta izin di percakapan, Anda dapat memilih "Izinkan Selamanya" untuk
|
|
64
|
+
mendaftarkannya ke sini.
|
|
65
|
+
</p>
|
|
66
|
+
</div>
|
|
67
|
+
) : (
|
|
68
|
+
<div className="space-y-2 max-h-72 overflow-y-auto custom-scrollbar pr-1">
|
|
69
|
+
{paths.map((item) => {
|
|
70
|
+
const isCommand = String(item).startsWith('cmd:')
|
|
71
|
+
const displayVal = isCommand ? item.slice(4) : item
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div
|
|
75
|
+
key={item}
|
|
76
|
+
className="flex items-center justify-between p-3 bg-base-200/80 rounded-xl border border-white/5 hover:border-white/10 transition-all gap-3 group"
|
|
77
|
+
>
|
|
78
|
+
<div className="flex items-center gap-2.5 min-w-0 flex-1">
|
|
79
|
+
<div className="p-1.5 rounded-lg bg-black/40 text-white/70 shrink-0">
|
|
80
|
+
{isCommand ? (
|
|
81
|
+
<Terminal className="w-3.5 h-3.5 text-warning" />
|
|
82
|
+
) : (
|
|
83
|
+
<Folder className="w-3.5 h-3.5 text-primary" />
|
|
84
|
+
)}
|
|
85
|
+
</div>
|
|
86
|
+
<div className="flex flex-col min-w-0 flex-1">
|
|
87
|
+
<div className="flex items-center gap-2">
|
|
88
|
+
<span
|
|
89
|
+
className={`badge badge-xs font-mono text-[9px] px-1.5 py-0.5 ${
|
|
90
|
+
isCommand
|
|
91
|
+
? 'bg-warning/15 text-warning border-warning/30'
|
|
92
|
+
: 'bg-primary/15 text-primary border-primary/30'
|
|
93
|
+
}`}
|
|
94
|
+
>
|
|
95
|
+
{isCommand ? 'Perintah Shell' : 'Folder / Berkas'}
|
|
96
|
+
</span>
|
|
97
|
+
</div>
|
|
98
|
+
<span
|
|
99
|
+
className="font-mono text-xs text-white/90 truncate mt-0.5"
|
|
100
|
+
title={displayVal}
|
|
101
|
+
>
|
|
102
|
+
{displayVal}
|
|
103
|
+
</span>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<button
|
|
108
|
+
type="button"
|
|
109
|
+
onClick={() => handleDelete(item)}
|
|
110
|
+
disabled={isDeleting === item}
|
|
111
|
+
className="btn btn-ghost btn-xs text-error hover:bg-error/20 p-1.5 h-auto rounded-lg transition-colors cursor-pointer shrink-0"
|
|
112
|
+
title="Cabut Izin Permanen"
|
|
113
|
+
>
|
|
114
|
+
{isDeleting === item ? (
|
|
115
|
+
<span className="loading loading-spinner loading-xs" />
|
|
116
|
+
) : (
|
|
117
|
+
<Trash2 className="w-3.5 h-3.5" />
|
|
118
|
+
)}
|
|
119
|
+
</button>
|
|
120
|
+
</div>
|
|
121
|
+
)
|
|
122
|
+
})}
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
125
|
+
</div>
|
|
126
|
+
</section>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export default PermissionsSection
|