@contractspec/lib.example-shared-ui 6.0.6 → 6.0.10

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 (82) hide show
  1. package/.turbo/turbo-build.log +90 -84
  2. package/AGENTS.md +43 -25
  3. package/CHANGELOG.md +24 -0
  4. package/README.md +63 -35
  5. package/dist/EvolutionDashboard.js +9 -9
  6. package/dist/EvolutionSidebar.js +15 -15
  7. package/dist/LocalDataIndicator.js +3 -3
  8. package/dist/MarkdownView.d.ts +0 -7
  9. package/dist/MarkdownView.js +76 -172
  10. package/dist/PersonalizationInsights.js +12 -12
  11. package/dist/SaveToStudioButton.js +2 -2
  12. package/dist/SpecDrivenTemplateShell.d.ts +1 -1
  13. package/dist/SpecDrivenTemplateShell.js +10 -10
  14. package/dist/SpecEditorPanel.js +3 -3
  15. package/dist/TemplateShell.js +10 -10
  16. package/dist/browser/EvolutionDashboard.js +9 -9
  17. package/dist/browser/EvolutionSidebar.js +15 -15
  18. package/dist/browser/LocalDataIndicator.js +3 -3
  19. package/dist/browser/MarkdownView.js +76 -172
  20. package/dist/browser/PersonalizationInsights.js +12 -12
  21. package/dist/browser/SaveToStudioButton.js +2 -2
  22. package/dist/browser/SpecDrivenTemplateShell.js +10 -10
  23. package/dist/browser/SpecEditorPanel.js +3 -3
  24. package/dist/browser/TemplateShell.js +10 -10
  25. package/dist/browser/hooks/index.js +29 -29
  26. package/dist/browser/index.js +193 -286
  27. package/dist/browser/lib/component-registry.js +1 -1
  28. package/dist/browser/markdown/formatPresentationName.js +9 -0
  29. package/dist/browser/markdown/useMarkdownPresentation.js +65 -0
  30. package/dist/hooks/index.d.ts +3 -3
  31. package/dist/hooks/index.js +29 -29
  32. package/dist/index.d.ts +12 -11
  33. package/dist/index.js +193 -286
  34. package/dist/lib/component-registry.js +1 -1
  35. package/dist/markdown/formatPresentationName.d.ts +1 -0
  36. package/dist/markdown/formatPresentationName.js +10 -0
  37. package/dist/markdown/useMarkdownPresentation.d.ts +21 -0
  38. package/dist/markdown/useMarkdownPresentation.js +66 -0
  39. package/dist/node/EvolutionDashboard.js +9 -9
  40. package/dist/node/EvolutionSidebar.js +15 -15
  41. package/dist/node/LocalDataIndicator.js +3 -3
  42. package/dist/node/MarkdownView.js +76 -172
  43. package/dist/node/PersonalizationInsights.js +12 -12
  44. package/dist/node/SaveToStudioButton.js +2 -2
  45. package/dist/node/SpecDrivenTemplateShell.js +10 -10
  46. package/dist/node/SpecEditorPanel.js +3 -3
  47. package/dist/node/TemplateShell.js +10 -10
  48. package/dist/node/hooks/index.js +29 -29
  49. package/dist/node/index.js +193 -286
  50. package/dist/node/lib/component-registry.js +1 -1
  51. package/dist/node/markdown/formatPresentationName.js +9 -0
  52. package/dist/node/markdown/useMarkdownPresentation.js +65 -0
  53. package/dist/utils/index.d.ts +1 -1
  54. package/package.json +40 -13
  55. package/src/EvolutionDashboard.tsx +415 -415
  56. package/src/EvolutionSidebar.tsx +245 -245
  57. package/src/LocalDataIndicator.tsx +28 -28
  58. package/src/MarkdownView.tsx +119 -372
  59. package/src/OverlayContextProvider.tsx +272 -272
  60. package/src/PersonalizationInsights.tsx +232 -232
  61. package/src/SaveToStudioButton.tsx +51 -51
  62. package/src/SpecDrivenTemplateShell.tsx +59 -59
  63. package/src/SpecEditorPanel.tsx +138 -138
  64. package/src/TemplateShell.tsx +50 -50
  65. package/src/bundles/ExampleTemplateBundle.ts +78 -78
  66. package/src/hooks/index.ts +3 -3
  67. package/src/hooks/useBehaviorTracking.ts +252 -252
  68. package/src/hooks/useEvolution.ts +437 -437
  69. package/src/hooks/useRegistryTemplates.ts +42 -42
  70. package/src/hooks/useSpecContent.ts +214 -214
  71. package/src/hooks/useWorkflowComposer.ts +567 -567
  72. package/src/index.ts +12 -11
  73. package/src/lib/component-registry.tsx +40 -40
  74. package/src/lib/runtime-context.tsx +31 -31
  75. package/src/lib/types.ts +57 -57
  76. package/src/markdown/formatPresentationName.ts +9 -0
  77. package/src/markdown/useMarkdownPresentation.ts +107 -0
  78. package/src/overlay-types.ts +15 -15
  79. package/src/utils/fetchPresentationData.ts +13 -13
  80. package/src/utils/generateSpecFromTemplate.ts +29 -29
  81. package/src/utils/index.ts +1 -1
  82. package/tsconfig.json +8 -8
