mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 05:03:45 +08:00
refactor: auto-truncate long text on home profile card
fix: sync system proxy and TUN mode status indicators on home proxy mode card
This commit is contained in:
parent
a28887be8e
commit
f739836891
38
UPDATELOG.md
38
UPDATELOG.md
@ -2,21 +2,22 @@
|
|||||||
|
|
||||||
**发行代号:拓**
|
**发行代号:拓**
|
||||||
|
|
||||||
|
感谢 Tunglies 对 Verge 后端重构,性能优化做出的重大贡献!
|
||||||
|
|
||||||
代号释义: 本次发布在功能上的大幅扩展。新首页设计为用户带来全新交互体验,DNS 覆写功能增强网络控制能力,解锁测试页面助力内容访问自由度提升,轻量模式提供灵活使用选择。此外,macOS 应用菜单集成、sidecar 模式、诊断信息导出等新特性进一步丰富了软件的适用场景。这些新增功能显著拓宽了 Clash Verge 的功能边界,为用户提供了更强大的工具和可能性。
|
代号释义: 本次发布在功能上的大幅扩展。新首页设计为用户带来全新交互体验,DNS 覆写功能增强网络控制能力,解锁测试页面助力内容访问自由度提升,轻量模式提供灵活使用选择。此外,macOS 应用菜单集成、sidecar 模式、诊断信息导出等新特性进一步丰富了软件的适用场景。这些新增功能显著拓宽了 Clash Verge 的功能边界,为用户提供了更强大的工具和可能性。
|
||||||
|
|
||||||
#### 修复
|
2.2.1 相对于 2.2.0(已下架不在提供) 修复了:
|
||||||
1. **首页**
|
1. **首页**
|
||||||
- 修复 Direct 模式首页无法渲染。
|
- 修复 Direct 模式首页无法渲染。
|
||||||
- 修复 首页启用轻量模式导致 ClashVergeRev 从托盘退出。
|
- 修复 首页启用轻量模式导致 ClashVergeRev 从托盘退出。
|
||||||
|
- 增加 首页文本过长自动截断
|
||||||
|
- 修复 系统代理标识判断不准的问题
|
||||||
2. **系统**
|
2. **系统**
|
||||||
- 修复 MacOS 无法使用快捷键粘贴/选择/复制订阅地址。
|
- 修复 MacOS 无法使用快捷键粘贴/选择/复制订阅地址。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## v2.2.0
|
## v2.2.0(已下架不在提供)
|
||||||
|
|
||||||
**发行代号:拓**
|
|
||||||
|
|
||||||
代号释义: 本次发布在功能上的大幅扩展。新首页设计为用户带来全新交互体验,DNS 覆写功能增强网络控制能力,解锁测试页面助力内容访问自由度提升,轻量模式提供灵活使用选择。此外,macOS 应用菜单集成、sidecar 模式、诊断信息导出等新特性进一步丰富了软件的适用场景。这些新增功能显著拓宽了 Clash Verge 的功能边界,为用户提供了更强大的工具和可能性。
|
|
||||||
|
|
||||||
#### 新增功能
|
#### 新增功能
|
||||||
1. **首页**
|
1. **首页**
|
||||||
@ -27,8 +28,8 @@
|
|||||||
- 限制首页配置文件卡片URL长度。
|
- 限制首页配置文件卡片URL长度。
|
||||||
|
|
||||||
2. **DNS 设置与覆写**
|
2. **DNS 设置与覆写**
|
||||||
- 默认启用 DNS 设置。
|
|
||||||
- 新增 DNS 覆写功能。
|
- 新增 DNS 覆写功能。
|
||||||
|
- 默认启用 DNS 覆写。
|
||||||
|
|
||||||
3. **解锁测试**
|
3. **解锁测试**
|
||||||
- 新增解锁测试页面。
|
- 新增解锁测试页面。
|
||||||
@ -38,10 +39,11 @@
|
|||||||
- 添加自动轻量模式定时器。
|
- 添加自动轻量模式定时器。
|
||||||
|
|
||||||
5. **系统支持**
|
5. **系统支持**
|
||||||
|
- Mihomo(meta)内核升级 1.19.3
|
||||||
- macOS 支持 CMD+W 关闭窗口。
|
- macOS 支持 CMD+W 关闭窗口。
|
||||||
- 新增 macOS 应用菜单。
|
- 新增 macOS 应用菜单。
|
||||||
- 添加管理员权限提示。
|
- 添加 macOS 安装服务时候的管理员权限提示。
|
||||||
- 新增 sidecar 模式。
|
- 新增 sidecar(用户空间启动内核) 模式。
|
||||||
|
|
||||||
6. **其他**
|
6. **其他**
|
||||||
- 增强延迟测试日志和错误处理。
|
- 增强延迟测试日志和错误处理。
|
||||||
@ -53,28 +55,28 @@
|
|||||||
- 修复 Windows 热键崩溃。
|
- 修复 Windows 热键崩溃。
|
||||||
- 修复 macOS 无框标题。
|
- 修复 macOS 无框标题。
|
||||||
- 修复 macOS 静默启动崩溃。
|
- 修复 macOS 静默启动崩溃。
|
||||||
|
- 修复 macOS tray图标错位到左上角的问题。
|
||||||
- 修复 Windows/Linux 运行时崩溃。
|
- 修复 Windows/Linux 运行时崩溃。
|
||||||
- 修复 Netflix 检测错误。
|
- 修复 Win10 阴影和边框问题。
|
||||||
- 修复服务模式检测失败。
|
|
||||||
|
|
||||||
2. **性能**
|
2. **构建**
|
||||||
- 优化小数值速度更新。
|
|
||||||
- 增加请求超时至 60 秒。
|
|
||||||
- 修复代理节点选择同步。
|
|
||||||
- 优化修改verge配置性能。
|
|
||||||
|
|
||||||
3. **构建**
|
|
||||||
- 修复构建失败问题。
|
- 修复构建失败问题。
|
||||||
|
|
||||||
#### 优化
|
#### 优化
|
||||||
1. **性能**
|
1. **性能**
|
||||||
|
- 重构后端,巨幅性能优化。
|
||||||
- 优化首页组件性能。
|
- 优化首页组件性能。
|
||||||
- 优化流量图表资源使用。
|
- 优化流量图表资源使用。
|
||||||
- 提升代理组列表滚动性能。
|
- 提升代理组列表滚动性能。
|
||||||
- 加快应用退出速度。
|
- 加快应用退出速度。
|
||||||
- 加快进入轻量模式速度。
|
- 加快进入轻量模式速度。
|
||||||
|
- 优化小数值速度更新。
|
||||||
|
- 增加请求超时至 60 秒。
|
||||||
|
- 修复代理节点选择同步。
|
||||||
|
- 优化修改verge配置性能。
|
||||||
|
|
||||||
2. **重构**
|
2. **重构**
|
||||||
|
- 重构后端,巨幅性能优化。
|
||||||
- 优化定时器管理。
|
- 优化定时器管理。
|
||||||
- 重构 MihomoManager 处理流量。
|
- 重构 MihomoManager 处理流量。
|
||||||
- 优化 WebSocket 连接。
|
- 优化 WebSocket 连接。
|
||||||
|
@ -31,6 +31,16 @@ export const EnhancedCard = ({
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isDark = theme.palette.mode === "dark";
|
const isDark = theme.palette.mode === "dark";
|
||||||
|
|
||||||
|
// 统一的标题截断样式
|
||||||
|
const titleTruncateStyle = {
|
||||||
|
minWidth: 0,
|
||||||
|
maxWidth: "100%",
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
display: "block"
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -52,7 +62,13 @@ export const EnhancedCard = ({
|
|||||||
borderColor: "divider",
|
borderColor: "divider",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
<Box sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
minWidth: 0,
|
||||||
|
flex: 1,
|
||||||
|
overflow: "hidden"
|
||||||
|
}}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -62,21 +78,32 @@ export const EnhancedCard = ({
|
|||||||
width: 38,
|
width: 38,
|
||||||
height: 38,
|
height: 38,
|
||||||
mr: 1.5,
|
mr: 1.5,
|
||||||
|
flexShrink: 0,
|
||||||
backgroundColor: alpha(theme.palette[iconColor].main, 0.12),
|
backgroundColor: alpha(theme.palette[iconColor].main, 0.12),
|
||||||
color: theme.palette[iconColor].main,
|
color: theme.palette[iconColor].main,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
</Box>
|
</Box>
|
||||||
{typeof title === "string" ? (
|
<Box sx={{ minWidth: 0, flex: 1 }}>
|
||||||
<Typography variant="h6" fontWeight="medium" fontSize={18}>
|
{typeof title === "string" ? (
|
||||||
{title}
|
<Typography
|
||||||
</Typography>
|
variant="h6"
|
||||||
) : (
|
fontWeight="medium"
|
||||||
title
|
fontSize={18}
|
||||||
)}
|
sx={titleTruncateStyle}
|
||||||
|
title={title}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Box sx={titleTruncateStyle}>
|
||||||
|
{title}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{action}
|
{action && <Box sx={{ ml: 2, flexShrink: 0 }}>{action}</Box>}
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -35,19 +35,10 @@ const round = keyframes`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
// 辅助函数解析URL和过期时间
|
// 辅助函数解析URL和过期时间
|
||||||
const parseUrl = (url?: string, maxLength: number = 25) => {
|
const parseUrl = (url?: string) => {
|
||||||
if (!url) return "-";
|
if (!url) return "-";
|
||||||
let parsedUrl = "";
|
if (url.startsWith("http")) return new URL(url).host;
|
||||||
if (url.startsWith("http")) {
|
return "local";
|
||||||
parsedUrl = new URL(url).host;
|
|
||||||
} else {
|
|
||||||
parsedUrl = "local";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsedUrl.length > maxLength) {
|
|
||||||
return parsedUrl.substring(0, maxLength - 3) + "...";
|
|
||||||
}
|
|
||||||
return parsedUrl;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseExpire = (expire?: number) => {
|
const parseExpire = (expire?: number) => {
|
||||||
@ -81,6 +72,14 @@ export interface HomeProfileCardProps {
|
|||||||
onProfileUpdated?: () => void;
|
onProfileUpdated?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加一个通用的截断样式
|
||||||
|
const truncateStyle = {
|
||||||
|
maxWidth: "calc(100% - 28px)",
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
whiteSpace: "nowrap"
|
||||||
|
};
|
||||||
|
|
||||||
// 提取独立组件减少主组件复杂度
|
// 提取独立组件减少主组件复杂度
|
||||||
const ProfileDetails = ({ current, onUpdateProfile, updating }: {
|
const ProfileDetails = ({ current, onUpdateProfile, updating }: {
|
||||||
current: ProfileItem;
|
current: ProfileItem;
|
||||||
@ -109,31 +108,55 @@ const ProfileDetails = ({ current, onUpdateProfile, updating }: {
|
|||||||
{current.url && (
|
{current.url && (
|
||||||
<Stack direction="row" alignItems="center" spacing={1}>
|
<Stack direction="row" alignItems="center" spacing={1}>
|
||||||
<DnsOutlined fontSize="small" color="action" />
|
<DnsOutlined fontSize="small" color="action" />
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" color="text.secondary" noWrap sx={{ display: "flex", alignItems: "center" }}>
|
||||||
{t("From")}:{" "}
|
<span style={{ flexShrink: 0 }}>{t("From")}: </span>
|
||||||
{current.home ? (
|
{current.home ? (
|
||||||
<Link
|
<Link
|
||||||
component="button"
|
component="button"
|
||||||
fontWeight="medium"
|
fontWeight="medium"
|
||||||
onClick={() => current.home && openWebUrl(current.home)}
|
onClick={() => current.home && openWebUrl(current.home)}
|
||||||
sx={{
|
sx={{
|
||||||
display: "inline-flex",
|
display: "inline-flex",
|
||||||
alignItems: "center"
|
alignItems: "center",
|
||||||
|
minWidth: 0,
|
||||||
|
maxWidth: "calc(100% - 40px)",
|
||||||
|
ml: 0.5
|
||||||
}}
|
}}
|
||||||
|
title={parseUrl(current.url)}
|
||||||
>
|
>
|
||||||
{parseUrl(current.url)}
|
<Typography
|
||||||
|
component="span"
|
||||||
|
sx={{
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
minWidth: 0,
|
||||||
|
flex: 1
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{parseUrl(current.url)}
|
||||||
|
</Typography>
|
||||||
<LaunchOutlined
|
<LaunchOutlined
|
||||||
fontSize="inherit"
|
fontSize="inherit"
|
||||||
sx={{ ml: 0.5, fontSize: "0.8rem", opacity: 0.7, flexShrink: 0 }}
|
sx={{ ml: 0.5, fontSize: "0.8rem", opacity: 0.7, flexShrink: 0 }}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<Box
|
<Typography
|
||||||
component="span"
|
component="span"
|
||||||
fontWeight="medium"
|
fontWeight="medium"
|
||||||
|
sx={{
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
minWidth: 0,
|
||||||
|
flex: 1,
|
||||||
|
ml: 0.5
|
||||||
|
}}
|
||||||
|
title={parseUrl(current.url)}
|
||||||
>
|
>
|
||||||
{parseUrl(current.url)}
|
{parseUrl(current.url)}
|
||||||
</Box>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
@ -285,16 +308,30 @@ export const HomeProfileCard = ({ current, onProfileUpdated }: HomeProfileCardPr
|
|||||||
fontSize={18}
|
fontSize={18}
|
||||||
onClick={() => current.home && openWebUrl(current.home)}
|
onClick={() => current.home && openWebUrl(current.home)}
|
||||||
sx={{
|
sx={{
|
||||||
display: "inline-flex",
|
|
||||||
alignItems: "center",
|
|
||||||
color: "inherit",
|
color: "inherit",
|
||||||
textDecoration: "none",
|
textDecoration: "none",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
minWidth: 0,
|
||||||
|
maxWidth: "100%",
|
||||||
|
"& > span": {
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
flex: 1
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
|
title={current.name}
|
||||||
>
|
>
|
||||||
{current.name}
|
<span>{current.name}</span>
|
||||||
<LaunchOutlined
|
<LaunchOutlined
|
||||||
fontSize="inherit"
|
fontSize="inherit"
|
||||||
sx={{ ml: 0.5, fontSize: "0.8rem", opacity: 0.7 }}
|
sx={{
|
||||||
|
ml: 0.5,
|
||||||
|
fontSize: "0.8rem",
|
||||||
|
opacity: 0.7,
|
||||||
|
flexShrink: 0
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
@ -24,6 +24,7 @@ import {
|
|||||||
getAutotemProxy,
|
getAutotemProxy,
|
||||||
getRunningMode,
|
getRunningMode,
|
||||||
} from "@/services/cmds";
|
} from "@/services/cmds";
|
||||||
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
|
|
||||||
const LOCAL_STORAGE_TAB_KEY = "clash-verge-proxy-active-tab";
|
const LOCAL_STORAGE_TAB_KEY = "clash-verge-proxy-active-tab";
|
||||||
|
|
||||||
@ -151,6 +152,10 @@ export const ProxyTunCard: FC = () => {
|
|||||||
// 获取代理状态信息
|
// 获取代理状态信息
|
||||||
const { data: sysproxy } = useSWR("getSystemProxy", getSystemProxy);
|
const { data: sysproxy } = useSWR("getSystemProxy", getSystemProxy);
|
||||||
const { data: runningMode } = useSWR("getRunningMode", getRunningMode);
|
const { data: runningMode } = useSWR("getRunningMode", getRunningMode);
|
||||||
|
const { verge } = useVerge();
|
||||||
|
|
||||||
|
// 从verge配置中获取开关状态
|
||||||
|
const { enable_system_proxy, enable_tun_mode } = verge ?? {};
|
||||||
|
|
||||||
// 是否以sidecar模式运行
|
// 是否以sidecar模式运行
|
||||||
const isSidecarMode = runningMode === "sidecar";
|
const isSidecarMode = runningMode === "sidecar";
|
||||||
@ -170,7 +175,7 @@ export const ProxyTunCard: FC = () => {
|
|||||||
const tabDescription = useMemo(() => {
|
const tabDescription = useMemo(() => {
|
||||||
if (activeTab === "system") {
|
if (activeTab === "system") {
|
||||||
return {
|
return {
|
||||||
text: sysproxy?.enable
|
text: enable_system_proxy
|
||||||
? t("System Proxy Enabled")
|
? t("System Proxy Enabled")
|
||||||
: t("System Proxy Disabled"),
|
: t("System Proxy Disabled"),
|
||||||
tooltip: t("System Proxy Info")
|
tooltip: t("System Proxy Info")
|
||||||
@ -179,11 +184,13 @@ export const ProxyTunCard: FC = () => {
|
|||||||
return {
|
return {
|
||||||
text: isSidecarMode
|
text: isSidecarMode
|
||||||
? t("TUN Mode Service Required")
|
? t("TUN Mode Service Required")
|
||||||
: t("TUN Mode Intercept Info"),
|
: enable_tun_mode
|
||||||
|
? t("TUN Mode Enabled")
|
||||||
|
: t("TUN Mode Disabled"),
|
||||||
tooltip: t("Tun Mode Info")
|
tooltip: t("Tun Mode Info")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [activeTab, sysproxy?.enable, isSidecarMode, t]);
|
}, [activeTab, enable_system_proxy, enable_tun_mode, isSidecarMode, t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
<Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||||
@ -203,13 +210,14 @@ export const ProxyTunCard: FC = () => {
|
|||||||
onClick={() => handleTabChange("system")}
|
onClick={() => handleTabChange("system")}
|
||||||
icon={ComputerRounded}
|
icon={ComputerRounded}
|
||||||
label={t("System Proxy")}
|
label={t("System Proxy")}
|
||||||
hasIndicator={sysproxy?.enable}
|
hasIndicator={enable_system_proxy}
|
||||||
/>
|
/>
|
||||||
<TabButton
|
<TabButton
|
||||||
isActive={activeTab === "tun"}
|
isActive={activeTab === "tun"}
|
||||||
onClick={() => handleTabChange("tun")}
|
onClick={() => handleTabChange("tun")}
|
||||||
icon={TroubleshootRounded}
|
icon={TroubleshootRounded}
|
||||||
label={t("Tun Mode")}
|
label={t("Tun Mode")}
|
||||||
|
hasIndicator={enable_tun_mode && !isSidecarMode}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
|
@ -120,17 +120,7 @@ const ProxyControlSwitches = ({ label, onError }: ProxySwitchProps) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||||
{proxy_auto_config ? (
|
{enable_system_proxy ? (
|
||||||
autoproxy?.enable ? (
|
|
||||||
<PlayCircleOutlineRounded
|
|
||||||
sx={{ color: "success.main", mr: 1.5, fontSize: 28 }}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<PauseCircleOutlineRounded
|
|
||||||
sx={{ color: "text.disabled", mr: 1.5, fontSize: 28 }}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
) : sysproxy?.enable ? (
|
|
||||||
<PlayCircleOutlineRounded
|
<PlayCircleOutlineRounded
|
||||||
sx={{ color: "success.main", mr: 1.5, fontSize: 28 }}
|
sx={{ color: "success.main", mr: 1.5, fontSize: 28 }}
|
||||||
/>
|
/>
|
||||||
|
@ -570,7 +570,6 @@
|
|||||||
"No": "No",
|
"No": "No",
|
||||||
"Failed": "Failed",
|
"Failed": "Failed",
|
||||||
"Completed": "Completed",
|
"Completed": "Completed",
|
||||||
"Bahamut Anime": "Bahamut Anime",
|
|
||||||
"Disallowed ISP": "Disallowed ISP",
|
"Disallowed ISP": "Disallowed ISP",
|
||||||
"Originals Only": "Originals Only",
|
"Originals Only": "Originals Only",
|
||||||
"No (IP Banned By Disney+)": "No (IP Banned By Disney+)",
|
"No (IP Banned By Disney+)": "No (IP Banned By Disney+)",
|
||||||
|
@ -570,7 +570,6 @@
|
|||||||
"No": "不支持",
|
"No": "不支持",
|
||||||
"Failed": "测试失败",
|
"Failed": "测试失败",
|
||||||
"Completed": "检测完成",
|
"Completed": "检测完成",
|
||||||
"Bahamut Anime": "动画疯",
|
|
||||||
"Disallowed ISP": "不允许的 ISP",
|
"Disallowed ISP": "不允许的 ISP",
|
||||||
"Originals Only": "仅限原创",
|
"Originals Only": "仅限原创",
|
||||||
"No (IP Banned By Disney+)": "不支持(IP被Disney+禁止)",
|
"No (IP Banned By Disney+)": "不支持(IP被Disney+禁止)",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user