mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 03:43:44 +08:00
Style: UI improvement & 1.4.6 ready
This commit is contained in:
parent
ac9f49f8c9
commit
7a7f5cd4a8
20
README.md
20
README.md
@ -41,18 +41,18 @@ A Clash Meta GUI based on <a href="https://github.com/tauri-apps/tauri">Tauri</a
|
|||||||
|
|
||||||
Download from [release](https://github.com/clash-verge-rev/clash-verge-rev/releases). Supports Windows (x64/x86), Linux (x64/arm64) and macOS 10.15+ (intel/apple).
|
Download from [release](https://github.com/clash-verge-rev/clash-verge-rev/releases). Supports Windows (x64/x86), Linux (x64/arm64) and macOS 10.15+ (intel/apple).
|
||||||
|
|
||||||
- [Windows x64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_x64-setup.exe)
|
- [Windows x64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/Clash.Verge_1.4.6_x64-setup.exe)
|
||||||
- [Windows x86](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_x86-setup.exe)
|
- [Windows x86](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/Clash.Verge_1.4.6_x86-setup.exe)
|
||||||
- [Windows arm64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_arm64-setup.exe)
|
- [Windows arm64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/Clash.Verge_1.4.6_arm64-setup.exe)
|
||||||
|
|
||||||
- [macOS intel](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_x64.dmg)
|
- [macOS intel](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/Clash.Verge_1.4.6_x64.dmg)
|
||||||
- [macOS apple](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_aarch64.dmg)
|
- [macOS apple](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/Clash.Verge_1.4.6_aarch64.dmg)
|
||||||
|
|
||||||
- [Linux x64 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_amd64.AppImage)
|
- [Linux x64 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/clash-verge_1.4.6_amd64.AppImage)
|
||||||
- [Linux x64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_amd64.deb)
|
- [Linux x64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/clash-verge_1.4.6_amd64.deb)
|
||||||
- [Linux x86 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_i386.AppImage)
|
- [Linux x86 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/clash-verge_1.4.6_i386.AppImage)
|
||||||
- [Linux x86 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_i386.deb)
|
- [Linux x86 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/clash-verge_1.4.6_i386.deb)
|
||||||
- [Linux arm64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_arm64.deb)
|
- [Linux arm64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/clash-verge_1.4.6_arm64.deb)
|
||||||
|
|
||||||
Or you can build it yourself. Supports Windows, Linux and macOS 10.15+
|
Or you can build it yourself. Supports Windows, Linux and macOS 10.15+
|
||||||
|
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
## v1.4.6
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- 更新 Clash Meta(mihomo) Core to v1.18.0
|
||||||
|
- UI 优化调整
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## v1.4.5
|
## v1.4.5
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "clash-verge",
|
"name": "clash-verge",
|
||||||
"version": "1.4.5",
|
"version": "1.4.6",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "tauri dev",
|
"dev": "tauri dev",
|
||||||
|
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@ -559,7 +559,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clash-verge"
|
name = "clash-verge"
|
||||||
version = "1.4.5"
|
version = "1.4.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"auto-launch",
|
"auto-launch",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "clash-verge"
|
name = "clash-verge"
|
||||||
version = "1.4.5"
|
version = "1.4.6"
|
||||||
description = "clash verge"
|
description = "clash verge"
|
||||||
authors = ["zzzgydi", "wonfen", "MystiPanda"]
|
authors = ["zzzgydi", "wonfen", "MystiPanda"]
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "Clash Verge",
|
"productName": "Clash Verge",
|
||||||
"version": "1.4.5"
|
"version": "1.4.6"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"distDir": "../dist",
|
"distDir": "../dist",
|
||||||
|
@ -27,15 +27,25 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: 5px 5px;
|
padding: 10px 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
scrollbar-gutter: stable;
|
scrollbar-gutter: stable;
|
||||||
|
|
||||||
.base-content {
|
.base-content {
|
||||||
width: 100%;
|
width: calc(100% - 10px * 2);
|
||||||
// max-width: 850px;
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.no-padding {
|
||||||
|
> section {
|
||||||
|
padding: 0;
|
||||||
|
overflow: visible;
|
||||||
|
|
||||||
|
.base-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,11 @@ interface Props {
|
|||||||
header?: React.ReactNode; // something behind title
|
header?: React.ReactNode; // something behind title
|
||||||
contentStyle?: React.CSSProperties;
|
contentStyle?: React.CSSProperties;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
|
full?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BasePage: React.FC<Props> = (props) => {
|
export const BasePage: React.FC<Props> = (props) => {
|
||||||
const { title, header, contentStyle, children } = props;
|
const { title, header, contentStyle, full, children } = props;
|
||||||
const { theme } = useCustomTheme();
|
const { theme } = useCustomTheme();
|
||||||
|
|
||||||
const isDark = theme.palette.mode === "dark";
|
const isDark = theme.palette.mode === "dark";
|
||||||
@ -28,7 +29,7 @@ export const BasePage: React.FC<Props> = (props) => {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="base-container"
|
className={full ? "base-container no-padding" : "base-container"}
|
||||||
style={{ backgroundColor: isDark ? "#090909" : "#ffffff" }}
|
style={{ backgroundColor: isDark ? "#090909" : "#ffffff" }}
|
||||||
>
|
>
|
||||||
<section
|
<section
|
||||||
|
@ -114,6 +114,7 @@ const ConnectionsPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<BasePage
|
<BasePage
|
||||||
|
full
|
||||||
title={t("Connections")}
|
title={t("Connections")}
|
||||||
contentStyle={{ height: "100%" }}
|
contentStyle={{ height: "100%" }}
|
||||||
header={
|
header={
|
||||||
@ -142,75 +143,72 @@ const ConnectionsPage = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Box sx={{ boxShadow: 0, height: "100%" }}>
|
<Box
|
||||||
<Box
|
sx={{
|
||||||
sx={{
|
pt: 1,
|
||||||
pt: 1,
|
mb: 0.5,
|
||||||
mb: 0.5,
|
mx: "10px",
|
||||||
mx: "12px",
|
height: "36px",
|
||||||
height: "36px",
|
display: "flex",
|
||||||
display: "flex",
|
alignItems: "center",
|
||||||
alignItems: "center",
|
userSelect: "text",
|
||||||
userSelect: "text",
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{!isTableLayout && (
|
||||||
{!isTableLayout && (
|
<Select
|
||||||
<Select
|
|
||||||
size="small"
|
|
||||||
autoComplete="off"
|
|
||||||
value={curOrderOpt}
|
|
||||||
onChange={(e) => setOrderOpt(e.target.value)}
|
|
||||||
sx={{
|
|
||||||
mr: 1,
|
|
||||||
width: i18n.language === "en" ? 190 : 120,
|
|
||||||
'[role="button"]': { py: 0.65 },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{Object.keys(orderOpts).map((opt) => (
|
|
||||||
<MenuItem key={opt} value={opt}>
|
|
||||||
<span style={{ fontSize: 14 }}>{t(opt)}</span>
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<TextField
|
|
||||||
hiddenLabel
|
|
||||||
fullWidth
|
|
||||||
size="small"
|
size="small"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
spellCheck="false"
|
value={curOrderOpt}
|
||||||
variant="outlined"
|
onChange={(e) => setOrderOpt(e.target.value)}
|
||||||
placeholder={t("Filter conditions")}
|
sx={{
|
||||||
value={filterText}
|
mr: 1,
|
||||||
onChange={(e) => setFilterText(e.target.value)}
|
width: i18n.language === "en" ? 190 : 120,
|
||||||
sx={{ input: { py: 0.65, px: 1.25 } }}
|
'[role="button"]': { py: 0.65 },
|
||||||
/>
|
}}
|
||||||
</Box>
|
>
|
||||||
|
{Object.keys(orderOpts).map((opt) => (
|
||||||
|
<MenuItem key={opt} value={opt}>
|
||||||
|
<span style={{ fontSize: 14 }}>{t(opt)}</span>
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
|
|
||||||
<Box height="calc(100% - 50px)" sx={{ userSelect: "text" }}>
|
<TextField
|
||||||
{filterConn.length === 0 ? (
|
hiddenLabel
|
||||||
<BaseEmpty text="No Connections" />
|
fullWidth
|
||||||
) : isTableLayout ? (
|
size="small"
|
||||||
<ConnectionTable
|
autoComplete="off"
|
||||||
connections={filterConn}
|
spellCheck="false"
|
||||||
onShowDetail={(detail) => detailRef.current?.open(detail)}
|
variant="outlined"
|
||||||
/>
|
placeholder={t("Filter conditions")}
|
||||||
) : (
|
value={filterText}
|
||||||
<Virtuoso
|
onChange={(e) => setFilterText(e.target.value)}
|
||||||
data={filterConn}
|
sx={{ input: { py: 0.65, px: 1.25 } }}
|
||||||
itemContent={(index, item) => (
|
/>
|
||||||
<ConnectionItem
|
|
||||||
value={item}
|
|
||||||
onShowDetail={() => detailRef.current?.open(item)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<ConnectionDetail ref={detailRef} />
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
<Box height="calc(100% - 50px)" sx={{ userSelect: "text" }}>
|
||||||
|
{filterConn.length === 0 ? (
|
||||||
|
<BaseEmpty text="No Connections" />
|
||||||
|
) : isTableLayout ? (
|
||||||
|
<ConnectionTable
|
||||||
|
connections={filterConn}
|
||||||
|
onShowDetail={(detail) => detailRef.current?.open(detail)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Virtuoso
|
||||||
|
data={filterConn}
|
||||||
|
itemContent={(index, item) => (
|
||||||
|
<ConnectionItem
|
||||||
|
value={item}
|
||||||
|
onShowDetail={() => detailRef.current?.open(item)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<ConnectionDetail ref={detailRef} />
|
||||||
</BasePage>
|
</BasePage>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -38,6 +38,7 @@ const LogPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<BasePage
|
<BasePage
|
||||||
|
full
|
||||||
title={t("Logs")}
|
title={t("Logs")}
|
||||||
contentStyle={{ height: "100%" }}
|
contentStyle={{ height: "100%" }}
|
||||||
header={
|
header={
|
||||||
@ -66,61 +67,52 @@ const LogPage = () => {
|
|||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
boxSizing: "border-box",
|
pt: 1,
|
||||||
boxShadow: 0,
|
mb: 0.5,
|
||||||
height: "100%",
|
mx: "10px",
|
||||||
userSelect: "text",
|
height: "36px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Select
|
||||||
sx={{
|
size="small"
|
||||||
pt: 1,
|
autoComplete="off"
|
||||||
mb: 0.5,
|
value={logState}
|
||||||
mx: "12px",
|
onChange={(e) => setLogState(e.target.value)}
|
||||||
height: "36px",
|
sx={{ width: 120, mr: 1, '[role="button"]': { py: 0.65 } }}
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Select
|
<MenuItem value="all">ALL</MenuItem>
|
||||||
size="small"
|
<MenuItem value="inf">INFO</MenuItem>
|
||||||
autoComplete="off"
|
<MenuItem value="warn">WARN</MenuItem>
|
||||||
value={logState}
|
<MenuItem value="err">ERROR</MenuItem>
|
||||||
onChange={(e) => setLogState(e.target.value)}
|
</Select>
|
||||||
sx={{ width: 120, mr: 1, '[role="button"]': { py: 0.65 } }}
|
|
||||||
>
|
|
||||||
<MenuItem value="all">ALL</MenuItem>
|
|
||||||
<MenuItem value="inf">INFO</MenuItem>
|
|
||||||
<MenuItem value="warn">WARN</MenuItem>
|
|
||||||
<MenuItem value="err">ERROR</MenuItem>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
hiddenLabel
|
hiddenLabel
|
||||||
fullWidth
|
fullWidth
|
||||||
size="small"
|
size="small"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
placeholder={t("Filter conditions")}
|
placeholder={t("Filter conditions")}
|
||||||
value={filterText}
|
value={filterText}
|
||||||
onChange={(e) => setFilterText(e.target.value)}
|
onChange={(e) => setFilterText(e.target.value)}
|
||||||
sx={{ input: { py: 0.65, px: 1.25 } }}
|
sx={{ input: { py: 0.65, px: 1.25 } }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box height="calc(100% - 50px)">
|
||||||
|
{filterLogs.length > 0 ? (
|
||||||
|
<Virtuoso
|
||||||
|
initialTopMostItemIndex={999}
|
||||||
|
data={filterLogs}
|
||||||
|
itemContent={(index, item) => <LogItem value={item} />}
|
||||||
|
followOutput={"smooth"}
|
||||||
/>
|
/>
|
||||||
</Box>
|
) : (
|
||||||
|
<BaseEmpty text="No Logs" />
|
||||||
<Box height="calc(100% - 50px)">
|
)}
|
||||||
{filterLogs.length > 0 ? (
|
|
||||||
<Virtuoso
|
|
||||||
initialTopMostItemIndex={999}
|
|
||||||
data={filterLogs}
|
|
||||||
itemContent={(index, item) => <LogItem value={item} />}
|
|
||||||
followOutput={"smooth"}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<BaseEmpty text="No Logs" />
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
</BasePage>
|
</BasePage>
|
||||||
);
|
);
|
||||||
|
@ -51,6 +51,7 @@ const ProxyPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<BasePage
|
<BasePage
|
||||||
|
full
|
||||||
contentStyle={{ height: "100%" }}
|
contentStyle={{ height: "100%" }}
|
||||||
title={t("Proxy Groups")}
|
title={t("Proxy Groups")}
|
||||||
header={
|
header={
|
||||||
@ -72,16 +73,7 @@ const ProxyPage = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Box
|
<ProxyGroups mode={curMode!} />
|
||||||
sx={{
|
|
||||||
borderRadius: 1,
|
|
||||||
boxShadow: 0,
|
|
||||||
height: "100%",
|
|
||||||
boxSizing: "border-box",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ProxyGroups mode={curMode!} />
|
|
||||||
</Box>
|
|
||||||
</BasePage>
|
</BasePage>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -18,45 +18,43 @@ const RulesPage = () => {
|
|||||||
}, [data, filterText]);
|
}, [data, filterText]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BasePage title={t("Rules")} contentStyle={{ height: "100%" }}>
|
<BasePage full title={t("Rules")} contentStyle={{ height: "100%" }}>
|
||||||
<Box sx={{ boxSizing: "border-box", boxShadow: 0, height: "100%" }}>
|
<Box
|
||||||
<Box
|
sx={{
|
||||||
sx={{
|
pt: 1,
|
||||||
pt: 1,
|
mb: 0.5,
|
||||||
mb: 0.5,
|
mx: "10px",
|
||||||
mx: "12px",
|
height: "36px",
|
||||||
height: "36px",
|
display: "flex",
|
||||||
display: "flex",
|
alignItems: "center",
|
||||||
alignItems: "center",
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<TextField
|
||||||
<TextField
|
hiddenLabel
|
||||||
hiddenLabel
|
fullWidth
|
||||||
fullWidth
|
size="small"
|
||||||
size="small"
|
autoComplete="off"
|
||||||
autoComplete="off"
|
variant="outlined"
|
||||||
variant="outlined"
|
spellCheck="false"
|
||||||
spellCheck="false"
|
placeholder={t("Filter conditions")}
|
||||||
placeholder={t("Filter conditions")}
|
value={filterText}
|
||||||
value={filterText}
|
onChange={(e) => setFilterText(e.target.value)}
|
||||||
onChange={(e) => setFilterText(e.target.value)}
|
sx={{ input: { py: 0.65, px: 1.25 } }}
|
||||||
sx={{ input: { py: 0.65, px: 1.25 } }}
|
/>
|
||||||
/>
|
</Box>
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box height="calc(100% - 50px)">
|
<Box height="calc(100% - 50px)">
|
||||||
{rules.length > 0 ? (
|
{rules.length > 0 ? (
|
||||||
<Virtuoso
|
<Virtuoso
|
||||||
data={rules}
|
data={rules}
|
||||||
itemContent={(index, item) => (
|
itemContent={(index, item) => (
|
||||||
<RuleItem index={index + 1} value={item} />
|
<RuleItem index={index + 1} value={item} />
|
||||||
)}
|
)}
|
||||||
followOutput={"smooth"}
|
followOutput={"smooth"}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<BaseEmpty text="No Rules" />
|
<BaseEmpty text="No Rules" />
|
||||||
)}
|
)}
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
</BasePage>
|
</BasePage>
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user