@@ -1,49 +1,49 @@
1
1
  import { useQuery } from '@tanstack/react-query';
2
2
 
3
3
  export interface RegistryTemplate {
4
- id: string;
5
- name: string;
6
- description: string;
7
- tags: string[];
8
- source: 'registry';
9
- registryUrl?: string | null;
4
+ id: string;
5
+ name: string;
6
+ description: string;
7
+ tags: string[];
8
+ source: 'registry';
9
+ registryUrl?: string | null;
10
10
  }
11
11
 
12
12
  export function useRegistryTemplates() {
13
- return useQuery({
14
- queryKey: ['registryTemplates'] as const,
15
- queryFn: async (): Promise<RegistryTemplate[]> => {
16
- const registryUrl =
17
- process.env.NEXT_PUBLIC_CONTRACTSPEC_REGISTRY_URL ?? '';
18
- if (!registryUrl) return [];
19
- const res = await fetch(
20
- `${registryUrl.replace(/\/$/, '')}/r/contractspec.json`,
21
- {
22
- method: 'GET',
23
- headers: { Accept: 'application/json' },
24
- }
25
- );
26
- if (!res.ok) return [];
27
- const json = (await res.json()) as {
28
- items?: {
29
- name: string;
30
- type: string;
31
- title: string;
32
- description: string;
33
- meta?: { tags?: string[] };
34
- }[];
35
- };
36
- const items = json.items ?? [];
37
- return items
38
- .filter((i) => i.type === 'contractspec:template')
39
- .map((i) => ({
40
- id: i.name,
41
- name: i.title ?? i.name,
42
- description: i.description,
43
- tags: i.meta?.tags ?? [],
44
- source: 'registry' as const,
45
- registryUrl,
46
- }));
47
- },
48
- });
13
+ return useQuery({
14
+ queryKey: ['registryTemplates'] as const,
15
+ queryFn: async (): Promise<RegistryTemplate[]> => {
16
+ const registryUrl =
17
+ process.env.NEXT_PUBLIC_CONTRACTSPEC_REGISTRY_URL ?? '';
18
+ if (!registryUrl) return [];
19
+ const res = await fetch(
20
+ `${registryUrl.replace(/\/$/, '')}/r/contractspec.json`,
21
+ {
22
+ method: 'GET',
23
+ headers: { Accept: 'application/json' },
24
+ }
25
+ );
26
+ if (!res.ok) return [];
27
+ const json = (await res.json()) as {
28
+ items?: {
29
+ name: string;
30
+ type: string;
31
+ title: string;
32
+ description: string;
33
+ meta?: { tags?: string[] };
34
+ }[];
35
+ };
36
+ const items = json.items ?? [];
37
+ return items
38
+ .filter((i) => i.type === 'contractspec:template')
39
+ .map((i) => ({
40
+ id: i.name,
41
+ name: i.title ?? i.name,
42
+ description: i.description,
43
+ tags: i.meta?.tags ?? [],
44
+ source: 'registry' as const,
45
+ registryUrl,
46
+ }));
47
+ },
48
+ });
49
49
  }
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
 
