@contentful/experience-design-system-cli 2.6.2-dev-build-3f849e3.0 → 2.6.2-dev-build-864e323.0
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/dist/package.json +1 -1
- package/dist/src/analyze/extract/scoring.js +8 -4
- package/dist/src/analyze/select/tui/App.js +60 -37
- package/dist/src/analyze/select/tui/components/ComponentDetail.js +9 -3
- package/dist/src/analyze/select/tui/components/Sidebar.js +2 -25
- package/dist/src/analyze/tui/AnalyzeView.js +26 -8
- package/package.json +2 -2
package/dist/package.json
CHANGED
|
@@ -31,14 +31,18 @@ const OBVIOUS_PROP_NAMES = new Set([
|
|
|
31
31
|
'aria-label',
|
|
32
32
|
'aria-describedby',
|
|
33
33
|
]);
|
|
34
|
-
// A union is "wide" if it mixes
|
|
34
|
+
// A union is "wide" if it mixes 3+ distinct base primitive types (ignoring nullability modifiers).
|
|
35
|
+
// e.g. "string | number | boolean" → wide. "string | null | undefined" → NOT wide (just nullable string).
|
|
35
36
|
function isWidePrimitiveUnion(type) {
|
|
36
|
-
// e.g. "string | number | boolean" — three or more base primitives
|
|
37
37
|
const parts = type.split('|').map((p) => p.trim());
|
|
38
38
|
if (parts.length < 3)
|
|
39
39
|
return false;
|
|
40
|
-
const
|
|
41
|
-
|
|
40
|
+
const basePrimitives = new Set(['string', 'number', 'boolean']);
|
|
41
|
+
const nullability = new Set(['null', 'undefined']);
|
|
42
|
+
const baseCount = parts.filter((p) => basePrimitives.has(p)).length;
|
|
43
|
+
const nullabilityCount = parts.filter((p) => nullability.has(p)).length;
|
|
44
|
+
// Wide only when there are 3+ base primitives, or 2+ base primitives combined with nullability modifiers
|
|
45
|
+
return baseCount >= 3 || (baseCount >= 2 && nullabilityCount > 0 && baseCount + nullabilityCount >= 3);
|
|
42
46
|
}
|
|
43
47
|
export function computeExtractionScore(component) {
|
|
44
48
|
let confidence = 100;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { Box, Text, useStdout } from 'ink';
|
|
4
4
|
import { readFile } from 'node:fs/promises';
|
|
5
5
|
import { createReviewSessionDetail } from '../types.js';
|
|
@@ -40,6 +40,11 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
|
|
|
40
40
|
const [finalizedResult, setFinalizedResult] = useState(null);
|
|
41
41
|
const [sidebarScrollOffset, setSidebarScrollOffset] = useState(0);
|
|
42
42
|
const [jsonScrollOffset, setJsonScrollOffset] = useState(0);
|
|
43
|
+
// Keeps the visual sort order in sync for use inside keymap handlers (which close over a stale render)
|
|
44
|
+
const sortedIdsRef = useRef([]);
|
|
45
|
+
// Source code kept separate from session state so lazy loading never mutates
|
|
46
|
+
// session.components — that would invalidate useMemo(sessionSummary) and flash the sidebar
|
|
47
|
+
const [sourceCodeById, setSourceCodeById] = useState({});
|
|
43
48
|
const [previewAnnotations, setPreviewAnnotations] = useState(() => {
|
|
44
49
|
const raw = process.env['EDS_PREVIEW_ANNOTATIONS'];
|
|
45
50
|
if (!raw)
|
|
@@ -138,13 +143,22 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
|
|
|
138
143
|
clearTimeout(previewDebounceRef.current);
|
|
139
144
|
};
|
|
140
145
|
}, []);
|
|
141
|
-
// Sync loaded session into local state
|
|
146
|
+
// Sync loaded session into local state; selectedId is set to null here
|
|
147
|
+
// and will be corrected to the first sorted item on first render via the effect below
|
|
142
148
|
useEffect(() => {
|
|
143
149
|
if (loadedSession && !session) {
|
|
144
150
|
setSession(loadedSession);
|
|
145
|
-
setSelectedId(
|
|
151
|
+
setSelectedId(null);
|
|
146
152
|
}
|
|
147
153
|
}, [loadedSession]);
|
|
154
|
+
// Once the sort order is known (after first render), select the first sorted item.
|
|
155
|
+
// Runs only when session transitions from null → loaded.
|
|
156
|
+
const sessionLoaded = session !== null;
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
if (sessionLoaded && selectedId === null && sortedIdsRef.current.length > 0) {
|
|
159
|
+
setSelectedId(sortedIdsRef.current[0]);
|
|
160
|
+
}
|
|
161
|
+
}, [sessionLoaded]);
|
|
148
162
|
useEffect(() => {
|
|
149
163
|
if (session && !previewResponse) {
|
|
150
164
|
const raw = process.env['EDS_PREVIEW_COUNTS'];
|
|
@@ -172,26 +186,22 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
|
|
|
172
186
|
}
|
|
173
187
|
}
|
|
174
188
|
}, [session]);
|
|
175
|
-
// Lazy source code loading
|
|
189
|
+
// Lazy source code loading — writes to sourceCodeById, NOT session.components,
|
|
190
|
+
// so it doesn't invalidate useMemo(sessionSummary) and cause a sidebar flash
|
|
176
191
|
useEffect(() => {
|
|
177
192
|
if (!session || !selectedId)
|
|
178
193
|
return;
|
|
194
|
+
if (sourceCodeById[selectedId] !== undefined)
|
|
195
|
+
return; // already loaded
|
|
179
196
|
const selectedComponent = session.components.find((c) => c.id === selectedId);
|
|
180
|
-
if (!selectedComponent
|
|
197
|
+
if (!selectedComponent)
|
|
181
198
|
return;
|
|
182
199
|
readFile(selectedComponent.resolvedSourcePath, 'utf8')
|
|
183
200
|
.then((code) => {
|
|
184
|
-
|
|
185
|
-
if (!prev)
|
|
186
|
-
return prev;
|
|
187
|
-
return {
|
|
188
|
-
...prev,
|
|
189
|
-
components: prev.components.map((c) => (c.id === selectedComponent.id ? { ...c, sourceCode: code } : c)),
|
|
190
|
-
};
|
|
191
|
-
});
|
|
201
|
+
setSourceCodeById((prev) => ({ ...prev, [selectedId]: code }));
|
|
192
202
|
})
|
|
193
203
|
.catch(() => {
|
|
194
|
-
|
|
204
|
+
setSourceCodeById((prev) => ({ ...prev, [selectedId]: '' }));
|
|
195
205
|
});
|
|
196
206
|
}, [selectedId]);
|
|
197
207
|
// SIGINT handler
|
|
@@ -235,9 +245,10 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
|
|
|
235
245
|
onSidebarUp: () => {
|
|
236
246
|
if (!session)
|
|
237
247
|
return;
|
|
238
|
-
const
|
|
248
|
+
const ids = sortedIdsRef.current;
|
|
249
|
+
const idx = ids.indexOf(selectedId ?? '');
|
|
239
250
|
if (idx > 0) {
|
|
240
|
-
setSelectedId(
|
|
251
|
+
setSelectedId(ids[idx - 1]);
|
|
241
252
|
setJsonScrollOffset(0);
|
|
242
253
|
setSidebarScrollOffset((prev) => Math.min(prev, idx - 1));
|
|
243
254
|
}
|
|
@@ -245,9 +256,10 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
|
|
|
245
256
|
onSidebarDown: () => {
|
|
246
257
|
if (!session)
|
|
247
258
|
return;
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
259
|
+
const ids = sortedIdsRef.current;
|
|
260
|
+
const idx = ids.indexOf(selectedId ?? '');
|
|
261
|
+
if (idx < ids.length - 1) {
|
|
262
|
+
setSelectedId(ids[idx + 1]);
|
|
251
263
|
setJsonScrollOffset(0);
|
|
252
264
|
setSidebarScrollOffset((prev) => {
|
|
253
265
|
const newIdx = idx + 1;
|
|
@@ -273,7 +285,10 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
|
|
|
273
285
|
setEditMode(true);
|
|
274
286
|
setDraftsByComponentId((prev) => ({
|
|
275
287
|
...prev,
|
|
276
|
-
[selectedId]:
|
|
288
|
+
[selectedId]: (() => {
|
|
289
|
+
const { extractionConfidence: _c, reviewReasons: _r, needsReview: _n, ...rest } = component.editedProposal;
|
|
290
|
+
return prev[selectedId] ?? JSON.stringify(rest, null, 2);
|
|
291
|
+
})(),
|
|
277
292
|
}));
|
|
278
293
|
},
|
|
279
294
|
onToggleSource: () => {
|
|
@@ -315,19 +330,10 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
|
|
|
315
330
|
},
|
|
316
331
|
onToggleHelp: () => setShowHelp((prev) => !prev),
|
|
317
332
|
});
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
return (_jsxs(Text, { color: "red", children: [sessionError, '\nPress q to exit.'] }));
|
|
323
|
-
}
|
|
324
|
-
if (!session || !paths) {
|
|
325
|
-
return _jsx(Text, { color: "red", children: "Session unavailable." });
|
|
326
|
-
}
|
|
327
|
-
if (finalizedResult) {
|
|
328
|
-
return _jsx(FinalizedScreen, { result: finalizedResult });
|
|
329
|
-
}
|
|
330
|
-
const sessionSummary = session.components
|
|
333
|
+
// Sorted order: flagged+unresolved first, then ascending confidence.
|
|
334
|
+
// Memoised above early returns (React rules of hooks) so the array reference
|
|
335
|
+
// is stable between renders — prevents Sidebar repainting on scroll/select.
|
|
336
|
+
const sessionSummary = useMemo(() => (session?.components ?? [])
|
|
331
337
|
.map((c) => ({
|
|
332
338
|
id: c.id,
|
|
333
339
|
name: c.name,
|
|
@@ -337,13 +343,27 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
|
|
|
337
343
|
needsReview: c.originalProposal.needsReview ?? false,
|
|
338
344
|
}))
|
|
339
345
|
.sort((a, b) => {
|
|
340
|
-
// Unresolved flagged items first, then by ascending confidence
|
|
341
346
|
const aFlagged = a.needsReview && a.status === 'needs-review' ? 0 : 1;
|
|
342
347
|
const bFlagged = b.needsReview && b.status === 'needs-review' ? 0 : 1;
|
|
343
348
|
if (aFlagged !== bFlagged)
|
|
344
349
|
return aFlagged - bFlagged;
|
|
345
350
|
return a.extractionConfidence - b.extractionConfidence;
|
|
346
|
-
});
|
|
351
|
+
}), [session?.components, previewAnnotations]);
|
|
352
|
+
// Stable ID order — kept in a ref for use inside keymap handlers
|
|
353
|
+
const sortedIds = useMemo(() => sessionSummary.map((c) => c.id), [sessionSummary]);
|
|
354
|
+
sortedIdsRef.current = sortedIds;
|
|
355
|
+
if (loading) {
|
|
356
|
+
return _jsx(Text, { children: "Loading session..." });
|
|
357
|
+
}
|
|
358
|
+
if (sessionError) {
|
|
359
|
+
return (_jsxs(Text, { color: "red", children: [sessionError, '\nPress q to exit.'] }));
|
|
360
|
+
}
|
|
361
|
+
if (!session || !paths) {
|
|
362
|
+
return _jsx(Text, { color: "red", children: "Session unavailable." });
|
|
363
|
+
}
|
|
364
|
+
if (finalizedResult) {
|
|
365
|
+
return _jsx(FinalizedScreen, { result: finalizedResult });
|
|
366
|
+
}
|
|
347
367
|
const selectedRecord = session.components.find((c) => c.id === selectedId) ?? null;
|
|
348
368
|
const sessionDetail = selectedRecord ? createReviewSessionDetail({ ...session, components: [selectedRecord] }) : null;
|
|
349
369
|
const selectedDetail = sessionDetail?.components[0] ?? null;
|
|
@@ -363,7 +383,10 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
|
|
|
363
383
|
{ key: 'q', label: 'quit' },
|
|
364
384
|
];
|
|
365
385
|
const collapsed = terminalWidth < 80;
|
|
366
|
-
|
|
386
|
+
// TopBar(1) + statusbar(1) + footer(1) + border padding(2) = ~5 chrome rows
|
|
387
|
+
const CHROME_ROWS = 5;
|
|
388
|
+
const terminalRows = stdout?.rows ?? 24;
|
|
389
|
+
const visibleCount = Math.max(1, terminalRows - CHROME_ROWS);
|
|
367
390
|
const longestName = session.components.reduce((max, c) => Math.max(max, c.name.length), 0);
|
|
368
391
|
// icon + space + name + 2 border chars; min 14, max 22
|
|
369
392
|
const sidebarWidth = collapsed ? 3 : Math.min(Math.max(longestName + 4, 14), 22);
|
|
@@ -479,7 +502,7 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
|
|
|
479
502
|
}, onCancel: () => setShowQuitDialog(false) })), !showHelp && !showFinalizeDialog && !showQuitDialog && (_jsxs(Box, { flexGrow: 1, children: [_jsx(Sidebar, { components: sessionSummary, selectedId: selectedId, focused: sidebarFocused, scrollOffset: sidebarScrollOffset, visibleCount: visibleCount, onSelect: (id) => {
|
|
480
503
|
setSelectedId(id);
|
|
481
504
|
setJsonScrollOffset(0);
|
|
482
|
-
}, onScrollChange: setSidebarScrollOffset, collapsed: collapsed, width: sidebarWidth }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: selectedDetail ? (_jsx(ComponentDetail, { component: selectedDetail, sourceCode:
|
|
505
|
+
}, onScrollChange: setSidebarScrollOffset, collapsed: collapsed, width: sidebarWidth }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: selectedDetail ? (_jsx(ComponentDetail, { component: selectedDetail, sourceCode: selectedId ? (sourceCodeById[selectedId] ?? null) : null, draftValue: selectedId ? (draftsByComponentId[selectedId] ?? '') : '', editMode: editMode, sourceVisible: sourceVisible, jsonScrollOffset: jsonScrollOffset, sourceScrollX: 0, sourceScrollY: 0, terminalWidth: terminalWidth, previewAnnotation: selectedRecord ? previewAnnotations[selectedRecord.name] : undefined, onDraftChange: (value) => {
|
|
483
506
|
if (!selectedId)
|
|
484
507
|
return;
|
|
485
508
|
setDraftsByComponentId((prev) => ({
|
|
@@ -34,10 +34,16 @@ export function ComponentDetail({ component, sourceCode, draftValue, editMode, s
|
|
|
34
34
|
editWidth = availableWidth - originalWidth - 3;
|
|
35
35
|
sourceWidth = 0;
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
const
|
|
37
|
+
// Strip internal scoring fields — they're metadata, not part of the component definition
|
|
38
|
+
const stripScoring = ({ extractionConfidence: _c, reviewReasons: _r, needsReview: _n, ...rest }) => rest;
|
|
39
|
+
const originalJson = JSON.stringify(stripScoring(component.originalProposal), null, 2);
|
|
40
|
+
const editedJson = JSON.stringify(stripScoring(component.editedProposal), null, 2);
|
|
41
|
+
const conf = component.originalProposal.extractionConfidence ?? 100;
|
|
42
|
+
const nr = component.originalProposal.needsReview ?? false;
|
|
43
|
+
const confColor = nr ? 'red' : conf >= 80 ? 'white' : conf >= 50 ? 'yellow' : 'red';
|
|
44
|
+
const confLabel = (nr ? '⚑ ' : '') + 'confidence: ' + String(conf);
|
|
39
45
|
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: component.name }), (() => {
|
|
40
46
|
const ann = annotationLabel(previewAnnotation);
|
|
41
47
|
return ann ? _jsx(Text, { color: ann.color, children: ann.text }) : null;
|
|
42
|
-
})(), _jsx(Box, { flexGrow: 1 }), _jsxs(Text, { dimColor: true, children: [sourceVisible ? '[s] hide src' : '[s] src', editMode ? '' : ' [e] edit'] })] }), _jsxs(Box, { children: [_jsx(JsonPanel, { label: "ORIGINAL (read-only)", value: originalJson, scrollOffset: jsonScrollOffset, width: originalWidth, height: panelHeight, active: false }), _jsx(Text, { children: " " }), editMode ? (_jsx(FieldEditor, { value: draftValue || editedJson, width: editWidth, height: panelHeight, onChange: onDraftChange, onSave: onSaveDraft, onDiscard: onDiscardDraft })) : (_jsx(JsonPanel, { label: "EDIT (draft)", value: draftValue || editedJson, scrollOffset: jsonScrollOffset, width: editWidth, height: panelHeight, active: true })), sourceVisible && sourceWidth > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(SourcePanel, { sourceCode: sourceCode, filePath: component.originalProposal.source, width: sourceWidth, height: panelHeight, scrollX: sourceScrollX, scrollY: sourceScrollY })] }))] }), !editMode && _jsx(Text, { dimColor: true, children: ' [a] accept [r] reject [e] edit [A] accept all [↑↓] scroll' })] }));
|
|
48
|
+
})(), _jsx(Text, { color: confColor, children: ' — ' + confLabel }), _jsx(Box, { flexGrow: 1 }), _jsxs(Text, { dimColor: true, children: [sourceVisible ? '[s] hide src' : '[s] src', editMode ? '' : ' [e] edit'] })] }), _jsxs(Box, { children: [_jsx(JsonPanel, { label: "ORIGINAL (read-only)", value: originalJson, scrollOffset: jsonScrollOffset, width: originalWidth, height: panelHeight, active: false }), _jsx(Text, { children: " " }), editMode ? (_jsx(FieldEditor, { value: draftValue || editedJson, width: editWidth, height: panelHeight, onChange: onDraftChange, onSave: onSaveDraft, onDiscard: onDiscardDraft })) : (_jsx(JsonPanel, { label: "EDIT (draft)", value: draftValue || editedJson, scrollOffset: jsonScrollOffset, width: editWidth, height: panelHeight, active: true })), sourceVisible && sourceWidth > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(SourcePanel, { sourceCode: sourceCode, filePath: component.originalProposal.source, width: sourceWidth, height: panelHeight, scrollX: sourceScrollX, scrollY: sourceScrollY })] }))] }), !editMode && _jsx(Text, { dimColor: true, children: ' [a] accept [r] reject [e] edit [A] accept all [↑↓] scroll' })] }));
|
|
43
49
|
}
|
|
@@ -29,24 +29,6 @@ function truncateName(name, maxLen) {
|
|
|
29
29
|
return name;
|
|
30
30
|
return name.slice(0, maxLen) + '…';
|
|
31
31
|
}
|
|
32
|
-
function confidenceColor(confidence, needsReview) {
|
|
33
|
-
if (needsReview)
|
|
34
|
-
return 'red';
|
|
35
|
-
if (confidence >= 80)
|
|
36
|
-
return 'green';
|
|
37
|
-
if (confidence >= 50)
|
|
38
|
-
return 'yellow';
|
|
39
|
-
return 'red';
|
|
40
|
-
}
|
|
41
|
-
function confidenceDot(confidence, needsReview) {
|
|
42
|
-
if (needsReview)
|
|
43
|
-
return '⚑';
|
|
44
|
-
if (confidence >= 80)
|
|
45
|
-
return '●';
|
|
46
|
-
if (confidence >= 50)
|
|
47
|
-
return '◐';
|
|
48
|
-
return '○';
|
|
49
|
-
}
|
|
50
32
|
export function Sidebar({ components, selectedId, focused, scrollOffset, visibleCount, collapsed = false, width: widthProp, }) {
|
|
51
33
|
const visible = components.slice(scrollOffset, scrollOffset + visibleCount);
|
|
52
34
|
const showScrollUp = scrollOffset > 0;
|
|
@@ -56,16 +38,11 @@ export function Sidebar({ components, selectedId, focused, scrollOffset, visible
|
|
|
56
38
|
const isSelected = component.id === selectedId;
|
|
57
39
|
const icon = statusIcon(component.status);
|
|
58
40
|
const color = statusColor(component.status);
|
|
59
|
-
const
|
|
60
|
-
const nr = component.needsReview ?? false;
|
|
61
|
-
const dot = confidenceDot(conf, nr);
|
|
62
|
-
const dotColor = confidenceColor(conf, nr);
|
|
63
|
-
// reserve 2 chars for dot + space on top of existing icon + space
|
|
64
|
-
const maxNameLen = Math.max(1, width - 7);
|
|
41
|
+
const maxNameLen = Math.max(1, width - 4);
|
|
65
42
|
const name = truncateName(component.name, maxNameLen);
|
|
66
43
|
if (collapsed) {
|
|
67
44
|
return (_jsx(Box, { children: _jsx(Text, { color: color, inverse: isSelected && focused, underline: isSelected && !focused, children: icon }) }, component.id));
|
|
68
45
|
}
|
|
69
|
-
return (
|
|
46
|
+
return (_jsx(Box, { children: _jsx(Text, { color: color, inverse: isSelected && focused, underline: isSelected && !focused, wrap: "truncate", children: icon + ' ' + name }) }, component.id));
|
|
70
47
|
}), showScrollDown && !collapsed && _jsx(Text, { dimColor: true, children: "\u25BC" })] }));
|
|
71
48
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import { Box, Text } from 'ink';
|
|
3
|
+
import { Box, Text, useStdout } from 'ink';
|
|
4
4
|
import { TopBar } from '../select/tui/components/TopBar.js';
|
|
5
5
|
import { useImmediateInput } from '../select/tui/hooks/useImmediateInput.js';
|
|
6
6
|
function truncateName(name, maxLen = 30) {
|
|
@@ -10,6 +10,12 @@ function truncateName(name, maxLen = 30) {
|
|
|
10
10
|
}
|
|
11
11
|
export function AnalyzeView({ result, onExit }) {
|
|
12
12
|
const [scrollOffset, setScrollOffset] = useState(0);
|
|
13
|
+
const { stdout } = useStdout();
|
|
14
|
+
// Header lines: TopBar(1) + summary(3) + blank(1) + dividers+header(3) + blank(1) + footer(1) + footer-bar(1) = ~12
|
|
15
|
+
const HEADER_ROWS = 12;
|
|
16
|
+
const terminalRows = stdout?.rows ?? 24;
|
|
17
|
+
const visibleCount = Math.max(1, terminalRows - HEADER_ROWS);
|
|
18
|
+
const maxOffset = Math.max(0, result.components.length - visibleCount);
|
|
13
19
|
useImmediateInput((input, key) => {
|
|
14
20
|
if (input === 'q' || key.return) {
|
|
15
21
|
onExit();
|
|
@@ -19,24 +25,36 @@ export function AnalyzeView({ result, onExit }) {
|
|
|
19
25
|
setScrollOffset((o) => Math.max(0, o - 1));
|
|
20
26
|
}
|
|
21
27
|
else if (key.downArrow || input === 'j') {
|
|
22
|
-
setScrollOffset((o) => o + 1);
|
|
28
|
+
setScrollOffset((o) => Math.min(maxOffset, o + 1));
|
|
23
29
|
}
|
|
24
30
|
else if (input === 'g') {
|
|
25
31
|
setScrollOffset(0);
|
|
26
32
|
}
|
|
27
33
|
else if (input === 'G') {
|
|
28
|
-
setScrollOffset(
|
|
34
|
+
setScrollOffset(maxOffset);
|
|
29
35
|
}
|
|
30
36
|
});
|
|
37
|
+
const visible = result.components.slice(scrollOffset, scrollOffset + visibleCount);
|
|
38
|
+
const showScrollUp = scrollOffset > 0;
|
|
39
|
+
const showScrollDown = scrollOffset + visibleCount < result.components.length;
|
|
31
40
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(TopBar, { subcommand: "analyze", hints: [
|
|
32
41
|
{ key: '?', label: 'help' },
|
|
33
42
|
{ key: 'q', label: 'quit' },
|
|
34
|
-
] }), _jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Text, { children: 'Scanned ' + result.fileCount + ' source files in ' + result.sourceDirectory }), _jsx(Text, { children: 'Extracted ' + result.components.length + ' components' }), _jsx(Text, { dimColor: true, children: 'Session: ' + result.sessionId }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { bold: true, children:
|
|
43
|
+
] }), _jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsx(Text, { children: 'Scanned ' + result.fileCount + ' source files in ' + result.sourceDirectory }), _jsx(Text, { children: 'Extracted ' + result.components.length + ' components' }), _jsx(Text, { dimColor: true, children: 'Session: ' + result.sessionId }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { bold: true, children: 'Components' +
|
|
44
|
+
(showScrollUp || showScrollDown
|
|
45
|
+
? ' (' +
|
|
46
|
+
(scrollOffset + 1) +
|
|
47
|
+
'–' +
|
|
48
|
+
Math.min(scrollOffset + visibleCount, result.components.length) +
|
|
49
|
+
' of ' +
|
|
50
|
+
result.components.length +
|
|
51
|
+
')'
|
|
52
|
+
: '') }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { children: " " }), showScrollUp && _jsx(Text, { dimColor: true, children: " \u25B2 scroll up" }), visible.map((component) => {
|
|
35
53
|
const conf = component.extractionConfidence ?? 100;
|
|
36
|
-
const confColor = component.needsReview ? 'red' : conf >= 80 ? '
|
|
37
|
-
const
|
|
38
|
-
return (_jsxs(Box, { children: [component.warnings.length > 0 && _jsx(Text, { color: "yellow", children: "\u26A0 " }), component.warnings.length === 0 && _jsx(Text, { children: " " }), _jsx(Text, { children: truncateName(component.name).padEnd(20) }), _jsx(Text, { dimColor: true, children: component.framework.padEnd(10) }), _jsx(Text, { children: (component.propCount + ' props').padEnd(10) }), _jsx(Text, { children: (component.slotCount + ' ' + (component.slotCount === 1 ? 'slot' : 'slots')).padEnd(8) }), _jsx(Text, { color: confColor, children:
|
|
39
|
-
}), result.totalWarnings > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { bold: true, color: "yellow", children: 'Warnings (' + result.totalWarnings + ')' }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { children: " " }), result.components
|
|
54
|
+
const confColor = component.needsReview ? 'red' : conf >= 80 ? 'white' : conf >= 50 ? 'yellow' : 'red';
|
|
55
|
+
const confLabel = (component.needsReview ? '⚑ ' : '') + String(conf);
|
|
56
|
+
return (_jsxs(Box, { children: [component.warnings.length > 0 && _jsx(Text, { color: "yellow", children: "\u26A0 " }), component.warnings.length === 0 && _jsx(Text, { children: " " }), _jsx(Text, { children: truncateName(component.name).padEnd(20) }), _jsx(Text, { dimColor: true, children: component.framework.padEnd(10) }), _jsx(Text, { children: (component.propCount + ' props').padEnd(10) }), _jsx(Text, { children: (component.slotCount + ' ' + (component.slotCount === 1 ? 'slot' : 'slots')).padEnd(8) }), _jsx(Text, { color: confColor, children: confLabel }), component.warnings.length > 0 && (_jsx(Text, { color: "yellow", children: ' ⚠ ' + component.warnings.length + ' warning' + (component.warnings.length === 1 ? '' : 's') }))] }, component.name));
|
|
57
|
+
}), showScrollDown && _jsx(Text, { dimColor: true, children: " \u25BC scroll down" }), result.totalWarnings > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { bold: true, color: "yellow", children: 'Warnings (' + result.totalWarnings + ')' }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { children: " " }), result.components
|
|
40
58
|
.filter((c) => c.warnings.length > 0)
|
|
41
59
|
.flatMap((c) => c.warnings.map((w) => ({ component: c.name, warning: w })))
|
|
42
60
|
.map((w, i) => (_jsx(Text, { color: "yellow", children: ' ⚠ ' + w.component + ': ' + w.warning }, i)))] })), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: 'Run: analyze select --session ' + result.sessionId }), _jsx(Text, { children: " " })] }), _jsx(Box, { borderStyle: "single", paddingX: 1, children: _jsx(Text, { dimColor: true, children: "Press Enter or q to exit" }) })] }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experience-design-system-cli",
|
|
3
|
-
"version": "2.6.2-dev-build-
|
|
3
|
+
"version": "2.6.2-dev-build-864e323.0",
|
|
4
4
|
"description": "Contentful Experiences design system import CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"react-dom": "^18.3.1",
|
|
37
37
|
"ts-morph": "^27.0.2",
|
|
38
38
|
"typescript": "^5.9.3",
|
|
39
|
-
"@contentful/experience-design-system-types": "2.6.2-dev-build-
|
|
39
|
+
"@contentful/experience-design-system-types": "2.6.2-dev-build-864e323.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@tsconfig/node24": "^24.0.3",
|