@thermal-label/brother-ql-core 0.4.0 → 0.6.0

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.
Files changed (46) hide show
  1. package/README.md +83 -12
  2. package/data/devices.json +442 -24
  3. package/data/media.json +56 -56
  4. package/dist/__tests__/devices.test.js +1 -0
  5. package/dist/__tests__/devices.test.js.map +1 -1
  6. package/dist/__tests__/media.test.js +7 -7
  7. package/dist/__tests__/protocol.test.js +1 -1
  8. package/dist/__tests__/status.test.js +56 -15
  9. package/dist/__tests__/status.test.js.map +1 -1
  10. package/dist/devices.d.ts +711 -2
  11. package/dist/devices.d.ts.map +1 -1
  12. package/dist/devices.generated.d.ts +44 -11
  13. package/dist/devices.generated.d.ts.map +1 -1
  14. package/dist/devices.generated.js +77 -32
  15. package/dist/devices.generated.js.map +1 -1
  16. package/dist/devices.js +6 -2
  17. package/dist/devices.js.map +1 -1
  18. package/dist/index.d.ts +8 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +8 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/media.d.ts +1 -1
  23. package/dist/media.generated.d.ts.map +1 -1
  24. package/dist/media.generated.js +114 -112
  25. package/dist/media.generated.js.map +1 -1
  26. package/dist/media.js +3 -3
  27. package/dist/protocol.js +4 -4
  28. package/dist/status.d.ts +8 -5
  29. package/dist/status.d.ts.map +1 -1
  30. package/dist/status.js +43 -7
  31. package/dist/status.js.map +1 -1
  32. package/dist/types.d.ts +7 -14
  33. package/dist/types.d.ts.map +1 -1
  34. package/package.json +3 -3
  35. package/src/__tests__/devices.test.ts +1 -0
  36. package/src/__tests__/media.test.ts +7 -7
  37. package/src/__tests__/protocol.test.ts +1 -1
  38. package/src/__tests__/status.test.ts +66 -16
  39. package/src/devices.generated.ts +98 -40
  40. package/src/devices.ts +7 -2
  41. package/src/index.ts +10 -0
  42. package/src/media.generated.ts +115 -114
  43. package/src/media.ts +3 -3
  44. package/src/protocol.ts +4 -4
  45. package/src/status.ts +47 -8
  46. package/src/types.ts +7 -14
package/src/media.ts CHANGED
@@ -8,7 +8,7 @@ export { MEDIA };
8
8
  * Resolve per-head-family geometry for a media entry against the
9
9
  * engine that's about to print it.
10
10
  *
11
- * DK entries fall back to the flat `printAreaDots` / `leftMarginPins` /
11
+ * DK entries fall back to the flat `printableDots` / `leftMarginPins` /
12
12
  * `rightMarginPins` fields on the entry — the same values every QL
13
13
  * code path read before the head-family split landed. TZe and HSe
14
14
  * entries dispatch on `engine.headDots`: 128 picks `geometry.narrow`,
