@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,22 +1,22 @@
1
1
  'use client';
2
2
 
3
- import { useCallback, useMemo } from 'react';
4
3
  import { Button } from '@contractspec/lib.design-system';
5
- import { Card } from '@contractspec/lib.ui-kit-web/ui/card';
6
4
  import { Badge } from '@contractspec/lib.ui-kit-web/ui/badge';
7
- import type { TemplateId } from './lib/types';
5
+ import { Card } from '@contractspec/lib.ui-kit-web/ui/card';
6
+ import { useCallback, useMemo } from 'react';
8
7
  import { type SpecSuggestion, useEvolution } from './hooks/useEvolution';
8
+ import type { TemplateId } from './lib/types';
9
9
 
10
10
  export interface EvolutionSidebarProps {
11
- templateId: TemplateId;
12
- /** Whether sidebar is expanded */
13
- expanded?: boolean;
14
- /** Toggle expanded state */
15
- onToggle?: () => void;
16
- /** Callback for logging actions */
17
- onLog?: (message: string) => void;
18
- /** Navigate to full evolution mode */
19
- onOpenEvolution?: () => void;
11
+ templateId: TemplateId;
12
+ /** Whether sidebar is expanded */
13
+ expanded?: boolean;
14
+ /** Toggle expanded state */
15
+ onToggle?: () => void;
16
+ /** Callback for logging actions */
17
+ onLog?: (message: string) => void;
18
+ /** Navigate to full evolution mode */
19
+ onOpenEvolution?: () => void;
20
20
  }
21
21
 
22
22
  /**
@@ -25,258 +25,258 @@ export interface EvolutionSidebarProps {
25
25
  * Collapsible by default.
26
26
  */
