@townco/ui 0.1.17 → 0.1.19

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 (214) hide show
  1. package/dist/core/hooks/use-chat-input.d.ts +17 -17
  2. package/dist/core/hooks/use-chat-input.js +55 -64
  3. package/dist/core/hooks/use-chat-messages.js +114 -121
  4. package/dist/core/hooks/use-chat-session.d.ts +1 -1
  5. package/dist/core/hooks/use-chat-session.js +80 -78
  6. package/dist/gui/components/Button.d.ts +7 -23
  7. package/dist/gui/components/Button.js +27 -40
  8. package/dist/gui/components/Card.d.ts +7 -26
  9. package/dist/gui/components/Card.js +8 -54
  10. package/dist/gui/components/ChatEmptyState.d.ts +18 -0
  11. package/dist/gui/components/ChatEmptyState.js +22 -0
  12. package/dist/gui/components/ChatInput.js +29 -1
  13. package/dist/gui/components/ChatLayout.js +2 -2
  14. package/dist/gui/components/ChatSecondaryPanel.d.ts +25 -14
  15. package/dist/gui/components/ChatSecondaryPanel.js +60 -115
  16. package/dist/gui/components/ChatStatus.d.ts +2 -4
  17. package/dist/gui/components/ChatStatus.js +34 -45
  18. package/dist/gui/components/Conversation.d.ts +14 -17
  19. package/dist/gui/components/Conversation.js +83 -143
  20. package/dist/gui/components/Dialog.d.ts +11 -57
  21. package/dist/gui/components/HeightTransition.d.ts +7 -12
  22. package/dist/gui/components/HeightTransition.js +77 -88
  23. package/dist/gui/components/Input.d.ts +6 -13
  24. package/dist/gui/components/Input.js +16 -27
  25. package/dist/gui/components/Label.d.ts +1 -7
  26. package/dist/gui/components/MarkdownRenderer.d.ts +4 -6
  27. package/dist/gui/components/MarkdownRenderer.js +81 -178
  28. package/dist/gui/components/Message.d.ts +1 -1
  29. package/dist/gui/components/MessageContent.d.ts +22 -29
  30. package/dist/gui/components/PanelTabsHeader.d.ts +17 -0
  31. package/dist/gui/components/PanelTabsHeader.js +31 -0
  32. package/dist/gui/components/Reasoning.d.ts +24 -30
  33. package/dist/gui/components/Reasoning.js +60 -187
  34. package/dist/gui/components/Response.d.ts +9 -11
  35. package/dist/gui/components/Response.js +90 -229
  36. package/dist/gui/components/Select.d.ts +10 -69
  37. package/dist/gui/components/Tabs.d.ts +4 -24
  38. package/dist/gui/components/Task.d.ts +24 -28
  39. package/dist/gui/components/Task.js +31 -164
  40. package/dist/gui/components/Textarea.d.ts +7 -15
  41. package/dist/gui/components/Textarea.js +46 -63
  42. package/dist/gui/components/ThinkingBlock.d.ts +10 -20
  43. package/dist/gui/components/ThinkingBlock.js +35 -134
  44. package/dist/gui/components/TodoList.d.ts +10 -12
  45. package/dist/gui/components/TodoList.js +7 -22
  46. package/dist/gui/components/TodoListItem.d.ts +6 -9
  47. package/dist/gui/components/TodoListItem.js +4 -18
  48. package/dist/gui/components/index.d.ts +2 -0
  49. package/dist/gui/components/index.js +2 -0
  50. package/dist/gui/lib/utils.js +1 -1
  51. package/dist/index.test.js +1 -0
  52. package/dist/sdk/client/acp-client.d.ts +76 -88
  53. package/dist/sdk/client/acp-client.js +217 -215
  54. package/dist/sdk/schemas/agent.d.ts +64 -111
  55. package/dist/sdk/schemas/agent.js +24 -24
  56. package/dist/sdk/schemas/message.d.ts +147 -245
  57. package/dist/sdk/schemas/message.js +40 -40
  58. package/dist/sdk/schemas/session.d.ts +6 -6
  59. package/dist/sdk/transports/http.d.ts +55 -55
  60. package/dist/sdk/transports/http.js +3 -3
  61. package/dist/sdk/transports/stdio.d.ts +20 -20
  62. package/dist/sdk/transports/types.d.ts +42 -42
  63. package/dist/sdk/transports/websocket.d.ts +12 -12
  64. package/dist/sdk/transports/websocket.js +46 -52
  65. package/dist/tui/components/ChatView.d.ts +2 -4
  66. package/dist/tui/components/GameOfLife.js +35 -64
  67. package/dist/tui/components/InputBox.d.ts +11 -18
  68. package/dist/tui/components/InputBox.js +10 -70
  69. package/dist/tui/components/ReadlineInput.d.ts +6 -12
  70. package/dist/tui/components/ReadlineInput.js +237 -252
  71. package/dist/tui/components/SingleSelect.d.ts +9 -15
  72. package/dist/tui/components/SingleSelect.js +43 -84
  73. package/dist/tui/components/StatusBar.d.ts +6 -11
  74. package/dist/tui/components/StatusBar.js +67 -102
  75. package/package.json +2 -2
  76. package/src/styles/global.css +64 -0
  77. package/dist/core/hooks/index.d.ts.map +0 -1
  78. package/dist/core/hooks/index.js.map +0 -1
  79. package/dist/core/hooks/use-chat-input.d.ts.map +0 -1
  80. package/dist/core/hooks/use-chat-input.js.map +0 -1
  81. package/dist/core/hooks/use-chat-messages.d.ts.map +0 -1
  82. package/dist/core/hooks/use-chat-messages.js.map +0 -1
  83. package/dist/core/hooks/use-chat-session.d.ts.map +0 -1
  84. package/dist/core/hooks/use-chat-session.js.map +0 -1
  85. package/dist/core/index.d.ts.map +0 -1
  86. package/dist/core/index.js.map +0 -1
  87. package/dist/core/schemas/chat.d.ts.map +0 -1
  88. package/dist/core/schemas/chat.js.map +0 -1
  89. package/dist/core/schemas/index.d.ts.map +0 -1
  90. package/dist/core/schemas/index.js.map +0 -1
  91. package/dist/core/store/chat-store.d.ts.map +0 -1
  92. package/dist/core/store/chat-store.js.map +0 -1
  93. package/dist/gui/components/Button.d.ts.map +0 -1
  94. package/dist/gui/components/Button.js.map +0 -1
  95. package/dist/gui/components/Card.d.ts.map +0 -1
  96. package/dist/gui/components/Card.js.map +0 -1
  97. package/dist/gui/components/ChatInput.d.ts.map +0 -1
  98. package/dist/gui/components/ChatInput.js.map +0 -1
  99. package/dist/gui/components/ChatInterface.d.ts +0 -12
  100. package/dist/gui/components/ChatInterface.d.ts.map +0 -1
  101. package/dist/gui/components/ChatInterface.js +0 -204
  102. package/dist/gui/components/ChatInterface.js.map +0 -1
  103. package/dist/gui/components/ChatPreview.d.ts +0 -12
  104. package/dist/gui/components/ChatPreview.d.ts.map +0 -1
  105. package/dist/gui/components/ChatPreview.js +0 -214
  106. package/dist/gui/components/ChatPreview.js.map +0 -1
  107. package/dist/gui/components/ChatSecondaryPanel.d.ts.map +0 -1
  108. package/dist/gui/components/ChatSecondaryPanel.js.map +0 -1
  109. package/dist/gui/components/ChatStatus.d.ts.map +0 -1
  110. package/dist/gui/components/ChatStatus.js.map +0 -1
  111. package/dist/gui/components/ChatView.d.ts +0 -8
  112. package/dist/gui/components/ChatView.d.ts.map +0 -1
  113. package/dist/gui/components/ChatView.js +0 -42
  114. package/dist/gui/components/ChatView.js.map +0 -1
  115. package/dist/gui/components/ConfigPanel.d.ts +0 -20
  116. package/dist/gui/components/ConfigPanel.d.ts.map +0 -1
  117. package/dist/gui/components/ConfigPanel.js +0 -225
  118. package/dist/gui/components/ConfigPanel.js.map +0 -1
  119. package/dist/gui/components/Conversation.d.ts.map +0 -1
  120. package/dist/gui/components/Conversation.js.map +0 -1
  121. package/dist/gui/components/Dialog.d.ts.map +0 -1
  122. package/dist/gui/components/Dialog.js.map +0 -1
  123. package/dist/gui/components/HeightTransition.d.ts.map +0 -1
  124. package/dist/gui/components/HeightTransition.js.map +0 -1
  125. package/dist/gui/components/Input.d.ts.map +0 -1
  126. package/dist/gui/components/Input.js.map +0 -1
  127. package/dist/gui/components/InputBox.d.ts +0 -21
  128. package/dist/gui/components/InputBox.d.ts.map +0 -1
  129. package/dist/gui/components/InputBox.js +0 -90
  130. package/dist/gui/components/InputBox.js.map +0 -1
  131. package/dist/gui/components/Label.d.ts.map +0 -1
  132. package/dist/gui/components/Label.js.map +0 -1
  133. package/dist/gui/components/MarkdownRenderer.d.ts.map +0 -1
  134. package/dist/gui/components/MarkdownRenderer.js.map +0 -1
  135. package/dist/gui/components/Message.d.ts.map +0 -1
  136. package/dist/gui/components/Message.js.map +0 -1
  137. package/dist/gui/components/MessageContent.d.ts.map +0 -1
  138. package/dist/gui/components/MessageContent.js.map +0 -1
  139. package/dist/gui/components/MessageList.d.ts.map +0 -1
  140. package/dist/gui/components/MessageList.js.map +0 -1
  141. package/dist/gui/components/PlaygroundLayout.d.ts +0 -14
  142. package/dist/gui/components/PlaygroundLayout.d.ts.map +0 -1
  143. package/dist/gui/components/PlaygroundLayout.js +0 -49
  144. package/dist/gui/components/PlaygroundLayout.js.map +0 -1
  145. package/dist/gui/components/Reasoning.d.ts.map +0 -1
  146. package/dist/gui/components/Reasoning.js.map +0 -1
  147. package/dist/gui/components/Response.d.ts.map +0 -1
  148. package/dist/gui/components/Response.js.map +0 -1
  149. package/dist/gui/components/Select.d.ts.map +0 -1
  150. package/dist/gui/components/Select.js.map +0 -1
  151. package/dist/gui/components/StatusBar.d.ts +0 -12
  152. package/dist/gui/components/StatusBar.d.ts.map +0 -1
  153. package/dist/gui/components/StatusBar.js +0 -58
  154. package/dist/gui/components/StatusBar.js.map +0 -1
  155. package/dist/gui/components/Tabs.d.ts.map +0 -1
  156. package/dist/gui/components/Tabs.js.map +0 -1
  157. package/dist/gui/components/Task.d.ts.map +0 -1
  158. package/dist/gui/components/Task.js.map +0 -1
  159. package/dist/gui/components/Textarea.d.ts.map +0 -1
  160. package/dist/gui/components/Textarea.js.map +0 -1
  161. package/dist/gui/components/ThinkingBlock.d.ts.map +0 -1
  162. package/dist/gui/components/ThinkingBlock.js.map +0 -1
  163. package/dist/gui/components/TodoList.d.ts.map +0 -1
  164. package/dist/gui/components/TodoList.js.map +0 -1
  165. package/dist/gui/components/TodoListItem.d.ts.map +0 -1
  166. package/dist/gui/components/TodoListItem.js.map +0 -1
  167. package/dist/gui/components/index.d.ts.map +0 -1
  168. package/dist/gui/components/index.js.map +0 -1
  169. package/dist/gui/index.d.ts.map +0 -1
  170. package/dist/gui/index.js.map +0 -1
  171. package/dist/gui/lib/utils.d.ts.map +0 -1
  172. package/dist/gui/lib/utils.js.map +0 -1
  173. package/dist/index.d.ts.map +0 -1
  174. package/dist/index.js.map +0 -1
  175. package/dist/sdk/client/acp-client.d.ts.map +0 -1
  176. package/dist/sdk/client/acp-client.js.map +0 -1
  177. package/dist/sdk/client/index.d.ts.map +0 -1
  178. package/dist/sdk/client/index.js.map +0 -1
  179. package/dist/sdk/index.d.ts.map +0 -1
  180. package/dist/sdk/index.js.map +0 -1
  181. package/dist/sdk/schemas/agent.d.ts.map +0 -1
  182. package/dist/sdk/schemas/agent.js.map +0 -1
  183. package/dist/sdk/schemas/index.d.ts.map +0 -1
  184. package/dist/sdk/schemas/index.js.map +0 -1
  185. package/dist/sdk/schemas/message.d.ts.map +0 -1
  186. package/dist/sdk/schemas/message.js.map +0 -1
  187. package/dist/sdk/schemas/session.d.ts.map +0 -1
  188. package/dist/sdk/schemas/session.js.map +0 -1
  189. package/dist/sdk/transports/http.d.ts.map +0 -1
  190. package/dist/sdk/transports/http.js.map +0 -1
  191. package/dist/sdk/transports/index.d.ts.map +0 -1
  192. package/dist/sdk/transports/index.js.map +0 -1
  193. package/dist/sdk/transports/stdio.d.ts.map +0 -1
  194. package/dist/sdk/transports/stdio.js.map +0 -1
  195. package/dist/sdk/transports/types.d.ts.map +0 -1
  196. package/dist/sdk/transports/types.js.map +0 -1
  197. package/dist/sdk/transports/websocket.d.ts.map +0 -1
  198. package/dist/sdk/transports/websocket.js.map +0 -1
  199. package/dist/tui/components/ChatView.d.ts.map +0 -1
  200. package/dist/tui/components/ChatView.js.map +0 -1
  201. package/dist/tui/components/GameOfLife.d.ts.map +0 -1
  202. package/dist/tui/components/GameOfLife.js.map +0 -1
  203. package/dist/tui/components/InputBox.d.ts.map +0 -1
  204. package/dist/tui/components/InputBox.js.map +0 -1
  205. package/dist/tui/components/MessageList.d.ts.map +0 -1
  206. package/dist/tui/components/MessageList.js.map +0 -1
  207. package/dist/tui/components/ReadlineInput.d.ts.map +0 -1
  208. package/dist/tui/components/ReadlineInput.js.map +0 -1
  209. package/dist/tui/components/StatusBar.d.ts.map +0 -1
  210. package/dist/tui/components/StatusBar.js.map +0 -1
  211. package/dist/tui/components/index.d.ts.map +0 -1
  212. package/dist/tui/components/index.js.map +0 -1
  213. package/dist/tui/index.d.ts.map +0 -1
  214. package/dist/tui/index.js.map +0 -1
