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:
@@ -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(),
|
||||
|
||||
@@ -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])));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user