(theme.palette.mode === 'dark' ? 'background.paper' : 'rgba(0, 0, 0, 0.02)'),
+ textDecoration: 'none',
+ color: 'inherit',
+ overflow: 'hidden',
+ cursor: 'pointer',
+ transition: 'all 160ms ease-in-out',
+ '&:hover': {
+ bgcolor: 'action.hover',
+ borderColor: 'primary.main',
+ boxShadow: (theme) =>
+ theme.palette.mode === 'dark'
+ ? '0 2px 10px rgba(0,0,0,0.5)'
+ : '0 2px 10px rgba(0,0,0,0.08)',
+ },
+ }}
+ >
+
+
+
+
+ {meta.publisher || domain}
+
+
+
+
+ {meta.title}
+
+
+ {meta.description ? (
+
+ {meta.description}
+
+ ) : null}
+
+
+ {meta.image ? (
+
+ {
+ e.currentTarget.parentElement.style.display = 'none';
+ }}
+ />
+
+ ) : null}
+
+ );
+ }
+
+ // Fallback: render favicon box with domain/label
+ const displayLabel = (typeof label === 'string' && label.trim() && label.trim() !== url)
+ ? label.trim()
+ : (children || domain || url);
+
+ return (
+ (theme.palette.mode === 'dark' ? 'background.paper' : 'rgba(0, 0, 0, 0.02)'),
+ textDecoration: 'none',
+ color: 'primary.main',
+ verticalAlign: 'middle',
+ maxWidth: '100%',
+ transition: 'all 150ms ease-in-out',
+ '&:hover': {
+ bgcolor: 'action.hover',
+ borderColor: 'primary.main',
+ textDecoration: 'underline',
+ },
+ }}
+ >
+
+
+ {displayLabel}
+
+
+ );
+ }
+}
+
function parseInline(src, keyPrefix = 'i') {
const out = [];
let buf = '';
@@ -177,11 +530,19 @@ function parseInline(src, keyPrefix = 'i') {
const url = (endUrl === -1 ? src.slice(close + 2) : src.slice(close + 2, endUrl)).trim();
const k = key();
if (endUrl !== -1 && SAFE_URL.test(url)) {
- push(
-
- {parseInline(label, k)}
- ,
- );
+ if (/^https?:\/\//i.test(url)) {
+ push(
+
+ {parseInline(label, k)}
+ ,
+ );
+ } else {
+ push(
+
+ {parseInline(label, k)}
+ ,
+ );
+ }
} else {
// Half-typed (or unsafe) link: show the label only.
push({parseInline(label, k)});
@@ -192,6 +553,18 @@ function parseInline(src, keyPrefix = 'i') {
}
}
+ // Bare HTTP(S) URLs in markdown text
+ if ((src.startsWith('http://', i) || src.startsWith('https://', i)) && (i === 0 || /\s|[([<]/.test(src[i - 1]))) {
+ const match = src.slice(i).match(/^(https?:\/\/[^\s<>'")\]]+)/);
+ if (match) {
+ const rawUrl = match[1].replace(/[.,;:)]+$/, '');
+ const k = key();
+ push();
+ i += rawUrl.length;
+ continue;
+ }
+ }
+
buf += c;
i++;
}
@@ -322,7 +695,7 @@ const rootSx = (theme) => ({
whiteSpace: 'normal',
'& > *:first-of-type': { mt: 0 },
'& > *:last-child': { mb: 0 },
- '& p': { m: 0, mb: 1, whiteSpace: 'pre-line' },
+ '& p, & .md-p': { m: 0, mb: 1, whiteSpace: 'pre-line' },
'& h1, & h2, & h3, & h4, & h5, & h6': { m: 0, mt: 1.25, mb: 0.75, lineHeight: 1.3, fontWeight: 600 },
'& h1': { fontSize: '1.2rem' },
'& h2': { fontSize: '1.1rem' },
@@ -497,10 +870,10 @@ function HealingMarkdown({ text, streaming = false }) {
}
default:
return (
-
+
{parseInline(b.text, k)}
{tail}
-
+
);
}
})}