aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-04-22 14:32:03 +0530
committerBobby <[email protected]>2026-04-22 14:32:03 +0530
commitbbe9d34700995ba1644d726743dbb61144052bd1 (patch)
treebee0455edb7023d08b348022672ec2c294f2579c
parent87f4e51a2804d0788c8cd1fcde56923d19d0e06c (diff)
downloadhollowdark-bbe9d34700995ba1644d726743dbb61144052bd1.tar.xz
hollowdark-bbe9d34700995ba1644d726743dbb61144052bd1.zip
Load the content manifest and cache every chunk to IndexedDB on initial loadHEADmain
-rw-r--r--content-system/registry/registry.ts14
-rw-r--r--content-system/registry/singleton.ts25
-rw-r--r--loading/content.ts46
-rw-r--r--routes/+layout.svelte4
-rw-r--r--routes/+page.svelte6
5 files changed, 88 insertions, 7 deletions
diff --git a/content-system/registry/registry.ts b/content-system/registry/registry.ts
index 38f0a91..6910cab 100644
--- a/content-system/registry/registry.ts
+++ b/content-system/registry/registry.ts
@@ -62,18 +62,26 @@ export class ContentRegistry {
}
}
+/** Per-chunk callback used to advance a progress bar as loading proceeds. */
+export type ChunkProgressCallback = (loaded: number, total: number) => void
+
/**
* Populate a fresh registry from the supplied manifest. Fetches every
* chunk whose id begins with a world-content prefix; ignores unknown
* prefixes so the registry can grow without breaking older clients.
+ * Chunks load in parallel; the callback reports completion order, not
+ * start order.
*/
export async function populateFromManifest(
manifest: ContentManifest,
- baseUrl: string
+ baseUrl: string,
+ onProgress?: ChunkProgressCallback
): Promise<ContentRegistry> {
const registry = new ContentRegistry()
-
const entries = Object.entries(manifest.chunks)
+ const total = entries.length
+ let loaded = 0
+
await Promise.all(
entries.map(async ([chunkId]) => {
const { data } = await loadChunk(chunkId, manifest, baseUrl)
@@ -84,6 +92,8 @@ export async function populateFromManifest(
} else if (chunkId.startsWith('world/institutions/')) {
registry.addInstitution(data as unknown as InstitutionContent)
}
+ loaded += 1
+ onProgress?.(loaded, total)
})
)
diff --git a/content-system/registry/singleton.ts b/content-system/registry/singleton.ts
new file mode 100644
index 0000000..d8a556e
--- /dev/null
+++ b/content-system/registry/singleton.ts
@@ -0,0 +1,25 @@
+import type { ContentRegistry } from '@hollowdark/content-system/registry/registry'
+
+let instance: ContentRegistry | null = null
+
+/** Record the registry populated during session startup. */
+export function setContentRegistry(registry: ContentRegistry): void {
+ instance = registry
+}
+
+/**
+ * Access the shared content registry. Throws if called before the
+ * loading pipeline has populated it — simulation code should never run
+ * until initial load is complete.
+ */
+export function getContentRegistry(): ContentRegistry {
+ if (instance === null) {
+ throw new Error('Content registry not initialised. Initial load must complete first.')
+ }
+ return instance
+}
+
+/** True once the registry has been populated. */
+export function hasContentRegistry(): boolean {
+ return instance !== null
+}
diff --git a/loading/content.ts b/loading/content.ts
new file mode 100644
index 0000000..d3181fa
--- /dev/null
+++ b/loading/content.ts
@@ -0,0 +1,46 @@
+import { fetchManifest } from '@hollowdark/content-system/loader/loader'
+import { populateFromManifest } from '@hollowdark/content-system/registry/registry'
+import { setContentRegistry } from '@hollowdark/content-system/registry/singleton'
+import { loadingProgress } from '@hollowdark/loading/progress'
+
+/**
+ * Run the real initial-load pipeline. Fetches the content manifest,
+ * walks every chunk into the shared registry, and reports progress to
+ * the loading store so the initial-load screen's bar moves in real
+ * time. Falls back to whatever is cached when offline.
+ *
+ * `baseUrl` is the app's base path — empty string in local dev, the
+ * hosting sub-path under GitHub Pages.
+ */
+export async function runInitialLoad(baseUrl: string): Promise<void> {
+ loadingProgress.set({
+ percentage: 0,
+ currentMessage: 'Preparing your reading space',
+ phase: 'loading'
+ })
+
+ const manifest = await fetchManifest(baseUrl)
+ const chunkCount = Object.keys(manifest.chunks).length
+
+ if (chunkCount === 0) {
+ setContentRegistry(
+ await populateFromManifest(manifest, baseUrl, () => {})
+ )
+ loadingProgress.set({
+ percentage: 1,
+ currentMessage: 'Preparing your reading space',
+ phase: 'complete'
+ })
+ return
+ }
+
+ const registry = await populateFromManifest(manifest, baseUrl, (loaded, total) => {
+ loadingProgress.set({
+ percentage: loaded / total,
+ currentMessage: 'Preparing your reading space',
+ phase: loaded === total ? 'complete' : 'loading'
+ })
+ })
+
+ setContentRegistry(registry)
+}
diff --git a/routes/+layout.svelte b/routes/+layout.svelte
index 0cf4c43..0d84d4d 100644
--- a/routes/+layout.svelte
+++ b/routes/+layout.svelte
@@ -1,12 +1,12 @@
<script lang="ts">
- import { base } from '$app/paths'
+ import { assets } from '$app/paths'
import AudioPlayer from '@hollowdark/lib/audio/AudioPlayer.svelte'
import { highContrast, reduceMotion } from '@hollowdark/lib/accessibility/state'
import { textSize, type TextSize } from '@hollowdark/lib/reading/state'
let { children } = $props()
- const titleTrackSrc = `${base}/audio/title/piano-relaxing.mp3`
+ const titleTrackSrc = `${assets}/audio/title/piano-relaxing.mp3`
const ALL_TEXT_SIZE_CLASSES: readonly string[] = [
'text-size-small',
diff --git a/routes/+page.svelte b/routes/+page.svelte
index babf994..debc068 100644
--- a/routes/+page.svelte
+++ b/routes/+page.svelte
@@ -1,10 +1,10 @@
<script lang="ts">
import { onMount } from 'svelte'
import { goto } from '$app/navigation'
- import { resolve } from '$app/paths'
+ import { assets, resolve } from '$app/paths'
import BeginScreen from '@hollowdark/lib/screens/BeginScreen.svelte'
import InitialLoadScreen from '@hollowdark/lib/screens/InitialLoadScreen.svelte'
- import { runStubInitialLoad } from '@hollowdark/loading/stub'
+ import { runInitialLoad } from '@hollowdark/loading/content'
import { detectBeginState, type BeginState } from '@hollowdark/loading/session'
import {
hasCompletedInitialLoad,
@@ -18,7 +18,7 @@
onMount(async () => {
if (!hasCompletedInitialLoad()) {
- await runStubInitialLoad()
+ await runInitialLoad(assets)
markInitialLoadComplete()
}
beginState = await detectBeginState()