@contentful/experience-design-system-cli 2.7.1 → 2.7.2-dev-build-bc65c2b.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.7.1",
3
+ "version": "2.7.2-dev-build-bc65c2b.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -3,7 +3,6 @@ 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';
6
- import { stripScoringFields } from '../../../types.js';
7
6
  import { TopBar } from './components/TopBar.js';
8
7
  import { Sidebar } from './components/Sidebar.js';
9
8
  import { ComponentDetail } from './components/ComponentDetail.js';
@@ -41,11 +40,6 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
41
40
  const [finalizedResult, setFinalizedResult] = useState(null);
42
41
  const [sidebarScrollOffset, setSidebarScrollOffset] = useState(0);
43
42
  const [jsonScrollOffset, setJsonScrollOffset] = useState(0);
44
- // Keeps the visual sort order in sync for use inside keymap handlers (which close over a stale render)
45
- const sortedIdsRef = useRef([]);
46
- // Source code kept separate from session state so lazy loading never mutates
47
- // session.components — that would invalidate useMemo(sessionSummary) and flash the sidebar
48
- const [sourceCodeById, setSourceCodeById] = useState({});
49
43
  const [previewAnnotations, setPreviewAnnotations] = useState(() => {
50
44
  const raw = process.env['EDS_PREVIEW_ANNOTATIONS'];
51
45
  if (!raw)
@@ -144,22 +138,13 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
144
138
  clearTimeout(previewDebounceRef.current);
145
139
  };
146
140
  }, []);
147
- // Sync loaded session into local state; selectedId is set to null here
148
- // and will be corrected to the first sorted item on first render via the effect below
141
+ // Sync loaded session into local state
149
142
  useEffect(() => {
150
143
  if (loadedSession && !session) {
151
144
  setSession(loadedSession);
152
- setSelectedId(null);
145
+ setSelectedId(loadedSession.components[0]?.id ?? null);
153
146
  }
154
147
  }, [loadedSession]);
155
- // Once the sort order is known (after first render), select the first sorted item.
156
- // Runs only when session transitions from null → loaded.
157
- const sessionLoaded = session !== null;
158
- useEffect(() => {
159
- if (sessionLoaded && selectedId === null && sortedIdsRef.current.length > 0) {
160
- setSelectedId(sortedIdsRef.current[0]);
161
- }
162
- }, [sessionLoaded]);
163
148
  useEffect(() => {
164
149
  if (session && !previewResponse) {
165
150
  const raw = process.env['EDS_PREVIEW_COUNTS'];
@@ -187,22 +172,26 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
187
172
  }
188
173
  }
189
174
  }, [session]);
190
- // Lazy source code loading — writes to sourceCodeById, NOT session.components,
191
- // so it doesn't invalidate useMemo(sessionSummary) and cause a sidebar flash
175
+ // Lazy source code loading
192
176
  useEffect(() => {
193
177
  if (!session || !selectedId)
194
178
  return;
195
- if (sourceCodeById[selectedId] !== undefined)
196
- return; // already loaded
197
179
  const selectedComponent = session.components.find((c) => c.id === selectedId);
198
- if (!selectedComponent)
180
+ if (!selectedComponent || selectedComponent.sourceCode !== null)
199
181
  return;
200
182
  readFile(selectedComponent.resolvedSourcePath, 'utf8')
201
183
  .then((code) => {
202
- setSourceCodeById((prev) => ({ ...prev, [selectedId]: code }));
184
+ setSession((prev) => {
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
+ });
203
192
  })
204
193
  .catch(() => {
205
- setSourceCodeById((prev) => ({ ...prev, [selectedId]: '' }));
194
+ // Leave as null; SourcePanel shows [No source available]
206
195
  });
207
196
  }, [selectedId]);
208
197
  // SIGINT handler