@@ -1,75 +1,15 @@
1
- import { Box, Text, useStdout } from "ink";
2
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text, useStdout } from "ink";
3
3
  import { ReadlineInput } from "./ReadlineInput.js";
4
- export function InputBox({
5
- value,
6
- isSubmitting,
7
- attachedFiles,
8
- onChange,
9
- onSubmit,
10
- onEscape,
11
- }) {
12
- const { stdout } = useStdout();
13
- const terminalWidth = stdout?.columns || 80;
14
- return _jsxs(Box, {
15
- flexDirection: "column",
16
- children: [
17
- _jsx(Text, { color: "blue", children: "─".repeat(terminalWidth) }),
18
- attachedFiles.length > 0 &&
19
- _jsxs(Box, {
20
- flexDirection: "column",
21
- paddingTop: 1,
22
- children: [
23
- _jsx(Text, { dimColor: true, children: "Attached files:" }),
24
- attachedFiles.map((file) =>
25
- _jsxs(
26
- Box,
27
- {
28
- children: [
29
- _jsxs(Text, { color: "cyan", children: [" ", file.name] }),
30
- _jsxs(Text, {
31
- dimColor: true,
32
- children: [" (", formatFileSize(file.size), ")"],
33
- }),
34
- ],
35
- },
36
- file.path,
37
- ),
38
- ),
39
- ],
40
- }),
41
- _jsxs(Box, {
42
- paddingY: 1,
43
- children: [
44
- _jsx(Text, { bold: true, color: "blue", children: "> " }),
45
- isSubmitting
46
- ? _jsx(Text, {
47
- color: "gray",
48
- italic: true,
49
- children: "Sending...",
50
- })
51
- : onEscape
52
- ? _jsx(ReadlineInput, {
53
- value: value,
54
- onChange: onChange,
55
- onSubmit: onSubmit,
56
- onEscape: onEscape,
57
- placeholder: "Type your message...",
58
- })
59
- : _jsx(ReadlineInput, {
60
- value: value,
61
- onChange: onChange,
62
- onSubmit: onSubmit,
63
- placeholder: "Type your message...",
64
- }),
65
- ],
66
- }),
67
- _jsx(Text, { color: "blue", children: "─".repeat(terminalWidth) }),
68
- ],
69
- });
4
+ export function InputBox({ value, isSubmitting, attachedFiles, onChange, onSubmit, onEscape, }) {
5
+ const { stdout } = useStdout();
6
+ const terminalWidth = stdout?.columns || 80;
7
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "blue", children: "─".repeat(terminalWidth) }), attachedFiles.length > 0 && (_jsxs(Box, { flexDirection: "column", paddingTop: 1, children: [_jsx(Text, { dimColor: true, children: "Attached files:" }), attachedFiles.map((file) => (_jsxs(Box, { children: [_jsxs(Text, { color: "cyan", children: [" ", file.name] }), _jsxs(Text, { dimColor: true, children: [" (", formatFileSize(file.size), ")"] })] }, file.path)))] })), _jsxs(Box, { paddingY: 1, children: [_jsx(Text, { bold: true, color: "blue", children: "> " }), isSubmitting ? (_jsx(Text, { color: "gray", italic: true, children: "Sending..." })) : onEscape ? (_jsx(ReadlineInput, { value: value, onChange: onChange, onSubmit: onSubmit, onEscape: onEscape, placeholder: "Type your message..." })) : (_jsx(ReadlineInput, { value: value, onChange: onChange, onSubmit: onSubmit, placeholder: "Type your message..." }))] }), _jsx(Text, { color: "blue", children: "─".repeat(terminalWidth) })] }));
70
8
  }
