Skip to content

Commit 8d92544

Browse files
committed
feat(core-editor): enhance localization and improve validation error paths
1 parent 9e2ce2d commit 8d92544

8 files changed

Lines changed: 58 additions & 12 deletions

File tree

dashboard/public/statics/locales/en.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2350,7 +2350,7 @@
23502350
"finishCurrentTitle": "Finish the current outbound first",
23512351
"finishCurrentDescription": "Add it to the list, or close the dialog and discard the draft, before starting another outbound.",
23522352
"tabForm": "Form",
2353-
"tabJson": "JSON",
2353+
"tabJson": "Advanced",
23542354
"linkLabel": "Link",
23552355
"linkPlaceholder": "vmess:// vless:// trojan:// ss:// hysteria2:// …",
23562356
"linkImportApply": "Apply share link",

dashboard/public/statics/locales/fa.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2261,6 +2261,11 @@
22612261
"discardEditDescription": "اگر اکنون ببندید، تغییرات شما روی این خروجی از بین می‌رود.",
22622262
"finishCurrentTitle": "ابتدا خروجی فعلی را تمام کنید",
22632263
"finishCurrentDescription": "آن را به فهرست اضافه کنید، یا دیالوگ را ببندید و پیش‌نویس را دور بیندازید، قبل از شروع خروجی دیگر.",
2264+
"tabForm": "فرم",
2265+
"tabJson": "پیشرفته",
2266+
"linkLabel": "درون‌ریزی از لینک اشتراک‌گذاری",
2267+
"linkPlaceholder": "vmess:// vless:// trojan:// ss:// hysteria2:// ...",
2268+
"linkImportApply": "اعمال لینک اشتراک‌گذاری",
22642269
"freedom": {
22652270
"blurb": "Freedom ترافیک را بدون تغییر هدایت می‌کند. فیلدهای زیر اختیاری‌اند؛ برای اشکال نادر از تب JSON استفاده کنید.",
22662271
"domainStrategy": "استراتژی دامنه",

dashboard/public/statics/locales/ru.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,6 +2235,11 @@
22352235
"discardEditDescription": "Ваши изменения этого исходящего будут потеряны, если закрыть окно сейчас.",
22362236
"finishCurrentTitle": "Сначала завершите текущий исходящий",
22372237
"finishCurrentDescription": "Добавьте его в список или закройте диалог и отмените черновик перед созданием другого исходящего.",
2238+
"tabForm": "Форма",
2239+
"tabJson": "Дополнительно",
2240+
"linkLabel": "Импорт из ссылки",
2241+
"linkPlaceholder": "vmess:// vless:// trojan:// ss:// hysteria2:// ...",
2242+
"linkImportApply": "Применить ссылку",
22382243
"freedom": {
22392244
"blurb": "Freedom пересылает трафик как есть. Ниже — необязательные поля; редкие схемы правьте во вкладке JSON.",
22402245
"domainStrategy": "Стратегия домена",

dashboard/public/statics/locales/zh.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2308,6 +2308,11 @@
23082308
"discardEditDescription": "如果现在关闭,对此出站的修改将会丢失。",
23092309
"finishCurrentTitle": "请先完成当前出站",
23102310
"finishCurrentDescription": "请先加入列表,或关闭对话框并丢弃草稿,再开始另一个出站。",
2311+
"tabForm": "表单",
2312+
"tabJson": "高级",
2313+
"linkLabel": "从分享链接导入",
2314+
"linkPlaceholder": "vmess:// vless:// trojan:// ss:// hysteria2:// ...",
2315+
"linkImportApply": "应用分享链接",
23112316
"freedom": {
23122317
"blurb": "Freedom 按原样转发流量。以下为可选字段;少见结构请用 JSON 标签页编辑。",
23132318
"domainStrategy": "域名策略",

dashboard/src/features/core-editor/components/shared/validation-summary.tsx

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
22
import { cn } from '@/lib/utils'
33
import type { CoreKitValidationIssue } from '@pasarguard/core-kit'
4-
import type { Issue } from '@pasarguard/xray-config-kit'
4+
import type { Issue, Profile } from '@pasarguard/xray-config-kit'
55
import type { WireGuardValidationIssue } from '@pasarguard/wireguard-config-kit'
66
import { useTranslation } from 'react-i18next'
7+
import { useCoreEditorStore } from '@/features/core-editor/state/core-editor-store'
78

89
export type ValidationListItem =
910
| { source: 'core-kit'; issue: CoreKitValidationIssue }
@@ -15,6 +16,32 @@ export function validationListItemPath(item: ValidationListItem): string {
1516
return typeof p === 'string' ? p : ''
1617
}
1718

19+
function formatXrayProfilePath(path: string, profile: Profile | null | undefined): string {
20+
if (!profile || !path.startsWith('/')) return path
21+
const parts = path.split('/')
22+
if (parts.length < 3) return path
23+
24+
const collection = parts[1]
25+
if (collection === 'routing' && parts[2] === 'rules') {
26+
const ruleIndex = Number(parts[3])
27+
if (!Number.isInteger(ruleIndex) || ruleIndex < 1) return path
28+
const rule = profile.routing?.rules?.[ruleIndex - 1] as Record<string, unknown> | undefined
29+
const label = String(rule?.tag ?? rule?.outboundTag ?? rule?.balancerTag ?? '').trim() || `#${ruleIndex}`
30+
return ['/', collection, parts[2], label, ...parts.slice(4)].join('/').replace('//', '/')
31+
}
32+
33+
const rawIndex = Number(parts[2])
34+
if (!Number.isInteger(rawIndex) || rawIndex < 1) return path
35+
const index = rawIndex - 1
36+
37+
let label = ''
38+
if (collection === 'inbounds') label = String(profile.inbounds?.[index]?.tag ?? '').trim()
39+
else if (collection === 'outbounds') label = String(profile.outbounds?.[index]?.tag ?? '').trim()
40+
41+
if (!label) label = `#${rawIndex}`
42+
return ['/', collection, label, ...parts.slice(3)].join('/').replace('//', '/')
43+
}
44+
1845
/** Same semantics as the “Validation errors” list (not warnings / info-only). */
1946
export function filterValidationListBlockingErrors(items: ValidationListItem[]): ValidationListItem[] {
2047
return items.filter(i => {
@@ -61,6 +88,7 @@ const DISPLAY_LIMIT = 48
6188
/** Lists blocking issues from the Xray config kit (strict compile), core-kit, WireGuard, etc. */
6289
export function ValidationSummary({ items, className }: ValidationSummaryProps) {
6390
const { t } = useTranslation()
91+
const profile = useCoreEditorStore(s => s.xrayProfile)
6492
if (items.length === 0) return null
6593
const errors = filterValidationListBlockingErrors(items)
6694
const list = errors.length > 0 ? errors : items
@@ -81,12 +109,12 @@ export function ValidationSummary({ items, className }: ValidationSummaryProps)
81109
<li key={idx}>
82110
{row.source === 'core-kit' && (
83111
<>
84-
{row.issue.path}: {row.issue.message}
112+
{formatXrayProfilePath(row.issue.path, profile)}: {row.issue.message}
85113
</>
86114
)}
87115
{row.source === 'xray' && (
88116
<>
89-
{row.issue.path}: {row.issue.message}
117+
{formatXrayProfilePath(row.issue.path, profile)}: {row.issue.message}
90118
{row.issue.code ? (
91119
<span className="ml-1 text-[0.8em] opacity-80">[{row.issue.code}]</span>
92120
) : null}

dashboard/src/features/core-editor/components/shell/sticky-save-bar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export function StickySaveBar({
5454
<span className="relative inline-flex rounded-md">
5555
{dirty ? (
5656
<span
57-
className="absolute -end-1 -top-1 z-10 h-2.5 w-2.5 rounded-full bg-amber-500 ring-2 ring-background"
57+
className="absolute -right-1 -top-1 z-10 h-2.5 w-2.5 rounded-full bg-amber-500 ring-2 ring-background"
5858
aria-hidden
5959
/>
6060
) : null}

dashboard/src/features/core-editor/components/xray/xray-outbounds-section.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,7 @@ export function XrayOutboundsSection({ headerAddPulse, headerAddEpoch }: XrayOut
604604
setOutboundJsonText(JSON.stringify(outboundEditorBodyFromOutbound(normalized), null, 2))
605605
setSettingsFormSeed(s => s + 1)
606606
form.reset(buildOutboundDetailFormValues(normalized, outboundCapsRef.current))
607+
setUriDraft('')
607608
toast.success(t('success', { defaultValue: 'Success' }))
608609
} catch (e) {
609610
toast.error(t('coreEditor.outbound.uriImportFailed', { defaultValue: 'Could not parse that share link.' }), {
@@ -777,7 +778,9 @@ export function XrayOutboundsSection({ headerAddPulse, headerAddEpoch }: XrayOut
777778
<Input
778779
value={uriDraft}
779780
onChange={e => setUriDraft(e.target.value)}
780-
placeholder="vmess:// vless:// trojan:// ss:// hysteria2:// …"
781+
placeholder={t('coreEditor.outbound.linkPlaceholder', {
782+
defaultValue: 'vmess:// vless:// trojan:// ss:// hysteria2:// ...',
783+
})}
781784
className="text-xs"
782785
dir="ltr"
783786
onKeyDown={e => {
@@ -800,12 +803,12 @@ export function XrayOutboundsSection({ headerAddPulse, headerAddEpoch }: XrayOut
800803
</div>
801804
</div>
802805

803-
<TabsList className="grid h-13 w-full grid-cols-2 gap-1 p-1">
804-
<TabsTrigger value="form" className="h-full rounded-md py-2.5 text-sm sm:py-3">
806+
<TabsList className="mx-auto grid h-11 w-full max-w-xs grid-cols-2 gap-1 p-1">
807+
<TabsTrigger value="form" className="h-9 rounded-sm px-4 py-1 text-xs">
805808
{t('coreEditor.outbound.tabForm', { defaultValue: 'Form' })}
806809
</TabsTrigger>
807-
<TabsTrigger value="json" className="h-full rounded-md py-2.5 text-sm sm:py-3">
808-
{t('coreEditor.outbound.tabJson', { defaultValue: 'JSON' })}
810+
<TabsTrigger value="json" className="h-9 rounded-sm px-4 py-1 text-xs">
811+
{t('coreEditor.outbound.tabJson', { defaultValue: 'Advanced' })}
809812
</TabsTrigger>
810813
</TabsList>
811814

dashboard/src/features/core-editor/routes/core-editor-page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,11 +413,11 @@ export default function CoreEditorPage() {
413413
type="button"
414414
variant="ghost"
415415
size="icon"
416-
className={cn("mt-0.5 shrink-0 sm:mt-0",dir==='rtl' && 'rotate-180')}
416+
className={cn('mt-0.5 h-11 w-11 shrink-0 sm:mt-0', dir === 'rtl' && 'rotate-180')}
417417
onClick={handleBack}
418418
aria-label={t('back', { defaultValue: 'Back' })}
419419
>
420-
<ArrowLeft className="h-4 w-4" />
420+
<ArrowLeft className="h-5 w-5" />
421421
</Button>
422422
<div className="min-w-0 flex-1 space-y-2">
423423
<div className="flex min-w-0 flex-row flex-wrap items-center gap-2 sm:gap-3">

0 commit comments

Comments
 (0)