@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.
- package/.turbo/turbo-build.log +90 -84
- package/AGENTS.md +43 -25
- package/CHANGELOG.md +24 -0
- package/README.md +63 -35
- package/dist/EvolutionDashboard.js +9 -9
- package/dist/EvolutionSidebar.js +15 -15
- package/dist/LocalDataIndicator.js +3 -3
- package/dist/MarkdownView.d.ts +0 -7
- package/dist/MarkdownView.js +76 -172
- package/dist/PersonalizationInsights.js +12 -12
- package/dist/SaveToStudioButton.js +2 -2
- package/dist/SpecDrivenTemplateShell.d.ts +1 -1
- package/dist/SpecDrivenTemplateShell.js +10 -10
- package/dist/SpecEditorPanel.js +3 -3
- package/dist/TemplateShell.js +10 -10
- package/dist/browser/EvolutionDashboard.js +9 -9
- package/dist/browser/EvolutionSidebar.js +15 -15
- package/dist/browser/LocalDataIndicator.js +3 -3
- package/dist/browser/MarkdownView.js +76 -172
- package/dist/browser/PersonalizationInsights.js +12 -12
- package/dist/browser/SaveToStudioButton.js +2 -2
- package/dist/browser/SpecDrivenTemplateShell.js +10 -10
- package/dist/browser/SpecEditorPanel.js +3 -3
- package/dist/browser/TemplateShell.js +10 -10
- package/dist/browser/hooks/index.js +29 -29
- package/dist/browser/index.js +193 -286
- package/dist/browser/lib/component-registry.js +1 -1
- package/dist/browser/markdown/formatPresentationName.js +9 -0
- package/dist/browser/markdown/useMarkdownPresentation.js +65 -0
- package/dist/hooks/index.d.ts +3 -3
- package/dist/hooks/index.js +29 -29
- package/dist/index.d.ts +12 -11
- package/dist/index.js +193 -286
- package/dist/lib/component-registry.js +1 -1
- package/dist/markdown/formatPresentationName.d.ts +1 -0
- package/dist/markdown/formatPresentationName.js +10 -0
- package/dist/markdown/useMarkdownPresentation.d.ts +21 -0
- package/dist/markdown/useMarkdownPresentation.js +66 -0
- package/dist/node/EvolutionDashboard.js +9 -9
- package/dist/node/EvolutionSidebar.js +15 -15
- package/dist/node/LocalDataIndicator.js +3 -3
- package/dist/node/MarkdownView.js +76 -172
- package/dist/node/PersonalizationInsights.js +12 -12
- package/dist/node/SaveToStudioButton.js +2 -2
- package/dist/node/SpecDrivenTemplateShell.js +10 -10
- package/dist/node/SpecEditorPanel.js +3 -3
- package/dist/node/TemplateShell.js +10 -10
- package/dist/node/hooks/index.js +29 -29
- package/dist/node/index.js +193 -286
- package/dist/node/lib/component-registry.js +1 -1
- package/dist/node/markdown/formatPresentationName.js +9 -0
- package/dist/node/markdown/useMarkdownPresentation.js +65 -0
- package/dist/utils/index.d.ts +1 -1
- package/package.json +40 -13
- package/src/EvolutionDashboard.tsx +415 -415
- package/src/EvolutionSidebar.tsx +245 -245
- package/src/LocalDataIndicator.tsx +28 -28
- package/src/MarkdownView.tsx +119 -372
- package/src/OverlayContextProvider.tsx +272 -272
- package/src/PersonalizationInsights.tsx +232 -232
- package/src/SaveToStudioButton.tsx +51 -51
- package/src/SpecDrivenTemplateShell.tsx +59 -59
- package/src/SpecEditorPanel.tsx +138 -138
- package/src/TemplateShell.tsx +50 -50
- package/src/bundles/ExampleTemplateBundle.ts +78 -78
- package/src/hooks/index.ts +3 -3
- package/src/hooks/useBehaviorTracking.ts +252 -252
- package/src/hooks/useEvolution.ts +437 -437
- package/src/hooks/useRegistryTemplates.ts +42 -42
- package/src/hooks/useSpecContent.ts +214 -214
- package/src/hooks/useWorkflowComposer.ts +567 -567
- package/src/index.ts +12 -11
- package/src/lib/component-registry.tsx +40 -40
- package/src/lib/runtime-context.tsx +31 -31
- package/src/lib/types.ts +57 -57
- package/src/markdown/formatPresentationName.ts +9 -0
- package/src/markdown/useMarkdownPresentation.ts +107 -0
- package/src/overlay-types.ts +15 -15
- package/src/utils/fetchPresentationData.ts +13 -13
- package/src/utils/generateSpecFromTemplate.ts +29 -29
- package/src/utils/index.ts +1 -1
- package/tsconfig.json +8 -8
package/src/EvolutionSidebar.tsx
CHANGED
|
@@ -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
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
templateId,
|
|
29
|
+
expanded = false,
|
|
30
|
+
onToggle,
|
|
31
|
+
onLog,
|
|
32
|
+
onOpenEvolution,
|
|
33
33
|
}: EvolutionSidebarProps) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
const {
|
|
35
|
+
anomalies,
|
|
36
|
+
suggestions,
|
|
37
|
+
loading,
|
|
38
|
+
approveSuggestion,
|
|
39
|
+
rejectSuggestion,
|
|
40
|
+
operationCount,
|
|
41
|
+
} = useEvolution(templateId);
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
const pendingSuggestions = useMemo(
|
|
44
|
+
() => suggestions.filter((s) => s.status === 'pending'),
|
|
45
|
+
[suggestions]
|
|
46
|
+
);
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
{loading && (
|
|
148
|
+
<div className="py-4 text-center text-muted-foreground text-sm">
|
|
149
|
+
Generating suggestions...
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
suggestion,
|
|
242
|
+
onApprove,
|
|
243
|
+
onReject,
|
|
244
244
|
}: {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
245
|
+
suggestion: SpecSuggestion;
|
|
246
|
+
onApprove: (id: string) => void;
|
|
247
|
+
onReject: (id: string) => void;
|
|
248
248
|
}) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
10
|
-
|
|
9
|
+
const { projectId, templateId, template, installer } = useTemplateRuntime();
|
|
10
|
+
const [isResetting, setIsResetting] = useState(false);
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
}
|