@contentful/experience-design-system-cli 2.7.0 → 2.7.1-dev-build-02830eb.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.
Files changed (34) hide show
  1. package/dist/package.json +1 -1
  2. package/dist/src/analyze/select/tui/App.js +54 -475
  3. package/dist/src/analyze/select/tui/components/ComponentDetail.d.ts +3 -5
  4. package/dist/src/analyze/select/tui/components/ComponentDetail.js +3 -3
  5. package/dist/src/analyze/select/tui/components/FinalizeDialog.d.ts +1 -1
  6. package/dist/src/analyze/select/tui/components/FinalizeDialog.js +2 -10
  7. package/dist/src/analyze/select/tui/components/HelpOverlay.d.ts +1 -1
  8. package/dist/src/analyze/select/tui/components/HelpOverlay.js +2 -7
  9. package/dist/src/analyze/select/tui/components/JsonEditor.d.ts +7 -5
  10. package/dist/src/analyze/select/tui/components/JsonEditor.js +15 -146
  11. package/dist/src/analyze/select/tui/components/QuitDialog.d.ts +1 -1
  12. package/dist/src/analyze/select/tui/components/QuitDialog.js +2 -10
  13. package/dist/src/analyze/select/tui/components/Sidebar.d.ts +0 -2
  14. package/dist/src/analyze/select/tui/components/StatusBar.d.ts +0 -2
  15. package/dist/src/analyze/select/tui/hooks/useImmediateInput.d.ts +5 -0
  16. package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +7 -4
  17. package/dist/src/analyze/select/tui/hooks/useKeymap.d.ts +0 -23
  18. package/dist/src/analyze/select/tui/hooks/useKeymap.js +1 -67
  19. package/dist/src/analyze/select/tui/hooks/useRawMode.d.ts +6 -0
  20. package/dist/src/analyze/select/tui/hooks/useRawMode.js +16 -0
  21. package/dist/src/analyze/select/tui/hooks/useSideEffects.d.ts +17 -0
  22. package/dist/src/analyze/select/tui/hooks/useSideEffects.js +226 -0
  23. package/dist/src/analyze/select/tui/hooks/useUndo.js +15 -21
  24. package/dist/src/analyze/select/tui/inputToAction.d.ts +13 -0
  25. package/dist/src/analyze/select/tui/inputToAction.js +68 -0
  26. package/dist/src/analyze/select/tui/state.d.ts +136 -0
  27. package/dist/src/analyze/select/tui/state.js +385 -0
  28. package/dist/src/analyze/select/tui/utils.d.ts +5 -0
  29. package/dist/src/analyze/select/tui/utils.js +11 -0
  30. package/dist/src/analyze/tui/AnalyzeView.js +2 -0
  31. package/dist/src/import/tui/WizardApp.js +2 -0
  32. package/dist/src/import/tui/steps/GenerateReviewStep.js +4 -11
  33. package/dist/src/print/validate/tui/ValidateView.js +2 -0
  34. package/package.json +2 -2
