@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
@@ -0,0 +1,104 @@
1
+ "use client";
2
+
3
+ import { useCallback, useMemo } from "react";
4
+ import { Rnd, type RndDragCallback, type RndResizeCallback } from "react-rnd";
5
+
6
+ import { useBlueprintStore } from "../store/blueprint-store.js";
7
+
8
+ function getDefaultFloatingGraphBounds() {
9
+ if (typeof window === "undefined") {
10
+ return { x: 48, y: 48, width: 420, height: 320 };
11
+ }
12
+
13
+ const width = Math.max(360, Math.round(window.innerWidth * 0.3));
14
+ const height = Math.max(260, Math.round(window.innerHeight * 0.35));
15
+ const x = Math.max(24, window.innerWidth - width - 40);
16
+ const y = Math.max(24, window.innerHeight - height - 120);
17
+
18
+ return { x, y, width, height };
19
+ }
20
+
21
+ type IdeLayoutProps = {
22
+ explorer: React.ReactNode;
23
+ mainContent: React.ReactNode;
24
+ bottomPanel?: React.ReactNode;
25
+ floatingGraphContent?: React.ReactNode;
26
+ rightSidebar?: React.ReactNode;
27
+ };
28
+
29
+ export function IdeLayout({
30
+ explorer,
31
+ mainContent,
32
+ bottomPanel,
33
+ floatingGraphContent,
34
+ rightSidebar
35
+ }: IdeLayoutProps) {
36
+ const { activeFile, floatingGraph, setFloatingGraph } = useBlueprintStore();
37
+
38
+ const resolvedFloatingGraph = useMemo(() => {
39
+ const defaults = getDefaultFloatingGraphBounds();
40
+ return {
41
+ x: floatingGraph.x || defaults.x,
42
+ y: floatingGraph.y || defaults.y,
43
+ width: floatingGraph.width || defaults.width,
44
+ height: floatingGraph.height || defaults.height
45
+ };
46
+ }, [floatingGraph.height, floatingGraph.width, floatingGraph.x, floatingGraph.y]);
47
+
48
+ const handleDragStop: RndDragCallback = useCallback(
49
+ (_event, data) => {
50
+ setFloatingGraph({ x: data.x, y: data.y });
51
+ },
52
+ [setFloatingGraph]
53
+ );
54
+
55
+ const handleResizeStop: RndResizeCallback = useCallback(
56
+ (_event, _direction, ref, _delta, position) => {
57
+ setFloatingGraph({
58
+ x: position.x,
59
+ y: position.y,
60
+ width: parseInt(ref.style.width, 10),
61
+ height: parseInt(ref.style.height, 10)
62
+ });
63
+ },
64
+ [setFloatingGraph]
65
+ );
66
+
67
+ return (
68
+ <div className="ide-layout-shell">
69
+ <aside className="ide-left-sidebar">
70
+ <div className="ide-pane-header">Explorer</div>
71
+ <div className="ide-pane-body">{explorer}</div>
72
+ </aside>
73
+ <div className="ide-main-stack">
74
+ <main className="ide-main-area">
75
+ {mainContent}
76
+ {activeFile && floatingGraph.visible && floatingGraphContent ? (
77
+ <Rnd
78
+ bounds="parent"
79
+ className="ide-floating-graph"
80
+ dragHandleClassName="ide-floating-graph-header"
81
+ minHeight={220}
82
+ minWidth={320}
83
+ onDragStop={handleDragStop}
84
+ onResizeStop={handleResizeStop}
85
+ position={{ x: resolvedFloatingGraph.x, y: resolvedFloatingGraph.y }}
86
+ size={{ width: resolvedFloatingGraph.width, height: resolvedFloatingGraph.height }}
87
+ >
88
+ <div className="ide-floating-graph-header">
89
+ <span>Live Graph</span>
90
+ <span>Drag to reposition</span>
91
+ </div>
92
+ <div className="ide-floating-graph-body">{floatingGraphContent}</div>
93
+ </Rnd>
94
+ ) : null}
95
+ </main>
96
+ <section className="ide-bottom-panel">{bottomPanel}</section>
97
+ </div>
98
+ <aside className="ide-right-sidebar">
99
+ <div className="ide-pane-header">Agent</div>
100
+ <div className="ide-pane-body">{rightSidebar ?? <div className="ide-agent-slot" />}</div>
101
+ </aside>
102
+ </div>
103
+ );
104
+ }
@@ -0,0 +1,5 @@
1
+ "use client";
2
+
3
+ export function IdeWorkbench({ children }: { children?: React.ReactNode }) {
4
+ return <>{children}</>;
5
+ }
@@ -0,0 +1,12 @@
1
+ export { IdeLayout } from "./ide-layout.js";
2
+ export { FileTree } from "./file-tree.js";
3
+ export { FileTabs } from "./file-tabs.js";
4
+ export { GraphCanvas } from "./graph-canvas.js";
5
+ export { CodeEditor } from "./code-editor.js";
6
+ export { CodeDiffEditor } from "./code-diff-editor.js";
7
+ export { BlueprintWorkbench } from "./blueprint-workbench.js";
8
+ export { PolicyWorkbench } from "./policy-workbench.js";
9
+ export { IdeWorkbench } from "./ide-workbench.js";
10
+ export { OpencodeSettings } from "./opencode-settings.js";
11
+ export { prepareMonaco, toMonacoPath } from "./monaco-setup.js";
12
+ export { TypeScriptLanguageService, getTypeScriptLanguageService } from "./ts-language-service.js";
@@ -0,0 +1,67 @@
1
+ import type * as Monaco from "monaco-editor";
2
+
3
+ import { getTypeScriptLanguageService } from "./ts-language-service.js";
4
+
5
+ type MonacoEnvironmentShape = {
6
+ getWorker: (_workerId: string, label: string) => Worker;
7
+ };
8
+
9
+ type MonacoGlobal = typeof globalThis & {
10
+ MonacoEnvironment?: MonacoEnvironmentShape;
11
+ };
12
+
13
+ let workersConfigured = false;
14
+
15
+ export function prepareMonaco(monaco: typeof Monaco): void {
16
+ if (!workersConfigured) {
17
+ const monacoGlobal = globalThis as MonacoGlobal;
18
+ monacoGlobal.MonacoEnvironment = {
19
+ getWorker(_workerId, label) {
20
+ if (label === "typescript" || label === "javascript") {
21
+ return new Worker(
22
+ new URL("monaco-editor/esm/vs/language/typescript/ts.worker.js", import.meta.url),
23
+ { type: "module" }
24
+ );
25
+ }
26
+
27
+ if (label === "json") {
28
+ return new Worker(
29
+ new URL("monaco-editor/esm/vs/language/json/json.worker.js", import.meta.url),
30
+ { type: "module" }
31
+ );
32
+ }
33
+
34
+ if (label === "css" || label === "scss" || label === "less") {
35
+ return new Worker(
36
+ new URL("monaco-editor/esm/vs/language/css/css.worker.js", import.meta.url),
37
+ { type: "module" }
38
+ );
39
+ }
40
+
41
+ if (label === "html" || label === "handlebars" || label === "razor") {
42
+ return new Worker(
43
+ new URL("monaco-editor/esm/vs/language/html/html.worker.js", import.meta.url),
44
+ { type: "module" }
45
+ );
46
+ }
47
+
48
+ return new Worker(
49
+ new URL("monaco-editor/esm/vs/editor/editor.worker.js", import.meta.url),
50
+ { type: "module" }
51
+ );
52
+ }
53
+ };
54
+ workersConfigured = true;
55
+ }
56
+
57
+ getTypeScriptLanguageService(monaco).configureDefaults();
58
+ }
59
+
60
+ export function toMonacoPath(filePath: string): string {
61
+ if (filePath.startsWith("file://")) {
62
+ return filePath;
63
+ }
64
+
65
+ const normalized = filePath.replace(/\\/g, "/").replace(/^\/+/, "");
66
+ return `file:///${normalized}`;
67
+ }
@@ -0,0 +1,82 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useState } from "react";
4
+ import type { OpencodeProvider, OpencodeServerInfo } from "../lib/types.js";
5
+
6
+ const PROVIDERS: { id: OpencodeProvider; label: string }[] = [
7
+ { id: "anthropic", label: "Anthropic (Claude)" },
8
+ { id: "openai", label: "OpenAI (GPT)" },
9
+ { id: "google", label: "Google (Gemini)" },
10
+ { id: "azure", label: "Azure OpenAI" },
11
+ { id: "groq", label: "Groq" },
12
+ { id: "mistral", label: "Mistral" },
13
+ { id: "cohere", label: "Cohere" },
14
+ { id: "perplexity", label: "Perplexity" },
15
+ { id: "openrouter", label: "OpenRouter" },
16
+ { id: "bedrock", label: "AWS Bedrock" },
17
+ { id: "local", label: "Local Model" },
18
+ ];
19
+
20
+ type Props = {
21
+ onClose?: () => void;
22
+ onStatusChange?: (status: OpencodeServerInfo) => void;
23
+ };
24
+
25
+ export function OpencodeSettings({ onClose, onStatusChange }: Props) {
26
+ const [provider, setProvider] = useState<OpencodeProvider>("anthropic");
27
+ const [apiKey, setApiKey] = useState("");
28
+ const [model, setModel] = useState("");
29
+ const [baseUrl, setBaseUrl] = useState("");
30
+ const [serverStatus, setServerStatus] = useState<OpencodeServerInfo>({ status: "stopped" });
31
+ const [isLoading, setIsLoading] = useState(false);
32
+ const [error, setError] = useState<string | null>(null);
33
+
34
+ return (
35
+ <div className="opencode-settings">
36
+ <div className="opencode-settings-header">
37
+ <h3>OpenCode Agent Settings</h3>
38
+ {onClose && (
39
+ <button onClick={onClose} type="button" className="close-btn">
40
+ ×
41
+ </button>
42
+ )}
43
+ </div>
44
+
45
+ <div className="server-status-bar">
46
+ <span className={`status-indicator status-${serverStatus.status}`} />
47
+ <span className="status-text">
48
+ {serverStatus.status === "running"
49
+ ? `Connected (${serverStatus.url})`
50
+ : serverStatus.status === "starting"
51
+ ? "Starting..."
52
+ : serverStatus.status === "error"
53
+ ? `Error: ${serverStatus.error}`
54
+ : "Not connected"}
55
+ </span>
56
+ </div>
57
+
58
+ {error && <div className="error-message">{error}</div>}
59
+
60
+ <label className="field">
61
+ <span>AI Provider</span>
62
+ <select value={provider} onChange={(e) => setProvider(e.target.value as OpencodeProvider)}>
63
+ {PROVIDERS.map((p) => (
64
+ <option key={p.id} value={p.id}>
65
+ {p.label}
66
+ </option>
67
+ ))}
68
+ </select>
69
+ </label>
70
+
71
+ <label className="field">
72
+ <span>API Key</span>
73
+ <input
74
+ type="password"
75
+ value={apiKey}
76
+ onChange={(e) => setApiKey(e.target.value)}
77
+ placeholder={`Enter your ${PROVIDERS.find((p) => p.id === provider)?.label || provider} API key`}
78
+ />
79
+ </label>
80
+ </div>
81
+ );
82
+ }
@@ -0,0 +1,233 @@
1
+ "use client";
2
+
3
+ import type { QueryResult } from "@abhinav2203/coderag";
4
+
5
+ import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from "react";
6
+
7
+ import { CodeEditor } from "./code-editor.js";
8
+ import { GraphCanvas } from "./graph-canvas.js";
9
+ import { buildDetailFlow } from "../lib/flow-view.js";
10
+ import { computeHeatmap } from "../lib/heatmap.js";
11
+ import type { HeatmapData } from "../lib/heatmap.js";
12
+ import type { CycleReport, SmellReport, GraphMetrics } from "../lib/types.js";
13
+ import type {
14
+ ApprovalRecord,
15
+ BlueprintGraph,
16
+ BlueprintNode,
17
+ ConflictReport,
18
+ ExecutionMode,
19
+ ExportResult,
20
+ ObservabilityLog,
21
+ PersistedSession,
22
+ RiskReport,
23
+ RunPlan,
24
+ RuntimeExecutionResult
25
+ } from "@abhinav2203/codeflow-core/schema";
26
+ import { emptyContract, traceSpanSchema } from "@abhinav2203/codeflow-core/schema";
27
+ import {
28
+ AUTO_IMPLEMENT_STORAGE_KEY,
29
+ LIVE_COMPLETIONS_STORAGE_KEY,
30
+ THEME_STORAGE_KEY,
31
+ loadSessionApiKey,
32
+ readLocalBooleanPreference,
33
+ readLocalPreference,
34
+ storeSessionApiKey,
35
+ writeLocalBooleanPreference,
36
+ writeLocalPreference
37
+ } from "../lib/browser/storage.js";
38
+
39
+ type BuildResponse = {
40
+ graph?: BlueprintGraph;
41
+ runPlan?: RunPlan;
42
+ session?: PersistedSession;
43
+ error?: string;
44
+ };
45
+
46
+ type ExportResponse = {
47
+ result?: ExportResult;
48
+ runPlan?: RunPlan;
49
+ riskReport?: RiskReport;
50
+ session?: PersistedSession;
51
+ approval?: ApprovalRecord;
52
+ requiresApproval?: boolean;
53
+ error?: string;
54
+ };
55
+
56
+ type ExecutionResponse = {
57
+ result?: RuntimeExecutionResult;
58
+ executedNodeId?: string;
59
+ graph?: BlueprintGraph;
60
+ runPlan?: RunPlan;
61
+ session?: PersistedSession;
62
+ error?: string;
63
+ };
64
+
65
+ type ObservabilityLatestResponse = {
66
+ graph?: BlueprintGraph | null;
67
+ latestSpans?: Array<{ spanId: string; name: string; status: string; runtime: string }>;
68
+ latestLogs?: ObservabilityLog[];
69
+ error?: string;
70
+ };
71
+
72
+ type ConflictResponse = {
73
+ report?: ConflictReport;
74
+ error?: string;
75
+ };
76
+
77
+ type CyclesResponse = {
78
+ report?: CycleReport;
79
+ error?: string;
80
+ };
81
+
82
+ type SmellsResponse = {
83
+ report?: SmellReport;
84
+ error?: string;
85
+ };
86
+
87
+ type MetricsResponse = {
88
+ metrics?: GraphMetrics;
89
+ error?: string;
90
+ };
91
+
92
+ type StatusTone = "info" | "success" | "danger";
93
+ type ThemePreference = "system" | "light" | "dark";
94
+ type ResolvedTheme = "light" | "dark";
95
+
96
+ const POLICY_CANVAS_PROMPT = `Act as an Enterprise Mobility Architect. Using the Google STITCH MCP server, design a secure Engineering Department device profile.
97
+
98
+ Create nodes for a managed Chrome browser policy enabling developer tools while disabling insecure extensions.
99
+ Add a node for a corporate VPN configuration.
100
+ Wire these to a Fleet: Engineering-Laptops group node.
101
+ Verify the STITCH contract before sync: every policy needs a valid version ID and the VPN policy needs its certificate reference.
102
+ If a policy node drifts from schema, mark it Invalid, highlight it in red, and suggest a Heal fix based on the latest Google management API spec.`;
103
+
104
+ const maskApiKey = (value: string) => {
105
+ const trimmed = value.trim();
106
+ if (trimmed.length <= 8) {
107
+ return trimmed;
108
+ }
109
+
110
+ return `${trimmed.slice(0, 4)}...${trimmed.slice(-4)}`;
111
+ };
112
+
113
+ export function PolicyWorkbench() {
114
+ const MIN_OBSERVABILITY_INTERVAL_SECS = 2;
115
+ const [projectName, setProjectName] = useState("CodeFlow Workspace");
116
+ const [repoPath, setRepoPath] = useState("");
117
+ const [prdText, setPrdText] = useState("");
118
+ const [aiPrompt, setAiPrompt] = useState("");
119
+ const [nvidiaApiKey, setNvidiaApiKey] = useState("");
120
+ const [mode, setMode] = useState<ExecutionMode>("essential");
121
+ const [outputDir, setOutputDir] = useState("");
122
+ const [traceInput, setTraceInput] = useState("");
123
+ const [runInput, setRunInput] = useState("{}");
124
+ const [graph, setGraph] = useState<BlueprintGraph | null>(null);
125
+ const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);
126
+ const [error, setError] = useState<string | null>(null);
127
+ const [busyLabel, setBusyLabel] = useState<string | null>(null);
128
+ const [exportResult, setExportResult] = useState<ExportResult | null>(null);
129
+ const [runPlan, setRunPlan] = useState<RunPlan | null>(null);
130
+ const [riskReport, setRiskReport] = useState<RiskReport | null>(null);
131
+ const [session, setSession] = useState<PersistedSession | null>(null);
132
+ const [pendingApproval, setPendingApproval] = useState<ApprovalRecord | null>(null);
133
+ const [executionResult, setExecutionResult] = useState<RuntimeExecutionResult | null>(null);
134
+ const [latestLogs, setLatestLogs] = useState<ObservabilityLog[]>([]);
135
+ const [latestSpans, setLatestSpans] = useState<ObservabilityLatestResponse["latestSpans"]>([]);
136
+ const [conflictReport, setConflictReport] = useState<ConflictReport | null>(null);
137
+ const [newNodeName, setNewNodeName] = useState("");
138
+ const [newNodeKind, setNewNodeKind] = useState<BlueprintNode["kind"]>("function");
139
+ const [edgeFrom, setEdgeFrom] = useState("");
140
+ const [edgeTo, setEdgeTo] = useState("");
141
+ const [edgeKind, setEdgeKind] = useState<"calls" | "imports" | "inherits">("calls");
142
+ const [useAI, setUseAI] = useState(true);
143
+ const [drilldownStack, setDrilldownStack] = useState<string[]>([]);
144
+ const [selectedDetailNodeId, setSelectedDetailNodeId] = useState<string | null>(null);
145
+ const [codeDrafts, setCodeDrafts] = useState<Record<string, string>>({});
146
+ const [suggestionInstruction, setSuggestionInstruction] = useState("");
147
+ const [codeSuggestion, setCodeSuggestion] = useState<{ summary: string; code: string; notes: string[] } | null>(null);
148
+ const [liveCompletionsEnabled, setLiveCompletionsEnabled] = useState(true);
149
+ const [serverApiKeyConfigured, setServerApiKeyConfigured] = useState(false);
150
+ const [apiKeyStatusLoaded, setApiKeyStatusLoaded] = useState(false);
151
+ const [statusTitle, setStatusTitle] = useState("Ready to build");
152
+ const [statusDetail, setStatusDetail] = useState(
153
+ "Enter a project description or repo input, then build a blueprint."
154
+ );
155
+ const [statusTone, setStatusTone] = useState<StatusTone>("info");
156
+ const [showSettings, setShowSettings] = useState(false);
157
+ const [showPromptPanel, setShowPromptPanel] = useState(true);
158
+ const [showEditPanel, setShowEditPanel] = useState(false);
159
+ const [showInspector, setShowInspector] = useState(false);
160
+ const [showAnalysisPanel, setShowAnalysisPanel] = useState(false);
161
+ const [showObservabilityPanel, setShowObservabilityPanel] = useState(false);
162
+ const [showPolicyLayerPanel, setShowPolicyLayerPanel] = useState(false);
163
+ const [themePreference, setThemePreference] = useState<ThemePreference>("system");
164
+ const [systemTheme, setSystemTheme] = useState<ResolvedTheme>("light");
165
+ const [autoObservability, setAutoObservability] = useState(false);
166
+ const [observabilityIntervalSecs, setObservabilityIntervalSecs] = useState(5);
167
+ const autoObsRef = useRef(autoObservability);
168
+ autoObsRef.current = autoObservability;
169
+ const [autoImplementNodes, setAutoImplementNodes] = useState(false);
170
+ const [cycleReport, setCycleReport] = useState<CycleReport | null>(null);
171
+ const [smellReport, setSmellReport] = useState<SmellReport | null>(null);
172
+ const [graphMetrics, setGraphMetrics] = useState<GraphMetrics | null>(null);
173
+ const [mermaidDiagram, setMermaidDiagram] = useState<string | null>(null);
174
+ const resolvedTheme = themePreference === "system" ? systemTheme : themePreference;
175
+ const topbarRef = useRef<HTMLElement | null>(null);
176
+ const [floatingPanelTop, setFloatingPanelTop] = useState(112);
177
+
178
+ const selectedNode = graph?.nodes.find((node) => node.id === selectedNodeId) ?? null;
179
+ const drilldownNodeId = drilldownStack.at(-1) ?? null;
180
+ const drilldownRootNode = graph?.nodes.find((node) => node.id === drilldownNodeId) ?? null;
181
+ const detailFlow =
182
+ graph && drilldownNodeId
183
+ ? buildDetailFlow(graph, drilldownNodeId, selectedDetailNodeId ?? undefined)
184
+ : null;
185
+ const heatmapData: HeatmapData | undefined = useMemo(
186
+ () =>
187
+ graph &&
188
+ graph.nodes.some(
189
+ (node) => node.traceState && node.traceState.count > 0
190
+ )
191
+ ? computeHeatmap(graph)
192
+ : undefined,
193
+ [graph]
194
+ );
195
+
196
+ const canStartImplementation = false;
197
+ const canStartIntegration = false;
198
+ const canImplementActiveNode = false;
199
+ const canRunActiveNode = false;
200
+ const canRunIntegration = false;
201
+ const isBusy = Boolean(busyLabel);
202
+ const isBuilding = busyLabel === "Building blueprint";
203
+
204
+ return (
205
+ <div className="workbench-shell" data-theme={resolvedTheme}>
206
+ <header className={`workbench-topbar ${graph ? "workbench-topbar-compact" : ""}`} ref={topbarRef}>
207
+ <div className="topbar-start">
208
+ <div className="brand-lockup">
209
+ <p className="brand-eyebrow">CodeFlow</p>
210
+ <h1>Policy Canvas</h1>
211
+ <p className="brand-caption">Graph-native architecture, compliance, and deployment control.</p>
212
+ </div>
213
+ </div>
214
+ </header>
215
+
216
+ <main className="policy-layout focus-layout">
217
+ <section className="workbench-main focus-main">
218
+ <section className="graph-panel full-graph focus-graph">
219
+ <GraphCanvas
220
+ graph={graph}
221
+ selectedNodeId={drilldownRootNode ? selectedDetailNodeId : selectedNodeId}
222
+ nodes={detailFlow?.nodes}
223
+ edges={detailFlow?.edges}
224
+ onSelect={(nodeId) => setSelectedNodeId(nodeId)}
225
+ heatmapData={drilldownRootNode ? undefined : heatmapData}
226
+ theme={resolvedTheme}
227
+ />
228
+ </section>
229
+ </section>
230
+ </main>
231
+ </div>
232
+ );
233
+ }
@@ -0,0 +1,170 @@
1
+ import type * as Monaco from "monaco-editor";
2
+
3
+ type LanguageDefaultsApi = {
4
+ addExtraLib: (content: string, filePath?: string) => Monaco.IDisposable;
5
+ setCompilerOptions: (options: Record<string, unknown>) => void;
6
+ setDiagnosticsOptions: (options: Record<string, unknown>) => void;
7
+ };
8
+
9
+ type MonacoTypeScriptApi = {
10
+ javascriptDefaults: LanguageDefaultsApi;
11
+ typescriptDefaults: LanguageDefaultsApi;
12
+ JsxEmit: {
13
+ ReactJSX?: number;
14
+ React?: number;
15
+ };
16
+ ModuleKind: {
17
+ ESNext: number;
18
+ };
19
+ ModuleResolutionKind: {
20
+ NodeJs: number;
21
+ };
22
+ ScriptTarget: {
23
+ ESNext: number;
24
+ };
25
+ };
26
+
27
+ function getTypeScriptApi(monaco: typeof Monaco): MonacoTypeScriptApi | null {
28
+ return (monaco.languages as unknown as { typescript?: MonacoTypeScriptApi }).typescript ?? null;
29
+ }
30
+
31
+ function toExtraLibPath(filePath: string): string {
32
+ const normalized = filePath.replace(/\\/g, "/").replace(/^\/+/, "");
33
+ return `file:///${normalized}`;
34
+ }
35
+
36
+ export class TypeScriptLanguageService {
37
+ private monaco: typeof Monaco;
38
+ private defaultsConfigured = false;
39
+ private workspaceLibs = new Map<string, Monaco.IDisposable>();
40
+ private globalLibDisposables: Monaco.IDisposable[] = [];
41
+
42
+ constructor(monaco: typeof Monaco) {
43
+ this.monaco = monaco;
44
+ }
45
+
46
+ configureDefaults(): void {
47
+ if (this.defaultsConfigured) {
48
+ return;
49
+ }
50
+
51
+ const api = getTypeScriptApi(this.monaco);
52
+ if (!api) {
53
+ return;
54
+ }
55
+
56
+ const jsxMode = api.JsxEmit.ReactJSX ?? api.JsxEmit.React ?? 2;
57
+ const compilerOptions = {
58
+ allowJs: true,
59
+ allowNonTsExtensions: true,
60
+ baseUrl: ".",
61
+ checkJs: true,
62
+ esModuleInterop: true,
63
+ jsx: jsxMode,
64
+ module: api.ModuleKind.ESNext,
65
+ moduleResolution: api.ModuleResolutionKind.NodeJs,
66
+ noEmit: true,
67
+ paths: {
68
+ "@/*": ["./src/*"],
69
+ "@/components/*": ["./src/components/*"],
70
+ "@/lib/*": ["./src/lib/*"],
71
+ "@/store/*": ["./src/store/*"]
72
+ },
73
+ strict: true,
74
+ target: api.ScriptTarget.ESNext
75
+ };
76
+
77
+ api.typescriptDefaults.setCompilerOptions(compilerOptions);
78
+ api.javascriptDefaults.setCompilerOptions(compilerOptions);
79
+
80
+ api.typescriptDefaults.setDiagnosticsOptions({
81
+ noSemanticValidation: false,
82
+ noSyntaxValidation: false
83
+ });
84
+ api.javascriptDefaults.setDiagnosticsOptions({
85
+ noSemanticValidation: false,
86
+ noSyntaxValidation: false
87
+ });
88
+
89
+ this.addGlobalTypes(api);
90
+ this.defaultsConfigured = true;
91
+ }
92
+
93
+ private addGlobalTypes(api: MonacoTypeScriptApi): void {
94
+ const reactTypes = `
95
+ declare namespace React {
96
+ type ReactNode = import('react').ReactNode;
97
+ type FC<P = {}> = import('react').FunctionComponent<P>;
98
+ type CSSProperties = import('react').CSSProperties;
99
+ }
100
+ declare module 'react' {
101
+ function useState<T>(initial: T | (() => T)): [T, (value: T) => void];
102
+ function useState<T>(): [T | undefined, (value: T) => void];
103
+ function useEffect(effect: () => void | (() => void)): void;
104
+ function useEffect(effect: () => void, deps: any[]): void;
105
+ function useCallback<T extends (...args: any[]) => any>(callback: T, deps: any[]): T;
106
+ function useMemo<T>(factory: () => T, deps: any[]): T;
107
+ function useRef<T>(initial: T): { current: T };
108
+ function useRef<T>(initial?: T): { current: T | undefined };
109
+ }
110
+ `;
111
+
112
+ const nextTypes = `
113
+ declare namespace Next {
114
+ function dynamic<T>(importFn: () => Promise<T>): T;
115
+ function dynamic<T>(importFn: () => Promise<T>, options: { ssr?: boolean }): T;
116
+ }
117
+ declare module 'next' {
118
+ export function GetServerSideProps(context: any): any;
119
+ export function GetStaticProps(context: any): any;
120
+ export function GetServerSideProps(context: any): any;
121
+ }
122
+ `;
123
+
124
+ const nodeTypes = `
125
+ declare module 'node:fs' {
126
+ export function readFile(path: string, encoding: string): Promise<string>;
127
+ export function writeFile(path: string, data: string): Promise<void>;
128
+ }
129
+ declare module 'node:path' {
130
+ export function resolve(...paths: string[]): string;
131
+ export function join(...paths: string[]): string;
132
+ }
133
+ `;
134
+
135
+ this.globalLibDisposables.push(
136
+ api.typescriptDefaults.addExtraLib(reactTypes, "file:///node_modules/@types/react-global.d.ts"),
137
+ api.typescriptDefaults.addExtraLib(nextTypes, "file:///node_modules/@types/next-global.d.ts"),
138
+ api.typescriptDefaults.addExtraLib(nodeTypes, "file:///node_modules/@types/node-global.d.ts")
139
+ );
140
+ }
141
+
142
+ upsertWorkspaceFile(filePath: string, content: string): void {
143
+ this.configureDefaults();
144
+ const api = getTypeScriptApi(this.monaco);
145
+ if (!api) {
146
+ return;
147
+ }
148
+
149
+ const extraLibPath = toExtraLibPath(filePath);
150
+ this.workspaceLibs.get(extraLibPath)?.dispose();
151
+ this.workspaceLibs.set(extraLibPath, api.typescriptDefaults.addExtraLib(content, extraLibPath));
152
+ }
153
+
154
+ dispose(): void {
155
+ this.globalLibDisposables.forEach((disposable) => disposable.dispose());
156
+ this.globalLibDisposables = [];
157
+ this.workspaceLibs.forEach((disposable) => disposable.dispose());
158
+ this.workspaceLibs.clear();
159
+ this.defaultsConfigured = false;
160
+ }
161
+ }
162
+
163
+ let languageService: TypeScriptLanguageService | null = null;
164
+
165
+ export function getTypeScriptLanguageService(monaco: typeof Monaco): TypeScriptLanguageService {
166
+ if (!languageService) {
167
+ languageService = new TypeScriptLanguageService(monaco);
168
+ }
169
+ return languageService;
170
+ }