mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-06 17:38:35 +00:00
fix(webui): finish mobile containment for inline code, tables, chips, user bubbles
57f9ec0 added a CSS containment safety net for .markdown-content, but several rendering paths still overflowed or truncated on narrow viewports because they bypass those rules or actively disable wrapping: - Inline code longer than 120 chars renders as `block whitespace-pre` with no scroll container, so it bled past the viewport (white-space:pre defeats overflow-wrap). It now scrolls inside its own box via max-w-full overflow-x-auto. - Wide markdown tables never actually scrolled: Tailwind Typography forces width:100% + table-layout:auto on .prose table, so they shrank to fit instead of overflowing. Switched to a wrapper-<div> pattern (a custom `table` component, the approach used by DeepSeek/others) with min-w-max so wide tables keep natural column widths and scroll inside the conversation column; the old display:block rule on .markdown-content table is removed. - File-reference chips and inline link-preview cards used `truncate`, so long filenames/labels were ellipsized to one line. Now [overflow-wrap:anywhere] sm:truncate — wrap on mobile, truncate on desktop. - User-authored message bubbles don't pass through .markdown-content, so a long URL in the user's own message still burst the screen: under flex min-width:auto, break-words cannot reduce the item's min-content width. Added max-w-full min-w-0 + [overflow-wrap:anywhere]. No JS/agent/provider behavior touched. Desktop layout is unchanged — the new rules only engage when content would otherwise overflow. Verified: bun run lint (clean), bun run test (438 passed), bun run build. Refs #4693 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
162b6ee5bd
commit
70505bd1fc
@ -87,7 +87,7 @@ export function FileReferenceChip({
|
|||||||
<span
|
<span
|
||||||
data-sheen-text={active ? displayText : undefined}
|
data-sheen-text={active ? displayText : undefined}
|
||||||
className={cn(
|
className={cn(
|
||||||
"min-w-0 max-w-full truncate",
|
"min-w-0 max-w-full [overflow-wrap:anywhere] sm:truncate",
|
||||||
active && "streaming-text-sheen file-reference-sheen",
|
active && "streaming-text-sheen file-reference-sheen",
|
||||||
textClassName,
|
textClassName,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -339,7 +339,7 @@ function InlineLinkPreviewRow({ link }: { link: InlineLinkPreview }) {
|
|||||||
<Globe2 className="h-3 w-3" />
|
<Globe2 className="h-3 w-3" />
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span className="min-w-0 truncate leading-normal">
|
<span className="min-w-0 [overflow-wrap:anywhere] leading-normal sm:truncate">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
@ -417,7 +417,7 @@ export default function MarkdownTextRenderer({
|
|||||||
return (
|
return (
|
||||||
<code
|
<code
|
||||||
className={cn(
|
className={cn(
|
||||||
"block min-w-0 whitespace-pre bg-transparent p-0 font-mono text-[0.8125rem]",
|
"block min-w-0 max-w-full overflow-x-auto whitespace-pre bg-transparent p-0 font-mono text-[0.8125rem]",
|
||||||
"leading-snug text-inherit",
|
"leading-snug text-inherit",
|
||||||
cls,
|
cls,
|
||||||
)}
|
)}
|
||||||
@ -497,6 +497,19 @@ export default function MarkdownTextRenderer({
|
|||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
table({ children, ...props }) {
|
||||||
|
// Wrap wide markdown tables in a horizontal-scroll container (the
|
||||||
|
// pattern used by DeepSeek/others) so a 6+ column table scrolls inside
|
||||||
|
// the conversation column instead of forcing the page wider than 100vw.
|
||||||
|
// min-w-max keeps natural column widths; w-full stretches narrow tables.
|
||||||
|
return (
|
||||||
|
<div className="w-full overflow-x-auto">
|
||||||
|
<table className="w-full min-w-max" {...props}>
|
||||||
|
{children}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
li({ children: markdownChildren, className: itemClassName }) {
|
li({ children: markdownChildren, className: itemClassName }) {
|
||||||
const link = inlineLinkPreviewFromChildren(markdownChildren);
|
const link = inlineLinkPreviewFromChildren(markdownChildren);
|
||||||
if (link) {
|
if (link) {
|
||||||
|
|||||||
@ -147,8 +147,8 @@ export function MessageBubble({
|
|||||||
{hasText ? (
|
{hasText ? (
|
||||||
<p
|
<p
|
||||||
className={cn(
|
className={cn(
|
||||||
"ml-auto w-fit rounded-[18px] bg-secondary/70 px-4 py-2",
|
"ml-auto w-fit max-w-full min-w-0 rounded-[18px] bg-secondary/70 px-4 py-2",
|
||||||
"text-left text-[16px]/[1.75] whitespace-pre-wrap break-words",
|
"text-left text-[16px]/[1.75] whitespace-pre-wrap [overflow-wrap:anywhere]",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CliAppMentionText
|
<CliAppMentionText
|
||||||
|
|||||||
@ -558,12 +558,10 @@
|
|||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wide markdown tables scroll horizontally within the conversation column. */
|
/* Wide markdown tables scroll horizontally within the conversation column.
|
||||||
.markdown-content table {
|
Tables are wrapped in an overflow-x:auto <div> by the markdown renderer's
|
||||||
display: block;
|
custom `table` component (see MarkdownTextRenderer); min-w-max on the
|
||||||
max-width: 100%;
|
<table> keeps natural column widths so it scrolls instead of shrinking. */
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clip any residual horizontal bleed at the thread column edge. Vertical
|
/* Clip any residual horizontal bleed at the thread column edge. Vertical
|
||||||
scrolling and internal code-block scroll are unaffected. */
|
scrolling and internal code-block scroll are unaffected. */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user