diff --git a/README.md b/README.md index f8f83e7..7d5ae6a 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,7 @@ See [CLOUD.md](CLOUD.md) for detailed cloud documentation. | **Ctrl+Y** | Redo | | **Tab** | Indent (2 spaces) | | **Shift+Tab** | Unindent | +| **Ctrl+Shift+End** | Add blank line at end of file | | **Enter** | New line | | **Backspace** | Delete character (joins lines at start) | diff --git a/src/components/TextBuffer.jsx b/src/components/TextBuffer.jsx index 35541fe..f7e96da 100644 --- a/src/components/TextBuffer.jsx +++ b/src/components/TextBuffer.jsx @@ -4,6 +4,13 @@ import { highlightMarkdownLine } from '../utils/syntaxHighlight.js'; let lineIdCounter = 0; +export function appendBlankLineToEnd(existingLines) { + return [ + ...existingLines, + { id: `line-${lineIdCounter++}`, text: '' }, + ]; +} + /** * Wrap a line of text to fit within maxWidth, breaking at word boundaries */ @@ -437,7 +444,17 @@ export default function TextBuffer({ content, onChange, isFocused = true, viewpo } if (key.end) { - if (key.ctrl) { + if (key.ctrl && key.shift) { + // Ctrl+Shift+End: Add new line at end of file and move cursor there + const newLines = appendBlankLineToEnd(lines); + + saveToHistory(lines, cursorLine, cursorCol); + setLines(newLines); + const lastLine = newLines.length - 1; + setCursorLine(lastLine); + setCursorCol(0); + setSelection(null); + } else if (key.ctrl) { // Ctrl+End: Go to end of file const lastLine = lines.length - 1; setCursorLine(lastLine); diff --git a/tests/components/TextBuffer.test.jsx b/tests/components/TextBuffer.test.jsx new file mode 100644 index 0000000..d8621dd --- /dev/null +++ b/tests/components/TextBuffer.test.jsx @@ -0,0 +1,19 @@ +import { describe, it, expect } from '@jest/globals'; +import { appendBlankLineToEnd } from '../../src/components/TextBuffer.jsx'; + +describe('TextBuffer helpers', () => { + it('appendBlankLineToEnd adds a blank line without mutating original array', () => { + const originalLines = [ + { id: 'line-1', text: 'Hello' }, + { id: 'line-2', text: 'World' }, + ]; + + const result = appendBlankLineToEnd(originalLines); + + expect(result).not.toBe(originalLines); + expect(result).toHaveLength(originalLines.length + 1); + expect(result.slice(0, originalLines.length)).toEqual(originalLines); + expect(result[result.length - 1].text).toBe(''); + expect(result[result.length - 1].id).toMatch(/^line-/); + }); +});