@elizaos/tui 2.0.0-alpha.10

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 (140) hide show
  1. package/README.md +761 -0
  2. package/dist/autocomplete.d.ts +48 -0
  3. package/dist/autocomplete.d.ts.map +1 -0
  4. package/dist/autocomplete.js +555 -0
  5. package/dist/components/box.d.ts +22 -0
  6. package/dist/components/box.d.ts.map +1 -0
  7. package/dist/components/box.js +103 -0
  8. package/dist/components/cancellable-loader.d.ts +22 -0
  9. package/dist/components/cancellable-loader.d.ts.map +1 -0
  10. package/dist/components/cancellable-loader.js +34 -0
  11. package/dist/components/editor/history.d.ts +38 -0
  12. package/dist/components/editor/history.d.ts.map +1 -0
  13. package/dist/components/editor/history.js +76 -0
  14. package/dist/components/editor/index.d.ts +18 -0
  15. package/dist/components/editor/index.d.ts.map +1 -0
  16. package/dist/components/editor/index.js +21 -0
  17. package/dist/components/editor/kill-ring.d.ts +41 -0
  18. package/dist/components/editor/kill-ring.d.ts.map +1 -0
  19. package/dist/components/editor/kill-ring.js +81 -0
  20. package/dist/components/editor/layout.d.ts +40 -0
  21. package/dist/components/editor/layout.d.ts.map +1 -0
  22. package/dist/components/editor/layout.js +205 -0
  23. package/dist/components/editor/types.d.ts +68 -0
  24. package/dist/components/editor/types.d.ts.map +1 -0
  25. package/dist/components/editor/types.js +4 -0
  26. package/dist/components/editor/undo.d.ts +46 -0
  27. package/dist/components/editor/undo.d.ts.map +1 -0
  28. package/dist/components/editor/undo.js +58 -0
  29. package/dist/components/editor.d.ts +196 -0
  30. package/dist/components/editor.d.ts.map +1 -0
  31. package/dist/components/editor.js +1707 -0
  32. package/dist/components/image.d.ts +28 -0
  33. package/dist/components/image.d.ts.map +1 -0
  34. package/dist/components/image.js +68 -0
  35. package/dist/components/input.d.ts +19 -0
  36. package/dist/components/input.d.ts.map +1 -0
  37. package/dist/components/input.js +195 -0
  38. package/dist/components/loader.d.ts +21 -0
  39. package/dist/components/loader.d.ts.map +1 -0
  40. package/dist/components/loader.js +49 -0
  41. package/dist/components/markdown/index.d.ts +13 -0
  42. package/dist/components/markdown/index.d.ts.map +1 -0
  43. package/dist/components/markdown/index.js +9 -0
  44. package/dist/components/markdown/inline-renderer.d.ts +22 -0
  45. package/dist/components/markdown/inline-renderer.d.ts.map +1 -0
  46. package/dist/components/markdown/inline-renderer.js +88 -0
  47. package/dist/components/markdown/list-renderer.d.ts +33 -0
  48. package/dist/components/markdown/list-renderer.d.ts.map +1 -0
  49. package/dist/components/markdown/list-renderer.js +110 -0
  50. package/dist/components/markdown/table-renderer.d.ts +43 -0
  51. package/dist/components/markdown/table-renderer.d.ts.map +1 -0
  52. package/dist/components/markdown/table-renderer.js +184 -0
  53. package/dist/components/markdown/types.d.ts +57 -0
  54. package/dist/components/markdown/types.d.ts.map +1 -0
  55. package/dist/components/markdown/types.js +13 -0
  56. package/dist/components/markdown.d.ts +44 -0
  57. package/dist/components/markdown.d.ts.map +1 -0
  58. package/dist/components/markdown.js +319 -0
  59. package/dist/components/progress-bar.d.ts +67 -0
  60. package/dist/components/progress-bar.d.ts.map +1 -0
  61. package/dist/components/progress-bar.js +124 -0
  62. package/dist/components/select-list.d.ts +32 -0
  63. package/dist/components/select-list.d.ts.map +1 -0
  64. package/dist/components/select-list.js +151 -0
  65. package/dist/components/settings-list.d.ts +50 -0
  66. package/dist/components/settings-list.d.ts.map +1 -0
  67. package/dist/components/settings-list.js +184 -0
  68. package/dist/components/spacer.d.ts +12 -0
  69. package/dist/components/spacer.d.ts.map +1 -0
  70. package/dist/components/spacer.js +22 -0
  71. package/dist/components/text.d.ts +19 -0
  72. package/dist/components/text.d.ts.map +1 -0
  73. package/dist/components/text.js +88 -0
  74. package/dist/components/toast.d.ts +73 -0
  75. package/dist/components/toast.d.ts.map +1 -0
  76. package/dist/components/toast.js +119 -0
  77. package/dist/components/truncated-text.d.ts +13 -0
  78. package/dist/components/truncated-text.d.ts.map +1 -0
  79. package/dist/components/truncated-text.js +50 -0
  80. package/dist/constants.d.ts +97 -0
  81. package/dist/constants.d.ts.map +1 -0
  82. package/dist/constants.js +126 -0
  83. package/dist/core/container.d.ts +32 -0
  84. package/dist/core/container.d.ts.map +1 -0
  85. package/dist/core/container.js +49 -0
  86. package/dist/core/index.d.ts +9 -0
  87. package/dist/core/index.d.ts.map +1 -0
  88. package/dist/core/index.js +10 -0
  89. package/dist/core/overlay.d.ts +44 -0
  90. package/dist/core/overlay.d.ts.map +1 -0
  91. package/dist/core/overlay.js +171 -0
  92. package/dist/core/types.d.ts +116 -0
  93. package/dist/core/types.d.ts.map +1 -0
  94. package/dist/core/types.js +14 -0
  95. package/dist/editor-component.d.ts +37 -0
  96. package/dist/editor-component.d.ts.map +1 -0
  97. package/dist/editor-component.js +1 -0
  98. package/dist/fuzzy.d.ts +16 -0
  99. package/dist/fuzzy.d.ts.map +1 -0
  100. package/dist/fuzzy.js +108 -0
  101. package/dist/index.d.ts +29 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +59 -0
  104. package/dist/keybindings.d.ts +39 -0
  105. package/dist/keybindings.d.ts.map +1 -0
  106. package/dist/keybindings.js +113 -0
  107. package/dist/keys.d.ts +153 -0
  108. package/dist/keys.d.ts.map +1 -0
  109. package/dist/keys.js +951 -0
  110. package/dist/stdin-buffer.d.ts +48 -0
  111. package/dist/stdin-buffer.d.ts.map +1 -0
  112. package/dist/stdin-buffer.js +316 -0
  113. package/dist/terminal-image.d.ts +68 -0
  114. package/dist/terminal-image.d.ts.map +1 -0
  115. package/dist/terminal-image.js +287 -0
  116. package/dist/terminal.d.ts +71 -0
  117. package/dist/terminal.d.ts.map +1 -0
  118. package/dist/terminal.js +216 -0
  119. package/dist/themes/index.d.ts +103 -0
  120. package/dist/themes/index.d.ts.map +1 -0
  121. package/dist/themes/index.js +161 -0
  122. package/dist/tui.d.ts +90 -0
  123. package/dist/tui.d.ts.map +1 -0
  124. package/dist/tui.js +745 -0
  125. package/dist/types/marked-tokens.d.ts +57 -0
  126. package/dist/types/marked-tokens.d.ts.map +1 -0
  127. package/dist/types/marked-tokens.js +17 -0
  128. package/dist/utils/cursor-movement.d.ts +127 -0
  129. package/dist/utils/cursor-movement.d.ts.map +1 -0
  130. package/dist/utils/cursor-movement.js +251 -0
  131. package/dist/utils/index.d.ts +6 -0
  132. package/dist/utils/index.d.ts.map +1 -0
  133. package/dist/utils/index.js +7 -0
  134. package/dist/utils/paste-handler.d.ts +86 -0
  135. package/dist/utils/paste-handler.d.ts.map +1 -0
  136. package/dist/utils/paste-handler.js +121 -0
  137. package/dist/utils.d.ts +75 -0
  138. package/dist/utils.d.ts.map +1 -0
  139. package/dist/utils.js +796 -0
  140. package/package.json +53 -0
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Text layout and word wrapping utilities for the Editor component.
3
+ */
4
+ import { getSegmenter, isWhitespaceChar, visibleWidth } from "../../utils.js";
5
+ const segmenter = getSegmenter();
6
+ /**
7
+ * Split a line into word-wrapped chunks.
8
+ * Wraps at word boundaries when possible, falling back to character-level
9
+ * wrapping for words longer than the available width.
10
+ *
11
+ * @param line - The text line to wrap
12
+ * @param maxWidth - Maximum visible width per chunk
13
+ * @returns Array of chunks with text and position information
14
+ */
15
+ export function wordWrapLine(line, maxWidth) {
16
+ if (!line || maxWidth <= 0) {
17
+ return [{ text: "", startIndex: 0, endIndex: 0 }];
18
+ }
19
+ const lineWidth = visibleWidth(line);
20
+ if (lineWidth <= maxWidth) {
21
+ return [{ text: line, startIndex: 0, endIndex: line.length }];
22
+ }
23
+ const chunks = [];
24
+ const segments = [...segmenter.segment(line)];
25
+ let currentWidth = 0;
26
+ let chunkStart = 0;
27
+ // Wrap opportunity: the position after the last whitespace before a non-whitespace
28
+ // grapheme, i.e. where a line break is allowed.
29
+ let wrapOppIndex = -1;
30
+ let wrapOppWidth = 0;
31
+ for (let i = 0; i < segments.length; i++) {
32
+ const seg = segments[i];
33
+ const grapheme = seg.segment;
34
+ const gWidth = visibleWidth(grapheme);
35
+ const charIndex = seg.index;
36
+ const isWs = isWhitespaceChar(grapheme);
37
+ // Overflow check before advancing.
38
+ if (currentWidth + gWidth > maxWidth) {
39
+ if (wrapOppIndex >= 0) {
40
+ // Backtrack to last wrap opportunity.
41
+ chunks.push({ text: line.slice(chunkStart, wrapOppIndex), startIndex: chunkStart, endIndex: wrapOppIndex });
42
+ chunkStart = wrapOppIndex;
43
+ currentWidth -= wrapOppWidth;
44
+ }
45
+ else if (chunkStart < charIndex) {
46
+ // No wrap opportunity: force-break at current position.
47
+ chunks.push({ text: line.slice(chunkStart, charIndex), startIndex: chunkStart, endIndex: charIndex });
48
+ chunkStart = charIndex;
49
+ currentWidth = 0;
50
+ }
51
+ wrapOppIndex = -1;
52
+ }
53
+ // Advance.
54
+ currentWidth += gWidth;
55
+ // Record wrap opportunity: whitespace followed by non-whitespace.
56
+ // Multiple spaces join (no break between them); the break point is
57
+ // after the last space before the next word.
58
+ const next = segments[i + 1];
59
+ if (isWs && next && !isWhitespaceChar(next.segment)) {
60
+ wrapOppIndex = next.index;
61
+ wrapOppWidth = currentWidth;
62
+ }
63
+ }
64
+ // Push final chunk.
65
+ chunks.push({ text: line.slice(chunkStart), startIndex: chunkStart, endIndex: line.length });
66
+ return chunks;
67
+ }
68
+ /**
69
+ * Layout text into visual lines for rendering.
70
+ *
71
+ * @param state - Current editor state
72
+ * @param contentWidth - Maximum width for content
73
+ * @returns Array of layout lines ready for rendering
74
+ */
75
+ export function layoutText(state, contentWidth) {
76
+ const layoutLines = [];
77
+ if (state.lines.length === 0 || (state.lines.length === 1 && state.lines[0] === "")) {
78
+ // Empty editor
79
+ layoutLines.push({
80
+ text: "",
81
+ hasCursor: true,
82
+ cursorPos: 0,
83
+ });
84
+ return layoutLines;
85
+ }
86
+ // Process each logical line
87
+ for (let i = 0; i < state.lines.length; i++) {
88
+ const line = state.lines[i];
89
+ const isCurrentLine = i === state.cursorLine;
90
+ const lineVisibleWidth = visibleWidth(line);
91
+ if (lineVisibleWidth <= contentWidth) {
92
+ // Line fits in one layout line
93
+ if (isCurrentLine) {
94
+ layoutLines.push({
95
+ text: line,
96
+ hasCursor: true,
97
+ cursorPos: state.cursorCol,
98
+ });
99
+ }
100
+ else {
101
+ layoutLines.push({
102
+ text: line,
103
+ hasCursor: false,
104
+ });
105
+ }
106
+ }
107
+ else {
108
+ // Line needs wrapping - use word-aware wrapping
109
+ const chunks = wordWrapLine(line, contentWidth);
110
+ for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex++) {
111
+ const chunk = chunks[chunkIndex];
112
+ if (!chunk)
113
+ continue;
114
+ const cursorPos = state.cursorCol;
115
+ const isLastChunk = chunkIndex === chunks.length - 1;
116
+ // Determine if cursor is in this chunk
117
+ let hasCursorInChunk = false;
118
+ let adjustedCursorPos = 0;
119
+ if (isCurrentLine) {
120
+ if (isLastChunk) {
121
+ // Last chunk: cursor belongs here if >= startIndex
122
+ hasCursorInChunk = cursorPos >= chunk.startIndex;
123
+ adjustedCursorPos = cursorPos - chunk.startIndex;
124
+ }
125
+ else {
126
+ // Non-last chunk: cursor belongs here if in range [startIndex, endIndex)
127
+ hasCursorInChunk = cursorPos >= chunk.startIndex && cursorPos < chunk.endIndex;
128
+ if (hasCursorInChunk) {
129
+ adjustedCursorPos = cursorPos - chunk.startIndex;
130
+ // Clamp to text length (in case cursor was in trimmed whitespace)
131
+ if (adjustedCursorPos > chunk.text.length) {
132
+ adjustedCursorPos = chunk.text.length;
133
+ }
134
+ }
135
+ }
136
+ }
137
+ layoutLines.push({
138
+ text: chunk.text,
139
+ hasCursor: hasCursorInChunk,
140
+ cursorPos: hasCursorInChunk ? adjustedCursorPos : undefined,
141
+ });
142
+ }
143
+ }
144
+ }
145
+ return layoutLines;
146
+ }
147
+ /**
148
+ * Build a mapping from visual lines to logical positions.
149
+ *
150
+ * @param state - Current editor state
151
+ * @param width - Maximum width for content
152
+ * @returns Array where each element represents a visual line
153
+ */
154
+ export function buildVisualLineMap(state, width) {
155
+ const visualLines = [];
156
+ for (let i = 0; i < state.lines.length; i++) {
157
+ const line = state.lines[i];
158
+ const lineVisWidth = visibleWidth(line);
159
+ if (line.length === 0) {
160
+ // Empty line still takes one visual line
161
+ visualLines.push({ logicalLine: i, startCol: 0, length: 0 });
162
+ }
163
+ else if (lineVisWidth <= width) {
164
+ visualLines.push({ logicalLine: i, startCol: 0, length: line.length });
165
+ }
166
+ else {
167
+ // Line needs wrapping - use word-aware wrapping
168
+ const chunks = wordWrapLine(line, width);
169
+ for (const chunk of chunks) {
170
+ visualLines.push({
171
+ logicalLine: i,
172
+ startCol: chunk.startIndex,
173
+ length: chunk.endIndex - chunk.startIndex,
174
+ });
175
+ }
176
+ }
177
+ }
178
+ return visualLines;
179
+ }
180
+ /**
181
+ * Find the visual line index for the current cursor position.
182
+ *
183
+ * @param visualLines - Visual line mappings
184
+ * @param cursorLine - Current logical line
185
+ * @param cursorCol - Current column position
186
+ * @returns Visual line index
187
+ */
188
+ export function findCurrentVisualLine(visualLines, cursorLine, cursorCol) {
189
+ for (let i = 0; i < visualLines.length; i++) {
190
+ const vl = visualLines[i];
191
+ if (!vl)
192
+ continue;
193
+ if (vl.logicalLine === cursorLine) {
194
+ const colInSegment = cursorCol - vl.startCol;
195
+ // Cursor is in this segment if it's within range
196
+ // For the last segment of a logical line, cursor can be at length (end position)
197
+ const isLastSegmentOfLine = i === visualLines.length - 1 || visualLines[i + 1]?.logicalLine !== vl.logicalLine;
198
+ if (colInSegment >= 0 && (colInSegment < vl.length || (isLastSegmentOfLine && colInSegment <= vl.length))) {
199
+ return i;
200
+ }
201
+ }
202
+ }
203
+ // Fallback: return last visual line
204
+ return visualLines.length - 1;
205
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Type definitions for the Editor component.
3
+ */
4
+ import type { SelectListTheme } from "../select-list.js";
5
+ /**
6
+ * Editor state containing the text content and cursor position.
7
+ */
8
+ export interface EditorState {
9
+ /** Lines of text in the editor */
10
+ lines: string[];
11
+ /** Current logical line index (0-based) */
12
+ cursorLine: number;
13
+ /** Current column position within the line */
14
+ cursorCol: number;
15
+ }
16
+ /**
17
+ * A single layout line after word wrapping.
18
+ */
19
+ export interface LayoutLine {
20
+ /** The text content of this layout line */
21
+ text: string;
22
+ /** Whether this line contains the cursor */
23
+ hasCursor: boolean;
24
+ /** Position of cursor within this line (if hasCursor is true) */
25
+ cursorPos?: number;
26
+ }
27
+ /**
28
+ * Theme configuration for the Editor component.
29
+ */
30
+ export interface EditorTheme {
31
+ /** Function to apply border color styling */
32
+ borderColor: (str: string) => string;
33
+ /** Theme for the autocomplete dropdown */
34
+ selectList: SelectListTheme;
35
+ }
36
+ /**
37
+ * Options for the Editor component.
38
+ */
39
+ export interface EditorOptions {
40
+ /** Horizontal padding (default: 0) */
41
+ paddingX?: number;
42
+ /** Maximum visible items in autocomplete dropdown (default: 5) */
43
+ autocompleteMaxVisible?: number;
44
+ }
45
+ /**
46
+ * Represents a chunk of text for word-wrap layout.
47
+ * Tracks both the text content and its position in the original line.
48
+ */
49
+ export interface TextChunk {
50
+ /** The text content of this chunk */
51
+ text: string;
52
+ /** Starting index in the original line */
53
+ startIndex: number;
54
+ /** Ending index in the original line */
55
+ endIndex: number;
56
+ }
57
+ /**
58
+ * Visual line mapping for cursor navigation.
59
+ */
60
+ export interface VisualLineMapping {
61
+ /** Index into the logical lines array */
62
+ logicalLine: number;
63
+ /** Starting column in the logical line */
64
+ startCol: number;
65
+ /** Length of this visual line segment */
66
+ length: number;
67
+ }
68
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/editor/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,kCAAkC;IAClC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,SAAS,EAAE,OAAO,CAAC;IACnB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,6CAA6C;IAC7C,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,0CAA0C;IAC1C,UAAU,EAAE,eAAe,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,sBAAsB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACzB,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;CACf"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Type definitions for the Editor component.
3
+ */
4
+ export {};
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Undo/redo support for the Editor component.
3
+ */
4
+ import type { EditorState } from "./types.js";
5
+ /**
6
+ * Manages undo stack for the Editor.
7
+ */
8
+ export declare class UndoManager {
9
+ private stack;
10
+ /**
11
+ * Capture a snapshot of the current state.
12
+ *
13
+ * @param state - Current editor state
14
+ * @returns Deep clone of the state
15
+ */
16
+ captureSnapshot(state: EditorState): EditorState;
17
+ /**
18
+ * Push a snapshot onto the undo stack.
19
+ *
20
+ * @param state - Current editor state to save
21
+ */
22
+ push(state: EditorState): void;
23
+ /**
24
+ * Pop and return the most recent snapshot.
25
+ *
26
+ * @returns The previous state, or undefined if stack is empty
27
+ */
28
+ pop(): EditorState | undefined;
29
+ /**
30
+ * Check if there are any undo snapshots available.
31
+ */
32
+ canUndo(): boolean;
33
+ /**
34
+ * Clear the undo stack.
35
+ */
36
+ clear(): void;
37
+ get length(): number;
38
+ }
39
+ /**
40
+ * Restore an undo snapshot to the target state object.
41
+ *
42
+ * @param target - State object to restore to
43
+ * @param snapshot - Snapshot to restore
44
+ */
45
+ export declare function restoreSnapshot(target: EditorState, snapshot: EditorState): void;
46
+ //# sourceMappingURL=undo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undo.d.ts","sourceRoot":"","sources":["../../../src/components/editor/undo.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;GAEG;AACH,qBAAa,WAAW;IACvB,OAAO,CAAC,KAAK,CAAqB;IAElC;;;;;OAKG;IACH,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW;IAIhD;;;;OAIG;IACH,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAI9B;;;;OAIG;IACH,GAAG,IAAI,WAAW,GAAG,SAAS;IAI9B;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb,IAAI,MAAM,IAAI,MAAM,CAEnB;CACD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,GAAG,IAAI,CAEhF"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Undo/redo support for the Editor component.
3
+ */
4
+ /**
5
+ * Manages undo stack for the Editor.
6
+ */
7
+ export class UndoManager {
8
+ stack = [];
9
+ /**
10
+ * Capture a snapshot of the current state.
11
+ *
12
+ * @param state - Current editor state
13
+ * @returns Deep clone of the state
14
+ */
15
+ captureSnapshot(state) {
16
+ return structuredClone(state);
17
+ }
18
+ /**
19
+ * Push a snapshot onto the undo stack.
20
+ *
21
+ * @param state - Current editor state to save
22
+ */
23
+ push(state) {
24
+ this.stack.push(this.captureSnapshot(state));
25
+ }
26
+ /**
27
+ * Pop and return the most recent snapshot.
28
+ *
29
+ * @returns The previous state, or undefined if stack is empty
30
+ */
31
+ pop() {
32
+ return this.stack.pop();
33
+ }
34
+ /**
35
+ * Check if there are any undo snapshots available.
36
+ */
37
+ canUndo() {
38
+ return this.stack.length > 0;
39
+ }
40
+ /**
41
+ * Clear the undo stack.
42
+ */
43
+ clear() {
44
+ this.stack.length = 0;
45
+ }
46
+ get length() {
47
+ return this.stack.length;
48
+ }
49
+ }
50
+ /**
51
+ * Restore an undo snapshot to the target state object.
52
+ *
53
+ * @param target - State object to restore to
54
+ * @param snapshot - Snapshot to restore
55
+ */
56
+ export function restoreSnapshot(target, snapshot) {
57
+ Object.assign(target, structuredClone(snapshot));
58
+ }
@@ -0,0 +1,196 @@
1
+ import type { AutocompleteProvider } from "../autocomplete.js";
2
+ import { type Component, type Focusable, type TUI } from "../tui.js";
3
+ export { DEFAULT_HISTORY_LIMIT, EditorHistory } from "./editor/history.js";
4
+ export { KillRing } from "./editor/kill-ring.js";
5
+ export { buildVisualLineMap, findCurrentVisualLine, layoutText, wordWrapLine, } from "./editor/layout.js";
6
+ export type { EditorOptions, EditorState, EditorTheme, LayoutLine, TextChunk, VisualLineMapping, } from "./editor/types.js";
7
+ export { restoreSnapshot, UndoManager } from "./editor/undo.js";
8
+ import type { EditorOptions, EditorTheme } from "./editor/types.js";
9
+ export declare class Editor implements Component, Focusable {
10
+ private state;
11
+ /** Get line at index, returns empty string for invalid indices */
12
+ private getLine;
13
+ /** Get current line at cursor */
14
+ private get currentLine();
15
+ /** Focusable interface - set by TUI when focus changes */
16
+ focused: boolean;
17
+ protected tui: TUI;
18
+ private theme;
19
+ private paddingX;
20
+ private lastWidth;
21
+ private scrollOffset;
22
+ borderColor: (str: string) => string;
23
+ private autocompleteProvider?;
24
+ private autocompleteList?;
25
+ private autocompleteState;
26
+ private autocompletePrefix;
27
+ private autocompleteMaxVisible;
28
+ private pastes;
29
+ private pasteCounter;
30
+ private pasteBuffer;
31
+ private isInPaste;
32
+ private history;
33
+ private historyIndex;
34
+ private killRing;
35
+ private lastAction;
36
+ private jumpMode;
37
+ private preferredVisualCol;
38
+ private undoStack;
39
+ onSubmit?: (text: string) => void;
40
+ onChange?: (text: string) => void;
41
+ disableSubmit: boolean;
42
+ constructor(tui: TUI, theme: EditorTheme, options?: EditorOptions);
43
+ getPaddingX(): number;
44
+ setPaddingX(padding: number): void;
45
+ getAutocompleteMaxVisible(): number;
46
+ setAutocompleteMaxVisible(maxVisible: number): void;
47
+ setAutocompleteProvider(provider: AutocompleteProvider): void;
48
+ /**
49
+ * Add a prompt to history for up/down arrow navigation.
50
+ * Called after successful submission.
51
+ */
52
+ addToHistory(text: string): void;
53
+ private isEditorEmpty;
54
+ private isOnFirstVisualLine;
55
+ private isOnLastVisualLine;
56
+ private navigateHistory;
57
+ /** Internal setText that doesn't reset history state - used by navigateHistory */
58
+ private setTextInternal;
59
+ invalidate(): void;
60
+ render(width: number): string[];
61
+ handleInput(data: string): void;
62
+ private layoutText;
63
+ getText(): string;
64
+ /**
65
+ * Get text with paste markers expanded to their actual content.
66
+ * Use this when you need the full content (e.g., for external editor).
67
+ */
68
+ getExpandedText(): string;
69
+ getLines(): string[];
70
+ getCursor(): {
71
+ line: number;
72
+ col: number;
73
+ };
74
+ setText(text: string): void;
75
+ /**
76
+ * Insert text at the current cursor position.
77
+ * Used for programmatic insertion (e.g., clipboard image markers).
78
+ * This is atomic for undo - single undo restores entire pre-insert state.
79
+ */
80
+ insertTextAtCursor(text: string): void;
81
+ /**
82
+ * Internal text insertion at cursor. Handles single and multi-line text.
83
+ * Does not push undo snapshots or trigger autocomplete - caller is responsible.
84
+ * Normalizes line endings and calls onChange once at the end.
85
+ */
86
+ private insertTextAtCursorInternal;
87
+ private insertCharacter;
88
+ private handlePaste;
89
+ private addNewLine;
90
+ private shouldSubmitOnBackslashEnter;
91
+ private submitValue;
92
+ private handleBackspace;
93
+ /**
94
+ * Set cursor column and clear preferredVisualCol.
95
+ * Use this for all non-vertical cursor movements to reset sticky column behavior.
96
+ */
97
+ private setCursorCol;
98
+ /**
99
+ * Move cursor to a target visual line, applying sticky column logic.
100
+ * Shared by moveCursor() and pageScroll().
101
+ */
102
+ private moveToVisualLine;
103
+ /**
104
+ * Compute the target visual column for vertical cursor movement.
105
+ * Implements the sticky column decision table:
106
+ *
107
+ * | P | S | T | U | Scenario | Set Preferred | Move To |
108
+ * |---|---|---|---| ---------------------------------------------------- |---------------|-------------|
109
+ * | 0 | * | 0 | - | Start nav, target fits | null | current |
110
+ * | 0 | * | 1 | - | Start nav, target shorter | current | target end |
111
+ * | 1 | 0 | 0 | 0 | Clamped, target fits preferred | null | preferred |
112
+ * | 1 | 0 | 0 | 1 | Clamped, target longer but still can't fit preferred | keep | target end |
113
+ * | 1 | 0 | 1 | - | Clamped, target even shorter | keep | target end |
114
+ * | 1 | 1 | 0 | - | Rewrapped, target fits current | null | current |
115
+ * | 1 | 1 | 1 | - | Rewrapped, target shorter than current | current | target end |
116
+ *
117
+ * Where:
118
+ * - P = preferred col is set
119
+ * - S = cursor in middle of source line (not clamped to end)
120
+ * - T = target line shorter than current visual col
121
+ * - U = target line shorter than preferred col
122
+ */
123
+ private computeVerticalMoveColumn;
124
+ private moveToLineStart;
125
+ private moveToLineEnd;
126
+ private deleteToStartOfLine;
127
+ private deleteToEndOfLine;
128
+ private deleteWordBackwards;
129
+ private deleteWordForward;
130
+ private handleForwardDelete;
131
+ /**
132
+ * Build a mapping from visual lines to logical positions.
133
+ * Returns an array where each element represents a visual line with:
134
+ * - logicalLine: index into this.state.lines
135
+ * - startCol: starting column in the logical line
136
+ * - length: length of this visual line segment
137
+ */
138
+ private buildVisualLineMap;
139
+ /**
140
+ * Find the visual line index for the current cursor position.
141
+ */
142
+ private findCurrentVisualLine;
143
+ private moveCursor;
144
+ /**
145
+ * Scroll by a page (direction: -1 for up, 1 for down).
146
+ * Moves cursor by the page size while keeping it in bounds.
147
+ */
148
+ private pageScroll;
149
+ private moveWordBackwards;
150
+ /**
151
+ * Yank (paste) the most recent kill ring entry at cursor position.
152
+ */
153
+ private yank;
154
+ /**
155
+ * Cycle through kill ring (only works immediately after yank or yank-pop).
156
+ * Replaces the last yanked text with the previous entry in the ring.
157
+ */
158
+ private yankPop;
159
+ /**
160
+ * Insert text at cursor position (used by yank operations).
161
+ */
162
+ private insertYankedText;
163
+ /**
164
+ * Delete the previously yanked text (used by yank-pop).
165
+ * The yanked text is derived from killRing[end] since it hasn't been rotated yet.
166
+ */
167
+ private deleteYankedText;
168
+ /**
169
+ * Add text to the kill ring.
170
+ * If lastAction is "kill", accumulates with the previous entry.
171
+ * @param text - The text to add
172
+ * @param prepend - If accumulating, prepend (true) or append (false) to existing entry
173
+ */
174
+ private addToKillRing;
175
+ private captureUndoSnapshot;
176
+ private restoreUndoSnapshot;
177
+ private pushUndoSnapshot;
178
+ private undo;
179
+ /**
180
+ * Jump to the first occurrence of a character in the specified direction.
181
+ * Multi-line search. Case-sensitive. Skips the current cursor position.
182
+ */
183
+ private jumpToChar;
184
+ private moveWordForwards;
185
+ private isSlashMenuAllowed;
186
+ private isAtStartOfMessage;
187
+ private isInSlashCommandContext;
188
+ private tryTriggerAutocomplete;
189
+ private handleTabCompletion;
190
+ private handleSlashCommandCompletion;
191
+ private forceFileAutocomplete;
192
+ private cancelAutocomplete;
193
+ isShowingAutocomplete(): boolean;
194
+ private updateAutocomplete;
195
+ }
196
+ //# sourceMappingURL=editor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/components/editor.ts"],"names":[],"mappings":"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"}