Compare commits
4 Commits
9681f77e20
...
a7de9b2588
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7de9b2588 | ||
|
|
f34cc976b4 | ||
|
|
814112f541 | ||
|
|
717239f56b |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 19 KiB |
BIN
build/icon.icns
BIN
build/icon.ico
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 76 KiB |
BIN
build/icon.png
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 229 KiB After Width: | Height: | Size: 204 KiB |
23
changelog.md
@ -1,8 +1,31 @@
|
||||
## 1.8.8
|
||||
|
||||
### 新功能 (Feat)
|
||||
- 增加内核版本选择
|
||||
- 记住日志页面的筛选关键字
|
||||
- Webdav增加Cron定时备份
|
||||
- 连接卡片纯数字显示样式
|
||||
- 支持修改窗口触发行为
|
||||
|
||||
### 修复 (Fix)
|
||||
- MacOS 首次启动时的 ENOENT: no such file or directory(config.yaml)
|
||||
- 自动更新获取老的文件名称
|
||||
- 修复 mihomo.yaml 文件缺失的问题
|
||||
- Smart 配置文件验证出错的问题
|
||||
- 开发环境的 electron 问题
|
||||
|
||||
### 优化 (Optimize)
|
||||
- 加快以管理员模式重启速度
|
||||
- 优化仅用户滚动滚轮时触发自动滚动
|
||||
- 改进俄语翻译
|
||||
- 使用重载替换不必要的重启
|
||||
|
||||
# 其他 (chore)
|
||||
- 更新依赖
|
||||
|
||||
### 样式调整 (Sytle)
|
||||
- 改进 logo 设计
|
||||
- 卡片尺寸
|
||||
|
||||
## 1.8.7
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 200 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.2 KiB |
@ -4,12 +4,12 @@ import { GenIcon } from 'react-icons'
|
||||
function MihomoIcon(props: any): React.JSX.Element {
|
||||
return GenIcon({
|
||||
tag: 'svg',
|
||||
attr: { viewBox: '0 0 120 150' },
|
||||
attr: { viewBox: '0 0 58 61.53' },
|
||||
child: [
|
||||
{
|
||||
tag: 'path',
|
||||
attr: {
|
||||
d: 'M123,105.41c0,15.99-13.44,20.46-20.54,20.6h-29.1c5.33-1.74,8.97-5.24,9.18-5.44.05-.05.28-.27.55-.56h19.31c1.44-.04,14.6-.78,14.6-14.6,0-9.22-3.85-21.88-34.64-29.67l-1.47-6.53c28.67,6.6,42.11,18.22,42.11,36.2Z',
|
||||
d: 'M57.99,14.06c-.26-4.98-4.34-9.05-9.31-9.31-5.69-.3-10.39,4.22-10.39,9.84h0c0,.39-.24.73-.6.86l-1.08.38c-.49.17-.77.68-.65,1.18l.2.9c.13.6.76.95,1.34.74l.7-.25c.42-.15.88.04,1.07.45,1.59,3.31,4.96,5.59,8.88,5.59,5.62,0,10.14-4.7,9.84-10.39Z',
|
||||
fill: 'currentColor'
|
||||
},
|
||||
child: []
|
||||
@ -17,27 +17,7 @@ function MihomoIcon(props: any): React.JSX.Element {
|
||||
{
|
||||
tag: 'path',
|
||||
attr: {
|
||||
d: 'M81.1,37.6c-2.42.76-4.68,1.55-6.78,2.37l-1.33-5.92c2.05-.77,4.21-1.51,6.47-2.21.27,2.03.83,3.96,1.64,5.76Z',
|
||||
fill: 'currentColor'
|
||||
},
|
||||
child: []
|
||||
},
|
||||
{
|
||||
tag: 'circle',
|
||||
attr: {
|
||||
cx: '53.83',
|
||||
cy: '33.01',
|
||||
r: '6',
|
||||
fill: 'currentColor'
|
||||
},
|
||||
child: []
|
||||
},
|
||||
{
|
||||
tag: 'circle',
|
||||
attr: {
|
||||
cx: '33.83',
|
||||
cy: '33.01',
|
||||
r: '6',
|
||||
d: 'M40.49,33.96l-1.01-.08.84,3.69h.03s.14,0,.14,0c6.11,0,12.43,4.21,12.43,11.26,0,5.02-3.54,9.1-7.89,9.1h-3.81c-.33.57-.71,1.13-1.13,1.66-.58.72-1.22,1.37-1.91,1.95h6.85c6.35,0,11.49-5.69,11.49-12.71h0c0-9-7.9-14.87-16.04-14.87Z',
|
||||
fill: 'currentColor'
|
||||
},
|
||||
child: []
|
||||
@ -45,23 +25,7 @@ function MihomoIcon(props: any): React.JSX.Element {
|
||||
{
|
||||
tag: 'path',
|
||||
attr: {
|
||||
d: 'M67.53,126.01h-1.49c-1.83,0-3.4-1.54-3.19-3.36.17-1.52,1.4-2.64,2.98-2.64h1.7c4.3,0,8.3-1.9,11-5.3,2.7-3.3,3.6-7.6,2.7-11.8L60.13,8.91l-.4.4c-4.3,4.4-9.9,6.7-15.9,6.7s-11.6-2.3-15.8-6.6l-.5-.4L6.33,103.01c-.9,4.1,0,8.4,2.7,11.8s6.7,5.3,11,5.3h.59c1.83,0,3.4,1.54,3.19,3.36-.17,1.52-1.4,2.64-2.98,2.64h-.8c-6.1,0-11.8-2.7-15.7-7.5-3.8-4.9-5.2-11-3.8-17L22.93,2.31c.2-1.1,1-1.9,2.1-2.2,1-.3,2.2,0,3,.8l4.3,4.3c3,3.1,7.1,4.8,11.5,4.8s8.5-1.7,11.6-4.8l4.24-4.24c.37-.37.81-.68,1.31-.83,1.95-.58,3.56.68,3.86,2.16l22.3,99.3c1.3,5.9-.1,12.1-3.9,16.8-.4.5-1.4,1.5-1.4,1.5,0,0-6.26,6.1-14.3,6.1h0Z',
|
||||
fill: 'currentColor'
|
||||
},
|
||||
child: []
|
||||
},
|
||||
{
|
||||
tag: 'path',
|
||||
attr: {
|
||||
d: 'M22.23,70.11c1.6-.3,3.2.7,3.5,2.4l9,45.1c.3,1.5,1.5,2.5,3,2.5,1.7,0,3-1.3,3-3v-34.1c0-1.7,1.3-3,3-3s3,1.3,3,3v34c0,1.6,1.4,3,3.1,3h1c1.5,0,2.7-1,3-2.5l9.1-45.1c.3-1.6,1.9-2.7,3.5-2.3,1.6.3,2.7,1.9,2.3,3.5l-9.1,45c-.7,4.3-4.5,7.4-8.9,7.4h-1c-2.3,0-4.5-.9-6.1-2.3-1.6,1.4-3.7,2.3-6,2.3-4.3,0-7.9-3-8.8-7.3l-9-45.1c-.3-1.6.8-3.2,2.4-3.5Z',
|
||||
fill: 'currentColor'
|
||||
},
|
||||
child: []
|
||||
},
|
||||
{
|
||||
tag: 'path',
|
||||
attr: {
|
||||
d: 'M100.75,7.39c-11.85,0-21.5,9.64-21.5,21.5,0,1,.07,1.99.21,2.95.27,2.03.83,3.96,1.64,5.76,3.35,7.53,10.9,12.79,19.65,12.79,11.86,0,21.5-9.65,21.5-21.5s-9.64-21.5-21.5-21.5ZM100.75,45.39c-9.1,0-16.5-7.4-16.5-16.5s7.4-16.5,16.5-16.5,16.5,7.4,16.5,16.5-7.4,16.5-16.5,16.5Z',
|
||||
d: 'M14.79,13.24c1.93,0,3.49,1.56,3.49,3.49s-1.56,3.49-3.49,3.49-3.49-1.56-3.49-3.49,1.56-3.49,3.49-3.49ZM25.6,13.24c1.93,0,3.49,1.56,3.49,3.49s-1.56,3.49-3.49,3.49-3.49-1.56-3.49-3.49,1.56-3.49,3.49-3.49ZM40.12,48.91L29.04,0h-.02c0,.07.02.13.02.2,0,3.3-3.96,5.98-8.84,5.98S11.36,3.51,11.36.2c0-.07.01-.13.02-.2h-.04L.26,48.91c-1.47,6.47,3.53,12.62,10.26,12.62h19.34c6.73,0,11.73-6.15,10.26-12.62ZM16.61,58.42c-.78,0-1.4-.63-1.4-1.4,0-14.27-6.25-14.65-6.52-14.66-.78,0-1.39-.63-1.39-1.4s.64-1.4,1.42-1.4c.38,0,9.3.2,9.3,17.47,0,.78-.63,1.4-1.4,1.4ZM31.7,42.36c-.31.01-6.52.45-6.52,14.66,0,.78-.63,1.4-1.4,1.4s-1.4-.63-1.4-1.4c0-17.27,8.92-17.47,9.3-17.47.78,0,1.4.63,1.4,1.4s-.62,1.39-1.38,1.4Z',
|
||||
fill: 'currentColor'
|
||||
},
|
||||
child: []
|
||||
|
||||
356
src/renderer/src/components/settings/webui-config.tsx
Normal file
@ -0,0 +1,356 @@
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import SettingCard from '../base/base-setting-card'
|
||||
import SettingItem from '../base/base-setting-item'
|
||||
import { Button, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Input } from '@heroui/react'
|
||||
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { MdEdit, MdDelete, MdOpenInNew } from 'react-icons/md'
|
||||
|
||||
interface WebUIPanel {
|
||||
id: string
|
||||
name: string
|
||||
url: string
|
||||
isDefault?: boolean
|
||||
}
|
||||
|
||||
// 用于高亮显示URL中的变量
|
||||
const HighlightedUrl: React.FC<{ url: string }> = ({ url }) => {
|
||||
const parts = url.split(/(%host|%port|%secret)/g)
|
||||
|
||||
return (
|
||||
<p className="text-sm text-default-500 break-all">
|
||||
{parts.map((part, index) => {
|
||||
if (part === '%host' || part === '%port' || part === '%secret') {
|
||||
return (
|
||||
<span key={index} className="bg-warning-200 text-warning-800 px-1 rounded">
|
||||
{part}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return part
|
||||
})}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
// 可点击的变量标签组件
|
||||
const ClickableVariableTag: React.FC<{
|
||||
variable: string;
|
||||
onClick: (variable: string) => void
|
||||
}> = ({ variable, onClick }) => {
|
||||
return (
|
||||
<span
|
||||
className="bg-warning-200 text-warning-800 px-1 rounded ml-1 cursor-pointer hover:bg-warning-300"
|
||||
onClick={() => onClick(variable)}
|
||||
>
|
||||
{variable}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
const WebUIConfig: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { controledMihomoConfig } = useControledMihomoConfig()
|
||||
|
||||
const externalController = controledMihomoConfig?.['external-controller'] || ''
|
||||
const secret = controledMihomoConfig?.secret || ''
|
||||
|
||||
// 解析主机和端口
|
||||
const parseController = () => {
|
||||
if (externalController) {
|
||||
const [host, port] = externalController.split(':')
|
||||
return { host: host.replace('0.0.0.0', '127.0.0.1'), port }
|
||||
}
|
||||
return { host: '127.0.0.1', port: '9090' }
|
||||
}
|
||||
|
||||
const { host, port } = parseController()
|
||||
|
||||
// 默认WebUI面板选项
|
||||
const defaultWebUIPanels: WebUIPanel[] = [
|
||||
{
|
||||
id: 'metacubexd',
|
||||
name: 'MetaCubeXD',
|
||||
url: 'https://metacubex.github.io/metacubexd/#/setup?http=true&hostname=%host&port=%port&secret=%secret',
|
||||
isDefault: true
|
||||
},
|
||||
{
|
||||
id: 'yacd',
|
||||
name: 'YACD',
|
||||
url: 'https://yacd.metacubex.one/?hostname=%host&port=%port&secret=%secret',
|
||||
isDefault: true
|
||||
},
|
||||
{
|
||||
id: 'zashboard',
|
||||
name: 'Zashboard',
|
||||
url: 'https://board.zash.run.place/#/setup?http=true&hostname=%host&port=%port&secret=%secret',
|
||||
isDefault: true
|
||||
}
|
||||
]
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
const [allPanels, setAllPanels] = useState<WebUIPanel[]>([])
|
||||
const [editingPanel, setEditingPanel] = useState<WebUIPanel | null>(null)
|
||||
const [newPanelName, setNewPanelName] = useState('')
|
||||
const [newPanelUrl, setNewPanelUrl] = useState('')
|
||||
|
||||
|
||||
const urlInputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
// 初始化面板列表
|
||||
useEffect(() => {
|
||||
const savedPanels = localStorage.getItem('webui-panels')
|
||||
if (savedPanels) {
|
||||
setAllPanels(JSON.parse(savedPanels))
|
||||
} else {
|
||||
setAllPanels(defaultWebUIPanels)
|
||||
}
|
||||
}, [])
|
||||
|
||||
// 保存面板列表到localStorage
|
||||
useEffect(() => {
|
||||
if (allPanels.length > 0) {
|
||||
localStorage.setItem('webui-panels', JSON.stringify(allPanels))
|
||||
}
|
||||
}, [allPanels])
|
||||
|
||||
// 在URL输入框光标处插入或替换变量
|
||||
const insertVariableAtCursor = (variable: string) => {
|
||||
if (!urlInputRef.current) return
|
||||
|
||||
const input = urlInputRef.current
|
||||
const start = input.selectionStart || 0
|
||||
const end = input.selectionEnd || 0
|
||||
const currentValue = newPanelUrl || ''
|
||||
|
||||
// 如果有选中文本,则替换选中的文本
|
||||
const newValue = currentValue.substring(0, start) + variable + currentValue.substring(end)
|
||||
|
||||
setNewPanelUrl(newValue)
|
||||
|
||||
// 设置光标位置到插入变量之后
|
||||
setTimeout(() => {
|
||||
if (urlInputRef.current) {
|
||||
const newCursorPos = start + variable.length
|
||||
urlInputRef.current.setSelectionRange(newCursorPos, newCursorPos)
|
||||
urlInputRef.current.focus()
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
// 打开WebUI面板
|
||||
const openWebUI = (panel: WebUIPanel) => {
|
||||
const url = panel.url
|
||||
.replace('%host', host)
|
||||
.replace('%port', port)
|
||||
.replace('%secret', secret)
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
|
||||
// 添加新面板
|
||||
const addNewPanel = () => {
|
||||
if (newPanelName && newPanelUrl) {
|
||||
const newPanel: WebUIPanel = {
|
||||
id: Date.now().toString(),
|
||||
name: newPanelName,
|
||||
url: newPanelUrl
|
||||
}
|
||||
setAllPanels([...allPanels, newPanel])
|
||||
setNewPanelName('')
|
||||
setNewPanelUrl('')
|
||||
setEditingPanel(null)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新面板
|
||||
const updatePanel = () => {
|
||||
if (editingPanel && newPanelName && newPanelUrl) {
|
||||
const updatedPanels = allPanels.map(panel =>
|
||||
panel.id === editingPanel.id
|
||||
? { ...panel, name: newPanelName, url: newPanelUrl }
|
||||
: panel
|
||||
)
|
||||
setAllPanels(updatedPanels)
|
||||
setEditingPanel(null)
|
||||
setNewPanelName('')
|
||||
setNewPanelUrl('')
|
||||
}
|
||||
}
|
||||
|
||||
// 删除面板
|
||||
const deletePanel = (id: string) => {
|
||||
setAllPanels(allPanels.filter(panel => panel.id !== id))
|
||||
}
|
||||
|
||||
// 开始编辑面板
|
||||
const startEditing = (panel: WebUIPanel) => {
|
||||
setEditingPanel(panel)
|
||||
setNewPanelName(panel.name)
|
||||
setNewPanelUrl(panel.url)
|
||||
}
|
||||
|
||||
// 取消编辑
|
||||
const cancelEditing = () => {
|
||||
setEditingPanel(null)
|
||||
setNewPanelName('')
|
||||
setNewPanelUrl('')
|
||||
}
|
||||
|
||||
// 恢复默认面板
|
||||
const restoreDefaultPanels = () => {
|
||||
setAllPanels(defaultWebUIPanels)
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingCard>
|
||||
<SettingItem title={t('settings.webui.title')} divider>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
color="primary"
|
||||
onPress={() => setIsModalOpen(true)}
|
||||
>
|
||||
{t('settings.webui.manage')}
|
||||
</Button>
|
||||
</div>
|
||||
</SettingItem>
|
||||
<SettingItem title={t('settings.webui.currentConfig')}>
|
||||
<div className="text-sm text-default-500">
|
||||
<p>{t('settings.webui.host')}: {host}</p>
|
||||
<p>{t('settings.webui.port')}: {port}</p>
|
||||
</div>
|
||||
</SettingItem>
|
||||
|
||||
{/* 面板管理模态框 */}
|
||||
<Modal
|
||||
isOpen={isModalOpen}
|
||||
onOpenChange={setIsModalOpen}
|
||||
size="5xl"
|
||||
scrollBehavior="inside"
|
||||
backdrop="blur"
|
||||
classNames={{ backdrop: 'top-[48px]' }}
|
||||
hideCloseButton
|
||||
>
|
||||
<ModalContent className="h-full w-[calc(100%-100px)]">
|
||||
<ModalHeader className="flex pb-0 app-drag">
|
||||
{t('settings.webui.manage')}
|
||||
</ModalHeader>
|
||||
<ModalBody className="flex flex-col h-full">
|
||||
<div className="flex flex-col h-full">
|
||||
{/* 添加/编辑面板表单 */}
|
||||
<div className="flex flex-col gap-2 p-3 bg-default-100 rounded-lg flex-shrink-0">
|
||||
<Input
|
||||
label={t('settings.webui.panelName')}
|
||||
placeholder={t('settings.webui.panelNamePlaceholder')}
|
||||
value={newPanelName}
|
||||
onValueChange={setNewPanelName}
|
||||
/>
|
||||
<Input
|
||||
ref={urlInputRef}
|
||||
label={t('settings.webui.panelUrl')}
|
||||
placeholder={t('settings.webui.panelUrlPlaceholder')}
|
||||
value={newPanelUrl}
|
||||
onValueChange={setNewPanelUrl}
|
||||
/>
|
||||
<div className="text-xs text-default-500">
|
||||
{t('settings.webui.variableHint')}:
|
||||
<ClickableVariableTag variable="%host" onClick={insertVariableAtCursor} />
|
||||
<ClickableVariableTag variable="%port" onClick={insertVariableAtCursor} />
|
||||
<ClickableVariableTag variable="%secret" onClick={insertVariableAtCursor} />
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{editingPanel ? (
|
||||
<>
|
||||
<Button
|
||||
size="sm"
|
||||
color="primary"
|
||||
onPress={updatePanel}
|
||||
isDisabled={!newPanelName || !newPanelUrl}
|
||||
>
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
color="default"
|
||||
variant="bordered"
|
||||
onPress={cancelEditing}
|
||||
>
|
||||
{t('common.cancel')}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
size="sm"
|
||||
color="primary"
|
||||
onPress={addNewPanel}
|
||||
isDisabled={!newPanelName || !newPanelUrl}
|
||||
>
|
||||
{t('settings.webui.addPanel')}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
size="sm"
|
||||
color="warning"
|
||||
variant="bordered"
|
||||
onPress={restoreDefaultPanels}
|
||||
>
|
||||
{t('settings.webui.restoreDefaults')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 面板列表 */}
|
||||
<div className="flex flex-col gap-2 mt-2 overflow-y-auto flex-grow">
|
||||
<h3 className="text-lg font-semibold">{t('settings.webui.panels')}</h3>
|
||||
{allPanels.map(panel => (
|
||||
<div key={panel.id} className="flex items-start justify-between p-3 bg-default-50 rounded-lg flex-shrink-0">
|
||||
<div className="flex-1 mr-2">
|
||||
<p className="font-medium">{panel.name}</p>
|
||||
<HighlightedUrl url={panel.url} />
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
isIconOnly
|
||||
size="sm"
|
||||
color="primary"
|
||||
onPress={() => openWebUI(panel)}
|
||||
>
|
||||
<MdOpenInNew />
|
||||
</Button>
|
||||
<Button
|
||||
isIconOnly
|
||||
size="sm"
|
||||
color="warning"
|
||||
onPress={() => startEditing(panel)}
|
||||
>
|
||||
<MdEdit />
|
||||
</Button>
|
||||
<Button
|
||||
isIconOnly
|
||||
size="sm"
|
||||
color="danger"
|
||||
onPress={() => deletePanel(panel.id)}
|
||||
>
|
||||
<MdDelete />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter className="pt-0">
|
||||
<Button
|
||||
color="primary"
|
||||
onPress={() => setIsModalOpen(false)}
|
||||
>
|
||||
{t('common.close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</SettingCard>
|
||||
)
|
||||
}
|
||||
|
||||
export default WebUIConfig
|
||||
@ -296,4 +296,4 @@ const loadImage = (url: string): Promise<string> => {
|
||||
})
|
||||
}
|
||||
|
||||
const trayIconBase64 = `data:image/png;base64,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`
|
||||
const trayIconBase64 = `data:image/png;base64,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`
|
||||
|
||||
@ -91,6 +91,21 @@
|
||||
"settings.links.github": "GitHub Repository",
|
||||
"settings.links.telegram": "Telegram Group",
|
||||
"settings.title": "Application Settings",
|
||||
"settings.webui.title": "WebUI Management Panel",
|
||||
"settings.webui.manage": "Manage Panels",
|
||||
"settings.webui.currentConfig": "Current Configuration",
|
||||
"settings.webui.host": "Host",
|
||||
"settings.webui.port": "Port",
|
||||
"settings.webui.secret": "Secret",
|
||||
"settings.webui.noSecret": "No Secret",
|
||||
"settings.webui.panelName": "Panel Name",
|
||||
"settings.webui.panelUrl": "Panel URL",
|
||||
"settings.webui.panelNamePlaceholder": "Enter panel name",
|
||||
"settings.webui.panelUrlPlaceholder": "Enter panel URL",
|
||||
"settings.webui.variableHint": "Available Variables",
|
||||
"settings.webui.addPanel": "Add Panel",
|
||||
"settings.webui.panels": "Panel List",
|
||||
"settings.webui.restoreDefaults": "Restore Defaults",
|
||||
"mihomo.userAgent": "Subscription User Agent",
|
||||
"mihomo.userAgentPlaceholder": "Default: mihomo.party/v{{version}} (clash.meta)",
|
||||
"mihomo.delayTest.url": "Delay Test URL",
|
||||
@ -388,6 +403,7 @@
|
||||
"dns.customHosts.valuePlaceholder": "Domain or IP",
|
||||
"dns.saveOnly": "Save Only",
|
||||
"profiles.title": "Profile Management",
|
||||
"profiles.input.placeholder": "Enter your subscription URL",
|
||||
"profiles.updateAll": "Update All Profiles",
|
||||
"profiles.useProxy": "Proxy",
|
||||
"profiles.import": "Import",
|
||||
@ -404,12 +420,6 @@
|
||||
"profiles.remote": "Remote",
|
||||
"profiles.local": "Local",
|
||||
"profiles.trafficUsage": "Traffic Usage Progress",
|
||||
"profiles.openWebUI.title": "WebUI Management Panel",
|
||||
"profiles.openWebUI.description": "Select a WebUI panel to open",
|
||||
"profiles.openWebUI.local": "Local WebUI",
|
||||
"profiles.updateWebUI.button": "Update Panel",
|
||||
"profiles.updateWebUI.success": "WebUI panel updated successfully",
|
||||
"profiles.updateWebUI.failed": "WebUI panel update failed: {{error}}",
|
||||
"profiles.editInfo.title": "Edit Information",
|
||||
"profiles.editInfo.name": "Name",
|
||||
"profiles.editInfo.url": "Subscription URL",
|
||||
|
||||
@ -377,6 +377,7 @@
|
||||
"dns.customHosts.valuePlaceholder": "دامنه یا IP",
|
||||
"dns.saveOnly": "فقط ذخیره",
|
||||
"profiles.title": "مدیریت پروفایل",
|
||||
"profiles.input.placeholder": "آدرس اشتراک خود را وارد کنید",
|
||||
"profiles.updateAll": "بهروزرسانی همه پروفایلها",
|
||||
"profiles.useProxy": "پراکسی",
|
||||
"profiles.import": "وارد کردن",
|
||||
|
||||
@ -377,6 +377,7 @@
|
||||
"dns.customHosts.valuePlaceholder": "Домен или IP",
|
||||
"dns.saveOnly": "Только сохранить",
|
||||
"profiles.title": "Управление профилями",
|
||||
"profiles.input.placeholder": "Введите URL вашей подписки",
|
||||
"profiles.updateAll": "Обновить все профили",
|
||||
"profiles.useProxy": "Прокси",
|
||||
"profiles.import": "Импорт",
|
||||
|
||||
@ -91,6 +91,21 @@
|
||||
"settings.links.github": "GitHub 仓库",
|
||||
"settings.links.telegram": "Telegram 群组",
|
||||
"settings.title": "应用设置",
|
||||
"settings.webui.title": "WebUI 管理面板",
|
||||
"settings.webui.manage": "管理面板",
|
||||
"settings.webui.currentConfig": "当前配置信息",
|
||||
"settings.webui.host": "主机",
|
||||
"settings.webui.port": "端口",
|
||||
"settings.webui.secret": "密钥",
|
||||
"settings.webui.noSecret": "无密钥",
|
||||
"settings.webui.panelName": "面板名称",
|
||||
"settings.webui.panelUrl": "面板URL",
|
||||
"settings.webui.panelNamePlaceholder": "输入面板名称",
|
||||
"settings.webui.panelUrlPlaceholder": "输入面板URL",
|
||||
"settings.webui.variableHint": "可用变量",
|
||||
"settings.webui.addPanel": "添加面板",
|
||||
"settings.webui.panels": "面板列表",
|
||||
"settings.webui.restoreDefaults": "恢复默认",
|
||||
"mihomo.title": "内核设置",
|
||||
"mihomo.restart": "重启内核",
|
||||
"mihomo.memory": "内存使用",
|
||||
@ -388,6 +403,7 @@
|
||||
"dns.fallbackFilter.domainPlaceholder": "例:+.google.com",
|
||||
"dns.saveOnly": "仅保存",
|
||||
"profiles.title": "订阅管理",
|
||||
"profiles.input.placeholder": "请输入您的订阅网址",
|
||||
"profiles.updateAll": "更新全部订阅",
|
||||
"profiles.useProxy": "代理",
|
||||
"profiles.import": "导入",
|
||||
@ -409,12 +425,6 @@
|
||||
"profiles.traffic.expired": "已过期",
|
||||
"profiles.traffic.remainingDays": "剩余 {{days}} 天",
|
||||
"profiles.traffic.lastUpdate": "最后更新:{{time}}",
|
||||
"profiles.openWebUI.title": "WebUI 管理面板",
|
||||
"profiles.openWebUI.description": "选择一个 WebUI 面板打开",
|
||||
"profiles.openWebUI.local": "本地 WebUI",
|
||||
"profiles.updateWebUI.button": "更新面板",
|
||||
"profiles.updateWebUI.success": "WebUI 面板更新成功",
|
||||
"profiles.updateWebUI.failed": "WebUI 面板更新失败: {{error}}",
|
||||
"profiles.editInfo.title": "编辑信息",
|
||||
"profiles.editInfo.name": "名称",
|
||||
"profiles.editInfo.url": "订阅地址",
|
||||
|
||||
@ -7,16 +7,12 @@ import {
|
||||
DropdownItem,
|
||||
DropdownMenu,
|
||||
DropdownTrigger,
|
||||
Input,
|
||||
Card,
|
||||
CardBody,
|
||||
CardHeader
|
||||
Input
|
||||
} from '@heroui/react'
|
||||
import BasePage from '@renderer/components/base/base-page'
|
||||
import ProfileItem from '@renderer/components/profiles/profile-item'
|
||||
import { useProfileConfig } from '@renderer/hooks/use-profile-config'
|
||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
||||
import { getFilePath, readTextFile, subStoreCollections, subStoreSubs } from '@renderer/utils/ipc'
|
||||
import type { KeyboardEvent } from 'react'
|
||||
import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
@ -36,7 +32,6 @@ import SubStoreIcon from '@renderer/components/base/substore-icon'
|
||||
import useSWR from 'swr'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { mihomoUpgradeUI } from '@renderer/utils/ipc'
|
||||
|
||||
const Profiles: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
@ -50,9 +45,6 @@ const Profiles: React.FC = () => {
|
||||
mutateProfileConfig
|
||||
} = useProfileConfig()
|
||||
const { appConfig } = useAppConfig()
|
||||
const { controledMihomoConfig } = useControledMihomoConfig()
|
||||
const externalController = controledMihomoConfig?.['external-controller'] || ''
|
||||
const externalUI = (controledMihomoConfig as any)?.['external-ui']
|
||||
const { useSubStore = true, useCustomSubStore = false, customSubStoreUrl = '' } = appConfig || {}
|
||||
const { current, items = [] } = profileConfig || {}
|
||||
const navigate = useNavigate()
|
||||
@ -203,26 +195,6 @@ const Profiles: React.FC = () => {
|
||||
setSortedItems(items)
|
||||
}, [items])
|
||||
|
||||
// 获取本地WebUI的URL
|
||||
const getLocalWebUIUrl = (): string => {
|
||||
if (externalController) {
|
||||
// 将地址转换为WebUI URL
|
||||
// 例如: 127.0.0.1:9090 -> http://127.0.0.1:9090/ui
|
||||
const controller = externalController.replace('0.0.0.0', '127.0.0.1')
|
||||
// 如果配置了external-ui,使用/ui路径,否则可能需要使用不同的路径
|
||||
const uiPath = externalUI ? '/ui' : '/ui' // 默认使用/ui路径
|
||||
return `http://${controller}${uiPath}`
|
||||
}
|
||||
// 默认URL
|
||||
return 'http://127.0.0.1:9090/ui'
|
||||
}
|
||||
|
||||
// 检查本地WebUI是否可用
|
||||
const isLocalWebUIAvailable = (): boolean => {
|
||||
// 如果有配置的external-controller,则认为本地WebUI可用
|
||||
return !!externalController
|
||||
}
|
||||
|
||||
return (
|
||||
<BasePage
|
||||
ref={pageRef}
|
||||
@ -256,6 +228,7 @@ const Profiles: React.FC = () => {
|
||||
<div className="flex p-2">
|
||||
<Input
|
||||
size="sm"
|
||||
placeholder={t('profiles.input.placeholder')}
|
||||
value={url}
|
||||
onValueChange={setUrl}
|
||||
onKeyUp={handleInputKeyUp}
|
||||
@ -406,53 +379,6 @@ const Profiles: React.FC = () => {
|
||||
</div>
|
||||
<Divider />
|
||||
</div>
|
||||
{/* WebUI Card with Multiple Options */}
|
||||
<div className="m-2">
|
||||
<Card>
|
||||
<CardHeader className="flex gap-3">
|
||||
<div className="flex flex-col">
|
||||
<p className="text-md">{t('profiles.openWebUI.title')}</p>
|
||||
<p className="text-small text-default-500">{t('profiles.openWebUI.description')}</p>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<Button
|
||||
onPress={() => window.open('https://metacubexd.pages.dev/', '_blank')}
|
||||
>
|
||||
MetaCubeXD
|
||||
</Button>
|
||||
<Button
|
||||
onPress={() => window.open('https://zashboard.pages.dev/', '_blank')}
|
||||
>
|
||||
Zashboard
|
||||
</Button>
|
||||
{isLocalWebUIAvailable() && (
|
||||
<>
|
||||
<Button
|
||||
onPress={() => window.open(getLocalWebUIUrl(), '_blank')}
|
||||
>
|
||||
{t('profiles.openWebUI.local')}
|
||||
</Button>
|
||||
<Button
|
||||
color="success"
|
||||
onPress={async () => {
|
||||
try {
|
||||
await mihomoUpgradeUI()
|
||||
new Notification(t('profiles.updateWebUI.success'))
|
||||
} catch (e) {
|
||||
new Notification(t('profiles.updateWebUI.failed', { error: String(e) }))
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('profiles.updateWebUI.button')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={onDragEnd}>
|
||||
<div
|
||||
className={`${fileOver ? 'blur-sm' : ''} grid sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-2 m-2`}
|
||||
|
||||
@ -10,6 +10,7 @@ import ShortcutConfig from '@renderer/components/settings/shortcut-config'
|
||||
import { FaTelegramPlane } from 'react-icons/fa'
|
||||
import SiderConfig from '@renderer/components/settings/sider-config'
|
||||
import SubStoreConfig from '@renderer/components/settings/substore-config'
|
||||
import WebUIConfig from '@renderer/components/settings/webui-config'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const Settings: React.FC = () => {
|
||||
@ -60,6 +61,7 @@ const Settings: React.FC = () => {
|
||||
}
|
||||
>
|
||||
<GeneralConfig />
|
||||
<WebUIConfig />
|
||||
<SubStoreConfig />
|
||||
<SiderConfig />
|
||||
<WebdavConfig />
|
||||
|
||||