aegiscode 3.1.10 → 3.2.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.
Files changed (209) hide show
  1. package/bin/aegis-cli-linux-x64 +0 -0
  2. package/bin/cli.js +68 -0
  3. package/package.json +11 -48
  4. package/scripts/download-binary.mjs +155 -0
  5. package/dist/main.js +0 -2171
  6. package/gui/run-gui.cjs +0 -13
  7. package/src/agent/Agent.ts +0 -903
  8. package/src/agent/SimpleAgent.ts +0 -48
  9. package/src/agent/index.ts +0 -54
  10. package/src/agent/orchestrator/AppBuilder.ts +0 -443
  11. package/src/agent/orchestrator/CouncilAgent.ts +0 -310
  12. package/src/agent/orchestrator/DiscussionRoom.ts +0 -462
  13. package/src/agent/orchestrator/OrchestratorAgent.ts +0 -637
  14. package/src/agent/orchestrator/index.ts +0 -38
  15. package/src/agent/orchestrator/utils.ts +0 -397
  16. package/src/agent/pricing.ts +0 -115
  17. package/src/agent/router.ts +0 -74
  18. package/src/agent/routerStats.ts +0 -121
  19. package/src/agent/types.ts +0 -318
  20. package/src/auth/login.ts +0 -383
  21. package/src/cli/config.ts +0 -189
  22. package/src/cli/index.ts +0 -17
  23. package/src/cli/middleware.ts +0 -119
  24. package/src/cli/types.ts +0 -75
  25. package/src/config/ConfigManager.ts +0 -587
  26. package/src/config/index.ts +0 -7
  27. package/src/config/types.ts +0 -584
  28. package/src/context/CompactionService.ts +0 -300
  29. package/src/context/ContextManager.ts +0 -450
  30. package/src/context/FileAnalyzer.ts +0 -267
  31. package/src/context/TokenCounter.ts +0 -265
  32. package/src/context/index.ts +0 -27
  33. package/src/context/storage/CacheStore.ts +0 -176
  34. package/src/context/storage/JSONLStore.ts +0 -201
  35. package/src/context/storage/MemoryStore.ts +0 -205
  36. package/src/context/storage/PersistentStore.ts +0 -327
  37. package/src/context/storage/index.ts +0 -9
  38. package/src/context/storage/pathUtils.ts +0 -114
  39. package/src/context/test.ts +0 -309
  40. package/src/context/types.ts +0 -268
  41. package/src/hooks/HookExecutor.ts +0 -434
  42. package/src/hooks/HookManager.ts +0 -596
  43. package/src/hooks/HookService.ts +0 -269
  44. package/src/hooks/Matcher.ts +0 -157
  45. package/src/hooks/index.ts +0 -63
  46. package/src/hooks/types.ts +0 -424
  47. package/src/main.tsx +0 -596
  48. package/src/mcp/HealthMonitor.ts +0 -150
  49. package/src/mcp/McpClient.ts +0 -491
  50. package/src/mcp/McpRegistry.ts +0 -321
  51. package/src/mcp/createMcpTool.ts +0 -251
  52. package/src/mcp/index.ts +0 -15
  53. package/src/mcp/server.ts +0 -334
  54. package/src/mcp/test-server.ts +0 -88
  55. package/src/mcp/test.ts +0 -372
  56. package/src/mcp/types.ts +0 -247
  57. package/src/memory/AgentMemoryBus.ts +0 -432
  58. package/src/memory/CloudSync.ts +0 -99
  59. package/src/memory/DriveSync.ts +0 -106
  60. package/src/memory/SharedMemory.ts +0 -951
  61. package/src/memory/index.ts +0 -14
  62. package/src/memory/machineFingerprint.ts +0 -40
  63. package/src/orchestrator/SubAgentMetadata.ts +0 -136
  64. package/src/prompts/builder.ts +0 -213
  65. package/src/prompts/default.ts +0 -144
  66. package/src/prompts/index.ts +0 -16
  67. package/src/prompts/plan.ts +0 -64
  68. package/src/prompts/test.ts +0 -78
  69. package/src/services/AnthropicChatService.ts +0 -341
  70. package/src/services/ChatService.ts +0 -347
  71. package/src/services/ClaudeCliChatService.ts +0 -256
  72. package/src/services/CloudSync.ts +0 -168
  73. package/src/services/CostLedger.ts +0 -211
  74. package/src/services/Heartbeat.ts +0 -135
  75. package/src/services/LearningCollector.ts +0 -291
  76. package/src/services/OllamaInstaller.ts +0 -342
  77. package/src/services/VersionChecker.ts +0 -445
  78. package/src/services/index.ts +0 -57
  79. package/src/services/streaming/OpenAIEventAdapter.ts +0 -126
  80. package/src/services/streaming/RenderingProfile.ts +0 -90
  81. package/src/services/streaming/StreamEventParser.ts +0 -181
  82. package/src/services/streaming/ThrottledRenderer.ts +0 -139
  83. package/src/services/streaming/TranscriptBuffer.ts +0 -574
  84. package/src/services/streaming/eventStatusMap.ts +0 -52
  85. package/src/services/streaming/index.ts +0 -46
  86. package/src/services/streaming/renderFormatting.ts +0 -79
  87. package/src/services/streaming/types.ts +0 -234
  88. package/src/skills/SkillLoader.ts +0 -126
  89. package/src/skills/SkillRegistry.ts +0 -366
  90. package/src/skills/index.ts +0 -48
  91. package/src/skills/types.ts +0 -146
  92. package/src/slash-commands/billing.ts +0 -70
  93. package/src/slash-commands/build.ts +0 -413
  94. package/src/slash-commands/builtinCommands.ts +0 -2733
  95. package/src/slash-commands/clone.ts +0 -242
  96. package/src/slash-commands/council.ts +0 -125
  97. package/src/slash-commands/custom/CustomCommandExecutor.ts +0 -143
  98. package/src/slash-commands/custom/CustomCommandLoader.ts +0 -251
  99. package/src/slash-commands/custom/CustomCommandRegistry.ts +0 -144
  100. package/src/slash-commands/custom/index.ts +0 -7
  101. package/src/slash-commands/debate.ts +0 -254
  102. package/src/slash-commands/gmail.ts +0 -105
  103. package/src/slash-commands/index.ts +0 -388
  104. package/src/slash-commands/mcpCommand.ts +0 -205
  105. package/src/slash-commands/types.ts +0 -201
  106. package/src/store/index.ts +0 -76
  107. package/src/store/selectors.ts +0 -246
  108. package/src/store/slices/appSlice.ts +0 -205
  109. package/src/store/slices/commandSlice.ts +0 -115
  110. package/src/store/slices/configSlice.ts +0 -46
  111. package/src/store/slices/focusSlice.ts +0 -64
  112. package/src/store/slices/index.ts +0 -9
  113. package/src/store/slices/sessionSlice.ts +0 -424
  114. package/src/store/streaming-buffer.ts +0 -425
  115. package/src/store/test.ts +0 -296
  116. package/src/store/types.ts +0 -274
  117. package/src/store/vanilla.ts +0 -186
  118. package/src/tools/builtin/bash.ts +0 -236
  119. package/src/tools/builtin/council.ts +0 -105
  120. package/src/tools/builtin/edit.ts +0 -213
  121. package/src/tools/builtin/glob.ts +0 -136
  122. package/src/tools/builtin/grep.ts +0 -263
  123. package/src/tools/builtin/index.ts +0 -61
  124. package/src/tools/builtin/memory.ts +0 -66
  125. package/src/tools/builtin/read.ts +0 -168
  126. package/src/tools/builtin/skill.ts +0 -97
  127. package/src/tools/builtin/snapshot.ts +0 -40
  128. package/src/tools/builtin/task.ts +0 -106
  129. package/src/tools/builtin/write.ts +0 -134
  130. package/src/tools/createTool.ts +0 -221
  131. package/src/tools/execution/ExecutionPipeline.ts +0 -263
  132. package/src/tools/execution/index.ts +0 -40
  133. package/src/tools/execution/stages/CacheStage.ts +0 -131
  134. package/src/tools/execution/stages/ConfirmationStage.ts +0 -203
  135. package/src/tools/execution/stages/DiscoveryStage.ts +0 -46
  136. package/src/tools/execution/stages/ExecutionStage.ts +0 -48
  137. package/src/tools/execution/stages/FormattingStage.ts +0 -44
  138. package/src/tools/execution/stages/HookStage.ts +0 -72
  139. package/src/tools/execution/stages/PermissionStage.ts +0 -287
  140. package/src/tools/execution/stages/PostHookStage.ts +0 -71
  141. package/src/tools/execution/stages/index.ts +0 -12
  142. package/src/tools/execution/test.ts +0 -266
  143. package/src/tools/execution/types.ts +0 -273
  144. package/src/tools/index.ts +0 -81
  145. package/src/tools/registry.ts +0 -304
  146. package/src/tools/schemas.ts +0 -109
  147. package/src/tools/test.ts +0 -220
  148. package/src/tools/types.ts +0 -175
  149. package/src/tools/validation/PermissionChecker.ts +0 -242
  150. package/src/tools/validation/SensitiveFileDetector.ts +0 -210
  151. package/src/tools/validation/index.ts +0 -11
  152. package/src/ui/App.tsx +0 -166
  153. package/src/ui/components/AegisInterface.tsx +0 -484
  154. package/src/ui/components/common/ChatSearch.tsx +0 -150
  155. package/src/ui/components/common/ErrorBoundary.tsx +0 -82
  156. package/src/ui/components/common/ExitMessage.tsx +0 -120
  157. package/src/ui/components/common/LoadingIndicator.tsx +0 -49
  158. package/src/ui/components/common/index.ts +0 -6
  159. package/src/ui/components/dialog/ConfirmationPrompt.tsx +0 -208
  160. package/src/ui/components/dialog/InteractiveSelector.tsx +0 -149
  161. package/src/ui/components/dialog/SetupWizard.tsx +0 -297
  162. package/src/ui/components/dialog/UpdatePrompt.tsx +0 -155
  163. package/src/ui/components/dialog/index.ts +0 -8
  164. package/src/ui/components/index.ts +0 -28
  165. package/src/ui/components/input/CommandSuggestions.tsx +0 -139
  166. package/src/ui/components/input/CustomTextInput.tsx +0 -220
  167. package/src/ui/components/input/InputArea.tsx +0 -361
  168. package/src/ui/components/input/PromptSuggestions.tsx +0 -66
  169. package/src/ui/components/input/index.ts +0 -6
  170. package/src/ui/components/layout/ChatStatusBar.tsx +0 -90
  171. package/src/ui/components/layout/ContextBar.tsx +0 -79
  172. package/src/ui/components/layout/MessageArea.tsx +0 -96
  173. package/src/ui/components/layout/MessageList.tsx +0 -647
  174. package/src/ui/components/layout/MessageSeparator.tsx +0 -26
  175. package/src/ui/components/layout/WelcomeMessage.tsx +0 -93
  176. package/src/ui/components/layout/index.ts +0 -7
  177. package/src/ui/components/markdown/CodeHighlighter.tsx +0 -292
  178. package/src/ui/components/markdown/MessageRenderer.tsx +0 -1211
  179. package/src/ui/components/markdown/index.ts +0 -8
  180. package/src/ui/components/markdown/parser.ts +0 -336
  181. package/src/ui/components/markdown/types.ts +0 -66
  182. package/src/ui/focus/FocusManager.ts +0 -137
  183. package/src/ui/focus/index.ts +0 -13
  184. package/src/ui/focus/types.ts +0 -54
  185. package/src/ui/focus/useFocus.ts +0 -75
  186. package/src/ui/hooks/index.ts +0 -11
  187. package/src/ui/hooks/useAgent.ts +0 -284
  188. package/src/ui/hooks/useCommandHistory.ts +0 -87
  189. package/src/ui/hooks/useCommandProcessor.ts +0 -443
  190. package/src/ui/hooks/useConfirmation.ts +0 -99
  191. package/src/ui/hooks/useCtrlCHandler.ts +0 -100
  192. package/src/ui/hooks/useInputBuffer.ts +0 -122
  193. package/src/ui/hooks/useTerminalSize.ts +0 -68
  194. package/src/ui/hooks/useTerminalWidth.ts +0 -5
  195. package/src/ui/hooks/useWindowedList.ts +0 -118
  196. package/src/ui/render-debugger.ts +0 -621
  197. package/src/ui/test.ts +0 -189
  198. package/src/ui/themes/ThemeManager.ts +0 -332
  199. package/src/ui/themes/aegisTheme.ts +0 -87
  200. package/src/ui/themes/darkTheme.ts +0 -87
  201. package/src/ui/themes/defaultTheme.ts +0 -85
  202. package/src/ui/themes/index.ts +0 -10
  203. package/src/ui/themes/lightTheme.ts +0 -89
  204. package/src/ui/themes/popularThemes.ts +0 -187
  205. package/src/ui/themes/types.ts +0 -130
  206. package/src/utils/clipboard.ts +0 -48
  207. package/src/utils/debug.ts +0 -43
  208. package/src/utils/environment.ts +0 -68
  209. package/src/utils/index.ts +0 -10
