@abhinav2203/codeflow-canvas 0.1.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 (134) hide show
  1. package/abhinav2203-codeflow-canvas-0.1.0.tgz +0 -0
  2. package/dist/bin/cli.d.ts +3 -0
  3. package/dist/bin/cli.d.ts.map +1 -0
  4. package/dist/bin/cli.js +84 -0
  5. package/dist/bin/cli.js.map +1 -0
  6. package/dist/components/blueprint-workbench.d.ts +2 -0
  7. package/dist/components/blueprint-workbench.d.ts.map +1 -0
  8. package/dist/components/blueprint-workbench.js +144 -0
  9. package/dist/components/blueprint-workbench.js.map +1 -0
  10. package/dist/components/code-diff-editor.d.ts +12 -0
  11. package/dist/components/code-diff-editor.d.ts.map +1 -0
  12. package/dist/components/code-diff-editor.js +39 -0
  13. package/dist/components/code-diff-editor.js.map +1 -0
  14. package/dist/components/code-editor.d.ts +25 -0
  15. package/dist/components/code-editor.d.ts.map +1 -0
  16. package/dist/components/code-editor.js +264 -0
  17. package/dist/components/code-editor.js.map +1 -0
  18. package/dist/components/file-tabs.d.ts +5 -0
  19. package/dist/components/file-tabs.d.ts.map +1 -0
  20. package/dist/components/file-tabs.js +164 -0
  21. package/dist/components/file-tabs.js.map +1 -0
  22. package/dist/components/file-tree.d.ts +7 -0
  23. package/dist/components/file-tree.d.ts.map +1 -0
  24. package/dist/components/file-tree.js +176 -0
  25. package/dist/components/file-tree.js.map +1 -0
  26. package/dist/components/graph-canvas.d.ts +25 -0
  27. package/dist/components/graph-canvas.d.ts.map +1 -0
  28. package/dist/components/graph-canvas.js +224 -0
  29. package/dist/components/graph-canvas.js.map +1 -0
  30. package/dist/components/ide-layout.d.ts +10 -0
  31. package/dist/components/ide-layout.d.ts.map +1 -0
  32. package/dist/components/ide-layout.js +40 -0
  33. package/dist/components/ide-layout.js.map +1 -0
  34. package/dist/components/ide-workbench.d.ts +4 -0
  35. package/dist/components/ide-workbench.d.ts.map +1 -0
  36. package/dist/components/ide-workbench.js +6 -0
  37. package/dist/components/ide-workbench.js.map +1 -0
  38. package/dist/components/index.d.ts +13 -0
  39. package/dist/components/index.d.ts.map +1 -0
  40. package/dist/components/index.js +13 -0
  41. package/dist/components/index.js.map +1 -0
  42. package/dist/components/monaco-setup.d.ts +4 -0
  43. package/dist/components/monaco-setup.d.ts.map +1 -0
  44. package/dist/components/monaco-setup.js +34 -0
  45. package/dist/components/monaco-setup.js.map +1 -0
  46. package/dist/components/opencode-settings.d.ts +8 -0
  47. package/dist/components/opencode-settings.d.ts.map +1 -0
  48. package/dist/components/opencode-settings.js +33 -0
  49. package/dist/components/opencode-settings.js.map +1 -0
  50. package/dist/components/policy-workbench.d.ts +2 -0
  51. package/dist/components/policy-workbench.d.ts.map +1 -0
  52. package/dist/components/policy-workbench.js +102 -0
  53. package/dist/components/policy-workbench.js.map +1 -0
  54. package/dist/components/ts-language-service.d.ts +14 -0
  55. package/dist/components/ts-language-service.d.ts.map +1 -0
  56. package/dist/components/ts-language-service.js +123 -0
  57. package/dist/components/ts-language-service.js.map +1 -0
  58. package/dist/index.d.ts +23 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +22 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/lib/browser/storage.d.ts +16 -0
  63. package/dist/lib/browser/storage.d.ts.map +1 -0
  64. package/dist/lib/browser/storage.js +18 -0
  65. package/dist/lib/browser/storage.js.map +1 -0
  66. package/dist/lib/edit.d.ts +14 -0
  67. package/dist/lib/edit.d.ts.map +1 -0
  68. package/dist/lib/edit.js +57 -0
  69. package/dist/lib/edit.js.map +1 -0
  70. package/dist/lib/flow-view.d.ts +80 -0
  71. package/dist/lib/flow-view.d.ts.map +1 -0
  72. package/dist/lib/flow-view.js +850 -0
  73. package/dist/lib/flow-view.js.map +1 -0
  74. package/dist/lib/heatmap.d.ts +28 -0
  75. package/dist/lib/heatmap.d.ts.map +1 -0
  76. package/dist/lib/heatmap.js +61 -0
  77. package/dist/lib/heatmap.js.map +1 -0
  78. package/dist/lib/index.d.ts +9 -0
  79. package/dist/lib/index.d.ts.map +1 -0
  80. package/dist/lib/index.js +6 -0
  81. package/dist/lib/index.js.map +1 -0
  82. package/dist/lib/node-navigation.d.ts +36 -0
  83. package/dist/lib/node-navigation.d.ts.map +1 -0
  84. package/dist/lib/node-navigation.js +52 -0
  85. package/dist/lib/node-navigation.js.map +1 -0
  86. package/dist/lib/traces.d.ts +3 -0
  87. package/dist/lib/traces.d.ts.map +1 -0
  88. package/dist/lib/traces.js +64 -0
  89. package/dist/lib/traces.js.map +1 -0
  90. package/dist/lib/types.d.ts +57 -0
  91. package/dist/lib/types.d.ts.map +1 -0
  92. package/dist/lib/types.js +7 -0
  93. package/dist/lib/types.js.map +1 -0
  94. package/dist/store/blueprint-store.d.ts +35 -0
  95. package/dist/store/blueprint-store.d.ts.map +1 -0
  96. package/dist/store/blueprint-store.js +79 -0
  97. package/dist/store/blueprint-store.js.map +1 -0
  98. package/dist/store/index.d.ts +3 -0
  99. package/dist/store/index.d.ts.map +1 -0
  100. package/dist/store/index.js +2 -0
  101. package/dist/store/index.js.map +1 -0
  102. package/package.json +52 -0
  103. package/scripts/wrap-cli.mjs +15 -0
  104. package/src/bin/cli.ts +128 -0
  105. package/src/components/blueprint-workbench.tsx +305 -0
  106. package/src/components/code-diff-editor.tsx +80 -0
  107. package/src/components/code-editor.tsx +389 -0
  108. package/src/components/file-tabs.tsx +288 -0
  109. package/src/components/file-tree.tsx +301 -0
  110. package/src/components/graph-canvas.tsx +404 -0
  111. package/src/components/ide-layout.tsx +104 -0
  112. package/src/components/ide-workbench.tsx +5 -0
  113. package/src/components/index.ts +12 -0
  114. package/src/components/monaco-setup.ts +67 -0
  115. package/src/components/opencode-settings.tsx +82 -0
  116. package/src/components/policy-workbench.tsx +233 -0
  117. package/src/components/ts-language-service.ts +170 -0
  118. package/src/index.ts +54 -0
  119. package/src/lib/browser/storage.ts +19 -0
  120. package/src/lib/edit.ts +74 -0
  121. package/src/lib/flow-view.ts +1176 -0
  122. package/src/lib/heatmap.ts +103 -0
  123. package/src/lib/index.ts +41 -0
  124. package/src/lib/node-navigation.ts +76 -0
  125. package/src/lib/traces.ts +79 -0
  126. package/src/lib/types.ts +79 -0
  127. package/src/store/blueprint-store.ts +136 -0
  128. package/src/store/index.ts +2 -0
  129. package/test-fixtures/minimal-blueprint.json +34 -0
  130. package/test-fixtures/sample-blueprint.json +184 -0
  131. package/tsconfig.build.json +9 -0
  132. package/tsconfig.json +22 -0
  133. package/tsconfig.tsbuildinfo +1 -0
  134. package/vitest.config.ts +9 -0
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@abhinav2203/codeflow-canvas",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "React Flow graph canvas with Monaco code editors and IDE layout components",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./flow-view": { "types": "./dist/flow-view/index.d.ts", "default": "./dist/flow-view/index.js" },
15
+ "./edit": { "types": "./dist/edit/index.d.ts", "default": "./dist/edit/index.js" },
16
+ "./traces": { "types": "./dist/traces/index.d.ts", "default": "./dist/traces/index.js" },
17
+ "./heatmap": { "types": "./dist/heatmap/index.d.ts", "default": "./dist/heatmap/index.js" },
18
+ "./store": { "types": "./dist/store/index.d.ts", "default": "./dist/store/index.js" }
19
+ },
20
+ "bin": {
21
+ "codeflow-canvas": "./dist/bin/cli.js"
22
+ },
23
+ "scripts": {
24
+ "check": "tsc --noEmit",
25
+ "test": "vitest run",
26
+ "build": "tsc --build && node scripts/wrap-cli.mjs",
27
+ "clean": "rm -rf dist"
28
+ },
29
+ "dependencies": {
30
+ "@abhinav2203/codeflow-core": "^1.1.6",
31
+ "@xyflow/react": "^12.0.0",
32
+ "@monaco-editor/react": "^4.0.0",
33
+ "monaco-editor": "^0.52.0",
34
+ "react": "^18.0.0",
35
+ "react-dom": "^18.0.0",
36
+ "react-rnd": "^10.5.3",
37
+ "zustand": "^5.0.0",
38
+ "dotenv": "^16.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^22.0.0",
42
+ "@types/react": "^18.0.0",
43
+ "next": "^16.0.0",
44
+ "typescript": "^5.7.0",
45
+ "vitest": "^3.0.0"
46
+ },
47
+ "peerDependencies": {
48
+ "next": "^16.0.0",
49
+ "react": "^18.0.0",
50
+ "react-dom": "^18.0.0"
51
+ }
52
+ }
@@ -0,0 +1,15 @@
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ import { resolve, dirname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const cliSrc = resolve(__dirname, "../bin/cli.js");
7
+ const cliDest = resolve(__dirname, "../dist/bin/cli.js");
8
+
9
+ try {
10
+ const content = readFileSync(cliSrc, "utf-8");
11
+ writeFileSync(cliDest, content, "utf-8");
12
+ console.log("CLI wrapper created at dist/bin/cli.js");
13
+ } catch {
14
+ // If the compiled CLI doesn't exist yet, that's fine for the build step
15
+ }
package/src/bin/cli.ts ADDED
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { resolve, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+
8
+ interface RawLog {
9
+ level: string;
10
+ msg: string;
11
+ time?: string;
12
+ [key: string]: unknown;
13
+ }
14
+
15
+ interface RawSpan {
16
+ spanId: string;
17
+ name: string;
18
+ status: string;
19
+ runtime: string;
20
+ timestamp: string;
21
+ }
22
+
23
+ interface RenderOptions {
24
+ format?: "json" | "text";
25
+ nodeId?: string;
26
+ }
27
+
28
+ function renderJson(graph: BlueprintGraph, options: RenderOptions): string {
29
+ if (options.nodeId) {
30
+ const node = graph.nodes.find((n) => n.id === options.nodeId);
31
+ if (!node) {
32
+ return JSON.stringify({ error: `Node ${options.nodeId} not found` }, null, 2);
33
+ }
34
+ return JSON.stringify(node, null, 2);
35
+ }
36
+ return JSON.stringify(graph, null, 2);
37
+ }
38
+
39
+ function renderText(graph: BlueprintGraph): string {
40
+ const lines: string[] = [];
41
+ lines.push(`# ${graph.projectName}`);
42
+ lines.push(`Phase: ${graph.phase}`);
43
+ lines.push("");
44
+ lines.push("## Nodes");
45
+ for (const node of graph.nodes) {
46
+ lines.push(`- [${node.kind}] ${node.name}: ${node.summary}`);
47
+ }
48
+ lines.push("");
49
+ lines.push("## Edges");
50
+ for (const edge of graph.edges) {
51
+ lines.push(`- ${edge.from} --[${edge.kind}]--> ${edge.to}`);
52
+ }
53
+ return lines.join("\n");
54
+ }
55
+
56
+ interface BlueprintGraph {
57
+ projectName: string;
58
+ phase: string;
59
+ nodes: Array<{
60
+ id: string;
61
+ kind: string;
62
+ name: string;
63
+ summary: string;
64
+ [key: string]: unknown;
65
+ }>;
66
+ edges: Array<{
67
+ from: string;
68
+ to: string;
69
+ kind: string;
70
+ label?: string;
71
+ [key: string]: unknown;
72
+ }>;
73
+ workflows: Array<{ id: string; name: string; [key: string]: unknown }>;
74
+ }
75
+
76
+ async function main() {
77
+ const args = process.argv.slice(2);
78
+ let filePath: string | undefined;
79
+ let command = "render";
80
+ const options: RenderOptions = { format: "json" };
81
+
82
+ for (let i = 0; i < args.length; i++) {
83
+ if (args[i] === "render" && i + 1 < args.length) {
84
+ command = "render";
85
+ filePath = args[++i];
86
+ } else if (args[i] === "--format" && i + 1 < args.length) {
87
+ options.format = args[++i] as "json" | "text";
88
+ } else if (args[i] === "--node" && i + 1 < args.length) {
89
+ options.nodeId = args[++i];
90
+ } else if (!args[i].startsWith("--")) {
91
+ filePath = args[i];
92
+ }
93
+ }
94
+
95
+ if (!filePath) {
96
+ console.error("Usage: codeflow-canvas render <file> [--format json|text] [--node <id>]");
97
+ process.exit(1);
98
+ }
99
+
100
+ const resolvedPath = resolve(filePath);
101
+ let graph: BlueprintGraph;
102
+
103
+ try {
104
+ const content = readFileSync(resolvedPath, "utf-8");
105
+ graph = JSON.parse(content);
106
+ } catch {
107
+ console.error(`Failed to read or parse blueprint file: ${resolvedPath}`);
108
+ process.exit(1);
109
+ }
110
+
111
+ switch (command) {
112
+ case "render":
113
+ if (options.format === "text") {
114
+ console.log(renderText(graph));
115
+ } else {
116
+ console.log(renderJson(graph, options));
117
+ }
118
+ break;
119
+ default:
120
+ console.error(`Unknown command: ${command}`);
121
+ process.exit(1);
122
+ }
123
+ }
124
+
125
+ main().catch((err) => {
126
+ console.error("CLI error:", err);
127
+ process.exit(1);
128
+ });
@@ -0,0 +1,305 @@
1
+ "use client";
2
+
3
+ import type { QueryResult } from "@abhinav2203/coderag";
4
+ import { z } from "zod";
5
+
6
+ import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
7
+
8
+ import { useBlueprintStore } from "../store/blueprint-store.js";
9
+
10
+ import { CodeEditor } from "./code-editor.js";
11
+ import { FileTabs } from "./file-tabs.js";
12
+ import { FileTree } from "./file-tree.js";
13
+ import { GraphCanvas } from "./graph-canvas.js";
14
+ import { IdeLayout } from "./ide-layout.js";
15
+ import { buildDetailFlow, indexRuntimeExecutionResult } from "../lib/flow-view.js";
16
+ import { computeHeatmap } from "../lib/heatmap.js";
17
+ import type { HeatmapData } from "../lib/heatmap.js";
18
+ import { formatNavigationTarget, getNavigationTarget, isValidNavigationTarget } from "../lib/node-navigation.js";
19
+ import { applyTraceOverlay } from "../lib/traces.js";
20
+ import type { CycleReport, SmellReport, GraphMetrics, RefactorReport, HealResult, OpencodeServerInfo } from "../lib/types.js";
21
+ import {
22
+ AUTO_IMPLEMENT_STORAGE_KEY,
23
+ LIVE_COMPLETIONS_STORAGE_KEY,
24
+ loadSessionApiKey,
25
+ readLocalBooleanPreference,
26
+ readRepoPath,
27
+ storeSessionApiKey,
28
+ writeLocalBooleanPreference,
29
+ writeRepoPath
30
+ } from "../lib/browser/storage.js";
31
+ import type {
32
+ ApprovalRecord,
33
+ BlueprintGraph,
34
+ BlueprintNode,
35
+ BranchDiff,
36
+ ConflictReport,
37
+ DigitalTwinSnapshot,
38
+ ExecutionMode,
39
+ ExportResult,
40
+ ExecutionArtifact,
41
+ ExecutionStep,
42
+ GhostNode,
43
+ GraphBranch,
44
+ McpServerConfig,
45
+ McpTool,
46
+ ObservabilityLog,
47
+ PersistedSession,
48
+ RiskReport,
49
+ RunPlan,
50
+ RuntimeExecutionResult,
51
+ TournamentResult,
52
+ VcrRecording
53
+ } from "@abhinav2203/codeflow-core/schema";
54
+ import { emptyContract, traceSpanSchema } from "@abhinav2203/codeflow-core/schema";
55
+
56
+ type BuildResponse = {
57
+ graph?: BlueprintGraph;
58
+ runPlan?: RunPlan;
59
+ session?: PersistedSession;
60
+ error?: string;
61
+ };
62
+
63
+ type ExportResponse = {
64
+ result?: ExportResult;
65
+ runPlan?: RunPlan;
66
+ riskReport?: RiskReport;
67
+ session?: PersistedSession;
68
+ approval?: ApprovalRecord;
69
+ requiresApproval?: boolean;
70
+ error?: string;
71
+ };
72
+
73
+ type ExecutionResponse = {
74
+ result?: RuntimeExecutionResult;
75
+ executedNodeId?: string;
76
+ graph?: BlueprintGraph;
77
+ runPlan?: RunPlan;
78
+ session?: PersistedSession;
79
+ error?: string;
80
+ };
81
+
82
+ type ObservabilityLatestResponse = {
83
+ graph?: BlueprintGraph | null;
84
+ latestSpans?: Array<{ spanId: string; name: string; status: string; runtime: string; provenance?: string }>;
85
+ latestLogs?: ObservabilityLog[];
86
+ error?: string;
87
+ };
88
+
89
+ type ConflictResponse = {
90
+ report?: ConflictReport;
91
+ error?: string;
92
+ };
93
+
94
+ type CyclesResponse = {
95
+ report?: CycleReport;
96
+ error?: string;
97
+ };
98
+
99
+ type SmellsResponse = {
100
+ report?: SmellReport;
101
+ error?: string;
102
+ };
103
+
104
+ type MetricsResponse = {
105
+ metrics?: GraphMetrics;
106
+ error?: string;
107
+ };
108
+
109
+ const tracesSchema = z.array(traceSpanSchema);
110
+
111
+ const maskApiKey = (value: string) => {
112
+ const trimmed = value.trim();
113
+ if (trimmed.length <= 8) {
114
+ return trimmed;
115
+ }
116
+
117
+ return `${trimmed.slice(0, 4)}...${trimmed.slice(-4)}`;
118
+ };
119
+
120
+ type StatusTone = "info" | "success" | "danger";
121
+ type IdeDockTab = "terminal" | "repo" | "heatmap" | "vcr" | "traces" | "problems";
122
+ type ActivityEntryTone = "info" | "success" | "error" | "command";
123
+ type ActivityEntry = {
124
+ id: string;
125
+ source: string;
126
+ message: string;
127
+ tone: ActivityEntryTone;
128
+ timestamp: string;
129
+ detail?: string;
130
+ };
131
+
132
+ export function BlueprintWorkbench() {
133
+ const {
134
+ activeFile,
135
+ floatingGraph,
136
+ graph,
137
+ openFiles,
138
+ repoPath,
139
+ selectedNodeId,
140
+ setActiveFile,
141
+ setFloatingGraph,
142
+ setGraph,
143
+ setOpenFiles,
144
+ setRepoPath,
145
+ setSelectedNodeId
146
+ } = useBlueprintStore();
147
+
148
+ const MIN_OBSERVABILITY_INTERVAL_SECS = 2;
149
+ const [projectName, setProjectName] = useState("CodeFlow Workspace");
150
+ const [prdText, setPrdText] = useState("");
151
+ const [aiPrompt, setAiPrompt] = useState("");
152
+ const [nvidiaApiKey, setNvidiaApiKey] = useState("");
153
+ const [executionMode, setExecutionMode] = useState<ExecutionMode>("essential");
154
+ const [outputDir, setOutputDir] = useState("");
155
+ const [traceInput, setTraceInput] = useState("");
156
+ const [runInput, setRunInput] = useState("{}");
157
+ const [error, setError] = useState<string | null>(null);
158
+ const [busyLabel, setBusyLabel] = useState<string | null>(null);
159
+ const [exportResult, setExportResult] = useState<ExportResult | null>(null);
160
+ const [runPlan, setRunPlan] = useState<RunPlan | null>(null);
161
+ const [riskReport, setRiskReport] = useState<RiskReport | null>(null);
162
+ const [session, setSession] = useState<PersistedSession | null>(null);
163
+ const [pendingApproval, setPendingApproval] = useState<ApprovalRecord | null>(null);
164
+ const [executionResult, setExecutionResult] = useState<RuntimeExecutionResult | null>(null);
165
+ const [latestLogs, setLatestLogs] = useState<ObservabilityLog[]>([]);
166
+ const [latestSpans, setLatestSpans] = useState<ObservabilityLatestResponse["latestSpans"]>([]);
167
+ const [conflictReport, setConflictReport] = useState<ConflictReport | null>(null);
168
+ const [newNodeName, setNewNodeName] = useState("");
169
+ const [newNodeKind, setNewNodeKind] = useState<BlueprintNode["kind"]>("function");
170
+ const [edgeFrom, setEdgeFrom] = useState("");
171
+ const [edgeTo, setEdgeTo] = useState("");
172
+ const [edgeKind, setEdgeKind] = useState<"calls" | "imports" | "inherits">("calls");
173
+ const [useAI, setUseAI] = useState(true);
174
+ const [drilldownStack, setDrilldownStack] = useState<string[]>([]);
175
+ const [selectedDetailNodeId, setSelectedDetailNodeId] = useState<string | null>(null);
176
+ const [codeDrafts, setCodeDrafts] = useState<Record<string, string>>({});
177
+ const [suggestionInstruction, setSuggestionInstruction] = useState("");
178
+ const [codeSuggestion, setCodeSuggestion] = useState<{ summary: string; code: string; notes: string[] } | null>(null);
179
+ const [liveCompletionsEnabled, setLiveCompletionsEnabled] = useState(true);
180
+ const [serverApiKeyConfigured, setServerApiKeyConfigured] = useState(false);
181
+ const [apiKeyStatusLoaded, setApiKeyStatusLoaded] = useState(false);
182
+ const [statusTitle, setStatusTitle] = useState("Ready to build");
183
+ const [statusDetail, setStatusDetail] = useState(
184
+ "Enter a project description or repo input, then build a blueprint."
185
+ );
186
+ const [statusTone, setStatusTone] = useState<StatusTone>("info");
187
+ const [activeDockTab, setActiveDockTab] = useState<IdeDockTab>("terminal");
188
+ const [activityFeed, setActivityFeed] = useState<ActivityEntry[]>([]);
189
+ const [showSettings, setShowSettings] = useState(false);
190
+ const [showPromptPanel, setShowPromptPanel] = useState(true);
191
+ const [showEditPanel, setShowEditPanel] = useState(false);
192
+ const [showInspector, setShowInspector] = useState(false);
193
+ const [showObservabilityPanel, setShowObservabilityPanel] = useState(false);
194
+ const [autoObservability, setAutoObservability] = useState(false);
195
+ const [observabilityIntervalSecs, setObservabilityIntervalSecs] = useState(5);
196
+ const autoObsRef = useRef(autoObservability);
197
+ autoObsRef.current = autoObservability;
198
+ const [autoImplementNodes, setAutoImplementNodes] = useState(false);
199
+ const [cycleReport, setCycleReport] = useState<CycleReport | null>(null);
200
+ const [smellReport, setSmellReport] = useState<SmellReport | null>(null);
201
+ const [graphMetrics, setGraphMetrics] = useState<GraphMetrics | null>(null);
202
+ const [mermaidDiagram, setMermaidDiagram] = useState<string | null>(null);
203
+ const [ghostSuggestions, setGhostSuggestions] = useState<GhostNode[]>([]);
204
+ const [showMcpPanel, setShowMcpPanel] = useState(false);
205
+ const [mcpServerUrl, setMcpServerUrl] = useState("");
206
+ const [mcpHeadersJson, setMcpHeadersJson] = useState("{}");
207
+ const [mcpToolName, setMcpToolName] = useState("");
208
+ const [mcpToolArgsJson, setMcpToolArgsJson] = useState("{}");
209
+ const [availableMcpTools, setAvailableMcpTools] = useState<McpTool[]>([]);
210
+ const [mcpInvokeResult, setMcpInvokeResult] = useState<string | null>(null);
211
+ const [mcpError, setMcpError] = useState<string | null>(null);
212
+
213
+ const [branches, setBranches] = useState<GraphBranch[]>([]);
214
+ const [showBranchPanel, setShowBranchPanel] = useState(false);
215
+ const [newBranchName, setNewBranchName] = useState("");
216
+ const [newBranchDescription, setNewBranchDescription] = useState("");
217
+ const [activeBranchId, setActiveBranchId] = useState<string | null>(null);
218
+ const [branchDiff, setBranchDiff] = useState<BranchDiff | null>(null);
219
+ const [diffTargetBranchId, setDiffTargetBranchId] = useState<string | null>(null);
220
+
221
+ const [showVcrPanel, setShowVcrPanel] = useState(false);
222
+ const [vcrRecording, setVcrRecording] = useState<VcrRecording | null>(null);
223
+ const [vcrFrameIndex, setVcrFrameIndex] = useState(0);
224
+ const [vcrPlaying, setVcrPlaying] = useState(false);
225
+ const [vcrGraph, setVcrGraph] = useState<BlueprintGraph | null>(null);
226
+ const [vcrError, setVcrError] = useState<string | null>(null);
227
+
228
+ const [showDigitalTwinPanel, setShowDigitalTwinPanel] = useState(false);
229
+ const [digitalTwinSnapshot, setDigitalTwinSnapshot] = useState<DigitalTwinSnapshot | null>(null);
230
+ const [digitalTwinGraph, setDigitalTwinGraph] = useState<BlueprintGraph | null>(null);
231
+ const [digitalTwinWindowSecs, setDigitalTwinWindowSecs] = useState(60);
232
+ const [autoDigitalTwin, setAutoDigitalTwin] = useState(false);
233
+ const autoDigitalTwinRef = useRef(autoDigitalTwin);
234
+ autoDigitalTwinRef.current = autoDigitalTwin;
235
+ const [simulateNodeIds, setSimulateNodeIds] = useState("");
236
+ const [simulateLabel, setSimulateLabel] = useState("");
237
+ const [digitalTwinError, setDigitalTwinError] = useState<string | null>(null);
238
+ const [digitalTwinPollError, setDigitalTwinPollError] = useState<string | null>(null);
239
+ const [digitalTwinLastUpdatedAt, setDigitalTwinLastUpdatedAt] = useState<string | null>(null);
240
+
241
+ const [showRefactorPanel, setShowRefactorPanel] = useState(false);
242
+ const [refactorReport, setRefactorReport] = useState<RefactorReport | null>(null);
243
+ const [healResult, setHealResult] = useState<HealResult | null>(null);
244
+ const [refactorError, setRefactorError] = useState<string | null>(null);
245
+ const graphReplacedByHealRef = useRef(false);
246
+ const refactorAbortRef = useRef<AbortController | null>(null);
247
+
248
+ const [showGeneticPanel, setShowGeneticPanel] = useState(false);
249
+ const [showMascotPanel, setShowMascotPanel] = useState(false);
250
+ const [showPhasePanel, setShowPhasePanel] = useState(false);
251
+ const [geneticGenerations, setGeneticGenerations] = useState(3);
252
+ const [geneticPopulationSize, setGeneticPopulationSize] = useState(6);
253
+ const [tournamentResult, setTournamentResult] = useState<TournamentResult | null>(null);
254
+ const [geneticError, setGeneticError] = useState<string | null>(null);
255
+ const [editorRevealTarget, setEditorRevealTarget] = useState<ReturnType<typeof getNavigationTarget>>(null);
256
+ const [navigationError, setNavigationError] = useState<string | null>(null);
257
+
258
+ const [showOpencodePanel, setShowOpencodePanel] = useState(false);
259
+ const [opencodeStatus, setOpencodeStatus] = useState<OpencodeServerInfo>({ status: "stopped" });
260
+ const [useOpencodeForAgent, setUseOpencodeForAgent] = useState(false);
261
+
262
+ const selectedNode = graph?.nodes.find((node) => node.id === selectedNodeId) ?? null;
263
+ const drilldownNodeId = drilldownStack.at(-1) ?? null;
264
+ const drilldownRootNode = graph?.nodes.find((node) => node.id === drilldownNodeId) ?? null;
265
+ const executionIndex = useMemo(
266
+ () => indexRuntimeExecutionResult(executionResult),
267
+ [executionResult]
268
+ );
269
+ const detailFlow =
270
+ graph && drilldownNodeId
271
+ ? buildDetailFlow(graph, drilldownNodeId, selectedDetailNodeId ?? undefined, executionResult)
272
+ : null;
273
+ const heatmapData: HeatmapData | undefined = useMemo(
274
+ () =>
275
+ graph &&
276
+ graph.nodes.some(
277
+ (node) => node.traceState && node.traceState.count > 0
278
+ )
279
+ ? computeHeatmap(graph)
280
+ : undefined,
281
+ [graph]
282
+ );
283
+
284
+ const canStartImplementation = false;
285
+ const canStartIntegration = false;
286
+ const canImplementActiveNode = false;
287
+ const canRunActiveNode = false;
288
+ const isBusy = Boolean(busyLabel);
289
+ const isBuilding = busyLabel === "Building blueprint";
290
+
291
+ return (
292
+ <div className="workbench-shell">
293
+ <div className="workbench-main">
294
+ <div className="graph-panel">
295
+ <GraphCanvas
296
+ graph={graph}
297
+ selectedNodeId={selectedNodeId}
298
+ onSelect={setSelectedNodeId}
299
+ heatmapData={heatmapData}
300
+ />
301
+ </div>
302
+ </div>
303
+ </div>
304
+ );
305
+ }
@@ -0,0 +1,80 @@
1
+ "use client";
2
+
3
+ import { useRef } from "react";
4
+
5
+ import dynamic from "next/dynamic";
6
+ import type * as Monaco from "monaco-editor";
7
+
8
+ import { prepareMonaco, toMonacoPath } from "./monaco-setup.js";
9
+
10
+ const MonacoDiffEditor = dynamic(() => import("@monaco-editor/react").then(mod => mod.DiffEditor), {
11
+ ssr: false,
12
+ loading: () => <div className="code-diff-editor-loading">Loading diff editor...</div>
13
+ });
14
+
15
+ type CodeDiffEditorProps = {
16
+ originalValue: string;
17
+ modifiedValue: string;
18
+ language?: "typescript" | "javascript" | "json" | "markdown";
19
+ height?: string;
20
+ readOnly?: boolean;
21
+ theme?: "light" | "dark";
22
+ onModifiedChange?: (value: string) => void;
23
+ };
24
+
25
+ export function CodeDiffEditor({
26
+ originalValue,
27
+ modifiedValue,
28
+ language = "typescript",
29
+ height = "28rem",
30
+ readOnly = false,
31
+ theme = "dark",
32
+ onModifiedChange
33
+ }: CodeDiffEditorProps): React.JSX.Element {
34
+ const monacoRef = useRef<typeof Monaco | null>(null);
35
+ const modifiedListenerRef = useRef<Monaco.IDisposable | null>(null);
36
+
37
+ return (
38
+ <div
39
+ className="code-diff-editor-shell"
40
+ style={{
41
+ height,
42
+ minHeight: height === "100%" ? 0 : height
43
+ }}
44
+ >
45
+ <MonacoDiffEditor
46
+ beforeMount={prepareMonaco}
47
+ height={height}
48
+ language={language}
49
+ modified={modifiedValue}
50
+ modifiedModelPath={toMonacoPath("diff/modified.ts")}
51
+ options={{
52
+ automaticLayout: true,
53
+ diffCodeLens: true,
54
+ enableSplitViewResizing: true,
55
+ fontFamily: "IBM Plex Mono, SFMono-Regular, SF Mono, monospace",
56
+ fontLigatures: true,
57
+ fontSize: 14,
58
+ lineNumbersMinChars: 3,
59
+ minimap: { enabled: false },
60
+ padding: { top: 16, bottom: 16 },
61
+ readOnly,
62
+ renderSideBySide: true,
63
+ scrollBeyondLastLine: false,
64
+ smoothScrolling: true,
65
+ wordWrap: "on"
66
+ }}
67
+ original={originalValue}
68
+ originalModelPath={toMonacoPath("diff/original.ts")}
69
+ onMount={(editor, monaco) => {
70
+ monacoRef.current = monaco;
71
+ modifiedListenerRef.current?.dispose();
72
+ modifiedListenerRef.current = editor.getModifiedEditor().onDidChangeModelContent(() => {
73
+ onModifiedChange?.(editor.getModifiedEditor().getValue());
74
+ });
75
+ }}
76
+ theme={theme === "dark" ? "vs-dark" : "vs-light"}
77
+ />
78
+ </div>
79
+ );
80
+ }