@lewishowles/cli-style 0.9.0 → 0.10.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/CHANGELOG.md +13 -0
- package/README.md +77 -1
- package/bin/cli-style.js +10 -0
- package/package.json +1 -1
- package/src/capability/terminal.js +98 -0
- package/src/catalogue/renderer-catalogue.js +62 -0
- package/src/cli/catalogue-command.js +108 -0
- package/src/cli/gallery-command.js +8 -1
- package/src/cli/help.js +5 -0
- package/src/cli/render-command.js +17 -2
- package/src/formatters/ansi.js +17 -1
- package/src/gallery/render-gallery.js +108 -2
- package/src/index.js +18 -2
- package/src/patterns/diff-block.js +333 -0
- package/src/primitives/chip.js +1 -1
- package/src/primitives/panel.js +6 -1
- package/src/primitives/sparkline.js +209 -0
- package/src/theme/colours.js +172 -36
|
@@ -4,6 +4,7 @@ import { commandResult } from "../patterns/command-result.js";
|
|
|
4
4
|
import { compactDataTable } from "../patterns/compact-data-table.js";
|
|
5
5
|
import { confirmationResult } from "../patterns/confirmation-result.js";
|
|
6
6
|
import { diagnosticReport } from "../patterns/diagnostic-report.js";
|
|
7
|
+
import { diffBlock } from "../patterns/diff-block.js";
|
|
7
8
|
import { nextStepBlock } from "../patterns/next-step-block.js";
|
|
8
9
|
import { taskSummary } from "../patterns/task-summary.js";
|
|
9
10
|
import { stripAnsi } from "../formatters/ansi.js";
|
|
@@ -15,6 +16,7 @@ import { panel } from "../primitives/panel.js";
|
|
|
15
16
|
import { progressBar } from "../primitives/progress-bar.js";
|
|
16
17
|
import { row } from "../primitives/row.js";
|
|
17
18
|
import { rowGroup } from "../primitives/row-group.js";
|
|
19
|
+
import { sparkline } from "../primitives/sparkline.js";
|
|
18
20
|
import { span } from "../primitives/span.js";
|
|
19
21
|
import { status } from "../primitives/status.js";
|
|
20
22
|
import { step, stepProgress, stepStates } from "../primitives/step-progress.js";
|
|
@@ -115,7 +117,7 @@ export const galleryVariants = ["current", "no-colour", "no-unicode", "plain"];
|
|
|
115
117
|
// Gallery sections available for focused output.
|
|
116
118
|
export const gallerySections = ["primitives", "patterns"];
|
|
117
119
|
|
|
118
|
-
//
|
|
120
|
+
// Fixtures available for focused output and interactive search.
|
|
119
121
|
export const galleryFixtures = [
|
|
120
122
|
"diagnostic-report",
|
|
121
123
|
"reporter",
|
|
@@ -127,6 +129,8 @@ export const galleryFixtures = [
|
|
|
127
129
|
"compact-data-table-narrow",
|
|
128
130
|
"confirmation-result",
|
|
129
131
|
"next-step-block",
|
|
132
|
+
"diff-block",
|
|
133
|
+
"sparkline",
|
|
130
134
|
];
|
|
131
135
|
|
|
132
136
|
/**
|
|
@@ -179,7 +183,11 @@ function renderVariant(variant, options, request) {
|
|
|
179
183
|
const sections = [];
|
|
180
184
|
|
|
181
185
|
if (request.fixture !== undefined) {
|
|
182
|
-
sections.push(
|
|
186
|
+
sections.push(
|
|
187
|
+
request.fixture === "sparkline"
|
|
188
|
+
? renderSparklineFixture(resolvedVariant.options)
|
|
189
|
+
: renderPatterns(resolvedVariant.options, request.fixture),
|
|
190
|
+
);
|
|
183
191
|
} else {
|
|
184
192
|
if (request.section === undefined || request.section === "primitives") {
|
|
185
193
|
sections.push(renderPrimitives(resolvedVariant.options));
|
|
@@ -434,6 +442,36 @@ function renderPatterns(options, fixture) {
|
|
|
434
442
|
options,
|
|
435
443
|
);
|
|
436
444
|
|
|
445
|
+
const diff = diffBlock(
|
|
446
|
+
{
|
|
447
|
+
lines: [
|
|
448
|
+
{
|
|
449
|
+
text: "render()",
|
|
450
|
+
type: "header",
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
newLineNumber: 102,
|
|
454
|
+
text: "return nextStep;",
|
|
455
|
+
type: "added",
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
oldLineNumber: 101,
|
|
459
|
+
text: "return currentStep;",
|
|
460
|
+
type: "removed",
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
newLineNumber: 100,
|
|
464
|
+
oldLineNumber: 100,
|
|
465
|
+
text: "const currentStep = getStep();",
|
|
466
|
+
type: "context",
|
|
467
|
+
},
|
|
468
|
+
],
|
|
469
|
+
path: "src/render.js",
|
|
470
|
+
title: "Renderer change",
|
|
471
|
+
},
|
|
472
|
+
options,
|
|
473
|
+
);
|
|
474
|
+
|
|
437
475
|
const reporter = createReporter(options);
|
|
438
476
|
|
|
439
477
|
reporter.divider("Project setup", "Claude + Codex");
|
|
@@ -561,6 +599,7 @@ function renderPatterns(options, fixture) {
|
|
|
561
599
|
"compact-data-table-narrow": ["Compact data table (narrow)", narrowCompactTable],
|
|
562
600
|
"confirmation-result": ["Confirmation result", confirmation],
|
|
563
601
|
"diagnostic-report": ["Diagnostic report", report],
|
|
602
|
+
"diff-block": ["Diff block", diff],
|
|
564
603
|
"next-step-block": ["Next-step block", nextStep],
|
|
565
604
|
reporter: ["Reporter", reporterOutput],
|
|
566
605
|
"task-summary": ["Task summary", summary],
|
|
@@ -594,6 +633,9 @@ function renderPatterns(options, fixture) {
|
|
|
594
633
|
"Audit finding",
|
|
595
634
|
frameExample(audit, options),
|
|
596
635
|
"",
|
|
636
|
+
"Diff block",
|
|
637
|
+
frameExample(diff, options),
|
|
638
|
+
"",
|
|
597
639
|
"Compact data table",
|
|
598
640
|
frameExample(compactTable, options),
|
|
599
641
|
"",
|
|
@@ -622,6 +664,18 @@ function renderPatternFixture(fixture, options) {
|
|
|
622
664
|
return [fixture[0], frameExample(fixture[1], options)].join("\n");
|
|
623
665
|
}
|
|
624
666
|
|
|
667
|
+
/**
|
|
668
|
+
* Render the focused sparkline fixture.
|
|
669
|
+
*
|
|
670
|
+
* @param {object} options
|
|
671
|
+
* Rendering options.
|
|
672
|
+
* @returns {string}
|
|
673
|
+
* Framed sparkline fixture output.
|
|
674
|
+
*/
|
|
675
|
+
function renderSparklineFixture(options) {
|
|
676
|
+
return ["Sparkline", frameExample(renderSparklineExample(options), options)].join("\n");
|
|
677
|
+
}
|
|
678
|
+
|
|
625
679
|
/**
|
|
626
680
|
* Add gallery-only framing around one rendered example.
|
|
627
681
|
*
|
|
@@ -668,6 +722,9 @@ function renderPrimitives(options) {
|
|
|
668
722
|
"Bar charts",
|
|
669
723
|
renderBarChartExample(options),
|
|
670
724
|
"",
|
|
725
|
+
"Sparklines",
|
|
726
|
+
renderSparklineExample(options),
|
|
727
|
+
"",
|
|
671
728
|
"Tables",
|
|
672
729
|
renderTableExample(options),
|
|
673
730
|
"",
|
|
@@ -806,6 +863,55 @@ function renderBarChartExample(options) {
|
|
|
806
863
|
});
|
|
807
864
|
}
|
|
808
865
|
|
|
866
|
+
/**
|
|
867
|
+
* Render representative sparkline sequences.
|
|
868
|
+
*
|
|
869
|
+
* @param {object} options
|
|
870
|
+
* Rendering options.
|
|
871
|
+
* @returns {string}
|
|
872
|
+
* Sparkline examples.
|
|
873
|
+
*/
|
|
874
|
+
function renderSparklineExample(options) {
|
|
875
|
+
const examples = [
|
|
876
|
+
{
|
|
877
|
+
label: "Rising",
|
|
878
|
+
tone: "success",
|
|
879
|
+
values: [1, 2, 3, 4, 5, 6],
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
label: "Falling",
|
|
883
|
+
tone: "danger",
|
|
884
|
+
values: [6, 5, 4, 3, 2, 1],
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
label: "Flat",
|
|
888
|
+
tone: "info",
|
|
889
|
+
values: [4, 4, 4, 4, 4, 4],
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
label: "Negative",
|
|
893
|
+
tone: "warning",
|
|
894
|
+
values: [-6, -4, -5, -2, -3, -1],
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
label: "Narrow mixed",
|
|
898
|
+
tone: "info",
|
|
899
|
+
values: [0, 10, -5, 5, 2, 8, -2],
|
|
900
|
+
width: 4,
|
|
901
|
+
},
|
|
902
|
+
];
|
|
903
|
+
|
|
904
|
+
return examples
|
|
905
|
+
.map((example) =>
|
|
906
|
+
sparkline({
|
|
907
|
+
...options,
|
|
908
|
+
...example,
|
|
909
|
+
width: example.width ?? 16,
|
|
910
|
+
}),
|
|
911
|
+
)
|
|
912
|
+
.join("\n");
|
|
913
|
+
}
|
|
914
|
+
|
|
809
915
|
/**
|
|
810
916
|
* Render common progress values and tones.
|
|
811
917
|
*
|
package/src/index.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
export { createCliStyle, profiles } from "./create-cli-style.js";
|
|
2
2
|
|
|
3
3
|
export { resolveProfile } from "./capability/resolve-profile.js";
|
|
4
|
-
export { resolveTerminalCapabilities } from "./capability/terminal.js";
|
|
4
|
+
export { resolveTerminalCapabilities, resolveTheme } from "./capability/terminal.js";
|
|
5
5
|
export { isProfile } from "./profiles/profiles.js";
|
|
6
6
|
|
|
7
7
|
export { renderHelp } from "./cli/help.js";
|
|
8
|
+
export { parseCatalogueRequest, renderCatalogue } from "./cli/catalogue-command.js";
|
|
9
|
+
export {
|
|
10
|
+
getRendererMetadata,
|
|
11
|
+
rendererCatalogue,
|
|
12
|
+
rendererNames,
|
|
13
|
+
} from "./catalogue/renderer-catalogue.js";
|
|
14
|
+
|
|
8
15
|
export {
|
|
9
16
|
adapterNames,
|
|
10
17
|
parseAdapterPathRequest,
|
|
11
18
|
renderAdapterPath,
|
|
12
19
|
} from "./cli/adapter-path-command.js";
|
|
13
|
-
|
|
20
|
+
|
|
21
|
+
export { parseRenderRequest, renderJsonInput } from "./cli/render-command.js";
|
|
22
|
+
|
|
14
23
|
export {
|
|
15
24
|
galleryFixtures,
|
|
16
25
|
gallerySections,
|
|
@@ -26,6 +35,7 @@ export { commandResult } from "./patterns/command-result.js";
|
|
|
26
35
|
export { compactDataTable } from "./patterns/compact-data-table.js";
|
|
27
36
|
export { confirmationResult } from "./patterns/confirmation-result.js";
|
|
28
37
|
export { diagnosticReport } from "./patterns/diagnostic-report.js";
|
|
38
|
+
export { diffBlock } from "./patterns/diff-block.js";
|
|
29
39
|
export { nextStepBlock } from "./patterns/next-step-block.js";
|
|
30
40
|
export { taskSummary } from "./patterns/task-summary.js";
|
|
31
41
|
export { barChart } from "./primitives/bar-chart.js";
|
|
@@ -36,10 +46,12 @@ export { panel } from "./primitives/panel.js";
|
|
|
36
46
|
export { progressBar } from "./primitives/progress-bar.js";
|
|
37
47
|
export { row } from "./primitives/row.js";
|
|
38
48
|
export { rowGroup } from "./primitives/row-group.js";
|
|
49
|
+
export { sparkline } from "./primitives/sparkline.js";
|
|
39
50
|
export { span } from "./primitives/span.js";
|
|
40
51
|
export { status } from "./primitives/status.js";
|
|
41
52
|
export { step, stepProgress, stepStates } from "./primitives/step-progress.js";
|
|
42
53
|
export { table } from "./primitives/table.js";
|
|
54
|
+
|
|
43
55
|
export {
|
|
44
56
|
createReporter,
|
|
45
57
|
renderGroup,
|
|
@@ -47,10 +59,12 @@ export {
|
|
|
47
59
|
renderReporterStatus,
|
|
48
60
|
renderSection,
|
|
49
61
|
} from "./reporters/create-reporter.js";
|
|
62
|
+
|
|
50
63
|
export {
|
|
51
64
|
chartColours,
|
|
52
65
|
chipColours,
|
|
53
66
|
colourTokens,
|
|
67
|
+
diffColours,
|
|
54
68
|
getColourToken,
|
|
55
69
|
getToneColour,
|
|
56
70
|
panelColours,
|
|
@@ -58,8 +72,10 @@ export {
|
|
|
58
72
|
resolveColourValue,
|
|
59
73
|
tableColours,
|
|
60
74
|
terminalColours,
|
|
75
|
+
themes,
|
|
61
76
|
toneColours,
|
|
62
77
|
} from "./theme/colours.js";
|
|
78
|
+
|
|
63
79
|
export {
|
|
64
80
|
getHighestSeverityResult,
|
|
65
81
|
getResultSymbol,
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { background, foreground, stripAnsi } from "../formatters/ansi.js";
|
|
2
|
+
import { profiles } from "../profiles/profiles.js";
|
|
3
|
+
import { diffColours } from "../theme/colours.js";
|
|
4
|
+
import { isNonEmptyString, isRecord, renderTitle } from "./helpers.js";
|
|
5
|
+
|
|
6
|
+
// Diff line types map stable prefixes and semantic tones for text profiles.
|
|
7
|
+
const diffLineTypes = {
|
|
8
|
+
added: {
|
|
9
|
+
prefix: "+ ",
|
|
10
|
+
tone: "success",
|
|
11
|
+
},
|
|
12
|
+
context: {
|
|
13
|
+
prefix: " ",
|
|
14
|
+
tone: "muted",
|
|
15
|
+
},
|
|
16
|
+
header: {
|
|
17
|
+
prefix: "@@ ",
|
|
18
|
+
tone: "info",
|
|
19
|
+
},
|
|
20
|
+
removed: {
|
|
21
|
+
prefix: "- ",
|
|
22
|
+
tone: "danger",
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Full-row fills apply only to colour-enabled terminal text profiles.
|
|
27
|
+
const backgroundProfiles = new Set([profiles.DIAGNOSTIC, profiles.HUMAN]);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Render a structured diff block for human and agent output.
|
|
31
|
+
*
|
|
32
|
+
* @param {object} diff
|
|
33
|
+
* Structured diff input.
|
|
34
|
+
* @param {object[]} diff.lines
|
|
35
|
+
* Structured diff lines.
|
|
36
|
+
* @param {number} diff.lines[].oldLineNumber
|
|
37
|
+
* Optional caller-supplied old line number.
|
|
38
|
+
* @param {number} diff.lines[].newLineNumber
|
|
39
|
+
* Optional caller-supplied new line number.
|
|
40
|
+
* @param {string} diff.path
|
|
41
|
+
* Optional affected path.
|
|
42
|
+
* @param {string} diff.title
|
|
43
|
+
* Optional block title.
|
|
44
|
+
* @param {object} options
|
|
45
|
+
* Rendering options.
|
|
46
|
+
* @returns {object|string}
|
|
47
|
+
* Structured JSON input or rendered diff block.
|
|
48
|
+
*/
|
|
49
|
+
export function diffBlock(diff, options = {}) {
|
|
50
|
+
if (options.profile === profiles.JSON) {
|
|
51
|
+
return diff;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!isRecord(diff)) {
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const lines = normaliseLines(diff.lines);
|
|
59
|
+
|
|
60
|
+
if (lines.length === 0) {
|
|
61
|
+
return "";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const gutterWidth = getGutterWidth(lines);
|
|
65
|
+
const renderedLines = lines.map((line) => renderLine(line, options, gutterWidth));
|
|
66
|
+
const summary = gutterWidth === undefined ? "" : renderChangeSummary(lines, options);
|
|
67
|
+
const metadata = renderMetadata(diff, options, summary);
|
|
68
|
+
|
|
69
|
+
if (options.profile === profiles.AGENT) {
|
|
70
|
+
const codeFence = ["```diff", renderedLines.join("\n"), "```"].join("\n");
|
|
71
|
+
|
|
72
|
+
return [...metadata, codeFence].join("\n\n");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return [...metadata, renderedLines.join("\n")].filter((section) => section !== "").join("\n\n");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Return structured lines with supported types and text.
|
|
80
|
+
*
|
|
81
|
+
* @param {*} lines
|
|
82
|
+
* Candidate diff lines.
|
|
83
|
+
* @returns {object[]}
|
|
84
|
+
* Valid diff lines.
|
|
85
|
+
*/
|
|
86
|
+
function normaliseLines(lines) {
|
|
87
|
+
if (!Array.isArray(lines)) {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return lines.filter(isValidLine).map((line) => ({
|
|
92
|
+
newLineNumber: normaliseLineNumber(line.newLineNumber),
|
|
93
|
+
oldLineNumber: normaliseLineNumber(line.oldLineNumber),
|
|
94
|
+
text: stripAnsi(line.text),
|
|
95
|
+
type: line.type,
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Return a valid positive line number or undefined.
|
|
101
|
+
*
|
|
102
|
+
* @param {*} value
|
|
103
|
+
* Candidate line number.
|
|
104
|
+
* @returns {number|undefined}
|
|
105
|
+
* Normalised line number.
|
|
106
|
+
*/
|
|
107
|
+
function normaliseLineNumber(value) {
|
|
108
|
+
return Number.isInteger(value) && value > 0 ? value : undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Return the stable gutter width for a fully numbered block.
|
|
113
|
+
*
|
|
114
|
+
* @param {object[]} lines
|
|
115
|
+
* Normalised diff lines.
|
|
116
|
+
* @returns {number|undefined}
|
|
117
|
+
* Widest line-number width, or undefined when gutters are unavailable.
|
|
118
|
+
*/
|
|
119
|
+
function getGutterWidth(lines) {
|
|
120
|
+
const codeLines = lines.filter((line) => line.type !== "header");
|
|
121
|
+
|
|
122
|
+
if (codeLines.length === 0 || !codeLines.every(hasRequiredLineNumbers)) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return Math.max(
|
|
127
|
+
...codeLines
|
|
128
|
+
.flatMap(getVisibleLineNumbers)
|
|
129
|
+
.filter((lineNumber) => lineNumber !== undefined)
|
|
130
|
+
.map((lineNumber) => String(lineNumber).length),
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Return line numbers visible for one diff type.
|
|
136
|
+
*
|
|
137
|
+
* @param {object} line
|
|
138
|
+
* Normalised diff line.
|
|
139
|
+
* @returns {number[]}
|
|
140
|
+
* Visible old and new line numbers.
|
|
141
|
+
*/
|
|
142
|
+
function getVisibleLineNumbers(line) {
|
|
143
|
+
if (line.type === "added") {
|
|
144
|
+
return [line.newLineNumber];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (line.type === "removed") {
|
|
148
|
+
return [line.oldLineNumber];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return [line.oldLineNumber, line.newLineNumber];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Check whether a line has the numbers required by its diff type.
|
|
156
|
+
*
|
|
157
|
+
* @param {object} line
|
|
158
|
+
* Normalised diff line.
|
|
159
|
+
* @returns {boolean}
|
|
160
|
+
* Whether the line can participate in a numbered gutter.
|
|
161
|
+
*/
|
|
162
|
+
function hasRequiredLineNumbers(line) {
|
|
163
|
+
if (line.type === "added") {
|
|
164
|
+
return line.newLineNumber !== undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (line.type === "removed") {
|
|
168
|
+
return line.oldLineNumber !== undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return line.oldLineNumber !== undefined && line.newLineNumber !== undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Check whether a candidate is a supported diff line.
|
|
176
|
+
*
|
|
177
|
+
* @param {*} line
|
|
178
|
+
* Candidate diff line.
|
|
179
|
+
* @returns {boolean}
|
|
180
|
+
* Whether the line can be rendered safely.
|
|
181
|
+
*/
|
|
182
|
+
function isValidLine(line) {
|
|
183
|
+
return isRecord(line) && Object.hasOwn(diffLineTypes, line.type) && typeof line.text === "string";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Render optional diff metadata for the active profile.
|
|
188
|
+
*
|
|
189
|
+
* @param {object} diff
|
|
190
|
+
* Structured diff input.
|
|
191
|
+
* @param {object} options
|
|
192
|
+
* Rendering options.
|
|
193
|
+
* @param {string} summary
|
|
194
|
+
* Optional added and removed line count.
|
|
195
|
+
* @returns {string[]}
|
|
196
|
+
* Rendered metadata sections.
|
|
197
|
+
*/
|
|
198
|
+
function renderMetadata(diff, options, summary) {
|
|
199
|
+
const metadata = [];
|
|
200
|
+
|
|
201
|
+
if (isNonEmptyString(diff.title)) {
|
|
202
|
+
const title = stripAnsi(diff.title);
|
|
203
|
+
|
|
204
|
+
metadata.push(renderTitle(formatMetadataValue(title, summary), options));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (isNonEmptyString(diff.path)) {
|
|
208
|
+
const path = stripAnsi(diff.path);
|
|
209
|
+
|
|
210
|
+
metadata.push(`Path: ${formatMetadataValue(path, summary, diff.title)}`);
|
|
211
|
+
} else if (!isNonEmptyString(diff.title) && summary !== "") {
|
|
212
|
+
metadata.push(summary);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return metadata;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Append a change summary to the first available metadata value.
|
|
220
|
+
*
|
|
221
|
+
* @param {string} value
|
|
222
|
+
* Metadata value.
|
|
223
|
+
* @param {string} summary
|
|
224
|
+
* Optional change summary.
|
|
225
|
+
* @param {*} title
|
|
226
|
+
* Title value used to avoid duplicating a summary on the path.
|
|
227
|
+
* @returns {string}
|
|
228
|
+
* Metadata value with optional summary.
|
|
229
|
+
*/
|
|
230
|
+
function formatMetadataValue(value, summary, title) {
|
|
231
|
+
return summary !== "" && !isNonEmptyString(title) ? `${value} ${summary}` : value;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Count added and removed lines already present in the block.
|
|
236
|
+
*
|
|
237
|
+
* @param {object[]} lines
|
|
238
|
+
* Normalised diff lines.
|
|
239
|
+
* @param {object} options
|
|
240
|
+
* Rendering options.
|
|
241
|
+
* @returns {string}
|
|
242
|
+
* Optional change summary.
|
|
243
|
+
*/
|
|
244
|
+
function renderChangeSummary(lines, options) {
|
|
245
|
+
const added = lines.filter((line) => line.type === "added").length;
|
|
246
|
+
const removed = lines.filter((line) => line.type === "removed").length;
|
|
247
|
+
|
|
248
|
+
if (added === 0 && removed === 0) {
|
|
249
|
+
return "";
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const profile = options.profile ?? profiles.HUMAN;
|
|
253
|
+
const addedValue = `+${added}`;
|
|
254
|
+
const removedValue = `-${removed}`;
|
|
255
|
+
|
|
256
|
+
if (options.colour !== true || !backgroundProfiles.has(profile)) {
|
|
257
|
+
return `(${addedValue} ${removedValue})`;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return `(${foreground(addedValue, "success", options)} ${foreground(removedValue, "danger", options)})`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Render one diff line with a stable semantic prefix.
|
|
265
|
+
*
|
|
266
|
+
* @param {object} line
|
|
267
|
+
* Normalised diff line.
|
|
268
|
+
* @param {object} options
|
|
269
|
+
* Rendering options.
|
|
270
|
+
* @param {number|undefined} gutterWidth
|
|
271
|
+
* Stable gutter width for the block.
|
|
272
|
+
* @returns {string}
|
|
273
|
+
* Rendered diff line.
|
|
274
|
+
*/
|
|
275
|
+
function renderLine(line, options, gutterWidth) {
|
|
276
|
+
const lineType = diffLineTypes[line.type];
|
|
277
|
+
const gutter = gutterWidth === undefined ? "" : renderGutter(line, gutterWidth);
|
|
278
|
+
const lineValue = `${gutter}${lineType.prefix}${line.text}`;
|
|
279
|
+
const profile = options.profile ?? profiles.HUMAN;
|
|
280
|
+
const fill = diffColours[line.type];
|
|
281
|
+
|
|
282
|
+
if (options.colour === true && backgroundProfiles.has(profile) && fill !== undefined) {
|
|
283
|
+
return background(foreground(lineValue, fill.foreground, options), fill.background, options);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const prefix = foreground(lineType.prefix, lineType.tone, options);
|
|
287
|
+
|
|
288
|
+
return `${gutter}${prefix}${line.text}`;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Render one stable old/new line-number gutter.
|
|
293
|
+
*
|
|
294
|
+
* @param {object} line
|
|
295
|
+
* Normalised diff line.
|
|
296
|
+
* @param {number} width
|
|
297
|
+
* Widest line-number width in the block.
|
|
298
|
+
* @returns {string}
|
|
299
|
+
* Rendered gutter.
|
|
300
|
+
*/
|
|
301
|
+
function renderGutter(line, width) {
|
|
302
|
+
const blank = " ".repeat(width);
|
|
303
|
+
const oldNumber = formatLineNumber(line.oldLineNumber, width);
|
|
304
|
+
const newNumber = formatLineNumber(line.newLineNumber, width);
|
|
305
|
+
|
|
306
|
+
if (line.type === "added") {
|
|
307
|
+
return `${blank} ${newNumber} `;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (line.type === "removed") {
|
|
311
|
+
return `${oldNumber} ${blank} `;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (line.type === "context" && line.oldLineNumber === line.newLineNumber) {
|
|
315
|
+
return `${oldNumber} ${blank} `;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return `${oldNumber} ${newNumber} `;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Pad one line number to the block gutter width.
|
|
323
|
+
*
|
|
324
|
+
* @param {number|undefined} value
|
|
325
|
+
* Line number to format.
|
|
326
|
+
* @param {number} width
|
|
327
|
+
* Gutter width.
|
|
328
|
+
* @returns {string}
|
|
329
|
+
* Right-aligned number or blank placeholder.
|
|
330
|
+
*/
|
|
331
|
+
function formatLineNumber(value, width) {
|
|
332
|
+
return value === undefined ? " ".repeat(width) : String(value).padStart(width, " ");
|
|
333
|
+
}
|
package/src/primitives/chip.js
CHANGED
|
@@ -21,7 +21,7 @@ const fallbackTone = "neutral";
|
|
|
21
21
|
export function chip(label, tone = fallbackTone, options = {}) {
|
|
22
22
|
const token = chipColours[tone] ?? chipColours[fallbackTone];
|
|
23
23
|
|
|
24
|
-
if (options.colour !== true) {
|
|
24
|
+
if (options.colour !== true || options.theme === "auto") {
|
|
25
25
|
return `[${label}]`;
|
|
26
26
|
}
|
|
27
27
|
|
package/src/primitives/panel.js
CHANGED
|
@@ -72,7 +72,12 @@ function renderLine(line, width, isTitle, options) {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
const renderedAccent = renderPanelColour(accent, options);
|
|
75
|
-
|
|
75
|
+
|
|
76
|
+
const renderedContent = foreground(
|
|
77
|
+
background(content, panelColours.background, options),
|
|
78
|
+
panelColours.body,
|
|
79
|
+
options,
|
|
80
|
+
);
|
|
76
81
|
|
|
77
82
|
return `${renderedAccent}${isTitle ? style(renderedContent, "bold", options) : renderedContent}`;
|
|
78
83
|
}
|