@@ -5,24 +5,22 @@ import {
55 useCallback ,
66 useEffect ,
77 useImperativeHandle ,
8- useMemo ,
98 useRef ,
109 useState ,
1110} from 'react'
1211
1312import { InputCursor } from './input-cursor'
1413import { useOpentuiPaste } from '../hooks/use-opentui-paste'
1514import { useTheme } from '../hooks/use-theme'
16- import { logger } from '../utils/logger'
1715import { clamp } from '../utils/math'
1816import { calculateNewCursorPosition } from '../utils/word-wrap-utils'
1917
2018import type { InputValue } from '../state/chat-store'
2119import type {
2220 KeyEvent ,
23- LineInfo ,
2421 PasteEvent ,
2522 ScrollBoxRenderable ,
23+ TextBufferView ,
2624 TextRenderable ,
2725} from '@opentui/core'
2826
@@ -131,11 +129,6 @@ export const MultilineInput = forwardRef<
131129 const scrollBoxRef = useRef < ScrollBoxRenderable | null > ( null )
132130 const [ measuredCols , setMeasuredCols ] = useState < number | null > ( null )
133131 const [ lastActivity , setLastActivity ] = useState ( Date . now ( ) )
134- const lineInfoRef = useRef < LineInfo > ( {
135- lineStarts : [ ] ,
136- lineWidths : [ ] ,
137- maxLineWidth : 0 ,
138- } )
139132
140133 // Update last activity on value or cursor changes
141134 useEffect ( ( ) => {
@@ -144,19 +137,13 @@ export const MultilineInput = forwardRef<
144137
145138 const textRef = useRef < TextRenderable | null > ( null )
146139
147- if ( textRef . current ) {
148- lineInfoRef . current = ( textRef . current as any ) . textBufferView . lineInfo
149- }
140+ const lineInfo = textRef . current
141+ ? (
142+ ( textRef . current satisfies TextRenderable as any )
143+ . textBufferView as TextBufferView
144+ ) . lineInfo
145+ : null
150146
151- const getEffectiveCols = useCallback ( ( ) => {
152- // Prefer measured viewport columns; fallback to a conservative
153- // estimate: outer width minus border(2) minus padding(2) = 4.
154- const fallbackCols = Math . max ( 1 , width - 4 )
155- const cols = measuredCols ?? fallbackCols
156- // No extra negative fudge; use the true measured width to avoid
157- // early wrap by a few characters.
158- return Math . max ( 1 , cols )
159- } , [ measuredCols , width ] )
160147 useImperativeHandle (
161148 forwardedRef ,
162149 ( ) => ( {
@@ -190,12 +177,14 @@ export const MultilineInput = forwardRef<
190177 ) ,
191178 )
192179
193- const cursorRow = Math . max (
194- 0 ,
195- lineInfoRef . current . lineStarts . findLastIndex (
196- ( lineStart ) => lineStart <= cursorPosition ,
197- ) ,
198- )
180+ const cursorRow = lineInfo
181+ ? Math . max (
182+ 0 ,
183+ lineInfo . lineStarts . findLastIndex (
184+ ( lineStart ) => lineStart <= cursorPosition ,
185+ ) ,
186+ )
187+ : 0
199188
200189 // Auto-scroll to cursor when content changes
201190 useEffect ( ( ) => {
@@ -270,43 +259,32 @@ export const MultilineInput = forwardRef<
270259 renderCursorPosition += displayValue [ i ] === '\t' ? TAB_WIDTH : 1
271260 }
272261
273- const { beforeCursor, afterCursor, activeChar, shouldHighlight } =
274- useMemo ( ( ) => {
275- if ( ! showCursor ) {
276- return {
277- beforeCursor : '' ,
278- afterCursor : '' ,
279- activeChar : ' ' ,
280- shouldHighlight : false ,
281- }
262+ const { beforeCursor, afterCursor, activeChar, shouldHighlight } = ( ( ) => {
263+ if ( ! showCursor ) {
264+ return {
265+ beforeCursor : '' ,
266+ afterCursor : '' ,
267+ activeChar : ' ' ,
268+ shouldHighlight : false ,
282269 }
270+ }
283271
284- const beforeCursor = displayValueForRendering . slice (
285- 0 ,
286- renderCursorPosition ,
287- )
288- const afterCursor = displayValueForRendering . slice ( renderCursorPosition )
289- const activeChar = afterCursor . charAt ( 0 ) || ' '
290- const shouldHighlight =
291- ! isPlaceholder &&
292- renderCursorPosition < displayValueForRendering . length &&
293- displayValue [ cursorPosition ] !== '\n' &&
294- displayValue [ cursorPosition ] !== '\t'
272+ const beforeCursor = displayValueForRendering . slice ( 0 , renderCursorPosition )
273+ const afterCursor = displayValueForRendering . slice ( renderCursorPosition )
274+ const activeChar = afterCursor . charAt ( 0 ) || ' '
275+ const shouldHighlight =
276+ ! isPlaceholder &&
277+ renderCursorPosition < displayValueForRendering . length &&
278+ displayValue [ cursorPosition ] !== '\n' &&
279+ displayValue [ cursorPosition ] !== '\t'
295280
296- return {
297- beforeCursor,
298- afterCursor,
299- activeChar,
300- shouldHighlight,
301- }
302- } , [
303- showCursor ,
304- displayValueForRendering ,
305- renderCursorPosition ,
306- cursorPosition ,
307- isPlaceholder ,
308- displayValue ,
309- ] )
281+ return {
282+ beforeCursor,
283+ afterCursor,
284+ activeChar,
285+ shouldHighlight,
286+ }
287+ } ) ( )
310288
311289 // Handle all keyboard input with advanced shortcuts
312290 useKeyboard (
@@ -701,7 +679,7 @@ export const MultilineInput = forwardRef<
701679 text : value ,
702680 cursorPosition : calculateNewCursorPosition ( {
703681 cursorPosition,
704- lineInfo : lineInfoRef . current ,
682+ lineStarts : lineInfo ?. lineStarts ?? [ ] ,
705683 cursorIsChar : ! shouldHighlight ,
706684 direction : 'up' ,
707685 } ) ,
@@ -716,7 +694,7 @@ export const MultilineInput = forwardRef<
716694 text : value ,
717695 cursorPosition : calculateNewCursorPosition ( {
718696 cursorPosition,
719- lineInfo : lineInfoRef . current ,
697+ lineStarts : lineInfo ?. lineStarts ?? [ ] ,
720698 cursorIsChar : ! shouldHighlight ,
721699 direction : 'down' ,
722700 } ) ,
@@ -758,7 +736,7 @@ export const MultilineInput = forwardRef<
758736 value ,
759737 cursorPosition ,
760738 shouldHighlight ,
761- lineInfoRef . current ,
739+ lineInfo ,
762740 onChange ,
763741 onSubmit ,
764742 onKeyIntercept ,
@@ -768,12 +746,12 @@ export const MultilineInput = forwardRef<
768746 ) ,
769747 )
770748
771- const layoutMetrics = useMemo ( ( ) => {
749+ const layoutMetrics = ( ( ) => {
772750 const safeMaxHeight = Math . max ( 1 , maxHeight )
773751 const effectiveMinHeight = Math . max ( 1 , Math . min ( minHeight , safeMaxHeight ) )
774752
775753 const totalLines =
776- measuredCols === 0 ? 0 : lineInfoRef . current . lineStarts . length
754+ measuredCols === 0 || lineInfo === null ? 0 : lineInfo . lineStarts . length
777755
778756 // Add bottom gutter when cursor is on line 2 of exactly 2 lines
779757 const gutterEnabled =
@@ -790,7 +768,7 @@ export const MultilineInput = forwardRef<
790768 heightLines,
791769 gutterEnabled,
792770 }
793- } , [ maxHeight , minHeight , lineInfoRef . current , cursorRow ] )
771+ } ) ( )
794772
795773 const inputColor = isPlaceholder
796774 ? theme . muted
0 commit comments