diff --git a/ui/src/components/MarkdownBody.tsx b/ui/src/components/MarkdownBody.tsx index 683adc53..06845527 100644 --- a/ui/src/components/MarkdownBody.tsx +++ b/ui/src/components/MarkdownBody.tsx @@ -8,6 +8,8 @@ import { useTheme } from "../context/ThemeContext"; interface MarkdownBodyProps { children: string; className?: string; + /** Optional resolver for relative image paths (e.g. within export packages) */ + resolveImageSrc?: (src: string) => string | null; } let mermaidLoaderPromise: Promise | null = null; @@ -112,7 +114,7 @@ function MermaidDiagramBlock({ source, darkMode }: { source: string; darkMode: b ); } -export function MarkdownBody({ children, className }: MarkdownBodyProps) { +export function MarkdownBody({ children, className, resolveImageSrc }: MarkdownBodyProps) { const { theme } = useTheme(); return (
); }, + img: resolveImageSrc + ? ({ src, alt, ...imgProps }) => { + const resolved = src ? resolveImageSrc(src) : null; + return {alt; + } + : undefined, }} > {children} diff --git a/ui/src/pages/CompanyExport.tsx b/ui/src/pages/CompanyExport.tsx index 200ce50e..13eda7fc 100644 --- a/ui/src/pages/CompanyExport.tsx +++ b/ui/src/pages/CompanyExport.tsx @@ -470,10 +470,12 @@ function generateReadmeFromSelection( function ExportPreviewPane({ selectedFile, content, + allFiles, onSkillClick, }: { selectedFile: string | null; content: CompanyPortabilityFileEntry | null; + allFiles: Record; onSkillClick?: (skill: string) => void; }) { if (!selectedFile || content === null) { @@ -487,6 +489,20 @@ function ExportPreviewPane({ const parsed = isMarkdown && textContent ? parseFrontmatter(textContent) : null; const imageSrc = isPortableImageFile(selectedFile, content) ? getPortableFileDataUrl(selectedFile, content) : null; + // Resolve relative image paths within the export package (e.g. images/org-chart.png) + const resolveImageSrc = isMarkdown + ? (src: string) => { + // Skip absolute URLs and data URIs + if (/^(?:https?:|data:)/i.test(src)) return null; + // Resolve relative to the directory of the current markdown file + const dir = selectedFile.includes("/") ? selectedFile.slice(0, selectedFile.lastIndexOf("/") + 1) : ""; + const resolved = dir + src; + const entry = allFiles[resolved] ?? allFiles[src]; + if (!entry) return null; + return getPortableFileDataUrl(resolved in allFiles ? resolved : src, entry); + } + : undefined; + return (
@@ -496,10 +512,10 @@ function ExportPreviewPane({ {parsed ? ( <> - {parsed.body.trim() && {parsed.body}} + {parsed.body.trim() && {parsed.body}} ) : isMarkdown ? ( - {textContent ?? ""} + {textContent ?? ""} ) : imageSrc ? (
{selectedFile} @@ -924,7 +940,7 @@ export function CompanyExport() {
- +
diff --git a/ui/src/pages/CompanyImport.tsx b/ui/src/pages/CompanyImport.tsx index 20220dab..10b00266 100644 --- a/ui/src/pages/CompanyImport.tsx +++ b/ui/src/pages/CompanyImport.tsx @@ -177,11 +177,13 @@ function importFileRowClassName(_node: FileTreeNode, checked: boolean) { function ImportPreviewPane({ selectedFile, content, + allFiles, action, renamedTo, }: { selectedFile: string | null; content: CompanyPortabilityFileEntry | null; + allFiles: Record; action: string | null; renamedTo: string | null; }) { @@ -197,6 +199,18 @@ function ImportPreviewPane({ const imageSrc = isPortableImageFile(selectedFile, content) ? getPortableFileDataUrl(selectedFile, content) : null; const actionColor = action ? (ACTION_COLORS[action] ?? ACTION_COLORS.skip) : ""; + // Resolve relative image paths within the import package + const resolveImageSrc = isMarkdown + ? (src: string) => { + if (/^(?:https?:|data:)/i.test(src)) return null; + const dir = selectedFile.includes("/") ? selectedFile.slice(0, selectedFile.lastIndexOf("/") + 1) : ""; + const resolved = dir + src; + const entry = allFiles[resolved] ?? allFiles[src]; + if (!entry) return null; + return getPortableFileDataUrl(resolved in allFiles ? resolved : src, entry); + } + : undefined; + return (
@@ -223,10 +237,10 @@ function ImportPreviewPane({ {parsed ? ( <> - {parsed.body.trim() && {parsed.body}} + {parsed.body.trim() && {parsed.body}} ) : isMarkdown ? ( - {textContent ?? ""} + {textContent ?? ""} ) : imageSrc ? (
{selectedFile} @@ -1265,6 +1279,7 @@ export function CompanyImport() {