@contractspec/lib.example-shared-ui 6.0.16 → 6.0.18
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/.turbo/turbo-build.log +81 -81
- package/CHANGELOG.md +32 -0
- package/dist/EvolutionDashboard.js +1 -803
- package/dist/EvolutionSidebar.js +1 -531
- package/dist/LocalDataIndicator.js +1 -62
- package/dist/MarkdownView.js +1 -207
- package/dist/OverlayContextProvider.js +1 -202
- package/dist/PersonalizationInsights.js +1 -455
- package/dist/SaveToStudioButton.js +1 -73
- package/dist/SpecDrivenTemplateShell.js +1 -197
- package/dist/SpecEditorPanel.js +17 -358
- package/dist/TemplateShell.js +1 -189
- package/dist/browser/EvolutionDashboard.js +1 -803
- package/dist/browser/EvolutionSidebar.js +1 -531
- package/dist/browser/LocalDataIndicator.js +1 -62
- package/dist/browser/MarkdownView.js +1 -207
- package/dist/browser/OverlayContextProvider.js +1 -202
- package/dist/browser/PersonalizationInsights.js +1 -455
- package/dist/browser/SaveToStudioButton.js +1 -73
- package/dist/browser/SpecDrivenTemplateShell.js +1 -197
- package/dist/browser/SpecEditorPanel.js +17 -358
- package/dist/browser/TemplateShell.js +1 -189
- package/dist/browser/bundles/ExampleTemplateBundle.js +1 -85
- package/dist/browser/bundles/index.js +1 -85
- package/dist/browser/hooks/index.js +40 -1145
- package/dist/browser/hooks/useBehaviorTracking.js +1 -157
- package/dist/browser/hooks/useEvolution.js +1 -260
- package/dist/browser/hooks/useRegistryTemplates.js +1 -31
- package/dist/browser/hooks/useSpecContent.js +17 -218
- package/dist/browser/hooks/useWorkflowComposer.js +24 -483
- package/dist/browser/index.js +40 -3110
- package/dist/browser/lib/component-registry.js +1 -42
- package/dist/browser/lib/runtime-context.js +1 -15
- package/dist/browser/markdown/formatPresentationName.js +1 -9
- package/dist/browser/markdown/useMarkdownPresentation.js +1 -65
- package/dist/browser/utils/fetchPresentationData.js +1 -15
- package/dist/browser/utils/generateSpecFromTemplate.js +16 -62
- package/dist/browser/utils/index.js +16 -76
- package/dist/bundles/ExampleTemplateBundle.js +1 -85
- package/dist/bundles/index.js +1 -85
- package/dist/hooks/index.js +40 -1145
- package/dist/hooks/useBehaviorTracking.js +1 -157
- package/dist/hooks/useEvolution.js +1 -260
- package/dist/hooks/useRegistryTemplates.js +1 -31
- package/dist/hooks/useSpecContent.js +17 -218
- package/dist/hooks/useWorkflowComposer.js +24 -483
- package/dist/index.js +40 -3110
- package/dist/lib/component-registry.js +1 -42
- package/dist/lib/runtime-context.d.ts +2 -1
- package/dist/lib/runtime-context.js +1 -15
- package/dist/lib/singletons.test.d.ts +1 -0
- package/dist/markdown/formatPresentationName.js +1 -9
- package/dist/markdown/useMarkdownPresentation.js +1 -65
- package/dist/node/EvolutionDashboard.js +1 -803
- package/dist/node/EvolutionSidebar.js +1 -531
- package/dist/node/LocalDataIndicator.js +1 -62
- package/dist/node/MarkdownView.js +1 -207
- package/dist/node/OverlayContextProvider.js +1 -202
- package/dist/node/PersonalizationInsights.js +1 -455
- package/dist/node/SaveToStudioButton.js +1 -73
- package/dist/node/SpecDrivenTemplateShell.js +1 -197
- package/dist/node/SpecEditorPanel.js +17 -358
- package/dist/node/TemplateShell.js +1 -189
- package/dist/node/bundles/ExampleTemplateBundle.js +1 -85
- package/dist/node/bundles/index.js +1 -85
- package/dist/node/hooks/index.js +40 -1145
- package/dist/node/hooks/useBehaviorTracking.js +1 -157
- package/dist/node/hooks/useEvolution.js +1 -260
- package/dist/node/hooks/useRegistryTemplates.js +1 -31
- package/dist/node/hooks/useSpecContent.js +17 -218
- package/dist/node/hooks/useWorkflowComposer.js +24 -483
- package/dist/node/index.js +40 -3110
- package/dist/node/lib/component-registry.js +1 -42
- package/dist/node/lib/runtime-context.js +1 -15
- package/dist/node/markdown/formatPresentationName.js +1 -9
- package/dist/node/markdown/useMarkdownPresentation.js +1 -65
- package/dist/node/utils/fetchPresentationData.js +1 -15
- package/dist/node/utils/generateSpecFromTemplate.js +16 -62
- package/dist/node/utils/index.js +16 -76
- package/dist/utils/fetchPresentationData.js +1 -15
- package/dist/utils/generateSpecFromTemplate.js +16 -62
- package/dist/utils/index.js +16 -76
- package/package.json +14 -14
- package/src/lib/component-registry.tsx +16 -1
- package/src/lib/runtime-context.tsx +17 -3
- package/src/lib/singletons.test.ts +51 -0
package/dist/index.js
CHANGED
|
@@ -1,2141 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// src/hooks/useEvolution.ts
|
|
3
|
-
|
|
4
|
-
"use client";
|
|
5
|
-
var EVOLUTION_STORAGE_KEY = "contractspec-evolution-data";
|
|
6
|
-
function useEvolution(templateId) {
|
|
7
|
-
const [usageStats, setUsageStats] = useState([]);
|
|
8
|
-
const [anomalies, setAnomalies] = useState([]);
|
|
9
|
-
const [suggestions, setSuggestions] = useState([]);
|
|
10
|
-
const [hints, setHints] = useState([]);
|
|
11
|
-
const [loading, setLoading] = useState(false);
|
|
12
|
-
const metricsRef = useRef([]);
|
|
13
|
-
const [operationCount, setOperationCount] = useState(0);
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
try {
|
|
16
|
-
const stored = localStorage.getItem(`${EVOLUTION_STORAGE_KEY}-${templateId}`);
|
|
17
|
-
if (stored) {
|
|
18
|
-
const data = JSON.parse(stored);
|
|
19
|
-
setSuggestions(data.suggestions.map((s) => ({
|
|
20
|
-
...s,
|
|
21
|
-
createdAt: new Date(s.createdAt)
|
|
22
|
-
})));
|
|
23
|
-
}
|
|
24
|
-
} catch {}
|
|
25
|
-
}, [templateId]);
|
|
26
|
-
useEffect(() => {
|
|
27
|
-
try {
|
|
28
|
-
localStorage.setItem(`${EVOLUTION_STORAGE_KEY}-${templateId}`, JSON.stringify({ suggestions }));
|
|
29
|
-
} catch {}
|
|
30
|
-
}, [suggestions, templateId]);
|
|
31
|
-
const trackOperation = useCallback((operationName, durationMs, success, errorCode) => {
|
|
32
|
-
const sample = {
|
|
33
|
-
operation: {
|
|
34
|
-
name: operationName,
|
|
35
|
-
version: "1.0.0",
|
|
36
|
-
tenantId: "sandbox"
|
|
37
|
-
},
|
|
38
|
-
durationMs,
|
|
39
|
-
success,
|
|
40
|
-
timestamp: new Date,
|
|
41
|
-
errorCode
|
|
42
|
-
};
|
|
43
|
-
metricsRef.current.push(sample);
|
|
44
|
-
setOperationCount((prev) => prev + 1);
|
|
45
|
-
}, []);
|
|
46
|
-
const analyzeUsage = useCallback(() => {
|
|
47
|
-
const samples = metricsRef.current;
|
|
48
|
-
if (samples.length < 5)
|
|
49
|
-
return;
|
|
50
|
-
const groups = new Map;
|
|
51
|
-
for (const sample of samples) {
|
|
52
|
-
const key = `${sample.operation.name}.v${sample.operation.version}`;
|
|
53
|
-
const arr = groups.get(key) ?? [];
|
|
54
|
-
arr.push(sample);
|
|
55
|
-
groups.set(key, arr);
|
|
56
|
-
}
|
|
57
|
-
const stats = [];
|
|
58
|
-
const detectedAnomalies = [];
|
|
59
|
-
groups.forEach((opSamples) => {
|
|
60
|
-
if (opSamples.length < 3)
|
|
61
|
-
return;
|
|
62
|
-
const durations = opSamples.map((s) => s.durationMs).sort((a, b) => a - b);
|
|
63
|
-
const errors = opSamples.filter((s) => !s.success);
|
|
64
|
-
const totalCalls = opSamples.length;
|
|
65
|
-
const errorRate = errors.length / totalCalls;
|
|
66
|
-
const averageLatencyMs = durations.reduce((sum, value) => sum + value, 0) / totalCalls;
|
|
67
|
-
const timestamps = opSamples.map((s) => s.timestamp.getTime());
|
|
68
|
-
const firstSample = opSamples[0];
|
|
69
|
-
if (!firstSample)
|
|
70
|
-
return;
|
|
71
|
-
const stat = {
|
|
72
|
-
operation: firstSample.operation,
|
|
73
|
-
totalCalls,
|
|
74
|
-
successRate: 1 - errorRate,
|
|
75
|
-
errorRate,
|
|
76
|
-
averageLatencyMs,
|
|
77
|
-
p95LatencyMs: percentile(durations, 0.95),
|
|
78
|
-
p99LatencyMs: percentile(durations, 0.99),
|
|
79
|
-
maxLatencyMs: Math.max(...durations),
|
|
80
|
-
lastSeenAt: new Date(Math.max(...timestamps)),
|
|
81
|
-
windowStart: new Date(Math.min(...timestamps)),
|
|
82
|
-
windowEnd: new Date(Math.max(...timestamps)),
|
|
83
|
-
topErrors: errors.reduce((acc, s) => {
|
|
84
|
-
if (s.errorCode) {
|
|
85
|
-
acc[s.errorCode] = (acc[s.errorCode] ?? 0) + 1;
|
|
86
|
-
}
|
|
87
|
-
return acc;
|
|
88
|
-
}, {})
|
|
89
|
-
};
|
|
90
|
-
stats.push(stat);
|
|
91
|
-
if (errorRate > 0.1) {
|
|
92
|
-
detectedAnomalies.push({
|
|
93
|
-
operation: stat.operation,
|
|
94
|
-
severity: errorRate > 0.3 ? "high" : errorRate > 0.2 ? "medium" : "low",
|
|
95
|
-
metric: "error-rate",
|
|
96
|
-
description: `Error rate ${(errorRate * 100).toFixed(1)}% exceeds threshold`,
|
|
97
|
-
detectedAt: new Date,
|
|
98
|
-
threshold: 0.1,
|
|
99
|
-
observedValue: errorRate
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
if (stat.p99LatencyMs > 500) {
|
|
103
|
-
detectedAnomalies.push({
|
|
104
|
-
operation: stat.operation,
|
|
105
|
-
severity: stat.p99LatencyMs > 1000 ? "high" : stat.p99LatencyMs > 750 ? "medium" : "low",
|
|
106
|
-
metric: "latency",
|
|
107
|
-
description: `P99 latency ${stat.p99LatencyMs.toFixed(0)}ms exceeds threshold`,
|
|
108
|
-
detectedAt: new Date,
|
|
109
|
-
threshold: 500,
|
|
110
|
-
observedValue: stat.p99LatencyMs
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
setUsageStats(stats);
|
|
115
|
-
setAnomalies(detectedAnomalies);
|
|
116
|
-
const newHints = detectedAnomalies.map((anomaly) => ({
|
|
117
|
-
operation: anomaly.operation,
|
|
118
|
-
category: anomaly.metric === "latency" ? "performance" : "error-handling",
|
|
119
|
-
summary: anomaly.metric === "latency" ? "Latency regression detected" : "Error spike detected",
|
|
120
|
-
justification: anomaly.description,
|
|
121
|
-
recommendedActions: anomaly.metric === "latency" ? [
|
|
122
|
-
"Add caching layer",
|
|
123
|
-
"Optimize database queries",
|
|
124
|
-
"Consider pagination"
|
|
125
|
-
] : [
|
|
126
|
-
"Add retry logic",
|
|
127
|
-
"Improve error handling",
|
|
128
|
-
"Add circuit breaker"
|
|
129
|
-
]
|
|
130
|
-
}));
|
|
131
|
-
setHints(newHints);
|
|
132
|
-
}, []);
|
|
133
|
-
const generateSuggestions = useCallback(async () => {
|
|
134
|
-
if (anomalies.length === 0)
|
|
135
|
-
return;
|
|
136
|
-
setLoading(true);
|
|
137
|
-
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
138
|
-
const newSuggestions = anomalies.map((anomaly) => ({
|
|
139
|
-
id: `suggestion-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
140
|
-
intent: {
|
|
141
|
-
id: `intent-${anomaly.operation.name}`,
|
|
142
|
-
type: anomaly.metric === "latency" ? "latency-regression" : anomaly.metric === "error-rate" ? "error-spike" : "throughput-drop",
|
|
143
|
-
description: anomaly.description,
|
|
144
|
-
operation: anomaly.operation,
|
|
145
|
-
confidence: {
|
|
146
|
-
score: anomaly.severity === "high" ? 0.9 : anomaly.severity === "medium" ? 0.7 : 0.5,
|
|
147
|
-
sampleSize: usageStats.find((s) => s.operation.name === anomaly.operation.name)?.totalCalls ?? 0
|
|
148
|
-
}
|
|
149
|
-
},
|
|
150
|
-
target: anomaly.operation,
|
|
151
|
-
proposal: {
|
|
152
|
-
summary: generateSuggestionSummary(anomaly),
|
|
153
|
-
rationale: generateSuggestionRationale(anomaly),
|
|
154
|
-
changeType: anomaly.metric === "error-rate" ? "policy-update" : "revision",
|
|
155
|
-
recommendedActions: generateRecommendedActions(anomaly)
|
|
156
|
-
},
|
|
157
|
-
confidence: anomaly.severity === "high" ? 0.85 : anomaly.severity === "medium" ? 0.7 : 0.55,
|
|
158
|
-
createdAt: new Date,
|
|
159
|
-
createdBy: "ai-evolution-agent",
|
|
160
|
-
status: "pending",
|
|
161
|
-
priority: anomaly.severity
|
|
162
|
-
}));
|
|
163
|
-
setSuggestions((prev) => [...prev, ...newSuggestions]);
|
|
164
|
-
setLoading(false);
|
|
165
|
-
}, [anomalies, usageStats]);
|
|
166
|
-
const approveSuggestion = useCallback((id, _notes) => {
|
|
167
|
-
setSuggestions((prev) => prev.map((s) => s.id === id ? { ...s, status: "approved" } : s));
|
|
168
|
-
}, []);
|
|
169
|
-
const rejectSuggestion = useCallback((id, _notes) => {
|
|
170
|
-
setSuggestions((prev) => prev.map((s) => s.id === id ? { ...s, status: "rejected" } : s));
|
|
171
|
-
}, []);
|
|
172
|
-
const clear = useCallback(() => {
|
|
173
|
-
metricsRef.current = [];
|
|
174
|
-
setOperationCount(0);
|
|
175
|
-
setUsageStats([]);
|
|
176
|
-
setAnomalies([]);
|
|
177
|
-
setSuggestions([]);
|
|
178
|
-
setHints([]);
|
|
179
|
-
localStorage.removeItem(`${EVOLUTION_STORAGE_KEY}-${templateId}`);
|
|
180
|
-
}, [templateId]);
|
|
181
|
-
return useMemo(() => ({
|
|
182
|
-
usageStats,
|
|
183
|
-
anomalies,
|
|
184
|
-
suggestions,
|
|
185
|
-
hints,
|
|
186
|
-
loading,
|
|
187
|
-
trackOperation,
|
|
188
|
-
analyzeUsage,
|
|
189
|
-
generateSuggestions,
|
|
190
|
-
approveSuggestion,
|
|
191
|
-
rejectSuggestion,
|
|
192
|
-
clear,
|
|
193
|
-
operationCount
|
|
194
|
-
}), [
|
|
195
|
-
usageStats,
|
|
196
|
-
anomalies,
|
|
197
|
-
suggestions,
|
|
198
|
-
hints,
|
|
199
|
-
loading,
|
|
200
|
-
trackOperation,
|
|
201
|
-
analyzeUsage,
|
|
202
|
-
generateSuggestions,
|
|
203
|
-
approveSuggestion,
|
|
204
|
-
rejectSuggestion,
|
|
205
|
-
clear,
|
|
206
|
-
operationCount
|
|
207
|
-
]);
|
|
208
|
-
}
|
|
209
|
-
function percentile(values, p) {
|
|
210
|
-
if (!values.length)
|
|
211
|
-
return 0;
|
|
212
|
-
if (values.length === 1)
|
|
213
|
-
return values[0] ?? 0;
|
|
214
|
-
const idx = Math.min(values.length - 1, Math.floor(p * values.length));
|
|
215
|
-
return values[idx] ?? 0;
|
|
216
|
-
}
|
|
217
|
-
function generateSuggestionSummary(anomaly) {
|
|
218
|
-
if (anomaly.metric === "latency") {
|
|
219
|
-
return `Add caching and pagination to ${anomaly.operation.name} to reduce latency`;
|
|
220
|
-
}
|
|
221
|
-
if (anomaly.metric === "error-rate") {
|
|
222
|
-
return `Add retry policy and circuit breaker to ${anomaly.operation.name}`;
|
|
223
|
-
}
|
|
224
|
-
return `Optimize ${anomaly.operation.name} for improved throughput`;
|
|
225
|
-
}
|
|
226
|
-
function generateSuggestionRationale(anomaly) {
|
|
227
|
-
if (anomaly.metric === "latency") {
|
|
228
|
-
return `The operation ${anomaly.operation.name} is experiencing P99 latency of ${anomaly.observedValue?.toFixed(0)}ms, which is above the recommended threshold of ${anomaly.threshold}ms. This can impact user experience and downstream operations.`;
|
|
229
|
-
}
|
|
230
|
-
if (anomaly.metric === "error-rate") {
|
|
231
|
-
return `The error rate for ${anomaly.operation.name} is ${((anomaly.observedValue ?? 0) * 100).toFixed(1)}%, indicating potential issues with input validation, external dependencies, or resource limits.`;
|
|
232
|
-
}
|
|
233
|
-
return `Throughput for ${anomaly.operation.name} has dropped significantly, suggesting potential bottlenecks or reduced demand that should be investigated.`;
|
|
234
|
-
}
|
|
235
|
-
function generateRecommendedActions(anomaly) {
|
|
236
|
-
if (anomaly.metric === "latency") {
|
|
237
|
-
return [
|
|
238
|
-
"Add response caching for frequently accessed data",
|
|
239
|
-
"Implement pagination for large result sets",
|
|
240
|
-
"Optimize database queries with proper indexing",
|
|
241
|
-
"Consider adding a GraphQL DataLoader for batching"
|
|
242
|
-
];
|
|
243
|
-
}
|
|
244
|
-
if (anomaly.metric === "error-rate") {
|
|
245
|
-
return [
|
|
246
|
-
"Add input validation at the contract level",
|
|
247
|
-
"Implement retry policy with exponential backoff",
|
|
248
|
-
"Add circuit breaker for external dependencies",
|
|
249
|
-
"Enhance error logging for better debugging"
|
|
250
|
-
];
|
|
251
|
-
}
|
|
252
|
-
return [
|
|
253
|
-
"Review resource allocation and scaling policies",
|
|
254
|
-
"Check for upstream routing or load balancer issues",
|
|
255
|
-
"Validate feature flag configurations",
|
|
256
|
-
"Monitor dependency health metrics"
|
|
257
|
-
];
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// src/EvolutionDashboard.tsx
|
|
261
|
-
import { Button, LoaderBlock } from "@contractspec/lib.design-system";
|
|
262
|
-
import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
|
|
263
|
-
import { Card } from "@contractspec/lib.ui-kit-web/ui/card";
|
|
264
|
-
import { useCallback as useCallback2, useMemo as useMemo2 } from "react";
|
|
265
|
-
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
266
|
-
"use client";
|
|
267
|
-
function EvolutionDashboard({
|
|
268
|
-
templateId,
|
|
269
|
-
onLog
|
|
270
|
-
}) {
|
|
271
|
-
const {
|
|
272
|
-
usageStats,
|
|
273
|
-
anomalies,
|
|
274
|
-
suggestions,
|
|
275
|
-
hints,
|
|
276
|
-
loading,
|
|
277
|
-
trackOperation,
|
|
278
|
-
analyzeUsage,
|
|
279
|
-
generateSuggestions,
|
|
280
|
-
approveSuggestion,
|
|
281
|
-
rejectSuggestion,
|
|
282
|
-
clear,
|
|
283
|
-
operationCount
|
|
284
|
-
} = useEvolution(templateId);
|
|
285
|
-
const handleSimulateOperations = useCallback2(() => {
|
|
286
|
-
const operations = [
|
|
287
|
-
{ name: `${templateId}.list`, duration: 150, success: true },
|
|
288
|
-
{ name: `${templateId}.list`, duration: 180, success: true },
|
|
289
|
-
{ name: `${templateId}.create`, duration: 350, success: true },
|
|
290
|
-
{
|
|
291
|
-
name: `${templateId}.create`,
|
|
292
|
-
duration: 420,
|
|
293
|
-
success: false,
|
|
294
|
-
error: "VALIDATION_ERROR"
|
|
295
|
-
},
|
|
296
|
-
{ name: `${templateId}.list`, duration: 200, success: true },
|
|
297
|
-
{ name: `${templateId}.get`, duration: 80, success: true },
|
|
298
|
-
{ name: `${templateId}.update`, duration: 280, success: true },
|
|
299
|
-
{ name: `${templateId}.list`, duration: 950, success: true },
|
|
300
|
-
{
|
|
301
|
-
name: `${templateId}.delete`,
|
|
302
|
-
duration: 150,
|
|
303
|
-
success: false,
|
|
304
|
-
error: "NOT_FOUND"
|
|
305
|
-
},
|
|
306
|
-
{ name: `${templateId}.create`, duration: 380, success: true }
|
|
307
|
-
];
|
|
308
|
-
for (const op of operations) {
|
|
309
|
-
trackOperation(op.name, op.duration, op.success, op.error);
|
|
310
|
-
}
|
|
311
|
-
onLog?.(`Simulated ${operations.length} operations`);
|
|
312
|
-
setTimeout(() => {
|
|
313
|
-
analyzeUsage();
|
|
314
|
-
onLog?.("Analysis complete");
|
|
315
|
-
}, 100);
|
|
316
|
-
}, [templateId, trackOperation, analyzeUsage, onLog]);
|
|
317
|
-
const handleGenerateSuggestions = useCallback2(async () => {
|
|
318
|
-
await generateSuggestions();
|
|
319
|
-
onLog?.("AI suggestions generated");
|
|
320
|
-
}, [generateSuggestions, onLog]);
|
|
321
|
-
const handleApproveSuggestion = useCallback2((id) => {
|
|
322
|
-
approveSuggestion(id);
|
|
323
|
-
onLog?.(`Suggestion ${id.slice(0, 8)} approved`);
|
|
324
|
-
}, [approveSuggestion, onLog]);
|
|
325
|
-
const handleRejectSuggestion = useCallback2((id) => {
|
|
326
|
-
rejectSuggestion(id);
|
|
327
|
-
onLog?.(`Suggestion ${id.slice(0, 8)} rejected`);
|
|
328
|
-
}, [rejectSuggestion, onLog]);
|
|
329
|
-
const handleClear = useCallback2(() => {
|
|
330
|
-
clear();
|
|
331
|
-
onLog?.("Evolution data cleared");
|
|
332
|
-
}, [clear, onLog]);
|
|
333
|
-
const pendingSuggestions = useMemo2(() => suggestions.filter((s) => s.status === "pending"), [suggestions]);
|
|
334
|
-
return /* @__PURE__ */ jsxDEV("div", {
|
|
335
|
-
className: "space-y-6",
|
|
336
|
-
children: [
|
|
337
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
338
|
-
className: "flex items-center justify-between",
|
|
339
|
-
children: [
|
|
340
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
341
|
-
children: [
|
|
342
|
-
/* @__PURE__ */ jsxDEV("h2", {
|
|
343
|
-
className: "font-semibold text-xl",
|
|
344
|
-
children: "AI Evolution Engine"
|
|
345
|
-
}, undefined, false, undefined, this),
|
|
346
|
-
/* @__PURE__ */ jsxDEV("p", {
|
|
347
|
-
className: "text-muted-foreground text-sm",
|
|
348
|
-
children: "Analyze usage patterns and get AI-powered suggestions"
|
|
349
|
-
}, undefined, false, undefined, this)
|
|
350
|
-
]
|
|
351
|
-
}, undefined, true, undefined, this),
|
|
352
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
353
|
-
className: "flex items-center gap-2",
|
|
354
|
-
children: [
|
|
355
|
-
/* @__PURE__ */ jsxDEV(Badge, {
|
|
356
|
-
variant: "secondary",
|
|
357
|
-
children: [
|
|
358
|
-
operationCount,
|
|
359
|
-
" ops tracked"
|
|
360
|
-
]
|
|
361
|
-
}, undefined, true, undefined, this),
|
|
362
|
-
/* @__PURE__ */ jsxDEV(Button, {
|
|
363
|
-
variant: "ghost",
|
|
364
|
-
size: "sm",
|
|
365
|
-
onPress: handleClear,
|
|
366
|
-
children: "Clear"
|
|
367
|
-
}, undefined, false, undefined, this)
|
|
368
|
-
]
|
|
369
|
-
}, undefined, true, undefined, this)
|
|
370
|
-
]
|
|
371
|
-
}, undefined, true, undefined, this),
|
|
372
|
-
/* @__PURE__ */ jsxDEV(Card, {
|
|
373
|
-
className: "p-4",
|
|
374
|
-
children: [
|
|
375
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
376
|
-
className: "flex flex-wrap items-center gap-3",
|
|
377
|
-
children: [
|
|
378
|
-
/* @__PURE__ */ jsxDEV(Button, {
|
|
379
|
-
variant: "default",
|
|
380
|
-
size: "sm",
|
|
381
|
-
onPress: handleSimulateOperations,
|
|
382
|
-
children: "Simulate Operations"
|
|
383
|
-
}, undefined, false, undefined, this),
|
|
384
|
-
/* @__PURE__ */ jsxDEV(Button, {
|
|
385
|
-
variant: "outline",
|
|
386
|
-
size: "sm",
|
|
387
|
-
onPress: analyzeUsage,
|
|
388
|
-
disabled: operationCount < 5,
|
|
389
|
-
children: "Analyze Usage"
|
|
390
|
-
}, undefined, false, undefined, this),
|
|
391
|
-
/* @__PURE__ */ jsxDEV(Button, {
|
|
392
|
-
variant: "outline",
|
|
393
|
-
size: "sm",
|
|
394
|
-
onPress: handleGenerateSuggestions,
|
|
395
|
-
disabled: anomalies.length === 0 || loading,
|
|
396
|
-
children: loading ? "Generating..." : "Generate AI Suggestions"
|
|
397
|
-
}, undefined, false, undefined, this)
|
|
398
|
-
]
|
|
399
|
-
}, undefined, true, undefined, this),
|
|
400
|
-
/* @__PURE__ */ jsxDEV("p", {
|
|
401
|
-
className: "mt-2 text-muted-foreground text-xs",
|
|
402
|
-
children: "Simulate sandbox operations, analyze patterns, and generate AI improvement suggestions."
|
|
403
|
-
}, undefined, false, undefined, this)
|
|
404
|
-
]
|
|
405
|
-
}, undefined, true, undefined, this),
|
|
406
|
-
loading && /* @__PURE__ */ jsxDEV(LoaderBlock, {
|
|
407
|
-
label: "Generating AI suggestions..."
|
|
408
|
-
}, undefined, false, undefined, this),
|
|
409
|
-
usageStats.length > 0 && /* @__PURE__ */ jsxDEV(Card, {
|
|
410
|
-
className: "p-4",
|
|
411
|
-
children: [
|
|
412
|
-
/* @__PURE__ */ jsxDEV("h3", {
|
|
413
|
-
className: "mb-3 font-semibold",
|
|
414
|
-
children: "Usage Statistics"
|
|
415
|
-
}, undefined, false, undefined, this),
|
|
416
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
417
|
-
className: "grid gap-3 md:grid-cols-2 lg:grid-cols-3",
|
|
418
|
-
children: usageStats.map((stat) => /* @__PURE__ */ jsxDEV(UsageStatCard, {
|
|
419
|
-
stat
|
|
420
|
-
}, stat.operation.name, false, undefined, this))
|
|
421
|
-
}, undefined, false, undefined, this)
|
|
422
|
-
]
|
|
423
|
-
}, undefined, true, undefined, this),
|
|
424
|
-
anomalies.length > 0 && /* @__PURE__ */ jsxDEV(Card, {
|
|
425
|
-
className: "p-4",
|
|
426
|
-
children: [
|
|
427
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
428
|
-
className: "mb-3 flex items-center justify-between",
|
|
429
|
-
children: [
|
|
430
|
-
/* @__PURE__ */ jsxDEV("h3", {
|
|
431
|
-
className: "font-semibold",
|
|
432
|
-
children: "Detected Anomalies"
|
|
433
|
-
}, undefined, false, undefined, this),
|
|
434
|
-
/* @__PURE__ */ jsxDEV(Badge, {
|
|
435
|
-
variant: "secondary",
|
|
436
|
-
className: "border-amber-500/30 bg-amber-500/20 text-amber-400",
|
|
437
|
-
children: [
|
|
438
|
-
anomalies.length,
|
|
439
|
-
" issues"
|
|
440
|
-
]
|
|
441
|
-
}, undefined, true, undefined, this)
|
|
442
|
-
]
|
|
443
|
-
}, undefined, true, undefined, this),
|
|
444
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
445
|
-
className: "space-y-2",
|
|
446
|
-
children: anomalies.map((anomaly, index) => /* @__PURE__ */ jsxDEV(AnomalyCard, {
|
|
447
|
-
anomaly
|
|
448
|
-
}, `${anomaly.operation.name}-${index}`, false, undefined, this))
|
|
449
|
-
}, undefined, false, undefined, this)
|
|
450
|
-
]
|
|
451
|
-
}, undefined, true, undefined, this),
|
|
452
|
-
suggestions.length > 0 && /* @__PURE__ */ jsxDEV(Card, {
|
|
453
|
-
className: "p-4",
|
|
454
|
-
children: [
|
|
455
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
456
|
-
className: "mb-3 flex items-center justify-between",
|
|
457
|
-
children: [
|
|
458
|
-
/* @__PURE__ */ jsxDEV("h3", {
|
|
459
|
-
className: "font-semibold",
|
|
460
|
-
children: "AI Suggestions"
|
|
461
|
-
}, undefined, false, undefined, this),
|
|
462
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
463
|
-
className: "flex items-center gap-2",
|
|
464
|
-
children: pendingSuggestions.length > 0 && /* @__PURE__ */ jsxDEV(Badge, {
|
|
465
|
-
variant: "secondary",
|
|
466
|
-
className: "border-amber-500/30 bg-amber-500/20 text-amber-400",
|
|
467
|
-
children: [
|
|
468
|
-
pendingSuggestions.length,
|
|
469
|
-
" pending"
|
|
470
|
-
]
|
|
471
|
-
}, undefined, true, undefined, this)
|
|
472
|
-
}, undefined, false, undefined, this)
|
|
473
|
-
]
|
|
474
|
-
}, undefined, true, undefined, this),
|
|
475
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
476
|
-
className: "space-y-3",
|
|
477
|
-
children: suggestions.map((suggestion) => /* @__PURE__ */ jsxDEV(SuggestionCard, {
|
|
478
|
-
suggestion,
|
|
479
|
-
onApprove: handleApproveSuggestion,
|
|
480
|
-
onReject: handleRejectSuggestion
|
|
481
|
-
}, suggestion.id, false, undefined, this))
|
|
482
|
-
}, undefined, false, undefined, this)
|
|
483
|
-
]
|
|
484
|
-
}, undefined, true, undefined, this),
|
|
485
|
-
hints.length > 0 && /* @__PURE__ */ jsxDEV(Card, {
|
|
486
|
-
className: "p-4",
|
|
487
|
-
children: [
|
|
488
|
-
/* @__PURE__ */ jsxDEV("h3", {
|
|
489
|
-
className: "mb-3 font-semibold",
|
|
490
|
-
children: "Optimization Hints"
|
|
491
|
-
}, undefined, false, undefined, this),
|
|
492
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
493
|
-
className: "space-y-2",
|
|
494
|
-
children: hints.map((hint, index) => /* @__PURE__ */ jsxDEV(HintCard, {
|
|
495
|
-
hint
|
|
496
|
-
}, `${hint.operation.name}-${index}`, false, undefined, this))
|
|
497
|
-
}, undefined, false, undefined, this)
|
|
498
|
-
]
|
|
499
|
-
}, undefined, true, undefined, this),
|
|
500
|
-
usageStats.length === 0 && anomalies.length === 0 && suggestions.length === 0 && /* @__PURE__ */ jsxDEV(Card, {
|
|
501
|
-
className: "p-8 text-center",
|
|
502
|
-
children: /* @__PURE__ */ jsxDEV("p", {
|
|
503
|
-
className: "text-muted-foreground",
|
|
504
|
-
children: 'Click "Simulate Operations" to generate sample data for analysis.'
|
|
505
|
-
}, undefined, false, undefined, this)
|
|
506
|
-
}, undefined, false, undefined, this)
|
|
507
|
-
]
|
|
508
|
-
}, undefined, true, undefined, this);
|
|
509
|
-
}
|
|
510
|
-
function UsageStatCard({ stat }) {
|
|
511
|
-
return /* @__PURE__ */ jsxDEV("div", {
|
|
512
|
-
className: "rounded-lg border border-violet-500/20 bg-violet-500/5 p-3",
|
|
513
|
-
children: [
|
|
514
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
515
|
-
className: "mb-2 flex items-center justify-between",
|
|
516
|
-
children: [
|
|
517
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
518
|
-
className: "font-medium font-mono text-sm",
|
|
519
|
-
children: stat.operation.name
|
|
520
|
-
}, undefined, false, undefined, this),
|
|
521
|
-
/* @__PURE__ */ jsxDEV(Badge, {
|
|
522
|
-
variant: stat.errorRate > 0.1 ? "destructive" : "default",
|
|
523
|
-
className: stat.errorRate > 0.1 ? "" : "border-green-500/30 bg-green-500/20 text-green-400",
|
|
524
|
-
children: [
|
|
525
|
-
((1 - stat.errorRate) * 100).toFixed(0),
|
|
526
|
-
"% success"
|
|
527
|
-
]
|
|
528
|
-
}, undefined, true, undefined, this)
|
|
529
|
-
]
|
|
530
|
-
}, undefined, true, undefined, this),
|
|
531
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
532
|
-
className: "grid grid-cols-2 gap-2 text-xs",
|
|
533
|
-
children: [
|
|
534
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
535
|
-
children: [
|
|
536
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
537
|
-
className: "text-muted-foreground",
|
|
538
|
-
children: "Total Calls:"
|
|
539
|
-
}, undefined, false, undefined, this),
|
|
540
|
-
" ",
|
|
541
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
542
|
-
className: "font-medium",
|
|
543
|
-
children: stat.totalCalls
|
|
544
|
-
}, undefined, false, undefined, this)
|
|
545
|
-
]
|
|
546
|
-
}, undefined, true, undefined, this),
|
|
547
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
548
|
-
children: [
|
|
549
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
550
|
-
className: "text-muted-foreground",
|
|
551
|
-
children: "Avg Latency:"
|
|
552
|
-
}, undefined, false, undefined, this),
|
|
553
|
-
" ",
|
|
554
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
555
|
-
className: "font-medium",
|
|
556
|
-
children: [
|
|
557
|
-
stat.averageLatencyMs.toFixed(0),
|
|
558
|
-
"ms"
|
|
559
|
-
]
|
|
560
|
-
}, undefined, true, undefined, this)
|
|
561
|
-
]
|
|
562
|
-
}, undefined, true, undefined, this),
|
|
563
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
564
|
-
children: [
|
|
565
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
566
|
-
className: "text-muted-foreground",
|
|
567
|
-
children: "P95:"
|
|
568
|
-
}, undefined, false, undefined, this),
|
|
569
|
-
" ",
|
|
570
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
571
|
-
className: "font-medium",
|
|
572
|
-
children: [
|
|
573
|
-
stat.p95LatencyMs.toFixed(0),
|
|
574
|
-
"ms"
|
|
575
|
-
]
|
|
576
|
-
}, undefined, true, undefined, this)
|
|
577
|
-
]
|
|
578
|
-
}, undefined, true, undefined, this),
|
|
579
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
580
|
-
children: [
|
|
581
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
582
|
-
className: "text-muted-foreground",
|
|
583
|
-
children: "P99:"
|
|
584
|
-
}, undefined, false, undefined, this),
|
|
585
|
-
" ",
|
|
586
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
587
|
-
className: "font-medium",
|
|
588
|
-
children: [
|
|
589
|
-
stat.p99LatencyMs.toFixed(0),
|
|
590
|
-
"ms"
|
|
591
|
-
]
|
|
592
|
-
}, undefined, true, undefined, this)
|
|
593
|
-
]
|
|
594
|
-
}, undefined, true, undefined, this)
|
|
595
|
-
]
|
|
596
|
-
}, undefined, true, undefined, this)
|
|
597
|
-
]
|
|
598
|
-
}, undefined, true, undefined, this);
|
|
599
|
-
}
|
|
600
|
-
function AnomalyCard({ anomaly }) {
|
|
601
|
-
const severityColors = {
|
|
602
|
-
low: "text-amber-400",
|
|
603
|
-
medium: "text-orange-400",
|
|
604
|
-
high: "text-red-400"
|
|
605
|
-
};
|
|
606
|
-
return /* @__PURE__ */ jsxDEV("div", {
|
|
607
|
-
className: "flex items-center justify-between rounded-lg border border-amber-500/30 bg-amber-500/5 p-3",
|
|
608
|
-
children: [
|
|
609
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
610
|
-
className: "flex items-center gap-3",
|
|
611
|
-
children: [
|
|
612
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
613
|
-
className: `text-lg ${severityColors[anomaly.severity]}`,
|
|
614
|
-
title: `${anomaly.severity} severity`,
|
|
615
|
-
children: anomaly.severity === "high" ? "\uD83D\uDD34" : anomaly.severity === "medium" ? "\uD83D\uDFE0" : "\uD83D\uDFE1"
|
|
616
|
-
}, undefined, false, undefined, this),
|
|
617
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
618
|
-
children: [
|
|
619
|
-
/* @__PURE__ */ jsxDEV("p", {
|
|
620
|
-
className: "font-medium text-sm",
|
|
621
|
-
children: anomaly.description
|
|
622
|
-
}, undefined, false, undefined, this),
|
|
623
|
-
/* @__PURE__ */ jsxDEV("p", {
|
|
624
|
-
className: "text-muted-foreground text-xs",
|
|
625
|
-
children: [
|
|
626
|
-
anomaly.operation.name,
|
|
627
|
-
" \u2022 ",
|
|
628
|
-
anomaly.metric
|
|
629
|
-
]
|
|
630
|
-
}, undefined, true, undefined, this)
|
|
631
|
-
]
|
|
632
|
-
}, undefined, true, undefined, this)
|
|
633
|
-
]
|
|
634
|
-
}, undefined, true, undefined, this),
|
|
635
|
-
/* @__PURE__ */ jsxDEV(Badge, {
|
|
636
|
-
variant: anomaly.severity === "high" ? "destructive" : "secondary",
|
|
637
|
-
className: anomaly.severity === "medium" ? "border-amber-500/30 bg-amber-500/20 text-amber-400" : "",
|
|
638
|
-
children: anomaly.severity
|
|
639
|
-
}, undefined, false, undefined, this)
|
|
640
|
-
]
|
|
641
|
-
}, undefined, true, undefined, this);
|
|
642
|
-
}
|
|
643
|
-
function SuggestionCard({
|
|
644
|
-
suggestion,
|
|
645
|
-
onApprove,
|
|
646
|
-
onReject
|
|
647
|
-
}) {
|
|
648
|
-
const getStatusStyles = (status) => {
|
|
649
|
-
switch (status) {
|
|
650
|
-
case "pending":
|
|
651
|
-
return {
|
|
652
|
-
variant: "secondary",
|
|
653
|
-
className: "bg-amber-500/20 text-amber-400 border-amber-500/30"
|
|
654
|
-
};
|
|
655
|
-
case "approved":
|
|
656
|
-
return {
|
|
657
|
-
variant: "default",
|
|
658
|
-
className: "bg-green-500/20 text-green-400 border-green-500/30"
|
|
659
|
-
};
|
|
660
|
-
case "rejected":
|
|
661
|
-
return { variant: "destructive", className: "" };
|
|
662
|
-
default:
|
|
663
|
-
return { variant: "secondary", className: "" };
|
|
664
|
-
}
|
|
665
|
-
};
|
|
666
|
-
const statusStyle = getStatusStyles(suggestion.status);
|
|
667
|
-
return /* @__PURE__ */ jsxDEV("div", {
|
|
668
|
-
className: "rounded-lg border border-violet-500/30 bg-violet-500/5 p-4",
|
|
669
|
-
children: [
|
|
670
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
671
|
-
className: "mb-2 flex items-start justify-between",
|
|
672
|
-
children: [
|
|
673
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
674
|
-
className: "flex-1",
|
|
675
|
-
children: [
|
|
676
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
677
|
-
className: "flex items-center gap-2",
|
|
678
|
-
children: [
|
|
679
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
680
|
-
className: "text-lg",
|
|
681
|
-
children: suggestion.intent.type === "latency-regression" ? "\u26A1" : suggestion.intent.type === "error-spike" ? "\uD83D\uDD25" : "\uD83D\uDCC9"
|
|
682
|
-
}, undefined, false, undefined, this),
|
|
683
|
-
/* @__PURE__ */ jsxDEV("h4", {
|
|
684
|
-
className: "font-medium",
|
|
685
|
-
children: suggestion.proposal.summary
|
|
686
|
-
}, undefined, false, undefined, this)
|
|
687
|
-
]
|
|
688
|
-
}, undefined, true, undefined, this),
|
|
689
|
-
/* @__PURE__ */ jsxDEV("p", {
|
|
690
|
-
className: "mt-1 text-muted-foreground text-sm",
|
|
691
|
-
children: suggestion.proposal.rationale
|
|
692
|
-
}, undefined, false, undefined, this)
|
|
693
|
-
]
|
|
694
|
-
}, undefined, true, undefined, this),
|
|
695
|
-
/* @__PURE__ */ jsxDEV(Badge, {
|
|
696
|
-
variant: statusStyle.variant,
|
|
697
|
-
className: statusStyle.className,
|
|
698
|
-
children: suggestion.status
|
|
699
|
-
}, undefined, false, undefined, this)
|
|
700
|
-
]
|
|
701
|
-
}, undefined, true, undefined, this),
|
|
702
|
-
suggestion.proposal.recommendedActions && suggestion.proposal.recommendedActions.length > 0 && /* @__PURE__ */ jsxDEV("div", {
|
|
703
|
-
className: "mt-3",
|
|
704
|
-
children: [
|
|
705
|
-
/* @__PURE__ */ jsxDEV("p", {
|
|
706
|
-
className: "mb-1 font-semibold text-violet-400 text-xs uppercase",
|
|
707
|
-
children: "Recommended Actions"
|
|
708
|
-
}, undefined, false, undefined, this),
|
|
709
|
-
/* @__PURE__ */ jsxDEV("ul", {
|
|
710
|
-
className: "list-inside list-disc space-y-1 text-xs",
|
|
711
|
-
children: suggestion.proposal.recommendedActions.slice(0, 3).map((action, i) => /* @__PURE__ */ jsxDEV("li", {
|
|
712
|
-
children: action
|
|
713
|
-
}, i, false, undefined, this))
|
|
714
|
-
}, undefined, false, undefined, this)
|
|
715
|
-
]
|
|
716
|
-
}, undefined, true, undefined, this),
|
|
717
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
718
|
-
className: "mt-3 flex items-center justify-between",
|
|
719
|
-
children: [
|
|
720
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
721
|
-
className: "flex items-center gap-2 text-xs",
|
|
722
|
-
children: [
|
|
723
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
724
|
-
className: "text-muted-foreground",
|
|
725
|
-
children: [
|
|
726
|
-
"Confidence: ",
|
|
727
|
-
(suggestion.confidence * 100).toFixed(0),
|
|
728
|
-
"%"
|
|
729
|
-
]
|
|
730
|
-
}, undefined, true, undefined, this),
|
|
731
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
732
|
-
className: "text-muted-foreground",
|
|
733
|
-
children: "\u2022"
|
|
734
|
-
}, undefined, false, undefined, this),
|
|
735
|
-
/* @__PURE__ */ jsxDEV(Badge, {
|
|
736
|
-
variant: "secondary",
|
|
737
|
-
children: suggestion.priority
|
|
738
|
-
}, undefined, false, undefined, this)
|
|
739
|
-
]
|
|
740
|
-
}, undefined, true, undefined, this),
|
|
741
|
-
suggestion.status === "pending" && /* @__PURE__ */ jsxDEV("div", {
|
|
742
|
-
className: "flex items-center gap-2",
|
|
743
|
-
children: [
|
|
744
|
-
/* @__PURE__ */ jsxDEV(Button, {
|
|
745
|
-
variant: "outline",
|
|
746
|
-
size: "sm",
|
|
747
|
-
onPress: () => onReject(suggestion.id),
|
|
748
|
-
children: "Reject"
|
|
749
|
-
}, undefined, false, undefined, this),
|
|
750
|
-
/* @__PURE__ */ jsxDEV(Button, {
|
|
751
|
-
variant: "default",
|
|
752
|
-
size: "sm",
|
|
753
|
-
onPress: () => onApprove(suggestion.id),
|
|
754
|
-
children: "Approve"
|
|
755
|
-
}, undefined, false, undefined, this)
|
|
756
|
-
]
|
|
757
|
-
}, undefined, true, undefined, this)
|
|
758
|
-
]
|
|
759
|
-
}, undefined, true, undefined, this)
|
|
760
|
-
]
|
|
761
|
-
}, undefined, true, undefined, this);
|
|
762
|
-
}
|
|
763
|
-
function HintCard({ hint }) {
|
|
764
|
-
const categoryIcons = {
|
|
765
|
-
schema: "\uD83D\uDCD0",
|
|
766
|
-
policy: "\uD83D\uDD12",
|
|
767
|
-
performance: "\u26A1",
|
|
768
|
-
"error-handling": "\uD83D\uDEE1\uFE0F"
|
|
769
|
-
};
|
|
770
|
-
return /* @__PURE__ */ jsxDEV("div", {
|
|
771
|
-
className: "rounded-lg border border-blue-500/20 bg-blue-500/5 p-3",
|
|
772
|
-
children: /* @__PURE__ */ jsxDEV("div", {
|
|
773
|
-
className: "flex items-start gap-3",
|
|
774
|
-
children: [
|
|
775
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
776
|
-
className: "text-lg",
|
|
777
|
-
children: categoryIcons[hint.category]
|
|
778
|
-
}, undefined, false, undefined, this),
|
|
779
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
780
|
-
className: "flex-1",
|
|
781
|
-
children: [
|
|
782
|
-
/* @__PURE__ */ jsxDEV("p", {
|
|
783
|
-
className: "font-medium",
|
|
784
|
-
children: hint.summary
|
|
785
|
-
}, undefined, false, undefined, this),
|
|
786
|
-
/* @__PURE__ */ jsxDEV("p", {
|
|
787
|
-
className: "mt-1 text-muted-foreground text-xs",
|
|
788
|
-
children: hint.justification
|
|
789
|
-
}, undefined, false, undefined, this),
|
|
790
|
-
hint.recommendedActions.length > 0 && /* @__PURE__ */ jsxDEV("ul", {
|
|
791
|
-
className: "mt-2 list-inside list-disc text-xs",
|
|
792
|
-
children: hint.recommendedActions.slice(0, 2).map((action, i) => /* @__PURE__ */ jsxDEV("li", {
|
|
793
|
-
children: action
|
|
794
|
-
}, i, false, undefined, this))
|
|
795
|
-
}, undefined, false, undefined, this)
|
|
796
|
-
]
|
|
797
|
-
}, undefined, true, undefined, this)
|
|
798
|
-
]
|
|
799
|
-
}, undefined, true, undefined, this)
|
|
800
|
-
}, undefined, false, undefined, this);
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
// src/EvolutionSidebar.tsx
|
|
804
|
-
import { Button as Button2 } from "@contractspec/lib.design-system";
|
|
805
|
-
import { Badge as Badge2 } from "@contractspec/lib.ui-kit-web/ui/badge";
|
|
806
|
-
import { Card as Card2 } from "@contractspec/lib.ui-kit-web/ui/card";
|
|
807
|
-
import { useCallback as useCallback3, useMemo as useMemo3 } from "react";
|
|
808
|
-
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
809
|
-
"use client";
|
|
810
|
-
function EvolutionSidebar({
|
|
811
|
-
templateId,
|
|
812
|
-
expanded = false,
|
|
813
|
-
onToggle,
|
|
814
|
-
onLog,
|
|
815
|
-
onOpenEvolution
|
|
816
|
-
}) {
|
|
817
|
-
const {
|
|
818
|
-
anomalies,
|
|
819
|
-
suggestions,
|
|
820
|
-
loading,
|
|
821
|
-
approveSuggestion,
|
|
822
|
-
rejectSuggestion,
|
|
823
|
-
operationCount
|
|
824
|
-
} = useEvolution(templateId);
|
|
825
|
-
const pendingSuggestions = useMemo3(() => suggestions.filter((s) => s.status === "pending"), [suggestions]);
|
|
826
|
-
const topAnomalies = useMemo3(() => anomalies.sort((a, b) => {
|
|
827
|
-
const severityOrder = { high: 0, medium: 1, low: 2 };
|
|
828
|
-
return severityOrder[a.severity] - severityOrder[b.severity];
|
|
829
|
-
}).slice(0, 3), [anomalies]);
|
|
830
|
-
const handleApprove = useCallback3((id) => {
|
|
831
|
-
approveSuggestion(id);
|
|
832
|
-
onLog?.(`Approved suggestion ${id.slice(0, 8)}`);
|
|
833
|
-
}, [approveSuggestion, onLog]);
|
|
834
|
-
const handleReject = useCallback3((id) => {
|
|
835
|
-
rejectSuggestion(id);
|
|
836
|
-
onLog?.(`Rejected suggestion ${id.slice(0, 8)}`);
|
|
837
|
-
}, [rejectSuggestion, onLog]);
|
|
838
|
-
if (!expanded) {
|
|
839
|
-
return /* @__PURE__ */ jsxDEV2("button", {
|
|
840
|
-
onClick: onToggle,
|
|
841
|
-
className: "flex items-center gap-2 rounded-lg border border-violet-500/30 bg-violet-500/10 px-3 py-2 text-sm transition hover:bg-violet-500/20",
|
|
842
|
-
type: "button",
|
|
843
|
-
children: [
|
|
844
|
-
/* @__PURE__ */ jsxDEV2("span", {
|
|
845
|
-
children: "\uD83E\uDD16"
|
|
846
|
-
}, undefined, false, undefined, this),
|
|
847
|
-
/* @__PURE__ */ jsxDEV2("span", {
|
|
848
|
-
children: "Evolution"
|
|
849
|
-
}, undefined, false, undefined, this),
|
|
850
|
-
pendingSuggestions.length > 0 && /* @__PURE__ */ jsxDEV2(Badge2, {
|
|
851
|
-
variant: "secondary",
|
|
852
|
-
className: "border-amber-500/30 bg-amber-500/20 text-amber-400",
|
|
853
|
-
children: pendingSuggestions.length
|
|
854
|
-
}, undefined, false, undefined, this),
|
|
855
|
-
anomalies.length > 0 && pendingSuggestions.length === 0 && /* @__PURE__ */ jsxDEV2(Badge2, {
|
|
856
|
-
variant: "destructive",
|
|
857
|
-
children: anomalies.length
|
|
858
|
-
}, undefined, false, undefined, this)
|
|
859
|
-
]
|
|
860
|
-
}, undefined, true, undefined, this);
|
|
861
|
-
}
|
|
862
|
-
return /* @__PURE__ */ jsxDEV2(Card2, {
|
|
863
|
-
className: "w-80 overflow-hidden",
|
|
864
|
-
children: [
|
|
865
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
866
|
-
className: "flex items-center justify-between border-violet-500/20 border-b bg-violet-500/5 px-3 py-2",
|
|
867
|
-
children: [
|
|
868
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
869
|
-
className: "flex items-center gap-2",
|
|
870
|
-
children: [
|
|
871
|
-
/* @__PURE__ */ jsxDEV2("span", {
|
|
872
|
-
children: "\uD83E\uDD16"
|
|
873
|
-
}, undefined, false, undefined, this),
|
|
874
|
-
/* @__PURE__ */ jsxDEV2("span", {
|
|
875
|
-
className: "font-semibold text-sm",
|
|
876
|
-
children: "Evolution"
|
|
877
|
-
}, undefined, false, undefined, this)
|
|
878
|
-
]
|
|
879
|
-
}, undefined, true, undefined, this),
|
|
880
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
881
|
-
className: "flex items-center gap-1",
|
|
882
|
-
children: [
|
|
883
|
-
onOpenEvolution && /* @__PURE__ */ jsxDEV2(Button2, {
|
|
884
|
-
variant: "ghost",
|
|
885
|
-
size: "sm",
|
|
886
|
-
onPress: onOpenEvolution,
|
|
887
|
-
children: "Expand"
|
|
888
|
-
}, undefined, false, undefined, this),
|
|
889
|
-
/* @__PURE__ */ jsxDEV2("button", {
|
|
890
|
-
onClick: onToggle,
|
|
891
|
-
className: "p-1 text-muted-foreground hover:text-foreground",
|
|
892
|
-
type: "button",
|
|
893
|
-
title: "Collapse",
|
|
894
|
-
children: "\u2715"
|
|
895
|
-
}, undefined, false, undefined, this)
|
|
896
|
-
]
|
|
897
|
-
}, undefined, true, undefined, this)
|
|
898
|
-
]
|
|
899
|
-
}, undefined, true, undefined, this),
|
|
900
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
901
|
-
className: "max-h-96 overflow-y-auto p-3",
|
|
902
|
-
children: [
|
|
903
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
904
|
-
className: "mb-3 flex items-center justify-between text-xs",
|
|
905
|
-
children: [
|
|
906
|
-
/* @__PURE__ */ jsxDEV2("span", {
|
|
907
|
-
className: "text-muted-foreground",
|
|
908
|
-
children: [
|
|
909
|
-
operationCount,
|
|
910
|
-
" ops tracked"
|
|
911
|
-
]
|
|
912
|
-
}, undefined, true, undefined, this),
|
|
913
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
914
|
-
className: "flex items-center gap-2",
|
|
915
|
-
children: [
|
|
916
|
-
anomalies.length > 0 && /* @__PURE__ */ jsxDEV2(Badge2, {
|
|
917
|
-
variant: "destructive",
|
|
918
|
-
children: [
|
|
919
|
-
anomalies.length,
|
|
920
|
-
" anomalies"
|
|
921
|
-
]
|
|
922
|
-
}, undefined, true, undefined, this),
|
|
923
|
-
pendingSuggestions.length > 0 && /* @__PURE__ */ jsxDEV2(Badge2, {
|
|
924
|
-
variant: "secondary",
|
|
925
|
-
className: "border-amber-500/30 bg-amber-500/20 text-amber-400",
|
|
926
|
-
children: [
|
|
927
|
-
pendingSuggestions.length,
|
|
928
|
-
" pending"
|
|
929
|
-
]
|
|
930
|
-
}, undefined, true, undefined, this)
|
|
931
|
-
]
|
|
932
|
-
}, undefined, true, undefined, this)
|
|
933
|
-
]
|
|
934
|
-
}, undefined, true, undefined, this),
|
|
935
|
-
loading && /* @__PURE__ */ jsxDEV2("div", {
|
|
936
|
-
className: "py-4 text-center text-muted-foreground text-sm",
|
|
937
|
-
children: "Generating suggestions..."
|
|
938
|
-
}, undefined, false, undefined, this),
|
|
939
|
-
topAnomalies.length > 0 && /* @__PURE__ */ jsxDEV2("div", {
|
|
940
|
-
className: "mb-4",
|
|
941
|
-
children: [
|
|
942
|
-
/* @__PURE__ */ jsxDEV2("p", {
|
|
943
|
-
className: "mb-2 font-semibold text-violet-400 text-xs uppercase",
|
|
944
|
-
children: "Top Issues"
|
|
945
|
-
}, undefined, false, undefined, this),
|
|
946
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
947
|
-
className: "space-y-2",
|
|
948
|
-
children: topAnomalies.map((anomaly, index) => /* @__PURE__ */ jsxDEV2("div", {
|
|
949
|
-
className: "rounded border border-amber-500/20 bg-amber-500/5 p-2 text-xs",
|
|
950
|
-
children: [
|
|
951
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
952
|
-
className: "flex items-center gap-2",
|
|
953
|
-
children: [
|
|
954
|
-
/* @__PURE__ */ jsxDEV2("span", {
|
|
955
|
-
children: anomaly.severity === "high" ? "\uD83D\uDD34" : anomaly.severity === "medium" ? "\uD83D\uDFE0" : "\uD83D\uDFE1"
|
|
956
|
-
}, undefined, false, undefined, this),
|
|
957
|
-
/* @__PURE__ */ jsxDEV2("span", {
|
|
958
|
-
className: "truncate font-medium",
|
|
959
|
-
children: anomaly.operation.name
|
|
960
|
-
}, undefined, false, undefined, this)
|
|
961
|
-
]
|
|
962
|
-
}, undefined, true, undefined, this),
|
|
963
|
-
/* @__PURE__ */ jsxDEV2("p", {
|
|
964
|
-
className: "mt-1 truncate text-muted-foreground",
|
|
965
|
-
children: anomaly.description
|
|
966
|
-
}, undefined, false, undefined, this)
|
|
967
|
-
]
|
|
968
|
-
}, `${anomaly.operation.name}-${index}`, true, undefined, this))
|
|
969
|
-
}, undefined, false, undefined, this)
|
|
970
|
-
]
|
|
971
|
-
}, undefined, true, undefined, this),
|
|
972
|
-
pendingSuggestions.length > 0 && /* @__PURE__ */ jsxDEV2("div", {
|
|
973
|
-
children: [
|
|
974
|
-
/* @__PURE__ */ jsxDEV2("p", {
|
|
975
|
-
className: "mb-2 font-semibold text-violet-400 text-xs uppercase",
|
|
976
|
-
children: "Pending Suggestions"
|
|
977
|
-
}, undefined, false, undefined, this),
|
|
978
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
979
|
-
className: "space-y-2",
|
|
980
|
-
children: [
|
|
981
|
-
pendingSuggestions.slice(0, 3).map((suggestion) => /* @__PURE__ */ jsxDEV2(CompactSuggestionCard, {
|
|
982
|
-
suggestion,
|
|
983
|
-
onApprove: handleApprove,
|
|
984
|
-
onReject: handleReject
|
|
985
|
-
}, suggestion.id, false, undefined, this)),
|
|
986
|
-
pendingSuggestions.length > 3 && /* @__PURE__ */ jsxDEV2("p", {
|
|
987
|
-
className: "text-center text-muted-foreground text-xs",
|
|
988
|
-
children: [
|
|
989
|
-
"+",
|
|
990
|
-
pendingSuggestions.length - 3,
|
|
991
|
-
" more suggestions"
|
|
992
|
-
]
|
|
993
|
-
}, undefined, true, undefined, this)
|
|
994
|
-
]
|
|
995
|
-
}, undefined, true, undefined, this)
|
|
996
|
-
]
|
|
997
|
-
}, undefined, true, undefined, this),
|
|
998
|
-
anomalies.length === 0 && pendingSuggestions.length === 0 && !loading && /* @__PURE__ */ jsxDEV2("div", {
|
|
999
|
-
className: "py-4 text-center text-muted-foreground text-xs",
|
|
1000
|
-
children: "No issues detected. Keep coding!"
|
|
1001
|
-
}, undefined, false, undefined, this)
|
|
1002
|
-
]
|
|
1003
|
-
}, undefined, true, undefined, this),
|
|
1004
|
-
onOpenEvolution && /* @__PURE__ */ jsxDEV2("div", {
|
|
1005
|
-
className: "border-violet-500/20 border-t p-2",
|
|
1006
|
-
children: /* @__PURE__ */ jsxDEV2(Button2, {
|
|
1007
|
-
variant: "ghost",
|
|
1008
|
-
size: "sm",
|
|
1009
|
-
className: "w-full",
|
|
1010
|
-
onPress: onOpenEvolution,
|
|
1011
|
-
children: "Open Evolution Dashboard \u2192"
|
|
1012
|
-
}, undefined, false, undefined, this)
|
|
1013
|
-
}, undefined, false, undefined, this)
|
|
1014
|
-
]
|
|
1015
|
-
}, undefined, true, undefined, this);
|
|
1016
|
-
}
|
|
1017
|
-
function CompactSuggestionCard({
|
|
1018
|
-
suggestion,
|
|
1019
|
-
onApprove,
|
|
1020
|
-
onReject
|
|
1021
|
-
}) {
|
|
1022
|
-
return /* @__PURE__ */ jsxDEV2("div", {
|
|
1023
|
-
className: "rounded border border-violet-500/20 bg-violet-500/5 p-2",
|
|
1024
|
-
children: [
|
|
1025
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
1026
|
-
className: "flex items-start justify-between gap-2",
|
|
1027
|
-
children: /* @__PURE__ */ jsxDEV2("div", {
|
|
1028
|
-
className: "min-w-0 flex-1",
|
|
1029
|
-
children: [
|
|
1030
|
-
/* @__PURE__ */ jsxDEV2("p", {
|
|
1031
|
-
className: "truncate font-medium text-xs",
|
|
1032
|
-
children: suggestion.proposal.summary
|
|
1033
|
-
}, undefined, false, undefined, this),
|
|
1034
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
1035
|
-
className: "mt-1 flex items-center gap-2 text-xs",
|
|
1036
|
-
children: [
|
|
1037
|
-
/* @__PURE__ */ jsxDEV2(Badge2, {
|
|
1038
|
-
variant: "secondary",
|
|
1039
|
-
children: suggestion.priority
|
|
1040
|
-
}, undefined, false, undefined, this),
|
|
1041
|
-
/* @__PURE__ */ jsxDEV2("span", {
|
|
1042
|
-
className: "text-muted-foreground",
|
|
1043
|
-
children: [
|
|
1044
|
-
(suggestion.confidence * 100).toFixed(0),
|
|
1045
|
-
"%"
|
|
1046
|
-
]
|
|
1047
|
-
}, undefined, true, undefined, this)
|
|
1048
|
-
]
|
|
1049
|
-
}, undefined, true, undefined, this)
|
|
1050
|
-
]
|
|
1051
|
-
}, undefined, true, undefined, this)
|
|
1052
|
-
}, undefined, false, undefined, this),
|
|
1053
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
1054
|
-
className: "mt-2 flex justify-end gap-1",
|
|
1055
|
-
children: [
|
|
1056
|
-
/* @__PURE__ */ jsxDEV2("button", {
|
|
1057
|
-
onClick: () => onReject(suggestion.id),
|
|
1058
|
-
className: "rounded px-2 py-0.5 text-red-400 text-xs hover:bg-red-400/10",
|
|
1059
|
-
type: "button",
|
|
1060
|
-
children: "Reject"
|
|
1061
|
-
}, undefined, false, undefined, this),
|
|
1062
|
-
/* @__PURE__ */ jsxDEV2("button", {
|
|
1063
|
-
onClick: () => onApprove(suggestion.id),
|
|
1064
|
-
className: "rounded bg-violet-500/20 px-2 py-0.5 text-violet-400 text-xs hover:bg-violet-500/30",
|
|
1065
|
-
type: "button",
|
|
1066
|
-
children: "Approve"
|
|
1067
|
-
}, undefined, false, undefined, this)
|
|
1068
|
-
]
|
|
1069
|
-
}, undefined, true, undefined, this)
|
|
1070
|
-
]
|
|
1071
|
-
}, undefined, true, undefined, this);
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
// src/lib/runtime-context.tsx
|
|
1075
|
-
import { createContext, useContext } from "react";
|
|
1076
|
-
"use client";
|
|
1077
|
-
var TemplateRuntimeContext = createContext(null);
|
|
1078
|
-
function useTemplateRuntime() {
|
|
1079
|
-
const context = useContext(TemplateRuntimeContext);
|
|
1080
|
-
if (!context) {
|
|
1081
|
-
throw new Error("useTemplateRuntime must be used within a TemplateRuntimeProvider");
|
|
1082
|
-
}
|
|
1083
|
-
return context;
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
// src/LocalDataIndicator.tsx
|
|
1087
|
-
import { RefreshCw, Shield } from "lucide-react";
|
|
1088
|
-
import { useState as useState2 } from "react";
|
|
1089
|
-
import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
|
|
1090
|
-
"use client";
|
|
1091
|
-
function LocalDataIndicator() {
|
|
1092
|
-
const { projectId, templateId, template, installer } = useTemplateRuntime();
|
|
1093
|
-
const [isResetting, setIsResetting] = useState2(false);
|
|
1094
|
-
const handleReset = async () => {
|
|
1095
|
-
setIsResetting(true);
|
|
1096
|
-
try {
|
|
1097
|
-
await installer.install(templateId, { projectId });
|
|
1098
|
-
} finally {
|
|
1099
|
-
setIsResetting(false);
|
|
1100
|
-
}
|
|
1101
|
-
};
|
|
1102
|
-
return /* @__PURE__ */ jsxDEV3("div", {
|
|
1103
|
-
className: "inline-flex items-center gap-2 rounded-full border border-border bg-muted/40 px-3 py-1 text-muted-foreground text-xs",
|
|
1104
|
-
children: [
|
|
1105
|
-
/* @__PURE__ */ jsxDEV3(Shield, {
|
|
1106
|
-
className: "h-3.5 w-3.5 text-violet-400"
|
|
1107
|
-
}, undefined, false, undefined, this),
|
|
1108
|
-
/* @__PURE__ */ jsxDEV3("span", {
|
|
1109
|
-
children: [
|
|
1110
|
-
"Local runtime \xB7",
|
|
1111
|
-
" ",
|
|
1112
|
-
/* @__PURE__ */ jsxDEV3("span", {
|
|
1113
|
-
className: "font-semibold text-foreground",
|
|
1114
|
-
children: template.name
|
|
1115
|
-
}, undefined, false, undefined, this)
|
|
1116
|
-
]
|
|
1117
|
-
}, undefined, true, undefined, this),
|
|
1118
|
-
/* @__PURE__ */ jsxDEV3("button", {
|
|
1119
|
-
type: "button",
|
|
1120
|
-
className: "inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 font-semibold text-[11px] text-muted-foreground hover:text-foreground",
|
|
1121
|
-
onClick: handleReset,
|
|
1122
|
-
disabled: isResetting,
|
|
1123
|
-
children: [
|
|
1124
|
-
/* @__PURE__ */ jsxDEV3(RefreshCw, {
|
|
1125
|
-
className: "h-3 w-3"
|
|
1126
|
-
}, undefined, false, undefined, this),
|
|
1127
|
-
isResetting ? "Resetting\u2026" : "Reset data"
|
|
1128
|
-
]
|
|
1129
|
-
}, undefined, true, undefined, this)
|
|
1130
|
-
]
|
|
1131
|
-
}, undefined, true, undefined, this);
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
// src/markdown/formatPresentationName.ts
|
|
1135
|
-
function formatPresentationName(name) {
|
|
1136
|
-
const parts = name.split(".");
|
|
1137
|
-
const lastPart = parts[parts.length - 1] ?? name;
|
|
1138
|
-
return lastPart.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
// src/markdown/useMarkdownPresentation.ts
|
|
1142
|
-
import { useCallback as useCallback4, useEffect as useEffect2, useState as useState3 } from "react";
|
|
1143
|
-
"use client";
|
|
1144
|
-
function useMarkdownPresentation({
|
|
1145
|
-
engine,
|
|
1146
|
-
fetchData,
|
|
1147
|
-
presentationId,
|
|
1148
|
-
presentations,
|
|
1149
|
-
resolvePresentation,
|
|
1150
|
-
templateId
|
|
1151
|
-
}) {
|
|
1152
|
-
const [selectedPresentation, setSelectedPresentation] = useState3("");
|
|
1153
|
-
const [markdownContent, setMarkdownContent] = useState3("");
|
|
1154
|
-
const [loading, setLoading] = useState3(false);
|
|
1155
|
-
const [error, setError] = useState3(null);
|
|
1156
|
-
useEffect2(() => {
|
|
1157
|
-
if (presentationId && presentations.includes(presentationId)) {
|
|
1158
|
-
setSelectedPresentation(presentationId);
|
|
1159
|
-
return;
|
|
1160
|
-
}
|
|
1161
|
-
if (presentations.length === 0) {
|
|
1162
|
-
setSelectedPresentation("");
|
|
1163
|
-
return;
|
|
1164
|
-
}
|
|
1165
|
-
if (!presentations.includes(selectedPresentation)) {
|
|
1166
|
-
setSelectedPresentation(presentations[0] ?? "");
|
|
1167
|
-
}
|
|
1168
|
-
}, [presentationId, presentations, selectedPresentation, templateId]);
|
|
1169
|
-
const renderMarkdown = useCallback4(async () => {
|
|
1170
|
-
if (!selectedPresentation || !engine)
|
|
1171
|
-
return;
|
|
1172
|
-
setLoading(true);
|
|
1173
|
-
setError(null);
|
|
1174
|
-
try {
|
|
1175
|
-
if (!resolvePresentation) {
|
|
1176
|
-
throw new Error("resolvePresentation not available in runtime context");
|
|
1177
|
-
}
|
|
1178
|
-
const descriptor = resolvePresentation(selectedPresentation);
|
|
1179
|
-
if (!descriptor) {
|
|
1180
|
-
throw new Error(`Presentation descriptor not found: ${selectedPresentation}`);
|
|
1181
|
-
}
|
|
1182
|
-
const dataResult = await fetchData(selectedPresentation);
|
|
1183
|
-
const result = await engine.render("markdown", descriptor, { data: dataResult.data });
|
|
1184
|
-
setMarkdownContent(result.body);
|
|
1185
|
-
} catch (err) {
|
|
1186
|
-
setError(err instanceof Error ? err : new Error("Failed to render markdown"));
|
|
1187
|
-
} finally {
|
|
1188
|
-
setLoading(false);
|
|
1189
|
-
}
|
|
1190
|
-
}, [engine, fetchData, resolvePresentation, selectedPresentation]);
|
|
1191
|
-
useEffect2(() => {
|
|
1192
|
-
renderMarkdown();
|
|
1193
|
-
}, [renderMarkdown]);
|
|
1194
|
-
return {
|
|
1195
|
-
error,
|
|
1196
|
-
loading,
|
|
1197
|
-
markdownContent,
|
|
1198
|
-
renderMarkdown,
|
|
1199
|
-
selectedPresentation,
|
|
1200
|
-
setSelectedPresentation
|
|
1201
|
-
};
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
// src/MarkdownView.tsx
|
|
1205
|
-
import {
|
|
1206
|
-
Button as Button3,
|
|
1207
|
-
ErrorState,
|
|
1208
|
-
LoaderBlock as LoaderBlock2,
|
|
1209
|
-
MarkdownRenderer
|
|
1210
|
-
} from "@contractspec/lib.design-system";
|
|
1211
|
-
import { Badge as Badge3 } from "@contractspec/lib.ui-kit-web/ui/badge";
|
|
1212
|
-
import { Card as Card3 } from "@contractspec/lib.ui-kit-web/ui/card";
|
|
1213
|
-
import { useCallback as useCallback5 } from "react";
|
|
1214
|
-
import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
|
|
1215
|
-
"use client";
|
|
1216
|
-
function MarkdownView({
|
|
1217
|
-
templateId: propTemplateId,
|
|
1218
|
-
presentationId,
|
|
1219
|
-
className
|
|
1220
|
-
}) {
|
|
1221
|
-
const {
|
|
1222
|
-
engine,
|
|
1223
|
-
template,
|
|
1224
|
-
templateId: contextTemplateId,
|
|
1225
|
-
resolvePresentation,
|
|
1226
|
-
fetchData
|
|
1227
|
-
} = useTemplateRuntime();
|
|
1228
|
-
const templateId = propTemplateId ?? contextTemplateId;
|
|
1229
|
-
const presentations = template?.presentations ?? [];
|
|
1230
|
-
const {
|
|
1231
|
-
error,
|
|
1232
|
-
loading,
|
|
1233
|
-
markdownContent,
|
|
1234
|
-
renderMarkdown,
|
|
1235
|
-
selectedPresentation,
|
|
1236
|
-
setSelectedPresentation
|
|
1237
|
-
} = useMarkdownPresentation({
|
|
1238
|
-
engine,
|
|
1239
|
-
fetchData,
|
|
1240
|
-
presentationId,
|
|
1241
|
-
presentations,
|
|
1242
|
-
resolvePresentation,
|
|
1243
|
-
templateId
|
|
1244
|
-
});
|
|
1245
|
-
if (!presentations.length) {
|
|
1246
|
-
return /* @__PURE__ */ jsxDEV4(Card3, {
|
|
1247
|
-
className,
|
|
1248
|
-
children: /* @__PURE__ */ jsxDEV4("div", {
|
|
1249
|
-
className: "p-6 text-center",
|
|
1250
|
-
children: /* @__PURE__ */ jsxDEV4("p", {
|
|
1251
|
-
className: "text-muted-foreground",
|
|
1252
|
-
children: "No presentations available for this template."
|
|
1253
|
-
}, undefined, false, undefined, this)
|
|
1254
|
-
}, undefined, false, undefined, this)
|
|
1255
|
-
}, undefined, false, undefined, this);
|
|
1256
|
-
}
|
|
1257
|
-
const handleCopy = useCallback5(() => {
|
|
1258
|
-
if (markdownContent) {
|
|
1259
|
-
navigator.clipboard.writeText(markdownContent);
|
|
1260
|
-
}
|
|
1261
|
-
}, [markdownContent]);
|
|
1262
|
-
return /* @__PURE__ */ jsxDEV4("div", {
|
|
1263
|
-
className,
|
|
1264
|
-
children: [
|
|
1265
|
-
/* @__PURE__ */ jsxDEV4("div", {
|
|
1266
|
-
className: "mb-4 flex flex-wrap items-center gap-2",
|
|
1267
|
-
children: [
|
|
1268
|
-
/* @__PURE__ */ jsxDEV4("span", {
|
|
1269
|
-
className: "font-medium text-muted-foreground text-sm",
|
|
1270
|
-
children: "Presentation:"
|
|
1271
|
-
}, undefined, false, undefined, this),
|
|
1272
|
-
presentations.map((name) => /* @__PURE__ */ jsxDEV4(Button3, {
|
|
1273
|
-
variant: selectedPresentation === name ? "default" : "outline",
|
|
1274
|
-
size: "sm",
|
|
1275
|
-
onPress: () => setSelectedPresentation(name),
|
|
1276
|
-
children: formatPresentationName(name)
|
|
1277
|
-
}, name, false, undefined, this)),
|
|
1278
|
-
/* @__PURE__ */ jsxDEV4("div", {
|
|
1279
|
-
className: "ml-auto flex items-center gap-2",
|
|
1280
|
-
children: [
|
|
1281
|
-
/* @__PURE__ */ jsxDEV4(Badge3, {
|
|
1282
|
-
variant: "secondary",
|
|
1283
|
-
children: "LLM-friendly"
|
|
1284
|
-
}, undefined, false, undefined, this),
|
|
1285
|
-
/* @__PURE__ */ jsxDEV4(Button3, {
|
|
1286
|
-
variant: "outline",
|
|
1287
|
-
size: "sm",
|
|
1288
|
-
onPress: handleCopy,
|
|
1289
|
-
disabled: !markdownContent || loading,
|
|
1290
|
-
children: "Copy"
|
|
1291
|
-
}, undefined, false, undefined, this)
|
|
1292
|
-
]
|
|
1293
|
-
}, undefined, true, undefined, this)
|
|
1294
|
-
]
|
|
1295
|
-
}, undefined, true, undefined, this),
|
|
1296
|
-
/* @__PURE__ */ jsxDEV4(Card3, {
|
|
1297
|
-
className: "overflow-hidden",
|
|
1298
|
-
children: [
|
|
1299
|
-
loading && /* @__PURE__ */ jsxDEV4(LoaderBlock2, {
|
|
1300
|
-
label: "Rendering markdown..."
|
|
1301
|
-
}, undefined, false, undefined, this),
|
|
1302
|
-
error && /* @__PURE__ */ jsxDEV4(ErrorState, {
|
|
1303
|
-
title: "Render failed",
|
|
1304
|
-
description: error.message,
|
|
1305
|
-
onRetry: renderMarkdown,
|
|
1306
|
-
retryLabel: "Retry"
|
|
1307
|
-
}, undefined, false, undefined, this),
|
|
1308
|
-
!loading && !error && markdownContent && /* @__PURE__ */ jsxDEV4("div", {
|
|
1309
|
-
className: "p-6",
|
|
1310
|
-
children: /* @__PURE__ */ jsxDEV4(MarkdownRenderer, {
|
|
1311
|
-
content: markdownContent
|
|
1312
|
-
}, undefined, false, undefined, this)
|
|
1313
|
-
}, undefined, false, undefined, this),
|
|
1314
|
-
!loading && !error && !markdownContent && /* @__PURE__ */ jsxDEV4("div", {
|
|
1315
|
-
className: "p-6 text-center",
|
|
1316
|
-
children: /* @__PURE__ */ jsxDEV4("p", {
|
|
1317
|
-
className: "text-muted-foreground",
|
|
1318
|
-
children: "Select a presentation to view its markdown output."
|
|
1319
|
-
}, undefined, false, undefined, this)
|
|
1320
|
-
}, undefined, false, undefined, this)
|
|
1321
|
-
]
|
|
1322
|
-
}, undefined, true, undefined, this)
|
|
1323
|
-
]
|
|
1324
|
-
}, undefined, true, undefined, this);
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
// src/OverlayContextProvider.tsx
|
|
1328
|
-
import * as React from "react";
|
|
1329
|
-
import { useContext as useContext2, useMemo as useMemo4 } from "react";
|
|
1330
|
-
import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
|
|
1331
|
-
"use client";
|
|
1332
|
-
var OverlayContext = React.createContext(null);
|
|
1333
|
-
function OverlayContextProvider({
|
|
1334
|
-
templateId,
|
|
1335
|
-
role = "user",
|
|
1336
|
-
device = "desktop",
|
|
1337
|
-
children
|
|
1338
|
-
}) {
|
|
1339
|
-
const overlays = useMemo4(() => getTemplateOverlays(templateId, role), [templateId, role]);
|
|
1340
|
-
const activeOverlays = useMemo4(() => {
|
|
1341
|
-
return overlays.filter((overlay) => {
|
|
1342
|
-
const conditions = overlay.conditions;
|
|
1343
|
-
if (!conditions)
|
|
1344
|
-
return true;
|
|
1345
|
-
if (conditions.role && !conditions.role.includes(role)) {
|
|
1346
|
-
return false;
|
|
1347
|
-
}
|
|
1348
|
-
if (conditions.device && conditions.device !== "any" && conditions.device !== device) {
|
|
1349
|
-
return false;
|
|
1350
|
-
}
|
|
1351
|
-
return true;
|
|
1352
|
-
});
|
|
1353
|
-
}, [overlays, role, device]);
|
|
1354
|
-
const overlayMap = useMemo4(() => {
|
|
1355
|
-
const map = new Map;
|
|
1356
|
-
for (const overlay of activeOverlays) {
|
|
1357
|
-
map.set(overlay.target, overlay);
|
|
1358
|
-
}
|
|
1359
|
-
return map;
|
|
1360
|
-
}, [activeOverlays]);
|
|
1361
|
-
const applyOverlay = useMemo4(() => (path, target) => {
|
|
1362
|
-
const overlay = overlayMap.get(path);
|
|
1363
|
-
if (!overlay)
|
|
1364
|
-
return target;
|
|
1365
|
-
let result = { ...target };
|
|
1366
|
-
for (const mod of overlay.modifications) {
|
|
1367
|
-
switch (mod.op) {
|
|
1368
|
-
case "hide":
|
|
1369
|
-
result = { ...result, hidden: true };
|
|
1370
|
-
break;
|
|
1371
|
-
case "relabel":
|
|
1372
|
-
result = { ...result, label: mod.label };
|
|
1373
|
-
break;
|
|
1374
|
-
case "reorder":
|
|
1375
|
-
result = { ...result, position: mod.position };
|
|
1376
|
-
break;
|
|
1377
|
-
case "restyle":
|
|
1378
|
-
result = {
|
|
1379
|
-
...result,
|
|
1380
|
-
className: mod.className,
|
|
1381
|
-
variant: mod.variant
|
|
1382
|
-
};
|
|
1383
|
-
break;
|
|
1384
|
-
case "set-default":
|
|
1385
|
-
result = { ...result, defaultValue: mod.value };
|
|
1386
|
-
break;
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
return result;
|
|
1390
|
-
}, [overlayMap]);
|
|
1391
|
-
const isHidden = useMemo4(() => (path) => {
|
|
1392
|
-
const overlay = overlayMap.get(path);
|
|
1393
|
-
return overlay?.modifications.some((m) => m.op === "hide") ?? false;
|
|
1394
|
-
}, [overlayMap]);
|
|
1395
|
-
const getLabel = useMemo4(() => (path, defaultLabel) => {
|
|
1396
|
-
const overlay = overlayMap.get(path);
|
|
1397
|
-
const relabel = overlay?.modifications.find((m) => m.op === "relabel");
|
|
1398
|
-
return relabel && relabel.op === "relabel" ? relabel.label : defaultLabel;
|
|
1399
|
-
}, [overlayMap]);
|
|
1400
|
-
const getPosition = useMemo4(() => (path, defaultPosition) => {
|
|
1401
|
-
const overlay = overlayMap.get(path);
|
|
1402
|
-
const reorder = overlay?.modifications.find((m) => m.op === "reorder");
|
|
1403
|
-
return reorder && reorder.op === "reorder" ? reorder.position : defaultPosition;
|
|
1404
|
-
}, [overlayMap]);
|
|
1405
|
-
const getStyle = useMemo4(() => (path) => {
|
|
1406
|
-
const overlay = overlayMap.get(path);
|
|
1407
|
-
const restyle = overlay?.modifications.find((m) => m.op === "restyle");
|
|
1408
|
-
if (restyle && restyle.op === "restyle") {
|
|
1409
|
-
return {
|
|
1410
|
-
className: restyle.className,
|
|
1411
|
-
variant: restyle.variant
|
|
1412
|
-
};
|
|
1413
|
-
}
|
|
1414
|
-
return {};
|
|
1415
|
-
}, [overlayMap]);
|
|
1416
|
-
const getDefault = useMemo4(() => (path, defaultValue) => {
|
|
1417
|
-
const overlay = overlayMap.get(path);
|
|
1418
|
-
const setDefault = overlay?.modifications.find((m) => m.op === "set-default");
|
|
1419
|
-
return setDefault && setDefault.op === "set-default" ? setDefault.value : defaultValue;
|
|
1420
|
-
}, [overlayMap]);
|
|
1421
|
-
const value = useMemo4(() => ({
|
|
1422
|
-
overlays: activeOverlays,
|
|
1423
|
-
applyOverlay,
|
|
1424
|
-
isHidden,
|
|
1425
|
-
getLabel,
|
|
1426
|
-
getPosition,
|
|
1427
|
-
getStyle,
|
|
1428
|
-
getDefault,
|
|
1429
|
-
role,
|
|
1430
|
-
device
|
|
1431
|
-
}), [
|
|
1432
|
-
activeOverlays,
|
|
1433
|
-
applyOverlay,
|
|
1434
|
-
isHidden,
|
|
1435
|
-
getLabel,
|
|
1436
|
-
getPosition,
|
|
1437
|
-
getStyle,
|
|
1438
|
-
getDefault,
|
|
1439
|
-
role,
|
|
1440
|
-
device
|
|
1441
|
-
]);
|
|
1442
|
-
return /* @__PURE__ */ jsxDEV5(OverlayContext.Provider, {
|
|
1443
|
-
value,
|
|
1444
|
-
children
|
|
1445
|
-
}, undefined, false, undefined, this);
|
|
1446
|
-
}
|
|
1447
|
-
function useOverlayContext() {
|
|
1448
|
-
const context = useContext2(OverlayContext);
|
|
1449
|
-
if (!context) {
|
|
1450
|
-
throw new Error("useOverlayContext must be used within an OverlayContextProvider");
|
|
1451
|
-
}
|
|
1452
|
-
return context;
|
|
1453
|
-
}
|
|
1454
|
-
function useIsInOverlayContext() {
|
|
1455
|
-
return useContext2(OverlayContext) !== null;
|
|
1456
|
-
}
|
|
1457
|
-
function getTemplateOverlays(templateId, _role) {
|
|
1458
|
-
const templateOverlays = {
|
|
1459
|
-
"crm-pipeline": [
|
|
1460
|
-
{
|
|
1461
|
-
id: "crm-hide-internal-fields",
|
|
1462
|
-
target: "deal.internalNotes",
|
|
1463
|
-
modifications: [{ op: "hide" }],
|
|
1464
|
-
conditions: { role: ["viewer", "user"] }
|
|
1465
|
-
},
|
|
1466
|
-
{
|
|
1467
|
-
id: "crm-relabel-value",
|
|
1468
|
-
target: "deal.value",
|
|
1469
|
-
modifications: [{ op: "relabel", label: "Deal Amount" }]
|
|
1470
|
-
}
|
|
1471
|
-
],
|
|
1472
|
-
"saas-boilerplate": [
|
|
1473
|
-
{
|
|
1474
|
-
id: "saas-hide-billing",
|
|
1475
|
-
target: "settings.billing",
|
|
1476
|
-
modifications: [{ op: "hide" }],
|
|
1477
|
-
conditions: { role: ["viewer"] }
|
|
1478
|
-
},
|
|
1479
|
-
{
|
|
1480
|
-
id: "saas-restyle-plan",
|
|
1481
|
-
target: "settings.plan",
|
|
1482
|
-
modifications: [{ op: "restyle", variant: "premium" }],
|
|
1483
|
-
conditions: { role: ["admin"] }
|
|
1484
|
-
}
|
|
1485
|
-
],
|
|
1486
|
-
"agent-console": [
|
|
1487
|
-
{
|
|
1488
|
-
id: "agent-hide-cost",
|
|
1489
|
-
target: "run.cost",
|
|
1490
|
-
modifications: [{ op: "hide" }],
|
|
1491
|
-
conditions: { role: ["viewer"] }
|
|
1492
|
-
},
|
|
1493
|
-
{
|
|
1494
|
-
id: "agent-relabel-tokens",
|
|
1495
|
-
target: "run.tokens",
|
|
1496
|
-
modifications: [{ op: "relabel", label: "Token Usage" }]
|
|
1497
|
-
}
|
|
1498
|
-
],
|
|
1499
|
-
"todos-app": [
|
|
1500
|
-
{
|
|
1501
|
-
id: "todos-hide-assignee",
|
|
1502
|
-
target: "task.assignee",
|
|
1503
|
-
modifications: [{ op: "hide" }],
|
|
1504
|
-
conditions: { device: "mobile" }
|
|
1505
|
-
}
|
|
1506
|
-
],
|
|
1507
|
-
"messaging-app": [
|
|
1508
|
-
{
|
|
1509
|
-
id: "messaging-reorder-timestamp",
|
|
1510
|
-
target: "message.timestamp",
|
|
1511
|
-
modifications: [{ op: "reorder", position: 0 }]
|
|
1512
|
-
}
|
|
1513
|
-
],
|
|
1514
|
-
"recipe-app-i18n": [
|
|
1515
|
-
{
|
|
1516
|
-
id: "recipe-relabel-servings",
|
|
1517
|
-
target: "recipe.servings",
|
|
1518
|
-
modifications: [{ op: "relabel", label: "Portions" }]
|
|
1519
|
-
}
|
|
1520
|
-
]
|
|
1521
|
-
};
|
|
1522
|
-
return templateOverlays[templateId] ?? [];
|
|
1523
|
-
}
|
|
1524
|
-
|
|
1525
|
-
// src/hooks/useBehaviorTracking.ts
|
|
1526
|
-
import { useCallback as useCallback6, useEffect as useEffect3, useMemo as useMemo5, useRef as useRef2, useState as useState4 } from "react";
|
|
1527
|
-
"use client";
|
|
1528
|
-
var BEHAVIOR_STORAGE_KEY = "contractspec-behavior-data";
|
|
1529
|
-
var ALL_FEATURES = [
|
|
1530
|
-
"playground",
|
|
1531
|
-
"specs",
|
|
1532
|
-
"builder",
|
|
1533
|
-
"markdown",
|
|
1534
|
-
"evolution",
|
|
1535
|
-
"canvas_add",
|
|
1536
|
-
"canvas_delete",
|
|
1537
|
-
"spec_save",
|
|
1538
|
-
"spec_validate",
|
|
1539
|
-
"ai_suggestions"
|
|
1540
|
-
];
|
|
1541
|
-
function useBehaviorTracking(templateId) {
|
|
1542
|
-
const [events, setEvents] = useState4([]);
|
|
1543
|
-
const sessionStartRef = useRef2(new Date);
|
|
1544
|
-
const [eventCount, setEventCount] = useState4(0);
|
|
1545
|
-
useEffect3(() => {
|
|
1546
|
-
try {
|
|
1547
|
-
const stored = localStorage.getItem(BEHAVIOR_STORAGE_KEY);
|
|
1548
|
-
if (stored) {
|
|
1549
|
-
const data = JSON.parse(stored);
|
|
1550
|
-
setEvents(data.events.map((e) => ({
|
|
1551
|
-
...e,
|
|
1552
|
-
timestamp: new Date(e.timestamp)
|
|
1553
|
-
})));
|
|
1554
|
-
sessionStartRef.current = new Date(data.sessionStart);
|
|
1555
|
-
}
|
|
1556
|
-
} catch {}
|
|
1557
|
-
}, []);
|
|
1558
|
-
useEffect3(() => {
|
|
1559
|
-
if (events.length > 0) {
|
|
1560
|
-
try {
|
|
1561
|
-
localStorage.setItem(BEHAVIOR_STORAGE_KEY, JSON.stringify({
|
|
1562
|
-
events: events.map((e) => ({
|
|
1563
|
-
...e,
|
|
1564
|
-
timestamp: e.timestamp.toISOString()
|
|
1565
|
-
})),
|
|
1566
|
-
sessionStart: sessionStartRef.current.toISOString()
|
|
1567
|
-
}));
|
|
1568
|
-
} catch {}
|
|
1569
|
-
}
|
|
1570
|
-
}, [events]);
|
|
1571
|
-
const trackEvent = useCallback6((type, metadata) => {
|
|
1572
|
-
const event = {
|
|
1573
|
-
type,
|
|
1574
|
-
timestamp: new Date,
|
|
1575
|
-
templateId,
|
|
1576
|
-
metadata
|
|
1577
|
-
};
|
|
1578
|
-
setEvents((prev) => [...prev, event]);
|
|
1579
|
-
setEventCount((prev) => prev + 1);
|
|
1580
|
-
}, [templateId]);
|
|
1581
|
-
const getEventsByType = useCallback6((type) => {
|
|
1582
|
-
return events.filter((e) => e.type === type);
|
|
1583
|
-
}, [events]);
|
|
1584
|
-
const getSummary = useCallback6(() => {
|
|
1585
|
-
const now = new Date;
|
|
1586
|
-
const sessionDuration = now.getTime() - sessionStartRef.current.getTime();
|
|
1587
|
-
const templateCounts = new Map;
|
|
1588
|
-
for (const event of events) {
|
|
1589
|
-
const count = templateCounts.get(event.templateId) ?? 0;
|
|
1590
|
-
templateCounts.set(event.templateId, count + 1);
|
|
1591
|
-
}
|
|
1592
|
-
const mostUsedTemplates = Array.from(templateCounts.entries()).map(([templateId2, count]) => ({ templateId: templateId2, count })).sort((a, b) => b.count - a.count).slice(0, 3);
|
|
1593
|
-
const modeCounts = new Map;
|
|
1594
|
-
for (const event of events) {
|
|
1595
|
-
if (event.type === "mode_change" && event.metadata?.mode) {
|
|
1596
|
-
const mode = event.metadata.mode;
|
|
1597
|
-
const count = modeCounts.get(mode) ?? 0;
|
|
1598
|
-
modeCounts.set(mode, count + 1);
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
const mostUsedModes = Array.from(modeCounts.entries()).map(([mode, count]) => ({ mode, count })).sort((a, b) => b.count - a.count);
|
|
1602
|
-
const featuresUsed = new Set;
|
|
1603
|
-
for (const event of events) {
|
|
1604
|
-
if (event.type === "mode_change" && event.metadata?.mode) {
|
|
1605
|
-
featuresUsed.add(event.metadata.mode);
|
|
1606
|
-
}
|
|
1607
|
-
if (event.type === "feature_usage" && event.metadata?.feature) {
|
|
1608
|
-
featuresUsed.add(event.metadata.feature);
|
|
1609
|
-
}
|
|
1610
|
-
if (event.type === "canvas_interaction") {
|
|
1611
|
-
const action = event.metadata?.action;
|
|
1612
|
-
if (action === "add")
|
|
1613
|
-
featuresUsed.add("canvas_add");
|
|
1614
|
-
if (action === "delete")
|
|
1615
|
-
featuresUsed.add("canvas_delete");
|
|
1616
|
-
}
|
|
1617
|
-
if (event.type === "spec_edit") {
|
|
1618
|
-
const action = event.metadata?.action;
|
|
1619
|
-
if (action === "save")
|
|
1620
|
-
featuresUsed.add("spec_save");
|
|
1621
|
-
if (action === "validate")
|
|
1622
|
-
featuresUsed.add("spec_validate");
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
const unusedFeatures = ALL_FEATURES.filter((f) => !featuresUsed.has(f));
|
|
1626
|
-
const errorCount = events.filter((e) => e.type === "error").length;
|
|
1627
|
-
const recommendations = generateRecommendations(Array.from(featuresUsed), unusedFeatures, mostUsedModes, events.length);
|
|
1628
|
-
return {
|
|
1629
|
-
totalEvents: events.length,
|
|
1630
|
-
sessionDuration,
|
|
1631
|
-
mostUsedTemplates,
|
|
1632
|
-
mostUsedModes,
|
|
1633
|
-
featuresUsed: Array.from(featuresUsed),
|
|
1634
|
-
unusedFeatures,
|
|
1635
|
-
errorCount,
|
|
1636
|
-
recommendations
|
|
1637
|
-
};
|
|
1638
|
-
}, [events]);
|
|
1639
|
-
const clear = useCallback6(() => {
|
|
1640
|
-
setEvents([]);
|
|
1641
|
-
setEventCount(0);
|
|
1642
|
-
sessionStartRef.current = new Date;
|
|
1643
|
-
localStorage.removeItem(BEHAVIOR_STORAGE_KEY);
|
|
1644
|
-
}, []);
|
|
1645
|
-
return useMemo5(() => ({
|
|
1646
|
-
trackEvent,
|
|
1647
|
-
getSummary,
|
|
1648
|
-
getEventsByType,
|
|
1649
|
-
eventCount,
|
|
1650
|
-
sessionStart: sessionStartRef.current,
|
|
1651
|
-
clear
|
|
1652
|
-
}), [trackEvent, getSummary, getEventsByType, eventCount, clear]);
|
|
1653
|
-
}
|
|
1654
|
-
function generateRecommendations(featuresUsed, unusedFeatures, mostUsedModes, totalEvents) {
|
|
1655
|
-
const recommendations = [];
|
|
1656
|
-
if (unusedFeatures.includes("evolution")) {
|
|
1657
|
-
recommendations.push("Try the AI Evolution mode to get automated improvement suggestions");
|
|
1658
|
-
}
|
|
1659
|
-
if (unusedFeatures.includes("markdown")) {
|
|
1660
|
-
recommendations.push("Use Markdown preview to see documentation for your specs");
|
|
1661
|
-
}
|
|
1662
|
-
if (unusedFeatures.includes("builder")) {
|
|
1663
|
-
recommendations.push("Explore the Visual Builder to design your UI components");
|
|
1664
|
-
}
|
|
1665
|
-
if (!featuresUsed.includes("spec_validate") && featuresUsed.includes("specs")) {
|
|
1666
|
-
recommendations.push("Don't forget to validate your specs before saving");
|
|
1667
|
-
}
|
|
1668
|
-
if (featuresUsed.includes("evolution") && !featuresUsed.includes("ai_suggestions")) {
|
|
1669
|
-
recommendations.push("Generate AI suggestions to get actionable improvement recommendations");
|
|
1670
|
-
}
|
|
1671
|
-
if (totalEvents > 50) {
|
|
1672
|
-
recommendations.push("Great engagement! Consider saving your work regularly");
|
|
1673
|
-
}
|
|
1674
|
-
if (mostUsedModes.length === 1) {
|
|
1675
|
-
recommendations.push("Try different modes to explore all sandbox capabilities");
|
|
1676
|
-
}
|
|
1677
|
-
return recommendations;
|
|
1678
|
-
}
|
|
1679
|
-
|
|
1680
|
-
// src/PersonalizationInsights.tsx
|
|
1681
|
-
import { Button as Button4 } from "@contractspec/lib.design-system";
|
|
1682
|
-
import { Badge as Badge4 } from "@contractspec/lib.ui-kit-web/ui/badge";
|
|
1683
|
-
import { Card as Card4 } from "@contractspec/lib.ui-kit-web/ui/card";
|
|
1684
|
-
import { useCallback as useCallback7, useMemo as useMemo6, useState as useState5 } from "react";
|
|
1685
|
-
import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
|
|
1686
|
-
"use client";
|
|
1687
|
-
function PersonalizationInsights({
|
|
1688
|
-
templateId,
|
|
1689
|
-
collapsed = false,
|
|
1690
|
-
onToggle
|
|
1691
|
-
}) {
|
|
1692
|
-
const { getSummary, eventCount, clear, sessionStart } = useBehaviorTracking(templateId);
|
|
1693
|
-
const [showDetails, setShowDetails] = useState5(false);
|
|
1694
|
-
const summary = useMemo6(() => getSummary(), [getSummary]);
|
|
1695
|
-
const formatDuration = useCallback7((ms) => {
|
|
1696
|
-
const seconds = Math.floor(ms / 1000);
|
|
1697
|
-
const minutes = Math.floor(seconds / 60);
|
|
1698
|
-
const hours = Math.floor(minutes / 60);
|
|
1699
|
-
if (hours > 0) {
|
|
1700
|
-
return `${hours}h ${minutes % 60}m`;
|
|
1701
|
-
}
|
|
1702
|
-
if (minutes > 0) {
|
|
1703
|
-
return `${minutes}m ${seconds % 60}s`;
|
|
1704
|
-
}
|
|
1705
|
-
return `${seconds}s`;
|
|
1706
|
-
}, []);
|
|
1707
|
-
const handleClear = useCallback7(() => {
|
|
1708
|
-
clear();
|
|
1709
|
-
}, [clear]);
|
|
1710
|
-
if (collapsed) {
|
|
1711
|
-
return /* @__PURE__ */ jsxDEV6("button", {
|
|
1712
|
-
onClick: onToggle,
|
|
1713
|
-
className: "flex items-center gap-2 rounded-lg border border-blue-500/30 bg-blue-500/10 px-3 py-2 text-sm transition hover:bg-blue-500/20",
|
|
1714
|
-
type: "button",
|
|
1715
|
-
children: [
|
|
1716
|
-
/* @__PURE__ */ jsxDEV6("span", {
|
|
1717
|
-
children: "\uD83D\uDCCA"
|
|
1718
|
-
}, undefined, false, undefined, this),
|
|
1719
|
-
/* @__PURE__ */ jsxDEV6("span", {
|
|
1720
|
-
children: "Insights"
|
|
1721
|
-
}, undefined, false, undefined, this),
|
|
1722
|
-
/* @__PURE__ */ jsxDEV6(Badge4, {
|
|
1723
|
-
variant: "secondary",
|
|
1724
|
-
children: eventCount
|
|
1725
|
-
}, undefined, false, undefined, this)
|
|
1726
|
-
]
|
|
1727
|
-
}, undefined, true, undefined, this);
|
|
1728
|
-
}
|
|
1729
|
-
return /* @__PURE__ */ jsxDEV6(Card4, {
|
|
1730
|
-
className: "overflow-hidden",
|
|
1731
|
-
children: [
|
|
1732
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1733
|
-
className: "flex items-center justify-between border-blue-500/20 border-b bg-blue-500/5 px-4 py-3",
|
|
1734
|
-
children: [
|
|
1735
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1736
|
-
className: "flex items-center gap-2",
|
|
1737
|
-
children: [
|
|
1738
|
-
/* @__PURE__ */ jsxDEV6("span", {
|
|
1739
|
-
children: "\uD83D\uDCCA"
|
|
1740
|
-
}, undefined, false, undefined, this),
|
|
1741
|
-
/* @__PURE__ */ jsxDEV6("span", {
|
|
1742
|
-
className: "font-semibold",
|
|
1743
|
-
children: "Personalization Insights"
|
|
1744
|
-
}, undefined, false, undefined, this)
|
|
1745
|
-
]
|
|
1746
|
-
}, undefined, true, undefined, this),
|
|
1747
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1748
|
-
className: "flex items-center gap-2",
|
|
1749
|
-
children: [
|
|
1750
|
-
/* @__PURE__ */ jsxDEV6(Button4, {
|
|
1751
|
-
variant: "ghost",
|
|
1752
|
-
size: "sm",
|
|
1753
|
-
onPress: () => setShowDetails(!showDetails),
|
|
1754
|
-
children: showDetails ? "Hide Details" : "Show Details"
|
|
1755
|
-
}, undefined, false, undefined, this),
|
|
1756
|
-
onToggle && /* @__PURE__ */ jsxDEV6("button", {
|
|
1757
|
-
onClick: onToggle,
|
|
1758
|
-
className: "p-1 text-muted-foreground hover:text-foreground",
|
|
1759
|
-
type: "button",
|
|
1760
|
-
title: "Collapse",
|
|
1761
|
-
children: "\u2715"
|
|
1762
|
-
}, undefined, false, undefined, this)
|
|
1763
|
-
]
|
|
1764
|
-
}, undefined, true, undefined, this)
|
|
1765
|
-
]
|
|
1766
|
-
}, undefined, true, undefined, this),
|
|
1767
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1768
|
-
className: "p-4",
|
|
1769
|
-
children: [
|
|
1770
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1771
|
-
className: "mb-4 grid grid-cols-2 gap-3 md:grid-cols-4",
|
|
1772
|
-
children: [
|
|
1773
|
-
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
1774
|
-
label: "Session Time",
|
|
1775
|
-
value: formatDuration(summary.sessionDuration),
|
|
1776
|
-
icon: "\u23F1\uFE0F"
|
|
1777
|
-
}, undefined, false, undefined, this),
|
|
1778
|
-
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
1779
|
-
label: "Events Tracked",
|
|
1780
|
-
value: summary.totalEvents.toString(),
|
|
1781
|
-
icon: "\uD83D\uDCC8"
|
|
1782
|
-
}, undefined, false, undefined, this),
|
|
1783
|
-
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
1784
|
-
label: "Features Used",
|
|
1785
|
-
value: `${summary.featuresUsed.length}/${summary.featuresUsed.length + summary.unusedFeatures.length}`,
|
|
1786
|
-
icon: "\u2728"
|
|
1787
|
-
}, undefined, false, undefined, this),
|
|
1788
|
-
/* @__PURE__ */ jsxDEV6(StatCard, {
|
|
1789
|
-
label: "Errors",
|
|
1790
|
-
value: summary.errorCount.toString(),
|
|
1791
|
-
icon: "\u26A0\uFE0F",
|
|
1792
|
-
variant: summary.errorCount > 0 ? "warning" : "success"
|
|
1793
|
-
}, undefined, false, undefined, this)
|
|
1794
|
-
]
|
|
1795
|
-
}, undefined, true, undefined, this),
|
|
1796
|
-
summary.recommendations.length > 0 && /* @__PURE__ */ jsxDEV6("div", {
|
|
1797
|
-
className: "mb-4",
|
|
1798
|
-
children: [
|
|
1799
|
-
/* @__PURE__ */ jsxDEV6("h4", {
|
|
1800
|
-
className: "mb-2 font-semibold text-blue-400 text-xs uppercase",
|
|
1801
|
-
children: "Recommendations"
|
|
1802
|
-
}, undefined, false, undefined, this),
|
|
1803
|
-
/* @__PURE__ */ jsxDEV6("ul", {
|
|
1804
|
-
className: "space-y-1",
|
|
1805
|
-
children: summary.recommendations.map((rec, index) => /* @__PURE__ */ jsxDEV6("li", {
|
|
1806
|
-
className: "flex items-start gap-2 text-sm",
|
|
1807
|
-
children: [
|
|
1808
|
-
/* @__PURE__ */ jsxDEV6("span", {
|
|
1809
|
-
className: "text-blue-400",
|
|
1810
|
-
children: "\uD83D\uDCA1"
|
|
1811
|
-
}, undefined, false, undefined, this),
|
|
1812
|
-
/* @__PURE__ */ jsxDEV6("span", {
|
|
1813
|
-
children: rec
|
|
1814
|
-
}, undefined, false, undefined, this)
|
|
1815
|
-
]
|
|
1816
|
-
}, index, true, undefined, this))
|
|
1817
|
-
}, undefined, false, undefined, this)
|
|
1818
|
-
]
|
|
1819
|
-
}, undefined, true, undefined, this),
|
|
1820
|
-
summary.unusedFeatures.length > 0 && /* @__PURE__ */ jsxDEV6("div", {
|
|
1821
|
-
className: "mb-4",
|
|
1822
|
-
children: [
|
|
1823
|
-
/* @__PURE__ */ jsxDEV6("h4", {
|
|
1824
|
-
className: "mb-2 font-semibold text-blue-400 text-xs uppercase",
|
|
1825
|
-
children: "Try These Features"
|
|
1826
|
-
}, undefined, false, undefined, this),
|
|
1827
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1828
|
-
className: "flex flex-wrap gap-2",
|
|
1829
|
-
children: summary.unusedFeatures.slice(0, 5).map((feature) => /* @__PURE__ */ jsxDEV6(Badge4, {
|
|
1830
|
-
variant: "secondary",
|
|
1831
|
-
children: formatFeatureName(feature)
|
|
1832
|
-
}, feature, false, undefined, this))
|
|
1833
|
-
}, undefined, false, undefined, this)
|
|
1834
|
-
]
|
|
1835
|
-
}, undefined, true, undefined, this),
|
|
1836
|
-
showDetails && /* @__PURE__ */ jsxDEV6(DetailedInsights, {
|
|
1837
|
-
summary,
|
|
1838
|
-
sessionStart
|
|
1839
|
-
}, undefined, false, undefined, this),
|
|
1840
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1841
|
-
className: "mt-4 flex justify-end border-blue-500/10 border-t pt-4",
|
|
1842
|
-
children: /* @__PURE__ */ jsxDEV6(Button4, {
|
|
1843
|
-
variant: "ghost",
|
|
1844
|
-
size: "sm",
|
|
1845
|
-
onPress: handleClear,
|
|
1846
|
-
children: "Clear Data"
|
|
1847
|
-
}, undefined, false, undefined, this)
|
|
1848
|
-
}, undefined, false, undefined, this)
|
|
1849
|
-
]
|
|
1850
|
-
}, undefined, true, undefined, this)
|
|
1851
|
-
]
|
|
1852
|
-
}, undefined, true, undefined, this);
|
|
1853
|
-
}
|
|
1854
|
-
function StatCard({
|
|
1855
|
-
label,
|
|
1856
|
-
value,
|
|
1857
|
-
icon,
|
|
1858
|
-
variant = "default"
|
|
1859
|
-
}) {
|
|
1860
|
-
const bgColors = {
|
|
1861
|
-
default: "bg-blue-500/5 border-blue-500/20",
|
|
1862
|
-
warning: "bg-amber-500/5 border-amber-500/20",
|
|
1863
|
-
success: "bg-green-500/5 border-green-500/20"
|
|
1864
|
-
};
|
|
1865
|
-
return /* @__PURE__ */ jsxDEV6("div", {
|
|
1866
|
-
className: `rounded-lg border p-3 text-center ${bgColors[variant]}`,
|
|
1867
|
-
children: [
|
|
1868
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1869
|
-
className: "mb-1 text-lg",
|
|
1870
|
-
children: icon
|
|
1871
|
-
}, undefined, false, undefined, this),
|
|
1872
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1873
|
-
className: "font-bold text-lg",
|
|
1874
|
-
children: value
|
|
1875
|
-
}, undefined, false, undefined, this),
|
|
1876
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1877
|
-
className: "text-muted-foreground text-xs",
|
|
1878
|
-
children: label
|
|
1879
|
-
}, undefined, false, undefined, this)
|
|
1880
|
-
]
|
|
1881
|
-
}, undefined, true, undefined, this);
|
|
1882
|
-
}
|
|
1883
|
-
function DetailedInsights({
|
|
1884
|
-
summary,
|
|
1885
|
-
sessionStart
|
|
1886
|
-
}) {
|
|
1887
|
-
return /* @__PURE__ */ jsxDEV6("div", {
|
|
1888
|
-
className: "mt-4 space-y-4 border-blue-500/10 border-t pt-4",
|
|
1889
|
-
children: [
|
|
1890
|
-
summary.mostUsedTemplates.length > 0 && /* @__PURE__ */ jsxDEV6("div", {
|
|
1891
|
-
children: [
|
|
1892
|
-
/* @__PURE__ */ jsxDEV6("h4", {
|
|
1893
|
-
className: "mb-2 font-semibold text-blue-400 text-xs uppercase",
|
|
1894
|
-
children: "Most Used Templates"
|
|
1895
|
-
}, undefined, false, undefined, this),
|
|
1896
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1897
|
-
className: "space-y-1",
|
|
1898
|
-
children: summary.mostUsedTemplates.map(({ templateId, count }) => /* @__PURE__ */ jsxDEV6("div", {
|
|
1899
|
-
className: "flex items-center justify-between text-sm",
|
|
1900
|
-
children: [
|
|
1901
|
-
/* @__PURE__ */ jsxDEV6("span", {
|
|
1902
|
-
children: formatTemplateId(templateId)
|
|
1903
|
-
}, undefined, false, undefined, this),
|
|
1904
|
-
/* @__PURE__ */ jsxDEV6("span", {
|
|
1905
|
-
className: "text-muted-foreground",
|
|
1906
|
-
children: [
|
|
1907
|
-
count,
|
|
1908
|
-
" events"
|
|
1909
|
-
]
|
|
1910
|
-
}, undefined, true, undefined, this)
|
|
1911
|
-
]
|
|
1912
|
-
}, templateId, true, undefined, this))
|
|
1913
|
-
}, undefined, false, undefined, this)
|
|
1914
|
-
]
|
|
1915
|
-
}, undefined, true, undefined, this),
|
|
1916
|
-
summary.mostUsedModes.length > 0 && /* @__PURE__ */ jsxDEV6("div", {
|
|
1917
|
-
children: [
|
|
1918
|
-
/* @__PURE__ */ jsxDEV6("h4", {
|
|
1919
|
-
className: "mb-2 font-semibold text-blue-400 text-xs uppercase",
|
|
1920
|
-
children: "Mode Usage"
|
|
1921
|
-
}, undefined, false, undefined, this),
|
|
1922
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1923
|
-
className: "space-y-1",
|
|
1924
|
-
children: summary.mostUsedModes.map(({ mode, count }) => /* @__PURE__ */ jsxDEV6("div", {
|
|
1925
|
-
className: "flex items-center justify-between text-sm",
|
|
1926
|
-
children: [
|
|
1927
|
-
/* @__PURE__ */ jsxDEV6("span", {
|
|
1928
|
-
children: formatFeatureName(mode)
|
|
1929
|
-
}, undefined, false, undefined, this),
|
|
1930
|
-
/* @__PURE__ */ jsxDEV6("span", {
|
|
1931
|
-
className: "text-muted-foreground",
|
|
1932
|
-
children: [
|
|
1933
|
-
count,
|
|
1934
|
-
" switches"
|
|
1935
|
-
]
|
|
1936
|
-
}, undefined, true, undefined, this)
|
|
1937
|
-
]
|
|
1938
|
-
}, mode, true, undefined, this))
|
|
1939
|
-
}, undefined, false, undefined, this)
|
|
1940
|
-
]
|
|
1941
|
-
}, undefined, true, undefined, this),
|
|
1942
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1943
|
-
children: [
|
|
1944
|
-
/* @__PURE__ */ jsxDEV6("h4", {
|
|
1945
|
-
className: "mb-2 font-semibold text-blue-400 text-xs uppercase",
|
|
1946
|
-
children: "Features Used"
|
|
1947
|
-
}, undefined, false, undefined, this),
|
|
1948
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1949
|
-
className: "flex flex-wrap gap-2",
|
|
1950
|
-
children: summary.featuresUsed.map((feature) => /* @__PURE__ */ jsxDEV6(Badge4, {
|
|
1951
|
-
variant: "default",
|
|
1952
|
-
className: "border-green-500/30 bg-green-500/20 text-green-400",
|
|
1953
|
-
children: [
|
|
1954
|
-
"\u2713 ",
|
|
1955
|
-
formatFeatureName(feature)
|
|
1956
|
-
]
|
|
1957
|
-
}, feature, true, undefined, this))
|
|
1958
|
-
}, undefined, false, undefined, this)
|
|
1959
|
-
]
|
|
1960
|
-
}, undefined, true, undefined, this),
|
|
1961
|
-
/* @__PURE__ */ jsxDEV6("div", {
|
|
1962
|
-
className: "text-muted-foreground text-xs",
|
|
1963
|
-
children: [
|
|
1964
|
-
"Session started: ",
|
|
1965
|
-
sessionStart.toLocaleString()
|
|
1966
|
-
]
|
|
1967
|
-
}, undefined, true, undefined, this)
|
|
1968
|
-
]
|
|
1969
|
-
}, undefined, true, undefined, this);
|
|
1970
|
-
}
|
|
1971
|
-
function formatFeatureName(feature) {
|
|
1972
|
-
return feature.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
1973
|
-
}
|
|
1974
|
-
function formatTemplateId(id) {
|
|
1975
|
-
return id.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
1976
|
-
}
|
|
1977
|
-
|
|
1978
|
-
// src/SaveToStudioButton.tsx
|
|
1979
|
-
import { Sparkles } from "lucide-react";
|
|
1980
|
-
import { useState as useState6 } from "react";
|
|
1981
|
-
import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
|
|
1982
|
-
"use client";
|
|
1983
|
-
function SaveToStudioButton({
|
|
1984
|
-
organizationId = "demo-org",
|
|
1985
|
-
projectName,
|
|
1986
|
-
endpoint,
|
|
1987
|
-
token
|
|
1988
|
-
}) {
|
|
1989
|
-
const { installer, templateId, template } = useTemplateRuntime();
|
|
1990
|
-
const [status, setStatus] = useState6("idle");
|
|
1991
|
-
const [error, setError] = useState6(null);
|
|
1992
|
-
const handleSave = async () => {
|
|
1993
|
-
setStatus("saving");
|
|
1994
|
-
setError(null);
|
|
1995
|
-
try {
|
|
1996
|
-
await installer.saveToStudio({
|
|
1997
|
-
templateId,
|
|
1998
|
-
projectName: projectName ?? `${template.name} demo`,
|
|
1999
|
-
organizationId,
|
|
2000
|
-
endpoint,
|
|
2001
|
-
token
|
|
2002
|
-
});
|
|
2003
|
-
setStatus("saved");
|
|
2004
|
-
setTimeout(() => setStatus("idle"), 3000);
|
|
2005
|
-
} catch (err) {
|
|
2006
|
-
setStatus("error");
|
|
2007
|
-
setError(err instanceof Error ? err.message : "Unknown error");
|
|
2008
|
-
}
|
|
2009
|
-
};
|
|
2010
|
-
return /* @__PURE__ */ jsxDEV7("div", {
|
|
2011
|
-
className: "flex flex-col items-end gap-1",
|
|
2012
|
-
children: [
|
|
2013
|
-
/* @__PURE__ */ jsxDEV7("button", {
|
|
2014
|
-
type: "button",
|
|
2015
|
-
className: "btn-primary inline-flex items-center gap-2 text-sm",
|
|
2016
|
-
onClick: handleSave,
|
|
2017
|
-
disabled: status === "saving",
|
|
2018
|
-
children: [
|
|
2019
|
-
/* @__PURE__ */ jsxDEV7(Sparkles, {
|
|
2020
|
-
className: "h-4 w-4"
|
|
2021
|
-
}, undefined, false, undefined, this),
|
|
2022
|
-
status === "saving" ? "Publishing\u2026" : "Save to Studio"
|
|
2023
|
-
]
|
|
2024
|
-
}, undefined, true, undefined, this),
|
|
2025
|
-
status === "error" && error ? /* @__PURE__ */ jsxDEV7("p", {
|
|
2026
|
-
className: "text-destructive text-xs",
|
|
2027
|
-
children: error
|
|
2028
|
-
}, undefined, false, undefined, this) : null,
|
|
2029
|
-
status === "saved" ? /* @__PURE__ */ jsxDEV7("p", {
|
|
2030
|
-
className: "text-emerald-400 text-xs",
|
|
2031
|
-
children: "Template sent to Studio."
|
|
2032
|
-
}, undefined, false, undefined, this) : null
|
|
2033
|
-
]
|
|
2034
|
-
}, undefined, true, undefined, this);
|
|
2035
|
-
}
|
|
2036
|
-
|
|
2037
|
-
// src/SpecDrivenTemplateShell.tsx
|
|
2038
|
-
import {
|
|
2039
|
-
BundleProvider,
|
|
2040
|
-
BundleRenderer
|
|
2041
|
-
} from "@contractspec/lib.surface-runtime/react";
|
|
2042
|
-
import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
|
|
2043
|
-
function SpecDrivenTemplateShell({
|
|
2044
|
-
plan,
|
|
2045
|
-
title,
|
|
2046
|
-
description,
|
|
2047
|
-
sidebar,
|
|
2048
|
-
actions,
|
|
2049
|
-
showSaveAction = true,
|
|
2050
|
-
saveProps,
|
|
2051
|
-
children
|
|
2052
|
-
}) {
|
|
2053
|
-
const headerContent = /* @__PURE__ */ jsxDEV8("header", {
|
|
2054
|
-
className: "rounded-2xl border border-border bg-card p-6 shadow-sm",
|
|
2055
|
-
children: [
|
|
2056
|
-
/* @__PURE__ */ jsxDEV8("div", {
|
|
2057
|
-
className: "flex flex-wrap items-center justify-between gap-4",
|
|
2058
|
-
children: [
|
|
2059
|
-
/* @__PURE__ */ jsxDEV8("div", {
|
|
2060
|
-
children: [
|
|
2061
|
-
/* @__PURE__ */ jsxDEV8("p", {
|
|
2062
|
-
className: "font-semibold text-muted-foreground text-sm uppercase tracking-wide",
|
|
2063
|
-
children: "ContractSpec Templates"
|
|
2064
|
-
}, undefined, false, undefined, this),
|
|
2065
|
-
/* @__PURE__ */ jsxDEV8("h1", {
|
|
2066
|
-
className: "font-bold text-3xl",
|
|
2067
|
-
children: title
|
|
2068
|
-
}, undefined, false, undefined, this),
|
|
2069
|
-
description ? /* @__PURE__ */ jsxDEV8("p", {
|
|
2070
|
-
className: "mt-2 max-w-2xl text-muted-foreground text-sm",
|
|
2071
|
-
children: description
|
|
2072
|
-
}, undefined, false, undefined, this) : null
|
|
2073
|
-
]
|
|
2074
|
-
}, undefined, true, undefined, this),
|
|
2075
|
-
/* @__PURE__ */ jsxDEV8("div", {
|
|
2076
|
-
className: "flex flex-col items-end gap-2",
|
|
2077
|
-
children: [
|
|
2078
|
-
/* @__PURE__ */ jsxDEV8(LocalDataIndicator, {}, undefined, false, undefined, this),
|
|
2079
|
-
showSaveAction ? /* @__PURE__ */ jsxDEV8(SaveToStudioButton, {
|
|
2080
|
-
...saveProps
|
|
2081
|
-
}, undefined, false, undefined, this) : null
|
|
2082
|
-
]
|
|
2083
|
-
}, undefined, true, undefined, this)
|
|
2084
|
-
]
|
|
2085
|
-
}, undefined, true, undefined, this),
|
|
2086
|
-
actions ? /* @__PURE__ */ jsxDEV8("div", {
|
|
2087
|
-
className: "mt-4",
|
|
2088
|
-
children: actions
|
|
2089
|
-
}, undefined, false, undefined, this) : null
|
|
2090
|
-
]
|
|
2091
|
-
}, undefined, true, undefined, this);
|
|
2092
|
-
const slotContent = {
|
|
2093
|
-
header: headerContent,
|
|
2094
|
-
primary: /* @__PURE__ */ jsxDEV8("main", {
|
|
2095
|
-
className: "space-y-4 p-2",
|
|
2096
|
-
children
|
|
2097
|
-
}, undefined, false, undefined, this)
|
|
2098
|
-
};
|
|
2099
|
-
if (sidebar != null) {
|
|
2100
|
-
slotContent.sidebar = /* @__PURE__ */ jsxDEV8("aside", {
|
|
2101
|
-
className: "rounded-2xl border border-border bg-card p-4",
|
|
2102
|
-
children: sidebar
|
|
2103
|
-
}, undefined, false, undefined, this);
|
|
2104
|
-
}
|
|
2105
|
-
return /* @__PURE__ */ jsxDEV8(BundleProvider, {
|
|
2106
|
-
plan,
|
|
2107
|
-
children: /* @__PURE__ */ jsxDEV8(BundleRenderer, {
|
|
2108
|
-
slotContent
|
|
2109
|
-
}, undefined, false, undefined, this)
|
|
2110
|
-
}, undefined, false, undefined, this);
|
|
2111
|
-
}
|
|
2112
|
-
|
|
2113
|
-
// src/utils/generateSpecFromTemplate.ts
|
|
2114
|
-
function generateSpecFromTemplate(template) {
|
|
2115
|
-
const templateId = template?.id ?? "unknown";
|
|
2116
|
-
if (!template) {
|
|
2117
|
-
return generateDefaultSpec(templateId);
|
|
2118
|
-
}
|
|
2119
|
-
switch (templateId) {
|
|
2120
|
-
case "crm-pipeline":
|
|
2121
|
-
return generateCrmPipelineSpec(template.schema.contracts);
|
|
2122
|
-
case "saas-boilerplate":
|
|
2123
|
-
return generateSaasBoilerplateSpec(template.schema.contracts);
|
|
2124
|
-
case "agent-console":
|
|
2125
|
-
return generateAgentConsoleSpec(template.schema.contracts);
|
|
2126
|
-
case "todos-app":
|
|
2127
|
-
return generateTodosSpec(template.schema.contracts);
|
|
2128
|
-
case "messaging-app":
|
|
2129
|
-
return generateMessagingSpec(template.schema.contracts);
|
|
2130
|
-
case "recipe-app-i18n":
|
|
2131
|
-
return generateRecipeSpec(template.schema.contracts);
|
|
2132
|
-
default:
|
|
2133
|
-
return generateDefaultSpec(templateId);
|
|
2134
|
-
}
|
|
2135
|
-
}
|
|
2136
|
-
function generateCrmPipelineSpec(contracts) {
|
|
2137
|
-
return `// CRM Pipeline Specs
|
|
2138
|
-
// Contracts: ${contracts.join(", ")}
|
|
2
|
+
import{useCallback as d,useEffect as B0,useMemo as $1,useRef as N1,useState as m}from"react";var D0="contractspec-evolution-data";function q0($){let[N,q]=m([]),[J,X]=m([]),[K,z]=m([]),[U,w]=m([]),[V,f]=m(!1),F=N1([]),[Y,b]=m(0);B0(()=>{try{let P=localStorage.getItem(`${D0}-${$}`);if(P){let G=JSON.parse(P);z(G.suggestions.map((M)=>({...M,createdAt:new Date(M.createdAt)})))}}catch{}},[$]),B0(()=>{try{localStorage.setItem(`${D0}-${$}`,JSON.stringify({suggestions:K}))}catch{}},[K,$]);let W=d((P,G,M,E)=>{let u={operation:{name:P,version:"1.0.0",tenantId:"sandbox"},durationMs:G,success:M,timestamp:new Date,errorCode:E};F.current.push(u),b((B)=>B+1)},[]),Q=d(()=>{let P=F.current;if(P.length<5)return;let G=new Map;for(let B of P){let y=`${B.operation.name}.v${B.operation.version}`,a=G.get(y)??[];a.push(B),G.set(y,a)}let M=[],E=[];G.forEach((B)=>{if(B.length<3)return;let y=B.map((k)=>k.durationMs).sort((k,x)=>k-x),a=B.filter((k)=>!k.success),F0=B.length,j=a.length/F0,e0=y.reduce((k,x)=>k+x,0)/F0,Y0=B.map((k)=>k.timestamp.getTime()),O0=B[0];if(!O0)return;let c={operation:O0.operation,totalCalls:F0,successRate:1-j,errorRate:j,averageLatencyMs:e0,p95LatencyMs:L0(y,0.95),p99LatencyMs:L0(y,0.99),maxLatencyMs:Math.max(...y),lastSeenAt:new Date(Math.max(...Y0)),windowStart:new Date(Math.min(...Y0)),windowEnd:new Date(Math.max(...Y0)),topErrors:a.reduce((k,x)=>{if(x.errorCode)k[x.errorCode]=(k[x.errorCode]??0)+1;return k},{})};if(M.push(c),j>0.1)E.push({operation:c.operation,severity:j>0.3?"high":j>0.2?"medium":"low",metric:"error-rate",description:`Error rate ${(j*100).toFixed(1)}% exceeds threshold`,detectedAt:new Date,threshold:0.1,observedValue:j});if(c.p99LatencyMs>500)E.push({operation:c.operation,severity:c.p99LatencyMs>1000?"high":c.p99LatencyMs>750?"medium":"low",metric:"latency",description:`P99 latency ${c.p99LatencyMs.toFixed(0)}ms exceeds threshold`,detectedAt:new Date,threshold:500,observedValue:c.p99LatencyMs})}),q(M),X(E);let u=E.map((B)=>({operation:B.operation,category:B.metric==="latency"?"performance":"error-handling",summary:B.metric==="latency"?"Latency regression detected":"Error spike detected",justification:B.description,recommendedActions:B.metric==="latency"?["Add caching layer","Optimize database queries","Consider pagination"]:["Add retry logic","Improve error handling","Add circuit breaker"]}));w(u)},[]),H=d(async()=>{if(J.length===0)return;f(!0),await new Promise((G)=>setTimeout(G,800));let P=J.map((G)=>({id:`suggestion-${Date.now()}-${Math.random().toString(36).slice(2,9)}`,intent:{id:`intent-${G.operation.name}`,type:G.metric==="latency"?"latency-regression":G.metric==="error-rate"?"error-spike":"throughput-drop",description:G.description,operation:G.operation,confidence:{score:G.severity==="high"?0.9:G.severity==="medium"?0.7:0.5,sampleSize:N.find((M)=>M.operation.name===G.operation.name)?.totalCalls??0}},target:G.operation,proposal:{summary:q1(G),rationale:P1(G),changeType:G.metric==="error-rate"?"policy-update":"revision",recommendedActions:J1(G)},confidence:G.severity==="high"?0.85:G.severity==="medium"?0.7:0.55,createdAt:new Date,createdBy:"ai-evolution-agent",status:"pending",priority:G.severity}));z((G)=>[...G,...P]),f(!1)},[J,N]),Z=d((P,G)=>{z((M)=>M.map((E)=>E.id===P?{...E,status:"approved"}:E))},[]),A=d((P,G)=>{z((M)=>M.map((E)=>E.id===P?{...E,status:"rejected"}:E))},[]),L=d(()=>{F.current=[],b(0),q([]),X([]),z([]),w([]),localStorage.removeItem(`${D0}-${$}`)},[$]);return $1(()=>({usageStats:N,anomalies:J,suggestions:K,hints:U,loading:V,trackOperation:W,analyzeUsage:Q,generateSuggestions:H,approveSuggestion:Z,rejectSuggestion:A,clear:L,operationCount:Y}),[N,J,K,U,V,W,Q,H,Z,A,L,Y])}function L0($,N){if(!$.length)return 0;if($.length===1)return $[0]??0;let q=Math.min($.length-1,Math.floor(N*$.length));return $[q]??0}function q1($){if($.metric==="latency")return`Add caching and pagination to ${$.operation.name} to reduce latency`;if($.metric==="error-rate")return`Add retry policy and circuit breaker to ${$.operation.name}`;return`Optimize ${$.operation.name} for improved throughput`}function P1($){if($.metric==="latency")return`The operation ${$.operation.name} is experiencing P99 latency of ${$.observedValue?.toFixed(0)}ms, which is above the recommended threshold of ${$.threshold}ms. This can impact user experience and downstream operations.`;if($.metric==="error-rate")return`The error rate for ${$.operation.name} is ${(($.observedValue??0)*100).toFixed(1)}%, indicating potential issues with input validation, external dependencies, or resource limits.`;return`Throughput for ${$.operation.name} has dropped significantly, suggesting potential bottlenecks or reduced demand that should be investigated.`}function J1($){if($.metric==="latency")return["Add response caching for frequently accessed data","Implement pagination for large result sets","Optimize database queries with proper indexing","Consider adding a GraphQL DataLoader for batching"];if($.metric==="error-rate")return["Add input validation at the contract level","Implement retry policy with exponential backoff","Add circuit breaker for external dependencies","Enhance error logging for better debugging"];return["Review resource allocation and scaling policies","Check for upstream routing or load balancer issues","Validate feature flag configurations","Monitor dependency health metrics"]}import{Button as n,LoaderBlock as Z1}from"@contractspec/lib.design-system";import{Badge as g}from"@contractspec/lib.ui-kit-web/ui/badge";import{Card as l}from"@contractspec/lib.ui-kit-web/ui/card";import{useCallback as t,useMemo as Q1}from"react";import{jsx as D,jsxs as _}from"react/jsx-runtime";function K2({templateId:$,onLog:N}){let{usageStats:q,anomalies:J,suggestions:X,hints:K,loading:z,trackOperation:U,analyzeUsage:w,generateSuggestions:V,approveSuggestion:f,rejectSuggestion:F,clear:Y,operationCount:b}=q0($),W=t(()=>{let P=[{name:`${$}.list`,duration:150,success:!0},{name:`${$}.list`,duration:180,success:!0},{name:`${$}.create`,duration:350,success:!0},{name:`${$}.create`,duration:420,success:!1,error:"VALIDATION_ERROR"},{name:`${$}.list`,duration:200,success:!0},{name:`${$}.get`,duration:80,success:!0},{name:`${$}.update`,duration:280,success:!0},{name:`${$}.list`,duration:950,success:!0},{name:`${$}.delete`,duration:150,success:!1,error:"NOT_FOUND"},{name:`${$}.create`,duration:380,success:!0}];for(let G of P)U(G.name,G.duration,G.success,G.error);N?.(`Simulated ${P.length} operations`),setTimeout(()=>{w(),N?.("Analysis complete")},100)},[$,U,w,N]),Q=t(async()=>{await V(),N?.("AI suggestions generated")},[V,N]),H=t((P)=>{f(P),N?.(`Suggestion ${P.slice(0,8)} approved`)},[f,N]),Z=t((P)=>{F(P),N?.(`Suggestion ${P.slice(0,8)} rejected`)},[F,N]),A=t(()=>{Y(),N?.("Evolution data cleared")},[Y,N]),L=Q1(()=>X.filter((P)=>P.status==="pending"),[X]);return _("div",{className:"space-y-6",children:[_("div",{className:"flex items-center justify-between",children:[_("div",{children:[D("h2",{className:"font-semibold text-xl",children:"AI Evolution Engine"}),D("p",{className:"text-muted-foreground text-sm",children:"Analyze usage patterns and get AI-powered suggestions"})]}),_("div",{className:"flex items-center gap-2",children:[_(g,{variant:"secondary",children:[b," ops tracked"]}),D(n,{variant:"ghost",size:"sm",onPress:A,children:"Clear"})]})]}),_(l,{className:"p-4",children:[_("div",{className:"flex flex-wrap items-center gap-3",children:[D(n,{variant:"default",size:"sm",onPress:W,children:"Simulate Operations"}),D(n,{variant:"outline",size:"sm",onPress:w,disabled:b<5,children:"Analyze Usage"}),D(n,{variant:"outline",size:"sm",onPress:Q,disabled:J.length===0||z,children:z?"Generating...":"Generate AI Suggestions"})]}),D("p",{className:"mt-2 text-muted-foreground text-xs",children:"Simulate sandbox operations, analyze patterns, and generate AI improvement suggestions."})]}),z&&D(Z1,{label:"Generating AI suggestions..."}),q.length>0&&_(l,{className:"p-4",children:[D("h3",{className:"mb-3 font-semibold",children:"Usage Statistics"}),D("div",{className:"grid gap-3 md:grid-cols-2 lg:grid-cols-3",children:q.map((P)=>D(X1,{stat:P},P.operation.name))})]}),J.length>0&&_(l,{className:"p-4",children:[_("div",{className:"mb-3 flex items-center justify-between",children:[D("h3",{className:"font-semibold",children:"Detected Anomalies"}),_(g,{variant:"secondary",className:"border-amber-500/30 bg-amber-500/20 text-amber-400",children:[J.length," issues"]})]}),D("div",{className:"space-y-2",children:J.map((P,G)=>D(z1,{anomaly:P},`${P.operation.name}-${G}`))})]}),X.length>0&&_(l,{className:"p-4",children:[_("div",{className:"mb-3 flex items-center justify-between",children:[D("h3",{className:"font-semibold",children:"AI Suggestions"}),D("div",{className:"flex items-center gap-2",children:L.length>0&&_(g,{variant:"secondary",className:"border-amber-500/30 bg-amber-500/20 text-amber-400",children:[L.length," pending"]})})]}),D("div",{className:"space-y-3",children:X.map((P)=>D(H1,{suggestion:P,onApprove:H,onReject:Z},P.id))})]}),K.length>0&&_(l,{className:"p-4",children:[D("h3",{className:"mb-3 font-semibold",children:"Optimization Hints"}),D("div",{className:"space-y-2",children:K.map((P,G)=>D(G1,{hint:P},`${P.operation.name}-${G}`))})]}),q.length===0&&J.length===0&&X.length===0&&D(l,{className:"p-8 text-center",children:D("p",{className:"text-muted-foreground",children:'Click "Simulate Operations" to generate sample data for analysis.'})})]})}function X1({stat:$}){return _("div",{className:"rounded-lg border border-violet-500/20 bg-violet-500/5 p-3",children:[_("div",{className:"mb-2 flex items-center justify-between",children:[D("span",{className:"font-medium font-mono text-sm",children:$.operation.name}),_(g,{variant:$.errorRate>0.1?"destructive":"default",className:$.errorRate>0.1?"":"border-green-500/30 bg-green-500/20 text-green-400",children:[((1-$.errorRate)*100).toFixed(0),"% success"]})]}),_("div",{className:"grid grid-cols-2 gap-2 text-xs",children:[_("div",{children:[D("span",{className:"text-muted-foreground",children:"Total Calls:"})," ",D("span",{className:"font-medium",children:$.totalCalls})]}),_("div",{children:[D("span",{className:"text-muted-foreground",children:"Avg Latency:"})," ",_("span",{className:"font-medium",children:[$.averageLatencyMs.toFixed(0),"ms"]})]}),_("div",{children:[D("span",{className:"text-muted-foreground",children:"P95:"})," ",_("span",{className:"font-medium",children:[$.p95LatencyMs.toFixed(0),"ms"]})]}),_("div",{children:[D("span",{className:"text-muted-foreground",children:"P99:"})," ",_("span",{className:"font-medium",children:[$.p99LatencyMs.toFixed(0),"ms"]})]})]})]})}function z1({anomaly:$}){return _("div",{className:"flex items-center justify-between rounded-lg border border-amber-500/30 bg-amber-500/5 p-3",children:[_("div",{className:"flex items-center gap-3",children:[D("span",{className:`text-lg ${{low:"text-amber-400",medium:"text-orange-400",high:"text-red-400"}[$.severity]}`,title:`${$.severity} severity`,children:$.severity==="high"?"\uD83D\uDD34":$.severity==="medium"?"\uD83D\uDFE0":"\uD83D\uDFE1"}),_("div",{children:[D("p",{className:"font-medium text-sm",children:$.description}),_("p",{className:"text-muted-foreground text-xs",children:[$.operation.name," \u2022 ",$.metric]})]})]}),D(g,{variant:$.severity==="high"?"destructive":"secondary",className:$.severity==="medium"?"border-amber-500/30 bg-amber-500/20 text-amber-400":"",children:$.severity})]})}function H1({suggestion:$,onApprove:N,onReject:q}){let X=((K)=>{switch(K){case"pending":return{variant:"secondary",className:"bg-amber-500/20 text-amber-400 border-amber-500/30"};case"approved":return{variant:"default",className:"bg-green-500/20 text-green-400 border-green-500/30"};case"rejected":return{variant:"destructive",className:""};default:return{variant:"secondary",className:""}}})($.status);return _("div",{className:"rounded-lg border border-violet-500/30 bg-violet-500/5 p-4",children:[_("div",{className:"mb-2 flex items-start justify-between",children:[_("div",{className:"flex-1",children:[_("div",{className:"flex items-center gap-2",children:[D("span",{className:"text-lg",children:$.intent.type==="latency-regression"?"\u26A1":$.intent.type==="error-spike"?"\uD83D\uDD25":"\uD83D\uDCC9"}),D("h4",{className:"font-medium",children:$.proposal.summary})]}),D("p",{className:"mt-1 text-muted-foreground text-sm",children:$.proposal.rationale})]}),D(g,{variant:X.variant,className:X.className,children:$.status})]}),$.proposal.recommendedActions&&$.proposal.recommendedActions.length>0&&_("div",{className:"mt-3",children:[D("p",{className:"mb-1 font-semibold text-violet-400 text-xs uppercase",children:"Recommended Actions"}),D("ul",{className:"list-inside list-disc space-y-1 text-xs",children:$.proposal.recommendedActions.slice(0,3).map((K,z)=>D("li",{children:K},z))})]}),_("div",{className:"mt-3 flex items-center justify-between",children:[_("div",{className:"flex items-center gap-2 text-xs",children:[_("span",{className:"text-muted-foreground",children:["Confidence: ",($.confidence*100).toFixed(0),"%"]}),D("span",{className:"text-muted-foreground",children:"\u2022"}),D(g,{variant:"secondary",children:$.priority})]}),$.status==="pending"&&_("div",{className:"flex items-center gap-2",children:[D(n,{variant:"outline",size:"sm",onPress:()=>q($.id),children:"Reject"}),D(n,{variant:"default",size:"sm",onPress:()=>N($.id),children:"Approve"})]})]})]})}function G1({hint:$}){return D("div",{className:"rounded-lg border border-blue-500/20 bg-blue-500/5 p-3",children:_("div",{className:"flex items-start gap-3",children:[D("span",{className:"text-lg",children:{schema:"\uD83D\uDCD0",policy:"\uD83D\uDD12",performance:"\u26A1","error-handling":"\uD83D\uDEE1\uFE0F"}[$.category]}),_("div",{className:"flex-1",children:[D("p",{className:"font-medium",children:$.summary}),D("p",{className:"mt-1 text-muted-foreground text-xs",children:$.justification}),$.recommendedActions.length>0&&D("ul",{className:"mt-2 list-inside list-disc text-xs",children:$.recommendedActions.slice(0,2).map((q,J)=>D("li",{children:q},J))})]})]})})}import{Button as E0}from"@contractspec/lib.design-system";import{Badge as s}from"@contractspec/lib.ui-kit-web/ui/badge";import{Card as K1}from"@contractspec/lib.ui-kit-web/ui/card";import{useCallback as k0,useMemo as C0}from"react";import{jsx as h,jsxs as T}from"react/jsx-runtime";function U2({templateId:$,expanded:N=!1,onToggle:q,onLog:J,onOpenEvolution:X}){let{anomalies:K,suggestions:z,loading:U,approveSuggestion:w,rejectSuggestion:V,operationCount:f}=q0($),F=C0(()=>z.filter((Q)=>Q.status==="pending"),[z]),Y=C0(()=>K.sort((Q,H)=>{let Z={high:0,medium:1,low:2};return Z[Q.severity]-Z[H.severity]}).slice(0,3),[K]),b=k0((Q)=>{w(Q),J?.(`Approved suggestion ${Q.slice(0,8)}`)},[w,J]),W=k0((Q)=>{V(Q),J?.(`Rejected suggestion ${Q.slice(0,8)}`)},[V,J]);if(!N)return T("button",{onClick:q,className:"flex items-center gap-2 rounded-lg border border-violet-500/30 bg-violet-500/10 px-3 py-2 text-sm transition hover:bg-violet-500/20",type:"button",children:[h("span",{children:"\uD83E\uDD16"}),h("span",{children:"Evolution"}),F.length>0&&h(s,{variant:"secondary",className:"border-amber-500/30 bg-amber-500/20 text-amber-400",children:F.length}),K.length>0&&F.length===0&&h(s,{variant:"destructive",children:K.length})]});return T(K1,{className:"w-80 overflow-hidden",children:[T("div",{className:"flex items-center justify-between border-violet-500/20 border-b bg-violet-500/5 px-3 py-2",children:[T("div",{className:"flex items-center gap-2",children:[h("span",{children:"\uD83E\uDD16"}),h("span",{className:"font-semibold text-sm",children:"Evolution"})]}),T("div",{className:"flex items-center gap-1",children:[X&&h(E0,{variant:"ghost",size:"sm",onPress:X,children:"Expand"}),h("button",{onClick:q,className:"p-1 text-muted-foreground hover:text-foreground",type:"button",title:"Collapse",children:"\u2715"})]})]}),T("div",{className:"max-h-96 overflow-y-auto p-3",children:[T("div",{className:"mb-3 flex items-center justify-between text-xs",children:[T("span",{className:"text-muted-foreground",children:[f," ops tracked"]}),T("div",{className:"flex items-center gap-2",children:[K.length>0&&T(s,{variant:"destructive",children:[K.length," anomalies"]}),F.length>0&&T(s,{variant:"secondary",className:"border-amber-500/30 bg-amber-500/20 text-amber-400",children:[F.length," pending"]})]})]}),U&&h("div",{className:"py-4 text-center text-muted-foreground text-sm",children:"Generating suggestions..."}),Y.length>0&&T("div",{className:"mb-4",children:[h("p",{className:"mb-2 font-semibold text-violet-400 text-xs uppercase",children:"Top Issues"}),h("div",{className:"space-y-2",children:Y.map((Q,H)=>T("div",{className:"rounded border border-amber-500/20 bg-amber-500/5 p-2 text-xs",children:[T("div",{className:"flex items-center gap-2",children:[h("span",{children:Q.severity==="high"?"\uD83D\uDD34":Q.severity==="medium"?"\uD83D\uDFE0":"\uD83D\uDFE1"}),h("span",{className:"truncate font-medium",children:Q.operation.name})]}),h("p",{className:"mt-1 truncate text-muted-foreground",children:Q.description})]},`${Q.operation.name}-${H}`))})]}),F.length>0&&T("div",{children:[h("p",{className:"mb-2 font-semibold text-violet-400 text-xs uppercase",children:"Pending Suggestions"}),T("div",{className:"space-y-2",children:[F.slice(0,3).map((Q)=>h(V1,{suggestion:Q,onApprove:b,onReject:W},Q.id)),F.length>3&&T("p",{className:"text-center text-muted-foreground text-xs",children:["+",F.length-3," more suggestions"]})]})]}),K.length===0&&F.length===0&&!U&&h("div",{className:"py-4 text-center text-muted-foreground text-xs",children:"No issues detected. Keep coding!"})]}),X&&h("div",{className:"border-violet-500/20 border-t p-2",children:h(E0,{variant:"ghost",size:"sm",className:"w-full",onPress:X,children:"Open Evolution Dashboard \u2192"})})]})}function V1({suggestion:$,onApprove:N,onReject:q}){return T("div",{className:"rounded border border-violet-500/20 bg-violet-500/5 p-2",children:[h("div",{className:"flex items-start justify-between gap-2",children:T("div",{className:"min-w-0 flex-1",children:[h("p",{className:"truncate font-medium text-xs",children:$.proposal.summary}),T("div",{className:"mt-1 flex items-center gap-2 text-xs",children:[h(s,{variant:"secondary",children:$.priority}),T("span",{className:"text-muted-foreground",children:[($.confidence*100).toFixed(0),"%"]})]})]})}),T("div",{className:"mt-2 flex justify-end gap-1",children:[h("button",{onClick:()=>q($.id),className:"rounded px-2 py-0.5 text-red-400 text-xs hover:bg-red-400/10",type:"button",children:"Reject"}),h("button",{onClick:()=>N($.id),className:"rounded bg-violet-500/20 px-2 py-0.5 text-violet-400 text-xs hover:bg-violet-500/30",type:"button",children:"Approve"})]})]})}import{createContext as F1,useContext as Y1}from"react";var I0=Symbol.for("@contractspec/lib.example-shared-ui/template-runtime-context");function D1(){let $=globalThis;return $[I0]??=F1(null),$[I0]}var f1=D1();function r(){let $=Y1(f1);if(!$)throw Error("useTemplateRuntime must be used within a TemplateRuntimeProvider");return $}import{RefreshCw as W1,Shield as _1}from"lucide-react";import{useState as U1}from"react";import{jsx as f0,jsxs as W0}from"react/jsx-runtime";function P0(){let{projectId:$,templateId:N,template:q,installer:J}=r(),[X,K]=U1(!1),z=async()=>{K(!0);try{await J.install(N,{projectId:$})}finally{K(!1)}};return W0("div",{className:"inline-flex items-center gap-2 rounded-full border border-border bg-muted/40 px-3 py-1 text-muted-foreground text-xs",children:[f0(_1,{className:"h-3.5 w-3.5 text-violet-400"}),W0("span",{children:["Local runtime \xB7"," ",f0("span",{className:"font-semibold text-foreground",children:q.name})]}),W0("button",{type:"button",className:"inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 font-semibold text-[11px] text-muted-foreground hover:text-foreground",onClick:z,disabled:X,children:[f0(W1,{className:"h-3 w-3"}),X?"Resetting\u2026":"Reset data"]})]})}function S0($){let N=$.split(".");return(N[N.length-1]??$).split("-").map((J)=>J.charAt(0).toUpperCase()+J.slice(1)).join(" ")}import{useCallback as A1,useEffect as v0,useState as J0}from"react";function y0({engine:$,fetchData:N,presentationId:q,presentations:J,resolvePresentation:X,templateId:K}){let[z,U]=J0(""),[w,V]=J0(""),[f,F]=J0(!1),[Y,b]=J0(null);v0(()=>{if(q&&J.includes(q)){U(q);return}if(J.length===0){U("");return}if(!J.includes(z))U(J[0]??"")},[q,J,z,K]);let W=A1(async()=>{if(!z||!$)return;F(!0),b(null);try{if(!X)throw Error("resolvePresentation not available in runtime context");let Q=X(z);if(!Q)throw Error(`Presentation descriptor not found: ${z}`);let H=await N(z),Z=await $.render("markdown",Q,{data:H.data});V(Z.body)}catch(Q){b(Q instanceof Error?Q:Error("Failed to render markdown"))}finally{F(!1)}},[$,N,X,z]);return v0(()=>{W()},[W]),{error:Y,loading:f,markdownContent:w,renderMarkdown:W,selectedPresentation:z,setSelectedPresentation:U}}import{Button as c0,ErrorState as w1,LoaderBlock as b1,MarkdownRenderer as R1}from"@contractspec/lib.design-system";import{Badge as h1}from"@contractspec/lib.ui-kit-web/ui/badge";import{Card as r0}from"@contractspec/lib.ui-kit-web/ui/card";import{useCallback as M1}from"react";import{jsx as C,jsxs as Z0}from"react/jsx-runtime";function i2({templateId:$,presentationId:N,className:q}){let{engine:J,template:X,templateId:K,resolvePresentation:z,fetchData:U}=r(),w=$??K,V=X?.presentations??[],{error:f,loading:F,markdownContent:Y,renderMarkdown:b,selectedPresentation:W,setSelectedPresentation:Q}=y0({engine:J,fetchData:U,presentationId:N,presentations:V,resolvePresentation:z,templateId:w});if(!V.length)return C(r0,{className:q,children:C("div",{className:"p-6 text-center",children:C("p",{className:"text-muted-foreground",children:"No presentations available for this template."})})});let H=M1(()=>{if(Y)navigator.clipboard.writeText(Y)},[Y]);return Z0("div",{className:q,children:[Z0("div",{className:"mb-4 flex flex-wrap items-center gap-2",children:[C("span",{className:"font-medium text-muted-foreground text-sm",children:"Presentation:"}),V.map((Z)=>C(c0,{variant:W===Z?"default":"outline",size:"sm",onPress:()=>Q(Z),children:S0(Z)},Z)),Z0("div",{className:"ml-auto flex items-center gap-2",children:[C(h1,{variant:"secondary",children:"LLM-friendly"}),C(c0,{variant:"outline",size:"sm",onPress:H,disabled:!Y||F,children:"Copy"})]})]}),Z0(r0,{className:"overflow-hidden",children:[F&&C(b1,{label:"Rendering markdown..."}),f&&C(w1,{title:"Render failed",description:f.message,onRetry:b,retryLabel:"Retry"}),!F&&!f&&Y&&C("div",{className:"p-6",children:C(R1,{content:Y})}),!F&&!f&&!Y&&C("div",{className:"p-6 text-center",children:C("p",{className:"text-muted-foreground",children:"Select a presentation to view its markdown output."})})]})]})}import*as i0 from"react";import{useContext as j0,useMemo as I}from"react";import{jsx as O1}from"react/jsx-runtime";var _0=i0.createContext(null);function p2({templateId:$,role:N="user",device:q="desktop",children:J}){let X=I(()=>T1($,N),[$,N]),K=I(()=>{return X.filter((W)=>{let Q=W.conditions;if(!Q)return!0;if(Q.role&&!Q.role.includes(N))return!1;if(Q.device&&Q.device!=="any"&&Q.device!==q)return!1;return!0})},[X,N,q]),z=I(()=>{let W=new Map;for(let Q of K)W.set(Q.target,Q);return W},[K]),U=I(()=>(W,Q)=>{let H=z.get(W);if(!H)return Q;let Z={...Q};for(let A of H.modifications)switch(A.op){case"hide":Z={...Z,hidden:!0};break;case"relabel":Z={...Z,label:A.label};break;case"reorder":Z={...Z,position:A.position};break;case"restyle":Z={...Z,className:A.className,variant:A.variant};break;case"set-default":Z={...Z,defaultValue:A.value};break}return Z},[z]),w=I(()=>(W)=>{return z.get(W)?.modifications.some((H)=>H.op==="hide")??!1},[z]),V=I(()=>(W,Q)=>{let Z=z.get(W)?.modifications.find((A)=>A.op==="relabel");return Z&&Z.op==="relabel"?Z.label:Q},[z]),f=I(()=>(W,Q)=>{let Z=z.get(W)?.modifications.find((A)=>A.op==="reorder");return Z&&Z.op==="reorder"?Z.position:Q},[z]),F=I(()=>(W)=>{let H=z.get(W)?.modifications.find((Z)=>Z.op==="restyle");if(H&&H.op==="restyle")return{className:H.className,variant:H.variant};return{}},[z]),Y=I(()=>(W,Q)=>{let Z=z.get(W)?.modifications.find((A)=>A.op==="set-default");return Z&&Z.op==="set-default"?Z.value:Q},[z]),b=I(()=>({overlays:K,applyOverlay:U,isHidden:w,getLabel:V,getPosition:f,getStyle:F,getDefault:Y,role:N,device:q}),[K,U,w,V,f,F,Y,N,q]);return O1(_0.Provider,{value:b,children:J})}function u2(){let $=j0(_0);if(!$)throw Error("useOverlayContext must be used within an OverlayContextProvider");return $}function d2(){return j0(_0)!==null}function T1($,N){return{"crm-pipeline":[{id:"crm-hide-internal-fields",target:"deal.internalNotes",modifications:[{op:"hide"}],conditions:{role:["viewer","user"]}},{id:"crm-relabel-value",target:"deal.value",modifications:[{op:"relabel",label:"Deal Amount"}]}],"saas-boilerplate":[{id:"saas-hide-billing",target:"settings.billing",modifications:[{op:"hide"}],conditions:{role:["viewer"]}},{id:"saas-restyle-plan",target:"settings.plan",modifications:[{op:"restyle",variant:"premium"}],conditions:{role:["admin"]}}],"agent-console":[{id:"agent-hide-cost",target:"run.cost",modifications:[{op:"hide"}],conditions:{role:["viewer"]}},{id:"agent-relabel-tokens",target:"run.tokens",modifications:[{op:"relabel",label:"Token Usage"}]}],"todos-app":[{id:"todos-hide-assignee",target:"task.assignee",modifications:[{op:"hide"}],conditions:{device:"mobile"}}],"messaging-app":[{id:"messaging-reorder-timestamp",target:"message.timestamp",modifications:[{op:"reorder",position:0}]}],"recipe-app-i18n":[{id:"recipe-relabel-servings",target:"recipe.servings",modifications:[{op:"relabel",label:"Portions"}]}]}[$]??[]}import{useCallback as Q0,useEffect as x0,useMemo as B1,useRef as L1,useState as g0}from"react";var U0="contractspec-behavior-data",E1=["playground","specs","builder","markdown","evolution","canvas_add","canvas_delete","spec_save","spec_validate","ai_suggestions"];function p0($){let[N,q]=g0([]),J=L1(new Date),[X,K]=g0(0);x0(()=>{try{let f=localStorage.getItem(U0);if(f){let F=JSON.parse(f);q(F.events.map((Y)=>({...Y,timestamp:new Date(Y.timestamp)}))),J.current=new Date(F.sessionStart)}}catch{}},[]),x0(()=>{if(N.length>0)try{localStorage.setItem(U0,JSON.stringify({events:N.map((f)=>({...f,timestamp:f.timestamp.toISOString()})),sessionStart:J.current.toISOString()}))}catch{}},[N]);let z=Q0((f,F)=>{let Y={type:f,timestamp:new Date,templateId:$,metadata:F};q((b)=>[...b,Y]),K((b)=>b+1)},[$]),U=Q0((f)=>{return N.filter((F)=>F.type===f)},[N]),w=Q0(()=>{let F=new Date().getTime()-J.current.getTime(),Y=new Map;for(let P of N){let G=Y.get(P.templateId)??0;Y.set(P.templateId,G+1)}let b=Array.from(Y.entries()).map(([P,G])=>({templateId:P,count:G})).sort((P,G)=>G.count-P.count).slice(0,3),W=new Map;for(let P of N)if(P.type==="mode_change"&&P.metadata?.mode){let G=P.metadata.mode,M=W.get(G)??0;W.set(G,M+1)}let Q=Array.from(W.entries()).map(([P,G])=>({mode:P,count:G})).sort((P,G)=>G.count-P.count),H=new Set;for(let P of N){if(P.type==="mode_change"&&P.metadata?.mode)H.add(P.metadata.mode);if(P.type==="feature_usage"&&P.metadata?.feature)H.add(P.metadata.feature);if(P.type==="canvas_interaction"){let G=P.metadata?.action;if(G==="add")H.add("canvas_add");if(G==="delete")H.add("canvas_delete")}if(P.type==="spec_edit"){let G=P.metadata?.action;if(G==="save")H.add("spec_save");if(G==="validate")H.add("spec_validate")}}let Z=E1.filter((P)=>!H.has(P)),A=N.filter((P)=>P.type==="error").length,L=k1(Array.from(H),Z,Q,N.length);return{totalEvents:N.length,sessionDuration:F,mostUsedTemplates:b,mostUsedModes:Q,featuresUsed:Array.from(H),unusedFeatures:Z,errorCount:A,recommendations:L}},[N]),V=Q0(()=>{q([]),K(0),J.current=new Date,localStorage.removeItem(U0)},[]);return B1(()=>({trackEvent:z,getSummary:w,getEventsByType:U,eventCount:X,sessionStart:J.current,clear:V}),[z,w,U,X,V])}function k1($,N,q,J){let X=[];if(N.includes("evolution"))X.push("Try the AI Evolution mode to get automated improvement suggestions");if(N.includes("markdown"))X.push("Use Markdown preview to see documentation for your specs");if(N.includes("builder"))X.push("Explore the Visual Builder to design your UI components");if(!$.includes("spec_validate")&&$.includes("specs"))X.push("Don't forget to validate your specs before saving");if($.includes("evolution")&&!$.includes("ai_suggestions"))X.push("Generate AI suggestions to get actionable improvement recommendations");if(J>50)X.push("Great engagement! Consider saving your work regularly");if(q.length===1)X.push("Try different modes to explore all sandbox capabilities");return X}import{Button as u0}from"@contractspec/lib.design-system";import{Badge as A0}from"@contractspec/lib.ui-kit-web/ui/badge";import{Card as C1}from"@contractspec/lib.ui-kit-web/ui/card";import{useCallback as d0,useMemo as I1,useState as S1}from"react";import{jsx as R,jsxs as O}from"react/jsx-runtime";function N4({templateId:$,collapsed:N=!1,onToggle:q}){let{getSummary:J,eventCount:X,clear:K,sessionStart:z}=p0($),[U,w]=S1(!1),V=I1(()=>J(),[J]),f=d0((Y)=>{let b=Math.floor(Y/1000),W=Math.floor(b/60),Q=Math.floor(W/60);if(Q>0)return`${Q}h ${W%60}m`;if(W>0)return`${W}m ${b%60}s`;return`${b}s`},[]),F=d0(()=>{K()},[K]);if(N)return O("button",{onClick:q,className:"flex items-center gap-2 rounded-lg border border-blue-500/30 bg-blue-500/10 px-3 py-2 text-sm transition hover:bg-blue-500/20",type:"button",children:[R("span",{children:"\uD83D\uDCCA"}),R("span",{children:"Insights"}),R(A0,{variant:"secondary",children:X})]});return O(C1,{className:"overflow-hidden",children:[O("div",{className:"flex items-center justify-between border-blue-500/20 border-b bg-blue-500/5 px-4 py-3",children:[O("div",{className:"flex items-center gap-2",children:[R("span",{children:"\uD83D\uDCCA"}),R("span",{className:"font-semibold",children:"Personalization Insights"})]}),O("div",{className:"flex items-center gap-2",children:[R(u0,{variant:"ghost",size:"sm",onPress:()=>w(!U),children:U?"Hide Details":"Show Details"}),q&&R("button",{onClick:q,className:"p-1 text-muted-foreground hover:text-foreground",type:"button",title:"Collapse",children:"\u2715"})]})]}),O("div",{className:"p-4",children:[O("div",{className:"mb-4 grid grid-cols-2 gap-3 md:grid-cols-4",children:[R(X0,{label:"Session Time",value:f(V.sessionDuration),icon:"\u23F1\uFE0F"}),R(X0,{label:"Events Tracked",value:V.totalEvents.toString(),icon:"\uD83D\uDCC8"}),R(X0,{label:"Features Used",value:`${V.featuresUsed.length}/${V.featuresUsed.length+V.unusedFeatures.length}`,icon:"\u2728"}),R(X0,{label:"Errors",value:V.errorCount.toString(),icon:"\u26A0\uFE0F",variant:V.errorCount>0?"warning":"success"})]}),V.recommendations.length>0&&O("div",{className:"mb-4",children:[R("h4",{className:"mb-2 font-semibold text-blue-400 text-xs uppercase",children:"Recommendations"}),R("ul",{className:"space-y-1",children:V.recommendations.map((Y,b)=>O("li",{className:"flex items-start gap-2 text-sm",children:[R("span",{className:"text-blue-400",children:"\uD83D\uDCA1"}),R("span",{children:Y})]},b))})]}),V.unusedFeatures.length>0&&O("div",{className:"mb-4",children:[R("h4",{className:"mb-2 font-semibold text-blue-400 text-xs uppercase",children:"Try These Features"}),R("div",{className:"flex flex-wrap gap-2",children:V.unusedFeatures.slice(0,5).map((Y)=>R(A0,{variant:"secondary",children:w0(Y)},Y))})]}),U&&R(v1,{summary:V,sessionStart:z}),R("div",{className:"mt-4 flex justify-end border-blue-500/10 border-t pt-4",children:R(u0,{variant:"ghost",size:"sm",onPress:F,children:"Clear Data"})})]})]})}function X0({label:$,value:N,icon:q,variant:J="default"}){return O("div",{className:`rounded-lg border p-3 text-center ${{default:"bg-blue-500/5 border-blue-500/20",warning:"bg-amber-500/5 border-amber-500/20",success:"bg-green-500/5 border-green-500/20"}[J]}`,children:[R("div",{className:"mb-1 text-lg",children:q}),R("div",{className:"font-bold text-lg",children:N}),R("div",{className:"text-muted-foreground text-xs",children:$})]})}function v1({summary:$,sessionStart:N}){return O("div",{className:"mt-4 space-y-4 border-blue-500/10 border-t pt-4",children:[$.mostUsedTemplates.length>0&&O("div",{children:[R("h4",{className:"mb-2 font-semibold text-blue-400 text-xs uppercase",children:"Most Used Templates"}),R("div",{className:"space-y-1",children:$.mostUsedTemplates.map(({templateId:q,count:J})=>O("div",{className:"flex items-center justify-between text-sm",children:[R("span",{children:y1(q)}),O("span",{className:"text-muted-foreground",children:[J," events"]})]},q))})]}),$.mostUsedModes.length>0&&O("div",{children:[R("h4",{className:"mb-2 font-semibold text-blue-400 text-xs uppercase",children:"Mode Usage"}),R("div",{className:"space-y-1",children:$.mostUsedModes.map(({mode:q,count:J})=>O("div",{className:"flex items-center justify-between text-sm",children:[R("span",{children:w0(q)}),O("span",{className:"text-muted-foreground",children:[J," switches"]})]},q))})]}),O("div",{children:[R("h4",{className:"mb-2 font-semibold text-blue-400 text-xs uppercase",children:"Features Used"}),R("div",{className:"flex flex-wrap gap-2",children:$.featuresUsed.map((q)=>O(A0,{variant:"default",className:"border-green-500/30 bg-green-500/20 text-green-400",children:["\u2713 ",w0(q)]},q))})]}),O("div",{className:"text-muted-foreground text-xs",children:["Session started: ",N.toLocaleString()]})]})}function w0($){return $.replace(/_/g," ").replace(/\b\w/g,(N)=>N.toUpperCase())}function y1($){return $.replace(/-/g," ").replace(/\b\w/g,(N)=>N.toUpperCase())}import{Sparkles as c1}from"lucide-react";import{useState as m0}from"react";import{jsx as b0,jsxs as l0}from"react/jsx-runtime";function z0({organizationId:$="demo-org",projectName:N,endpoint:q,token:J}){let{installer:X,templateId:K,template:z}=r(),[U,w]=m0("idle"),[V,f]=m0(null);return l0("div",{className:"flex flex-col items-end gap-1",children:[l0("button",{type:"button",className:"btn-primary inline-flex items-center gap-2 text-sm",onClick:async()=>{w("saving"),f(null);try{await X.saveToStudio({templateId:K,projectName:N??`${z.name} demo`,organizationId:$,endpoint:q,token:J}),w("saved"),setTimeout(()=>w("idle"),3000)}catch(Y){w("error"),f(Y instanceof Error?Y.message:"Unknown error")}},disabled:U==="saving",children:[b0(c1,{className:"h-4 w-4"}),U==="saving"?"Publishing\u2026":"Save to Studio"]}),U==="error"&&V?b0("p",{className:"text-destructive text-xs",children:V}):null,U==="saved"?b0("p",{className:"text-emerald-400 text-xs",children:"Template sent to Studio."}):null]})}import{BundleProvider as r1,BundleRenderer as i1}from"@contractspec/lib.surface-runtime/react";import{jsx as S,jsxs as H0}from"react/jsx-runtime";function V4({plan:$,title:N,description:q,sidebar:J,actions:X,showSaveAction:K=!0,saveProps:z,children:U}){let V={header:H0("header",{className:"rounded-2xl border border-border bg-card p-6 shadow-sm",children:[H0("div",{className:"flex flex-wrap items-center justify-between gap-4",children:[H0("div",{children:[S("p",{className:"font-semibold text-muted-foreground text-sm uppercase tracking-wide",children:"ContractSpec Templates"}),S("h1",{className:"font-bold text-3xl",children:N}),q?S("p",{className:"mt-2 max-w-2xl text-muted-foreground text-sm",children:q}):null]}),H0("div",{className:"flex flex-col items-end gap-2",children:[S(P0,{}),K?S(z0,{...z}):null]})]}),X?S("div",{className:"mt-4",children:X}):null]}),primary:S("main",{className:"space-y-4 p-2",children:U})};if(J!=null)V.sidebar=S("aside",{className:"rounded-2xl border border-border bg-card p-4",children:J});return S(r1,{plan:$,children:S(i1,{slotContent:V})})}function e($){let N=$?.id??"unknown";if(!$)return n0(N);switch(N){case"crm-pipeline":return j1($.schema.contracts);case"saas-boilerplate":return x1($.schema.contracts);case"agent-console":return g1($.schema.contracts);case"todos-app":return p1($.schema.contracts);case"messaging-app":return u1($.schema.contracts);case"recipe-app-i18n":return d1($.schema.contracts);default:return n0(N)}}function j1($){return`// CRM Pipeline Specs
|
|
3
|
+
// Contracts: ${$.join(", ")}
|
|
2139
4
|
|
|
2140
5
|
contractSpec("crm.deal.updateStage.v1", {
|
|
2141
6
|
goal: "Move a deal to a different pipeline stage",
|
|
@@ -2204,11 +69,8 @@ contractSpec("crm.contact.list.v1", {
|
|
|
2204
69
|
hasMore: "boolean"
|
|
2205
70
|
}
|
|
2206
71
|
}
|
|
2207
|
-
})
|
|
2208
|
-
}
|
|
2209
|
-
function generateSaasBoilerplateSpec(contracts) {
|
|
2210
|
-
return `// SaaS Boilerplate Specs
|
|
2211
|
-
// Contracts: ${contracts.join(", ")}
|
|
72
|
+
});`}function x1($){return`// SaaS Boilerplate Specs
|
|
73
|
+
// Contracts: ${$.join(", ")}
|
|
2212
74
|
|
|
2213
75
|
contractSpec("saas.project.create.v1", {
|
|
2214
76
|
goal: "Create a new project in an organization",
|
|
@@ -2271,11 +133,8 @@ contractSpec("saas.settings.update.v1", {
|
|
|
2271
133
|
}
|
|
2272
134
|
},
|
|
2273
135
|
events: ["settings.updated"]
|
|
2274
|
-
})
|
|
2275
|
-
}
|
|
2276
|
-
function generateAgentConsoleSpec(contracts) {
|
|
2277
|
-
return `// Agent Console Specs
|
|
2278
|
-
// Contracts: ${contracts.join(", ")}
|
|
136
|
+
});`}function g1($){return`// Agent Console Specs
|
|
137
|
+
// Contracts: ${$.join(", ")}
|
|
2279
138
|
|
|
2280
139
|
contractSpec("agent.run.execute.v1", {
|
|
2281
140
|
goal: "Execute an agent run with specified tools",
|
|
@@ -2341,11 +200,8 @@ contractSpec("agent.agent.create.v1", {
|
|
|
2341
200
|
}
|
|
2342
201
|
},
|
|
2343
202
|
events: ["agent.created"]
|
|
2344
|
-
})
|
|
2345
|
-
}
|
|
2346
|
-
function generateTodosSpec(contracts) {
|
|
2347
|
-
return `// To-dos App Specs
|
|
2348
|
-
// Contracts: ${contracts.join(", ")}
|
|
203
|
+
});`}function p1($){return`// To-dos App Specs
|
|
204
|
+
// Contracts: ${$.join(", ")}
|
|
2349
205
|
|
|
2350
206
|
contractSpec("tasks.board.v1", {
|
|
2351
207
|
goal: "Assign and approve craft work",
|
|
@@ -2404,11 +260,8 @@ contractSpec("tasks.complete.v1", {
|
|
|
2404
260
|
}
|
|
2405
261
|
},
|
|
2406
262
|
events: ["task.completed"]
|
|
2407
|
-
})
|
|
2408
|
-
}
|
|
2409
|
-
function generateMessagingSpec(contracts) {
|
|
2410
|
-
return `// Messaging App Specs
|
|
2411
|
-
// Contracts: ${contracts.join(", ")}
|
|
263
|
+
});`}function u1($){return`// Messaging App Specs
|
|
264
|
+
// Contracts: ${$.join(", ")}
|
|
2412
265
|
|
|
2413
266
|
contractSpec("messaging.send.v1", {
|
|
2414
267
|
goal: "Deliver intent-rich updates",
|
|
@@ -2461,11 +314,8 @@ contractSpec("messaging.read.v1", {
|
|
|
2461
314
|
}
|
|
2462
315
|
},
|
|
2463
316
|
events: ["message.read"]
|
|
2464
|
-
})
|
|
2465
|
-
}
|
|
2466
|
-
function generateRecipeSpec(contracts) {
|
|
2467
|
-
return `// Recipe App (i18n) Specs
|
|
2468
|
-
// Contracts: ${contracts.join(", ")}
|
|
317
|
+
});`}function d1($){return`// Recipe App (i18n) Specs
|
|
318
|
+
// Contracts: ${$.join(", ")}
|
|
2469
319
|
|
|
2470
320
|
contractSpec("recipes.lookup.v1", {
|
|
2471
321
|
goal: "Serve bilingual rituals",
|
|
@@ -2512,13 +362,10 @@ contractSpec("recipes.favorite.toggle.v1", {
|
|
|
2512
362
|
}
|
|
2513
363
|
},
|
|
2514
364
|
events: ["recipe.favorited", "recipe.unfavorited"]
|
|
2515
|
-
})
|
|
2516
|
-
}
|
|
2517
|
-
function generateDefaultSpec(templateId) {
|
|
2518
|
-
return `// ${templateId} Specs
|
|
365
|
+
});`}function n0($){return`// ${$} Specs
|
|
2519
366
|
|
|
2520
|
-
contractSpec("${
|
|
2521
|
-
goal: "Main operation for ${
|
|
367
|
+
contractSpec("${$}.main.v1", {
|
|
368
|
+
goal: "Main operation for ${$}",
|
|
2522
369
|
transport: { gql: { query: "main" } },
|
|
2523
370
|
io: {
|
|
2524
371
|
input: {
|
|
@@ -2528,955 +375,38 @@ contractSpec("${templateId}.main.v1", {
|
|
|
2528
375
|
result: "unknown"
|
|
2529
376
|
}
|
|
2530
377
|
}
|
|
2531
|
-
})
|
|
2532
|
-
}
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
const [loading, setLoading] = useState7(true);
|
|
2543
|
-
const [validation, setValidation] = useState7(null);
|
|
2544
|
-
const [lastSaved, setLastSaved] = useState7(null);
|
|
2545
|
-
useEffect4(() => {
|
|
2546
|
-
setLoading(true);
|
|
2547
|
-
try {
|
|
2548
|
-
const stored = localStorage.getItem(`${SPEC_STORAGE_KEY}-${templateId}`);
|
|
2549
|
-
if (stored) {
|
|
2550
|
-
const parsed = JSON.parse(stored);
|
|
2551
|
-
if (parsed.content) {
|
|
2552
|
-
setContentState(parsed.content);
|
|
2553
|
-
setSavedContent(parsed.content);
|
|
2554
|
-
setLastSaved(parsed.savedAt);
|
|
2555
|
-
} else {
|
|
2556
|
-
const generated = generateSpecFromTemplate(template);
|
|
2557
|
-
setContentState(generated);
|
|
2558
|
-
setSavedContent(generated);
|
|
2559
|
-
}
|
|
2560
|
-
} else {
|
|
2561
|
-
const generated = generateSpecFromTemplate(template);
|
|
2562
|
-
setContentState(generated);
|
|
2563
|
-
setSavedContent(generated);
|
|
2564
|
-
}
|
|
2565
|
-
} catch {
|
|
2566
|
-
const generated = generateSpecFromTemplate(template);
|
|
2567
|
-
setContentState(generated);
|
|
2568
|
-
setSavedContent(generated);
|
|
2569
|
-
}
|
|
2570
|
-
setLoading(false);
|
|
2571
|
-
}, [templateId]);
|
|
2572
|
-
const setContent = useCallback8((newContent) => {
|
|
2573
|
-
setContentState(newContent);
|
|
2574
|
-
setValidation(null);
|
|
2575
|
-
}, []);
|
|
2576
|
-
const save = useCallback8(() => {
|
|
2577
|
-
try {
|
|
2578
|
-
const savedAt = new Date().toISOString();
|
|
2579
|
-
localStorage.setItem(`${SPEC_STORAGE_KEY}-${templateId}`, JSON.stringify({
|
|
2580
|
-
content,
|
|
2581
|
-
savedAt
|
|
2582
|
-
}));
|
|
2583
|
-
setSavedContent(content);
|
|
2584
|
-
setLastSaved(savedAt);
|
|
2585
|
-
} catch {}
|
|
2586
|
-
}, [content, templateId]);
|
|
2587
|
-
const validate = useCallback8(() => {
|
|
2588
|
-
const errors = [];
|
|
2589
|
-
const lines = content.split(`
|
|
2590
|
-
`);
|
|
2591
|
-
if (!content.includes("contractSpec(")) {
|
|
2592
|
-
errors.push({
|
|
2593
|
-
line: 1,
|
|
2594
|
-
message: "Spec must contain a contractSpec() definition",
|
|
2595
|
-
severity: "error"
|
|
2596
|
-
});
|
|
2597
|
-
}
|
|
2598
|
-
if (!content.includes("goal:")) {
|
|
2599
|
-
errors.push({
|
|
2600
|
-
line: 1,
|
|
2601
|
-
message: "Spec should have a goal field",
|
|
2602
|
-
severity: "warning"
|
|
2603
|
-
});
|
|
2604
|
-
}
|
|
2605
|
-
if (!content.includes("io:")) {
|
|
2606
|
-
errors.push({
|
|
2607
|
-
line: 1,
|
|
2608
|
-
message: "Spec should define io (input/output)",
|
|
2609
|
-
severity: "warning"
|
|
2610
|
-
});
|
|
2611
|
-
}
|
|
2612
|
-
const openBraces = (content.match(/{/g) ?? []).length;
|
|
2613
|
-
const closeBraces = (content.match(/}/g) ?? []).length;
|
|
2614
|
-
if (openBraces !== closeBraces) {
|
|
2615
|
-
errors.push({
|
|
2616
|
-
line: lines.length,
|
|
2617
|
-
message: `Unbalanced braces: ${openBraces} opening, ${closeBraces} closing`,
|
|
2618
|
-
severity: "error"
|
|
2619
|
-
});
|
|
2620
|
-
}
|
|
2621
|
-
const openParens = (content.match(/\(/g) ?? []).length;
|
|
2622
|
-
const closeParens = (content.match(/\)/g) ?? []).length;
|
|
2623
|
-
if (openParens !== closeParens) {
|
|
2624
|
-
errors.push({
|
|
2625
|
-
line: lines.length,
|
|
2626
|
-
message: `Unbalanced parentheses: ${openParens} opening, ${closeParens} closing`,
|
|
2627
|
-
severity: "error"
|
|
2628
|
-
});
|
|
2629
|
-
}
|
|
2630
|
-
lines.forEach((line, index) => {
|
|
2631
|
-
const singleQuotes = (line.match(/'/g) ?? []).length;
|
|
2632
|
-
const doubleQuotes = (line.match(/"/g) ?? []).length;
|
|
2633
|
-
if (singleQuotes % 2 !== 0) {
|
|
2634
|
-
errors.push({
|
|
2635
|
-
line: index + 1,
|
|
2636
|
-
message: "Unclosed single quote",
|
|
2637
|
-
severity: "error"
|
|
2638
|
-
});
|
|
2639
|
-
}
|
|
2640
|
-
if (doubleQuotes % 2 !== 0) {
|
|
2641
|
-
errors.push({
|
|
2642
|
-
line: index + 1,
|
|
2643
|
-
message: "Unclosed double quote",
|
|
2644
|
-
severity: "error"
|
|
2645
|
-
});
|
|
2646
|
-
}
|
|
2647
|
-
});
|
|
2648
|
-
const result = {
|
|
2649
|
-
valid: errors.filter((e) => e.severity === "error").length === 0,
|
|
2650
|
-
errors
|
|
2651
|
-
};
|
|
2652
|
-
setValidation(result);
|
|
2653
|
-
return result;
|
|
2654
|
-
}, [content]);
|
|
2655
|
-
const reset = useCallback8(() => {
|
|
2656
|
-
const generated = generateSpecFromTemplate(template);
|
|
2657
|
-
setContentState(generated);
|
|
2658
|
-
setSavedContent(generated);
|
|
2659
|
-
setValidation(null);
|
|
2660
|
-
setLastSaved(null);
|
|
2661
|
-
try {
|
|
2662
|
-
localStorage.removeItem(`${SPEC_STORAGE_KEY}-${templateId}`);
|
|
2663
|
-
} catch {}
|
|
2664
|
-
}, [templateId]);
|
|
2665
|
-
return {
|
|
2666
|
-
content,
|
|
2667
|
-
loading,
|
|
2668
|
-
isDirty: content !== savedContent,
|
|
2669
|
-
validation,
|
|
2670
|
-
setContent,
|
|
2671
|
-
save,
|
|
2672
|
-
validate,
|
|
2673
|
-
reset,
|
|
2674
|
-
lastSaved
|
|
2675
|
-
};
|
|
2676
|
-
}
|
|
2677
|
-
|
|
2678
|
-
// src/SpecEditorPanel.tsx
|
|
2679
|
-
import { Button as Button5, LoaderBlock as LoaderBlock3 } from "@contractspec/lib.design-system";
|
|
2680
|
-
import { Badge as Badge5 } from "@contractspec/lib.ui-kit-web/ui/badge";
|
|
2681
|
-
import { useCallback as useCallback9, useEffect as useEffect5 } from "react";
|
|
2682
|
-
import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
|
|
2683
|
-
"use client";
|
|
2684
|
-
function SpecEditorPanel({
|
|
2685
|
-
templateId,
|
|
2686
|
-
SpecEditor,
|
|
2687
|
-
onLog
|
|
2688
|
-
}) {
|
|
2689
|
-
const {
|
|
2690
|
-
content,
|
|
2691
|
-
loading,
|
|
2692
|
-
isDirty,
|
|
2693
|
-
validation,
|
|
2694
|
-
setContent,
|
|
2695
|
-
save,
|
|
2696
|
-
validate,
|
|
2697
|
-
reset,
|
|
2698
|
-
lastSaved
|
|
2699
|
-
} = useSpecContent(templateId);
|
|
2700
|
-
useEffect5(() => {
|
|
2701
|
-
if (!loading && content) {
|
|
2702
|
-
onLog?.(`Spec loaded for ${templateId}`);
|
|
2703
|
-
}
|
|
2704
|
-
}, [loading, content, templateId, onLog]);
|
|
2705
|
-
const handleSave = useCallback9(() => {
|
|
2706
|
-
save();
|
|
2707
|
-
onLog?.("Spec saved locally");
|
|
2708
|
-
}, [save, onLog]);
|
|
2709
|
-
const handleValidate = useCallback9(() => {
|
|
2710
|
-
const result = validate();
|
|
2711
|
-
if (result.valid) {
|
|
2712
|
-
onLog?.("Spec validation passed");
|
|
2713
|
-
} else {
|
|
2714
|
-
const errorCount = result.errors.filter((e) => e.severity === "error").length;
|
|
2715
|
-
const warnCount = result.errors.filter((e) => e.severity === "warning").length;
|
|
2716
|
-
onLog?.(`Spec validation: ${errorCount} errors, ${warnCount} warnings`);
|
|
2717
|
-
}
|
|
2718
|
-
}, [validate, onLog]);
|
|
2719
|
-
const handleReset = useCallback9(() => {
|
|
2720
|
-
reset();
|
|
2721
|
-
onLog?.("Spec reset to template defaults");
|
|
2722
|
-
}, [reset, onLog]);
|
|
2723
|
-
if (loading) {
|
|
2724
|
-
return /* @__PURE__ */ jsxDEV9(LoaderBlock3, {
|
|
2725
|
-
label: "Loading spec..."
|
|
2726
|
-
}, undefined, false, undefined, this);
|
|
2727
|
-
}
|
|
2728
|
-
return /* @__PURE__ */ jsxDEV9("div", {
|
|
2729
|
-
className: "space-y-4",
|
|
2730
|
-
children: [
|
|
2731
|
-
/* @__PURE__ */ jsxDEV9("div", {
|
|
2732
|
-
className: "flex items-center justify-between",
|
|
2733
|
-
children: [
|
|
2734
|
-
/* @__PURE__ */ jsxDEV9("div", {
|
|
2735
|
-
className: "flex items-center gap-2",
|
|
2736
|
-
children: [
|
|
2737
|
-
/* @__PURE__ */ jsxDEV9(Button5, {
|
|
2738
|
-
variant: "default",
|
|
2739
|
-
size: "sm",
|
|
2740
|
-
onClick: handleSave,
|
|
2741
|
-
children: "Save"
|
|
2742
|
-
}, undefined, false, undefined, this),
|
|
2743
|
-
/* @__PURE__ */ jsxDEV9(Button5, {
|
|
2744
|
-
variant: "outline",
|
|
2745
|
-
size: "sm",
|
|
2746
|
-
onClick: handleValidate,
|
|
2747
|
-
children: "Validate"
|
|
2748
|
-
}, undefined, false, undefined, this),
|
|
2749
|
-
isDirty && /* @__PURE__ */ jsxDEV9(Badge5, {
|
|
2750
|
-
variant: "secondary",
|
|
2751
|
-
className: "border-amber-500/30 bg-amber-500/20 text-amber-400",
|
|
2752
|
-
children: "Unsaved changes"
|
|
2753
|
-
}, undefined, false, undefined, this),
|
|
2754
|
-
validation && /* @__PURE__ */ jsxDEV9(Badge5, {
|
|
2755
|
-
variant: validation.valid ? "default" : "destructive",
|
|
2756
|
-
className: validation.valid ? "border-green-500/30 bg-green-500/20 text-green-400" : "",
|
|
2757
|
-
children: validation.valid ? "Valid" : `${validation.errors.filter((e) => e.severity === "error").length} errors`
|
|
2758
|
-
}, undefined, false, undefined, this)
|
|
2759
|
-
]
|
|
2760
|
-
}, undefined, true, undefined, this),
|
|
2761
|
-
/* @__PURE__ */ jsxDEV9("div", {
|
|
2762
|
-
className: "flex items-center gap-2",
|
|
2763
|
-
children: [
|
|
2764
|
-
lastSaved && /* @__PURE__ */ jsxDEV9("span", {
|
|
2765
|
-
className: "text-muted-foreground text-xs",
|
|
2766
|
-
children: [
|
|
2767
|
-
"Last saved: ",
|
|
2768
|
-
new Date(lastSaved).toLocaleTimeString()
|
|
2769
|
-
]
|
|
2770
|
-
}, undefined, true, undefined, this),
|
|
2771
|
-
/* @__PURE__ */ jsxDEV9(Button5, {
|
|
2772
|
-
variant: "ghost",
|
|
2773
|
-
size: "sm",
|
|
2774
|
-
onPress: handleReset,
|
|
2775
|
-
children: "Reset"
|
|
2776
|
-
}, undefined, false, undefined, this)
|
|
2777
|
-
]
|
|
2778
|
-
}, undefined, true, undefined, this)
|
|
2779
|
-
]
|
|
2780
|
-
}, undefined, true, undefined, this),
|
|
2781
|
-
validation && validation.errors.length > 0 && /* @__PURE__ */ jsxDEV9("div", {
|
|
2782
|
-
className: "rounded-lg border border-amber-500/50 bg-amber-500/10 p-3",
|
|
2783
|
-
children: [
|
|
2784
|
-
/* @__PURE__ */ jsxDEV9("p", {
|
|
2785
|
-
className: "mb-2 font-semibold text-amber-400 text-xs uppercase",
|
|
2786
|
-
children: "Validation Issues"
|
|
2787
|
-
}, undefined, false, undefined, this),
|
|
2788
|
-
/* @__PURE__ */ jsxDEV9("ul", {
|
|
2789
|
-
className: "space-y-1",
|
|
2790
|
-
children: validation.errors.map((error, index) => /* @__PURE__ */ jsxDEV9("li", {
|
|
2791
|
-
className: `text-xs ${error.severity === "error" ? "text-red-400" : "text-amber-400"}`,
|
|
2792
|
-
children: [
|
|
2793
|
-
"Line ",
|
|
2794
|
-
error.line,
|
|
2795
|
-
": ",
|
|
2796
|
-
error.message
|
|
2797
|
-
]
|
|
2798
|
-
}, `${error.line}-${error.message}-${index}`, true, undefined, this))
|
|
2799
|
-
}, undefined, false, undefined, this)
|
|
2800
|
-
]
|
|
2801
|
-
}, undefined, true, undefined, this),
|
|
2802
|
-
/* @__PURE__ */ jsxDEV9("div", {
|
|
2803
|
-
className: "rounded-2xl border border-border bg-card p-4",
|
|
2804
|
-
children: /* @__PURE__ */ jsxDEV9(SpecEditor, {
|
|
2805
|
-
projectId: "sandbox",
|
|
2806
|
-
type: "CAPABILITY",
|
|
2807
|
-
content,
|
|
2808
|
-
onChange: setContent,
|
|
2809
|
-
metadata: { template: templateId },
|
|
2810
|
-
onSave: handleSave,
|
|
2811
|
-
onValidate: handleValidate
|
|
2812
|
-
}, undefined, false, undefined, this)
|
|
2813
|
-
}, undefined, false, undefined, this)
|
|
2814
|
-
]
|
|
2815
|
-
}, undefined, true, undefined, this);
|
|
2816
|
-
}
|
|
2817
|
-
|
|
2818
|
-
// src/TemplateShell.tsx
|
|
2819
|
-
import { jsxDEV as jsxDEV10 } from "react/jsx-dev-runtime";
|
|
2820
|
-
var TemplateShell = ({
|
|
2821
|
-
title,
|
|
2822
|
-
description,
|
|
2823
|
-
sidebar,
|
|
2824
|
-
actions,
|
|
2825
|
-
showSaveAction = true,
|
|
2826
|
-
saveProps,
|
|
2827
|
-
children
|
|
2828
|
-
}) => /* @__PURE__ */ jsxDEV10("div", {
|
|
2829
|
-
className: "space-y-6",
|
|
2830
|
-
children: [
|
|
2831
|
-
/* @__PURE__ */ jsxDEV10("header", {
|
|
2832
|
-
className: "rounded-2xl border border-border bg-card p-6 shadow-sm",
|
|
2833
|
-
children: [
|
|
2834
|
-
/* @__PURE__ */ jsxDEV10("div", {
|
|
2835
|
-
className: "flex flex-wrap items-center justify-between gap-4",
|
|
2836
|
-
children: [
|
|
2837
|
-
/* @__PURE__ */ jsxDEV10("div", {
|
|
2838
|
-
children: [
|
|
2839
|
-
/* @__PURE__ */ jsxDEV10("p", {
|
|
2840
|
-
className: "font-semibold text-muted-foreground text-sm uppercase tracking-wide",
|
|
2841
|
-
children: "ContractSpec Templates"
|
|
2842
|
-
}, undefined, false, undefined, this),
|
|
2843
|
-
/* @__PURE__ */ jsxDEV10("h1", {
|
|
2844
|
-
className: "font-bold text-3xl",
|
|
2845
|
-
children: title
|
|
2846
|
-
}, undefined, false, undefined, this),
|
|
2847
|
-
description ? /* @__PURE__ */ jsxDEV10("p", {
|
|
2848
|
-
className: "mt-2 max-w-2xl text-muted-foreground text-sm",
|
|
2849
|
-
children: description
|
|
2850
|
-
}, undefined, false, undefined, this) : null
|
|
2851
|
-
]
|
|
2852
|
-
}, undefined, true, undefined, this),
|
|
2853
|
-
/* @__PURE__ */ jsxDEV10("div", {
|
|
2854
|
-
className: "flex flex-col items-end gap-2",
|
|
2855
|
-
children: [
|
|
2856
|
-
/* @__PURE__ */ jsxDEV10(LocalDataIndicator, {}, undefined, false, undefined, this),
|
|
2857
|
-
showSaveAction ? /* @__PURE__ */ jsxDEV10(SaveToStudioButton, {
|
|
2858
|
-
...saveProps
|
|
2859
|
-
}, undefined, false, undefined, this) : null
|
|
2860
|
-
]
|
|
2861
|
-
}, undefined, true, undefined, this)
|
|
2862
|
-
]
|
|
2863
|
-
}, undefined, true, undefined, this),
|
|
2864
|
-
actions ? /* @__PURE__ */ jsxDEV10("div", {
|
|
2865
|
-
className: "mt-4",
|
|
2866
|
-
children: actions
|
|
2867
|
-
}, undefined, false, undefined, this) : null
|
|
2868
|
-
]
|
|
2869
|
-
}, undefined, true, undefined, this),
|
|
2870
|
-
/* @__PURE__ */ jsxDEV10("div", {
|
|
2871
|
-
className: sidebar ? "grid gap-6 lg:grid-cols-[minmax(0,1fr)_320px]" : "w-full",
|
|
2872
|
-
children: [
|
|
2873
|
-
/* @__PURE__ */ jsxDEV10("main", {
|
|
2874
|
-
className: "space-y-4 p-2",
|
|
2875
|
-
children
|
|
2876
|
-
}, undefined, false, undefined, this),
|
|
2877
|
-
sidebar ? /* @__PURE__ */ jsxDEV10("aside", {
|
|
2878
|
-
className: "rounded-2xl border border-border bg-card p-4",
|
|
2879
|
-
children: sidebar
|
|
2880
|
-
}, undefined, false, undefined, this) : null
|
|
2881
|
-
]
|
|
2882
|
-
}, undefined, true, undefined, this)
|
|
2883
|
-
]
|
|
2884
|
-
}, undefined, true, undefined, this);
|
|
2885
|
-
|
|
2886
|
-
// src/hooks/useRegistryTemplates.ts
|
|
2887
|
-
import { useQuery } from "@tanstack/react-query";
|
|
2888
|
-
function useRegistryTemplates() {
|
|
2889
|
-
return useQuery({
|
|
2890
|
-
queryKey: ["registryTemplates"],
|
|
2891
|
-
queryFn: async () => {
|
|
2892
|
-
const registryUrl = process.env.NEXT_PUBLIC_CONTRACTSPEC_REGISTRY_URL ?? "";
|
|
2893
|
-
if (!registryUrl)
|
|
2894
|
-
return [];
|
|
2895
|
-
const res = await fetch(`${registryUrl.replace(/\/$/, "")}/r/contractspec.json`, {
|
|
2896
|
-
method: "GET",
|
|
2897
|
-
headers: { Accept: "application/json" }
|
|
2898
|
-
});
|
|
2899
|
-
if (!res.ok)
|
|
2900
|
-
return [];
|
|
2901
|
-
const json = await res.json();
|
|
2902
|
-
const items = json.items ?? [];
|
|
2903
|
-
return items.filter((i) => i.type === "contractspec:template").map((i) => ({
|
|
2904
|
-
id: i.name,
|
|
2905
|
-
name: i.title ?? i.name,
|
|
2906
|
-
description: i.description,
|
|
2907
|
-
tags: i.meta?.tags ?? [],
|
|
2908
|
-
source: "registry",
|
|
2909
|
-
registryUrl
|
|
2910
|
-
}));
|
|
2911
|
-
}
|
|
2912
|
-
});
|
|
2913
|
-
}
|
|
2914
|
-
|
|
2915
|
-
// src/hooks/useWorkflowComposer.ts
|
|
2916
|
-
import { useCallback as useCallback10, useEffect as useEffect6, useMemo as useMemo7, useState as useState8 } from "react";
|
|
2917
|
-
"use client";
|
|
2918
|
-
function useWorkflowComposer(templateId) {
|
|
2919
|
-
const [selectedWorkflow, setSelectedWorkflow] = useState8(null);
|
|
2920
|
-
const [extensions, setExtensions] = useState8([]);
|
|
2921
|
-
const [loading, _setLoading] = useState8(false);
|
|
2922
|
-
const [error, _setError] = useState8(null);
|
|
2923
|
-
const baseWorkflows = useMemo7(() => getTemplateWorkflows(templateId), [templateId]);
|
|
2924
|
-
useEffect6(() => {
|
|
2925
|
-
const firstWorkflow = baseWorkflows[0];
|
|
2926
|
-
if (baseWorkflows.length > 0 && !selectedWorkflow && firstWorkflow) {
|
|
2927
|
-
setSelectedWorkflow(firstWorkflow.meta.key);
|
|
2928
|
-
}
|
|
2929
|
-
}, [baseWorkflows, selectedWorkflow]);
|
|
2930
|
-
const currentBase = useMemo7(() => {
|
|
2931
|
-
return baseWorkflows.find((w) => w.meta.key === selectedWorkflow) ?? null;
|
|
2932
|
-
}, [baseWorkflows, selectedWorkflow]);
|
|
2933
|
-
const compose = useCallback10((scope) => {
|
|
2934
|
-
if (!currentBase)
|
|
2935
|
-
return null;
|
|
2936
|
-
const applicableExtensions = extensions.filter((ext) => ext.workflow === currentBase.meta.key).filter((ext) => matchesScope(ext, scope)).sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
2937
|
-
if (applicableExtensions.length === 0) {
|
|
2938
|
-
return currentBase;
|
|
2939
|
-
}
|
|
2940
|
-
let composedWorkflow = { ...currentBase, steps: [...currentBase.steps] };
|
|
2941
|
-
for (const extension of applicableExtensions) {
|
|
2942
|
-
composedWorkflow = applyExtension(composedWorkflow, extension);
|
|
2943
|
-
}
|
|
2944
|
-
return composedWorkflow;
|
|
2945
|
-
}, [currentBase, extensions]);
|
|
2946
|
-
const workflow = useMemo7(() => compose(), [compose]);
|
|
2947
|
-
const selectWorkflow = useCallback10((workflowName) => {
|
|
2948
|
-
setSelectedWorkflow(workflowName);
|
|
2949
|
-
}, []);
|
|
2950
|
-
const addExtension = useCallback10((extension) => {
|
|
2951
|
-
setExtensions((prev) => [...prev, extension]);
|
|
2952
|
-
}, []);
|
|
2953
|
-
const removeExtension = useCallback10((workflowName, index) => {
|
|
2954
|
-
setExtensions((prev) => {
|
|
2955
|
-
const forWorkflow = prev.filter((e) => e.workflow === workflowName);
|
|
2956
|
-
const others = prev.filter((e) => e.workflow !== workflowName);
|
|
2957
|
-
forWorkflow.splice(index, 1);
|
|
2958
|
-
return [...others, ...forWorkflow];
|
|
2959
|
-
});
|
|
2960
|
-
}, []);
|
|
2961
|
-
const generateSpecCode = useCallback10(() => {
|
|
2962
|
-
const composed = workflow;
|
|
2963
|
-
if (!composed) {
|
|
2964
|
-
return "// No workflow selected";
|
|
2965
|
-
}
|
|
2966
|
-
const stepsCode = composed.steps.map((step) => ` {
|
|
2967
|
-
id: '${step.id}',
|
|
2968
|
-
name: '${step.name}',
|
|
2969
|
-
type: '${step.type}',${step.description ? `
|
|
2970
|
-
description: '${step.description}',` : ""}${step.next ? `
|
|
2971
|
-
next: ${JSON.stringify(step.next)},` : ""}${step.condition ? `
|
|
2972
|
-
condition: '${step.condition}',` : ""}${step.timeout ? `
|
|
2973
|
-
timeout: ${step.timeout},` : ""}${step.retries ? `
|
|
2974
|
-
retries: ${step.retries},` : ""}${step.onError ? `
|
|
2975
|
-
onError: '${step.onError}',` : ""}
|
|
378
|
+
});`}import{useCallback as G0,useEffect as m1,useState as $0}from"react";var R0="contractspec-spec-content";function o0($){let{template:N}=r(),[q,J]=$0(""),[X,K]=$0(""),[z,U]=$0(!0),[w,V]=$0(null),[f,F]=$0(null);m1(()=>{U(!0);try{let H=localStorage.getItem(`${R0}-${$}`);if(H){let Z=JSON.parse(H);if(Z.content)J(Z.content),K(Z.content),F(Z.savedAt);else{let A=e(N);J(A),K(A)}}else{let Z=e(N);J(Z),K(Z)}}catch{let H=e(N);J(H),K(H)}U(!1)},[$]);let Y=G0((H)=>{J(H),V(null)},[]),b=G0(()=>{try{let H=new Date().toISOString();localStorage.setItem(`${R0}-${$}`,JSON.stringify({content:q,savedAt:H})),K(q),F(H)}catch{}},[q,$]),W=G0(()=>{let H=[],Z=q.split(`
|
|
379
|
+
`);if(!q.includes("contractSpec("))H.push({line:1,message:"Spec must contain a contractSpec() definition",severity:"error"});if(!q.includes("goal:"))H.push({line:1,message:"Spec should have a goal field",severity:"warning"});if(!q.includes("io:"))H.push({line:1,message:"Spec should define io (input/output)",severity:"warning"});let A=(q.match(/{/g)??[]).length,L=(q.match(/}/g)??[]).length;if(A!==L)H.push({line:Z.length,message:`Unbalanced braces: ${A} opening, ${L} closing`,severity:"error"});let P=(q.match(/\(/g)??[]).length,G=(q.match(/\)/g)??[]).length;if(P!==G)H.push({line:Z.length,message:`Unbalanced parentheses: ${P} opening, ${G} closing`,severity:"error"});Z.forEach((E,u)=>{let B=(E.match(/'/g)??[]).length,y=(E.match(/"/g)??[]).length;if(B%2!==0)H.push({line:u+1,message:"Unclosed single quote",severity:"error"});if(y%2!==0)H.push({line:u+1,message:"Unclosed double quote",severity:"error"})});let M={valid:H.filter((E)=>E.severity==="error").length===0,errors:H};return V(M),M},[q]),Q=G0(()=>{let H=e(N);J(H),K(H),V(null),F(null);try{localStorage.removeItem(`${R0}-${$}`)}catch{}},[$]);return{content:q,loading:z,isDirty:q!==X,validation:w,setContent:Y,save:b,validate:W,reset:Q,lastSaved:f}}import{Button as h0,LoaderBlock as l1}from"@contractspec/lib.design-system";import{Badge as a0}from"@contractspec/lib.ui-kit-web/ui/badge";import{useCallback as M0,useEffect as n1}from"react";import{jsx as v,jsxs as p}from"react/jsx-runtime";function h4({templateId:$,SpecEditor:N,onLog:q}){let{content:J,loading:X,isDirty:K,validation:z,setContent:U,save:w,validate:V,reset:f,lastSaved:F}=o0($);n1(()=>{if(!X&&J)q?.(`Spec loaded for ${$}`)},[X,J,$,q]);let Y=M0(()=>{w(),q?.("Spec saved locally")},[w,q]),b=M0(()=>{let Q=V();if(Q.valid)q?.("Spec validation passed");else{let H=Q.errors.filter((A)=>A.severity==="error").length,Z=Q.errors.filter((A)=>A.severity==="warning").length;q?.(`Spec validation: ${H} errors, ${Z} warnings`)}},[V,q]),W=M0(()=>{f(),q?.("Spec reset to template defaults")},[f,q]);if(X)return v(l1,{label:"Loading spec..."});return p("div",{className:"space-y-4",children:[p("div",{className:"flex items-center justify-between",children:[p("div",{className:"flex items-center gap-2",children:[v(h0,{variant:"default",size:"sm",onClick:Y,children:"Save"}),v(h0,{variant:"outline",size:"sm",onClick:b,children:"Validate"}),K&&v(a0,{variant:"secondary",className:"border-amber-500/30 bg-amber-500/20 text-amber-400",children:"Unsaved changes"}),z&&v(a0,{variant:z.valid?"default":"destructive",className:z.valid?"border-green-500/30 bg-green-500/20 text-green-400":"",children:z.valid?"Valid":`${z.errors.filter((Q)=>Q.severity==="error").length} errors`})]}),p("div",{className:"flex items-center gap-2",children:[F&&p("span",{className:"text-muted-foreground text-xs",children:["Last saved: ",new Date(F).toLocaleTimeString()]}),v(h0,{variant:"ghost",size:"sm",onPress:W,children:"Reset"})]})]}),z&&z.errors.length>0&&p("div",{className:"rounded-lg border border-amber-500/50 bg-amber-500/10 p-3",children:[v("p",{className:"mb-2 font-semibold text-amber-400 text-xs uppercase",children:"Validation Issues"}),v("ul",{className:"space-y-1",children:z.errors.map((Q,H)=>p("li",{className:`text-xs ${Q.severity==="error"?"text-red-400":"text-amber-400"}`,children:["Line ",Q.line,": ",Q.message]},`${Q.line}-${Q.message}-${H}`))})]}),v("div",{className:"rounded-2xl border border-border bg-card p-4",children:v(N,{projectId:"sandbox",type:"CAPABILITY",content:J,onChange:U,metadata:{template:$},onSave:Y,onValidate:b})})]})}import{jsx as i,jsxs as o}from"react/jsx-runtime";var L4=({title:$,description:N,sidebar:q,actions:J,showSaveAction:X=!0,saveProps:K,children:z})=>o("div",{className:"space-y-6",children:[o("header",{className:"rounded-2xl border border-border bg-card p-6 shadow-sm",children:[o("div",{className:"flex flex-wrap items-center justify-between gap-4",children:[o("div",{children:[i("p",{className:"font-semibold text-muted-foreground text-sm uppercase tracking-wide",children:"ContractSpec Templates"}),i("h1",{className:"font-bold text-3xl",children:$}),N?i("p",{className:"mt-2 max-w-2xl text-muted-foreground text-sm",children:N}):null]}),o("div",{className:"flex flex-col items-end gap-2",children:[i(P0,{}),X?i(z0,{...K}):null]})]}),J?i("div",{className:"mt-4",children:J}):null]}),o("div",{className:q?"grid gap-6 lg:grid-cols-[minmax(0,1fr)_320px]":"w-full",children:[i("main",{className:"space-y-4 p-2",children:z}),q?i("aside",{className:"rounded-2xl border border-border bg-card p-4",children:q}):null]})]});import{useQuery as o1}from"@tanstack/react-query";function I4(){return o1({queryKey:["registryTemplates"],queryFn:async()=>{let $=process.env.NEXT_PUBLIC_CONTRACTSPEC_REGISTRY_URL??"";if(!$)return[];let N=await fetch(`${$.replace(/\/$/,"")}/r/contractspec.json`,{method:"GET",headers:{Accept:"application/json"}});if(!N.ok)return[];return((await N.json()).items??[]).filter((X)=>X.type==="contractspec:template").map((X)=>({id:X.name,name:X.title??X.name,description:X.description,tags:X.meta?.tags??[],source:"registry",registryUrl:$}))}})}import{useCallback as N0,useEffect as a1,useMemo as T0,useState as K0}from"react";function y4($){let[N,q]=K0(null),[J,X]=K0([]),[K,z]=K0(!1),[U,w]=K0(null),V=T0(()=>$2($),[$]);a1(()=>{let Z=V[0];if(V.length>0&&!N&&Z)q(Z.meta.key)},[V,N]);let f=T0(()=>{return V.find((Z)=>Z.meta.key===N)??null},[V,N]),F=N0((Z)=>{if(!f)return null;let A=J.filter((P)=>P.workflow===f.meta.key).filter((P)=>t1(P,Z)).sort((P,G)=>(P.priority??0)-(G.priority??0));if(A.length===0)return f;let L={...f,steps:[...f.steps]};for(let P of A)L=s1(L,P);return L},[f,J]),Y=T0(()=>F(),[F]),b=N0((Z)=>{q(Z)},[]),W=N0((Z)=>{X((A)=>[...A,Z])},[]),Q=N0((Z,A)=>{X((L)=>{let P=L.filter((M)=>M.workflow===Z),G=L.filter((M)=>M.workflow!==Z);return P.splice(A,1),[...G,...P]})},[]),H=N0(()=>{let Z=Y;if(!Z)return"// No workflow selected";let A=Z.steps.map((P)=>` {
|
|
380
|
+
id: '${P.id}',
|
|
381
|
+
name: '${P.name}',
|
|
382
|
+
type: '${P.type}',${P.description?`
|
|
383
|
+
description: '${P.description}',`:""}${P.next?`
|
|
384
|
+
next: ${JSON.stringify(P.next)},`:""}${P.condition?`
|
|
385
|
+
condition: '${P.condition}',`:""}${P.timeout?`
|
|
386
|
+
timeout: ${P.timeout},`:""}${P.retries?`
|
|
387
|
+
retries: ${P.retries},`:""}${P.onError?`
|
|
388
|
+
onError: '${P.onError}',`:""}
|
|
2976
389
|
}`).join(`,
|
|
2977
|
-
`)
|
|
2978
|
-
const extensionsCode = extensions.length > 0 ? `
|
|
390
|
+
`),L=J.length>0?`
|
|
2979
391
|
|
|
2980
392
|
// Extensions applied:
|
|
2981
|
-
${
|
|
2982
|
-
`)}
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
${extensionsCode}
|
|
393
|
+
${J.map((P)=>`// - ${P.workflow} (priority: ${P.priority??0})${P.customSteps?.length?` +${P.customSteps.length} steps`:""}${P.hiddenSteps?.length?` -${P.hiddenSteps.length} hidden`:""}`).join(`
|
|
394
|
+
`)}`:"";return`// Workflow Spec: ${Z.meta.key} v${Z.meta.version}
|
|
395
|
+
// Generated from ${$} template
|
|
396
|
+
${L}
|
|
2986
397
|
|
|
2987
398
|
import { workflowSpec } from '@contractspec/lib.contracts-spec/workflow';
|
|
2988
399
|
|
|
2989
|
-
export const ${
|
|
400
|
+
export const ${e1(Z.meta.key)}Workflow = workflowSpec({
|
|
2990
401
|
meta: {
|
|
2991
|
-
key: '${
|
|
2992
|
-
version: ${
|
|
2993
|
-
description: '${
|
|
402
|
+
key: '${Z.meta.key}',
|
|
403
|
+
version: ${Z.meta.version},${Z.meta.description?`
|
|
404
|
+
description: '${Z.meta.description}',`:""}
|
|
2994
405
|
},
|
|
2995
|
-
start: '${
|
|
406
|
+
start: '${Z.start}',
|
|
2996
407
|
steps: [
|
|
2997
|
-
${
|
|
2998
|
-
],${
|
|
2999
|
-
context: ${JSON.stringify(
|
|
408
|
+
${A}
|
|
409
|
+
],${Z.context?`
|
|
410
|
+
context: ${JSON.stringify(Z.context,null,2)},`:""}
|
|
3000
411
|
});
|
|
3001
|
-
|
|
3002
|
-
}, [workflow, extensions, templateId]);
|
|
3003
|
-
return {
|
|
3004
|
-
workflow,
|
|
3005
|
-
baseWorkflows,
|
|
3006
|
-
extensions,
|
|
3007
|
-
selectWorkflow,
|
|
3008
|
-
addExtension,
|
|
3009
|
-
removeExtension,
|
|
3010
|
-
compose,
|
|
3011
|
-
generateSpecCode,
|
|
3012
|
-
loading,
|
|
3013
|
-
error
|
|
3014
|
-
};
|
|
3015
|
-
}
|
|
3016
|
-
function matchesScope(extension, scope) {
|
|
3017
|
-
if (!scope)
|
|
3018
|
-
return true;
|
|
3019
|
-
if (extension.tenantId && extension.tenantId !== scope.tenantId) {
|
|
3020
|
-
return false;
|
|
3021
|
-
}
|
|
3022
|
-
if (extension.role && extension.role !== scope.role) {
|
|
3023
|
-
return false;
|
|
3024
|
-
}
|
|
3025
|
-
if (extension.device && extension.device !== scope.device) {
|
|
3026
|
-
return false;
|
|
3027
|
-
}
|
|
3028
|
-
return true;
|
|
3029
|
-
}
|
|
3030
|
-
function applyExtension(workflow, extension) {
|
|
3031
|
-
let steps = [...workflow.steps];
|
|
3032
|
-
if (extension.hiddenSteps) {
|
|
3033
|
-
steps = steps.filter((s) => !extension.hiddenSteps?.includes(s.id));
|
|
3034
|
-
}
|
|
3035
|
-
if (extension.customSteps) {
|
|
3036
|
-
for (const injection of extension.customSteps) {
|
|
3037
|
-
const stepToInject = {
|
|
3038
|
-
...injection.inject,
|
|
3039
|
-
id: injection.id ?? injection.inject.id
|
|
3040
|
-
};
|
|
3041
|
-
if (injection.after) {
|
|
3042
|
-
const afterIndex = steps.findIndex((s) => s.id === injection.after);
|
|
3043
|
-
if (afterIndex !== -1) {
|
|
3044
|
-
steps.splice(afterIndex + 1, 0, stepToInject);
|
|
3045
|
-
}
|
|
3046
|
-
} else if (injection.before) {
|
|
3047
|
-
const beforeIndex = steps.findIndex((s) => s.id === injection.before);
|
|
3048
|
-
if (beforeIndex !== -1) {
|
|
3049
|
-
steps.splice(beforeIndex, 0, stepToInject);
|
|
3050
|
-
}
|
|
3051
|
-
} else {
|
|
3052
|
-
steps.push(stepToInject);
|
|
3053
|
-
}
|
|
3054
|
-
}
|
|
3055
|
-
}
|
|
3056
|
-
return { ...workflow, steps };
|
|
3057
|
-
}
|
|
3058
|
-
function toCamelCase(str) {
|
|
3059
|
-
return str.replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toLowerCase());
|
|
3060
|
-
}
|
|
3061
|
-
function getTemplateWorkflows(templateId) {
|
|
3062
|
-
const templateWorkflows = {
|
|
3063
|
-
"crm-pipeline": [
|
|
3064
|
-
{
|
|
3065
|
-
meta: {
|
|
3066
|
-
key: "deal.qualification",
|
|
3067
|
-
version: "1.0.0",
|
|
3068
|
-
description: "Deal qualification workflow"
|
|
3069
|
-
},
|
|
3070
|
-
start: "lead-received",
|
|
3071
|
-
steps: [
|
|
3072
|
-
{
|
|
3073
|
-
id: "lead-received",
|
|
3074
|
-
name: "Lead Received",
|
|
3075
|
-
type: "action",
|
|
3076
|
-
description: "New lead enters the pipeline",
|
|
3077
|
-
next: "qualify-lead"
|
|
3078
|
-
},
|
|
3079
|
-
{
|
|
3080
|
-
id: "qualify-lead",
|
|
3081
|
-
name: "Qualify Lead",
|
|
3082
|
-
type: "decision",
|
|
3083
|
-
description: "Determine if lead meets qualification criteria",
|
|
3084
|
-
next: ["qualified", "disqualified"],
|
|
3085
|
-
condition: "lead.score >= threshold"
|
|
3086
|
-
},
|
|
3087
|
-
{
|
|
3088
|
-
id: "qualified",
|
|
3089
|
-
name: "Lead Qualified",
|
|
3090
|
-
type: "action",
|
|
3091
|
-
next: "assign-rep"
|
|
3092
|
-
},
|
|
3093
|
-
{
|
|
3094
|
-
id: "disqualified",
|
|
3095
|
-
name: "Lead Disqualified",
|
|
3096
|
-
type: "end"
|
|
3097
|
-
},
|
|
3098
|
-
{
|
|
3099
|
-
id: "assign-rep",
|
|
3100
|
-
name: "Assign Sales Rep",
|
|
3101
|
-
type: "action",
|
|
3102
|
-
next: "complete"
|
|
3103
|
-
},
|
|
3104
|
-
{
|
|
3105
|
-
id: "complete",
|
|
3106
|
-
name: "Workflow Complete",
|
|
3107
|
-
type: "end"
|
|
3108
|
-
}
|
|
3109
|
-
]
|
|
3110
|
-
},
|
|
3111
|
-
{
|
|
3112
|
-
meta: {
|
|
3113
|
-
key: "deal.closing",
|
|
3114
|
-
version: "1.0.0",
|
|
3115
|
-
description: "Deal closing workflow"
|
|
3116
|
-
},
|
|
3117
|
-
start: "proposal-sent",
|
|
3118
|
-
steps: [
|
|
3119
|
-
{
|
|
3120
|
-
id: "proposal-sent",
|
|
3121
|
-
name: "Proposal Sent",
|
|
3122
|
-
type: "action",
|
|
3123
|
-
next: "wait-response"
|
|
3124
|
-
},
|
|
3125
|
-
{
|
|
3126
|
-
id: "wait-response",
|
|
3127
|
-
name: "Wait for Response",
|
|
3128
|
-
type: "wait",
|
|
3129
|
-
timeout: 604800000,
|
|
3130
|
-
next: "negotiate",
|
|
3131
|
-
onError: "follow-up"
|
|
3132
|
-
},
|
|
3133
|
-
{
|
|
3134
|
-
id: "follow-up",
|
|
3135
|
-
name: "Follow Up",
|
|
3136
|
-
type: "action",
|
|
3137
|
-
next: "wait-response",
|
|
3138
|
-
retries: 3
|
|
3139
|
-
},
|
|
3140
|
-
{
|
|
3141
|
-
id: "negotiate",
|
|
3142
|
-
name: "Negotiation",
|
|
3143
|
-
type: "action",
|
|
3144
|
-
next: "finalize"
|
|
3145
|
-
},
|
|
3146
|
-
{
|
|
3147
|
-
id: "finalize",
|
|
3148
|
-
name: "Finalize Deal",
|
|
3149
|
-
type: "decision",
|
|
3150
|
-
next: ["won", "lost"],
|
|
3151
|
-
condition: "deal.accepted"
|
|
3152
|
-
},
|
|
3153
|
-
{
|
|
3154
|
-
id: "won",
|
|
3155
|
-
name: "Deal Won",
|
|
3156
|
-
type: "end"
|
|
3157
|
-
},
|
|
3158
|
-
{
|
|
3159
|
-
id: "lost",
|
|
3160
|
-
name: "Deal Lost",
|
|
3161
|
-
type: "end"
|
|
3162
|
-
}
|
|
3163
|
-
]
|
|
3164
|
-
}
|
|
3165
|
-
],
|
|
3166
|
-
"saas-boilerplate": [
|
|
3167
|
-
{
|
|
3168
|
-
meta: {
|
|
3169
|
-
key: "user.onboarding",
|
|
3170
|
-
version: "1.0.0",
|
|
3171
|
-
description: "User onboarding workflow"
|
|
3172
|
-
},
|
|
3173
|
-
start: "signup",
|
|
3174
|
-
steps: [
|
|
3175
|
-
{
|
|
3176
|
-
id: "signup",
|
|
3177
|
-
name: "User Signup",
|
|
3178
|
-
type: "action",
|
|
3179
|
-
next: "verify-email"
|
|
3180
|
-
},
|
|
3181
|
-
{
|
|
3182
|
-
id: "verify-email",
|
|
3183
|
-
name: "Verify Email",
|
|
3184
|
-
type: "wait",
|
|
3185
|
-
timeout: 86400000,
|
|
3186
|
-
next: "profile-setup",
|
|
3187
|
-
onError: "resend-verification"
|
|
3188
|
-
},
|
|
3189
|
-
{
|
|
3190
|
-
id: "resend-verification",
|
|
3191
|
-
name: "Resend Verification",
|
|
3192
|
-
type: "action",
|
|
3193
|
-
next: "verify-email",
|
|
3194
|
-
retries: 2
|
|
3195
|
-
},
|
|
3196
|
-
{
|
|
3197
|
-
id: "profile-setup",
|
|
3198
|
-
name: "Setup Profile",
|
|
3199
|
-
type: "action",
|
|
3200
|
-
next: "onboarding-tour"
|
|
3201
|
-
},
|
|
3202
|
-
{
|
|
3203
|
-
id: "onboarding-tour",
|
|
3204
|
-
name: "Onboarding Tour",
|
|
3205
|
-
type: "action",
|
|
3206
|
-
next: "complete"
|
|
3207
|
-
},
|
|
3208
|
-
{
|
|
3209
|
-
id: "complete",
|
|
3210
|
-
name: "Onboarding Complete",
|
|
3211
|
-
type: "end"
|
|
3212
|
-
}
|
|
3213
|
-
]
|
|
3214
|
-
}
|
|
3215
|
-
],
|
|
3216
|
-
"agent-console": [
|
|
3217
|
-
{
|
|
3218
|
-
meta: {
|
|
3219
|
-
key: "agent.execution",
|
|
3220
|
-
version: "1.0.0",
|
|
3221
|
-
description: "Agent execution workflow"
|
|
3222
|
-
},
|
|
3223
|
-
start: "receive-task",
|
|
3224
|
-
steps: [
|
|
3225
|
-
{
|
|
3226
|
-
id: "receive-task",
|
|
3227
|
-
name: "Receive Task",
|
|
3228
|
-
type: "action",
|
|
3229
|
-
next: "plan-execution"
|
|
3230
|
-
},
|
|
3231
|
-
{
|
|
3232
|
-
id: "plan-execution",
|
|
3233
|
-
name: "Plan Execution",
|
|
3234
|
-
type: "action",
|
|
3235
|
-
next: "execute-steps"
|
|
3236
|
-
},
|
|
3237
|
-
{
|
|
3238
|
-
id: "execute-steps",
|
|
3239
|
-
name: "Execute Steps",
|
|
3240
|
-
type: "parallel",
|
|
3241
|
-
next: ["tool-call", "observe", "reason"]
|
|
3242
|
-
},
|
|
3243
|
-
{
|
|
3244
|
-
id: "tool-call",
|
|
3245
|
-
name: "Tool Call",
|
|
3246
|
-
type: "action",
|
|
3247
|
-
next: "evaluate"
|
|
3248
|
-
},
|
|
3249
|
-
{
|
|
3250
|
-
id: "observe",
|
|
3251
|
-
name: "Observe",
|
|
3252
|
-
type: "action",
|
|
3253
|
-
next: "evaluate"
|
|
3254
|
-
},
|
|
3255
|
-
{
|
|
3256
|
-
id: "reason",
|
|
3257
|
-
name: "Reason",
|
|
3258
|
-
type: "action",
|
|
3259
|
-
next: "evaluate"
|
|
3260
|
-
},
|
|
3261
|
-
{
|
|
3262
|
-
id: "evaluate",
|
|
3263
|
-
name: "Evaluate Result",
|
|
3264
|
-
type: "decision",
|
|
3265
|
-
condition: "task.isComplete",
|
|
3266
|
-
next: ["complete", "execute-steps"]
|
|
3267
|
-
},
|
|
3268
|
-
{
|
|
3269
|
-
id: "complete",
|
|
3270
|
-
name: "Task Complete",
|
|
3271
|
-
type: "end"
|
|
3272
|
-
}
|
|
3273
|
-
]
|
|
3274
|
-
}
|
|
3275
|
-
],
|
|
3276
|
-
"todos-app": [
|
|
3277
|
-
{
|
|
3278
|
-
meta: {
|
|
3279
|
-
key: "task.lifecycle",
|
|
3280
|
-
version: "1.0.0",
|
|
3281
|
-
description: "Task lifecycle workflow"
|
|
3282
|
-
},
|
|
3283
|
-
start: "created",
|
|
3284
|
-
steps: [
|
|
3285
|
-
{
|
|
3286
|
-
id: "created",
|
|
3287
|
-
name: "Task Created",
|
|
3288
|
-
type: "action",
|
|
3289
|
-
next: "in-progress"
|
|
3290
|
-
},
|
|
3291
|
-
{
|
|
3292
|
-
id: "in-progress",
|
|
3293
|
-
name: "In Progress",
|
|
3294
|
-
type: "action",
|
|
3295
|
-
next: "review"
|
|
3296
|
-
},
|
|
3297
|
-
{
|
|
3298
|
-
id: "review",
|
|
3299
|
-
name: "Review",
|
|
3300
|
-
type: "decision",
|
|
3301
|
-
condition: "task.approved",
|
|
3302
|
-
next: ["done", "in-progress"]
|
|
3303
|
-
},
|
|
3304
|
-
{
|
|
3305
|
-
id: "done",
|
|
3306
|
-
name: "Done",
|
|
3307
|
-
type: "end"
|
|
3308
|
-
}
|
|
3309
|
-
]
|
|
3310
|
-
}
|
|
3311
|
-
],
|
|
3312
|
-
"messaging-app": [
|
|
3313
|
-
{
|
|
3314
|
-
meta: {
|
|
3315
|
-
key: "message.delivery",
|
|
3316
|
-
version: "1.0.0",
|
|
3317
|
-
description: "Message delivery workflow"
|
|
3318
|
-
},
|
|
3319
|
-
start: "compose",
|
|
3320
|
-
steps: [
|
|
3321
|
-
{
|
|
3322
|
-
id: "compose",
|
|
3323
|
-
name: "Compose Message",
|
|
3324
|
-
type: "action",
|
|
3325
|
-
next: "send"
|
|
3326
|
-
},
|
|
3327
|
-
{
|
|
3328
|
-
id: "send",
|
|
3329
|
-
name: "Send Message",
|
|
3330
|
-
type: "action",
|
|
3331
|
-
next: "deliver"
|
|
3332
|
-
},
|
|
3333
|
-
{
|
|
3334
|
-
id: "deliver",
|
|
3335
|
-
name: "Deliver",
|
|
3336
|
-
type: "decision",
|
|
3337
|
-
condition: "recipient.online",
|
|
3338
|
-
next: ["delivered", "queue"]
|
|
3339
|
-
},
|
|
3340
|
-
{
|
|
3341
|
-
id: "queue",
|
|
3342
|
-
name: "Queue for Delivery",
|
|
3343
|
-
type: "wait",
|
|
3344
|
-
next: "deliver"
|
|
3345
|
-
},
|
|
3346
|
-
{
|
|
3347
|
-
id: "delivered",
|
|
3348
|
-
name: "Message Delivered",
|
|
3349
|
-
type: "action",
|
|
3350
|
-
next: "read"
|
|
3351
|
-
},
|
|
3352
|
-
{
|
|
3353
|
-
id: "read",
|
|
3354
|
-
name: "Message Read",
|
|
3355
|
-
type: "end"
|
|
3356
|
-
}
|
|
3357
|
-
]
|
|
3358
|
-
}
|
|
3359
|
-
],
|
|
3360
|
-
"recipe-app-i18n": [
|
|
3361
|
-
{
|
|
3362
|
-
meta: {
|
|
3363
|
-
key: "recipe.creation",
|
|
3364
|
-
version: "1.0.0",
|
|
3365
|
-
description: "Recipe creation workflow"
|
|
3366
|
-
},
|
|
3367
|
-
start: "draft",
|
|
3368
|
-
steps: [
|
|
3369
|
-
{
|
|
3370
|
-
id: "draft",
|
|
3371
|
-
name: "Draft Recipe",
|
|
3372
|
-
type: "action",
|
|
3373
|
-
next: "add-ingredients"
|
|
3374
|
-
},
|
|
3375
|
-
{
|
|
3376
|
-
id: "add-ingredients",
|
|
3377
|
-
name: "Add Ingredients",
|
|
3378
|
-
type: "action",
|
|
3379
|
-
next: "add-steps"
|
|
3380
|
-
},
|
|
3381
|
-
{
|
|
3382
|
-
id: "add-steps",
|
|
3383
|
-
name: "Add Steps",
|
|
3384
|
-
type: "action",
|
|
3385
|
-
next: "review"
|
|
3386
|
-
},
|
|
3387
|
-
{
|
|
3388
|
-
id: "review",
|
|
3389
|
-
name: "Review Recipe",
|
|
3390
|
-
type: "decision",
|
|
3391
|
-
condition: "recipe.isComplete",
|
|
3392
|
-
next: ["publish", "draft"]
|
|
3393
|
-
},
|
|
3394
|
-
{
|
|
3395
|
-
id: "publish",
|
|
3396
|
-
name: "Publish Recipe",
|
|
3397
|
-
type: "end"
|
|
3398
|
-
}
|
|
3399
|
-
]
|
|
3400
|
-
}
|
|
3401
|
-
]
|
|
3402
|
-
};
|
|
3403
|
-
return templateWorkflows[templateId] ?? [];
|
|
3404
|
-
}
|
|
3405
|
-
// src/lib/component-registry.tsx
|
|
3406
|
-
import { useEffect as useEffect7, useState as useState9 } from "react";
|
|
3407
|
-
"use client";
|
|
3408
|
-
|
|
3409
|
-
class TemplateComponentRegistry {
|
|
3410
|
-
components = new Map;
|
|
3411
|
-
listeners = new Set;
|
|
3412
|
-
register(templateId, registration) {
|
|
3413
|
-
this.components.set(templateId, registration);
|
|
3414
|
-
this.listeners.forEach((l) => l(templateId));
|
|
3415
|
-
}
|
|
3416
|
-
get(templateId) {
|
|
3417
|
-
return this.components.get(templateId);
|
|
3418
|
-
}
|
|
3419
|
-
subscribe(listener) {
|
|
3420
|
-
this.listeners.add(listener);
|
|
3421
|
-
return () => {
|
|
3422
|
-
this.listeners.delete(listener);
|
|
3423
|
-
};
|
|
3424
|
-
}
|
|
3425
|
-
}
|
|
3426
|
-
var templateComponentRegistry = new TemplateComponentRegistry;
|
|
3427
|
-
function registerTemplateComponents(templateId, components) {
|
|
3428
|
-
templateComponentRegistry.register(templateId, components);
|
|
3429
|
-
}
|
|
3430
|
-
function useTemplateComponents(templateId) {
|
|
3431
|
-
const [components, setComponents] = useState9(() => templateComponentRegistry.get(templateId));
|
|
3432
|
-
useEffect7(() => {
|
|
3433
|
-
return templateComponentRegistry.subscribe((updatedId) => {
|
|
3434
|
-
if (updatedId === templateId) {
|
|
3435
|
-
setComponents(templateComponentRegistry.get(templateId));
|
|
3436
|
-
}
|
|
3437
|
-
});
|
|
3438
|
-
}, [templateId]);
|
|
3439
|
-
return components;
|
|
3440
|
-
}
|
|
3441
|
-
// src/utils/fetchPresentationData.ts
|
|
3442
|
-
async function fetchPresentationData(_presentationName, _templateId) {
|
|
3443
|
-
throw new Error("fetchPresentationData is deprecated. Use fetchData from TemplateRuntimeContext.");
|
|
3444
|
-
}
|
|
3445
|
-
function hasPresentationDataFetcher(_presentationName) {
|
|
3446
|
-
return false;
|
|
3447
|
-
}
|
|
3448
|
-
function getRegisteredPresentationFetchers() {
|
|
3449
|
-
return [];
|
|
3450
|
-
}
|
|
3451
|
-
// src/index.ts
|
|
3452
|
-
import { MarkdownRenderer as MarkdownRenderer2 } from "@contractspec/lib.design-system";
|
|
3453
|
-
export {
|
|
3454
|
-
useWorkflowComposer,
|
|
3455
|
-
useTemplateRuntime,
|
|
3456
|
-
useTemplateComponents,
|
|
3457
|
-
useSpecContent,
|
|
3458
|
-
useRegistryTemplates,
|
|
3459
|
-
useOverlayContext,
|
|
3460
|
-
useIsInOverlayContext,
|
|
3461
|
-
useEvolution,
|
|
3462
|
-
useBehaviorTracking,
|
|
3463
|
-
templateComponentRegistry,
|
|
3464
|
-
registerTemplateComponents,
|
|
3465
|
-
hasPresentationDataFetcher,
|
|
3466
|
-
getRegisteredPresentationFetchers,
|
|
3467
|
-
generateSpecFromTemplate,
|
|
3468
|
-
fetchPresentationData,
|
|
3469
|
-
TemplateShell,
|
|
3470
|
-
TemplateRuntimeContext,
|
|
3471
|
-
TemplateComponentRegistry,
|
|
3472
|
-
SpecEditorPanel,
|
|
3473
|
-
SpecDrivenTemplateShell,
|
|
3474
|
-
SaveToStudioButton,
|
|
3475
|
-
PersonalizationInsights,
|
|
3476
|
-
OverlayContextProvider,
|
|
3477
|
-
MarkdownView,
|
|
3478
|
-
MarkdownRenderer2 as MarkdownRenderer,
|
|
3479
|
-
LocalDataIndicator,
|
|
3480
|
-
EvolutionSidebar,
|
|
3481
|
-
EvolutionDashboard
|
|
3482
|
-
};
|
|
412
|
+
`},[Y,J,$]);return{workflow:Y,baseWorkflows:V,extensions:J,selectWorkflow:b,addExtension:W,removeExtension:Q,compose:F,generateSpecCode:H,loading:K,error:U}}function t1($,N){if(!N)return!0;if($.tenantId&&$.tenantId!==N.tenantId)return!1;if($.role&&$.role!==N.role)return!1;if($.device&&$.device!==N.device)return!1;return!0}function s1($,N){let q=[...$.steps];if(N.hiddenSteps)q=q.filter((J)=>!N.hiddenSteps?.includes(J.id));if(N.customSteps)for(let J of N.customSteps){let X={...J.inject,id:J.id??J.inject.id};if(J.after){let K=q.findIndex((z)=>z.id===J.after);if(K!==-1)q.splice(K+1,0,X)}else if(J.before){let K=q.findIndex((z)=>z.id===J.before);if(K!==-1)q.splice(K,0,X)}else q.push(X)}return{...$,steps:q}}function e1($){return $.replace(/[^a-zA-Z0-9]+(.)/g,(N,q)=>q.toUpperCase()).replace(/^./,(N)=>N.toLowerCase())}function $2($){return{"crm-pipeline":[{meta:{key:"deal.qualification",version:"1.0.0",description:"Deal qualification workflow"},start:"lead-received",steps:[{id:"lead-received",name:"Lead Received",type:"action",description:"New lead enters the pipeline",next:"qualify-lead"},{id:"qualify-lead",name:"Qualify Lead",type:"decision",description:"Determine if lead meets qualification criteria",next:["qualified","disqualified"],condition:"lead.score >= threshold"},{id:"qualified",name:"Lead Qualified",type:"action",next:"assign-rep"},{id:"disqualified",name:"Lead Disqualified",type:"end"},{id:"assign-rep",name:"Assign Sales Rep",type:"action",next:"complete"},{id:"complete",name:"Workflow Complete",type:"end"}]},{meta:{key:"deal.closing",version:"1.0.0",description:"Deal closing workflow"},start:"proposal-sent",steps:[{id:"proposal-sent",name:"Proposal Sent",type:"action",next:"wait-response"},{id:"wait-response",name:"Wait for Response",type:"wait",timeout:604800000,next:"negotiate",onError:"follow-up"},{id:"follow-up",name:"Follow Up",type:"action",next:"wait-response",retries:3},{id:"negotiate",name:"Negotiation",type:"action",next:"finalize"},{id:"finalize",name:"Finalize Deal",type:"decision",next:["won","lost"],condition:"deal.accepted"},{id:"won",name:"Deal Won",type:"end"},{id:"lost",name:"Deal Lost",type:"end"}]}],"saas-boilerplate":[{meta:{key:"user.onboarding",version:"1.0.0",description:"User onboarding workflow"},start:"signup",steps:[{id:"signup",name:"User Signup",type:"action",next:"verify-email"},{id:"verify-email",name:"Verify Email",type:"wait",timeout:86400000,next:"profile-setup",onError:"resend-verification"},{id:"resend-verification",name:"Resend Verification",type:"action",next:"verify-email",retries:2},{id:"profile-setup",name:"Setup Profile",type:"action",next:"onboarding-tour"},{id:"onboarding-tour",name:"Onboarding Tour",type:"action",next:"complete"},{id:"complete",name:"Onboarding Complete",type:"end"}]}],"agent-console":[{meta:{key:"agent.execution",version:"1.0.0",description:"Agent execution workflow"},start:"receive-task",steps:[{id:"receive-task",name:"Receive Task",type:"action",next:"plan-execution"},{id:"plan-execution",name:"Plan Execution",type:"action",next:"execute-steps"},{id:"execute-steps",name:"Execute Steps",type:"parallel",next:["tool-call","observe","reason"]},{id:"tool-call",name:"Tool Call",type:"action",next:"evaluate"},{id:"observe",name:"Observe",type:"action",next:"evaluate"},{id:"reason",name:"Reason",type:"action",next:"evaluate"},{id:"evaluate",name:"Evaluate Result",type:"decision",condition:"task.isComplete",next:["complete","execute-steps"]},{id:"complete",name:"Task Complete",type:"end"}]}],"todos-app":[{meta:{key:"task.lifecycle",version:"1.0.0",description:"Task lifecycle workflow"},start:"created",steps:[{id:"created",name:"Task Created",type:"action",next:"in-progress"},{id:"in-progress",name:"In Progress",type:"action",next:"review"},{id:"review",name:"Review",type:"decision",condition:"task.approved",next:["done","in-progress"]},{id:"done",name:"Done",type:"end"}]}],"messaging-app":[{meta:{key:"message.delivery",version:"1.0.0",description:"Message delivery workflow"},start:"compose",steps:[{id:"compose",name:"Compose Message",type:"action",next:"send"},{id:"send",name:"Send Message",type:"action",next:"deliver"},{id:"deliver",name:"Deliver",type:"decision",condition:"recipient.online",next:["delivered","queue"]},{id:"queue",name:"Queue for Delivery",type:"wait",next:"deliver"},{id:"delivered",name:"Message Delivered",type:"action",next:"read"},{id:"read",name:"Message Read",type:"end"}]}],"recipe-app-i18n":[{meta:{key:"recipe.creation",version:"1.0.0",description:"Recipe creation workflow"},start:"draft",steps:[{id:"draft",name:"Draft Recipe",type:"action",next:"add-ingredients"},{id:"add-ingredients",name:"Add Ingredients",type:"action",next:"add-steps"},{id:"add-steps",name:"Add Steps",type:"action",next:"review"},{id:"review",name:"Review Recipe",type:"decision",condition:"recipe.isComplete",next:["publish","draft"]},{id:"publish",name:"Publish Recipe",type:"end"}]}]}[$]??[]}import{useEffect as N2,useState as q2}from"react";class s0{components=new Map;listeners=new Set;register($,N){this.components.set($,N),this.listeners.forEach((q)=>q($))}get($){return this.components.get($)}subscribe($){return this.listeners.add($),()=>{this.listeners.delete($)}}}var t0=Symbol.for("@contractspec/lib.example-shared-ui/template-component-registry");function P2(){let $=globalThis;return $[t0]??=new s0,$[t0]}var V0=P2();function m4($,N){V0.register($,N)}function l4($){let[N,q]=q2(()=>V0.get($));return N2(()=>{return V0.subscribe((J)=>{if(J===$)q(V0.get($))})},[$]),N}async function o4($,N){throw Error("fetchPresentationData is deprecated. Use fetchData from TemplateRuntimeContext.")}function a4($){return!1}function t4(){return[]}import{MarkdownRenderer as J3}from"@contractspec/lib.design-system";export{y4 as useWorkflowComposer,r as useTemplateRuntime,l4 as useTemplateComponents,o0 as useSpecContent,I4 as useRegistryTemplates,u2 as useOverlayContext,d2 as useIsInOverlayContext,q0 as useEvolution,p0 as useBehaviorTracking,V0 as templateComponentRegistry,m4 as registerTemplateComponents,a4 as hasPresentationDataFetcher,t4 as getRegisteredPresentationFetchers,e as generateSpecFromTemplate,o4 as fetchPresentationData,L4 as TemplateShell,f1 as TemplateRuntimeContext,s0 as TemplateComponentRegistry,h4 as SpecEditorPanel,V4 as SpecDrivenTemplateShell,z0 as SaveToStudioButton,N4 as PersonalizationInsights,p2 as OverlayContextProvider,i2 as MarkdownView,J3 as MarkdownRenderer,P0 as LocalDataIndicator,U2 as EvolutionSidebar,K2 as EvolutionDashboard};
|