@@ -23,14 +23,14 @@ export function resolveTapeGeometry(
23
23
  ): TapeGeometry {
24
24
  if (media.tapeSystem === 'dk') {
25
25
  if (
26
- typeof media.printAreaDots !== 'number' ||
26
+ typeof media.printableDots !== 'number' ||
27
27
  typeof media.leftMarginPins !== 'number' ||
28
28
  typeof media.rightMarginPins !== 'number'
29
29
  ) {
30
30
  throw new Error(`DK media ${media.id.toString()} missing flat geometry fields`);
31
31
  }
32
32
  return {
33
- printAreaDots: media.printAreaDots,
33
+ printableDots: media.printableDots,
34
34
  leftMarginPins: media.leftMarginPins,
35
35
  rightMarginPins: media.rightMarginPins,
36
36
  };
package/src/protocol.ts CHANGED
@@ -229,14 +229,14 @@ function resolveEncoderGeometry(
229
229
  ): TapeGeometry {
230
230
  if (media.tapeSystem === 'dk') {
231
231
  if (
232
- typeof media.printAreaDots !== 'number' ||
232
+ typeof media.printableDots !== 'number' ||
233
233
  typeof media.leftMarginPins !== 'number' ||
234
234
  typeof media.rightMarginPins !== 'number'
235
235
  ) {
236
236
  throw new Error(`DK media ${media.id.toString()} missing flat geometry fields`);
237
237
  }
238
238
  return {
239
- printAreaDots: media.printAreaDots,
239
+ printableDots: media.printableDots,
240
240
  leftMarginPins: media.leftMarginPins,
241
241
  rightMarginPins: media.rightMarginPins,
242
242
  };
@@ -329,10 +329,10 @@ function encodeRasterJob(pages: PageData[], options: JobOptions, ctx: EncodeCont
329
329
  if (compress) chunks.push(buildCompression(true));
330
330
 
331
331
  // Each raster row must cover the full print head width (derived from media geometry).
332
- // leftMarginPins + printAreaDots + rightMarginPins = head pin count (720 / 1296 / 128 / 560).
332
+ // leftMarginPins + printableDots + rightMarginPins = head pin count (720 / 1296 / 128 / 560).
333
333
  const geometry = resolveEncoderGeometry(media, engine);
334
334
  const leftMarginPins = geometry.leftMarginPins;
335
- const totalPins = leftMarginPins + geometry.printAreaDots + geometry.rightMarginPins;
335
+ const totalPins = leftMarginPins + geometry.printableDots + geometry.rightMarginPins;
336
336
  const rowByteLen = Math.ceil(totalPins / 8);
337
337
 
338
338
  // Rows interleaved per raster line (matches Python brother_ql behaviour).
package/src/status.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { PrintEngine, PrinterError } from '@thermal-label/contracts';
1
+ import type { PrintEngine, PrinterError, StatusDetail } from '@thermal-label/contracts';
2
2
  import type { BrotherQLStatus } from './types.js';
3
3
  import { findMediaByDimensions } from './media.js';
4
4
 
@@ -37,15 +37,18 @@ const ERROR_INFO_2: { bit: number; code: string; message: string }[] = [
37
37
  * byte 11 — media type (0x0A continuous, 0x0B die-cut)
38
38
  * byte 17 — media length (mm), 0 for continuous
39
39
  * byte 18 — status type (0x02 = error response)
40
+ * byte 19 — phase type (0x00 receiving, 0x01 printing)
41
+ * byte 22 — notification (0x03 cooling started, 0x04 cooling finished)
40
42
  * byte 25 — bit 7 set when the loaded roll is two-color (DK-22251);
41
43
  * clear on single-color rolls. See scripts/STATUS-CAPTURE.md.
42
44
  *
43
45
  * `detectedMedia` is resolved against the media registry via
44
- * `findMediaByDimensions`. `editorLiteMode` is a driver-specific
45
- * extension on `BrotherQLStatus` — the status-type byte doesn't
46
- * actually report it, but keeping the field here means callers can
47
- * set it from other signals (e.g. mass-storage PID detected during
48
- * discovery) without changing the return type.
46
+ * `findMediaByDimensions`.
47
+ *
48
+ * `details` carries the contracts-standard `StatusDetail[]` diagnostic
49
+ * rows the harness renders verbatim: the print phase (always present)
50
+ * and the head-cooling notification (only when the printer reports
51
+ * one).
49
52
  */
50
53
  export function parseStatus(
51
54
  bytes: Uint8Array,
@@ -62,6 +65,8 @@ export function parseStatus(
62
65
  const mediaTypeByte = view.getUint8(11);
63
66
  const mediaLengthMm = view.getUint8(17);
64
67
  const statusType = view.getUint8(18);
68
+ const phaseType = view.getUint8(19);
69
+ const notification = view.getUint8(22);
65
70
  const twoColorFlag = (view.getUint8(25) & 0x80) !== 0;
66
71
 
67
72
  const errors: PrinterError[] = [];
@@ -81,9 +86,43 @@ export function parseStatus(
81
86
  ready: errors.length === 0 && statusType !== 0x02,
82
87
  mediaLoaded,
83
88
  ...(detected === undefined ? {} : { detectedMedia: detected }),
84
- ...(mediaLoaded ? { twoColorRoll: twoColorFlag } : {}),
85
89
  errors,
86
- editorLiteMode: false,
90
+ details: buildStatusDetails(phaseType, notification),
87
91
  rawBytes: bytes,
88
92
  };
89
93
  }
94
+
95
+ /**
96
+ * Build the contracts-standard `StatusDetail[]` rows for a parsed
97
+ * Brother QL status.
98
+ *
99
+ * - The print-phase row (byte 19) is always emitted — it is the most
100
+ * useful "is the device doing anything" signal for a stuck report.
101
+ * - The head-cooling row (byte 22) is emitted only when the printer
102
+ * reports a cooling notification; `0x00` (none) produces no row so a
103
+ * normal idle status stays uncluttered. "Cooling started" is a
104
+ * `warn` (printing is paused), "cooling finished" is plain `info`.
105
+ */
106
+ function buildStatusDetails(phaseType: number, notification: number): StatusDetail[] {
107
+ const details: StatusDetail[] = [];
108
+
109
+ details.push({
110
+ label: 'Print phase',
111
+ value: phaseType === 0x01 ? 'printing' : 'receiving',
112
+ });
113
+
114
+ if (notification === 0x03) {
115
+ details.push({
116
+ label: 'Head cooling',
117
+ value: 'cooling started',
118
+ severity: 'warn',
119
+ });
120
+ } else if (notification === 0x04) {
121
+ details.push({
122
+ label: 'Head cooling',
123
+ value: 'cooling finished',
124
+ });
125
+ }
126
+
127
+ return details;
128
+ }
package/src/types.ts CHANGED
@@ -25,13 +25,13 @@ export type TapeSystem = 'dk' | 'tze' | 'hse-2to1' | 'hse-3to1';
25
25
  *
26
26
  * Brother's PT-P / PT-E line ships two head families with different
27
27
  * per-tape pin layouts. The same TZe id maps to different
28
- * `printAreaDots` / `leftMarginPins` / `rightMarginPins` values on a
28
+ * `printableDots` / `leftMarginPins` / `rightMarginPins` values on a
29
29
  * 128-pin head (PT-E550W, PT-P750W) versus a 560-pin head (PT-P900,
30
30
  * P900W, P950NW, P910BT). DK media leaves these unset and resolves via
31
31
  * the flat fields on `BrotherQLMedia` directly.
32
32
  */
33
33
  export interface TapeGeometry {
34
- printAreaDots: number;
34
+ printableDots: number;
35
35
  leftMarginPins: number;
36
36
  rightMarginPins: number;
37
37
  }
@@ -104,7 +104,7 @@ export interface BrotherQLMedia extends MediaDescriptor {
104
104
  */
105
105
  geometry?: { narrow?: TapeGeometry; wide?: TapeGeometry };
106
106
  /** DK-only flat geometry. PT-* entries populate `geometry` instead. */
107
- printAreaDots?: number;
107
+ printableDots?: number;
108
108
  leftMarginPins?: number;
109
109
  rightMarginPins?: number;
110
110
  /** Die-cut masked area in dots (registration windows). */
@@ -112,18 +112,11 @@ export interface BrotherQLMedia extends MediaDescriptor {
112
112
  }
113
113
 
114
114
  /**
115
- * Brother QL status — contracts `PrinterStatus` plus the
116
- * `editorLiteMode` flag (pre-paired QL-820NWB silently drops raster
117
- * jobs when in Editor Lite mode; callers need to know).
115
+ * Brother QL status — the plain contract `PrinterStatus`. Brother QL
116
+ * adds no structural extension: driver-specific diagnostic facts (print
117
+ * phase, head cooling) ride in the contract-standard `details[]` rows.
118
118
  */
119
- export interface BrotherQLStatus extends PrinterStatus {
120
- editorLiteMode: boolean;
121
- /**
122
- * True when the loaded roll reports two-color capability via byte 25
123
- * bit 7 of the status response. Undefined when no media is loaded.
124
- */
125
- twoColorRoll?: boolean;
126
- }
119
+ export type BrotherQLStatus = PrinterStatus;
127
120
 
128
121
  export interface PageData {
129
122
  bitmap: LabelBitmap;