🤩 style: DialogImage, Update Stylesheet, and Improve Accessibility (#8014)

* 🔧 fix: Adjust typography and border styles for improved readability in markdown components

* 🔧 fix: Enhance code block styling in markdown for better visibility and consistency

* 🔧 fix: Adjust margins and line heights for improved readability in markdown elements

* 🔧 fix: Adjust spacing for horizontal rules in markdown for improved consistency

* 🔧 fix: Refactor DialogImage component for improved quality styling and layout consistency

* 🔧 fix: Enhance zoom and pan functionality in DialogImage component with improved controls and user experience

* 🔧 fix: Improve zoom and pan functionality in DialogImage component with enhanced controls and reset zoom feature
This commit is contained in:
Marco Beretta 2025-06-23 20:30:15 +02:00 committed by GitHub
parent 5c947be455
commit 1b7e044bf5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 314 additions and 81 deletions

View file

@ -1,14 +1,33 @@
import { useState, useEffect } from 'react'; import { useState, useEffect, useCallback, useRef } from 'react';
import { X, ArrowDownToLine, PanelLeftOpen, PanelLeftClose } from 'lucide-react'; import { X, ArrowDownToLine, PanelLeftOpen, PanelLeftClose, RotateCcw } from 'lucide-react';
import { Button, OGDialog, OGDialogContent, TooltipAnchor } from '~/components'; import { Button, OGDialog, OGDialogContent, TooltipAnchor } from '~/components';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
const getQualityStyles = (quality: string): string => {
if (quality === 'high') {
return 'bg-green-100 text-green-800';
}
if (quality === 'low') {
return 'bg-orange-100 text-orange-800';
}
return 'bg-gray-100 text-gray-800';
};
export default function DialogImage({ isOpen, onOpenChange, src = '', downloadImage, args }) { export default function DialogImage({ isOpen, onOpenChange, src = '', downloadImage, args }) {
const localize = useLocalize(); const localize = useLocalize();
const [isPromptOpen, setIsPromptOpen] = useState(false); const [isPromptOpen, setIsPromptOpen] = useState(false);
const [imageSize, setImageSize] = useState<string | null>(null); const [imageSize, setImageSize] = useState<string | null>(null);
const getImageSize = async (url: string) => { // Zoom and pan state
const [zoom, setZoom] = useState(1);
const [panX, setPanX] = useState(0);
const [panY, setPanY] = useState(0);
const [isDragging, setIsDragging] = useState(false);
const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
const containerRef = useRef<HTMLDivElement>(null);
const getImageSize = useCallback(async (url: string) => {
try { try {
const response = await fetch(url, { method: 'HEAD' }); const response = await fetch(url, { method: 'HEAD' });
const contentLength = response.headers.get('Content-Length'); const contentLength = response.headers.get('Content-Length');
@ -25,7 +44,7 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
console.error('Error getting image size:', error); console.error('Error getting image size:', error);
return null; return null;
} }
}; }, []);
const formatFileSize = (bytes: number): string => { const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 Bytes'; if (bytes === 0) return '0 Bytes';
@ -37,11 +56,129 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}; };
const getImageMaxWidth = () => {
// On mobile (when panel overlays), use full width minus padding
// On desktop, account for the side panel width
if (isPromptOpen) {
return window.innerWidth >= 640 ? 'calc(100vw - 22rem)' : 'calc(100vw - 2rem)';
}
return 'calc(100vw - 2rem)';
};
const resetZoom = useCallback(() => {
setZoom(1);
setPanX(0);
setPanY(0);
}, []);
const getCursor = () => {
if (zoom <= 1) return 'default';
return isDragging ? 'grabbing' : 'grab';
};
const handleDoubleClick = useCallback(() => {
if (zoom > 1) {
resetZoom();
} else {
// Zoom in to 2x on double click when at normal zoom
setZoom(2);
}
}, [zoom, resetZoom]);
const handleWheel = useCallback(
(e: React.WheelEvent<HTMLDivElement>) => {
e.preventDefault();
if (!containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
// Calculate zoom factor
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
const newZoom = Math.min(Math.max(zoom * zoomFactor, 1), 5);
if (newZoom === zoom) return;
// If zooming back to 1, reset pan to center the image
if (newZoom === 1) {
setZoom(1);
setPanX(0);
setPanY(0);
return;
}
// Calculate the zoom center relative to the current viewport
const containerCenterX = rect.width / 2;
const containerCenterY = rect.height / 2;
// Calculate new pan position to zoom towards mouse cursor
const zoomRatio = newZoom / zoom;
const deltaX = (mouseX - containerCenterX - panX) * (zoomRatio - 1);
const deltaY = (mouseY - containerCenterY - panY) * (zoomRatio - 1);
setZoom(newZoom);
setPanX(panX - deltaX);
setPanY(panY - deltaY);
},
[zoom, panX, panY],
);
const handleMouseDown = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
e.preventDefault();
if (zoom <= 1) return;
setIsDragging(true);
setDragStart({
x: e.clientX - panX,
y: e.clientY - panY,
});
},
[zoom, panX, panY],
);
const handleMouseMove = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
if (!isDragging || zoom <= 1) return;
const newPanX = e.clientX - dragStart.x;
const newPanY = e.clientY - dragStart.y;
setPanX(newPanX);
setPanY(newPanY);
},
[isDragging, dragStart, zoom],
);
const handleMouseUp = useCallback(() => {
setIsDragging(false);
}, []);
useEffect(() => {
const onKey = (e: KeyboardEvent) => e.key === 'Escape' && resetZoom();
document.addEventListener('keydown', onKey);
return () => document.removeEventListener('keydown', onKey);
}, [resetZoom]);
useEffect(() => { useEffect(() => {
if (isOpen && src) { if (isOpen && src) {
getImageSize(src).then(setImageSize); getImageSize(src).then(setImageSize);
resetZoom();
} }
}, [isOpen, src]); }, [isOpen, src, getImageSize, resetZoom]);
// Ensure image is centered when zoom changes to 1
useEffect(() => {
if (zoom === 1) {
setPanX(0);
setPanY(0);
}
}, [zoom]);
// Reset pan when panel opens/closes to maintain centering
useEffect(() => {
if (zoom === 1) {
setPanX(0);
setPanY(0);
}
}, [isPromptOpen, zoom]);
return ( return (
<OGDialog open={isOpen} onOpenChange={onOpenChange}> <OGDialog open={isOpen} onOpenChange={onOpenChange}>
@ -52,7 +189,7 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
overlayClassName="bg-surface-primary opacity-95 z-50" overlayClassName="bg-surface-primary opacity-95 z-50"
> >
<div <div
className={`absolute left-0 top-0 z-10 flex items-center justify-between p-4 transition-all duration-500 ease-in-out ${isPromptOpen ? 'right-80' : 'right-0'}`} className={`ease-[cubic-bezier(0.175,0.885,0.32,1.275)] absolute left-0 top-0 z-10 flex items-center justify-between p-3 transition-all duration-500 sm:p-4 ${isPromptOpen ? 'right-0 sm:right-80' : 'right-0'}`}
> >
<TooltipAnchor <TooltipAnchor
description={localize('com_ui_close')} description={localize('com_ui_close')}
@ -62,11 +199,21 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
variant="ghost" variant="ghost"
className="h-10 w-10 p-0 hover:bg-surface-hover" className="h-10 w-10 p-0 hover:bg-surface-hover"
> >
<X className="size-6" /> <X className="size-7 sm:size-6" />
</Button> </Button>
} }
/> />
<div className="flex items-center gap-2"> <div className="flex items-center gap-1 sm:gap-2">
{zoom > 1 && (
<TooltipAnchor
description={localize('com_ui_reset_zoom')}
render={
<Button onClick={resetZoom} variant="ghost" className="h-10 w-10 p-0">
<RotateCcw className="size-6" />
</Button>
}
/>
)}
<TooltipAnchor <TooltipAnchor
description={localize('com_ui_download')} description={localize('com_ui_download')}
render={ render={
@ -88,9 +235,9 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
className="h-10 w-10 p-0" className="h-10 w-10 p-0"
> >
{isPromptOpen ? ( {isPromptOpen ? (
<PanelLeftOpen className="size-6" /> <PanelLeftOpen className="size-7 sm:size-6" />
) : ( ) : (
<PanelLeftClose className="size-6" /> <PanelLeftClose className="size-7 sm:size-6" />
)} )}
</Button> </Button>
} }
@ -100,36 +247,81 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
{/* Main content area with image */} {/* Main content area with image */}
<div <div
className={`flex h-full transition-all duration-500 ease-in-out ${isPromptOpen ? 'mr-80' : 'mr-0'}`} className={`ease-[cubic-bezier(0.175,0.885,0.32,1.275)] flex h-full transition-all duration-500 ${isPromptOpen ? 'mr-0 sm:mr-80' : 'mr-0'}`}
> >
<div className="flex flex-1 items-center justify-center px-4 pb-4 pt-20"> <div
<img ref={containerRef}
src={src} className="flex flex-1 items-center justify-center px-2 pb-4 pt-16 sm:px-4 sm:pt-20"
alt="Image" onWheel={handleWheel}
className="max-h-full max-w-full object-contain" onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseUp}
onDoubleClick={handleDoubleClick}
style={{
cursor: getCursor(),
overflow: zoom > 1 ? 'hidden' : 'visible',
minHeight: 0, // Allow flexbox to shrink
}}
>
<div
className="flex items-center justify-center transition-transform duration-100 ease-out"
style={{ style={{
maxHeight: 'calc(100vh - 6rem)', transform: `translate(${panX}px, ${panY}px) scale(${zoom})`,
maxWidth: '100%', transformOrigin: 'center center',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}} }}
/> >
<img
src={src}
alt="Image"
className="block object-contain"
style={{
maxHeight: 'calc(100vh - 8rem)',
maxWidth: getImageMaxWidth(),
width: 'auto',
height: 'auto',
}}
/>
</div>
</div> </div>
</div> </div>
{/* Side Panel */} {/* Side Panel */}
<div <div
className={`shadow-l-lg fixed right-0 top-0 z-20 h-full w-80 transform rounded-l-2xl border-l border-border-light bg-surface-primary transition-transform duration-500 ease-in-out ${ className={`sm:shadow-l-lg ease-[cubic-bezier(0.175,0.885,0.32,1.275)] fixed right-0 top-0 z-20 h-full w-full transform border-l border-border-light bg-surface-primary shadow-2xl backdrop-blur-sm transition-transform duration-500 sm:w-80 sm:rounded-l-2xl ${
isPromptOpen ? 'translate-x-0' : 'translate-x-full' isPromptOpen ? 'translate-x-0' : 'translate-x-full'
}`} }`}
> >
<div className="h-full overflow-y-auto p-6"> {/* Mobile pull handle - removed for cleaner look */}
<div className="mb-4">
<div className="h-full overflow-y-auto p-4 sm:p-6">
{/* Mobile close button */}
<div className="mb-4 flex items-center justify-between sm:hidden">
<h3 className="text-lg font-semibold text-text-primary">
{localize('com_ui_image_details')}
</h3>
<Button
onClick={() => setIsPromptOpen(false)}
variant="ghost"
className="h-12 w-12 p-0"
>
<X className="size-6" />
</Button>
</div>
<div className="mb-4 hidden sm:block">
<h3 className="mb-2 text-lg font-semibold text-text-primary"> <h3 className="mb-2 text-lg font-semibold text-text-primary">
{localize('com_ui_image_details')} {localize('com_ui_image_details')}
</h3> </h3>
<div className="mb-4 h-px bg-border-medium"></div> <div className="mb-4 h-px bg-border-medium"></div>
</div> </div>
<div className="space-y-6"> <div className="space-y-4 sm:space-y-6">
{/* Prompt Section */} {/* Prompt Section */}
<div> <div>
<h4 className="mb-2 text-sm font-medium text-text-primary"> <h4 className="mb-2 text-sm font-medium text-text-primary">
@ -157,13 +349,7 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<span className="text-sm text-text-primary">{localize('com_ui_quality')}:</span> <span className="text-sm text-text-primary">{localize('com_ui_quality')}:</span>
<span <span
className={`rounded px-2 py-1 text-xs font-medium capitalize ${ className={`rounded px-2 py-1 text-xs font-medium capitalize ${getQualityStyles(args?.quality || '')}`}
args?.quality === 'high'
? 'bg-green-100 text-green-800'
: args?.quality === 'low'
? 'bg-orange-100 text-orange-800'
: 'bg-gray-100 text-gray-800'
}`}
> >
{args?.quality || 'Standard'} {args?.quality || 'Standard'}
</span> </span>