71
9
  function formatFileSize(bytes) {
72
- if (bytes < 1024) return `${bytes} B`;
73
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
74
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
10
+ if (bytes < 1024)
11
+ return `${bytes} B`;
12
+ if (bytes < 1024 * 1024)
13
+ return `${(bytes / 1024).toFixed(1)} KB`;
14
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
75
15
  }
@@ -1,14 +1,8 @@
1
1
  export interface ReadlineInputProps {
2
- value: string;
3
- onChange: (value: string) => void;
4
- onSubmit: () => void;
5
- onEscape?: () => void;
6
- placeholder?: string;
2
+ value: string;
3
+ onChange: (value: string) => void;
4
+ onSubmit: () => void;
5
+ onEscape?: () => void;
6
+ placeholder?: string;
7
7
  }
8
- export declare function ReadlineInput({
9
- value,
10
- onChange,
11
- onSubmit,
12
- onEscape,
13
- placeholder,
14
- }: ReadlineInputProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function ReadlineInput({ value, onChange, onSubmit, onEscape, placeholder, }: ReadlineInputProps): import("react/jsx-runtime").JSX.Element;
@@ -1,255 +1,240 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
2
  import { Text, useInput } from "ink";
2
3
  import React, { useState } from "react";
3
- import {
4
- Fragment as _Fragment,
5
- jsx as _jsx,
6
- jsxs as _jsxs,
7
- } from "react/jsx-runtime";
8
- export function ReadlineInput({
9
- value,
10
- onChange,
11
- onSubmit,
12
- onEscape,
13
- placeholder = "",
14
- }) {
15
- const [cursorOffset, setCursorOffset] = useState(0);
16
- useInput((input, key) => {
17
- // Handle Esc: Call escape handler
18
- if (key.escape) {
19
- if (onEscape) {
20
- onEscape();
21
- }
22
- return;
23
- }
24
- // Handle special keys
25
- if (key.return) {
26
- const cursorPos = value.length + cursorOffset;
27
- // Shift+Enter: Insert backslash and newline
28
- if (key.shift) {
29
- const newValue = `${value.slice(0, cursorPos)}\\\n${value.slice(cursorPos)}`;
30
- onChange(newValue);
31
- if (cursorOffset < 0) {
32
- setCursorOffset(cursorOffset - 2);
33
- }
34
- return;
35
- }
36
- // Check if cursor is at end and last character is backslash (line continuation)
37
- if (cursorPos === value.length && value.endsWith("\\")) {
38
- // Remove backslash and add newline (line continuation)
39
- const newValue = `${value.slice(0, -1)}\n`;
40
- onChange(newValue);
41
- setCursorOffset(0); // Move cursor to end
42
- return;
43
- }
44
- // Alt+Enter adds a newline, Enter submits
45
- if (key.meta) {
46
- const newValue = `${value.slice(0, cursorPos)}\n${value.slice(cursorPos)}`;
47
- onChange(newValue);
48
- if (cursorOffset < 0) {
49
- setCursorOffset(cursorOffset - 1);
50
- }
51
- } else {
52
- onSubmit();
53
- }
54
- return;
55
- }
56
- if (key.ctrl) {
57
- // Ctrl+A: Move to beginning of line
58
- if (input === "a") {
59
- setCursorOffset(-value.length);
60
- return;
61
- }
62
- // Ctrl+E: Move to end of line
63
- if (input === "e") {
64
- setCursorOffset(0);
65
- return;
66
- }
67
- // Ctrl+W: Delete word backward
68
- if (input === "w") {
69
- const cursorPos = value.length + cursorOffset;
70
- const before = value.slice(0, cursorPos);
71
- const after = value.slice(cursorPos);
72
- // Find the start of the word to delete
73
- const match = before.match(/\s*\S*$/);
74
- if (match) {
75
- const newBefore = before.slice(0, -match[0].length);
76
- onChange(newBefore + after);
77
- setCursorOffset(-after.length);
78
- }
79
- return;
80
- }
81
- // Ctrl+U: Delete from cursor to beginning of line
82
- if (input === "u") {
83
- const cursorPos = value.length + cursorOffset;
84
- onChange(value.slice(cursorPos));
85
- setCursorOffset(-value.slice(cursorPos).length);
86
- return;
87
- }
88
- // Ctrl+K: Delete from cursor to end of line
89
- if (input === "k") {
90
- const cursorPos = value.length + cursorOffset;
91
- onChange(value.slice(0, cursorPos));
92
- setCursorOffset(0);
93
- return;
94
- }
95
- // Ctrl+D: Delete character forward
96
- if (input === "d") {
97
- const cursorPos = value.length + cursorOffset;
98
- if (cursorPos < value.length) {
99
- onChange(value.slice(0, cursorPos) + value.slice(cursorPos + 1));
100
- }
101
- return;
102
- }
103
- // Ctrl+B: Move backward one character
104
- if (input === "b") {
105
- const cursorPos = value.length + cursorOffset;
106
- if (cursorPos > 0) {
107
- setCursorOffset(cursorOffset - 1);
108
- }
109
- return;
110
- }
111
- // Ctrl+F: Move forward one character
112
- if (input === "f") {
113
- const cursorPos = value.length + cursorOffset;
114
- if (cursorPos < value.length) {
115
- setCursorOffset(cursorOffset + 1);
116
- }
117
- return;
118
- }
119
- }
120
- // Handle backspace
121
- if (key.backspace || key.delete) {
122
- const cursorPos = value.length + cursorOffset;
123
- if (cursorPos > 0) {
124
- onChange(value.slice(0, cursorPos - 1) + value.slice(cursorPos));
125
- if (cursorOffset < 0) {
126
- setCursorOffset(cursorOffset + 1);
127
- }
128
- }
129
- return;
130
- }
131
- // Handle left/right arrow keys
132
- if (key.leftArrow) {
133
- const cursorPos = value.length + cursorOffset;
134
- if (cursorPos > 0) {
135
- setCursorOffset(cursorOffset - 1);
136
- }
137
- return;
138
- }
139
- if (key.rightArrow) {
140
- const cursorPos = value.length + cursorOffset;
141
- if (cursorPos < value.length) {
142
- setCursorOffset(cursorOffset + 1);
143
- }
144
- return;
145
- }
146
- // Handle up/down arrow keys for multi-line navigation
147
- if (key.upArrow) {
148
- const cursorPos = value.length + cursorOffset;
149
- const lines = value.split("\n");
150
- // Find which line we're on and our position within it
151
- let currentLineIndex = 0;
152
- let charCount = 0;
153
- let posInLine = cursorPos;
154
- for (let i = 0; i < lines.length; i++) {
155
- const line = lines[i];
156
- if (!line) continue;
157
- const lineLength = line.length + (i < lines.length - 1 ? 1 : 0); // +1 for \n
158
- if (charCount + lineLength > cursorPos || i === lines.length - 1) {
159
- currentLineIndex = i;
160
- posInLine = cursorPos - charCount;
161
- break;
162
- }
163
- charCount += lineLength;
164
- }
165
- // Move to previous line if possible
166
- if (currentLineIndex > 0) {
167
- const prevLine = lines[currentLineIndex - 1];
168
- const currentLine = lines[currentLineIndex];
169
- if (prevLine && currentLine) {
170
- const prevLineStart = charCount - currentLine.length - 1;
171
- const targetPos = Math.min(posInLine, prevLine.length);
172
- const newCursorPos = prevLineStart + targetPos;
173
- setCursorOffset(newCursorPos - value.length);
174
- }
175
- }
176
- return;
177
- }
178
- if (key.downArrow) {
179
- const cursorPos = value.length + cursorOffset;
180
- const lines = value.split("\n");
181
- // Find which line we're on and our position within it
182
- let currentLineIndex = 0;
183
- let charCount = 0;
184
- let posInLine = cursorPos;
185
- for (let i = 0; i < lines.length; i++) {
186
- const line = lines[i];
187
- if (!line) continue;
188
- const lineLength = line.length + (i < lines.length - 1 ? 1 : 0); // +1 for \n
189
- if (charCount + lineLength > cursorPos || i === lines.length - 1) {
190
- currentLineIndex = i;
191
- posInLine = cursorPos - charCount;
192
- break;
193
- }
194
- charCount += lineLength;
195
- }
196
- // Move to next line if possible
197
- if (currentLineIndex < lines.length - 1) {
198
- const nextLine = lines[currentLineIndex + 1];
199
- const currentLine = lines[currentLineIndex];
200
- if (nextLine && currentLine) {
201
- const nextLineStart = charCount + currentLine.length + 1;
202
- const targetPos = Math.min(posInLine, nextLine.length);
203
- const newCursorPos = nextLineStart + targetPos;
204
- setCursorOffset(newCursorPos - value.length);
205
- }
206
- }
207
- return;
208
- }
209
- // Regular character input and paste detection
210
- // Ink automatically detects paste and sends the whole string at once
211
- if (!key.ctrl && !key.meta && input.length > 0) {
212
- const cursorPos = value.length + cursorOffset;
213
- // Check if this is a paste with more than 3 lines
214
- let contentToInsert = input;
215
- if (input.length > 1) {
216
- // Pasted content (more than 1 char at once)
217
- const lines = input.split("\n");
218
- if (lines.length > 3) {
219
- contentToInsert = `[Pasted +${lines.length} lines]`;
220
- }
221
- }
222
- const newValue =
223
- value.slice(0, cursorPos) + contentToInsert + value.slice(cursorPos);
224
- onChange(newValue);
225
- // Keep cursor at the same relative position
226
- if (cursorOffset < 0) {
227
- setCursorOffset(cursorOffset - contentToInsert.length);
228
- }
229
- }
230
- });
231
- // Reset cursor when value changes externally (e.g., after submit)
232
- React.useEffect(() => {
233
- if (value.length === 0) {
234
- setCursorOffset(0);
235
- }
236
- }, [value]);
237
- // Display the input with cursor
238
- const cursorPos = value.length + cursorOffset;
239
- // Show placeholder if empty
240
- if (value.length === 0) {
241
- return _jsxs(_Fragment, {
242
- children: [
243
- _jsx(Text, { inverse: true, children: " " }),
244
- _jsx(Text, { dimColor: true, children: placeholder }),
245
- ],
246
- });
247
- }
248
- // Show value with cursor
249
- const before = value.slice(0, cursorPos);
250
- const cursor = value[cursorPos] || " ";
251
- const after = value.slice(cursorPos + 1);
252
- return _jsxs(Text, {
253
- children: [before, _jsx(Text, { inverse: true, children: cursor }), after],
254
- });
4
+ export function ReadlineInput({ value, onChange, onSubmit, onEscape, placeholder = "", }) {
5
+ const [cursorOffset, setCursorOffset] = useState(0);
6
+ useInput((input, key) => {
7
+ // Handle Esc: Call escape handler
8
+ if (key.escape) {
9
+ if (onEscape) {
10
+ onEscape();
11
+ }
12
+ return;
13
+ }
14
+ // Handle special keys
15
+ if (key.return) {
16
+ const cursorPos = value.length + cursorOffset;
17
+ // Shift+Enter: Insert backslash and newline
18
+ if (key.shift) {
19
+ const newValue = `${value.slice(0, cursorPos)}\\\n${value.slice(cursorPos)}`;
20
+ onChange(newValue);
21
+ if (cursorOffset < 0) {
22
+ setCursorOffset(cursorOffset - 2);
23
+ }
24
+ return;
25
+ }
26
+ // Check if cursor is at end and last character is backslash (line continuation)
27
+ if (cursorPos === value.length && value.endsWith("\\")) {
28
+ // Remove backslash and add newline (line continuation)
29
+ const newValue = `${value.slice(0, -1)}\n`;
30
+ onChange(newValue);
31
+ setCursorOffset(0); // Move cursor to end
32
+ return;
33
+ }
34
+ // Alt+Enter adds a newline, Enter submits
35
+ if (key.meta) {
36
+ const newValue = `${value.slice(0, cursorPos)}\n${value.slice(cursorPos)}`;
37
+ onChange(newValue);
38
+ if (cursorOffset < 0) {
39
+ setCursorOffset(cursorOffset - 1);
40
+ }
41
+ }
42
+ else {
43
+ onSubmit();
44
+ }
45
+ return;
46
+ }
47
+ if (key.ctrl) {
48
+ // Ctrl+A: Move to beginning of line
49
+ if (input === "a") {
50
+ setCursorOffset(-value.length);
51
+ return;
52
+ }
53
+ // Ctrl+E: Move to end of line
54
+ if (input === "e") {
55
+ setCursorOffset(0);
56
+ return;
57
+ }
58
+ // Ctrl+W: Delete word backward
59
+ if (input === "w") {
60
+ const cursorPos = value.length + cursorOffset;
61
+ const before = value.slice(0, cursorPos);
62
+ const after = value.slice(cursorPos);
63
+ // Find the start of the word to delete
64
+ const match = before.match(/\s*\S*$/);
65
+ if (match) {
66
+ const newBefore = before.slice(0, -match[0].length);
67
+ onChange(newBefore + after);
68
+ setCursorOffset(-after.length);
69
+ }
70
+ return;
71
+ }
72
+ // Ctrl+U: Delete from cursor to beginning of line
73
+ if (input === "u") {
74
+ const cursorPos = value.length + cursorOffset;
75
+ onChange(value.slice(cursorPos));
76
+ setCursorOffset(-value.slice(cursorPos).length);
77
+ return;
78
+ }
79
+ // Ctrl+K: Delete from cursor to end of line
80
+ if (input === "k") {
81
+ const cursorPos = value.length + cursorOffset;
82
+ onChange(value.slice(0, cursorPos));
83
+ setCursorOffset(0);
84
+ return;
85
+ }
86
+ // Ctrl+D: Delete character forward
87
+ if (input === "d") {
88
+ const cursorPos = value.length + cursorOffset;
89
+ if (cursorPos < value.length) {
90
+ onChange(value.slice(0, cursorPos) + value.slice(cursorPos + 1));
91
+ }
92
+ return;
93
+ }
94
+ // Ctrl+B: Move backward one character
95
+ if (input === "b") {
96
+ const cursorPos = value.length + cursorOffset;
97
+ if (cursorPos > 0) {
98
+ setCursorOffset(cursorOffset - 1);
99
+ }
100
+ return;
101
+ }
102
+ // Ctrl+F: Move forward one character
103
+ if (input === "f") {
104
+ const cursorPos = value.length + cursorOffset;
105
+ if (cursorPos < value.length) {
106
+ setCursorOffset(cursorOffset + 1);
107
+ }
108
+ return;
109
+ }
110
+ }
111
+ // Handle backspace
112
+ if (key.backspace || key.delete) {
113
+ const cursorPos = value.length + cursorOffset;
114
+ if (cursorPos > 0) {
115
+ onChange(value.slice(0, cursorPos - 1) + value.slice(cursorPos));
116
+ if (cursorOffset < 0) {
117
+ setCursorOffset(cursorOffset + 1);
118
+ }
119
+ }
120
+ return;
121
+ }
122
+ // Handle left/right arrow keys
123
+ if (key.leftArrow) {
124
+ const cursorPos = value.length + cursorOffset;
125
+ if (cursorPos > 0) {
126
+ setCursorOffset(cursorOffset - 1);
127
+ }
128
+ return;
129
+ }
130
+ if (key.rightArrow) {
131
+ const cursorPos = value.length + cursorOffset;
132
+ if (cursorPos < value.length) {
133
+ setCursorOffset(cursorOffset + 1);
134
+ }
135
+ return;
136
+ }
137
+ // Handle up/down arrow keys for multi-line navigation
138
+ if (key.upArrow) {
139
+ const cursorPos = value.length + cursorOffset;
140
+ const lines = value.split("\n");
141
+ // Find which line we're on and our position within it
142
+ let currentLineIndex = 0;
143
+ let charCount = 0;
144
+ let posInLine = cursorPos;
145
+ for (let i = 0; i < lines.length; i++) {
146
+ const line = lines[i];
147
+ if (!line)
148
+ continue;
149
+ const lineLength = line.length + (i < lines.length - 1 ? 1 : 0); // +1 for \n
150
+ if (charCount + lineLength > cursorPos || i === lines.length - 1) {
151
+ currentLineIndex = i;
152
+ posInLine = cursorPos - charCount;
153
+ break;
154
+ }
155
+ charCount += lineLength;
156
+ }
157
+ // Move to previous line if possible
158
+ if (currentLineIndex > 0) {
159
+ const prevLine = lines[currentLineIndex - 1];
160
+ const currentLine = lines[currentLineIndex];
161
+ if (prevLine && currentLine) {
162
+ const prevLineStart = charCount - currentLine.length - 1;
163
+ const targetPos = Math.min(posInLine, prevLine.length);
164
+ const newCursorPos = prevLineStart + targetPos;
165
+ setCursorOffset(newCursorPos - value.length);
166
+ }
167
+ }
168
+ return;
169
+ }
170
+ if (key.downArrow) {
171
+ const cursorPos = value.length + cursorOffset;
172
+ const lines = value.split("\n");
173
+ // Find which line we're on and our position within it
174
+ let currentLineIndex = 0;
175
+ let charCount = 0;
176
+ let posInLine = cursorPos;
177
+ for (let i = 0; i < lines.length; i++) {
178
+ const line = lines[i];
179
+ if (!line)
180
+ continue;
181
+ const lineLength = line.length + (i < lines.length - 1 ? 1 : 0); // +1 for \n
182
+ if (charCount + lineLength > cursorPos || i === lines.length - 1) {
183
+ currentLineIndex = i;
184
+ posInLine = cursorPos - charCount;
185
+ break;
186
+ }
187
+ charCount += lineLength;
188
+ }
189
+ // Move to next line if possible
190
+ if (currentLineIndex < lines.length - 1) {
191
+ const nextLine = lines[currentLineIndex + 1];
192
+ const currentLine = lines[currentLineIndex];
193
+ if (nextLine && currentLine) {
194
+ const nextLineStart = charCount + currentLine.length + 1;
195
+ const targetPos = Math.min(posInLine, nextLine.length);
196
+ const newCursorPos = nextLineStart + targetPos;
197
+ setCursorOffset(newCursorPos - value.length);
198
+ }
199
+ }
200
+ return;
201
+ }
202
+ // Regular character input and paste detection
203
+ // Ink automatically detects paste and sends the whole string at once
204
+ if (!key.ctrl && !key.meta && input.length > 0) {
205
+ const cursorPos = value.length + cursorOffset;
206
+ // Check if this is a paste with more than 3 lines
207
+ let contentToInsert = input;
208
+ if (input.length > 1) {
209
+ // Pasted content (more than 1 char at once)
210
+ const lines = input.split("\n");
211
+ if (lines.length > 3) {
212
+ contentToInsert = `[Pasted +${lines.length} lines]`;
213
+ }
214
+ }
215
+ const newValue = value.slice(0, cursorPos) + contentToInsert + value.slice(cursorPos);
216
+ onChange(newValue);
217
+ // Keep cursor at the same relative position
218
+ if (cursorOffset < 0) {
219
+ setCursorOffset(cursorOffset - contentToInsert.length);
220
+ }
221
+ }
222
+ });
223
+ // Reset cursor when value changes externally (e.g., after submit)
224
+ React.useEffect(() => {
225
+ if (value.length === 0) {
226
+ setCursorOffset(0);
227
+ }
228
+ }, [value]);
229
+ // Display the input with cursor
230
+ const cursorPos = value.length + cursorOffset;
231
+ // Show placeholder if empty
232
+ if (value.length === 0) {
233
+ return (_jsxs(_Fragment, { children: [_jsx(Text, { inverse: true, children: " " }), _jsx(Text, { dimColor: true, children: placeholder })] }));
234
+ }
235
+ // Show value with cursor
236
+ const before = value.slice(0, cursorPos);
237
+ const cursor = value[cursorPos] || " ";
238
+ const after = value.slice(cursorPos + 1);
239
+ return (_jsxs(Text, { children: [before, _jsx(Text, { inverse: true, children: cursor }), after] }));
255
240
  }
@@ -1,19 +1,13 @@
1
1
  export interface SingleSelectOption {
2
- label: string;
3
- value: string;
4
- description?: string;
2
+ label: string;
3
+ value: string;
4
+ description?: string;
5
5
  }
6
6
  export interface SingleSelectProps {
7
- options: SingleSelectOption[];
8
- selected: string | null;
9
- onChange: (selected: string) => void;
10
- onSubmit: (selected: string) => void;
11
- onCancel?: () => void;
7
+ options: SingleSelectOption[];
8
+ selected: string | null;
9
+ onChange: (selected: string) => void;
10
+ onSubmit: (selected: string) => void;
11
+ onCancel?: () => void;
12
12
  }
13
- export declare function SingleSelect({
14
- options,
15
- selected: _selected,
16
- onChange,
17
- onSubmit,
18
- onCancel,
19
- }: SingleSelectProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function SingleSelect({ options, selected: _selected, onChange, onSubmit, onCancel, }: SingleSelectProps): import("react/jsx-runtime").JSX.Element;