@contractspec/lib.example-shared-ui 6.0.17 → 6.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/.turbo/turbo-build.log +81 -81
  2. package/CHANGELOG.md +17 -0
  3. package/dist/EvolutionDashboard.js +1 -803
  4. package/dist/EvolutionSidebar.js +1 -531
  5. package/dist/LocalDataIndicator.js +1 -68
  6. package/dist/MarkdownView.js +1 -213
  7. package/dist/OverlayContextProvider.js +1 -202
  8. package/dist/PersonalizationInsights.js +1 -455
  9. package/dist/SaveToStudioButton.js +1 -79
  10. package/dist/SpecDrivenTemplateShell.js +1 -203
  11. package/dist/SpecEditorPanel.js +17 -364
  12. package/dist/TemplateShell.js +1 -195
  13. package/dist/browser/EvolutionDashboard.js +1 -803
  14. package/dist/browser/EvolutionSidebar.js +1 -531
  15. package/dist/browser/LocalDataIndicator.js +1 -68
  16. package/dist/browser/MarkdownView.js +1 -213
  17. package/dist/browser/OverlayContextProvider.js +1 -202
  18. package/dist/browser/PersonalizationInsights.js +1 -455
  19. package/dist/browser/SaveToStudioButton.js +1 -79
  20. package/dist/browser/SpecDrivenTemplateShell.js +1 -203
  21. package/dist/browser/SpecEditorPanel.js +17 -364
  22. package/dist/browser/TemplateShell.js +1 -195
  23. package/dist/browser/bundles/ExampleTemplateBundle.js +1 -85
  24. package/dist/browser/bundles/index.js +1 -85
  25. package/dist/browser/hooks/index.js +40 -1151
  26. package/dist/browser/hooks/useBehaviorTracking.js +1 -157
  27. package/dist/browser/hooks/useEvolution.js +1 -260
  28. package/dist/browser/hooks/useRegistryTemplates.js +1 -31
  29. package/dist/browser/hooks/useSpecContent.js +17 -224
  30. package/dist/browser/hooks/useWorkflowComposer.js +24 -483
  31. package/dist/browser/index.js +40 -3122
  32. package/dist/browser/lib/component-registry.js +1 -48
  33. package/dist/browser/lib/runtime-context.js +1 -21
  34. package/dist/browser/markdown/formatPresentationName.js +1 -9
  35. package/dist/browser/markdown/useMarkdownPresentation.js +1 -65
  36. package/dist/browser/utils/fetchPresentationData.js +1 -15
  37. package/dist/browser/utils/generateSpecFromTemplate.js +16 -62
  38. package/dist/browser/utils/index.js +16 -76
  39. package/dist/bundles/ExampleTemplateBundle.js +1 -85
  40. package/dist/bundles/index.js +1 -85
  41. package/dist/hooks/index.js +40 -1151
  42. package/dist/hooks/useBehaviorTracking.js +1 -157
  43. package/dist/hooks/useEvolution.js +1 -260
  44. package/dist/hooks/useRegistryTemplates.js +1 -31
  45. package/dist/hooks/useSpecContent.js +17 -224
  46. package/dist/hooks/useWorkflowComposer.js +24 -483
  47. package/dist/index.js +40 -3122
  48. package/dist/lib/component-registry.js +1 -48
  49. package/dist/lib/runtime-context.js +1 -21
  50. package/dist/markdown/formatPresentationName.js +1 -9
  51. package/dist/markdown/useMarkdownPresentation.js +1 -65
  52. package/dist/node/EvolutionDashboard.js +1 -803
  53. package/dist/node/EvolutionSidebar.js +1 -531
  54. package/dist/node/LocalDataIndicator.js +1 -68
  55. package/dist/node/MarkdownView.js +1 -213
  56. package/dist/node/OverlayContextProvider.js +1 -202
  57. package/dist/node/PersonalizationInsights.js +1 -455
  58. package/dist/node/SaveToStudioButton.js +1 -79
  59. package/dist/node/SpecDrivenTemplateShell.js +1 -203
  60. package/dist/node/SpecEditorPanel.js +17 -364
  61. package/dist/node/TemplateShell.js +1 -195
  62. package/dist/node/bundles/ExampleTemplateBundle.js +1 -85
  63. package/dist/node/bundles/index.js +1 -85
  64. package/dist/node/hooks/index.js +40 -1151
  65. package/dist/node/hooks/useBehaviorTracking.js +1 -157
  66. package/dist/node/hooks/useEvolution.js +1 -260
  67. package/dist/node/hooks/useRegistryTemplates.js +1 -31
  68. package/dist/node/hooks/useSpecContent.js +17 -224
  69. package/dist/node/hooks/useWorkflowComposer.js +24 -483
  70. package/dist/node/index.js +40 -3122
  71. package/dist/node/lib/component-registry.js +1 -48
  72. package/dist/node/lib/runtime-context.js +1 -21
  73. package/dist/node/markdown/formatPresentationName.js +1 -9
  74. package/dist/node/markdown/useMarkdownPresentation.js +1 -65
  75. package/dist/node/utils/fetchPresentationData.js +1 -15
  76. package/dist/node/utils/generateSpecFromTemplate.js +16 -62
  77. package/dist/node/utils/index.js +16 -76
  78. package/dist/utils/fetchPresentationData.js +1 -15
  79. package/dist/utils/generateSpecFromTemplate.js +16 -62
  80. package/dist/utils/index.js +16 -76
  81. package/package.json +13 -13
  82. package/src/lib/component-registry.tsx +2 -1
  83. package/src/lib/runtime-context.tsx +1 -1
  84. package/src/lib/singletons.test.ts +19 -22