27
27
  export function EvolutionSidebar({
28
- templateId,
29
- expanded = false,
30
- onToggle,
31
- onLog,
32
- onOpenEvolution,
28
+ templateId,
29
+ expanded = false,
30
+ onToggle,
31
+ onLog,
32
+ onOpenEvolution,
33
33
  }: EvolutionSidebarProps) {
34
- const {
35
- anomalies,
36
- suggestions,
37
- loading,
38
- approveSuggestion,
39
- rejectSuggestion,
40
- operationCount,
41
- } = useEvolution(templateId);
34
+ const {
35
+ anomalies,
36
+ suggestions,
37
+ loading,
38
+ approveSuggestion,
39
+ rejectSuggestion,
40
+ operationCount,
41
+ } = useEvolution(templateId);
42
42
 
43
- const pendingSuggestions = useMemo(
44
- () => suggestions.filter((s) => s.status === 'pending'),
45
- [suggestions]
46
- );
43
+ const pendingSuggestions = useMemo(
44
+ () => suggestions.filter((s) => s.status === 'pending'),
45
+ [suggestions]
46
+ );
47
47
 
48
- const topAnomalies = useMemo(
49
- () =>
50
- anomalies
51
- .sort((a, b) => {
52
- const severityOrder = { high: 0, medium: 1, low: 2 };
53
- return severityOrder[a.severity] - severityOrder[b.severity];
54
- })
55
- .slice(0, 3),
56
- [anomalies]
57
- );
48
+ const topAnomalies = useMemo(
49
+ () =>
50
+ anomalies
51
+ .sort((a, b) => {
52
+ const severityOrder = { high: 0, medium: 1, low: 2 };
53
+ return severityOrder[a.severity] - severityOrder[b.severity];
54
+ })
55
+ .slice(0, 3),
56
+ [anomalies]
57
+ );
58
58
 
59
- const handleApprove = useCallback(
60
- (id: string) => {
61
- approveSuggestion(id);
62
- onLog?.(`Approved suggestion ${id.slice(0, 8)}`);
63
- },
64
- [approveSuggestion, onLog]
65
- );
59
+ const handleApprove = useCallback(
60
+ (id: string) => {
61
+ approveSuggestion(id);
62
+ onLog?.(`Approved suggestion ${id.slice(0, 8)}`);
63
+ },
64
+ [approveSuggestion, onLog]
65
+ );
66
66
 
67
- const handleReject = useCallback(
68
- (id: string) => {
69
- rejectSuggestion(id);
70
- onLog?.(`Rejected suggestion ${id.slice(0, 8)}`);
71
- },
72
- [rejectSuggestion, onLog]
73
- );
67
+ const handleReject = useCallback(
68
+ (id: string) => {
69
+ rejectSuggestion(id);
70
+ onLog?.(`Rejected suggestion ${id.slice(0, 8)}`);
71
+ },
72
+ [rejectSuggestion, onLog]
73
+ );
74
74
 
75
- // Collapsed view - just show badge
76
- if (!expanded) {
77
- return (
78
- <button
79
- onClick={onToggle}
80
- 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"
81
- type="button"
82
- >
83
- <span>🤖</span>
84
- <span>Evolution</span>
85
- {pendingSuggestions.length > 0 && (
86
- <Badge
87
- variant="secondary"
88
- className="border-amber-500/30 bg-amber-500/20 text-amber-400"
89
- >
90
- {pendingSuggestions.length}
91
- </Badge>
92
- )}
93
- {anomalies.length > 0 && pendingSuggestions.length === 0 && (
94
- <Badge variant="destructive">{anomalies.length}</Badge>
95
- )}
96
- </button>
97
- );
98
- }
75
+ // Collapsed view - just show badge
76
+ if (!expanded) {
77
+ return (
78
+ <button
79
+ onClick={onToggle}
80
+ 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"
81
+ type="button"
82
+ >
83
+ <span>🤖</span>
84
+ <span>Evolution</span>
85
+ {pendingSuggestions.length > 0 && (
86
+ <Badge
87
+ variant="secondary"
88
+ className="border-amber-500/30 bg-amber-500/20 text-amber-400"
89
+ >
90
+ {pendingSuggestions.length}
91
+ </Badge>
92
+ )}
93
+ {anomalies.length > 0 && pendingSuggestions.length === 0 && (
94
+ <Badge variant="destructive">{anomalies.length}</Badge>
95
+ )}
96
+ </button>
97
+ );
98
+ }
99
99
 
100
- // Expanded view
101
- return (
102
- <Card className="w-80 overflow-hidden">
103
- {/* Header */}
104
- <div className="flex items-center justify-between border-b border-violet-500/20 bg-violet-500/5 px-3 py-2">
105
- <div className="flex items-center gap-2">
106
- <span>🤖</span>
107
- <span className="text-sm font-semibold">Evolution</span>
108
- </div>
109
- <div className="flex items-center gap-1">
110
- {onOpenEvolution && (
111
- <Button variant="ghost" size="sm" onPress={onOpenEvolution}>
112
- Expand
113
- </Button>
114
- )}
115
- <button
116
- onClick={onToggle}
117
- className="text-muted-foreground hover:text-foreground p-1"
118
- type="button"
119
- title="Collapse"
120
- >
121
-
122
- </button>
123
- </div>
124
- </div>
100
+ // Expanded view
101
+ return (
102
+ <Card className="w-80 overflow-hidden">
103
+ {/* Header */}
104
+ <div className="flex items-center justify-between border-violet-500/20 border-b bg-violet-500/5 px-3 py-2">
105
+ <div className="flex items-center gap-2">
106
+ <span>🤖</span>
107
+ <span className="font-semibold text-sm">Evolution</span>
108
+ </div>
109
+ <div className="flex items-center gap-1">
110
+ {onOpenEvolution && (
111
+ <Button variant="ghost" size="sm" onPress={onOpenEvolution}>
112
+ Expand
113
+ </Button>
114
+ )}
115
+ <button
116
+ onClick={onToggle}
117
+ className="p-1 text-muted-foreground hover:text-foreground"
118
+ type="button"
119
+ title="Collapse"
120
+ >
121
+
122
+ </button>
123
+ </div>
124
+ </div>
125
125
 
126
- <div className="max-h-96 overflow-y-auto p-3">
127
- {/* Stats */}
128
- <div className="mb-3 flex items-center justify-between text-xs">
129
- <span className="text-muted-foreground">
130
- {operationCount} ops tracked
131
- </span>
132
- <div className="flex items-center gap-2">
133
- {anomalies.length > 0 && (
134
- <Badge variant="destructive">{anomalies.length} anomalies</Badge>
135
- )}
136
- {pendingSuggestions.length > 0 && (
137
- <Badge
138
- variant="secondary"
139
- className="border-amber-500/30 bg-amber-500/20 text-amber-400"
140
- >
141
- {pendingSuggestions.length} pending
142
- </Badge>
143
- )}
144
- </div>
145
- </div>
126
+ <div className="max-h-96 overflow-y-auto p-3">
127
+ {/* Stats */}
128
+ <div className="mb-3 flex items-center justify-between text-xs">
129
+ <span className="text-muted-foreground">
130
+ {operationCount} ops tracked
131
+ </span>
132
+ <div className="flex items-center gap-2">
133
+ {anomalies.length > 0 && (
134
+ <Badge variant="destructive">{anomalies.length} anomalies</Badge>
135
+ )}
136
+ {pendingSuggestions.length > 0 && (
137
+ <Badge
138
+ variant="secondary"
139
+ className="border-amber-500/30 bg-amber-500/20 text-amber-400"
140
+ >
141
+ {pendingSuggestions.length} pending
142
+ </Badge>
143
+ )}
144
+ </div>
145
+ </div>
146
146
 
147
- {loading && (
148
- <div className="text-muted-foreground py-4 text-center text-sm">
149
- Generating suggestions...
150
- </div>
151
- )}
147
+ {loading && (
148
+ <div className="py-4 text-center text-muted-foreground text-sm">
149
+ Generating suggestions...
150
+ </div>
151
+ )}
152
152
 
153
- {/* Top Anomalies */}
154
- {topAnomalies.length > 0 && (
155
- <div className="mb-4">
156
- <p className="mb-2 text-xs font-semibold text-violet-400 uppercase">
157
- Top Issues
158
- </p>
159
- <div className="space-y-2">
160
- {topAnomalies.map((anomaly, index) => (
161
- <div
162
- key={`${anomaly.operation.name}-${index}`}
163
- className="rounded border border-amber-500/20 bg-amber-500/5 p-2 text-xs"
164
- >
165
- <div className="flex items-center gap-2">
166
- <span>
167
- {anomaly.severity === 'high'
168
- ? '🔴'
169
- : anomaly.severity === 'medium'
170
- ? '🟠'
171
- : '🟡'}
172
- </span>
173
- <span className="truncate font-medium">
174
- {anomaly.operation.name}
175
- </span>
176
- </div>
177
- <p className="text-muted-foreground mt-1 truncate">
178
- {anomaly.description}
179
- </p>
180
- </div>
181
- ))}
182
- </div>
183
- </div>
184
- )}
153
+ {/* Top Anomalies */}
154
+ {topAnomalies.length > 0 && (
155
+ <div className="mb-4">
156
+ <p className="mb-2 font-semibold text-violet-400 text-xs uppercase">
157
+ Top Issues
158
+ </p>
159
+ <div className="space-y-2">
160
+ {topAnomalies.map((anomaly, index) => (
161
+ <div
162
+ key={`${anomaly.operation.name}-${index}`}
163
+ className="rounded border border-amber-500/20 bg-amber-500/5 p-2 text-xs"
164
+ >
165
+ <div className="flex items-center gap-2">
166
+ <span>
167
+ {anomaly.severity === 'high'
168
+ ? '🔴'
169
+ : anomaly.severity === 'medium'
170
+ ? '🟠'
171
+ : '🟡'}
172
+ </span>
173
+ <span className="truncate font-medium">
174
+ {anomaly.operation.name}
175
+ </span>
176
+ </div>
177
+ <p className="mt-1 truncate text-muted-foreground">
178
+ {anomaly.description}
179
+ </p>
180
+ </div>
181
+ ))}
182
+ </div>
183
+ </div>
184
+ )}
185
185
 
186
- {/* Pending Suggestions */}
187
- {pendingSuggestions.length > 0 && (
188
- <div>
189
- <p className="mb-2 text-xs font-semibold text-violet-400 uppercase">
190
- Pending Suggestions
191
- </p>
192
- <div className="space-y-2">
193
- {pendingSuggestions.slice(0, 3).map((suggestion) => (
194
- <CompactSuggestionCard
195
- key={suggestion.id}
196
- suggestion={suggestion}
197
- onApprove={handleApprove}
198
- onReject={handleReject}
199
- />
200
- ))}
201
- {pendingSuggestions.length > 3 && (
202
- <p className="text-muted-foreground text-center text-xs">
203
- +{pendingSuggestions.length - 3} more suggestions
204
- </p>
205
- )}
206
- </div>
207
- </div>
208
- )}
186
+ {/* Pending Suggestions */}
187
+ {pendingSuggestions.length > 0 && (
188
+ <div>
189
+ <p className="mb-2 font-semibold text-violet-400 text-xs uppercase">
190
+ Pending Suggestions
191
+ </p>
192
+ <div className="space-y-2">
193
+ {pendingSuggestions.slice(0, 3).map((suggestion) => (
194
+ <CompactSuggestionCard
195
+ key={suggestion.id}
196
+ suggestion={suggestion}
197
+ onApprove={handleApprove}
198
+ onReject={handleReject}
199
+ />
200
+ ))}
201
+ {pendingSuggestions.length > 3 && (
202
+ <p className="text-center text-muted-foreground text-xs">
203
+ +{pendingSuggestions.length - 3} more suggestions
204
+ </p>
205
+ )}
206
+ </div>
207
+ </div>
208
+ )}
209
209
 
210
- {/* Empty State */}
211
- {anomalies.length === 0 &&
212
- pendingSuggestions.length === 0 &&
213
- !loading && (
214
- <div className="text-muted-foreground py-4 text-center text-xs">
215
- No issues detected. Keep coding!
216
- </div>
217
- )}
218
- </div>
210
+ {/* Empty State */}
211
+ {anomalies.length === 0 &&
212
+ pendingSuggestions.length === 0 &&
213
+ !loading && (
214
+ <div className="py-4 text-center text-muted-foreground text-xs">
215
+ No issues detected. Keep coding!
216
+ </div>
217
+ )}
218
+ </div>
219
219
 
220
- {/* Footer */}
221
- {onOpenEvolution && (
222
- <div className="border-t border-violet-500/20 p-2">
223
- <Button
224
- variant="ghost"
225
- size="sm"
226
- className="w-full"
227
- onPress={onOpenEvolution}
228
- >
229
- Open Evolution Dashboard →
230
- </Button>
231
- </div>
232
- )}
233
- </Card>
234
- );
220
+ {/* Footer */}
221
+ {onOpenEvolution && (
222
+ <div className="border-violet-500/20 border-t p-2">
223
+ <Button
224
+ variant="ghost"
225
+ size="sm"
226
+ className="w-full"
227
+ onPress={onOpenEvolution}
228
+ >
229
+ Open Evolution Dashboard →
230
+ </Button>
231
+ </div>
232
+ )}
233
+ </Card>
234
+ );
235
235
  }
