Compare commits

..

12 Commits

Author SHA1 Message Date
ezequielnick
0a969e9e38 fix: incompatible CSS style 2025-08-04 23:04:06 +08:00
ezequielnick
f62b8cff67 1.8.1 Released 2025-08-04 18:49:13 +08:00
ezequielnick
1b7c2ee01e refactor: rewrite DNS control module with override logic 2025-08-04 18:49:13 +08:00
ezequielnick
866cdb4661 feat: optimize and improve subscription switching 2025-08-04 18:49:13 +08:00
ezequielnick
2e4090460d chore: Upgrade tailwindcss 2025-08-04 18:49:13 +08:00
ezequielnick
f2ed0caced chore: update deps 2025-08-04 18:49:13 +08:00
ezequielnick
6993015ce1 chore: remove default policy-priority 2025-08-04 18:49:13 +08:00
ezequielnick
e3e373d579 fix: smart gruop "MATCH" 2025-08-04 18:49:13 +08:00
ezequielnick
be7f8677b0 fix: smart core prepare 2025-08-04 18:49:13 +08:00
ezequielnick
dab2ead5fd fix: smart core version fetch logic 2025-08-04 18:49:13 +08:00
ezequielnick
1260b8fb4e 1.8.0 Smart Core Test Released 2025-08-04 18:49:13 +08:00
ezequielnick
46aa654ee3 feat: add smart core & one-click smart 2025-08-04 18:49:07 +08:00
14 changed files with 47 additions and 68 deletions

View File

