@@ -276,6 +276,36 @@ const loadingIndicatorArc = loadingIndicatorEl.querySelector(
276276// Track current display mode
277277let currentDisplayMode : "inline" | "fullscreen" = "inline" ;
278278
279+ // Whether the user has manually zoomed (disables auto fit-to-width)
280+ let userHasZoomed = false ;
281+
282+ /**
283+ * Compute a scale that fits the PDF page width to the available container width.
284+ * Returns null if the container isn't visible or the page width is unavailable.
285+ */
286+ async function computeFitToWidthScale ( ) : Promise < number | null > {
287+ if ( ! pdfDocument ) return null ;
288+
289+ try {
290+ const page = await pdfDocument . getPage ( currentPage ) ;
291+ const naturalViewport = page . getViewport ( { scale : 1.0 } ) ;
292+ const pageWidth = naturalViewport . width ;
293+
294+ const container = canvasContainerEl as HTMLElement ;
295+ const containerStyle = getComputedStyle ( container ) ;
296+ const paddingLeft = parseFloat ( containerStyle . paddingLeft ) ;
297+ const paddingRight = parseFloat ( containerStyle . paddingRight ) ;
298+ const availableWidth = container . clientWidth - paddingLeft - paddingRight ;
299+
300+ if ( availableWidth <= 0 || pageWidth <= 0 ) return null ;
301+ if ( availableWidth >= pageWidth ) return null ; // Already fits
302+
303+ return availableWidth / pageWidth ;
304+ } catch {
305+ return null ;
306+ }
307+ }
308+
279309/**
280310 * Request the host to resize the app to fit the current PDF page.
281311 * Only applies in inline mode - fullscreen mode uses scrolling.
@@ -4033,16 +4063,19 @@ function scrollSelectionIntoView(): void {
40334063}
40344064
40354065function zoomIn ( ) {
4066+ userHasZoomed = true ;
40364067 scale = Math . min ( scale + 0.25 , 3.0 ) ;
40374068 renderPage ( ) . then ( scrollSelectionIntoView ) ;
40384069}
40394070
40404071function zoomOut ( ) {
4072+ userHasZoomed = true ;
40414073 scale = Math . max ( scale - 0.25 , 0.5 ) ;
40424074 renderPage ( ) . then ( scrollSelectionIntoView ) ;
40434075}
40444076
40454077function resetZoom ( ) {
4078+ userHasZoomed = false ;
40464079 scale = 1.0 ;
40474080 renderPage ( ) . then ( scrollSelectionIntoView ) ;
40484081}
@@ -4887,6 +4920,14 @@ app.ontoolresult = async (result: CallToolResult) => {
48874920 syncFormValuesToStorage ( ) ;
48884921
48894922 updateAnnotationsBadge ( ) ;
4923+
4924+ // Compute fit-to-width scale for narrow containers (e.g. mobile)
4925+ const fitScale = await computeFitToWidthScale ( ) ;
4926+ if ( fitScale !== null ) {
4927+ scale = fitScale ;
4928+ log . info ( "Fit-to-width scale:" , scale ) ;
4929+ }
4930+
48904931 renderPage ( ) ;
48914932 // Start background preloading of all pages for text extraction
48924933 startPreloading ( ) ;
@@ -5196,17 +5237,30 @@ function handleHostContextChanged(ctx: McpUiHostContext) {
51965237 applyHostStyleVariables ( ctx . styles . variables ) ;
51975238 }
51985239
5199- // Apply safe area insets
5240+ // Apply safe area insets — set CSS custom properties for use in both
5241+ // inline mode (padding on .main) and fullscreen mode (padding on .toolbar)
52005242 if ( ctx . safeAreaInsets ) {
5201- mainEl . style . paddingTop = `${ ctx . safeAreaInsets . top } px` ;
5202- mainEl . style . paddingRight = `${ ctx . safeAreaInsets . right } px` ;
5203- mainEl . style . paddingBottom = `${ ctx . safeAreaInsets . bottom } px` ;
5204- mainEl . style . paddingLeft = `${ ctx . safeAreaInsets . left } px` ;
5205- }
5206-
5207- // Log containerDimensions for debugging
5208- if ( ctx . containerDimensions ) {
5209- log . info ( "Container dimensions:" , ctx . containerDimensions ) ;
5243+ const { top, right, bottom, left } = ctx . safeAreaInsets ;
5244+ mainEl . style . setProperty ( "--safe-top" , `${ top } px` ) ;
5245+ mainEl . style . setProperty ( "--safe-right" , `${ right } px` ) ;
5246+ mainEl . style . setProperty ( "--safe-bottom" , `${ bottom } px` ) ;
5247+ mainEl . style . setProperty ( "--safe-left" , `${ left } px` ) ;
5248+ mainEl . style . paddingTop = `${ top } px` ;
5249+ mainEl . style . paddingRight = `${ right } px` ;
5250+ mainEl . style . paddingBottom = `${ bottom } px` ;
5251+ mainEl . style . paddingLeft = `${ left } px` ;
5252+ }
5253+
5254+ // Recompute fit-to-width when container dimensions change
5255+ if ( ctx . containerDimensions && pdfDocument && ! userHasZoomed ) {
5256+ log . info ( "Container dimensions changed:" , ctx . containerDimensions ) ;
5257+ computeFitToWidthScale ( ) . then ( ( fitScale ) => {
5258+ if ( fitScale !== null && Math . abs ( fitScale - scale ) > 0.01 ) {
5259+ scale = fitScale ;
5260+ log . info ( "Recomputed fit-to-width scale:" , scale ) ;
5261+ renderPage ( ) ;
5262+ }
5263+ } ) ;
52105264 }
52115265
52125266 // Handle display mode changes
0 commit comments