mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-06 17:12:07 +08:00
fix(ui): use utf-8 encoding for inline SVG icons to support non-latin characters (#7892)
* fix(ui): use utf-8 encoding for inline SVG icons to support non-latin characters * chore(test-item): re-add data-sortable-handle to TestItem box * docs: update changelog
This commit is contained in:
+20
-18
@@ -2,24 +2,26 @@
|
||||
|
||||
### 🐞 修复问题
|
||||
|
||||
修复 macOS 多显示器切换时托盘速率样式不同步
|
||||
修复 WebSocket 在 onConnected 初始化完成前被保存的问题,避免初始化失败后留下无监听且无法重试的无效连接。
|
||||
修复 Mihomo API 变更后 inline proxy-provider 被错误过滤的问题,解决节点显示 Unknown 及延迟测试失败的问题
|
||||
修复 macOS 开启「访问系统范围设置需要管理员密码」时无法设置系统代理的问题
|
||||
修复从未开启过系统代理的用户,每次内核启动都收到报错的问题
|
||||
修复受限机器上「安装服务 → 自动切换到服务模式」整条流程无法走通的问题
|
||||
修复 Windows 卸载后保留 TUN 配置、重新安装时因系统服务缺失而无法启动内核的问题,启动时会在当前会话回退到 Sidecar,并保留原 TUN 偏好
|
||||
修复系统代理开关切换失败后,界面停留在错误状态的问题
|
||||
修复托盘与热键切换系统代理失败时没有任何提示的问题
|
||||
修复代理守卫反复失败后无限重试且不告知用户的问题
|
||||
修复错误提示只显示最外层信息、丢失具体原因的问题
|
||||
修复过长的错误提示撑满界面的问题(右键复制仍为完整内容)
|
||||
修复部分错误提示显示为 [object Object] 的问题
|
||||
修复 i18n 清理脚本因导入方式错误而无法运行的问题
|
||||
修复 Linux 桌面环境下主题切换时应用外观不跟随的问题
|
||||
修复 Gemini 测试正确性
|
||||
修复 macOS/Linux 用户模式下旧版服务遗留的 cache.db 属主为 root 且当前用户不可写,导致无法记住上次选择节点的问题
|
||||
修复代理组节点 `PASS-RULE` 被过滤丢失的问题
|
||||
- 修复 macOS 多显示器切换时托盘速率样式不同步
|
||||
- 修复 WebSocket 在 onConnected 初始化完成前被保存的问题,避免初始化失败后留下无监听且无法重试的无效连接。
|
||||
- 修复 Mihomo API 变更后 inline proxy-provider 被错误过滤的问题,解决节点显示 Unknown 及延迟测试失败的问题
|
||||
- 修复 macOS 开启「访问系统范围设置需要管理员密码」时无法设置系统代理的问题
|
||||
- 修复从未开启过系统代理的用户,每次内核启动都收到报错的问题
|
||||
- 修复受限机器上「安装服务 → 自动切换到服务模式」整条流程无法走通的问题
|
||||
- 修复 Windows 卸载后保留 TUN 配置、重新安装时因系统服务缺失而无法启动内核的问题,启动时会在当前会话回退到 Sidecar,并保留原 TUN 偏好
|
||||
- 修复系统代理开关切换失败后,界面停留在错误状态的问题
|
||||
- 修复托盘与热键切换系统代理失败时没有任何提示的问题
|
||||
- 修复代理守卫反复失败后无限重试且不告知用户的问题
|
||||
- 修复错误提示只显示最外层信息、丢失具体原因的问题
|
||||
- 修复过长的错误提示撑满界面的问题(右键复制仍为完整内容)
|
||||
- 修复部分错误提示显示为 [object Object] 的问题
|
||||
- 修复 i18n 清理脚本因导入方式错误而无法运行的问题
|
||||
- 修复 Linux 桌面环境下主题切换时应用外观不跟随的问题
|
||||
- 修复 Gemini 测试正确性
|
||||
- 修复 macOS/Linux 用户模式下旧版服务遗留的 cache.db 属主为 root 且当前用户不可写,导致无法记住上次选择节点的问题
|
||||
- 修复代理组节点 `PASS-RULE` 被过滤丢失的问题
|
||||
- 修复 Windows 主窗口打开时关机被「此程序正在阻止关机」卡住、无法退出的问题
|
||||
- 修复代理组与首页测试列表的内联 SVG 图标含中文等非 Latin-1 字符时无法显示、甚至导致页面报错的问题,改用 UTF-8 URL 编码渲染图标
|
||||
|
||||
<details>
|
||||
<summary><strong> ✨ 新增功能 </strong></summary>
|
||||
|
||||
@@ -43,35 +43,26 @@ export const GroupItem = (props: Props) => {
|
||||
>
|
||||
{group?.icon?.trim().startsWith('http') && (
|
||||
<img
|
||||
alt="group icon"
|
||||
alt={group.name}
|
||||
src={iconCachePath === '' ? group.icon : iconCachePath}
|
||||
width="32px"
|
||||
style={{
|
||||
marginRight: '12px',
|
||||
borderRadius: '6px',
|
||||
}}
|
||||
style={{ marginRight: '12px', borderRadius: '6px' }}
|
||||
/>
|
||||
)}
|
||||
{group?.icon?.trim().startsWith('data') && (
|
||||
<img
|
||||
alt="group icon"
|
||||
alt={group.name}
|
||||
src={group.icon}
|
||||
width="32px"
|
||||
style={{
|
||||
marginRight: '12px',
|
||||
borderRadius: '6px',
|
||||
}}
|
||||
style={{ marginRight: '12px', borderRadius: '6px' }}
|
||||
/>
|
||||
)}
|
||||
{group?.icon?.trim().startsWith('<svg') && (
|
||||
<img
|
||||
alt="group icon"
|
||||
src={`data:image/svg+xml;base64,${btoa(group.icon ?? '')}`}
|
||||
alt={group.name}
|
||||
src={`data:image/svg+xml;charset=utf-8,${encodeURIComponent(group.icon)}`}
|
||||
width="32px"
|
||||
style={{
|
||||
marginRight: '12px',
|
||||
borderRadius: '6px',
|
||||
}}
|
||||
style={{ marginRight: '12px', borderRadius: '6px' }}
|
||||
/>
|
||||
)}
|
||||
<ListItemText
|
||||
|
||||
@@ -112,7 +112,7 @@ export const ProxyRender = memo(function ProxyRender(props: RenderProps) {
|
||||
{enable_group_icon && group.icon?.trim().startsWith('http') && (
|
||||
<img
|
||||
src={iconCachePath === '' ? group.icon : iconCachePath}
|
||||
alt="group icon"
|
||||
alt={group.name}
|
||||
width="32px"
|
||||
style={{ marginRight: '12px', borderRadius: '6px' }}
|
||||
/>
|
||||
@@ -120,15 +120,15 @@ export const ProxyRender = memo(function ProxyRender(props: RenderProps) {
|
||||
{enable_group_icon && group.icon?.trim().startsWith('data') && (
|
||||
<img
|
||||
src={group.icon}
|
||||
alt="group icon"
|
||||
alt={group.name}
|
||||
width="32px"
|
||||
style={{ marginRight: '12px', borderRadius: '6px' }}
|
||||
/>
|
||||
)}
|
||||
{enable_group_icon && group.icon?.trim().startsWith('<svg') && (
|
||||
<img
|
||||
src={`data:image/svg+xml;base64,${btoa(group.icon)}`}
|
||||
alt="group icon"
|
||||
src={`data:image/svg+xml;charset=utf-8,${encodeURIComponent(group.icon)}`}
|
||||
alt={group.name}
|
||||
width="32px"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -72,16 +72,18 @@ export const TestItem = ({ itemData, onEdit, onDelete: removeTest }: Props) => {
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
|
||||
{icon.trim().startsWith('http') && (
|
||||
<img
|
||||
alt={name}
|
||||
src={iconCachePath === '' ? icon : iconCachePath}
|
||||
height="40px"
|
||||
/>
|
||||
)}
|
||||
{icon.trim().startsWith('data') && (
|
||||
<img src={icon} height="40px" />
|
||||
<img alt={name} src={icon} height="40px" />
|
||||
)}
|
||||
{icon.trim().startsWith('<svg') && (
|
||||
<img
|
||||
src={`data:image/svg+xml;base64,${btoa(icon)}`}
|
||||
alt={name}
|
||||
src={`data:image/svg+xml;charset=utf-8,${encodeURIComponent(icon)}`}
|
||||
height="40px"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -75,34 +75,17 @@ export const TestViewer = forwardRef<TestViewerRef, Props>(
|
||||
throw new Error(t('tests.modals.test.errors.urlRequired'))
|
||||
}
|
||||
|
||||
let newList
|
||||
let uid
|
||||
|
||||
if (form.icon && form.icon.startsWith('<svg')) {
|
||||
// 移除 icon 中的注释
|
||||
if (form.icon) {
|
||||
form.icon = form.icon.replace(/<!--[\s\S]*?-->/g, '')
|
||||
}
|
||||
const doc = new DOMParser().parseFromString(
|
||||
form.icon,
|
||||
'image/svg+xml',
|
||||
)
|
||||
if (doc.querySelector('parsererror')) {
|
||||
throw new Error(t('tests.modals.test.errors.invalidSvg'))
|
||||
}
|
||||
}
|
||||
|
||||
if (openType === 'new') {
|
||||
uid = nanoid()
|
||||
const uid = nanoid()
|
||||
const item = { ...form, uid }
|
||||
newList = [...testList, item]
|
||||
const newList = [...testList, item]
|
||||
await patchVerge({ test_list: newList })
|
||||
onChange(uid)
|
||||
} else {
|
||||
if (!form.uid) {
|
||||
throw new Error(t('tests.modals.test.errors.uidMissing'))
|
||||
}
|
||||
uid = form.uid
|
||||
const uid = form.uid
|
||||
|
||||
await patchTestList(uid, form)
|
||||
onChange(uid, form)
|
||||
|
||||
Reference in New Issue
Block a user