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

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 (81) hide show
  1. package/.turbo/turbo-build.log +90 -84
  2. package/AGENTS.md +43 -25
  3. package/README.md +63 -35
  4. package/dist/EvolutionDashboard.js +9 -9
  5. package/dist/EvolutionSidebar.js +15 -15
  6. package/dist/LocalDataIndicator.js +3 -3
  7. package/dist/MarkdownView.d.ts +0 -7
  8. package/dist/MarkdownView.js +76 -172
  9. package/dist/PersonalizationInsights.js +12 -12
  10. package/dist/SaveToStudioButton.js +2 -2
  11. package/dist/SpecDrivenTemplateShell.d.ts +1 -1
  12. package/dist/SpecDrivenTemplateShell.js +10 -10
  13. package/dist/SpecEditorPanel.js +3 -3
  14. package/dist/TemplateShell.js +10 -10
  15. package/dist/browser/EvolutionDashboard.js +9 -9
  16. package/dist/browser/EvolutionSidebar.js +15 -15
  17. package/dist/browser/LocalDataIndicator.js +3 -3
  18. package/dist/browser/MarkdownView.js +76 -172
  19. package/dist/browser/PersonalizationInsights.js +12 -12
  20. package/dist/browser/SaveToStudioButton.js +2 -2
  21. package/dist/browser/SpecDrivenTemplateShell.js +10 -10
  22. package/dist/browser/SpecEditorPanel.js +3 -3
  23. package/dist/browser/TemplateShell.js +10 -10
  24. package/dist/browser/hooks/index.js +29 -29
  25. package/dist/browser/index.js +193 -286
  26. package/dist/browser/lib/component-registry.js +1 -1
  27. package/dist/browser/markdown/formatPresentationName.js +9 -0
  28. package/dist/browser/markdown/useMarkdownPresentation.js +65 -0
  29. package/dist/hooks/index.d.ts +3 -3
  30. package/dist/hooks/index.js +29 -29
  31. package/dist/index.d.ts +12 -11
  32. package/dist/index.js +193 -286
  33. package/dist/lib/component-registry.js +1 -1
  34. package/dist/markdown/formatPresentationName.d.ts +1 -0
  35. package/dist/markdown/formatPresentationName.js +10 -0
  36. package/dist/markdown/useMarkdownPresentation.d.ts +21 -0
  37. package/dist/markdown/useMarkdownPresentation.js +66 -0
  38. package/dist/node/EvolutionDashboard.js +9 -9
  39. package/dist/node/EvolutionSidebar.js +15 -15
  40. package/dist/node/LocalDataIndicator.js +3 -3
  41. package/dist/node/MarkdownView.js +76 -172
  42. package/dist/node/PersonalizationInsights.js +12 -12
  43. package/dist/node/SaveToStudioButton.js +2 -2
  44. package/dist/node/SpecDrivenTemplateShell.js +10 -10
  45. package/dist/node/SpecEditorPanel.js +3 -3
  46. package/dist/node/TemplateShell.js +10 -10
  47. package/dist/node/hooks/index.js +29 -29
  48. package/dist/node/index.js +193 -286
  49. package/dist/node/lib/component-registry.js +1 -1
  50. package/dist/node/markdown/formatPresentationName.js +9 -0
  51. package/dist/node/markdown/useMarkdownPresentation.js +65 -0
  52. package/dist/utils/index.d.ts +1 -1
  53. package/package.json +38 -11
  54. package/src/EvolutionDashboard.tsx +415 -415
  55. package/src/EvolutionSidebar.tsx +245 -245
  56. package/src/LocalDataIndicator.tsx +28 -28
  57. package/src/MarkdownView.tsx +119 -372
  58. package/src/OverlayContextProvider.tsx +272 -272
  59. package/src/PersonalizationInsights.tsx +232 -232
  60. package/src/SaveToStudioButton.tsx +51 -51
  61. package/src/SpecDrivenTemplateShell.tsx +59 -59
  62. package/src/SpecEditorPanel.tsx +138 -138
  63. package/src/TemplateShell.tsx +50 -50
  64. package/src/bundles/ExampleTemplateBundle.ts +78 -78
  65. package/src/hooks/index.ts +3 -3
  66. package/src/hooks/useBehaviorTracking.ts +252 -252
  67. package/src/hooks/useEvolution.ts +437 -437
  68. package/src/hooks/useRegistryTemplates.ts +42 -42
  69. package/src/hooks/useSpecContent.ts +214 -214
  70. package/src/hooks/useWorkflowComposer.ts +567 -567
  71. package/src/index.ts +12 -11
  72. package/src/lib/component-registry.tsx +40 -40
  73. package/src/lib/runtime-context.tsx +31 -31
  74. package/src/lib/types.ts +57 -57
  75. package/src/markdown/formatPresentationName.ts +9 -0
  76. package/src/markdown/useMarkdownPresentation.ts +107 -0
  77. package/src/overlay-types.ts +15 -15
  78. package/src/utils/fetchPresentationData.ts +13 -13
  79. package/src/utils/generateSpecFromTemplate.ts +29 -29
  80. package/src/utils/index.ts +1 -1
  81. 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
  }