3
3
  import { useCallback, useEffect, useState } from 'react';
4
- import type { TemplateId } from '../lib/types';
5
4
  import { useTemplateRuntime } from '../lib/runtime-context';
5
+ import type { TemplateId } from '../lib/types';
6
6
  import { generateSpecFromTemplate } from '../utils/generateSpecFromTemplate';
7
7
 
8
8
  /**
@@ -14,36 +14,36 @@ const SPEC_STORAGE_KEY = 'contractspec-spec-content';
14
14
  * Validation result for spec content
15
15
  */
16
16
  export interface SpecValidationResult {
17
- valid: boolean;
18
- errors: {
19
- line: number;
20
- message: string;
21
- severity: 'error' | 'warning';
22
- }[];
17
+ valid: boolean;
18
+ errors: {
19
+ line: number;
20
+ message: string;
21
+ severity: 'error' | 'warning';
22
+ }[];
23
23
  }
24
24
 
25
25
  /**
26
26
  * Hook return type
27
27
  */
28
28
  export interface UseSpecContentReturn {
29
- /** Current spec content */
30
- content: string;
31
- /** Whether the spec is loading */
32
- loading: boolean;
33
- /** Whether the spec has unsaved changes */
34
- isDirty: boolean;
35
- /** Last validation result */
36
- validation: SpecValidationResult | null;
37
- /** Update spec content */
38
- setContent: (content: string) => void;
39
- /** Save spec content to storage */
40
- save: () => void;
41
- /** Validate spec content */
42
- validate: () => SpecValidationResult;
43
- /** Reset to template default */
44
- reset: () => void;
45
- /** Last saved timestamp */
46
- lastSaved: string | null;
29
+ /** Current spec content */
30
+ content: string;
31
+ /** Whether the spec is loading */
32
+ loading: boolean;
33
+ /** Whether the spec has unsaved changes */
34
+ isDirty: boolean;
35
+ /** Last validation result */
36
+ validation: SpecValidationResult | null;
37
+ /** Update spec content */
38
+ setContent: (content: string) => void;
39
+ /** Save spec content to storage */
40
+ save: () => void;
41
+ /** Validate spec content */
42
+ validate: () => SpecValidationResult;
43
+ /** Reset to template default */
44
+ reset: () => void;
45
+ /** Last saved timestamp */
46
+ lastSaved: string | null;
47
47
  }
48
48
 
49
49
  /**
@@ -51,193 +51,193 @@ export interface UseSpecContentReturn {
51
51
  * Uses localStorage for persistence in the sandbox environment.
52
52
  */
