Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions website/src/components/doc-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ export async function loadDocContent(docPath) {
link.setAttribute('rel', 'noopener noreferrer')
})

// Re-root relative image paths. Rendered AsciiDoc emits paths like
// "docs/workflow-diagram.svg" relative to the document, but doc pages are
// served under clean URLs (e.g. /spec-driven-development/), so the browser
// resolves the relative path against the route and 404s. Prefix the site
// base so it points at the asset's real location. Absolute, protocol and
// data URLs are left untouched.
contentEl.querySelectorAll('img[src]').forEach((img) => {
const src = img.getAttribute('src')
if (!src || /^(https?:|data:|\/|#)/.test(src)) return
img.setAttribute('src', `${import.meta.env.BASE_URL}${src}`)
})

// Attach click-to-load handlers for any YouTube placeholders in the doc.
// Keeps us DSGVO-compliant: YouTube is only contacted after user consent.
hydrateYouTubeFacades(contentEl)
Expand Down
18 changes: 18 additions & 0 deletions website/src/components/doc-page.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,22 @@ describe('doc-page', () => {
'Failed to Load Documentation'
)
})

it('re-roots relative image paths to the site base, leaving absolute ones', async () => {
global.fetch.mockResolvedValue({
ok: true,
text: async () =>
'<h1>Spec</h1><img src="docs/workflow-diagram.svg" alt="d"><img src="https://x.test/a.png" alt="ext">',
})

await loadDocContent('docs/spec-driven-workflow.adoc')

const base = import.meta.env.BASE_URL
const rel = document.querySelector('#doc-content img[alt="d"]')
const ext = document.querySelector('#doc-content img[alt="ext"]')
// relative AsciiDoc image path must be re-rooted (clean-URL routes break it otherwise)
expect(rel.getAttribute('src')).toBe(`${base}docs/workflow-diagram.svg`)
// absolute/external sources are left untouched
expect(ext.getAttribute('src')).toBe('https://x.test/a.png')
})
})
Loading