fix(frontend): improve dense QR readability (#6396)

Dense AmneziaWG configs crossed a QR version boundary at the fixed display size, and the generated symbol had no quiet zone. Use low error correction and a four-module margin to reduce module density while keeping the payload unchanged.
This commit is contained in:
dawn
2026-09-03 22:32:27 +08:00
committed by GitHub
parent e95fe80fc4
commit 04e8458054
2 changed files with 91 additions and 0 deletions
@@ -140,6 +140,8 @@ export default function QrPanel({
className="qr-code"
value={value}
size={size}
errorLevel="L"
marginSize={4}
type="svg"
bordered={false}
color="#000000"
@@ -0,0 +1,89 @@
import { render } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { genAmneziaWGConfig } from '@/lib/xray/inbound-link';
import QrPanel from '@/pages/inbounds/qr/QrPanel';
import { AmneziawgInboundSettingsSchema } from '@/schemas/protocols/inbound/amneziawg';
const KEY = `${'A'.repeat(43)}=`;
function awgConfig(disableCookies: boolean): string {
const settings = AmneziawgInboundSettingsSchema.parse({
server: {
publicKey: KEY,
mtu: 1420,
primaryDns: '8.8.8.8',
secondaryDns: '8.8.4.4',
jc: 4,
jmin: 65,
jmax: 220,
s1: 87,
s2: 44,
s3: 21,
s4: 19,
h1: '462980921-463150218',
h2: '1177681572-1177787900',
h3: '1907413509-1907903969',
h4: '2029908558-2030313135',
i1: '<r 148>',
headerProtectionKey: KEY,
contentPaddingAddition: '17-49',
rekeyAfterTime: '111-139',
rekeyTimeout: '4-7',
rejectAfterTime: '187-251',
keepaliveTimeout: '9-14',
maxHandshakeAttempts: '19-36',
randomTrailers: true,
disableCookies,
},
clients: [
{
email: 'my-client',
privateKey: KEY,
preSharedKey: KEY,
allowedIPs: ['10.8.1.2/32'],
keepAlive: 25,
},
],
});
return genAmneziaWGConfig({
settings,
address: 'your-server.example.com',
port: 443,
remark: 'my-client',
peerIndex: 0,
});
}
function qrGeometry(value: string): { viewBox: string; foreground: string } {
const { container } = render(<QrPanel value={value} />);
const svg = container.querySelector('.qr-code svg');
const paths = svg?.querySelectorAll('path');
expect(svg).not.toBeNull();
expect(paths).toHaveLength(2);
return {
viewBox: svg?.getAttribute('viewBox') ?? '',
foreground: paths?.item(1).getAttribute('d') ?? '',
};
}
describe('QrPanel dense AmneziaWG config', () => {
it('keeps the complete 3.1 config readable across the DisableCookies QR boundary', () => {
const complete = awgConfig(true);
const withoutDisableCookies = awgConfig(false);
expect(complete).toContain('DisableCookies = on\n');
expect(complete.length - withoutDisableCookies.length).toBe(20);
const completeQr = qrGeometry(complete);
const shorterQr = qrGeometry(withoutDisableCookies);
expect(completeQr.viewBox).toBe('0 0 105 105');
expect(shorterQr.viewBox).toBe('0 0 101 101');
expect(completeQr.foreground).toMatch(/^M4 4h7/);
expect(shorterQr.foreground).toMatch(/^M4 4h7/);
});
});