mirror of
https://github.com/arc53/DocsGPT.git
synced 2026-02-21 20:01:26 +00:00
Scrollbar normalize styles (#2277)
* normalize styles * Fix agent subhead width * Dark scrollbar color adjust * different browser support --------- Co-authored-by: Alex <a@tushynski.me>
This commit is contained in:
@@ -394,7 +394,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
</NavLink>
|
||||
<div
|
||||
id="conversationsMainDiv"
|
||||
className="mb-auto h-[78vh] overflow-x-hidden overflow-y-auto dark:text-white"
|
||||
className="mb-auto h-[78vh] overflow-x-hidden overflow-y-auto scrollbar-overlay dark:text-white"
|
||||
>
|
||||
{conversations?.loading && !isDeletingConversation && (
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform">
|
||||
|
||||
@@ -112,7 +112,7 @@ export default function AgentPreview() {
|
||||
}, [queries]);
|
||||
return (
|
||||
<div className="relative h-full w-full">
|
||||
<div className="scrollbar-thin absolute inset-0 bottom-[180px] overflow-hidden px-4 pt-4 [&>div>div]:w-full! [&>div>div]:max-w-none!">
|
||||
<div className="scrollbar-overlay absolute inset-0 bottom-[180px] overflow-hidden px-4 pt-4 [&>div>div]:w-full! [&>div>div]:max-w-none!">
|
||||
<ConversationMessages
|
||||
handleQuestion={handleQuestion}
|
||||
handleQuestionSubmission={handleQuestionSubmission}
|
||||
|
||||
@@ -162,7 +162,7 @@ export default function AgentsList() {
|
||||
<h1 className="text-eerie-black mb-0 text-[32px] font-bold lg:text-[40px] dark:text-[#E0E0E0]">
|
||||
{t('agents.title')}
|
||||
</h1>
|
||||
<p className="dark:text-gray-4000 mt-5 max-w-lg text-[15px] leading-6 text-[#71717A]">
|
||||
<p className="dark:text-gray-4000 mt-5 text-[15px] leading-6 text-[#71717A]">
|
||||
{t('agents.description')}
|
||||
</p>
|
||||
|
||||
|
||||
@@ -744,7 +744,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 flex w-full flex-1 grid-cols-5 flex-col gap-10 rounded-[30px] bg-[#F6F6F6] p-5 max-[1179px]:overflow-visible min-[1180px]:grid min-[1180px]:gap-5 min-[1180px]:overflow-hidden dark:bg-[#383838]">
|
||||
<div className="scrollbar-thin col-span-2 flex flex-col gap-5 max-[1179px]:overflow-visible min-[1180px]:max-h-full min-[1180px]:overflow-y-auto min-[1180px]:pr-3">
|
||||
<div className="scrollbar-overlay col-span-2 flex flex-col gap-5 max-[1179px]:overflow-visible min-[1180px]:max-h-full min-[1180px]:overflow-y-auto min-[1180px]:pr-3">
|
||||
<div className="dark:bg-raisin-black rounded-[30px] bg-white px-6 py-3 dark:text-[#E0E0E0]">
|
||||
<h2 className="text-lg font-semibold">
|
||||
{t('agents.form.sections.meta')}
|
||||
|
||||
@@ -443,7 +443,7 @@ export const FilePicker: React.FC<CloudFilePickerProps> = ({
|
||||
<TableContainer
|
||||
ref={scrollContainerRef}
|
||||
height="288px"
|
||||
className="scrollbar-thin md:w-4xl lg:w-5xl"
|
||||
className="scrollbar-overlay md:w-4xl lg:w-5xl"
|
||||
bordered={false}
|
||||
>
|
||||
{
|
||||
|
||||
@@ -212,7 +212,7 @@ export default function MultiSelectPopup({
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-b-2 border-gray-900 dark:border-white"></div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-full overflow-y-auto [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-gray-400 dark:[&::-webkit-scrollbar-thumb]:bg-gray-600 [&::-webkit-scrollbar-track]:bg-gray-200 dark:[&::-webkit-scrollbar-track]:bg-[#2C2E3C]">
|
||||
<div className="h-full overflow-y-auto scrollbar-overlay">
|
||||
{filteredOptions.length === 0 ? (
|
||||
<div className="flex h-full flex-col items-center justify-center px-4 py-8 text-center">
|
||||
<img
|
||||
|
||||
@@ -139,7 +139,7 @@ export default function SourcesPopup({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="dark:border-dim-gray mx-4 grow overflow-y-auto rounded-md border border-[#D9D9D9] [&::-webkit-scrollbar-thumb]:bg-[#888] [&::-webkit-scrollbar-thumb]:hover:bg-[#555] [&::-webkit-scrollbar-track]:bg-[#E2E8F0] dark:[&::-webkit-scrollbar-track]:bg-[#2C2E3C]">
|
||||
<div className="dark:border-dim-gray mx-4 grow overflow-y-auto rounded-md border border-[#D9D9D9] scrollbar-overlay">
|
||||
{options ? (
|
||||
<>
|
||||
{filteredOptions?.map((option: any, index: number) => {
|
||||
|
||||
@@ -175,7 +175,7 @@ export default function ToolsPopup({
|
||||
</div>
|
||||
) : (
|
||||
<div className="dark:border-dim-gray mx-4 grow overflow-hidden rounded-md border border-[#D9D9D9]">
|
||||
<div className="h-full overflow-y-auto [&::-webkit-scrollbar-thumb]:bg-[#888] [&::-webkit-scrollbar-thumb]:hover:bg-[#555] [&::-webkit-scrollbar-track]:bg-[#E2E8F0] dark:[&::-webkit-scrollbar-track]:bg-[#2C2E3C]">
|
||||
<div className="h-full overflow-y-auto scrollbar-overlay">
|
||||
{filteredTools.length === 0 ? (
|
||||
<div className="flex h-full flex-col items-center justify-center py-8">
|
||||
<img
|
||||
|
||||
@@ -455,7 +455,6 @@ const ConversationBubble = forwardRef<
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
borderRadius: 0,
|
||||
scrollbarWidth: 'thin',
|
||||
}}
|
||||
>
|
||||
{String(children).replace(/\n$/, '')}
|
||||
@@ -645,7 +644,7 @@ function AllSources(sources: AllSourcesProps) {
|
||||
<p className="text-left text-xl">{`${sources.sources.length} ${t('conversation.sources.title')}`}</p>
|
||||
<div className="mx-1 mt-2 h-[0.8px] w-full rounded-full bg-[#C4C4C4]/40 lg:w-[95%]"></div>
|
||||
</div>
|
||||
<div className="scrollbar-thin mt-6 flex h-[90%] w-52 flex-col gap-4 overflow-y-auto pr-3 sm:w-64">
|
||||
<div className="mt-6 flex h-[90%] w-52 flex-col gap-4 overflow-y-auto pr-3 sm:w-64">
|
||||
{sources.sources.map((source, index) => {
|
||||
const isExternalSource = source.link && source.link !== 'local';
|
||||
return (
|
||||
@@ -866,7 +865,6 @@ function Thought({
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
borderRadius: 0,
|
||||
scrollbarWidth: 'thin',
|
||||
}}
|
||||
>
|
||||
{String(children).replace(/\n$/, '')}
|
||||
|
||||
@@ -110,7 +110,6 @@ layer(base);
|
||||
}
|
||||
|
||||
@utility scrollbar-thin {
|
||||
/* Thin scrollbar utility */
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
@@ -122,31 +121,82 @@ layer(base);
|
||||
|
||||
/* Light theme scrollbar */
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(215, 215, 215, 1);
|
||||
border-radius: 3px;
|
||||
background: #E2E8F0;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(195, 195, 195, 1);
|
||||
background: #8C9198;
|
||||
}
|
||||
|
||||
/* Dark theme scrollbar */
|
||||
.dark &::-webkit-scrollbar-thumb {
|
||||
background: rgba(77, 78, 88, 1);
|
||||
border-radius: 3px;
|
||||
background: #949494;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.dark &::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(97, 98, 108, 1);
|
||||
background: #F0F0F0;
|
||||
}
|
||||
|
||||
/* For Firefox - Light theme */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(215, 215, 215, 1) transparent;
|
||||
scrollbar-color: #E2E8F0 transparent;
|
||||
|
||||
/* For Firefox - Dark theme */
|
||||
.dark & {
|
||||
scrollbar-color: rgba(77, 78, 88, 1) transparent;
|
||||
scrollbar-color: #949494 transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@utility scrollbar-overlay {
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: transparent;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
&:hover::-webkit-scrollbar-thumb {
|
||||
background: #E2E8F0;
|
||||
}
|
||||
|
||||
&:hover::-webkit-scrollbar-thumb:hover {
|
||||
background: #8C9198;
|
||||
}
|
||||
|
||||
.dark &::-webkit-scrollbar-thumb {
|
||||
background: transparent;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.dark &:hover::-webkit-scrollbar-thumb {
|
||||
background: #949494;
|
||||
}
|
||||
|
||||
.dark &:hover::-webkit-scrollbar-thumb:hover {
|
||||
background: #F0F0F0;
|
||||
}
|
||||
|
||||
/* Standard scrollbar properties (Chrome 121+, Firefox) */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: transparent transparent;
|
||||
|
||||
&:hover {
|
||||
scrollbar-color: #E2E8F0 transparent;
|
||||
}
|
||||
|
||||
.dark & {
|
||||
scrollbar-color: transparent transparent;
|
||||
}
|
||||
|
||||
.dark &:hover {
|
||||
scrollbar-color: #949494 transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,16 +227,25 @@ layer(base);
|
||||
min-width: 150px;
|
||||
max-width: 320px;
|
||||
overflow: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: grey transparent;
|
||||
}
|
||||
|
||||
& td {
|
||||
min-width: 150px;
|
||||
max-width: 320px;
|
||||
overflow: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: grey transparent;
|
||||
}
|
||||
|
||||
@supports (-moz-appearance: none) {
|
||||
& th,
|
||||
& td {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #E2E8F0 transparent;
|
||||
}
|
||||
|
||||
.dark & th,
|
||||
.dark & td {
|
||||
scrollbar-color: #949494 transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,23 +265,35 @@ layer(base);
|
||||
background-color: #202124; /* raisin-black */
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
.dark ::-webkit-scrollbar-track {
|
||||
background: #2f3036;
|
||||
background: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #888;
|
||||
border-radius: 40px;
|
||||
background: #E2E8F0;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
background: #8C9198;
|
||||
}
|
||||
.dark ::-webkit-scrollbar-thumb {
|
||||
background: #949494;
|
||||
}
|
||||
.dark ::-webkit-scrollbar-thumb:hover {
|
||||
background: #b1afaf;
|
||||
background: #F0F0F0;
|
||||
}
|
||||
|
||||
/* Firefox: base scrollbar styles (Firefox ignores ::-webkit-scrollbar) */
|
||||
@supports (-moz-appearance: none) {
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #E2E8F0 transparent;
|
||||
}
|
||||
.dark * {
|
||||
scrollbar-color: #949494 transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -637,9 +708,6 @@ Avoid over-scrolling in mobile browsers
|
||||
src: url('/fonts/IBMPlexMono-Medium.ttf');
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10;
|
||||
}
|
||||
/* Light mode specific autofill styles */
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:hover,
|
||||
|
||||
@@ -6,6 +6,66 @@ import { Provider } from 'react-redux';
|
||||
import store from './store';
|
||||
import './index.css';
|
||||
|
||||
// Show scrollbar on scroll for scrollbar-overlay elements, hide after 1s idle
|
||||
const scrollTimers = new WeakMap<Element, ReturnType<typeof setTimeout>>();
|
||||
let sbIdCounter = 0;
|
||||
const sbStyleEl = document.createElement('style');
|
||||
document.head.appendChild(sbStyleEl);
|
||||
const activeSbs = new Map<string, string>();
|
||||
function rebuildSbStyles() {
|
||||
sbStyleEl.textContent = Array.from(activeSbs.values()).join('');
|
||||
}
|
||||
function showOverlayScrollbar(el: HTMLElement) {
|
||||
if (!el.dataset.sbId) el.dataset.sbId = String(++sbIdCounter);
|
||||
const sbId = el.dataset.sbId;
|
||||
const isDark = document.body.classList.contains('dark');
|
||||
const thumb = isDark ? '#949494' : '#E2E8F0';
|
||||
const thumbHover = isDark ? '#F0F0F0' : '#8C9198';
|
||||
// Webkit: inject <style> (Safari only re-renders scrollbar on stylesheet changes)
|
||||
activeSbs.set(
|
||||
sbId,
|
||||
`[data-sb-id="${sbId}"]::-webkit-scrollbar-thumb{background:${thumb}!important;border-radius:9999px}` +
|
||||
`[data-sb-id="${sbId}"]::-webkit-scrollbar-thumb:hover{background:${thumbHover}!important}`,
|
||||
);
|
||||
rebuildSbStyles();
|
||||
// Standard property (Chrome 121+, Firefox)
|
||||
el.style.scrollbarColor = `${thumb} transparent`;
|
||||
|
||||
const prev = scrollTimers.get(el);
|
||||
if (prev) clearTimeout(prev);
|
||||
scrollTimers.set(
|
||||
el,
|
||||
setTimeout(() => {
|
||||
activeSbs.delete(sbId);
|
||||
rebuildSbStyles();
|
||||
el.style.removeProperty('scrollbar-color');
|
||||
}, 1000),
|
||||
);
|
||||
}
|
||||
// scroll events don't bubble — use capture phase, target is the scrolling element
|
||||
document.addEventListener(
|
||||
'scroll',
|
||||
(e) => {
|
||||
const target = e.target;
|
||||
if (
|
||||
target instanceof HTMLElement &&
|
||||
target.classList.contains('scrollbar-overlay')
|
||||
) {
|
||||
showOverlayScrollbar(target);
|
||||
}
|
||||
},
|
||||
true,
|
||||
);
|
||||
// wheel events bubble — use closest() to find the overlay container (works in Safari)
|
||||
document.addEventListener(
|
||||
'wheel',
|
||||
(e) => {
|
||||
const el = (e.target as Element)?.closest?.('.scrollbar-overlay');
|
||||
if (el instanceof HTMLElement) showOverlayScrollbar(el);
|
||||
},
|
||||
{ passive: true },
|
||||
);
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<BrowserRouter>
|
||||
|
||||
@@ -190,7 +190,7 @@ function Log({
|
||||
</div>
|
||||
{isOpen && (
|
||||
<div className="rounded-b-xl bg-[#F1F1F1] px-4 py-3 dark:bg-[#1B1B1B]">
|
||||
<div className="scrollbar-thin overflow-y-auto">
|
||||
<div className="scrollbar-overlay overflow-y-auto">
|
||||
<pre className="px-2 font-mono text-xs leading-relaxed break-words whitespace-pre-wrap text-gray-700 dark:text-gray-400">
|
||||
{JSON.stringify(filteredLog, null, 2)}
|
||||
</pre>
|
||||
|
||||
@@ -273,7 +273,7 @@ export default function ToolConfig({
|
||||
});
|
||||
};
|
||||
return (
|
||||
<div className="scrollbar-thin mt-8 flex flex-col gap-4">
|
||||
<div className="scrollbar-overlay mt-8 flex flex-col gap-4">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<div className="text-eerie-black dark:text-bright-gray flex items-center gap-3 text-sm">
|
||||
<button
|
||||
@@ -818,7 +818,7 @@ function APIToolConfig({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="scrollbar-thin flex flex-col gap-4">
|
||||
<div className="scrollbar-overlay flex flex-col gap-4">
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
@@ -1659,7 +1659,7 @@ function APIActionTable({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="scrollbar-thin flex flex-col gap-6">
|
||||
<div className="scrollbar-overlay flex flex-col gap-6">
|
||||
<div>
|
||||
<h3 className="text-eerie-black dark:text-bright-gray mb-1 text-base font-normal">
|
||||
{t('settings.tools.headers')}
|
||||
|
||||
Reference in New Issue
Block a user