ink-prompt 0.2.2 → 0.2.4

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 (61) hide show
  1. package/README.md +2 -2
  2. package/dist/components/MultilineInput/AtomicBlocks.d.ts +10 -20
  3. package/dist/components/MultilineInput/AtomicBlocks.js +33 -42
  4. package/dist/components/MultilineInput/BlockMarker.d.ts +19 -0
  5. package/dist/components/MultilineInput/BlockMarker.js +83 -0
  6. package/dist/components/MultilineInput/BlockRegistry.d.ts +39 -0
  7. package/dist/components/MultilineInput/BlockRegistry.js +236 -0
  8. package/dist/components/MultilineInput/BlockTypes.d.ts +22 -0
  9. package/dist/components/MultilineInput/ImageTypes.d.ts +0 -2
  10. package/dist/components/MultilineInput/ImageTypes.js +1 -2
  11. package/dist/components/MultilineInput/ImageValidator.js +1 -1
  12. package/dist/components/MultilineInput/KeyHandler.d.ts +1 -1
  13. package/dist/components/MultilineInput/TextBuffer.d.ts +5 -31
  14. package/dist/components/MultilineInput/TextBuffer.js +91 -161
  15. package/dist/components/MultilineInput/TextRenderer.d.ts +6 -7
  16. package/dist/components/MultilineInput/TextRenderer.js +7 -7
  17. package/dist/components/MultilineInput/__tests__/BlockMarker.test.js +130 -0
  18. package/dist/components/MultilineInput/__tests__/BlockRegistry.test.js +225 -0
  19. package/dist/components/MultilineInput/__tests__/Placeholder.integration.test.js +44 -65
  20. package/dist/components/MultilineInput/__tests__/TextBuffer_images.test.js +10 -31
  21. package/dist/components/MultilineInput/__tests__/TextRenderer_images.test.js +27 -13
  22. package/dist/components/MultilineInput/__tests__/integration_images.test.js +2 -4
  23. package/dist/components/MultilineInput/__tests__/useTextInput_images.test.js +30 -29
  24. package/dist/components/MultilineInput/index.d.ts +6 -6
  25. package/dist/components/MultilineInput/index.js +56 -13
  26. package/dist/components/MultilineInput/types.d.ts +0 -20
  27. package/dist/components/MultilineInput/useTextInput.d.ts +4 -11
  28. package/dist/components/MultilineInput/useTextInput.js +79 -76
  29. package/package.json +1 -1
  30. package/dist/components/MultilineInput/Placeholder.d.ts +0 -30
  31. package/dist/components/MultilineInput/Placeholder.js +0 -200
  32. package/dist/components/MultilineInput/__tests__/Placeholder.test.js +0 -235
  33. package/dist/examples/examples/basic.js +0 -9
  34. package/dist/examples/src/components/MultilineInput/KeyHandler.d.ts +0 -15
  35. package/dist/examples/src/components/MultilineInput/KeyHandler.js +0 -97
  36. package/dist/examples/src/components/MultilineInput/TextBuffer.d.ts +0 -34
  37. package/dist/examples/src/components/MultilineInput/TextBuffer.js +0 -127
  38. package/dist/examples/src/components/MultilineInput/TextRenderer.d.ts +0 -24
  39. package/dist/examples/src/components/MultilineInput/TextRenderer.js +0 -72
  40. package/dist/examples/src/components/MultilineInput/__tests__/KeyHandler.test.js +0 -115
  41. package/dist/examples/src/components/MultilineInput/__tests__/TextBuffer.test.d.ts +0 -1
  42. package/dist/examples/src/components/MultilineInput/__tests__/TextBuffer.test.js +0 -254
  43. package/dist/examples/src/components/MultilineInput/__tests__/TextRenderer.test.d.ts +0 -1
  44. package/dist/examples/src/components/MultilineInput/__tests__/TextRenderer.test.js +0 -176
  45. package/dist/examples/src/components/MultilineInput/__tests__/integration.test.d.ts +0 -1
  46. package/dist/examples/src/components/MultilineInput/__tests__/integration.test.js +0 -71
  47. package/dist/examples/src/components/MultilineInput/__tests__/useTextInput.test.d.ts +0 -1
  48. package/dist/examples/src/components/MultilineInput/__tests__/useTextInput.test.js +0 -65
  49. package/dist/examples/src/components/MultilineInput/index.d.ts +0 -39
  50. package/dist/examples/src/components/MultilineInput/index.js +0 -82
  51. package/dist/examples/src/components/MultilineInput/types.d.ts +0 -55
  52. package/dist/examples/src/components/MultilineInput/types.js +0 -1
  53. package/dist/examples/src/components/MultilineInput/useTextInput.d.ts +0 -16
  54. package/dist/examples/src/components/MultilineInput/useTextInput.js +0 -82
  55. package/dist/examples/src/hello.test.d.ts +0 -1
  56. package/dist/examples/src/hello.test.js +0 -13
  57. package/dist/examples/src/index.d.ts +0 -2
  58. package/dist/examples/src/index.js +0 -2
  59. /package/dist/components/MultilineInput/{__tests__/Placeholder.test.d.ts → BlockTypes.js} +0 -0
  60. /package/dist/{examples/examples/basic.d.ts → components/MultilineInput/__tests__/BlockMarker.test.d.ts} +0 -0
  61. /package/dist/{examples/src/components/MultilineInput/__tests__/KeyHandler.test.d.ts → components/MultilineInput/__tests__/BlockRegistry.test.d.ts} +0 -0
