@kgdiff/cli 0.0.0-stage → 0.1.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 (113) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +198 -2
  3. package/dist/apply-command.d.ts +34 -0
  4. package/dist/apply-command.d.ts.map +1 -0
  5. package/dist/apply-command.js +135 -0
  6. package/dist/apply-command.js.map +1 -0
  7. package/dist/args.d.ts +96 -0
  8. package/dist/args.d.ts.map +1 -0
  9. package/dist/args.js +463 -0
  10. package/dist/args.js.map +1 -0
  11. package/dist/bin.d.ts +3 -0
  12. package/dist/bin.d.ts.map +1 -0
  13. package/dist/bin.js +17 -0
  14. package/dist/bin.js.map +1 -0
  15. package/dist/diff-command.d.ts +29 -0
  16. package/dist/diff-command.d.ts.map +1 -0
  17. package/dist/diff-command.js +236 -0
  18. package/dist/diff-command.js.map +1 -0
  19. package/dist/errors.d.ts +48 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +92 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/help.d.ts +11 -0
  24. package/dist/help.d.ts.map +1 -0
  25. package/dist/help.js +247 -0
  26. package/dist/help.js.map +1 -0
  27. package/dist/html.d.ts +53 -0
  28. package/dist/html.d.ts.map +1 -0
  29. package/dist/html.js +107 -0
  30. package/dist/html.js.map +1 -0
  31. package/dist/index.d.ts +25 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +24 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/inputs.d.ts +54 -0
  36. package/dist/inputs.d.ts.map +1 -0
  37. package/dist/inputs.js +204 -0
  38. package/dist/inputs.js.map +1 -0
  39. package/dist/io.d.ts +64 -0
  40. package/dist/io.d.ts.map +1 -0
  41. package/dist/io.js +2 -0
  42. package/dist/io.js.map +1 -0
  43. package/dist/locale.d.ts +58 -0
  44. package/dist/locale.d.ts.map +1 -0
  45. package/dist/locale.js +121 -0
  46. package/dist/locale.js.map +1 -0
  47. package/dist/main.d.ts +10 -0
  48. package/dist/main.d.ts.map +1 -0
  49. package/dist/main.js +51 -0
  50. package/dist/main.js.map +1 -0
  51. package/dist/markdown.d.ts +30 -0
  52. package/dist/markdown.d.ts.map +1 -0
  53. package/dist/markdown.js +93 -0
  54. package/dist/markdown.js.map +1 -0
  55. package/dist/model.d.ts +63 -0
  56. package/dist/model.d.ts.map +1 -0
  57. package/dist/model.js +151 -0
  58. package/dist/model.js.map +1 -0
  59. package/dist/node-io.d.ts +12 -0
  60. package/dist/node-io.d.ts.map +1 -0
  61. package/dist/node-io.js +135 -0
  62. package/dist/node-io.js.map +1 -0
  63. package/dist/output.d.ts +27 -0
  64. package/dist/output.d.ts.map +1 -0
  65. package/dist/output.js +50 -0
  66. package/dist/output.js.map +1 -0
  67. package/dist/overwrite.d.ts +14 -0
  68. package/dist/overwrite.d.ts.map +1 -0
  69. package/dist/overwrite.js +55 -0
  70. package/dist/overwrite.js.map +1 -0
  71. package/dist/report.d.ts +52 -0
  72. package/dist/report.d.ts.map +1 -0
  73. package/dist/report.js +64 -0
  74. package/dist/report.js.map +1 -0
  75. package/dist/style.d.ts +46 -0
  76. package/dist/style.d.ts.map +1 -0
  77. package/dist/style.js +140 -0
  78. package/dist/style.js.map +1 -0
  79. package/dist/summary.d.ts +40 -0
  80. package/dist/summary.d.ts.map +1 -0
  81. package/dist/summary.js +352 -0
  82. package/dist/summary.js.map +1 -0
  83. package/dist/version.d.ts +6 -0
  84. package/dist/version.d.ts.map +1 -0
  85. package/dist/version.js +19 -0
  86. package/dist/version.js.map +1 -0
  87. package/dist/width.d.ts +45 -0
  88. package/dist/width.d.ts.map +1 -0
  89. package/dist/width.js +232 -0
  90. package/dist/width.js.map +1 -0
  91. package/package.json +56 -3
  92. package/src/apply-command.ts +201 -0
  93. package/src/args.ts +605 -0
  94. package/src/bin.ts +22 -0
  95. package/src/diff-command.ts +299 -0
  96. package/src/errors.ts +131 -0
  97. package/src/help.ts +286 -0
  98. package/src/html.ts +145 -0
  99. package/src/index.ts +71 -0
  100. package/src/inputs.ts +264 -0
  101. package/src/io.ts +68 -0
  102. package/src/locale.ts +177 -0
  103. package/src/main.ts +56 -0
  104. package/src/markdown.ts +119 -0
  105. package/src/model.ts +181 -0
  106. package/src/node-io.ts +146 -0
  107. package/src/output.ts +81 -0
  108. package/src/overwrite.ts +75 -0
  109. package/src/report.ts +101 -0
  110. package/src/style.ts +164 -0
  111. package/src/summary.ts +440 -0
  112. package/src/version.ts +21 -0
  113. package/src/width.ts +222 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Outputs never overwrite inputs, and two outputs never share a file — under any spelling:
