@jungjaehoon/mama-os 0.1.1
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/CHANGELOG.md +67 -0
- package/README.md +643 -0
- package/dist/agent/agent-loop.d.ts +98 -0
- package/dist/agent/agent-loop.d.ts.map +1 -0
- package/dist/agent/agent-loop.js +417 -0
- package/dist/agent/agent-loop.js.map +1 -0
- package/dist/agent/auto-recall.d.ts +48 -0
- package/dist/agent/auto-recall.d.ts.map +1 -0
- package/dist/agent/auto-recall.js +178 -0
- package/dist/agent/auto-recall.js.map +1 -0
- package/dist/agent/claude-cli-wrapper.d.ts +130 -0
- package/dist/agent/claude-cli-wrapper.d.ts.map +1 -0
- package/dist/agent/claude-cli-wrapper.js +227 -0
- package/dist/agent/claude-cli-wrapper.js.map +1 -0
- package/dist/agent/claude-client.d.ts +50 -0
- package/dist/agent/claude-client.d.ts.map +1 -0
- package/dist/agent/claude-client.js +214 -0
- package/dist/agent/claude-client.js.map +1 -0
- package/dist/agent/gateway-tool-executor.d.ts +75 -0
- package/dist/agent/gateway-tool-executor.d.ts.map +1 -0
- package/dist/agent/gateway-tool-executor.js +348 -0
- package/dist/agent/gateway-tool-executor.js.map +1 -0
- package/dist/agent/index.d.ts +13 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +18 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/mcp-executor.d.ts +75 -0
- package/dist/agent/mcp-executor.d.ts.map +1 -0
- package/dist/agent/mcp-executor.js +307 -0
- package/dist/agent/mcp-executor.js.map +1 -0
- package/dist/agent/session-pool.d.ts +148 -0
- package/dist/agent/session-pool.d.ts.map +1 -0
- package/dist/agent/session-pool.js +272 -0
- package/dist/agent/session-pool.js.map +1 -0
- package/dist/agent/streaming-callback-manager.d.ts +85 -0
- package/dist/agent/streaming-callback-manager.d.ts.map +1 -0
- package/dist/agent/streaming-callback-manager.js +103 -0
- package/dist/agent/streaming-callback-manager.js.map +1 -0
- package/dist/agent/types.d.ts +437 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +29 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/api/cron-handler.d.ts +44 -0
- package/dist/api/cron-handler.d.ts.map +1 -0
- package/dist/api/cron-handler.js +195 -0
- package/dist/api/cron-handler.js.map +1 -0
- package/dist/api/error-handler.d.ts +22 -0
- package/dist/api/error-handler.d.ts.map +1 -0
- package/dist/api/error-handler.js +104 -0
- package/dist/api/error-handler.js.map +1 -0
- package/dist/api/heartbeat-handler.d.ts +49 -0
- package/dist/api/heartbeat-handler.d.ts.map +1 -0
- package/dist/api/heartbeat-handler.js +91 -0
- package/dist/api/heartbeat-handler.js.map +1 -0
- package/dist/api/index.d.ts +61 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +145 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/types.d.ts +156 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +62 -0
- package/dist/api/types.js.map +1 -0
- package/dist/auth/index.d.ts +7 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +11 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/oauth-manager.d.ts +59 -0
- package/dist/auth/oauth-manager.d.ts.map +1 -0
- package/dist/auth/oauth-manager.js +237 -0
- package/dist/auth/oauth-manager.js.map +1 -0
- package/dist/auth/types.d.ts +92 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +23 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/cli/commands/init.d.ts +19 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +155 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/run.d.ts +19 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +89 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +19 -0
- package/dist/cli/commands/setup.d.ts.map +1 -0
- package/dist/cli/commands/setup.js +134 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/start.d.ts +24 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +1073 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.d.ts +10 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +85 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +10 -0
- package/dist/cli/commands/stop.d.ts.map +1 -0
- package/dist/cli/commands/stop.js +65 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/config/config-manager.d.ts +51 -0
- package/dist/cli/config/config-manager.d.ts.map +1 -0
- package/dist/cli/config/config-manager.js +216 -0
- package/dist/cli/config/config-manager.js.map +1 -0
- package/dist/cli/config/types.d.ts +172 -0
- package/dist/cli/config/types.d.ts.map +1 -0
- package/dist/cli/config/types.js +48 -0
- package/dist/cli/config/types.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +92 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/pid-manager.d.ts +66 -0
- package/dist/cli/utils/pid-manager.d.ts.map +1 -0
- package/dist/cli/utils/pid-manager.js +167 -0
- package/dist/cli/utils/pid-manager.js.map +1 -0
- package/dist/concurrency/index.d.ts +13 -0
- package/dist/concurrency/index.d.ts.map +1 -0
- package/dist/concurrency/index.js +22 -0
- package/dist/concurrency/index.js.map +1 -0
- package/dist/concurrency/lane-manager.d.ts +113 -0
- package/dist/concurrency/lane-manager.d.ts.map +1 -0
- package/dist/concurrency/lane-manager.js +245 -0
- package/dist/concurrency/lane-manager.js.map +1 -0
- package/dist/concurrency/session-key.d.ts +41 -0
- package/dist/concurrency/session-key.d.ts.map +1 -0
- package/dist/concurrency/session-key.js +61 -0
- package/dist/concurrency/session-key.js.map +1 -0
- package/dist/concurrency/types.d.ts +69 -0
- package/dist/concurrency/types.d.ts.map +1 -0
- package/dist/concurrency/types.js +16 -0
- package/dist/concurrency/types.js.map +1 -0
- package/dist/gateways/channel-history.d.ts +102 -0
- package/dist/gateways/channel-history.d.ts.map +1 -0
- package/dist/gateways/channel-history.js +181 -0
- package/dist/gateways/channel-history.js.map +1 -0
- package/dist/gateways/context-injector.d.ts +74 -0
- package/dist/gateways/context-injector.d.ts.map +1 -0
- package/dist/gateways/context-injector.js +121 -0
- package/dist/gateways/context-injector.js.map +1 -0
- package/dist/gateways/discord.d.ts +122 -0
- package/dist/gateways/discord.d.ts.map +1 -0
- package/dist/gateways/discord.js +602 -0
- package/dist/gateways/discord.js.map +1 -0
- package/dist/gateways/index.d.ts +30 -0
- package/dist/gateways/index.d.ts.map +1 -0
- package/dist/gateways/index.js +49 -0
- package/dist/gateways/index.js.map +1 -0
- package/dist/gateways/message-router.d.ts +116 -0
- package/dist/gateways/message-router.d.ts.map +1 -0
- package/dist/gateways/message-router.js +315 -0
- package/dist/gateways/message-router.js.map +1 -0
- package/dist/gateways/message-splitter.d.ts +54 -0
- package/dist/gateways/message-splitter.d.ts.map +1 -0
- package/dist/gateways/message-splitter.js +146 -0
- package/dist/gateways/message-splitter.js.map +1 -0
- package/dist/gateways/plugin-loader.d.ts +76 -0
- package/dist/gateways/plugin-loader.d.ts.map +1 -0
- package/dist/gateways/plugin-loader.js +221 -0
- package/dist/gateways/plugin-loader.js.map +1 -0
- package/dist/gateways/session-store.d.ts +77 -0
- package/dist/gateways/session-store.d.ts.map +1 -0
- package/dist/gateways/session-store.js +233 -0
- package/dist/gateways/session-store.js.map +1 -0
- package/dist/gateways/slack.d.ts +90 -0
- package/dist/gateways/slack.d.ts.map +1 -0
- package/dist/gateways/slack.js +281 -0
- package/dist/gateways/slack.js.map +1 -0
- package/dist/gateways/telegram.d.ts +79 -0
- package/dist/gateways/telegram.d.ts.map +1 -0
- package/dist/gateways/telegram.js +207 -0
- package/dist/gateways/telegram.js.map +1 -0
- package/dist/gateways/types.d.ts +340 -0
- package/dist/gateways/types.d.ts.map +1 -0
- package/dist/gateways/types.js +6 -0
- package/dist/gateways/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/memory-logger.d.ts +47 -0
- package/dist/memory/memory-logger.d.ts.map +1 -0
- package/dist/memory/memory-logger.js +126 -0
- package/dist/memory/memory-logger.js.map +1 -0
- package/dist/onboarding/all-tools.d.ts +18 -0
- package/dist/onboarding/all-tools.d.ts.map +1 -0
- package/dist/onboarding/all-tools.js +149 -0
- package/dist/onboarding/all-tools.js.map +1 -0
- package/dist/onboarding/autonomous-discovery-tools.d.ts +13 -0
- package/dist/onboarding/autonomous-discovery-tools.d.ts.map +1 -0
- package/dist/onboarding/autonomous-discovery-tools.js +268 -0
- package/dist/onboarding/autonomous-discovery-tools.js.map +1 -0
- package/dist/onboarding/bootstrap-template.d.ts +5 -0
- package/dist/onboarding/bootstrap-template.d.ts.map +1 -0
- package/dist/onboarding/bootstrap-template.js +142 -0
- package/dist/onboarding/bootstrap-template.js.map +1 -0
- package/dist/onboarding/complete-autonomous-prompt.d.ts +13 -0
- package/dist/onboarding/complete-autonomous-prompt.d.ts.map +1 -0
- package/dist/onboarding/complete-autonomous-prompt.js +1220 -0
- package/dist/onboarding/complete-autonomous-prompt.js.map +1 -0
- package/dist/onboarding/onboarding-state.d.ts +70 -0
- package/dist/onboarding/onboarding-state.d.ts.map +1 -0
- package/dist/onboarding/onboarding-state.js +184 -0
- package/dist/onboarding/onboarding-state.js.map +1 -0
- package/dist/onboarding/personality-quiz.d.ts +35 -0
- package/dist/onboarding/personality-quiz.d.ts.map +1 -0
- package/dist/onboarding/personality-quiz.js +219 -0
- package/dist/onboarding/personality-quiz.js.map +1 -0
- package/dist/onboarding/phase-5-summary.d.ts +22 -0
- package/dist/onboarding/phase-5-summary.d.ts.map +1 -0
- package/dist/onboarding/phase-5-summary.js +151 -0
- package/dist/onboarding/phase-5-summary.js.map +1 -0
- package/dist/onboarding/phase-6-security.d.ts +33 -0
- package/dist/onboarding/phase-6-security.d.ts.map +1 -0
- package/dist/onboarding/phase-6-security.js +473 -0
- package/dist/onboarding/phase-6-security.js.map +1 -0
- package/dist/onboarding/phase-7-integrations.d.ts +66 -0
- package/dist/onboarding/phase-7-integrations.d.ts.map +1 -0
- package/dist/onboarding/phase-7-integrations.js +619 -0
- package/dist/onboarding/phase-7-integrations.js.map +1 -0
- package/dist/onboarding/phase-8-demo.d.ts +43 -0
- package/dist/onboarding/phase-8-demo.d.ts.map +1 -0
- package/dist/onboarding/phase-8-demo.js +346 -0
- package/dist/onboarding/phase-8-demo.js.map +1 -0
- package/dist/onboarding/phase-9-finalization.d.ts +22 -0
- package/dist/onboarding/phase-9-finalization.d.ts.map +1 -0
- package/dist/onboarding/phase-9-finalization.js +375 -0
- package/dist/onboarding/phase-9-finalization.js.map +1 -0
- package/dist/onboarding/ritual-prompt.d.ts +2 -0
- package/dist/onboarding/ritual-prompt.d.ts.map +1 -0
- package/dist/onboarding/ritual-prompt.js +285 -0
- package/dist/onboarding/ritual-prompt.js.map +1 -0
- package/dist/onboarding/ritual-tools.d.ts +13 -0
- package/dist/onboarding/ritual-tools.d.ts.map +1 -0
- package/dist/onboarding/ritual-tools.js +93 -0
- package/dist/onboarding/ritual-tools.js.map +1 -0
- package/dist/runners/cli-runner.d.ts +59 -0
- package/dist/runners/cli-runner.d.ts.map +1 -0
- package/dist/runners/cli-runner.js +190 -0
- package/dist/runners/cli-runner.js.map +1 -0
- package/dist/runners/index.d.ts +11 -0
- package/dist/runners/index.d.ts.map +1 -0
- package/dist/runners/index.js +15 -0
- package/dist/runners/index.js.map +1 -0
- package/dist/runners/types.d.ts +81 -0
- package/dist/runners/types.d.ts.map +1 -0
- package/dist/runners/types.js +31 -0
- package/dist/runners/types.js.map +1 -0
- package/dist/scheduler/cron-scheduler.d.ts +115 -0
- package/dist/scheduler/cron-scheduler.d.ts.map +1 -0
- package/dist/scheduler/cron-scheduler.js +320 -0
- package/dist/scheduler/cron-scheduler.js.map +1 -0
- package/dist/scheduler/heartbeat.d.ts +53 -0
- package/dist/scheduler/heartbeat.d.ts.map +1 -0
- package/dist/scheduler/heartbeat.js +160 -0
- package/dist/scheduler/heartbeat.js.map +1 -0
- package/dist/scheduler/index.d.ts +22 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/scheduler/index.js +31 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/scheduler/job-lock.d.ts +85 -0
- package/dist/scheduler/job-lock.d.ts.map +1 -0
- package/dist/scheduler/job-lock.js +137 -0
- package/dist/scheduler/job-lock.js.map +1 -0
- package/dist/scheduler/recovery.d.ts +78 -0
- package/dist/scheduler/recovery.d.ts.map +1 -0
- package/dist/scheduler/recovery.js +124 -0
- package/dist/scheduler/recovery.js.map +1 -0
- package/dist/scheduler/schedule-store.d.ts +112 -0
- package/dist/scheduler/schedule-store.d.ts.map +1 -0
- package/dist/scheduler/schedule-store.js +259 -0
- package/dist/scheduler/schedule-store.js.map +1 -0
- package/dist/scheduler/token-keep-alive.d.ts +49 -0
- package/dist/scheduler/token-keep-alive.d.ts.map +1 -0
- package/dist/scheduler/token-keep-alive.js +102 -0
- package/dist/scheduler/token-keep-alive.js.map +1 -0
- package/dist/scheduler/types.d.ts +96 -0
- package/dist/scheduler/types.d.ts.map +1 -0
- package/dist/scheduler/types.js +21 -0
- package/dist/scheduler/types.js.map +1 -0
- package/dist/setup/setup-prompt.d.ts +2 -0
- package/dist/setup/setup-prompt.d.ts.map +1 -0
- package/dist/setup/setup-prompt.js +138 -0
- package/dist/setup/setup-prompt.js.map +1 -0
- package/dist/setup/setup-server.d.ts +8 -0
- package/dist/setup/setup-server.d.ts.map +1 -0
- package/dist/setup/setup-server.js +71 -0
- package/dist/setup/setup-server.js.map +1 -0
- package/dist/setup/setup-tools.d.ts +13 -0
- package/dist/setup/setup-tools.d.ts.map +1 -0
- package/dist/setup/setup-tools.js +103 -0
- package/dist/setup/setup-tools.js.map +1 -0
- package/dist/setup/setup-websocket.d.ts +6 -0
- package/dist/setup/setup-websocket.d.ts.map +1 -0
- package/dist/setup/setup-websocket.js +312 -0
- package/dist/setup/setup-websocket.js.map +1 -0
- package/dist/skills/index.d.ts +10 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +26 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/skill-executor.d.ts +48 -0
- package/dist/skills/skill-executor.d.ts.map +1 -0
- package/dist/skills/skill-executor.js +483 -0
- package/dist/skills/skill-executor.js.map +1 -0
- package/dist/skills/skill-loader.d.ts +40 -0
- package/dist/skills/skill-loader.d.ts.map +1 -0
- package/dist/skills/skill-loader.js +225 -0
- package/dist/skills/skill-loader.js.map +1 -0
- package/dist/skills/skill-matcher.d.ts +33 -0
- package/dist/skills/skill-matcher.d.ts.map +1 -0
- package/dist/skills/skill-matcher.js +190 -0
- package/dist/skills/skill-matcher.js.map +1 -0
- package/dist/skills/types.d.ts +123 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/skills/types.js +12 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/tools/browser-tool.d.ts +149 -0
- package/dist/tools/browser-tool.d.ts.map +1 -0
- package/dist/tools/browser-tool.js +257 -0
- package/dist/tools/browser-tool.js.map +1 -0
- package/package.json +84 -0
- package/public/favicon.ico +0 -0
- package/public/setup.html +1026 -0
- package/public/viewer/icons/icon-192.png +0 -0
- package/public/viewer/icons/icon-512.png +0 -0
- package/public/viewer/js/modules/chat.js +1587 -0
- package/public/viewer/js/modules/dashboard.js +275 -0
- package/public/viewer/js/modules/graph.js +997 -0
- package/public/viewer/js/modules/memory.js +353 -0
- package/public/viewer/js/modules/settings.js +255 -0
- package/public/viewer/js/utils/api.js +169 -0
- package/public/viewer/js/utils/dom.js +92 -0
- package/public/viewer/js/utils/format.js +192 -0
- package/public/viewer/manifest.json +26 -0
- package/public/viewer/sw.js +131 -0
- package/public/viewer/viewer.css +500 -0
- package/public/viewer/viewer.html +1535 -0
- package/scripts/postinstall.js +118 -0
- package/templates/skills/document-analyze.md +63 -0
- package/templates/skills/heartbeat-report.md +75 -0
- package/templates/skills/image-translate.md +67 -0
- package/templates/workspace/skill-forge/DESIGN.md +115 -0
- package/templates/workspace/skill-forge/agents/architect.ts +295 -0
- package/templates/workspace/skill-forge/agents/developer.ts +364 -0
- package/templates/workspace/skill-forge/agents/qa.ts +313 -0
- package/templates/workspace/skill-forge/claude-api.ts +353 -0
- package/templates/workspace/skill-forge/discord-ui.ts +580 -0
- package/templates/workspace/skill-forge/error-handler.ts +354 -0
- package/templates/workspace/skill-forge/mama-integration.ts +357 -0
- package/templates/workspace/skill-forge/orchestrator.ts +495 -0
- package/templates/workspace/skill-forge/output/generated-skills/skills/hello-world/README.md +24 -0
- package/templates/workspace/skill-forge/output/generated-skills/skills/hello-world/index.ts +79 -0
- package/templates/workspace/skill-forge/output/generated-skills/skills/hello-world/types.ts +17 -0
- package/templates/workspace/skill-forge/package.json +21 -0
- package/templates/workspace/skill-forge/state/session.json +132 -0
- package/templates/workspace/skill-forge/test-e2e.ts +139 -0
- package/templates/workspace/skill-forge/tsconfig.json +20 -0
- package/templates/workspace/skill-forge/types.ts +159 -0
|
@@ -0,0 +1,997 @@
|
|
|
1
|
+
/* global marked */
|
|
2
|
+
/**
|
|
3
|
+
* Graph Module - Decision Graph Visualization
|
|
4
|
+
* @module modules/graph
|
|
5
|
+
* @version 1.0.0
|
|
6
|
+
*
|
|
7
|
+
* Handles Graph visualization using vis.js:
|
|
8
|
+
* - Network initialization and rendering
|
|
9
|
+
* - Node/edge styling and clustering
|
|
10
|
+
* - Search and filter functionality
|
|
11
|
+
* - Detail panel for node information
|
|
12
|
+
* - BFS traversal for connected nodes
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/* eslint-env browser */
|
|
16
|
+
/* global vis */
|
|
17
|
+
|
|
18
|
+
import { escapeHtml, debounce, showToast } from '../utils/dom.js';
|
|
19
|
+
import { API } from '../utils/api.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Graph Module Class
|
|
23
|
+
*/
|
|
24
|
+
export class GraphModule {
|
|
25
|
+
constructor() {
|
|
26
|
+
// Network state
|
|
27
|
+
this.network = null;
|
|
28
|
+
this.graphData = { nodes: [], edges: [], meta: {} };
|
|
29
|
+
this.currentNodeId = null;
|
|
30
|
+
this.adjacencyList = new Map();
|
|
31
|
+
|
|
32
|
+
// Search state
|
|
33
|
+
this.searchMatches = [];
|
|
34
|
+
this.currentSearchIndex = 0;
|
|
35
|
+
this.debouncedSearch = debounce(() => this.search(), 300);
|
|
36
|
+
|
|
37
|
+
// Color management
|
|
38
|
+
this.topicColors = {};
|
|
39
|
+
this.colorPalette = [
|
|
40
|
+
'#6366f1',
|
|
41
|
+
'#8b5cf6',
|
|
42
|
+
'#a855f7',
|
|
43
|
+
'#d946ef',
|
|
44
|
+
'#ec4899',
|
|
45
|
+
'#f43f5e',
|
|
46
|
+
'#ef4444',
|
|
47
|
+
'#f97316',
|
|
48
|
+
'#f59e0b',
|
|
49
|
+
'#eab308',
|
|
50
|
+
'#84cc16',
|
|
51
|
+
'#22c55e',
|
|
52
|
+
'#10b981',
|
|
53
|
+
'#14b8a6',
|
|
54
|
+
'#06b6d4',
|
|
55
|
+
'#0ea5e9',
|
|
56
|
+
];
|
|
57
|
+
this.colorIndex = 0;
|
|
58
|
+
|
|
59
|
+
// Edge styles
|
|
60
|
+
this.edgeStyles = {
|
|
61
|
+
supersedes: { color: '#848484', dashes: false },
|
|
62
|
+
builds_on: { color: '#457b9d', dashes: [5, 5] },
|
|
63
|
+
debates: { color: '#e63946', dashes: [5, 5] },
|
|
64
|
+
synthesizes: { color: '#9b59b6', width: 3, dashes: false },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// =============================================
|
|
69
|
+
// Data Loading
|
|
70
|
+
// =============================================
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Fetch graph data from API
|
|
74
|
+
*/
|
|
75
|
+
async fetchData() {
|
|
76
|
+
try {
|
|
77
|
+
this.graphData = await API.getGraph();
|
|
78
|
+
console.log('[MAMA] Graph data loaded:', this.graphData.meta);
|
|
79
|
+
return this.graphData;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error('[MAMA] Failed to fetch graph:', error);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// =============================================
|
|
87
|
+
// Graph Initialization
|
|
88
|
+
// =============================================
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Initialize vis-network
|
|
92
|
+
*/
|
|
93
|
+
init(data) {
|
|
94
|
+
const container = document.getElementById('graph-canvas');
|
|
95
|
+
if (!container) {
|
|
96
|
+
console.error('[MAMA] graph-canvas element not found');
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Ensure container has dimensions for vis-network
|
|
101
|
+
if (container.offsetHeight === 0) {
|
|
102
|
+
container.style.minHeight = '400px';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.log(
|
|
106
|
+
'[MAMA] Graph canvas dimensions:',
|
|
107
|
+
container.offsetWidth,
|
|
108
|
+
'x',
|
|
109
|
+
container.offsetHeight
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
this.graphData = data;
|
|
113
|
+
|
|
114
|
+
// Build adjacency list for BFS
|
|
115
|
+
this.buildAdjacencyList(data.edges);
|
|
116
|
+
|
|
117
|
+
// Calculate connection counts for sizing
|
|
118
|
+
const connectionCounts = this.calculateConnectionCounts(data.nodes, data.edges);
|
|
119
|
+
|
|
120
|
+
// Map nodes to vis-network format
|
|
121
|
+
const nodes = data.nodes.map((n) => ({
|
|
122
|
+
id: n.id,
|
|
123
|
+
label: n.topic || n.id.substring(0, 20),
|
|
124
|
+
title: this.createNodeTooltip(n),
|
|
125
|
+
color: {
|
|
126
|
+
background: this.getTopicColor(n.topic),
|
|
127
|
+
border: this.getOutcomeBorderColor(n.outcome),
|
|
128
|
+
highlight: { background: this.getTopicColor(n.topic), border: '#fff' },
|
|
129
|
+
},
|
|
130
|
+
size: this.getNodeSize(connectionCounts[n.id] || 0),
|
|
131
|
+
font: { color: '#fff', size: 12 },
|
|
132
|
+
borderWidth: 3,
|
|
133
|
+
data: n,
|
|
134
|
+
}));
|
|
135
|
+
|
|
136
|
+
// Map edges to vis-network format
|
|
137
|
+
const edges = data.edges.map((e) => {
|
|
138
|
+
const style = this.getEdgeStyle(e.relationship);
|
|
139
|
+
return {
|
|
140
|
+
from: e.from,
|
|
141
|
+
to: e.to,
|
|
142
|
+
arrows: { to: { enabled: true, scaleFactor: 0.5 } },
|
|
143
|
+
color: style.color,
|
|
144
|
+
dashes: style.dashes,
|
|
145
|
+
width: style.width || 2,
|
|
146
|
+
title: e.relationship,
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const networkData = {
|
|
151
|
+
nodes: new vis.DataSet(nodes),
|
|
152
|
+
edges: new vis.DataSet(edges),
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const options = {
|
|
156
|
+
nodes: {
|
|
157
|
+
shape: 'dot',
|
|
158
|
+
scaling: { min: 10, max: 30 },
|
|
159
|
+
},
|
|
160
|
+
edges: {
|
|
161
|
+
smooth: { type: 'continuous', roundness: 0.5 },
|
|
162
|
+
width: 2,
|
|
163
|
+
},
|
|
164
|
+
physics: {
|
|
165
|
+
enabled: true,
|
|
166
|
+
barnesHut: {
|
|
167
|
+
gravitationalConstant: -8000,
|
|
168
|
+
centralGravity: 0.3,
|
|
169
|
+
springLength: 150,
|
|
170
|
+
springConstant: 0.04,
|
|
171
|
+
damping: 0.09,
|
|
172
|
+
avoidOverlap: 0.5,
|
|
173
|
+
},
|
|
174
|
+
stabilization: {
|
|
175
|
+
enabled: true,
|
|
176
|
+
iterations: 200,
|
|
177
|
+
updateInterval: 50,
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
interaction: {
|
|
181
|
+
hover: true,
|
|
182
|
+
tooltipDelay: 200,
|
|
183
|
+
zoomView: true,
|
|
184
|
+
dragView: true,
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
this.network = new vis.Network(container, networkData, options);
|
|
189
|
+
|
|
190
|
+
// Event handlers
|
|
191
|
+
this.network.on('click', (params) => {
|
|
192
|
+
try {
|
|
193
|
+
if (params.nodes.length > 0) {
|
|
194
|
+
const nodeId = params.nodes[0];
|
|
195
|
+
const node = data.nodes.find((n) => n.id === nodeId);
|
|
196
|
+
if (node) {
|
|
197
|
+
console.log('[MAMA] Node clicked:', nodeId, node);
|
|
198
|
+
this.showDetail(node);
|
|
199
|
+
this.highlightConnectedNodes(nodeId);
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
this.closeDetail();
|
|
203
|
+
this.resetNodeHighlight();
|
|
204
|
+
}
|
|
205
|
+
} catch (error) {
|
|
206
|
+
console.error('[MAMA] Error handling click:', error);
|
|
207
|
+
console.error('[MAMA] Error stack:', error.stack);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
this.network.on('stabilized', () => {
|
|
212
|
+
const loadingEl = document.getElementById('graph-loading');
|
|
213
|
+
if (loadingEl) {
|
|
214
|
+
loadingEl.style.display = 'none';
|
|
215
|
+
}
|
|
216
|
+
console.log('[MAMA] Graph stabilized');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Backup: hide loading after 3 seconds even if stabilization doesn't complete
|
|
220
|
+
setTimeout(() => {
|
|
221
|
+
const loadingEl = document.getElementById('graph-loading');
|
|
222
|
+
if (loadingEl && loadingEl.style.display !== 'none') {
|
|
223
|
+
loadingEl.style.display = 'none';
|
|
224
|
+
console.log('[MAMA] Graph loading hidden by timeout');
|
|
225
|
+
}
|
|
226
|
+
}, 3000);
|
|
227
|
+
|
|
228
|
+
// Populate topic filter
|
|
229
|
+
const topics = [...new Set(data.nodes.map((n) => n.topic))].sort();
|
|
230
|
+
this.populateTopicFilter(topics);
|
|
231
|
+
|
|
232
|
+
console.log('[MAMA] Graph initialized with', nodes.length, 'nodes and', edges.length, 'edges');
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// =============================================
|
|
236
|
+
// Styling Utilities
|
|
237
|
+
// =============================================
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get color for topic
|
|
241
|
+
*/
|
|
242
|
+
getTopicColor(topic) {
|
|
243
|
+
if (!this.topicColors[topic]) {
|
|
244
|
+
this.topicColors[topic] = this.colorPalette[this.colorIndex % this.colorPalette.length];
|
|
245
|
+
this.colorIndex++;
|
|
246
|
+
}
|
|
247
|
+
return this.topicColors[topic];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get border color based on outcome
|
|
252
|
+
*/
|
|
253
|
+
getOutcomeBorderColor(outcome) {
|
|
254
|
+
switch (outcome?.toLowerCase()) {
|
|
255
|
+
case 'success':
|
|
256
|
+
return '#22c55e';
|
|
257
|
+
case 'failed':
|
|
258
|
+
return '#ef4444';
|
|
259
|
+
case 'partial':
|
|
260
|
+
return '#f59e0b';
|
|
261
|
+
default:
|
|
262
|
+
return '#4a4a6a';
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Get edge style by relationship type
|
|
268
|
+
*/
|
|
269
|
+
getEdgeStyle(relationship) {
|
|
270
|
+
return this.edgeStyles[relationship] || { color: '#4a4a6a', dashes: false };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Get node size based on connection count
|
|
275
|
+
*/
|
|
276
|
+
getNodeSize(connectionCount) {
|
|
277
|
+
if (connectionCount <= 2) {
|
|
278
|
+
return 12;
|
|
279
|
+
}
|
|
280
|
+
if (connectionCount <= 5) {
|
|
281
|
+
return 18;
|
|
282
|
+
}
|
|
283
|
+
if (connectionCount <= 10) {
|
|
284
|
+
return 24;
|
|
285
|
+
}
|
|
286
|
+
return 30;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Create node tooltip
|
|
291
|
+
*/
|
|
292
|
+
createNodeTooltip(node) {
|
|
293
|
+
return `
|
|
294
|
+
<strong>${escapeHtml(node.topic || 'Unknown')}</strong><br>
|
|
295
|
+
Decision: ${escapeHtml((node.decision || '').substring(0, 100))}...<br>
|
|
296
|
+
Outcome: ${node.outcome || 'PENDING'}<br>
|
|
297
|
+
Confidence: ${Math.round((node.confidence || 0) * 100)}%
|
|
298
|
+
`;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// =============================================
|
|
302
|
+
// Data Processing
|
|
303
|
+
// =============================================
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Build adjacency list for BFS
|
|
307
|
+
*/
|
|
308
|
+
buildAdjacencyList(edges) {
|
|
309
|
+
this.adjacencyList = new Map();
|
|
310
|
+
|
|
311
|
+
edges.forEach((edge) => {
|
|
312
|
+
if (!this.adjacencyList.has(edge.from)) {
|
|
313
|
+
this.adjacencyList.set(edge.from, []);
|
|
314
|
+
}
|
|
315
|
+
if (!this.adjacencyList.has(edge.to)) {
|
|
316
|
+
this.adjacencyList.set(edge.to, []);
|
|
317
|
+
}
|
|
318
|
+
this.adjacencyList.get(edge.from).push(edge.to);
|
|
319
|
+
this.adjacencyList.get(edge.to).push(edge.from);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
console.log('[MAMA] Adjacency list built with', this.adjacencyList.size, 'nodes');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Calculate connection count for each node
|
|
327
|
+
*/
|
|
328
|
+
calculateConnectionCounts(nodes, edges) {
|
|
329
|
+
const counts = {};
|
|
330
|
+
nodes.forEach((n) => (counts[n.id] = 0));
|
|
331
|
+
|
|
332
|
+
edges.forEach((edge) => {
|
|
333
|
+
if (counts[edge.from] !== undefined) {
|
|
334
|
+
counts[edge.from]++;
|
|
335
|
+
}
|
|
336
|
+
if (counts[edge.to] !== undefined) {
|
|
337
|
+
counts[edge.to]++;
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
return counts;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// =============================================
|
|
345
|
+
// Graph Traversal & Highlighting
|
|
346
|
+
// =============================================
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Get connected node IDs using BFS
|
|
350
|
+
*/
|
|
351
|
+
getConnectedNodeIds(nodeId, maxDepth = 3) {
|
|
352
|
+
const visited = new Set();
|
|
353
|
+
const queue = [{ id: nodeId, depth: 0 }];
|
|
354
|
+
visited.add(nodeId);
|
|
355
|
+
|
|
356
|
+
while (queue.length > 0) {
|
|
357
|
+
const { id, depth } = queue.shift();
|
|
358
|
+
|
|
359
|
+
if (depth >= maxDepth) {
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const neighbors = this.adjacencyList.get(id) || [];
|
|
364
|
+
neighbors.forEach((neighborId) => {
|
|
365
|
+
if (!visited.has(neighborId)) {
|
|
366
|
+
visited.add(neighborId);
|
|
367
|
+
queue.push({ id: neighborId, depth: depth + 1 });
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return Array.from(visited);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Highlight connected nodes
|
|
377
|
+
*/
|
|
378
|
+
highlightConnectedNodes(nodeId) {
|
|
379
|
+
if (!this.network) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const connectedIds = this.getConnectedNodeIds(nodeId, 3);
|
|
384
|
+
const allNodes = this.network.body.data.nodes.get();
|
|
385
|
+
|
|
386
|
+
allNodes.forEach((node) => {
|
|
387
|
+
const isConnected = connectedIds.includes(node.id);
|
|
388
|
+
const opacity = isConnected ? 1.0 : 0.2;
|
|
389
|
+
|
|
390
|
+
this.network.body.data.nodes.update({
|
|
391
|
+
id: node.id,
|
|
392
|
+
opacity: opacity,
|
|
393
|
+
font: { ...node.font, color: isConnected ? '#fff' : '#666' },
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const allEdges = this.network.body.data.edges.get();
|
|
398
|
+
allEdges.forEach((edge) => {
|
|
399
|
+
const isConnected = connectedIds.includes(edge.from) && connectedIds.includes(edge.to);
|
|
400
|
+
this.network.body.data.edges.update({
|
|
401
|
+
id: edge.id,
|
|
402
|
+
opacity: isConnected ? 1.0 : 0.1,
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Reset node highlight
|
|
409
|
+
*/
|
|
410
|
+
resetNodeHighlight() {
|
|
411
|
+
if (!this.network) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const allNodes = this.network.body.data.nodes.get();
|
|
416
|
+
allNodes.forEach((node) => {
|
|
417
|
+
this.network.body.data.nodes.update({
|
|
418
|
+
id: node.id,
|
|
419
|
+
opacity: 1.0,
|
|
420
|
+
font: { ...node.font, color: '#fff' },
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
const allEdges = this.network.body.data.edges.get();
|
|
425
|
+
allEdges.forEach((edge) => {
|
|
426
|
+
this.network.body.data.edges.update({
|
|
427
|
+
id: edge.id,
|
|
428
|
+
opacity: 1.0,
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// =============================================
|
|
434
|
+
// Detail Panel
|
|
435
|
+
// =============================================
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Show node detail panel
|
|
439
|
+
*/
|
|
440
|
+
async showDetail(node) {
|
|
441
|
+
try {
|
|
442
|
+
console.log('[MAMA] showDetail called with node:', node);
|
|
443
|
+
this.currentNodeId = node.id;
|
|
444
|
+
const panel = document.getElementById('decision-detail-modal');
|
|
445
|
+
|
|
446
|
+
if (!panel) {
|
|
447
|
+
console.error('[MAMA] decision-detail-modal element not found');
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Update existing DOM elements with markdown rendering
|
|
452
|
+
document.getElementById('detail-topic').textContent = node.topic || 'Unknown Topic';
|
|
453
|
+
const decisionEl = document.getElementById('detail-decision');
|
|
454
|
+
const reasoningEl = document.getElementById('detail-reasoning');
|
|
455
|
+
|
|
456
|
+
// Use marked for markdown if available
|
|
457
|
+
if (typeof marked !== 'undefined') {
|
|
458
|
+
decisionEl.innerHTML = marked.parse(node.decision || '-');
|
|
459
|
+
reasoningEl.innerHTML = marked.parse(node.reasoning || '-');
|
|
460
|
+
} else {
|
|
461
|
+
decisionEl.textContent = node.decision || '-';
|
|
462
|
+
reasoningEl.textContent = node.reasoning || '-';
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const outcomeSelect = document.getElementById('detail-outcome-select');
|
|
466
|
+
if (outcomeSelect) {
|
|
467
|
+
outcomeSelect.value = (node.outcome || 'PENDING').toUpperCase();
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Clear outcome status
|
|
471
|
+
const outcomeStatus = document.getElementById('outcome-status');
|
|
472
|
+
if (outcomeStatus) {
|
|
473
|
+
outcomeStatus.textContent = '';
|
|
474
|
+
outcomeStatus.className = '';
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
document.getElementById('detail-confidence').textContent = node.confidence
|
|
478
|
+
? `${(node.confidence * 100).toFixed(0)}%`
|
|
479
|
+
: '-';
|
|
480
|
+
|
|
481
|
+
const createdEl = document.getElementById('detail-created');
|
|
482
|
+
if (createdEl) {
|
|
483
|
+
createdEl.textContent = node.created_at ? new Date(node.created_at).toLocaleString() : '-';
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Reset reasoning toggle
|
|
487
|
+
const reasoningArrow = document.getElementById('reasoning-arrow');
|
|
488
|
+
if (reasoningArrow) {
|
|
489
|
+
reasoningArrow.textContent = '▶';
|
|
490
|
+
}
|
|
491
|
+
document.getElementById('detail-reasoning').classList.add('hidden');
|
|
492
|
+
|
|
493
|
+
// Show loading state for similar decisions
|
|
494
|
+
const similarEl = document.getElementById('detail-similar');
|
|
495
|
+
if (similarEl) {
|
|
496
|
+
similarEl.innerHTML = '<span class="loading-similar">Searching...</span>';
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Show panel
|
|
500
|
+
panel.classList.add('visible');
|
|
501
|
+
|
|
502
|
+
// Fetch similar decisions
|
|
503
|
+
console.log('[MAMA] Fetching similar decisions...');
|
|
504
|
+
this.fetchSimilarDecisions(node.id);
|
|
505
|
+
console.log('[MAMA] showDetail completed successfully');
|
|
506
|
+
} catch (error) {
|
|
507
|
+
console.error('[MAMA] Error in showDetail:', error);
|
|
508
|
+
console.error('[MAMA] Error stack:', error.stack);
|
|
509
|
+
console.error('[MAMA] Node data:', node);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Get outcome icon name
|
|
515
|
+
*/
|
|
516
|
+
getOutcomeIcon(outcome) {
|
|
517
|
+
const outcomeMap = {
|
|
518
|
+
pending: 'clock',
|
|
519
|
+
success: 'check-circle',
|
|
520
|
+
failed: 'x-circle',
|
|
521
|
+
partial: 'alert-circle',
|
|
522
|
+
};
|
|
523
|
+
return outcomeMap[(outcome || 'pending').toLowerCase()] || 'clock';
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Toggle reasoning full text
|
|
528
|
+
*/
|
|
529
|
+
toggleReasoning() {
|
|
530
|
+
const arrow = document.getElementById('reasoning-arrow');
|
|
531
|
+
const content = document.getElementById('detail-reasoning');
|
|
532
|
+
if (arrow && content) {
|
|
533
|
+
const isHidden = content.classList.contains('hidden');
|
|
534
|
+
content.classList.toggle('hidden');
|
|
535
|
+
arrow.textContent = isHidden ? '▼' : '▶';
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Close detail panel
|
|
541
|
+
*/
|
|
542
|
+
closeDetail() {
|
|
543
|
+
const panel = document.getElementById('decision-detail-modal');
|
|
544
|
+
if (panel) {
|
|
545
|
+
panel.classList.remove('visible');
|
|
546
|
+
}
|
|
547
|
+
this.currentNodeId = null;
|
|
548
|
+
this.resetNodeHighlight();
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Fetch similar decisions
|
|
553
|
+
*/
|
|
554
|
+
async fetchSimilarDecisions(nodeId) {
|
|
555
|
+
console.log('[MAMA] fetchSimilarDecisions called for node:', nodeId);
|
|
556
|
+
const container = document.getElementById('detail-similar');
|
|
557
|
+
|
|
558
|
+
if (!container) {
|
|
559
|
+
console.warn('[MAMA] detail-similar element not found');
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
try {
|
|
564
|
+
console.log('[MAMA] Calling API.getSimilarDecisions...');
|
|
565
|
+
const data = await API.getSimilarDecisions(nodeId);
|
|
566
|
+
console.log('[MAMA] Similar decisions received:', data);
|
|
567
|
+
|
|
568
|
+
if (data.error) {
|
|
569
|
+
container.innerHTML = `<span style="color:#666">${data.message || 'Search failed'}</span>`;
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const similar = data.similar || [];
|
|
574
|
+
|
|
575
|
+
if (similar.length === 0) {
|
|
576
|
+
console.log('[MAMA] No similar decisions found');
|
|
577
|
+
container.innerHTML = '<span style="color:#666">No similar decisions found</span>';
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
console.log('[MAMA] Building similar decisions HTML for', similar.length, 'items');
|
|
582
|
+
const html = similar
|
|
583
|
+
.map(
|
|
584
|
+
(s) => `
|
|
585
|
+
<button class="w-full text-left p-2 mb-2 bg-gray-100 dark:bg-gray-800 hover:bg-indigo-100 dark:hover:bg-indigo-900/30 border border-gray-200 dark:border-gray-700 rounded-lg transition-colors" onclick="window.graphModule.navigateToNode('${s.id}')">
|
|
586
|
+
<div class="text-xs font-semibold text-indigo-600 dark:text-indigo-400">${escapeHtml(s.topic)}</div>
|
|
587
|
+
<div class="text-xs text-gray-600 dark:text-gray-400 mt-1 line-clamp-2">${escapeHtml((s.decision || '').substring(0, 80))}...</div>
|
|
588
|
+
<div class="text-xs text-gray-500 mt-1">${Math.round((s.similarity || 0) * 100)}% match</div>
|
|
589
|
+
</button>
|
|
590
|
+
`
|
|
591
|
+
)
|
|
592
|
+
.join('');
|
|
593
|
+
|
|
594
|
+
console.log('[MAMA] Setting similar decisions HTML...');
|
|
595
|
+
container.innerHTML = html;
|
|
596
|
+
console.log('[MAMA] fetchSimilarDecisions completed');
|
|
597
|
+
} catch (error) {
|
|
598
|
+
console.error('[MAMA] Failed to fetch similar decisions:', error);
|
|
599
|
+
console.error('[MAMA] Error stack:', error.stack);
|
|
600
|
+
container.innerHTML = '<span style="color:#f66">Failed to load</span>';
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Save outcome for current node
|
|
606
|
+
*/
|
|
607
|
+
async saveOutcome() {
|
|
608
|
+
const select = document.getElementById('detail-outcome-select');
|
|
609
|
+
const newOutcome = select.value;
|
|
610
|
+
|
|
611
|
+
if (!this.currentNodeId || !newOutcome) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
try {
|
|
616
|
+
await API.updateOutcome(this.currentNodeId, newOutcome);
|
|
617
|
+
|
|
618
|
+
// Update local data
|
|
619
|
+
const node = this.graphData.nodes.find((n) => n.id === this.currentNodeId);
|
|
620
|
+
if (node) {
|
|
621
|
+
node.outcome = newOutcome.toUpperCase();
|
|
622
|
+
|
|
623
|
+
// Update visualization
|
|
624
|
+
this.network.body.data.nodes.update({
|
|
625
|
+
id: this.currentNodeId,
|
|
626
|
+
color: {
|
|
627
|
+
border: this.getOutcomeBorderColor(newOutcome),
|
|
628
|
+
},
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// Refresh detail panel
|
|
632
|
+
this.showDetail(node);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
console.log('[MAMA] Outcome updated:', this.currentNodeId, newOutcome);
|
|
636
|
+
} catch (error) {
|
|
637
|
+
console.error('[MAMA] Failed to update outcome:', error);
|
|
638
|
+
alert('Failed to update outcome: ' + error.message);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// =============================================
|
|
643
|
+
// Navigation
|
|
644
|
+
// =============================================
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Navigate to specific node
|
|
648
|
+
*/
|
|
649
|
+
async navigateToNode(nodeId) {
|
|
650
|
+
if (!this.network) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Try exact match first
|
|
655
|
+
let node = this.graphData.nodes.find((n) => n.id === nodeId);
|
|
656
|
+
|
|
657
|
+
// If not found, try partial match (for short IDs from checkpoints)
|
|
658
|
+
if (!node) {
|
|
659
|
+
console.log('[MAMA] Exact match not found, trying partial match for:', nodeId);
|
|
660
|
+
node = this.graphData.nodes.find((n) => n.id.startsWith(nodeId));
|
|
661
|
+
|
|
662
|
+
if (node) {
|
|
663
|
+
console.log('[MAMA] Found node via partial match:', node.id);
|
|
664
|
+
nodeId = node.id; // Update nodeId to the full ID
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// If node not in current graph, reload without filters
|
|
669
|
+
if (!node) {
|
|
670
|
+
console.log('[MAMA] Node not in current graph, reloading all nodes...');
|
|
671
|
+
|
|
672
|
+
// Reset topic filter
|
|
673
|
+
const topicFilter = document.getElementById('topic-filter');
|
|
674
|
+
if (topicFilter) {
|
|
675
|
+
topicFilter.value = '';
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Reload graph: fetch fresh data and reinitialize
|
|
679
|
+
await this.fetchData();
|
|
680
|
+
this.init(this.graphData);
|
|
681
|
+
|
|
682
|
+
// Try exact match again
|
|
683
|
+
node = this.graphData.nodes.find((n) => n.id === nodeId);
|
|
684
|
+
|
|
685
|
+
// If still not found, try partial match
|
|
686
|
+
if (!node) {
|
|
687
|
+
node = this.graphData.nodes.find((n) => n.id.startsWith(nodeId));
|
|
688
|
+
if (node) {
|
|
689
|
+
console.log('[MAMA] Found node via partial match after reload:', node.id);
|
|
690
|
+
nodeId = node.id;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (!node) {
|
|
695
|
+
console.warn('[MAMA] Node not found even after reload:', nodeId);
|
|
696
|
+
showToast('⚠️ Decision not found in graph');
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Focus on node
|
|
702
|
+
this.network.focus(nodeId, {
|
|
703
|
+
scale: 1.5,
|
|
704
|
+
animation: { duration: 500, easingFunction: 'easeInOutQuad' },
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
// Select node (triggers click event)
|
|
708
|
+
this.network.selectNodes([nodeId]);
|
|
709
|
+
|
|
710
|
+
// Show detail
|
|
711
|
+
this.showDetail(node);
|
|
712
|
+
this.highlightConnectedNodes(nodeId);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Get connected edge types
|
|
717
|
+
*/
|
|
718
|
+
getConnectedEdges(nodeId) {
|
|
719
|
+
const edges = this.graphData.edges.filter((e) => e.from === nodeId || e.to === nodeId);
|
|
720
|
+
|
|
721
|
+
const outgoing = edges.filter((e) => e.from === nodeId);
|
|
722
|
+
const incoming = edges.filter((e) => e.to === nodeId);
|
|
723
|
+
|
|
724
|
+
return { outgoing, incoming, all: edges };
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// =============================================
|
|
728
|
+
// Filtering
|
|
729
|
+
// =============================================
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Populate topic filter dropdown
|
|
733
|
+
*/
|
|
734
|
+
populateTopicFilter(topics) {
|
|
735
|
+
const select = document.getElementById('topic-filter');
|
|
736
|
+
if (!select) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
select.innerHTML = '<option value="">All Topics</option>';
|
|
741
|
+
topics.forEach((topic) => {
|
|
742
|
+
const option = document.createElement('option');
|
|
743
|
+
option.value = topic;
|
|
744
|
+
option.textContent = topic;
|
|
745
|
+
select.appendChild(option);
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Filter by topic
|
|
751
|
+
*/
|
|
752
|
+
filterByTopic(topic) {
|
|
753
|
+
if (!this.network) {
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
const allNodes = this.network.body.data.nodes.get();
|
|
758
|
+
|
|
759
|
+
if (!topic) {
|
|
760
|
+
// Show all
|
|
761
|
+
allNodes.forEach((node) => {
|
|
762
|
+
this.network.body.data.nodes.update({
|
|
763
|
+
id: node.id,
|
|
764
|
+
hidden: false,
|
|
765
|
+
});
|
|
766
|
+
});
|
|
767
|
+
} else {
|
|
768
|
+
// Filter
|
|
769
|
+
allNodes.forEach((node) => {
|
|
770
|
+
const nodeData = this.graphData.nodes.find((n) => n.id === node.id);
|
|
771
|
+
this.network.body.data.nodes.update({
|
|
772
|
+
id: node.id,
|
|
773
|
+
hidden: nodeData?.topic !== topic,
|
|
774
|
+
});
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
console.log('[MAMA] Filtered by topic:', topic || 'all');
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Filter by outcome
|
|
783
|
+
*/
|
|
784
|
+
filterByOutcome(outcome) {
|
|
785
|
+
if (!this.network) {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
const allNodes = this.network.body.data.nodes.get();
|
|
790
|
+
|
|
791
|
+
if (!outcome) {
|
|
792
|
+
// Show all
|
|
793
|
+
allNodes.forEach((node) => {
|
|
794
|
+
this.network.body.data.nodes.update({
|
|
795
|
+
id: node.id,
|
|
796
|
+
hidden: false,
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
} else {
|
|
800
|
+
// Filter by outcome
|
|
801
|
+
allNodes.forEach((node) => {
|
|
802
|
+
const nodeData = this.graphData.nodes.find((n) => n.id === node.id);
|
|
803
|
+
const nodeOutcome = (nodeData?.outcome || 'pending').toLowerCase();
|
|
804
|
+
this.network.body.data.nodes.update({
|
|
805
|
+
id: node.id,
|
|
806
|
+
hidden: nodeOutcome !== outcome.toLowerCase(),
|
|
807
|
+
});
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
console.log('[MAMA] Filtered by outcome:', outcome || 'all');
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Clear all filters
|
|
816
|
+
*/
|
|
817
|
+
clearFilters() {
|
|
818
|
+
if (!this.network) {
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// Show all nodes
|
|
823
|
+
const allNodes = this.network.body.data.nodes.get();
|
|
824
|
+
allNodes.forEach((node) => {
|
|
825
|
+
this.network.body.data.nodes.update({
|
|
826
|
+
id: node.id,
|
|
827
|
+
hidden: false,
|
|
828
|
+
opacity: 1.0,
|
|
829
|
+
font: { ...node.font, color: '#fff' },
|
|
830
|
+
});
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
// Show all edges
|
|
834
|
+
const allEdges = this.network.body.data.edges.get();
|
|
835
|
+
allEdges.forEach((edge) => {
|
|
836
|
+
this.network.body.data.edges.update({
|
|
837
|
+
id: edge.id,
|
|
838
|
+
opacity: 1.0,
|
|
839
|
+
});
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
// Clear search state
|
|
843
|
+
this.searchMatches = [];
|
|
844
|
+
this.currentSearchIndex = 0;
|
|
845
|
+
|
|
846
|
+
const countEl = document.getElementById('search-count');
|
|
847
|
+
if (countEl) {
|
|
848
|
+
countEl.style.display = 'none';
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
console.log('[MAMA] All filters cleared');
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// =============================================
|
|
855
|
+
// Search
|
|
856
|
+
// =============================================
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Perform search
|
|
860
|
+
*/
|
|
861
|
+
search() {
|
|
862
|
+
const query = document.getElementById('search-input').value.trim().toLowerCase();
|
|
863
|
+
|
|
864
|
+
if (!query) {
|
|
865
|
+
this.clearSearch();
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Search in topic, decision, and reasoning
|
|
870
|
+
this.searchMatches = this.graphData.nodes.filter(
|
|
871
|
+
(node) =>
|
|
872
|
+
(node.topic || '').toLowerCase().includes(query) ||
|
|
873
|
+
(node.decision || '').toLowerCase().includes(query) ||
|
|
874
|
+
(node.reasoning || '').toLowerCase().includes(query)
|
|
875
|
+
);
|
|
876
|
+
|
|
877
|
+
this.currentSearchIndex = 0;
|
|
878
|
+
this.updateSearchResults();
|
|
879
|
+
|
|
880
|
+
if (this.searchMatches.length > 0) {
|
|
881
|
+
this.highlightSearchResults();
|
|
882
|
+
this.navigateToNode(this.searchMatches[0].id);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
console.log('[MAMA] Search:', query, '- Found', this.searchMatches.length, 'matches');
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* Handle search input
|
|
890
|
+
*/
|
|
891
|
+
handleSearchInput(event) {
|
|
892
|
+
if (event.key === 'Enter' && this.searchMatches.length > 0) {
|
|
893
|
+
this.nextSearchResult();
|
|
894
|
+
} else {
|
|
895
|
+
this.debouncedSearch();
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* Navigate to next search result
|
|
901
|
+
*/
|
|
902
|
+
nextSearchResult() {
|
|
903
|
+
if (this.searchMatches.length === 0) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
this.currentSearchIndex = (this.currentSearchIndex + 1) % this.searchMatches.length;
|
|
908
|
+
this.navigateToNode(this.searchMatches[this.currentSearchIndex].id);
|
|
909
|
+
this.updateSearchResults();
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Navigate to previous search result
|
|
914
|
+
*/
|
|
915
|
+
prevSearchResult() {
|
|
916
|
+
if (this.searchMatches.length === 0) {
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
this.currentSearchIndex =
|
|
921
|
+
(this.currentSearchIndex - 1 + this.searchMatches.length) % this.searchMatches.length;
|
|
922
|
+
this.navigateToNode(this.searchMatches[this.currentSearchIndex].id);
|
|
923
|
+
this.updateSearchResults();
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Update search count display
|
|
928
|
+
*/
|
|
929
|
+
updateSearchResults() {
|
|
930
|
+
const countEl = document.getElementById('search-count');
|
|
931
|
+
if (!countEl) {
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
if (this.searchMatches.length > 0) {
|
|
936
|
+
countEl.textContent = `${this.currentSearchIndex + 1} / ${this.searchMatches.length}`;
|
|
937
|
+
countEl.style.display = 'inline';
|
|
938
|
+
} else {
|
|
939
|
+
countEl.textContent = 'No results';
|
|
940
|
+
countEl.style.display = 'inline';
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* Highlight search results
|
|
946
|
+
*/
|
|
947
|
+
highlightSearchResults() {
|
|
948
|
+
if (!this.network) {
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
const matchIds = this.searchMatches.map((n) => n.id);
|
|
953
|
+
const allNodes = this.network.body.data.nodes.get();
|
|
954
|
+
|
|
955
|
+
allNodes.forEach((node) => {
|
|
956
|
+
const isMatch = matchIds.includes(node.id);
|
|
957
|
+
this.network.body.data.nodes.update({
|
|
958
|
+
id: node.id,
|
|
959
|
+
opacity: isMatch ? 1.0 : 0.2,
|
|
960
|
+
font: { ...node.font, color: isMatch ? '#fff' : '#666' },
|
|
961
|
+
});
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* Clear search
|
|
967
|
+
*/
|
|
968
|
+
clearSearch() {
|
|
969
|
+
this.searchMatches = [];
|
|
970
|
+
this.currentSearchIndex = 0;
|
|
971
|
+
this.resetNodeHighlight();
|
|
972
|
+
|
|
973
|
+
const countEl = document.getElementById('search-count');
|
|
974
|
+
if (countEl) {
|
|
975
|
+
countEl.style.display = 'none';
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Open search panel
|
|
981
|
+
*/
|
|
982
|
+
openSearch() {
|
|
983
|
+
const searchContainer = document.getElementById('search-container');
|
|
984
|
+
const searchInput = document.getElementById('search-input');
|
|
985
|
+
|
|
986
|
+
searchContainer.style.display = 'flex';
|
|
987
|
+
searchInput.focus();
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* Close search panel
|
|
992
|
+
*/
|
|
993
|
+
closeSearch() {
|
|
994
|
+
document.getElementById('search-container').style.display = 'none';
|
|
995
|
+
this.clearSearch();
|
|
996
|
+
}
|
|
997
|
+
}
|