@bitmagic/cli 0.1.53-dev.0 → 0.1.53-dev.10
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/README.md +130 -29
- package/dist/assets/revoxelize.d.ts +116 -0
- package/dist/assets/revoxelize.js +343 -0
- package/dist/assets/revoxelize.js.map +1 -0
- package/dist/cli.d.ts +27 -1
- package/dist/cli.js +18 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/assets.d.ts +18 -0
- package/dist/commands/assets.js +110 -1
- package/dist/commands/assets.js.map +1 -1
- package/dist/commands/cover.d.ts +5 -0
- package/dist/commands/cover.js +3 -0
- package/dist/commands/cover.js.map +1 -1
- package/dist/commands/dev.js +1 -2
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/forge.d.ts +4 -0
- package/dist/commands/forge.js +9 -0
- package/dist/commands/forge.js.map +1 -1
- package/dist/commands/generate.d.ts +25 -0
- package/dist/commands/generate.js +80 -8
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +12 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/publish.d.ts +31 -4
- package/dist/commands/publish.js +86 -15
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/reload.js +1 -3
- package/dist/commands/reload.js.map +1 -1
- package/dist/commands/reset-account.js +25 -8
- package/dist/commands/reset-account.js.map +1 -1
- package/dist/commands/self-update.d.ts +67 -0
- package/dist/commands/self-update.js +196 -0
- package/dist/commands/self-update.js.map +1 -0
- package/dist/commands/upgrade.js +5 -2
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/editor/asset-detail-panel.d.ts +2 -2
- package/dist/editor/asset-detail-panel.js +5 -2
- package/dist/editor/asset-detail-panel.js.map +1 -1
- package/dist/editor/cli-update.d.ts +32 -0
- package/dist/editor/cli-update.js +67 -0
- package/dist/editor/cli-update.js.map +1 -0
- package/dist/editor/publish-panel.d.ts +31 -0
- package/dist/editor/publish-panel.js +69 -0
- package/dist/editor/publish-panel.js.map +1 -0
- package/dist/editor/server.js +43 -1
- package/dist/editor/server.js.map +1 -1
- package/dist/editor/shell-page.js +249 -8
- package/dist/editor/shell-page.js.map +1 -1
- package/dist/forge/upload-proxy.d.ts +14 -0
- package/dist/forge/upload-proxy.js +28 -1
- package/dist/forge/upload-proxy.js.map +1 -1
- package/dist/generate/model.d.ts +7 -0
- package/dist/generate/model.js +13 -3
- package/dist/generate/model.js.map +1 -1
- package/dist/generate/prop.d.ts +20 -0
- package/dist/generate/prop.js +22 -2
- package/dist/generate/prop.js.map +1 -1
- package/dist/generate/vehicle.d.ts +2 -0
- package/dist/generate/vehicle.js +1 -0
- package/dist/generate/vehicle.js.map +1 -1
- package/dist/project/dev-handle.d.ts +28 -0
- package/dist/project/dev-handle.js +43 -0
- package/dist/project/dev-handle.js.map +1 -1
- package/dist/project/jobs.js +11 -9
- package/dist/project/jobs.js.map +1 -1
- package/dist/publish/client.d.ts +13 -2
- package/dist/publish/client.js.map +1 -1
- package/dist/publish/record.d.ts +22 -0
- package/dist/publish/record.js +62 -0
- package/dist/publish/record.js.map +1 -0
- package/dist/render/qr-output.d.ts +44 -0
- package/dist/render/qr-output.js +71 -0
- package/dist/render/qr-output.js.map +1 -0
- package/dist/render/qr-png.d.ts +37 -0
- package/dist/render/qr-png.js +111 -0
- package/dist/render/qr-png.js.map +1 -0
- package/dist/render/qr-terminal.d.ts +63 -0
- package/dist/render/qr-terminal.js +142 -0
- package/dist/render/qr-terminal.js.map +1 -0
- package/dist/render/qr.d.ts +51 -0
- package/dist/render/qr.js +516 -0
- package/dist/render/qr.js.map +1 -0
- package/dist/scaffold/project-files.js +44 -27
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/scaffold/suggest-template.d.ts +2 -0
- package/dist/scaffold/suggest-template.js +2 -1
- package/dist/scaffold/suggest-template.js.map +1 -1
- package/dist/telemetry/command-context.d.ts +12 -0
- package/dist/telemetry/command-context.js +7 -0
- package/dist/telemetry/command-context.js.map +1 -1
- package/dist/telemetry/creator-prompt.d.ts +35 -0
- package/dist/telemetry/creator-prompt.js +45 -0
- package/dist/telemetry/creator-prompt.js.map +1 -0
- package/dist/update/check.js +2 -1
- package/dist/update/check.js.map +1 -1
- package/dist/update/notice.d.ts +31 -1
- package/dist/update/notice.js +39 -5
- package/dist/update/notice.js.map +1 -1
- package/dist/update/self-install.d.ts +175 -0
- package/dist/update/self-install.js +335 -0
- package/dist/update/self-install.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a publish's QR code goes: the terminal when someone is looking at it, a file when nobody
|
|
3
|
+
* is.
|
|
4
|
+
*
|
|
5
|
+
* This module exists because the two are one decision, not two. `qr-terminal.ts` refuses a
|
|
6
|
+
* non-TTY for a good reason — escape sequences in a captured stream are noise — but refusing to
|
|
7
|
+
* DRAW is not the same as having nothing to offer, and for this CLI the captured stream is the
|
|
8
|
+
* common case. The pro lane is driven by an agent, so stdout is a pipe on almost every real
|
|
9
|
+
* publish, and the creator who would scan the code never sees the terminal at all. Gating the
|
|
10
|
+
* feature on `isTTY` alone turned it off exactly where the URL is most worth handing over.
|
|
11
|
+
*
|
|
12
|
+
* So: draw it if it can be drawn, and write `.bitmagic/publish-qr.png` whenever it cannot. That
|
|
13
|
+
* covers the pipe, and also the two smaller cases with the same shape — `TERM=dumb`, and a
|
|
14
|
+
* terminal too narrow to hold the code unwrapped — where a person is present but still has nothing
|
|
15
|
+
* to scan. A terminal that draws successfully writes no file: it already did the job.
|
|
16
|
+
*
|
|
17
|
+
* `.bitmagic/` is excluded from the publish fingerprint (`EXCLUDED_DIRS` in
|
|
18
|
+
* `@bitmagic/asset-core/publish-source`), so writing here cannot invalidate the verify the gate
|
|
19
|
+
* just checked.
|
|
20
|
+
*/
|
|
21
|
+
import * as fs from 'fs';
|
|
22
|
+
import * as path from 'path';
|
|
23
|
+
import { encodeQr } from './qr.js';
|
|
24
|
+
import { encodeQrPng } from './qr-png.js';
|
|
25
|
+
import { renderTerminalQr } from './qr-terminal.js';
|
|
26
|
+
/** Where a publish leaves its QR image. One per project, overwritten each publish. */
|
|
27
|
+
export function publishQrPath(root) {
|
|
28
|
+
return path.join(root, '.bitmagic', 'publish-qr.png');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Show `url` as a QR code however this run can, and return the image path when one was written.
|
|
32
|
+
*
|
|
33
|
+
* Never throws and never reports failure as an error: the caller has already printed the URL, and
|
|
34
|
+
* a code that could not be produced costs the creator a retype, not a publish.
|
|
35
|
+
*/
|
|
36
|
+
export function emitQr(url, options) {
|
|
37
|
+
const env = options.env ?? process.env;
|
|
38
|
+
if (options.disabled || env.BITMAGIC_NO_QR)
|
|
39
|
+
return null;
|
|
40
|
+
// Drawing is tried first and, when it works, is the whole answer — a file nobody opens is
|
|
41
|
+
// clutter in a directory the creator does read.
|
|
42
|
+
if (!options.json) {
|
|
43
|
+
const drawn = renderTerminalQr(url, {
|
|
44
|
+
env,
|
|
45
|
+
...(options.isTTY === undefined ? {} : { isTTY: options.isTTY }),
|
|
46
|
+
columns: options.columns,
|
|
47
|
+
...(options.write === undefined ? {} : { write: options.write }),
|
|
48
|
+
});
|
|
49
|
+
if (drawn)
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const matrix = encodeQr(url);
|
|
53
|
+
if (matrix === null)
|
|
54
|
+
return null;
|
|
55
|
+
const file = publishQrPath(options.root);
|
|
56
|
+
try {
|
|
57
|
+
if (options.writeFile) {
|
|
58
|
+
options.writeFile(file, encodeQrPng(matrix));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
62
|
+
fs.writeFileSync(file, encodeQrPng(matrix));
|
|
63
|
+
}
|
|
64
|
+
return file;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// A read-only or full disk is not worth failing a successful publish over.
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=qr-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qr-output.js","sourceRoot":"","sources":["../../src/render/qr-output.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAsB,MAAM,kBAAkB,CAAC;AAExE,sFAAsF;AACtF,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,gBAAgB,CAAC,CAAC;AACxD,CAAC;AAiBD;;;;;GAKG;AACH,MAAM,UAAU,MAAM,CAAC,GAAW,EAAE,OAAsB;IACxD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,IAAI,OAAO,CAAC,QAAQ,IAAI,GAAG,CAAC,cAAc;QAAE,OAAO,IAAI,CAAC;IAExD,0FAA0F;IAC1F,gDAAgD;IAChD,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,EAAE;YAClC,GAAG;YACH,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;YAChE,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;SACjE,CAAC,CAAC;QACH,IAAI,KAAK;YAAE,OAAO,IAAI,CAAC;IACzB,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAEjC,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,CAAC;QACH,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACtD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,2EAA2E;QAC3E,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A QR code as PNG bytes, for the creator who cannot scan the terminal.
|
|
3
|
+
*
|
|
4
|
+
* `qr-terminal.ts` covers the case where a person is looking at the output. That is not the common
|
|
5
|
+
* case for this CLI: the pro lane is driven by an agent, so stdout is a pipe, and the human who
|
|
6
|
+
* would do the scanning is on the far side of it. A file crosses that boundary where escape
|
|
7
|
+
* sequences cannot — an agent can hand an image to its creator, and cannot hand them a repainted
|
|
8
|
+
* terminal.
|
|
9
|
+
*
|
|
10
|
+
* Greyscale, one byte per pixel. A QR carries one bit per module, so colour would be three bytes of
|
|
11
|
+
* nothing; and at this size the whole image deflates to a few hundred bytes either way. The
|
|
12
|
+
* encoder is hand-rolled for the same reason `qr.ts` is — `@bitmagic/cli` ships four runtime
|
|
13
|
+
* dependencies on purpose, and node's own `zlib` (already used in `publish/` and `assets/`) does
|
|
14
|
+
* the only part that is genuinely hard.
|
|
15
|
+
*
|
|
16
|
+
* Two other hand-rolled PNG encoders exist in this repo — `world-forger-internal`'s vehicle atlas
|
|
17
|
+
* and `game-play-agent`'s solid-colour tile — and neither is reachable from here: the first is in a
|
|
18
|
+
* `private: true` package the CLI does not depend on, the second is server code. A shared home
|
|
19
|
+
* would have to be `@bitmagic/asset-core`; that is worth doing when something needs the third copy,
|
|
20
|
+
* not for the second.
|
|
21
|
+
*/
|
|
22
|
+
import { Buffer } from 'buffer';
|
|
23
|
+
export interface QrPngOptions {
|
|
24
|
+
/** Pixels per module. Defaults to `MODULE_PIXELS`. */
|
|
25
|
+
scale?: number;
|
|
26
|
+
/** Quiet zone in modules. Defaults to `QUIET_MODULES`. */
|
|
27
|
+
quietZone?: number;
|
|
28
|
+
}
|
|
29
|
+
/** The pixel width (and height) `encodeQrPng` produces for a matrix of this size. */
|
|
30
|
+
export declare function qrPngDimension(matrix: boolean[][], options?: QrPngOptions): number;
|
|
31
|
+
/**
|
|
32
|
+
* Encode a module matrix as a greyscale PNG: dark modules black, everything else white.
|
|
33
|
+
*
|
|
34
|
+
* White is 0xff and dark is 0x00, never inverted — a scanner wants dark on light, and unlike the
|
|
35
|
+
* terminal there is no theme here to fight about it.
|
|
36
|
+
*/
|
|
37
|
+
export declare function encodeQrPng(matrix: boolean[][], options?: QrPngOptions): Buffer;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A QR code as PNG bytes, for the creator who cannot scan the terminal.
|
|
3
|
+
*
|
|
4
|
+
* `qr-terminal.ts` covers the case where a person is looking at the output. That is not the common
|
|
5
|
+
* case for this CLI: the pro lane is driven by an agent, so stdout is a pipe, and the human who
|
|
6
|
+
* would do the scanning is on the far side of it. A file crosses that boundary where escape
|
|
7
|
+
* sequences cannot — an agent can hand an image to its creator, and cannot hand them a repainted
|
|
8
|
+
* terminal.
|
|
9
|
+
*
|
|
10
|
+
* Greyscale, one byte per pixel. A QR carries one bit per module, so colour would be three bytes of
|
|
11
|
+
* nothing; and at this size the whole image deflates to a few hundred bytes either way. The
|
|
12
|
+
* encoder is hand-rolled for the same reason `qr.ts` is — `@bitmagic/cli` ships four runtime
|
|
13
|
+
* dependencies on purpose, and node's own `zlib` (already used in `publish/` and `assets/`) does
|
|
14
|
+
* the only part that is genuinely hard.
|
|
15
|
+
*
|
|
16
|
+
* Two other hand-rolled PNG encoders exist in this repo — `world-forger-internal`'s vehicle atlas
|
|
17
|
+
* and `game-play-agent`'s solid-colour tile — and neither is reachable from here: the first is in a
|
|
18
|
+
* `private: true` package the CLI does not depend on, the second is server code. A shared home
|
|
19
|
+
* would have to be `@bitmagic/asset-core`; that is worth doing when something needs the third copy,
|
|
20
|
+
* not for the second.
|
|
21
|
+
*/
|
|
22
|
+
import { Buffer } from 'buffer';
|
|
23
|
+
import * as zlib from 'zlib';
|
|
24
|
+
/** Pixels per QR module. Large enough that a phone camera reads it off a normal screen. */
|
|
25
|
+
const MODULE_PIXELS = 8;
|
|
26
|
+
/**
|
|
27
|
+
* Quiet zone in modules. Four, the value the spec asks for, rather than the two `qr-terminal.ts`
|
|
28
|
+
* uses — that one is trading margin for terminal columns, and a file has no such constraint.
|
|
29
|
+
*/
|
|
30
|
+
const QUIET_MODULES = 4;
|
|
31
|
+
const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
32
|
+
/** The standard CRC-32 table (PNG spec annex D), built once. */
|
|
33
|
+
function buildCrcTable() {
|
|
34
|
+
const table = new Uint32Array(256);
|
|
35
|
+
for (let n = 0; n < 256; n += 1) {
|
|
36
|
+
let c = n;
|
|
37
|
+
for (let k = 0; k < 8; k += 1) {
|
|
38
|
+
c = (c & 1) !== 0 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
39
|
+
}
|
|
40
|
+
table[n] = c >>> 0;
|
|
41
|
+
}
|
|
42
|
+
return table;
|
|
43
|
+
}
|
|
44
|
+
const CRC_TABLE = buildCrcTable();
|
|
45
|
+
function crc32(bytes) {
|
|
46
|
+
let crc = 0xffffffff;
|
|
47
|
+
for (const byte of bytes) {
|
|
48
|
+
crc = CRC_TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8);
|
|
49
|
+
}
|
|
50
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
51
|
+
}
|
|
52
|
+
/** One PNG chunk: length, type, data, and the CRC over type+data (never over the length). */
|
|
53
|
+
function chunk(type, data) {
|
|
54
|
+
const length = Buffer.alloc(4);
|
|
55
|
+
length.writeUInt32BE(data.length, 0);
|
|
56
|
+
const typeAndData = Buffer.concat([Buffer.from(type, 'ascii'), data]);
|
|
57
|
+
const crc = Buffer.alloc(4);
|
|
58
|
+
crc.writeUInt32BE(crc32(typeAndData), 0);
|
|
59
|
+
return Buffer.concat([length, typeAndData, crc]);
|
|
60
|
+
}
|
|
61
|
+
/** The pixel width (and height) `encodeQrPng` produces for a matrix of this size. */
|
|
62
|
+
export function qrPngDimension(matrix, options = {}) {
|
|
63
|
+
const scale = options.scale ?? MODULE_PIXELS;
|
|
64
|
+
const quiet = options.quietZone ?? QUIET_MODULES;
|
|
65
|
+
return (matrix.length + quiet * 2) * scale;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Encode a module matrix as a greyscale PNG: dark modules black, everything else white.
|
|
69
|
+
*
|
|
70
|
+
* White is 0xff and dark is 0x00, never inverted — a scanner wants dark on light, and unlike the
|
|
71
|
+
* terminal there is no theme here to fight about it.
|
|
72
|
+
*/
|
|
73
|
+
export function encodeQrPng(matrix, options = {}) {
|
|
74
|
+
const scale = options.scale ?? MODULE_PIXELS;
|
|
75
|
+
const quiet = options.quietZone ?? QUIET_MODULES;
|
|
76
|
+
const dimension = qrPngDimension(matrix, options);
|
|
77
|
+
// One filter byte per scanline (0 = None: a QR is flat colour, so the other filters buy nothing
|
|
78
|
+
// that deflate does not already get from the runs), then one byte per pixel.
|
|
79
|
+
const raw = Buffer.alloc(dimension * (dimension + 1), 0xff);
|
|
80
|
+
for (let y = 0; y < dimension; y += 1) {
|
|
81
|
+
const rowStart = y * (dimension + 1);
|
|
82
|
+
raw[rowStart] = 0;
|
|
83
|
+
const moduleRow = Math.floor(y / scale) - quiet;
|
|
84
|
+
if (moduleRow < 0 || moduleRow >= matrix.length)
|
|
85
|
+
continue;
|
|
86
|
+
for (let x = 0; x < dimension; x += 1) {
|
|
87
|
+
const moduleCol = Math.floor(x / scale) - quiet;
|
|
88
|
+
if (moduleCol < 0 || moduleCol >= matrix.length)
|
|
89
|
+
continue;
|
|
90
|
+
if (matrix[moduleRow][moduleCol])
|
|
91
|
+
raw[rowStart + 1 + x] = 0x00;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const ihdr = Buffer.alloc(13);
|
|
95
|
+
ihdr.writeUInt32BE(dimension, 0);
|
|
96
|
+
ihdr.writeUInt32BE(dimension, 4);
|
|
97
|
+
ihdr[8] = 8; // bit depth
|
|
98
|
+
ihdr[9] = 0; // colour type 0 — greyscale
|
|
99
|
+
ihdr[10] = 0; // compression: deflate, the only value PNG defines
|
|
100
|
+
ihdr[11] = 0; // filter method: adaptive, the only value PNG defines
|
|
101
|
+
ihdr[12] = 0; // interlace: none
|
|
102
|
+
return Buffer.concat([
|
|
103
|
+
PNG_SIGNATURE,
|
|
104
|
+
chunk('IHDR', ihdr),
|
|
105
|
+
// deflateSync, not fflate's — fflate's `deflateSync` emits RAW deflate with no zlib header,
|
|
106
|
+
// and an IDAT must carry the zlib wrapper. (fflate's `zlibSync` would also do.)
|
|
107
|
+
chunk('IDAT', zlib.deflateSync(raw)),
|
|
108
|
+
chunk('IEND', Buffer.alloc(0)),
|
|
109
|
+
]);
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=qr-png.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qr-png.js","sourceRoot":"","sources":["../../src/render/qr-png.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drawing a QR code into the terminal, so the URL `bitmagic publish` just printed can be scanned
|
|
3
|
+
* off the screen instead of retyped into a phone. The Creator has had this since it shipped — the
|
|
4
|
+
* publish dialog ends on a QR — and the CLI's publish is the same moment: `--visibility private`
|
|
5
|
+
* puts a real, phone-reachable URL on a game nobody else can find, which is exactly a "try it on
|
|
6
|
+
* my phone" build.
|
|
7
|
+
*
|
|
8
|
+
* The contract is `inline-image.ts`'s, deliberately: the code is strictly ADDITIONAL. The caller
|
|
9
|
+
* prints the URL first, unconditionally; this either draws underneath it or does nothing at all. It
|
|
10
|
+
* never replaces a line and it never reports failure as an error, because an agent tool is usually
|
|
11
|
+
* what reads this output.
|
|
12
|
+
*
|
|
13
|
+
* That last clause is also why nothing should call this module directly. Refusing to DRAW into a
|
|
14
|
+
* pipe is right; having nothing to offer when there is one is not, and since an agent drives most
|
|
15
|
+
* publishes, the pipe is the common case rather than the edge. `qr-output.ts` owns that decision —
|
|
16
|
+
* it asks this module first and falls back to a PNG file — and `bitmagic publish` goes through it.
|
|
17
|
+
*
|
|
18
|
+
* Where it DIVERGES from that file is the gate. `supportsInlineImage` is an allowlist of terminals
|
|
19
|
+
* known to implement the iTerm2 protocol, because the ones that do not print a screenful of raw
|
|
20
|
+
* base64. Nothing here is exotic: half-block characters and SGR colour are universal, and tmux
|
|
21
|
+
* passes both through rather than swallowing them. So the gate below asks what the terminal can do
|
|
22
|
+
* — is it a terminal at all, is it wide enough — and never who it is.
|
|
23
|
+
*/
|
|
24
|
+
export interface TerminalQrEnv {
|
|
25
|
+
TERM?: string | undefined;
|
|
26
|
+
BITMAGIC_NO_QR?: string | undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Whether this terminal can show a scannable code at all.
|
|
30
|
+
*
|
|
31
|
+
* Not an allowlist — see the file header. `TERM=dumb` is the one terminal that answers no on its
|
|
32
|
+
* own behalf, and a non-TTY means something is reading this rather than looking at it — which is a
|
|
33
|
+
* reason not to paint escape sequences, NOT a reason to withhold the code. `qr-output.ts` writes an
|
|
34
|
+
* image for every case this function turns down.
|
|
35
|
+
*/
|
|
36
|
+
export declare function supportsTerminalQr(env: TerminalQrEnv, isTTY: boolean): boolean;
|
|
37
|
+
/** How many columns the drawn code occupies, quiet zone included. */
|
|
38
|
+
export declare function terminalQrWidth(matrix: boolean[][]): number;
|
|
39
|
+
/**
|
|
40
|
+
* The code as terminal lines, quiet zone included and each line self-contained.
|
|
41
|
+
*
|
|
42
|
+
* Exported so a test can read the output without a TTY. The padded symbol has an odd number of
|
|
43
|
+
* module rows — the module count is always odd and the quiet zone adds an even number — so the
|
|
44
|
+
* final terminal row pairs its top half against a light row that is not in the symbol. That is
|
|
45
|
+
* correct rather than a rounding artefact: it reads as one more row of quiet zone.
|
|
46
|
+
*/
|
|
47
|
+
export declare function terminalQrLines(matrix: boolean[][]): string[];
|
|
48
|
+
export interface RenderTerminalQrOptions {
|
|
49
|
+
env?: TerminalQrEnv;
|
|
50
|
+
isTTY?: boolean;
|
|
51
|
+
/** Terminal width. Undefined means unknown, which is treated as wide enough. */
|
|
52
|
+
columns?: number | undefined;
|
|
53
|
+
write?: (chunk: string) => void;
|
|
54
|
+
caption?: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Draw `text` as a QR code if this terminal can show one. Returns whether anything was drawn, so a
|
|
58
|
+
* caller can decide what else to say — never throws, and never writes when unsupported.
|
|
59
|
+
*
|
|
60
|
+
* The caption and the code go out in a single write, so the creator never sees an invitation to
|
|
61
|
+
* scan with nothing under it.
|
|
62
|
+
*/
|
|
63
|
+
export declare function renderTerminalQr(text: string, options?: RenderTerminalQrOptions): boolean;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drawing a QR code into the terminal, so the URL `bitmagic publish` just printed can be scanned
|
|
3
|
+
* off the screen instead of retyped into a phone. The Creator has had this since it shipped — the
|
|
4
|
+
* publish dialog ends on a QR — and the CLI's publish is the same moment: `--visibility private`
|
|
5
|
+
* puts a real, phone-reachable URL on a game nobody else can find, which is exactly a "try it on
|
|
6
|
+
* my phone" build.
|
|
7
|
+
*
|
|
8
|
+
* The contract is `inline-image.ts`'s, deliberately: the code is strictly ADDITIONAL. The caller
|
|
9
|
+
* prints the URL first, unconditionally; this either draws underneath it or does nothing at all. It
|
|
10
|
+
* never replaces a line and it never reports failure as an error, because an agent tool is usually
|
|
11
|
+
* what reads this output.
|
|
12
|
+
*
|
|
13
|
+
* That last clause is also why nothing should call this module directly. Refusing to DRAW into a
|
|
14
|
+
* pipe is right; having nothing to offer when there is one is not, and since an agent drives most
|
|
15
|
+
* publishes, the pipe is the common case rather than the edge. `qr-output.ts` owns that decision —
|
|
16
|
+
* it asks this module first and falls back to a PNG file — and `bitmagic publish` goes through it.
|
|
17
|
+
*
|
|
18
|
+
* Where it DIVERGES from that file is the gate. `supportsInlineImage` is an allowlist of terminals
|
|
19
|
+
* known to implement the iTerm2 protocol, because the ones that do not print a screenful of raw
|
|
20
|
+
* base64. Nothing here is exotic: half-block characters and SGR colour are universal, and tmux
|
|
21
|
+
* passes both through rather than swallowing them. So the gate below asks what the terminal can do
|
|
22
|
+
* — is it a terminal at all, is it wide enough — and never who it is.
|
|
23
|
+
*/
|
|
24
|
+
import { encodeQr } from './qr.js';
|
|
25
|
+
/** Written as an escape rather than a literal control byte, so the source stays greppable. */
|
|
26
|
+
const ESC = '\u001b';
|
|
27
|
+
/**
|
|
28
|
+
* Black on bright white, and the reset that closes every line.
|
|
29
|
+
*
|
|
30
|
+
* Explicit on purpose. A scanner wants dark modules on a light field, and the creator's terminal is
|
|
31
|
+
* probably a dark theme — left to inherit, the code would come out inverted, which many scanners
|
|
32
|
+
* cope with and some do not. The reset ends each line so the colour cannot bleed into a wrapped
|
|
33
|
+
* line or the prompt. Bright white (107) rather than plain white (47) because the latter is a light
|
|
34
|
+
* grey in most palettes, and contrast is the one thing a scanner cannot make up for.
|
|
35
|
+
*/
|
|
36
|
+
const PAINT = `${ESC}[30;107m`;
|
|
37
|
+
const RESET = `${ESC}[0m`;
|
|
38
|
+
/**
|
|
39
|
+
* Quiet-zone width in modules, matching the `margin: 2` the Creator passes `QRCode.toDataURL` —
|
|
40
|
+
* a value already proven to scan in production. The spec asks for 4; if scans ever prove flaky,
|
|
41
|
+
* that is the knob, and it costs 4 columns and 1 line.
|
|
42
|
+
*/
|
|
43
|
+
const QUIET_ZONE = 2;
|
|
44
|
+
/**
|
|
45
|
+
* Two module rows per terminal row, drawn with half-blocks.
|
|
46
|
+
*
|
|
47
|
+
* This is what makes the code square. Terminal cells are roughly twice as tall as they are wide, so
|
|
48
|
+
* one module per cell would stretch the symbol into a rectangle no scanner would read; half-blocks
|
|
49
|
+
* put two module rows in one cell and bring the aspect ratio back to 1:1.
|
|
50
|
+
*/
|
|
51
|
+
const BOTH_DARK = '█'; // full block
|
|
52
|
+
const TOP_DARK = '▀'; // upper half block
|
|
53
|
+
const BOTTOM_DARK = '▄'; // lower half block
|
|
54
|
+
const BOTH_LIGHT = ' ';
|
|
55
|
+
/**
|
|
56
|
+
* Whether this terminal can show a scannable code at all.
|
|
57
|
+
*
|
|
58
|
+
* Not an allowlist — see the file header. `TERM=dumb` is the one terminal that answers no on its
|
|
59
|
+
* own behalf, and a non-TTY means something is reading this rather than looking at it — which is a
|
|
60
|
+
* reason not to paint escape sequences, NOT a reason to withhold the code. `qr-output.ts` writes an
|
|
61
|
+
* image for every case this function turns down.
|
|
62
|
+
*/
|
|
63
|
+
export function supportsTerminalQr(env, isTTY) {
|
|
64
|
+
if (!isTTY)
|
|
65
|
+
return false;
|
|
66
|
+
if (env.BITMAGIC_NO_QR)
|
|
67
|
+
return false;
|
|
68
|
+
return env.TERM !== 'dumb';
|
|
69
|
+
}
|
|
70
|
+
/** How many columns the drawn code occupies, quiet zone included. */
|
|
71
|
+
export function terminalQrWidth(matrix) {
|
|
72
|
+
return matrix.length + QUIET_ZONE * 2;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The code as terminal lines, quiet zone included and each line self-contained.
|
|
76
|
+
*
|
|
77
|
+
* Exported so a test can read the output without a TTY. The padded symbol has an odd number of
|
|
78
|
+
* module rows — the module count is always odd and the quiet zone adds an even number — so the
|
|
79
|
+
* final terminal row pairs its top half against a light row that is not in the symbol. That is
|
|
80
|
+
* correct rather than a rounding artefact: it reads as one more row of quiet zone.
|
|
81
|
+
*/
|
|
82
|
+
export function terminalQrLines(matrix) {
|
|
83
|
+
const width = terminalQrWidth(matrix);
|
|
84
|
+
const height = width;
|
|
85
|
+
const dark = (row, col) => {
|
|
86
|
+
const r = row - QUIET_ZONE;
|
|
87
|
+
const c = col - QUIET_ZONE;
|
|
88
|
+
if (r < 0 || c < 0 || r >= matrix.length || c >= matrix.length)
|
|
89
|
+
return false;
|
|
90
|
+
return matrix[r][c];
|
|
91
|
+
};
|
|
92
|
+
const lines = [];
|
|
93
|
+
for (let row = 0; row < height; row += 2) {
|
|
94
|
+
let line = '';
|
|
95
|
+
for (let col = 0; col < width; col += 1) {
|
|
96
|
+
const top = dark(row, col);
|
|
97
|
+
const bottom = row + 1 < height && dark(row + 1, col);
|
|
98
|
+
if (top && bottom)
|
|
99
|
+
line += BOTH_DARK;
|
|
100
|
+
else if (top)
|
|
101
|
+
line += TOP_DARK;
|
|
102
|
+
else if (bottom)
|
|
103
|
+
line += BOTTOM_DARK;
|
|
104
|
+
else
|
|
105
|
+
line += BOTH_LIGHT;
|
|
106
|
+
}
|
|
107
|
+
lines.push(`${PAINT}${line}${RESET}`);
|
|
108
|
+
}
|
|
109
|
+
return lines;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Draw `text` as a QR code if this terminal can show one. Returns whether anything was drawn, so a
|
|
113
|
+
* caller can decide what else to say — never throws, and never writes when unsupported.
|
|
114
|
+
*
|
|
115
|
+
* The caption and the code go out in a single write, so the creator never sees an invitation to
|
|
116
|
+
* scan with nothing under it.
|
|
117
|
+
*/
|
|
118
|
+
export function renderTerminalQr(text, options = {}) {
|
|
119
|
+
const env = options.env ?? process.env;
|
|
120
|
+
const isTTY = options.isTTY ?? process.stdout.isTTY === true;
|
|
121
|
+
if (!supportsTerminalQr(env, isTTY))
|
|
122
|
+
return false;
|
|
123
|
+
const matrix = encodeQr(text);
|
|
124
|
+
if (matrix === null)
|
|
125
|
+
return false;
|
|
126
|
+
// A code wider than the terminal wraps, and a wrapped code is not a code — it is noise that
|
|
127
|
+
// looks like output. Better to leave the plain URL standing on its own.
|
|
128
|
+
const columns = options.columns ?? process.stdout.columns;
|
|
129
|
+
if (typeof columns === 'number' && columns < terminalQrWidth(matrix))
|
|
130
|
+
return false;
|
|
131
|
+
const caption = options.caption ?? 'Scan to play on your phone:';
|
|
132
|
+
const write = options.write ?? ((chunk) => process.stdout.write(chunk));
|
|
133
|
+
try {
|
|
134
|
+
write(`\n${caption}\n\n${terminalQrLines(matrix).join('\n')}\n\n`);
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// A closed or non-writable stdout is not worth failing a successful publish over.
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=qr-terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qr-terminal.js","sourceRoot":"","sources":["../../src/render/qr-terminal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A QR encoder, so `bitmagic publish` can hand the creator's phone the URL it just printed.
|
|
3
|
+
*
|
|
4
|
+
* The Creator does this server-side already — `PublishController.generateQRCode` calls the `qrcode`
|
|
5
|
+
* package and the publish dialog shows the PNG. The CLI cannot follow it there: `qrcode` drags in
|
|
6
|
+
* `dijkstrajs`, `pngjs` and `yargs@15`, and `@bitmagic/cli` is a published package with four
|
|
7
|
+
* deliberately-chosen runtime dependencies. So the encoder lives here instead, cut down to exactly
|
|
8
|
+
* what a published-game URL needs:
|
|
9
|
+
*
|
|
10
|
+
* - **Byte mode only.** A URL is bytes. Numeric, alphanumeric and kanji modes would encode it
|
|
11
|
+
* smaller in theory and never in practice, because `https://` alone rules out alphanumeric.
|
|
12
|
+
* - **Error correction level M only**, matching the Creator's `errorCorrectionLevel: 'M'` — a code
|
|
13
|
+
* scanned from this terminal and one scanned from the publish dialog then behave identically.
|
|
14
|
+
* - **Versions 1-9 only.** The character-count indicator is 8 bits up to version 9 and 16 bits
|
|
15
|
+
* from version 10, so stopping here removes a branch and four tables. Version 9 holds 180 bytes;
|
|
16
|
+
* the longest URL this ever sees is the dev portal's, around 60. Anything longer returns null
|
|
17
|
+
* rather than growing the tables — see `encodeQr`.
|
|
18
|
+
*
|
|
19
|
+
* Structure follows ISO/IEC 18004. Every table below is a literal with the spec's own name for it,
|
|
20
|
+
* and `__tests__/qr.test.ts` checks each one against fixtures generated by the `qrcode` package, so
|
|
21
|
+
* a transposed digit fails loudly instead of producing a code that scans as garbage.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* The spec's four penalty rules (8.8.2), summed. Lower is better.
|
|
25
|
+
*
|
|
26
|
+
* Scored over the entire symbol including function patterns, as the spec requires — the finders'
|
|
27
|
+
* own 1:1:3:1:1 ratio is exactly what rule 3 hunts for elsewhere, and excluding them would let a
|
|
28
|
+
* mask hide a false finder against a real one.
|
|
29
|
+
*/
|
|
30
|
+
export declare function maskPenalty(matrix: boolean[][]): number;
|
|
31
|
+
/**
|
|
32
|
+
* Encode `text` with an explicitly chosen mask.
|
|
33
|
+
*
|
|
34
|
+
* Exported for the fixture tests, which pin the mask so they compare structure — codewords, ECC,
|
|
35
|
+
* interleaving, function patterns, format bits — rather than a mask heuristic. Callers outside the
|
|
36
|
+
* tests want `encodeQr`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function encodeQrWithMask(text: string, mask: number): boolean[][] | null;
|
|
39
|
+
/**
|
|
40
|
+
* Encode `text` as a matrix of modules, row-major, true meaning dark. Null when it does not fit.
|
|
41
|
+
*
|
|
42
|
+
* The mask is chosen by the spec's penalty rules. That choice is a legibility heuristic and not a
|
|
43
|
+
* correctness property — all eight masks produce a valid, scannable symbol, and a decoder reads the
|
|
44
|
+
* chosen one out of the format information either way.
|
|
45
|
+
*
|
|
46
|
+
* There is no quiet zone here: it belongs to whatever draws the symbol, which knows what it is
|
|
47
|
+
* drawing onto. `qr-terminal.ts` paints its own.
|
|
48
|
+
*/
|
|
49
|
+
export declare function encodeQr(text: string): boolean[][] | null;
|
|
50
|
+
/** The longest text this encoder can hold, in bytes. Exported so a caller can explain a refusal. */
|
|
51
|
+
export declare const MAX_BYTES: number;
|