@contractspec/lib.example-shared-ui 1.11.0 → 1.12.0

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