@@ -86,7 +86,8 @@ function suggestOutput(input: string, suffix: string, ext?: string) {
8686 return `${ dir } ${ stem } .${ suffix } ${ finalExt } ` ;
8787}
8888
89- const PAGE_SIZE = 100 ;
89+ const PAGE_SIZE_OPTIONS = [ 50 , 100 , 250 , 500 , 1000 ] ;
90+ const DEFAULT_PAGE_SIZE = 100 ;
9091
9192// ---------- root -----------------------------------------------------------
9293
@@ -96,6 +97,7 @@ export default function App() {
9697 const [ info , setInfo ] = useState < main . FileInfo | null > ( null ) ;
9798 const [ page , setPage ] = useState < main . PagePayload | null > ( null ) ;
9899 const [ offset , setOffset ] = useState ( 0 ) ;
100+ const [ pageSize , setPageSize ] = useState ( DEFAULT_PAGE_SIZE ) ;
99101 const [ loadingFile , setLoadingFile ] = useState ( false ) ;
100102 const [ loadingPage , setLoadingPage ] = useState ( false ) ;
101103 const [ stats , setStats ] = useState < main . StatsPayload | null > ( null ) ;
@@ -134,7 +136,7 @@ export default function App() {
134136 const i = await FileInfoCSV ( path ) ;
135137 setInfo ( i ) ;
136138 setOffset ( 0 ) ;
137- const p = await ReadPage ( path , 0 , PAGE_SIZE ) ;
139+ const p = await ReadPage ( path , 0 , pageSize ) ;
138140 setPage ( p ) ;
139141 } finally {
140142 setLoadingFile ( false ) ;
@@ -146,18 +148,25 @@ export default function App() {
146148 if ( p ) await loadFile ( p ) ;
147149 }
148150
149- async function gotoOffset ( newOffset : number ) {
151+ async function gotoOffset ( newOffset : number , size = pageSize ) {
150152 if ( ! info ) return ;
151153 setLoadingPage ( true ) ;
152154 try {
153- const p = await ReadPage ( info . path , Math . max ( 0 , newOffset ) , PAGE_SIZE ) ;
155+ const p = await ReadPage ( info . path , Math . max ( 0 , newOffset ) , size ) ;
154156 setPage ( p ) ;
155157 setOffset ( p . offset ) ;
156158 } finally {
157159 setLoadingPage ( false ) ;
158160 }
159161 }
160162
163+ async function changePageSize ( size : number ) {
164+ setPageSize ( size ) ;
165+ // Snap to the page boundary that contains the current offset.
166+ const newOffset = Math . floor ( offset / size ) * size ;
167+ await gotoOffset ( newOffset , size ) ;
168+ }
169+
161170 async function loadStatsIfNeeded ( ) : Promise < main . StatsPayload | null > {
162171 if ( stats ) return stats ;
163172 if ( ! info ) return null ;
@@ -180,9 +189,11 @@ export default function App() {
180189 < Toolbar
181190 info = { info }
182191 offset = { offset }
192+ pageSize = { pageSize }
193+ onPageSize = { changePageSize }
183194 page = { page }
184- onPrev = { ( ) => gotoOffset ( offset - PAGE_SIZE ) }
185- onNext = { ( ) => gotoOffset ( offset + PAGE_SIZE ) }
195+ onPrev = { ( ) => gotoOffset ( offset - pageSize ) }
196+ onNext = { ( ) => gotoOffset ( offset + pageSize ) }
186197 onJump = { ( o ) => gotoOffset ( o ) }
187198 onAction = { setAction }
188199 />
@@ -288,9 +299,11 @@ function FileInfoBar({ info }: { info: main.FileInfo }) {
288299 ) ;
289300}
290301
291- function Toolbar ( { info, offset, page, onPrev, onNext, onJump, onAction } : {
302+ function Toolbar ( { info, offset, pageSize , onPageSize , page, onPrev, onNext, onJump, onAction } : {
292303 info : main . FileInfo ;
293304 offset : number ;
305+ pageSize : number ;
306+ onPageSize : ( n : number ) => void ;
294307 page : main . PagePayload | null ;
295308 onPrev : ( ) => void ;
296309 onNext : ( ) => void ;
@@ -299,18 +312,18 @@ function Toolbar({ info, offset, page, onPrev, onNext, onJump, onAction }: {
299312} ) {
300313 const total = page ?. totalRows ?? info . rows ;
301314 const showingFrom = total === 0 ? 0 : offset + 1 ;
302- const showingTo = Math . min ( offset + PAGE_SIZE , total ) ;
303- const totalPages = Math . max ( 1 , Math . ceil ( total / PAGE_SIZE ) ) ;
304- const currentPage = Math . floor ( offset / PAGE_SIZE ) + 1 ;
315+ const showingTo = Math . min ( offset + pageSize , total ) ;
316+ const totalPages = Math . max ( 1 , Math . ceil ( total / pageSize ) ) ;
317+ const currentPage = Math . floor ( offset / pageSize ) + 1 ;
305318 const canPrev = offset > 0 ;
306- const canNext = offset + PAGE_SIZE < total ;
319+ const canNext = offset + pageSize < total ;
307320
308321 const [ pageInput , setPageInput ] = useState ( String ( currentPage ) ) ;
309322 useEffect ( ( ) => setPageInput ( String ( currentPage ) ) , [ currentPage ] ) ;
310323
311324 function jumpFromInput ( ) {
312325 const n = Math . max ( 1 , Math . min ( totalPages , Number ( pageInput ) || 1 ) ) ;
313- onJump ( ( n - 1 ) * PAGE_SIZE ) ;
326+ onJump ( ( n - 1 ) * pageSize ) ;
314327 }
315328
316329 return (
@@ -339,6 +352,17 @@ function Toolbar({ info, offset, page, onPrev, onNext, onJump, onAction }: {
339352 </ span >
340353
341354 < div className = "ml-auto flex items-center gap-2" >
355+ < div className = "flex items-center gap-1.5 text-xs text-muted-foreground" >
356+ < span > Per page</ span >
357+ < Select value = { String ( pageSize ) } onValueChange = { ( v ) => onPageSize ( Number ( v ) ) } >
358+ < SelectTrigger className = "h-7 w-[78px]" > < SelectValue /> </ SelectTrigger >
359+ < SelectContent >
360+ { PAGE_SIZE_OPTIONS . map ( ( n ) => (
361+ < SelectItem key = { n } value = { String ( n ) } > { n } </ SelectItem >
362+ ) ) }
363+ </ SelectContent >
364+ </ Select >
365+ </ div >
342366 < DropdownMenu >
343367 < DropdownMenuTrigger asChild >
344368 < Button size = "sm" >
0 commit comments