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:
wonfen 2025-03-21 05:23:45 +08:00
parent a28887be8e
commit f739836891
7 changed files with 130 additions and 68 deletions

View File

@ -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 连接。

View File

@ -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={{

View File

@ -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>
); );

View File

@ -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>

View File

@ -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 }}
/> />

View File

@ -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+)",

View File

@ -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+禁止)",