View file

@ -1054,6 +1054,7 @@
"com_ui_x_selected": "{{0}} selected", "com_ui_x_selected": "{{0}} selected",
"com_ui_yes": "Yes", "com_ui_yes": "Yes",
"com_ui_zoom": "Zoom", "com_ui_zoom": "Zoom",
"com_ui_reset_zoom": "Reset Zoom",
"com_user_message": "You", "com_user_message": "You",
"com_warning_resubmit_unsupported": "Resubmitting the AI message is not supported for this endpoint." "com_warning_resubmit_unsupported": "Resubmitting the AI message is not supported for this endpoint."
} }

View file

@ -818,14 +818,14 @@ pre {
max-width: 65ch; max-width: 65ch;
font-size: var(--markdown-font-size, var(--font-size-base)); font-size: var(--markdown-font-size, var(--font-size-base));
line-height: calc( line-height: calc(
28px * var(--markdown-font-size, var(--font-size-base)) / var(--font-size-base) 22px * var(--markdown-font-size, var(--font-size-base)) / var(--font-size-base)
); );
} }
.prose :where([class~='lead']):not(:where([class~='not-prose'] *)) { .prose :where([class~='lead']):not(:where([class~='not-prose'] *)) {
color: var(--tw-prose-lead); color: var(--tw-prose-lead);
font-size: 1.25em; font-size: 1.25em;
line-height: 1.6; line-height: 1.3;
margin-bottom: 1.2em; margin-bottom: 1.2em;
margin-top: 1.2em; margin-top: 1.2em;
} }
@ -853,8 +853,8 @@ pre {
.prose :where(hr):not(:where([class~='not-prose'] *)) { .prose :where(hr):not(:where([class~='not-prose'] *)) {
border-color: var(--tw-prose-hr); border-color: var(--tw-prose-hr);
border-top-width: 1px; border-top-width: 1px;
margin-bottom: 3em; margin-bottom: 0.8em;
margin-top: 3em; margin-top: 0.8em;
} }
.prose :where(blockquote):not(:where([class~='not-prose'] *)) { .prose :where(blockquote):not(:where([class~='not-prose'] *)) {
border-left-color: var(--tw-prose-quote-borders); border-left-color: var(--tw-prose-quote-borders);
@ -878,9 +878,9 @@ pre {
color: var(--tw-prose-headings); color: var(--tw-prose-headings);
font-size: 2.25em; font-size: 2.25em;
font-weight: 800; font-weight: 800;
line-height: 1.1111111; line-height: 1;
margin-bottom: 0.8888889em; margin-bottom: 0.4em;
margin-top: 0; margin-top: 0.6em;
} }
.prose :where(h1 strong):not(:where([class~='not-prose'] *)) { .prose :where(h1 strong):not(:where([class~='not-prose'] *)) {
color: inherit; color: inherit;
@ -890,9 +890,9 @@ pre {
color: var(--tw-prose-headings); color: var(--tw-prose-headings);
font-size: 1.5em; font-size: 1.5em;
font-weight: 700; font-weight: 700;
line-height: 1.3333333; line-height: 1.1;
margin-bottom: 1em; margin-bottom: 0.4em;
margin-top: 2em; margin-top: 0.8em;
} }
.prose :where(h2 strong):not(:where([class~='not-prose'] *)) { .prose :where(h2 strong):not(:where([class~='not-prose'] *)) {
color: inherit; color: inherit;
@ -902,9 +902,9 @@ pre {
color: var(--tw-prose-headings); color: var(--tw-prose-headings);
font-size: 1.25em; font-size: 1.25em;
font-weight: 600; font-weight: 600;
line-height: 1.6; line-height: 1.3;
margin-bottom: 0.6em; margin-bottom: 0.3em;
margin-top: 1.6em; margin-top: 0.6em;
} }
.prose :where(h3 strong):not(:where([class~='not-prose'] *)) { .prose :where(h3 strong):not(:where([class~='not-prose'] *)) {
color: inherit; color: inherit;
@ -913,9 +913,9 @@ pre {
.prose :where(h4):not(:where([class~='not-prose'] *)) { .prose :where(h4):not(:where([class~='not-prose'] *)) {
color: var(--tw-prose-headings); color: var(--tw-prose-headings);
font-weight: 600; font-weight: 600;
line-height: 1.5; line-height: 1.2;
margin-bottom: 0.5em; margin-bottom: 0.3em;
margin-top: 1.5em; margin-top: 0.5em;
} }
.prose :where(h4 strong):not(:where([class~='not-prose'] *)) { .prose :where(h4 strong):not(:where([class~='not-prose'] *)) {
color: inherit; color: inherit;
@ -932,19 +932,19 @@ pre {
.prose :where(figcaption):not(:where([class~='not-prose'] *)) { .prose :where(figcaption):not(:where([class~='not-prose'] *)) {
color: var(--tw-prose-captions); color: var(--tw-prose-captions);
font-size: 0.875em; font-size: 0.875em;
line-height: 1.4285714; line-height: 1.2;
margin-top: 0.8571429em; margin-top: 0.8571429em;
} }
.prose :where(code):not(:where([class~='not-prose'] *)) { .prose :where(code):not(:where([class~='not-prose'] *)) {
color: var(--tw-prose-code); color: var(--tw-prose-code);
font-size: 0.875em; font-size: 0.875em;
font-weight: 600; font-weight: 600;
background-color: var(--gray-200);
padding: 0.125rem 0.25rem;
border-radius: 0.35rem;
} }
.prose :where(code):not(:where([class~='not-prose'] *)):before { .dark .prose :where(code):not(:where([class~='not-prose'] *)):not(:where(pre *)) {
content: '`'; background-color: var(--gray-600);
}
.prose :where(code):not(:where([class~='not-prose'] *)):after {
content: '`';
} }
.prose :where(a code):not(:where([class~='not-prose'] *)) { .prose :where(a code):not(:where([class~='not-prose'] *)) {
color: inherit; color: inherit;
@ -971,11 +971,11 @@ pre {
} }
.prose :where(pre):not(:where([class~='not-prose'] *)) { .prose :where(pre):not(:where([class~='not-prose'] *)) {
background-color: transparent; background-color: transparent;
border-radius: 0.375rem; border-radius: 0.75rem;
color: currentColor; color: currentColor;
font-size: 0.875em; font-size: 0.875em;
font-weight: 400; font-weight: 400;
line-height: 1.7142857; line-height: 1.4;
margin: 0; margin: 0;
overflow-x: auto; overflow-x: auto;
padding: 0; padding: 0;
@ -999,7 +999,7 @@ pre {
} }
.prose :where(table):not(:where([class~='not-prose'] *)) { .prose :where(table):not(:where([class~='not-prose'] *)) {
font-size: 0.875em; font-size: 0.875em;
line-height: 1.7142857; line-height: 1.4;
margin-bottom: 2em; margin-bottom: 2em;
margin-top: 2em; margin-top: 2em;
table-layout: auto; table-layout: auto;
@ -1036,14 +1036,14 @@ pre {
vertical-align: top; vertical-align: top;
} }
.prose { .prose {
--tw-prose-body: #374151; --tw-prose-body: #424242;
--tw-prose-headings: #111827; --tw-prose-headings: #111827;
--tw-prose-lead: #4b5563; --tw-prose-lead: #4b5563;
--tw-prose-links: #0066cc; --tw-prose-links: #0066cc;
--tw-prose-bold: #111827; --tw-prose-bold: #111827;
--tw-prose-counters: #6b7280; --tw-prose-counters: #6b7280;
--tw-prose-bullets: #d1d5db; --tw-prose-bullets: #d1d5db;
--tw-prose-hr: #e5e7eb; --tw-prose-hr: #cdcdcd;
--tw-prose-quotes: #111827; --tw-prose-quotes: #111827;
--tw-prose-quote-borders: #e5e7eb; --tw-prose-quote-borders: #e5e7eb;
--tw-prose-captions: #6b7280; --tw-prose-captions: #6b7280;
@ -1059,17 +1059,17 @@ pre {
--tw-prose-invert-bold: #fff; --tw-prose-invert-bold: #fff;
--tw-prose-invert-counters: #9ca3af; --tw-prose-invert-counters: #9ca3af;
--tw-prose-invert-bullets: #4b5563; --tw-prose-invert-bullets: #4b5563;
--tw-prose-invert-hr: #374151; --tw-prose-invert-hr: #424242;
--tw-prose-invert-quotes: #f3f4f6; --tw-prose-invert-quotes: #f3f4f6;
--tw-prose-invert-quote-borders: #374151; --tw-prose-invert-quote-borders: #424242;
--tw-prose-invert-captions: #9ca3af; --tw-prose-invert-captions: #9ca3af;
--tw-prose-invert-code: #fff; --tw-prose-invert-code: #fff;
--tw-prose-invert-pre-code: #d1d5db; --tw-prose-invert-pre-code: #d1d5db;
--tw-prose-invert-pre-bg: rgba(0, 0, 0, 0.5); --tw-prose-invert-pre-bg: rgba(0, 0, 0, 0.5);
--tw-prose-invert-th-borders: #4b5563; --tw-prose-invert-th-borders: #4b5563;
--tw-prose-invert-td-borders: #374151; --tw-prose-invert-td-borders: #424242;
font-size: 1rem; font-size: 1rem;
line-height: 1.75; line-height: 1.4;
} }
.prose :where(p):not(:where([class~='not-prose'] *)) { .prose :where(p):not(:where([class~='not-prose'] *)) {
margin-bottom: 1.25em; margin-bottom: 1.25em;
@ -1112,6 +1112,13 @@ pre {
.prose :where(h4 + *):not(:where([class~='not-prose'] *)) { .prose :where(h4 + *):not(:where([class~='not-prose'] *)) {
margin-top: 0; margin-top: 0;
} }
/* Ensure symmetrical spacing around hr */
.prose :where(* + hr):not(:where([class~='not-prose'] *)) {
margin-top: 0.8em;
}
.prose :where(hr + h1, hr + h2, hr + h3, hr + h4):not(:where([class~='not-prose'] *)) {
margin-top: 0.4em;
}
.prose :where(thead th:first-child):not(:where([class~='not-prose'] *)) { .prose :where(thead th:first-child):not(:where([class~='not-prose'] *)) {
padding-left: 0; padding-left: 0;
} }
@ -1213,6 +1220,14 @@ pre {
.prose-2xl :where(.prose > :last-child):not(:where([class~='not-prose'] *)) { .prose-2xl :where(.prose > :last-child):not(:where([class~='not-prose'] *)) {
margin-bottom: 0; margin-bottom: 0;
} }
.prose :where(ul > li):has(input[type='checkbox']):not(:where([class~='not-prose'] *)) {
margin-bottom: 0;
margin-top: 0;
}
.prose :where(ul > li):has(input[type='checkbox']) p:not(:where([class~='not-prose'] *)) {
margin-bottom: 0;
margin-top: 0;
}
code, code,
pre { pre {
@ -1484,7 +1499,7 @@ html {
max-width: none; max-width: none;
font-size: var(--markdown-font-size, var(--font-size-base)); font-size: var(--markdown-font-size, var(--font-size-base));
line-height: calc( line-height: calc(
28px * var(--markdown-font-size, var(--font-size-base)) / var(--font-size-base) 22px * var(--markdown-font-size, var(--font-size-base)) / var(--font-size-base)
); );
} }
@ -1496,8 +1511,8 @@ html {
} }
.markdown h2 { .markdown h2 {
margin-bottom: 1rem; margin-bottom: 0.4rem;
margin-top: 2rem; margin-top: 0.8rem;
} }
.markdown h3 { .markdown h3 {
@ -1507,8 +1522,8 @@ html {
.markdown h3, .markdown h3,
.markdown h4 { .markdown h4 {
margin-bottom: 0.5rem; margin-bottom: 0.3rem;
margin-top: 1rem; margin-top: 0.6rem;
} }
.markdown h4 { .markdown h4 {
@ -1523,7 +1538,7 @@ html {
.markdown blockquote { .markdown blockquote {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgba(142, 142, 160, var(--tw-border-opacity)); border-color: var(--gray-400);
border-left-width: 2px; border-left-width: 2px;
line-height: 1rem; line-height: 1rem;
padding-left: 1rem; padding-left: 1rem;
@ -1551,6 +1566,7 @@ html {
.markdown th:last-child { .markdown th:last-child {
border-right-width: 1px; border-right-width: 1px;
border-color: #d1d5db;
border-top-right-radius: 0.375rem; border-top-right-radius: 0.375rem;
} }
@ -1751,16 +1767,16 @@ html {
font-weight: 600; font-weight: 600;
} }
.markdown h2 { .markdown h2 {
margin-bottom: 1rem; margin-bottom: 0.4rem;
margin-top: 2rem; margin-top: 0.8rem;
} }
.markdown h3 { .markdown h3 {
font-weight: 600; font-weight: 600;
} }
.markdown h3, .markdown h3,
.markdown h4 { .markdown h4 {
margin-bottom: 0.5rem; margin-bottom: 0.3rem;
margin-top: 1rem; margin-top: 0.6rem;
} }
.markdown h4 { .markdown h4 {
font-weight: 400; font-weight: 400;
@ -1770,45 +1786,63 @@ html {
} }
.markdown blockquote { .markdown blockquote {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgba(142, 142, 160, var(--tw-border-opacity)); border-color: var(--gray-300);
border-left-width: 2px; border-left-width: 2px;
line-height: 1rem; line-height: 1rem;
padding-left: 1rem; padding-left: 1rem;
} }
.dark .markdown blockquote {
border-color: var(--gray-600);
}
.markdown table { .markdown table {
--tw-border-spacing-x: 0px; --tw-border-spacing-x: 0px;
--tw-border-spacing-y: 0px; --tw-border-spacing-y: 0px;
border-collapse: separate; border-collapse: separate;
border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y);
width: 100%; width: 100%;
border-color: var(--gray-300);
} }
.markdown th { .markdown th {
background-color: rgba(236, 236, 241, 0.2); background-color: var(--gray-100);
border-bottom-width: 1px; border-bottom-width: 1px;
border-left-width: 1px; border-left-width: 1px;
border-top-width: 1px; border-top-width: 1px;
border-color: var(--gray-300);
padding: 0.25rem 0.75rem; padding: 0.25rem 0.75rem;
font-weight: 600;
}
.dark .markdown th {
border-color: var(--gray-600);
background-color: var(--gray-600);
} }
.markdown th:first-child { .markdown th:first-child {
border-top-left-radius: 0.375rem; border-top-left-radius: 0.75rem;
} }
.markdown th:last-child { .markdown th:last-child {
border-right-width: 1px; border-right-width: 1px;
border-top-right-radius: 0.375rem; border-top-right-radius: 0.75rem;
} }
.markdown td { .markdown td {
border-bottom-width: 1px; border-bottom-width: 1px;
border-left-width: 1px; border-left-width: 1px;
border-color: var(--gray-300);
padding: 0.25rem 0.75rem; padding: 0.25rem 0.75rem;
} }
.markdown td:last-child { .markdown td:last-child {
border-right-width: 1px; border-right-width: 1px;
border-color: var(--gray-300);
}
.dark .markdown td {
border-color: var(--gray-600);
}
.dark .markdown td:last-child {
border-color: var(--gray-600);
} }
.markdown tbody tr:last-child td:first-child { .markdown tbody tr:last-child td:first-child {
border-bottom-left-radius: 0.375rem; border-bottom-left-radius: 0.75rem;
} }
.markdown tbody tr:last-child td:last-child { .markdown tbody tr:last-child td:last-child {
border-bottom-right-radius: 0.375rem; border-bottom-right-radius: 0.75rem;
} }
.markdown a { .markdown a {
text-decoration-line: underline; text-decoration-line: underline;
@ -2011,7 +2045,7 @@ html {
.dark .assistant-item:after { .dark .assistant-item:after {
--tw-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.25); --tw-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.25);
--tw-shadow-colored: inset 0 0 0 1px var(--tw-shadow-color); --tw-shadow-colored: inset 0 0 0 0 1px var(--tw-shadow-color);
} }
.result-streaming > :not(ol):not(ul):not(pre):last-child:after, .result-streaming > :not(ol):not(ul):not(pre):last-child:after,
@ -2248,7 +2282,13 @@ html {
/* Nested unordered lists */ /* Nested unordered lists */
.prose ul ul, .prose ul ul,
.markdown ul ul { .markdown ul ul {
list-style-type: circle; list-style-type: disc;
}
.prose ul ul > li::marker,
.markdown ul ul > li::marker {
color: var(--tw-prose-bullets);
font-size: 0.8em;
} }
.prose ul ul ul, .prose ul ul ul,
@ -2256,6 +2296,12 @@ html {
list-style-type: square; list-style-type: square;
} }
.prose ul ul ul > li::marker,
.markdown ul ul ul > li::marker {
color: var(--tw-prose-bullets);
font-size: 0.7em;
}
/* Nested lists */ /* Nested lists */
.prose ol ol, .prose ol ol,
.prose ul ul, .prose ul ul,
@ -2450,7 +2496,7 @@ html {
.message-content { .message-content {
font-size: var(--markdown-font-size, var(--font-size-base)); font-size: var(--markdown-font-size, var(--font-size-base));
line-height: 1.75; line-height: 1.4;
} }
.message-content pre code { .message-content pre code {