236
236
 
237
237
  /**
238
238
  * Compact suggestion card for sidebar
239
239
  */
240
240
  function CompactSuggestionCard({
241
- suggestion,
242
- onApprove,
243
- onReject,
241
+ suggestion,
242
+ onApprove,
243
+ onReject,
244
244
  }: {
245
- suggestion: SpecSuggestion;
246
- onApprove: (id: string) => void;
247
- onReject: (id: string) => void;
245
+ suggestion: SpecSuggestion;
246
+ onApprove: (id: string) => void;
247
+ onReject: (id: string) => void;
248
248
  }) {
249
- return (
250
- <div className="rounded border border-violet-500/20 bg-violet-500/5 p-2">
251
- <div className="flex items-start justify-between gap-2">
252
- <div className="min-w-0 flex-1">
253
- <p className="truncate text-xs font-medium">
254
- {suggestion.proposal.summary}
255
- </p>
256
- <div className="mt-1 flex items-center gap-2 text-xs">
257
- <Badge variant="secondary">{suggestion.priority}</Badge>
258
- <span className="text-muted-foreground">
259
- {(suggestion.confidence * 100).toFixed(0)}%
260
- </span>
261
- </div>
262
- </div>
263
- </div>
264
- <div className="mt-2 flex justify-end gap-1">
265
- <button
266
- onClick={() => onReject(suggestion.id)}
267
- className="rounded px-2 py-0.5 text-xs text-red-400 hover:bg-red-400/10"
268
- type="button"
269
- >
270
- Reject
271
- </button>
272
- <button
273
- onClick={() => onApprove(suggestion.id)}
274
- className="rounded bg-violet-500/20 px-2 py-0.5 text-xs text-violet-400 hover:bg-violet-500/30"
275
- type="button"
276
- >
277
- Approve
278
- </button>
279
- </div>
280
- </div>
281
- );
249
+ return (
250
+ <div className="rounded border border-violet-500/20 bg-violet-500/5 p-2">
251
+ <div className="flex items-start justify-between gap-2">
252
+ <div className="min-w-0 flex-1">
253
+ <p className="truncate font-medium text-xs">
254
+ {suggestion.proposal.summary}
255
+ </p>
256
+ <div className="mt-1 flex items-center gap-2 text-xs">
257
+ <Badge variant="secondary">{suggestion.priority}</Badge>
258
+ <span className="text-muted-foreground">
259
+ {(suggestion.confidence * 100).toFixed(0)}%
260
+ </span>
261
+ </div>
262
+ </div>
263
+ </div>
264
+ <div className="mt-2 flex justify-end gap-1">
265
+ <button
266
+ onClick={() => onReject(suggestion.id)}
267
+ className="rounded px-2 py-0.5 text-red-400 text-xs hover:bg-red-400/10"
268
+ type="button"
269
+ >
270
+ Reject
271
+ </button>
272
+ <button
273
+ onClick={() => onApprove(suggestion.id)}
274
+ className="rounded bg-violet-500/20 px-2 py-0.5 text-violet-400 text-xs hover:bg-violet-500/30"
275
+ type="button"
276
+ >
277
+ Approve
278
+ </button>
279
+ </div>
280
+ </div>
281
+ );
282
282
  }
