fix(chat): scroll wide markdown tables horizontally

Wrap chat markdown tables in a focusable scroll region and let the table
keep its natural width so narrow screens can scroll instead of squeezing
columns into the chat width.
This commit is contained in:
Federico Jaramillo Martinez
2026-07-26 10:32:42 +02:00
parent 2fb626204c
commit 111db63f4a
3 changed files with 29 additions and 4 deletions
@@ -0,0 +1,5 @@
---
"@jmfederico/pi-web": patch
---
Let chat markdown tables keep their natural width and scroll horizontally instead of being squeezed into the chat column, making them readable on mobile.
+6 -4
View File
@@ -465,8 +465,8 @@ export const chatStyles = css`
export const formattedTextStyles = css`
:host { display: block; }
.formatted { white-space: normal; overflow-wrap: anywhere; line-height: 1.45; text-align: start; unicode-bidi: plaintext; }
p, ul, ol, pre, blockquote, table, .code-block-wrapper { margin: 0 0 10px; }
:is(p, ul, ol, pre, blockquote, table, .code-block-wrapper):last-child { margin-bottom: 0; }
p, ul, ol, pre, blockquote, .table-scroll, .code-block-wrapper { margin: 0 0 10px; }
:is(p, ul, ol, pre, blockquote, .table-scroll, .code-block-wrapper):last-child { margin-bottom: 0; }
ul, ol { padding-left: 22px; }
li + li { margin-top: 3px; }
code { border: 1px solid var(--pi-border); border-radius: 4px; background: var(--pi-bg); padding: 1px 4px; font: 13px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; direction: ltr; text-align: left; unicode-bidi: isolate; }
@@ -484,8 +484,10 @@ export const formattedTextStyles = css`
h2 { font-size: 17px; }
h3 { font-size: 15px; }
h4 { font-size: 14px; }
table { border-collapse: collapse; display: block; overflow-x: auto; overflow-y: hidden; }
th, td { border: 1px solid var(--pi-border); padding: 4px 8px; }
.table-scroll { max-width: 100%; overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch; }
.table-scroll:focus-visible { outline: 1px solid var(--pi-accent); outline-offset: 2px; }
table { border-collapse: collapse; width: max-content; min-width: 100%; max-width: none; }
th, td { border: 1px solid var(--pi-border); padding: 4px 8px; max-width: 48ch; overflow-wrap: anywhere; }
th { background: var(--pi-surface); }
`;
+18
View File
@@ -26,6 +26,8 @@ function escapeHtml(text: string): string {
.replaceAll(">", ">");
}
const TABLE_SCROLL_CLASS = "table-scroll";
function sanitizeHtml(html: string): string {
const template = document.createElement("template");
template.innerHTML = html;
@@ -41,9 +43,25 @@ function sanitizeHtml(html: string): string {
element.setAttribute("rel", "noreferrer noopener");
}
});
wrapTablesInScrollRegions(template.content);
return template.innerHTML;
}
// Markdown tables stay at their natural width and scroll horizontally instead of
// being squeezed into the chat column, which is unreadable on narrow screens.
function wrapTablesInScrollRegions(root: DocumentFragment): void {
root.querySelectorAll("table").forEach((table) => {
if (table.parentElement?.classList.contains(TABLE_SCROLL_CLASS) === true) return;
const wrapper = document.createElement("div");
wrapper.className = TABLE_SCROLL_CLASS;
wrapper.setAttribute("role", "region");
wrapper.setAttribute("aria-label", "Table");
wrapper.setAttribute("tabindex", "0");
table.before(wrapper);
wrapper.append(table);
});
}
function isSafeUrl(url: string): boolean {
if (url.startsWith("#") || url.startsWith("/")) return true;
try {