Fix markdown editor rendering and pasted-link duplication

- Restore heading/list typography in the editor: MDXEditor relies on the
  browser's default styles for <h1>/<ul> sizing, which Tailwind's Preflight
  resets. Markdown shortcuts fired but the block looked unchanged. Style the
  editor content explicitly, mirroring the rendered display.
- Keep pasted/typed URLs as plain text (linkPlugin disableAutoLink) so they
  don't become `[url](url)`, which duplicated both the URL and its preview
  card. Also dedupe extractUrls defensively so a repeated URL yields one card.

https://claude.ai/code/session_019DU6V5z6Nr4Vu7b1fBDnq4
This commit is contained in:
Claude
2026-05-29 17:01:13 +00:00
parent 98068c8089
commit 273916d7a7
3 changed files with 114 additions and 7 deletions
@@ -1,7 +1,12 @@
/* Blend MDXEditor into the compose glass card.
We lean on MDXEditor's bundled `dark-theme` for typography (heading sizes,
list markers, code styling) and only override the chrome: transparent
background, no border, and padding/colors tuned to the surrounding card. */
/* Blend MDXEditor into the compose glass card, and restore prose typography.
*
* MDXEditor renders real <h1>/<ul>/<blockquote> elements and leans on the
* browser's default styles for their look — it ships no heading/list
* typography of its own. Tailwind's Preflight resets those defaults
* (headings -> `font-size: inherit`, lists -> `list-style: none`), which would
* otherwise make a heading or bullet look identical to body text. So we style
* the content elements explicitly here, mirroring the rendered display in
* text-particle-view.tsx so "write" and "read" match. */
.llink-mdxeditor {
--baseBg: transparent;
@@ -24,10 +29,106 @@
line-height: 1.7;
}
.llink-mdx-content :where(h1, h2, h3, h4, h5, h6) {
/* Headings */
.llink-mdx-content h1,
.llink-mdx-content h2,
.llink-mdx-content h3,
.llink-mdx-content h4,
.llink-mdx-content h5,
.llink-mdx-content h6 {
color: #fff;
line-height: 1.25;
}
.llink-mdx-content h1 {
font-size: 1.875rem;
font-weight: 700;
margin-bottom: 0.75rem;
}
.llink-mdx-content h2 {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.llink-mdx-content h3 {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.llink-mdx-content h4 {
font-size: 1.125rem;
font-weight: 500;
margin-bottom: 0.25rem;
}
.llink-mdx-content h5 {
font-size: 1rem;
font-weight: 500;
margin-bottom: 0.25rem;
}
.llink-mdx-content h6 {
font-size: 0.875rem;
font-weight: 500;
margin-bottom: 0.25rem;
}
/* Block spacing */
.llink-mdx-content p {
margin-bottom: 0.75rem;
line-height: 1.625;
}
.llink-mdx-content > :last-child {
margin-bottom: 0;
}
/* Inline emphasis */
.llink-mdx-content strong {
font-weight: 600;
color: #fff;
}
.llink-mdx-content em {
font-style: italic;
}
.llink-mdx-content a {
color: #60a5fa;
text-decoration: underline;
}
/* Lists — Preflight strips the marker and indentation, so restore both */
.llink-mdx-content ul,
.llink-mdx-content ol {
margin-bottom: 0.75rem;
padding-left: 1.25rem;
}
.llink-mdx-content ul {
list-style: disc;
}
.llink-mdx-content ol {
list-style: decimal;
}
.llink-mdx-content li {
margin-bottom: 0.25rem;
line-height: 1.625;
}
/* Blockquote */
.llink-mdx-content blockquote {
margin-bottom: 0.75rem;
padding-left: 1rem;
border-left: 2px solid rgb(255 255 255 / 0.3);
font-style: italic;
color: rgb(255 255 255 / 0.7);
}
/* Inline code */
.llink-mdx-content code {
border-radius: 0.25rem;
background: rgb(255 255 255 / 0.1);
padding: 0.1rem 0.375rem;
font-size: 0.85em;
}
/* Horizontal rule */
.llink-mdx-content hr {
margin: 1rem 0;
border: none;
border-top: 1px solid rgb(255 255 255 / 0.1);
}
@@ -83,7 +83,11 @@ export function MarkdownEditor({
listsPlugin(),
quotePlugin(),
thematicBreakPlugin(),
linkPlugin(),
// disableAutoLink: keep pasted/typed URLs as plain text rather than
// turning them into `[url](url)`. The compose flow already turns bare
// URLs into link-preview cards, so auto-linking would both duplicate
// the URL in the markdown and double up the preview cards.
linkPlugin({ disableAutoLink: true }),
codeBlockPlugin({ defaultCodeBlockLanguage: "" }),
codeMirrorPlugin({ codeBlockLanguages: CODE_BLOCK_LANGUAGES }),
markdownShortcutPlugin(),
+3 -1
View File
@@ -10,5 +10,7 @@ export interface LinkMetadata {
const URL_REGEX = /https?:\/\/[^\s<>"')\]]+/g;
export function extractUrls(text: string): string[] {
return Array.from(text.matchAll(URL_REGEX), (m) => m[0]);
// Dedupe: a URL repeated in the text (e.g. a `[url](url)` markdown link)
// should only yield a single preview card.
return Array.from(new Set(Array.from(text.matchAll(URL_REGEX), (m) => m[0])));
}