diff --git a/apps/web/e2e/pwa.spec.ts b/apps/web/e2e/pwa.spec.ts index dd03a27..797b2d6 100644 --- a/apps/web/e2e/pwa.spec.ts +++ b/apps/web/e2e/pwa.spec.ts @@ -33,6 +33,24 @@ test('installs the app shell and requests persistent storage', async ({ page }) expect(await page.evaluate(() => Reflect.get(globalThis, '__inkfinitePersistRequested'))).toBe( true ); + + await page.getByRole('button', { name: 'About Inkfinite' }).click(); + const displayedVersion = (await page.getByText(/^Version v/).textContent())?.replace( + 'Version ', + '' + ); + const cachedVersions = await page.evaluate(async () => + (await caches.keys()) + .filter((name) => name.startsWith('inkfinite-')) + .flatMap((name) => { + try { + return [JSON.parse(name.slice('inkfinite-'.length)).display as string]; + } catch { + return []; + } + }) + ); + expect(cachedVersions).toContain(displayedVersion); }); test('creates, reopens, and exports a board while offline', async ({ editor, page, context }) => { diff --git a/apps/web/src/lib/docs/DocsShell.svelte b/apps/web/src/lib/docs/DocsShell.svelte index 56d36d6..417444b 100644 --- a/apps/web/src/lib/docs/DocsShell.svelte +++ b/apps/web/src/lib/docs/DocsShell.svelte @@ -6,6 +6,7 @@ import DocsPageNavigation from './DocsPageNavigation.svelte'; import DocsSeo from './DocsSeo.svelte'; import DocsSidebar from './DocsSidebar.svelte'; + import SiteFooter from './SiteFooter.svelte'; import SiteHeader from './SiteHeader.svelte'; import DocsToc from './DocsToc.svelte'; import { getAdjacentDocs } from './content'; @@ -52,6 +53,8 @@ + + diff --git a/apps/web/src/lib/version.ts b/apps/web/src/lib/version.ts new file mode 100644 index 0000000..b31c2c3 --- /dev/null +++ b/apps/web/src/lib/version.ts @@ -0,0 +1,39 @@ +import { version as VERSION } from '$app/environment'; + +const REPOSITORY_URL = 'https://github.com/stormlightlabs/inkfinite'; + +/** Git-derived version details embedded in each web build. */ +export type BuildVersion = { display: string; commit: string; version: string }; + +const fallbackVersion: BuildVersion = { display: 'v0.0.0', commit: 'unknown', version: 'v0.0.0' }; + +function isBuildVersion(value: unknown): value is BuildVersion { + if (!value || typeof value !== 'object') return false; + + const candidate = value as Record; + return ( + typeof candidate.display === 'string' && + typeof candidate.commit === 'string' && + typeof candidate.version === 'string' + ); +} + +function parseBuildVersion(value: string): BuildVersion { + try { + const parsed: unknown = JSON.parse(value); + if (isBuildVersion(parsed)) return parsed; + } catch { + // Development and fallback builds can expose an ordinary SvelteKit version string. + } + + return fallbackVersion; +} + +/** Version details for the current web build. */ +export const buildVersion = parseBuildVersion(VERSION); + +/** Source page for the commit used by the current web build. */ +export const buildCommitUrl = + buildVersion.commit === 'unknown' + ? REPOSITORY_URL + : `${REPOSITORY_URL}/commit/${buildVersion.commit}`; diff --git a/apps/web/src/routes/+page.svelte b/apps/web/src/routes/+page.svelte index 05b486c..06d149b 100644 --- a/apps/web/src/routes/+page.svelte +++ b/apps/web/src/routes/+page.svelte @@ -4,6 +4,7 @@ import Seo from '$lib/Seo.svelte'; import { site } from '$lib/site'; import '$lib/docs/docs.css'; + import SiteFooter from '$lib/docs/SiteFooter.svelte'; import SiteHeader from '$lib/docs/SiteHeader.svelte'; import { Icon } from '$ui'; @@ -287,6 +288,7 @@ +