@@ -246,10 +235,9 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
246
235
  onSidebarUp: () => {
247
236
  if (!session)
248
237
  return;
249
- const ids = sortedIdsRef.current;
250
- const idx = ids.indexOf(selectedId ?? '');
238
+ const idx = session.components.findIndex((c) => c.id === selectedId);
251
239
  if (idx > 0) {
252
- setSelectedId(ids[idx - 1]);
240
+ setSelectedId(session.components[idx - 1].id);
253
241
  setJsonScrollOffset(0);
254
242
  setSidebarScrollOffset((prev) => Math.min(prev, idx - 1));
255
243
  }
@@ -257,10 +245,9 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
257
245
  onSidebarDown: () => {
258
246
  if (!session)
259
247
  return;
260
- const ids = sortedIdsRef.current;
261
- const idx = ids.indexOf(selectedId ?? '');
262
- if (idx < ids.length - 1) {
263
- setSelectedId(ids[idx + 1]);
248
+ const idx = session.components.findIndex((c) => c.id === selectedId);
249
+ if (idx < session.components.length - 1) {
250
+ setSelectedId(session.components[idx + 1].id);
264
251
  setJsonScrollOffset(0);
265
252
  setSidebarScrollOffset((prev) => {
266
253
  const newIdx = idx + 1;
@@ -286,7 +273,7 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
286
273
  setEditMode(true);
287
274
  setDraftsByComponentId((prev) => ({
288
275
  ...prev,
289
- [selectedId]: prev[selectedId] ?? JSON.stringify(stripScoringFields(component.editedProposal), null, 2),
276
+ [selectedId]: prev[selectedId] ?? JSON.stringify(component.editedProposal, null, 2),
290
277
  }));
291
278
  },
292
279
  onToggleSource: () => {
@@ -328,9 +315,7 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
328
315
  },
329
316
  onToggleHelp: () => setShowHelp((prev) => !prev),
330
317
  });
331
- // Sorted order: flagged+unresolved first, then ascending confidence.
332
- // Memoised above early returns (React rules of hooks) so the array reference
333
- // is stable between renders — prevents Sidebar repainting on scroll/select.
318
+ // Must be before early returns Rules of Hooks
334
319
  const sessionSummary = useMemo(() => (session?.components ?? [])
335
320
  .map((c) => ({
336
321
  id: c.id,
@@ -341,18 +326,12 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
341
326
  needsReview: c.originalProposal.needsReview ?? false,
342
327
  }))
343
328
  .sort((a, b) => {
344
- const aFlagged = a.needsReview && a.status === 'needs-review' ? 0 : 1;
345
- const bFlagged = b.needsReview && b.status === 'needs-review' ? 0 : 1;
346
- if (aFlagged !== bFlagged)
347
- return aFlagged - bFlagged;
348
- // null (unscored) sorts last; lower numeric score sorts first (most concerning)
349
- const aConf = a.extractionConfidence ?? 6;
350
- const bConf = b.extractionConfidence ?? 6;
351
- return aConf - bConf;
329
+ const aF = a.needsReview && a.status === 'needs-review' ? 0 : 1;
330
+ const bF = b.needsReview && b.status === 'needs-review' ? 0 : 1;
331
+ if (aF !== bF)
332
+ return aF - bF;
333
+ return (a.extractionConfidence ?? 6) - (b.extractionConfidence ?? 6);
352
334
  }), [session?.components, previewAnnotations]);
353
- // Stable ID order — kept in a ref for use inside keymap handlers
354
- const sortedIds = useMemo(() => sessionSummary.map((c) => c.id), [sessionSummary]);
355
- sortedIdsRef.current = sortedIds;
356
335
  if (loading) {
357
336
  return _jsx(Text, { children: "Loading session..." });
358
337
  }
@@ -384,17 +363,13 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
384
363
  { key: 'q', label: 'quit' },
385
364
  ];
386
365
  const collapsed = terminalWidth < 80;
387
- // TopBar(1) + statusbar(1) + footer(1) + border padding(2) = ~5 chrome rows
388
- const CHROME_ROWS = 5;
389
- const terminalRows = stdout?.rows ?? 24;
390
- const visibleCount = Math.max(1, terminalRows - CHROME_ROWS);
366
+ const visibleCount = 20;
391
367
  const longestName = session.components.reduce((max, c) => Math.max(max, c.name.length), 0);
392
368
  // icon + space + name + 2 border chars; min 14, max 22
393
369
  const sidebarWidth = collapsed ? 3 : Math.min(Math.max(longestName + 4, 14), 22);
394
- const handleDraftSave = async () => {
370
+ const handleDraftSave = async (draft) => {
395
371
  if (!selectedId || !session || !paths)
396
372
  return;
397
- const draft = draftsByComponentId[selectedId];
398
373
  if (!draft)
399
374
  return;
400
375
  try {
@@ -503,16 +478,7 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
503
478
  }, 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) => {
504
479
  setSelectedId(id);
505
480
  setJsonScrollOffset(0);
506
- }, 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) => {
507
- if (!selectedId)
508
- return;
509
- setDraftsByComponentId((prev) => ({
510
- ...prev,
511
- [selectedId]: value,
512
- }));
513
- }, onSaveDraft: () => {
514
- void handleDraftSave();
515
- }, onDiscardDraft: handleDraftDiscard, onScrollChange: setJsonScrollOffset })) : (_jsx(Text, { dimColor: true, children: "No component selected" })) })] })), _jsx(PreviewSummaryBar, { preview: previewResponse, loading: previewLoading }), previewError && _jsx(Text, { color: "yellow", children: '⚠ Preview: ' + previewError }), saveError && _jsx(Text, { color: "red", children: '⚠ ' + saveError }), !dialogOpen && (_jsx(StatusBar, { accepted: acceptedCount, rejected: rejectedCount, reviewed: reviewedCount, needsReview: needsReviewCount, onApproveAll: () => { }, onFinalize: () => setShowFinalizeDialog(true) }))] }));
481
+ }, onScrollChange: setSidebarScrollOffset, collapsed: collapsed, width: sidebarWidth }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: selectedDetail ? (_jsx(ComponentDetail, { component: selectedDetail, sourceCode: selectedRecord?.sourceCode ?? null, draftValue: selectedId ? (draftsByComponentId[selectedId] ?? '') : '', editMode: editMode, sourceVisible: sourceVisible, jsonScrollOffset: jsonScrollOffset, sourceScrollX: 0, sourceScrollY: 0, terminalWidth: terminalWidth, previewAnnotation: selectedRecord ? previewAnnotations[selectedRecord.name] : undefined, onSaveDraft: (value) => void handleDraftSave(value), onDiscardDraft: handleDraftDiscard, onScrollChange: setJsonScrollOffset })) : (_jsx(Text, { dimColor: true, children: "No component selected" })) })] })), _jsx(PreviewSummaryBar, { preview: previewResponse, loading: previewLoading }), previewError && _jsx(Text, { color: "yellow", children: '⚠ Preview: ' + previewError }), saveError && _jsx(Text, { color: "red", children: '⚠ ' + saveError }), !dialogOpen && (_jsx(StatusBar, { accepted: acceptedCount, rejected: rejectedCount, reviewed: reviewedCount, needsReview: needsReviewCount, onApproveAll: () => { }, onFinalize: () => setShowFinalizeDialog(true) }))] }));
516
482
  }
