diff --git a/frontend/src/pages/CustomConfigDetail/index.tsx b/frontend/src/pages/CustomConfigDetail/index.tsx
index b1cbae7..a87ec46 100644
--- a/frontend/src/pages/CustomConfigDetail/index.tsx
+++ b/frontend/src/pages/CustomConfigDetail/index.tsx
@@ -1,11 +1,12 @@
import { useState, useEffect, useMemo, useCallback, useRef } from 'react'
-import { useParams, useNavigate, useSearchParams, useBlocker } from 'react-router-dom'
+import { useParams, useSearchParams, useBlocker } from 'react-router-dom'
+import { useBreadcrumb } from '@/store/breadcrumb'
import type { Blocker } from 'react-router-dom'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
+import yaml from 'js-yaml'
import {
- ArrowLeft,
Plus,
Trash2,
Edit,
@@ -41,10 +42,12 @@ import {
import { CSS } from '@dnd-kit/utilities'
import equal from 'fast-deep-equal'
import { customConfigsApi } from '@/api/custom-configs'
-import { ruleProvidersApi } from '@/api/rule-providers'
-import { hostedRuleSetsApi } from '@/api/hosted-rule-sets'
+import { configHistoryApi } from '@/api/config-history'
+import type { QueryClient } from '@tanstack/react-query'
+
+import { ruleSetsApi } from '@/api/rule-sets'
import { providersApi } from '@/api/providers'
-import type { ProxyNode, ProxyGroup } from '@/types'
+import type { ProxyGroup } from '@/types'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Badge } from '@/components/ui/badge'
@@ -66,17 +69,21 @@ import {
import { Sheet, SheetClose, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet'
import { YamlEditor } from '@/components/YamlEditor'
import { ConfigPayloadDiffDialog } from '@/components/ConfigPayloadDiffDialog'
+import { HistoryDiffDialog } from '@/components/HistoryDiffDialog'
import { cn } from '@/lib/utils'
import { useRegisterContextSaveBar } from '@/store/context-save-bar'
import { hasProxyOrGroupNameConflict, renameProxyOrGroupRefs } from '@/lib/rename-refs'
+import type { ParsedRule } from '@/domain/rules'
import {
buildRuleAnalysis,
canUseMatchType,
hasMatchRule as hasMatchRuleInList,
insertRule,
parseRule,
- type ParsedRule,
parseRulesText,
+ parseRulesTextFull,
+ parseRulesWithComments,
+ serializeRulesWithComments,
type RuleQuickFixAction,
RULE_TEMPLATE_MAP,
ruleSupportsNoResolve,
@@ -87,12 +94,7 @@ import {
import { getRuleTypeMeta } from './rules/RuleTypeMeta'
import { RuleStatusIndicator } from './rules/RuleStatusIndicator'
import { SortableRuleRow } from './rules/SortableRuleRow'
-import { ProxyDialog } from './proxies/ProxyDialog'
import { ProxyGroupDialog } from './proxies/ProxyGroupDialog'
-import {
- makeUniqueDuplicateProxyName,
- buildDuplicatedProxyNode,
-} from './proxies/proxy-form'
import {
SORTABLE_TABLE_LAYOUT,
sortableInstantReorder,
@@ -110,6 +112,14 @@ import {
} from './shared/constants'
+function draftToYamlText(obj: object): string {
+ try {
+ return yaml.dump(obj, { lineWidth: -1, quotingType: '"', forceQuotes: false })
+ } catch {
+ return JSON.stringify(obj, null, 2)
+ }
+}
+
function buildRuleSaveChecklist(
finalRules: string[],
currentRuleStrings: string[],
@@ -166,7 +176,7 @@ function SortableProxyGroupRow({
)}
onClick={() => onEdit(group, idx)}
>
- | e.stopPropagation()}>
+ | e.stopPropagation()}>
|
- {group.name} |
-
- {group.type}
+ |
+
+ {group.icon && (
+  { (e.currentTarget as HTMLImageElement).style.display = 'none' }}
+ />
+ )}
+ {group.name}
+
|
-
+ |
+ {group.type}
+ |
+
{(group.proxies || []).slice(0, 3).join(', ')}
{(group.proxies || []).length > 3 && ` +${(group.proxies || []).length - 3}`}
|
- e.stopPropagation()}>
+ {/* 移动端紧凑信息列 */}
+ |
+ {group.type}
+ |
+ e.stopPropagation()}>
|