+
{proxyGroupAdvancedModeEnabled ? "已开启" : "未开启"}
@@ -352,7 +351,7 @@ export function ProxyGroupsCategories() {
type="button"
variant="outline"
size="sm"
- className="ml-auto h-6 gap-1 rounded-md px-2 text-[10px] text-white/60 hover:bg-white/10 hover:text-white/85"
+ className="ml-auto h-6 gap-1 rounded-md px-2 text-[10px] text-fg-60 hover:bg-ink/10 hover:text-fg-85"
title="恢复隐藏分组"
>
@@ -395,18 +394,18 @@ export function ProxyGroupsCategories() {
return (
toggleCategory(categoryId)}
- className="w-full flex items-center gap-2 px-2 py-1.5 bg-white/5 hover:bg-white/10 transition-colors"
+ className="w-full flex items-center gap-2 px-2 py-1.5 bg-ink/5 hover:bg-ink/10 transition-colors"
>
{expandedCategories.has(categoryId) ? (
-
+
) : (
-
+
)}
-
+
{categoryInfo.name}
@@ -459,7 +458,7 @@ export function ProxyGroupsCategories() {
「{display.full}
」属于核心分流组。删除后会从列表隐藏,并从生成配置中移除,可能导致生成的订阅不可用或分流异常。
-
+
之后可以通过“已隐藏”菜单恢复该分组。
@@ -532,7 +531,7 @@ export function ProxyGroupsCategories() {
「{display.full}
」属于核心分流组。关闭后,生成的订阅可能无法正常使用,或出现分流异常。
-
+
除非你打算之后手动修改配置文件,否则不建议取消勾选。
@@ -672,20 +671,19 @@ export function ProxyGroupsCategories() {
groupName={resolveModuleDisplayName(settingsModule).full}
groupType={(advancedConfig.groupType ?? settingsModule.groupType) as ProxyGroupGroupType}
strategy={advancedConfig.strategy}
+ testUrl={advancedConfig.testUrl}
listenerTarget={target}
listenerBinding={findGroupListenerBinding(groupListeners, target)}
conflictState={listenerConflictState}
- onSave={({ groupType, strategy, listener }) => {
+ onSave={({ groupType, strategy, listener, testUrl }) => {
updateProxyGroupAdvanced(settingsModule.id, {
+ testUrl,
groupType,
...(groupType === "load-balance"
? { strategy: strategy ?? DEFAULT_LOAD_BALANCE_STRATEGY }
: { strategy: undefined }),
});
- setGroupListener(
- target,
- listener ? { port: listener.port, enabled: listener.enabled, allowLan: listener.allowLan } : null
- );
+ setGroupListener(target, listener);
}}
/>
);
diff --git a/packages/ui/src/product/converter/advanced-mode/sections/proxy-groups-custom-groups-panel.test.ts b/packages/ui/src/product/converter/advanced-mode/sections/proxy-groups-custom-groups-panel.test.ts
index fe8fd2f1..61cdd516 100644
--- a/packages/ui/src/product/converter/advanced-mode/sections/proxy-groups-custom-groups-panel.test.ts
+++ b/packages/ui/src/product/converter/advanced-mode/sections/proxy-groups-custom-groups-panel.test.ts
@@ -74,6 +74,9 @@ vi.mock("@subboost/core/generator/proxy-groups", () => ({
rules: [{ id: "builtin-a", name: "Builtin A", behavior: "domain", path: "geosite/builtin-a.mrs" }],
},
{ id: "fallback", name: "Fallback", rules: [] },
+ { id: "cn", name: "CN", rules: [
+ { id: "cn-ip", name: "CN IP", behavior: "ipcidr", path: "geoip/cn.mrs", noResolve: true },
+ ] },
],
}));
vi.mock("@subboost/core/proxy-group-name", () => ({
@@ -342,17 +345,22 @@ describe("ProxyGroupsCustomGroupsPanel", () => {
expect(mocks.captures.ruleRows).toEqual(
expect.arrayContaining([
- expect.objectContaining({ name: "Builtin A", source: "preset", state: "moved" }),
+ expect.objectContaining({ name: "Builtin A", source: "preset", state: "active" }),
]),
);
const builtinMove = mocks.captures.moveMenus.find((menu: any) => menu.ariaLabel === "移动 Builtin A 规则集");
builtinMove.onMove({ kind: "module", id: "fallback", name: "Fallback" });
- expect(mocks.store.moveModuleRule).toHaveBeenCalledWith("auto", "builtin-a", {
+ expect(mocks.store.moveModuleRule).toHaveBeenCalledWith("custom-1", "builtin-a", {
kind: "module",
id: "fallback",
name: "Fallback",
});
+ builtinMove.onMove({ kind: "module", id: "auto", name: "Auto" });
+ expect(mocks.store.moveModuleRule).toHaveBeenLastCalledWith("custom-1", "builtin-a", {
+ kind: "module", id: "auto", name: "Auto",
+ });
+
mocks.captures.buttons.find((button: any) => button["aria-label"] === "删除 Builtin A 规则集").onClick();
expect(mocks.store.removeModuleRule).toHaveBeenCalledWith("custom-1", "builtin-a");
});
@@ -378,6 +386,35 @@ describe("ProxyGroupsCustomGroupsPanel", () => {
expect(mocks.captures.ruleRows).toEqual([]);
});
+ it.each([true, false])("keeps moved CN IP no-resolve=%s and hides disabled rules", (noResolve) => {
+ mocks.store.cnIpNoResolve = noResolve;
+ mocks.store.builtinRuleEdits = {
+ "module:cn:cn-ip": { target: { kind: "custom", id: "custom-1" } },
+ };
+ renderPanel({ 0: new Set(["custom-1"]) });
+ const row = mocks.captures.ruleRows.find((item: any) => item.name === "CN IP");
+ expect(row.state).toBe("active");
+ expect(row.noResolve).toBe(noResolve);
+
+ mocks.store.builtinRuleEdits["module:cn:cn-ip"].enabled = false;
+ renderPanel({ 0: new Set(["custom-1"]) });
+ expect(mocks.captures.ruleRows.some((item: any) => item.name === "CN IP")).toBe(false);
+ });
+
+ it("follows custom target identity after rename and retargeting", () => {
+ mocks.store.builtinRuleEdits = {
+ "module:auto:builtin-a": { target: { kind: "custom", id: "custom-1" } },
+ };
+ mocks.store.customProxyGroups = [{ ...customGroup, name: "Renamed" }, targetGroup];
+ renderPanel({ 0: new Set(["custom-1", "custom-2"]) });
+ const getMenu = () => mocks.captures.moveMenus.find((menu: any) => menu.ariaLabel === "移动 Builtin A 规则集");
+ expect(getMenu().currentTarget).toEqual({ kind: "custom", id: "custom-1", name: "Renamed" });
+ mocks.store.builtinRuleEdits["module:auto:builtin-a"].target.id = "custom-2";
+ renderPanel({ 0: new Set(["custom-1", "custom-2"]) });
+ expect(getMenu().currentTarget.id).toBe("custom-2");
+ expect(mocks.captures.ruleRows.filter((item: any) => item.name === "Builtin A")).toHaveLength(1);
+ });
+
it("covers custom group edit controls and duplicate rename guard", () => {
mocks.store.customProxyGroups = [customGroup, { ...targetGroup, name: "🧩 Target" }];
const { setters } = renderPanel({ 0: new Set(["custom-1"]), 3: "custom-1", 4: "🧩 Target", 5: "" });
diff --git a/packages/ui/src/product/converter/advanced-mode/sections/proxy-groups-custom-groups-panel.tsx b/packages/ui/src/product/converter/advanced-mode/sections/proxy-groups-custom-groups-panel.tsx
index 2e2ff2be..5d013276 100644
--- a/packages/ui/src/product/converter/advanced-mode/sections/proxy-groups-custom-groups-panel.tsx
+++ b/packages/ui/src/product/converter/advanced-mode/sections/proxy-groups-custom-groups-panel.tsx
@@ -52,6 +52,7 @@ export function ProxyGroupsCustomGroupsPanel({
customRules = [],
customRuleSets = [],
builtinRuleEdits = {},
+ cnIpNoResolve,
customProxyGroups = [],
addCustomProxyGroup,
removeCustomProxyGroup,
@@ -206,7 +207,7 @@ export function ProxyGroupsCustomGroupsPanel({
value={newCustomGroupDescription}
onChange={(event) => setNewCustomGroupDescription(event.target.value)}
placeholder="描述文本(默认: 自定义代理组)"
- className="h-7 min-w-0 border-white/10 bg-white/5 text-xs"
+ className="h-7 min-w-0 border-ink/10 bg-ink/5 text-xs"
/>
暂无自定义分组
+