517
483
  function FinalizedScreen({ result, }) {
518
484
  useImmediateInput((_input, key) => {
@@ -11,10 +11,9 @@ type ComponentDetailProps = {
11
11
  sourceScrollY: number;
12
12
  terminalWidth: number;
13
13
  previewAnnotation?: PreviewAnnotation;
14
- onDraftChange: (value: string) => void;
15
- onSaveDraft: () => void;
14
+ onSaveDraft: (value: string) => void;
16
15
  onDiscardDraft: () => void;
17
16
  onScrollChange: (offset: number) => void;
18
17
  };
19
- export declare function ComponentDetail({ component, sourceCode, draftValue, editMode, sourceVisible, jsonScrollOffset, sourceScrollX, sourceScrollY, terminalWidth, previewAnnotation, onDraftChange, onSaveDraft, onDiscardDraft, }: ComponentDetailProps): React.ReactElement;
18
+ export declare function ComponentDetail({ component, sourceCode, draftValue, editMode, sourceVisible, jsonScrollOffset, sourceScrollX, sourceScrollY, terminalWidth, previewAnnotation, onSaveDraft, onDiscardDraft, }: ComponentDetailProps): React.ReactElement;
20
19
  export {};
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { Box, Text } from 'ink';
3
3
  import { stripScoringFields } from '../../../../types.js';
4
4
  import { JsonPanel } from './JsonPanel.js';
5
- import { FieldEditor } from './FieldEditor.js';
5
+ import { JsonEditor } from './JsonEditor.js';
6
6
  import { SourcePanel } from './SourcePanel.js';
7
7
  function annotationLabel(annotation) {
8
8
  switch (annotation) {
@@ -18,7 +18,7 @@ function annotationLabel(annotation) {
18
18
  return null;
19
19
  }
20
20
  }
21
- export function ComponentDetail({ component, sourceCode, draftValue, editMode, sourceVisible, jsonScrollOffset, sourceScrollX, sourceScrollY, terminalWidth, previewAnnotation, onDraftChange, onSaveDraft, onDiscardDraft, }) {
21
+ export function ComponentDetail({ component, sourceCode, draftValue, editMode, sourceVisible, jsonScrollOffset, sourceScrollX, sourceScrollY, terminalWidth, previewAnnotation, onSaveDraft, onDiscardDraft, }) {
22
22
  const sidebarWidth = terminalWidth < 80 ? 5 : 20;
23
23
  const availableWidth = terminalWidth - sidebarWidth - 2;
24
24
  const panelHeight = 20;
@@ -44,5 +44,5 @@ export function ComponentDetail({ component, sourceCode, draftValue, editMode, s
44
44
  return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: component.name }), (() => {
45
45
  const ann = annotationLabel(previewAnnotation);
46
46
  return ann ? _jsx(Text, { color: ann.color, children: ann.text }) : null;
47
- })(), _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' })] }));
47
+ })(), _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(JsonEditor, { value: draftValue || editedJson, width: editWidth, height: panelHeight, 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
48
  }
@@ -3,9 +3,8 @@ type JsonEditorProps = {
3
3
  value: string;
4
4
  width: number;
5
5
  height: number;
6
- onChange: (value: string) => void;
7
- onSave: () => void;
6
+ onSave: (value: string) => void;
8
7
  onDiscard: () => void;
9
8
  };
10
- export declare function JsonEditor({ value, width, height, onChange, onSave, onDiscard }: JsonEditorProps): React.ReactElement;
9
+ export declare function JsonEditor({ value, width, height, onSave, onDiscard }: JsonEditorProps): React.ReactElement;
11
10
  export {};
@@ -1,43 +1,36 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useState } from 'react';
2
+ import { useState } from 'react';
3
3
  import { Box, Text } from 'ink';
4
4
  import { useUndo } from '../hooks/useUndo.js';
5
5
  import { useImmediateInput } from '../hooks/useImmediateInput.js';
6
- export function JsonEditor({ value, width, height, onChange, onSave, onDiscard }) {
6
+ export function JsonEditor({ value, width, height, onSave, onDiscard }) {
7
7
  const undo = useUndo({
8
8
  lines: value.split('\n'),
9
9
  cursorRow: 0,
10
10
  cursorCol: 0,
11
11
  });
12
12
  const [scrollRow, setScrollRow] = useState(0);
13
- const [scrollCol] = useState(0);
14
13
  const [validationError, setValidationError] = useState(null);
15
- const [cursorVisible, setCursorVisible] = useState(true);
16
- // Blinking cursor
17
- useEffect(() => {
18
- const interval = setInterval(() => setCursorVisible((v) => !v), 500);
19
- return () => clearInterval(interval);
20
- }, []);
21
14
  const { lines, cursorRow, cursorCol } = undo.current;
22
- // Keep scroll in sync with cursor
23
- useEffect(() => {
24
- if (cursorRow < scrollRow)
25
- setScrollRow(cursorRow);
26
- if (cursorRow >= scrollRow + height)
27
- setScrollRow(cursorRow - height + 1);
28
- }, [cursorRow, scrollRow, height]);
15
+ const scrollCol = 0;
16
+ // Sync scroll with cursor — pure derivation, no setState needed when within bounds
17
+ let effectiveScrollRow = scrollRow;
18
+ if (cursorRow < scrollRow)
19
+ effectiveScrollRow = cursorRow;
20
+ if (cursorRow >= scrollRow + height)
21
+ effectiveScrollRow = cursorRow - height + 1;
22
+ if (effectiveScrollRow !== scrollRow)
23
+ setScrollRow(effectiveScrollRow);
29
24
  useImmediateInput((input, key) => {
30
25
  const currentLines = undo.current.lines;
31
26
  const currentRow = undo.current.cursorRow;
32
27
  const currentCol = undo.current.cursorCol;
33
28
  if (key.ctrl && input === 's') {
34
- // Validate and save
35
29
  const text = currentLines.join('\n');
36
30
  try {
37
31
  JSON.parse(text);
38
32
  setValidationError(null);
39
- onChange(text);
40
- onSave();
33
+ onSave(text);
41
34
  }
42
35
  catch (e) {
43
36
  setValidationError(e instanceof Error ? e.message : String(e));
@@ -50,7 +43,6 @@ export function JsonEditor({ value, width, height, onChange, onSave, onDiscard }
50
43
  }
51
44
  if (key.ctrl && input === 'z') {
52
45
  undo.undo();
53
- onChange(undo.current.lines.join('\n'));
54
46
  return;
55
47
  }
56
48
  let newLines = [...currentLines];
@@ -63,44 +55,39 @@ export function JsonEditor({ value, width, height, onChange, onSave, onDiscard }
63
55
  newRow = currentRow + 1;
64
56
  newCol = 0;
65
57
  }
66
- else if (key.backspace || key.delete) {
67
- if (key.backspace) {
68
- if (currentCol > 0) {
69
- newLines[currentRow] = newLines[currentRow].slice(0, currentCol - 1) + newLines[currentRow].slice(currentCol);
70
- newCol = currentCol - 1;
71
- }
72
- else if (currentRow > 0) {
73
- const prevLen = newLines[currentRow - 1].length;
74
- newLines[currentRow - 1] = newLines[currentRow - 1] + newLines[currentRow];
75
- newLines = [...newLines.slice(0, currentRow), ...newLines.slice(currentRow + 1)];
76
- newRow = currentRow - 1;
77
- newCol = prevLen;
78
- }
58
+ else if (key.backspace) {
59
+ if (currentCol > 0) {
60
+ newLines[currentRow] = newLines[currentRow].slice(0, currentCol - 1) + newLines[currentRow].slice(currentCol);
61
+ newCol = currentCol - 1;
79
62
  }
80
- else {
81
- // Delete key
82
- if (currentCol < newLines[currentRow].length) {
83
- newLines[currentRow] = newLines[currentRow].slice(0, currentCol) + newLines[currentRow].slice(currentCol + 1);
84
- }
85
- else if (currentRow < newLines.length - 1) {
86
- newLines[currentRow] = newLines[currentRow] + newLines[currentRow + 1];
87
- newLines = [...newLines.slice(0, currentRow + 1), ...newLines.slice(currentRow + 2)];
88
- }
63
+ else if (currentRow > 0) {
64
+ const prevLen = newLines[currentRow - 1].length;
65
+ newLines[currentRow - 1] = newLines[currentRow - 1] + newLines[currentRow];
66
+ newLines = [...newLines.slice(0, currentRow), ...newLines.slice(currentRow + 1)];
67
+ newRow = currentRow - 1;
68
+ newCol = prevLen;
69
+ }
70
+ }
71
+ else if (key.delete) {
72
+ if (currentCol < newLines[currentRow].length) {
73
+ newLines[currentRow] = newLines[currentRow].slice(0, currentCol) + newLines[currentRow].slice(currentCol + 1);
74
+ }
75
+ else if (currentRow < newLines.length - 1) {
76
+ newLines[currentRow] = newLines[currentRow] + newLines[currentRow + 1];
77
+ newLines = [...newLines.slice(0, currentRow + 1), ...newLines.slice(currentRow + 2)];
89
78
  }
90
79
  }
91
80
  else if (key.leftArrow) {
92
- if (currentCol > 0) {
81
+ if (currentCol > 0)
93
82
  newCol = currentCol - 1;
94
- }
95
83
  else if (currentRow > 0) {
96
84
  newRow = currentRow - 1;
97
85
  newCol = newLines[currentRow - 1].length;
98
86
  }
99
87
  }
100
88
  else if (key.rightArrow) {
101
- if (currentCol < newLines[currentRow].length) {
89
+ if (currentCol < newLines[currentRow].length)
102
90
  newCol = currentCol + 1;
103
- }
104
91
  else if (currentRow < newLines.length - 1) {
105
92
  newRow = currentRow + 1;
106
93
  newCol = 0;
@@ -118,36 +105,26 @@ export function JsonEditor({ value, width, height, onChange, onSave, onDiscard }
118
105
  newCol = Math.min(currentCol, newLines[currentRow + 1].length);
119
106
  }
120
107
  }
121
- else if (input === '\x1b[H' || input === '\x1b[1~') {
122
- // Home key
123
- newCol = 0;
124
- }
125
- else if (input === '\x1b[F' || input === '\x1b[4~') {
126
- // End key
127
- newCol = newLines[currentRow].length;
128
- }
129
108
  else if (input && input.length === 1 && !key.ctrl && !key.meta) {
130
- // Printable character
131
- newLines[currentRow] = newLines[currentRow].slice(0, currentCol) + input + newLines[currentRow].slice(currentCol);
109
+ newLines[currentRow] =
110
+ newLines[currentRow].slice(0, currentCol) + input + newLines[currentRow].slice(currentCol);
132
111
  newCol = currentCol + 1;
133
112
  }
134
113
  else {
135
- return; // No change
114
+ return;
136
115
  }
137
116
  undo.push({ lines: newLines, cursorRow: newRow, cursorCol: newCol });
138
- onChange(newLines.join('\n'));
139
117
  });
140
118
  const innerWidth = Math.max(1, width - 2);
141
- const visibleLines = lines.slice(scrollRow, scrollRow + height);
119
+ const visibleLines = lines.slice(effectiveScrollRow, effectiveScrollRow + height);
142
120
  return (_jsxs(Box, { flexDirection: "column", width: width, borderStyle: "single", borderColor: "white", children: [_jsx(Text, { bold: true, children: 'EDIT [EDITING — Ctrl+S save · Esc discard]' }), visibleLines.map((line, displayRow) => {
143
- const actualRow = displayRow + scrollRow;
121
+ const actualRow = displayRow + effectiveScrollRow;
144
122
  const displayLine = line.slice(scrollCol, scrollCol + innerWidth);
145
123
  if (actualRow === cursorRow) {
146
- // Render cursor on this line
147
124
  const beforeCursor = displayLine.slice(0, cursorCol - scrollCol);
148
125
  const cursorChar = displayLine[cursorCol - scrollCol] ?? ' ';
149
126
  const afterCursor = displayLine.slice(cursorCol - scrollCol + 1);
150
- return (_jsxs(Box, { children: [_jsx(Text, { children: beforeCursor }), _jsx(Text, { inverse: cursorVisible, children: cursorChar }), _jsx(Text, { children: afterCursor })] }, displayRow));
127
+ return (_jsxs(Box, { children: [_jsx(Text, { children: beforeCursor }), _jsx(Text, { inverse: true, children: cursorChar }), _jsx(Text, { children: afterCursor })] }, displayRow));
151
128
  }
152
129
  return _jsx(Text, { children: displayLine }, displayRow);
153
130
  }), validationError && _jsx(Text, { color: "red", children: '✗ Invalid JSON: ' + validationError })] }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.7.1",
3
+ "version": "2.7.2-dev-build-bc65c2b.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.1"
39
+ "@contentful/experience-design-system-types": "2.7.2-dev-build-bc65c2b.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@tsconfig/node24": "^24.0.3",