@ -8,5 +8,7 @@
<true/> <true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key> <key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/> <true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@ -1,28 +1,21 @@
## 1.8.1
### 新功能 (Feat)
- 重构 DNS 控制模块改为“覆写”逻辑当开关打开后使用DNS 设置中的配置覆盖订阅原始配置,关闭开关恢复订阅原始配置
### 性能提升(Perf)
- 更新依赖,提升页面响应性
- 优化订阅切换逻辑,大幅提升切换速度和稳定性
### 修复 (Fix)
- “使用自动 Smart 规则覆写”没有覆盖兜底的 MATCH 规则
- 移除默认的 Smart "policy-priority" 规则
-
## 1.8.0 ## 1.8.0
### 新功能 (Feat) ### 新功能 (Feat)
**重大更新:本次更新增加了 Smart Core可以根据用户使用习惯和节点质量自动选择符合您的最优节点。并内置了“一键开启”适合不想折腾自定义规则的用户 **重大更新:本次更新增加了 Smart Core可以根据用户使用习惯和节点质量自动选择符合您的最优节点。并内置了“一键开启”适合不想折腾自定义规则的用户
“一键开启”内置 Smart规则的功能在“内核设置”下的“使用自动 Smart 规则覆写”,原理:当开关开启后,自动载入覆写脚本,新增 Smart Group并替换当前配置文件下的默认出站规则为"Smart Group",您的所有代理流量都将从此分组下的节点流出。如果使用“全局模式”请选择名称为"Smart Group"的节点,以使用该功能。** “一键开启”内置 Smart规则的功能在“内核设置”下的“使用自动 Smart 规则覆写”,原理:当开关开启后,自动载入覆写脚本,新增 Smart Group并替换当前配置文件下的默认出站规则为"Smart Group",您的所有代理流量都将从此分组下的节点流出。如果使用“全局模式”请选择名称为"Smart Group"的节点,以使用该功能。**
注意:本功能还在测试中,如遇到问题请发 issue 反馈 注意:本功能还在测试中,如遇到问题请发 issue 反馈
## 1.7.7
### 新功能 (Feat)
- Mihomo 内核升级 v1.19.12
- 新增 Webdav 最大备数设置和清理逻辑
### 修复 (Fix)
- 修复 MacOS 下无法启动的问题(重置工作目录权限)
- 尝试修复不同版本 MacOS 下安装软件时候的报错Input/output error
- 部分遗漏的多国语言翻译
## 1.7.6
**此版本修复了 1.7.5 中的几个严重 bug推荐所有人更新**
### 修复 (Fix)
- 修复了内核1.19.8更新后gist同步失效的问题(#780)
- 部分遗漏的多国语言翻译
- MacOS 下启动Error: EACCES: permission denied
- MacOS 系统代理 bypass 不生效
- MacOS 系统代理开启时 500 报错

View File

@ -39,12 +39,14 @@ mac:
target: target:
- pkg - pkg
entitlementsInherit: build/entitlements.mac.plist entitlementsInherit: build/entitlements.mac.plist
hardenedRuntime: true
gatekeeperAssess: false
extendInfo: extendInfo:
- NSCameraUsageDescription: Application requests access to the device's camera. - NSCameraUsageDescription: Application requests access to the device's camera.
- NSMicrophoneUsageDescription: Application requests access to the device's microphone. - NSMicrophoneUsageDescription: Application requests access to the device's microphone.
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
notarize: true notarize: false
artifactName: ${name}-macos-${version}-${arch}.${ext} artifactName: ${name}-macos-${version}-${arch}.${ext}
pkg: pkg:
allowAnywhere: false allowAnywhere: false

View File

@ -1,6 +1,6 @@
{ {
"name": "mihomo-party", "name": "mihomo-party",
"version": "1.8.0", "version": "1.8.1",
"description": "Mihomo Party", "description": "Mihomo Party",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "mihomo-party-org", "author": "mihomo-party-org",

View File

@ -4,12 +4,11 @@ import { addOverrideItem, removeOverrideItem, getOverrideItem } from './override
const SMART_OVERRIDE_ID = 'smart-core-override' const SMART_OVERRIDE_ID = 'smart-core-override'
/** /**
* Smart * Smart
*/ */
function generateSmartOverrideTemplate(useLightGBM: boolean, collectData: boolean, strategy: string): string { function generateSmartOverrideTemplate(useLightGBM: boolean, collectData: boolean, strategy: string): string {
return ` return `
// Smart 内核覆写配置 // 配置会在启用 Smart 内核时自动应用
// 此配置会在启用 Smart 内核时自动应用
function main(config) { function main(config) {
try { try {
@ -30,7 +29,7 @@ function main(config) {
config['proxy-groups'] = [] config['proxy-groups'] = []
} }
// 查找现有的 Smart 代理组并更新配置 // 查找现有的 Smart 代理组并更新
let smartGroupExists = false let smartGroupExists = false
for (let i = 0; i < config['proxy-groups'].length; i++) { for (let i = 0; i < config['proxy-groups'].length; i++) {
const group = config['proxy-groups'][i] const group = config['proxy-groups'][i]
@ -38,11 +37,9 @@ function main(config) {
smartGroupExists = true smartGroupExists = true
console.log('[Smart Override] Found existing smart group:', group.name) console.log('[Smart Override] Found existing smart group:', group.name)
// 确保 Smart 组有正确的配置
if (!group['policy-priority']) { if (!group['policy-priority']) {
group['policy-priority'] = '' // policy-priority: <1 means lower priority, >1 means higher priority, the default is 1, pattern support regex and string group['policy-priority'] = '' // policy-priority: <1 means lower priority, >1 means higher priority, the default is 1, pattern support regex and string
} }
// 始终使用用户配置的值
group.uselightgbm = ${useLightGBM} group.uselightgbm = ${useLightGBM}
group.collectdata = ${collectData} group.collectdata = ${collectData}
group.strategy = '${strategy}' group.strategy = '${strategy}'
@ -50,11 +47,11 @@ function main(config) {
} }
} }
// 如果没有 Smart 组且有可用代理,创建一个示例组 // 如果没有 Smart 组且有可用代理,创建示例组
if (!smartGroupExists && config.proxies && Array.isArray(config.proxies) && config.proxies.length > 0) { if (!smartGroupExists && config.proxies && Array.isArray(config.proxies) && config.proxies.length > 0) {
console.log('[Smart Override] Creating new smart group with', config.proxies.length, 'proxies') console.log('[Smart Override] Creating new smart group with', config.proxies.length, 'proxies')
// 获取所有代理的名称确保代理对象有name属性 // 获取所有代理的名称
const proxyNames = config.proxies const proxyNames = config.proxies
.filter(proxy => proxy && typeof proxy === 'object' && proxy.name) .filter(proxy => proxy && typeof proxy === 'object' && proxy.name)
.map(proxy => proxy.name) .map(proxy => proxy.name)
@ -69,7 +66,6 @@ function main(config) {
strategy: '${strategy}', strategy: '${strategy}',
proxies: proxyNames proxies: proxyNames
} }
// 将 Smart 分组插入到第一个位置
config['proxy-groups'].unshift(smartGroup) config['proxy-groups'].unshift(smartGroup)
console.log('[Smart Override] Created smart group at first position with proxies:', proxyNames) console.log('[Smart Override] Created smart group at first position with proxies:', proxyNames)
} else { } else {
@ -79,11 +75,11 @@ function main(config) {
console.log('[Smart Override] No proxies available, skipping smart group creation') console.log('[Smart Override] No proxies available, skipping smart group creation')
} }
// 处理规则替换:将非 DIRECT 规则替换为 Smart Group // 处理规则替换
if (config.rules && Array.isArray(config.rules)) { if (config.rules && Array.isArray(config.rules)) {
console.log('[Smart Override] Processing rules, original count:', config.rules.length) console.log('[Smart Override] Processing rules, original count:', config.rules.length)
// 收集所有代理组名称,用于智能识别 // 收集所有代理组名称
const proxyGroupNames = new Set() const proxyGroupNames = new Set()
if (config['proxy-groups'] && Array.isArray(config['proxy-groups'])) { if (config['proxy-groups'] && Array.isArray(config['proxy-groups'])) {
config['proxy-groups'].forEach(group => { config['proxy-groups'].forEach(group => {
@ -102,7 +98,7 @@ function main(config) {
'COMPATIBLE' 'COMPATIBLE'
]) ])
// 添加常见的规则参数,这些不应该替换 // 添加常见的规则参数,不应该替换
const ruleParams = new Set(['no-resolve', 'force-remote-dns', 'prefer-ipv6']) const ruleParams = new Set(['no-resolve', 'force-remote-dns', 'prefer-ipv6'])
console.log('[Smart Override] Found', proxyGroupNames.size, 'proxy groups:', Array.from(proxyGroupNames)) console.log('[Smart Override] Found', proxyGroupNames.size, 'proxy groups:', Array.from(proxyGroupNames))
@ -123,12 +119,12 @@ function main(config) {
let targetIndex = -1 let targetIndex = -1
let targetValue = '' let targetValue = ''
// 对于 MATCH 规则目标在第2个位置索引1 // 处理 MATCH 规则
if (parts[0] === 'MATCH' && parts.length === 2) { if (parts[0] === 'MATCH' && parts.length === 2) {
targetIndex = 1 targetIndex = 1
targetValue = parts[1] targetValue = parts[1]
} else if (parts.length >= 3) { } else if (parts.length >= 3) {
// 对于其他规则从第3个参数开始查找索引2找到第一个不是规则参数的值 // 处理其他规则
for (let i = 2; i < parts.length; i++) { for (let i = 2; i < parts.length; i++) {
const part = parts[i] const part = parts[i]
if (!ruleParams.has(part)) { if (!ruleParams.has(part)) {
@ -140,7 +136,7 @@ function main(config) {
} }
if (targetIndex !== -1 && targetValue) { if (targetIndex !== -1 && targetValue) {
// 检查是否应该替换这个目标 // 检查是否应该替换
const shouldReplace = !builtinTargets.has(targetValue) && const shouldReplace = !builtinTargets.has(targetValue) &&
(proxyGroupNames.has(targetValue) || (proxyGroupNames.has(targetValue) ||
!ruleParams.has(targetValue)) !ruleParams.has(targetValue))
@ -154,7 +150,7 @@ function main(config) {
} }
} }
} else if (typeof rule === 'object' && rule !== null) { } else if (typeof rule === 'object' && rule !== null) {
// 处理对象格式的规则 // 处理对象格式
let targetField = '' let targetField = ''
let targetValue = '' let targetValue = ''
@ -268,10 +264,8 @@ export async function manageSmartOverride(): Promise<void> {
const { enableSmartCore = true, enableSmartOverride = true, core } = await getAppConfig() const { enableSmartCore = true, enableSmartOverride = true, core } = await getAppConfig()
if (enableSmartCore && enableSmartOverride && core === 'mihomo-smart') { if (enableSmartCore && enableSmartOverride && core === 'mihomo-smart') {
// 启用 Smart 内核且启用覆写时创建或更新覆写配置
await createSmartOverride() await createSmartOverride()
} else { } else {
// 其他情况删除覆写配置
await removeSmartOverride() await removeSmartOverride()
} }
} }

View File

@ -266,7 +266,6 @@ async function checkProfile(): Promise<void> {
console.log('Profile check stdout:', stdout) console.log('Profile check stdout:', stdout)
console.log('Profile check stderr:', stderr) console.log('Profile check stderr:', stderr)
// 提取错误信息
const errorLines = stdout const errorLines = stdout
.split('\n') .split('\n')
.filter((line) => line.includes('level=error') || line.includes('error')) .filter((line) => line.includes('level=error') || line.includes('error'))
@ -278,7 +277,6 @@ async function checkProfile(): Promise<void> {
}) })
.filter(line => line.length > 0) .filter(line => line.length > 0)
// 如果没有找到错误行,显示完整输出
if (errorLines.length === 0) { if (errorLines.length === 0) {
const allLines = stdout.split('\n').filter(line => line.trim().length > 0) const allLines = stdout.split('\n').filter(line => line.trim().length > 0)
throw new Error(`${i18next.t('mihomo.error.profileCheckFailed')}:\n${allLines.join('\n')}`) throw new Error(`${i18next.t('mihomo.error.profileCheckFailed')}:\n${allLines.join('\n')}`)

View File

@ -168,7 +168,7 @@ export const mihomoUpgrade = async (): Promise<void> => {
return await instance.post('/upgrade') return await instance.post('/upgrade')
} }
// Smart 内核特有的 API // Smart 内核 API
export const mihomoSmartGroupWeights = async (groupName: string): Promise<Record<string, number>> => { export const mihomoSmartGroupWeights = async (groupName: string): Promise<Record<string, number>> => {
const instance = await getAxios() const instance = await getAxios()
return await instance.get(`/group/${encodeURIComponent(groupName)}/weights`) return await instance.get(`/group/${encodeURIComponent(groupName)}/weights`)

View File

@ -69,7 +69,7 @@ export function mihomoCoreDir(): string {
export function mihomoCorePath(core: string): string { export function mihomoCorePath(core: string): string {
const isWin = process.platform === 'win32' const isWin = process.platform === 'win32'
// 处理 Smart 内核的特殊情况 // 处理 Smart 内核
if (core === 'mihomo-smart') { if (core === 'mihomo-smart') {
return path.join(mihomoCoreDir(), `mihomo-smart${isWin ? '.exe' : ''}`) return path.join(mihomoCoreDir(), `mihomo-smart${isWin ? '.exe' : ''}`)
} }

View File

@ -143,7 +143,7 @@ export function registerIpcMainHandlers(): void {
ipcErrorWrapper(mihomoGroupDelay)(group, url) ipcErrorWrapper(mihomoGroupDelay)(group, url)
) )
ipcMain.handle('patchMihomoConfig', (_e, patch) => ipcErrorWrapper(patchMihomoConfig)(patch)) ipcMain.handle('patchMihomoConfig', (_e, patch) => ipcErrorWrapper(patchMihomoConfig)(patch))
// Smart 内核特有的 API // Smart 内核 API
ipcMain.handle('mihomoSmartGroupWeights', (_e, groupName) => ipcMain.handle('mihomoSmartGroupWeights', (_e, groupName) =>
ipcErrorWrapper(mihomoSmartGroupWeights)(groupName) ipcErrorWrapper(mihomoSmartGroupWeights)(groupName)
) )

View File

@ -39,7 +39,7 @@ const ConnectionItem: React.FC<Props> = (props) => {
<Chip <Chip
color={`${info.isActive ? 'primary' : 'danger'}`} color={`${info.isActive ? 'primary' : 'danger'}`}
size="sm" size="sm"
radius="xs" radius="sm"
variant="dot" variant="dot"
> >
{info.metadata.type}({info.metadata.network.toUpperCase()}) {info.metadata.type}({info.metadata.network.toUpperCase()})
@ -65,16 +65,16 @@ const ConnectionItem: React.FC<Props> = (props) => {
<Chip <Chip
className="flag-emoji text-ellipsis whitespace-nowrap overflow-hidden" className="flag-emoji text-ellipsis whitespace-nowrap overflow-hidden"
size="sm" size="sm"
radius="xs" radius="sm"
variant="bordered" variant="bordered"
> >
{info.chains[0]} {info.chains[0]}
</Chip> </Chip>
<Chip size="sm" radius="xs" variant="bordered"> <Chip size="sm" radius="sm" variant="bordered">
{calcTraffic(info.upload)} {calcTraffic(info.download)} {calcTraffic(info.upload)} {calcTraffic(info.download)}
</Chip> </Chip>
{info.uploadSpeed !== 0 || info.downloadSpeed !== 0 ? ( {info.uploadSpeed !== 0 || info.downloadSpeed !== 0 ? (
<Chip color="primary" size="sm" radius="xs" variant="bordered"> <Chip color="primary" size="sm" radius="sm" variant="bordered">
{calcTraffic(info.uploadSpeed || 0)}/s {calcTraffic(info.downloadSpeed || 0)} {calcTraffic(info.uploadSpeed || 0)}/s {calcTraffic(info.downloadSpeed || 0)}
/s /s
</Chip> </Chip>

View File

@ -68,7 +68,7 @@ const ProxyItem: React.FC<Props> = (props) => {
? 'bg-primary/30 border-r-2 border-r-primary border-l-2 border-l-primary' ? 'bg-primary/30 border-r-2 border-r-primary border-l-2 border-l-primary'
: 'bg-content2' : 'bg-content2'
}`} }`}
radius="xs" radius="sm"
> >
<CardBody className="p-1"> <CardBody className="p-1">
{proxyDisplayMode === 'full' ? ( {proxyDisplayMode === 'full' ? (

View File

@ -89,11 +89,9 @@ const Mihomo: React.FC = () => {
await patchAppConfig({ [key]: value }) await patchAppConfig({ [key]: value })
await restartCore() await restartCore()
} catch (e) { } catch (e) {
// 显示详细的错误信息
const errorMessage = e instanceof Error ? e.message : String(e) const errorMessage = e instanceof Error ? e.message : String(e)
console.error('Core restart failed:', errorMessage) console.error('Core restart failed:', errorMessage)
// 如果是配置检查失败,显示详细错误
if (errorMessage.includes('配置检查失败') || errorMessage.includes('Profile Check Failed')) { if (errorMessage.includes('配置检查失败') || errorMessage.includes('Profile Check Failed')) {
await showDetailedError(t('mihomo.error.profileCheckFailed'), errorMessage) await showDetailedError(t('mihomo.error.profileCheckFailed'), errorMessage)
} else { } else {
@ -108,7 +106,7 @@ const Mihomo: React.FC = () => {
<> <>
{lanOpen && <InterfaceModal onClose={() => setLanOpen(false)} />} {lanOpen && <InterfaceModal onClose={() => setLanOpen(false)} />}
<BasePage title={t('mihomo.title')}> <BasePage title={t('mihomo.title')}>
{/* Smart 内核专用设置区域 */} {/* Smart 内核设置 */}
<SettingCard> <SettingCard>
<div className={`rounded-md border p-2 transition-all duration-200 ${ <div className={`rounded-md border p-2 transition-all duration-200 ${
enableSmartCore enableSmartCore
@ -126,17 +124,15 @@ const Mihomo: React.FC = () => {
onValueChange={async (v) => { onValueChange={async (v) => {
await patchAppConfig({ enableSmartCore: v }) await patchAppConfig({ enableSmartCore: v })
if (v && core !== 'mihomo-smart') { if (v && core !== 'mihomo-smart') {
// 启用 Smart 内核时,自动切换到 Smart 内核
await handleConfigChangeWithRestart('core', 'mihomo-smart') await handleConfigChangeWithRestart('core', 'mihomo-smart')
} else if (!v && core === 'mihomo-smart') { } else if (!v && core === 'mihomo-smart') {
// 禁用 Smart 内核时,切换回普通内核
await handleConfigChangeWithRestart('core', 'mihomo') await handleConfigChangeWithRestart('core', 'mihomo')
} }
}} }}
/> />
</SettingItem> </SettingItem>
{/* Smart 覆写开关 - 只在启用 Smart 内核时显示 */} {/* Smart 覆写开关 */}
{enableSmartCore && ( {enableSmartCore && (
<SettingItem <SettingItem
title={ title={
@ -159,7 +155,6 @@ const Mihomo: React.FC = () => {
color="primary" color="primary"
onValueChange={async (v) => { onValueChange={async (v) => {
await patchAppConfig({ enableSmartOverride: v }) await patchAppConfig({ enableSmartOverride: v })
// 重新生成覆写配置
await restartCore() await restartCore()
}} }}
/> />
@ -219,10 +214,8 @@ const Mihomo: React.FC = () => {
}} }}
> >
{enableSmartCore ? ( {enableSmartCore ? (
// 启用 Smart 内核时,只显示 Smart 版本
<SelectItem key="mihomo-smart">{t(CoreMap['mihomo-smart'])}</SelectItem> <SelectItem key="mihomo-smart">{t(CoreMap['mihomo-smart'])}</SelectItem>
) : ( ) : (
// 未启用 Smart 内核时,显示普通版本
<> <>
<SelectItem key="mihomo">{t(CoreMap['mihomo'])}</SelectItem> <SelectItem key="mihomo">{t(CoreMap['mihomo'])}</SelectItem>
<SelectItem key="mihomo-alpha">{t(CoreMap['mihomo-alpha'])}</SelectItem> <SelectItem key="mihomo-alpha">{t(CoreMap['mihomo-alpha'])}</SelectItem>
@ -231,7 +224,7 @@ const Mihomo: React.FC = () => {
</Select> </Select>
</SettingItem> </SettingItem>
{/* Smart 内核专用配置项 */} {/* Smart 内核配置项 */}
{enableSmartCore && core === 'mihomo-smart' && ( {enableSmartCore && core === 'mihomo-smart' && (
<> <>
<SettingItem <SettingItem
@ -244,7 +237,6 @@ const Mihomo: React.FC = () => {
isSelected={smartCoreUseLightGBM} isSelected={smartCoreUseLightGBM}
onValueChange={async (v) => { onValueChange={async (v) => {
await patchAppConfig({ smartCoreUseLightGBM: v }) await patchAppConfig({ smartCoreUseLightGBM: v })
// 重新生成覆写配置
await restartCore() await restartCore()
}} }}
/> />
@ -260,7 +252,6 @@ const Mihomo: React.FC = () => {
isSelected={smartCoreCollectData} isSelected={smartCoreCollectData}
onValueChange={async (v) => { onValueChange={async (v) => {
await patchAppConfig({ smartCoreCollectData: v }) await patchAppConfig({ smartCoreCollectData: v })
// 重新生成覆写配置
await restartCore() await restartCore()
}} }}
/> />
@ -279,7 +270,6 @@ const Mihomo: React.FC = () => {
onSelectionChange={async (v) => { onSelectionChange={async (v) => {
const strategy = v.currentKey as 'sticky-sessions' | 'round-robin' const strategy = v.currentKey as 'sticky-sessions' | 'round-robin'
await patchAppConfig({ smartCoreStrategy: strategy }) await patchAppConfig({ smartCoreStrategy: strategy })
// 重新生成覆写配置
await restartCore() await restartCore()
}} }}
> >

View File

@ -85,7 +85,7 @@ const Profiles: React.FC = () => {
<div> <div>
{sub.tag?.map((tag) => { {sub.tag?.map((tag) => {
return ( return (
<Chip key={tag} size="sm" className="ml-1" radius="xs"> <Chip key={tag} size="sm" className="ml-1" radius="sm">
{tag} {tag}
</Chip> </Chip>
) )
@ -108,7 +108,7 @@ const Profiles: React.FC = () => {
<div> <div>
{sub.tag?.map((tag) => { {sub.tag?.map((tag) => {
return ( return (
<Chip key={tag} size="sm" className="ml-1" radius="xs"> <Chip key={tag} size="sm" className="ml-1" radius="sm">
{tag} {tag}
</Chip> </Chip>
) )

View File

@ -324,7 +324,7 @@ const Proxies: React.FC = () => {
<Avatar <Avatar
className="bg-transparent mr-2" className="bg-transparent mr-2"
size="sm" size="sm"
radius="xs" radius="sm"
src={ src={
groups[index].icon.startsWith('<svg') groups[index].icon.startsWith('<svg')
? `data:image/svg+xml;utf8,${groups[index].icon}` ? `data:image/svg+xml;utf8,${groups[index].icon}`