Merge upstream main (3.0.0) into feature/adguard

Upstream rewrote install.sh to 3.0.0. Re-apply the fork installer changes on
top: point at the NeoTachyon Gitea repo (REPO_HOST/OWNER/NAME), use the Gitea
API base for release resolution and support optional GITEA_USER/TOKEN auth for
the API and downloads.
This commit is contained in:
2026-09-17 20:48:47 +03:00
43 changed files with 6065 additions and 7348 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+11 -11
View File
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: TACHYON\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-17 13:43+0300\n"
"PO-Revision-Date: 2026-09-17 13:43+0300\n"
"POT-Creation-Date: 2026-09-17 19:42+0300\n"
"PO-Revision-Date: 2026-09-17 19:42+0300\n"
"Last-Translator: Dushnilin\n"
"Language-Team: none\n"
"Language: ru\n"
@@ -273,10 +273,10 @@ msgid "Add URLs or local paths to .lst lists containing domains. IP entries are
msgstr "Добавьте URL или локальные пути к спискам .lst, содержащим домены. IP-записи игнорируются."
msgid "Add URLs or local paths to .srs / .json / .lst / .txt lists. Only domain rules are supported."
msgstr ""
msgstr "Укажите URL или локальные пути к спискам .srs / .json / .lst / .txt. Поддерживаются только правила для доменов."
msgid "Add URLs or local paths to .srs / .json / .lst / .txt lists. Subnets are ignored by default."
msgstr ""
msgstr "Укажите URL или локальные пути к спискам .srs / .json / .lst / .txt. Подсети по умолчанию игнорируются."
msgid "Add URLTest / Priority group"
msgstr "Добавить группу URLTest / Приоритет"
@@ -1395,7 +1395,7 @@ msgid "Direct connection via ISP"
msgstr "Прямое подключение через провайдера"
msgid "Direct Proxy"
msgstr "Прямой прокси"
msgstr "Direct Proxy"
msgid "Direct WAN"
msgstr "Прямой WAN"
@@ -1440,7 +1440,7 @@ msgid "DNS Blocking"
msgstr "Блокировка DNS"
msgid "DNS Cache Size"
msgstr ""
msgstr "Размер кэша DNS"
msgid "DNS Check Interval"
msgstr "Интервал проверки DNS"
@@ -1797,7 +1797,7 @@ msgid "Enabled (always prioritize)"
msgstr "Включено (всегда повышать приоритет)"
msgid "Enables high-capacity local DNS caching in dnsmasq to accelerate DNS queries and absorb transient latency during service restarts."
msgstr ""
msgstr "Включает емкий локальный кэш DNS в dnsmasq для ускорения запросов и устранения задержек при перезапуске служб."
msgid "Enables the background daemon that polls Telegram for commands and sends notifications."
msgstr "Включает фоновый демон, который опрашивает Telegram на наличие команд и отправляет уведомления."
@@ -3102,7 +3102,7 @@ msgid "Local Address"
msgstr "Локальный адрес"
msgid "Local DNS Cache"
msgstr ""
msgstr "Локальный кэш DNS"
msgid "Local SOCKS proxy desynchronizer (ciadpi)"
msgstr "Локальный десинхронизатор SOCKS-прокси (ciadpi)"
@@ -3480,7 +3480,7 @@ msgid "Notify on Subscription Updates"
msgstr "Уведомлять об обновлениях подписок"
msgid "Number of cached DNS domain entries in dnsmasq (default: 10000)."
msgstr ""
msgstr "Количество кэшируемых доменных записей в dnsmasq (по умолчанию: 10000)."
msgid "Number of consecutive failed checks required before switching DNS servers."
msgstr "Количество последовательных неудачных проверок до переключения DNS-серверов."
@@ -4287,7 +4287,7 @@ msgid "Rule Name"
msgstr "Название правила"
msgid "Rule set must be an HTTP(S) URL or a local .srs / .json / .lst / .txt path"
msgstr ""
msgstr "Набор правил должен быть URL HTTP(S) или локальным путем к .srs / .json / .lst / .txt"
msgid "Rule set settings"
msgstr "Настройки набора правил"
@@ -5997,7 +5997,7 @@ msgid "Add URLs or local paths to .srs / .json lists. Subnets are ignored by def
msgstr "Добавьте URL или локальные пути к спискам .srs / .json. Подсети игнорируются по умолчанию."
msgid "Plain .lst / .txt lists belong in \"Domain and IP lists\", not \"Rule sets\""
msgstr ""
msgstr "Текстовые списки .lst / .txt относятся к «Спискам доменов и IP», а не к «Наборам правил»"
msgid "Rule set must be an HTTP(S) URL or a local .srs / .json path"
msgstr "Набор правил должен быть HTTP(S) URL или локальным путём .srs / .json"
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "fe-app-tachyon",
"version": "1.3.28",
"version": "1.3.29",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "fe-app-tachyon",
"version": "1.3.28",
"version": "1.3.29",
"license": "MIT",
"devDependencies": {
"@babel/parser": "7.28.4",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "fe-app-tachyon",
"version": "1.3.28",
"version": "1.3.29",
"license": "MIT",
"type": "module",
"scripts": {
@@ -47,6 +47,19 @@ interface IRenderSectionsProps {
testingOutboundCodes?: Record<string, boolean>;
}
const CONNECTION_ACTION_LABELS: Record<string, string> = {
awg: 'AmneziaWG',
warp: 'WARP',
anytls: 'AnyTLS',
snell: 'Snell',
mieru: 'Mieru',
sudoku: 'Sudoku',
masque: 'MASQUE',
openvpn: 'OpenVPN',
fptn: 'FPTN',
vpn: 'VPN',
};
function renderFailedState() {
return E(
'div',
@@ -529,89 +542,88 @@ function renderDefaultState({
.join(' ');
if (isConnectionNode) {
const statusText = latencyFetching
? _('Checking...')
: outbound.latency === -1 || outbound.latency < 0
? _('Not responding')
: outbound.runtimeAvailable
? _('Connected')
: _('Not connected');
const statusColor = latencyFetching
? 'var(--warn-color-medium, orange)'
: outbound.latency === -1 || outbound.latency < 0
? 'var(--error-color-medium, red)'
: outbound.runtimeAvailable
? 'var(--success-color-medium, green)'
: 'var(--error-color-medium, red)';
const latencyDisplay =
outbound.latency && outbound.latency > 0
? `${outbound.latency} ms`
: '';
return E(
'div',
{
class: className,
style:
'display: flex; align-items: center; justify-content: space-between; padding: 12px; min-width: 0; gap: 16px;',
'display: flex; flex-direction: column; justify-content: space-between; padding: 12px; min-width: 0;',
},
[
E(
'div',
{
style:
'display: flex; align-items: center; gap: 12px; min-width: 0;',
class: 'tachyon_dashboard-page__outbound-grid__item__header',
style: 'margin-bottom: 8px;',
},
[
E(
'b',
{
style:
'overflow-wrap: anywhere; word-break: break-all; min-width: 0;',
},
renderFlagEmojis(outbound.displayName),
),
outbound.prefix
? E(
'span',
{
class:
'tachyon_dashboard-page__outbound-grid__item__prefix-badge',
},
renderFlagEmojis(outbound.prefix),
)
: '',
E(
'span',
{
style:
'opacity: 0.7; font-size: 13px; white-space: nowrap; flex-shrink: 0;',
},
[formatOutboundType(outbound.type, outbound.transport)].filter(
Boolean,
),
),
E(
'div',
{
class: getLatencyClass(),
style: 'white-space: nowrap; flex-shrink: 0;',
class:
'tachyon_dashboard-page__outbound-grid__item__title-wrapper',
},
connectionStatusText,
[
E(
'span',
{
style: `color: ${statusColor}; font-weight: 500; font-size: 13px;`,
},
statusText,
),
],
),
],
),
E(
'button',
'div',
{
type: 'button',
class: 'btn dashboard-sections-grid-item-test-latency',
style: 'padding: 4px 12px; height: 30px; flex-shrink: 0;',
disabled: latencyFetching ? true : undefined,
click: (event: MouseEvent) => {
event.preventDefault();
event.stopPropagation();
if (latencyFetching) return;
testLatency();
},
class: 'tachyon_dashboard-page__outbound-grid__item__footer',
style:
'display: flex; align-items: center; justify-content: space-between; gap: 8px;',
},
latencyFetching
? [
renderLoaderCircleIcon24(),
E(
'span',
[
E(
'div',
{
class: 'tachyon_dashboard-page__outbound-grid__item__type',
style:
'overflow: hidden; text-overflow: ellipsis; white-space: nowrap;',
},
renderFlagEmojis(outbound.displayName),
),
latencyDisplay
? E(
'div',
{
class: 'dashboard-sections-grid-item-test-latency__label',
class: getLatencyClass(),
style: 'white-space: nowrap; flex-shrink: 0;',
},
_('Checking...'),
),
]
: E(
'span',
{ class: 'dashboard-sections-grid-item-test-latency__label' },
_('Check Connection'),
),
latencyDisplay,
)
: '',
],
),
],
);
@@ -817,26 +829,6 @@ function renderDefaultState({
);
}
if (isConnectionNode) {
return E(
'div',
{
class: 'tachyon_dashboard-page__outbound-section',
style: 'border: none; padding: 0;',
},
[
E(
'div',
{
style:
'display: flex; flex-direction: column; gap: 8px; padding: 0;',
},
[...section.outbounds.map((outbound) => renderOutbound(outbound))],
),
],
);
}
const metadataNodes = (section.subscriptionMetadata || [])
.map((metadata) => renderSubscriptionMetadata(metadata))
.filter(Boolean) as HTMLElement[];
@@ -907,6 +899,11 @@ function renderDefaultState({
'openvpn',
].includes(section.action || '');
const connectionTitle =
(section.action &&
CONNECTION_ACTION_LABELS[section.action]) ||
selectedOutbound.displayName;
function getLatencyColor() {
if (isConnectionNode) {
if (latencyFetching)
@@ -974,7 +971,10 @@ function renderDefaultState({
E(
'span',
{ style: 'opacity: 0.7;' },
selectedOutbound.displayName,
isConnectionNode
? connectionTitle
: selectedOutbound.prefix ||
selectedOutbound.displayName,
),
latencyText
? E(
@@ -986,7 +986,32 @@ function renderDefaultState({
],
);
})()
: '',
: isConnectionNode
? (() => {
const selectedOutbound = section.outbounds.find(
(o) => o.selected,
);
if (!selectedOutbound) return '';
const connectionTitle =
(section.action &&
CONNECTION_ACTION_LABELS[section.action]) ||
selectedOutbound.displayName;
if (
section.displayName.toLowerCase().trim() ===
connectionTitle.toLowerCase().trim()
) {
return '';
}
return E(
'span',
{
style:
'font-size: 13px; font-weight: normal; margin-left: 8px; opacity: 0.7;',
},
connectionTitle,
);
})()
: '',
],
),
E(
@@ -527,4 +527,81 @@ describe('renderSections', () => {
expect(prefixBadge).not.toBeNull();
expect(prefixBadge?.textContent).toBe('Geodema Network');
});
it('renders connection node (AWG) with collapsible header and check connection action', () => {
const section: any = {
sectionName: 'AWG',
displayName: 'AMNEZIA',
action: 'awg',
outbounds: [
{
code: 'awg-out',
displayName: 'AmneziaWG 3.0',
latency: 155,
runtimeAvailable: true,
selected: true,
},
],
};
// 1. Collapsed state
const collapsedEl = renderSections({
loading: false,
failed: false,
section,
isCollapsed: true,
onTestLatency: vi.fn(),
onChooseOutbound: vi.fn(),
onCopyOutbound: vi.fn(),
onShowUrlTestInfo: vi.fn(),
onShowPriorityInfo: vi.fn(),
onUpdateSubscription: vi.fn(),
latencyFetching: false,
subscriptionUpdating: false,
});
const titleSec = collapsedEl.querySelector(
'.tachyon_dashboard-page__outbound-section__title-section',
);
expect(titleSec).not.toBeNull();
expect(titleSec?.textContent).toContain('AMNEZIA');
expect(titleSec?.textContent).toContain('AmneziaWG');
expect(titleSec?.textContent).toContain('155ms');
expect(titleSec?.textContent).toContain('Check Connection');
expect(
collapsedEl.querySelector('.tachyon_dashboard-page__outbound-grid'),
).toBeNull();
// 2. Expanded state
const expandedEl = renderSections({
loading: false,
failed: false,
section,
isCollapsed: false,
onTestLatency: vi.fn(),
onChooseOutbound: vi.fn(),
onCopyOutbound: vi.fn(),
onShowUrlTestInfo: vi.fn(),
onShowPriorityInfo: vi.fn(),
onUpdateSubscription: vi.fn(),
latencyFetching: false,
subscriptionUpdating: false,
});
const expandedTitleSec = expandedEl.querySelector(
'.tachyon_dashboard-page__outbound-section__title-section',
);
expect(expandedTitleSec).not.toBeNull();
expect(expandedTitleSec?.textContent).toContain('AMNEZIA');
expect(expandedTitleSec?.textContent).toContain('AmneziaWG');
expect(expandedTitleSec?.textContent).toContain('Check Connection');
const grid = expandedEl.querySelector(
'.tachyon_dashboard-page__outbound-grid',
);
expect(grid).not.toBeNull();
expect(grid?.textContent).toContain('Connected');
expect(grid?.textContent).toContain('AmneziaWG 3.0');
expect(grid?.textContent).toContain('155 ms');
});
});