Fix web ui & optimise publish plugin/prettier actions (#1344)
* fix: display selected plugin * fix: app bar responsive * fix: sticky header * added user-agent tooltip * fix: responsive pop * responsivve parsenovel * chatper list responsive * update dependecies * update npm i action * test * skip prepare * add terser to deps * update tsc command * remove ts-node * fix: tsc options * separate tsconfig for publish plugins * revert host_plugins.yml * test only terser & typescipt * revert github action file * cheapter prettier action
This commit is contained in:
+2
-2
@@ -1,8 +1,8 @@
|
||||
#root {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
width: 100vw;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
|
||||
+2
-1
@@ -32,10 +32,11 @@ function App() {
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<Appbar />
|
||||
|
||||
<Container
|
||||
sx={{
|
||||
mt: 6,
|
||||
width: { sm: 500, md: 1024, lg: '90vw' },
|
||||
width: '90%',
|
||||
}}
|
||||
>
|
||||
<HeadersSection />
|
||||
|
||||
@@ -5,16 +5,17 @@ import Toolbar from '@mui/material/Toolbar';
|
||||
import {
|
||||
Avatar,
|
||||
Backdrop,
|
||||
Button,
|
||||
FormControl,
|
||||
InputAdornment,
|
||||
Link,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { Search, Translate } from '@mui/icons-material';
|
||||
import { Search } from '@mui/icons-material';
|
||||
import { searchPlugins } from '@provider/plugins';
|
||||
import { Plugin } from '@typings/plugin';
|
||||
import { FixedSizeList, ListChildComponentProps } from 'react-window';
|
||||
@@ -28,11 +29,12 @@ function renderPlugin(setListVisible: (v: boolean) => void) {
|
||||
return function (props: ListChildComponentProps<Plugin.PluginBase[]>) {
|
||||
const { index, style, data } = props;
|
||||
const plugin = data[index];
|
||||
const selectPlugin = useAppStore(state => state.selectPlugin);
|
||||
const { selectPlugin, pluginItem } = useAppStore(state => state);
|
||||
|
||||
return (
|
||||
<ListItem style={style} key={index} component="div" disablePadding>
|
||||
<ListItemButton
|
||||
selected={plugin.id === pluginItem?.id}
|
||||
onClick={() => {
|
||||
selectPlugin({
|
||||
id: plugin.id,
|
||||
@@ -67,79 +69,93 @@ export default function Appbar() {
|
||||
}, [keyword]);
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<AppBar position="fixed" color="inherit" elevation={0}>
|
||||
<Toolbar>
|
||||
<FormControl
|
||||
sx={{
|
||||
display: 'block',
|
||||
position: 'relative',
|
||||
zIndex: 10,
|
||||
<AppBar
|
||||
sx={{ width: '100vw' }}
|
||||
position="sticky"
|
||||
color="inherit"
|
||||
elevation={0}
|
||||
>
|
||||
<Toolbar>
|
||||
<FormControl
|
||||
sx={{
|
||||
display: 'block',
|
||||
position: 'relative',
|
||||
zIndex: 10,
|
||||
width: {
|
||||
xs: '80%',
|
||||
md: '40%',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
size="small"
|
||||
variant="outlined"
|
||||
placeholder="Seach plugin"
|
||||
value={keyword}
|
||||
onChange={e => setKeyword(e.target.value)}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<Search />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
size="small"
|
||||
variant="outlined"
|
||||
placeholder="Seach plugin"
|
||||
value={keyword}
|
||||
onChange={e => setKeyword(e.target.value)}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<Search />
|
||||
</InputAdornment>
|
||||
),
|
||||
fullWidth
|
||||
autoComplete="off"
|
||||
onFocus={() => {
|
||||
setListVisible(true);
|
||||
if (searchedPlugins.length === 0)
|
||||
setSearchPlugins(searchPlugins(keyword));
|
||||
}}
|
||||
/>
|
||||
{listVisible ? (
|
||||
<FixedSizeList
|
||||
height={400}
|
||||
width={'100%'}
|
||||
itemData={searchedPlugins}
|
||||
itemSize={46}
|
||||
itemCount={searchedPlugins.length}
|
||||
overscanCount={5}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
boxShadow: '2px 10px 20px rgba(0, 0, 0, 0.2)',
|
||||
backgroundColor: 'white',
|
||||
}}
|
||||
sx={{ width: 434 }}
|
||||
onFocus={() => {
|
||||
setListVisible(true);
|
||||
if (searchedPlugins.length === 0)
|
||||
setSearchPlugins(searchPlugins(keyword));
|
||||
}}
|
||||
/>
|
||||
{listVisible ? (
|
||||
<FixedSizeList
|
||||
height={400}
|
||||
width={434}
|
||||
itemData={searchedPlugins}
|
||||
itemSize={46}
|
||||
itemCount={searchedPlugins.length}
|
||||
overscanCount={5}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
boxShadow: '2px 10px 20px rgba(0, 0, 0, 0.2)',
|
||||
backgroundColor: 'white',
|
||||
}}
|
||||
>
|
||||
{renderPlugin(setListVisible)}
|
||||
</FixedSizeList>
|
||||
) : null}
|
||||
</FormControl>
|
||||
<Backdrop
|
||||
open={listVisible}
|
||||
onClick={() => setListVisible(false)}
|
||||
sx={{ zIndex: 9 }}
|
||||
invisible={true}
|
||||
></Backdrop>
|
||||
<InputAdornment position="end">
|
||||
<Button variant="contained" endIcon={<Translate />}>
|
||||
Language
|
||||
</Button>
|
||||
</InputAdornment>
|
||||
{plugin ? (
|
||||
<Box sx={{ pl: 2, display: 'flex', alignItems: 'center' }}>
|
||||
<Avatar
|
||||
sx={{ mr: 1 }}
|
||||
src={resovleIcon(plugin.icon)}
|
||||
variant="square"
|
||||
/>
|
||||
<a href={plugin.site} target="_blank">
|
||||
{plugin.name} - v{plugin.version}
|
||||
</a>
|
||||
</Box>
|
||||
>
|
||||
{renderPlugin(setListVisible)}
|
||||
</FixedSizeList>
|
||||
) : null}
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
</Box>
|
||||
</FormControl>
|
||||
<Backdrop
|
||||
open={listVisible}
|
||||
onClick={() => setListVisible(false)}
|
||||
sx={{ zIndex: 9 }}
|
||||
invisible={true}
|
||||
/>
|
||||
{plugin ? (
|
||||
<Link
|
||||
sx={{
|
||||
pl: 2,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
textDecoration: 'none',
|
||||
}}
|
||||
href={plugin.site}
|
||||
target="_blank"
|
||||
>
|
||||
<Avatar
|
||||
sx={{ mr: 1 }}
|
||||
src={resovleIcon(plugin.icon)}
|
||||
variant="square"
|
||||
/>
|
||||
<Typography
|
||||
sx={{ display: { xs: 'none', sm: 'none', md: 'block' } }}
|
||||
>
|
||||
{plugin.name} - v{plugin.version}
|
||||
</Typography>
|
||||
</Link>
|
||||
) : null}
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
MenuItem,
|
||||
Select,
|
||||
TextField,
|
||||
Tooltip,
|
||||
} from '@mui/material';
|
||||
import Checkbox from '@mui/material/Checkbox';
|
||||
import useDebounce from '@hooks/useDebounce';
|
||||
@@ -57,17 +58,21 @@ export default function HeadersSection() {
|
||||
</Alert>
|
||||
) : null}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<TextField
|
||||
size="small"
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">User Agent</InputAdornment>
|
||||
),
|
||||
}}
|
||||
disabled
|
||||
value={navigator.userAgent}
|
||||
sx={{ width: '100%' }}
|
||||
/>
|
||||
<Tooltip title={navigator.userAgent}>
|
||||
<TextField
|
||||
size="small"
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">User Agent</InputAdornment>
|
||||
),
|
||||
},
|
||||
}}
|
||||
disabled
|
||||
value={navigator.userAgent}
|
||||
sx={{ width: '100%' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Checkbox
|
||||
size="large"
|
||||
checked={useUserAgent}
|
||||
@@ -77,10 +82,12 @@ export default function HeadersSection() {
|
||||
<Box sx={{ height: 10 }} />
|
||||
<TextField
|
||||
size="small"
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">Cookies</InputAdornment>
|
||||
),
|
||||
slotProps={{
|
||||
input: {
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">Cookies</InputAdornment>
|
||||
),
|
||||
},
|
||||
}}
|
||||
value={cookies}
|
||||
onChange={e => {
|
||||
|
||||
@@ -62,7 +62,7 @@ export default function ChapterList({
|
||||
chapters: Plugin.ChapterItem[];
|
||||
}) {
|
||||
return (
|
||||
<Paper style={{ height: 400, width: '100%', textAlign: 'left' }}>
|
||||
<Paper style={{ height: 400, minWidth: 600, textAlign: 'left' }}>
|
||||
<TableVirtuoso
|
||||
data={chapters}
|
||||
components={VirtuosoTableComponents}
|
||||
|
||||
@@ -11,10 +11,10 @@ import {
|
||||
Stack,
|
||||
Tooltip,
|
||||
Typography,
|
||||
useTheme,
|
||||
} from '@mui/material';
|
||||
import CopiableItem from '@components/CopiableItem';
|
||||
import PathItem from '@components/PathItem';
|
||||
import Textarea from '@components/Textarea';
|
||||
|
||||
const FlexRow = ({ children }: { children: ReactNode }) => (
|
||||
<Stack direction="row" gap={1} sx={{ alignItems: 'center' }}>
|
||||
@@ -28,12 +28,12 @@ export default function SourceNovelCard({
|
||||
sourceNovel: Plugin.SourceNovel;
|
||||
}) {
|
||||
return (
|
||||
<Card sx={{ display: 'flex', p: 1, width: '100%', textAlign: 'left' }}>
|
||||
<Box sx={{ display: 'flex', p: 1, width: '100%', textAlign: 'left' }}>
|
||||
<CopiableItem content={sourceNovel.cover}>
|
||||
<Tooltip title={sourceNovel.cover}>
|
||||
<CardMedia
|
||||
component="img"
|
||||
sx={{ width: 200, height: '100%' }}
|
||||
sx={{ width: 300, height: 400, objectFit: 'cover' }}
|
||||
image={sourceNovel.cover}
|
||||
/>
|
||||
</Tooltip>
|
||||
@@ -99,14 +99,19 @@ export default function SourceNovelCard({
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<Typography>Summary</Typography>
|
||||
<Textarea
|
||||
sx={{ width: '100%' }}
|
||||
maxRows={5}
|
||||
value={sourceNovel.summary}
|
||||
disabled
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
overflowY: 'auto',
|
||||
height: 300,
|
||||
p: 2,
|
||||
borderRadius: 2,
|
||||
backgroundColor: 'rgba(0,0,0,0.1)',
|
||||
}}
|
||||
>
|
||||
{sourceNovel.summary}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Box>
|
||||
</Card>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useAppStore } from '@store';
|
||||
export default function ParseNovel() {
|
||||
const plugin = useAppStore(state => state.plugin);
|
||||
const [sourceNovel, setSouceNovel] = useState<
|
||||
Plugin.SourceNovel | undefined
|
||||
(Plugin.SourceNovel & { totalPages?: number }) | undefined
|
||||
>();
|
||||
const [chapters, setChapters] = useState<Plugin.ChapterItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -37,34 +37,34 @@ export default function ParseNovel() {
|
||||
};
|
||||
return (
|
||||
<AccordionContainer title="Parse Novel" loading={loading}>
|
||||
<Stack direction={'row'} spacing={2}>
|
||||
<TextField
|
||||
value={novelPath}
|
||||
onChange={e => setNovelPath(e.target.value)}
|
||||
size="small"
|
||||
label="Novel path"
|
||||
/>
|
||||
<Button
|
||||
disabled={plugin === undefined}
|
||||
onClick={fetchNovel}
|
||||
variant="contained"
|
||||
>
|
||||
Fetch
|
||||
</Button>
|
||||
</Stack>
|
||||
{sourceNovel ? <SourceNovelCard sourceNovel={sourceNovel} /> : null}
|
||||
<Box sx={{ height: 20 }} />
|
||||
{chapters.length ? <ChapterList chapters={chapters} /> : null}
|
||||
<Box sx={{ height: 20 }} />
|
||||
{/* @ts-ignore */}
|
||||
{sourceNovel?.totalPages ? (
|
||||
<Pagination
|
||||
// @ts-ignore
|
||||
count={sourceNovel.totalPages}
|
||||
onChange={(e, page) => fetchPage(page)}
|
||||
variant="outlined"
|
||||
/>
|
||||
) : null}
|
||||
<Box sx={{ overflowX: 'auto', width: '100%' }}>
|
||||
<Stack direction={'row'} spacing={2}>
|
||||
<TextField
|
||||
value={novelPath}
|
||||
onChange={e => setNovelPath(e.target.value)}
|
||||
size="small"
|
||||
label="Novel path"
|
||||
/>
|
||||
<Button
|
||||
disabled={plugin === undefined}
|
||||
onClick={fetchNovel}
|
||||
variant="contained"
|
||||
>
|
||||
Fetch
|
||||
</Button>
|
||||
</Stack>
|
||||
{sourceNovel ? <SourceNovelCard sourceNovel={sourceNovel} /> : null}
|
||||
<Box sx={{ height: 20 }} />
|
||||
{chapters.length ? <ChapterList chapters={chapters} /> : null}
|
||||
<Box sx={{ height: 20 }} />
|
||||
{sourceNovel?.totalPages ? (
|
||||
<Pagination
|
||||
count={sourceNovel.totalPages}
|
||||
onChange={(e, page) => fetchPage(page)}
|
||||
variant="outlined"
|
||||
/>
|
||||
) : null}
|
||||
</Box>
|
||||
</AccordionContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -80,7 +80,13 @@ export default function PopularNovels() {
|
||||
|
||||
return (
|
||||
<AccordionContainer title="Popular Novels" loading={loading}>
|
||||
<Stack direction={'row'} spacing={2} alignItems={'center'}>
|
||||
<Stack
|
||||
direction={'row'}
|
||||
useFlexGap
|
||||
sx={{ flexWrap: 'wrap' }}
|
||||
spacing={2}
|
||||
alignItems={'center'}
|
||||
>
|
||||
<FormGroup>
|
||||
<FormControlLabel
|
||||
control={
|
||||
@@ -140,13 +146,12 @@ export default function PopularNovels() {
|
||||
sx={{
|
||||
width: '100%',
|
||||
overflowX: 'auto',
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<Stack sx={{ width: 'max-content' }} direction={'row'} spacing={2}>
|
||||
{novels.map((novel, index) => (
|
||||
<NovelItemCard key={'novelItem_' + index} item={novel} />
|
||||
))}
|
||||
</Stack>
|
||||
{novels.map((novel, index) => (
|
||||
<NovelItemCard key={'novelItem_' + index} item={novel} />
|
||||
))}
|
||||
</Box>
|
||||
</AccordionContainer>
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ import CopiableItem from '@components/CopiableItem';
|
||||
import PathItem from '@components/PathItem';
|
||||
export default function NovelItemCard({ item }: { item: Plugin.NovelItem }) {
|
||||
return (
|
||||
<Card sx={{ display: 'flex', p: 1 }}>
|
||||
<Box sx={{ display: 'flex', p: 1 }}>
|
||||
<CopiableItem content={item.cover}>
|
||||
<Tooltip title={item.cover}>
|
||||
<CardMedia
|
||||
@@ -50,6 +50,6 @@ export default function NovelItemCard({ item }: { item: Plugin.NovelItem }) {
|
||||
<PathItem path={item.path} />
|
||||
</CardContent>
|
||||
</Box>
|
||||
</Card>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user