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
61 changes: 31 additions & 30 deletions src/components/repositories/ContributingViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
import axios from 'axios';
import { STATUS_COLORS } from '../../theme';
import { resolveRelativeUrl } from './MarkdownRenderers';

interface ContributingViewerProps {
repositoryFullName: string; // e.g., "opentensor/bittensor"
Expand Down Expand Up @@ -81,35 +82,6 @@ const ContributingViewer: React.FC<ContributingViewerProps> = ({
);
}

// Custom renderer for images to handle relative paths
const ImageRenderer = (props: any) => {
const { src, alt, ...rest } = props;
let finalSrc = src;

if (src && !src.startsWith('http') && !src.startsWith('//')) {
const cleanPath = src.startsWith('./')
? src.slice(2)
: src.startsWith('/')
? src.slice(1)
: src;
finalSrc = `https://cdn.jsdelivr.net/gh/${repositoryFullName}@${defaultBranch}/${cleanPath}`;
}

return (
<img
src={finalSrc}
alt={alt}
style={{
maxWidth: '100%',
height: 'auto',
borderRadius: '6px',
margin: '16px 0',
}}
{...rest}
/>
);
};

return (
<Paper
elevation={0}
Expand Down Expand Up @@ -204,7 +176,36 @@ const ContributingViewer: React.FC<ContributingViewerProps> = ({
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw]}
components={{ img: ImageRenderer }}
components={{
a: ({ href, children, ...rest }: any) => (
<a
href={resolveRelativeUrl(href, repositoryFullName, defaultBranch)}
target="_blank"
rel="noopener noreferrer"
{...rest}
>
{children}
</a>
),
img: ({ src, alt, ...rest }: any) => (
<img
src={resolveRelativeUrl(
src,
repositoryFullName,
defaultBranch,
'cdn',
)}
alt={alt}
style={{
maxWidth: '100%',
height: 'auto',
borderRadius: '6px',
margin: '16px 0',
}}
{...rest}
/>
),
}}
>
{content || ''}
</ReactMarkdown>
Expand Down
30 changes: 30 additions & 0 deletions src/components/repositories/MarkdownRenderers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Resolve a relative URL to an absolute GitHub URL.
* Returns the original URL if it's already absolute.
*/
export const resolveRelativeUrl = (
url: string | undefined,
repositoryFullName: string,
defaultBranch: string,
type: 'blob' | 'cdn' = 'blob',
): string | undefined => {
if (
!url ||
url.startsWith('http') ||
url.startsWith('//') ||
url.startsWith('#') ||
url.startsWith('mailto:')
) {
return url;
}

const cleanPath = url.replace(/^\.\//, '').replace(/^\//, '');

if (type === 'cdn') {
return `https://cdn.jsdelivr.net/gh/${repositoryFullName}@${defaultBranch}/${cleanPath}`;
}

const hasExtension = /\.[a-zA-Z0-9]+$/.test(cleanPath.replace(/\/$/, ''));
const ghType = cleanPath.endsWith('/') || !hasExtension ? 'tree' : 'blob';
return `https://github.com/${repositoryFullName}/${ghType}/${defaultBranch}/${cleanPath.replace(/\/$/, '')}`;
};
65 changes: 31 additions & 34 deletions src/components/repositories/ReadmeViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
import axios from 'axios';
import { STATUS_COLORS } from '../../theme';
import { resolveRelativeUrl } from './MarkdownRenderers';

interface ReadmeViewerProps {
repositoryFullName: string; // e.g., "opentensor/bittensor"
Expand Down Expand Up @@ -68,37 +69,6 @@ const ReadmeViewer: React.FC<ReadmeViewerProps> = ({ repositoryFullName }) => {
);
}

// Custom renderer for images to handle relative paths
const ImageRenderer = (props: any) => {
const { src, alt, ...rest } = props;
let finalSrc = src;

if (src && !src.startsWith('http') && !src.startsWith('//')) {
// Convert relative path to absolute GitHub user content path
// e.g. ./assets/img.png -> https://raw.githubusercontent.com/user/repo/branch/assets/img.png
const cleanPath = src.startsWith('./')
? src.slice(2)
: src.startsWith('/')
? src.slice(1)
: src;
finalSrc = `https://cdn.jsdelivr.net/gh/${repositoryFullName}@${defaultBranch}/${cleanPath}`;
}

return (
<img
src={finalSrc}
alt={alt}
style={{
maxWidth: '100%',
height: 'auto',
borderRadius: '6px',
margin: '16px 0',
}}
{...rest}
/>
);
};

return (
<Paper
elevation={0}
Expand All @@ -117,7 +87,7 @@ const ReadmeViewer: React.FC<ReadmeViewerProps> = ({ repositoryFullName }) => {
borderBottom: '1px solid #30363d',
pb: 0.3,
mb: 3,
mt: 1, // Reduced from 4
mt: 1,
fontWeight: 600,
color: '#ffffff',
},
Expand All @@ -126,7 +96,7 @@ const ReadmeViewer: React.FC<ReadmeViewerProps> = ({ repositoryFullName }) => {
borderBottom: '1px solid #30363d',
pb: 0.3,
mb: 3,
mt: 2, // Reduced from 4
mt: 2,
fontWeight: 600,
color: '#ffffff',
},
Expand Down Expand Up @@ -209,7 +179,34 @@ const ReadmeViewer: React.FC<ReadmeViewerProps> = ({ repositoryFullName }) => {
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw]}
components={{
img: ImageRenderer,
a: ({ href, children, ...rest }: any) => (
<a
href={resolveRelativeUrl(href, repositoryFullName, defaultBranch)}
target="_blank"
rel="noopener noreferrer"
{...rest}
>
{children}
</a>
),
img: ({ src, alt, ...rest }: any) => (
<img
src={resolveRelativeUrl(
src,
repositoryFullName,
defaultBranch,
'cdn',
)}
alt={alt}
style={{
maxWidth: '100%',
height: 'auto',
borderRadius: '6px',
margin: '16px 0',
}}
{...rest}
/>
),
}}
>
{content || ''}
Expand Down
1 change: 1 addition & 0 deletions src/components/repositories/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export { default as ReadmeViewer } from './ReadmeViewer';
export { default as RepositoryCodeBrowser } from './RepositoryCodeBrowser';
export { default as RepositoryMaintainers } from './RepositoryMaintainers';
export { default as RepositoryCheckTab } from './RepositoryCheckTab';
export { resolveRelativeUrl } from './MarkdownRenderers';
Loading