mirror of
https://github.com/fallenbagel/jellyseerr.git
synced 2025-12-28 20:49:05 -05:00
Compare commits
21 Commits
preview-fi
...
preview-fe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7528a7c266 | ||
|
|
8be79fcad8 | ||
|
|
e9d6046997 | ||
|
|
eddbf2d0ec | ||
|
|
6d9a1c596e | ||
|
|
c4054531cc | ||
|
|
98c5500967 | ||
|
|
943d84e4d5 | ||
|
|
faec3260f6 | ||
|
|
6bcaa672ad | ||
|
|
15568b1f6b | ||
|
|
0f6ba544f8 | ||
|
|
27cd125532 | ||
|
|
8f6cbefbb3 | ||
|
|
1b2b137760 | ||
|
|
543341d9f9 | ||
|
|
5865bfac96 | ||
|
|
c5e35aad05 | ||
|
|
a46cdb3a4f | ||
|
|
bebbd690b4 | ||
|
|
99443d2796 |
@@ -730,7 +730,6 @@ authRoutes.post('/reset-password/:guid', async (req, res, next) => {
|
||||
});
|
||||
}
|
||||
user.recoveryLinkExpirationDate = null;
|
||||
await user.setPassword(req.body.password);
|
||||
userRepository.save(user);
|
||||
logger.info('Successfully reset password', {
|
||||
label: 'API',
|
||||
|
||||
@@ -7,10 +7,9 @@ type RateLimiteState<T extends (...args: Parameters<T>) => Promise<U>, U> = {
|
||||
queue: {
|
||||
args: Parameters<T>;
|
||||
resolve: (value: U) => void;
|
||||
reject: (reason?: unknown) => void;
|
||||
}[];
|
||||
lastTimestamps: number[];
|
||||
timeout: ReturnType<typeof setTimeout>;
|
||||
activeRequests: number;
|
||||
timer: NodeJS.Timeout | null;
|
||||
};
|
||||
|
||||
const rateLimitById: Record<string, unknown> = {};
|
||||
@@ -28,40 +27,46 @@ export default function rateLimit<
|
||||
>(fn: T, options: RateLimitOptions): (...args: Parameters<T>) => Promise<U> {
|
||||
const state: RateLimiteState<T, U> = (rateLimitById[
|
||||
options.id || ''
|
||||
] as RateLimiteState<T, U>) || { queue: [], lastTimestamps: [] };
|
||||
] as RateLimiteState<T, U>) || { queue: [], activeRequests: 0, timer: null };
|
||||
if (options.id) {
|
||||
rateLimitById[options.id] = state;
|
||||
}
|
||||
|
||||
const processQueue = () => {
|
||||
// remove old timestamps
|
||||
state.lastTimestamps = state.lastTimestamps.filter(
|
||||
(timestamp) => Date.now() - timestamp < 1000
|
||||
);
|
||||
if (state.queue.length === 0) {
|
||||
if (state.timer) {
|
||||
clearInterval(state.timer);
|
||||
state.timer = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.lastTimestamps.length < options.maxRPS) {
|
||||
// process requests if RPS not exceeded
|
||||
while (state.activeRequests < options.maxRPS) {
|
||||
state.activeRequests++;
|
||||
const item = state.queue.shift();
|
||||
if (!item) return;
|
||||
state.lastTimestamps.push(Date.now());
|
||||
const { args, resolve, reject } = item;
|
||||
if (!item) break;
|
||||
const { args, resolve } = item;
|
||||
fn(...args)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
processQueue();
|
||||
} else {
|
||||
// rerun once the oldest item in queue is older than 1s
|
||||
if (state.timeout) clearTimeout(state.timeout);
|
||||
state.timeout = setTimeout(
|
||||
processQueue,
|
||||
1000 - (Date.now() - state.lastTimestamps[0])
|
||||
);
|
||||
.finally(() => {
|
||||
state.activeRequests--;
|
||||
if (state.queue.length > 0) {
|
||||
if (!state.timer) {
|
||||
state.timer = setInterval(processQueue, 1000);
|
||||
}
|
||||
} else {
|
||||
if (state.timer) {
|
||||
clearInterval(state.timer);
|
||||
state.timer = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (...args: Parameters<T>): Promise<U> => {
|
||||
return new Promise<U>((resolve, reject) => {
|
||||
state.queue.push({ args, resolve, reject });
|
||||
return new Promise<U>((resolve) => {
|
||||
state.queue.push({ args, resolve });
|
||||
processQueue();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,85 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 26.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="plex-logo"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 1000 460.89727"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="plex-logo.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"><metadata
|
||||
id="metadata25"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs23">
|
||||
</defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#111111"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
id="namedview21"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="0.27956081"
|
||||
inkscape:cx="783.06912"
|
||||
inkscape:cy="-132.85701"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="plex-logo" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#EBAF00;}
|
||||
</style>
|
||||
<path
|
||||
class="st0"
|
||||
d="m 164.18919,82.43243 c -39.86487,0 -65.540543,11.48648 -87.162163,38.51351 V 91.21621 H 0 v 366.21621 c 0,0 1.3513514,0.67567 5.4054053,1.35135 5.4054057,1.35135 33.7837827,7.43243 54.7297287,-10.13514 18.243244,-15.54054 22.297295,-33.78378 22.297295,-54.05405 v -52.7027 c 22.297301,23.64864 47.297301,33.78378 82.432431,33.78378 75.67567,0 133.78378,-61.48648 133.78378,-143.24323 0,-88.51352 -56.08108,-150 -134.45945,-150 z m -14.86487,223.64864 c -42.56756,0 -76.351351,-35.13513 -76.351351,-77.7027 0,-41.89189 39.864871,-75.67567 76.351351,-75.67567 43.24324,0 76.35135,33.1081 76.35135,76.35135 0,43.24324 -33.78378,77.02702 -76.35135,77.02702 z"
|
||||
id="path4"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff;stroke-width:6.75675678" /><path
|
||||
class="st0"
|
||||
d="m 408.1081,223.64864 c 0,31.75676 3.37838,70.27027 34.45946,112.16216 0.67567,0.67567 2.02702,2.7027 2.02702,2.7027 -12.83783,21.62162 -28.37837,36.48648 -49.32432,36.48648 -16.21622,0 -32.43243,-8.78378 -45.94595,-23.64864 -14.18918,-16.21622 -20.94594,-37.16216 -20.94594,-59.45946 V 0 h 79.05405 z"
|
||||
id="path6"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff;stroke-width:6.75675678" /><polygon
|
||||
class="st1"
|
||||
points="117.9,33.9 104.1,13.5 118.3,13.5 132,33.9 118.3,54.2 104.1,54.2 "
|
||||
id="polygon8"
|
||||
style="fill:#ebaf00"
|
||||
transform="scale(6.7567568)" /><polygon
|
||||
class="st0"
|
||||
points="135.7,31.6 148,13.5 133.8,13.5 128.7,21 "
|
||||
id="polygon10"
|
||||
style="fill:#ffffff"
|
||||
transform="scale(6.7567568)" /><path
|
||||
class="st0"
|
||||
d="m 869.59458,316.2162 c 0,0 16.2162,22.2973 16.2162,22.2973 15.54058,24.32432 35.8108,36.48648 59.45949,36.48648 25,-0.67567 42.56752,-22.29729 49.3243,-30.4054 0,0 -12.16218,-10.81081 -27.7027,-29.05405 -20.94598,-24.32432 -48.64868,-68.91892 -49.3243,-70.94594 z"
|
||||
id="path12"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff;stroke-width:6.75675678" /><path
|
||||
style="fill:#ffffff;stroke-width:6.75675678"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path16"
|
||||
d="m 632.43242,287.16215 c -16.21622,14.86486 -27.02703,22.97297 -49.32432,22.97297 -39.86487,0 -62.83784,-28.37837 -66.21622,-59.45945 h 211.4865 c 1.35131,-4.05406 2.027,-9.45946 2.027,-18.24324 0,-85.81082 -62.83783,-150 -145.27026,-150 -78.37837,0 -142.56756,65.54054 -142.56756,147.29729 0,81.08108 64.18919,145.27026 144.59459,145.27026 56.08108,0 104.72973,-31.75675 131.08105,-87.83783 z M 585.8108,147.29729 c 35.13513,0 61.48648,22.97297 67.56756,53.37838 H 519.59458 c 6.75676,-31.75676 31.75676,-53.37838 66.21622,-53.37838 z"
|
||||
class="st0" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320.03 103.61"><defs><style>.cls-1{fill:#fff}.cls-2{fill:url(#a)}.cls-3{fill:#e5a00d}</style><radialGradient id="a" cx="258.33" cy="51.76" r="42.95" gradientUnits="userSpaceOnUse"><stop offset=".17" stop-color="#f9be03"/><stop offset=".51" stop-color="#e8a50b"/><stop offset="1" stop-color="#cc7c19"/></radialGradient></defs><polygon points="320.03 -.09 289.96 -.09 259.88 51.76 289.96 103.61 320.01 103.61 289.96 51.79" class="cls-1"/><polygon points="226.7 -.09 256.78 -.09 289.96 51.76 256.78 103.61 226.7 103.61 259.88 51.76" class="cls-2"/><polygon points="226.7 -.09 256.78 -.09 289.96 51.76 256.78 103.61 226.7 103.61 259.88 51.76" class="cls-3"/><path d="M216.32,103.61H156.49V-.09h59.83v18h-37.8V40.69H213.7v18H178.52V85.45h37.8Z" class="cls-1"/><path d="M82.07,103.61V-.09h22V85.45h42.07v18.16Z" class="cls-1"/><path d="M71.66,32.25Q71.66,49,61.2,57.87T31.44,66.73H22v36.88H0V-.09H33.14Q52-.09,61.83,8T71.66,32.25ZM22,48.71h7.24q10.15,0,15.18-4c3.37-2.66,5-6.56,5-11.67s-1.41-9-4.22-11.42S38,17.93,32,17.93H22Z" class="cls-1"/></svg>
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -50,7 +50,7 @@ const DiscoverTvNetwork = () => {
|
||||
{firstResultData?.network.logoPath ? (
|
||||
<div className="mb-6 flex justify-center">
|
||||
<Image
|
||||
src={`https://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)${firstResultData.network.logoPath}`}
|
||||
src={`//image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)${firstResultData.network.logoPath}`}
|
||||
alt={firstResultData.network.name}
|
||||
className="max-h-24 sm:max-h-32"
|
||||
fill
|
||||
|
||||
@@ -50,7 +50,7 @@ const DiscoverMovieStudio = () => {
|
||||
{firstResultData?.studio.logoPath ? (
|
||||
<div className="mb-6 flex justify-center">
|
||||
<Image
|
||||
src={`https://image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)${firstResultData.studio.logoPath}`}
|
||||
src={`//image.tmdb.org/t/p/w780_filter(duotone,ffffff,bababa)${firstResultData.studio.logoPath}`}
|
||||
alt={firstResultData.studio.name}
|
||||
className="max-h-24 sm:max-h-32"
|
||||
fill
|
||||
|
||||
@@ -181,9 +181,6 @@ const IssueComment = ({
|
||||
`/api/v1/issueComment/${comment.id}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ message: values.newMessage }),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -126,9 +126,6 @@ const IssueDetails = () => {
|
||||
try {
|
||||
const res = await fetch(`/api/v1/issueComment/${firstComment.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ message: newMessage }),
|
||||
});
|
||||
if (!res.ok) throw new Error();
|
||||
@@ -504,9 +501,6 @@ const IssueDetails = () => {
|
||||
`/api/v1/issue/${issueData?.id}/comment`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ message: values.message }),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -102,9 +102,6 @@ const CreateIssueModal = ({
|
||||
try {
|
||||
const res = await fetch('/api/v1/issue', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
issueType: values.selectedIssue.issueType,
|
||||
message: values.message,
|
||||
|
||||
@@ -17,45 +17,47 @@ interface UserWarningsProps {
|
||||
const UserWarnings: React.FC<UserWarningsProps> = ({ onClick }) => {
|
||||
const intl = useIntl();
|
||||
const { user } = useUser();
|
||||
//check if a user has warnings
|
||||
if (!user || !user.warnings || user.warnings.length === 0) {
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let res = null;
|
||||
|
||||
user.warnings.forEach((warning) => {
|
||||
let link = '';
|
||||
let warningText = '';
|
||||
let warningTitle = '';
|
||||
switch (warning) {
|
||||
case 'userEmailRequired':
|
||||
link = '/profile/settings/';
|
||||
warningTitle = 'Profile is incomplete';
|
||||
warningText = intl.formatMessage(messages.emailRequired);
|
||||
}
|
||||
//check if a user has warnings
|
||||
if (user.warnings.length > 0) {
|
||||
user.warnings.forEach((warning) => {
|
||||
let link = '';
|
||||
let warningText = '';
|
||||
let warningTitle = '';
|
||||
switch (warning) {
|
||||
case 'userEmailRequired':
|
||||
link = '/profile/settings/';
|
||||
warningTitle = 'Profile is incomplete';
|
||||
warningText = intl.formatMessage(messages.emailRequired);
|
||||
}
|
||||
|
||||
res = (
|
||||
<Link
|
||||
href={link}
|
||||
onClick={onClick}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && onClick) {
|
||||
onClick();
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className="mx-2 mb-2 flex items-center rounded-lg bg-yellow-500 p-2 text-xs text-white ring-1 ring-gray-700 transition duration-300 hover:bg-yellow-400"
|
||||
>
|
||||
<ExclamationTriangleIcon className="h-6 w-6" />
|
||||
<div className="flex min-w-0 flex-1 flex-col truncate px-2 last:pr-0">
|
||||
<span className="font-bold">{warningTitle}</span>
|
||||
<span className="truncate">{warningText}</span>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
res = (
|
||||
<Link
|
||||
href={link}
|
||||
onClick={onClick}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && onClick) {
|
||||
onClick();
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className="mx-2 mb-2 flex items-center rounded-lg bg-yellow-500 p-2 text-xs text-white ring-1 ring-gray-700 transition duration-300 hover:bg-yellow-400"
|
||||
>
|
||||
<ExclamationTriangleIcon className="h-6 w-6" />
|
||||
<div className="flex min-w-0 flex-1 flex-col truncate px-2 last:pr-0">
|
||||
<span className="font-bold">{warningTitle}</span>
|
||||
<span className="truncate">{warningText}</span>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
@@ -59,9 +59,6 @@ const AddEmailModal: React.FC<AddEmailModalProps> = ({
|
||||
try {
|
||||
const res = await fetch('/api/v1/auth/jellyfin', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: username,
|
||||
password: password,
|
||||
|
||||
@@ -45,9 +45,6 @@ const Login = () => {
|
||||
try {
|
||||
const res = await fetch('/api/v1/auth/plex', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ authToken }),
|
||||
});
|
||||
if (!res.ok) throw new Error();
|
||||
|
||||
@@ -117,7 +117,6 @@ const ManageSlideOver = ({
|
||||
});
|
||||
if (!res.ok) throw new Error();
|
||||
revalidate();
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -134,7 +133,6 @@ const ManageSlideOver = ({
|
||||
if (!res2.ok) throw new Error();
|
||||
|
||||
revalidate();
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -57,48 +57,44 @@ const ShowMoreCard = ({ url, posters }: ShowMoreCardProps) => {
|
||||
>
|
||||
<div style={{ paddingBottom: '150%' }}>
|
||||
<div className="absolute inset-0 flex h-full w-full flex-col items-center p-2">
|
||||
<div className="relative z-10 grid h-full w-full grid-cols-2 items-center justify-center gap-2 opacity-30">
|
||||
<div className="relative z-10 flex h-full flex-wrap items-center justify-center opacity-30">
|
||||
{posters[0] && (
|
||||
<div className="">
|
||||
<div className="w-1/2 p-1">
|
||||
<Image
|
||||
src={`https://image.tmdb.org/t/p/w300_and_h450_face${posters[0]}`}
|
||||
src={`//image.tmdb.org/t/p/w300_and_h450_face${posters[0]}`}
|
||||
alt=""
|
||||
className="rounded-md"
|
||||
width={300}
|
||||
height={450}
|
||||
className="w-full rounded-md"
|
||||
fill
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{posters[1] && (
|
||||
<div className="">
|
||||
<div className="w-1/2 p-1">
|
||||
<Image
|
||||
src={`https://image.tmdb.org/t/p/w300_and_h450_face${posters[1]}`}
|
||||
src={`//image.tmdb.org/t/p/w300_and_h450_face${posters[1]}`}
|
||||
alt=""
|
||||
className="rounded-md"
|
||||
width={300}
|
||||
height={450}
|
||||
className="w-full rounded-md"
|
||||
fill
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{posters[2] && (
|
||||
<div className="">
|
||||
<div className="w-1/2 p-1">
|
||||
<Image
|
||||
src={`https://image.tmdb.org/t/p/w300_and_h450_face${posters[2]}`}
|
||||
src={`//image.tmdb.org/t/p/w300_and_h450_face${posters[2]}`}
|
||||
alt=""
|
||||
className="rounded-md"
|
||||
width={300}
|
||||
height={450}
|
||||
className="w-full rounded-md"
|
||||
fill
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{posters[3] && (
|
||||
<div className="">
|
||||
<div className="w-1/2 p-1">
|
||||
<Image
|
||||
src={`https://image.tmdb.org/t/p/w300_and_h450_face${posters[3]}`}
|
||||
src={`//image.tmdb.org/t/p/w300_and_h450_face${posters[3]}`}
|
||||
alt=""
|
||||
className="rounded-md"
|
||||
width={300}
|
||||
height={450}
|
||||
className="w-full rounded-md"
|
||||
fill
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -252,7 +252,7 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
|
||||
|
||||
const searchParams = new URLSearchParams(params.enable ? params : {});
|
||||
const res = await fetch(
|
||||
`/api/v1/settings/jellyfin/library?${searchParams.toString()}`
|
||||
`/api/v1/settings/jellyfin/library?${searchParams.toString}`
|
||||
);
|
||||
if (!res.ok) throw new Error();
|
||||
} else {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,29 +7,6 @@
|
||||
min-height: calc(100% + env(safe-area-inset-top));
|
||||
padding: env(safe-area-inset-top) env(safe-area-inset-right)
|
||||
calc(4rem + env(safe-area-inset-bottom)) env(safe-area-inset-left);
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #4b5563 #1f2937;
|
||||
}
|
||||
|
||||
html:hover {
|
||||
scrollbar-color: #6b7280 #1f2937;
|
||||
}
|
||||
|
||||
/* WebKit scrollbar styles */
|
||||
html::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
html::-webkit-scrollbar-track {
|
||||
background: #1f2937;
|
||||
}
|
||||
|
||||
html::-webkit-scrollbar-thumb {
|
||||
background-color: #4b5563;
|
||||
}
|
||||
|
||||
html:hover::-webkit-scrollbar-thumb {
|
||||
background-color: #6b7280;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
|
||||
Reference in New Issue
Block a user