@@ -1,456 +1,2 @@
1
1
  // @bun
2
- // src/hooks/useBehaviorTracking.ts
3
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
- "use client";
5
- var BEHAVIOR_STORAGE_KEY = "contractspec-behavior-data";
6
- var ALL_FEATURES = [
7
- "playground",
8
- "specs",
9
- "builder",
10
- "markdown",
11
- "evolution",
12
- "canvas_add",
13
- "canvas_delete",
14
- "spec_save",
15
- "spec_validate",
16
- "ai_suggestions"
17
- ];
18
- function useBehaviorTracking(templateId) {
19
- const [events, setEvents] = useState([]);
20
- const sessionStartRef = useRef(new Date);
21
- const [eventCount, setEventCount] = useState(0);
22
- useEffect(() => {
23
- try {
24
- const stored = localStorage.getItem(BEHAVIOR_STORAGE_KEY);
25
- if (stored) {
26
- const data = JSON.parse(stored);
27
- setEvents(data.events.map((e) => ({
28
- ...e,
29
- timestamp: new Date(e.timestamp)
30
- })));
31
- sessionStartRef.current = new Date(data.sessionStart);
32
- }
33
- } catch {}
34
- }, []);
35
- useEffect(() => {
36
- if (events.length > 0) {
37
- try {
38
- localStorage.setItem(BEHAVIOR_STORAGE_KEY, JSON.stringify({
39
- events: events.map((e) => ({
40
- ...e,
41
- timestamp: e.timestamp.toISOString()
42
- })),
43
- sessionStart: sessionStartRef.current.toISOString()
44
- }));
45
- } catch {}
46
- }
47
- }, [events]);
48
- const trackEvent = useCallback((type, metadata) => {
49
- const event = {
50
- type,
51
- timestamp: new Date,
52
- templateId,
53
- metadata
54
- };
55
- setEvents((prev) => [...prev, event]);
56
- setEventCount((prev) => prev + 1);
57
- }, [templateId]);
58
- const getEventsByType = useCallback((type) => {
59
- return events.filter((e) => e.type === type);
60
- }, [events]);
61
- const getSummary = useCallback(() => {
62
- const now = new Date;
63
- const sessionDuration = now.getTime() - sessionStartRef.current.getTime();
64
- const templateCounts = new Map;
65
- for (const event of events) {
66
- const count = templateCounts.get(event.templateId) ?? 0;
67
- templateCounts.set(event.templateId, count + 1);
68
- }
69
- const mostUsedTemplates = Array.from(templateCounts.entries()).map(([templateId2, count]) => ({ templateId: templateId2, count })).sort((a, b) => b.count - a.count).slice(0, 3);
70
- const modeCounts = new Map;
71
- for (const event of events) {
72
- if (event.type === "mode_change" && event.metadata?.mode) {
73
- const mode = event.metadata.mode;
74
- const count = modeCounts.get(mode) ?? 0;
75
- modeCounts.set(mode, count + 1);
76
- }
77
- }
78
- const mostUsedModes = Array.from(modeCounts.entries()).map(([mode, count]) => ({ mode, count })).sort((a, b) => b.count - a.count);
79
- const featuresUsed = new Set;
80
- for (const event of events) {
81
- if (event.type === "mode_change" && event.metadata?.mode) {
82
- featuresUsed.add(event.metadata.mode);
83
- }
84
- if (event.type === "feature_usage" && event.metadata?.feature) {
85
- featuresUsed.add(event.metadata.feature);
86
- }
87
- if (event.type === "canvas_interaction") {
88
- const action = event.metadata?.action;
89
- if (action === "add")
90
- featuresUsed.add("canvas_add");
91
- if (action === "delete")
92
- featuresUsed.add("canvas_delete");
93
- }
94
- if (event.type === "spec_edit") {
95
- const action = event.metadata?.action;
96
- if (action === "save")
97
- featuresUsed.add("spec_save");
98
- if (action === "validate")
99
- featuresUsed.add("spec_validate");
100
- }
101
- }
102
- const unusedFeatures = ALL_FEATURES.filter((f) => !featuresUsed.has(f));
103
- const errorCount = events.filter((e) => e.type === "error").length;
104
- const recommendations = generateRecommendations(Array.from(featuresUsed), unusedFeatures, mostUsedModes, events.length);
105
- return {
106
- totalEvents: events.length,
107
- sessionDuration,
108
- mostUsedTemplates,
109
- mostUsedModes,
110
- featuresUsed: Array.from(featuresUsed),
111
- unusedFeatures,
112
- errorCount,
113
- recommendations
114
- };
115
- }, [events]);
116
- const clear = useCallback(() => {
117
- setEvents([]);
118
- setEventCount(0);
119
- sessionStartRef.current = new Date;
120
- localStorage.removeItem(BEHAVIOR_STORAGE_KEY);
121
- }, []);
122
- return useMemo(() => ({
123
- trackEvent,
124
- getSummary,
125
- getEventsByType,
126
- eventCount,
127
- sessionStart: sessionStartRef.current,
128
- clear
129
- }), [trackEvent, getSummary, getEventsByType, eventCount, clear]);
130
- }
131
- function generateRecommendations(featuresUsed, unusedFeatures, mostUsedModes, totalEvents) {
132
- const recommendations = [];
133
- if (unusedFeatures.includes("evolution")) {
134
- recommendations.push("Try the AI Evolution mode to get automated improvement suggestions");
135
- }
136
- if (unusedFeatures.includes("markdown")) {
137
- recommendations.push("Use Markdown preview to see documentation for your specs");
138
- }
139
- if (unusedFeatures.includes("builder")) {
140
- recommendations.push("Explore the Visual Builder to design your UI components");
141
- }
142
- if (!featuresUsed.includes("spec_validate") && featuresUsed.includes("specs")) {
143
- recommendations.push("Don't forget to validate your specs before saving");
144
- }
145
- if (featuresUsed.includes("evolution") && !featuresUsed.includes("ai_suggestions")) {
146
- recommendations.push("Generate AI suggestions to get actionable improvement recommendations");
147
- }
148
- if (totalEvents > 50) {
149
- recommendations.push("Great engagement! Consider saving your work regularly");
150
- }
151
- if (mostUsedModes.length === 1) {
152
- recommendations.push("Try different modes to explore all sandbox capabilities");
153
- }
154
- return recommendations;
155
- }
156
-
157
- // src/PersonalizationInsights.tsx
158
- import { Button } from "@contractspec/lib.design-system";
159
- import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
160
- import { Card } from "@contractspec/lib.ui-kit-web/ui/card";
161
- import { useCallback as useCallback2, useMemo as useMemo2, useState as useState2 } from "react";
162
- import { jsxDEV } from "react/jsx-dev-runtime";
163
- "use client";
164
- function PersonalizationInsights({
165
- templateId,
166
- collapsed = false,
167
- onToggle
168
- }) {
169
- const { getSummary, eventCount, clear, sessionStart } = useBehaviorTracking(templateId);
170
- const [showDetails, setShowDetails] = useState2(false);
171
- const summary = useMemo2(() => getSummary(), [getSummary]);
172
- const formatDuration = useCallback2((ms) => {
173
- const seconds = Math.floor(ms / 1000);
174
- const minutes = Math.floor(seconds / 60);
175
- const hours = Math.floor(minutes / 60);
176
- if (hours > 0) {
177
- return `${hours}h ${minutes % 60}m`;
178
- }
179
- if (minutes > 0) {
180
- return `${minutes}m ${seconds % 60}s`;
181
- }
182
- return `${seconds}s`;
183
- }, []);
184
- const handleClear = useCallback2(() => {
185
- clear();
186
- }, [clear]);
187
- if (collapsed) {
188
- return /* @__PURE__ */ jsxDEV("button", {
189
- onClick: onToggle,
190
- 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",
191
- type: "button",
192
- children: [
193
- /* @__PURE__ */ jsxDEV("span", {
194
- children: "\uD83D\uDCCA"
195
- }, undefined, false, undefined, this),
196
- /* @__PURE__ */ jsxDEV("span", {
197
- children: "Insights"
198
- }, undefined, false, undefined, this),
199
- /* @__PURE__ */ jsxDEV(Badge, {
200
- variant: "secondary",
201
- children: eventCount
202
- }, undefined, false, undefined, this)
203
- ]
204
- }, undefined, true, undefined, this);
205
- }
206
- return /* @__PURE__ */ jsxDEV(Card, {
207
- className: "overflow-hidden",
208
- children: [
209
- /* @__PURE__ */ jsxDEV("div", {
210
- className: "flex items-center justify-between border-blue-500/20 border-b bg-blue-500/5 px-4 py-3",
211
- children: [
212
- /* @__PURE__ */ jsxDEV("div", {
213
- className: "flex items-center gap-2",
214
- children: [
215
- /* @__PURE__ */ jsxDEV("span", {
216
- children: "\uD83D\uDCCA"
217
- }, undefined, false, undefined, this),
218
- /* @__PURE__ */ jsxDEV("span", {
219
- className: "font-semibold",
220
- children: "Personalization Insights"
221
- }, undefined, false, undefined, this)
222
- ]
223
- }, undefined, true, undefined, this),
224
- /* @__PURE__ */ jsxDEV("div", {
225
- className: "flex items-center gap-2",
226
- children: [
227
- /* @__PURE__ */ jsxDEV(Button, {
228
- variant: "ghost",
229
- size: "sm",
230
- onPress: () => setShowDetails(!showDetails),
231
- children: showDetails ? "Hide Details" : "Show Details"
232
- }, undefined, false, undefined, this),
233
- onToggle && /* @__PURE__ */ jsxDEV("button", {
234
- onClick: onToggle,
235
- className: "p-1 text-muted-foreground hover:text-foreground",
236
- type: "button",
237
- title: "Collapse",
238
- children: "\u2715"
239
- }, undefined, false, undefined, this)
240
- ]
241
- }, undefined, true, undefined, this)
242
- ]
243
- }, undefined, true, undefined, this),
244
- /* @__PURE__ */ jsxDEV("div", {
245
- className: "p-4",
246
- children: [
247
- /* @__PURE__ */ jsxDEV("div", {
248
- className: "mb-4 grid grid-cols-2 gap-3 md:grid-cols-4",
249
- children: [
250
- /* @__PURE__ */ jsxDEV(StatCard, {
251
- label: "Session Time",
252
- value: formatDuration(summary.sessionDuration),
253
- icon: "\u23F1\uFE0F"
254
- }, undefined, false, undefined, this),
255
- /* @__PURE__ */ jsxDEV(StatCard, {
256
- label: "Events Tracked",
257
- value: summary.totalEvents.toString(),
258
- icon: "\uD83D\uDCC8"
259
- }, undefined, false, undefined, this),
260
- /* @__PURE__ */ jsxDEV(StatCard, {
261
- label: "Features Used",
262
- value: `${summary.featuresUsed.length}/${summary.featuresUsed.length + summary.unusedFeatures.length}`,
263
- icon: "\u2728"
264
- }, undefined, false, undefined, this),
265
- /* @__PURE__ */ jsxDEV(StatCard, {
266
- label: "Errors",
267
- value: summary.errorCount.toString(),
268
- icon: "\u26A0\uFE0F",
269
- variant: summary.errorCount > 0 ? "warning" : "success"
270
- }, undefined, false, undefined, this)
271
- ]
272
- }, undefined, true, undefined, this),
273
- summary.recommendations.length > 0 && /* @__PURE__ */ jsxDEV("div", {
274
- className: "mb-4",
275
- children: [
276
- /* @__PURE__ */ jsxDEV("h4", {
277
- className: "mb-2 font-semibold text-blue-400 text-xs uppercase",
278
- children: "Recommendations"
279
- }, undefined, false, undefined, this),
280
- /* @__PURE__ */ jsxDEV("ul", {
281
- className: "space-y-1",
282
- children: summary.recommendations.map((rec, index) => /* @__PURE__ */ jsxDEV("li", {
283
- className: "flex items-start gap-2 text-sm",
284
- children: [
285
- /* @__PURE__ */ jsxDEV("span", {
286
- className: "text-blue-400",
287
- children: "\uD83D\uDCA1"
288
- }, undefined, false, undefined, this),
289
- /* @__PURE__ */ jsxDEV("span", {
290
- children: rec
291
- }, undefined, false, undefined, this)
292
- ]
293
- }, index, true, undefined, this))
294
- }, undefined, false, undefined, this)
295
- ]
296
- }, undefined, true, undefined, this),
297
- summary.unusedFeatures.length > 0 && /* @__PURE__ */ jsxDEV("div", {
298
- className: "mb-4",
299
- children: [
300
- /* @__PURE__ */ jsxDEV("h4", {
301
- className: "mb-2 font-semibold text-blue-400 text-xs uppercase",
302
- children: "Try These Features"
303
- }, undefined, false, undefined, this),
304
- /* @__PURE__ */ jsxDEV("div", {
305
- className: "flex flex-wrap gap-2",
306
- children: summary.unusedFeatures.slice(0, 5).map((feature) => /* @__PURE__ */ jsxDEV(Badge, {
307
- variant: "secondary",
308
- children: formatFeatureName(feature)
309
- }, feature, false, undefined, this))
310
- }, undefined, false, undefined, this)
311
- ]
312
- }, undefined, true, undefined, this),
313
- showDetails && /* @__PURE__ */ jsxDEV(DetailedInsights, {
314
- summary,
315
- sessionStart
316
- }, undefined, false, undefined, this),
317
- /* @__PURE__ */ jsxDEV("div", {
318
- className: "mt-4 flex justify-end border-blue-500/10 border-t pt-4",
319
- children: /* @__PURE__ */ jsxDEV(Button, {
320
- variant: "ghost",
321
- size: "sm",
322
- onPress: handleClear,
323
- children: "Clear Data"
324
- }, undefined, false, undefined, this)
325
- }, undefined, false, undefined, this)
326
- ]
327
- }, undefined, true, undefined, this)
328
- ]
329
- }, undefined, true, undefined, this);
330
- }
331
- function StatCard({
332
- label,
333
- value,
334
- icon,
335
- variant = "default"
336
- }) {
337
- const bgColors = {
338
- default: "bg-blue-500/5 border-blue-500/20",
339
- warning: "bg-amber-500/5 border-amber-500/20",
340
- success: "bg-green-500/5 border-green-500/20"
341
- };
342
- return /* @__PURE__ */ jsxDEV("div", {
343
- className: `rounded-lg border p-3 text-center ${bgColors[variant]}`,
344
- children: [
345
- /* @__PURE__ */ jsxDEV("div", {
346
- className: "mb-1 text-lg",
347
- children: icon
348
- }, undefined, false, undefined, this),
349
- /* @__PURE__ */ jsxDEV("div", {
350
- className: "font-bold text-lg",
351
- children: value
352
- }, undefined, false, undefined, this),
353
- /* @__PURE__ */ jsxDEV("div", {
354
- className: "text-muted-foreground text-xs",
355
- children: label
356
- }, undefined, false, undefined, this)
357
- ]
358
- }, undefined, true, undefined, this);
359
- }
360
- function DetailedInsights({
361
- summary,
362
- sessionStart
363
- }) {
364
- return /* @__PURE__ */ jsxDEV("div", {
365
- className: "mt-4 space-y-4 border-blue-500/10 border-t pt-4",
366
- children: [
367
- summary.mostUsedTemplates.length > 0 && /* @__PURE__ */ jsxDEV("div", {
368
- children: [
369
- /* @__PURE__ */ jsxDEV("h4", {
370
- className: "mb-2 font-semibold text-blue-400 text-xs uppercase",
371
- children: "Most Used Templates"
372
- }, undefined, false, undefined, this),
373
- /* @__PURE__ */ jsxDEV("div", {
374
- className: "space-y-1",
375
- children: summary.mostUsedTemplates.map(({ templateId, count }) => /* @__PURE__ */ jsxDEV("div", {
376
- className: "flex items-center justify-between text-sm",
377
- children: [
378
- /* @__PURE__ */ jsxDEV("span", {
379
- children: formatTemplateId(templateId)
380
- }, undefined, false, undefined, this),
381
- /* @__PURE__ */ jsxDEV("span", {
382
- className: "text-muted-foreground",
383
- children: [
384
- count,
385
- " events"
386
- ]
387
- }, undefined, true, undefined, this)
388
- ]
389
- }, templateId, true, undefined, this))
390
- }, undefined, false, undefined, this)
391
- ]
392
- }, undefined, true, undefined, this),
393
- summary.mostUsedModes.length > 0 && /* @__PURE__ */ jsxDEV("div", {
394
- children: [
395
- /* @__PURE__ */ jsxDEV("h4", {
396
- className: "mb-2 font-semibold text-blue-400 text-xs uppercase",
397
- children: "Mode Usage"
398
- }, undefined, false, undefined, this),
399
- /* @__PURE__ */ jsxDEV("div", {
400
- className: "space-y-1",
401
- children: summary.mostUsedModes.map(({ mode, count }) => /* @__PURE__ */ jsxDEV("div", {
402
- className: "flex items-center justify-between text-sm",
403
- children: [
404
- /* @__PURE__ */ jsxDEV("span", {
405
- children: formatFeatureName(mode)
406
- }, undefined, false, undefined, this),
407
- /* @__PURE__ */ jsxDEV("span", {
408
- className: "text-muted-foreground",
409
- children: [
410
- count,
411
- " switches"
412
- ]
413
- }, undefined, true, undefined, this)
414
- ]
415
- }, mode, true, undefined, this))
416
- }, undefined, false, undefined, this)
417
- ]
418
- }, undefined, true, undefined, this),
419
- /* @__PURE__ */ jsxDEV("div", {
420
- children: [
421
- /* @__PURE__ */ jsxDEV("h4", {
422
- className: "mb-2 font-semibold text-blue-400 text-xs uppercase",
423
- children: "Features Used"
424
- }, undefined, false, undefined, this),
425
- /* @__PURE__ */ jsxDEV("div", {
426
- className: "flex flex-wrap gap-2",
427
- children: summary.featuresUsed.map((feature) => /* @__PURE__ */ jsxDEV(Badge, {
428
- variant: "default",
429
- className: "border-green-500/30 bg-green-500/20 text-green-400",
430
- children: [
431
- "\u2713 ",
432
- formatFeatureName(feature)
433
- ]
434
- }, feature, true, undefined, this))
435
- }, undefined, false, undefined, this)
436
- ]
437
- }, undefined, true, undefined, this),
438
- /* @__PURE__ */ jsxDEV("div", {
439
- className: "text-muted-foreground text-xs",
440
- children: [
441
- "Session started: ",
442
- sessionStart.toLocaleString()
443
- ]
444
- }, undefined, true, undefined, this)
445
- ]
446
- }, undefined, true, undefined, this);
447
- }
448
- function formatFeatureName(feature) {
449
- return feature.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
450
- }
451
- function formatTemplateId(id) {
452
- return id.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
453
- }
454
- export {
455
- PersonalizationInsights
456
- };
2
+ import{useCallback as A,useEffect as b,useMemo as p,useRef as y,useState as I}from"react";var k="contractspec-behavior-data",f=["playground","specs","builder","markdown","evolution","canvas_add","canvas_delete","spec_save","spec_validate","ai_suggestions"];function T(P){let[J,W]=I([]),Z=y(new Date),[H,O]=I(0);b(()=>{try{let K=localStorage.getItem(k);if(K){let V=JSON.parse(K);W(V.events.map((L)=>({...L,timestamp:new Date(L.timestamp)}))),Z.current=new Date(V.sessionStart)}}catch{}},[]),b(()=>{if(J.length>0)try{localStorage.setItem(k,JSON.stringify({events:J.map((K)=>({...K,timestamp:K.timestamp.toISOString()})),sessionStart:Z.current.toISOString()}))}catch{}},[J]);let M=A((K,V)=>{let L={type:K,timestamp:new Date,templateId:P,metadata:V};W((N)=>[...N,L]),O((N)=>N+1)},[P]),w=A((K)=>{return J.filter((V)=>V.type===K)},[J]),_=A(()=>{let V=new Date().getTime()-Z.current.getTime(),L=new Map;for(let z of J){let X=L.get(z.templateId)??0;L.set(z.templateId,X+1)}let N=Array.from(L.entries()).map(([z,X])=>({templateId:z,count:X})).sort((z,X)=>X.count-z.count).slice(0,3),G=new Map;for(let z of J)if(z.type==="mode_change"&&z.metadata?.mode){let X=z.metadata.mode,g=G.get(X)??0;G.set(X,g+1)}let D=Array.from(G.entries()).map(([z,X])=>({mode:z,count:X})).sort((z,X)=>X.count-z.count),Y=new Set;for(let z of J){if(z.type==="mode_change"&&z.metadata?.mode)Y.add(z.metadata.mode);if(z.type==="feature_usage"&&z.metadata?.feature)Y.add(z.metadata.feature);if(z.type==="canvas_interaction"){let X=z.metadata?.action;if(X==="add")Y.add("canvas_add");if(X==="delete")Y.add("canvas_delete")}if(z.type==="spec_edit"){let X=z.metadata?.action;if(X==="save")Y.add("spec_save");if(X==="validate")Y.add("spec_validate")}}let R=f.filter((z)=>!Y.has(z)),E=J.filter((z)=>z.type==="error").length,S=j(Array.from(Y),R,D,J.length);return{totalEvents:J.length,sessionDuration:V,mostUsedTemplates:N,mostUsedModes:D,featuresUsed:Array.from(Y),unusedFeatures:R,errorCount:E,recommendations:S}},[J]),$=A(()=>{W([]),O(0),Z.current=new Date,localStorage.removeItem(k)},[]);return p(()=>({trackEvent:M,getSummary:_,getEventsByType:w,eventCount:H,sessionStart:Z.current,clear:$}),[M,_,w,H,$])}function j(P,J,W,Z){let H=[];if(J.includes("evolution"))H.push("Try the AI Evolution mode to get automated improvement suggestions");if(J.includes("markdown"))H.push("Use Markdown preview to see documentation for your specs");if(J.includes("builder"))H.push("Explore the Visual Builder to design your UI components");if(!P.includes("spec_validate")&&P.includes("specs"))H.push("Don't forget to validate your specs before saving");if(P.includes("evolution")&&!P.includes("ai_suggestions"))H.push("Generate AI suggestions to get actionable improvement recommendations");if(Z>50)H.push("Great engagement! Consider saving your work regularly");if(W.length===1)H.push("Try different modes to explore all sandbox capabilities");return H}import{Button as U}from"@contractspec/lib.design-system";import{Badge as B}from"@contractspec/lib.ui-kit-web/ui/badge";import{Card as v}from"@contractspec/lib.ui-kit-web/ui/card";import{useCallback as C,useMemo as x,useState as i}from"react";import{jsx as q,jsxs as Q}from"react/jsx-runtime";function t({templateId:P,collapsed:J=!1,onToggle:W}){let{getSummary:Z,eventCount:H,clear:O,sessionStart:M}=T(P),[w,_]=i(!1),$=x(()=>Z(),[Z]),K=C((L)=>{let N=Math.floor(L/1000),G=Math.floor(N/60),D=Math.floor(G/60);if(D>0)return`${D}h ${G%60}m`;if(G>0)return`${G}m ${N%60}s`;return`${N}s`},[]),V=C(()=>{O()},[O]);if(J)return Q("button",{onClick:W,className:"flex items-center gap-2 rounded-lg border border-blue-500/30 bg-blue-500/10 px-3 py-2 text-sm transition hover:bg-blue-500/20",type:"button",children:[q("span",{children:"\uD83D\uDCCA"}),q("span",{children:"Insights"}),q(B,{variant:"secondary",children:H})]});return Q(v,{className:"overflow-hidden",children:[Q("div",{className:"flex items-center justify-between border-blue-500/20 border-b bg-blue-500/5 px-4 py-3",children:[Q("div",{className:"flex items-center gap-2",children:[q("span",{children:"\uD83D\uDCCA"}),q("span",{className:"font-semibold",children:"Personalization Insights"})]}),Q("div",{className:"flex items-center gap-2",children:[q(U,{variant:"ghost",size:"sm",onPress:()=>_(!w),children:w?"Hide Details":"Show Details"}),W&&q("button",{onClick:W,className:"p-1 text-muted-foreground hover:text-foreground",type:"button",title:"Collapse",children:"\u2715"})]})]}),Q("div",{className:"p-4",children:[Q("div",{className:"mb-4 grid grid-cols-2 gap-3 md:grid-cols-4",children:[q(h,{label:"Session Time",value:K($.sessionDuration),icon:"\u23F1\uFE0F"}),q(h,{label:"Events Tracked",value:$.totalEvents.toString(),icon:"\uD83D\uDCC8"}),q(h,{label:"Features Used",value:`${$.featuresUsed.length}/${$.featuresUsed.length+$.unusedFeatures.length}`,icon:"\u2728"}),q(h,{label:"Errors",value:$.errorCount.toString(),icon:"\u26A0\uFE0F",variant:$.errorCount>0?"warning":"success"})]}),$.recommendations.length>0&&Q("div",{className:"mb-4",children:[q("h4",{className:"mb-2 font-semibold text-blue-400 text-xs uppercase",children:"Recommendations"}),q("ul",{className:"space-y-1",children:$.recommendations.map((L,N)=>Q("li",{className:"flex items-start gap-2 text-sm",children:[q("span",{className:"text-blue-400",children:"\uD83D\uDCA1"}),q("span",{children:L})]},N))})]}),$.unusedFeatures.length>0&&Q("div",{className:"mb-4",children:[q("h4",{className:"mb-2 font-semibold text-blue-400 text-xs uppercase",children:"Try These Features"}),q("div",{className:"flex flex-wrap gap-2",children:$.unusedFeatures.slice(0,5).map((L)=>q(B,{variant:"secondary",children:F(L)},L))})]}),w&&q(l,{summary:$,sessionStart:M}),q("div",{className:"mt-4 flex justify-end border-blue-500/10 border-t pt-4",children:q(U,{variant:"ghost",size:"sm",onPress:V,children:"Clear Data"})})]})]})}function h({label:P,value:J,icon:W,variant:Z="default"}){return Q("div",{className:`rounded-lg border p-3 text-center ${{default:"bg-blue-500/5 border-blue-500/20",warning:"bg-amber-500/5 border-amber-500/20",success:"bg-green-500/5 border-green-500/20"}[Z]}`,children:[q("div",{className:"mb-1 text-lg",children:W}),q("div",{className:"font-bold text-lg",children:J}),q("div",{className:"text-muted-foreground text-xs",children:P})]})}function l({summary:P,sessionStart:J}){return Q("div",{className:"mt-4 space-y-4 border-blue-500/10 border-t pt-4",children:[P.mostUsedTemplates.length>0&&Q("div",{children:[q("h4",{className:"mb-2 font-semibold text-blue-400 text-xs uppercase",children:"Most Used Templates"}),q("div",{className:"space-y-1",children:P.mostUsedTemplates.map(({templateId:W,count:Z})=>Q("div",{className:"flex items-center justify-between text-sm",children:[q("span",{children:c(W)}),Q("span",{className:"text-muted-foreground",children:[Z," events"]})]},W))})]}),P.mostUsedModes.length>0&&Q("div",{children:[q("h4",{className:"mb-2 font-semibold text-blue-400 text-xs uppercase",children:"Mode Usage"}),q("div",{className:"space-y-1",children:P.mostUsedModes.map(({mode:W,count:Z})=>Q("div",{className:"flex items-center justify-between text-sm",children:[q("span",{children:F(W)}),Q("span",{className:"text-muted-foreground",children:[Z," switches"]})]},W))})]}),Q("div",{children:[q("h4",{className:"mb-2 font-semibold text-blue-400 text-xs uppercase",children:"Features Used"}),q("div",{className:"flex flex-wrap gap-2",children:P.featuresUsed.map((W)=>Q(B,{variant:"default",className:"border-green-500/30 bg-green-500/20 text-green-400",children:["\u2713 ",F(W)]},W))})]}),Q("div",{className:"text-muted-foreground text-xs",children:["Session started: ",J.toLocaleString()]})]})}function F(P){return P.replace(/_/g," ").replace(/\b\w/g,(J)=>J.toUpperCase())}function c(P){return P.replace(/-/g," ").replace(/\b\w/g,(J)=>J.toUpperCase())}export{t as PersonalizationInsights};
@@ -1,80 +1,2 @@
1
1
  // @bun
