2026-05-08 12:13:26 +00:00
|
|
|
<script setup>
|
2026-05-08 15:41:01 +00:00
|
|
|
import { computed, ref } from 'vue';
|
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards
Replaces hardcoded English with t() calls in the components every
user sees on every page load. The translations themselves come from
the existing TOML files via the sync script — no new strings, no
new locale keys.
Per component:
- AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings /
xray / logout). Computed so the sidebar re-renders when the
cookie-driven locale flips on reload.
- IndexPage.vue: Quick actions card title + Logs / Backup / Up-to-
date / Update buttons.
- StatusCard.vue: CPU / Memory / Swap / Storage labels +
logical-processors / frequency tooltips.
- XrayStatusCard.vue: card title + error popover header + Stop /
Restart / Switch xray action labels (kept the v-prefix version
string as-is — it's content, not a label).
- SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons +
unsaved-changes warning.
- XrayPage.vue: 6 tab titles + Save / Restart-xray buttons +
unsaved-changes warning.
- InboundsPage.vue: 5 summary-stat card titles.
- InboundList.vue: 10 column titles (computed for live locale),
Add inbound / General actions buttons + every dropdown menu item,
search placeholder, filter radio labels, popover titles
(disabled / depleted / depleting / online), traffic + info
popover row labels.
Total: ~75 strings localised across 8 files. The remaining English
labels live in the per-tab settings forms, the form modals
(Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and
the per-row table cell helpers — all incremental work that doesn't
touch infrastructure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
|
|
|
import { useI18n } from 'vue-i18n';
|
2026-05-08 15:39:36 +00:00
|
|
|
import { Modal, message } from 'ant-design-vue';
|
2026-05-08 12:13:26 +00:00
|
|
|
import {
|
|
|
|
|
SettingOutlined,
|
|
|
|
|
SwapOutlined,
|
|
|
|
|
UploadOutlined,
|
|
|
|
|
ClusterOutlined,
|
|
|
|
|
DatabaseOutlined,
|
|
|
|
|
CodeOutlined,
|
|
|
|
|
QuestionCircleOutlined,
|
|
|
|
|
} from '@ant-design/icons-vue';
|
|
|
|
|
|
2026-05-08 15:39:36 +00:00
|
|
|
import { theme as themeState, antdThemeConfig } from '@/composables/useTheme.js';
|
2026-05-08 12:13:26 +00:00
|
|
|
import { useMediaQuery } from '@/composables/useMediaQuery.js';
|
|
|
|
|
import AppSidebar from '@/components/AppSidebar.vue';
|
feat(frontend): Phase 6-ii — xray Basics tab structured editor
Replaces the placeholder on the Basics tab with a structured form for
the most-touched fields of the xray template — outbound + routing
strategy, log levels, traffic stat counters, and the "basic routing"
shortcuts (block torrent / IPs / domains, direct IPs / domains, IPv4
forced, WARP / NordVPN routing).
- useXraySetting.js: hoists a parsed `templateSettings` reactive
alongside the JSON string, with two cooperating watches that keep
them in sync. Editing structured fields stringifies into xraySetting
for the dirty-poll + Advanced JSON tab; editing the JSON re-parses
into templateSettings only when valid, so structured tabs stay
readable mid-edit.
- BasicsTab.vue: collapse panels mirror the legacy partial — General,
Statistics, Logs, Basic routing. Every input is a computed v-model
reading/writing into templateSettings; the routing-rule shortcuts
funnel through ruleGetter/ruleSetter which match the legacy
templateRuleGetter/templateRuleSetter behavior (replace-first,
drop-duplicates, pop-the-rule-when-empty). Direct/IPv4 setters
also call syncOutbound() to provision/prune the matching outbound.
- XrayPage.vue: imports BasicsTab + derives `warpExist`/`nordExist`
from the parsed templateSettings. WARP/NordVPN provisioning modals
are still placeholders that toast — those land in 6-v with the
routing/outbound editors.
Default tab flips back to Basics so users land on the structured
editor.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:18:21 +00:00
|
|
|
import BasicsTab from './BasicsTab.vue';
|
2026-05-08 12:23:02 +00:00
|
|
|
import RoutingTab from './RoutingTab.vue';
|
2026-05-08 12:27:40 +00:00
|
|
|
import OutboundsTab from './OutboundsTab.vue';
|
2026-05-08 12:30:48 +00:00
|
|
|
import BalancersTab from './BalancersTab.vue';
|
|
|
|
|
import DnsTab from './DnsTab.vue';
|
feat(frontend): Phase 6-vi — WARP + NordVPN provisioning modals
Replaces the toast stubs on the Basics tab and Outbounds toolbar
with the legacy WARP + NordVPN provisioning flows. Both modals now
stage their wireguard outbounds back into templateSettings.outbounds
through the same event channels OutboundsTab uses, so the existing
add / reset / delete / refresh-traffic surface keeps working.
- WarpModal.vue: empty state shows a single Create button that
generates a wireguard keypair locally (Wireguard.generateKeypair)
and posts it to /panel/xray/warp/reg; populated state surfaces
the access_token / device_id / license_key / private_key, lets
the user upgrade to WARP+ via /panel/xray/warp/license, refreshes
the account info from /panel/xray/warp/config (plan / quota /
usage in human-readable bytes), and stages a wireguard outbound
with the WARP-specific reserved-byte encoding pulled from
client_id. Add / Reset / Delete go through events the parent
routes back to templateSettings.outbounds.
- NordModal.vue: dual-tab login (NordVPN access token →
/panel/xray/nord/reg, or paste a NordLynx private key →
/panel/xray/nord/setKey). Once authenticated, country / city /
server selectors fetch from /panel/xray/nord/{countries,servers},
servers sort by load ascending, the lowest-load server in the
current city auto-selects. Reset emits oldTag/newTag so the
parent renames matching routing rules in place; logout emits a
remove-routing-rules event with prefix `nord-` to purge any
dangling references.
- XrayPage.vue: holds warpOpen / nordOpen flags, ensures the
outbounds array exists before mutating it, and wires the modal
events (add-outbound / reset-outbound / remove-outbound /
remove-routing-rules) to in-place edits of templateSettings.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:44:46 +00:00
|
|
|
import WarpModal from './WarpModal.vue';
|
|
|
|
|
import NordModal from './NordModal.vue';
|
2026-05-08 12:13:26 +00:00
|
|
|
import { useXraySetting } from './useXraySetting.js';
|
|
|
|
|
|
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards
Replaces hardcoded English with t() calls in the components every
user sees on every page load. The translations themselves come from
the existing TOML files via the sync script — no new strings, no
new locale keys.
Per component:
- AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings /
xray / logout). Computed so the sidebar re-renders when the
cookie-driven locale flips on reload.
- IndexPage.vue: Quick actions card title + Logs / Backup / Up-to-
date / Update buttons.
- StatusCard.vue: CPU / Memory / Swap / Storage labels +
logical-processors / frequency tooltips.
- XrayStatusCard.vue: card title + error popover header + Stop /
Restart / Switch xray action labels (kept the v-prefix version
string as-is — it's content, not a label).
- SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons +
unsaved-changes warning.
- XrayPage.vue: 6 tab titles + Save / Restart-xray buttons +
unsaved-changes warning.
- InboundsPage.vue: 5 summary-stat card titles.
- InboundList.vue: 10 column titles (computed for live locale),
Add inbound / General actions buttons + every dropdown menu item,
search placeholder, filter radio labels, popover titles
(disabled / depleted / depleting / online), traffic + info
popover row labels.
Total: ~75 strings localised across 8 files. The remaining English
labels live in the per-tab settings forms, the form modals
(Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and
the per-row table cell helpers — all incremental work that doesn't
touch infrastructure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
|
|
|
const { t } = useI18n();
|
|
|
|
|
|
2026-05-08 12:13:26 +00:00
|
|
|
const {
|
|
|
|
|
fetched,
|
|
|
|
|
spinning,
|
|
|
|
|
saveDisabled,
|
2026-05-08 15:20:30 +00:00
|
|
|
fetchError,
|
2026-05-08 12:13:26 +00:00
|
|
|
xraySetting,
|
feat(frontend): Phase 6-ii — xray Basics tab structured editor
Replaces the placeholder on the Basics tab with a structured form for
the most-touched fields of the xray template — outbound + routing
strategy, log levels, traffic stat counters, and the "basic routing"
shortcuts (block torrent / IPs / domains, direct IPs / domains, IPv4
forced, WARP / NordVPN routing).
- useXraySetting.js: hoists a parsed `templateSettings` reactive
alongside the JSON string, with two cooperating watches that keep
them in sync. Editing structured fields stringifies into xraySetting
for the dirty-poll + Advanced JSON tab; editing the JSON re-parses
into templateSettings only when valid, so structured tabs stay
readable mid-edit.
- BasicsTab.vue: collapse panels mirror the legacy partial — General,
Statistics, Logs, Basic routing. Every input is a computed v-model
reading/writing into templateSettings; the routing-rule shortcuts
funnel through ruleGetter/ruleSetter which match the legacy
templateRuleGetter/templateRuleSetter behavior (replace-first,
drop-duplicates, pop-the-rule-when-empty). Direct/IPv4 setters
also call syncOutbound() to provision/prune the matching outbound.
- XrayPage.vue: imports BasicsTab + derives `warpExist`/`nordExist`
from the parsed templateSettings. WARP/NordVPN provisioning modals
are still placeholders that toast — those land in 6-v with the
routing/outbound editors.
Default tab flips back to Basics so users land on the structured
editor.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:18:21 +00:00
|
|
|
templateSettings,
|
2026-05-08 12:13:26 +00:00
|
|
|
outboundTestUrl,
|
2026-05-08 12:23:02 +00:00
|
|
|
inboundTags,
|
|
|
|
|
clientReverseTags,
|
2026-05-08 12:13:26 +00:00
|
|
|
restartResult,
|
2026-05-08 12:27:40 +00:00
|
|
|
outboundsTraffic,
|
|
|
|
|
outboundTestStates,
|
2026-05-08 15:20:30 +00:00
|
|
|
fetchAll,
|
2026-05-08 12:27:40 +00:00
|
|
|
fetchOutboundsTraffic,
|
|
|
|
|
resetOutboundsTraffic,
|
|
|
|
|
testOutbound,
|
2026-05-08 12:13:26 +00:00
|
|
|
saveAll,
|
|
|
|
|
restartXray,
|
|
|
|
|
} = useXraySetting();
|
feat(frontend): Phase 6-ii — xray Basics tab structured editor
Replaces the placeholder on the Basics tab with a structured form for
the most-touched fields of the xray template — outbound + routing
strategy, log levels, traffic stat counters, and the "basic routing"
shortcuts (block torrent / IPs / domains, direct IPs / domains, IPv4
forced, WARP / NordVPN routing).
- useXraySetting.js: hoists a parsed `templateSettings` reactive
alongside the JSON string, with two cooperating watches that keep
them in sync. Editing structured fields stringifies into xraySetting
for the dirty-poll + Advanced JSON tab; editing the JSON re-parses
into templateSettings only when valid, so structured tabs stay
readable mid-edit.
- BasicsTab.vue: collapse panels mirror the legacy partial — General,
Statistics, Logs, Basic routing. Every input is a computed v-model
reading/writing into templateSettings; the routing-rule shortcuts
funnel through ruleGetter/ruleSetter which match the legacy
templateRuleGetter/templateRuleSetter behavior (replace-first,
drop-duplicates, pop-the-rule-when-empty). Direct/IPv4 setters
also call syncOutbound() to provision/prune the matching outbound.
- XrayPage.vue: imports BasicsTab + derives `warpExist`/`nordExist`
from the parsed templateSettings. WARP/NordVPN provisioning modals
are still placeholders that toast — those land in 6-v with the
routing/outbound editors.
Default tab flips back to Basics so users land on the structured
editor.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:18:21 +00:00
|
|
|
|
2026-05-08 12:27:40 +00:00
|
|
|
async function onTestOutbound(idx) {
|
|
|
|
|
const outbound = templateSettings.value?.outbounds?.[idx];
|
|
|
|
|
if (outbound) await testOutbound(idx, outbound);
|
|
|
|
|
}
|
|
|
|
|
|
feat(frontend): Phase 6-ii — xray Basics tab structured editor
Replaces the placeholder on the Basics tab with a structured form for
the most-touched fields of the xray template — outbound + routing
strategy, log levels, traffic stat counters, and the "basic routing"
shortcuts (block torrent / IPs / domains, direct IPs / domains, IPv4
forced, WARP / NordVPN routing).
- useXraySetting.js: hoists a parsed `templateSettings` reactive
alongside the JSON string, with two cooperating watches that keep
them in sync. Editing structured fields stringifies into xraySetting
for the dirty-poll + Advanced JSON tab; editing the JSON re-parses
into templateSettings only when valid, so structured tabs stay
readable mid-edit.
- BasicsTab.vue: collapse panels mirror the legacy partial — General,
Statistics, Logs, Basic routing. Every input is a computed v-model
reading/writing into templateSettings; the routing-rule shortcuts
funnel through ruleGetter/ruleSetter which match the legacy
templateRuleGetter/templateRuleSetter behavior (replace-first,
drop-duplicates, pop-the-rule-when-empty). Direct/IPv4 setters
also call syncOutbound() to provision/prune the matching outbound.
- XrayPage.vue: imports BasicsTab + derives `warpExist`/`nordExist`
from the parsed templateSettings. WARP/NordVPN provisioning modals
are still placeholders that toast — those land in 6-v with the
routing/outbound editors.
Default tab flips back to Basics so users land on the structured
editor.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:18:21 +00:00
|
|
|
// `WarpExist` / `NordExist` derive from the parsed templateSettings —
|
|
|
|
|
// the Basics tab gates its WARP / NordVPN domain selectors on whether
|
|
|
|
|
// the matching outbound is provisioned, falling back to a "configure"
|
|
|
|
|
// button that today just toasts (the modals land in 6-v).
|
|
|
|
|
const warpExist = computed(
|
|
|
|
|
() => !!templateSettings.value?.outbounds?.find((o) => o?.tag === 'warp'),
|
|
|
|
|
);
|
|
|
|
|
const nordExist = computed(
|
|
|
|
|
() => !!templateSettings.value?.outbounds?.find((o) => o?.tag?.startsWith?.('nord-')),
|
|
|
|
|
);
|
|
|
|
|
|
feat(frontend): Phase 6-vi — WARP + NordVPN provisioning modals
Replaces the toast stubs on the Basics tab and Outbounds toolbar
with the legacy WARP + NordVPN provisioning flows. Both modals now
stage their wireguard outbounds back into templateSettings.outbounds
through the same event channels OutboundsTab uses, so the existing
add / reset / delete / refresh-traffic surface keeps working.
- WarpModal.vue: empty state shows a single Create button that
generates a wireguard keypair locally (Wireguard.generateKeypair)
and posts it to /panel/xray/warp/reg; populated state surfaces
the access_token / device_id / license_key / private_key, lets
the user upgrade to WARP+ via /panel/xray/warp/license, refreshes
the account info from /panel/xray/warp/config (plan / quota /
usage in human-readable bytes), and stages a wireguard outbound
with the WARP-specific reserved-byte encoding pulled from
client_id. Add / Reset / Delete go through events the parent
routes back to templateSettings.outbounds.
- NordModal.vue: dual-tab login (NordVPN access token →
/panel/xray/nord/reg, or paste a NordLynx private key →
/panel/xray/nord/setKey). Once authenticated, country / city /
server selectors fetch from /panel/xray/nord/{countries,servers},
servers sort by load ascending, the lowest-load server in the
current city auto-selects. Reset emits oldTag/newTag so the
parent renames matching routing rules in place; logout emits a
remove-routing-rules event with prefix `nord-` to purge any
dangling references.
- XrayPage.vue: holds warpOpen / nordOpen flags, ensures the
outbounds array exists before mutating it, and wires the modal
events (add-outbound / reset-outbound / remove-outbound /
remove-routing-rules) to in-place edits of templateSettings.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:44:46 +00:00
|
|
|
// === WARP / NordVPN provisioning modals ============================
|
|
|
|
|
const warpOpen = ref(false);
|
|
|
|
|
const nordOpen = ref(false);
|
|
|
|
|
|
|
|
|
|
function showWarp() { warpOpen.value = true; }
|
|
|
|
|
function showNord() { nordOpen.value = true; }
|
|
|
|
|
|
|
|
|
|
function ensureOutbounds() {
|
|
|
|
|
if (!templateSettings.value) return null;
|
|
|
|
|
if (!Array.isArray(templateSettings.value.outbounds)) {
|
|
|
|
|
templateSettings.value.outbounds = [];
|
|
|
|
|
}
|
|
|
|
|
return templateSettings.value.outbounds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onAddOutbound(outbound) {
|
|
|
|
|
const list = ensureOutbounds();
|
|
|
|
|
if (list) list.push(outbound);
|
|
|
|
|
}
|
|
|
|
|
function onResetOutbound({ index, outbound, oldTag, newTag }) {
|
|
|
|
|
const list = ensureOutbounds();
|
|
|
|
|
if (!list || index < 0) return;
|
|
|
|
|
list[index] = outbound;
|
|
|
|
|
// Tag rename across routing rules — preserves Nord's
|
|
|
|
|
// server-switch flow without dangling references.
|
|
|
|
|
if (oldTag && newTag && oldTag !== newTag) {
|
|
|
|
|
const rules = templateSettings.value?.routing?.rules || [];
|
|
|
|
|
for (const r of rules) {
|
|
|
|
|
if (r?.outboundTag === oldTag) r.outboundTag = newTag;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
function onRemoveOutboundByTag(tag) {
|
|
|
|
|
const list = ensureOutbounds();
|
|
|
|
|
if (!list) return;
|
|
|
|
|
const idx = list.findIndex((o) => o?.tag === tag);
|
|
|
|
|
if (idx >= 0) list.splice(idx, 1);
|
|
|
|
|
}
|
|
|
|
|
function onRemoveOutboundByIndex(index) {
|
|
|
|
|
const list = ensureOutbounds();
|
|
|
|
|
if (list && index >= 0) list.splice(index, 1);
|
|
|
|
|
}
|
|
|
|
|
function onRemoveRoutingRules({ prefix }) {
|
|
|
|
|
const rules = templateSettings.value?.routing?.rules;
|
|
|
|
|
if (!Array.isArray(rules)) return;
|
|
|
|
|
templateSettings.value.routing.rules = rules.filter(
|
|
|
|
|
(r) => !r?.outboundTag?.startsWith?.(prefix),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `message` is used by some of the in-progress UX flows (kept around
|
|
|
|
|
// because future provisioning errors will surface through it).
|
|
|
|
|
void message;
|
2026-05-08 12:13:26 +00:00
|
|
|
const { isMobile } = useMediaQuery();
|
|
|
|
|
|
|
|
|
|
const basePath = window.__X_UI_BASE_PATH__ || '';
|
|
|
|
|
const requestUri = window.location.pathname;
|
|
|
|
|
|
2026-05-08 15:20:30 +00:00
|
|
|
// See SettingsPage scrollTarget — wrap so `document` is in scope.
|
|
|
|
|
function scrollTarget() {
|
|
|
|
|
return document.getElementById('content-layout');
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-08 12:13:26 +00:00
|
|
|
function confirmRestart() {
|
|
|
|
|
Modal.confirm({
|
|
|
|
|
title: 'Restart xray?',
|
|
|
|
|
content: 'Reloads the xray service with the saved configuration.',
|
|
|
|
|
okText: 'Restart',
|
|
|
|
|
cancelText: 'Cancel',
|
|
|
|
|
onOk: () => restartXray(),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<a-config-provider :theme="antdThemeConfig">
|
|
|
|
|
<a-layout
|
|
|
|
|
class="xray-page"
|
|
|
|
|
:class="{ 'is-dark': themeState.isDark, 'is-ultra': themeState.isUltra }"
|
|
|
|
|
>
|
|
|
|
|
<AppSidebar :base-path="basePath" :request-uri="requestUri" />
|
|
|
|
|
|
|
|
|
|
<a-layout class="content-shell">
|
|
|
|
|
<a-layout-content id="content-layout" class="content-area">
|
|
|
|
|
<a-spin :spinning="spinning || !fetched" :delay="200" tip="Loading…" size="large">
|
|
|
|
|
<div v-if="!fetched" class="loading-spacer" />
|
|
|
|
|
|
2026-05-08 15:20:30 +00:00
|
|
|
<a-result
|
|
|
|
|
v-else-if="fetchError"
|
|
|
|
|
status="error"
|
|
|
|
|
:title="t('somethingWentWrong')"
|
|
|
|
|
:sub-title="fetchError"
|
|
|
|
|
>
|
|
|
|
|
<template #extra>
|
|
|
|
|
<a-button type="primary" @click="fetchAll">{{ t('check') }}</a-button>
|
|
|
|
|
</template>
|
|
|
|
|
</a-result>
|
|
|
|
|
|
2026-05-08 12:13:26 +00:00
|
|
|
<template v-else>
|
|
|
|
|
<a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]">
|
|
|
|
|
<!-- Save / Restart bar -->
|
|
|
|
|
<a-col :span="24">
|
|
|
|
|
<a-card hoverable>
|
|
|
|
|
<a-row class="header-row">
|
|
|
|
|
<a-col :xs="24" :sm="14" class="header-actions">
|
|
|
|
|
<a-space direction="horizontal">
|
|
|
|
|
<a-button type="primary" :disabled="saveDisabled" @click="saveAll">
|
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards
Replaces hardcoded English with t() calls in the components every
user sees on every page load. The translations themselves come from
the existing TOML files via the sync script — no new strings, no
new locale keys.
Per component:
- AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings /
xray / logout). Computed so the sidebar re-renders when the
cookie-driven locale flips on reload.
- IndexPage.vue: Quick actions card title + Logs / Backup / Up-to-
date / Update buttons.
- StatusCard.vue: CPU / Memory / Swap / Storage labels +
logical-processors / frequency tooltips.
- XrayStatusCard.vue: card title + error popover header + Stop /
Restart / Switch xray action labels (kept the v-prefix version
string as-is — it's content, not a label).
- SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons +
unsaved-changes warning.
- XrayPage.vue: 6 tab titles + Save / Restart-xray buttons +
unsaved-changes warning.
- InboundsPage.vue: 5 summary-stat card titles.
- InboundList.vue: 10 column titles (computed for live locale),
Add inbound / General actions buttons + every dropdown menu item,
search placeholder, filter radio labels, popover titles
(disabled / depleted / depleting / online), traffic + info
popover row labels.
Total: ~75 strings localised across 8 files. The remaining English
labels live in the per-tab settings forms, the form modals
(Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and
the per-row table cell helpers — all incremental work that doesn't
touch infrastructure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
|
|
|
{{ t('pages.xray.save') }}
|
2026-05-08 12:13:26 +00:00
|
|
|
</a-button>
|
|
|
|
|
<a-button type="primary" danger :disabled="!saveDisabled" @click="confirmRestart">
|
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards
Replaces hardcoded English with t() calls in the components every
user sees on every page load. The translations themselves come from
the existing TOML files via the sync script — no new strings, no
new locale keys.
Per component:
- AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings /
xray / logout). Computed so the sidebar re-renders when the
cookie-driven locale flips on reload.
- IndexPage.vue: Quick actions card title + Logs / Backup / Up-to-
date / Update buttons.
- StatusCard.vue: CPU / Memory / Swap / Storage labels +
logical-processors / frequency tooltips.
- XrayStatusCard.vue: card title + error popover header + Stop /
Restart / Switch xray action labels (kept the v-prefix version
string as-is — it's content, not a label).
- SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons +
unsaved-changes warning.
- XrayPage.vue: 6 tab titles + Save / Restart-xray buttons +
unsaved-changes warning.
- InboundsPage.vue: 5 summary-stat card titles.
- InboundList.vue: 10 column titles (computed for live locale),
Add inbound / General actions buttons + every dropdown menu item,
search placeholder, filter radio labels, popover titles
(disabled / depleted / depleting / online), traffic + info
popover row labels.
Total: ~75 strings localised across 8 files. The remaining English
labels live in the per-tab settings forms, the form modals
(Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and
the per-row table cell helpers — all incremental work that doesn't
touch infrastructure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
|
|
|
{{ t('pages.xray.restart') }}
|
2026-05-08 12:13:26 +00:00
|
|
|
</a-button>
|
|
|
|
|
<a-popover v-if="restartResult" placement="rightTop">
|
|
|
|
|
<template #title>Xray restart output</template>
|
|
|
|
|
<template #content>
|
|
|
|
|
<pre class="restart-result">{{ restartResult }}</pre>
|
|
|
|
|
</template>
|
|
|
|
|
<QuestionCircleOutlined class="restart-icon" />
|
|
|
|
|
</a-popover>
|
|
|
|
|
</a-space>
|
|
|
|
|
</a-col>
|
|
|
|
|
<a-col :xs="24" :sm="10" class="header-info">
|
2026-05-08 15:20:30 +00:00
|
|
|
<a-back-top :target="scrollTarget" :visibility-height="200" />
|
2026-05-08 12:13:26 +00:00
|
|
|
<a-alert
|
|
|
|
|
type="warning"
|
|
|
|
|
show-icon
|
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards
Replaces hardcoded English with t() calls in the components every
user sees on every page load. The translations themselves come from
the existing TOML files via the sync script — no new strings, no
new locale keys.
Per component:
- AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings /
xray / logout). Computed so the sidebar re-renders when the
cookie-driven locale flips on reload.
- IndexPage.vue: Quick actions card title + Logs / Backup / Up-to-
date / Update buttons.
- StatusCard.vue: CPU / Memory / Swap / Storage labels +
logical-processors / frequency tooltips.
- XrayStatusCard.vue: card title + error popover header + Stop /
Restart / Switch xray action labels (kept the v-prefix version
string as-is — it's content, not a label).
- SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons +
unsaved-changes warning.
- XrayPage.vue: 6 tab titles + Save / Restart-xray buttons +
unsaved-changes warning.
- InboundsPage.vue: 5 summary-stat card titles.
- InboundList.vue: 10 column titles (computed for live locale),
Add inbound / General actions buttons + every dropdown menu item,
search placeholder, filter radio labels, popover titles
(disabled / depleted / depleting / online), traffic + info
popover row labels.
Total: ~75 strings localised across 8 files. The remaining English
labels live in the per-tab settings forms, the form modals
(Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and
the per-row table cell helpers — all incremental work that doesn't
touch infrastructure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
|
|
|
:message="t('pages.settings.infoDesc')"
|
2026-05-08 12:13:26 +00:00
|
|
|
/>
|
|
|
|
|
</a-col>
|
|
|
|
|
</a-row>
|
|
|
|
|
</a-card>
|
|
|
|
|
</a-col>
|
|
|
|
|
|
|
|
|
|
<!-- Tabs -->
|
|
|
|
|
<a-col :span="24">
|
feat(frontend): Phase 6-ii — xray Basics tab structured editor
Replaces the placeholder on the Basics tab with a structured form for
the most-touched fields of the xray template — outbound + routing
strategy, log levels, traffic stat counters, and the "basic routing"
shortcuts (block torrent / IPs / domains, direct IPs / domains, IPv4
forced, WARP / NordVPN routing).
- useXraySetting.js: hoists a parsed `templateSettings` reactive
alongside the JSON string, with two cooperating watches that keep
them in sync. Editing structured fields stringifies into xraySetting
for the dirty-poll + Advanced JSON tab; editing the JSON re-parses
into templateSettings only when valid, so structured tabs stay
readable mid-edit.
- BasicsTab.vue: collapse panels mirror the legacy partial — General,
Statistics, Logs, Basic routing. Every input is a computed v-model
reading/writing into templateSettings; the routing-rule shortcuts
funnel through ruleGetter/ruleSetter which match the legacy
templateRuleGetter/templateRuleSetter behavior (replace-first,
drop-duplicates, pop-the-rule-when-empty). Direct/IPv4 setters
also call syncOutbound() to provision/prune the matching outbound.
- XrayPage.vue: imports BasicsTab + derives `warpExist`/`nordExist`
from the parsed templateSettings. WARP/NordVPN provisioning modals
are still placeholders that toast — those land in 6-v with the
routing/outbound editors.
Default tab flips back to Basics so users land on the structured
editor.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:18:21 +00:00
|
|
|
<a-tabs default-active-key="tpl-basic">
|
2026-05-08 12:13:26 +00:00
|
|
|
<a-tab-pane key="tpl-basic" class="tab-pane">
|
|
|
|
|
<template #tab>
|
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards
Replaces hardcoded English with t() calls in the components every
user sees on every page load. The translations themselves come from
the existing TOML files via the sync script — no new strings, no
new locale keys.
Per component:
- AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings /
xray / logout). Computed so the sidebar re-renders when the
cookie-driven locale flips on reload.
- IndexPage.vue: Quick actions card title + Logs / Backup / Up-to-
date / Update buttons.
- StatusCard.vue: CPU / Memory / Swap / Storage labels +
logical-processors / frequency tooltips.
- XrayStatusCard.vue: card title + error popover header + Stop /
Restart / Switch xray action labels (kept the v-prefix version
string as-is — it's content, not a label).
- SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons +
unsaved-changes warning.
- XrayPage.vue: 6 tab titles + Save / Restart-xray buttons +
unsaved-changes warning.
- InboundsPage.vue: 5 summary-stat card titles.
- InboundList.vue: 10 column titles (computed for live locale),
Add inbound / General actions buttons + every dropdown menu item,
search placeholder, filter radio labels, popover titles
(disabled / depleted / depleting / online), traffic + info
popover row labels.
Total: ~75 strings localised across 8 files. The remaining English
labels live in the per-tab settings forms, the form modals
(Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and
the per-row table cell helpers — all incremental work that doesn't
touch infrastructure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
|
|
|
<SettingOutlined /> <span>{{ t('pages.xray.basicTemplate') }}</span>
|
2026-05-08 12:13:26 +00:00
|
|
|
</template>
|
feat(frontend): Phase 6-ii — xray Basics tab structured editor
Replaces the placeholder on the Basics tab with a structured form for
the most-touched fields of the xray template — outbound + routing
strategy, log levels, traffic stat counters, and the "basic routing"
shortcuts (block torrent / IPs / domains, direct IPs / domains, IPv4
forced, WARP / NordVPN routing).
- useXraySetting.js: hoists a parsed `templateSettings` reactive
alongside the JSON string, with two cooperating watches that keep
them in sync. Editing structured fields stringifies into xraySetting
for the dirty-poll + Advanced JSON tab; editing the JSON re-parses
into templateSettings only when valid, so structured tabs stay
readable mid-edit.
- BasicsTab.vue: collapse panels mirror the legacy partial — General,
Statistics, Logs, Basic routing. Every input is a computed v-model
reading/writing into templateSettings; the routing-rule shortcuts
funnel through ruleGetter/ruleSetter which match the legacy
templateRuleGetter/templateRuleSetter behavior (replace-first,
drop-duplicates, pop-the-rule-when-empty). Direct/IPv4 setters
also call syncOutbound() to provision/prune the matching outbound.
- XrayPage.vue: imports BasicsTab + derives `warpExist`/`nordExist`
from the parsed templateSettings. WARP/NordVPN provisioning modals
are still placeholders that toast — those land in 6-v with the
routing/outbound editors.
Default tab flips back to Basics so users land on the structured
editor.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:18:21 +00:00
|
|
|
<BasicsTab
|
|
|
|
|
:template-settings="templateSettings"
|
|
|
|
|
:outbound-test-url="outboundTestUrl"
|
|
|
|
|
:warp-exist="warpExist"
|
|
|
|
|
:nord-exist="nordExist"
|
|
|
|
|
@update:outbound-test-url="(v) => (outboundTestUrl = v)"
|
|
|
|
|
@show-warp="showWarp"
|
|
|
|
|
@show-nord="showNord"
|
|
|
|
|
/>
|
2026-05-08 12:13:26 +00:00
|
|
|
</a-tab-pane>
|
|
|
|
|
|
|
|
|
|
<a-tab-pane key="tpl-routing" class="tab-pane">
|
|
|
|
|
<template #tab>
|
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards
Replaces hardcoded English with t() calls in the components every
user sees on every page load. The translations themselves come from
the existing TOML files via the sync script — no new strings, no
new locale keys.
Per component:
- AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings /
xray / logout). Computed so the sidebar re-renders when the
cookie-driven locale flips on reload.
- IndexPage.vue: Quick actions card title + Logs / Backup / Up-to-
date / Update buttons.
- StatusCard.vue: CPU / Memory / Swap / Storage labels +
logical-processors / frequency tooltips.
- XrayStatusCard.vue: card title + error popover header + Stop /
Restart / Switch xray action labels (kept the v-prefix version
string as-is — it's content, not a label).
- SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons +
unsaved-changes warning.
- XrayPage.vue: 6 tab titles + Save / Restart-xray buttons +
unsaved-changes warning.
- InboundsPage.vue: 5 summary-stat card titles.
- InboundList.vue: 10 column titles (computed for live locale),
Add inbound / General actions buttons + every dropdown menu item,
search placeholder, filter radio labels, popover titles
(disabled / depleted / depleting / online), traffic + info
popover row labels.
Total: ~75 strings localised across 8 files. The remaining English
labels live in the per-tab settings forms, the form modals
(Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and
the per-row table cell helpers — all incremental work that doesn't
touch infrastructure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
|
|
|
<SwapOutlined /> <span>{{ t('pages.xray.Routings') }}</span>
|
2026-05-08 12:13:26 +00:00
|
|
|
</template>
|
2026-05-08 12:23:02 +00:00
|
|
|
<RoutingTab
|
|
|
|
|
:template-settings="templateSettings"
|
|
|
|
|
:inbound-tags="inboundTags"
|
|
|
|
|
:client-reverse-tags="clientReverseTags"
|
|
|
|
|
:is-mobile="isMobile"
|
|
|
|
|
/>
|
2026-05-08 12:13:26 +00:00
|
|
|
</a-tab-pane>
|
|
|
|
|
|
|
|
|
|
<a-tab-pane key="tpl-outbound" class="tab-pane">
|
|
|
|
|
<template #tab>
|
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards
Replaces hardcoded English with t() calls in the components every
user sees on every page load. The translations themselves come from
the existing TOML files via the sync script — no new strings, no
new locale keys.
Per component:
- AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings /
xray / logout). Computed so the sidebar re-renders when the
cookie-driven locale flips on reload.
- IndexPage.vue: Quick actions card title + Logs / Backup / Up-to-
date / Update buttons.
- StatusCard.vue: CPU / Memory / Swap / Storage labels +
logical-processors / frequency tooltips.
- XrayStatusCard.vue: card title + error popover header + Stop /
Restart / Switch xray action labels (kept the v-prefix version
string as-is — it's content, not a label).
- SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons +
unsaved-changes warning.
- XrayPage.vue: 6 tab titles + Save / Restart-xray buttons +
unsaved-changes warning.
- InboundsPage.vue: 5 summary-stat card titles.
- InboundList.vue: 10 column titles (computed for live locale),
Add inbound / General actions buttons + every dropdown menu item,
search placeholder, filter radio labels, popover titles
(disabled / depleted / depleting / online), traffic + info
popover row labels.
Total: ~75 strings localised across 8 files. The remaining English
labels live in the per-tab settings forms, the form modals
(Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and
the per-row table cell helpers — all incremental work that doesn't
touch infrastructure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
|
|
|
<UploadOutlined /> <span>{{ t('pages.xray.Outbounds') }}</span>
|
2026-05-08 12:13:26 +00:00
|
|
|
</template>
|
2026-05-08 12:27:40 +00:00
|
|
|
<OutboundsTab
|
|
|
|
|
:template-settings="templateSettings"
|
|
|
|
|
:outbounds-traffic="outboundsTraffic"
|
|
|
|
|
:outbound-test-states="outboundTestStates"
|
|
|
|
|
:is-mobile="isMobile"
|
|
|
|
|
@refresh-traffic="fetchOutboundsTraffic"
|
|
|
|
|
@reset-traffic="resetOutboundsTraffic"
|
|
|
|
|
@test="onTestOutbound"
|
|
|
|
|
@show-warp="showWarp"
|
|
|
|
|
@show-nord="showNord"
|
|
|
|
|
/>
|
2026-05-08 12:13:26 +00:00
|
|
|
</a-tab-pane>
|
|
|
|
|
|
|
|
|
|
<a-tab-pane key="tpl-balancer" class="tab-pane">
|
|
|
|
|
<template #tab>
|
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards
Replaces hardcoded English with t() calls in the components every
user sees on every page load. The translations themselves come from
the existing TOML files via the sync script — no new strings, no
new locale keys.
Per component:
- AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings /
xray / logout). Computed so the sidebar re-renders when the
cookie-driven locale flips on reload.
- IndexPage.vue: Quick actions card title + Logs / Backup / Up-to-
date / Update buttons.
- StatusCard.vue: CPU / Memory / Swap / Storage labels +
logical-processors / frequency tooltips.
- XrayStatusCard.vue: card title + error popover header + Stop /
Restart / Switch xray action labels (kept the v-prefix version
string as-is — it's content, not a label).
- SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons +
unsaved-changes warning.
- XrayPage.vue: 6 tab titles + Save / Restart-xray buttons +
unsaved-changes warning.
- InboundsPage.vue: 5 summary-stat card titles.
- InboundList.vue: 10 column titles (computed for live locale),
Add inbound / General actions buttons + every dropdown menu item,
search placeholder, filter radio labels, popover titles
(disabled / depleted / depleting / online), traffic + info
popover row labels.
Total: ~75 strings localised across 8 files. The remaining English
labels live in the per-tab settings forms, the form modals
(Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and
the per-row table cell helpers — all incremental work that doesn't
touch infrastructure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
|
|
|
<ClusterOutlined /> <span>{{ t('pages.xray.Balancers') }}</span>
|
2026-05-08 12:13:26 +00:00
|
|
|
</template>
|
2026-05-08 12:30:48 +00:00
|
|
|
<BalancersTab :template-settings="templateSettings" />
|
2026-05-08 12:13:26 +00:00
|
|
|
</a-tab-pane>
|
|
|
|
|
|
|
|
|
|
<a-tab-pane key="tpl-dns" class="tab-pane">
|
|
|
|
|
<template #tab>
|
|
|
|
|
<DatabaseOutlined /> <span>DNS</span>
|
|
|
|
|
</template>
|
2026-05-08 12:30:48 +00:00
|
|
|
<DnsTab :template-settings="templateSettings" />
|
2026-05-08 12:13:26 +00:00
|
|
|
</a-tab-pane>
|
|
|
|
|
|
|
|
|
|
<a-tab-pane key="tpl-advanced" class="tab-pane">
|
|
|
|
|
<template #tab>
|
i18n(frontend): translate page chrome — sidebar, save bars, tabs, summary cards
Replaces hardcoded English with t() calls in the components every
user sees on every page load. The translations themselves come from
the existing TOML files via the sync script — no new strings, no
new locale keys.
Per component:
- AppSidebar.vue: 5 menu titles (dashboard / inbounds / settings /
xray / logout). Computed so the sidebar re-renders when the
cookie-driven locale flips on reload.
- IndexPage.vue: Quick actions card title + Logs / Backup / Up-to-
date / Update buttons.
- StatusCard.vue: CPU / Memory / Swap / Storage labels +
logical-processors / frequency tooltips.
- XrayStatusCard.vue: card title + error popover header + Stop /
Restart / Switch xray action labels (kept the v-prefix version
string as-is — it's content, not a label).
- SettingsPage.vue: 5 tab titles + Save / Restart-panel buttons +
unsaved-changes warning.
- XrayPage.vue: 6 tab titles + Save / Restart-xray buttons +
unsaved-changes warning.
- InboundsPage.vue: 5 summary-stat card titles.
- InboundList.vue: 10 column titles (computed for live locale),
Add inbound / General actions buttons + every dropdown menu item,
search placeholder, filter radio labels, popover titles
(disabled / depleted / depleting / online), traffic + info
popover row labels.
Total: ~75 strings localised across 8 files. The remaining English
labels live in the per-tab settings forms, the form modals
(Inbound / Client / Outbound / Rule / Balancer / WARP / Nord), and
the per-row table cell helpers — all incremental work that doesn't
touch infrastructure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:07:41 +00:00
|
|
|
<CodeOutlined /> <span>{{ t('pages.xray.advancedTemplate') }}</span>
|
2026-05-08 12:13:26 +00:00
|
|
|
</template>
|
|
|
|
|
<a-form layout="vertical">
|
|
|
|
|
<a-form-item label="xraySetting (full JSON)">
|
|
|
|
|
<a-textarea
|
|
|
|
|
v-model:value="xraySetting"
|
|
|
|
|
:auto-size="{ minRows: 18, maxRows: 40 }"
|
|
|
|
|
spellcheck="false"
|
|
|
|
|
class="json-editor"
|
|
|
|
|
/>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
</a-form>
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
</a-tabs>
|
|
|
|
|
</a-col>
|
|
|
|
|
</a-row>
|
|
|
|
|
</template>
|
|
|
|
|
</a-spin>
|
|
|
|
|
</a-layout-content>
|
|
|
|
|
</a-layout>
|
feat(frontend): Phase 6-vi — WARP + NordVPN provisioning modals
Replaces the toast stubs on the Basics tab and Outbounds toolbar
with the legacy WARP + NordVPN provisioning flows. Both modals now
stage their wireguard outbounds back into templateSettings.outbounds
through the same event channels OutboundsTab uses, so the existing
add / reset / delete / refresh-traffic surface keeps working.
- WarpModal.vue: empty state shows a single Create button that
generates a wireguard keypair locally (Wireguard.generateKeypair)
and posts it to /panel/xray/warp/reg; populated state surfaces
the access_token / device_id / license_key / private_key, lets
the user upgrade to WARP+ via /panel/xray/warp/license, refreshes
the account info from /panel/xray/warp/config (plan / quota /
usage in human-readable bytes), and stages a wireguard outbound
with the WARP-specific reserved-byte encoding pulled from
client_id. Add / Reset / Delete go through events the parent
routes back to templateSettings.outbounds.
- NordModal.vue: dual-tab login (NordVPN access token →
/panel/xray/nord/reg, or paste a NordLynx private key →
/panel/xray/nord/setKey). Once authenticated, country / city /
server selectors fetch from /panel/xray/nord/{countries,servers},
servers sort by load ascending, the lowest-load server in the
current city auto-selects. Reset emits oldTag/newTag so the
parent renames matching routing rules in place; logout emits a
remove-routing-rules event with prefix `nord-` to purge any
dangling references.
- XrayPage.vue: holds warpOpen / nordOpen flags, ensures the
outbounds array exists before mutating it, and wires the modal
events (add-outbound / reset-outbound / remove-outbound /
remove-routing-rules) to in-place edits of templateSettings.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:44:46 +00:00
|
|
|
|
|
|
|
|
<WarpModal
|
|
|
|
|
v-model:open="warpOpen"
|
|
|
|
|
:template-settings="templateSettings"
|
|
|
|
|
@add-outbound="onAddOutbound"
|
|
|
|
|
@reset-outbound="onResetOutbound"
|
|
|
|
|
@remove-outbound="onRemoveOutboundByTag"
|
|
|
|
|
/>
|
|
|
|
|
<NordModal
|
|
|
|
|
v-model:open="nordOpen"
|
|
|
|
|
:template-settings="templateSettings"
|
|
|
|
|
@add-outbound="onAddOutbound"
|
|
|
|
|
@reset-outbound="onResetOutbound"
|
|
|
|
|
@remove-outbound="onRemoveOutboundByIndex"
|
|
|
|
|
@remove-routing-rules="onRemoveRoutingRules"
|
|
|
|
|
/>
|
2026-05-08 12:13:26 +00:00
|
|
|
</a-layout>
|
|
|
|
|
</a-config-provider>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.xray-page {
|
|
|
|
|
--bg-page: #f0f2f5;
|
|
|
|
|
--bg-card: #ffffff;
|
|
|
|
|
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
background: var(--bg-page);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.xray-page.is-dark {
|
|
|
|
|
--bg-page: #0a1222;
|
|
|
|
|
--bg-card: #151f31;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.xray-page.is-dark.is-ultra {
|
|
|
|
|
--bg-page: #21242a;
|
|
|
|
|
--bg-card: #0c0e12;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.xray-page :deep(.ant-layout),
|
|
|
|
|
.xray-page :deep(.ant-layout-content) {
|
|
|
|
|
background: transparent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.content-shell { background: transparent; }
|
|
|
|
|
.content-area { padding: 24px; }
|
|
|
|
|
|
|
|
|
|
.loading-spacer { min-height: calc(100vh - 120px); }
|
|
|
|
|
|
|
|
|
|
.header-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
.header-actions { padding: 4px; }
|
|
|
|
|
.header-info {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab-pane { padding-top: 20px; }
|
|
|
|
|
|
|
|
|
|
.restart-icon {
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
color: var(--ant-primary-color, #1890ff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.restart-result {
|
|
|
|
|
max-width: 480px;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.json-editor {
|
|
|
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|