a642bf16a2
design/ 同步自最新设计导出,新增 ui_kits/tablet/ 平板分栏布局;todo/ 录入 18 个并行实施任务。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
10644 lines
291 KiB
JavaScript
10644 lines
291 KiB
JavaScript
/* @ds-bundle: {"format":3,"namespace":"VPNDesignSystem_4c9564","components":[],"sourceHashes":{"ui_kits/desktop/dapp.jsx":"0261ad8c8093","ui_kits/desktop/dflows.jsx":"b52d86fc92ea","ui_kits/desktop/dparts.jsx":"e7fec2d75fc0","ui_kits/desktop/macos-window.jsx":"83e5cb33277e","ui_kits/mobile/flows.jsx":"838173b11d20","ui_kits/mobile/ios-frame.jsx":"39f3a091d97d","ui_kits/mobile/parts.jsx":"44efd184bf6a","ui_kits/mobile/screens.jsx":"cde11b0b1ad6","ui_kits/tablet/tabapp.jsx":"86e2e0f22462","ui_kits/usercenter/ucapp.jsx":"abe00336c077","ui_kits/usercenter/ucparts.jsx":"ba322907d9b7","ui_kits/website/site.js":"47ee2f38bcef"},"inlinedExternals":[],"unexposedExports":[]} */
|
||
|
||
(() => {
|
||
|
||
const __ds_ns = (window.VPNDesignSystem_4c9564 = window.VPNDesignSystem_4c9564 || {});
|
||
|
||
const __ds_scope = {};
|
||
|
||
(__ds_ns.__errors = __ds_ns.__errors || []);
|
||
|
||
// ui_kits/desktop/dapp.jsx
|
||
try { (() => {
|
||
/* dapp.jsx — 穿山甲 VPN desktop · window + views (i18n) */
|
||
const {
|
||
useState,
|
||
useEffect,
|
||
useRef
|
||
} = React;
|
||
function DToggle({
|
||
on,
|
||
onChange
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
onClick: onChange,
|
||
style: {
|
||
width: 42,
|
||
height: 25,
|
||
borderRadius: 999,
|
||
position: 'relative',
|
||
cursor: 'pointer',
|
||
flexShrink: 0,
|
||
background: on ? 'var(--accent)' : 'var(--border-strong)',
|
||
transition: 'background 220ms var(--ease-out)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
top: 3,
|
||
left: on ? 20 : 3,
|
||
width: 19,
|
||
height: 19,
|
||
borderRadius: '50%',
|
||
background: '#fff',
|
||
boxShadow: 'var(--shadow-sm)',
|
||
transition: 'left 220ms var(--ease-out)'
|
||
}
|
||
}));
|
||
}
|
||
function DLangSwitch({
|
||
lang,
|
||
setLang
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
background: 'var(--bg-subtle)',
|
||
borderRadius: 999,
|
||
padding: 3,
|
||
gap: 2
|
||
}
|
||
}, [['zh', '中文'], ['en', 'EN']].map(([v, l]) => /*#__PURE__*/React.createElement("button", {
|
||
key: v,
|
||
onClick: () => setLang(v),
|
||
style: {
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
borderRadius: 999,
|
||
padding: '4px 12px',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 12,
|
||
fontWeight: 700,
|
||
background: lang === v ? 'var(--accent)' : 'transparent',
|
||
color: lang === v ? 'var(--fg-on-accent)' : 'var(--fg3)',
|
||
transition: 'all 140ms var(--ease-out)'
|
||
}
|
||
}, l)));
|
||
}
|
||
function NavItem({
|
||
icon,
|
||
label,
|
||
active,
|
||
onClick
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("button", {
|
||
onClick: onClick,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 11,
|
||
width: '100%',
|
||
textAlign: 'left',
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
background: active ? 'var(--accent-subtle)' : 'transparent',
|
||
color: active ? 'var(--accent)' : 'var(--fg2)',
|
||
padding: '9px 12px',
|
||
borderRadius: 'var(--radius-md)',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 13.5,
|
||
fontWeight: active ? 600 : 500,
|
||
transition: 'background-color 140ms var(--ease-out), color 140ms var(--ease-out)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: icon,
|
||
size: 18,
|
||
stroke: active ? 2.3 : 2,
|
||
color: active ? 'var(--accent)' : 'var(--fg3)'
|
||
}), label);
|
||
}
|
||
function DConnect({
|
||
t,
|
||
lang,
|
||
status,
|
||
onToggle,
|
||
server,
|
||
smart,
|
||
free,
|
||
mins,
|
||
adDone,
|
||
onAd,
|
||
elapsed
|
||
}) {
|
||
const map = {
|
||
off: {
|
||
fill: 'var(--sand-100)',
|
||
fg: 'var(--clay-500)',
|
||
icon: 'power',
|
||
cap: t('capOff'),
|
||
sub: t('subOff'),
|
||
ring: 'track'
|
||
},
|
||
connecting: {
|
||
fill: 'var(--accent)',
|
||
fg: '#fff',
|
||
icon: 'loader-circle',
|
||
cap: t('capConnecting'),
|
||
sub: t('subConnecting'),
|
||
ring: 'spin'
|
||
},
|
||
on: {
|
||
fill: 'var(--success)',
|
||
fg: '#fff',
|
||
icon: 'shield-check',
|
||
cap: t('capOn'),
|
||
sub: t('subOn'),
|
||
ring: 'full'
|
||
}
|
||
}[status];
|
||
const fmt = s => `${String(Math.floor(s / 3600)).padStart(2, '0')}:${String(Math.floor(s % 3600 / 60)).padStart(2, '0')}:${String(s % 60).padStart(2, '0')}`;
|
||
const shadow = status === 'off' ? 'inset 0 0 0 1px var(--sand-200), var(--shadow-md)' : `0 0 0 8px ${status === 'on' ? 'var(--success-subtle)' : 'var(--accent-subtle)'}, var(--shadow-lg)`;
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
height: '100%',
|
||
gap: 24,
|
||
padding: '10px 40px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("button", {
|
||
onClick: onToggle,
|
||
style: {
|
||
position: 'relative',
|
||
width: 176,
|
||
height: 176,
|
||
borderRadius: '50%',
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
background: map.fill,
|
||
color: map.fg,
|
||
boxShadow: shadow,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 7,
|
||
transition: 'box-shadow 300ms var(--ease-out), background-color 300ms var(--ease-out)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("svg", {
|
||
viewBox: "0 0 176 176",
|
||
style: {
|
||
position: 'absolute',
|
||
inset: 0,
|
||
width: '100%',
|
||
height: '100%',
|
||
transformOrigin: 'center',
|
||
animation: map.ring === 'spin' ? 'pg-spin 1.4s linear infinite' : 'none'
|
||
}
|
||
}, map.ring === 'track' && /*#__PURE__*/React.createElement("circle", {
|
||
cx: "88",
|
||
cy: "88",
|
||
r: "81",
|
||
fill: "none",
|
||
stroke: "var(--sand-200)",
|
||
strokeWidth: "3",
|
||
strokeDasharray: "2 8",
|
||
strokeLinecap: "round"
|
||
}), map.ring === 'spin' && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("circle", {
|
||
cx: "88",
|
||
cy: "88",
|
||
r: "81",
|
||
fill: "none",
|
||
stroke: "rgba(255,255,255,.3)",
|
||
strokeWidth: "4"
|
||
}), /*#__PURE__*/React.createElement("circle", {
|
||
cx: "88",
|
||
cy: "88",
|
||
r: "81",
|
||
fill: "none",
|
||
stroke: "#fff",
|
||
strokeWidth: "4",
|
||
strokeLinecap: "round",
|
||
strokeDasharray: "130 380"
|
||
})), map.ring === 'full' && /*#__PURE__*/React.createElement("circle", {
|
||
cx: "88",
|
||
cy: "88",
|
||
r: "81",
|
||
fill: "none",
|
||
stroke: "rgba(255,255,255,.85)",
|
||
strokeWidth: "4",
|
||
strokeLinecap: "round"
|
||
})), /*#__PURE__*/React.createElement(DIcon, {
|
||
name: map.icon,
|
||
size: 46,
|
||
stroke: 1.7,
|
||
style: status === 'connecting' ? {
|
||
animation: 'pg-spin 1s linear infinite'
|
||
} : {}
|
||
}), status === 'off' && /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontWeight: 700,
|
||
fontSize: 13
|
||
}
|
||
}, t('connectNow')), status === 'connecting' && /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontWeight: 700,
|
||
fontSize: 13,
|
||
letterSpacing: '0.08em'
|
||
}
|
||
}, "\xB7\xB7\xB7"), status === 'on' && /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontWeight: 600,
|
||
fontSize: 15
|
||
}
|
||
}, fmt(elapsed))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
textAlign: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 22,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, map.cap), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--fg3)',
|
||
marginTop: 4
|
||
}
|
||
}, map.sub), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 7,
|
||
marginTop: 10,
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: '6px 14px',
|
||
fontSize: 12.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg2)'
|
||
}
|
||
}, smart && /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "zap",
|
||
size: 13,
|
||
stroke: 2.4,
|
||
color: "var(--accent)"
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, smart ? `${t('smartSelect')} · ` : '', dName(server, lang), " \xB7 ", server.ping, "ms"))), free && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 340,
|
||
background: 'var(--surface)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '12px 14px',
|
||
boxShadow: 'var(--shadow-sm)',
|
||
border: '1px solid var(--border)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 7,
|
||
marginBottom: 8
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "clock",
|
||
size: 14,
|
||
stroke: 2.2,
|
||
color: "var(--accent)"
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 12,
|
||
fontWeight: 600,
|
||
color: 'var(--fg2)'
|
||
}
|
||
}, t('quotaToday')), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 13.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, mins, " ", t('minutes')), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
marginLeft: 'auto',
|
||
fontSize: 10.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg3)',
|
||
background: 'var(--bg-subtle)',
|
||
padding: '3px 9px',
|
||
borderRadius: 'var(--radius-full)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, t('quotaFree'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
height: 6,
|
||
background: 'var(--bg-subtle)',
|
||
borderRadius: 3,
|
||
overflow: 'hidden',
|
||
marginBottom: 10
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: `${Math.min(100, mins / 10 * 100)}%`,
|
||
height: '100%',
|
||
background: mins <= 3 ? 'var(--warning)' : 'var(--accent)',
|
||
borderRadius: 3
|
||
}
|
||
})), adDone ? /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 7,
|
||
color: 'var(--success)',
|
||
fontWeight: 700,
|
||
fontSize: 13,
|
||
padding: '8px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "check-circle",
|
||
size: 15,
|
||
stroke: 2.2,
|
||
color: "var(--success)"
|
||
}), t('adUnlocked')) : /*#__PURE__*/React.createElement("button", {
|
||
onClick: onAd,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 7,
|
||
width: '100%',
|
||
border: '1.5px solid var(--accent-border)',
|
||
background: 'var(--accent-subtle)',
|
||
color: 'var(--accent)',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontWeight: 700,
|
||
fontSize: 13,
|
||
padding: '8px',
|
||
borderRadius: 'var(--radius-full)',
|
||
cursor: 'pointer'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "play-circle",
|
||
size: 15,
|
||
stroke: 2.2
|
||
}), t('watchAd'))), status === 'on' && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 10
|
||
}
|
||
}, [['arrow-down', '86.4', 'Mb/s'], ['arrow-up', '12.1', 'Mb/s'], ['zap', String(server.ping), 'ms']].map(([ic, v, u]) => /*#__PURE__*/React.createElement("div", {
|
||
key: ic,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 7,
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '8px 14px',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: ic,
|
||
size: 15,
|
||
stroke: 2.2,
|
||
color: "var(--accent)"
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 14,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, v, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 10,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, " ", u))))));
|
||
}
|
||
function DServers({
|
||
t,
|
||
lang,
|
||
current,
|
||
onPick
|
||
}) {
|
||
const [q, setQ] = useState('');
|
||
const list = DSERVERS.filter(s => (s.name + s.en).toLowerCase().includes(q.toLowerCase()));
|
||
const smartActive = current === 'AUTO';
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '4px 28px 24px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 9,
|
||
background: 'var(--bg-subtle)',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '9px 13px',
|
||
marginBottom: 16,
|
||
maxWidth: 340
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "search",
|
||
size: 17,
|
||
color: "var(--fg3)"
|
||
}), /*#__PURE__*/React.createElement("input", {
|
||
value: q,
|
||
onChange: e => setQ(e.target.value),
|
||
placeholder: t('search'),
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
outline: 'none',
|
||
flex: 1,
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 13.5,
|
||
color: 'var(--fg1)'
|
||
}
|
||
})), /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => onPick('AUTO'),
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
width: '100%',
|
||
textAlign: 'left',
|
||
cursor: 'pointer',
|
||
background: 'var(--accent-subtle)',
|
||
border: `1.5px solid ${smartActive ? 'var(--accent)' : 'var(--accent-border)'}`,
|
||
borderRadius: 'var(--radius-xl)',
|
||
padding: '14px',
|
||
boxShadow: smartActive ? 'var(--shadow-md)' : 'var(--shadow-sm)',
|
||
marginBottom: 10
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 38,
|
||
height: 38,
|
||
borderRadius: 'var(--radius-md)',
|
||
background: 'linear-gradient(150deg,var(--clay-500),var(--clay-700))',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "zap",
|
||
size: 18,
|
||
stroke: 2.2,
|
||
color: "#fff"
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 8
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 14.5,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('smartSelect')), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
color: '#fff',
|
||
background: 'var(--accent)',
|
||
padding: '2px 8px',
|
||
borderRadius: 999,
|
||
letterSpacing: '0.04em'
|
||
}
|
||
}, t('recommended'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11.5,
|
||
color: 'var(--fg2)',
|
||
marginTop: 3
|
||
}
|
||
}, t('smartSub'))), smartActive && /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "check",
|
||
size: 18,
|
||
color: "var(--accent)",
|
||
stroke: 2.6
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: '1fr 1fr',
|
||
gap: 10
|
||
}
|
||
}, list.map(s => {
|
||
const active = s.code === current;
|
||
return /*#__PURE__*/React.createElement("button", {
|
||
key: s.code,
|
||
onClick: () => onPick(s.code),
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
textAlign: 'left',
|
||
cursor: 'pointer',
|
||
background: active ? 'var(--accent-subtle)' : 'var(--surface)',
|
||
border: `1.5px solid ${active ? 'var(--accent-border)' : 'var(--border)'}`,
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '12px 14px',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DCC, {
|
||
code: s.code,
|
||
active: active
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
minWidth: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 14,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, dName(s, lang)), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11.5,
|
||
color: 'var(--fg3)',
|
||
whiteSpace: 'nowrap',
|
||
overflow: 'hidden',
|
||
textOverflow: 'ellipsis'
|
||
}
|
||
}, dSub(s, lang))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'flex-end',
|
||
gap: 5
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 11.5,
|
||
color: 'var(--fg2)'
|
||
}
|
||
}, s.ping, "ms"), /*#__PURE__*/React.createElement(DSignal, {
|
||
ping: s.ping
|
||
})), active && /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "check",
|
||
size: 17,
|
||
color: "var(--accent)"
|
||
}));
|
||
})));
|
||
}
|
||
function DStats({
|
||
t,
|
||
lang
|
||
}) {
|
||
const vals = [2.1, 3.4, 1.8, 4.6, 5.2, 6.1, 3.0];
|
||
const labels = t('days');
|
||
const max = 6.1;
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '8px 32px 24px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 14,
|
||
marginBottom: 22
|
||
}
|
||
}, [[t('trafficMonth'), '42.6 GB'], [t('avgPing'), '29 ms'], [t('durMonth'), '86.4 h']].map(([l, v]) => /*#__PURE__*/React.createElement("div", {
|
||
key: l,
|
||
style: {
|
||
flex: 1,
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '16px 18px',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg3)',
|
||
fontWeight: 600
|
||
}
|
||
}, l), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 24,
|
||
fontWeight: 500,
|
||
color: 'var(--fg1)',
|
||
marginTop: 6
|
||
}
|
||
}, v)))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '18px 20px',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 18
|
||
}
|
||
}, t('weekTraffic')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'flex-end',
|
||
gap: 18,
|
||
height: 120
|
||
}
|
||
}, vals.map((v, i) => /*#__PURE__*/React.createElement("div", {
|
||
key: i,
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
gap: 8
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 10,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, v), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: '100%',
|
||
maxWidth: 34,
|
||
height: `${v / max * 86}px`,
|
||
background: 'var(--accent)',
|
||
borderRadius: '6px 6px 0 0',
|
||
opacity: 0.85
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, labels[i]))))));
|
||
}
|
||
function DSettings({
|
||
t,
|
||
lang,
|
||
setLang,
|
||
dark,
|
||
setDark,
|
||
free,
|
||
setFree,
|
||
onReplayAuth,
|
||
onReplayOnboarding,
|
||
onRedeem
|
||
}) {
|
||
const [auto, setAuto] = useState(true);
|
||
const [kill, setKill] = useState(true);
|
||
const [boot, setBoot] = useState(true);
|
||
const card = {
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
boxShadow: 'var(--shadow-sm)',
|
||
overflow: 'hidden',
|
||
marginBottom: 16
|
||
};
|
||
const Row = ({
|
||
title,
|
||
sub,
|
||
right,
|
||
last,
|
||
onClick
|
||
}) => /*#__PURE__*/React.createElement("div", {
|
||
onClick: onClick,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
padding: '14px 18px',
|
||
borderBottom: last ? 'none' : '1px solid var(--border)',
|
||
cursor: onClick ? 'pointer' : 'default'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14,
|
||
fontWeight: 500,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, title), sub && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg3)',
|
||
marginTop: 2
|
||
}
|
||
}, sub)), right);
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '8px 32px 24px',
|
||
maxWidth: 560
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: card
|
||
}, /*#__PURE__*/React.createElement(Row, {
|
||
title: t('redeemEntry'),
|
||
sub: t('proMember'),
|
||
right: /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "chevron-right",
|
||
size: 18,
|
||
color: "var(--fg3)"
|
||
}),
|
||
last: true,
|
||
onClick: onRedeem
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: card
|
||
}, /*#__PURE__*/React.createElement(Row, {
|
||
title: t('autostart'),
|
||
sub: t('autostartSub'),
|
||
right: /*#__PURE__*/React.createElement(DToggle, {
|
||
on: boot,
|
||
onChange: () => setBoot(!boot)
|
||
})
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
title: t('smartRoute'),
|
||
sub: t('smartRouteSub'),
|
||
right: /*#__PURE__*/React.createElement(DToggle, {
|
||
on: auto,
|
||
onChange: () => setAuto(!auto)
|
||
})
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
title: t('killSwitch'),
|
||
sub: t('killSwitchSub'),
|
||
right: /*#__PURE__*/React.createElement(DToggle, {
|
||
on: kill,
|
||
onChange: () => setKill(!kill)
|
||
}),
|
||
last: true
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: card
|
||
}, /*#__PURE__*/React.createElement(Row, {
|
||
title: t('language'),
|
||
right: /*#__PURE__*/React.createElement(DLangSwitch, {
|
||
lang: lang,
|
||
setLang: setLang
|
||
})
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
title: t('darkAppearance'),
|
||
sub: dark ? t('stateOn') : t('followLight'),
|
||
right: /*#__PURE__*/React.createElement(DToggle, {
|
||
on: dark,
|
||
onChange: () => setDark(!dark)
|
||
})
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
title: t('protocol'),
|
||
right: /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 13,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, "WireGuard")
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
title: t('checkUpdate'),
|
||
right: /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "chevron-right",
|
||
size: 18,
|
||
color: "var(--fg3)"
|
||
})
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
title: t('version'),
|
||
right: /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 13,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, "v2.4.0 (macOS)"),
|
||
last: true
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: card
|
||
}, /*#__PURE__*/React.createElement(Row, {
|
||
title: t('replayAuth'),
|
||
sub: t('replayAuthSub'),
|
||
right: /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "chevron-right",
|
||
size: 18,
|
||
color: "var(--fg3)"
|
||
}),
|
||
onClick: onReplayAuth
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
title: t('replayOnboarding'),
|
||
sub: t('replayOnboardingSub'),
|
||
right: /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "chevron-right",
|
||
size: 18,
|
||
color: "var(--fg3)"
|
||
}),
|
||
onClick: onReplayOnboarding
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
title: t('freeDemo'),
|
||
sub: t('freeDemoSub'),
|
||
right: /*#__PURE__*/React.createElement(DToggle, {
|
||
on: free,
|
||
onChange: () => setFree(!free)
|
||
}),
|
||
last: true
|
||
})));
|
||
}
|
||
function DesktopApp() {
|
||
const [stage, setStage] = useState('app'); // app | auth | onboarding — UI kit opens on the main app
|
||
const [view, setView] = useState('connect');
|
||
const [status, setStatus] = useState('off');
|
||
const [code, setCode] = useState('AUTO'); // 'AUTO' = 智能选择(默认)
|
||
const [dark, setDark] = useState(false);
|
||
const [lang, setLang] = useState('zh');
|
||
const [free, setFree] = useState(true); // 演示:免费版视角(与移动端一致)
|
||
const [mins, setMins] = useState(6);
|
||
const [adDone, setAdDone] = useState(false);
|
||
const [elapsed, setElapsed] = useState(0);
|
||
const timer = useRef(null);
|
||
const t = dT(lang);
|
||
const smart = code === 'AUTO';
|
||
const server = smart ? DSERVERS.reduce((a, b) => a.ping < b.ping ? a : b) : DSERVERS.find(s => s.code === code);
|
||
const titles = {
|
||
connect: t('connect'),
|
||
servers: t('servers'),
|
||
stats: t('stats'),
|
||
account: t('navAccount'),
|
||
plans: t('choosePlan'),
|
||
redeem: t('redeemTitle'),
|
||
contact: t('navContact'),
|
||
settings: t('settings')
|
||
};
|
||
function toggle() {
|
||
if (status === 'off') {
|
||
setStatus('connecting');
|
||
setTimeout(() => {
|
||
setStatus('on');
|
||
setElapsed(0);
|
||
}, 1500);
|
||
} else setStatus('off');
|
||
}
|
||
function pick(c) {
|
||
setCode(c);
|
||
setView('connect');
|
||
if (status === 'on') {
|
||
setStatus('connecting');
|
||
setTimeout(() => {
|
||
setStatus('on');
|
||
setElapsed(0);
|
||
}, 1200);
|
||
}
|
||
}
|
||
useEffect(() => {
|
||
if (status === 'on') timer.current = setInterval(() => setElapsed(e => e + 1), 1000);
|
||
return () => clearInterval(timer.current);
|
||
}, [status]);
|
||
if (stage === 'auth') return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
minHeight: '100vh',
|
||
padding: 28,
|
||
boxSizing: 'border-box'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DAuthFlow, {
|
||
t: t,
|
||
lang: lang,
|
||
dark: dark,
|
||
onDone: () => setStage('onboarding')
|
||
}));
|
||
if (stage === 'onboarding') return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
minHeight: '100vh',
|
||
padding: 28,
|
||
boxSizing: 'border-box'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DOnboarding, {
|
||
t: t,
|
||
lang: lang,
|
||
dark: dark,
|
||
onDone: () => setStage('app')
|
||
}));
|
||
let main;
|
||
if (view === 'connect') main = /*#__PURE__*/React.createElement(DConnect, {
|
||
t: t,
|
||
lang: lang,
|
||
status: status,
|
||
onToggle: toggle,
|
||
server: server,
|
||
smart: smart,
|
||
free: free,
|
||
mins: mins,
|
||
adDone: adDone,
|
||
onAd: () => setAdDone(true),
|
||
elapsed: elapsed
|
||
});else if (view === 'servers') main = /*#__PURE__*/React.createElement(DServers, {
|
||
t: t,
|
||
lang: lang,
|
||
current: code,
|
||
onPick: pick
|
||
});else if (view === 'stats') main = /*#__PURE__*/React.createElement(DStats, {
|
||
t: t,
|
||
lang: lang
|
||
});else if (view === 'account') main = /*#__PURE__*/React.createElement(DAccountView, {
|
||
t: t,
|
||
lang: lang,
|
||
free: free,
|
||
onUpgrade: () => setView('plans'),
|
||
onSignOut: () => setStage('auth')
|
||
});else if (view === 'plans') main = /*#__PURE__*/React.createElement(DPlansView, {
|
||
t: t,
|
||
lang: lang,
|
||
onChoose: () => setView('redeem')
|
||
});else if (view === 'redeem') main = /*#__PURE__*/React.createElement(DRedeemView, {
|
||
t: t,
|
||
lang: lang
|
||
});else if (view === 'contact') main = /*#__PURE__*/React.createElement(DContactView, {
|
||
t: t,
|
||
lang: lang
|
||
});else main = /*#__PURE__*/React.createElement(DSettings, {
|
||
t: t,
|
||
lang: lang,
|
||
setLang: setLang,
|
||
dark: dark,
|
||
setDark: setDark,
|
||
free: free,
|
||
setFree: setFree,
|
||
onReplayAuth: () => setStage('auth'),
|
||
onReplayOnboarding: () => setStage('onboarding'),
|
||
onRedeem: () => setView('redeem')
|
||
});
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
"data-theme": dark ? 'dark' : 'light',
|
||
style: {
|
||
width: 920,
|
||
height: 600,
|
||
borderRadius: 16,
|
||
overflow: 'hidden',
|
||
display: 'flex',
|
||
background: 'var(--bg)',
|
||
boxShadow: '0 0 0 1px rgba(0,0,0,0.12), 0 28px 70px rgba(0,0,0,0.4)',
|
||
fontFamily: 'var(--font-sans)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 204,
|
||
flexShrink: 0,
|
||
background: 'var(--bg-subtle)',
|
||
borderRight: '1px solid var(--border)',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
padding: '14px 12px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '2px 4px 16px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(MacTrafficLights, null)), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 9,
|
||
padding: '0 6px 16px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DMark, {
|
||
size: 28
|
||
}), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 16,
|
||
color: 'var(--fg1)',
|
||
lineHeight: 1
|
||
}
|
||
}, lang === 'zh' ? '穿山甲' : 'Pangolin'), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 9,
|
||
fontWeight: 600,
|
||
letterSpacing: '0.18em',
|
||
color: 'var(--accent)',
|
||
marginTop: 3
|
||
}
|
||
}, "PANGOLIN"))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 3
|
||
}
|
||
}, /*#__PURE__*/React.createElement(NavItem, {
|
||
icon: "power",
|
||
label: t('navConnect'),
|
||
active: view === 'connect',
|
||
onClick: () => setView('connect')
|
||
}), /*#__PURE__*/React.createElement(NavItem, {
|
||
icon: "globe",
|
||
label: t('navServers'),
|
||
active: view === 'servers',
|
||
onClick: () => setView('servers')
|
||
}), /*#__PURE__*/React.createElement(NavItem, {
|
||
icon: "chart-no-axes-column",
|
||
label: t('navStats'),
|
||
active: view === 'stats',
|
||
onClick: () => setView('stats')
|
||
}), /*#__PURE__*/React.createElement(NavItem, {
|
||
icon: "user",
|
||
label: t('navAccount'),
|
||
active: view === 'account' || view === 'plans' || view === 'redeem',
|
||
onClick: () => setView('account')
|
||
}), /*#__PURE__*/React.createElement(NavItem, {
|
||
icon: "message-circle",
|
||
label: t('navContact'),
|
||
active: view === 'contact',
|
||
onClick: () => setView('contact')
|
||
}), /*#__PURE__*/React.createElement(NavItem, {
|
||
icon: "settings",
|
||
label: t('navSettings'),
|
||
active: view === 'settings',
|
||
onClick: () => setView('settings')
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 10,
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '9px 11px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 30,
|
||
height: 30,
|
||
borderRadius: '50%',
|
||
background: 'linear-gradient(150deg,var(--clay-500),var(--clay-700))',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "crown",
|
||
size: 15,
|
||
color: "#fff"
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
minWidth: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
whiteSpace: 'nowrap',
|
||
overflow: 'hidden',
|
||
textOverflow: 'ellipsis'
|
||
}
|
||
}, free ? t('freePlanName') : t('proMember')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 10.5,
|
||
color: 'var(--fg3)',
|
||
whiteSpace: 'nowrap',
|
||
overflow: 'hidden',
|
||
textOverflow: 'ellipsis'
|
||
}
|
||
}, free ? t('quotaFree') : t('until'))))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
minWidth: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
height: 52,
|
||
flexShrink: 0,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
padding: '0 24px',
|
||
borderBottom: '1px solid var(--border)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 10
|
||
}
|
||
}, (view === 'redeem' || view === 'plans') && /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => setView('account'),
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
cursor: 'pointer',
|
||
padding: 4,
|
||
display: 'flex'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "arrow-left",
|
||
size: 20,
|
||
color: "var(--fg1)"
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 17,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, titles[view])), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 12,
|
||
fontWeight: 600,
|
||
whiteSpace: 'nowrap',
|
||
color: status === 'on' ? 'var(--success)' : 'var(--fg3)'
|
||
}
|
||
}, status === 'on' ? '● ' + t('connected') + ' ' + server.code : '○ ' + t('notConnected')), /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => setDark(!dark),
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
cursor: 'pointer',
|
||
padding: 6,
|
||
borderRadius: 'var(--radius-sm)',
|
||
display: 'flex'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: dark ? 'sun' : 'moon',
|
||
size: 18,
|
||
color: "var(--fg2)"
|
||
})))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
overflow: 'auto',
|
||
minHeight: 0
|
||
}
|
||
}, main)));
|
||
}
|
||
Object.assign(window, {
|
||
DesktopApp
|
||
});
|
||
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/desktop/dapp.jsx", error: String((e && e.message) || e) }); }
|
||
|
||
// ui_kits/desktop/dflows.jsx
|
||
try { (() => {
|
||
/* dflows.jsx — 穿山甲 VPN desktop · auth + account management (integrated dashboard) + redeem */
|
||
const {
|
||
useState: useStateD
|
||
} = React;
|
||
const dCard = {
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-xl)',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
};
|
||
const dInput = {
|
||
border: '1.5px solid var(--border-strong)',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '11px 13px',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 14,
|
||
color: 'var(--fg1)',
|
||
background: 'var(--bg)',
|
||
outline: 'none',
|
||
width: '100%',
|
||
boxSizing: 'border-box'
|
||
};
|
||
function DField({
|
||
label,
|
||
children
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("label", {
|
||
style: {
|
||
display: 'block'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
display: 'block',
|
||
fontSize: 12.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg2)',
|
||
marginBottom: 6
|
||
}
|
||
}, label), children);
|
||
}
|
||
|
||
/* ───────── AUTH — split panel, brand left / form right ───────── */
|
||
function DAuthFlow({
|
||
t,
|
||
lang,
|
||
dark,
|
||
onDone
|
||
}) {
|
||
const [mode, setMode] = useStateD('login');
|
||
const [step, setStep] = useStateD(0);
|
||
const [email, setEmail] = useStateD('');
|
||
const [code, setCode] = useStateD('');
|
||
const [pw, setPw] = useStateD('');
|
||
const [sent, setSent] = useStateD(false);
|
||
const [totp, setTotp] = useStateD(false); // 2FA step after login (PRO)
|
||
const [otp, setOtp] = useStateD('');
|
||
const valid = /\S+@\S+\.\S+/.test(email);
|
||
const tab = (id, label) => /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => {
|
||
setMode(id);
|
||
setStep(0);
|
||
setSent(false);
|
||
},
|
||
style: {
|
||
flex: 1,
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
background: 'transparent',
|
||
padding: '10px 0',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 15,
|
||
fontWeight: mode === id ? 700 : 500,
|
||
color: mode === id ? 'var(--fg1)' : 'var(--fg3)',
|
||
borderBottom: mode === id ? '2px solid var(--accent)' : '2px solid transparent'
|
||
}
|
||
}, label);
|
||
const primary = (label, onClick, enabled) => /*#__PURE__*/React.createElement("button", {
|
||
onClick: enabled ? onClick : undefined,
|
||
disabled: !enabled,
|
||
style: {
|
||
width: '100%',
|
||
border: 'none',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: '13px',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontWeight: 700,
|
||
fontSize: 15,
|
||
cursor: enabled ? 'pointer' : 'not-allowed',
|
||
background: 'var(--accent)',
|
||
color: 'var(--fg-on-accent)',
|
||
opacity: enabled ? 1 : 0.45,
|
||
marginTop: 4
|
||
}
|
||
}, label);
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
"data-theme": dark ? 'dark' : 'light',
|
||
style: {
|
||
width: 880,
|
||
height: 560,
|
||
borderRadius: 16,
|
||
overflow: 'hidden',
|
||
display: 'flex',
|
||
boxShadow: '0 0 0 1px rgba(0,0,0,0.12), 0 28px 70px rgba(0,0,0,0.4)',
|
||
fontFamily: 'var(--font-sans)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 340,
|
||
flexShrink: 0,
|
||
background: 'linear-gradient(165deg,var(--clay-500),var(--clay-800))',
|
||
color: '#fff',
|
||
padding: '48px 40px',
|
||
display: 'flex',
|
||
flexDirection: 'column'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 11
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DMark, {
|
||
size: 34,
|
||
color: "#fff"
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 20
|
||
}
|
||
}, lang === 'zh' ? '穿山甲' : 'Pangolin')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
justifyContent: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 28,
|
||
lineHeight: 1.3
|
||
}
|
||
}, t('authSidePitch'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
opacity: .8,
|
||
letterSpacing: '0.04em'
|
||
}
|
||
}, t('authTagline'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
background: 'var(--bg)',
|
||
padding: '44px 46px',
|
||
display: 'flex',
|
||
flexDirection: 'column'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
borderBottom: '1px solid var(--border)',
|
||
marginBottom: 26
|
||
}
|
||
}, tab('login', t('tabLogin')), tab('register', t('tabRegister'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 16
|
||
}
|
||
}, mode === 'login' && !totp && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DField, {
|
||
label: t('emailLabel')
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
style: dInput,
|
||
value: email,
|
||
onChange: e => setEmail(e.target.value),
|
||
placeholder: t('emailPh')
|
||
})), /*#__PURE__*/React.createElement(DField, {
|
||
label: t('pwLabel')
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
style: dInput,
|
||
type: "password",
|
||
value: pw,
|
||
onChange: e => setPw(e.target.value),
|
||
placeholder: t('pwPh')
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
textAlign: 'right',
|
||
marginTop: -6
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--accent)',
|
||
fontWeight: 600,
|
||
cursor: 'pointer'
|
||
}
|
||
}, t('forgotPw'))), primary(t('doLogin'), () => setTotp(true), valid && pw.length > 0)), mode === 'login' && totp && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 8
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "shield-check",
|
||
size: 18,
|
||
color: "var(--accent)"
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 17,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('twoFATitle')), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
color: 'var(--accent)',
|
||
background: 'var(--accent-subtle)',
|
||
border: '1px solid var(--accent-border)',
|
||
padding: '2px 8px',
|
||
borderRadius: 999
|
||
}
|
||
}, "PRO")), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
lineHeight: 1.6
|
||
}
|
||
}, t('twoFAHint')), /*#__PURE__*/React.createElement("input", {
|
||
value: otp,
|
||
onChange: e => setOtp(e.target.value.replace(/\D/g, '').slice(0, 6)),
|
||
placeholder: "\xB7\xB7\xB7\xB7\xB7\xB7",
|
||
style: {
|
||
...dInput,
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 20,
|
||
letterSpacing: '0.5em',
|
||
textAlign: 'center'
|
||
}
|
||
}), primary(t('twoFAConfirm'), onDone, otp.length === 6), /*#__PURE__*/React.createElement("div", {
|
||
onClick: () => setTotp(false),
|
||
style: {
|
||
textAlign: 'center',
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
cursor: 'pointer'
|
||
}
|
||
}, t('twoFABack'))), mode === 'register' && step === 0 && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DField, {
|
||
label: t('emailLabel')
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 8
|
||
}
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
style: dInput,
|
||
value: email,
|
||
onChange: e => setEmail(e.target.value),
|
||
placeholder: t('emailPh')
|
||
}), /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => valid && setSent(true),
|
||
style: {
|
||
border: '1.5px solid var(--border-strong)',
|
||
background: 'transparent',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '0 16px',
|
||
cursor: valid ? 'pointer' : 'default',
|
||
color: valid ? 'var(--accent)' : 'var(--fg3)',
|
||
fontWeight: 700,
|
||
fontSize: 13,
|
||
whiteSpace: 'nowrap',
|
||
flexShrink: 0
|
||
}
|
||
}, sent ? t('resend') : t('sendCode')))), sent && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--success)',
|
||
marginTop: -8,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 6
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "check-circle",
|
||
size: 14,
|
||
color: "var(--success)"
|
||
}), t('codeSentTo'), " ", email), /*#__PURE__*/React.createElement(DField, {
|
||
label: t('codeLabel')
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
style: {
|
||
...dInput,
|
||
fontFamily: 'var(--font-mono)',
|
||
letterSpacing: '0.3em'
|
||
},
|
||
value: code,
|
||
onChange: e => setCode(e.target.value.replace(/\D/g, '').slice(0, 6)),
|
||
placeholder: "\xB7\xB7\xB7\xB7\xB7\xB7"
|
||
})), primary(t('doNext'), () => setStep(1), sent && code.length === 6)), mode === 'register' && step === 1 && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--fg2)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 7
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "check-circle",
|
||
size: 15,
|
||
color: "var(--success)"
|
||
}), email), /*#__PURE__*/React.createElement(DField, {
|
||
label: t('pwLabel')
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
style: dInput,
|
||
type: "password",
|
||
value: pw,
|
||
onChange: e => setPw(e.target.value),
|
||
placeholder: t('setPwPh')
|
||
})), primary(t('doCreate'), onDone, pw.length >= 6))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11,
|
||
color: 'var(--fg3)',
|
||
textAlign: 'center',
|
||
lineHeight: 1.5,
|
||
paddingTop: 18
|
||
}
|
||
}, t('tos'))));
|
||
}
|
||
|
||
/* ───────── ACCOUNT (integrated: plan + account info + devices) ───────── */
|
||
function DAccountView({
|
||
t,
|
||
lang,
|
||
free,
|
||
onUpgrade,
|
||
onSignOut
|
||
}) {
|
||
const [twoFA, setTwoFA] = useStateD(true);
|
||
const [devices, setDevices] = useStateD([{
|
||
id: 1,
|
||
icon: 'laptop',
|
||
name: 'MacBook Pro',
|
||
os: 'macOS 26',
|
||
active: t('thisDevice'),
|
||
me: true
|
||
}, {
|
||
id: 2,
|
||
icon: 'smartphone',
|
||
name: 'iPhone 17',
|
||
os: 'iOS 26',
|
||
active: '2 min',
|
||
me: false
|
||
}, {
|
||
id: 3,
|
||
icon: 'monitor-smartphone',
|
||
name: 'iPad Air',
|
||
os: 'iPadOS 26',
|
||
active: lang === 'zh' ? '3 小时前' : '3h ago',
|
||
me: false
|
||
}]);
|
||
const infoRow = (label, value, action, last) => /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
padding: '14px 18px',
|
||
borderBottom: last ? 'none' : '1px solid var(--border)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 96,
|
||
fontSize: 13,
|
||
fontWeight: 600,
|
||
color: 'var(--fg2)'
|
||
}
|
||
}, label), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
fontSize: 14,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, value), action);
|
||
const smallBtn = {
|
||
border: '1.5px solid var(--border-strong)',
|
||
background: 'transparent',
|
||
color: 'var(--fg2)',
|
||
fontSize: 13,
|
||
fontWeight: 600,
|
||
padding: '7px 14px',
|
||
borderRadius: 'var(--radius-full)',
|
||
cursor: 'pointer'
|
||
};
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '8px 32px 28px',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 22,
|
||
maxWidth: 760
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...dCard,
|
||
background: 'linear-gradient(150deg,var(--clay-600),var(--clay-800))',
|
||
border: 'none',
|
||
color: '#fff',
|
||
padding: '20px 24px',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 15
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 48,
|
||
height: 48,
|
||
borderRadius: '50%',
|
||
background: 'rgba(255,255,255,.18)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "crown",
|
||
size: 24,
|
||
color: "#fff"
|
||
})), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
opacity: .8
|
||
}
|
||
}, t('curPlan')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 20,
|
||
fontWeight: 700,
|
||
marginTop: 2
|
||
}
|
||
}, free ? t('freePlanName') : t('proMember')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11.5,
|
||
opacity: .8,
|
||
marginTop: 3,
|
||
fontFamily: 'var(--font-mono)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, free ? t('quotaFree') : `${t('expires')} 2026-12-31 · ${t('accUsage')} 42.6 GB`))), /*#__PURE__*/React.createElement("button", {
|
||
onClick: onUpgrade,
|
||
style: {
|
||
border: 'none',
|
||
background: '#fff',
|
||
color: 'var(--clay-700)',
|
||
fontWeight: 700,
|
||
fontSize: 14,
|
||
padding: '11px 20px',
|
||
borderRadius: 'var(--radius-full)',
|
||
cursor: 'pointer',
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 8
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "ticket",
|
||
size: 16,
|
||
color: "var(--clay-700)"
|
||
}), t('renew'))), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 17,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 14
|
||
}
|
||
}, t('accInfoTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...dCard,
|
||
overflow: 'hidden'
|
||
}
|
||
}, infoRow(t('accEmail'), 'me@pangolin.vpn', /*#__PURE__*/React.createElement("button", {
|
||
style: smallBtn
|
||
}, t('accChange'))), infoRow(t('accPassword'), '••••••••••', /*#__PURE__*/React.createElement("button", {
|
||
style: smallBtn
|
||
}, t('accChange'))), infoRow(t('twoFA'), t('twoFASub'), /*#__PURE__*/React.createElement(DToggle, {
|
||
on: twoFA,
|
||
onChange: () => setTwoFA(!twoFA)
|
||
})), infoRow(t('ucEntry'), t('ucEntrySub'), /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => window.open('../usercenter/index.html', '_blank'),
|
||
style: {
|
||
...smallBtn,
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 6
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "external-link",
|
||
size: 14,
|
||
color: "var(--fg2)"
|
||
}), t('ucOpen'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
padding: '14px 18px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 96,
|
||
fontSize: 13,
|
||
fontWeight: 600,
|
||
color: 'var(--danger)'
|
||
}
|
||
}, t('accSignOut')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}), /*#__PURE__*/React.createElement("button", {
|
||
onClick: onSignOut,
|
||
style: {
|
||
...smallBtn,
|
||
color: 'var(--danger)',
|
||
borderColor: 'var(--danger-subtle)',
|
||
background: 'var(--danger-subtle)',
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 7
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "log-out",
|
||
size: 15,
|
||
color: "var(--danger)"
|
||
}), t('accSignOut'))))), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 17,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('myDevices')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
margin: '3px 0 14px'
|
||
}
|
||
}, t('devicesSub')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...dCard,
|
||
overflow: 'hidden'
|
||
}
|
||
}, devices.map((d, i) => /*#__PURE__*/React.createElement("div", {
|
||
key: d.id,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 14,
|
||
padding: '14px 18px',
|
||
borderBottom: i < devices.length - 1 ? '1px solid var(--border)' : 'none'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 40,
|
||
height: 40,
|
||
borderRadius: 'var(--radius-md)',
|
||
background: 'var(--accent-subtle)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: d.icon,
|
||
size: 20,
|
||
color: "var(--accent)"
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 8
|
||
}
|
||
}, d.name, d.me && /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 11,
|
||
fontWeight: 600,
|
||
color: 'var(--success)',
|
||
background: 'var(--success-subtle)',
|
||
padding: '2px 8px',
|
||
borderRadius: 999
|
||
}
|
||
}, d.active)), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg3)',
|
||
marginTop: 2
|
||
}
|
||
}, d.os, !d.me && ` · ${t('lastActive')} ${d.active}`)), !d.me && /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => setDevices(devices.filter(x => x.id !== d.id)),
|
||
style: {
|
||
border: '1.5px solid var(--border-strong)',
|
||
background: 'transparent',
|
||
color: 'var(--fg2)',
|
||
fontSize: 13,
|
||
fontWeight: 600,
|
||
padding: '7px 14px',
|
||
borderRadius: 'var(--radius-full)',
|
||
cursor: 'pointer'
|
||
}
|
||
}, t('remove')))))));
|
||
}
|
||
|
||
/* ───────── PLANS (shown on renew / upgrade) ───────── */
|
||
function DPlansView({
|
||
t,
|
||
lang,
|
||
onChoose
|
||
}) {
|
||
const plans = [{
|
||
id: 'free',
|
||
name: t('free'),
|
||
price: '¥0',
|
||
feats: t('feFree'),
|
||
cta: t('current'),
|
||
variant: 'cur'
|
||
}, {
|
||
id: 'pro',
|
||
name: t('proPlan'),
|
||
price: '¥25',
|
||
feats: t('fePro'),
|
||
cta: t('upgrade'),
|
||
variant: 'pro'
|
||
}, {
|
||
id: 'team',
|
||
name: t('team'),
|
||
price: '¥99',
|
||
feats: t('feTeam'),
|
||
cta: t('choose'),
|
||
variant: 'plain'
|
||
}];
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '8px 32px 28px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: '1fr 1fr 1fr',
|
||
gap: 16,
|
||
alignItems: 'start',
|
||
maxWidth: 760
|
||
}
|
||
}, plans.map(p => {
|
||
const isPro = p.variant === 'pro';
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
key: p.id,
|
||
style: {
|
||
...dCard,
|
||
position: 'relative',
|
||
padding: '22px 20px',
|
||
...(isPro ? {
|
||
background: 'linear-gradient(160deg,var(--clay-600),var(--clay-800))',
|
||
border: 'none',
|
||
color: '#fff',
|
||
boxShadow: 'var(--shadow-md)'
|
||
} : {})
|
||
}
|
||
}, isPro && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
top: -10,
|
||
left: '50%',
|
||
transform: 'translateX(-50%)',
|
||
background: '#fff',
|
||
color: 'var(--clay-700)',
|
||
fontSize: 11,
|
||
fontWeight: 700,
|
||
padding: '3px 11px',
|
||
borderRadius: 999,
|
||
boxShadow: 'var(--shadow-sm)'
|
||
}
|
||
}, t('mostPopular')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13.5,
|
||
fontWeight: 600,
|
||
color: isPro ? '#fff' : 'var(--fg2)',
|
||
opacity: isPro ? .85 : 1
|
||
}
|
||
}, p.name), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 30,
|
||
fontWeight: 700,
|
||
marginTop: 6,
|
||
color: isPro ? '#fff' : 'var(--fg1)'
|
||
}
|
||
}, p.price, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 13,
|
||
fontWeight: 500,
|
||
opacity: .7
|
||
}
|
||
}, t('perMonth'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 9,
|
||
margin: '16px 0 18px'
|
||
}
|
||
}, p.feats.map((f, i) => /*#__PURE__*/React.createElement("div", {
|
||
key: i,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 9,
|
||
fontSize: 13,
|
||
color: isPro ? 'rgba(255,255,255,.92)' : 'var(--fg2)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "check",
|
||
size: 15,
|
||
color: isPro ? '#fff' : 'var(--success)',
|
||
stroke: 2.4
|
||
}), f))), /*#__PURE__*/React.createElement("button", {
|
||
onClick: p.variant !== 'cur' ? onChoose : undefined,
|
||
style: {
|
||
width: '100%',
|
||
border: p.variant === 'cur' ? '1.5px solid var(--border-strong)' : 'none',
|
||
cursor: p.variant === 'cur' ? 'default' : 'pointer',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: '11px',
|
||
fontWeight: 600,
|
||
fontSize: 14,
|
||
background: isPro ? '#fff' : p.variant === 'cur' ? 'transparent' : 'var(--accent)',
|
||
color: isPro ? 'var(--clay-700)' : p.variant === 'cur' ? 'var(--fg2)' : '#fff'
|
||
}
|
||
}, p.cta));
|
||
})));
|
||
}
|
||
|
||
/* ───────── REDEEM & BUY ───────── */
|
||
function DRedeemView({
|
||
t,
|
||
lang
|
||
}) {
|
||
const [code, setCode] = useStateD('');
|
||
const [ok, setOk] = useStateD(false);
|
||
const channels = [{
|
||
icon: 'shopping-bag',
|
||
name: t('chStore'),
|
||
sub: t('chStoreSub'),
|
||
accent: true
|
||
}, {
|
||
icon: 'credit-card',
|
||
name: 'USDT (TRC20)',
|
||
sub: t('chUsdtSub'),
|
||
mono: false
|
||
}, {
|
||
icon: 'send',
|
||
name: 'Telegram',
|
||
sub: t('chTgSub'),
|
||
mono: true
|
||
}, {
|
||
icon: 'message-circle',
|
||
name: 'LINE',
|
||
sub: t('chLineSub'),
|
||
mono: true
|
||
}, {
|
||
icon: 'mail',
|
||
name: t('chEmail'),
|
||
sub: t('chEmailSub'),
|
||
mono: true
|
||
}];
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '8px 32px 28px',
|
||
maxWidth: 680
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...dCard,
|
||
padding: '20px 22px',
|
||
marginBottom: 24
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14.5,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 12
|
||
}
|
||
}, t('redeemCodeTitle')), ok ? /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 9,
|
||
color: 'var(--success)',
|
||
fontWeight: 600,
|
||
fontSize: 15,
|
||
padding: '6px 0'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "check-circle",
|
||
size: 20,
|
||
color: "var(--success)"
|
||
}), t('redeemOk')) : /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 10
|
||
}
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
value: code,
|
||
onChange: e => setCode(e.target.value.toUpperCase()),
|
||
placeholder: t('redeemPh'),
|
||
style: {
|
||
...dInput,
|
||
fontFamily: 'var(--font-mono)',
|
||
letterSpacing: '0.08em'
|
||
}
|
||
}), /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => code.length >= 4 && setOk(true),
|
||
style: {
|
||
border: 'none',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '0 22px',
|
||
background: 'var(--accent)',
|
||
color: '#fff',
|
||
fontWeight: 700,
|
||
fontSize: 14,
|
||
cursor: 'pointer',
|
||
flexShrink: 0
|
||
}
|
||
}, t('redeemBtn')))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14.5,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 6
|
||
}
|
||
}, t('buyTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
lineHeight: 1.5,
|
||
marginBottom: 14
|
||
}
|
||
}, t('buySub')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: '1fr 1fr',
|
||
gap: 12
|
||
}
|
||
}, channels.map(c => /*#__PURE__*/React.createElement("button", {
|
||
key: c.name,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 13,
|
||
textAlign: 'left',
|
||
cursor: 'pointer',
|
||
background: c.accent ? 'var(--accent-subtle)' : 'var(--surface)',
|
||
border: `1px solid ${c.accent ? 'var(--accent-border)' : 'var(--border)'}`,
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '14px 16px',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 40,
|
||
height: 40,
|
||
borderRadius: 'var(--radius-md)',
|
||
background: c.accent ? 'var(--accent)' : 'var(--bg-subtle)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: c.icon,
|
||
size: 20,
|
||
color: c.accent ? '#fff' : 'var(--accent)'
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
minWidth: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, c.name), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg3)',
|
||
fontFamily: c.mono ? 'var(--font-mono)' : 'var(--font-sans)',
|
||
whiteSpace: 'nowrap',
|
||
overflow: 'hidden',
|
||
textOverflow: 'ellipsis'
|
||
}
|
||
}, c.sub)), /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "external-link",
|
||
size: 16,
|
||
color: "var(--fg3)"
|
||
})))));
|
||
}
|
||
|
||
/* ───────── ONBOARDING — window card, 3 screens ───────── */
|
||
function DOnboarding({
|
||
t,
|
||
lang,
|
||
dark,
|
||
onDone
|
||
}) {
|
||
const [i, setI] = useStateD(0);
|
||
const steps = [{
|
||
icon: 'globe',
|
||
title: t('ob1Title'),
|
||
sub: t('ob1Sub'),
|
||
cta: t('obNext')
|
||
}, {
|
||
icon: 'lock',
|
||
title: t('ob2Title'),
|
||
sub: t('ob2Sub'),
|
||
cta: t('obAllow')
|
||
}, {
|
||
icon: 'shield-check',
|
||
title: t('ob3Title'),
|
||
sub: t('ob3Sub'),
|
||
cta: t('obStart')
|
||
}];
|
||
const s = steps[i];
|
||
const last = i === steps.length - 1;
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
"data-theme": dark ? 'dark' : 'light',
|
||
style: {
|
||
width: 560,
|
||
height: 520,
|
||
borderRadius: 16,
|
||
overflow: 'hidden',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
background: 'var(--bg)',
|
||
boxShadow: '0 0 0 1px rgba(0,0,0,0.12), 0 28px 70px rgba(0,0,0,0.4)',
|
||
fontFamily: 'var(--font-sans)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
padding: '18px 22px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 9
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DMark, {
|
||
size: 24
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 15,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, lang === 'zh' ? '穿山甲' : 'Pangolin')), !last && /*#__PURE__*/React.createElement("button", {
|
||
onClick: onDone,
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
cursor: 'pointer',
|
||
fontSize: 13,
|
||
fontWeight: 600,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, t('obSkip'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
textAlign: 'center',
|
||
padding: '0 56px',
|
||
gap: 22
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 108,
|
||
height: 108,
|
||
borderRadius: '50%',
|
||
background: 'var(--accent-subtle)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: s.icon,
|
||
size: 52,
|
||
stroke: 1.6,
|
||
color: "var(--accent)"
|
||
})), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 26,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, s.title), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 15,
|
||
color: 'var(--fg2)',
|
||
lineHeight: 1.6,
|
||
marginTop: 10,
|
||
maxWidth: 380
|
||
}
|
||
}, s.sub))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '0 40px 36px',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
gap: 20
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 8
|
||
}
|
||
}, steps.map((_, k) => /*#__PURE__*/React.createElement("div", {
|
||
key: k,
|
||
style: {
|
||
width: k === i ? 22 : 8,
|
||
height: 8,
|
||
borderRadius: 999,
|
||
background: k === i ? 'var(--accent)' : 'var(--border-strong)',
|
||
transition: 'all 220ms var(--ease-out)'
|
||
}
|
||
}))), /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => last ? onDone() : setI(i + 1),
|
||
style: {
|
||
width: '100%',
|
||
maxWidth: 360,
|
||
border: 'none',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: '13px',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontWeight: 700,
|
||
fontSize: 15,
|
||
cursor: 'pointer',
|
||
background: 'var(--accent)',
|
||
color: 'var(--fg-on-accent)'
|
||
}
|
||
}, s.cta)));
|
||
}
|
||
|
||
/* ───────── CONTACT US ───────── */
|
||
function DContactView({
|
||
t,
|
||
lang
|
||
}) {
|
||
const channels = [{
|
||
icon: 'send',
|
||
name: t('contactTg'),
|
||
sub: '@PangolinVPN_bot',
|
||
accent: true
|
||
}, {
|
||
icon: 'message-circle',
|
||
name: t('contactLine'),
|
||
sub: '@pangolinvpn'
|
||
}, {
|
||
icon: 'mail',
|
||
name: t('contactEmail'),
|
||
sub: 'support@pangolin.vpn'
|
||
}, {
|
||
icon: 'shopping-bag',
|
||
name: t('contactStore'),
|
||
sub: 'shop.pangolin.vpn'
|
||
}];
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '8px 32px 28px',
|
||
maxWidth: 680
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13.5,
|
||
color: 'var(--fg2)',
|
||
lineHeight: 1.6,
|
||
marginBottom: 18
|
||
}
|
||
}, t('contactIntro')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: '1fr 1fr',
|
||
gap: 12,
|
||
marginBottom: 22
|
||
}
|
||
}, channels.map(c => /*#__PURE__*/React.createElement("button", {
|
||
key: c.name,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 13,
|
||
textAlign: 'left',
|
||
cursor: 'pointer',
|
||
background: c.accent ? 'var(--accent-subtle)' : 'var(--surface)',
|
||
border: `1px solid ${c.accent ? 'var(--accent-border)' : 'var(--border)'}`,
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '14px 16px',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 40,
|
||
height: 40,
|
||
borderRadius: 'var(--radius-md)',
|
||
background: c.accent ? 'var(--accent)' : 'var(--bg-subtle)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(DIcon, {
|
||
name: c.icon,
|
||
size: 20,
|
||
color: c.accent ? '#fff' : 'var(--accent)'
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
minWidth: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, c.name), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg3)',
|
||
fontFamily: 'var(--font-mono)',
|
||
whiteSpace: 'nowrap',
|
||
overflow: 'hidden',
|
||
textOverflow: 'ellipsis'
|
||
}
|
||
}, c.sub)), /*#__PURE__*/React.createElement(DIcon, {
|
||
name: "external-link",
|
||
size: 16,
|
||
color: "var(--fg3)"
|
||
})))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 14
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...dCard,
|
||
flex: 1,
|
||
padding: '16px 18px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, t('contactHoursTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 15,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
marginTop: 6,
|
||
fontFamily: 'var(--font-mono)'
|
||
}
|
||
}, t('contactHours'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...dCard,
|
||
flex: 2,
|
||
padding: '16px 18px',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 14
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, t('contactFaqTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--fg2)',
|
||
marginTop: 5,
|
||
lineHeight: 1.5
|
||
}
|
||
}, t('contactFaq'))), /*#__PURE__*/React.createElement("button", {
|
||
style: {
|
||
border: '1.5px solid var(--border-strong)',
|
||
background: 'transparent',
|
||
color: 'var(--fg1)',
|
||
fontSize: 13,
|
||
fontWeight: 600,
|
||
padding: '9px 15px',
|
||
borderRadius: 'var(--radius-full)',
|
||
cursor: 'pointer',
|
||
whiteSpace: 'nowrap',
|
||
flexShrink: 0
|
||
}
|
||
}, t('contactFaqBtn')))));
|
||
}
|
||
Object.assign(window, {
|
||
DAuthFlow,
|
||
DOnboarding,
|
||
DAccountView,
|
||
DPlansView,
|
||
DRedeemView,
|
||
DContactView
|
||
});
|
||
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/desktop/dflows.jsx", error: String((e && e.message) || e) }); }
|
||
|
||
// ui_kits/desktop/dparts.jsx
|
||
try { (() => {
|
||
/* dparts.jsx — 穿山甲 VPN desktop · atoms (inline Lucide paths) */
|
||
const DLUCIDE = {
|
||
power: '<path d="M12 2v10"/><path d="M18.36 6.64a9 9 0 1 1-12.73 0"/>',
|
||
'shield-check': '<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/>',
|
||
globe: '<circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/>',
|
||
'loader-circle': '<path d="M21 12a9 9 0 1 1-6.219-8.56"/>',
|
||
'chart-no-axes-column': '<line x1="18" x2="18" y1="20" y2="10"/><line x1="12" x2="12" y1="20" y2="4"/><line x1="6" x2="6" y1="20" y2="14"/>',
|
||
settings: '<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/>',
|
||
check: '<path d="M20 6 9 17l-5-5"/>',
|
||
zap: '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
|
||
'arrow-down': '<path d="M12 5v14"/><path d="m19 12-7 7-7-7"/>',
|
||
'arrow-up': '<path d="M12 19V5"/><path d="m5 12 7-7 7 7"/>',
|
||
search: '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
|
||
moon: '<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>',
|
||
sun: '<circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/>',
|
||
crown: '<path d="M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z"/><path d="M5 21h14"/>',
|
||
mail: '<rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/>',
|
||
lock: '<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
|
||
'check-circle': '<path d="M21.801 10A10 10 0 1 1 17 3.335"/><path d="m9 11 3 3L22 4"/>',
|
||
'arrow-left': '<path d="m12 19-7-7 7-7"/><path d="M19 12H5"/>',
|
||
'chevron-right': '<path d="m9 18 6-6-6-6"/>',
|
||
send: '<path d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"/><path d="m21.854 2.147-10.94 10.939"/>',
|
||
'message-circle': '<path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/>',
|
||
'external-link': '<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>',
|
||
'shopping-bag': '<path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 0 1-8 0"/>',
|
||
laptop: '<path d="M18 5a2 2 0 0 1 2 2v8.526a2 2 0 0 0 .212.894l1.575 3.15A.5.5 0 0 1 21.342 21H2.658a.5.5 0 0 1-.445-.724l1.575-3.15A2 2 0 0 0 4 16.526V7a2 2 0 0 1 2-2z"/><path d="M20.054 15.987H3.946"/>',
|
||
smartphone: '<rect width="14" height="20" x="5" y="2" rx="2" ry="2"/><path d="M12 18h.01"/>',
|
||
'monitor-smartphone': '<path d="M18 8V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8"/><path d="M10 19v-3.96 3.15"/><path d="M7 19h5"/><rect width="6" height="10" x="16" y="12" rx="2"/>',
|
||
'log-out': '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/>',
|
||
ticket: '<path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"/><path d="M13 5v2"/><path d="M13 17v2"/><path d="M13 11v2"/>',
|
||
clock: '<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>',
|
||
'play-circle': '<circle cx="12" cy="12" r="10"/><polygon points="10 8 16 12 10 16 10 8"/>',
|
||
compass: '<path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/>',
|
||
'refresh-cw': '<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M3 21v-5h5"/>',
|
||
'credit-card': '<rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/>',
|
||
user: '<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>'
|
||
};
|
||
function DIcon({
|
||
name,
|
||
size = 20,
|
||
stroke = 2,
|
||
color = 'currentColor',
|
||
style = {}
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("svg", {
|
||
width: size,
|
||
height: size,
|
||
viewBox: "0 0 24 24",
|
||
fill: "none",
|
||
stroke: color,
|
||
strokeWidth: stroke,
|
||
strokeLinecap: "round",
|
||
strokeLinejoin: "round",
|
||
style: {
|
||
display: 'block',
|
||
flexShrink: 0,
|
||
...style
|
||
},
|
||
dangerouslySetInnerHTML: {
|
||
__html: DLUCIDE[name] || ''
|
||
}
|
||
});
|
||
}
|
||
function DMark({
|
||
size = 28,
|
||
color = 'var(--accent)'
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("svg", {
|
||
width: size,
|
||
height: size,
|
||
viewBox: "0 0 96 96",
|
||
fill: "none",
|
||
style: {
|
||
display: 'block'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("g", {
|
||
transform: "translate(96,0) scale(-1,1)"
|
||
}, /*#__PURE__*/React.createElement("path", {
|
||
d: "M16 59 Q17 51 26 50 Q34 31 45 30 Q55 30 62 37 Q69 41 75 45 Q85 49 89 44 Q92 50 84 52 Q75 53 67 53 Q66 62 60 62 L56 62 Q54 55 49 55 Q47 62 37 62 L33 62 Q31 56 26 56 Q20 59 16 59 Z",
|
||
fill: color
|
||
}), /*#__PURE__*/React.createElement("g", {
|
||
stroke: "#fff",
|
||
strokeWidth: "2.4",
|
||
strokeLinecap: "round",
|
||
strokeLinejoin: "round",
|
||
fill: "none",
|
||
opacity: "0.5"
|
||
}, /*#__PURE__*/React.createElement("path", {
|
||
d: "M26 50 Q34 43 42 50"
|
||
}), /*#__PURE__*/React.createElement("path", {
|
||
d: "M36 48 Q44 41 52 48"
|
||
}), /*#__PURE__*/React.createElement("path", {
|
||
d: "M46 48 Q54 41 62 49"
|
||
})), /*#__PURE__*/React.createElement("circle", {
|
||
cx: "23",
|
||
cy: "55",
|
||
r: "2.2",
|
||
fill: "#fff",
|
||
opacity: "0.85"
|
||
})));
|
||
}
|
||
function DCC({
|
||
code,
|
||
active,
|
||
size = 34
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: size,
|
||
height: size,
|
||
borderRadius: 'var(--radius-md)',
|
||
flexShrink: 0,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: size * 0.31,
|
||
fontWeight: 600,
|
||
background: active ? 'var(--accent)' : 'var(--bg-subtle)',
|
||
color: active ? '#fff' : 'var(--fg2)'
|
||
}
|
||
}, code);
|
||
}
|
||
function DSignal({
|
||
ping
|
||
}) {
|
||
const lvl = ping < 40 ? 3 : ping < 90 ? 2 : 1;
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 2,
|
||
alignItems: 'flex-end',
|
||
height: 13
|
||
}
|
||
}, [4, 8, 13].map((h, i) => /*#__PURE__*/React.createElement("div", {
|
||
key: i,
|
||
style: {
|
||
width: 3,
|
||
height: h,
|
||
borderRadius: 1,
|
||
background: i < lvl ? 'var(--success)' : 'var(--border-strong)'
|
||
}
|
||
})));
|
||
}
|
||
const DSTRINGS = {
|
||
connect: {
|
||
zh: '连接',
|
||
en: 'Connect'
|
||
},
|
||
servers: {
|
||
zh: '选择节点',
|
||
en: 'Choose server'
|
||
},
|
||
stats: {
|
||
zh: '统计',
|
||
en: 'Stats'
|
||
},
|
||
settings: {
|
||
zh: '设置',
|
||
en: 'Settings'
|
||
},
|
||
navConnect: {
|
||
zh: '连接',
|
||
en: 'Connect'
|
||
},
|
||
navServers: {
|
||
zh: '节点',
|
||
en: 'Servers'
|
||
},
|
||
navStats: {
|
||
zh: '统计',
|
||
en: 'Stats'
|
||
},
|
||
navSettings: {
|
||
zh: '设置',
|
||
en: 'Settings'
|
||
},
|
||
proMember: {
|
||
zh: 'PRO 会员',
|
||
en: 'PRO member'
|
||
},
|
||
until: {
|
||
zh: '至 2026-12-31',
|
||
en: 'until 2026-12-31'
|
||
},
|
||
connected: {
|
||
zh: '已连接',
|
||
en: 'Connected'
|
||
},
|
||
notConnected: {
|
||
zh: '未连接',
|
||
en: 'Not connected'
|
||
},
|
||
capOff: {
|
||
zh: '未连接',
|
||
en: 'Not connected'
|
||
},
|
||
subOff: {
|
||
zh: '点击下方按钮开始保护',
|
||
en: 'Tap the button below to start'
|
||
},
|
||
capConnecting: {
|
||
zh: '连接中…',
|
||
en: 'Connecting…'
|
||
},
|
||
subConnecting: {
|
||
zh: '正在建立加密隧道',
|
||
en: 'Establishing encrypted tunnel'
|
||
},
|
||
capOn: {
|
||
zh: '已连接',
|
||
en: 'Connected'
|
||
},
|
||
subOn: {
|
||
zh: '你的网络已加密保护',
|
||
en: 'Your network is encrypted'
|
||
},
|
||
connectNow: {
|
||
zh: '点击连接',
|
||
en: 'CONNECT'
|
||
},
|
||
search: {
|
||
zh: '搜索国家 / 城市',
|
||
en: 'Search country / city'
|
||
},
|
||
trafficMonth: {
|
||
zh: '本月流量',
|
||
en: 'Traffic'
|
||
},
|
||
avgPing: {
|
||
zh: '平均延迟',
|
||
en: 'Avg ping'
|
||
},
|
||
durMonth: {
|
||
zh: '本月时长',
|
||
en: 'Time'
|
||
},
|
||
weekTraffic: {
|
||
zh: '本周流量 (GB)',
|
||
en: 'This week (GB)'
|
||
},
|
||
autostart: {
|
||
zh: '开机自启动',
|
||
en: 'Launch at login'
|
||
},
|
||
autostartSub: {
|
||
zh: '登录系统时自动启动穿山甲',
|
||
en: 'Start Pangolin when you log in'
|
||
},
|
||
smartRoute: {
|
||
zh: '智能分流',
|
||
en: 'Smart routing'
|
||
},
|
||
smartRouteSub: {
|
||
zh: '海外应用加速,本地直连',
|
||
en: 'Accelerate overseas apps, keep local direct'
|
||
},
|
||
killSwitch: {
|
||
zh: 'Kill Switch',
|
||
en: 'Kill Switch'
|
||
},
|
||
killSwitchSub: {
|
||
zh: '断线时阻断网络,防止 IP 泄露',
|
||
en: 'Block traffic if the VPN drops'
|
||
},
|
||
language: {
|
||
zh: '语言',
|
||
en: 'Language'
|
||
},
|
||
darkAppearance: {
|
||
zh: '深色外观',
|
||
en: 'Dark appearance'
|
||
},
|
||
stateOn: {
|
||
zh: '已开启',
|
||
en: 'On'
|
||
},
|
||
followLight: {
|
||
zh: '跟随浅色',
|
||
en: 'Off'
|
||
},
|
||
protocol: {
|
||
zh: '协议',
|
||
en: 'Protocol'
|
||
},
|
||
version: {
|
||
zh: '版本',
|
||
en: 'Version'
|
||
},
|
||
days: {
|
||
zh: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||
en: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||
},
|
||
/* auth */
|
||
authTagline: {
|
||
zh: '极速 · 稳定 · 省心',
|
||
en: 'Fast · Stable · Effortless'
|
||
},
|
||
tabLogin: {
|
||
zh: '登录',
|
||
en: 'Log in'
|
||
},
|
||
tabRegister: {
|
||
zh: '注册',
|
||
en: 'Sign up'
|
||
},
|
||
emailLabel: {
|
||
zh: '邮箱',
|
||
en: 'Email'
|
||
},
|
||
emailPh: {
|
||
zh: '你的邮箱地址',
|
||
en: 'your@email.com'
|
||
},
|
||
pwLabel: {
|
||
zh: '密码',
|
||
en: 'Password'
|
||
},
|
||
pwPh: {
|
||
zh: '输入密码',
|
||
en: 'Enter password'
|
||
},
|
||
setPwPh: {
|
||
zh: '设置登录密码(用于多端登录)',
|
||
en: 'Set a password (for multi-device login)'
|
||
},
|
||
codeLabel: {
|
||
zh: '邮箱验证码',
|
||
en: 'Verification code'
|
||
},
|
||
codeSentTo: {
|
||
zh: '验证码已发送至',
|
||
en: 'Code sent to'
|
||
},
|
||
sendCode: {
|
||
zh: '发送验证码',
|
||
en: 'Send code'
|
||
},
|
||
resend: {
|
||
zh: '重新发送',
|
||
en: 'Resend'
|
||
},
|
||
doLogin: {
|
||
zh: '登录',
|
||
en: 'Log in'
|
||
},
|
||
doNext: {
|
||
zh: '下一步',
|
||
en: 'Next'
|
||
},
|
||
doCreate: {
|
||
zh: '创建账户',
|
||
en: 'Create account'
|
||
},
|
||
forgotPw: {
|
||
zh: '忘记密码?',
|
||
en: 'Forgot password?'
|
||
},
|
||
tos: {
|
||
zh: '继续即代表同意《服务条款》与《隐私政策》',
|
||
en: 'By continuing you agree to our Terms & Privacy Policy'
|
||
},
|
||
authSidePitch: {
|
||
zh: '一个账户,全平台同步。守护你的每一次连接。',
|
||
en: 'One account, every device. Protecting every connection.'
|
||
},
|
||
/* redeem & buy */
|
||
redeemEntry: {
|
||
zh: '兑换 & 购买',
|
||
en: 'Redeem & buy'
|
||
},
|
||
redeemTitle: {
|
||
zh: '兑换 & 购买',
|
||
en: 'Redeem & buy'
|
||
},
|
||
redeemCodeTitle: {
|
||
zh: '兑换激活码',
|
||
en: 'Redeem a code'
|
||
},
|
||
redeemPh: {
|
||
zh: '输入激活码',
|
||
en: 'Enter activation code'
|
||
},
|
||
redeemBtn: {
|
||
zh: '激活',
|
||
en: 'Redeem'
|
||
},
|
||
redeemOk: {
|
||
zh: '激活成功 · PRO 已开通',
|
||
en: 'Activated · PRO unlocked'
|
||
},
|
||
buyTitle: {
|
||
zh: '购买渠道',
|
||
en: 'Where to buy'
|
||
},
|
||
buySub: {
|
||
zh: '客户端内不支持直接支付。请通过以下渠道获取激活码:',
|
||
en: 'In-app payment is unavailable. Get an activation code via:'
|
||
},
|
||
chStore: {
|
||
zh: '自助发卡商店',
|
||
en: 'Self-serve store'
|
||
},
|
||
chStoreSub: {
|
||
zh: '支付宝 / 微信 · 自动发码',
|
||
en: 'Alipay / WeChat · instant code'
|
||
},
|
||
chUsdtSub: {
|
||
zh: '链上转账 · 最隐私 · 自动发码',
|
||
en: 'On-chain · most private · instant code'
|
||
},
|
||
smartSelect: {
|
||
zh: '智能选择',
|
||
en: 'Smart select'
|
||
},
|
||
smartSub: {
|
||
zh: '根据当前网络环境,自动选择最优节点',
|
||
en: 'Picks the best node for your network'
|
||
},
|
||
recommended: {
|
||
zh: '推荐',
|
||
en: 'Recommended'
|
||
},
|
||
twoFA: {
|
||
zh: '双重认证',
|
||
en: 'Two-factor auth'
|
||
},
|
||
twoFASub: {
|
||
zh: 'PRO 专享 · 登录需身份验证器动态码',
|
||
en: 'PRO only · TOTP code at login'
|
||
},
|
||
ucEntry: {
|
||
zh: 'Web 用户中心',
|
||
en: 'Web account center'
|
||
},
|
||
ucEntrySub: {
|
||
zh: '订阅导入 · 邀请返利',
|
||
en: 'Subscription import · referral'
|
||
},
|
||
ucOpen: {
|
||
zh: '打开',
|
||
en: 'Open'
|
||
},
|
||
/* quota + free (口径见 CLAUDE.md §7,与移动端一致) */
|
||
quotaToday: {
|
||
zh: '今日剩余',
|
||
en: 'Left today'
|
||
},
|
||
minutes: {
|
||
zh: '分钟',
|
||
en: 'min'
|
||
},
|
||
quotaFree: {
|
||
zh: '免费版 · 每日 10 分钟',
|
||
en: 'Free · 10 min/day'
|
||
},
|
||
watchAd: {
|
||
zh: '看广告开始使用',
|
||
en: 'Watch ad to start'
|
||
},
|
||
adUnlocked: {
|
||
zh: '已解锁 · 今日可用',
|
||
en: 'Unlocked for today'
|
||
},
|
||
freePlanName: {
|
||
zh: '免费版',
|
||
en: 'Free'
|
||
},
|
||
freeDemo: {
|
||
zh: '演示:免费版视角',
|
||
en: 'Demo: free-tier view'
|
||
},
|
||
freeDemoSub: {
|
||
zh: '切换连接页的额度展示与套餐横幅',
|
||
en: 'Toggles quota UI & plan banner'
|
||
},
|
||
checkUpdate: {
|
||
zh: '检查更新',
|
||
en: 'Check for updates'
|
||
},
|
||
replayOnboarding: {
|
||
zh: '查看首次引导',
|
||
en: 'View onboarding'
|
||
},
|
||
replayOnboardingSub: {
|
||
zh: '演示用 · 重看 3 屏引导',
|
||
en: 'Demo · replay the 3 intro screens'
|
||
},
|
||
/* 2FA login step (与用户中心一致) */
|
||
twoFATitle: {
|
||
zh: '双重认证',
|
||
en: 'Two-factor auth'
|
||
},
|
||
twoFAHint: {
|
||
zh: '你的账户已开启双重认证。请输入身份验证器 App 中的 6 位动态码。',
|
||
en: 'Two-factor auth is on for this account. Enter the 6-digit code from your authenticator app.'
|
||
},
|
||
twoFAConfirm: {
|
||
zh: '验证并登录',
|
||
en: 'Verify & log in'
|
||
},
|
||
twoFABack: {
|
||
zh: '返回上一步',
|
||
en: 'Back'
|
||
},
|
||
chTgSub: {
|
||
zh: '@PangolinVPN_bot',
|
||
en: '@PangolinVPN_bot'
|
||
},
|
||
chLineSub: {
|
||
zh: '@pangolinvpn',
|
||
en: '@pangolinvpn'
|
||
},
|
||
chEmail: {
|
||
zh: '邮箱客服',
|
||
en: 'Email support'
|
||
},
|
||
chEmailSub: {
|
||
zh: 'buy@pangolin.vpn',
|
||
en: 'buy@pangolin.vpn'
|
||
},
|
||
/* account management (integrated dashboard) */
|
||
navAccount: {
|
||
zh: '账户',
|
||
en: 'Account'
|
||
},
|
||
curPlan: {
|
||
zh: '当前套餐',
|
||
en: 'Current plan'
|
||
},
|
||
renew: {
|
||
zh: '续费 / 升级',
|
||
en: 'Renew / Upgrade'
|
||
},
|
||
myDevices: {
|
||
zh: '我的设备',
|
||
en: 'My devices'
|
||
},
|
||
devicesSub: {
|
||
zh: 'PRO 套餐最多 5 台设备同时在线',
|
||
en: 'Up to 5 devices on PRO'
|
||
},
|
||
thisDevice: {
|
||
zh: '当前设备',
|
||
en: 'This device'
|
||
},
|
||
lastActive: {
|
||
zh: '最近活跃',
|
||
en: 'Last active'
|
||
},
|
||
remove: {
|
||
zh: '移除',
|
||
en: 'Remove'
|
||
},
|
||
choosePlan: {
|
||
zh: '选择套餐',
|
||
en: 'Choose your plan'
|
||
},
|
||
free: {
|
||
zh: '免费版',
|
||
en: 'Free'
|
||
},
|
||
proPlan: {
|
||
zh: '专业版',
|
||
en: 'Pro'
|
||
},
|
||
team: {
|
||
zh: '团队版',
|
||
en: 'Team'
|
||
},
|
||
perMonth: {
|
||
zh: '/月',
|
||
en: '/mo'
|
||
},
|
||
current: {
|
||
zh: '当前',
|
||
en: 'Current'
|
||
},
|
||
upgrade: {
|
||
zh: '立即升级',
|
||
en: 'Upgrade'
|
||
},
|
||
choose: {
|
||
zh: '选择',
|
||
en: 'Choose'
|
||
},
|
||
mostPopular: {
|
||
zh: '最受欢迎',
|
||
en: 'Popular'
|
||
},
|
||
feFree: {
|
||
zh: ['仅 1 个基础节点', '每日 10 分钟时长', '使用前观看广告', '注册享 7 天免费试用'],
|
||
en: ['1 basic node only', '10 min per day', 'Watch an ad to start', '7-day free trial on sign-up']
|
||
},
|
||
fePro: {
|
||
zh: ['80+ 全球节点', '无限流量 · 极速', '5 台设备同时在线', '流媒体优化'],
|
||
en: ['80+ locations', 'Unlimited · fast', '5 devices', 'Streaming optimized']
|
||
},
|
||
feTeam: {
|
||
zh: ['Pro 全部功能', '10 个成员席位', '集中计费与管理', '优先客服'],
|
||
en: ['Everything in Pro', '10 seats', 'Central billing', 'Priority support']
|
||
},
|
||
/* account info */
|
||
accInfoTitle: {
|
||
zh: '账户信息',
|
||
en: 'Account info'
|
||
},
|
||
accEmail: {
|
||
zh: '邮箱',
|
||
en: 'Email'
|
||
},
|
||
accPassword: {
|
||
zh: '密码',
|
||
en: 'Password'
|
||
},
|
||
accChange: {
|
||
zh: '修改',
|
||
en: 'Change'
|
||
},
|
||
accUsage: {
|
||
zh: '本月用量',
|
||
en: 'Used this month'
|
||
},
|
||
accSignOut: {
|
||
zh: '退出登录',
|
||
en: 'Sign out'
|
||
},
|
||
backToAccount: {
|
||
zh: '返回账户',
|
||
en: 'Back to account'
|
||
},
|
||
/* demo replay */
|
||
replayAuth: {
|
||
zh: '查看登录 / 注册流程',
|
||
en: 'View login / sign-up'
|
||
},
|
||
replayAuthSub: {
|
||
zh: '演示用 · 退出到登录页',
|
||
en: 'Demo · back to login'
|
||
},
|
||
replayOnboarding: {
|
||
zh: '查看首次引导',
|
||
en: 'View onboarding'
|
||
},
|
||
replayOnboardingSub: {
|
||
zh: '演示用 · 重看引导页',
|
||
en: 'Demo · replay intro'
|
||
},
|
||
/* onboarding */
|
||
obSkip: {
|
||
zh: '跳过',
|
||
en: 'Skip'
|
||
},
|
||
obNext: {
|
||
zh: '下一步',
|
||
en: 'Next'
|
||
},
|
||
obAllow: {
|
||
zh: '允许并继续',
|
||
en: 'Allow & continue'
|
||
},
|
||
obStart: {
|
||
zh: '开始使用',
|
||
en: 'Get started'
|
||
},
|
||
ob1Title: {
|
||
zh: '一键连接全球',
|
||
en: 'Connect worldwide'
|
||
},
|
||
ob1Sub: {
|
||
zh: '80+ 节点,智能选择最快线路,稳定不掉线。',
|
||
en: '80+ locations, auto-fastest routing, rock-solid.'
|
||
},
|
||
ob2Title: {
|
||
zh: '授权网络配置',
|
||
en: 'Allow network setup'
|
||
},
|
||
ob2Sub: {
|
||
zh: '系统会请求添加一个网络配置,用于建立加密隧道。',
|
||
en: 'Your system will ask to add a network profile to build the encrypted tunnel.'
|
||
},
|
||
ob3Title: {
|
||
zh: '安全 · 无日志',
|
||
en: 'Private by design'
|
||
},
|
||
ob3Sub: {
|
||
zh: '端到端加密,我们不记录你的任何浏览数据。',
|
||
en: 'End-to-end encrypted. We keep zero browsing logs.'
|
||
},
|
||
/* contact us */
|
||
navContact: {
|
||
zh: '联系我们',
|
||
en: 'Contact us'
|
||
},
|
||
contactIntro: {
|
||
zh: '遇到问题?通过以下任一渠道联系我们,通常数分钟内回复。',
|
||
en: 'Need help? Reach us through any channel below — we usually reply within minutes.'
|
||
},
|
||
contactTg: {
|
||
zh: 'Telegram 客服',
|
||
en: 'Telegram support'
|
||
},
|
||
contactLine: {
|
||
zh: 'LINE 客服',
|
||
en: 'LINE support'
|
||
},
|
||
contactEmail: {
|
||
zh: '邮箱客服',
|
||
en: 'Email support'
|
||
},
|
||
contactStore: {
|
||
zh: '自助发卡商店',
|
||
en: 'Self-serve store'
|
||
},
|
||
contactHoursTitle: {
|
||
zh: '服务时间',
|
||
en: 'Support hours'
|
||
},
|
||
contactHours: {
|
||
zh: '每日 9:00 – 24:00 (GMT+8)',
|
||
en: 'Daily 9:00 – 24:00 (GMT+8)'
|
||
},
|
||
contactFaqTitle: {
|
||
zh: '常见问题',
|
||
en: 'FAQ'
|
||
},
|
||
contactFaq: {
|
||
zh: '连不上 / 兑换码无效 / 多设备登录,先查阅帮助中心。',
|
||
en: 'Connection issues, invalid codes, multi-device — check the Help Center first.'
|
||
},
|
||
contactFaqBtn: {
|
||
zh: '打开帮助中心',
|
||
en: 'Open Help Center'
|
||
}
|
||
};
|
||
function dT(lang) {
|
||
return k => {
|
||
const v = DSTRINGS[k];
|
||
return v ? v[lang] || k : k;
|
||
};
|
||
}
|
||
function dName(s, lang) {
|
||
return lang === 'zh' ? s.name : s.en;
|
||
}
|
||
function dSub(s, lang) {
|
||
if (lang === 'zh') return s.tag || s.en;
|
||
return s.tag === '流媒体优化' ? 'Streaming' : s.tag === 'P2P' ? 'P2P' : s.name;
|
||
}
|
||
Object.assign(window, {
|
||
DIcon,
|
||
DMark,
|
||
DCC,
|
||
DSignal,
|
||
DSTRINGS,
|
||
dT,
|
||
dName,
|
||
dSub,
|
||
DSERVERS: [{
|
||
code: 'HK',
|
||
name: '香港 · 流媒体',
|
||
en: 'Hong Kong',
|
||
ping: 18,
|
||
tag: '流媒体优化'
|
||
}, {
|
||
code: 'JP',
|
||
name: '日本 东京',
|
||
en: 'Tokyo',
|
||
ping: 32,
|
||
tag: 'P2P'
|
||
}, {
|
||
code: 'TW',
|
||
name: '台湾 台北',
|
||
en: 'Taipei',
|
||
ping: 28,
|
||
tag: ''
|
||
}, {
|
||
code: 'SG',
|
||
name: '新加坡',
|
||
en: 'Singapore',
|
||
ping: 54,
|
||
tag: 'P2P'
|
||
}, {
|
||
code: 'KR',
|
||
name: '韩国 首尔',
|
||
en: 'Seoul',
|
||
ping: 41,
|
||
tag: ''
|
||
}, {
|
||
code: 'US',
|
||
name: '美国 洛杉矶',
|
||
en: 'Los Angeles',
|
||
ping: 146,
|
||
tag: '流媒体优化'
|
||
}, {
|
||
code: 'DE',
|
||
name: '德国 法兰克福',
|
||
en: 'Frankfurt',
|
||
ping: 198,
|
||
tag: ''
|
||
}, {
|
||
code: 'UK',
|
||
name: '英国 伦敦',
|
||
en: 'London',
|
||
ping: 210,
|
||
tag: ''
|
||
}]
|
||
});
|
||
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/desktop/dparts.jsx", error: String((e && e.message) || e) }); }
|
||
|
||
// ui_kits/desktop/macos-window.jsx
|
||
try { (() => {
|
||
/* BEGIN USAGE */
|
||
// MacOS.jsx — Simplified macOS Tahoe (Liquid Glass) window
|
||
// Based on the macOS Tahoe UI Kit. No image assets, no dependencies.
|
||
// Exports (to window): MacWindow, MacSidebar, MacSidebarItem, MacSidebarHeader, MacToolbar, MacGlass, MacTrafficLights
|
||
//
|
||
// Usage — wrap your app content in <MacWindow> to get the window chrome
|
||
// (traffic lights + titlebar). Props: width, height, title, sidebar (pass a
|
||
// <MacSidebar> element); compose MacToolbar/MacGlass inside as needed:
|
||
//
|
||
// <MacWindow width={980} height={620} title="Documents"
|
||
// sidebar={<MacSidebar>…</MacSidebar>}>
|
||
// ...your app content...
|
||
// </MacWindow>
|
||
/* END USAGE */
|
||
|
||
const MAC_FONT = '-apple-system, BlinkMacSystemFont, "SF Pro", "Helvetica Neue", sans-serif';
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// Liquid glass primitive — blur + white tint + inset highlight
|
||
// ─────────────────────────────────────────────────────────────
|
||
function MacGlass({
|
||
children,
|
||
radius = 296,
|
||
dark = false,
|
||
style = {}
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'relative',
|
||
borderRadius: radius,
|
||
...style
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
inset: 0,
|
||
borderRadius: radius,
|
||
background: dark ? 'rgba(255,255,255,0.08)' : 'rgba(255,255,255,0.35)',
|
||
backdropFilter: 'blur(40px) saturate(180%)',
|
||
WebkitBackdropFilter: 'blur(40px) saturate(180%)',
|
||
border: dark ? '0.5px solid rgba(255,255,255,0.12)' : '0.5px solid rgba(255,255,255,0.6)',
|
||
boxShadow: dark ? '0 8px 40px rgba(0,0,0,0.2)' : '0 8px 40px rgba(0,0,0,0.08), inset 0 1px 0 rgba(255,255,255,0.4)'
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'relative',
|
||
zIndex: 1
|
||
}
|
||
}, children));
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// Traffic lights (14px, Tahoe colors)
|
||
// ─────────────────────────────────────────────────────────────
|
||
function MacTrafficLights({
|
||
style = {}
|
||
}) {
|
||
const dot = bg => /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 14,
|
||
height: 14,
|
||
borderRadius: '50%',
|
||
background: bg,
|
||
border: '0.5px solid rgba(0,0,0,0.1)'
|
||
}
|
||
});
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 9,
|
||
alignItems: 'center',
|
||
padding: 1,
|
||
...style
|
||
}
|
||
}, dot('#ff736a'), dot('#febc2e'), dot('#19c332'));
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// Toolbar — title + single glass pill icon
|
||
// ─────────────────────────────────────────────────────────────
|
||
function MacToolbar({
|
||
title = 'Folder'
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 8,
|
||
alignItems: 'center',
|
||
padding: 8,
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: MAC_FONT,
|
||
fontSize: 15,
|
||
fontWeight: 700,
|
||
color: 'rgba(0,0,0,0.85)',
|
||
whiteSpace: 'nowrap',
|
||
paddingLeft: 8
|
||
}
|
||
}, title), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}), /*#__PURE__*/React.createElement(MacGlass, null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 36,
|
||
height: 36,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 14,
|
||
height: 14,
|
||
borderRadius: '50%',
|
||
background: '#4c4c4c',
|
||
opacity: 0.4
|
||
}
|
||
}))), /*#__PURE__*/React.createElement(MacGlass, null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 140,
|
||
height: 36,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 6,
|
||
padding: '0 12px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("svg", {
|
||
width: "13",
|
||
height: "13",
|
||
viewBox: "0 0 13 13",
|
||
fill: "none"
|
||
}, /*#__PURE__*/React.createElement("circle", {
|
||
cx: "5.5",
|
||
cy: "5.5",
|
||
r: "4",
|
||
stroke: "#727272",
|
||
strokeWidth: "1.5"
|
||
}), /*#__PURE__*/React.createElement("path", {
|
||
d: "M8.5 8.5l3 3",
|
||
stroke: "#727272",
|
||
strokeWidth: "1.5",
|
||
strokeLinecap: "round"
|
||
})), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: MAC_FONT,
|
||
fontSize: 13,
|
||
fontWeight: 500,
|
||
color: '#727272'
|
||
}
|
||
}, "Search"))));
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// Sidebar — frosted glass panel floating inside the window
|
||
// ─────────────────────────────────────────────────────────────
|
||
function MacSidebarItem({
|
||
label,
|
||
selected = false
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 6,
|
||
height: 24,
|
||
padding: '4px 10px 4px 6px',
|
||
margin: '0 10px',
|
||
borderRadius: 8,
|
||
position: 'relative',
|
||
fontFamily: MAC_FONT,
|
||
fontSize: 11,
|
||
fontWeight: 500
|
||
}
|
||
}, selected && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
inset: 0,
|
||
borderRadius: 8,
|
||
background: 'rgba(0,0,0,0.11)',
|
||
mixBlendMode: 'multiply'
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 14,
|
||
height: 14,
|
||
borderRadius: '50%',
|
||
background: selected ? '#007aff' : 'rgba(0,0,0,0.4)',
|
||
opacity: selected ? 1 : 0.5,
|
||
flexShrink: 0,
|
||
position: 'relative'
|
||
}
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
color: 'rgba(0,0,0,0.85)',
|
||
position: 'relative'
|
||
}
|
||
}, label));
|
||
}
|
||
function MacSidebar({
|
||
children
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 220,
|
||
height: '100%',
|
||
padding: 8,
|
||
flexShrink: 0,
|
||
position: 'relative',
|
||
display: 'flex',
|
||
flexDirection: 'column'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
inset: 8,
|
||
borderRadius: 18,
|
||
background: 'rgba(210,225,245,0.45)',
|
||
backdropFilter: 'blur(50px) saturate(200%)',
|
||
WebkitBackdropFilter: 'blur(50px) saturate(200%)',
|
||
border: '0.5px solid rgba(255,255,255,0.5)',
|
||
boxShadow: '0 8px 40px rgba(0,0,0,0.10), inset 0 1px 0 rgba(255,255,255,0.35)'
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'relative',
|
||
zIndex: 1,
|
||
padding: '10px 0',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 2
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
height: 32,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
padding: '0 10px',
|
||
marginBottom: 4
|
||
}
|
||
}, /*#__PURE__*/React.createElement(MacTrafficLights, null)), children));
|
||
}
|
||
function MacSidebarHeader({
|
||
title
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '14px 18px 5px',
|
||
fontFamily: MAC_FONT,
|
||
fontSize: 11,
|
||
fontWeight: 700,
|
||
color: 'rgba(0,0,0,0.5)'
|
||
}
|
||
}, title);
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// Window — r:26, big shadow, sidebar + toolbar + content
|
||
// ─────────────────────────────────────────────────────────────
|
||
function MacWindow({
|
||
width = 900,
|
||
height = 600,
|
||
title = 'Folder',
|
||
sidebar,
|
||
children
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width,
|
||
height,
|
||
borderRadius: 26,
|
||
overflow: 'hidden',
|
||
background: '#fff',
|
||
boxShadow: '0 0 0 1px rgba(0,0,0,0.23), 0 16px 48px rgba(0,0,0,0.35)',
|
||
display: 'flex',
|
||
position: 'relative',
|
||
fontFamily: MAC_FONT
|
||
}
|
||
}, /*#__PURE__*/React.createElement(MacSidebar, null, sidebar), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
flexDirection: 'column'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(MacToolbar, {
|
||
title: title
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
overflow: 'auto',
|
||
padding: '4px 8px'
|
||
}
|
||
}, children)));
|
||
}
|
||
Object.assign(window, {
|
||
MacWindow,
|
||
MacSidebar,
|
||
MacSidebarItem,
|
||
MacSidebarHeader,
|
||
MacToolbar,
|
||
MacGlass,
|
||
MacTrafficLights
|
||
});
|
||
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/desktop/macos-window.jsx", error: String((e && e.message) || e) }); }
|
||
|
||
// ui_kits/mobile/flows.jsx
|
||
try { (() => {
|
||
/* flows.jsx — 穿山甲 VPN mobile · auth, onboarding, redeem/buy */
|
||
const {
|
||
useState: useStateF
|
||
} = React;
|
||
|
||
/* shared field */
|
||
function Field({
|
||
icon,
|
||
label,
|
||
children
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("label", {
|
||
style: {
|
||
display: 'block'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
display: 'block',
|
||
fontSize: 12,
|
||
fontWeight: 600,
|
||
color: 'var(--fg2)',
|
||
marginBottom: 7
|
||
}
|
||
}, label), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 10,
|
||
background: 'var(--surface)',
|
||
border: '1.5px solid var(--border-strong)',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '12px 14px'
|
||
}
|
||
}, icon && /*#__PURE__*/React.createElement(Icon, {
|
||
name: icon,
|
||
size: 18,
|
||
color: "var(--fg3)"
|
||
}), children));
|
||
}
|
||
const inputStyle = {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
outline: 'none',
|
||
flex: 1,
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 15,
|
||
color: 'var(--fg1)',
|
||
minWidth: 0
|
||
};
|
||
|
||
/* ───────── AUTH (login + email-code register) ───────── */
|
||
function AuthFlow({
|
||
t,
|
||
lang,
|
||
onDone
|
||
}) {
|
||
const [mode, setMode] = useStateF('login'); // login | register
|
||
const [step, setStep] = useStateF(0); // register: 0 email+code, 1 set password
|
||
const [email, setEmail] = useStateF('');
|
||
const [code, setCode] = useStateF('');
|
||
const [pw, setPw] = useStateF('');
|
||
const [sent, setSent] = useStateF(false);
|
||
const [totp, setTotp] = useStateF(false); // 2FA step after login (PRO)
|
||
const [otp, setOtp] = useStateF('');
|
||
const valid = /\S+@\S+\.\S+/.test(email);
|
||
const tab = (id, label) => /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => {
|
||
setMode(id);
|
||
setStep(0);
|
||
setSent(false);
|
||
},
|
||
style: {
|
||
flex: 1,
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
background: 'transparent',
|
||
padding: '10px 0',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 15,
|
||
fontWeight: mode === id ? 700 : 500,
|
||
color: mode === id ? 'var(--fg1)' : 'var(--fg3)',
|
||
borderBottom: mode === id ? '2px solid var(--accent)' : '2px solid transparent'
|
||
}
|
||
}, label);
|
||
const primaryBtn = (label, onClick, enabled) => /*#__PURE__*/React.createElement("button", {
|
||
onClick: enabled ? onClick : undefined,
|
||
disabled: !enabled,
|
||
style: {
|
||
width: '100%',
|
||
border: 'none',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: '14px',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontWeight: 700,
|
||
fontSize: 15,
|
||
cursor: enabled ? 'pointer' : 'not-allowed',
|
||
background: 'var(--accent)',
|
||
color: 'var(--fg-on-accent)',
|
||
opacity: enabled ? 1 : 0.45,
|
||
marginTop: 4
|
||
}
|
||
}, label);
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
height: '100%',
|
||
padding: '0 28px',
|
||
background: 'var(--bg)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
paddingTop: 18,
|
||
paddingBottom: 28
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Mark, {
|
||
size: 56
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
textAlign: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 24,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('brand')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--accent)',
|
||
fontWeight: 600,
|
||
letterSpacing: '0.04em',
|
||
marginTop: 4
|
||
}
|
||
}, t('authTagline')))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
borderBottom: '1px solid var(--border)',
|
||
marginBottom: 22
|
||
}
|
||
}, tab('login', t('tabLogin')), tab('register', t('tabRegister'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 16,
|
||
flex: 1
|
||
}
|
||
}, mode === 'login' && !totp && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Field, {
|
||
icon: "mail",
|
||
label: t('emailLabel')
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
style: inputStyle,
|
||
value: email,
|
||
onChange: e => setEmail(e.target.value),
|
||
placeholder: t('emailPh')
|
||
})), /*#__PURE__*/React.createElement(Field, {
|
||
icon: "lock",
|
||
label: t('pwLabel')
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
style: inputStyle,
|
||
type: "password",
|
||
value: pw,
|
||
onChange: e => setPw(e.target.value),
|
||
placeholder: t('pwPh')
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
textAlign: 'right',
|
||
marginTop: -6
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--accent)',
|
||
fontWeight: 600
|
||
}
|
||
}, t('forgotPw'))), primaryBtn(t('doLogin'), () => setTotp(true), valid && pw.length > 0)), mode === 'login' && totp && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 8
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "shield-check",
|
||
size: 19,
|
||
color: "var(--accent)"
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 18,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('twoFATitle')), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
color: 'var(--accent)',
|
||
background: 'var(--accent-subtle)',
|
||
border: '1px solid var(--accent-border)',
|
||
padding: '2px 8px',
|
||
borderRadius: 999
|
||
}
|
||
}, "PRO")), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
lineHeight: 1.6,
|
||
marginTop: -6
|
||
}
|
||
}, t('twoFAHint')), /*#__PURE__*/React.createElement(Field, {
|
||
icon: "shield-check",
|
||
label: t('twoFATitle')
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
style: {
|
||
...inputStyle,
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 20,
|
||
letterSpacing: '0.5em',
|
||
textAlign: 'center'
|
||
},
|
||
value: otp,
|
||
onChange: e => setOtp(e.target.value.replace(/\D/g, '').slice(0, 6)),
|
||
placeholder: "\xB7\xB7\xB7\xB7\xB7\xB7"
|
||
})), primaryBtn(t('twoFAConfirm'), onDone, otp.length === 6), /*#__PURE__*/React.createElement("div", {
|
||
onClick: () => setTotp(false),
|
||
style: {
|
||
textAlign: 'center',
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
cursor: 'pointer'
|
||
}
|
||
}, t('twoFABack'))), mode === 'register' && step === 0 && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Field, {
|
||
icon: "mail",
|
||
label: t('emailLabel')
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
style: inputStyle,
|
||
value: email,
|
||
onChange: e => setEmail(e.target.value),
|
||
placeholder: t('emailPh')
|
||
}), /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => valid && setSent(true),
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
cursor: valid ? 'pointer' : 'default',
|
||
color: valid ? 'var(--accent)' : 'var(--fg3)',
|
||
fontWeight: 700,
|
||
fontSize: 13,
|
||
whiteSpace: 'nowrap',
|
||
flexShrink: 0
|
||
}
|
||
}, sent ? t('resend') : t('sendCode'))), sent && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--success)',
|
||
marginTop: -8,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 6
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "check-circle",
|
||
size: 14,
|
||
color: "var(--success)"
|
||
}), t('codeSentTo'), " ", email), /*#__PURE__*/React.createElement(Field, {
|
||
icon: "shield-check",
|
||
label: t('codeLabel')
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
style: {
|
||
...inputStyle,
|
||
fontFamily: 'var(--font-mono)',
|
||
letterSpacing: '0.3em'
|
||
},
|
||
value: code,
|
||
onChange: e => setCode(e.target.value.replace(/\D/g, '').slice(0, 6)),
|
||
placeholder: "\xB7\xB7\xB7\xB7\xB7\xB7"
|
||
})), primaryBtn(t('doNext'), () => setStep(1), sent && code.length === 6)), mode === 'register' && step === 1 && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--fg2)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 7,
|
||
marginBottom: -4
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "check-circle",
|
||
size: 15,
|
||
color: "var(--success)"
|
||
}), email), /*#__PURE__*/React.createElement(Field, {
|
||
icon: "lock",
|
||
label: t('pwLabel')
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
style: inputStyle,
|
||
type: "password",
|
||
value: pw,
|
||
onChange: e => setPw(e.target.value),
|
||
placeholder: t('setPwPh')
|
||
})), primaryBtn(t('doCreate'), onDone, pw.length >= 6))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11,
|
||
color: 'var(--fg3)',
|
||
textAlign: 'center',
|
||
lineHeight: 1.5,
|
||
padding: '18px 8px 24px'
|
||
}
|
||
}, t('tos')));
|
||
}
|
||
|
||
/* ───────── ONBOARDING (3 screens) ───────── */
|
||
function Onboarding({
|
||
t,
|
||
lang,
|
||
onDone
|
||
}) {
|
||
const [i, setI] = useStateF(0);
|
||
const slides = [{
|
||
icon: 'globe',
|
||
tint: 'var(--accent)',
|
||
bg: 'var(--accent-subtle)',
|
||
title: t('ob1Title'),
|
||
sub: t('ob1Sub'),
|
||
cta: t('obNext')
|
||
}, {
|
||
icon: 'shield',
|
||
tint: 'var(--accent)',
|
||
bg: 'var(--accent-subtle)',
|
||
title: t('ob2Title'),
|
||
sub: t('ob2Sub'),
|
||
cta: t('obAllow')
|
||
}, {
|
||
icon: 'shield-check',
|
||
tint: 'var(--success)',
|
||
bg: 'var(--success-subtle)',
|
||
title: t('ob3Title'),
|
||
sub: t('ob3Sub'),
|
||
cta: t('obStart')
|
||
}];
|
||
const s = slides[i];
|
||
const next = () => i < 2 ? setI(i + 1) : onDone();
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
height: '100%',
|
||
padding: '0 28px',
|
||
background: 'var(--bg)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
justifyContent: 'flex-end',
|
||
paddingTop: 10
|
||
}
|
||
}, /*#__PURE__*/React.createElement("button", {
|
||
onClick: onDone,
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
cursor: 'pointer',
|
||
color: 'var(--fg3)',
|
||
fontSize: 14,
|
||
fontWeight: 600,
|
||
padding: 8
|
||
}
|
||
}, t('obSkip'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 28,
|
||
textAlign: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 120,
|
||
height: 120,
|
||
borderRadius: '50%',
|
||
background: s.bg,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: s.icon,
|
||
size: 52,
|
||
stroke: 1.6,
|
||
color: s.tint
|
||
})), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 26,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, s.title), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 15,
|
||
color: 'var(--fg2)',
|
||
lineHeight: 1.6,
|
||
marginTop: 12,
|
||
maxWidth: 280
|
||
}
|
||
}, s.sub))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
justifyContent: 'center',
|
||
gap: 8,
|
||
marginBottom: 22
|
||
}
|
||
}, slides.map((_, k) => /*#__PURE__*/React.createElement("div", {
|
||
key: k,
|
||
style: {
|
||
width: k === i ? 22 : 7,
|
||
height: 7,
|
||
borderRadius: 999,
|
||
background: k === i ? 'var(--accent)' : 'var(--border-strong)',
|
||
transition: 'all 220ms var(--ease-out)'
|
||
}
|
||
}))), /*#__PURE__*/React.createElement("button", {
|
||
onClick: next,
|
||
style: {
|
||
width: '100%',
|
||
border: 'none',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: '15px',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontWeight: 700,
|
||
fontSize: 15,
|
||
cursor: 'pointer',
|
||
background: 'var(--accent)',
|
||
color: 'var(--fg-on-accent)',
|
||
marginBottom: 26
|
||
}
|
||
}, s.cta));
|
||
}
|
||
|
||
/* ───────── REDEEM & BUY ───────── */
|
||
function RedeemScreen({
|
||
t,
|
||
lang,
|
||
onBack
|
||
}) {
|
||
const [code, setCode] = useStateF('');
|
||
const [ok, setOk] = useStateF(false);
|
||
const channels = [{
|
||
icon: 'shopping-bag',
|
||
name: t('chStore'),
|
||
sub: t('chStoreSub'),
|
||
accent: true
|
||
}, {
|
||
icon: 'credit-card',
|
||
name: 'USDT (TRC20)',
|
||
sub: t('chUsdtSub')
|
||
}, {
|
||
icon: 'send',
|
||
name: 'Telegram',
|
||
sub: t('chTgSub')
|
||
}, {
|
||
icon: 'message-circle',
|
||
name: 'LINE',
|
||
sub: t('chLineSub')
|
||
}, {
|
||
icon: 'mail',
|
||
name: t('chEmail'),
|
||
sub: t('chEmailSub')
|
||
}];
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
height: '100%'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 8,
|
||
padding: '6px 16px 14px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("button", {
|
||
onClick: onBack,
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
cursor: 'pointer',
|
||
padding: 6,
|
||
display: 'flex'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "arrow-left",
|
||
size: 22,
|
||
color: "var(--fg1)"
|
||
})), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 18,
|
||
color: 'var(--fg1)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, t('redeemTitle'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
overflow: 'auto',
|
||
padding: '0 20px 20px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
boxShadow: 'var(--shadow-sm)',
|
||
padding: 18,
|
||
marginBottom: 22
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 12
|
||
}
|
||
}, t('redeemCodeTitle')), ok ? /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 9,
|
||
color: 'var(--success)',
|
||
fontWeight: 600,
|
||
fontSize: 15,
|
||
padding: '6px 0'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "check-circle",
|
||
size: 20,
|
||
color: "var(--success)"
|
||
}), t('redeemOk')) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 10
|
||
}
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
value: code,
|
||
onChange: e => setCode(e.target.value.toUpperCase()),
|
||
placeholder: t('redeemPh'),
|
||
style: {
|
||
flex: 1,
|
||
minWidth: 0,
|
||
border: '1.5px solid var(--border-strong)',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '12px 14px',
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 14,
|
||
letterSpacing: '0.08em',
|
||
color: 'var(--fg1)',
|
||
background: 'var(--bg)',
|
||
outline: 'none'
|
||
}
|
||
}), /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => code.length >= 4 && setOk(true),
|
||
style: {
|
||
border: 'none',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '0 20px',
|
||
background: 'var(--accent)',
|
||
color: '#fff',
|
||
fontWeight: 700,
|
||
fontSize: 14,
|
||
cursor: 'pointer',
|
||
flexShrink: 0
|
||
}
|
||
}, t('redeemBtn'))))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 6
|
||
}
|
||
}, t('buyTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
lineHeight: 1.5,
|
||
marginBottom: 14
|
||
}
|
||
}, t('buySub')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 10
|
||
}
|
||
}, channels.map(c => /*#__PURE__*/React.createElement("button", {
|
||
key: c.name,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 13,
|
||
width: '100%',
|
||
textAlign: 'left',
|
||
cursor: 'pointer',
|
||
background: c.accent ? 'var(--accent-subtle)' : 'var(--surface)',
|
||
border: `1px solid ${c.accent ? 'var(--accent-border)' : 'var(--border)'}`,
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '14px 16px',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 40,
|
||
height: 40,
|
||
borderRadius: 'var(--radius-md)',
|
||
background: c.accent ? 'var(--accent)' : 'var(--bg-subtle)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: c.icon,
|
||
size: 20,
|
||
color: c.accent ? '#fff' : 'var(--accent)'
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 15,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, c.name), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
fontFamily: c.icon === 'send' || c.icon === 'mail' || c.icon === 'message-circle' ? 'var(--font-mono)' : 'var(--font-sans)'
|
||
}
|
||
}, c.sub)), /*#__PURE__*/React.createElement(Icon, {
|
||
name: "external-link",
|
||
size: 17,
|
||
color: "var(--fg3)"
|
||
}))))));
|
||
}
|
||
|
||
/* ───────── PLANS (shown on upgrade) ───────── */
|
||
function PlansScreen({
|
||
t,
|
||
lang,
|
||
onBack,
|
||
onChoose
|
||
}) {
|
||
const plans = [{
|
||
id: 'free',
|
||
name: t('free'),
|
||
price: '¥0',
|
||
feats: t('feFree'),
|
||
cta: t('current'),
|
||
variant: 'cur'
|
||
}, {
|
||
id: 'pro',
|
||
name: t('proPlan'),
|
||
price: '¥25',
|
||
feats: t('fePro'),
|
||
cta: t('upgrade'),
|
||
variant: 'pro'
|
||
}, {
|
||
id: 'team',
|
||
name: t('team'),
|
||
price: '¥99',
|
||
feats: t('feTeam'),
|
||
cta: t('choose'),
|
||
variant: 'plain'
|
||
}];
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
height: '100%'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 8,
|
||
padding: '6px 16px 14px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("button", {
|
||
onClick: onBack,
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
cursor: 'pointer',
|
||
padding: 6,
|
||
display: 'flex'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "arrow-left",
|
||
size: 22,
|
||
color: "var(--fg1)"
|
||
})), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 18,
|
||
color: 'var(--fg1)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, t('choosePlan'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
overflow: 'auto',
|
||
padding: '4px 20px 20px',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 14
|
||
}
|
||
}, plans.map(p => {
|
||
const isPro = p.variant === 'pro';
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
key: p.id,
|
||
style: {
|
||
position: 'relative',
|
||
borderRadius: 'var(--radius-xl)',
|
||
padding: '18px 20px',
|
||
...(isPro ? {
|
||
background: 'linear-gradient(155deg,var(--clay-600),var(--clay-800))',
|
||
color: '#fff',
|
||
boxShadow: 'var(--shadow-md)'
|
||
} : {
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
})
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'baseline',
|
||
justifyContent: 'space-between'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14,
|
||
fontWeight: 600,
|
||
color: isPro ? '#fff' : 'var(--fg2)',
|
||
opacity: isPro ? 0.9 : 1
|
||
}
|
||
}, p.name, isPro && /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
marginLeft: 8,
|
||
fontSize: 10.5,
|
||
fontWeight: 700,
|
||
background: '#fff',
|
||
color: 'var(--clay-700)',
|
||
padding: '2px 8px',
|
||
borderRadius: 999
|
||
}
|
||
}, t('mostPopular'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 26,
|
||
fontWeight: 700,
|
||
color: isPro ? '#fff' : 'var(--fg1)'
|
||
}
|
||
}, p.price, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 12,
|
||
fontWeight: 500,
|
||
opacity: 0.7
|
||
}
|
||
}, t('perMonth')))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexWrap: 'wrap',
|
||
gap: '7px 16px',
|
||
margin: '14px 0 16px'
|
||
}
|
||
}, p.feats.map((f, i) => /*#__PURE__*/React.createElement("div", {
|
||
key: i,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 7,
|
||
fontSize: 12.5,
|
||
color: isPro ? 'rgba(255,255,255,.92)' : 'var(--fg2)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "check",
|
||
size: 14,
|
||
color: isPro ? '#fff' : 'var(--success)',
|
||
stroke: 2.4
|
||
}), f))), /*#__PURE__*/React.createElement("button", {
|
||
onClick: p.variant !== 'cur' ? onChoose : undefined,
|
||
style: {
|
||
width: '100%',
|
||
border: p.variant === 'cur' ? '1.5px solid var(--border-strong)' : 'none',
|
||
cursor: p.variant === 'cur' ? 'default' : 'pointer',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: '12px',
|
||
fontWeight: 700,
|
||
fontSize: 14,
|
||
background: isPro ? '#fff' : p.variant === 'cur' ? 'transparent' : 'var(--accent)',
|
||
color: isPro ? 'var(--clay-700)' : p.variant === 'cur' ? 'var(--fg2)' : '#fff'
|
||
}
|
||
}, p.cta));
|
||
})));
|
||
}
|
||
|
||
/* ───────── CONTACT US ───────── */
|
||
function ContactScreen({
|
||
t,
|
||
lang,
|
||
onBack
|
||
}) {
|
||
const channels = [{
|
||
icon: 'send',
|
||
name: 'Telegram',
|
||
sub: '@PangolinVPN_bot',
|
||
accent: true
|
||
}, {
|
||
icon: 'message-circle',
|
||
name: 'LINE',
|
||
sub: '@pangolinvpn'
|
||
}, {
|
||
icon: 'mail',
|
||
name: t('contactEmail'),
|
||
sub: 'support@pangolin.vpn'
|
||
}, {
|
||
icon: 'shopping-bag',
|
||
name: t('contactStore'),
|
||
sub: 'shop.pangolin.vpn'
|
||
}];
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
height: '100%'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 8,
|
||
padding: '6px 16px 14px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("button", {
|
||
onClick: onBack,
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
cursor: 'pointer',
|
||
padding: 6,
|
||
display: 'flex'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "arrow-left",
|
||
size: 22,
|
||
color: "var(--fg1)"
|
||
})), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 18,
|
||
color: 'var(--fg1)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, t('contactTitle'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
overflow: 'auto',
|
||
padding: '0 20px 20px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--fg2)',
|
||
lineHeight: 1.6,
|
||
marginBottom: 16
|
||
}
|
||
}, t('contactIntro')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 10,
|
||
marginBottom: 18
|
||
}
|
||
}, channels.map(c => /*#__PURE__*/React.createElement("button", {
|
||
key: c.name,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 13,
|
||
width: '100%',
|
||
textAlign: 'left',
|
||
cursor: 'pointer',
|
||
background: c.accent ? 'var(--accent-subtle)' : 'var(--surface)',
|
||
border: `1px solid ${c.accent ? 'var(--accent-border)' : 'var(--border)'}`,
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '14px 16px',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 40,
|
||
height: 40,
|
||
borderRadius: 'var(--radius-md)',
|
||
background: c.accent ? 'var(--accent)' : 'var(--bg-subtle)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: c.icon,
|
||
size: 20,
|
||
color: c.accent ? '#fff' : 'var(--accent)'
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
minWidth: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 15,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, c.name), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
fontFamily: 'var(--font-mono)'
|
||
}
|
||
}, c.sub)), /*#__PURE__*/React.createElement(Icon, {
|
||
name: "external-link",
|
||
size: 17,
|
||
color: "var(--fg3)"
|
||
})))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
boxShadow: 'var(--shadow-sm)',
|
||
padding: '14px 16px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
fontWeight: 600,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, t('contactHoursTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
marginTop: 5,
|
||
fontFamily: 'var(--font-mono)'
|
||
}
|
||
}, t('contactHours')))));
|
||
}
|
||
Object.assign(window, {
|
||
AuthFlow,
|
||
Onboarding,
|
||
RedeemScreen,
|
||
PlansScreen,
|
||
ContactScreen
|
||
});
|
||
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/mobile/flows.jsx", error: String((e && e.message) || e) }); }
|
||
|
||
// ui_kits/mobile/ios-frame.jsx
|
||
try { (() => {
|
||
/* BEGIN USAGE */
|
||
// iOS.jsx — Simplified iOS 26 (Liquid Glass) device frame
|
||
// Based on the iOS 26 UI Kit + Figma status bar spec. No assets, no deps.
|
||
// Exports (to window): IOSDevice, IOSStatusBar, IOSNavBar, IOSGlassPill, IOSList, IOSListRow, IOSKeyboard
|
||
//
|
||
// Usage — wrap your screen content in <IOSDevice> to get the bezel, status bar
|
||
// and home indicator (props: title, dark, keyboard):
|
||
//
|
||
// <IOSDevice title="Settings">
|
||
// ...your screen content...
|
||
// </IOSDevice>
|
||
// <IOSDevice dark title="Search" keyboard>…</IOSDevice>
|
||
/* END USAGE */
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// Status bar
|
||
// ─────────────────────────────────────────────────────────────
|
||
function IOSStatusBar({
|
||
dark = false,
|
||
time = '9:41'
|
||
}) {
|
||
const c = dark ? '#fff' : '#000';
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 154,
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
padding: '21px 24px 19px',
|
||
boxSizing: 'border-box',
|
||
position: 'relative',
|
||
zIndex: 20,
|
||
width: '100%'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
height: 22,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
paddingTop: 1.5
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: '-apple-system, "SF Pro", system-ui',
|
||
fontWeight: 590,
|
||
fontSize: 17,
|
||
lineHeight: '22px',
|
||
color: c
|
||
}
|
||
}, time)), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
height: 22,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 7,
|
||
paddingTop: 1,
|
||
paddingRight: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("svg", {
|
||
width: "19",
|
||
height: "12",
|
||
viewBox: "0 0 19 12"
|
||
}, /*#__PURE__*/React.createElement("rect", {
|
||
x: "0",
|
||
y: "7.5",
|
||
width: "3.2",
|
||
height: "4.5",
|
||
rx: "0.7",
|
||
fill: c
|
||
}), /*#__PURE__*/React.createElement("rect", {
|
||
x: "4.8",
|
||
y: "5",
|
||
width: "3.2",
|
||
height: "7",
|
||
rx: "0.7",
|
||
fill: c
|
||
}), /*#__PURE__*/React.createElement("rect", {
|
||
x: "9.6",
|
||
y: "2.5",
|
||
width: "3.2",
|
||
height: "9.5",
|
||
rx: "0.7",
|
||
fill: c
|
||
}), /*#__PURE__*/React.createElement("rect", {
|
||
x: "14.4",
|
||
y: "0",
|
||
width: "3.2",
|
||
height: "12",
|
||
rx: "0.7",
|
||
fill: c
|
||
})), /*#__PURE__*/React.createElement("svg", {
|
||
width: "17",
|
||
height: "12",
|
||
viewBox: "0 0 17 12"
|
||
}, /*#__PURE__*/React.createElement("path", {
|
||
d: "M8.5 3.2C10.8 3.2 12.9 4.1 14.4 5.6L15.5 4.5C13.7 2.7 11.2 1.5 8.5 1.5C5.8 1.5 3.3 2.7 1.5 4.5L2.6 5.6C4.1 4.1 6.2 3.2 8.5 3.2Z",
|
||
fill: c
|
||
}), /*#__PURE__*/React.createElement("path", {
|
||
d: "M8.5 6.8C9.9 6.8 11.1 7.3 12 8.2L13.1 7.1C11.8 5.9 10.2 5.1 8.5 5.1C6.8 5.1 5.2 5.9 3.9 7.1L5 8.2C5.9 7.3 7.1 6.8 8.5 6.8Z",
|
||
fill: c
|
||
}), /*#__PURE__*/React.createElement("circle", {
|
||
cx: "8.5",
|
||
cy: "10.5",
|
||
r: "1.5",
|
||
fill: c
|
||
})), /*#__PURE__*/React.createElement("svg", {
|
||
width: "27",
|
||
height: "13",
|
||
viewBox: "0 0 27 13"
|
||
}, /*#__PURE__*/React.createElement("rect", {
|
||
x: "0.5",
|
||
y: "0.5",
|
||
width: "23",
|
||
height: "12",
|
||
rx: "3.5",
|
||
stroke: c,
|
||
strokeOpacity: "0.35",
|
||
fill: "none"
|
||
}), /*#__PURE__*/React.createElement("rect", {
|
||
x: "2",
|
||
y: "2",
|
||
width: "20",
|
||
height: "9",
|
||
rx: "2",
|
||
fill: c
|
||
}), /*#__PURE__*/React.createElement("path", {
|
||
d: "M25 4.5V8.5C25.8 8.2 26.5 7.2 26.5 6.5C26.5 5.8 25.8 4.8 25 4.5Z",
|
||
fill: c,
|
||
fillOpacity: "0.4"
|
||
}))));
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// Liquid glass pill — blur + tint + shine
|
||
// ─────────────────────────────────────────────────────────────
|
||
function IOSGlassPill({
|
||
children,
|
||
dark = false,
|
||
style = {}
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
height: 44,
|
||
minWidth: 44,
|
||
borderRadius: 9999,
|
||
position: 'relative',
|
||
overflow: 'hidden',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
boxShadow: dark ? '0 2px 6px rgba(0,0,0,0.35), 0 6px 16px rgba(0,0,0,0.2)' : '0 1px 3px rgba(0,0,0,0.07), 0 3px 10px rgba(0,0,0,0.06)',
|
||
...style
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
inset: 0,
|
||
borderRadius: 9999,
|
||
backdropFilter: 'blur(12px) saturate(180%)',
|
||
WebkitBackdropFilter: 'blur(12px) saturate(180%)',
|
||
background: dark ? 'rgba(120,120,128,0.28)' : 'rgba(255,255,255,0.5)'
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
inset: 0,
|
||
borderRadius: 9999,
|
||
boxShadow: dark ? 'inset 1.5px 1.5px 1px rgba(255,255,255,0.15), inset -1px -1px 1px rgba(255,255,255,0.08)' : 'inset 1.5px 1.5px 1px rgba(255,255,255,0.7), inset -1px -1px 1px rgba(255,255,255,0.4)',
|
||
border: dark ? '0.5px solid rgba(255,255,255,0.15)' : '0.5px solid rgba(0,0,0,0.06)'
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'relative',
|
||
zIndex: 1,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
padding: '0 4px'
|
||
}
|
||
}, children));
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// Navigation bar — glass pills + large title
|
||
// ─────────────────────────────────────────────────────────────
|
||
function IOSNavBar({
|
||
title = 'Title',
|
||
dark = false,
|
||
trailingIcon = true
|
||
}) {
|
||
const muted = dark ? 'rgba(255,255,255,0.6)' : '#404040';
|
||
const text = dark ? '#fff' : '#000';
|
||
const pillIcon = content => /*#__PURE__*/React.createElement(IOSGlassPill, {
|
||
dark: dark
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 36,
|
||
height: 36,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center'
|
||
}
|
||
}, content));
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 10,
|
||
paddingTop: 62,
|
||
paddingBottom: 10,
|
||
position: 'relative',
|
||
zIndex: 5
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
padding: '0 16px'
|
||
}
|
||
}, pillIcon(/*#__PURE__*/React.createElement("svg", {
|
||
width: "12",
|
||
height: "20",
|
||
viewBox: "0 0 12 20",
|
||
fill: "none",
|
||
style: {
|
||
marginLeft: -1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("path", {
|
||
d: "M10 2L2 10l8 8",
|
||
stroke: muted,
|
||
strokeWidth: "2.5",
|
||
strokeLinecap: "round",
|
||
strokeLinejoin: "round"
|
||
}))), trailingIcon && pillIcon(/*#__PURE__*/React.createElement("svg", {
|
||
width: "22",
|
||
height: "6",
|
||
viewBox: "0 0 22 6"
|
||
}, /*#__PURE__*/React.createElement("circle", {
|
||
cx: "3",
|
||
cy: "3",
|
||
r: "2.5",
|
||
fill: muted
|
||
}), /*#__PURE__*/React.createElement("circle", {
|
||
cx: "11",
|
||
cy: "3",
|
||
r: "2.5",
|
||
fill: muted
|
||
}), /*#__PURE__*/React.createElement("circle", {
|
||
cx: "19",
|
||
cy: "3",
|
||
r: "2.5",
|
||
fill: muted
|
||
})))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '0 16px',
|
||
fontFamily: '-apple-system, system-ui',
|
||
fontSize: 34,
|
||
fontWeight: 700,
|
||
lineHeight: '41px',
|
||
color: text,
|
||
letterSpacing: 0.4
|
||
}
|
||
}, title));
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// Grouped list (inset card, r:26) + row (52px)
|
||
// ─────────────────────────────────────────────────────────────
|
||
function IOSListRow({
|
||
title,
|
||
detail,
|
||
icon,
|
||
chevron = true,
|
||
isLast = false,
|
||
dark = false
|
||
}) {
|
||
const text = dark ? '#fff' : '#000';
|
||
const sec = dark ? 'rgba(235,235,245,0.6)' : 'rgba(60,60,67,0.6)';
|
||
const ter = dark ? 'rgba(235,235,245,0.3)' : 'rgba(60,60,67,0.3)';
|
||
const sep = dark ? 'rgba(84,84,88,0.65)' : 'rgba(60,60,67,0.12)';
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
minHeight: 52,
|
||
padding: '0 16px',
|
||
position: 'relative',
|
||
fontFamily: '-apple-system, system-ui',
|
||
fontSize: 17,
|
||
letterSpacing: -0.43
|
||
}
|
||
}, icon && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 30,
|
||
height: 30,
|
||
borderRadius: 7,
|
||
background: icon,
|
||
marginRight: 12,
|
||
flexShrink: 0
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
color: text
|
||
}
|
||
}, title), detail && /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
color: sec,
|
||
marginRight: 6
|
||
}
|
||
}, detail), chevron && /*#__PURE__*/React.createElement("svg", {
|
||
width: "8",
|
||
height: "14",
|
||
viewBox: "0 0 8 14",
|
||
style: {
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement("path", {
|
||
d: "M1 1l6 6-6 6",
|
||
stroke: ter,
|
||
strokeWidth: "2",
|
||
fill: "none",
|
||
strokeLinecap: "round",
|
||
strokeLinejoin: "round"
|
||
})), !isLast && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
bottom: 0,
|
||
right: 0,
|
||
left: icon ? 58 : 16,
|
||
height: 0.5,
|
||
background: sep
|
||
}
|
||
}));
|
||
}
|
||
function IOSList({
|
||
header,
|
||
children,
|
||
dark = false
|
||
}) {
|
||
const hc = dark ? 'rgba(235,235,245,0.6)' : 'rgba(60,60,67,0.6)';
|
||
const bg = dark ? '#1C1C1E' : '#fff';
|
||
return /*#__PURE__*/React.createElement("div", null, header && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: '-apple-system, system-ui',
|
||
fontSize: 13,
|
||
color: hc,
|
||
textTransform: 'uppercase',
|
||
padding: '8px 36px 6px',
|
||
letterSpacing: -0.08
|
||
}
|
||
}, header), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
background: bg,
|
||
borderRadius: 26,
|
||
margin: '0 16px',
|
||
overflow: 'hidden'
|
||
}
|
||
}, children));
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// Device frame
|
||
// ─────────────────────────────────────────────────────────────
|
||
function IOSDevice({
|
||
children,
|
||
width = 402,
|
||
height = 874,
|
||
dark = false,
|
||
title,
|
||
keyboard = false
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width,
|
||
height,
|
||
borderRadius: 48,
|
||
overflow: 'hidden',
|
||
position: 'relative',
|
||
background: dark ? '#000' : '#F2F2F7',
|
||
boxShadow: '0 40px 80px rgba(0,0,0,0.18), 0 0 0 1px rgba(0,0,0,0.12)',
|
||
fontFamily: '-apple-system, system-ui, sans-serif',
|
||
WebkitFontSmoothing: 'antialiased'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
top: 11,
|
||
left: '50%',
|
||
transform: 'translateX(-50%)',
|
||
width: 126,
|
||
height: 37,
|
||
borderRadius: 24,
|
||
background: '#000',
|
||
zIndex: 50
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
top: 0,
|
||
left: 0,
|
||
right: 0,
|
||
zIndex: 10
|
||
}
|
||
}, /*#__PURE__*/React.createElement(IOSStatusBar, {
|
||
dark: dark
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
height: '100%',
|
||
display: 'flex',
|
||
flexDirection: 'column'
|
||
}
|
||
}, title !== undefined && /*#__PURE__*/React.createElement(IOSNavBar, {
|
||
title: title,
|
||
dark: dark
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
overflow: 'auto'
|
||
}
|
||
}, children), keyboard && /*#__PURE__*/React.createElement(IOSKeyboard, {
|
||
dark: dark
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
bottom: 0,
|
||
left: 0,
|
||
right: 0,
|
||
zIndex: 60,
|
||
height: 34,
|
||
display: 'flex',
|
||
justifyContent: 'center',
|
||
alignItems: 'flex-end',
|
||
paddingBottom: 8,
|
||
pointerEvents: 'none'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 139,
|
||
height: 5,
|
||
borderRadius: 100,
|
||
background: dark ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.25)'
|
||
}
|
||
})));
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// Keyboard — iOS 26 liquid glass
|
||
// ─────────────────────────────────────────────────────────────
|
||
function IOSKeyboard({
|
||
dark = false
|
||
}) {
|
||
const glyph = dark ? 'rgba(255,255,255,0.7)' : '#595959';
|
||
const sugg = dark ? 'rgba(255,255,255,0.6)' : '#333';
|
||
const keyBg = dark ? 'rgba(255,255,255,0.22)' : 'rgba(255,255,255,0.85)';
|
||
|
||
// special-key icons
|
||
const icons = {
|
||
shift: /*#__PURE__*/React.createElement("svg", {
|
||
width: "19",
|
||
height: "17",
|
||
viewBox: "0 0 19 17"
|
||
}, /*#__PURE__*/React.createElement("path", {
|
||
d: "M9.5 1L1 9.5h4.5V16h8V9.5H18L9.5 1z",
|
||
fill: glyph
|
||
})),
|
||
del: /*#__PURE__*/React.createElement("svg", {
|
||
width: "23",
|
||
height: "17",
|
||
viewBox: "0 0 23 17"
|
||
}, /*#__PURE__*/React.createElement("path", {
|
||
d: "M7 1h13a2 2 0 012 2v11a2 2 0 01-2 2H7l-6-7.5L7 1z",
|
||
fill: "none",
|
||
stroke: glyph,
|
||
strokeWidth: "1.6",
|
||
strokeLinejoin: "round"
|
||
}), /*#__PURE__*/React.createElement("path", {
|
||
d: "M10 5l7 7M17 5l-7 7",
|
||
stroke: glyph,
|
||
strokeWidth: "1.6",
|
||
strokeLinecap: "round"
|
||
})),
|
||
ret: /*#__PURE__*/React.createElement("svg", {
|
||
width: "20",
|
||
height: "14",
|
||
viewBox: "0 0 20 14"
|
||
}, /*#__PURE__*/React.createElement("path", {
|
||
d: "M18 1v6H4m0 0l4-4M4 7l4 4",
|
||
fill: "none",
|
||
stroke: "#fff",
|
||
strokeWidth: "1.8",
|
||
strokeLinecap: "round",
|
||
strokeLinejoin: "round"
|
||
}))
|
||
};
|
||
const key = (content, {
|
||
w,
|
||
flex,
|
||
ret,
|
||
fs = 25,
|
||
k
|
||
} = {}) => /*#__PURE__*/React.createElement("div", {
|
||
key: k,
|
||
style: {
|
||
height: 42,
|
||
borderRadius: 8.5,
|
||
flex: flex ? 1 : undefined,
|
||
width: w,
|
||
minWidth: 0,
|
||
background: ret ? '#08f' : keyBg,
|
||
boxShadow: '0 1px 0 rgba(0,0,0,0.075)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
fontFamily: '-apple-system, "SF Compact", system-ui',
|
||
fontSize: fs,
|
||
fontWeight: 458,
|
||
color: ret ? '#fff' : glyph
|
||
}
|
||
}, content);
|
||
const row = (keys, pad = 0) => /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 6.5,
|
||
justifyContent: 'center',
|
||
padding: `0 ${pad}px`
|
||
}
|
||
}, keys.map(l => key(l, {
|
||
flex: true,
|
||
k: l
|
||
})));
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'relative',
|
||
zIndex: 15,
|
||
borderRadius: 27,
|
||
overflow: 'hidden',
|
||
padding: '11px 0 2px',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
boxShadow: dark ? '0 -2px 20px rgba(0,0,0,0.09)' : '0 -1px 6px rgba(0,0,0,0.018), 0 -3px 20px rgba(0,0,0,0.012)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
inset: 0,
|
||
borderRadius: 27,
|
||
backdropFilter: 'blur(12px) saturate(180%)',
|
||
WebkitBackdropFilter: 'blur(12px) saturate(180%)',
|
||
background: dark ? 'rgba(120,120,128,0.14)' : 'rgba(255,255,255,0.25)'
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
inset: 0,
|
||
borderRadius: 27,
|
||
boxShadow: dark ? 'inset 1.5px 1.5px 1px rgba(255,255,255,0.15)' : 'inset 1.5px 1.5px 1px rgba(255,255,255,0.7), inset -1px -1px 1px rgba(255,255,255,0.4)',
|
||
border: dark ? '0.5px solid rgba(255,255,255,0.15)' : '0.5px solid rgba(0,0,0,0.06)',
|
||
pointerEvents: 'none'
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 20,
|
||
alignItems: 'center',
|
||
padding: '8px 22px 13px',
|
||
width: '100%',
|
||
boxSizing: 'border-box',
|
||
position: 'relative'
|
||
}
|
||
}, ['"The"', 'the', 'to'].map((w, i) => /*#__PURE__*/React.createElement(React.Fragment, {
|
||
key: i
|
||
}, i > 0 && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 1,
|
||
height: 25,
|
||
background: '#ccc',
|
||
opacity: 0.3
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
textAlign: 'center',
|
||
fontFamily: '-apple-system, system-ui',
|
||
fontSize: 17,
|
||
color: sugg,
|
||
letterSpacing: -0.43,
|
||
lineHeight: '22px'
|
||
}
|
||
}, w)))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 13,
|
||
padding: '0 6.5px',
|
||
width: '100%',
|
||
boxSizing: 'border-box',
|
||
position: 'relative'
|
||
}
|
||
}, row(['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']), row(['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'], 20), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 14.25,
|
||
alignItems: 'center'
|
||
}
|
||
}, key(icons.shift, {
|
||
w: 45,
|
||
k: 'shift'
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 6.5,
|
||
flex: 1
|
||
}
|
||
}, ['z', 'x', 'c', 'v', 'b', 'n', 'm'].map(l => key(l, {
|
||
flex: true,
|
||
k: l
|
||
}))), key(icons.del, {
|
||
w: 45,
|
||
k: 'del'
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 6,
|
||
alignItems: 'center'
|
||
}
|
||
}, key('ABC', {
|
||
w: 92.25,
|
||
fs: 18,
|
||
k: 'abc'
|
||
}), key('', {
|
||
flex: true,
|
||
k: 'space'
|
||
}), key(icons.ret, {
|
||
w: 92.25,
|
||
ret: true,
|
||
k: 'ret'
|
||
}))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
height: 56,
|
||
width: '100%',
|
||
position: 'relative'
|
||
}
|
||
}));
|
||
}
|
||
Object.assign(window, {
|
||
IOSDevice,
|
||
IOSStatusBar,
|
||
IOSNavBar,
|
||
IOSGlassPill,
|
||
IOSList,
|
||
IOSListRow,
|
||
IOSKeyboard
|
||
});
|
||
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/mobile/ios-frame.jsx", error: String((e && e.message) || e) }); }
|
||
|
||
// ui_kits/mobile/parts.jsx
|
||
try { (() => {
|
||
/* parts.jsx — 穿山甲 VPN mobile · shared atoms
|
||
Icon uses inline Lucide path data (lucide.dev) for reliability in React. */
|
||
|
||
const LUCIDE = {
|
||
power: '<path d="M12 2v10"/><path d="M18.36 6.64a9 9 0 1 1-12.73 0"/>',
|
||
'shield-check': '<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/>',
|
||
globe: '<circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/>',
|
||
settings: '<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/>',
|
||
'chevron-right': '<path d="m9 18 6-6-6-6"/>',
|
||
'chevron-down': '<path d="m6 9 6 6 6-6"/>',
|
||
check: '<path d="M20 6 9 17l-5-5"/>',
|
||
zap: '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
|
||
'loader-circle': '<path d="M21 12a9 9 0 1 1-6.219-8.56"/>',
|
||
user: '<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>',
|
||
x: '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
|
||
crown: '<path d="M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z"/><path d="M5 21h14"/>',
|
||
'map-pin': '<path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"/><circle cx="12" cy="10" r="3"/>',
|
||
'arrow-down': '<path d="M12 5v14"/><path d="m19 12-7 7-7-7"/>',
|
||
'arrow-up': '<path d="M12 19V5"/><path d="m5 12 7-7 7 7"/>',
|
||
search: '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
|
||
moon: '<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>',
|
||
sun: '<circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/>',
|
||
shield: '<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/>',
|
||
'refresh-cw': '<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M3 21v-5h5"/>',
|
||
chart: '<line x1="18" x2="18" y1="20" y2="10"/><line x1="12" x2="12" y1="20" y2="4"/><line x1="6" x2="6" y1="20" y2="14"/>',
|
||
wifi: '<path d="M12 20h.01"/><path d="M2 8.82a15 15 0 0 1 20 0"/><path d="M5 12.859a10 10 0 0 1 14 0"/><path d="M8.5 16.429a5 5 0 0 1 7 0"/>',
|
||
mail: '<rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/>',
|
||
lock: '<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
|
||
'arrow-left': '<path d="m12 19-7-7 7-7"/><path d="M19 12H5"/>',
|
||
'shopping-bag': '<path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 0 1-8 0"/>',
|
||
send: '<path d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"/><path d="m21.854 2.147-10.94 10.939"/>',
|
||
'message-circle': '<path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/>',
|
||
'external-link': '<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>',
|
||
'check-circle': '<path d="M21.801 10A10 10 0 1 1 17 3.335"/><path d="m9 11 3 3L22 4"/>',
|
||
'log-out': '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/>',
|
||
compass: '<path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/>',
|
||
laptop: '<path d="M18 5a2 2 0 0 1 2 2v8.526a2 2 0 0 0 .212.894l1.575 3.15A.5.5 0 0 1 21.342 21H2.658a.5.5 0 0 1-.445-.724l1.575-3.15A2 2 0 0 0 4 16.526V7a2 2 0 0 1 2-2z"/><path d="M20.054 15.987H3.946"/>',
|
||
smartphone: '<rect width="14" height="20" x="5" y="2" rx="2" ry="2"/><path d="M12 18h.01"/>',
|
||
'monitor-smartphone': '<path d="M18 8V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8"/><path d="M10 19v-3.96 3.15"/><path d="M7 19h5"/><rect width="6" height="10" x="16" y="12" rx="2"/>',
|
||
ticket: '<path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"/><path d="M13 5v2"/><path d="M13 17v2"/><path d="M13 11v2"/>',
|
||
clock: '<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>',
|
||
'credit-card': '<rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/>',
|
||
'play-circle': '<circle cx="12" cy="12" r="10"/><polygon points="10 8 16 12 10 16 10 8"/>'
|
||
};
|
||
function Icon({
|
||
name,
|
||
size = 24,
|
||
stroke = 2,
|
||
color = 'currentColor',
|
||
style = {}
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("svg", {
|
||
width: size,
|
||
height: size,
|
||
viewBox: "0 0 24 24",
|
||
fill: "none",
|
||
stroke: color,
|
||
strokeWidth: stroke,
|
||
strokeLinecap: "round",
|
||
strokeLinejoin: "round",
|
||
style: {
|
||
display: 'block',
|
||
flexShrink: 0,
|
||
...style
|
||
},
|
||
dangerouslySetInnerHTML: {
|
||
__html: LUCIDE[name] || ''
|
||
}
|
||
});
|
||
}
|
||
|
||
/* Brand mark — walking pangolin (matches app icon), facing right */
|
||
function Mark({
|
||
size = 40,
|
||
color = 'var(--accent)'
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("svg", {
|
||
width: size,
|
||
height: size,
|
||
viewBox: "0 0 96 96",
|
||
fill: "none",
|
||
style: {
|
||
display: 'block'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("g", {
|
||
transform: "translate(96,0) scale(-1,1)"
|
||
}, /*#__PURE__*/React.createElement("path", {
|
||
d: "M16 59 Q17 51 26 50 Q34 31 45 30 Q55 30 62 37 Q69 41 75 45 Q85 49 89 44 Q92 50 84 52 Q75 53 67 53 Q66 62 60 62 L56 62 Q54 55 49 55 Q47 62 37 62 L33 62 Q31 56 26 56 Q20 59 16 59 Z",
|
||
fill: color
|
||
}), /*#__PURE__*/React.createElement("g", {
|
||
stroke: "#fff",
|
||
strokeWidth: "2.4",
|
||
strokeLinecap: "round",
|
||
strokeLinejoin: "round",
|
||
fill: "none",
|
||
opacity: "0.5"
|
||
}, /*#__PURE__*/React.createElement("path", {
|
||
d: "M26 50 Q34 43 42 50"
|
||
}), /*#__PURE__*/React.createElement("path", {
|
||
d: "M36 48 Q44 41 52 48"
|
||
}), /*#__PURE__*/React.createElement("path", {
|
||
d: "M46 48 Q54 41 62 49"
|
||
})), /*#__PURE__*/React.createElement("circle", {
|
||
cx: "23",
|
||
cy: "55",
|
||
r: "2.2",
|
||
fill: "#fff",
|
||
opacity: "0.85"
|
||
})));
|
||
}
|
||
|
||
/* Country code tile */
|
||
function CC({
|
||
code,
|
||
active
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 38,
|
||
height: 38,
|
||
borderRadius: 'var(--radius-md)',
|
||
flexShrink: 0,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 12,
|
||
fontWeight: 600,
|
||
background: active ? 'var(--accent)' : 'var(--bg-subtle)',
|
||
color: active ? '#fff' : 'var(--fg2)'
|
||
}
|
||
}, code);
|
||
}
|
||
|
||
/* Signal bars from ping */
|
||
function Signal({
|
||
ping
|
||
}) {
|
||
const lvl = ping < 40 ? 3 : ping < 90 ? 2 : 1;
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 2,
|
||
alignItems: 'flex-end',
|
||
height: 14
|
||
}
|
||
}, [5, 9, 14].map((h, i) => /*#__PURE__*/React.createElement("div", {
|
||
key: i,
|
||
style: {
|
||
width: 3,
|
||
height: h,
|
||
borderRadius: 1,
|
||
background: i < lvl ? 'var(--success)' : 'var(--border-strong)'
|
||
}
|
||
})));
|
||
}
|
||
|
||
/* Pill button */
|
||
function Pill({
|
||
children,
|
||
variant = 'primary',
|
||
onClick,
|
||
style = {},
|
||
disabled
|
||
}) {
|
||
const v = {
|
||
primary: {
|
||
background: 'var(--accent)',
|
||
color: 'var(--fg-on-accent)'
|
||
},
|
||
secondary: {
|
||
background: 'var(--surface)',
|
||
color: 'var(--fg1)',
|
||
border: '1.5px solid var(--border-strong)'
|
||
},
|
||
ghost: {
|
||
background: 'transparent',
|
||
color: 'var(--accent)'
|
||
},
|
||
danger: {
|
||
background: 'var(--danger-subtle)',
|
||
color: 'var(--danger)'
|
||
}
|
||
}[variant];
|
||
return /*#__PURE__*/React.createElement("button", {
|
||
onClick: onClick,
|
||
disabled: disabled,
|
||
style: {
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 8,
|
||
fontFamily: 'var(--font-sans)',
|
||
fontWeight: 600,
|
||
fontSize: 15,
|
||
border: 'none',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: '13px 22px',
|
||
cursor: disabled ? 'not-allowed' : 'pointer',
|
||
transition: 'all 140ms var(--ease-out)',
|
||
opacity: disabled ? 0.5 : 1,
|
||
...v,
|
||
...style
|
||
}
|
||
}, children);
|
||
}
|
||
|
||
/* ───────── i18n ─────────
|
||
Real product shows ONE language at a time (toggle in settings).
|
||
Bilingual specimens only live in the design-system preview cards. */
|
||
const STRINGS = {
|
||
brand: {
|
||
zh: '穿山甲',
|
||
en: 'Pangolin'
|
||
},
|
||
online: {
|
||
zh: '● 在线',
|
||
en: '● Online'
|
||
},
|
||
offline: {
|
||
zh: '○ 离线',
|
||
en: '○ Offline'
|
||
},
|
||
capOff: {
|
||
zh: '未连接 · 轻点连接',
|
||
en: 'Tap to connect'
|
||
},
|
||
capConnecting: {
|
||
zh: '连接中…',
|
||
en: 'Connecting…'
|
||
},
|
||
capOn: {
|
||
zh: '已连接 · 网络已加密',
|
||
en: 'Connected · Encrypted'
|
||
},
|
||
centerOff: {
|
||
zh: 'TAP',
|
||
en: 'TAP'
|
||
},
|
||
download: {
|
||
zh: '下载',
|
||
en: 'Down'
|
||
},
|
||
upload: {
|
||
zh: '上传',
|
||
en: 'Up'
|
||
},
|
||
latency: {
|
||
zh: '延迟',
|
||
en: 'Ping'
|
||
},
|
||
chooseNode: {
|
||
zh: '选择节点',
|
||
en: 'Choose server'
|
||
},
|
||
search: {
|
||
zh: '搜索国家 / 城市',
|
||
en: 'Search country / city'
|
||
},
|
||
tabConnect: {
|
||
zh: '连接',
|
||
en: 'Connect'
|
||
},
|
||
tabServers: {
|
||
zh: '节点',
|
||
en: 'Servers'
|
||
},
|
||
tabStats: {
|
||
zh: '统计',
|
||
en: 'Stats'
|
||
},
|
||
tabMe: {
|
||
zh: '我的',
|
||
en: 'Account'
|
||
},
|
||
connectNow: {
|
||
zh: '点击连接',
|
||
en: 'CONNECT'
|
||
},
|
||
secure: {
|
||
zh: '已加密',
|
||
en: 'SECURE'
|
||
},
|
||
statsTitle: {
|
||
zh: '使用统计',
|
||
en: 'Statistics'
|
||
},
|
||
trafficMonth: {
|
||
zh: '本月流量',
|
||
en: 'Traffic'
|
||
},
|
||
avgPing: {
|
||
zh: '平均延迟',
|
||
en: 'Avg ping'
|
||
},
|
||
durMonth: {
|
||
zh: '本月时长',
|
||
en: 'Time'
|
||
},
|
||
weekTraffic: {
|
||
zh: '本周流量 (GB)',
|
||
en: 'This week (GB)'
|
||
},
|
||
days7: {
|
||
zh: ['一', '二', '三', '四', '五', '六', '日'],
|
||
en: ['M', 'T', 'W', 'T', 'F', 'S', 'S']
|
||
},
|
||
proMember: {
|
||
zh: 'PRO 会员',
|
||
en: 'PRO member'
|
||
},
|
||
expires: {
|
||
zh: '有效期至',
|
||
en: 'Expires'
|
||
},
|
||
usage: {
|
||
zh: '本月用量',
|
||
en: 'Used this month'
|
||
},
|
||
smartRoute: {
|
||
zh: '智能分流',
|
||
en: 'Smart routing'
|
||
},
|
||
smartRouteSub: {
|
||
zh: '海外应用加速,本地直连',
|
||
en: 'Accelerate overseas apps, keep local direct'
|
||
},
|
||
killSwitch: {
|
||
zh: 'Kill Switch',
|
||
en: 'Kill Switch'
|
||
},
|
||
killSwitchSub: {
|
||
zh: '断线时阻断网络,防止泄露',
|
||
en: 'Block traffic if the VPN drops'
|
||
},
|
||
darkAppearance: {
|
||
zh: '深色外观',
|
||
en: 'Dark appearance'
|
||
},
|
||
stateOn: {
|
||
zh: '已开启',
|
||
en: 'On'
|
||
},
|
||
followLight: {
|
||
zh: '跟随浅色',
|
||
en: 'Off'
|
||
},
|
||
protocol: {
|
||
zh: '协议',
|
||
en: 'Protocol'
|
||
},
|
||
checkUpdate: {
|
||
zh: '检查更新',
|
||
en: 'Check for updates'
|
||
},
|
||
language: {
|
||
zh: '语言',
|
||
en: 'Language'
|
||
},
|
||
/* auth */
|
||
authTagline: {
|
||
zh: '极速 · 稳定 · 省心',
|
||
en: 'Fast · Stable · Effortless'
|
||
},
|
||
tabLogin: {
|
||
zh: '登录',
|
||
en: 'Log in'
|
||
},
|
||
tabRegister: {
|
||
zh: '注册',
|
||
en: 'Sign up'
|
||
},
|
||
emailLabel: {
|
||
zh: '邮箱',
|
||
en: 'Email'
|
||
},
|
||
emailPh: {
|
||
zh: '你的邮箱地址',
|
||
en: 'your@email.com'
|
||
},
|
||
pwLabel: {
|
||
zh: '密码',
|
||
en: 'Password'
|
||
},
|
||
pwPh: {
|
||
zh: '输入密码',
|
||
en: 'Enter password'
|
||
},
|
||
setPwPh: {
|
||
zh: '设置登录密码(用于多端登录)',
|
||
en: 'Set a password (for multi-device login)'
|
||
},
|
||
codeLabel: {
|
||
zh: '邮箱验证码',
|
||
en: 'Verification code'
|
||
},
|
||
codeSentTo: {
|
||
zh: '验证码已发送至',
|
||
en: 'Code sent to'
|
||
},
|
||
sendCode: {
|
||
zh: '发送验证码',
|
||
en: 'Send code'
|
||
},
|
||
resend: {
|
||
zh: '重新发送',
|
||
en: 'Resend'
|
||
},
|
||
doLogin: {
|
||
zh: '登录',
|
||
en: 'Log in'
|
||
},
|
||
doNext: {
|
||
zh: '下一步',
|
||
en: 'Next'
|
||
},
|
||
doCreate: {
|
||
zh: '创建账户',
|
||
en: 'Create account'
|
||
},
|
||
forgotPw: {
|
||
zh: '忘记密码?',
|
||
en: 'Forgot password?'
|
||
},
|
||
tos: {
|
||
zh: '继续即代表同意《服务条款》与《隐私政策》',
|
||
en: 'By continuing you agree to our Terms & Privacy Policy'
|
||
},
|
||
stepEmail: {
|
||
zh: '验证邮箱',
|
||
en: 'Verify email'
|
||
},
|
||
stepPw: {
|
||
zh: '设置密码',
|
||
en: 'Set password'
|
||
},
|
||
/* onboarding */
|
||
obSkip: {
|
||
zh: '跳过',
|
||
en: 'Skip'
|
||
},
|
||
obNext: {
|
||
zh: '下一步',
|
||
en: 'Next'
|
||
},
|
||
obAllow: {
|
||
zh: '允许并继续',
|
||
en: 'Allow & continue'
|
||
},
|
||
obStart: {
|
||
zh: '开始使用',
|
||
en: 'Get started'
|
||
},
|
||
ob1Title: {
|
||
zh: '一键连接全球',
|
||
en: 'Connect worldwide'
|
||
},
|
||
ob1Sub: {
|
||
zh: '80+ 节点,智能选择最快线路,稳定不掉线。',
|
||
en: '80+ locations, auto-fastest routing, rock-solid.'
|
||
},
|
||
ob2Title: {
|
||
zh: '授权网络配置',
|
||
en: 'Allow network setup'
|
||
},
|
||
ob2Sub: {
|
||
zh: '系统会请求添加一个网络配置,用于建立加密隧道。',
|
||
en: 'iOS will ask to add a network profile to build the encrypted tunnel.'
|
||
},
|
||
ob3Title: {
|
||
zh: '安全 · 无日志',
|
||
en: 'Private by design'
|
||
},
|
||
ob3Sub: {
|
||
zh: '端到端加密,我们不记录你的任何浏览数据。',
|
||
en: 'End-to-end encrypted. We keep zero browsing logs.'
|
||
},
|
||
/* redeem & buy */
|
||
redeemEntry: {
|
||
zh: '兑换 & 购买',
|
||
en: 'Redeem & buy'
|
||
},
|
||
redeemTitle: {
|
||
zh: '兑换 & 购买',
|
||
en: 'Redeem & buy'
|
||
},
|
||
redeemCodeTitle: {
|
||
zh: '兑换激活码',
|
||
en: 'Redeem a code'
|
||
},
|
||
redeemPh: {
|
||
zh: '输入激活码',
|
||
en: 'Enter activation code'
|
||
},
|
||
redeemBtn: {
|
||
zh: '激活',
|
||
en: 'Activate'
|
||
},
|
||
redeemOk: {
|
||
zh: '激活成功 · PRO 已开通',
|
||
en: 'Activated · PRO unlocked'
|
||
},
|
||
buyTitle: {
|
||
zh: '购买渠道',
|
||
en: 'Where to buy'
|
||
},
|
||
buySub: {
|
||
zh: 'App 内不支持直接支付。请通过以下渠道获取激活码:',
|
||
en: 'In-app payment is unavailable. Get an activation code via:'
|
||
},
|
||
chStore: {
|
||
zh: '自助发卡商店',
|
||
en: 'Self-serve store'
|
||
},
|
||
chStoreSub: {
|
||
zh: '支付宝 / 微信 · 自动发码',
|
||
en: 'Alipay / WeChat · instant code'
|
||
},
|
||
chUsdtSub: {
|
||
zh: '链上转账 · 最隐私 · 自动发码',
|
||
en: 'On-chain · most private · instant code'
|
||
},
|
||
chTgSub: {
|
||
zh: '@PangolinVPN_bot',
|
||
en: '@PangolinVPN_bot'
|
||
},
|
||
chLineSub: {
|
||
zh: '@pangolinvpn',
|
||
en: '@pangolinvpn'
|
||
},
|
||
chEmail: {
|
||
zh: '邮箱',
|
||
en: 'Email'
|
||
},
|
||
chEmailSub: {
|
||
zh: 'buy@pangolin.vpn',
|
||
en: 'buy@pangolin.vpn'
|
||
},
|
||
back: {
|
||
zh: '返回',
|
||
en: 'Back'
|
||
},
|
||
replayAuth: {
|
||
zh: '查看登录 / 注册流程',
|
||
en: 'View login / sign-up'
|
||
},
|
||
twoFA: {
|
||
zh: '双重认证',
|
||
en: 'Two-factor auth'
|
||
},
|
||
twoFASub: {
|
||
zh: 'PRO 专享 · 登录需身份验证器动态码',
|
||
en: 'PRO only · TOTP code at login'
|
||
},
|
||
twoFATitle: {
|
||
zh: '双重认证',
|
||
en: 'Two-factor auth'
|
||
},
|
||
twoFAHint: {
|
||
zh: '你的账户已开启双重认证。请输入身份验证器 App 中的 6 位动态码。',
|
||
en: 'Two-factor auth is on for this account. Enter the 6-digit code from your authenticator app.'
|
||
},
|
||
twoFAConfirm: {
|
||
zh: '验证并登录',
|
||
en: 'Verify & log in'
|
||
},
|
||
twoFABack: {
|
||
zh: '返回上一步',
|
||
en: 'Back'
|
||
},
|
||
ucEntry: {
|
||
zh: 'Web 用户中心',
|
||
en: 'Web account center'
|
||
},
|
||
ucEntrySub: {
|
||
zh: '订阅导入 · 邀请返利',
|
||
en: 'Subscription import · referral'
|
||
},
|
||
replayAuthSub: {
|
||
zh: '演示用 · 退出到登录页',
|
||
en: 'Demo · back to login'
|
||
},
|
||
replayOnboarding: {
|
||
zh: '查看首次引导',
|
||
en: 'View onboarding'
|
||
},
|
||
replayOnboardingSub: {
|
||
zh: '演示用 · 重看 3 屏引导',
|
||
en: 'Demo · replay the 3 intro screens'
|
||
},
|
||
/* account management (integrated) */
|
||
meTitle: {
|
||
zh: '我的',
|
||
en: 'Account'
|
||
},
|
||
curPlan: {
|
||
zh: '当前套餐',
|
||
en: 'Current plan'
|
||
},
|
||
expires: {
|
||
zh: '有效期至',
|
||
en: 'Expires'
|
||
},
|
||
upgradeBtn: {
|
||
zh: '续费 / 升级',
|
||
en: 'Renew / Upgrade'
|
||
},
|
||
accInfoTitle: {
|
||
zh: '账户信息',
|
||
en: 'Account info'
|
||
},
|
||
accEmail: {
|
||
zh: '邮箱',
|
||
en: 'Email'
|
||
},
|
||
accPassword: {
|
||
zh: '密码',
|
||
en: 'Password'
|
||
},
|
||
accChange: {
|
||
zh: '修改',
|
||
en: 'Change'
|
||
},
|
||
accSignOut: {
|
||
zh: '退出登录',
|
||
en: 'Sign out'
|
||
},
|
||
myDevices: {
|
||
zh: '我的设备',
|
||
en: 'My devices'
|
||
},
|
||
devicesSub: {
|
||
zh: 'PRO 套餐最多 5 台设备同时在线',
|
||
en: 'Up to 5 devices on PRO'
|
||
},
|
||
thisDevice: {
|
||
zh: '当前设备',
|
||
en: 'This device'
|
||
},
|
||
lastActive: {
|
||
zh: '最近活跃',
|
||
en: 'Last active'
|
||
},
|
||
remove: {
|
||
zh: '移除',
|
||
en: 'Remove'
|
||
},
|
||
choosePlan: {
|
||
zh: '选择套餐',
|
||
en: 'Choose plan'
|
||
},
|
||
free: {
|
||
zh: '免费版',
|
||
en: 'Free'
|
||
},
|
||
proPlan: {
|
||
zh: '专业版',
|
||
en: 'Pro'
|
||
},
|
||
team: {
|
||
zh: '团队版',
|
||
en: 'Team'
|
||
},
|
||
perMonth: {
|
||
zh: '/月',
|
||
en: '/mo'
|
||
},
|
||
current: {
|
||
zh: '当前',
|
||
en: 'Current'
|
||
},
|
||
upgrade: {
|
||
zh: '立即升级',
|
||
en: 'Upgrade'
|
||
},
|
||
choose: {
|
||
zh: '选择',
|
||
en: 'Choose'
|
||
},
|
||
mostPopular: {
|
||
zh: '最受欢迎',
|
||
en: 'Popular'
|
||
},
|
||
feFree: {
|
||
zh: ['仅 1 个基础节点', '每日 10 分钟时长', '使用前观看广告', '注册享 7 天免费试用'],
|
||
en: ['1 basic node only', '10 min per day', 'Watch an ad to start', '7-day free trial on sign-up']
|
||
},
|
||
fePro: {
|
||
zh: ['80+ 全球节点', '无限流量 · 极速', '5 台设备同时在线', '流媒体优化'],
|
||
en: ['80+ locations', 'Unlimited · fast', '5 devices', 'Streaming optimized']
|
||
},
|
||
feTeam: {
|
||
zh: ['Pro 全部功能', '10 个成员席位', '集中计费与管理', '优先客服'],
|
||
en: ['Everything in Pro', '10 seats', 'Central billing', 'Priority support']
|
||
},
|
||
/* contact */
|
||
contactEntry: {
|
||
zh: '联系我们',
|
||
en: 'Contact us'
|
||
},
|
||
contactTitle: {
|
||
zh: '联系我们',
|
||
en: 'Contact us'
|
||
},
|
||
contactIntro: {
|
||
zh: '遇到问题?通过以下任一渠道联系我们,通常数分钟内回复。',
|
||
en: 'Need help? Reach us via any channel below — usually replies in minutes.'
|
||
},
|
||
contactEmail: {
|
||
zh: '邮箱客服',
|
||
en: 'Email support'
|
||
},
|
||
contactStore: {
|
||
zh: '自助发卡商店',
|
||
en: 'Self-serve store'
|
||
},
|
||
contactHoursTitle: {
|
||
zh: '服务时间',
|
||
en: 'Support hours'
|
||
},
|
||
contactHours: {
|
||
zh: '每日 9:00 – 24:00 (GMT+8)',
|
||
en: 'Daily 9:00 – 24:00 (GMT+8)'
|
||
},
|
||
devicesCount: {
|
||
zh: '台设备',
|
||
en: 'devices'
|
||
},
|
||
/* smart select + quota (口径见 CLAUDE.md §7) */
|
||
smartSelect: {
|
||
zh: '智能选择',
|
||
en: 'Smart select'
|
||
},
|
||
smartSub: {
|
||
zh: '根据当前网络环境,自动选择最优节点',
|
||
en: 'Picks the best node for your network'
|
||
},
|
||
recommended: {
|
||
zh: '推荐',
|
||
en: 'Recommended'
|
||
},
|
||
quotaToday: {
|
||
zh: '今日剩余',
|
||
en: 'Left today'
|
||
},
|
||
minutes: {
|
||
zh: '分钟',
|
||
en: 'min'
|
||
},
|
||
quotaFree: {
|
||
zh: '免费版 · 每日 10 分钟',
|
||
en: 'Free · 10 min/day'
|
||
},
|
||
quotaTrial: {
|
||
zh: '体验期 · 7 天免费使用',
|
||
en: 'Trial · 7 days free'
|
||
},
|
||
watchAd: {
|
||
zh: '看广告开始使用',
|
||
en: 'Watch ad to start'
|
||
},
|
||
adUnlocked: {
|
||
zh: '已解锁 · 今日可用',
|
||
en: 'Unlocked for today'
|
||
},
|
||
freePlanName: {
|
||
zh: '免费版',
|
||
en: 'Free'
|
||
},
|
||
upgradeNow: {
|
||
zh: '升级 PRO',
|
||
en: 'Go PRO'
|
||
},
|
||
freeDemo: {
|
||
zh: '演示:免费版视角',
|
||
en: 'Demo: free-tier view'
|
||
},
|
||
freeDemoSub: {
|
||
zh: '切换连接页的额度展示与套餐横幅',
|
||
en: 'Toggles quota UI & plan banner'
|
||
}
|
||
};
|
||
function makeT(lang) {
|
||
return k => STRINGS[k] && STRINGS[k][lang] || k;
|
||
}
|
||
/* localized server label */
|
||
function srvName(s, lang) {
|
||
return lang === 'zh' ? s.name : s.en;
|
||
}
|
||
function srvSub(s, lang) {
|
||
const tag = s.tags && s.tags[0];
|
||
if (lang === 'zh') return tag ? tag : s.en;
|
||
return tag === '流媒体优化' ? 'Streaming' : tag === 'P2P' ? 'P2P' : s.name;
|
||
}
|
||
Object.assign(window, {
|
||
Icon,
|
||
Mark,
|
||
CC,
|
||
Signal,
|
||
Pill,
|
||
STRINGS,
|
||
makeT,
|
||
srvName,
|
||
srvSub,
|
||
SERVERS: [{
|
||
code: 'HK',
|
||
name: '香港 · 流媒体',
|
||
en: 'Hong Kong',
|
||
ping: 18,
|
||
tags: ['流媒体优化']
|
||
}, {
|
||
code: 'JP',
|
||
name: '日本 东京',
|
||
en: 'Tokyo',
|
||
ping: 32,
|
||
tags: ['P2P']
|
||
}, {
|
||
code: 'SG',
|
||
name: '新加坡',
|
||
en: 'Singapore',
|
||
ping: 54,
|
||
tags: ['P2P']
|
||
}, {
|
||
code: 'TW',
|
||
name: '台湾 台北',
|
||
en: 'Taipei',
|
||
ping: 28,
|
||
tags: []
|
||
}, {
|
||
code: 'US',
|
||
name: '美国 洛杉矶',
|
||
en: 'Los Angeles',
|
||
ping: 146,
|
||
tags: ['流媒体优化']
|
||
}, {
|
||
code: 'DE',
|
||
name: '德国 法兰克福',
|
||
en: 'Frankfurt',
|
||
ping: 198,
|
||
tags: []
|
||
}, {
|
||
code: 'UK',
|
||
name: '英国 伦敦',
|
||
en: 'London',
|
||
ping: 210,
|
||
tags: []
|
||
}, {
|
||
code: 'KR',
|
||
name: '韩国 首尔',
|
||
en: 'Seoul',
|
||
ping: 41,
|
||
tags: []
|
||
}]
|
||
});
|
||
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/mobile/parts.jsx", error: String((e && e.message) || e) }); }
|
||
|
||
// ui_kits/mobile/screens.jsx
|
||
try { (() => {
|
||
/* screens.jsx — 穿山甲 VPN mobile · screens + bottom tab (i18n) */
|
||
|
||
const {
|
||
useState,
|
||
useEffect,
|
||
useRef
|
||
} = React;
|
||
|
||
/* ───────── Top brand bar ───────── */
|
||
function TopBar({
|
||
t,
|
||
lang,
|
||
right
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
padding: '6px 20px 14px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 9
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Mark, {
|
||
size: 28
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 18,
|
||
color: 'var(--fg1)',
|
||
letterSpacing: '-0.01em'
|
||
}
|
||
}, t('brand'))), right);
|
||
}
|
||
|
||
/* ───────── CONNECT screen ───────── */
|
||
function ConnectScreen({
|
||
t,
|
||
lang,
|
||
status,
|
||
onToggle,
|
||
server,
|
||
smart,
|
||
free,
|
||
mins,
|
||
adDone,
|
||
onAd,
|
||
goServers,
|
||
elapsed
|
||
}) {
|
||
const map = {
|
||
off: {
|
||
fill: 'var(--sand-100)',
|
||
fg: 'var(--clay-500)',
|
||
icon: 'power',
|
||
cap: t('capOff'),
|
||
ring: 'track'
|
||
},
|
||
connecting: {
|
||
fill: 'var(--accent)',
|
||
fg: '#fff',
|
||
icon: 'loader-circle',
|
||
cap: t('capConnecting'),
|
||
ring: 'spin'
|
||
},
|
||
on: {
|
||
fill: 'var(--success)',
|
||
fg: '#fff',
|
||
icon: 'shield-check',
|
||
cap: t('capOn'),
|
||
ring: 'full'
|
||
}
|
||
}[status];
|
||
const fmt = s => `${String(Math.floor(s / 3600)).padStart(2, '0')}:${String(Math.floor(s % 3600 / 60)).padStart(2, '0')}:${String(s % 60).padStart(2, '0')}`;
|
||
const shadow = status === 'off' ? 'inset 0 0 0 1px var(--sand-200), var(--shadow-md)' : `0 0 0 9px ${status === 'on' ? 'var(--success-subtle)' : 'var(--accent-subtle)'}, var(--shadow-lg)`;
|
||
// in-circle center block
|
||
const center = status === 'off' ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontWeight: 700,
|
||
fontSize: 14
|
||
}
|
||
}, t('connectNow'))) : status === 'connecting' ? /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontWeight: 700,
|
||
fontSize: 14,
|
||
letterSpacing: '0.08em'
|
||
}
|
||
}, "\xB7\xB7\xB7") : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontWeight: 600,
|
||
fontSize: 18
|
||
}
|
||
}, fmt(elapsed)), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontWeight: 700,
|
||
fontSize: 10,
|
||
letterSpacing: '0.1em',
|
||
opacity: .9
|
||
}
|
||
}, t('secure')));
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
height: '100%'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(TopBar, {
|
||
t: t,
|
||
lang: lang,
|
||
right: /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 12,
|
||
whiteSpace: 'nowrap',
|
||
color: status === 'on' ? 'var(--success)' : 'var(--fg3)',
|
||
fontWeight: 600
|
||
}
|
||
}, status === 'on' ? t('online') : t('offline'))
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 26,
|
||
padding: '0 24px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("button", {
|
||
onClick: onToggle,
|
||
style: {
|
||
position: 'relative',
|
||
width: 208,
|
||
height: 208,
|
||
borderRadius: '50%',
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
background: map.fill,
|
||
color: map.fg,
|
||
boxShadow: shadow,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 6,
|
||
transition: 'box-shadow 300ms var(--ease-out), background-color 300ms var(--ease-out)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("svg", {
|
||
viewBox: "0 0 208 208",
|
||
style: {
|
||
position: 'absolute',
|
||
inset: 0,
|
||
width: '100%',
|
||
height: '100%',
|
||
transformOrigin: 'center',
|
||
animation: map.ring === 'spin' ? 'pg-spin 1.4s linear infinite' : 'none'
|
||
}
|
||
}, map.ring === 'track' && /*#__PURE__*/React.createElement("circle", {
|
||
cx: "104",
|
||
cy: "104",
|
||
r: "96",
|
||
fill: "none",
|
||
stroke: "var(--sand-200)",
|
||
strokeWidth: "3",
|
||
strokeDasharray: "2 8",
|
||
strokeLinecap: "round"
|
||
}), map.ring === 'spin' && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("circle", {
|
||
cx: "104",
|
||
cy: "104",
|
||
r: "96",
|
||
fill: "none",
|
||
stroke: "rgba(255,255,255,.3)",
|
||
strokeWidth: "4"
|
||
}), /*#__PURE__*/React.createElement("circle", {
|
||
cx: "104",
|
||
cy: "104",
|
||
r: "96",
|
||
fill: "none",
|
||
stroke: "#fff",
|
||
strokeWidth: "4",
|
||
strokeLinecap: "round",
|
||
strokeDasharray: "150 453"
|
||
})), map.ring === 'full' && /*#__PURE__*/React.createElement("circle", {
|
||
cx: "104",
|
||
cy: "104",
|
||
r: "96",
|
||
fill: "none",
|
||
stroke: "rgba(255,255,255,.85)",
|
||
strokeWidth: "4",
|
||
strokeLinecap: "round"
|
||
})), /*#__PURE__*/React.createElement(Icon, {
|
||
name: map.icon,
|
||
size: 50,
|
||
stroke: 1.7,
|
||
style: status === 'connecting' ? {
|
||
animation: 'pg-spin 1s linear infinite'
|
||
} : {}
|
||
}), center), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
textAlign: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 15,
|
||
fontWeight: 600,
|
||
color: 'var(--fg2)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, map.cap))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '0 20px 18px',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 12
|
||
}
|
||
}, free && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
background: 'var(--surface)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '12px 14px',
|
||
boxShadow: 'var(--shadow-sm)',
|
||
border: '1px solid var(--border)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 7,
|
||
marginBottom: 8
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "clock",
|
||
size: 15,
|
||
stroke: 2.2,
|
||
color: "var(--accent)"
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 12,
|
||
fontWeight: 600,
|
||
color: 'var(--fg2)'
|
||
}
|
||
}, t('quotaToday')), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 14,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, mins, " ", t('minutes')), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
marginLeft: 'auto',
|
||
fontSize: 10.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg3)',
|
||
background: 'var(--bg-subtle)',
|
||
padding: '3px 9px',
|
||
borderRadius: 'var(--radius-full)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, t('quotaFree'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
height: 6,
|
||
background: 'var(--bg-subtle)',
|
||
borderRadius: 3,
|
||
overflow: 'hidden',
|
||
marginBottom: 10
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: `${Math.min(100, mins / 10 * 100)}%`,
|
||
height: '100%',
|
||
background: mins <= 3 ? 'var(--warning)' : 'var(--accent)',
|
||
borderRadius: 3,
|
||
transition: 'width 300ms var(--ease-out)'
|
||
}
|
||
})), adDone ? /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 7,
|
||
color: 'var(--success)',
|
||
fontWeight: 700,
|
||
fontSize: 13,
|
||
padding: '9px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "check-circle",
|
||
size: 16,
|
||
stroke: 2.2,
|
||
color: "var(--success)"
|
||
}), t('adUnlocked')) : /*#__PURE__*/React.createElement("button", {
|
||
onClick: onAd,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 7,
|
||
width: '100%',
|
||
border: '1.5px solid var(--accent-border)',
|
||
background: 'var(--accent-subtle)',
|
||
color: 'var(--accent)',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontWeight: 700,
|
||
fontSize: 13,
|
||
padding: '9px',
|
||
borderRadius: 'var(--radius-full)',
|
||
cursor: 'pointer'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "play-circle",
|
||
size: 16,
|
||
stroke: 2.2
|
||
}), t('watchAd'))), status === 'on' && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 12
|
||
}
|
||
}, [['arrow-down', t('download'), '86.4', 'Mb/s'], ['arrow-up', t('upload'), '12.1', 'Mb/s'], ['zap', t('latency'), String(server.ping), 'ms']].map(([ic, l, v, u]) => /*#__PURE__*/React.createElement("div", {
|
||
key: l,
|
||
style: {
|
||
flex: 1,
|
||
background: 'var(--surface)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '12px 14px',
|
||
boxShadow: 'var(--shadow-sm)',
|
||
border: '1px solid var(--border)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 5,
|
||
color: 'var(--fg3)',
|
||
fontSize: 11,
|
||
fontWeight: 600,
|
||
marginBottom: 4
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: ic,
|
||
size: 13,
|
||
stroke: 2.2,
|
||
color: "var(--accent)"
|
||
}), l), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 17,
|
||
fontWeight: 500,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, v, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 11,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, " ", u))))), /*#__PURE__*/React.createElement("button", {
|
||
onClick: goServers,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
width: '100%',
|
||
textAlign: 'left',
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '12px 14px',
|
||
cursor: 'pointer',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(CC, {
|
||
code: server.code,
|
||
active: true
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 15,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 7
|
||
}
|
||
}, smart ? t('smartSelect') : srvName(server, lang), smart && /*#__PURE__*/React.createElement(Icon, {
|
||
name: "zap",
|
||
size: 13,
|
||
stroke: 2.4,
|
||
color: "var(--accent)"
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, smart ? `${srvName(server, lang)} · ${server.ping}ms` : `${srvSub(server, lang)} · ${server.ping}ms`)), /*#__PURE__*/React.createElement(Icon, {
|
||
name: "chevron-right",
|
||
size: 20,
|
||
color: "var(--fg3)"
|
||
}))));
|
||
}
|
||
|
||
/* ───────── SERVERS screen ───────── */
|
||
function ServersScreen({
|
||
t,
|
||
lang,
|
||
current,
|
||
onPick,
|
||
q,
|
||
setQ
|
||
}) {
|
||
const list = SERVERS.filter(s => (s.name + s.en).toLowerCase().includes(q.toLowerCase()));
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
height: '100%'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(TopBar, {
|
||
t: t,
|
||
lang: lang
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '0 20px 12px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 24,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 12
|
||
}
|
||
}, t('chooseNode')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 10,
|
||
background: 'var(--bg-subtle)',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '10px 14px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "search",
|
||
size: 18,
|
||
color: "var(--fg3)"
|
||
}), /*#__PURE__*/React.createElement("input", {
|
||
value: q,
|
||
onChange: e => setQ(e.target.value),
|
||
placeholder: t('search'),
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
outline: 'none',
|
||
flex: 1,
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 15,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
overflow: 'auto',
|
||
padding: '0 20px 20px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => onPick('AUTO'),
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 13,
|
||
width: '100%',
|
||
textAlign: 'left',
|
||
padding: '15px 14px',
|
||
border: `1.5px solid ${current === 'AUTO' ? 'var(--accent)' : 'var(--accent-border)'}`,
|
||
cursor: 'pointer',
|
||
background: 'var(--accent-subtle)',
|
||
borderRadius: 'var(--radius-xl)',
|
||
boxShadow: current === 'AUTO' ? 'var(--shadow-md)' : 'var(--shadow-sm)',
|
||
marginBottom: 12
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 42,
|
||
height: 42,
|
||
borderRadius: 'var(--radius-md)',
|
||
background: 'linear-gradient(150deg,var(--clay-500),var(--clay-700))',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "zap",
|
||
size: 21,
|
||
stroke: 2.2,
|
||
color: "#fff"
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 8
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 15.5,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('smartSelect')), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
color: '#fff',
|
||
background: 'var(--accent)',
|
||
padding: '2px 8px',
|
||
borderRadius: 999,
|
||
letterSpacing: '0.04em'
|
||
}
|
||
}, t('recommended'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg2)',
|
||
marginTop: 3,
|
||
lineHeight: 1.5
|
||
}
|
||
}, t('smartSub'))), current === 'AUTO' && /*#__PURE__*/React.createElement(Icon, {
|
||
name: "check",
|
||
size: 19,
|
||
color: "var(--accent)",
|
||
stroke: 2.6
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
background: 'var(--surface)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
boxShadow: 'var(--shadow-sm)',
|
||
overflow: 'hidden',
|
||
border: '1px solid var(--border)'
|
||
}
|
||
}, list.map((s, i) => {
|
||
const active = s.code === current;
|
||
return /*#__PURE__*/React.createElement("button", {
|
||
key: s.code,
|
||
onClick: () => onPick(s.code),
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 13,
|
||
width: '100%',
|
||
textAlign: 'left',
|
||
padding: '12px 14px',
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
borderBottom: i < list.length - 1 ? '1px solid var(--border)' : 'none',
|
||
background: active ? 'var(--accent-subtle)' : 'transparent'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(CC, {
|
||
code: s.code,
|
||
active: active
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 15,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, srvName(s, lang)), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, srvSub(s, lang))), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 12,
|
||
color: 'var(--fg2)'
|
||
}
|
||
}, s.ping, "ms"), /*#__PURE__*/React.createElement(Signal, {
|
||
ping: s.ping
|
||
}), active && /*#__PURE__*/React.createElement(Icon, {
|
||
name: "check",
|
||
size: 18,
|
||
color: "var(--accent)"
|
||
}));
|
||
}))));
|
||
}
|
||
|
||
/* ───────── SETTINGS screen ───────── */
|
||
function Toggle({
|
||
on,
|
||
onChange
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
onClick: onChange,
|
||
style: {
|
||
width: 46,
|
||
height: 28,
|
||
borderRadius: 999,
|
||
position: 'relative',
|
||
cursor: 'pointer',
|
||
flexShrink: 0,
|
||
background: on ? 'var(--accent)' : 'var(--border-strong)',
|
||
transition: 'background 220ms var(--ease-out)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
top: 3,
|
||
left: on ? 21 : 3,
|
||
width: 22,
|
||
height: 22,
|
||
borderRadius: '50%',
|
||
background: '#fff',
|
||
boxShadow: 'var(--shadow-sm)',
|
||
transition: 'left 220ms var(--ease-out)'
|
||
}
|
||
}));
|
||
}
|
||
/* segmented language switch 中 / EN */
|
||
function LangSwitch({
|
||
lang,
|
||
setLang
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
background: 'var(--bg-subtle)',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: 3,
|
||
gap: 2
|
||
}
|
||
}, [['zh', '中文'], ['en', 'EN']].map(([v, l]) => /*#__PURE__*/React.createElement("button", {
|
||
key: v,
|
||
onClick: () => setLang(v),
|
||
style: {
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: '5px 13px',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 12.5,
|
||
fontWeight: 700,
|
||
background: lang === v ? 'var(--accent)' : 'transparent',
|
||
color: lang === v ? 'var(--fg-on-accent)' : 'var(--fg3)',
|
||
transition: 'all 140ms var(--ease-out)'
|
||
}
|
||
}, l)));
|
||
}
|
||
function Row({
|
||
icon,
|
||
title,
|
||
sub,
|
||
right,
|
||
last,
|
||
onClick
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
onClick: onClick,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 13,
|
||
padding: '13px 16px',
|
||
borderBottom: last ? 'none' : '1px solid var(--border)',
|
||
cursor: onClick ? 'pointer' : 'default'
|
||
}
|
||
}, icon && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 32,
|
||
height: 32,
|
||
borderRadius: 'var(--radius-sm)',
|
||
background: 'var(--accent-subtle)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: icon,
|
||
size: 17,
|
||
color: "var(--accent)",
|
||
stroke: 2.2
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 15,
|
||
fontWeight: 500,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, title), sub && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, sub)), right);
|
||
}
|
||
function SettingsScreen({
|
||
t,
|
||
lang,
|
||
setLang,
|
||
dark,
|
||
setDark,
|
||
free,
|
||
setFree,
|
||
onRedeem,
|
||
onUpgrade,
|
||
onContact,
|
||
onSignOut,
|
||
onReplayAuth,
|
||
onReplayOnboarding
|
||
}) {
|
||
const [autostart, setAuto] = useState(true);
|
||
const [kill, setKill] = useState(true);
|
||
const [twoFA, setTwoFA] = useState(true);
|
||
const [devices, setDevices] = useState([{
|
||
id: 1,
|
||
icon: 'laptop',
|
||
name: 'MacBook Pro',
|
||
os: 'macOS 26',
|
||
active: t('thisDevice'),
|
||
me: true
|
||
}, {
|
||
id: 2,
|
||
icon: 'smartphone',
|
||
name: 'iPhone 17',
|
||
os: 'iOS 26',
|
||
active: '2 min',
|
||
me: false
|
||
}, {
|
||
id: 3,
|
||
icon: 'monitor-smartphone',
|
||
name: 'iPad Air',
|
||
os: 'iPadOS 26',
|
||
active: lang === 'zh' ? '3 小时前' : '3h ago',
|
||
me: false
|
||
}]);
|
||
const card = {
|
||
background: 'var(--surface)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
boxShadow: 'var(--shadow-sm)',
|
||
border: '1px solid var(--border)',
|
||
overflow: 'hidden',
|
||
marginBottom: 18
|
||
};
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
height: '100%'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(TopBar, {
|
||
t: t,
|
||
lang: lang
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
overflow: 'auto',
|
||
padding: '0 20px 20px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 24,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 14
|
||
}
|
||
}, t('meTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
background: 'linear-gradient(155deg,var(--clay-600),var(--clay-800))',
|
||
borderRadius: 'var(--radius-xl)',
|
||
padding: 18,
|
||
color: '#fff',
|
||
marginBottom: 18,
|
||
boxShadow: 'var(--shadow-md)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 11
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 44,
|
||
height: 44,
|
||
borderRadius: '50%',
|
||
background: 'rgba(255,255,255,0.18)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "crown",
|
||
size: 22,
|
||
color: "#fff"
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontWeight: 700,
|
||
fontSize: 16
|
||
}
|
||
}, "me@pangolin.vpn"), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 4,
|
||
fontSize: 11,
|
||
fontWeight: 600,
|
||
background: 'rgba(255,255,255,0.2)',
|
||
padding: '2px 8px',
|
||
borderRadius: 999,
|
||
marginTop: 4
|
||
}
|
||
}, free ? t('freePlanName') : t('proMember')))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'flex-end',
|
||
marginTop: 16,
|
||
paddingTop: 14,
|
||
borderTop: '1px solid rgba(255,255,255,0.18)',
|
||
fontSize: 12
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
opacity: 0.7
|
||
}
|
||
}, free ? t('quotaToday') : t('expires')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontWeight: 600,
|
||
marginTop: 2,
|
||
fontFamily: 'var(--font-mono)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, free ? t('quotaFree') : '2026-12-31 · 42.6 GB')), /*#__PURE__*/React.createElement("button", {
|
||
onClick: onUpgrade,
|
||
style: {
|
||
border: 'none',
|
||
background: '#fff',
|
||
color: 'var(--clay-700)',
|
||
fontWeight: 700,
|
||
fontSize: 13,
|
||
padding: '9px 16px',
|
||
borderRadius: 'var(--radius-full)',
|
||
cursor: 'pointer',
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 6
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "ticket",
|
||
size: 15,
|
||
color: "var(--clay-700)"
|
||
}), t('upgradeBtn')))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13,
|
||
fontWeight: 700,
|
||
color: 'var(--fg2)',
|
||
margin: '4px 4px 10px'
|
||
}
|
||
}, t('accInfoTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: card
|
||
}, /*#__PURE__*/React.createElement(Row, {
|
||
icon: "mail",
|
||
title: t('accEmail'),
|
||
sub: "me@pangolin.vpn",
|
||
right: /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--accent)',
|
||
fontWeight: 600
|
||
}
|
||
}, t('accChange'))
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
icon: "lock",
|
||
title: t('accPassword'),
|
||
sub: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
|
||
right: /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--accent)',
|
||
fontWeight: 600
|
||
}
|
||
}, t('accChange'))
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
icon: "shield",
|
||
title: t('twoFA'),
|
||
sub: t('twoFASub'),
|
||
right: /*#__PURE__*/React.createElement(Toggle, {
|
||
on: twoFA,
|
||
onChange: () => setTwoFA(!twoFA)
|
||
}),
|
||
last: true
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13,
|
||
fontWeight: 700,
|
||
color: 'var(--fg2)',
|
||
margin: '4px 4px 4px'
|
||
}
|
||
}, t('myDevices')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11.5,
|
||
color: 'var(--fg3)',
|
||
margin: '0 4px 10px'
|
||
}
|
||
}, t('devicesSub')), /*#__PURE__*/React.createElement("div", {
|
||
style: card
|
||
}, devices.map((d, i) => /*#__PURE__*/React.createElement("div", {
|
||
key: d.id,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
padding: '12px 16px',
|
||
borderBottom: i < devices.length - 1 ? '1px solid var(--border)' : 'none'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 34,
|
||
height: 34,
|
||
borderRadius: 'var(--radius-md)',
|
||
background: 'var(--accent-subtle)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: d.icon,
|
||
size: 18,
|
||
color: "var(--accent)"
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
minWidth: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 7
|
||
}
|
||
}, d.name, d.me && /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 10.5,
|
||
fontWeight: 600,
|
||
color: 'var(--success)',
|
||
background: 'var(--success-subtle)',
|
||
padding: '1px 7px',
|
||
borderRadius: 999
|
||
}
|
||
}, d.active)), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11.5,
|
||
color: 'var(--fg3)',
|
||
marginTop: 1
|
||
}
|
||
}, d.os, !d.me && ` · ${d.active}`)), !d.me && /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => setDevices(devices.filter(x => x.id !== d.id)),
|
||
style: {
|
||
border: '1.5px solid var(--border-strong)',
|
||
background: 'transparent',
|
||
color: 'var(--fg2)',
|
||
fontSize: 12.5,
|
||
fontWeight: 600,
|
||
padding: '6px 12px',
|
||
borderRadius: 'var(--radius-full)',
|
||
cursor: 'pointer'
|
||
}
|
||
}, t('remove'))))), /*#__PURE__*/React.createElement("div", {
|
||
style: card
|
||
}, /*#__PURE__*/React.createElement(Row, {
|
||
icon: "shopping-bag",
|
||
title: t('redeemEntry'),
|
||
right: /*#__PURE__*/React.createElement(Icon, {
|
||
name: "chevron-right",
|
||
size: 18,
|
||
color: "var(--fg3)"
|
||
}),
|
||
onClick: onRedeem
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
icon: "external-link",
|
||
title: t('ucEntry'),
|
||
sub: t('ucEntrySub'),
|
||
right: /*#__PURE__*/React.createElement(Icon, {
|
||
name: "chevron-right",
|
||
size: 18,
|
||
color: "var(--fg3)"
|
||
}),
|
||
onClick: () => window.open('../usercenter/index.html', '_blank')
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
icon: "message-circle",
|
||
title: t('contactEntry'),
|
||
right: /*#__PURE__*/React.createElement(Icon, {
|
||
name: "chevron-right",
|
||
size: 18,
|
||
color: "var(--fg3)"
|
||
}),
|
||
last: true,
|
||
onClick: onContact
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: card
|
||
}, /*#__PURE__*/React.createElement(Row, {
|
||
icon: "zap",
|
||
title: t('smartRoute'),
|
||
sub: t('smartRouteSub'),
|
||
right: /*#__PURE__*/React.createElement(Toggle, {
|
||
on: autostart,
|
||
onChange: () => setAuto(!autostart)
|
||
})
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
icon: "shield",
|
||
title: t('killSwitch'),
|
||
sub: t('killSwitchSub'),
|
||
right: /*#__PURE__*/React.createElement(Toggle, {
|
||
on: kill,
|
||
onChange: () => setKill(!kill)
|
||
}),
|
||
last: true
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: card
|
||
}, /*#__PURE__*/React.createElement(Row, {
|
||
icon: "globe",
|
||
title: t('language'),
|
||
right: /*#__PURE__*/React.createElement(LangSwitch, {
|
||
lang: lang,
|
||
setLang: setLang
|
||
})
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
icon: dark ? 'moon' : 'sun',
|
||
title: t('darkAppearance'),
|
||
sub: dark ? t('stateOn') : t('followLight'),
|
||
right: /*#__PURE__*/React.createElement(Toggle, {
|
||
on: dark,
|
||
onChange: () => setDark(!dark)
|
||
})
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
icon: "shield",
|
||
title: t('protocol'),
|
||
right: /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 13,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, "WireGuard")
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
icon: "refresh-cw",
|
||
title: t('checkUpdate'),
|
||
right: /*#__PURE__*/React.createElement(Icon, {
|
||
name: "chevron-right",
|
||
size: 18,
|
||
color: "var(--fg3)"
|
||
}),
|
||
last: true
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: card
|
||
}, /*#__PURE__*/React.createElement(Row, {
|
||
icon: "log-out",
|
||
title: t('replayAuth'),
|
||
sub: t('replayAuthSub'),
|
||
right: /*#__PURE__*/React.createElement(Icon, {
|
||
name: "chevron-right",
|
||
size: 18,
|
||
color: "var(--fg3)"
|
||
}),
|
||
onClick: onReplayAuth
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
icon: "compass",
|
||
title: t('replayOnboarding'),
|
||
sub: t('replayOnboardingSub'),
|
||
right: /*#__PURE__*/React.createElement(Icon, {
|
||
name: "chevron-right",
|
||
size: 18,
|
||
color: "var(--fg3)"
|
||
}),
|
||
onClick: onReplayOnboarding
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
icon: "clock",
|
||
title: t('freeDemo'),
|
||
sub: t('freeDemoSub'),
|
||
right: /*#__PURE__*/React.createElement(Toggle, {
|
||
on: free,
|
||
onChange: () => setFree(!free)
|
||
}),
|
||
last: true
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
textAlign: 'center',
|
||
fontSize: 12,
|
||
color: 'var(--fg3)',
|
||
fontFamily: 'var(--font-mono)'
|
||
}
|
||
}, "\u7A7F\u5C71\u7532 \xB7 v2.4.0")));
|
||
}
|
||
|
||
/* ───────── STATS screen ───────── */
|
||
function StatsScreen({
|
||
t,
|
||
lang
|
||
}) {
|
||
const vals = [2.1, 3.4, 1.8, 4.6, 5.2, 6.1, 3.0];
|
||
const max = 6.1;
|
||
const labels = t('days7');
|
||
const card = {
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
};
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
height: '100%'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(TopBar, {
|
||
t: t,
|
||
lang: lang
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
overflow: 'auto',
|
||
padding: '0 20px 20px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 24,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 16
|
||
}
|
||
}, t('statsTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 12,
|
||
marginBottom: 16
|
||
}
|
||
}, [[t('trafficMonth'), '42.6', 'GB'], [t('avgPing'), '29', 'ms'], [t('durMonth'), '86.4', 'h']].map(([l, v, u]) => /*#__PURE__*/React.createElement("div", {
|
||
key: l,
|
||
style: {
|
||
...card,
|
||
flex: 1,
|
||
padding: '14px 14px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11,
|
||
color: 'var(--fg3)',
|
||
fontWeight: 600
|
||
}
|
||
}, l), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 20,
|
||
fontWeight: 500,
|
||
color: 'var(--fg1)',
|
||
marginTop: 6
|
||
}
|
||
}, v, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 11,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, " ", u))))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...card,
|
||
padding: '18px 18px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 18
|
||
}
|
||
}, t('weekTraffic')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'flex-end',
|
||
gap: 12,
|
||
height: 120
|
||
}
|
||
}, vals.map((v, i) => /*#__PURE__*/React.createElement("div", {
|
||
key: i,
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
gap: 7
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 10,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, v), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: '100%',
|
||
maxWidth: 30,
|
||
height: `${v / max * 84}px`,
|
||
background: 'var(--accent)',
|
||
borderRadius: '6px 6px 0 0',
|
||
opacity: 0.85
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, labels[i])))))));
|
||
}
|
||
|
||
/* ───────── Bottom tab ───────── */
|
||
function BottomTab({
|
||
t,
|
||
tab,
|
||
setTab
|
||
}) {
|
||
const items = [['connect', 'power', t('tabConnect')], ['servers', 'globe', t('tabServers')], ['stats', 'chart', t('tabStats')], ['me', 'user', t('tabMe')]];
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
borderTop: '1px solid var(--border)',
|
||
background: 'var(--surface)',
|
||
paddingBottom: 22
|
||
}
|
||
}, items.map(([id, ic, label]) => {
|
||
const a = tab === id;
|
||
return /*#__PURE__*/React.createElement("button", {
|
||
key: id,
|
||
onClick: () => setTab(id),
|
||
style: {
|
||
flex: 1,
|
||
border: 'none',
|
||
background: 'transparent',
|
||
cursor: 'pointer',
|
||
padding: '10px 0 4px',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
gap: 4
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: ic,
|
||
size: 22,
|
||
stroke: a ? 2.4 : 2,
|
||
color: a ? 'var(--accent)' : 'var(--fg3)'
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 11,
|
||
fontWeight: a ? 700 : 500,
|
||
color: a ? 'var(--accent)' : 'var(--fg3)'
|
||
}
|
||
}, label));
|
||
}));
|
||
}
|
||
Object.assign(window, {
|
||
ConnectScreen,
|
||
ServersScreen,
|
||
StatsScreen,
|
||
SettingsScreen,
|
||
BottomTab,
|
||
LangSwitch
|
||
});
|
||
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/mobile/screens.jsx", error: String((e && e.message) || e) }); }
|
||
|
||
// ui_kits/tablet/tabapp.jsx
|
||
try { (() => {
|
||
/* tabapp.jsx — 穿山甲 iPad 客户端 · 分栏布局
|
||
复用 ../mobile/parts.jsx 的 Icon/Mark/CC/Signal/SERVERS/makeT/srvName/srvSub。
|
||
仅 iPad 专属字串定义在本文件 TSTR。 */
|
||
|
||
const {
|
||
useState,
|
||
useEffect,
|
||
useRef
|
||
} = React;
|
||
|
||
/* iPad 专属字串(双语单显) */
|
||
const TSTR = {
|
||
navConnect: {
|
||
zh: '连接',
|
||
en: 'Connect'
|
||
},
|
||
navNodes: {
|
||
zh: '节点',
|
||
en: 'Nodes'
|
||
},
|
||
navStats: {
|
||
zh: '统计',
|
||
en: 'Stats'
|
||
},
|
||
navAccount: {
|
||
zh: '账户',
|
||
en: 'Account'
|
||
},
|
||
capOff: {
|
||
zh: '未连接 · 轻点连接',
|
||
en: 'Not connected · tap to start'
|
||
},
|
||
capConnecting: {
|
||
zh: '连接中 Connecting…',
|
||
en: 'Connecting…'
|
||
},
|
||
capOn: {
|
||
zh: '已连接 · 网络已加密',
|
||
en: 'Connected · traffic encrypted'
|
||
},
|
||
download: {
|
||
zh: '下载',
|
||
en: 'Down'
|
||
},
|
||
upload: {
|
||
zh: '上传',
|
||
en: 'Up'
|
||
},
|
||
latency: {
|
||
zh: '延迟',
|
||
en: 'Ping'
|
||
},
|
||
curNode: {
|
||
zh: '当前节点',
|
||
en: 'Current node'
|
||
},
|
||
searchPh: {
|
||
zh: '搜索国家 / 城市',
|
||
en: 'Search country / city'
|
||
},
|
||
statTraffic: {
|
||
zh: '本月流量',
|
||
en: 'Data this month'
|
||
},
|
||
statPing: {
|
||
zh: '平均延迟',
|
||
en: 'Avg latency'
|
||
},
|
||
statTime: {
|
||
zh: '本月时长',
|
||
en: 'Time this month'
|
||
},
|
||
weekTraffic: {
|
||
zh: '本周流量 (GB)',
|
||
en: 'This week (GB)'
|
||
},
|
||
days: {
|
||
zh: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||
en: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||
},
|
||
acctEmail: {
|
||
zh: '邮箱',
|
||
en: 'Email'
|
||
},
|
||
language: {
|
||
zh: '语言',
|
||
en: 'Language'
|
||
},
|
||
darkMode: {
|
||
zh: '深色外观',
|
||
en: 'Dark appearance'
|
||
},
|
||
smartRoute: {
|
||
zh: '智能分流',
|
||
en: 'Smart routing'
|
||
},
|
||
smartRouteSub: {
|
||
zh: '海外应用走加速,国内直连',
|
||
en: 'Accelerate abroad, direct at home'
|
||
},
|
||
killSwitch: {
|
||
zh: 'Kill Switch',
|
||
en: 'Kill Switch'
|
||
},
|
||
killSub: {
|
||
zh: '断线时阻断网络,防止泄露',
|
||
en: 'Block traffic if the link drops'
|
||
},
|
||
freeBanner: {
|
||
zh: '免费版',
|
||
en: 'Free plan'
|
||
},
|
||
trialNote: {
|
||
zh: '升级 PRO:不限时长 · 80+ 加速线路 · 5 台设备',
|
||
en: 'Go PRO: unlimited time · 80+ routes · 5 devices'
|
||
},
|
||
version: {
|
||
zh: '穿山甲 · iPadOS · v2.4.0',
|
||
en: 'Pangolin · iPadOS · v2.4.0'
|
||
}
|
||
};
|
||
function makeTT(lang) {
|
||
return k => (TSTR[k] && TSTR[k][lang]) !== undefined ? TSTR[k][lang] : k;
|
||
}
|
||
function fmtHMS(s) {
|
||
return `${String(Math.floor(s / 3600)).padStart(2, '0')}:${String(Math.floor(s % 3600 / 60)).padStart(2, '0')}:${String(s % 60).padStart(2, '0')}`;
|
||
}
|
||
|
||
/* ── 左侧栏 ── */
|
||
function TabRail({
|
||
tt,
|
||
view,
|
||
setView,
|
||
free,
|
||
t
|
||
}) {
|
||
const items = [['connect', 'power', 'navConnect'], ['nodes', 'globe', 'navNodes'], ['stats', 'chart', 'navStats'], ['account', 'user', 'navAccount']];
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 232,
|
||
flexShrink: 0,
|
||
background: 'var(--bg-subtle)',
|
||
borderRight: '1px solid var(--border)',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
padding: '18px 14px 16px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 10,
|
||
padding: '0 8px 20px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Mark, {
|
||
size: 30
|
||
}), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 17,
|
||
color: 'var(--fg1)',
|
||
lineHeight: 1
|
||
}
|
||
}, "\u7A7F\u5C71\u7532"), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 9,
|
||
fontWeight: 600,
|
||
letterSpacing: '0.2em',
|
||
color: 'var(--accent)',
|
||
marginTop: 3
|
||
}
|
||
}, "PANGOLIN"))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 4
|
||
}
|
||
}, items.map(([id, ic, lk]) => {
|
||
const a = view === id;
|
||
return /*#__PURE__*/React.createElement("button", {
|
||
key: id,
|
||
onClick: () => setView(id),
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
width: '100%',
|
||
textAlign: 'left',
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
background: a ? 'var(--accent-subtle)' : 'transparent',
|
||
color: a ? 'var(--accent)' : 'var(--fg2)',
|
||
padding: '13px 14px',
|
||
borderRadius: 'var(--radius-md)',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 15,
|
||
fontWeight: a ? 700 : 500,
|
||
transition: 'background-color 140ms var(--ease-out), color 140ms var(--ease-out)',
|
||
minHeight: 48
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: ic,
|
||
size: 20,
|
||
stroke: a ? 2.4 : 2,
|
||
color: a ? 'var(--accent)' : 'var(--fg3)'
|
||
}), tt(lk));
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
background: free ? 'var(--surface)' : 'linear-gradient(150deg,var(--clay-600),var(--clay-800))',
|
||
border: free ? '1px solid var(--border)' : 'none',
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '12px 13px',
|
||
color: free ? 'var(--fg1)' : '#fff'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 9
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 32,
|
||
height: 32,
|
||
borderRadius: '50%',
|
||
background: free ? 'var(--bg-subtle)' : 'rgba(255,255,255,0.18)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: free ? 'user' : 'crown',
|
||
size: 16,
|
||
color: free ? 'var(--fg2)' : '#fff'
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
minWidth: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
fontWeight: 700,
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, free ? tt('freeBanner') : t('proMember')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 10.5,
|
||
opacity: 0.65,
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, "me@pangolin.vpn")))));
|
||
}
|
||
|
||
/* ── 连接视图:左大按钮 + 右信息列 ── */
|
||
function TabConnect({
|
||
t,
|
||
tt,
|
||
lang,
|
||
status,
|
||
onToggle,
|
||
server,
|
||
smart,
|
||
free,
|
||
mins,
|
||
adDone,
|
||
onAd,
|
||
elapsed,
|
||
goNodes
|
||
}) {
|
||
const map = {
|
||
off: {
|
||
ring: 'var(--border)',
|
||
fill: 'var(--surface)',
|
||
fg: 'var(--fg2)',
|
||
icon: 'power',
|
||
label: 'TAP',
|
||
cap: tt('capOff')
|
||
},
|
||
connecting: {
|
||
ring: 'var(--accent-subtle)',
|
||
fill: 'var(--accent)',
|
||
fg: '#fff',
|
||
icon: 'loader-circle',
|
||
label: '···',
|
||
cap: tt('capConnecting')
|
||
},
|
||
on: {
|
||
ring: 'var(--success-subtle)',
|
||
fill: 'var(--success)',
|
||
fg: '#fff',
|
||
icon: 'shield-check',
|
||
label: 'SAFE',
|
||
cap: tt('capOn')
|
||
}
|
||
}[status];
|
||
const card = {
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
};
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
height: '100%',
|
||
alignItems: 'stretch'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1.25,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 26
|
||
}
|
||
}, /*#__PURE__*/React.createElement("button", {
|
||
onClick: onToggle,
|
||
style: {
|
||
width: 216,
|
||
height: 216,
|
||
borderRadius: '50%',
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
background: map.fill,
|
||
color: map.fg,
|
||
boxShadow: `0 0 0 11px ${map.ring}, var(--shadow-lg)`,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 12,
|
||
transition: 'box-shadow 300ms var(--ease-out), background-color 300ms var(--ease-out)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: map.icon,
|
||
size: 60,
|
||
stroke: 1.6,
|
||
style: status === 'connecting' ? {
|
||
animation: 'pg-spin 1s linear infinite'
|
||
} : {}
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontWeight: 700,
|
||
fontSize: 14,
|
||
letterSpacing: '0.08em'
|
||
}
|
||
}, map.label)), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
textAlign: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 16,
|
||
fontWeight: 600,
|
||
color: 'var(--fg2)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, map.cap), status === 'on' && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 28,
|
||
fontWeight: 500,
|
||
color: 'var(--fg1)',
|
||
marginTop: 8,
|
||
letterSpacing: '-0.01em'
|
||
}
|
||
}, fmtHMS(elapsed)))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 332,
|
||
flexShrink: 0,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 14,
|
||
justifyContent: 'center',
|
||
paddingRight: 28
|
||
}
|
||
}, free && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...card,
|
||
padding: '14px 16px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 8,
|
||
marginBottom: 9
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "clock",
|
||
size: 15,
|
||
stroke: 2.2,
|
||
color: "var(--accent)"
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg2)'
|
||
}
|
||
}, t('quotaToday')), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 14,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, mins, " ", t('minutes')), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
marginLeft: 'auto',
|
||
fontSize: 10.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg3)',
|
||
background: 'var(--bg-subtle)',
|
||
padding: '3px 9px',
|
||
borderRadius: 'var(--radius-full)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, t('quotaFree'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
height: 6,
|
||
background: 'var(--bg-subtle)',
|
||
borderRadius: 3,
|
||
overflow: 'hidden',
|
||
marginBottom: 11
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: `${Math.min(100, mins / 10 * 100)}%`,
|
||
height: '100%',
|
||
background: mins <= 3 ? 'var(--warning)' : 'var(--accent)',
|
||
borderRadius: 3
|
||
}
|
||
})), adDone ? /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 7,
|
||
color: 'var(--success)',
|
||
fontWeight: 700,
|
||
fontSize: 13,
|
||
padding: '8px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "check-circle",
|
||
size: 16,
|
||
stroke: 2.2,
|
||
color: "var(--success)"
|
||
}), t('adUnlocked')) : /*#__PURE__*/React.createElement("button", {
|
||
onClick: onAd,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 7,
|
||
width: '100%',
|
||
border: '1.5px solid var(--accent-border)',
|
||
background: 'var(--accent-subtle)',
|
||
color: 'var(--accent)',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontWeight: 700,
|
||
fontSize: 13.5,
|
||
padding: '10px',
|
||
borderRadius: 'var(--radius-full)',
|
||
cursor: 'pointer',
|
||
minHeight: 44
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "play-circle",
|
||
size: 16,
|
||
stroke: 2.2
|
||
}), t('watchAd'))), /*#__PURE__*/React.createElement("button", {
|
||
onClick: goNodes,
|
||
style: {
|
||
...card,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 13,
|
||
textAlign: 'left',
|
||
padding: '13px 15px',
|
||
cursor: 'pointer',
|
||
minHeight: 48
|
||
}
|
||
}, /*#__PURE__*/React.createElement(CC, {
|
||
code: server.code,
|
||
active: true
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11,
|
||
fontWeight: 600,
|
||
color: 'var(--fg3)',
|
||
marginBottom: 2
|
||
}
|
||
}, tt('curNode')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 15,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 7
|
||
}
|
||
}, smart ? t('smartSelect') : srvName(server, lang), smart && /*#__PURE__*/React.createElement(Icon, {
|
||
name: "zap",
|
||
size: 13,
|
||
stroke: 2.4,
|
||
color: "var(--accent)"
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, smart ? `${srvName(server, lang)} · ${server.ping}ms` : `${srvSub(server, lang)} · ${server.ping}ms`)), /*#__PURE__*/React.createElement(Icon, {
|
||
name: "chevron-right",
|
||
size: 20,
|
||
color: "var(--fg3)"
|
||
})), status === 'on' && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 10
|
||
}
|
||
}, [['arrow-down', tt('download'), '86.4', 'Mb/s'], ['arrow-up', tt('upload'), '12.1', 'Mb/s'], ['zap', tt('latency'), String(server.ping), 'ms']].map(([ic, l, v, u]) => /*#__PURE__*/React.createElement("div", {
|
||
key: ic,
|
||
style: {
|
||
...card,
|
||
flex: 1,
|
||
padding: '11px 13px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 5,
|
||
color: 'var(--fg3)',
|
||
fontSize: 11,
|
||
fontWeight: 600,
|
||
marginBottom: 4
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: ic,
|
||
size: 13,
|
||
stroke: 2.2,
|
||
color: "var(--accent)"
|
||
}), l), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 16,
|
||
fontWeight: 500,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, v, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 10.5,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, " ", u)))))));
|
||
}
|
||
|
||
/* ── 节点视图 ── */
|
||
function TabNodes({
|
||
t,
|
||
tt,
|
||
lang,
|
||
current,
|
||
onPick
|
||
}) {
|
||
const [q, setQ] = useState('');
|
||
const list = SERVERS.filter(s => (s.name + s.en).toLowerCase().includes(q.toLowerCase()));
|
||
const smartActive = current === 'AUTO';
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '6px 28px 24px',
|
||
overflow: 'auto',
|
||
height: '100%',
|
||
boxSizing: 'border-box'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => onPick('AUTO'),
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 13,
|
||
width: '100%',
|
||
textAlign: 'left',
|
||
cursor: 'pointer',
|
||
background: 'var(--accent-subtle)',
|
||
border: `1.5px solid ${smartActive ? 'var(--accent)' : 'var(--accent-border)'}`,
|
||
borderRadius: 'var(--radius-xl)',
|
||
padding: '15px 16px',
|
||
boxShadow: smartActive ? 'var(--shadow-md)' : 'var(--shadow-sm)',
|
||
marginBottom: 14,
|
||
minHeight: 56
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 42,
|
||
height: 42,
|
||
borderRadius: 'var(--radius-md)',
|
||
background: 'linear-gradient(150deg,var(--clay-500),var(--clay-700))',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "zap",
|
||
size: 21,
|
||
stroke: 2.2,
|
||
color: "#fff"
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 8
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 15.5,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('smartSelect')), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 10,
|
||
fontWeight: 700,
|
||
color: '#fff',
|
||
background: 'var(--accent)',
|
||
padding: '2px 8px',
|
||
borderRadius: 999,
|
||
letterSpacing: '0.04em'
|
||
}
|
||
}, t('recommended'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg2)',
|
||
marginTop: 3
|
||
}
|
||
}, t('smartSub'))), smartActive && /*#__PURE__*/React.createElement(Icon, {
|
||
name: "check",
|
||
size: 19,
|
||
color: "var(--accent)",
|
||
stroke: 2.6
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 10,
|
||
background: 'var(--bg-subtle)',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '11px 14px',
|
||
marginBottom: 14,
|
||
maxWidth: 380
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "search",
|
||
size: 18,
|
||
color: "var(--fg3)"
|
||
}), /*#__PURE__*/React.createElement("input", {
|
||
value: q,
|
||
onChange: e => setQ(e.target.value),
|
||
placeholder: tt('searchPh'),
|
||
style: {
|
||
border: 'none',
|
||
background: 'transparent',
|
||
outline: 'none',
|
||
flex: 1,
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 14.5,
|
||
color: 'var(--fg1)'
|
||
}
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: '1fr 1fr',
|
||
gap: 11
|
||
}
|
||
}, list.map(s => {
|
||
const active = s.code === current;
|
||
return /*#__PURE__*/React.createElement("button", {
|
||
key: s.code,
|
||
onClick: () => onPick(s.code),
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
textAlign: 'left',
|
||
cursor: 'pointer',
|
||
background: active ? 'var(--accent-subtle)' : 'var(--surface)',
|
||
border: `1.5px solid ${active ? 'var(--accent-border)' : 'var(--border)'}`,
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '13px 15px',
|
||
boxShadow: 'var(--shadow-sm)',
|
||
minHeight: 56
|
||
}
|
||
}, /*#__PURE__*/React.createElement(CC, {
|
||
code: s.code,
|
||
active: active
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
minWidth: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 14.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, srvName(s, lang)), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg3)',
|
||
whiteSpace: 'nowrap',
|
||
overflow: 'hidden',
|
||
textOverflow: 'ellipsis'
|
||
}
|
||
}, srvSub(s, lang))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'flex-end',
|
||
gap: 5
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 12,
|
||
color: 'var(--fg2)'
|
||
}
|
||
}, s.ping, "ms"), /*#__PURE__*/React.createElement(Signal, {
|
||
ping: s.ping
|
||
})), active && /*#__PURE__*/React.createElement(Icon, {
|
||
name: "check",
|
||
size: 18,
|
||
color: "var(--accent)"
|
||
}));
|
||
})));
|
||
}
|
||
|
||
/* ── 统计视图 ── */
|
||
function TabStats({
|
||
tt,
|
||
lang
|
||
}) {
|
||
const vals = [2.1, 3.4, 1.8, 4.6, 5.2, 6.1, 3.0];
|
||
const days = TSTR.days[lang];
|
||
const max = Math.max(...vals);
|
||
const card = {
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
};
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '6px 28px 24px',
|
||
overflow: 'auto',
|
||
height: '100%',
|
||
boxSizing: 'border-box'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 14,
|
||
marginBottom: 20
|
||
}
|
||
}, [[tt('statTraffic'), '42.6 GB'], [tt('statPing'), '29 ms'], [tt('statTime'), '86.4 h']].map(([l, v]) => /*#__PURE__*/React.createElement("div", {
|
||
key: l,
|
||
style: {
|
||
...card,
|
||
flex: 1,
|
||
padding: '18px 20px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
fontWeight: 600
|
||
}
|
||
}, l), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 26,
|
||
fontWeight: 500,
|
||
color: 'var(--fg1)',
|
||
marginTop: 7
|
||
}
|
||
}, v)))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...card,
|
||
padding: '20px 22px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 20
|
||
}
|
||
}, tt('weekTraffic')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'flex-end',
|
||
gap: 22,
|
||
height: 150
|
||
}
|
||
}, vals.map((v, i) => /*#__PURE__*/React.createElement("div", {
|
||
key: i,
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
gap: 9
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 10.5,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, v), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: '100%',
|
||
maxWidth: 40,
|
||
height: `${v / max * 104}px`,
|
||
background: 'var(--accent)',
|
||
borderRadius: '6px 6px 0 0',
|
||
opacity: 0.85
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11.5,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, days[i]))))));
|
||
}
|
||
|
||
/* ── 账户视图 ── */
|
||
function TabToggle({
|
||
on,
|
||
onChange
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
onClick: onChange,
|
||
style: {
|
||
width: 48,
|
||
height: 29,
|
||
borderRadius: 999,
|
||
position: 'relative',
|
||
cursor: 'pointer',
|
||
flexShrink: 0,
|
||
background: on ? 'var(--accent)' : 'var(--border-strong)',
|
||
transition: 'background 220ms var(--ease-out)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'absolute',
|
||
top: 3,
|
||
left: on ? 22 : 3,
|
||
width: 23,
|
||
height: 23,
|
||
borderRadius: '50%',
|
||
background: '#fff',
|
||
boxShadow: 'var(--shadow-sm)',
|
||
transition: 'left 220ms var(--ease-out)'
|
||
}
|
||
}));
|
||
}
|
||
function TabAccount({
|
||
t,
|
||
tt,
|
||
lang,
|
||
setLang,
|
||
dark,
|
||
setDark,
|
||
free,
|
||
setFree
|
||
}) {
|
||
const [smartRoute, setSmartRoute] = useState(true);
|
||
const [kill, setKill] = useState(true);
|
||
const card = {
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-lg)',
|
||
boxShadow: 'var(--shadow-sm)',
|
||
overflow: 'hidden',
|
||
marginBottom: 16
|
||
};
|
||
const Row = ({
|
||
icon,
|
||
title,
|
||
sub,
|
||
right,
|
||
last
|
||
}) => /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 13,
|
||
padding: '15px 18px',
|
||
borderBottom: last ? 'none' : '1px solid var(--border)',
|
||
minHeight: 48,
|
||
boxSizing: 'border-box'
|
||
}
|
||
}, icon && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 34,
|
||
height: 34,
|
||
borderRadius: 'var(--radius-sm)',
|
||
background: 'var(--accent-subtle)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: icon,
|
||
size: 17,
|
||
color: "var(--accent)",
|
||
stroke: 2.2
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 15,
|
||
fontWeight: 500,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, title), sub && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg3)',
|
||
marginTop: 2
|
||
}
|
||
}, sub)), right);
|
||
const seg = val => ({
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
padding: '7px 16px',
|
||
borderRadius: 'var(--radius-full)',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 13,
|
||
fontWeight: 600,
|
||
minHeight: 32,
|
||
background: lang === val ? 'var(--surface)' : 'transparent',
|
||
color: lang === val ? 'var(--fg1)' : 'var(--fg3)',
|
||
boxShadow: lang === val ? 'var(--shadow-sm)' : 'none'
|
||
});
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '6px 28px 24px',
|
||
overflow: 'auto',
|
||
height: '100%',
|
||
boxSizing: 'border-box',
|
||
maxWidth: 640
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
background: free ? 'var(--surface)' : 'linear-gradient(155deg,var(--clay-600),var(--clay-800))',
|
||
border: free ? '1px solid var(--border)' : 'none',
|
||
borderRadius: 'var(--radius-xl)',
|
||
padding: 20,
|
||
color: free ? 'var(--fg1)' : '#fff',
|
||
marginBottom: 18,
|
||
boxShadow: 'var(--shadow-md)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 46,
|
||
height: 46,
|
||
borderRadius: '50%',
|
||
background: free ? 'var(--bg-subtle)' : 'rgba(255,255,255,0.18)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: free ? 'user' : 'crown',
|
||
size: 22,
|
||
color: free ? 'var(--fg2)' : '#fff'
|
||
})), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontWeight: 700,
|
||
fontSize: 16.5
|
||
}
|
||
}, "me@pangolin.vpn"), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'inline-block',
|
||
fontSize: 11,
|
||
fontWeight: 600,
|
||
background: free ? 'var(--bg-subtle)' : 'rgba(255,255,255,0.2)',
|
||
color: free ? 'var(--fg2)' : '#fff',
|
||
padding: '2px 9px',
|
||
borderRadius: 999,
|
||
marginTop: 5
|
||
}
|
||
}, free ? tt('freeBanner') : t('proMember')))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
marginTop: 14,
|
||
paddingTop: 13,
|
||
borderTop: `1px solid ${free ? 'var(--border)' : 'rgba(255,255,255,0.18)'}`,
|
||
fontSize: 12.5,
|
||
opacity: free ? 1 : 0.92,
|
||
color: free ? 'var(--fg2)' : '#fff'
|
||
}
|
||
}, free ? tt('trialNote') : '2026-12-31 · 42.6 GB')), /*#__PURE__*/React.createElement("div", {
|
||
style: card
|
||
}, /*#__PURE__*/React.createElement(Row, {
|
||
icon: "zap",
|
||
title: tt('smartRoute'),
|
||
sub: tt('smartRouteSub'),
|
||
right: /*#__PURE__*/React.createElement(TabToggle, {
|
||
on: smartRoute,
|
||
onChange: () => setSmartRoute(!smartRoute)
|
||
})
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
icon: "shield",
|
||
title: tt('killSwitch'),
|
||
sub: tt('killSub'),
|
||
right: /*#__PURE__*/React.createElement(TabToggle, {
|
||
on: kill,
|
||
onChange: () => setKill(!kill)
|
||
}),
|
||
last: true
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: card
|
||
}, /*#__PURE__*/React.createElement(Row, {
|
||
icon: "globe",
|
||
title: tt('language'),
|
||
right: /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 4,
|
||
background: 'var(--bg-subtle)',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: 3
|
||
}
|
||
}, /*#__PURE__*/React.createElement("button", {
|
||
style: seg('zh'),
|
||
onClick: () => setLang('zh')
|
||
}, "\u4E2D\u6587"), /*#__PURE__*/React.createElement("button", {
|
||
style: seg('en'),
|
||
onClick: () => setLang('en')
|
||
}, "EN"))
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
icon: dark ? 'moon' : 'sun',
|
||
title: tt('darkMode'),
|
||
right: /*#__PURE__*/React.createElement(TabToggle, {
|
||
on: dark,
|
||
onChange: () => setDark(!dark)
|
||
})
|
||
}), /*#__PURE__*/React.createElement(Row, {
|
||
icon: "clock",
|
||
title: t('freeDemo'),
|
||
sub: t('freeDemoSub'),
|
||
right: /*#__PURE__*/React.createElement(TabToggle, {
|
||
on: free,
|
||
onChange: () => setFree(!free)
|
||
}),
|
||
last: true
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
textAlign: 'center',
|
||
fontSize: 12,
|
||
color: 'var(--fg3)',
|
||
fontFamily: 'var(--font-mono)'
|
||
}
|
||
}, tt('version')));
|
||
}
|
||
|
||
/* ── iPad 外壳 + 应用 ── */
|
||
function TabletApp() {
|
||
const [view, setView] = useState('connect');
|
||
const [status, setStatus] = useState('off');
|
||
const [code, setCode] = useState('AUTO');
|
||
const [lang, setLang] = useState('zh');
|
||
const [dark, setDark] = useState(false);
|
||
const [free, setFree] = useState(true);
|
||
const [mins, setMins] = useState(6);
|
||
const [adDone, setAdDone] = useState(false);
|
||
const [elapsed, setElapsed] = useState(0);
|
||
const timer = useRef(null);
|
||
const t = makeT(lang),
|
||
tt = makeTT(lang);
|
||
const smart = code === 'AUTO';
|
||
const server = smart ? SERVERS.reduce((a, b) => a.ping < b.ping ? a : b) : SERVERS.find(s => s.code === code);
|
||
const titles = {
|
||
connect: tt('navConnect'),
|
||
nodes: tt('navNodes'),
|
||
stats: tt('navStats'),
|
||
account: tt('navAccount')
|
||
};
|
||
function toggle() {
|
||
if (status === 'off') {
|
||
setStatus('connecting');
|
||
setTimeout(() => {
|
||
setStatus('on');
|
||
setElapsed(0);
|
||
}, 1500);
|
||
} else setStatus('off');
|
||
}
|
||
function pick(c) {
|
||
setCode(c);
|
||
setView('connect');
|
||
if (status === 'on') {
|
||
setStatus('connecting');
|
||
setTimeout(() => {
|
||
setStatus('on');
|
||
setElapsed(0);
|
||
}, 1200);
|
||
}
|
||
}
|
||
useEffect(() => {
|
||
if (status === 'on') timer.current = setInterval(() => setElapsed(e => e + 1), 1000);
|
||
return () => clearInterval(timer.current);
|
||
}, [status]);
|
||
let main;
|
||
if (view === 'connect') main = /*#__PURE__*/React.createElement(TabConnect, {
|
||
t: t,
|
||
tt: tt,
|
||
lang: lang,
|
||
status: status,
|
||
onToggle: toggle,
|
||
server: server,
|
||
smart: smart,
|
||
free: free,
|
||
mins: mins,
|
||
adDone: adDone,
|
||
onAd: () => setAdDone(true),
|
||
elapsed: elapsed,
|
||
goNodes: () => setView('nodes')
|
||
});else if (view === 'nodes') main = /*#__PURE__*/React.createElement(TabNodes, {
|
||
t: t,
|
||
tt: tt,
|
||
lang: lang,
|
||
current: code,
|
||
onPick: pick
|
||
});else if (view === 'stats') main = /*#__PURE__*/React.createElement(TabStats, {
|
||
tt: tt,
|
||
lang: lang
|
||
});else main = /*#__PURE__*/React.createElement(TabAccount, {
|
||
t: t,
|
||
tt: tt,
|
||
lang: lang,
|
||
setLang: setLang,
|
||
dark: dark,
|
||
setDark: setDark,
|
||
free: free,
|
||
setFree: setFree
|
||
});
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
background: '#1a1714',
|
||
borderRadius: 34,
|
||
padding: 16,
|
||
boxShadow: '0 0 0 1px rgba(0,0,0,0.3), 0 30px 80px rgba(0,0,0,0.45)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
"data-theme": dark ? 'dark' : 'light',
|
||
style: {
|
||
width: 1180,
|
||
height: 820,
|
||
borderRadius: 20,
|
||
overflow: 'hidden',
|
||
background: 'var(--bg)',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
fontFamily: 'var(--font-sans)',
|
||
position: 'relative'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
height: 30,
|
||
flexShrink: 0,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
padding: '0 22px',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 12.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", null, "9:41"), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 8
|
||
}
|
||
}, /*#__PURE__*/React.createElement(Icon, {
|
||
name: "wifi",
|
||
size: 15,
|
||
stroke: 2.2,
|
||
color: "var(--fg1)"
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 11.5
|
||
}
|
||
}, "87%"), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 24,
|
||
height: 12,
|
||
border: '1.5px solid var(--fg2)',
|
||
borderRadius: 3.5,
|
||
padding: 1.5,
|
||
boxSizing: 'border-box'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: '80%',
|
||
height: '100%',
|
||
background: 'var(--fg1)',
|
||
borderRadius: 1.5
|
||
}
|
||
})))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
minHeight: 0,
|
||
display: 'flex'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(TabRail, {
|
||
tt: tt,
|
||
view: view,
|
||
setView: setView,
|
||
free: free,
|
||
t: t
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
minWidth: 0,
|
||
display: 'flex',
|
||
flexDirection: 'column'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
height: 56,
|
||
flexShrink: 0,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
padding: '0 28px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 19,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, titles[view]), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 12.5,
|
||
fontWeight: 600,
|
||
color: status === 'on' ? 'var(--success)' : 'var(--fg3)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, status === 'on' ? '● ' + server.code : '○')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
minHeight: 0
|
||
}
|
||
}, main)))));
|
||
}
|
||
Object.assign(window, {
|
||
TabletApp
|
||
});
|
||
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/tablet/tabapp.jsx", error: String((e && e.message) || e) }); }
|
||
|
||
// ui_kits/usercenter/ucapp.jsx
|
||
try { (() => {
|
||
/* ucapp.jsx — 穿山甲 Web 用户中心 · views */
|
||
const {
|
||
useState: useStateU
|
||
} = React;
|
||
const ucCard = {
|
||
background: 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-xl)',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
};
|
||
const ucInput = {
|
||
border: '1.5px solid var(--border-strong)',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '12px 14px',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 14.5,
|
||
color: 'var(--fg1)',
|
||
background: 'var(--surface)',
|
||
outline: 'none',
|
||
width: '100%',
|
||
boxSizing: 'border-box'
|
||
};
|
||
|
||
/* viewport hook — mobile ≤ 700px */
|
||
function useIsMobileUC() {
|
||
const [m, setM] = useStateU(typeof window !== 'undefined' && window.innerWidth <= 700);
|
||
React.useEffect(() => {
|
||
const f = () => setM(window.innerWidth <= 700);
|
||
window.addEventListener('resize', f);
|
||
return () => window.removeEventListener('resize', f);
|
||
}, []);
|
||
return m;
|
||
}
|
||
function UCLang({
|
||
lang,
|
||
setLang
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
background: 'var(--bg-subtle)',
|
||
borderRadius: 999,
|
||
padding: 3,
|
||
gap: 2
|
||
}
|
||
}, [['zh', '中文'], ['en', 'EN']].map(([v, l]) => /*#__PURE__*/React.createElement("button", {
|
||
key: v,
|
||
onClick: () => setLang(v),
|
||
style: {
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
borderRadius: 999,
|
||
padding: '5px 12px',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 12.5,
|
||
fontWeight: 700,
|
||
background: lang === v ? 'var(--accent)' : 'transparent',
|
||
color: lang === v ? 'var(--fg-on-accent)' : 'var(--fg3)'
|
||
}
|
||
}, l)));
|
||
}
|
||
|
||
/* ───────── Login (+ PRO 2FA step) ───────── */
|
||
function UCLogin({
|
||
t,
|
||
lang,
|
||
setLang,
|
||
onDone
|
||
}) {
|
||
const [email, setEmail] = useStateU('');
|
||
const [pw, setPw] = useStateU('');
|
||
const [step, setStep] = useStateU('cred'); // cred | totp
|
||
const [otp, setOtp] = useStateU('');
|
||
const ok = /\S+@\S+\.\S+/.test(email) && pw.length > 0;
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
minHeight: '100vh',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
background: 'var(--bg)',
|
||
padding: 24
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...ucCard,
|
||
width: 400,
|
||
maxWidth: '100%',
|
||
padding: '36px 34px',
|
||
boxSizing: 'border-box'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
marginBottom: 26
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 10
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCMark, {
|
||
size: 32
|
||
}), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 17,
|
||
color: 'var(--fg1)',
|
||
lineHeight: 1
|
||
}
|
||
}, "\u7A7F\u5C71\u7532"), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 8.5,
|
||
fontWeight: 600,
|
||
letterSpacing: '0.2em',
|
||
color: 'var(--accent)',
|
||
marginTop: 3
|
||
}
|
||
}, "PANGOLIN"))), /*#__PURE__*/React.createElement(UCLang, {
|
||
lang: lang,
|
||
setLang: setLang
|
||
})), step === 'cred' ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 22,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('loginTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13.5,
|
||
color: 'var(--fg3)',
|
||
margin: '6px 0 24px'
|
||
}
|
||
}, t('loginSub')), /*#__PURE__*/React.createElement("label", {
|
||
style: {
|
||
display: 'block',
|
||
marginBottom: 14
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
display: 'block',
|
||
fontSize: 12.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg2)',
|
||
marginBottom: 6
|
||
}
|
||
}, t('emailLabel')), /*#__PURE__*/React.createElement("input", {
|
||
style: ucInput,
|
||
value: email,
|
||
onChange: e => setEmail(e.target.value),
|
||
placeholder: t('emailPh')
|
||
})), /*#__PURE__*/React.createElement("label", {
|
||
style: {
|
||
display: 'block'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
display: 'block',
|
||
fontSize: 12.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg2)',
|
||
marginBottom: 6
|
||
}
|
||
}, t('pwLabel')), /*#__PURE__*/React.createElement("input", {
|
||
style: ucInput,
|
||
type: "password",
|
||
value: pw,
|
||
onChange: e => setPw(e.target.value),
|
||
placeholder: t('pwPh')
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
textAlign: 'right',
|
||
margin: '10px 0 18px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--accent)',
|
||
fontWeight: 600,
|
||
cursor: 'pointer'
|
||
}
|
||
}, t('forgotPw'))), /*#__PURE__*/React.createElement("button", {
|
||
onClick: ok ? () => setStep('totp') : undefined,
|
||
disabled: !ok,
|
||
style: {
|
||
width: '100%',
|
||
border: 'none',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: '13px',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontWeight: 700,
|
||
fontSize: 15,
|
||
cursor: ok ? 'pointer' : 'not-allowed',
|
||
background: 'var(--accent)',
|
||
color: 'var(--fg-on-accent)',
|
||
opacity: ok ? 1 : 0.45
|
||
}
|
||
}, t('doLogin')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
textAlign: 'center',
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
marginTop: 18
|
||
}
|
||
}, t('noAccount'))) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 9,
|
||
marginBottom: 6
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: "shield-check",
|
||
size: 20,
|
||
color: "var(--accent)"
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 20,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('twoFATitle')), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 10.5,
|
||
fontWeight: 700,
|
||
color: 'var(--accent)',
|
||
background: 'var(--accent-subtle)',
|
||
border: '1px solid var(--accent-border)',
|
||
padding: '2px 8px',
|
||
borderRadius: 999
|
||
}
|
||
}, "PRO")), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--fg3)',
|
||
margin: '4px 0 20px',
|
||
lineHeight: 1.6
|
||
}
|
||
}, t('twoFAHint')), /*#__PURE__*/React.createElement("input", {
|
||
value: otp,
|
||
onChange: e => setOtp(e.target.value.replace(/\D/g, '').slice(0, 6)),
|
||
placeholder: "\xB7\xB7\xB7\xB7\xB7\xB7",
|
||
style: {
|
||
...ucInput,
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 22,
|
||
letterSpacing: '0.5em',
|
||
textAlign: 'center',
|
||
padding: '14px'
|
||
}
|
||
}), /*#__PURE__*/React.createElement("button", {
|
||
onClick: otp.length === 6 ? onDone : undefined,
|
||
disabled: otp.length !== 6,
|
||
style: {
|
||
width: '100%',
|
||
border: 'none',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: '13px',
|
||
marginTop: 16,
|
||
fontFamily: 'var(--font-sans)',
|
||
fontWeight: 700,
|
||
fontSize: 15,
|
||
cursor: otp.length === 6 ? 'pointer' : 'not-allowed',
|
||
background: 'var(--accent)',
|
||
color: 'var(--fg-on-accent)',
|
||
opacity: otp.length === 6 ? 1 : 0.45
|
||
}
|
||
}, t('twoFAConfirm')), /*#__PURE__*/React.createElement("div", {
|
||
onClick: () => setStep('cred'),
|
||
style: {
|
||
textAlign: 'center',
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
marginTop: 18,
|
||
cursor: 'pointer'
|
||
}
|
||
}, t('twoFABack')))));
|
||
}
|
||
|
||
/* ───────── Overview ───────── */
|
||
function UCOverview({
|
||
t,
|
||
lang,
|
||
free,
|
||
mobile,
|
||
goSub,
|
||
goRedeem
|
||
}) {
|
||
const vals = [1.8, 2.4, 1.2, 3.1, 4.0, 5.2, 2.6];
|
||
const max = 5.2;
|
||
const labels = lang === 'zh' ? ['一', '二', '三', '四', '五', '六', '日'] : ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 20
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 25,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('greeting')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13.5,
|
||
color: 'var(--fg3)',
|
||
marginTop: 3
|
||
}
|
||
}, "me@pangolin.vpn")), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...ucCard,
|
||
background: 'linear-gradient(150deg,var(--clay-600),var(--clay-800))',
|
||
border: 'none',
|
||
color: '#fff',
|
||
padding: '20px 24px',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
gap: 16,
|
||
flexWrap: 'wrap'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 14
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 46,
|
||
height: 46,
|
||
borderRadius: '50%',
|
||
background: 'rgba(255,255,255,.18)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: "crown",
|
||
size: 23,
|
||
color: "#fff"
|
||
})), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
opacity: .8
|
||
}
|
||
}, t('curPlan')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 19,
|
||
fontWeight: 700,
|
||
marginTop: 2
|
||
}
|
||
}, free ? t('freePlan') : t('proMember')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11.5,
|
||
opacity: .8,
|
||
marginTop: 3,
|
||
fontFamily: 'var(--font-mono)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, free ? t('quotaFree') : `${t('expires')} 2026-12-31`))), /*#__PURE__*/React.createElement("button", {
|
||
onClick: goRedeem,
|
||
style: {
|
||
border: 'none',
|
||
background: '#fff',
|
||
color: 'var(--clay-700)',
|
||
fontWeight: 700,
|
||
fontSize: 13.5,
|
||
padding: '11px 20px',
|
||
borderRadius: 'var(--radius-full)',
|
||
cursor: 'pointer',
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 7
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: "ticket",
|
||
size: 15,
|
||
color: "var(--clay-700)"
|
||
}), t('renew'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: 'repeat(auto-fit,minmax(160px,1fr))',
|
||
gap: 14
|
||
}
|
||
}, [['clock', t('quotaToday'), free ? '6' : '∞', free ? 'min' : ''], ['arrow-down', t('dataToday'), '0.8', 'GB'], ['smartphone', t('devices'), '2 / 5', '']].map(([ic, l, v, u]) => /*#__PURE__*/React.createElement("div", {
|
||
key: l,
|
||
style: {
|
||
...ucCard,
|
||
padding: '16px 18px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 7,
|
||
color: 'var(--fg3)',
|
||
fontSize: 12,
|
||
fontWeight: 600
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: ic,
|
||
size: 15,
|
||
color: "var(--accent)"
|
||
}), l), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 23,
|
||
fontWeight: 500,
|
||
color: 'var(--fg1)',
|
||
marginTop: 7
|
||
}
|
||
}, v, u && /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, " ", u))))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: mobile ? '1fr' : '1.6fr 1fr',
|
||
gap: 14,
|
||
alignItems: 'start'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...ucCard,
|
||
padding: '18px 22px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 16
|
||
}
|
||
}, t('usageTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'flex-end',
|
||
gap: 14,
|
||
height: 110
|
||
}
|
||
}, vals.map((v, i) => /*#__PURE__*/React.createElement("div", {
|
||
key: i,
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
gap: 6
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 9.5,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, v), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: '100%',
|
||
maxWidth: 30,
|
||
height: `${v / max * 76}px`,
|
||
background: 'var(--accent)',
|
||
borderRadius: '5px 5px 0 0',
|
||
opacity: .85
|
||
}
|
||
}), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 10.5,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, labels[i]))))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...ucCard,
|
||
padding: '16px 18px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 12
|
||
}
|
||
}, t('quickSub')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 8
|
||
}
|
||
}, [['link', t('qaSub'), goSub], ['ticket', t('qaRedeem'), goRedeem], ['download', t('qaApp'), null]].map(([ic, l, fn]) => /*#__PURE__*/React.createElement("button", {
|
||
key: l,
|
||
onClick: fn || undefined,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 11,
|
||
width: '100%',
|
||
textAlign: 'left',
|
||
cursor: 'pointer',
|
||
background: 'var(--bg-subtle)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '11px 13px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: ic,
|
||
size: 16,
|
||
color: "var(--accent)"
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
flex: 1,
|
||
fontSize: 13.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, l), /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: "chevron-right",
|
||
size: 15,
|
||
color: "var(--fg3)"
|
||
})))))));
|
||
}
|
||
|
||
/* ───────── Subscription ───────── */
|
||
function UCSub({
|
||
t,
|
||
lang,
|
||
mobile
|
||
}) {
|
||
const [copied, setCopied] = useStateU(false);
|
||
const [reset, setReset] = useStateU(false);
|
||
const url = 'https://sub.pangolin.vpn/s/8f3kx92m' + (reset ? '-new' : '');
|
||
const clients = [{
|
||
name: '穿山甲 App',
|
||
sub: 'iOS / Android / 桌面',
|
||
icon: 'shield-check',
|
||
accent: true
|
||
}, {
|
||
name: 'Shadowrocket',
|
||
sub: 'iOS',
|
||
icon: 'external-link'
|
||
}, {
|
||
name: 'Clash Verge',
|
||
sub: 'Windows / macOS',
|
||
icon: 'external-link'
|
||
}, {
|
||
name: 'v2rayN',
|
||
sub: 'Windows',
|
||
icon: 'external-link'
|
||
}, {
|
||
name: 'sing-box',
|
||
sub: '全平台',
|
||
icon: 'external-link'
|
||
}];
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 18,
|
||
maxWidth: 720
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 25,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('subTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--fg3)',
|
||
marginTop: 5,
|
||
lineHeight: 1.6
|
||
}
|
||
}, t('subDesc'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: mobile ? '1fr' : '1fr auto',
|
||
gap: 14,
|
||
alignItems: 'start'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...ucCard,
|
||
padding: '18px 20px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 9,
|
||
background: 'var(--bg-subtle)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '11px 13px',
|
||
marginBottom: 12
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: "link",
|
||
size: 15,
|
||
color: "var(--fg3)"
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
flex: 1,
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 12.5,
|
||
color: 'var(--fg1)',
|
||
overflow: 'hidden',
|
||
textOverflow: 'ellipsis',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, url)), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 10,
|
||
flexWrap: 'wrap'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => {
|
||
setCopied(true);
|
||
setTimeout(() => setCopied(false), 1800);
|
||
},
|
||
style: {
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 7,
|
||
border: 'none',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: '10px 18px',
|
||
background: 'var(--accent)',
|
||
color: '#fff',
|
||
fontWeight: 700,
|
||
fontSize: 13.5,
|
||
cursor: 'pointer'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: copied ? 'check' : 'copy',
|
||
size: 15,
|
||
color: "#fff"
|
||
}), copied ? t('subCopied') : t('subCopy')), /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => setReset(true),
|
||
style: {
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 7,
|
||
border: '1.5px solid var(--border-strong)',
|
||
borderRadius: 'var(--radius-full)',
|
||
padding: '10px 18px',
|
||
background: 'transparent',
|
||
color: 'var(--fg2)',
|
||
fontWeight: 600,
|
||
fontSize: 13.5,
|
||
cursor: 'pointer'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: "refresh-cw",
|
||
size: 14,
|
||
color: "var(--fg2)"
|
||
}), t('subReset'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11.5,
|
||
color: reset ? 'var(--success)' : 'var(--fg3)',
|
||
marginTop: 10,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 6
|
||
}
|
||
}, reset && /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: "check-circle",
|
||
size: 13,
|
||
color: "var(--success)"
|
||
}), reset ? t('subResetOk') : t('subResetSub'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...ucCard,
|
||
padding: '16px',
|
||
width: mobile ? '100%' : 170,
|
||
boxSizing: 'border-box',
|
||
textAlign: 'center'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 138,
|
||
height: 138,
|
||
margin: '0 auto',
|
||
borderRadius: 'var(--radius-md)',
|
||
background: 'var(--bg-subtle)',
|
||
border: '1px dashed var(--border-strong)',
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: 8
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: "qr-code",
|
||
size: 42,
|
||
color: "var(--fg3)",
|
||
stroke: 1.5
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 10.5,
|
||
color: 'var(--fg3)',
|
||
fontWeight: 600
|
||
}
|
||
}, t('scanTitle'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 10.5,
|
||
color: 'var(--fg3)',
|
||
marginTop: 10,
|
||
lineHeight: 1.5
|
||
}
|
||
}, t('scanSub')))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...ucCard,
|
||
padding: '18px 20px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14.5,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('importTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
margin: '4px 0 14px'
|
||
}
|
||
}, t('importSub')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: 'repeat(auto-fill,minmax(190px,1fr))',
|
||
gap: 10
|
||
}
|
||
}, clients.map(c => /*#__PURE__*/React.createElement("button", {
|
||
key: c.name,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 11,
|
||
textAlign: 'left',
|
||
cursor: 'pointer',
|
||
background: c.accent ? 'var(--accent-subtle)' : 'var(--surface)',
|
||
border: `1px solid ${c.accent ? 'var(--accent-border)' : 'var(--border)'}`,
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '12px 14px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 34,
|
||
height: 34,
|
||
borderRadius: 'var(--radius-md)',
|
||
background: c.accent ? 'var(--accent)' : 'var(--bg-subtle)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: c.icon,
|
||
size: 17,
|
||
color: c.accent ? '#fff' : 'var(--accent)'
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
minWidth: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, c.name), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, c.sub))))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11.5,
|
||
color: 'var(--fg3)',
|
||
marginTop: 12
|
||
}
|
||
}, t('fmtNote'))));
|
||
}
|
||
|
||
/* ───────── Redeem ───────── */
|
||
function UCRedeem({
|
||
t,
|
||
lang
|
||
}) {
|
||
const [code, setCode] = useStateU('');
|
||
const [ok, setOk] = useStateU(false);
|
||
const channels = [{
|
||
icon: 'shopping-bag',
|
||
name: t('chStore'),
|
||
sub: t('chStoreSub'),
|
||
accent: true
|
||
}, {
|
||
icon: 'credit-card',
|
||
name: 'USDT (TRC20)',
|
||
sub: t('chUsdtSub')
|
||
}, {
|
||
icon: 'send',
|
||
name: 'Telegram',
|
||
sub: '@PangolinVPN_bot',
|
||
mono: true
|
||
}, {
|
||
icon: 'message-circle',
|
||
name: 'LINE',
|
||
sub: '@pangolinvpn',
|
||
mono: true
|
||
}, {
|
||
icon: 'mail',
|
||
name: t('chEmail'),
|
||
sub: 'buy@pangolin.vpn',
|
||
mono: true
|
||
}];
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 18,
|
||
maxWidth: 680
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 25,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('navRedeem')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...ucCard,
|
||
padding: '20px 22px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14.5,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 12
|
||
}
|
||
}, t('redeemTitle')), ok ? /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 9,
|
||
color: 'var(--success)',
|
||
fontWeight: 600,
|
||
fontSize: 15,
|
||
padding: '4px 0'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: "check-circle",
|
||
size: 19,
|
||
color: "var(--success)"
|
||
}), t('redeemOk')) : /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 10
|
||
}
|
||
}, /*#__PURE__*/React.createElement("input", {
|
||
value: code,
|
||
onChange: e => setCode(e.target.value.toUpperCase()),
|
||
placeholder: t('redeemPh'),
|
||
style: {
|
||
...ucInput,
|
||
fontFamily: 'var(--font-mono)',
|
||
letterSpacing: '0.08em'
|
||
}
|
||
}), /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => code.length >= 4 && setOk(true),
|
||
style: {
|
||
border: 'none',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '0 22px',
|
||
background: 'var(--accent)',
|
||
color: '#fff',
|
||
fontWeight: 700,
|
||
fontSize: 14,
|
||
cursor: 'pointer',
|
||
flexShrink: 0
|
||
}
|
||
}, t('redeemBtn')))), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14.5,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)',
|
||
marginBottom: 5
|
||
}
|
||
}, t('buyTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
color: 'var(--fg3)',
|
||
lineHeight: 1.5,
|
||
marginBottom: 13
|
||
}
|
||
}, t('buySub')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: 'repeat(auto-fit,minmax(240px,1fr))',
|
||
gap: 11
|
||
}
|
||
}, channels.map(c => /*#__PURE__*/React.createElement("button", {
|
||
key: c.name,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
textAlign: 'left',
|
||
cursor: 'pointer',
|
||
background: c.accent ? 'var(--accent-subtle)' : 'var(--surface)',
|
||
border: `1px solid ${c.accent ? 'var(--accent-border)' : 'var(--border)'}`,
|
||
borderRadius: 'var(--radius-lg)',
|
||
padding: '13px 15px',
|
||
boxShadow: 'var(--shadow-sm)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
width: 38,
|
||
height: 38,
|
||
borderRadius: 'var(--radius-md)',
|
||
background: c.accent ? 'var(--accent)' : 'var(--bg-subtle)',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: c.icon,
|
||
size: 18,
|
||
color: c.accent ? '#fff' : 'var(--accent)'
|
||
})), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
minWidth: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 14,
|
||
fontWeight: 600,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, c.name), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 11.5,
|
||
color: 'var(--fg3)',
|
||
fontFamily: c.mono ? 'var(--font-mono)' : 'var(--font-sans)',
|
||
whiteSpace: 'nowrap',
|
||
overflow: 'hidden',
|
||
textOverflow: 'ellipsis'
|
||
}
|
||
}, c.sub)), /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: "external-link",
|
||
size: 15,
|
||
color: "var(--fg3)"
|
||
}))))));
|
||
}
|
||
|
||
/* ───────── Invite ───────── */
|
||
function UCInvite({
|
||
t,
|
||
lang
|
||
}) {
|
||
const [copied, setCopied] = useStateU(false);
|
||
const records = [{
|
||
mail: 'k***@gmail.com',
|
||
date: '2026-06-08',
|
||
paid: true
|
||
}, {
|
||
mail: 'w***@outlook.com',
|
||
date: '2026-06-05',
|
||
paid: false
|
||
}, {
|
||
mail: 'z***@qq.com',
|
||
date: '2026-05-30',
|
||
paid: true
|
||
}];
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: 18,
|
||
maxWidth: 680
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontSize: 25,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, t('inviteTitle')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 13,
|
||
color: 'var(--fg3)',
|
||
marginTop: 5,
|
||
lineHeight: 1.6
|
||
}
|
||
}, t('inviteSub'))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...ucCard,
|
||
padding: '18px 20px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12.5,
|
||
fontWeight: 600,
|
||
color: 'var(--fg2)',
|
||
marginBottom: 8
|
||
}
|
||
}, t('inviteLink')), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 10
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 9,
|
||
background: 'var(--bg-subtle)',
|
||
border: '1px solid var(--border)',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '11px 13px',
|
||
minWidth: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: "gift",
|
||
size: 15,
|
||
color: "var(--fg3)"
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 12.5,
|
||
color: 'var(--fg1)',
|
||
overflow: 'hidden',
|
||
textOverflow: 'ellipsis',
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, "https://pangolin.vpn/r/PG8F3K")), /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => {
|
||
setCopied(true);
|
||
setTimeout(() => setCopied(false), 1800);
|
||
},
|
||
style: {
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 7,
|
||
border: 'none',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '0 18px',
|
||
background: 'var(--accent)',
|
||
color: '#fff',
|
||
fontWeight: 700,
|
||
fontSize: 13.5,
|
||
cursor: 'pointer',
|
||
flexShrink: 0
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: copied ? 'check' : 'copy',
|
||
size: 15,
|
||
color: "#fff"
|
||
}), copied ? t('subCopied') : t('subCopy')))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'grid',
|
||
gridTemplateColumns: 'repeat(auto-fit,minmax(150px,1fr))',
|
||
gap: 14
|
||
}
|
||
}, [[t('invited'), '8', ''], [t('paidUsers'), '3', ''], [t('earned'), '¥45', '']].map(([l, v]) => /*#__PURE__*/React.createElement("div", {
|
||
key: l,
|
||
style: {
|
||
...ucCard,
|
||
padding: '16px 18px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontSize: 12,
|
||
color: 'var(--fg3)',
|
||
fontWeight: 600
|
||
}
|
||
}, l), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 23,
|
||
fontWeight: 500,
|
||
color: 'var(--fg1)',
|
||
marginTop: 7
|
||
}
|
||
}, v)))), /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
...ucCard,
|
||
overflow: 'hidden'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
padding: '13px 18px',
|
||
fontSize: 13.5,
|
||
fontWeight: 700,
|
||
color: 'var(--fg1)',
|
||
borderBottom: '1px solid var(--border)'
|
||
}
|
||
}, t('inviteRecord')), records.map((r, i) => /*#__PURE__*/React.createElement("div", {
|
||
key: i,
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 12,
|
||
padding: '12px 18px',
|
||
borderBottom: i < records.length - 1 ? '1px solid var(--border)' : 'none'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
flex: 1,
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 13,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, r.mail), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-mono)',
|
||
fontSize: 12,
|
||
color: 'var(--fg3)'
|
||
}
|
||
}, r.date), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontSize: 11,
|
||
fontWeight: 600,
|
||
padding: '3px 10px',
|
||
borderRadius: 999,
|
||
background: r.paid ? 'var(--success-subtle)' : 'var(--bg-subtle)',
|
||
color: r.paid ? 'var(--success)' : 'var(--fg3)'
|
||
}
|
||
}, r.paid ? t('recPaid') : t('recRegistered'))))));
|
||
}
|
||
|
||
/* ───────── App shell ───────── */
|
||
function UserCenterApp() {
|
||
const [stage, setStage] = useStateU('app'); // app | login — opens on the dashboard
|
||
const [view, setView] = useStateU('overview');
|
||
const [lang, setLang] = useStateU('zh');
|
||
const [free] = useStateU(false);
|
||
const [dir, setDir] = useStateU(0); // -1 左进 / 1 右进 / 0 无动画
|
||
const mobile = useIsMobileUC();
|
||
const t = ucT(lang);
|
||
const touchRef = React.useRef({
|
||
x: 0,
|
||
y: 0
|
||
});
|
||
const ORDER = ['overview', 'sub', 'redeem', 'invite'];
|
||
function go(id) {
|
||
const a = ORDER.indexOf(view),
|
||
b = ORDER.indexOf(id);
|
||
setDir(b > a ? 1 : b < a ? -1 : 0);
|
||
setView(id);
|
||
setTimeout(() => setDir(0), 300);
|
||
}
|
||
function onTouchStart(e) {
|
||
const p = e.touches ? e.touches[0] : e;
|
||
touchRef.current = {
|
||
x: p.clientX,
|
||
y: p.clientY
|
||
};
|
||
}
|
||
function onTouchEnd(e) {
|
||
const p = e.changedTouches ? e.changedTouches[0] : e;
|
||
const dx = p.clientX - touchRef.current.x,
|
||
dy = p.clientY - touchRef.current.y;
|
||
if (Math.abs(dx) > 60 && Math.abs(dx) > Math.abs(dy) * 1.5) {
|
||
const i = ORDER.indexOf(view);
|
||
const ni = dx < 0 ? Math.min(ORDER.length - 1, i + 1) : Math.max(0, i - 1);
|
||
if (ni !== i) go(ORDER[ni]);
|
||
}
|
||
}
|
||
if (stage === 'login') return /*#__PURE__*/React.createElement(UCLogin, {
|
||
t: t,
|
||
lang: lang,
|
||
setLang: setLang,
|
||
onDone: () => setStage('app')
|
||
});
|
||
const nav = [['overview', 'layout-dashboard', t('navOverview')], ['sub', 'link', t('navSub')], ['redeem', 'ticket', t('navRedeem')], ['invite', 'users', t('navInvite')]];
|
||
let main;
|
||
if (view === 'overview') main = /*#__PURE__*/React.createElement(UCOverview, {
|
||
t: t,
|
||
lang: lang,
|
||
free: free,
|
||
mobile: mobile,
|
||
goSub: () => setView('sub'),
|
||
goRedeem: () => setView('redeem')
|
||
});else if (view === 'sub') main = /*#__PURE__*/React.createElement(UCSub, {
|
||
t: t,
|
||
lang: lang,
|
||
mobile: mobile
|
||
});else if (view === 'redeem') main = /*#__PURE__*/React.createElement(UCRedeem, {
|
||
t: t,
|
||
lang: lang
|
||
});else main = /*#__PURE__*/React.createElement(UCInvite, {
|
||
t: t,
|
||
lang: lang
|
||
});
|
||
const navBtns = nav.map(([id, ic, l]) => /*#__PURE__*/React.createElement("button", {
|
||
key: id,
|
||
onClick: () => go(id),
|
||
style: {
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 7,
|
||
border: 'none',
|
||
cursor: 'pointer',
|
||
borderRadius: 'var(--radius-md)',
|
||
padding: '8px 13px',
|
||
flexShrink: 0,
|
||
background: view === id ? 'var(--accent-subtle)' : 'transparent',
|
||
color: view === id ? 'var(--accent)' : 'var(--fg2)',
|
||
fontFamily: 'var(--font-sans)',
|
||
fontSize: 13.5,
|
||
fontWeight: view === id ? 700 : 500,
|
||
whiteSpace: 'nowrap'
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: ic,
|
||
size: 15,
|
||
color: view === id ? 'var(--accent)' : 'var(--fg3)'
|
||
}), l));
|
||
return /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
minHeight: '100vh',
|
||
background: 'var(--bg)',
|
||
fontFamily: 'var(--font-sans)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
position: 'sticky',
|
||
top: 0,
|
||
zIndex: 10,
|
||
background: 'color-mix(in srgb, var(--bg) 85%, transparent)',
|
||
backdropFilter: 'blur(12px)',
|
||
borderBottom: '1px solid var(--border)'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
maxWidth: 1000,
|
||
margin: '0 auto',
|
||
padding: mobile ? '0 16px' : '0 24px',
|
||
height: mobile ? 54 : 60,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: mobile ? 12 : 22
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 9
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCMark, {
|
||
size: 26
|
||
}), /*#__PURE__*/React.createElement("span", {
|
||
style: {
|
||
fontFamily: 'var(--font-display)',
|
||
fontWeight: 700,
|
||
fontSize: 16.5,
|
||
color: 'var(--fg1)'
|
||
}
|
||
}, "\u7A7F\u5C71\u7532")), !mobile && /*#__PURE__*/React.createElement("nav", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 4,
|
||
flex: 1
|
||
}
|
||
}, navBtns), mobile && /*#__PURE__*/React.createElement("div", {
|
||
style: {
|
||
flex: 1
|
||
}
|
||
}), /*#__PURE__*/React.createElement(UCLang, {
|
||
lang: lang,
|
||
setLang: setLang
|
||
}), /*#__PURE__*/React.createElement("button", {
|
||
onClick: () => setStage('login'),
|
||
style: {
|
||
display: 'inline-flex',
|
||
alignItems: 'center',
|
||
gap: 7,
|
||
border: 'none',
|
||
background: 'transparent',
|
||
cursor: 'pointer',
|
||
color: 'var(--fg2)',
|
||
fontSize: 13,
|
||
fontWeight: 600,
|
||
padding: 6
|
||
}
|
||
}, /*#__PURE__*/React.createElement(UCIcon, {
|
||
name: "log-out",
|
||
size: 15,
|
||
color: "var(--fg3)"
|
||
}), !mobile && t('signOut'))), mobile && /*#__PURE__*/React.createElement("nav", {
|
||
style: {
|
||
display: 'flex',
|
||
gap: 2,
|
||
padding: '0 12px 8px',
|
||
overflowX: 'auto'
|
||
}
|
||
}, navBtns)), /*#__PURE__*/React.createElement("div", {
|
||
onTouchStart: mobile ? onTouchStart : undefined,
|
||
onTouchEnd: mobile ? onTouchEnd : undefined,
|
||
style: {
|
||
maxWidth: 1000,
|
||
margin: '0 auto',
|
||
padding: mobile ? '20px 16px 40px' : '30px 24px 48px'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("div", {
|
||
key: view,
|
||
onAnimationEnd: () => setDir(0),
|
||
style: {
|
||
animation: dir !== 0 ? `uc-in-${dir === 1 ? 'l' : 'r'} 200ms var(--ease-out)` : 'none'
|
||
}
|
||
}, main)));
|
||
}
|
||
Object.assign(window, {
|
||
UserCenterApp
|
||
});
|
||
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/usercenter/ucapp.jsx", error: String((e && e.message) || e) }); }
|
||
|
||
// ui_kits/usercenter/ucparts.jsx
|
||
try { (() => {
|
||
/* ucparts.jsx — 穿山甲 Web 用户中心 · atoms + i18n */
|
||
const UCLUCIDE = {
|
||
'layout-dashboard': '<rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/>',
|
||
link: '<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>',
|
||
ticket: '<path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"/><path d="M13 5v2"/><path d="M13 17v2"/><path d="M13 11v2"/>',
|
||
users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
|
||
copy: '<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>',
|
||
'refresh-cw': '<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M3 21v-5h5"/>',
|
||
'qr-code': '<rect width="5" height="5" x="3" y="3" rx="1"/><rect width="5" height="5" x="16" y="3" rx="1"/><rect width="5" height="5" x="3" y="16" rx="1"/><path d="M21 16h-3a2 2 0 0 0-2 2v3"/><path d="M21 21v.01"/><path d="M12 7v3a2 2 0 0 1-2 2H7"/><path d="M3 12h.01"/><path d="M12 3h.01"/><path d="M12 16v.01"/><path d="M16 12h1"/><path d="M21 12v.01"/><path d="M12 21v-1"/>',
|
||
download: '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/>',
|
||
check: '<path d="M20 6 9 17l-5-5"/>',
|
||
'check-circle': '<path d="M21.801 10A10 10 0 1 1 17 3.335"/><path d="m9 11 3 3L22 4"/>',
|
||
zap: '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
|
||
clock: '<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>',
|
||
'shield-check': '<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/>',
|
||
crown: '<path d="M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z"/><path d="M5 21h14"/>',
|
||
'credit-card': '<rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/>',
|
||
send: '<path d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"/><path d="m21.854 2.147-10.94 10.939"/>',
|
||
'message-circle': '<path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/>',
|
||
mail: '<rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/>',
|
||
'shopping-bag': '<path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 0 1-8 0"/>',
|
||
'log-out': '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/>',
|
||
'external-link': '<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>',
|
||
gift: '<rect x="3" y="8" width="18" height="4" rx="1"/><path d="M12 8v13"/><path d="M19 12v7a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-7"/><path d="M7.5 8a2.5 2.5 0 0 1 0-5A4.8 8 0 0 1 12 8a4.8 8 0 0 1 4.5-5 2.5 2.5 0 0 1 0 5"/>',
|
||
smartphone: '<rect width="14" height="20" x="5" y="2" rx="2" ry="2"/><path d="M12 18h.01"/>',
|
||
lock: '<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
|
||
'chevron-right': '<path d="m9 18 6-6-6-6"/>',
|
||
'arrow-down': '<path d="M12 5v14"/><path d="m19 12-7 7-7-7"/>'
|
||
};
|
||
function UCIcon({
|
||
name,
|
||
size = 18,
|
||
stroke = 2,
|
||
color = 'currentColor',
|
||
style = {}
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("svg", {
|
||
width: size,
|
||
height: size,
|
||
viewBox: "0 0 24 24",
|
||
fill: "none",
|
||
stroke: color,
|
||
strokeWidth: stroke,
|
||
strokeLinecap: "round",
|
||
strokeLinejoin: "round",
|
||
style: {
|
||
display: 'block',
|
||
flexShrink: 0,
|
||
...style
|
||
},
|
||
dangerouslySetInnerHTML: {
|
||
__html: UCLUCIDE[name] || ''
|
||
}
|
||
});
|
||
}
|
||
function UCMark({
|
||
size = 30,
|
||
color = 'var(--accent)'
|
||
}) {
|
||
return /*#__PURE__*/React.createElement("svg", {
|
||
width: size,
|
||
height: size,
|
||
viewBox: "0 0 96 96",
|
||
fill: "none",
|
||
style: {
|
||
display: 'block'
|
||
}
|
||
}, /*#__PURE__*/React.createElement("g", {
|
||
transform: "translate(96,0) scale(-1,1)"
|
||
}, /*#__PURE__*/React.createElement("path", {
|
||
d: "M16 59 Q17 51 26 50 Q34 31 45 30 Q55 30 62 37 Q69 41 75 45 Q85 49 89 44 Q92 50 84 52 Q75 53 67 53 Q66 62 60 62 L56 62 Q54 55 49 55 Q47 62 37 62 L33 62 Q31 56 26 56 Q20 59 16 59 Z",
|
||
fill: color
|
||
}), /*#__PURE__*/React.createElement("g", {
|
||
stroke: "#fff",
|
||
strokeWidth: "2.4",
|
||
strokeLinecap: "round",
|
||
strokeLinejoin: "round",
|
||
fill: "none",
|
||
opacity: "0.5"
|
||
}, /*#__PURE__*/React.createElement("path", {
|
||
d: "M27 50 Q35 43 43 50"
|
||
}), /*#__PURE__*/React.createElement("path", {
|
||
d: "M37 48 Q45 41 53 48"
|
||
}), /*#__PURE__*/React.createElement("path", {
|
||
d: "M47 48 Q55 41 63 49"
|
||
})), /*#__PURE__*/React.createElement("circle", {
|
||
cx: "19",
|
||
cy: "56",
|
||
r: "2.2",
|
||
fill: "#fff",
|
||
opacity: "0.85"
|
||
})));
|
||
}
|
||
const UCSTRINGS = {
|
||
navOverview: {
|
||
zh: '概览',
|
||
en: 'Overview'
|
||
},
|
||
navSub: {
|
||
zh: '订阅',
|
||
en: 'Subscription'
|
||
},
|
||
navRedeem: {
|
||
zh: '兑换 & 购买',
|
||
en: 'Redeem & buy'
|
||
},
|
||
navInvite: {
|
||
zh: '邀请返利',
|
||
en: 'Referral'
|
||
},
|
||
signOut: {
|
||
zh: '退出',
|
||
en: 'Sign out'
|
||
},
|
||
/* login */
|
||
loginTitle: {
|
||
zh: '登录用户中心',
|
||
en: 'Log in to your account'
|
||
},
|
||
loginSub: {
|
||
zh: '管理订阅、兑换激活码、查看用量',
|
||
en: 'Manage subscription, redeem codes, track usage'
|
||
},
|
||
emailLabel: {
|
||
zh: '邮箱',
|
||
en: 'Email'
|
||
},
|
||
emailPh: {
|
||
zh: '你的邮箱地址',
|
||
en: 'your@email.com'
|
||
},
|
||
pwLabel: {
|
||
zh: '密码',
|
||
en: 'Password'
|
||
},
|
||
pwPh: {
|
||
zh: '输入密码',
|
||
en: 'Enter password'
|
||
},
|
||
doLogin: {
|
||
zh: '登录',
|
||
en: 'Log in'
|
||
},
|
||
forgotPw: {
|
||
zh: '忘记密码?',
|
||
en: 'Forgot password?'
|
||
},
|
||
noAccount: {
|
||
zh: '没有账户?在 App 内注册',
|
||
en: 'No account? Sign up in the app'
|
||
},
|
||
/* overview */
|
||
greeting: {
|
||
zh: '欢迎回来',
|
||
en: 'Welcome back'
|
||
},
|
||
curPlan: {
|
||
zh: '当前套餐',
|
||
en: 'Current plan'
|
||
},
|
||
freePlan: {
|
||
zh: '免费版',
|
||
en: 'Free'
|
||
},
|
||
proMember: {
|
||
zh: 'PRO 会员',
|
||
en: 'PRO member'
|
||
},
|
||
expires: {
|
||
zh: '有效期至',
|
||
en: 'Expires'
|
||
},
|
||
renew: {
|
||
zh: '续费 / 升级',
|
||
en: 'Renew / Upgrade'
|
||
},
|
||
quotaToday: {
|
||
zh: '今日剩余时长',
|
||
en: 'Time left today'
|
||
},
|
||
dataToday: {
|
||
zh: '今日已用流量',
|
||
en: 'Data used today'
|
||
},
|
||
devices: {
|
||
zh: '在线设备',
|
||
en: 'Devices online'
|
||
},
|
||
quotaFree: {
|
||
zh: '免费版 · 每日 10 分钟',
|
||
en: 'Free · 10 min/day'
|
||
},
|
||
usageTitle: {
|
||
zh: '近 7 日流量 (GB)',
|
||
en: 'Last 7 days (GB)'
|
||
},
|
||
quickSub: {
|
||
zh: '快速操作',
|
||
en: 'Quick actions'
|
||
},
|
||
qaSub: {
|
||
zh: '获取订阅链接',
|
||
en: 'Get subscription'
|
||
},
|
||
qaRedeem: {
|
||
zh: '兑换激活码',
|
||
en: 'Redeem a code'
|
||
},
|
||
qaApp: {
|
||
zh: '下载 App',
|
||
en: 'Download apps'
|
||
},
|
||
/* subscription */
|
||
subTitle: {
|
||
zh: '我的订阅',
|
||
en: 'My subscription'
|
||
},
|
||
subDesc: {
|
||
zh: '订阅链接是你的专属凭证,泄露后他人可使用你的额度。请勿分享。',
|
||
en: 'Your subscription link is a private credential. Never share it.'
|
||
},
|
||
subCopy: {
|
||
zh: '复制链接',
|
||
en: 'Copy link'
|
||
},
|
||
subCopied: {
|
||
zh: '已复制',
|
||
en: 'Copied'
|
||
},
|
||
subReset: {
|
||
zh: '重置链接',
|
||
en: 'Reset link'
|
||
},
|
||
subResetSub: {
|
||
zh: '旧链接立即失效,所有设备需重新导入',
|
||
en: 'Old link stops working; re-import on all devices'
|
||
},
|
||
subResetOk: {
|
||
zh: '已重置,请重新导入',
|
||
en: 'Reset — re-import on your devices'
|
||
},
|
||
scanTitle: {
|
||
zh: '扫码导入',
|
||
en: 'Scan to import'
|
||
},
|
||
scanSub: {
|
||
zh: '用穿山甲 App 或三方客户端扫码',
|
||
en: 'Scan with the Pangolin app or a 3rd-party client'
|
||
},
|
||
importTitle: {
|
||
zh: '一键导入三方客户端',
|
||
en: 'One-tap import'
|
||
},
|
||
importSub: {
|
||
zh: '已安装对应客户端时,点击即自动导入订阅。',
|
||
en: 'If the client is installed, tapping imports the subscription automatically.'
|
||
},
|
||
fmtNote: {
|
||
zh: '同一链接同时兼容 sing-box / Clash.Meta / v2ray 格式。',
|
||
en: 'One link serves sing-box / Clash.Meta / v2ray formats.'
|
||
},
|
||
/* redeem */
|
||
redeemTitle: {
|
||
zh: '兑换激活码',
|
||
en: 'Redeem a code'
|
||
},
|
||
redeemPh: {
|
||
zh: '输入激活码',
|
||
en: 'Enter activation code'
|
||
},
|
||
redeemBtn: {
|
||
zh: '激活',
|
||
en: 'Redeem'
|
||
},
|
||
redeemOk: {
|
||
zh: '激活成功 · 套餐已到账',
|
||
en: 'Activated — plan applied'
|
||
},
|
||
buyTitle: {
|
||
zh: '购买渠道',
|
||
en: 'Where to buy'
|
||
},
|
||
buySub: {
|
||
zh: '本站不直接收款。通过以下渠道购买激活码,回到本页兑换即可:',
|
||
en: 'We never take payment on this site. Buy a code via a channel below, then redeem here:'
|
||
},
|
||
chStore: {
|
||
zh: '自助发卡商店',
|
||
en: 'Self-serve store'
|
||
},
|
||
chStoreSub: {
|
||
zh: '支付宝 / 微信 · 自动发码',
|
||
en: 'Alipay / WeChat · instant code'
|
||
},
|
||
chUsdtSub: {
|
||
zh: '链上转账 · 最隐私 · 自动发码',
|
||
en: 'On-chain · most private · instant code'
|
||
},
|
||
chEmail: {
|
||
zh: '邮箱客服',
|
||
en: 'Email support'
|
||
},
|
||
/* invite */
|
||
inviteTitle: {
|
||
zh: '邀请返利',
|
||
en: 'Referral'
|
||
},
|
||
inviteSub: {
|
||
zh: '好友通过你的链接注册并付费,你获得其首单 20% 余额返利,可抵扣续费。',
|
||
en: 'When a friend signs up and pays via your link, you earn 20% of their first order as credit.'
|
||
},
|
||
inviteLink: {
|
||
zh: '我的邀请链接',
|
||
en: 'My invite link'
|
||
},
|
||
invited: {
|
||
zh: '已邀请',
|
||
en: 'Invited'
|
||
},
|
||
paidUsers: {
|
||
zh: '已付费',
|
||
en: 'Converted'
|
||
},
|
||
earned: {
|
||
zh: '累计返利',
|
||
en: 'Credit earned'
|
||
},
|
||
inviteRecord: {
|
||
zh: '邀请记录',
|
||
en: 'History'
|
||
},
|
||
recEmpty: {
|
||
zh: '暂无邀请记录',
|
||
en: 'No referrals yet'
|
||
},
|
||
recRegistered: {
|
||
zh: '已注册',
|
||
en: 'Registered'
|
||
},
|
||
recPaid: {
|
||
zh: '已付费',
|
||
en: 'Paid'
|
||
},
|
||
/* 2FA */
|
||
twoFATitle: {
|
||
zh: '双重认证',
|
||
en: 'Two-factor auth'
|
||
},
|
||
twoFAHint: {
|
||
zh: '你的账户已开启双重认证。请输入身份验证器 App 中的 6 位动态码。',
|
||
en: 'Two-factor auth is on for this account. Enter the 6-digit code from your authenticator app.'
|
||
},
|
||
twoFAConfirm: {
|
||
zh: '验证并登录',
|
||
en: 'Verify & log in'
|
||
},
|
||
twoFABack: {
|
||
zh: '返回上一步',
|
||
en: 'Back'
|
||
}
|
||
};
|
||
function ucT(lang) {
|
||
return k => {
|
||
const v = UCSTRINGS[k];
|
||
return v ? v[lang] || k : k;
|
||
};
|
||
}
|
||
Object.assign(window, {
|
||
UCIcon,
|
||
UCMark,
|
||
UCSTRINGS,
|
||
ucT
|
||
});
|
||
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/usercenter/ucparts.jsx", error: String((e && e.message) || e) }); }
|
||
|
||
// ui_kits/website/site.js
|
||
try { (() => {
|
||
/* site.js — 穿山甲 VPN 官网 · i18n + 交互 */
|
||
(function () {
|
||
// ---------- i18n ----------
|
||
const I18N = {
|
||
'ann.txt': ['新用户注册即享 7 天免费试用', 'New users get a 7-day free trial'],
|
||
'ann.cta': ['立即注册 →', 'Sign up →'],
|
||
'su.ph': ['输入邮箱,免费注册', 'Enter email to sign up free'],
|
||
'su.btn': ['免费注册', 'Sign up free'],
|
||
'su.ok': ['验证码已发送!下载 App 并用同一邮箱继续完成注册。', 'Code sent! Download the app and continue with the same email.'],
|
||
'su.hint': ['免费注册 · 无需付款 · 7 天免费试用', 'Free to join · no payment · 7-day free trial'],
|
||
'su.dl': ['或直接下载 App', 'or just download the app'],
|
||
'why.eyebrow': ['免费注册', 'Sign up'],
|
||
'why.h': ['注册一次,处处可用', 'One account, everywhere'],
|
||
'why.sub': ['一个邮箱就能开始。注册免费,不需要任何付款信息。', 'All you need is an email. Free to join — no payment details, ever.'],
|
||
'why.1t': ['7 天免费试用', '7-day free trial'],
|
||
'why.1d': ['新用户注册即享 7 天免费使用,无需任何付款信息。', 'New accounts get 7 days of free, unrestricted use — no payment details.'],
|
||
'why.2t': ['多端同步', 'Multi-device sync'],
|
||
'why.2d': ['手机、平板、电脑共用一个账户,套餐与设置随身走。', 'Phone, tablet and desktop share one account — your plan follows you.'],
|
||
'why.3t': ['无需付款信息', 'No payment details'],
|
||
'why.3d': ['注册只要邮箱。想升级时,再通过外部渠道兑换激活码。', 'Just an email to join. Upgrade later with an activation code.'],
|
||
'why.s1': ['填写邮箱', 'Enter email'],
|
||
'why.s2': ['输入验证码', 'Verify code'],
|
||
'why.s3': ['开始畅连', 'Start connecting'],
|
||
'why.cta': ['立即免费注册', 'Sign up free'],
|
||
'cta.btn2': ['免费注册', 'Sign up free'],
|
||
'nav.product': ['产品', 'Product'],
|
||
'nav.pricing': ['定价', 'Pricing'],
|
||
'nav.download': ['下载', 'Download'],
|
||
'nav.docs': ['文档', 'Docs'],
|
||
'nav.blog': ['Blog', 'Blog'],
|
||
'nav.login': ['登录', 'Log in'],
|
||
'nav.get': ['立即下载', 'Get the app'],
|
||
'hero.eyebrow': ['极速 · 稳定 · 省心', 'Fast · Stable · Effortless'],
|
||
'hero.h1': ['极速畅连,\n网络如丝顺滑', 'Faster, smoother,\neverywhere'],
|
||
'hero.lede': ['轻盈、亲和、即开即用的跨平台网络加速应用。一键连接,智能选线,稳定不掉线。', 'A lightweight, friendly cross-platform network accelerator. One tap, smart routing, rock-solid.'],
|
||
'hero.cta1': ['免费下载', 'Download free'],
|
||
'hero.cta2': ['查看定价', 'See pricing'],
|
||
'hero.t1': ['80+ 全球加速线路', '80+ global routes'],
|
||
'hero.t2': ['严格无日志', 'Strict no-logs'],
|
||
'hero.t3': ['端到端加密', 'End-to-end encrypted'],
|
||
'orb.cap': ['已连接', 'CONNECTED'],
|
||
'orb.down': ['下载', 'Down'],
|
||
'orb.up': ['上传', 'Up'],
|
||
'orb.node': ['香港 · 流媒体', 'Hong Kong'],
|
||
'orb.nodesub': ['延迟 18ms · 已连接', '18ms · Connected'],
|
||
'strip.txt': ['已支持', 'Available on'],
|
||
'feat.eyebrow': ['产品', 'Product'],
|
||
'feat.h': ['为日常而生的连接体验', 'Built for everyday connection'],
|
||
'feat.sub': ['没有复杂配置,没有技术门槛。打开就用,把稳定与隐私留给我们。', 'No complex setup, no jargon. Just open and go — we handle speed and privacy.'],
|
||
'feat.1t': ['一键连接', 'One-tap connect'],
|
||
'feat.1d': ['一颗按钮,智能挑选最快线路。无需手动配置,也能秒连。', 'One button picks the fastest route. No manual setup, instant connect.'],
|
||
'feat.2t': ['智能分流', 'Smart routing'],
|
||
'feat.2d': ['按规则智能分流,该加速的加速,本地服务直连,互不打扰。', 'Smart rule-based routing — accelerate what needs it, keep local traffic direct.'],
|
||
'feat.3t': ['80+ 全球加速线路', '80+ global routes'],
|
||
'feat.3d': ['覆盖港日新美欧韩等地,视频与游戏优化线路随心选。', 'Routes across HK / JP / SG / US / EU and more, tuned for video & gaming.'],
|
||
'feat.4t': ['严格无日志', 'Strict no-logs'],
|
||
'feat.4d': ['我们不记录你的浏览数据。隐私是底线,不是卖点。', 'We never log your browsing. Privacy is the baseline, not a feature.'],
|
||
'feat.5t': ['Kill Switch', 'Kill Switch'],
|
||
'feat.5d': ['一旦连接中断,立即阻断网络,杜绝真实 IP 泄露。', 'If the tunnel drops, traffic is blocked instantly — no IP leaks.'],
|
||
'feat.6t': ['多端同步', 'Multi-device'],
|
||
'feat.6d': ['一个账户,手机、平板、电脑同时在线,最多 5 台设备。', 'One account across phone, tablet and desktop — up to 5 devices.'],
|
||
'price.eyebrow': ['定价', 'Pricing'],
|
||
'price.h': ['简单透明的价格', 'Simple, honest pricing'],
|
||
'price.sub': ['随时升级或取消。所有套餐均含核心加密与无日志承诺。', 'Upgrade or cancel anytime. Every plan includes core encryption and our no-logs promise.'],
|
||
'price.monthly': ['按月', 'Monthly'],
|
||
'price.yearly': ['按年', 'Yearly'],
|
||
'price.save': ['省 20%', 'Save 20%'],
|
||
'price.permo': ['/月', '/mo'],
|
||
'price.free': ['免费版', 'Free'],
|
||
'price.freedesc': ['适合轻度使用与尝鲜', 'For light, casual use'],
|
||
'price.pro': ['专业版', 'Pro'],
|
||
'price.prodesc': ['个人用户的最佳选择', 'Best for individuals'],
|
||
'price.team': ['团队版', 'Team'],
|
||
'price.teamdesc': ['小团队集中管理', 'For small teams'],
|
||
'price.popular': ['最受欢迎', 'Most popular'],
|
||
'price.cta_free': ['免费下载', 'Download free'],
|
||
'price.cta_pro': ['获取激活码', 'Get a code'],
|
||
'price.cta_team': ['获取激活码', 'Get a code'],
|
||
'pf.free1': ['每日 10 分钟时长', '10 min per day'],
|
||
'pf.free2': ['仅 1 个基础节点', '1 basic node only'],
|
||
'pf.free3': ['使用前观看广告', 'Watch an ad to start'],
|
||
'pf.free4': ['核心加密 · 无日志', 'Core encryption · no-logs'],
|
||
'pf.pro1': ['80+ 全球加速线路', '80+ global routes'],
|
||
'pf.pro2': ['无限流量 · 极速', 'Unlimited · top speed'],
|
||
'pf.pro3': ['5 台设备同时在线', '5 devices at once'],
|
||
'pf.pro4': ['流媒体 & P2P 优化', 'Streaming & P2P routes'],
|
||
'pf.pro5': ['Kill Switch · 智能分流', 'Kill Switch · smart routing'],
|
||
'pf.team1': ['专业版全部功能', 'Everything in Pro'],
|
||
'pf.team2': ['10 个成员席位', '10 seats'],
|
||
'pf.team3': ['集中计费与管理', 'Central billing & admin'],
|
||
'pf.team4': ['优先客服', 'Priority support'],
|
||
'pay.title': ['为什么网页上没有支付按钮?', 'Why no checkout on this page?'],
|
||
'pay.body': ['出于风控与隐私考虑,穿山甲不在网页或 App 内直接收款。你可以通过以下任一渠道获取激活码,在客户端内兑换即可开通。', 'For risk and privacy reasons, Pangolin never takes payment in the web or app. Get an activation code through any channel below, then redeem it in the client.'],
|
||
'pay.store': ['自助发卡商店', 'Self-serve store'],
|
||
'pay.usdt': ['USDT (TRC20) · 最隐私', 'USDT (TRC20) · most private'],
|
||
'cmp.h': ['功能对比', 'Compare plans'],
|
||
'cmp.feature': ['功能', 'Feature'],
|
||
'cmp.locations': ['可用线路', 'Routes'],
|
||
'cmp.data': ['流量', 'Data'],
|
||
'cmp.time': ['每日时长', 'Daily time'],
|
||
'cmp.time_free': ['10 分钟/天(试用期不限)', '10 min/day (trial: unlimited)'],
|
||
'cmp.devices': ['设备数', 'Devices'],
|
||
'cmp.speed': ['极速线路', 'Top-speed routes'],
|
||
'cmp.stream': ['视频优化', 'Video routes'],
|
||
'cmp.kill': ['Kill Switch', 'Kill Switch'],
|
||
'cmp.support': ['客服', 'Support'],
|
||
'cmp.unlimited': ['无限', 'Unlimited'],
|
||
'cmp.daily': ['不限', 'Unlimited'],
|
||
'cmp.basic': ['标准', 'Standard'],
|
||
'cmp.priority': ['优先', 'Priority'],
|
||
'dl.eyebrow': ['下载', 'Download'],
|
||
'dl.h': ['全平台,随处可用', 'Every platform, everywhere'],
|
||
'dl.sub': ['一个账户,所有设备同步。下载即用,无需配置。', 'One account syncs every device. Download and go.'],
|
||
'dl.get': ['下载', 'Download'],
|
||
'docs.eyebrow': ['文档', 'Docs'],
|
||
'docs.h': ['需要帮助?都在这里', 'Need help? It’s all here'],
|
||
'docs.sub': ['从快速上手到协议细节,清晰直接。', 'From quickstart to protocol details — clear and direct.'],
|
||
'docs.1t': ['快速开始', 'Quickstart'],
|
||
'docs.1d': ['三分钟完成注册、下载与首次连接。', 'Sign up, download and connect in three minutes.'],
|
||
'docs.2t': ['常见问题', 'FAQ'],
|
||
'docs.2d': ['连接、计费、设备与兑换码的常见疑问。', 'Connection, billing, devices and redeem codes.'],
|
||
'docs.3t': ['协议与安全', 'Protocol & security'],
|
||
'docs.3d': ['WireGuard、加密方式与无日志架构说明。', 'WireGuard, encryption and our no-logs architecture.'],
|
||
'docs.4t': ['隐私政策', 'Privacy policy'],
|
||
'docs.4d': ['我们收集什么、不收集什么,一目了然。', 'Exactly what we collect — and what we never do.'],
|
||
'docs.read': ['阅读', 'Read'],
|
||
'blog.eyebrow': ['Blog', 'Blog'],
|
||
'blog.h': ['来自团队的最新动态', 'Latest from the team'],
|
||
'blog.sub': ['产品更新、安全科普与节点公告。', 'Product updates, security explainers and node news.'],
|
||
'blog.1tag': ['产品更新', 'Product'],
|
||
'blog.1t': ['v2.4:更快的智能分流与全新统计页', 'v2.4: faster smart routing & a new stats tab'],
|
||
'blog.1d': ['本次更新重写了分流引擎,连接建立速度提升约 40%。', 'We rewrote the routing engine — connections are ~40% faster.'],
|
||
'blog.2tag': ['安全', 'Security'],
|
||
'blog.2t': ['无日志到底意味着什么?', 'What “no-logs” really means'],
|
||
'blog.2d': ['我们逐条拆解收集与不收集的数据,以及背后的架构。', 'A line-by-line look at what we keep — and what we never touch.'],
|
||
'blog.3tag': ['公告', 'News'],
|
||
'blog.3t': ['新增首尔与法兰克福高速节点', 'New high-speed nodes: Seoul & Frankfurt'],
|
||
'blog.3d': ['两条新线路已上线,为东北亚与欧洲用户带来更低延迟。', 'Two new routes are live, cutting latency for NE Asia and Europe.'],
|
||
'blog.readmore': ['阅读全文', 'Read more'],
|
||
'cta.h': ['准备好体验极速畅连了吗?', 'Ready for a faster connection?'],
|
||
'cta.p': ['免费下载,数分钟内开始你的第一次极速连接。', 'Download free and get connected in minutes.'],
|
||
'cta.btn': ['免费下载', 'Download free'],
|
||
'ft.tag': ['极简、轻量、亲和的跨平台网络加速应用。守护你的每一次连接。', 'A minimal, friendly cross-platform network accelerator. Protecting every connection.'],
|
||
'ft.product': ['产品', 'Product'],
|
||
'ft.resources': ['资源', 'Resources'],
|
||
'ft.contact': ['联系与渠道', 'Contact'],
|
||
'ft.features': ['功能', 'Features'],
|
||
'ft.pricing': ['定价', 'Pricing'],
|
||
'ft.download': ['下载', 'Download'],
|
||
'ft.docs': ['文档', 'Docs'],
|
||
'ft.blog': ['Blog', 'Blog'],
|
||
'ft.faq': ['常见问题', 'FAQ'],
|
||
'ft.privacy': ['隐私政策', 'Privacy'],
|
||
'ft.status': ['服务状态', 'Status'],
|
||
'ft.hours': ['服务时间 每日 9:00–24:00 (GMT+8)', 'Hours: Daily 9:00–24:00 (GMT+8)'],
|
||
'ft.copy': ['© 2026 穿山甲 · Pangolin', '© 2026 Pangolin'],
|
||
'ft.madenote': ['App 内不支持直接支付 · 资金流全走外部渠道', 'No in-app payment · all purchases via external channels']
|
||
};
|
||
let lang = localStorage.getItem('pg_site_lang') || 'zh';
|
||
function applyLang() {
|
||
const i = lang === 'zh' ? 0 : 1;
|
||
document.documentElement.lang = lang === 'zh' ? 'zh' : 'en';
|
||
document.querySelectorAll('[data-i18n]').forEach(el => {
|
||
const k = el.getAttribute('data-i18n');
|
||
if (I18N[k]) el.textContent = I18N[k][i];
|
||
});
|
||
document.querySelectorAll('[data-i18n-ph]').forEach(el => {
|
||
const k = el.getAttribute('data-i18n-ph');
|
||
if (I18N[k]) el.placeholder = I18N[k][i];
|
||
});
|
||
document.querySelectorAll('.langseg button').forEach(b => b.classList.toggle('on', b.dataset.lang === lang));
|
||
}
|
||
window.__setLang = function (l) {
|
||
lang = l;
|
||
localStorage.setItem('pg_site_lang', l);
|
||
applyLang();
|
||
};
|
||
|
||
// ---------- pricing monthly/yearly ----------
|
||
const PRICES = {
|
||
free: ['¥0', '¥0'],
|
||
pro: ['¥25', '¥20'],
|
||
team: ['¥99', '¥79']
|
||
};
|
||
let cycle = 'monthly';
|
||
window.__setCycle = function (c) {
|
||
cycle = c;
|
||
const i = c === 'monthly' ? 0 : 1;
|
||
document.querySelectorAll('[data-price]').forEach(el => {
|
||
const p = el.getAttribute('data-price');
|
||
el.firstChild.textContent = PRICES[p][i];
|
||
});
|
||
document.querySelectorAll('.price-toggle button').forEach(b => b.classList.toggle('on', b.dataset.cycle === c));
|
||
};
|
||
|
||
// ---------- signup (demo: real impl sends a verification code) ----------
|
||
window.__signup = function (e) {
|
||
e.preventDefault();
|
||
const em = document.getElementById('su-email');
|
||
if (!/\S+@\S+\.\S+/.test(em.value)) {
|
||
em.focus();
|
||
return false;
|
||
}
|
||
document.getElementById('su-ok').hidden = false;
|
||
return false;
|
||
};
|
||
|
||
// ---------- announcement bar ----------
|
||
window.__closeAnn = function () {
|
||
const a = document.getElementById('ann');
|
||
if (a) a.remove();
|
||
};
|
||
|
||
// ---------- mobile menu ----------
|
||
window.__toggleMenu = function () {
|
||
document.getElementById('mnav').classList.toggle('open');
|
||
};
|
||
|
||
// ---------- scroll glass header ----------
|
||
function onScroll() {
|
||
document.querySelector('.hdr').classList.toggle('scrolled', window.scrollY > 8);
|
||
}
|
||
window.addEventListener('scroll', onScroll, {
|
||
passive: true
|
||
});
|
||
|
||
// ---------- init ----------
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
applyLang();
|
||
onScroll();
|
||
// close mobile menu when a link is tapped
|
||
document.querySelectorAll('#mnav a').forEach(a => a.addEventListener('click', () => document.getElementById('mnav').classList.remove('open')));
|
||
if (window.lucide) lucide.createIcons();
|
||
});
|
||
})();
|
||
})(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/website/site.js", error: String((e && e.message) || e) }); }
|
||
|
||
})();
|