Skip to content

Commit 60804c9

Browse files
committed
fix: pack multi-column pages evenly and round macOS thumbnails
1 parent be8b52c commit 60804c9

3 files changed

Lines changed: 23 additions & 5 deletions

File tree

‎server/reader-layout.test.mjs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,10 @@ import {test} from 'node:test';import assert from 'node:assert/strict';
22
import {buildReaderLayout,visibleReaderWindow,buildThumbnailLayout,visibleThumbnailWindow} from '../src/reader-layout.mjs';
33
const pages=Array.from({length:1000},(_,i)=>({number:i+1,width:i%2?792:612,height:i%2?612:792}));
44
test('mixed-size row geometry preserves column widths, gaps and four buffered rows',()=>{for(const columns of [1,2,4]){const layout=buildReaderLayout(pages,1,'vertical',columns),row=layout.rows[80],bounds={top:row.offset+100,bottom:row.offset+600,left:0,right:layout.width};const result=visibleReaderWindow(layout,bounds);assert.equal(result.firstRow,76);assert.equal(result.lastRow,84);assert.equal(result.numbers.length,9*columns);assert.equal(layout.frames[columns].y-layout.frames[0].height,24);assert.ok(result.visible.every(v=>v.row===80));assert.equal(layout.height,layout.rows.at(-1).offset+layout.rows.at(-1).extent);}});
5+
6+
test('mixed orientations use a fixed horizontal gap and center each packed row',()=>{
7+
const input=[{number:1,width:400,height:600},{number:2,width:400,height:600},{number:3,width:600,height:400},{number:4,width:400,height:600}];
8+
const layout=buildReaderLayout(input,1,'vertical',2,24,2);
9+
for(let i=0;i<4;i+=2){const left=layout.frames[i],right=layout.frames[i+1];assert.equal(right.x-left.x-left.width,24);assert.equal(left.x,layout.width-right.x-right.width);}
10+
assert.equal(layout.frames[2].y-layout.frames[0].height,24);
11+
});

‎src/mac-controls.css‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,3 +348,12 @@
348348
box-sizing:border-box;height:36px;min-height:36px;
349349
border-radius:12px;corner-shape:squircle;
350350
}
351+
352+
/* Match the palette's continuous corners in the macOS thumbnail rail. */
353+
.app[data-platform="darwin"] .sidebar .thumbnail-list .thumb,
354+
.app[data-platform="darwin"] .sidebar .thumbnail-highlight{
355+
border-radius:18px;corner-shape:squircle;
356+
}
357+
.app[data-platform="darwin"] .sidebar .thumbnail-list .thumb canvas{
358+
border-radius:10px;corner-shape:squircle;
359+
}

‎src/reader-layout.mjs‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
const firstAfter=(items,value,end)=>{let low=0,high=items.length;while(low<high){const middle=(low+high)>>>1;if(end(items[middle])<=value)low=middle+1;else high=middle;}return low;};
22
export function buildReaderLayout(pages,scale=1,direction='vertical',columns=1,gap=24,border=2){
3-
const count=direction==='horizontal'?1:columns,rows=[],frames=[],columnWidths=Array(count).fill(0);
4-
if(direction==='vertical')for(let i=0;i<pages.length;i++)columnWidths[i%count]=Math.max(columnWidths[i%count],pages[i].width*scale+border);
5-
const xs=columnWidths.map((_,i)=>columnWidths.slice(0,i).reduce((sum,w)=>sum+w+gap,0));
3+
const count=direction==='horizontal'?1:columns,rows=[],frames=[];
4+
const rowWidths=[];
5+
if(direction==='vertical')for(let start=0;start<pages.length;start+=count){const group=pages.slice(start,start+count);rowWidths.push(group.reduce((sum,p)=>sum+p.width*scale+border,0)+Math.max(0,group.length-1)*gap);}
6+
const layoutWidth=Math.max(0,...rowWidths);
67
let offset=0;const height=direction==='horizontal'?pages.reduce((max,p)=>Math.max(max,p.height*scale+border),0):0;
78
for(let start=0;start<pages.length;start+=count){
89
const group=pages.slice(start,start+count),extent=direction==='horizontal'?group[0].width*scale+border:Math.max(...group.map(p=>p.height*scale+border));
910
const row={start,end:start+group.length,offset,extent};rows.push(row);
10-
for(let i=0;i<group.length;i++){const p=group[i],width=p.width*scale+border,h=p.height*scale+border;frames.push({number:p.number,row:rows.length-1,x:direction==='horizontal'?offset:xs[i],y:direction==='horizontal'?(height-h)/2:offset,width,height:h});}
11+
let rowX=(layoutWidth-(rowWidths[rows.length-1]||0))/2;
12+
for(let i=0;i<group.length;i++){const p=group[i],width=p.width*scale+border,h=p.height*scale+border;frames.push({number:p.number,row:rows.length-1,x:direction==='horizontal'?offset:rowX,y:direction==='horizontal'?(height-h)/2:offset,width,height:h});rowX+=width+gap;}
1113
offset+=extent+gap;
1214
}
1315
const length=rows.length?offset-gap:0;
14-
return {direction,columns:count,rows,frames,width:direction==='horizontal'?length:columnWidths.reduce((a,b)=>a+b,0)+Math.max(0,count-1)*gap,height:direction==='horizontal'?height:length};
16+
return {direction,columns:count,rows,frames,width:direction==='horizontal'?length:layoutWidth,height:direction==='horizontal'?height:length};
1517
}
1618
export function visibleReaderWindow(layout,bounds,active=1,buffer=4){
1719
const horizontal=layout.direction==='horizontal',start=horizontal?bounds.left:bounds.top,end=horizontal?bounds.right:bounds.bottom;

0 commit comments

Comments
 (0)