@@ -0,0 +1,385 @@
1
+ import { stripScoringFields } from '../../../types.js';
2
+ import { computeScrollOffset } from './utils.js';
3
+ function makeEditorState(value) {
4
+ return {
5
+ cursor: { lines: value.split('\n'), cursorRow: 0, cursorCol: 0 },
6
+ undoStack: [],
7
+ scrollRow: 0,
8
+ validationError: null,
9
+ };
10
+ }
11
+ function editorWithCursor(e, next) {
12
+ return {
13
+ ...e,
14
+ cursor: next,
15
+ undoStack: e.undoStack.length >= 50 ? [...e.undoStack.slice(1), e.cursor] : [...e.undoStack, e.cursor],
16
+ };
17
+ }
18
+ function syncEditorScroll(e, visibleHeight) {
19
+ const { cursorRow } = e.cursor;
20
+ let { scrollRow } = e;
21
+ if (cursorRow < scrollRow)
22
+ scrollRow = cursorRow;
23
+ if (cursorRow >= scrollRow + visibleHeight)
24
+ scrollRow = cursorRow - visibleHeight + 1;
25
+ return scrollRow === e.scrollRow ? e : { ...e, scrollRow };
26
+ }
27
+ // Pure: apply one keypress to EditorState. Returns null if key has no effect.
28
+ export function applyEditorKey(e, input, key, visibleHeight) {
29
+ const { lines, cursorRow, cursorCol } = e.cursor;
30
+ // Ctrl+Z undo
31
+ if (key.ctrl && input === 'z') {
32
+ if (e.undoStack.length === 0)
33
+ return null;
34
+ const prev = e.undoStack[e.undoStack.length - 1];
35
+ return syncEditorScroll({ ...e, cursor: prev, undoStack: e.undoStack.slice(0, -1) }, visibleHeight);
36
+ }
37
+ let newLines = [...lines];
38
+ let newRow = cursorRow;
39
+ let newCol = cursorCol;
40
+ if (key.return) {
41
+ const before = newLines[cursorRow].slice(0, cursorCol);
42
+ const after = newLines[cursorRow].slice(cursorCol);
43
+ newLines = [...newLines.slice(0, cursorRow), before, after, ...newLines.slice(cursorRow + 1)];
44
+ newRow = cursorRow + 1;
45
+ newCol = 0;
46
+ }
47
+ else if (key.backspace) {
48
+ if (cursorCol > 0) {
49
+ newLines[cursorRow] = newLines[cursorRow].slice(0, cursorCol - 1) + newLines[cursorRow].slice(cursorCol);
50
+ newCol = cursorCol - 1;
51
+ }
52
+ else if (cursorRow > 0) {
53
+ const prevLen = newLines[cursorRow - 1].length;
54
+ newLines[cursorRow - 1] = newLines[cursorRow - 1] + newLines[cursorRow];
55
+ newLines = [...newLines.slice(0, cursorRow), ...newLines.slice(cursorRow + 1)];
56
+ newRow = cursorRow - 1;
57
+ newCol = prevLen;
58
+ }
59
+ else {
60
+ return null;
61
+ }
62
+ }
63
+ else if (key.delete) {
64
+ if (cursorCol < newLines[cursorRow].length) {
65
+ newLines[cursorRow] = newLines[cursorRow].slice(0, cursorCol) + newLines[cursorRow].slice(cursorCol + 1);
66
+ }
67
+ else if (cursorRow < newLines.length - 1) {
68
+ newLines[cursorRow] = newLines[cursorRow] + newLines[cursorRow + 1];
69
+ newLines = [...newLines.slice(0, cursorRow + 1), ...newLines.slice(cursorRow + 2)];
70
+ }
71
+ else {
72
+ return null;
73
+ }
74
+ }
75
+ else if (key.leftArrow) {
76
+ if (cursorCol > 0) {
77
+ newCol = cursorCol - 1;
78
+ }
79
+ else if (cursorRow > 0) {
80
+ newRow = cursorRow - 1;
81
+ newCol = newLines[cursorRow - 1].length;
82
+ }
83
+ else
84
+ return null;
85
+ }
86
+ else if (key.rightArrow) {
87
+ if (cursorCol < newLines[cursorRow].length) {
88
+ newCol = cursorCol + 1;
89
+ }
90
+ else if (cursorRow < newLines.length - 1) {
91
+ newRow = cursorRow + 1;
92
+ newCol = 0;
93
+ }
94
+ else
95
+ return null;
96
+ }
97
+ else if (key.upArrow) {
98
+ if (cursorRow === 0)
99
+ return null;
100
+ newRow = cursorRow - 1;
101
+ newCol = Math.min(cursorCol, newLines[newRow].length);
102
+ }
103
+ else if (key.downArrow) {
104
+ if (cursorRow >= newLines.length - 1)
105
+ return null;
106
+ newRow = cursorRow + 1;
107
+ newCol = Math.min(cursorCol, newLines[newRow].length);
108
+ }
109
+ else if (input === '\x1b[H' || input === '\x1b[1~') {
110
+ newCol = 0; // Home
111
+ }
112
+ else if (input === '\x1b[F' || input === '\x1b[4~') {
113
+ newCol = newLines[cursorRow].length; // End
114
+ }
115
+ else if (input && input.length === 1 && !key.ctrl && !key.meta) {
116
+ newLines[cursorRow] = newLines[cursorRow].slice(0, cursorCol) + input + newLines[cursorRow].slice(cursorCol);
117
+ newCol = cursorCol + 1;
118
+ }
119
+ else {
120
+ return null;
121
+ }
122
+ return syncEditorScroll(editorWithCursor(e, { lines: newLines, cursorRow: newRow, cursorCol: newCol }), visibleHeight);
123
+ }
124
+ export const initialState = {
125
+ mode: { type: 'browsing', sidebarFocused: true, sourceVisible: false },
126
+ session: null,
127
+ paths: null,
128
+ selectedId: null,
129
+ sortedIds: [],
130
+ sessionSummary: [],
131
+ sidebarScrollOffset: 0,
132
+ jsonScrollOffset: 0,
133
+ draftsByComponentId: {},
134
+ editor: null,
135
+ sourceCodeById: {},
136
+ previewAnnotations: (() => {
137
+ const raw = process.env['EDS_PREVIEW_ANNOTATIONS'];
138
+ if (!raw)
139
+ return {};
140
+ try {
141
+ return JSON.parse(raw);
142
+ }
143
+ catch {
144
+ return {};
145
+ }
146
+ })(),
147
+ previewResponse: null,
148
+ previewLoading: false,
149
+ previewError: null,
150
+ saveError: null,
151
+ };
152
+ // ── Derived data ──────────────────────────────────────────────────────────────
153
+ function computeSortedIds(components) {
154
+ return [...components]
155
+ .map((c) => ({
156
+ id: c.id,
157
+ needsReview: c.originalProposal.needsReview ?? false,
158
+ status: c.status,
159
+ conf: c.originalProposal.extractionConfidence ?? 6,
160
+ }))
161
+ .sort((a, b) => {
162
+ const aF = a.needsReview && a.status === 'needs-review' ? 0 : 1;
163
+ const bF = b.needsReview && b.status === 'needs-review' ? 0 : 1;
164
+ if (aF !== bF)
165
+ return aF - bF;
166
+ return a.conf - b.conf;
167
+ })
168
+ .map((c) => c.id);
169
+ }
170
+ // Builds a stable summary array for Sidebar — same reference if nothing changed.
171
+ // Keeping this in state means Sidebar never re-renders on scroll/selection alone.
172
+ function computeSessionSummary(components, annotations, sortedIds) {
173
+ return sortedIds
174
+ .map((id) => components.find((c) => c.id === id))
175
+ .filter((c) => !!c)
176
+ .map((c) => ({
177
+ id: c.id,
178
+ name: c.name,
179
+ status: c.status,
180
+ previewAnnotation: annotations[c.name],
181
+ extractionConfidence: c.originalProposal.extractionConfidence ?? null,
182
+ needsReview: c.originalProposal.needsReview ?? false,
183
+ }));
184
+ }
185
+ // Rebuild both sortedIds and sessionSummary from updated components + annotations.
186
+ function withUpdatedSession(state, components, annotations) {
187
+ const ann = annotations ?? state.previewAnnotations;
188
+ const sortedIds = computeSortedIds(components);
189
+ const sessionSummary = computeSessionSummary(components, ann, sortedIds);
190
+ return {
191
+ ...state,
192
+ session: { ...state.session, components },
193
+ previewAnnotations: ann,
194
+ sortedIds,
195
+ sessionSummary,
196
+ };
197
+ }
198
+ function updateStatus(state, newStatus) {
199
+ if (!state.session || !state.selectedId)
200
+ return state;
201
+ const components = state.session.components.map((c) => (c.id === state.selectedId ? { ...c, status: newStatus } : c));
202
+ return withUpdatedSession(state, components);
203
+ }
204
+ // ── Reducer ───────────────────────────────────────────────────────────────────
205
+ export function reducer(state, action) {
206
+ switch (action.type) {
207
+ case 'SESSION_LOADED': {
208
+ const sortedIds = computeSortedIds(action.session.components);
209
+ const sessionSummary = computeSessionSummary(action.session.components, state.previewAnnotations, sortedIds);
210
+ return {
211
+ ...state,
212
+ session: action.session,
213
+ paths: action.paths,
214
+ sortedIds,
215
+ sessionSummary,
216
+ selectedId: sortedIds[0] ?? null,
217
+ };
218
+ }
219
+ case 'SELECT':
220
+ return { ...state, selectedId: action.id, jsonScrollOffset: 0 };
221
+ case 'SIDEBAR_UP': {
222
+ if (!state.session || !state.selectedId)
223
+ return state;
224
+ const idx = state.sortedIds.indexOf(state.selectedId);
225
+ if (idx <= 0)
226
+ return state;
227
+ const ni = idx - 1;
228
+ return {
229
+ ...state,
230
+ selectedId: state.sortedIds[ni],
231
+ jsonScrollOffset: 0,
232
+ sidebarScrollOffset: computeScrollOffset(ni, state.sidebarScrollOffset, action.visibleCount),
233
+ };
234
+ }
235
+ case 'SIDEBAR_DOWN': {
236
+ if (!state.session || !state.selectedId)
237
+ return state;
238
+ const idx = state.sortedIds.indexOf(state.selectedId);
239
+ if (idx >= state.sortedIds.length - 1)
240
+ return state;
241
+ const ni = idx + 1;
242
+ return {
243
+ ...state,
244
+ selectedId: state.sortedIds[ni],
245
+ jsonScrollOffset: 0,
246
+ sidebarScrollOffset: computeScrollOffset(ni, state.sidebarScrollOffset, action.visibleCount),
247
+ };
248
+ }
249
+ case 'ACCEPT':
250
+ return updateStatus(state, 'accepted');
251
+ case 'REJECT':
252
+ return updateStatus(state, 'rejected');
253
+ case 'APPROVE_ALL': {
254
+ if (!state.session)
255
+ return state;
256
+ const components = state.session.components.map((c) => c.status === 'needs-review' ? { ...c, status: 'accepted' } : c);
257
+ return withUpdatedSession(state, components);
258
+ }
259
+ case 'ENTER_EDIT': {
260
+ if (state.mode.type !== 'browsing' || !state.selectedId || !state.session)
261
+ return state;
262
+ const component = state.session.components.find((c) => c.id === state.selectedId);
263
+ if (!component)
264
+ return state;
265
+ const existing = state.draftsByComponentId[state.selectedId];
266
+ const draft = existing ?? JSON.stringify(stripScoringFields(component.editedProposal), null, 2);
267
+ const editor = makeEditorState(draft);
268
+ return {
269
+ ...state,
270
+ mode: { type: 'editing', componentId: state.selectedId },
271
+ draftsByComponentId: { ...state.draftsByComponentId, [state.selectedId]: draft },
272
+ editor,
273
+ };
274
+ }
275
+ case 'EDITOR_KEY': {
276
+ if (!state.editor || state.mode.type !== 'editing')
277
+ return state;
278
+ const next = applyEditorKey(state.editor, action.input, action.key, action.visibleHeight);
279
+ if (!next)
280
+ return state;
281
+ const newValue = next.cursor.lines.join('\n');
282
+ return {
283
+ ...state,
284
+ editor: next,
285
+ draftsByComponentId: { ...state.draftsByComponentId, [state.mode.componentId]: newValue },
286
+ };
287
+ }
288
+ case 'EDITOR_VALIDATE': {
289
+ // inputToAction calls this on Ctrl+S — reducer does the parse check
290
+ if (!state.editor || state.mode.type !== 'editing')
291
+ return state;
292
+ const text = state.editor.cursor.lines.join('\n');
293
+ try {
294
+ JSON.parse(text);
295
+ // Valid — switch mode back to browsing; side effect will detect the transition and persist
296
+ return {
297
+ ...state,
298
+ editor: { ...state.editor, validationError: null },
299
+ mode: { type: 'browsing', sidebarFocused: true, sourceVisible: false },
300
+ // keep draft in draftsByComponentId — side effect will persist it and clear it
301
+ };
302
+ }
303
+ catch (e) {
304
+ return {
305
+ ...state,
306
+ editor: { ...state.editor, validationError: e instanceof Error ? e.message : String(e) },
307
+ };
308
+ }
309
+ }
310
+ case 'DRAFT_DISCARD': {
311
+ if (state.mode.type !== 'editing')
312
+ return state;
313
+ const { componentId } = state.mode;
314
+ const { [componentId]: _removed, ...remaining } = state.draftsByComponentId;
315
+ return {
316
+ ...state,
317
+ mode: { type: 'browsing', sidebarFocused: true, sourceVisible: false },
318
+ draftsByComponentId: remaining,
319
+ editor: null,
320
+ };
321
+ }
322
+ case 'DRAFT_PERSIST_DONE': {
323
+ const { componentId, updatedComponents } = action;
324
+ const { [componentId]: _removed, ...remaining } = state.draftsByComponentId;
325
+ const base = withUpdatedSession(state, updatedComponents);
326
+ return { ...base, draftsByComponentId: remaining };
327
+ }
328
+ case 'TOGGLE_FOCUS': {
329
+ if (state.mode.type !== 'browsing')
330
+ return state;
331
+ return { ...state, mode: { ...state.mode, sidebarFocused: !state.mode.sidebarFocused } };
332
+ }
333
+ case 'SCROLL_UP':
334
+ return { ...state, jsonScrollOffset: Math.max(0, state.jsonScrollOffset - 1) };
335
+ case 'SCROLL_DOWN':
336
+ return { ...state, jsonScrollOffset: state.jsonScrollOffset + 1 };
337
+ case 'TOGGLE_SOURCE': {
338
+ if (state.mode.type !== 'browsing')
339
+ return state;
340
+ if (action.terminalWidth < 120)
341
+ return { ...state, saveError: 'Terminal too narrow for source panel (need 120+ cols)' };
342
+ return { ...state, mode: { ...state.mode, sourceVisible: !state.mode.sourceVisible } };
343
+ }
344
+ case 'OPEN_DIALOG':
345
+ return { ...state, mode: { type: 'dialog', which: action.which } };
346
+ case 'CLOSE_DIALOG':
347
+ return { ...state, mode: { type: 'browsing', sidebarFocused: true, sourceVisible: false } };
348
+ case 'FINALIZE_CONFIRM': {
349
+ if (!state.session)
350
+ return state;
351
+ const accepted = state.session.components.filter((c) => c.status === 'accepted').length;
352
+ const rejected = state.session.components.filter((c) => c.status === 'rejected').length;
353
+ const excluded = state.session.components.filter((c) => c.status === 'needs-review').length;
354
+ return { ...state, mode: { type: 'finalized', accepted, rejected, excluded } };
355
+ }
356
+ case 'QUIT_CONFIRM':
357
+ return state; // side effect handles exit
358
+ case 'SOURCE_LOADED':
359
+ return { ...state, sourceCodeById: { ...state.sourceCodeById, [action.componentId]: action.code } };
360
+ case 'PREVIEW_START':
361
+ return { ...state, previewLoading: true };
362
+ case 'PREVIEW_SUCCESS': {
363
+ const components = state.session?.components ?? [];
364
+ const sortedIds = computeSortedIds(components);
365
+ const sessionSummary = computeSessionSummary(components, action.annotations, sortedIds);
366
+ return {
367
+ ...state,
368
+ previewResponse: action.response,
369
+ previewAnnotations: action.annotations,
370
+ previewLoading: false,
371
+ previewError: null,
372
+ sortedIds,
373
+ sessionSummary,
374
+ };
375
+ }
376
+ case 'PREVIEW_ERROR':
377
+ return { ...state, previewLoading: false, previewError: action.message };
378
+ case 'SAVE_ERROR':
379
+ return { ...state, saveError: action.message };
380
+ case 'CLEAR_ERRORS':
381
+ return { ...state, saveError: null, previewError: null };
382
+ default:
383
+ return state;
384
+ }
385
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Compute the new sidebar scroll offset after moving selection to `idx`.
3
+ * Keeps the selected item within the visible window.
4
+ */
5
+ export declare function computeScrollOffset(idx: number, prev: number, visibleCount: number): number;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Compute the new sidebar scroll offset after moving selection to `idx`.
3
+ * Keeps the selected item within the visible window.
4
+ */
5
+ export function computeScrollOffset(idx, prev, visibleCount) {
6
+ if (idx < prev)
7
+ return idx;
8
+ if (idx >= prev + visibleCount)
9
+ return idx - visibleCount + 1;
10
+ return prev;
11
+ }
@@ -3,12 +3,14 @@ import { useState } from 'react';
3
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
+ import { useRawMode } from '../select/tui/hooks/useRawMode.js';
6
7
  function truncateName(name, maxLen = 30) {
7
8
  if (name.length <= maxLen)
8
9
  return name;
9
10
  return name.slice(0, maxLen - 1) + '…';
10
11
  }
11
12
  export function AnalyzeView({ result, onExit }) {
13
+ useRawMode();
12
14
  const [scrollOffset, setScrollOffset] = useState(0);
13
15
  const { stdout } = useStdout();
14
16
  // Header lines: TopBar(1) + summary(3) + blank(1) + dividers+header(3) + blank(1) + footer(1) + footer-bar(1) = ~12
@@ -8,6 +8,7 @@ import { homedir, tmpdir } from 'node:os';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { execFile, spawn } from 'node:child_process';
10
10
  import { TopBar } from '../../analyze/select/tui/components/TopBar.js';
11
+ import { useRawMode } from '../../analyze/select/tui/hooks/useRawMode.js';
11
12
  import { WelcomeStep } from './steps/WelcomeStep.js';
12
13
  import { PathValidationStep } from './steps/PathValidationStep.js';
13
14
  import { RunningStep } from './steps/RunningStep.js';
@@ -44,6 +45,7 @@ function logStep(entry) {
44
45
  export function WizardApp({ initialSpaceId = '', initialEnvironmentId = 'master', initialCmaToken = '', initialHost, initialAgent, initialProjectPath, host, } = {}) {
45
46
  const defaultConfiguredHost = toConfiguredHost(host || process.env['EDS_HOST']) ?? DEFAULT_CONFIGURED_HOST;
46
47
  const resolveWizardHost = (hostValue) => hostValue || defaultConfiguredHost;
48
+ useRawMode();
47
49
  const { stdout } = useStdout();
48
50
  const terminalWidth = stdout?.columns ?? 80;
49
51
  const logInit = useRef(false);
@@ -8,6 +8,7 @@ import { StatusBar } from '../../../analyze/select/tui/components/StatusBar.js';
8
8
  import { FinalizeDialog } from '../../../analyze/select/tui/components/FinalizeDialog.js';
9
9
  import { QuitDialog } from '../../../analyze/select/tui/components/QuitDialog.js';
10
10
  import { useImmediateInput } from '../../../analyze/select/tui/hooks/useImmediateInput.js';
11
+ import { computeScrollOffset } from '../../../analyze/select/tui/utils.js';
11
12
  import { openPipelineDb, loadCDFComponents, storeCDFComponents } from '../../../session/db.js';
12
13
  const VISIBLE_COUNT = 20;
13
14
  const PANEL_HEIGHT = 22;
@@ -161,7 +162,7 @@ export function GenerateReviewStep({ extractSessionId, onFinalize, onQuit, }) {
161
162
  const newIdx = Math.min(components.length - 1, selectedIdx + 1);
162
163
  setSelectedIdx(newIdx);
163
164
  setJsonScrollOffset(0);
164
- setSidebarScrollOffset((prev) => (newIdx >= prev + VISIBLE_COUNT ? newIdx - VISIBLE_COUNT + 1 : prev));
165
+ setSidebarScrollOffset((prev) => computeScrollOffset(newIdx, prev, VISIBLE_COUNT));
165
166
  }
166
167
  }
167
168
  else {
@@ -196,15 +197,7 @@ export function GenerateReviewStep({ extractSessionId, onFinalize, onQuit, }) {
196
197
  const needsReview = components.filter((c) => c.status === 'needs-review').length;
197
198
  const propCount = selected ? Object.keys(selected.entry.$properties).length : 0;
198
199
  const slotCount = selected?.entry.$slots ? Object.keys(selected.entry.$slots).length : 0;
199
- return (_jsxs(Box, { flexDirection: "column", children: [showFinalize && (_jsx(FinalizeDialog, { accepted: accepted, rejected: rejected, needsReview: needsReview, onConfirm: handleFinalizeConfirm, onCancel: () => setShowFinalize(false) })), showQuit && _jsx(QuitDialog, { hasUnsavedDrafts: false, onConfirm: onQuit, onCancel: () => setShowQuit(false) }), !dialogOpen && (_jsxs(Box, { children: [_jsx(Sidebar, { components: sidebarItems, selectedId: selected?.key ?? null, focused: sidebarFocused, scrollOffset: sidebarScrollOffset, visibleCount: VISIBLE_COUNT, onSelect: (id) => {
200
- const idx = components.findIndex((c) => c.key === id);
201
- if (idx >= 0) {
202
- setSelectedIdx(idx);
203
- setJsonScrollOffset(0);
204
- }
205
- }, onScrollChange: setSidebarScrollOffset, width: sidebarWidth }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, flexDirection: "column", children: selected ? (_jsxs(_Fragment, { children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: selected.key }), _jsx(Box, { flexGrow: 1 }), _jsxs(Text, { dimColor: true, children: [propCount, " prop", propCount !== 1 ? 's' : '', slotCount > 0 ? ` · ${slotCount} slot${slotCount !== 1 ? 's' : ''}` : '', ' ', sidebarFocused ? '[Tab] focus panel' : '[Tab] focus list'] })] }), editMode ? (_jsx(FieldEditor, { value: draftValue || selectedJson, width: panelWidth, height: PANEL_HEIGHT, onChange: setDraftValue, onSave: handleEditSave, onDiscard: handleEditDiscard })) : (_jsx(JsonPanel, { label: "GENERATED DEFINITION", value: selectedJson, scrollOffset: jsonScrollOffset, width: panelWidth, height: PANEL_HEIGHT, active: !sidebarFocused })), saveError && _jsx(Text, { color: "red", children: '✗ ' + saveError }), _jsx(Text, { dimColor: true, children: editMode
200
+ return (_jsxs(Box, { flexDirection: "column", children: [showFinalize && (_jsx(FinalizeDialog, { accepted: accepted, rejected: rejected, needsReview: needsReview, onConfirm: handleFinalizeConfirm, onCancel: () => setShowFinalize(false) })), showQuit && _jsx(QuitDialog, { hasUnsavedDrafts: false, onConfirm: onQuit, onCancel: () => setShowQuit(false) }), !dialogOpen && (_jsxs(Box, { children: [_jsx(Sidebar, { components: sidebarItems, selectedId: selected?.key ?? null, focused: sidebarFocused, scrollOffset: sidebarScrollOffset, visibleCount: VISIBLE_COUNT, width: sidebarWidth }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, flexDirection: "column", children: selected ? (_jsxs(_Fragment, { children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: selected.key }), _jsx(Box, { flexGrow: 1 }), _jsxs(Text, { dimColor: true, children: [propCount, " prop", propCount !== 1 ? 's' : '', slotCount > 0 ? ` · ${slotCount} slot${slotCount !== 1 ? 's' : ''}` : '', ' ', sidebarFocused ? '[Tab] focus panel' : '[Tab] focus list'] })] }), editMode ? (_jsx(FieldEditor, { value: draftValue || selectedJson, width: panelWidth, height: PANEL_HEIGHT, onChange: setDraftValue, onSave: handleEditSave, onDiscard: handleEditDiscard })) : (_jsx(JsonPanel, { label: "GENERATED DEFINITION", value: selectedJson, scrollOffset: jsonScrollOffset, width: panelWidth, height: PANEL_HEIGHT, active: !sidebarFocused })), saveError && _jsx(Text, { color: "red", children: '✗ ' + saveError }), _jsx(Text, { dimColor: true, children: editMode
206
201
  ? ' [Ctrl+S] save [Esc] discard'
207
- : ' [a] accept [r] reject [e] edit [A] accept all [F] finalize [Tab] toggle focus [q] quit' })] })) : (_jsx(Text, { dimColor: true, children: "No component selected" })) })] })), !dialogOpen && (_jsx(StatusBar, { accepted: accepted, rejected: rejected, reviewed: 0, needsReview: needsReview, onApproveAll: () => {
208
- setComponents((prev) => prev.map((c) => (c.status === 'needs-review' ? { ...c, status: 'accepted' } : c)));
209
- }, onFinalize: () => setShowFinalize(true) }))] }));
202
+ : ' [a] accept [r] reject [e] edit [A] accept all [F] finalize [Tab] toggle focus [q] quit' })] })) : (_jsx(Text, { dimColor: true, children: "No component selected" })) })] })), !dialogOpen && _jsx(StatusBar, { accepted: accepted, rejected: rejected, reviewed: 0, needsReview: needsReview })] }));
210
203
  }
@@ -3,7 +3,9 @@ import { useState } from 'react';
3
3
  import { Box, Text } from 'ink';
4
4
  import { TopBar } from '../../../analyze/select/tui/components/TopBar.js';
5
5
  import { useImmediateInput } from '../../../analyze/select/tui/hooks/useImmediateInput.js';
6
+ import { useRawMode } from '../../../analyze/select/tui/hooks/useRawMode.js';
6
7
  export function ValidateView({ results, onExit }) {
8
+ useRawMode();
7
9
  const [scrollOffset, setScrollOffset] = useState(0);
8
10
  const allValid = results.every((r) => r.valid);
9
11
  useImmediateInput((input, key) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.7.0",
3
+ "version": "2.7.1-dev-build-02830eb.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.7.0"
39
+ "@contentful/experience-design-system-types": "2.7.1-dev-build-02830eb.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@tsconfig/node24": "^24.0.3",