@@ -143,98 +143,106 @@ export function App({ initialInput, showConfig, editProfile, overrides }: AppPro
143143 } ) ( ) ;
144144 } , [ ] ) ;
145145
146- const processInput = useCallback ( async ( rawInput : string , cfg : Config ) => {
147- const activeConfig = cfg ;
148- abortRef . current ?. abort ( ) ;
149- const controller = new AbortController ( ) ;
150- abortRef . current = controller ;
151- const { signal } = controller ;
152-
153- try {
154- // Extraction phase
155- setState ( "extracting" ) ;
156- setInput ( rawInput ) ;
157- setSummary ( "" ) ;
158- setExtraction ( undefined ) ;
159- setPendingResult ( undefined ) ;
160- setSummaryPinned ( false ) ;
161- setPinnedSummaries ( [ ] ) ;
162-
163- const result = await extract ( rawInput , signal ) ;
164-
165- if ( ! result . content && ! result . image ) {
166- setError ( {
167- message : "Couldn't extract content from this input." ,
168- hint : result . partial
169- ? "This content may be behind a paywall. Try pasting the text directly."
170- : undefined ,
171- } ) ;
172- setState ( "error" ) ;
173- return ;
174- }
146+ const processInput = useCallback (
147+ async ( rawInput : string , cfg : Config ) => {
148+ const activeConfig = cfg ;
149+ abortRef . current ?. abort ( ) ;
150+ const controller = new AbortController ( ) ;
151+ abortRef . current = controller ;
152+ const { signal } = controller ;
175153
176- setExtraction ( result ) ;
154+ try {
155+ // Extraction phase
156+ setState ( "extracting" ) ;
157+ setInput ( rawInput ) ;
158+ setSummary ( "" ) ;
159+ setExtraction ( undefined ) ;
160+ setPendingResult ( undefined ) ;
161+ setSummaryPinned ( false ) ;
162+ setPinnedSummaries ( [ ] ) ;
177163
178- // Truncation for very long content (skip for images)
179- let effectiveConfig = activeConfig ;
180- if ( ! result . image ) {
181- let content = result . content ;
182- const words = content . split ( / \s + / ) ;
183- if ( words . length > MAX_INPUT_WORDS ) {
184- content = words . slice ( 0 , MAX_INPUT_WORDS ) . join ( " " ) ;
185- result . content = content ;
186- }
164+ const result = await extract ( rawInput , signal ) ;
187165
188- // Scale max_tokens for long content
189- if ( words . length > 10_000 ) {
190- effectiveConfig = {
191- ...activeConfig ,
192- maxTokens : Math . min ( activeConfig . maxTokens * 2 , 4096 ) ,
193- } ;
166+ if ( ! result . content && ! result . image ) {
167+ setError ( {
168+ message : "Couldn't extract content from this input." ,
169+ hint : result . partial
170+ ? "This content may be behind a paywall. Try pasting the text directly."
171+ : undefined ,
172+ } ) ;
173+ setState ( "error" ) ;
174+ return ;
194175 }
195- }
196176
197- // Summarization phase
198- setState ( "summarizing" ) ;
199- setIsStreaming ( true ) ;
177+ setExtraction ( result ) ;
200178
201- const tldrResult = await summarize (
202- result ,
203- effectiveConfig ,
204- ( chunk ) => setSummary ( ( prev ) => prev + chunk ) ,
205- signal ,
206- ) ;
179+ // Truncation for very long content (skip for images)
180+ let effectiveConfig = activeConfig ;
181+ if ( ! result . image ) {
182+ let content = result . content ;
183+ const words = content . split ( / \s + / ) ;
184+ if ( words . length > MAX_INPUT_WORDS ) {
185+ content = words . slice ( 0 , MAX_INPUT_WORDS ) . join ( " " ) ;
186+ result . content = content ;
187+ }
207188
208- setIsStreaming ( false ) ;
189+ // Scale max_tokens for long content
190+ if ( words . length > 10_000 ) {
191+ effectiveConfig = {
192+ ...activeConfig ,
193+ maxTokens : Math . min ( activeConfig . maxTokens * 2 , 4096 ) ,
194+ } ;
195+ }
196+ }
209197
210- // Compute session paths BEFORE transitioning to result state
211- // so that currentSession.audioPath is available if user presses 'a' quickly
212- try {
213- const sessionPaths = getSessionPaths ( activeConfig . outputDir , result , tldrResult . summary ) ;
214- setCurrentSession ( sessionPaths ) ;
215- } catch {
216- // Non-fatal
217- }
198+ // Summarization phase
199+ setState ( "summarizing" ) ;
200+ setIsStreaming ( true ) ;
201+
202+ const tldrResult = await summarize (
203+ result ,
204+ effectiveConfig ,
205+ ( chunk ) => setSummary ( ( prev ) => prev + chunk ) ,
206+ signal ,
207+ ) ;
208+
209+ setIsStreaming ( false ) ;
210+
211+ // Compute session paths BEFORE transitioning to result state
212+ // so that currentSession.audioPath is available if user presses 'a' quickly
213+ try {
214+ const sessionPaths = getSessionPaths ( activeConfig . outputDir , result , tldrResult . summary ) ;
215+ setCurrentSession ( sessionPaths ) ;
216+ } catch {
217+ // Non-fatal
218+ }
218219
219- // Defer persistence — store result for saving on Enter
220- setPendingResult ( tldrResult ) ;
221- setState ( "result" ) ;
222- } catch ( err ) {
223- setIsStreaming ( false ) ;
224- // Silently return to idle on abort (ESC pressed)
225- if ( signal . aborted || ( err instanceof DOMException && err . name === "AbortError" ) ) {
226- setState ( "idle" ) ;
227- setSummary ( "" ) ;
228- setExtraction ( undefined ) ;
229- return ;
220+ // Defer persistence — store result for saving on Enter
221+ setPendingResult ( tldrResult ) ;
222+ setState ( "result" ) ;
223+ } catch ( err ) {
224+ setIsStreaming ( false ) ;
225+ // Silently return on abort (ESC pressed)
226+ if ( signal . aborted || ( err instanceof DOMException && err . name === "AbortError" ) ) {
227+ if ( initialInput ) {
228+ clearScreen ( ) ;
229+ exit ( ) ;
230+ return ;
231+ }
232+ setState ( "idle" ) ;
233+ setSummary ( "" ) ;
234+ setExtraction ( undefined ) ;
235+ return ;
236+ }
237+ const message = err instanceof Error ? err . message : "An unexpected error occurred." ;
238+ setError ( { message } ) ;
239+ setState ( "error" ) ;
240+ } finally {
241+ abortRef . current = null ;
230242 }
231- const message = err instanceof Error ? err . message : "An unexpected error occurred." ;
232- setError ( { message } ) ;
233- setState ( "error" ) ;
234- } finally {
235- abortRef . current = null ;
236- }
237- } , [ ] ) ;
243+ } ,
244+ [ clearScreen , exit , initialInput ] ,
245+ ) ;
238246
239247 const handleThemeChange = useCallback ( async ( newThemeConfig : ThemeConfig ) => {
240248 setThemeConfig ( newThemeConfig ) ;
0 commit comments