2
- // src/lib/runtime-context.tsx
3
- import { createContext, useContext } from "react";
4
- "use client";
5
- var TEMPLATE_RUNTIME_CONTEXT_KEY = Symbol.for("@contractspec/lib.example-shared-ui/template-runtime-context");
6
- function getTemplateRuntimeContextSingleton() {
7
- const store = globalThis;
8
- store[TEMPLATE_RUNTIME_CONTEXT_KEY] ??= createContext(null);
9
- return store[TEMPLATE_RUNTIME_CONTEXT_KEY];
10
- }
11
- var TemplateRuntimeContext = getTemplateRuntimeContextSingleton();
12
- function useTemplateRuntime() {
13
- const context = useContext(TemplateRuntimeContext);
14
- if (!context) {
15
- throw new Error("useTemplateRuntime must be used within a TemplateRuntimeProvider");
16
- }
17
- return context;
18
- }
19
-
20
- // src/SaveToStudioButton.tsx
21
- import { Sparkles } from "lucide-react";
22
- import { useState } from "react";
23
- import { jsxDEV } from "react/jsx-dev-runtime";
24
- "use client";
25
- function SaveToStudioButton({
26
- organizationId = "demo-org",
27
- projectName,
28
- endpoint,
29
- token
30
- }) {
31
- const { installer, templateId, template } = useTemplateRuntime();
32
- const [status, setStatus] = useState("idle");
33
- const [error, setError] = useState(null);
34
- const handleSave = async () => {
35
- setStatus("saving");
36
- setError(null);
37
- try {
38
- await installer.saveToStudio({
39
- templateId,
40
- projectName: projectName ?? `${template.name} demo`,
41
- organizationId,
42
- endpoint,
43
- token
44
- });
45
- setStatus("saved");
46
- setTimeout(() => setStatus("idle"), 3000);
47
- } catch (err) {
48
- setStatus("error");
49
- setError(err instanceof Error ? err.message : "Unknown error");
50
- }
51
- };
52
- return /* @__PURE__ */ jsxDEV("div", {
53
- className: "flex flex-col items-end gap-1",
54
- children: [
55
- /* @__PURE__ */ jsxDEV("button", {
56
- type: "button",
57
- className: "btn-primary inline-flex items-center gap-2 text-sm",
58
- onClick: handleSave,
59
- disabled: status === "saving",
60
- children: [
61
- /* @__PURE__ */ jsxDEV(Sparkles, {
62
- className: "h-4 w-4"
63
- }, undefined, false, undefined, this),
64
- status === "saving" ? "Publishing\u2026" : "Save to Studio"
65
- ]
66
- }, undefined, true, undefined, this),
67
- status === "error" && error ? /* @__PURE__ */ jsxDEV("p", {
68
- className: "text-destructive text-xs",
69
- children: error
70
- }, undefined, false, undefined, this) : null,
71
- status === "saved" ? /* @__PURE__ */ jsxDEV("p", {
72
- className: "text-emerald-400 text-xs",
73
- children: "Template sent to Studio."
74
- }, undefined, false, undefined, this) : null
75
- ]
76
- }, undefined, true, undefined, this);
77
- }
78
- export {
79
- SaveToStudioButton
80
- };
2
+ import{createContext as f,useContext as b}from"react";var i=Symbol.for("@contractspec/lib.example-shared-ui/template-runtime-context");function h(){let e=globalThis;return e[i]??=f(null),e[i]}var k=h();function l(){let e=b(k);if(!e)throw Error("useTemplateRuntime must be used within a TemplateRuntimeProvider");return e}import{Sparkles as y}from"lucide-react";import{useState as d}from"react";import{jsx as r,jsxs as u}from"react/jsx-runtime";function $({organizationId:e="demo-org",projectName:m,endpoint:p,token:c}){let{installer:v,templateId:S,template:g}=l(),[t,a]=d("idle"),[o,n]=d(null);return u("div",{className:"flex flex-col items-end gap-1",children:[u("button",{type:"button",className:"btn-primary inline-flex items-center gap-2 text-sm",onClick:async()=>{a("saving"),n(null);try{await v.saveToStudio({templateId:S,projectName:m??`${g.name} demo`,organizationId:e,endpoint:p,token:c}),a("saved"),setTimeout(()=>a("idle"),3000)}catch(s){a("error"),n(s instanceof Error?s.message:"Unknown error")}},disabled:t==="saving",children:[r(y,{className:"h-4 w-4"}),t==="saving"?"Publishing\u2026":"Save to Studio"]}),t==="error"&&o?r("p",{className:"text-destructive text-xs",children:o}):null,t==="saved"?r("p",{className:"text-emerald-400 text-xs",children:"Template sent to Studio."}):null]})}export{$ as SaveToStudioButton};