@@ -16,7 +16,7 @@ import { QuranManifestService } from "@/services/quran-manifest";
1616import { planEditionDownload , type InstalledVersions } from "@/services/quran-download-plan" ;
1717import { useQuranStore } from "@/stores/quran" ;
1818import { AppLogger } from "@/utils/appLogger" ;
19- import type { DownloadProgress } from "@/types/quran" ;
19+ import type { DownloadProgress , QuranManifestVersion } from "@/types/quran" ;
2020
2121const log = AppLogger . create ( "quran-download" ) ;
2222
@@ -125,6 +125,12 @@ const getVersionDir = (version: MushafVersion): Directory => {
125125 return new Directory ( Paths . document , `quran/${ version } ` ) ;
126126} ;
127127
128+ // Where the ayah-marker medallion frames live (AyahMarker reads them here);
129+ // populated by extracting the edition's ayah-marker ornament pack.
130+ const getMarkersDir = ( version : MushafVersion ) : Directory => {
131+ return new Directory ( Paths . document , `quran/${ version } /markers` ) ;
132+ } ;
133+
128134// Dark-theme images live in a sibling directory so they can be downloaded and
129135// deleted independently of the main (light) bundle.
130136const getDarkVersionDir = ( version : MushafVersion ) : Directory => {
@@ -315,6 +321,57 @@ const downloadAndExtractBundle = async (
315321 }
316322} ;
317323
324+ // Download + extract the edition's default ayah-marker frame pack into the markers
325+ // dir, when absent or out of date. Self-contained + non-fatal: any failure is
326+ // logged and swallowed so the caller's edition flow proceeds (just without markers).
327+ const ensureAyahMarkers = async (
328+ active : ActiveDownload ,
329+ version : MushafVersion ,
330+ manifestVersion : QuranManifestVersion
331+ ) : Promise < void > => {
332+ try {
333+ const pack = await QuranManifestService . getAyahMarkerPack ( manifestVersion ) ;
334+ if ( ! pack || active . cancelled ) return ;
335+ const markersDir = getMarkersDir ( version ) ;
336+ const haveFrames = new File ( markersDir , "marker-sepia.png" ) . exists ;
337+ if ( haveFrames && readInstalled ( version ) . markers === pack . version ) return ;
338+
339+ const outcome = await downloadAndExtractBundle ( active , {
340+ url : pack . url ,
341+ sizeBytes : 0 ,
342+ dir : markersDir ,
343+ zipName : `quran-${ version } -markers.zip` ,
344+ alreadyOnDisk : false ,
345+ emit : ( ) => { } ,
346+ } ) ;
347+ if ( outcome === BundleOutcome . EXTRACTED ) {
348+ writeInstalled ( version , { markers : pack . version } ) ;
349+ log . d ( "Download" , `Installed ayah-marker pack ${ pack . version } for ${ version } ` ) ;
350+ }
351+ } catch ( error ) {
352+ log . e (
353+ "Download" ,
354+ `Ayah-marker pack failed for ${ version } (markers absent)` ,
355+ error instanceof Error ? error : undefined
356+ ) ;
357+ }
358+ } ;
359+
360+ // Opportunistic, idempotent marker fetch for an already-installed edition (the
361+ // edition download covers fresh installs; this catches editions installed before
362+ // the ornament pack existed, or a pack version bump). Cheap: no-ops once the
363+ // current pack is on disk. Runs in its own lightweight context (not a tracked
364+ // download) so it never shows progress chrome.
365+ const ensureMarkersInstalled = async ( version : MushafVersion ) : Promise < void > => {
366+ const manifestVersion = await QuranManifestService . getVersionInfo ( version ) ;
367+ if ( ! manifestVersion ) return ;
368+ await ensureAyahMarkers (
369+ { controller : new AbortController ( ) , cancelled : false , promise : Promise . resolve ( ) } ,
370+ version ,
371+ manifestVersion
372+ ) ;
373+ } ;
374+
318375const start = ( version : MushafVersion ) : Promise < void > =>
319376 beginDownload ( activeDownloads , version , doStart ) ;
320377
@@ -431,6 +488,11 @@ const doStart = async (version: MushafVersion, active: ActiveDownload): Promise<
431488 }
432489 if ( plan . needMeta ) writeInstalled ( version , { meta : manifestVersion . meta . version } ) ;
433490
491+ // 3) Ayah-marker ornament (tiny ~20KB): the edition's default medallion frames
492+ // the reader overlays on each ayah. Fetched when missing or its version changed.
493+ // Non-fatal — a failure leaves the edition usable (text/images), just no markers.
494+ await ensureAyahMarkers ( active , version , manifestVersion ) ;
495+
434496 // Complete only once BOTH legs have landed.
435497 clearResume ( version ) ;
436498 store . updateDownloadState ( version , { status : DownloadStatus . COMPLETE } ) ;
@@ -656,6 +718,7 @@ const checkDiskSpace = (requiredMB: number): { available: boolean; availableMB:
656718
657719export const QuranDownload = {
658720 start,
721+ ensureMarkersInstalled,
659722 startDark,
660723 deleteDark,
661724 pause,
0 commit comments