@@ -1,139 +0,0 @@
1
- /**
2
- * CommandSuggestions - Dropdown autocomplete for slash commands
3
- *
4
- * Shows fuzzy-matched command suggestions when user types "/..."
5
- */
6
-
7
- import React, { useMemo, useEffect, useRef } from 'react';
8
- import { Box, Text, useInput } from 'ink';
9
- import { getCommandCompletions } from '../../../slash-commands/index.js';
10
- import { themeManager } from '../../themes/index.js';
11
- import { FocusId, focusManager } from '../../focus/index.js';
12
-
13
- interface CommandSuggestionsProps {
14
- /** The current input value */
15
- input: string;
16
- /** Cursor position */
17
- cursorPosition: number;
18
- /** Callback to set input value (for tab-complete) */
19
- onSelectSuggestion: (suggestion: string) => void;
20
- /** Whether suggestions are visible */
21
- visible: boolean;
22
- }
23
-
24
- export const CommandSuggestions: React.FC<CommandSuggestionsProps> = ({
25
- input,
26
- cursorPosition,
27
- onSelectSuggestion,
28
- visible,
29
- }) => {
30
- const [selectedIndex, setSelectedIndex] = React.useState(0);
31
- const theme = themeManager.getTheme();
32
-
33
- // Reset selection when suggestions change
34
- const lastInputRef = useRef(input);
35
- useEffect(() => {
36
- if (lastInputRef.current !== input) {
37
- setSelectedIndex(0);
38
- lastInputRef.current = input;
39
- }
40
- }, [input]);
41
-
42
- const suggestions = useMemo(() => {
43
- if (!visible || !input.startsWith('/')) return [];
44
-
45
- const partial = input.slice(0, cursorPosition);
46
- const results = getCommandCompletions(partial);
47
- const isListing = partial.trim() === '/';
48
-
49
- if (isListing) {
50
- // Alphabetical when showing all commands
51
- results.sort((a, b) => a.command.localeCompare(b.command));
52
- } else {
53
- results.sort((a, b) => (b.matchScore ?? 0) - (a.matchScore ?? 0));
54
- }
55
- return results;
56
- }, [input, cursorPosition, visible]);
57
-
58
- // Clamp selected index
59
- const clampedIndex = Math.min(selectedIndex, Math.max(0, suggestions.length - 1));
60
-
61
- // Tab key: cycle through suggestions
62
- useInput(
63
- (_, key) => {
64
- if (!visible || suggestions.length === 0) return;
65
- if (focusManager.getCurrentFocus() !== FocusId.MAIN_INPUT) return;
66
-
67
- if (key.tab && !key.shift) {
68
- // Select next suggestion (cycle)
69
- const next = (clampedIndex + 1) % suggestions.length;
70
- setSelectedIndex(next);
71
- return;
72
- }
73
-
74
- if (key.tab && key.shift) {
75
- // Select previous suggestion
76
- const prev = clampedIndex <= 0 ? suggestions.length - 1 : clampedIndex - 1;
77
- setSelectedIndex(prev);
78
- return;
79
- }
80
-
81
- // Enter on a suggestion: select it
82
- if (key.return && suggestions.length > 0) {
83
- const selected = suggestions[clampedIndex];
84
- if (selected) {
85
- // Replace the command part of the input
86
- const beforeCursor = input.slice(0, cursorPosition);
87
- const afterCursor = input.slice(cursorPosition);
88
- const slashIdx = beforeCursor.lastIndexOf('/');
89
-
90
- if (slashIdx !== -1) {
91
- const newBefore = beforeCursor.slice(0, slashIdx);
92
- const completed = `${newBefore}${selected.command} `;
93
- onSelectSuggestion(completed + afterCursor);
94
- }
95
- }
96
- }
97
- },
98
- { isActive: visible && suggestions.length > 0 }
99
- );
100
-
101
- if (!visible || suggestions.length === 0) return null;
102
-
103
- return (
104
- <Box
105
- flexDirection="column"
106
- marginLeft={1}
107
- marginBottom={0}
108
- borderStyle="round"
109
- borderColor={theme.colors.border.light}
110
- paddingX={1}
111
- paddingY={0}
112
- >
113
- <Text dimColor>Commands ({suggestions.length}):</Text>
114
- {suggestions.map((s, i) => {
115
- const isSelected = i === clampedIndex;
116
- return (
117
- <Box key={s.command} flexDirection="row">
118
- <Text>
119
- {isSelected ? (
120
- <Text color={theme.colors.primary} bold>{'>'}</Text>
121
- ) : (
122
- <Text> </Text>
123
- )}{' '}
124
- <Text color={isSelected ? theme.colors.primary : undefined} bold={isSelected}>
125
- {s.command}
126
- </Text>
127
- <Text color={theme.colors.text.muted}> — {s.description}</Text>
128
- </Text>
129
- </Box>
130
- );
131
- })}
132
- <Text dimColor>
133
- Tab/Shift+Tab to navigate · Enter to select
134
- </Text>
135
- </Box>
136
- );
137
- };
138
-
139
- export default CommandSuggestions;
@@ -1,220 +0,0 @@
1
- import React, { useMemo, useCallback, useRef, memo } from 'react';
2
- import { Text, useInput } from 'ink';
3
- import chalk from 'chalk';
4
- import { useIsFocused, FocusId, focusManager } from '../../focus/index.js';
5
-
6
- interface CustomTextInputProps {
7
- value: string;
8
- cursorPosition: number;
9
- /** Cursor visibility — computed by parent so this component doesn't re-render every tick */
10
- cursorOn?: boolean;
11
- onChange: (value: string) => void;
12
- onChangeCursorPosition: (pos: number) => void;
13
- onSubmit?: (value: string) => void;
14
- onPaste?: (text: string) => { prompt?: string } | void;
15
- onArrowUp?: () => void;
16
- onArrowDown?: () => void;
17
- placeholder?: string;
18
- focusId?: FocusId;
19
- disabled?: boolean;
20
- }
21
-
22
- const PASTE_CONFIG = {
23
- TIMEOUT_MS: 100,
24
- RAPID_INPUT_THRESHOLD_MS: 400,
25
- LARGE_INPUT_THRESHOLD: 100,
26
- };
27
-
28
- export const CustomTextInput: React.FC<CustomTextInputProps> = memo(
29
- ({
30
- value,
31
- cursorPosition,
32
- onChange,
33
- onChangeCursorPosition,
34
- onSubmit,
35
- onPaste,
36
- onArrowUp,
37
- onArrowDown,
38
- placeholder = '',
39
- focusId = FocusId.MAIN_INPUT,
40
- disabled = false,
41
- cursorOn = true,
42
- }) => {
43
- const isFocused = useIsFocused(focusId);
44
- const isActive = isFocused && !disabled;
45
-
46
- // Refs for all values accessed inside the stable useInput handler.
47
- // Updated on every render — never trigger re-renders themselves.
48
- const valueRef = useRef(value);
49
- const cursorPositionRef = useRef(cursorPosition);
50
- const onChangeRef = useRef(onChange);
51
- const onChangeCursorPositionRef = useRef(onChangeCursorPosition);
52
- const onSubmitRef = useRef(onSubmit);
53
- const onPasteRef = useRef(onPaste);
54
- const onArrowUpRef = useRef(onArrowUp);
55
- const onArrowDownRef = useRef(onArrowDown);
56
- const isActiveRef = useRef(isActive);
57
-
58
- valueRef.current = value;
59
- cursorPositionRef.current = cursorPosition;
60
- onChangeRef.current = onChange;
61
- onChangeCursorPositionRef.current = onChangeCursorPosition;
62
- onSubmitRef.current = onSubmit;
63
- onPasteRef.current = onPaste;
64
- onArrowUpRef.current = onArrowUp;
65
- onArrowDownRef.current = onArrowDown;
66
- isActiveRef.current = isActive;
67
-
68
- const renderedValue = useMemo(() => {
69
- if (!isActive) return value || chalk.dim(placeholder);
70
-
71
- if (value.length === 0) {
72
- const block = cursorOn
73
- ? chalk.bgHex('#00c8b4').hex('#000000')(' ')
74
- : ' ';
75
- return block + chalk.dim(placeholder);
76
- }
77
-
78
- const before = value.slice(0, cursorPosition);
79
- const charUnder = value[cursorPosition] ?? ' ';
80
- const after = value.slice(cursorPosition + 1);
81
-
82
- const cursorChar = cursorOn
83
- ? chalk.bgHex('#00c8b4').hex('#000000')(charUnder)
84
- : charUnder;
85
-
86
- return before + cursorChar + after;
87
- }, [value, cursorPosition, isActive, placeholder, cursorOn]);
88
-
89
- const pasteStateRef = useRef({
90
- chunks: [] as string[],
91
- timeoutId: null as NodeJS.Timeout | null,
92
- firstInputTime: null as number | null,
93
- });
94
-
95
- // Stable — accesses all mutable values via refs
96
- const handlePasteStable = useCallback((text: string) => {
97
- const val = valueRef.current;
98
- const pos = cursorPositionRef.current;
99
- // Claude Code behavior: trim trailing whitespace from pasted text
100
- const cleanText = text.trimEnd();
101
-
102
- if (onPasteRef.current) {
103
- const result = onPasteRef.current(text);
104
- if (result?.prompt) {
105
- onChangeRef.current(val + result.prompt);
106
- onChangeCursorPositionRef.current(val.length + result.prompt.length);
107
- return;
108
- }
109
- }
110
- const newValue = val.slice(0, pos) + cleanText + val.slice(pos);
111
- onChangeRef.current(newValue);
112
- onChangeCursorPositionRef.current(pos + cleanText.length);
113
- }, []);
114
-
115
- // Stable — Ink registers this once and never re-registers unless focusId changes.
116
- // All mutable state is read from refs at call time.
117
- const inputHandler = useCallback(
118
- (input: string, key: Parameters<Parameters<typeof useInput>[0]>[1]) => {
119
- if (focusManager.getCurrentFocus() !== focusId || !isActiveRef.current) return;
120
-
121
- const val = valueRef.current;
122
- const pos = cursorPositionRef.current;
123
- const now = Date.now();
124
- const pasteState = pasteStateRef.current;
125
- const timeSinceFirst = pasteState.firstInputTime
126
- ? now - pasteState.firstInputTime
127
- : 0;
128
-
129
- const isPaste =
130
- input.length > PASTE_CONFIG.LARGE_INPUT_THRESHOLD ||
131
- input.includes('\n') ||
132
- (timeSinceFirst < PASTE_CONFIG.RAPID_INPUT_THRESHOLD_MS &&
133
- pasteState.chunks.length > 0);
134
-
135
- if (isPaste && input.length > 1) {
136
- pasteState.chunks.push(input);
137
- if (!pasteState.firstInputTime) pasteState.firstInputTime = now;
138
- if (pasteState.timeoutId) clearTimeout(pasteState.timeoutId);
139
- pasteState.timeoutId = setTimeout(() => {
140
- const mergedText = pasteState.chunks.join('');
141
- handlePasteStable(mergedText);
142
- pasteState.chunks = [];
143
- pasteState.timeoutId = null;
144
- pasteState.firstInputTime = null;
145
- }, PASTE_CONFIG.TIMEOUT_MS);
146
- return;
147
- }
148
-
149
- // Multi-agent commands (/multi, /multiyolo, /research, /build, /forge, /council):
150
- // Enter inserts newline, Shift+Enter submits — enables multi-line prompts.
151
- // All other commands/messages: Enter submits immediately (as before).
152
- const MULTI_AGENT_CMDS = ['/multi', '/multiyolo', '/research', '/build', '/forge', '/council'];
153
- const isMultiAgent = MULTI_AGENT_CMDS.some(cmd => val.trimStart().startsWith(cmd));
154
- if (key.return) {
155
- if (isMultiAgent && !key.shift) {
156
- // Insert newline at cursor
157
- const newValue = val.slice(0, pos) + '\n' + val.slice(pos);
158
- onChangeRef.current(newValue);
159
- onChangeCursorPositionRef.current(pos + 1);
160
- } else {
161
- onSubmitRef.current?.(val);
162
- }
163
- return;
164
- }
165
- if (key.leftArrow) { if (pos > 0) onChangeCursorPositionRef.current(pos - 1); return; }
166
- if (key.rightArrow) { if (pos < val.length) onChangeCursorPositionRef.current(pos + 1); return; }
167
- if (key.upArrow) { onArrowUpRef.current?.(); return; }
168
- if (key.downArrow) { onArrowDownRef.current?.(); return; }
169
- if (key.backspace || key.delete) {
170
- if (pos > 0) {
171
- const newValue = val.slice(0, pos - 1) + val.slice(pos);
172
- onChangeRef.current(newValue);
173
- onChangeCursorPositionRef.current(pos - 1);
174
- }
175
- return;
176
- }
177
- if (key.ctrl && input === 'a') { onChangeCursorPositionRef.current(0); return; }
178
- if (key.ctrl && input === 'e') { onChangeCursorPositionRef.current(val.length); return; }
179
- if (key.ctrl && input === 'k') { onChangeRef.current(val.slice(0, pos)); return; }
180
- if (key.ctrl && input === 'u') {
181
- onChangeRef.current(val.slice(pos));
182
- onChangeCursorPositionRef.current(0);
183
- return;
184
- }
185
- if (input && !key.ctrl && !key.meta) {
186
- const newValue = val.slice(0, pos) + input + val.slice(pos);
187
- onChangeRef.current(newValue);
188
- onChangeCursorPositionRef.current(pos + input.length);
189
- }
190
- },
191
- [focusId, handlePasteStable] // both stable — focusId is a constant, handlePasteStable has empty deps
192
- );
193
-
194
- useInput(inputHandler, { isActive });
195
-
196
- return (
197
- <Text>
198
- {!isActive && value.length === 0 ? (
199
- <Text dimColor>{placeholder}</Text>
200
- ) : (
201
- renderedValue
202
- )}
203
- </Text>
204
- );
205
- },
206
- // Only re-render when the displayed output would actually change.
207
- // Callbacks are accessed via refs so they never need to cause a re-render.
208
- // focus changes trigger re-renders via useIsFocused internally (bypasses this comparator).
209
- (prev, next) =>
210
- prev.value === next.value &&
211
- prev.cursorPosition === next.cursorPosition &&
212
- prev.cursorOn === next.cursorOn &&
213
- prev.placeholder === next.placeholder &&
214
- prev.disabled === next.disabled &&
215
- prev.focusId === next.focusId
216
- );
217
-
218
- CustomTextInput.displayName = 'CustomTextInput';
219
-
220
- export default CustomTextInput;