@pellux/goodvibes-sdk 0.18.30 → 0.18.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/platform/pairing/qr-generator.d.ts +1 -6
- package/dist/_internal/platform/pairing/qr-generator.d.ts.map +1 -1
- package/dist/_internal/platform/pairing/qr-generator.js +24 -424
- package/dist/_internal/platform/pairing/vendor/qrcodegen.d.ts +66 -0
- package/dist/_internal/platform/pairing/vendor/qrcodegen.d.ts.map +1 -0
- package/dist/_internal/platform/pairing/vendor/qrcodegen.js +854 -0
- package/dist/_internal/platform/version.js +1 -1
- package/package.json +40 -40
|
@@ -4,17 +4,12 @@ export interface QrMatrix {
|
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
6
|
* Generate a QR code matrix for the given data string.
|
|
7
|
-
*
|
|
7
|
+
* Uses the Nayuki QR Code generator library (pure TypeScript, vendored).
|
|
8
8
|
*/
|
|
9
9
|
export declare function generateQrMatrix(data: string): QrMatrix;
|
|
10
10
|
/**
|
|
11
11
|
* Render a QR matrix to a Unicode block string suitable for terminal output.
|
|
12
12
|
* Uses half-block characters to pack 2 rows into 1 terminal row.
|
|
13
|
-
*
|
|
14
|
-
* - Full block `█` = dark top + dark bottom
|
|
15
|
-
* - Upper half `▀` = dark top + light bottom
|
|
16
|
-
* - Lower half `▄` = light top + dark bottom
|
|
17
|
-
* - Space ` ` = light top + light bottom
|
|
18
13
|
*/
|
|
19
14
|
export declare function renderQrToString(matrix: QrMatrix): string;
|
|
20
15
|
//# sourceMappingURL=qr-generator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr-generator.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/pairing/qr-generator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"qr-generator.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/pairing/qr-generator.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,EAAE,CAAC;CACxC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAcvD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAgBzD"}
|
|
@@ -1,433 +1,33 @@
|
|
|
1
|
-
import { execSync } from 'node:child_process';
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* QR Code generation using the vendored Nayuki QR Code generator (MIT license).
|
|
3
|
+
* Pure TypeScript, no npm dependencies.
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// where each character is one module.
|
|
13
|
-
// We request -m 0 (no margin) and -s 1 for raw matrix output.
|
|
14
|
-
const output = execSync(`qrencode -m 0 -t ASC ${JSON.stringify(data)}`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000 });
|
|
15
|
-
const rawLines = output.split('\n').filter((l) => l.length > 0);
|
|
16
|
-
if (rawLines.length === 0)
|
|
17
|
-
return null;
|
|
18
|
-
// qrencode ASC mode: each module is 2 characters wide ('##' dark, ' ' light)
|
|
19
|
-
const modules = rawLines.map((line) => {
|
|
20
|
-
const row = [];
|
|
21
|
-
for (let i = 0; i < line.length; i += 2) {
|
|
22
|
-
const pair = line.slice(i, i + 2);
|
|
23
|
-
row.push(pair === '##');
|
|
24
|
-
}
|
|
25
|
-
return row;
|
|
26
|
-
});
|
|
27
|
-
const size = modules[0]?.length ?? 0;
|
|
28
|
-
// Normalize all rows to the same size
|
|
29
|
-
const normalized = modules.map((row) => {
|
|
30
|
-
while (row.length < size)
|
|
31
|
-
row.push(false);
|
|
32
|
-
return row.slice(0, size);
|
|
33
|
-
});
|
|
34
|
-
return { size, modules: normalized };
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Minimal fallback QR matrix generator using a tiny Reed-Solomon QR implementation.
|
|
42
|
-
* Supports QR versions 1-9 (up to ~134 bytes, ECC level M).
|
|
43
|
-
*
|
|
44
|
-
* This is a self-contained implementation suitable for short connection strings.
|
|
45
|
-
*/
|
|
46
|
-
function generateQrMatrixMinimal(data) {
|
|
47
|
-
const bytes = new TextEncoder().encode(data);
|
|
48
|
-
const len = bytes.length;
|
|
49
|
-
// Version selection table: [version, dataCapacityBytes at ECC-M]
|
|
50
|
-
// Source: QR code spec Table 9
|
|
51
|
-
const versionTable = [
|
|
52
|
-
[1, 16], [2, 28], [3, 44], [4, 64], [5, 86],
|
|
53
|
-
[6, 108], [7, 124], [8, 154], [9, 182], [10, 216],
|
|
54
|
-
];
|
|
55
|
-
let version = 0;
|
|
56
|
-
for (const [v, cap] of versionTable) {
|
|
57
|
-
if (len <= cap) {
|
|
58
|
-
version = v;
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (version === 0) {
|
|
63
|
-
throw new Error(`Data too long for minimal QR generator (${len} bytes, max 216)`);
|
|
64
|
-
}
|
|
65
|
-
// Total codewords and ECC codewords per block (ECC level M)
|
|
66
|
-
// [version]: [totalCodewords, eccCodewords, blocks]
|
|
67
|
-
const eccTable = {
|
|
68
|
-
1: [26, 10, 1], 2: [44, 16, 1], 3: [70, 26, 2],
|
|
69
|
-
4: [100, 36, 2], 5: [134, 48, 2], 6: [172, 64, 4],
|
|
70
|
-
7: [196, 72, 4], 8: [242, 88, 4], 9: [292, 110, 5],
|
|
71
|
-
10: [346, 130, 5],
|
|
72
|
-
};
|
|
73
|
-
const [totalCodewords, eccPerBlock, numBlocks] = eccTable[version];
|
|
74
|
-
const dataCodewords = totalCodewords - (eccPerBlock * numBlocks);
|
|
75
|
-
// Build data bit stream: mode (byte=0100), char count, data, terminator, padding
|
|
76
|
-
const bits = [];
|
|
77
|
-
function pushBits(value, count) {
|
|
78
|
-
for (let i = count - 1; i >= 0; i--) {
|
|
79
|
-
bits.push((value >> i) & 1);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
// Mode indicator: byte mode = 0100
|
|
83
|
-
pushBits(0b0100, 4);
|
|
84
|
-
// Character count indicator (version 1-9: 8 bits for byte mode)
|
|
85
|
-
pushBits(len, 8);
|
|
86
|
-
// Data bytes
|
|
87
|
-
for (const b of bytes)
|
|
88
|
-
pushBits(b, 8);
|
|
89
|
-
// Terminator (up to 4 zero bits)
|
|
90
|
-
const maxBits = dataCodewords * 8;
|
|
91
|
-
for (let i = 0; i < 4 && bits.length < maxBits; i++)
|
|
92
|
-
bits.push(0);
|
|
93
|
-
// Pad to byte boundary
|
|
94
|
-
while (bits.length % 8 !== 0)
|
|
95
|
-
bits.push(0);
|
|
96
|
-
// Pad codewords
|
|
97
|
-
const padBytes = [0xEC, 0x11];
|
|
98
|
-
let padIdx = 0;
|
|
99
|
-
while (bits.length < maxBits) {
|
|
100
|
-
pushBits(padBytes[padIdx % 2], 8);
|
|
101
|
-
padIdx++;
|
|
102
|
-
}
|
|
103
|
-
// Convert bits to bytes
|
|
104
|
-
const dataBytes = [];
|
|
105
|
-
for (let i = 0; i < bits.length; i += 8) {
|
|
106
|
-
let b = 0;
|
|
107
|
-
for (let j = 0; j < 8; j++)
|
|
108
|
-
b = (b << 1) | (bits[i + j] ?? 0);
|
|
109
|
-
dataBytes.push(b);
|
|
110
|
-
}
|
|
111
|
-
// Split into blocks and compute ECC
|
|
112
|
-
const dataPerBlock = Math.floor(dataCodewords / numBlocks);
|
|
113
|
-
const extraBlocks = dataCodewords - dataPerBlock * numBlocks;
|
|
114
|
-
const blocks = [];
|
|
115
|
-
let offset = 0;
|
|
116
|
-
for (let b = 0; b < numBlocks; b++) {
|
|
117
|
-
const blockLen = dataPerBlock + (b < extraBlocks ? 1 : 0);
|
|
118
|
-
blocks.push(dataBytes.slice(offset, offset + blockLen));
|
|
119
|
-
offset += blockLen;
|
|
120
|
-
}
|
|
121
|
-
// Reed-Solomon ECC generation
|
|
122
|
-
function gfMul(a, b) {
|
|
123
|
-
if (a === 0 || b === 0)
|
|
124
|
-
return 0;
|
|
125
|
-
const logTable = buildLogTable();
|
|
126
|
-
const expTable = buildExpTable();
|
|
127
|
-
return expTable[(logTable[a] + logTable[b]) % 255];
|
|
128
|
-
}
|
|
129
|
-
// Lazily built lookup tables (closured)
|
|
130
|
-
let _expTable = null;
|
|
131
|
-
let _logTable = null;
|
|
132
|
-
function buildExpTable() {
|
|
133
|
-
if (_expTable)
|
|
134
|
-
return _expTable;
|
|
135
|
-
_expTable = new Array(256);
|
|
136
|
-
let x = 1;
|
|
137
|
-
for (let i = 0; i < 255; i++) {
|
|
138
|
-
_expTable[i] = x;
|
|
139
|
-
x = (x << 1) ^ (x >= 128 ? 0x11d : 0);
|
|
140
|
-
}
|
|
141
|
-
_expTable[255] = _expTable[0];
|
|
142
|
-
return _expTable;
|
|
143
|
-
}
|
|
144
|
-
function buildLogTable() {
|
|
145
|
-
if (_logTable)
|
|
146
|
-
return _logTable;
|
|
147
|
-
const exp = buildExpTable();
|
|
148
|
-
_logTable = new Array(256).fill(0);
|
|
149
|
-
for (let i = 0; i < 255; i++)
|
|
150
|
-
_logTable[exp[i]] = i;
|
|
151
|
-
return _logTable;
|
|
152
|
-
}
|
|
153
|
-
function rsEcc(data, eccCount) {
|
|
154
|
-
// Generator polynomial for eccCount error correction codewords
|
|
155
|
-
const expTable = buildExpTable();
|
|
156
|
-
const logTable = buildLogTable();
|
|
157
|
-
// Build generator polynomial
|
|
158
|
-
let gen = [1];
|
|
159
|
-
for (let i = 0; i < eccCount; i++) {
|
|
160
|
-
const factor = [1, expTable[i]];
|
|
161
|
-
const newGen = new Array(gen.length + factor.length - 1).fill(0);
|
|
162
|
-
for (let j = 0; j < gen.length; j++) {
|
|
163
|
-
for (let k = 0; k < factor.length; k++) {
|
|
164
|
-
newGen[j + k] ^= gfMul(gen[j], factor[k]);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
gen = newGen;
|
|
168
|
-
}
|
|
169
|
-
// Polynomial long division
|
|
170
|
-
const msg = [...data, ...new Array(eccCount).fill(0)];
|
|
171
|
-
for (let i = 0; i < data.length; i++) {
|
|
172
|
-
const coef = msg[i];
|
|
173
|
-
if (coef !== 0) {
|
|
174
|
-
const log = logTable[coef];
|
|
175
|
-
for (let j = 0; j < gen.length; j++) {
|
|
176
|
-
msg[i + j] ^= gfMul(gen[j], expTable[(log + logTable[gen[j]]) % 255] / gen[j] | 0);
|
|
177
|
-
// Correct formula using logs:
|
|
178
|
-
if (gen[j] !== 0) {
|
|
179
|
-
msg[i + j] ^= expTable[(log + logTable[gen[j]]) % 255];
|
|
180
|
-
// undo the wrong xor above
|
|
181
|
-
msg[i + j] ^= gfMul(gen[j], expTable[(log + logTable[gen[j]]) % 255] / gen[j] | 0);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return msg.slice(data.length);
|
|
187
|
-
}
|
|
188
|
-
// Simpler, correct RS ECC implementation:
|
|
189
|
-
function rsEccCorrect(data, eccCount) {
|
|
190
|
-
const expTable = buildExpTable();
|
|
191
|
-
const logTable = buildLogTable();
|
|
192
|
-
let gen = [1];
|
|
193
|
-
for (let i = 0; i < eccCount; i++) {
|
|
194
|
-
const factor = [1, expTable[i]];
|
|
195
|
-
const newGen = new Array(gen.length + factor.length - 1).fill(0);
|
|
196
|
-
for (let j = 0; j < gen.length; j++) {
|
|
197
|
-
for (let k = 0; k < factor.length; k++) {
|
|
198
|
-
newGen[j + k] ^= gfMul(gen[j], factor[k]);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
gen = newGen;
|
|
202
|
-
}
|
|
203
|
-
const msg = [...data, ...new Array(eccCount).fill(0)];
|
|
204
|
-
for (let i = 0; i < data.length; i++) {
|
|
205
|
-
const coef = msg[i];
|
|
206
|
-
if (coef !== 0) {
|
|
207
|
-
const logCoef = logTable[coef];
|
|
208
|
-
for (let j = 1; j < gen.length; j++) {
|
|
209
|
-
if (gen[j] !== 0) {
|
|
210
|
-
msg[i + j] ^= expTable[(logCoef + logTable[gen[j]]) % 255];
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return msg.slice(data.length);
|
|
216
|
-
}
|
|
217
|
-
// Interleave blocks and compute ECC
|
|
218
|
-
const eccBlocks = blocks.map((b) => rsEccCorrect(b, eccPerBlock));
|
|
219
|
-
const finalBytes = [];
|
|
220
|
-
// Interleave data blocks
|
|
221
|
-
const maxDataLen = Math.max(...blocks.map((b) => b.length));
|
|
222
|
-
for (let i = 0; i < maxDataLen; i++) {
|
|
223
|
-
for (const block of blocks) {
|
|
224
|
-
if (i < block.length)
|
|
225
|
-
finalBytes.push(block[i]);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
// Interleave ECC blocks
|
|
229
|
-
for (let i = 0; i < eccPerBlock; i++) {
|
|
230
|
-
for (const ecc of eccBlocks) {
|
|
231
|
-
finalBytes.push(ecc[i]);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
// Build the QR matrix
|
|
235
|
-
const size = 21 + (version - 1) * 4;
|
|
236
|
-
const matrix = Array.from({ length: size }, () => new Array(size).fill(null));
|
|
237
|
-
function setModule(row, col, dark) {
|
|
238
|
-
if (row >= 0 && row < size && col >= 0 && col < size) {
|
|
239
|
-
matrix[row][col] = dark;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
// Place finder pattern
|
|
243
|
-
function placeFinderPattern(r, c) {
|
|
244
|
-
for (let dy = -1; dy <= 7; dy++) {
|
|
245
|
-
for (let dx = -1; dx <= 7; dx++) {
|
|
246
|
-
const inOuter = dy >= 0 && dy <= 6 && dx >= 0 && dx <= 6;
|
|
247
|
-
const inWhite = dy >= 1 && dy <= 5 && dx >= 1 && dx <= 5;
|
|
248
|
-
const inInner = dy >= 2 && dy <= 4 && dx >= 2 && dx <= 4;
|
|
249
|
-
if (!inOuter) {
|
|
250
|
-
setModule(r + dy, c + dx, false); // separator
|
|
251
|
-
}
|
|
252
|
-
else if (inWhite && !inInner) {
|
|
253
|
-
setModule(r + dy, c + dx, false);
|
|
254
|
-
}
|
|
255
|
-
else {
|
|
256
|
-
setModule(r + dy, c + dx, inInner || (dy === 0 || dy === 6 || dx === 0 || dx === 6));
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
placeFinderPattern(0, 0);
|
|
262
|
-
placeFinderPattern(0, size - 7);
|
|
263
|
-
placeFinderPattern(size - 7, 0);
|
|
264
|
-
// Timing patterns
|
|
265
|
-
for (let i = 8; i < size - 8; i++) {
|
|
266
|
-
const dark = i % 2 === 0;
|
|
267
|
-
setModule(6, i, dark);
|
|
268
|
-
setModule(i, 6, dark);
|
|
269
|
-
}
|
|
270
|
-
// Alignment patterns (version >= 2)
|
|
271
|
-
const alignmentPositions = {
|
|
272
|
-
2: [6, 18], 3: [6, 22], 4: [6, 26], 5: [6, 30],
|
|
273
|
-
6: [6, 34], 7: [6, 22, 38], 8: [6, 24, 42],
|
|
274
|
-
9: [6, 28, 46], 10: [6, 28, 50],
|
|
275
|
-
};
|
|
276
|
-
const alignPos = version >= 2 ? alignmentPositions[version] ?? [] : [];
|
|
277
|
-
for (const r of alignPos) {
|
|
278
|
-
for (const c of alignPos) {
|
|
279
|
-
// Skip positions occupied by finder patterns
|
|
280
|
-
if ((r <= 8 && c <= 8) || (r <= 8 && c >= size - 8) || (r >= size - 8 && c <= 8))
|
|
281
|
-
continue;
|
|
282
|
-
for (let dy = -2; dy <= 2; dy++) {
|
|
283
|
-
for (let dx = -2; dx <= 2; dx++) {
|
|
284
|
-
const isEdge = Math.abs(dy) === 2 || Math.abs(dx) === 2;
|
|
285
|
-
const isCenter = dy === 0 && dx === 0;
|
|
286
|
-
setModule(r + dy, c + dx, isEdge || isCenter);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
// Dark module (always dark)
|
|
292
|
-
setModule(4 * version + 9, 8, true);
|
|
293
|
-
// Format information area — reserve (mark as false for now)
|
|
294
|
-
for (let i = 0; i < 9; i++) {
|
|
295
|
-
setModule(8, i, matrix[8]?.[i] ?? false);
|
|
296
|
-
setModule(i, 8, matrix[i]?.[8] ?? false);
|
|
297
|
-
}
|
|
298
|
-
for (let i = size - 8; i < size; i++) {
|
|
299
|
-
setModule(8, i, false);
|
|
300
|
-
setModule(i, 8, false);
|
|
301
|
-
}
|
|
302
|
-
// Place data bits (zigzag pattern)
|
|
303
|
-
const allBits = [];
|
|
304
|
-
for (const b of finalBytes) {
|
|
305
|
-
for (let i = 7; i >= 0; i--)
|
|
306
|
-
allBits.push((b >> i) & 1);
|
|
307
|
-
}
|
|
308
|
-
let bitIdx = 0;
|
|
309
|
-
let goUp = true;
|
|
310
|
-
// Right-to-left column pairs, skipping column 6 (timing)
|
|
311
|
-
for (let col = size - 1; col >= 1; col -= 2) {
|
|
312
|
-
if (col === 6)
|
|
313
|
-
col--; // skip timing column
|
|
314
|
-
const colRange = goUp
|
|
315
|
-
? Array.from({ length: size }, (_, i) => size - 1 - i)
|
|
316
|
-
: Array.from({ length: size }, (_, i) => i);
|
|
317
|
-
for (const row of colRange) {
|
|
318
|
-
for (const dc of [0, -1]) {
|
|
319
|
-
const c = col + dc;
|
|
320
|
-
if (matrix[row]?.[c] === null) {
|
|
321
|
-
const bit = allBits[bitIdx++] ?? 0;
|
|
322
|
-
matrix[row][c] = bit === 1;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
goUp = !goUp;
|
|
327
|
-
}
|
|
328
|
-
// Apply mask pattern 0 (checkerboard: (row + col) % 2 === 0)
|
|
329
|
-
// and place format information
|
|
330
|
-
const maskFn = (r, c) => (r + c) % 2 === 0;
|
|
331
|
-
for (let r = 0; r < size; r++) {
|
|
332
|
-
for (let c = 0; c < size; c++) {
|
|
333
|
-
const module = matrix[r]?.[c];
|
|
334
|
-
if (module === null) {
|
|
335
|
-
matrix[r][c] = false; // unfilled = light
|
|
336
|
-
}
|
|
337
|
-
else {
|
|
338
|
-
// Only apply mask to data modules (not function patterns)
|
|
339
|
-
// We re-apply masking carefully: function patterns are already fixed
|
|
340
|
-
// Data modules that were null are now set above
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
// Apply mask to data modules (those placed via zigzag)
|
|
345
|
-
bitIdx = 0;
|
|
346
|
-
goUp = true;
|
|
347
|
-
const maskedMatrix = matrix.map((row) => row.map((m) => m ?? false));
|
|
348
|
-
// Reset data area and re-place with masking
|
|
349
|
-
{
|
|
350
|
-
const tempMatrix = matrix.map((row) => [...row]);
|
|
351
|
-
// Re-mark data positions as null
|
|
352
|
-
let bi = 0;
|
|
353
|
-
let gu = true;
|
|
354
|
-
for (let col = size - 1; col >= 1; col -= 2) {
|
|
355
|
-
if (col === 6)
|
|
356
|
-
col--;
|
|
357
|
-
const colRange = gu
|
|
358
|
-
? Array.from({ length: size }, (_, i) => size - 1 - i)
|
|
359
|
-
: Array.from({ length: size }, (_, i) => i);
|
|
360
|
-
for (const row of colRange) {
|
|
361
|
-
for (const dc of [0, -1]) {
|
|
362
|
-
const c = col + dc;
|
|
363
|
-
if (tempMatrix[row]?.[c] !== null) {
|
|
364
|
-
// was set as data, apply mask
|
|
365
|
-
const dark = (allBits[bi++] ?? 0) === 1;
|
|
366
|
-
maskedMatrix[row][c] = maskFn(row, c) ? !dark : dark;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
gu = !gu;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
// Format string for ECC level M (01), mask pattern 0 (000)
|
|
374
|
-
// Format info = 01000 XOR 101010000010010 = 110010111100101
|
|
375
|
-
// Precomputed: ECC-M + mask-0 = format bits 101000000100101 (with BCH)
|
|
376
|
-
// Using known correct value for M+mask0: 0x5647 -> bits[14..0]
|
|
377
|
-
// Actually standard value: ECC-M=1, mask=0 -> data=01_000 -> format=01000 00000000
|
|
378
|
-
// BCH(01000)=0100000000+BCH = let's use precomputed: 0x72F3 for M+mask2, etc.
|
|
379
|
-
// Correct precomputed format words (ECC-M=0b01, masks 0-7):
|
|
380
|
-
const formatWords = [
|
|
381
|
-
0x5412, 0x5125, 0x5E7C, 0x5B4B, 0x45F9, 0x40CE, 0x4F97, 0x4AA0,
|
|
382
|
-
];
|
|
383
|
-
const formatWord = formatWords[0]; // mask 0
|
|
384
|
-
const formatXor = 0b101010000010010;
|
|
385
|
-
const fmt = (formatWord ^ formatXor) & 0x7FFF;
|
|
386
|
-
const fmtBits = Array.from({ length: 15 }, (_, i) => (fmt >> (14 - i)) & 1);
|
|
387
|
-
// Place format bits around finder patterns
|
|
388
|
-
// Around top-left finder:
|
|
389
|
-
const fmtPositions1 = [
|
|
390
|
-
[8, 0], [8, 1], [8, 2], [8, 3], [8, 4], [8, 5], [8, 7], [8, 8],
|
|
391
|
-
[7, 8], [5, 8], [4, 8], [3, 8], [2, 8], [1, 8], [0, 8],
|
|
392
|
-
];
|
|
393
|
-
fmtPositions1.forEach(([r, c], i) => {
|
|
394
|
-
maskedMatrix[r][c] = fmtBits[i] === 1;
|
|
395
|
-
});
|
|
396
|
-
// Around top-right and bottom-left finders:
|
|
397
|
-
const fmtPositions2 = [
|
|
398
|
-
[8, size - 8], [8, size - 7], [8, size - 6], [8, size - 5],
|
|
399
|
-
[8, size - 4], [8, size - 3], [8, size - 2], [8, size - 1],
|
|
400
|
-
];
|
|
401
|
-
fmtPositions2.forEach(([r, c], i) => {
|
|
402
|
-
maskedMatrix[r][c] = fmtBits[i] === 1;
|
|
403
|
-
});
|
|
404
|
-
const fmtPositions3 = [
|
|
405
|
-
[size - 7, 8], [size - 6, 8], [size - 5, 8],
|
|
406
|
-
[size - 4, 8], [size - 3, 8], [size - 2, 8], [size - 1, 8],
|
|
407
|
-
];
|
|
408
|
-
fmtPositions3.forEach(([r, c], i) => {
|
|
409
|
-
maskedMatrix[r][c] = fmtBits[i + 8] === 1;
|
|
410
|
-
});
|
|
411
|
-
return { size, modules: maskedMatrix };
|
|
412
|
-
}
|
|
5
|
+
import { qrcodegen } from './vendor/qrcodegen.js';
|
|
6
|
+
// The vendored file uses TypeScript namespaces with nested classes.
|
|
7
|
+
// Access QrCode and its Ecc inner class through the namespace.
|
|
8
|
+
const QrCodeClass = qrcodegen.QrCode;
|
|
9
|
+
// Ecc is a static nested class — cast to access it through the @ts-nocheck boundary.
|
|
10
|
+
const Ecc = QrCodeClass.Ecc;
|
|
413
11
|
/**
|
|
414
12
|
* Generate a QR code matrix for the given data string.
|
|
415
|
-
*
|
|
13
|
+
* Uses the Nayuki QR Code generator library (pure TypeScript, vendored).
|
|
416
14
|
*/
|
|
417
15
|
export function generateQrMatrix(data) {
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
16
|
+
const qr = QrCodeClass.encodeText(data, Ecc.MEDIUM);
|
|
17
|
+
const size = qr.size;
|
|
18
|
+
const modules = [];
|
|
19
|
+
for (let row = 0; row < size; row++) {
|
|
20
|
+
const rowData = [];
|
|
21
|
+
for (let col = 0; col < size; col++) {
|
|
22
|
+
rowData.push(qr.getModule(col, row));
|
|
23
|
+
}
|
|
24
|
+
modules.push(rowData);
|
|
25
|
+
}
|
|
26
|
+
return { size, modules };
|
|
422
27
|
}
|
|
423
28
|
/**
|
|
424
29
|
* Render a QR matrix to a Unicode block string suitable for terminal output.
|
|
425
30
|
* Uses half-block characters to pack 2 rows into 1 terminal row.
|
|
426
|
-
*
|
|
427
|
-
* - Full block `█` = dark top + dark bottom
|
|
428
|
-
* - Upper half `▀` = dark top + light bottom
|
|
429
|
-
* - Lower half `▄` = light top + dark bottom
|
|
430
|
-
* - Space ` ` = light top + light bottom
|
|
431
31
|
*/
|
|
432
32
|
export function renderQrToString(matrix) {
|
|
433
33
|
const { size, modules } = matrix;
|
|
@@ -438,13 +38,13 @@ export function renderQrToString(matrix) {
|
|
|
438
38
|
const top = modules[row]?.[col] ?? false;
|
|
439
39
|
const bottom = modules[row + 1]?.[col] ?? false;
|
|
440
40
|
if (top && bottom)
|
|
441
|
-
line += '\u2588';
|
|
41
|
+
line += '\u2588';
|
|
442
42
|
else if (top && !bottom)
|
|
443
|
-
line += '\u2580';
|
|
43
|
+
line += '\u2580';
|
|
444
44
|
else if (!top && bottom)
|
|
445
|
-
line += '\u2584';
|
|
45
|
+
line += '\u2584';
|
|
446
46
|
else
|
|
447
|
-
line += ' ';
|
|
47
|
+
line += ' ';
|
|
448
48
|
}
|
|
449
49
|
lines.push(line);
|
|
450
50
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export declare namespace qrcodegen {
|
|
2
|
+
type bit = number;
|
|
3
|
+
type byte = number;
|
|
4
|
+
type int = number;
|
|
5
|
+
export class QrCode {
|
|
6
|
+
readonly version: int;
|
|
7
|
+
readonly errorCorrectionLevel: QrCode.Ecc;
|
|
8
|
+
static encodeText(text: string, ecl: QrCode.Ecc): QrCode;
|
|
9
|
+
static encodeBinary(data: Readonly<Array<byte>>, ecl: QrCode.Ecc): QrCode;
|
|
10
|
+
static encodeSegments(segs: Readonly<Array<QrSegment>>, ecl: QrCode.Ecc, minVersion?: int, maxVersion?: int, mask?: int, boostEcl?: boolean): QrCode;
|
|
11
|
+
readonly size: int;
|
|
12
|
+
readonly mask: int;
|
|
13
|
+
private readonly modules;
|
|
14
|
+
private readonly isFunction;
|
|
15
|
+
constructor(version: int, errorCorrectionLevel: QrCode.Ecc, dataCodewords: Readonly<Array<byte>>, msk: int);
|
|
16
|
+
getModule(x: int, y: int): boolean;
|
|
17
|
+
private drawFunctionPatterns;
|
|
18
|
+
private drawFormatBits;
|
|
19
|
+
private drawVersion;
|
|
20
|
+
private drawFinderPattern;
|
|
21
|
+
private drawAlignmentPattern;
|
|
22
|
+
private setFunctionModule;
|
|
23
|
+
private addEccAndInterleave;
|
|
24
|
+
private drawCodewords;
|
|
25
|
+
private applyMask;
|
|
26
|
+
private getPenaltyScore;
|
|
27
|
+
private getAlignmentPatternPositions;
|
|
28
|
+
private static getNumRawDataModules;
|
|
29
|
+
private static getNumDataCodewords;
|
|
30
|
+
private static reedSolomonComputeDivisor;
|
|
31
|
+
private static reedSolomonComputeRemainder;
|
|
32
|
+
private static reedSolomonMultiply;
|
|
33
|
+
private finderPenaltyCountPatterns;
|
|
34
|
+
private finderPenaltyTerminateAndCount;
|
|
35
|
+
private finderPenaltyAddHistory;
|
|
36
|
+
static readonly MIN_VERSION: int;
|
|
37
|
+
static readonly MAX_VERSION: int;
|
|
38
|
+
private static readonly PENALTY_N1;
|
|
39
|
+
private static readonly PENALTY_N2;
|
|
40
|
+
private static readonly PENALTY_N3;
|
|
41
|
+
private static readonly PENALTY_N4;
|
|
42
|
+
private static readonly ECC_CODEWORDS_PER_BLOCK;
|
|
43
|
+
private static readonly NUM_ERROR_CORRECTION_BLOCKS;
|
|
44
|
+
}
|
|
45
|
+
export class QrSegment {
|
|
46
|
+
readonly mode: QrSegment.Mode;
|
|
47
|
+
readonly numChars: int;
|
|
48
|
+
private readonly bitData;
|
|
49
|
+
static makeBytes(data: Readonly<Array<byte>>): QrSegment;
|
|
50
|
+
static makeNumeric(digits: string): QrSegment;
|
|
51
|
+
static makeAlphanumeric(text: string): QrSegment;
|
|
52
|
+
static makeSegments(text: string): Array<QrSegment>;
|
|
53
|
+
static makeEci(assignVal: int): QrSegment;
|
|
54
|
+
static isNumeric(text: string): boolean;
|
|
55
|
+
static isAlphanumeric(text: string): boolean;
|
|
56
|
+
constructor(mode: QrSegment.Mode, numChars: int, bitData: Array<bit>);
|
|
57
|
+
getData(): Array<bit>;
|
|
58
|
+
static getTotalBits(segs: Readonly<Array<QrSegment>>, version: int): number;
|
|
59
|
+
private static toUtf8ByteArray;
|
|
60
|
+
private static readonly NUMERIC_REGEX;
|
|
61
|
+
private static readonly ALPHANUMERIC_REGEX;
|
|
62
|
+
private static readonly ALPHANUMERIC_CHARSET;
|
|
63
|
+
}
|
|
64
|
+
export {};
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=qrcodegen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qrcodegen.d.ts","sourceRoot":"","sources":["../../../../../src/_internal/platform/pairing/vendor/qrcodegen.ts"],"names":[],"mappings":"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"}
|