@@ -6,34 +6,34 @@ import { useState } from 'react';
6
6
  import { useTemplateRuntime } from './lib/runtime-context';
7
7
 
8
8
  export function LocalDataIndicator() {
9
- const { projectId, templateId, template, installer } = useTemplateRuntime();
10
- const [isResetting, setIsResetting] = useState(false);
9
+ const { projectId, templateId, template, installer } = useTemplateRuntime();
10
+ const [isResetting, setIsResetting] = useState(false);
11
11
 
12
- const handleReset = async () => {
13
- setIsResetting(true);
14
- try {
15
- await installer.install(templateId, { projectId });
16
- } finally {
17
- setIsResetting(false);
18
- }
19
- };
12
+ const handleReset = async () => {
13
+ setIsResetting(true);
14
+ try {
15
+ await installer.install(templateId, { projectId });
16
+ } finally {
17
+ setIsResetting(false);
18
+ }
19
+ };
20
20
 
21
- return (
22
- <div className="border-border bg-muted/40 text-muted-foreground inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs">
23
- <Shield className="h-3.5 w-3.5 text-violet-400" />
24
- <span>
25
- Local runtime ·{' '}
26
- <span className="text-foreground font-semibold">{template.name}</span>
27
- </span>
28
- <button
29
- type="button"
30
- 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"
31
- onClick={handleReset}
32
- disabled={isResetting}
33
- >
34
- <RefreshCw className="h-3 w-3" />
35
- {isResetting ? 'Resetting…' : 'Reset data'}
36
- </button>
37
- </div>
38
- );
21
+ return (
22
+ <div className="inline-flex items-center gap-2 rounded-full border border-border bg-muted/40 px-3 py-1 text-muted-foreground text-xs">
23
+ <Shield className="h-3.5 w-3.5 text-violet-400" />
24
+ <span>
25
+ Local runtime ·{' '}
26
+ <span className="font-semibold text-foreground">{template.name}</span>
27
+ </span>
28
+ <button
29
+ type="button"
30
+ className="inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 font-semibold text-[11px] text-muted-foreground hover:text-foreground"
31
+ onClick={handleReset}
32
+ disabled={isResetting}
33
+ >
34
+ <RefreshCw className="h-3 w-3" />
35
+ {isResetting ? 'Resetting…' : 'Reset data'}
36
+ </button>
37
+ </div>
38
+ );
39
39
  }