mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2026-02-10 19:50:28 +08:00
Compare commits
3 Commits
56e328191f
...
197c9d3af8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
197c9d3af8 | ||
|
|
388581d75e | ||
|
|
3c148f2c01 |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -205,6 +205,8 @@ jobs:
|
|||||||
GITHUB_EVENT_NAME: workflow_dispatch
|
GITHUB_EVENT_NAME: workflow_dispatch
|
||||||
run: node scripts/update-version.mjs
|
run: node scripts/update-version.mjs
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
|
env:
|
||||||
|
LEGACY_BUILD: 'true'
|
||||||
run: pnpm prepare --${{ matrix.arch }}
|
run: pnpm prepare --${{ matrix.arch }}
|
||||||
- name: Build
|
- name: Build
|
||||||
env:
|
env:
|
||||||
@ -263,7 +265,7 @@ jobs:
|
|||||||
arch:
|
arch:
|
||||||
- x64
|
- x64
|
||||||
- arm64
|
- arm64
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|||||||
@ -332,11 +332,13 @@ function getSysproxyNodeName() {
|
|||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
const isWin7Build = process.env.LEGACY_BUILD === 'true'
|
||||||
|
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case 'win32':
|
case 'win32':
|
||||||
if (arch === 'x64') return 'sysproxy.win32-x64-msvc.node'
|
if (arch === 'x64') return isWin7Build ? 'sysproxy.win32-x64-msvc-win7.node' : 'sysproxy.win32-x64-msvc.node'
|
||||||
if (arch === 'arm64') return 'sysproxy.win32-arm64-msvc.node'
|
if (arch === 'arm64') return 'sysproxy.win32-arm64-msvc.node'
|
||||||
if (arch === 'ia32') return 'sysproxy.win32-ia32-msvc.node'
|
if (arch === 'ia32') return isWin7Build ? 'sysproxy.win32-ia32-msvc-win7.node' : 'sysproxy.win32-ia32-msvc.node'
|
||||||
break
|
break
|
||||||
case 'darwin':
|
case 'darwin':
|
||||||
if (arch === 'x64') return 'sysproxy.darwin-x64.node'
|
if (arch === 'x64') return 'sysproxy.darwin-x64.node'
|
||||||
|
|||||||
@ -65,7 +65,11 @@ export async function getProfileItem(id: string | undefined): Promise<IProfileIt
|
|||||||
|
|
||||||
export async function changeCurrentProfile(id: string): Promise<void> {
|
export async function changeCurrentProfile(id: string): Promise<void> {
|
||||||
// 使用队列确保 profile 切换串行执行,避免竞态条件
|
// 使用队列确保 profile 切换串行执行,避免竞态条件
|
||||||
changeProfileQueue = changeProfileQueue.then(async () => {
|
let taskError: unknown = null
|
||||||
|
changeProfileQueue = changeProfileQueue
|
||||||
|
.catch(() => {
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
const { current } = await getProfileConfig()
|
const { current } = await getProfileConfig()
|
||||||
if (current === id) return
|
if (current === id) return
|
||||||
|
|
||||||
@ -81,10 +85,13 @@ export async function changeCurrentProfile(id: string): Promise<void> {
|
|||||||
config.current = current
|
config.current = current
|
||||||
return config
|
return config
|
||||||
})
|
})
|
||||||
throw e
|
taskError = e
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
await changeProfileQueue
|
await changeProfileQueue
|
||||||
|
if (taskError) {
|
||||||
|
throw taskError
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateProfileItem(item: IProfileItem): Promise<void> {
|
export async function updateProfileItem(item: IProfileItem): Promise<void> {
|
||||||
@ -249,6 +256,14 @@ export async function createProfile(item: Partial<IProfileItem>): Promise<IProfi
|
|||||||
useProxy: true,
|
useProxy: true,
|
||||||
timeout: subscriptionTimeout
|
timeout: subscriptionTimeout
|
||||||
})
|
})
|
||||||
|
} else if (newItem.substore) {
|
||||||
|
// SubStore requests (especially collections) need more time as they fetch and merge multiple subscriptions
|
||||||
|
// Use the full subscriptionTimeout since SubStore is a local server and doesn't need smart fallback
|
||||||
|
result = await fetchAndValidateSubscription({
|
||||||
|
...baseOptions,
|
||||||
|
useProxy: false,
|
||||||
|
timeout: subscriptionTimeout
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
const smartTimeout = 5000
|
const smartTimeout = 5000
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1,11 +1,19 @@
|
|||||||
const { existsSync } = require('fs')
|
const { existsSync } = require('fs')
|
||||||
const { join, dirname } = require('path')
|
const { join, dirname } = require('path')
|
||||||
|
const os = require('os')
|
||||||
|
|
||||||
const { platform, arch } = process
|
const { platform, arch } = process
|
||||||
|
|
||||||
let nativeBinding = null
|
let nativeBinding = null
|
||||||
let loadError = null
|
let loadError = null
|
||||||
|
|
||||||
|
function isWindows7() {
|
||||||
|
if (platform !== 'win32') return false
|
||||||
|
const release = os.release()
|
||||||
|
// Windows 7 is NT 6.1
|
||||||
|
return release.startsWith('6.1')
|
||||||
|
}
|
||||||
|
|
||||||
function isMusl() {
|
function isMusl() {
|
||||||
// 优先使用 process.report(Node.js 12+,最可靠)
|
// 优先使用 process.report(Node.js 12+,最可靠)
|
||||||
if (process.report && typeof process.report.getReport === 'function') {
|
if (process.report && typeof process.report.getReport === 'function') {
|
||||||
@ -23,10 +31,11 @@ function isMusl() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getBindingName() {
|
function getBindingName() {
|
||||||
|
const win7 = isWindows7()
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
case 'win32':
|
case 'win32':
|
||||||
if (arch === 'x64') return 'sysproxy.win32-x64-msvc.node'
|
if (arch === 'x64') return win7 ? 'sysproxy.win32-x64-msvc-win7.node' : 'sysproxy.win32-x64-msvc.node'
|
||||||
if (arch === 'ia32') return 'sysproxy.win32-ia32-msvc.node'
|
if (arch === 'ia32') return win7 ? 'sysproxy.win32-ia32-msvc-win7.node' : 'sysproxy.win32-ia32-msvc.node'
|
||||||
if (arch === 'arm64') return 'sysproxy.win32-arm64-msvc.node'
|
if (arch === 'arm64') return 'sysproxy.win32-arm64-msvc.node'
|
||||||
break
|
break
|
||||||
case 'darwin':
|
case 'darwin':
|
||||||
|
|||||||
@ -1,20 +1,60 @@
|
|||||||
import React, { useRef } from 'react'
|
import React, { useRef, useState, useCallback } from 'react'
|
||||||
import { Input, InputProps } from '@heroui/react'
|
import { Input, InputProps } from '@heroui/react'
|
||||||
import { FaSearch } from 'react-icons/fa'
|
import { FaSearch } from 'react-icons/fa'
|
||||||
|
|
||||||
interface CollapseInputProps extends InputProps {
|
interface CollapseInputProps extends Omit<InputProps, 'onValueChange'> {
|
||||||
title: string
|
title: string
|
||||||
|
onValueChange?: (value: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const CollapseInput: React.FC<CollapseInputProps> = (props) => {
|
const CollapseInput: React.FC<CollapseInputProps> = (props) => {
|
||||||
const { title, ...inputProps } = props
|
const { title, value, onValueChange, ...inputProps } = props
|
||||||
const inputRef = useRef<HTMLInputElement>(null)
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
const isComposingRef = useRef(false)
|
||||||
|
const [localValue, setLocalValue] = useState(value || '')
|
||||||
|
|
||||||
|
// 同步外部 value 变化
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!isComposingRef.current) {
|
||||||
|
setLocalValue(value || '')
|
||||||
|
}
|
||||||
|
}, [value])
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const newValue = e.target.value
|
||||||
|
setLocalValue(newValue)
|
||||||
|
// 只在非组合输入时触发外部更新
|
||||||
|
if (!isComposingRef.current) {
|
||||||
|
onValueChange?.(newValue)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onValueChange]
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleCompositionStart = useCallback(() => {
|
||||||
|
isComposingRef.current = true
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleCompositionEnd = useCallback(
|
||||||
|
(e: React.CompositionEvent<HTMLInputElement>) => {
|
||||||
|
isComposingRef.current = false
|
||||||
|
// 组合输入结束后,触发一次更新
|
||||||
|
onValueChange?.(e.currentTarget.value)
|
||||||
|
},
|
||||||
|
[onValueChange]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<Input
|
<Input
|
||||||
size="sm"
|
size="sm"
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
{...inputProps}
|
{...inputProps}
|
||||||
|
value={localValue as string}
|
||||||
|
onChange={handleChange}
|
||||||
|
onCompositionStart={handleCompositionStart}
|
||||||
|
onCompositionEnd={handleCompositionEnd}
|
||||||
style={{ paddingInlineEnd: 0 }}
|
style={{ paddingInlineEnd: 0 }}
|
||||||
classNames={{
|
classNames={{
|
||||||
inputWrapper: 'cursor-pointer bg-transparent p-0 data-[hover=true]:bg-content2',
|
inputWrapper: 'cursor-pointer bg-transparent p-0 data-[hover=true]:bg-content2',
|
||||||
|
|||||||
@ -303,7 +303,7 @@ const drawSvg = async (
|
|||||||
if (upload === currentUploadRef.current && download === currentDownloadRef.current) return
|
if (upload === currentUploadRef.current && download === currentDownloadRef.current) return
|
||||||
currentUploadRef.current = upload
|
currentUploadRef.current = upload
|
||||||
currentDownloadRef.current = download
|
currentDownloadRef.current = download
|
||||||
const svg = `data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 140 36"><image height="36" width="36" href="${trayIconBase64}"/><text x="140" y="15" font-size="18" font-family="PingFang SC" font-weight="bold" text-anchor="end">${calcTraffic(upload)}/s</text><text x="140" y="34" font-size="18" font-family="PingFang SC" font-weight="bold" text-anchor="end">${calcTraffic(download)}/s</text></svg>`
|
const svg = `data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 156 36"><image height="36" width="36" href="${trayIconBase64}"/><text x="156" y="15" font-size="18" font-family="PingFang SC" font-weight="bold" text-anchor="end" fill="black">${calcTraffic(upload)}/s</text><text x="156" y="34" font-size="18" font-family="PingFang SC" font-weight="bold" text-anchor="end" fill="black">${calcTraffic(download)}/s</text></svg>`
|
||||||
const image = await loadImage(svg)
|
const image = await loadImage(svg)
|
||||||
window.electron.ipcRenderer.send('trayIconUpdate', image, true)
|
window.electron.ipcRenderer.send('trayIconUpdate', image, true)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user