3
+ * case variants on case-insensitive file systems, symlinks and hard links all resolve to the
4
+ * same (device, inode) pair; files that do not exist yet compare by canonical directory and
5
+ * case-folded name.
6
+ */
7
+ import { basename, dirname, join } from 'node:path';
8
+ import { STDIO } from './args.js';
9
+ import { CliError } from './errors.js';
10
+ import { resolvePath } from './inputs.js';
11
+ import { sanitize } from './width.js';
12
+ /** Identity of the file at `path`: `inode:dev:ino` when it exists, else a canonical path. */
13
+ export async function fileIdentity(io, path) {
14
+ const absolute = resolvePath(io, path);
15
+ const stat = await io.fs.stat(absolute);
16
+ if (stat)
17
+ return `inode:${stat.dev}:${stat.ino}`;
18
+ let directory = dirname(absolute);
19
+ try {
20
+ directory = await io.fs.realpath(directory);
21
+ }
22
+ catch {
23
+ // A missing directory makes the write fail later with its own message.
24
+ }
25
+ // Case-folded: on case-insensitive file systems `A.json` and `a.json` are one file. On
26
+ // case-sensitive ones this can only refuse two outputs that differ in case alone.
27
+ return `path:${join(directory, basename(absolute)).toLowerCase()}`;
28
+ }
29
+ /**
30
+ * @throws CliError `usage` when an output is one of the inputs or two outputs are one file.
31
+ */
32
+ export async function checkNoOverwrite(io, inputs, outputs) {
33
+ const used = (p) => p.path !== undefined && p.path !== STDIO;
34
+ const inputIds = new Map();
35
+ for (const input of inputs.filter(used)) {
36
+ // Missing inputs are reported when they are read.
37
+ if (await io.fs.stat(resolvePath(io, input.path))) {
38
+ inputIds.set(await fileIdentity(io, input.path), input);
39
+ }
40
+ }
41
+ const outputIds = new Map();
42
+ for (const output of outputs.filter(used)) {
43
+ const id = await fileIdentity(io, output.path);
44
+ const input = inputIds.get(id);
45
+ if (input) {
46
+ throw new CliError('usage', `${output.role} "${sanitize(output.path)}" is the input ${input.role} "${sanitize(input.path)}"; kgdiff never overwrites its inputs.`, 'Write to another file, then move it into place if you want to replace the input.');
47
+ }
48
+ const other = outputIds.get(id);
49
+ if (other) {
50
+ throw new CliError('usage', `${other.role} and ${output.role} both write to "${sanitize(output.path)}".`, 'Give each output its own file.');
51
+ }
52
+ outputIds.set(id, output);
53
+ }
54
+ }
55
+ //# sourceMappingURL=overwrite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overwrite.js","sourceRoot":"","sources":["../src/overwrite.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Build the `ReportPayload` embedded in HTML reports: diff (`diffToJSON`) + layout
3
+ * (`layoutToJSON`) + meta + initial view preferences. `@kgdiff/layout` and `node:zlib` are
4
+ * loaded on demand, so text outputs never pay for them.
5
+ */
6
+ import { type DiffResult, type Locale } from '@kgdiff/core';
7
+ import type { ReportPayload, ThemePreference, ViewerMode } from '@kgdiff/viewer';
8
+ export interface ReportInfo {
9
+ readonly title: string;
10
+ /** e.g. `kgdiff 0.1.0`. */
11
+ readonly generator: string;
12
+ /** ISO timestamp; omitted for reproducible reports. */
13
+ readonly generatedAt?: string;
14
+ readonly before: {
15
+ readonly name: string;
16
+ readonly format?: string;
17
+ };
18
+ readonly after: {
19
+ readonly name: string;
20
+ readonly format?: string;
21
+ };
22
+ readonly view: {
23
+ readonly mode?: ViewerMode;
24
+ readonly theme?: ThemePreference;
25
+ readonly locale: Locale;
26
+ };
27
+ }
28
+ /** Lay out the diff and assemble the payload. Deterministic apart from `generatedAt`. */
29
+ export declare function buildReportPayload(diff: DiffResult, info: ReportInfo): Promise<ReportPayload>;
30
+ /**
31
+ * Deterministic gzip (level 9). The header's OS byte is normalized to 255 ("unknown") so the
32
+ * bytes do not depend on the platform the report was generated on.
33
+ */
34
+ export declare function createGzip(): Promise<(data: Uint8Array) => Uint8Array>;
35
+ /**
36
+ * Whether a report should inline the compute add-on: only when the layout covers just the
37
+ * changes (large graphs), where the viewer may need to lay out collapsed groups the user
38
+ * expands. Full layouts need no browser-side computation, so small reports stay small.
39
+ */
40
+ export declare function needsCompute(payload: ReportPayload): boolean;
41
+ export interface ReportBundles {
42
+ /** The standalone viewer bundle. */
43
+ readonly main: string;
44
+ /**
45
+ * Loads the compute add-on; called only when `needsCompute`. Returning `undefined` (e.g.
46
+ * `--no-compute`) leaves it out.
47
+ */
48
+ readonly compute: () => Promise<string | undefined>;
49
+ }
50
+ /** Full HTML document for a diff. */
51
+ export declare function buildReportHtml(diff: DiffResult, info: ReportInfo, bundles: ReportBundles, description: string): Promise<string>;
52
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,KAAK,UAAU,EAAc,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrE,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,QAAQ,CAAC,IAAI,EAAE;QACb,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;QAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC;QACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAED,yFAAyF;AACzF,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,aAAa,CAAC,CAsBxB;AAED;;;GAGG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,CAAC,IAAI,EAAE,UAAU,KAAK,UAAU,CAAC,CAO5E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAE5D;AAED,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CACrD;AAED,qCAAqC;AACrC,wBAAsB,eAAe,CACnC,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,aAAa,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,CAUjB"}
package/dist/report.js ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Build the `ReportPayload` embedded in HTML reports: diff (`diffToJSON`) + layout
3
+ * (`layoutToJSON`) + meta + initial view preferences. `@kgdiff/layout` and `node:zlib` are
4
+ * loaded on demand, so text outputs never pay for them.
5
+ */
6
+ import { diffToJSON } from '@kgdiff/core';
7
+ import { renderReportHtml } from './html.js';
8
+ /** Lay out the diff and assemble the payload. Deterministic apart from `generatedAt`. */
9
+ export async function buildReportPayload(diff, info) {
10
+ const { layoutDiff, layoutToJSON } = await import('@kgdiff/layout');
11
+ const layout = layoutToJSON(layoutDiff(diff));
12
+ const { mode, theme, locale } = info.view;
13
+ return {
14
+ format: 'kgdiff.report',
15
+ version: 1,
16
+ diff: diffToJSON(diff),
17
+ layout,
18
+ meta: {
19
+ title: info.title,
20
+ generator: info.generator,
21
+ ...(info.generatedAt !== undefined ? { generatedAt: info.generatedAt } : {}),
22
+ before: info.before,
23
+ after: info.after,
24
+ },
25
+ view: {
26
+ ...(mode !== undefined ? { mode } : {}),
27
+ ...(theme !== undefined ? { theme } : {}),
28
+ locale,
29
+ },
30
+ };
31
+ }
32
+ /**
33
+ * Deterministic gzip (level 9). The header's OS byte is normalized to 255 ("unknown") so the
34
+ * bytes do not depend on the platform the report was generated on.
35
+ */
36
+ export async function createGzip() {
37
+ const { gzipSync, constants } = await import('node:zlib');
38
+ return (data) => {
39
+ const out = gzipSync(data, { level: constants.Z_BEST_COMPRESSION });
40
+ out[9] = 0xff;
41
+ return out;
42
+ };
43
+ }
44
+ /**
45
+ * Whether a report should inline the compute add-on: only when the layout covers just the
46
+ * changes (large graphs), where the viewer may need to lay out collapsed groups the user
47
+ * expands. Full layouts need no browser-side computation, so small reports stay small.
48
+ */
49
+ export function needsCompute(payload) {
50
+ return payload.layout.scope === 'changes';
51
+ }
52
+ /** Full HTML document for a diff. */
53
+ export async function buildReportHtml(diff, info, bundles, description) {
54
+ const payload = await buildReportPayload(diff, info);
55
+ const compute = needsCompute(payload) ? await bundles.compute() : undefined;
56
+ return renderReportHtml({
57
+ payload,
58
+ bundle: bundles.main,
59
+ ...(compute !== undefined ? { compute } : {}),
60
+ description,
61
+ gzip: await createGzip(),
62
+ });
63
+ }
64
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAmB,UAAU,EAAe,MAAM,cAAc,CAAC;AAExE,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAiB7C,yFAAyF;AACzF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAAgB,EAChB,IAAgB;IAEhB,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9C,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC;IAC1C,OAAO;QACL,MAAM,EAAE,eAAe;QACvB,OAAO,EAAE,CAAC;QACV,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC;QACtB,MAAM;QACN,IAAI,EAAE;YACJ,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,GAAG,CAAC,IAAI,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5E,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB;QACD,IAAI,EAAE;YACJ,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvC,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,MAAM;SACP;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IAC1D,OAAO,CAAC,IAAI,EAAE,EAAE;QACd,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,kBAAkB,EAAE,CAAC,CAAC;QACpE,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;QACd,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,OAAsB;IACjD,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC;AAC5C,CAAC;AAYD,qCAAqC;AACrC,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAgB,EAChB,IAAgB,EAChB,OAAsB,EACtB,WAAmB;IAEnB,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5E,OAAO,gBAAgB,CAAC;QACtB,OAAO;QACP,MAAM,EAAE,OAAO,CAAC,IAAI;QACpB,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,WAAW;QACX,IAAI,EAAE,MAAM,UAAU,EAAE;KACzB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Terminal colors: support detection (`--color`, TTY, `NO_COLOR`, `FORCE_COLOR`, `COLORTERM`,
3
+ * `TERM`) and a tiny painter for the meaning families of docs/visual-language.md.
4
+ *
5
+ * Colors are the visual-language family colors: the dark ("night") base colors on dark or
6
+ * unknown backgrounds, the light ("paper") strong colors when `COLORFGBG` reports a light
7
+ * background. Truecolor terminals get the exact values, 256-color terminals the nearest xterm
8
+ * palette entry, others the closest of the 16 basic colors.
9
+ */
10
+ export type ColorMode = 'auto' | 'always' | 'never';
11
+ /** 0 = no color, 1 = 16 colors, 2 = 256 colors, 3 = truecolor. */
12
+ export type ColorLevel = 0 | 1 | 2 | 3;
13
+ export type Env = Readonly<Record<string, string | undefined>>;
14
+ /** Color tones used by the CLI: the five meaning families plus UI accents. */
15
+ export type Tone = 'added' | 'removed' | 'content' | 'structure' | 'identity' | 'accent';
16
+ /** Color depth the terminal supports, assuming color is wanted at all. */
17
+ export declare function terminalDepth(env: Env): ColorLevel;
18
+ /**
19
+ * Resolve the color level.
20
+ *
21
+ * - `never` → 0.
22
+ * - `always` → the level `FORCE_COLOR` names (1–3), else the terminal depth (at least 16
23
+ * colors), even when `NO_COLOR` is set.
24
+ * - `auto`: `FORCE_COLOR` wins (`0`/`false` disables, `1`–`3` force exactly that level, other
25
+ * values the terminal depth), then a non-empty `NO_COLOR` disables, then color only when the
26
+ * stream is a TTY and `TERM` is not `dumb`.
27
+ */
28
+ export declare function resolveColorLevel(mode: ColorMode, env: Env, isTTY: boolean): ColorLevel;
29
+ /** True when `COLORFGBG` (rxvt, Konsole, iTerm2…) reports a light background. */
30
+ export declare function isLightBackground(env: Env): boolean;
31
+ type Rgb = readonly [number, number, number];
32
+ /** Nearest xterm-256 palette index (6×6×6 cube or gray ramp) by squared RGB distance. */
33
+ export declare function ansi256([r, g, b]: Rgb): number;
34
+ /** Applies colors and text attributes; every method is the identity at level 0. */
35
+ export interface Painter {
36
+ readonly level: ColorLevel;
37
+ tone(tone: Tone, text: string): string;
38
+ bold(text: string): string;
39
+ dim(text: string): string;
40
+ }
41
+ /** Create a painter for `level`. `light` picks colors that read on light backgrounds. */
42
+ export declare function createPainter(level: ColorLevel, light?: boolean): Painter;
43
+ /** Remove SGR escape sequences (for measuring or comparing painted text). */
44
+ export declare function stripAnsi(text: string): string;
45
+ export {};
46
+ //# sourceMappingURL=style.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEpD,kEAAkE;AAClE,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEvC,MAAM,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAE/D,8EAA8E;AAC9E,MAAM,MAAM,IAAI,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC;AAgBzF,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,UAAU,CAUlD;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,GAAG,UAAU,CAYvF;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAInD;AAED,KAAK,GAAG,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAkC7C,yFAAyF;AACzF,wBAAgB,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,MAAM,CAS9C;AAED,mFAAmF;AACnF,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACvC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3B;AAED,yFAAyF;AACzF,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,UAAQ,GAAG,OAAO,CAwBvE;AAED,6EAA6E;AAC7E,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG9C"}
package/dist/style.js ADDED
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Terminal colors: support detection (`--color`, TTY, `NO_COLOR`, `FORCE_COLOR`, `COLORTERM`,
3
+ * `TERM`) and a tiny painter for the meaning families of docs/visual-language.md.
4
+ *
5
+ * Colors are the visual-language family colors: the dark ("night") base colors on dark or
6
+ * unknown backgrounds, the light ("paper") strong colors when `COLORFGBG` reports a light
7
+ * background. Truecolor terminals get the exact values, 256-color terminals the nearest xterm
8
+ * palette entry, others the closest of the 16 basic colors.
9
+ */
10
+ /**
11
+ * `FORCE_COLOR`: `0` / `false` → 0, `1` / `2` / `3` → exactly that level, `''` / `true` /
12
+ * anything else → `'depth'` (on, at the terminal's depth); unset → `undefined`.
13
+ */
14
+ function forceColorLevel(value) {
15
+ if (value === undefined)
16
+ return undefined;
17
+ const v = value.trim().toLowerCase();
18
+ if (v === 'false' || v === '0')
19
+ return 0;
20
+ if (v === '1')
21
+ return 1;
22
+ if (v === '2')
23
+ return 2;
24
+ if (v === '3')
25
+ return 3;
26
+ return 'depth';
27
+ }
28
+ /** Color depth the terminal supports, assuming color is wanted at all. */
29
+ export function terminalDepth(env) {
30
+ const colorterm = (env.COLORTERM ?? '').toLowerCase();
31
+ if (colorterm === 'truecolor' || colorterm === '24bit')
32
+ return 3;
33
+ const term = (env.TERM ?? '').toLowerCase();
34
+ if (env.WT_SESSION || term === 'xterm-kitty' || term === 'xterm-ghostty' || term === 'wezterm') {
35
+ return 3;
36
+ }
37
+ if (env.GITHUB_ACTIONS === 'true')
38
+ return 3;
39
+ if (/-256(colou?r)?$/.test(term) || env.TERM_PROGRAM === 'Apple_Terminal')
40
+ return 2;
41
+ return 1;
42
+ }
43
+ /**
44
+ * Resolve the color level.
45
+ *
46
+ * - `never` → 0.
47
+ * - `always` → the level `FORCE_COLOR` names (1–3), else the terminal depth (at least 16
48
+ * colors), even when `NO_COLOR` is set.
49
+ * - `auto`: `FORCE_COLOR` wins (`0`/`false` disables, `1`–`3` force exactly that level, other
50
+ * values the terminal depth), then a non-empty `NO_COLOR` disables, then color only when the
51
+ * stream is a TTY and `TERM` is not `dumb`.
52
+ */
53
+ export function resolveColorLevel(mode, env, isTTY) {
54
+ if (mode === 'never')
55
+ return 0;
56
+ const forced = forceColorLevel(env.FORCE_COLOR);
57
+ const depth = () => {
58
+ const d = terminalDepth(env);
59
+ return d === 0 ? 1 : d;
60
+ };
61
+ if (mode === 'always')
62
+ return typeof forced === 'number' && forced > 0 ? forced : depth();
63
+ if (forced !== undefined)
64
+ return forced === 'depth' ? depth() : forced;
65
+ if (env.NO_COLOR !== undefined && env.NO_COLOR !== '')
66
+ return 0;
67
+ if (!isTTY || env.TERM === 'dumb')
68
+ return 0;
69
+ return terminalDepth(env);
70
+ }
71
+ /** True when `COLORFGBG` (rxvt, Konsole, iTerm2…) reports a light background. */
72
+ export function isLightBackground(env) {
73
+ const parts = (env.COLORFGBG ?? '').split(';');
74
+ const bg = Number(parts[parts.length - 1]);
75
+ return bg === 7 || bg === 15;
76
+ }
77
+ const hex = (value) => {
78
+ const n = Number.parseInt(value.slice(1), 16);
79
+ return [(n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff];
80
+ };
81
+ /** docs/visual-language.md: dark `family.*.base`, light `family.*.strong`. */
82
+ const TONES = {
83
+ added: { dark: hex('#34D399'), light: hex('#15803D'), basic: 32 },
84
+ removed: { dark: hex('#F87171'), light: hex('#B91C1C'), basic: 31 },
85
+ content: { dark: hex('#FBBF24'), light: hex('#B45309'), basic: 33 },
86
+ structure: { dark: hex('#38BDF8'), light: hex('#0369A1'), basic: 36 },
87
+ identity: { dark: hex('#A78BFA'), light: hex('#6D28D9'), basic: 35 },
88
+ accent: { dark: hex('#7C9CFF'), light: hex('#2F49B5'), basic: 34 },
89
+ };
90
+ const CUBE = [0, 95, 135, 175, 215, 255];
91
+ function nearestCube(v) {
92
+ let best = 0;
93
+ for (let i = 1; i < CUBE.length; i++) {
94
+ if (Math.abs(CUBE[i] - v) < Math.abs(CUBE[best] - v))
95
+ best = i;
96
+ }
97
+ return best;
98
+ }
99
+ /** Nearest xterm-256 palette index (6×6×6 cube or gray ramp) by squared RGB distance. */
100
+ export function ansi256([r, g, b]) {
101
+ const ri = nearestCube(r);
102
+ const gi = nearestCube(g);
103
+ const bi = nearestCube(b);
104
+ const cube = [CUBE[ri], CUBE[gi], CUBE[bi]];
105
+ const grayLevel = Math.min(23, Math.max(0, Math.round(((r + g + b) / 3 - 8) / 10)));
106
+ const gray = 8 + grayLevel * 10;
107
+ const dist = (c) => (c[0] - r) ** 2 + (c[1] - g) ** 2 + (c[2] - b) ** 2;
108
+ return dist([gray, gray, gray]) < dist(cube) ? 232 + grayLevel : 16 + 36 * ri + 6 * gi + bi;
109
+ }
110
+ /** Create a painter for `level`. `light` picks colors that read on light backgrounds. */
111
+ export function createPainter(level, light = false) {
112
+ if (level === 0) {
113
+ const id = (text) => text;
114
+ return { level, tone: (_tone, text) => text, bold: id, dim: id };
115
+ }
116
+ const open = {};
117
+ for (const [name, colors] of Object.entries(TONES)) {
118
+ const rgb = light ? colors.light : colors.dark;
119
+ open[name] =
120
+ level === 3
121
+ ? `\x1b[38;2;${rgb[0]};${rgb[1]};${rgb[2]}m`
122
+ : level === 2
123
+ ? `\x1b[38;5;${ansi256(rgb)}m`
124
+ : `\x1b[${colors.basic}m`;
125
+ }
126
+ const wrap = (start, end) => (text) => text === '' ? '' : `${start}${text}${end}`;
127
+ return {
128
+ level,
129
+ tone: (tone, text) => (text === '' ? '' : `${open[tone]}${text}\x1b[39m`),
130
+ // SGR 22 ends both bold and dim; the CLI never nests the two.
131
+ bold: wrap('\x1b[1m', '\x1b[22m'),
132
+ dim: wrap('\x1b[2m', '\x1b[22m'),
133
+ };
134
+ }
135
+ /** Remove SGR escape sequences (for measuring or comparing painted text). */
136
+ export function stripAnsi(text) {
137
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: ESC is the point.
138
+ return text.replace(/\x1b\[[0-9;]*m/g, '');
139
+ }
140
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.js","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Terminal outputs: the default summary (header, counters, diffstat bar, hunk list), `--stat`
3
+ * and `--name-status` (docs/cli.md §Terminal summary).
4
+ *
5
+ * Every line is laid out on plain text first (measured with `stringWidth`, truncated with an
6
+ * ellipsis) and painted last, so alignment holds with and without color and with CJK labels.
7
+ */
8
+ import { type DiffResult, type Locale } from '@kgdiff/core';
9
+ import { type Messages } from './locale.js';
10
+ import { type Overview } from './model.js';
11
+ import type { Painter } from './style.js';
12
+ export interface TextContext {
13
+ readonly paint: Painter;
14
+ readonly locale: Locale;
15
+ /** Maximum line width in columns. */
16
+ readonly width: number;
17
+ /** Maximum hunks listed in the summary; 0 lists all. */
18
+ readonly limit: number;
19
+ }
20
+ /** `179 → 184 nodes · 351 → 353 edges` (single numbers when a count did not change). */
21
+ export declare function totalsText(diff: DiffResult, m: Messages): string;
22
+ /** Proportional diffstat bar: one segment per phase, sized by changed elements. */
23
+ export declare function diffstatBar(ov: Overview, width: number, paint: Painter): string;
24
+ /**
25
+ * The default terminal output: header (title, node and edge totals), per-phase counters, a
26
+ * proportional diffstat bar, then the hunks grouped by phase with glyph, title and a family /
27
+ * summary column. At most `ctx.limit` hunks are listed (0 = all), then "… and N more".
28
+ */
29
+ export declare function renderSummary(diff: DiffResult, title: string, ctx: TextContext): string;
30
+ /**
31
+ * `--stat`: header, counters and bar, a nodes / edges × added / removed / modified / unchanged
32
+ * table, and a git-style per-type breakdown (`+` added, `−` removed, `~` modified nodes).
33
+ */
34
+ export declare function renderStat(diff: DiffResult, title: string, ctx: TextContext): string;
35
+ /**
36
+ * `--name-status`: one line per hunk in review order: status letter, anchor id, title.
37
+ * Tab-separated (scriptable) unless `aligned`, which pads the columns for a terminal.
38
+ */
39
+ export declare function renderNameStatus(diff: DiffResult, ctx: TextContext, aligned: boolean): string;
40
+ //# sourceMappingURL=summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.d.ts","sourceRoot":"","sources":["../src/summary.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,KAAK,UAAU,EAAyB,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EAAa,KAAK,QAAQ,EAAY,MAAM,aAAa,CAAC;AACjE,OAAO,EAOL,KAAK,QAAQ,EAId,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAgBD,wFAAwF;AACxF,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAGhE;AAuED,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAY/E;AAmID;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,MAAM,CAmCvF;AAKD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,MAAM,CASpF;AAwFD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAuB7F"}