);
+
+ return (
+
+
+ {levels.map((level) =>
+ level in count && count[level] > 0 ? (
+ setActiveDialog(level)}
+ >
+ {count[level]}
+
+ ) : null,
+ )}
+
+
+
+
+ );
+}
+
+export default React.memo(LogViewer);
diff --git a/frontend/src/ui/components/Sidebar.tsx b/frontend/src/ui/components/Sidebar.tsx
index cbf8f8d..bcff485 100644
--- a/frontend/src/ui/components/Sidebar.tsx
+++ b/frontend/src/ui/components/Sidebar.tsx
@@ -3,13 +3,14 @@ import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { Link, useMatch, useResolvedPath } from 'react-router-dom';
-import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
+
import { RootState } from '../../store';
import { APPNAME, APPREPO } from '../theme';
+import BrowserLink from './BrowserLink';
+import Scrollbar from './utils/Scrollbar';
// @ts-expect-error Asset import
import logo from '../../assets/icon-logo.svg';
-import BrowserLink from './BrowserLink';
export interface RouteSection {
title: string;
@@ -38,6 +39,7 @@ const Header = styled('div', {
});
const AppName = styled('h1', {
+ userSelect: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
@@ -49,6 +51,7 @@ const AppName = styled('h1', {
});
const AppLink = styled(Link, {
+ userSelect: 'none',
all: 'unset',
cursor: 'pointer',
color: '$teal12',
@@ -69,6 +72,7 @@ const AppLink = styled(Link, {
});
const VersionLabel = styled('div', {
+ userSelect: 'none',
textTransform: 'uppercase',
fontSize: '0.75rem',
fontWeight: 'bold',
@@ -106,8 +110,10 @@ const MenuHeader = styled('header', {
fontWeight: 'bold',
padding: '0.5rem 0 0.5rem 0.8rem',
color: '$teal9',
+ userSelect: 'none',
});
const MenuLink = styled(Link, {
+ userSelect: 'none',
color: '$teal13 !important',
display: 'flex',
alignItems: 'center',
@@ -199,10 +205,7 @@ export default function Sidebar({
return (
-
+
@@ -230,7 +233,7 @@ export default function Sidebar({
))}
))}
-
+
);
}
diff --git a/frontend/src/ui/components/utils/Scrollbar.tsx b/frontend/src/ui/components/utils/Scrollbar.tsx
new file mode 100644
index 0000000..4635643
--- /dev/null
+++ b/frontend/src/ui/components/utils/Scrollbar.tsx
@@ -0,0 +1,64 @@
+import React from 'react';
+import * as ScrollArea from '@radix-ui/react-scroll-area';
+import { styled } from '../../theme';
+
+export interface ScrollbarProps {
+ vertical?: boolean;
+ horizontal?: boolean;
+ root?: React.CSSProperties;
+ viewport?: React.CSSProperties;
+}
+
+const StyledScrollbar = styled(ScrollArea.Scrollbar, {
+ display: 'flex',
+ userSelect: 'none',
+ touchAction: 'none',
+ padding: '2px',
+ background: '$blackA6',
+ transition: 'background 160ms ease-out',
+ '&:hover': {
+ background: '$blackA8',
+ },
+});
+
+const StyledThumb = styled(ScrollArea.Thumb, {
+ flex: '1',
+ background: '$teal6',
+ borderRadius: '10px',
+ position: 'relative',
+ '&:hover': {
+ background: '$teal8',
+ },
+});
+
+function Scrollbar({
+ vertical,
+ horizontal,
+ root,
+ viewport,
+ children,
+}: React.PropsWithChildren): React.ReactElement {
+ return (
+
+
+ {children}
+
+ {vertical ? (
+
+
+
+ ) : null}
+ {horizontal ? (
+
+
+
+ ) : null}
+
+
+ );
+}
+
+export default React.memo(Scrollbar);
diff --git a/frontend/src/ui/theme/forms.ts b/frontend/src/ui/theme/forms.ts
index 50a85c4..472f269 100644
--- a/frontend/src/ui/theme/forms.ts
+++ b/frontend/src/ui/theme/forms.ts
@@ -1,5 +1,6 @@
import * as UnstyledLabel from '@radix-ui/react-label';
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
+import * as ToggleGroup from '@radix-ui/react-toggle-group';
import { styled } from './theme';
import { theme } from '.';
@@ -115,15 +116,15 @@ export const MultiButton = styled('div', {
display: 'flex',
});
-export const Button = styled('button', {
+const button = {
all: 'unset',
cursor: 'pointer',
userSelect: 'none',
color: '$gray12',
fontWeight: '300',
- padding: '0.5rem 1rem',
borderRadius: theme.borderRadius.form,
fontSize: '1.1rem',
+ padding: '0.5rem 1rem',
border: '1px solid $gray6',
backgroundColor: '$gray4',
display: 'flex',
@@ -220,6 +221,37 @@ export const Button = styled('button', {
},
},
},
+};
+
+export const MultiToggle = styled(ToggleGroup.Root, {
+ display: 'inline-flex',
+ borderRadius: theme.borderRadius.form,
+ backgroundColor: '$gray4',
+});
+
+export const MultiToggleItem = styled(ToggleGroup.Item, {
+ ...button,
+ borderRadius: 0,
+ border: 0,
+ '&:first-child': {
+ borderTopLeftRadius: theme.borderRadius.form,
+ borderBottomLeftRadius: theme.borderRadius.form,
+ },
+ '&:last-child': {
+ borderTopRightRadius: theme.borderRadius.form,
+ borderBottomRightRadius: theme.borderRadius.form,
+ },
+ '&:hover': {
+ ...button['&:hover'],
+ },
+ "&[data-state='on']": {
+ ...button['&:active'],
+ background: '$gray8',
+ },
+});
+
+export const Button = styled('button', {
+ ...button,
});
export const ComboBox = styled('select', {
diff --git a/modules/http/safebool.go b/modules/http/safebool.go
deleted file mode 100644
index 3084a98..0000000
--- a/modules/http/safebool.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package http
-
-import "sync"
-
-type SafeBool struct {
- val bool
- mux sync.RWMutex
-}
-
-func newSafeBool(val bool) *SafeBool {
- return &SafeBool{val: val, mux: sync.RWMutex{}}
-}
-
-func (s *SafeBool) Set(val bool) {
- s.mux.Lock()
- s.val = val
- s.mux.Unlock()
-}
-
-func (s *SafeBool) Get() bool {
- s.mux.RLock()
- val := s.val
- s.mux.RUnlock()
- return val
-}