@@ -1,65 +0,0 @@
1
- import { renderHook, act } from '@testing-library/react';
2
- import { describe, it, expect } from 'vitest';
3
- import { useTextInput } from '../useTextInput';
4
- describe('useTextInput', () => {
5
- it('should initialize with empty buffer', () => {
6
- const { result } = renderHook(() => useTextInput());
7
- expect(result.current.value).toBe('');
8
- expect(result.current.cursor).toEqual({ line: 0, column: 0 });
9
- });
10
- it('should initialize with initial value', () => {
11
- const { result } = renderHook(() => useTextInput({ initialValue: 'Hello\nWorld' }));
12
- expect(result.current.value).toBe('Hello\nWorld');
13
- expect(result.current.cursor).toEqual({ line: 1, column: 5 }); // Cursor at end
14
- });
15
- it('should insert character', () => {
16
- const { result } = renderHook(() => useTextInput());
17
- act(() => {
18
- result.current.insert('a');
19
- });
20
- expect(result.current.value).toBe('a');
21
- expect(result.current.cursor).toEqual({ line: 0, column: 1 });
22
- });
23
- it('should delete character', () => {
24
- const { result } = renderHook(() => useTextInput({ initialValue: 'abc' }));
25
- act(() => {
26
- result.current.delete();
27
- });
28
- expect(result.current.value).toBe('ab');
29
- expect(result.current.cursor).toEqual({ line: 0, column: 2 });
30
- });
31
- it('should insert new line', () => {
32
- const { result } = renderHook(() => useTextInput({ initialValue: 'abc' }));
33
- // Move cursor to middle
34
- act(() => {
35
- result.current.moveCursor('left');
36
- });
37
- act(() => {
38
- result.current.newLine();
39
- });
40
- expect(result.current.value).toBe('ab\nc');
41
- expect(result.current.cursor).toEqual({ line: 1, column: 0 });
42
- });
43
- it('should move cursor', () => {
44
- const { result } = renderHook(() => useTextInput({ initialValue: 'abc' }));
45
- act(() => {
46
- result.current.moveCursor('left');
47
- });
48
- expect(result.current.cursor).toEqual({ line: 0, column: 2 });
49
- });
50
- it('should support undo/redo', () => {
51
- const { result } = renderHook(() => useTextInput());
52
- act(() => {
53
- result.current.insert('a');
54
- });
55
- expect(result.current.value).toBe('a');
56
- act(() => {
57
- result.current.undo();
58
- });
59
- expect(result.current.value).toBe('');
60
- act(() => {
61
- result.current.redo();
62
- });
63
- expect(result.current.value).toBe('a');
64
- });
65
- });
@@ -1,39 +0,0 @@
1
- import React from 'react';
2
- export interface MultilineInputProps {
3
- /** Controlled text value */
4
- value?: string;
5
- /** Called when text changes */
6
- onChange?: (value: string) => void;
7
- /** Called when user submits (Enter without backslash) */
8
- onSubmit?: (value: string) => void;
9
- /** Placeholder text when empty */
10
- placeholder?: string;
11
- /** Whether to show the cursor (defaults to true) */
12
- showCursor?: boolean;
13
- /** Terminal width for word wrapping */
14
- width?: number;
15
- /** Whether input is active/focused (defaults to true) */
16
- isActive?: boolean;
17
- }
18
- /**
19
- * Props for the core component (without Ink-specific hooks)
20
- * This allows testing the rendering logic separately.
21
- */
22
- export interface MultilineInputCoreProps {
23
- value?: string;
24
- onChange?: (value: string) => void;
25
- onSubmit?: (value: string) => void;
26
- placeholder?: string;
27
- showCursor?: boolean;
28
- width?: number;
29
- }
30
- /**
31
- * Core rendering component that can be tested without Ink runtime.
32
- * Does not include useInput/useStdout hooks.
33
- */
34
- export declare const MultilineInputCore: React.FC<MultilineInputCoreProps>;
35
- /**
36
- * Full MultilineInput with Ink keyboard handling.
37
- * This component uses Ink-specific hooks and must be rendered in an Ink context.
38
- */
39
- export declare const MultilineInput: React.FC<MultilineInputProps>;
@@ -1,82 +0,0 @@
1
- import React, { useEffect, useCallback } from 'react';
2
- import { useTextInput } from './useTextInput';
3
- import { handleKey } from './KeyHandler';
4
- import { TextRenderer } from './TextRenderer';
5
- import { createBuffer } from './TextBuffer';
6
- /**
7
- * Core rendering component that can be tested without Ink runtime.
8
- * Does not include useInput/useStdout hooks.
9
- */
10
- export const MultilineInputCore = ({ value, onChange, placeholder, showCursor = true, width = 80, }) => {
11
- const textInput = useTextInput({ initialValue: value ?? '' });
12
- // Sync external value changes
13
- useEffect(() => {
14
- if (value !== undefined && value !== textInput.value) {
15
- textInput.setText(value);
16
- }
17
- }, [value]);
18
- // Notify parent of changes
19
- useEffect(() => {
20
- onChange?.(textInput.value);
21
- }, [textInput.value, onChange]);
22
- // Create buffer for TextRenderer
23
- const buffer = createBuffer(textInput.value);
24
- // Show placeholder if empty and no cursor shown
25
- const isEmpty = textInput.value === '';
26
- const showPlaceholder = isEmpty && placeholder && !showCursor;
27
- if (showPlaceholder) {
28
- return React.createElement("div", { style: { opacity: 0.5 } }, placeholder);
29
- }
30
- return (React.createElement(TextRenderer, { buffer: buffer, cursor: textInput.cursor, width: width, showCursor: showCursor }));
31
- };
32
- /**
33
- * Full MultilineInput with Ink keyboard handling.
34
- * This component uses Ink-specific hooks and must be rendered in an Ink context.
35
- */
36
- export const MultilineInput = ({ value, onChange, onSubmit, placeholder, showCursor = true, width, isActive = true, }) => {
37
- // Import Ink hooks dynamically to avoid import-time errors in tests
38
- const { useInput, useStdout, Box, Text } = require('ink');
39
- // Get terminal width from Ink if not provided
40
- const { stdout } = useStdout();
41
- const terminalWidth = width ?? stdout?.columns ?? 80;
42
- const textInput = useTextInput({ initialValue: value ?? '' });
43
- // Sync external value changes
44
- useEffect(() => {
45
- if (value !== undefined && value !== textInput.value) {
46
- textInput.setText(value);
47
- }
48
- }, [value]);
49
- // Notify parent of changes
50
- useEffect(() => {
51
- onChange?.(textInput.value);
52
- }, [textInput.value, onChange]);
53
- // Create buffer for TextRenderer and KeyHandler
54
- const buffer = createBuffer(textInput.value);
55
- // Create submit handler
56
- const handleSubmit = useCallback(() => {
57
- onSubmit?.(textInput.value);
58
- }, [onSubmit, textInput.value]);
59
- // Create actions for KeyHandler
60
- const actions = {
61
- insert: textInput.insert,
62
- delete: textInput.delete,
63
- newLine: textInput.newLine,
64
- moveCursor: textInput.moveCursor,
65
- undo: textInput.undo,
66
- redo: textInput.redo,
67
- setText: textInput.setText,
68
- submit: handleSubmit,
69
- };
70
- // Handle keyboard input
71
- useInput((input, key) => {
72
- handleKey(key, input, buffer, actions, textInput.cursor);
73
- }, { isActive });
74
- // Show placeholder if empty and no cursor shown
75
- const isEmpty = textInput.value === '';
76
- const showPlaceholder = isEmpty && placeholder && !showCursor;
77
- if (showPlaceholder) {
78
- return (React.createElement(Box, null,
79
- React.createElement(Text, { dimColor: true }, placeholder)));
80
- }
81
- return (React.createElement(TextRenderer, { buffer: buffer, cursor: textInput.cursor, width: terminalWidth, showCursor: showCursor }));
82
- };
@@ -1,55 +0,0 @@
1
- /**
2
- * Cursor position in the text buffer
3
- */
4
- export interface Cursor {
5
- /** 0-indexed line number */
6
- line: number;
7
- /** 0-indexed column position */
8
- column: number;
9
- }
10
- /**
11
- * Text buffer containing multiple lines
12
- */
13
- export interface Buffer {
14
- /** Array of text lines (without newline characters) */
15
- lines: string[];
16
- }
17
- /**
18
- * Cursor movement directions
19
- */
20
- export type Direction = 'up' | 'down' | 'left' | 'right' | 'lineStart' | 'lineEnd';
21
- /**
22
- * Result of wrapping buffer lines for visual display
23
- */
24
- export interface WrapResult {
25
- /** Visual lines after wrapping */
26
- visualLines: string[];
27
- /** Row in visual lines where cursor appears */
28
- cursorVisualRow: number;
29
- /** Column in that visual row where cursor appears */
30
- cursorVisualCol: number;
31
- }
32
- /**
33
- * Keyboard key state (mirrors Ink's Key interface)
34
- * Defined locally to avoid ESM/CJS import issues with Ink
35
- */
36
- export interface Key {
37
- upArrow?: boolean;
38
- downArrow?: boolean;
39
- leftArrow?: boolean;
40
- rightArrow?: boolean;
41
- pageDown?: boolean;
42
- pageUp?: boolean;
43
- return?: boolean;
44
- escape?: boolean;
45
- ctrl?: boolean;
46
- shift?: boolean;
47
- tab?: boolean;
48
- backspace?: boolean;
49
- delete?: boolean;
50
- meta?: boolean;
51
- /** Home key (may not be available in all terminals) */
52
- home?: boolean;
53
- /** End key (may not be available in all terminals) */
54
- end?: boolean;
55
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,16 +0,0 @@
1
- import type { Cursor, Direction } from './types';
2
- export interface UseTextInputProps {
3
- initialValue?: string;
4
- }
5
- export interface UseTextInputResult {
6
- value: string;
7
- cursor: Cursor;
8
- insert: (char: string) => void;
9
- delete: () => void;
10
- newLine: () => void;
11
- moveCursor: (direction: Direction) => void;
12
- undo: () => void;
13
- redo: () => void;
14
- setText: (text: string) => void;
15
- }
16
- export declare function useTextInput({ initialValue }?: UseTextInputProps): UseTextInputResult;
@@ -1,82 +0,0 @@
1
- import { useState, useCallback } from 'react';
2
- import { createBuffer, insertChar as bufferInsertChar, deleteChar as bufferDeleteChar, insertNewLine as bufferInsertNewLine, moveCursor as bufferMoveCursor, getTextContent, } from './TextBuffer';
3
- export function useTextInput({ initialValue = '' } = {}) {
4
- const [buffer, setBuffer] = useState(() => createBuffer(initialValue));
5
- const [cursor, setCursor] = useState(() => {
6
- const lines = initialValue.split('\n');
7
- return {
8
- line: lines.length - 1,
9
- column: lines[lines.length - 1].length,
10
- };
11
- });
12
- const [undoStack, setUndoStack] = useState([]);
13
- const [redoStack, setRedoStack] = useState([]);
14
- const pushToHistory = useCallback((currentBuffer, currentCursor) => {
15
- setUndoStack((prev) => [...prev, { buffer: currentBuffer, cursor: currentCursor }]);
16
- setRedoStack([]);
17
- }, []);
18
- const insert = useCallback((char) => {
19
- pushToHistory(buffer, cursor);
20
- const result = bufferInsertChar(buffer, cursor, char);
21
- setBuffer(result.buffer);
22
- setCursor(result.cursor);
23
- }, [buffer, cursor, pushToHistory]);
24
- const deleteChar = useCallback(() => {
25
- pushToHistory(buffer, cursor);
26
- const result = bufferDeleteChar(buffer, cursor);
27
- setBuffer(result.buffer);
28
- setCursor(result.cursor);
29
- }, [buffer, cursor, pushToHistory]);
30
- const newLine = useCallback(() => {
31
- pushToHistory(buffer, cursor);
32
- const result = bufferInsertNewLine(buffer, cursor);
33
- setBuffer(result.buffer);
34
- setCursor(result.cursor);
35
- }, [buffer, cursor, pushToHistory]);
36
- const moveCursor = useCallback((direction) => {
37
- const newCursor = bufferMoveCursor(buffer, cursor, direction);
38
- setCursor(newCursor);
39
- }, [buffer, cursor]);
40
- const undo = useCallback(() => {
41
- if (undoStack.length === 0)
42
- return;
43
- const previousState = undoStack[undoStack.length - 1];
44
- const newUndoStack = undoStack.slice(0, -1);
45
- setRedoStack((prev) => [...prev, { buffer, cursor }]);
46
- setBuffer(previousState.buffer);
47
- setCursor(previousState.cursor);
48
- setUndoStack(newUndoStack);
49
- }, [buffer, cursor, undoStack]);
50
- const redo = useCallback(() => {
51
- if (redoStack.length === 0)
52
- return;
53
- const nextState = redoStack[redoStack.length - 1];
54
- const newRedoStack = redoStack.slice(0, -1);
55
- setUndoStack((prev) => [...prev, { buffer, cursor }]);
56
- setBuffer(nextState.buffer);
57
- setCursor(nextState.cursor);
58
- setRedoStack(newRedoStack);
59
- }, [buffer, cursor, redoStack]);
60
- const setText = useCallback((text) => {
61
- pushToHistory(buffer, cursor);
62
- const newBuffer = createBuffer(text);
63
- setBuffer(newBuffer);
64
- // Move cursor to end of new text
65
- const lines = text.split('\n');
66
- setCursor({
67
- line: lines.length - 1,
68
- column: lines[lines.length - 1].length,
69
- });
70
- }, [buffer, cursor, pushToHistory]);
71
- return {
72
- value: getTextContent(buffer),
73
- cursor,
74
- insert,
75
- delete: deleteChar,
76
- newLine,
77
- moveCursor,
78
- undo,
79
- redo,
80
- setText,
81
- };
82
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,13 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- describe('Hello World Test', () => {
3
- it('should pass a simple test', () => {
4
- expect(true).toBe(true);
5
- });
6
- it('should perform basic arithmetic', () => {
7
- expect(2 + 2).toBe(4);
8
- });
9
- it('should handle string concatenation', () => {
10
- const greeting = 'Hello' + ' ' + 'World';
11
- expect(greeting).toBe('Hello World');
12
- });
13
- });
@@ -1,2 +0,0 @@
1
- export { MultilineInput } from './components/MultilineInput';
2
- export type { MultilineInputProps } from './components/MultilineInput';
@@ -1,2 +0,0 @@
1
- // Re-export all components and utilities
2
- export { MultilineInput } from './components/MultilineInput/index.js';