53
53
  export function useSpecContent(templateId: TemplateId): UseSpecContentReturn {
54
- const { template } = useTemplateRuntime();
55
- const [content, setContentState] = useState<string>('');
56
- const [savedContent, setSavedContent] = useState<string>('');
57
- const [loading, setLoading] = useState(true);
58
- const [validation, setValidation] = useState<SpecValidationResult | null>(
59
- null
60
- );
61
- const [lastSaved, setLastSaved] = useState<string | null>(null);
62
-
63
- // Load spec content from storage on mount
64
- useEffect(() => {
65
- setLoading(true);
66
- try {
67
- const stored = localStorage.getItem(`${SPEC_STORAGE_KEY}-${templateId}`);
68
- if (stored) {
69
- const parsed = JSON.parse(stored) as {
70
- content: string;
71
- savedAt: string;
72
- };
73
- if (parsed.content) {
74
- setContentState(parsed.content);
75
- setSavedContent(parsed.content);
76
- setLastSaved(parsed.savedAt);
77
- } else {
78
- // Invalid stored state, generate from template
79
- const generated = generateSpecFromTemplate(template);
80
- setContentState(generated);
81
- setSavedContent(generated);
82
- }
83
- } else {
84
- // No stored state, generate from template
85
- const generated = generateSpecFromTemplate(template);
86
- setContentState(generated);
87
- setSavedContent(generated);
88
- }
89
- } catch {
90
- // On error, generate from template
91
- const generated = generateSpecFromTemplate(template);
92
- setContentState(generated);
93
- setSavedContent(generated);
94
- }
95
- setLoading(false);
96
- }, [templateId]);
97
-
98
- /**
99
- * Update spec content (in-memory only until save)
100
- */
101
- const setContent = useCallback((newContent: string): void => {
102
- setContentState(newContent);
103
- // Clear validation when content changes
104
- setValidation(null);
105
- }, []);
106
-
107
- /**
108
- * Save spec content to storage
109
- */
110
- const save = useCallback((): void => {
111
- try {
112
- const savedAt = new Date().toISOString();
113
- localStorage.setItem(
114
- `${SPEC_STORAGE_KEY}-${templateId}`,
115
- JSON.stringify({
116
- content,
117
- savedAt,
118
- })
119
- );
120
- setSavedContent(content);
121
- setLastSaved(savedAt);
122
- } catch {
123
- // Ignore storage errors
124
- }
125
- }, [content, templateId]);
126
-
127
- /**
128
- * Validate spec content
129
- * Performs basic syntax validation
130
- */
131
- const validate = useCallback((): SpecValidationResult => {
132
- const errors: SpecValidationResult['errors'] = [];
133
-
134
- // Basic validation rules
135
- const lines = content.split('\n');
136
-
137
- // Check for contractSpec function call
138
- if (!content.includes('contractSpec(')) {
139
- errors.push({
140
- line: 1,
141
- message: 'Spec must contain a contractSpec() definition',
142
- severity: 'error',
143
- });
144
- }
145
-
146
- // Check for required fields
147
- if (!content.includes('goal:')) {
148
- errors.push({
149
- line: 1,
150
- message: 'Spec should have a goal field',
151
- severity: 'warning',
152
- });
153
- }
154
-
155
- if (!content.includes('io:')) {
156
- errors.push({
157
- line: 1,
158
- message: 'Spec should define io (input/output)',
159
- severity: 'warning',
160
- });
161
- }
162
-
163
- // Check for balanced braces
164
- const openBraces = (content.match(/{/g) ?? []).length;
165
- const closeBraces = (content.match(/}/g) ?? []).length;
166
- if (openBraces !== closeBraces) {
167
- errors.push({
168
- line: lines.length,
169
- message: `Unbalanced braces: ${openBraces} opening, ${closeBraces} closing`,
170
- severity: 'error',
171
- });
172
- }
173
-
174
- // Check for balanced parentheses
175
- const openParens = (content.match(/\(/g) ?? []).length;
176
- const closeParens = (content.match(/\)/g) ?? []).length;
177
- if (openParens !== closeParens) {
178
- errors.push({
179
- line: lines.length,
180
- message: `Unbalanced parentheses: ${openParens} opening, ${closeParens} closing`,
181
- severity: 'error',
182
- });
183
- }
184
-
185
- // Check for unclosed strings (basic check)
186
- lines.forEach((line, index) => {
187
- const singleQuotes = (line.match(/'/g) ?? []).length;
188
- const doubleQuotes = (line.match(/"/g) ?? []).length;
189
- if (singleQuotes % 2 !== 0) {
190
- errors.push({
191
- line: index + 1,
192
- message: 'Unclosed single quote',
193
- severity: 'error',
194
- });
195
- }
196
- if (doubleQuotes % 2 !== 0) {
197
- errors.push({
198
- line: index + 1,
199
- message: 'Unclosed double quote',
200
- severity: 'error',
201
- });
202
- }
203
- });
204
-
205
- const result: SpecValidationResult = {
206
- valid: errors.filter((e) => e.severity === 'error').length === 0,
207
- errors,
208
- };
209
-
210
- setValidation(result);
211
- return result;
212
- }, [content]);
213
-
214
- /**
215
- * Reset to template default
216
- */
217
- const reset = useCallback((): void => {
218
- const generated = generateSpecFromTemplate(template);
219
- setContentState(generated);
220
- setSavedContent(generated);
221
- setValidation(null);
222
- setLastSaved(null);
223
-
224
- // Clear from storage
225
- try {
226
- localStorage.removeItem(`${SPEC_STORAGE_KEY}-${templateId}`);
227
- } catch {
228
- // Ignore storage errors
229
- }
230
- }, [templateId]);
231
-
232
- return {
233
- content,
234
- loading,
235
- isDirty: content !== savedContent,
236
- validation,
237
- setContent,
238
- save,
239
- validate,
240
- reset,
241
- lastSaved,
242
- };
54
+ const { template } = useTemplateRuntime();
55
+ const [content, setContentState] = useState<string>('');
56
+ const [savedContent, setSavedContent] = useState<string>('');
57
+ const [loading, setLoading] = useState(true);
58
+ const [validation, setValidation] = useState<SpecValidationResult | null>(
59
+ null
60
+ );
61
+ const [lastSaved, setLastSaved] = useState<string | null>(null);
62
+
63
+ // Load spec content from storage on mount
64
+ useEffect(() => {
65
+ setLoading(true);
66
+ try {
67
+ const stored = localStorage.getItem(`${SPEC_STORAGE_KEY}-${templateId}`);
68
+ if (stored) {
69
+ const parsed = JSON.parse(stored) as {
70
+ content: string;
71
+ savedAt: string;
72
+ };
73
+ if (parsed.content) {
74
+ setContentState(parsed.content);
75
+ setSavedContent(parsed.content);
76
+ setLastSaved(parsed.savedAt);
77
+ } else {
78
+ // Invalid stored state, generate from template
79
+ const generated = generateSpecFromTemplate(template);
80
+ setContentState(generated);
81
+ setSavedContent(generated);
82
+ }
83
+ } else {
84
+ // No stored state, generate from template
85
+ const generated = generateSpecFromTemplate(template);
86
+ setContentState(generated);
87
+ setSavedContent(generated);
88
+ }
89
+ } catch {
90
+ // On error, generate from template
91
+ const generated = generateSpecFromTemplate(template);
92
+ setContentState(generated);
93
+ setSavedContent(generated);
94
+ }
95
+ setLoading(false);
96
+ }, [templateId]);
97
+
98
+ /**
99
+ * Update spec content (in-memory only until save)
100
+ */
101
+ const setContent = useCallback((newContent: string): void => {
102
+ setContentState(newContent);
103
+ // Clear validation when content changes
104
+ setValidation(null);
105
+ }, []);
106
+
107
+ /**
108
+ * Save spec content to storage
109
+ */
110
+ const save = useCallback((): void => {
111
+ try {
112
+ const savedAt = new Date().toISOString();
113
+ localStorage.setItem(
114
+ `${SPEC_STORAGE_KEY}-${templateId}`,
115
+ JSON.stringify({
116
+ content,
117
+ savedAt,
118
+ })
119
+ );
120
+ setSavedContent(content);
121
+ setLastSaved(savedAt);
122
+ } catch {
123
+ // Ignore storage errors
124
+ }
125
+ }, [content, templateId]);
126
+
127
+ /**
128
+ * Validate spec content
129
+ * Performs basic syntax validation
130
+ */
131
+ const validate = useCallback((): SpecValidationResult => {
132
+ const errors: SpecValidationResult['errors'] = [];
133
+
134
+ // Basic validation rules
135
+ const lines = content.split('\n');
136
+
137
+ // Check for contractSpec function call
138
+ if (!content.includes('contractSpec(')) {
139
+ errors.push({
140
+ line: 1,
141
+ message: 'Spec must contain a contractSpec() definition',
142
+ severity: 'error',
143
+ });
144
+ }
145
+
146
+ // Check for required fields
147
+ if (!content.includes('goal:')) {
148
+ errors.push({
149
+ line: 1,
150
+ message: 'Spec should have a goal field',
151
+ severity: 'warning',
152
+ });
153
+ }
154
+
155
+ if (!content.includes('io:')) {
156
+ errors.push({
157
+ line: 1,
158
+ message: 'Spec should define io (input/output)',
159
+ severity: 'warning',
160
+ });
161
+ }
162
+
163
+ // Check for balanced braces
164
+ const openBraces = (content.match(/{/g) ?? []).length;
165
+ const closeBraces = (content.match(/}/g) ?? []).length;
166
+ if (openBraces !== closeBraces) {
167
+ errors.push({
168
+ line: lines.length,
169
+ message: `Unbalanced braces: ${openBraces} opening, ${closeBraces} closing`,
170
+ severity: 'error',
171
+ });
172
+ }
173
+
174
+ // Check for balanced parentheses
175
+ const openParens = (content.match(/\(/g) ?? []).length;
176
+ const closeParens = (content.match(/\)/g) ?? []).length;
177
+ if (openParens !== closeParens) {
178
+ errors.push({
179
+ line: lines.length,
180
+ message: `Unbalanced parentheses: ${openParens} opening, ${closeParens} closing`,
181
+ severity: 'error',
182
+ });
183
+ }
184
+
185
+ // Check for unclosed strings (basic check)
186
+ lines.forEach((line, index) => {
187
+ const singleQuotes = (line.match(/'/g) ?? []).length;
188
+ const doubleQuotes = (line.match(/"/g) ?? []).length;
189
+ if (singleQuotes % 2 !== 0) {
190
+ errors.push({
191
+ line: index + 1,
192
+ message: 'Unclosed single quote',
193
+ severity: 'error',
194
+ });
195
+ }
196
+ if (doubleQuotes % 2 !== 0) {
197
+ errors.push({
198
+ line: index + 1,
199
+ message: 'Unclosed double quote',
200
+ severity: 'error',
201
+ });
202
+ }
203
+ });
204
+
205
+ const result: SpecValidationResult = {
206
+ valid: errors.filter((e) => e.severity === 'error').length === 0,
207
+ errors,
208
+ };
209
+
210
+ setValidation(result);
211
+ return result;
212
+ }, [content]);
213
+
214
+ /**
215
+ * Reset to template default
216
+ */
217
+ const reset = useCallback((): void => {
218
+ const generated = generateSpecFromTemplate(template);
219
+ setContentState(generated);
220
+ setSavedContent(generated);
221
+ setValidation(null);
222
+ setLastSaved(null);
223
+
224
+ // Clear from storage
225
+ try {
226
+ localStorage.removeItem(`${SPEC_STORAGE_KEY}-${templateId}`);
227
+ } catch {
228
+ // Ignore storage errors
229
+ }
230
+ }, [templateId]);
231
+
232
+ return {
233
+ content,
234
+ loading,
235
+ isDirty: content !== savedContent,
236
+ validation,
237
+ setContent,
238
+ save,
239
+ validate,
240
+ reset,
241
+ lastSaved,
242
+ };
243
243
  }