@dheerajsom/pinhub 0.1.2 → 0.2.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.
- package/{README.md → CLI.md} +53 -13
- package/LICENSE +21 -0
- package/SETUP.md +16 -5
- package/dist/boards/arduino-uno-r3.js +1 -1
- package/dist/boards/esp32-devkit-v1.js +5 -4
- package/dist/boards/generated.js +1044 -218
- package/dist/cli.js +17 -1
- package/dist/model.d.ts +3 -0
- package/dist/render/board.d.ts +4 -2
- package/dist/render/board.js +174 -96
- package/dist/render/chars.d.ts +4 -0
- package/dist/render/chars.js +8 -0
- package/dist/render/meta.d.ts +5 -5
- package/dist/render/meta.js +118 -37
- package/dist/render/text.d.ts +22 -0
- package/dist/render/text.js +163 -0
- package/dist/render/theme.js +4 -1
- package/dist/run.d.ts +4 -2
- package/dist/run.js +96 -50
- package/dist/status.d.ts +28 -0
- package/dist/status.js +63 -0
- package/dist/version.d.ts +2 -2
- package/dist/version.js +5 -2
- package/package.json +17 -5
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { runCli } from "./run.js";
|
|
3
|
-
|
|
3
|
+
import { playSignalPulse, shouldAnimateSignalPulse, signalPulseLabel, } from "./status.js";
|
|
4
|
+
const argv = process.argv.slice(2);
|
|
5
|
+
const resultPromise = runCli(argv);
|
|
6
|
+
const pulsePromise = shouldAnimateSignalPulse(argv, {
|
|
7
|
+
stdoutIsTTY: Boolean(process.stdout.isTTY),
|
|
8
|
+
stderrIsTTY: Boolean(process.stderr.isTTY),
|
|
9
|
+
stderrColumns: process.stderr.columns,
|
|
10
|
+
env: process.env,
|
|
11
|
+
})
|
|
12
|
+
? playSignalPulse(process.stderr, {
|
|
13
|
+
ascii: argv.includes("--ascii"),
|
|
14
|
+
label: signalPulseLabel(argv),
|
|
15
|
+
})
|
|
16
|
+
: Promise.resolve();
|
|
17
|
+
// Await both concurrently so an unexpected command failure is observed
|
|
18
|
+
// immediately instead of becoming an unhandled rejection during the pulse.
|
|
19
|
+
const [result] = await Promise.all([resultPromise, pulsePromise]);
|
|
4
20
|
if (result.stdout)
|
|
5
21
|
process.stdout.write(result.stdout);
|
|
6
22
|
if (result.stderr)
|
package/dist/model.d.ts
CHANGED
|
@@ -9,10 +9,13 @@ export type Warning = {
|
|
|
9
9
|
severity: WarningSeverity;
|
|
10
10
|
text: string;
|
|
11
11
|
};
|
|
12
|
+
export type SourceType = "Docs" | "Datasheet" | "Manual" | "Schematic" | "Pinout";
|
|
12
13
|
export type Source = {
|
|
13
14
|
title: string;
|
|
14
15
|
url: string;
|
|
15
16
|
official: boolean;
|
|
17
|
+
/** What the source documents, used to prefer an exact pin map for wiring. */
|
|
18
|
+
type?: SourceType;
|
|
16
19
|
};
|
|
17
20
|
export type Pin = {
|
|
18
21
|
/** Physical pin number as printed or conventionally counted on the board. */
|
package/dist/render/board.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { ChalkInstance } from "chalk";
|
|
2
2
|
import type { Board } from "../model.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type CharSet } from "./chars.js";
|
|
4
|
+
export { wrapText } from "./text.js";
|
|
4
5
|
export type RenderOptions = {
|
|
5
6
|
chalk: ChalkInstance;
|
|
6
7
|
chars: CharSet;
|
|
7
8
|
width: number;
|
|
8
9
|
compact: boolean;
|
|
10
|
+
/** Show every alternate pin function, in addition to always-visible notes. */
|
|
11
|
+
details?: boolean;
|
|
9
12
|
};
|
|
10
|
-
export declare function wrapText(text: string, width: number, indent: string): string[];
|
|
11
13
|
export declare function renderWarnings(board: Board, opts: RenderOptions): string[];
|
|
12
14
|
export declare function renderBoard(board: Board, opts: RenderOptions): string;
|
package/dist/render/board.js
CHANGED
|
@@ -1,140 +1,187 @@
|
|
|
1
|
+
import { asciiChars } from "./chars.js";
|
|
2
|
+
import { normalizeWidth, padEndVisible, padStartVisible, toAscii, visibleWidth, wrapText, } from "./text.js";
|
|
1
3
|
import { pinStyle, warningStyle } from "./theme.js";
|
|
4
|
+
export { wrapText } from "./text.js";
|
|
2
5
|
const INDENT = " ";
|
|
3
|
-
|
|
4
|
-
|
|
6
|
+
function renderWidth(opts) {
|
|
7
|
+
return normalizeWidth(opts.width);
|
|
8
|
+
}
|
|
9
|
+
function finish(text, opts) {
|
|
10
|
+
return opts.chars === asciiChars ? toAscii(text) : text;
|
|
11
|
+
}
|
|
12
|
+
/** Label plus its primary alternate function and safety-note markers. */
|
|
13
|
+
function displayLabel(pin, ch) {
|
|
5
14
|
const fn = pin.functions?.[0];
|
|
6
15
|
const base = fn && fn !== pin.label ? `${pin.label} / ${fn}` : pin.label;
|
|
7
|
-
|
|
16
|
+
const reserved = pin.category === "reserved" ? " *" : "";
|
|
17
|
+
const noted = pin.notes?.length ? ` ${ch.note ?? "^"}` : "";
|
|
18
|
+
return `${base}${reserved}${noted}`;
|
|
8
19
|
}
|
|
9
20
|
function pinDetails(pin) {
|
|
10
21
|
return pin.voltage ? `${pin.category}, ${pin.voltage}` : pin.category;
|
|
11
22
|
}
|
|
12
|
-
export function wrapText(text, width, indent) {
|
|
13
|
-
const usable = Math.max(20, width - indent.length);
|
|
14
|
-
const words = text.split(/\s+/);
|
|
15
|
-
const lines = [];
|
|
16
|
-
let line = "";
|
|
17
|
-
for (const word of words) {
|
|
18
|
-
if (line && line.length + 1 + word.length > usable) {
|
|
19
|
-
lines.push(indent + line);
|
|
20
|
-
line = word;
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
line = line ? `${line} ${word}` : word;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (line)
|
|
27
|
-
lines.push(indent + line);
|
|
28
|
-
return lines;
|
|
29
|
-
}
|
|
30
23
|
function byPosition(header, column, row) {
|
|
31
|
-
return header.pins.find((
|
|
24
|
+
return header.pins.find((pin) => pin.position.column === column && pin.position.row === row);
|
|
32
25
|
}
|
|
33
26
|
/** Bordered two-column diagram: label | pin | pin | label. */
|
|
34
27
|
function renderDualRowTable(header, opts) {
|
|
35
28
|
const { chalk: c, chars: ch } = opts;
|
|
36
29
|
const rows = [];
|
|
37
|
-
for (let
|
|
38
|
-
rows.push([byPosition(header, 1,
|
|
30
|
+
for (let row = 1; row <= header.layout.rows; row += 1) {
|
|
31
|
+
rows.push([byPosition(header, 1, row), byPosition(header, 2, row)]);
|
|
39
32
|
}
|
|
40
|
-
const leftWidth = Math.max(...rows.map(([
|
|
41
|
-
const rightWidth = Math.max(...rows.map(([,
|
|
42
|
-
const numWidth = Math.max(...header.pins.map((
|
|
43
|
-
const
|
|
44
|
-
|
|
33
|
+
const leftWidth = Math.max(...rows.map(([left]) => (left ? visibleWidth(displayLabel(left, ch)) : 0)), 4);
|
|
34
|
+
const rightWidth = Math.max(...rows.map(([, right]) => (right ? visibleWidth(displayLabel(right, ch)) : 0)), 4);
|
|
35
|
+
const numWidth = Math.max(...header.pins.map((pin) => String(pin.physical).length), 2);
|
|
36
|
+
const horizontal = ch.horizontal;
|
|
37
|
+
const segment = (width) => horizontal.repeat(width + 2);
|
|
38
|
+
const top = `${INDENT}${ch.topLeft}${segment(leftWidth)}${ch.topJoin}${segment(numWidth)}${ch.topJoin}${segment(numWidth)}${ch.topJoin}${segment(rightWidth)}${ch.topRight}`;
|
|
39
|
+
if (visibleWidth(top) > renderWidth(opts))
|
|
45
40
|
return undefined;
|
|
46
|
-
const
|
|
47
|
-
const seg = (n) => h.repeat(n + 2);
|
|
48
|
-
const lines = [];
|
|
49
|
-
lines.push(`${INDENT}${ch.topLeft}${seg(leftWidth)}${ch.topJoin}${seg(numWidth)}${ch.topJoin}${seg(numWidth)}${ch.topJoin}${seg(rightWidth)}${ch.topRight}`);
|
|
41
|
+
const lines = [top];
|
|
50
42
|
for (const [left, right] of rows) {
|
|
51
|
-
const leftLabel = left
|
|
52
|
-
|
|
43
|
+
const leftLabel = left
|
|
44
|
+
? pinStyle(c, left)(padEndVisible(displayLabel(left, ch), leftWidth))
|
|
45
|
+
: " ".repeat(leftWidth);
|
|
46
|
+
const rightLabel = right
|
|
47
|
+
? pinStyle(c, right)(padEndVisible(displayLabel(right, ch), rightWidth))
|
|
48
|
+
: " ".repeat(rightWidth);
|
|
53
49
|
const leftNum = left ? String(left.physical).padStart(numWidth) : " ".repeat(numWidth);
|
|
54
50
|
const rightNum = right ? String(right.physical).padStart(numWidth) : " ".repeat(numWidth);
|
|
55
51
|
lines.push(`${INDENT}${ch.vertical} ${leftLabel} ${ch.vertical} ${leftNum} ${ch.vertical} ${rightNum} ${ch.vertical} ${rightLabel} ${ch.vertical}`);
|
|
56
52
|
}
|
|
57
|
-
lines.push(`${INDENT}${ch.bottomLeft}${
|
|
53
|
+
lines.push(`${INDENT}${ch.bottomLeft}${segment(leftWidth)}${ch.bottomJoin}${segment(numWidth)}${ch.bottomJoin}${segment(numWidth)}${ch.bottomJoin}${segment(rightWidth)}${ch.bottomRight}`);
|
|
58
54
|
return lines;
|
|
59
55
|
}
|
|
60
56
|
/** Borderless two-column pairs for --compact / narrower terminals. */
|
|
61
57
|
function renderDualRowCompact(header, opts) {
|
|
62
58
|
const { chalk: c, chars: ch } = opts;
|
|
63
59
|
const rows = [];
|
|
64
|
-
for (let
|
|
65
|
-
rows.push([byPosition(header, 1,
|
|
60
|
+
for (let row = 1; row <= header.layout.rows; row += 1) {
|
|
61
|
+
rows.push([byPosition(header, 1, row), byPosition(header, 2, row)]);
|
|
66
62
|
}
|
|
67
|
-
const leftWidth = Math.max(...rows.map(([
|
|
68
|
-
const rightWidth = Math.max(...rows.map(([,
|
|
69
|
-
const numWidth = Math.max(...header.pins.map((
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
63
|
+
const leftWidth = Math.max(...rows.map(([left]) => (left ? visibleWidth(displayLabel(left, ch)) : 0)), 4);
|
|
64
|
+
const rightWidth = Math.max(...rows.map(([, right]) => (right ? visibleWidth(displayLabel(right, ch)) : 0)), 4);
|
|
65
|
+
const numWidth = Math.max(...header.pins.map((pin) => String(pin.physical).length), 2);
|
|
66
|
+
const widest = `${INDENT}${" ".repeat(leftWidth)} ${" ".repeat(numWidth)} ${ch.pairSeparator} ${" ".repeat(numWidth)} ${" ".repeat(rightWidth)}`;
|
|
67
|
+
if (visibleWidth(widest) > renderWidth(opts))
|
|
72
68
|
return undefined;
|
|
73
69
|
return rows.map(([left, right]) => {
|
|
74
|
-
const leftLabel = left
|
|
75
|
-
|
|
70
|
+
const leftLabel = left
|
|
71
|
+
? pinStyle(c, left)(padStartVisible(displayLabel(left, ch), leftWidth))
|
|
72
|
+
: " ".repeat(leftWidth);
|
|
73
|
+
const rightLabel = right ? pinStyle(c, right)(displayLabel(right, ch)) : "";
|
|
76
74
|
const leftNum = left ? String(left.physical).padStart(numWidth) : " ".repeat(numWidth);
|
|
77
75
|
const rightNum = right ? String(right.physical).padEnd(numWidth) : " ".repeat(numWidth);
|
|
78
76
|
return `${INDENT}${leftLabel} ${leftNum} ${ch.pairSeparator} ${rightNum} ${rightLabel}`.trimEnd();
|
|
79
77
|
});
|
|
80
78
|
}
|
|
81
|
-
/** One pin per line
|
|
79
|
+
/** One pin per line, wrapping label and details independently when necessary. */
|
|
82
80
|
function renderPinList(header, opts) {
|
|
83
|
-
const { chalk: c } = opts;
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
81
|
+
const { chalk: c, chars: ch } = opts;
|
|
82
|
+
const width = renderWidth(opts);
|
|
83
|
+
const pins = [...header.pins].sort((left, right) => left.physical - right.physical);
|
|
84
|
+
const numWidth = Math.max(...pins.map((pin) => String(pin.physical).length), 2);
|
|
85
|
+
const labelWidth = Math.max(...pins.map((pin) => visibleWidth(displayLabel(pin, ch))), 4);
|
|
86
|
+
const detailWidth = Math.max(...pins.map((pin) => visibleWidth(pinDetails(pin))), 4);
|
|
87
|
+
const alignedWidth = visibleWidth(INDENT) + numWidth + 2 + labelWidth + 2 + detailWidth;
|
|
88
|
+
return pins.flatMap((pin) => {
|
|
89
|
+
const number = String(pin.physical).padStart(numWidth);
|
|
90
|
+
const label = displayLabel(pin, ch);
|
|
91
|
+
const details = pinDetails(pin);
|
|
92
|
+
if (alignedWidth <= width) {
|
|
93
|
+
return [
|
|
94
|
+
`${INDENT}${number} ${pinStyle(c, pin)(padEndVisible(label, labelWidth))} ${c.dim(details)}`,
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
const singleLine = `${INDENT}${number} ${label} ${details}`;
|
|
98
|
+
if (visibleWidth(singleLine) <= width) {
|
|
99
|
+
return [`${INDENT}${number} ${pinStyle(c, pin)(label)} ${c.dim(details)}`];
|
|
100
|
+
}
|
|
101
|
+
const prefix = `${INDENT}${number} `;
|
|
102
|
+
const continuation = " ".repeat(visibleWidth(prefix));
|
|
103
|
+
const labelLines = wrapText(label, width, prefix, continuation).map((line) => pinStyle(c, pin)(line));
|
|
104
|
+
const detailLines = wrapText(details, width, continuation, continuation).map((line) => c.dim(line));
|
|
105
|
+
return [...labelLines, ...detailLines];
|
|
90
106
|
});
|
|
91
107
|
}
|
|
92
108
|
/** Bordered single-column table: # | pin | details. */
|
|
93
109
|
function renderSingleColumnTable(header, opts) {
|
|
94
110
|
const { chalk: c, chars: ch } = opts;
|
|
95
|
-
const pins = [...header.pins].sort((
|
|
96
|
-
const numWidth = Math.max(...pins.map((
|
|
97
|
-
const labelWidth = Math.max(...pins.map((
|
|
98
|
-
const detailWidth = Math.max(...pins.map((
|
|
99
|
-
const
|
|
100
|
-
|
|
111
|
+
const pins = [...header.pins].sort((left, right) => left.physical - right.physical);
|
|
112
|
+
const numWidth = Math.max(...pins.map((pin) => String(pin.physical).length), 2);
|
|
113
|
+
const labelWidth = Math.max(...pins.map((pin) => visibleWidth(displayLabel(pin, ch))), 4);
|
|
114
|
+
const detailWidth = Math.max(...pins.map((pin) => visibleWidth(pinDetails(pin))), 4);
|
|
115
|
+
const horizontal = ch.horizontal;
|
|
116
|
+
const segment = (width) => horizontal.repeat(width + 2);
|
|
117
|
+
const top = `${INDENT}${ch.topLeft}${segment(numWidth)}${ch.topJoin}${segment(labelWidth)}${ch.topJoin}${segment(detailWidth)}${ch.topRight}`;
|
|
118
|
+
if (visibleWidth(top) > renderWidth(opts))
|
|
101
119
|
return undefined;
|
|
102
|
-
const
|
|
103
|
-
const seg = (n) => h.repeat(n + 2);
|
|
104
|
-
const lines = [];
|
|
105
|
-
lines.push(`${INDENT}${ch.topLeft}${seg(numWidth)}${ch.topJoin}${seg(labelWidth)}${ch.topJoin}${seg(detailWidth)}${ch.topRight}`);
|
|
120
|
+
const lines = [top];
|
|
106
121
|
for (const pin of pins) {
|
|
107
|
-
const label = pinStyle(c, pin)(displayLabel(pin)
|
|
108
|
-
lines.push(`${INDENT}${ch.vertical} ${String(pin.physical).padStart(numWidth)} ${ch.vertical} ${label} ${ch.vertical} ${pinDetails(pin)
|
|
122
|
+
const label = pinStyle(c, pin)(padEndVisible(displayLabel(pin, ch), labelWidth));
|
|
123
|
+
lines.push(`${INDENT}${ch.vertical} ${String(pin.physical).padStart(numWidth)} ${ch.vertical} ${label} ${ch.vertical} ${padEndVisible(pinDetails(pin), detailWidth)} ${ch.vertical}`);
|
|
124
|
+
}
|
|
125
|
+
lines.push(`${INDENT}${ch.bottomLeft}${segment(numWidth)}${ch.bottomJoin}${segment(labelWidth)}${ch.bottomJoin}${segment(detailWidth)}${ch.bottomRight}`);
|
|
126
|
+
return lines;
|
|
127
|
+
}
|
|
128
|
+
function renderPinAnnotations(header, opts) {
|
|
129
|
+
const { chalk: c, chars: ch } = opts;
|
|
130
|
+
const width = renderWidth(opts);
|
|
131
|
+
const pins = [...header.pins].sort((left, right) => left.physical - right.physical);
|
|
132
|
+
const annotated = pins.filter((pin) => Boolean(pin.notes?.length) || (Boolean(opts.details) && (pin.functions?.length ?? 0) > 1));
|
|
133
|
+
const lines = [];
|
|
134
|
+
if (annotated.length > 0) {
|
|
135
|
+
lines.push(c.bold("Pin notes"));
|
|
136
|
+
for (const pin of annotated) {
|
|
137
|
+
const details = [];
|
|
138
|
+
if (opts.details && (pin.functions?.length ?? 0) > 1) {
|
|
139
|
+
details.push(`additional functions: ${pin.functions.slice(1).join(", ")}`);
|
|
140
|
+
}
|
|
141
|
+
details.push(...(pin.notes ?? []));
|
|
142
|
+
const prefix = `${INDENT}${ch.note ?? "^"} Pin ${pin.physical}: `;
|
|
143
|
+
const continuation = " ".repeat(visibleWidth(prefix));
|
|
144
|
+
lines.push(...wrapText(details.join("; "), width, prefix, continuation));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!opts.details) {
|
|
148
|
+
const hiddenAlternates = pins.filter((pin) => (pin.functions?.length ?? 0) > 1).length;
|
|
149
|
+
if (hiddenAlternates > 0) {
|
|
150
|
+
if (lines.length > 0)
|
|
151
|
+
lines.push("");
|
|
152
|
+
lines.push(...wrapText(`${ch.info} ${hiddenAlternates} pin${hiddenAlternates === 1 ? " has" : "s have"} additional alternate functions; use --details to show them.`, width, INDENT, `${INDENT} `).map((line) => c.dim(line)));
|
|
153
|
+
}
|
|
109
154
|
}
|
|
110
|
-
lines.push(`${INDENT}${ch.bottomLeft}${seg(numWidth)}${ch.bottomJoin}${seg(labelWidth)}${ch.bottomJoin}${seg(detailWidth)}${ch.bottomRight}`);
|
|
111
155
|
return lines;
|
|
112
156
|
}
|
|
113
157
|
function renderHeader(header, opts) {
|
|
114
158
|
const { chalk: c } = opts;
|
|
115
|
-
const
|
|
116
|
-
lines.
|
|
159
|
+
const width = renderWidth(opts);
|
|
160
|
+
const lines = wrapText(header.name, width).map((line) => c.bold(line));
|
|
117
161
|
if (header.description) {
|
|
118
|
-
lines.push(...wrapText(header.description,
|
|
162
|
+
lines.push(...wrapText(header.description, width, INDENT).map((line) => c.dim(line)));
|
|
119
163
|
}
|
|
120
164
|
let table;
|
|
121
165
|
if (header.layout.columns === 2) {
|
|
122
166
|
table = opts.compact
|
|
123
|
-
? renderDualRowCompact(header, opts) ?? renderPinList(header, opts)
|
|
124
|
-
: renderDualRowTable(header, opts) ??
|
|
167
|
+
? (renderDualRowCompact(header, opts) ?? renderPinList(header, opts))
|
|
168
|
+
: (renderDualRowTable(header, opts) ??
|
|
125
169
|
renderDualRowCompact(header, opts) ??
|
|
126
|
-
renderPinList(header, opts);
|
|
170
|
+
renderPinList(header, opts));
|
|
127
171
|
}
|
|
128
172
|
else {
|
|
129
|
-
table = opts.compact
|
|
173
|
+
table = opts.compact
|
|
174
|
+
? renderPinList(header, opts)
|
|
175
|
+
: (renderSingleColumnTable(header, opts) ?? renderPinList(header, opts));
|
|
130
176
|
}
|
|
131
177
|
lines.push(...table);
|
|
178
|
+
const annotations = renderPinAnnotations(header, opts);
|
|
179
|
+
if (annotations.length > 0)
|
|
180
|
+
lines.push("", ...annotations);
|
|
132
181
|
return lines;
|
|
133
182
|
}
|
|
134
183
|
function renderLegend(board, opts) {
|
|
135
|
-
const { chalk: c } = opts;
|
|
136
|
-
if (c.level === 0)
|
|
137
|
-
return [];
|
|
184
|
+
const { chalk: c, chars: ch } = opts;
|
|
138
185
|
const order = [
|
|
139
186
|
"power",
|
|
140
187
|
"ground",
|
|
@@ -145,39 +192,68 @@ function renderLegend(board, opts) {
|
|
|
145
192
|
"reserved",
|
|
146
193
|
"nc",
|
|
147
194
|
];
|
|
148
|
-
const present = order.filter((
|
|
149
|
-
|
|
195
|
+
const present = order.filter((category) => board.headers.some((header) => header.pins.some((pin) => pin.category === category)));
|
|
196
|
+
if (present.length === 0)
|
|
197
|
+
return [];
|
|
198
|
+
const swatches = present.map((category) => {
|
|
150
199
|
const sample = board.headers
|
|
151
|
-
.flatMap((
|
|
152
|
-
.find((
|
|
153
|
-
return pinStyle(c, sample)(
|
|
200
|
+
.flatMap((header) => header.pins)
|
|
201
|
+
.find((pin) => pin.category === category);
|
|
202
|
+
return c.level === 0 ? category : pinStyle(c, sample)(category);
|
|
154
203
|
});
|
|
155
|
-
return
|
|
204
|
+
return wrapText(`Legend: ${swatches.join(` ${ch.itemSeparator ?? "|"} `)}`, renderWidth(opts)).map((line) => c.dim(line));
|
|
205
|
+
}
|
|
206
|
+
function warningMarker(ch, severity) {
|
|
207
|
+
switch (severity) {
|
|
208
|
+
case "danger":
|
|
209
|
+
return ch.danger ?? ch.warn;
|
|
210
|
+
case "warning":
|
|
211
|
+
return ch.warn;
|
|
212
|
+
case "info":
|
|
213
|
+
return ch.info;
|
|
214
|
+
}
|
|
156
215
|
}
|
|
157
216
|
export function renderWarnings(board, opts) {
|
|
158
217
|
const { chalk: c, chars: ch } = opts;
|
|
159
218
|
return board.warnings.flatMap((warning) => {
|
|
160
|
-
const marker = warning.severity
|
|
219
|
+
const marker = warningMarker(ch, warning.severity);
|
|
161
220
|
const style = warningStyle(c, warning.severity);
|
|
162
|
-
return wrapText(`${marker} ${warning.text}`, opts
|
|
221
|
+
return wrapText(`${marker} ${warning.text}`, renderWidth(opts), "", INDENT).map((line) => style(line));
|
|
163
222
|
});
|
|
164
223
|
}
|
|
224
|
+
function primarySourceFor(board) {
|
|
225
|
+
const typeScore = {
|
|
226
|
+
Pinout: 2000,
|
|
227
|
+
Schematic: 800,
|
|
228
|
+
Datasheet: 70,
|
|
229
|
+
Manual: 60,
|
|
230
|
+
Docs: 50,
|
|
231
|
+
};
|
|
232
|
+
return board.sources.reduce((best, source) => {
|
|
233
|
+
if (!best)
|
|
234
|
+
return source;
|
|
235
|
+
const score = (source.official ? 1000 : 0) + (source.type ? typeScore[source.type] : 0);
|
|
236
|
+
const bestScore = (best.official ? 1000 : 0) + (best.type ? typeScore[best.type] : 0);
|
|
237
|
+
return score > bestScore ? source : best;
|
|
238
|
+
}, undefined);
|
|
239
|
+
}
|
|
165
240
|
export function renderBoard(board, opts) {
|
|
166
241
|
const { chalk: c, chars: ch } = opts;
|
|
242
|
+
const width = renderWidth(opts);
|
|
167
243
|
const lines = [];
|
|
168
|
-
lines.push(`${
|
|
169
|
-
const primarySource = board
|
|
244
|
+
lines.push(...wrapText(`${board.name} ${ch.dash ?? "-"} ${board.manufacturer}`, width).map((line) => c.bold(line)));
|
|
245
|
+
const primarySource = primarySourceFor(board);
|
|
170
246
|
if (primarySource) {
|
|
171
247
|
const tag = primarySource.official ? "official" : "third-party";
|
|
172
|
-
lines.push(
|
|
248
|
+
lines.push(...wrapText(`Source: ${primarySource.title} (${tag}) ${ch.dash ?? "-"} see ph ${board.id} --source`, width, "", INDENT).map((line) => c.dim(line)));
|
|
173
249
|
}
|
|
174
250
|
if (board.revisionNote) {
|
|
175
|
-
lines.push(...wrapText(`${ch.info} ${board.revisionNote}`,
|
|
251
|
+
lines.push(...wrapText(`${ch.info} ${board.revisionNote}`, width, "", INDENT).map((line) => c.dim(line)));
|
|
176
252
|
}
|
|
177
253
|
lines.push(...renderWarnings(board, opts));
|
|
178
254
|
lines.push("");
|
|
179
255
|
if (board.headers.length === 0) {
|
|
180
|
-
lines.push(
|
|
256
|
+
lines.push(...wrapText(`No pinout data for this board yet ${ch.dash ?? "-"} see ph ${board.id} --source for documentation.`, width).map((line) => c.dim(line)));
|
|
181
257
|
}
|
|
182
258
|
board.headers.forEach((header, index) => {
|
|
183
259
|
if (index > 0)
|
|
@@ -185,13 +261,15 @@ export function renderBoard(board, opts) {
|
|
|
185
261
|
lines.push(...renderHeader(header, opts));
|
|
186
262
|
});
|
|
187
263
|
const legend = renderLegend(board, opts);
|
|
188
|
-
if (legend.length > 0)
|
|
189
|
-
lines.push("");
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
const hasReserved = board.headers.some((h) => h.pins.some((p) => p.category === "reserved"));
|
|
264
|
+
if (legend.length > 0)
|
|
265
|
+
lines.push("", ...legend);
|
|
266
|
+
const hasReserved = board.headers.some((header) => header.pins.some((pin) => pin.category === "reserved"));
|
|
193
267
|
if (hasReserved) {
|
|
194
|
-
lines.push(
|
|
268
|
+
lines.push(...wrapText(`* reserved / control pin ${ch.dash ?? "-"} see warnings above`, width).map((line) => c.dim(line)));
|
|
269
|
+
}
|
|
270
|
+
const hasNotes = board.headers.some((header) => header.pins.some((pin) => pin.notes?.length));
|
|
271
|
+
if (hasNotes) {
|
|
272
|
+
lines.push(...wrapText(`${ch.note ?? "^"} pin-specific note`, width).map((line) => c.dim(line)));
|
|
195
273
|
}
|
|
196
|
-
return lines.join("\n");
|
|
274
|
+
return finish(lines.join("\n"), opts);
|
|
197
275
|
}
|
package/dist/render/chars.d.ts
CHANGED
|
@@ -7,10 +7,14 @@ export type CharSet = {
|
|
|
7
7
|
vertical: string;
|
|
8
8
|
topJoin: string;
|
|
9
9
|
bottomJoin: string;
|
|
10
|
+
danger?: string;
|
|
10
11
|
warn: string;
|
|
11
12
|
info: string;
|
|
13
|
+
note?: string;
|
|
12
14
|
bullet: string;
|
|
13
15
|
pairSeparator: string;
|
|
16
|
+
dash?: string;
|
|
17
|
+
itemSeparator?: string;
|
|
14
18
|
};
|
|
15
19
|
export declare const unicodeChars: CharSet;
|
|
16
20
|
export declare const asciiChars: CharSet;
|
package/dist/render/chars.js
CHANGED
|
@@ -7,10 +7,14 @@ export const unicodeChars = {
|
|
|
7
7
|
vertical: "│",
|
|
8
8
|
topJoin: "┬",
|
|
9
9
|
bottomJoin: "┴",
|
|
10
|
+
danger: "✖",
|
|
10
11
|
warn: "⚠",
|
|
11
12
|
info: "ℹ",
|
|
13
|
+
note: "†",
|
|
12
14
|
bullet: "•",
|
|
13
15
|
pairSeparator: "│",
|
|
16
|
+
dash: "—",
|
|
17
|
+
itemSeparator: "·",
|
|
14
18
|
};
|
|
15
19
|
export const asciiChars = {
|
|
16
20
|
topLeft: "+",
|
|
@@ -21,8 +25,12 @@ export const asciiChars = {
|
|
|
21
25
|
vertical: "|",
|
|
22
26
|
topJoin: "+",
|
|
23
27
|
bottomJoin: "+",
|
|
28
|
+
danger: "X",
|
|
24
29
|
warn: "!",
|
|
25
30
|
info: "i",
|
|
31
|
+
note: "^",
|
|
26
32
|
bullet: "*",
|
|
27
33
|
pairSeparator: "|",
|
|
34
|
+
dash: "-",
|
|
35
|
+
itemSeparator: ".",
|
|
28
36
|
};
|
package/dist/render/meta.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ChalkInstance } from "chalk";
|
|
2
1
|
import type { Board } from "../model.js";
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
export declare function
|
|
6
|
-
export declare function
|
|
2
|
+
import type { RenderOptions } from "./board.js";
|
|
3
|
+
/** Width-responsive catalog/search table; cells wrap instead of being truncated. */
|
|
4
|
+
export declare function renderBoardTable(boardList: Board[], opts: RenderOptions): string;
|
|
5
|
+
export declare function renderSources(board: Board, opts: RenderOptions): string;
|
|
6
|
+
export declare function renderInfo(board: Board, opts: RenderOptions): string;
|