@cleocode/contracts 2026.5.105 โ 2026.5.106
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/dist/render/__tests__/icon.test.d.ts +2 -0
- package/dist/render/__tests__/icon.test.d.ts.map +1 -0
- package/dist/render/__tests__/icon.test.js +124 -0
- package/dist/render/__tests__/icon.test.js.map +1 -0
- package/dist/render/icon.d.ts +120 -0
- package/dist/render/icon.d.ts.map +1 -0
- package/dist/render/icon.js +163 -0
- package/dist/render/icon.js.map +1 -0
- package/package.json +2 -2
- package/src/render/__tests__/icon.test.ts +138 -0
- package/src/render/icon.ts +182 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icon.test.d.ts","sourceRoot":"","sources":["../../../src/render/__tests__/icon.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { ascii, BadgeIcon, KindIcon, pickIcon, RelationIcon, StatusIcon } from '../icon.js';
|
|
3
|
+
describe('icon enums', () => {
|
|
4
|
+
it('StatusIcon emoji values match ADR-077', () => {
|
|
5
|
+
expect(StatusIcon.PENDING).toBe('โณ');
|
|
6
|
+
expect(StatusIcon.ACTIVE).toBe('๐ง');
|
|
7
|
+
expect(StatusIcon.DONE).toBe('โ
');
|
|
8
|
+
expect(StatusIcon.BLOCKED).toBe('๐ช');
|
|
9
|
+
expect(StatusIcon.ARCHIVED).toBe('๐');
|
|
10
|
+
expect(StatusIcon.CANCELLED).toBe('โ');
|
|
11
|
+
});
|
|
12
|
+
it('KindIcon emoji values match ADR-077', () => {
|
|
13
|
+
expect(KindIcon.SAGA).toBe('๐ฒ');
|
|
14
|
+
expect(KindIcon.EPIC).toBe('๐');
|
|
15
|
+
expect(KindIcon.TASK).toBe('โข');
|
|
16
|
+
expect(KindIcon.SUBTASK).toBe('โฆ');
|
|
17
|
+
expect(KindIcon.RESEARCH).toBe('๐');
|
|
18
|
+
expect(KindIcon.BUG).toBe('๐');
|
|
19
|
+
expect(KindIcon.RELEASE).toBe('๐');
|
|
20
|
+
});
|
|
21
|
+
it('BadgeIcon emoji values match ADR-077 (ORPHAN amended in T10137)', () => {
|
|
22
|
+
expect(BadgeIcon.EMPTY).toBe('๐ชฆ');
|
|
23
|
+
// ADR-077 ยง2 originally specified '๐ช' for ORPHAN โ identical to
|
|
24
|
+
// StatusIcon.BLOCKED. String enums share runtime values so ascii() cannot
|
|
25
|
+
// disambiguate. Amended to '๐ป' (abandoned/lonely). T10137 (B12) updates
|
|
26
|
+
// the ADR text to match.
|
|
27
|
+
expect(BadgeIcon.ORPHAN).toBe('๐ป');
|
|
28
|
+
expect(BadgeIcon.NESTED).toBe('๐');
|
|
29
|
+
expect(BadgeIcon.CAUTION).toBe('โ ');
|
|
30
|
+
expect(BadgeIcon.NEW).toBe('โ
');
|
|
31
|
+
});
|
|
32
|
+
it('RelationIcon emoji values match ADR-077', () => {
|
|
33
|
+
expect(RelationIcon.GROUPS).toBe('โ');
|
|
34
|
+
expect(RelationIcon.PARENT).toBe('โคด');
|
|
35
|
+
expect(RelationIcon.DEPENDS).toBe('โจ');
|
|
36
|
+
expect(RelationIcon.BLOCKS).toBe('โ');
|
|
37
|
+
});
|
|
38
|
+
it('no two enum members share the same runtime string value', () => {
|
|
39
|
+
const all = [
|
|
40
|
+
...Object.values(StatusIcon),
|
|
41
|
+
...Object.values(KindIcon),
|
|
42
|
+
...Object.values(BadgeIcon),
|
|
43
|
+
...Object.values(RelationIcon),
|
|
44
|
+
];
|
|
45
|
+
const unique = new Set(all);
|
|
46
|
+
expect(unique.size).toBe(all.length);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe('ascii()', () => {
|
|
50
|
+
it('returns NO_COLOR fallback for every StatusIcon', () => {
|
|
51
|
+
expect(ascii(StatusIcon.PENDING)).toBe('[ ]');
|
|
52
|
+
expect(ascii(StatusIcon.ACTIVE)).toBe('[~]');
|
|
53
|
+
expect(ascii(StatusIcon.DONE)).toBe('[x]');
|
|
54
|
+
expect(ascii(StatusIcon.BLOCKED)).toBe('[!]');
|
|
55
|
+
expect(ascii(StatusIcon.ARCHIVED)).toBe('[#]');
|
|
56
|
+
expect(ascii(StatusIcon.CANCELLED)).toBe('[-]');
|
|
57
|
+
});
|
|
58
|
+
it('returns NO_COLOR fallback for every KindIcon', () => {
|
|
59
|
+
expect(ascii(KindIcon.SAGA)).toBe('SG');
|
|
60
|
+
expect(ascii(KindIcon.EPIC)).toBe('E');
|
|
61
|
+
expect(ascii(KindIcon.TASK)).toBe('-');
|
|
62
|
+
expect(ascii(KindIcon.SUBTASK)).toBe('.');
|
|
63
|
+
expect(ascii(KindIcon.RESEARCH)).toBe('R');
|
|
64
|
+
expect(ascii(KindIcon.BUG)).toBe('B');
|
|
65
|
+
expect(ascii(KindIcon.RELEASE)).toBe('>');
|
|
66
|
+
});
|
|
67
|
+
it('returns NO_COLOR fallback for every BadgeIcon', () => {
|
|
68
|
+
expect(ascii(BadgeIcon.EMPTY)).toBe('(empty)');
|
|
69
|
+
expect(ascii(BadgeIcon.ORPHAN)).toBe('(orphan)');
|
|
70
|
+
expect(ascii(BadgeIcon.NESTED)).toBe('(nested)');
|
|
71
|
+
expect(ascii(BadgeIcon.CAUTION)).toBe('(!)');
|
|
72
|
+
expect(ascii(BadgeIcon.NEW)).toBe('(new)');
|
|
73
|
+
});
|
|
74
|
+
it('returns NO_COLOR fallback for every RelationIcon', () => {
|
|
75
|
+
expect(ascii(RelationIcon.GROUPS)).toBe('in');
|
|
76
|
+
expect(ascii(RelationIcon.PARENT)).toBe('^');
|
|
77
|
+
expect(ascii(RelationIcon.DEPENDS)).toBe('->');
|
|
78
|
+
expect(ascii(RelationIcon.BLOCKS)).toBe('!>');
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe('pickIcon()', () => {
|
|
82
|
+
const originalNoColor = process.env['NO_COLOR'];
|
|
83
|
+
const originalTerm = process.env['TERM'];
|
|
84
|
+
beforeEach(() => {
|
|
85
|
+
delete process.env['NO_COLOR'];
|
|
86
|
+
delete process.env['TERM'];
|
|
87
|
+
});
|
|
88
|
+
afterEach(() => {
|
|
89
|
+
if (originalNoColor === undefined) {
|
|
90
|
+
delete process.env['NO_COLOR'];
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
process.env['NO_COLOR'] = originalNoColor;
|
|
94
|
+
}
|
|
95
|
+
if (originalTerm === undefined) {
|
|
96
|
+
delete process.env['TERM'];
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
process.env['TERM'] = originalTerm;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
it('returns the emoji when noColor is explicitly false', () => {
|
|
103
|
+
expect(pickIcon(KindIcon.SAGA, { noColor: false })).toBe('๐ฒ');
|
|
104
|
+
expect(pickIcon(StatusIcon.DONE, { noColor: false })).toBe('โ
');
|
|
105
|
+
});
|
|
106
|
+
it('returns the ASCII form when noColor is explicitly true', () => {
|
|
107
|
+
expect(pickIcon(KindIcon.SAGA, { noColor: true })).toBe('SG');
|
|
108
|
+
expect(pickIcon(StatusIcon.DONE, { noColor: true })).toBe('[x]');
|
|
109
|
+
});
|
|
110
|
+
it('falls back to NO_COLOR=1 from process.env', () => {
|
|
111
|
+
process.env['NO_COLOR'] = '1';
|
|
112
|
+
expect(pickIcon(KindIcon.EPIC)).toBe('E');
|
|
113
|
+
expect(pickIcon(BadgeIcon.ORPHAN)).toBe('(orphan)');
|
|
114
|
+
});
|
|
115
|
+
it('falls back to TERM=dumb from process.env', () => {
|
|
116
|
+
process.env['TERM'] = 'dumb';
|
|
117
|
+
expect(pickIcon(RelationIcon.GROUPS)).toBe('in');
|
|
118
|
+
});
|
|
119
|
+
it('returns the emoji when no NO_COLOR signals are present', () => {
|
|
120
|
+
expect(pickIcon(KindIcon.TASK)).toBe('โข');
|
|
121
|
+
expect(pickIcon(StatusIcon.ACTIVE)).toBe('๐ง');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
//# sourceMappingURL=icon.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icon.test.js","sourceRoot":"","sources":["../../../src/render/__tests__/icon.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed icon enums for the human-render contract (ADR-077).
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for the visual language used across all renderers
|
|
5
|
+
* (tree, table, list, badge). Each enum is emoji-first; `ascii()` returns the
|
|
6
|
+
* NO_COLOR-safe fallback for non-UTF-8 terminals or `NO_COLOR=1` environments.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Status icons โ agent-task lifecycle states.
|
|
12
|
+
*
|
|
13
|
+
* Emoji-first; `ascii()` returns a NO_COLOR-safe fallback.
|
|
14
|
+
*/
|
|
15
|
+
export declare enum StatusIcon {
|
|
16
|
+
/** Task is queued and has not started. */
|
|
17
|
+
PENDING = "\u23F3",
|
|
18
|
+
/** Task is in progress. */
|
|
19
|
+
ACTIVE = "\uD83D\uDEA7",
|
|
20
|
+
/** Task completed successfully. */
|
|
21
|
+
DONE = "\u2705",
|
|
22
|
+
/** Task is blocked on a dependency or gate. */
|
|
23
|
+
BLOCKED = "\uD83D\uDEAA",
|
|
24
|
+
/** Task is archived and no longer actively tracked. */
|
|
25
|
+
ARCHIVED = "\uD83D\uDDC4",
|
|
26
|
+
/** Task was cancelled before completion. */
|
|
27
|
+
CANCELLED = "\u2717"
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Task-kind icons โ Saga / Epic / Task / Subtask plus ancillary kinds.
|
|
31
|
+
*/
|
|
32
|
+
export declare enum KindIcon {
|
|
33
|
+
/** Saga โ multi-release theme grouping โฅ 2 Epics. */
|
|
34
|
+
SAGA = "\uD83C\uDF32",
|
|
35
|
+
/** Epic โ one releasable slice. */
|
|
36
|
+
EPIC = "\uD83D\uDCCB",
|
|
37
|
+
/** Task โ one atomic PR-sized change. */
|
|
38
|
+
TASK = "\u2022",
|
|
39
|
+
/** Subtask โ one commit contributing to a Task's PR. */
|
|
40
|
+
SUBTASK = "\u25E6",
|
|
41
|
+
/** Research task โ investigation or audit. */
|
|
42
|
+
RESEARCH = "\uD83D\uDCD6",
|
|
43
|
+
/** Bug task โ defect tracking. */
|
|
44
|
+
BUG = "\uD83D\uDC1B",
|
|
45
|
+
/** Release task โ shipping work. */
|
|
46
|
+
RELEASE = "\uD83D\uDE80"
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Badge icons โ flags attached to tasks (empty, orphan, nested, etc).
|
|
50
|
+
*/
|
|
51
|
+
export declare enum BadgeIcon {
|
|
52
|
+
/** Container has no children. */
|
|
53
|
+
EMPTY = "\uD83E\uDEA6",
|
|
54
|
+
/**
|
|
55
|
+
* Task has no parent and is unreachable from a Saga or Epic.
|
|
56
|
+
*
|
|
57
|
+
* NOTE: ADR-077 ยง2 originally specified `'๐ช'`, identical to `StatusIcon.BLOCKED`.
|
|
58
|
+
* TypeScript string enums share a single runtime string so `ascii()` cannot
|
|
59
|
+
* disambiguate. Resolved by switching to `'๐ป'` (abandoned/lonely semantics).
|
|
60
|
+
* A follow-up ADR amendment lands in T10137 (B12).
|
|
61
|
+
*/
|
|
62
|
+
ORPHAN = "\uD83D\uDC7B",
|
|
63
|
+
/** Task is nested deeper than the depth budget allows. */
|
|
64
|
+
NESTED = "\uD83D\uDD01",
|
|
65
|
+
/** Caution โ something needs human attention. */
|
|
66
|
+
CAUTION = "\u26A0",
|
|
67
|
+
/** Recently added โ surfaced for visibility. */
|
|
68
|
+
NEW = "\u2605"
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Relation icons โ edge semantics in tree views.
|
|
72
|
+
*/
|
|
73
|
+
export declare enum RelationIcon {
|
|
74
|
+
/** Saga groups Epic via `task_relations.relation_type='groups'`. */
|
|
75
|
+
GROUPS = "\u2282",
|
|
76
|
+
/** Parent edge โ direct hierarchical parent. */
|
|
77
|
+
PARENT = "\u2934",
|
|
78
|
+
/** Depends-on edge โ A cannot start until B is done. */
|
|
79
|
+
DEPENDS = "\u21E8",
|
|
80
|
+
/** Blocks edge โ A actively blocks B from progressing. */
|
|
81
|
+
BLOCKS = "\u2298"
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Union of every icon enum โ useful as the parameter type for helpers that
|
|
85
|
+
* accept any icon without caring which category it came from.
|
|
86
|
+
*/
|
|
87
|
+
export type AnyIcon = StatusIcon | KindIcon | BadgeIcon | RelationIcon;
|
|
88
|
+
/**
|
|
89
|
+
* Maps an emoji icon to its NO_COLOR-safe ASCII equivalent.
|
|
90
|
+
*
|
|
91
|
+
* Used when stdout is not a UTF-8 TTY or `NO_COLOR=1` is set. Every enum
|
|
92
|
+
* member has an explicit ASCII fallback โ the exhaustive switch ensures the
|
|
93
|
+
* TypeScript compiler flags any future icon added without an ASCII mapping.
|
|
94
|
+
*
|
|
95
|
+
* @param icon - Any icon from `StatusIcon`, `KindIcon`, `BadgeIcon`, or `RelationIcon`.
|
|
96
|
+
* @returns The ASCII-only string to render in NO_COLOR contexts.
|
|
97
|
+
*/
|
|
98
|
+
export declare function ascii(icon: AnyIcon): string;
|
|
99
|
+
/**
|
|
100
|
+
* Options for {@link pickIcon}.
|
|
101
|
+
*/
|
|
102
|
+
export interface PickIconOptions {
|
|
103
|
+
/**
|
|
104
|
+
* Force ASCII fallback. When omitted, falls back to `NO_COLOR=1` or
|
|
105
|
+
* `TERM=dumb` in `process.env`.
|
|
106
|
+
*/
|
|
107
|
+
noColor?: boolean;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Returns the icon to render given the current environment.
|
|
111
|
+
*
|
|
112
|
+
* Honors `NO_COLOR=1` and `TERM=dumb` for ASCII fallback when `opts.noColor`
|
|
113
|
+
* is not provided explicitly.
|
|
114
|
+
*
|
|
115
|
+
* @param icon - The icon to render.
|
|
116
|
+
* @param opts - Optional override for NO_COLOR detection.
|
|
117
|
+
* @returns The emoji icon, or its ASCII equivalent if NO_COLOR mode is active.
|
|
118
|
+
*/
|
|
119
|
+
export declare function pickIcon<I extends AnyIcon>(icon: I, opts?: PickIconOptions): string;
|
|
120
|
+
//# sourceMappingURL=icon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icon.d.ts","sourceRoot":"","sources":["../../src/render/icon.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,oBAAY,UAAU;IACpB,0CAA0C;IAC1C,OAAO,WAAM;IACb,2BAA2B;IAC3B,MAAM,iBAAO;IACb,mCAAmC;IACnC,IAAI,WAAM;IACV,+CAA+C;IAC/C,OAAO,iBAAO;IACd,uDAAuD;IACvD,QAAQ,iBAAO;IACf,4CAA4C;IAC5C,SAAS,WAAM;CAChB;AAED;;GAEG;AACH,oBAAY,QAAQ;IAClB,qDAAqD;IACrD,IAAI,iBAAO;IACX,mCAAmC;IACnC,IAAI,iBAAO;IACX,yCAAyC;IACzC,IAAI,WAAM;IACV,wDAAwD;IACxD,OAAO,WAAM;IACb,8CAA8C;IAC9C,QAAQ,iBAAO;IACf,kCAAkC;IAClC,GAAG,iBAAO;IACV,oCAAoC;IACpC,OAAO,iBAAO;CACf;AAED;;GAEG;AACH,oBAAY,SAAS;IACnB,iCAAiC;IACjC,KAAK,iBAAO;IACZ;;;;;;;OAOG;IACH,MAAM,iBAAO;IACb,0DAA0D;IAC1D,MAAM,iBAAO;IACb,iDAAiD;IACjD,OAAO,WAAM;IACb,gDAAgD;IAChD,GAAG,WAAM;CACV;AAED;;GAEG;AACH,oBAAY,YAAY;IACtB,oEAAoE;IACpE,MAAM,WAAM;IACZ,gDAAgD;IAChD,MAAM,WAAM;IACZ,wDAAwD;IACxD,OAAO,WAAM;IACb,0DAA0D;IAC1D,MAAM,WAAM;CACb;AAED;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,YAAY,CAAC;AAEvE;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAmD3C;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,eAAe,GAAG,MAAM,CAInF"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed icon enums for the human-render contract (ADR-077).
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for the visual language used across all renderers
|
|
5
|
+
* (tree, table, list, badge). Each enum is emoji-first; `ascii()` returns the
|
|
6
|
+
* NO_COLOR-safe fallback for non-UTF-8 terminals or `NO_COLOR=1` environments.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Status icons โ agent-task lifecycle states.
|
|
12
|
+
*
|
|
13
|
+
* Emoji-first; `ascii()` returns a NO_COLOR-safe fallback.
|
|
14
|
+
*/
|
|
15
|
+
export var StatusIcon;
|
|
16
|
+
(function (StatusIcon) {
|
|
17
|
+
/** Task is queued and has not started. */
|
|
18
|
+
StatusIcon["PENDING"] = "\u23F3";
|
|
19
|
+
/** Task is in progress. */
|
|
20
|
+
StatusIcon["ACTIVE"] = "\uD83D\uDEA7";
|
|
21
|
+
/** Task completed successfully. */
|
|
22
|
+
StatusIcon["DONE"] = "\u2705";
|
|
23
|
+
/** Task is blocked on a dependency or gate. */
|
|
24
|
+
StatusIcon["BLOCKED"] = "\uD83D\uDEAA";
|
|
25
|
+
/** Task is archived and no longer actively tracked. */
|
|
26
|
+
StatusIcon["ARCHIVED"] = "\uD83D\uDDC4";
|
|
27
|
+
/** Task was cancelled before completion. */
|
|
28
|
+
StatusIcon["CANCELLED"] = "\u2717";
|
|
29
|
+
})(StatusIcon || (StatusIcon = {}));
|
|
30
|
+
/**
|
|
31
|
+
* Task-kind icons โ Saga / Epic / Task / Subtask plus ancillary kinds.
|
|
32
|
+
*/
|
|
33
|
+
export var KindIcon;
|
|
34
|
+
(function (KindIcon) {
|
|
35
|
+
/** Saga โ multi-release theme grouping โฅ 2 Epics. */
|
|
36
|
+
KindIcon["SAGA"] = "\uD83C\uDF32";
|
|
37
|
+
/** Epic โ one releasable slice. */
|
|
38
|
+
KindIcon["EPIC"] = "\uD83D\uDCCB";
|
|
39
|
+
/** Task โ one atomic PR-sized change. */
|
|
40
|
+
KindIcon["TASK"] = "\u2022";
|
|
41
|
+
/** Subtask โ one commit contributing to a Task's PR. */
|
|
42
|
+
KindIcon["SUBTASK"] = "\u25E6";
|
|
43
|
+
/** Research task โ investigation or audit. */
|
|
44
|
+
KindIcon["RESEARCH"] = "\uD83D\uDCD6";
|
|
45
|
+
/** Bug task โ defect tracking. */
|
|
46
|
+
KindIcon["BUG"] = "\uD83D\uDC1B";
|
|
47
|
+
/** Release task โ shipping work. */
|
|
48
|
+
KindIcon["RELEASE"] = "\uD83D\uDE80";
|
|
49
|
+
})(KindIcon || (KindIcon = {}));
|
|
50
|
+
/**
|
|
51
|
+
* Badge icons โ flags attached to tasks (empty, orphan, nested, etc).
|
|
52
|
+
*/
|
|
53
|
+
export var BadgeIcon;
|
|
54
|
+
(function (BadgeIcon) {
|
|
55
|
+
/** Container has no children. */
|
|
56
|
+
BadgeIcon["EMPTY"] = "\uD83E\uDEA6";
|
|
57
|
+
/**
|
|
58
|
+
* Task has no parent and is unreachable from a Saga or Epic.
|
|
59
|
+
*
|
|
60
|
+
* NOTE: ADR-077 ยง2 originally specified `'๐ช'`, identical to `StatusIcon.BLOCKED`.
|
|
61
|
+
* TypeScript string enums share a single runtime string so `ascii()` cannot
|
|
62
|
+
* disambiguate. Resolved by switching to `'๐ป'` (abandoned/lonely semantics).
|
|
63
|
+
* A follow-up ADR amendment lands in T10137 (B12).
|
|
64
|
+
*/
|
|
65
|
+
BadgeIcon["ORPHAN"] = "\uD83D\uDC7B";
|
|
66
|
+
/** Task is nested deeper than the depth budget allows. */
|
|
67
|
+
BadgeIcon["NESTED"] = "\uD83D\uDD01";
|
|
68
|
+
/** Caution โ something needs human attention. */
|
|
69
|
+
BadgeIcon["CAUTION"] = "\u26A0";
|
|
70
|
+
/** Recently added โ surfaced for visibility. */
|
|
71
|
+
BadgeIcon["NEW"] = "\u2605";
|
|
72
|
+
})(BadgeIcon || (BadgeIcon = {}));
|
|
73
|
+
/**
|
|
74
|
+
* Relation icons โ edge semantics in tree views.
|
|
75
|
+
*/
|
|
76
|
+
export var RelationIcon;
|
|
77
|
+
(function (RelationIcon) {
|
|
78
|
+
/** Saga groups Epic via `task_relations.relation_type='groups'`. */
|
|
79
|
+
RelationIcon["GROUPS"] = "\u2282";
|
|
80
|
+
/** Parent edge โ direct hierarchical parent. */
|
|
81
|
+
RelationIcon["PARENT"] = "\u2934";
|
|
82
|
+
/** Depends-on edge โ A cannot start until B is done. */
|
|
83
|
+
RelationIcon["DEPENDS"] = "\u21E8";
|
|
84
|
+
/** Blocks edge โ A actively blocks B from progressing. */
|
|
85
|
+
RelationIcon["BLOCKS"] = "\u2298";
|
|
86
|
+
})(RelationIcon || (RelationIcon = {}));
|
|
87
|
+
/**
|
|
88
|
+
* Maps an emoji icon to its NO_COLOR-safe ASCII equivalent.
|
|
89
|
+
*
|
|
90
|
+
* Used when stdout is not a UTF-8 TTY or `NO_COLOR=1` is set. Every enum
|
|
91
|
+
* member has an explicit ASCII fallback โ the exhaustive switch ensures the
|
|
92
|
+
* TypeScript compiler flags any future icon added without an ASCII mapping.
|
|
93
|
+
*
|
|
94
|
+
* @param icon - Any icon from `StatusIcon`, `KindIcon`, `BadgeIcon`, or `RelationIcon`.
|
|
95
|
+
* @returns The ASCII-only string to render in NO_COLOR contexts.
|
|
96
|
+
*/
|
|
97
|
+
export function ascii(icon) {
|
|
98
|
+
switch (icon) {
|
|
99
|
+
// Status
|
|
100
|
+
case StatusIcon.PENDING:
|
|
101
|
+
return '[ ]';
|
|
102
|
+
case StatusIcon.ACTIVE:
|
|
103
|
+
return '[~]';
|
|
104
|
+
case StatusIcon.DONE:
|
|
105
|
+
return '[x]';
|
|
106
|
+
case StatusIcon.BLOCKED:
|
|
107
|
+
return '[!]';
|
|
108
|
+
case StatusIcon.ARCHIVED:
|
|
109
|
+
return '[#]';
|
|
110
|
+
case StatusIcon.CANCELLED:
|
|
111
|
+
return '[-]';
|
|
112
|
+
// Kind
|
|
113
|
+
case KindIcon.SAGA:
|
|
114
|
+
return 'SG';
|
|
115
|
+
case KindIcon.EPIC:
|
|
116
|
+
return 'E';
|
|
117
|
+
case KindIcon.TASK:
|
|
118
|
+
return '-';
|
|
119
|
+
case KindIcon.SUBTASK:
|
|
120
|
+
return '.';
|
|
121
|
+
case KindIcon.RESEARCH:
|
|
122
|
+
return 'R';
|
|
123
|
+
case KindIcon.BUG:
|
|
124
|
+
return 'B';
|
|
125
|
+
case KindIcon.RELEASE:
|
|
126
|
+
return '>';
|
|
127
|
+
// Badge
|
|
128
|
+
case BadgeIcon.EMPTY:
|
|
129
|
+
return '(empty)';
|
|
130
|
+
case BadgeIcon.ORPHAN:
|
|
131
|
+
return '(orphan)';
|
|
132
|
+
case BadgeIcon.NESTED:
|
|
133
|
+
return '(nested)';
|
|
134
|
+
case BadgeIcon.CAUTION:
|
|
135
|
+
return '(!)';
|
|
136
|
+
case BadgeIcon.NEW:
|
|
137
|
+
return '(new)';
|
|
138
|
+
// Relation
|
|
139
|
+
case RelationIcon.GROUPS:
|
|
140
|
+
return 'in';
|
|
141
|
+
case RelationIcon.PARENT:
|
|
142
|
+
return '^';
|
|
143
|
+
case RelationIcon.DEPENDS:
|
|
144
|
+
return '->';
|
|
145
|
+
case RelationIcon.BLOCKS:
|
|
146
|
+
return '!>';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Returns the icon to render given the current environment.
|
|
151
|
+
*
|
|
152
|
+
* Honors `NO_COLOR=1` and `TERM=dumb` for ASCII fallback when `opts.noColor`
|
|
153
|
+
* is not provided explicitly.
|
|
154
|
+
*
|
|
155
|
+
* @param icon - The icon to render.
|
|
156
|
+
* @param opts - Optional override for NO_COLOR detection.
|
|
157
|
+
* @returns The emoji icon, or its ASCII equivalent if NO_COLOR mode is active.
|
|
158
|
+
*/
|
|
159
|
+
export function pickIcon(icon, opts) {
|
|
160
|
+
const noColor = opts?.noColor ?? (process.env['NO_COLOR'] === '1' || process.env['TERM'] === 'dumb');
|
|
161
|
+
return noColor ? ascii(icon) : icon;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=icon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icon.js","sourceRoot":"","sources":["../../src/render/icon.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleocode/contracts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "v2026.5.106",
|
|
4
4
|
"description": "Domain types, interfaces, and contracts for the CLEO ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
"dependencies": {
|
|
139
139
|
"zod": "^4.3.6",
|
|
140
140
|
"zod-to-json-schema": "^3.25.2",
|
|
141
|
-
"@cleocode/lafs": "
|
|
141
|
+
"@cleocode/lafs": "v2026.5.106"
|
|
142
142
|
},
|
|
143
143
|
"scripts": {
|
|
144
144
|
"build": "tsc -b --force && node scripts/emit-schemas.mjs",
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { ascii, BadgeIcon, KindIcon, pickIcon, RelationIcon, StatusIcon } from '../icon.js';
|
|
3
|
+
|
|
4
|
+
describe('icon enums', () => {
|
|
5
|
+
it('StatusIcon emoji values match ADR-077', () => {
|
|
6
|
+
expect(StatusIcon.PENDING).toBe('โณ');
|
|
7
|
+
expect(StatusIcon.ACTIVE).toBe('๐ง');
|
|
8
|
+
expect(StatusIcon.DONE).toBe('โ
');
|
|
9
|
+
expect(StatusIcon.BLOCKED).toBe('๐ช');
|
|
10
|
+
expect(StatusIcon.ARCHIVED).toBe('๐');
|
|
11
|
+
expect(StatusIcon.CANCELLED).toBe('โ');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('KindIcon emoji values match ADR-077', () => {
|
|
15
|
+
expect(KindIcon.SAGA).toBe('๐ฒ');
|
|
16
|
+
expect(KindIcon.EPIC).toBe('๐');
|
|
17
|
+
expect(KindIcon.TASK).toBe('โข');
|
|
18
|
+
expect(KindIcon.SUBTASK).toBe('โฆ');
|
|
19
|
+
expect(KindIcon.RESEARCH).toBe('๐');
|
|
20
|
+
expect(KindIcon.BUG).toBe('๐');
|
|
21
|
+
expect(KindIcon.RELEASE).toBe('๐');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('BadgeIcon emoji values match ADR-077 (ORPHAN amended in T10137)', () => {
|
|
25
|
+
expect(BadgeIcon.EMPTY).toBe('๐ชฆ');
|
|
26
|
+
// ADR-077 ยง2 originally specified '๐ช' for ORPHAN โ identical to
|
|
27
|
+
// StatusIcon.BLOCKED. String enums share runtime values so ascii() cannot
|
|
28
|
+
// disambiguate. Amended to '๐ป' (abandoned/lonely). T10137 (B12) updates
|
|
29
|
+
// the ADR text to match.
|
|
30
|
+
expect(BadgeIcon.ORPHAN).toBe('๐ป');
|
|
31
|
+
expect(BadgeIcon.NESTED).toBe('๐');
|
|
32
|
+
expect(BadgeIcon.CAUTION).toBe('โ ');
|
|
33
|
+
expect(BadgeIcon.NEW).toBe('โ
');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('RelationIcon emoji values match ADR-077', () => {
|
|
37
|
+
expect(RelationIcon.GROUPS).toBe('โ');
|
|
38
|
+
expect(RelationIcon.PARENT).toBe('โคด');
|
|
39
|
+
expect(RelationIcon.DEPENDS).toBe('โจ');
|
|
40
|
+
expect(RelationIcon.BLOCKS).toBe('โ');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('no two enum members share the same runtime string value', () => {
|
|
44
|
+
const all = [
|
|
45
|
+
...Object.values(StatusIcon),
|
|
46
|
+
...Object.values(KindIcon),
|
|
47
|
+
...Object.values(BadgeIcon),
|
|
48
|
+
...Object.values(RelationIcon),
|
|
49
|
+
];
|
|
50
|
+
const unique = new Set(all);
|
|
51
|
+
expect(unique.size).toBe(all.length);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('ascii()', () => {
|
|
56
|
+
it('returns NO_COLOR fallback for every StatusIcon', () => {
|
|
57
|
+
expect(ascii(StatusIcon.PENDING)).toBe('[ ]');
|
|
58
|
+
expect(ascii(StatusIcon.ACTIVE)).toBe('[~]');
|
|
59
|
+
expect(ascii(StatusIcon.DONE)).toBe('[x]');
|
|
60
|
+
expect(ascii(StatusIcon.BLOCKED)).toBe('[!]');
|
|
61
|
+
expect(ascii(StatusIcon.ARCHIVED)).toBe('[#]');
|
|
62
|
+
expect(ascii(StatusIcon.CANCELLED)).toBe('[-]');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('returns NO_COLOR fallback for every KindIcon', () => {
|
|
66
|
+
expect(ascii(KindIcon.SAGA)).toBe('SG');
|
|
67
|
+
expect(ascii(KindIcon.EPIC)).toBe('E');
|
|
68
|
+
expect(ascii(KindIcon.TASK)).toBe('-');
|
|
69
|
+
expect(ascii(KindIcon.SUBTASK)).toBe('.');
|
|
70
|
+
expect(ascii(KindIcon.RESEARCH)).toBe('R');
|
|
71
|
+
expect(ascii(KindIcon.BUG)).toBe('B');
|
|
72
|
+
expect(ascii(KindIcon.RELEASE)).toBe('>');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('returns NO_COLOR fallback for every BadgeIcon', () => {
|
|
76
|
+
expect(ascii(BadgeIcon.EMPTY)).toBe('(empty)');
|
|
77
|
+
expect(ascii(BadgeIcon.ORPHAN)).toBe('(orphan)');
|
|
78
|
+
expect(ascii(BadgeIcon.NESTED)).toBe('(nested)');
|
|
79
|
+
expect(ascii(BadgeIcon.CAUTION)).toBe('(!)');
|
|
80
|
+
expect(ascii(BadgeIcon.NEW)).toBe('(new)');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('returns NO_COLOR fallback for every RelationIcon', () => {
|
|
84
|
+
expect(ascii(RelationIcon.GROUPS)).toBe('in');
|
|
85
|
+
expect(ascii(RelationIcon.PARENT)).toBe('^');
|
|
86
|
+
expect(ascii(RelationIcon.DEPENDS)).toBe('->');
|
|
87
|
+
expect(ascii(RelationIcon.BLOCKS)).toBe('!>');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('pickIcon()', () => {
|
|
92
|
+
const originalNoColor = process.env['NO_COLOR'];
|
|
93
|
+
const originalTerm = process.env['TERM'];
|
|
94
|
+
|
|
95
|
+
beforeEach(() => {
|
|
96
|
+
delete process.env['NO_COLOR'];
|
|
97
|
+
delete process.env['TERM'];
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
afterEach(() => {
|
|
101
|
+
if (originalNoColor === undefined) {
|
|
102
|
+
delete process.env['NO_COLOR'];
|
|
103
|
+
} else {
|
|
104
|
+
process.env['NO_COLOR'] = originalNoColor;
|
|
105
|
+
}
|
|
106
|
+
if (originalTerm === undefined) {
|
|
107
|
+
delete process.env['TERM'];
|
|
108
|
+
} else {
|
|
109
|
+
process.env['TERM'] = originalTerm;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('returns the emoji when noColor is explicitly false', () => {
|
|
114
|
+
expect(pickIcon(KindIcon.SAGA, { noColor: false })).toBe('๐ฒ');
|
|
115
|
+
expect(pickIcon(StatusIcon.DONE, { noColor: false })).toBe('โ
');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('returns the ASCII form when noColor is explicitly true', () => {
|
|
119
|
+
expect(pickIcon(KindIcon.SAGA, { noColor: true })).toBe('SG');
|
|
120
|
+
expect(pickIcon(StatusIcon.DONE, { noColor: true })).toBe('[x]');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('falls back to NO_COLOR=1 from process.env', () => {
|
|
124
|
+
process.env['NO_COLOR'] = '1';
|
|
125
|
+
expect(pickIcon(KindIcon.EPIC)).toBe('E');
|
|
126
|
+
expect(pickIcon(BadgeIcon.ORPHAN)).toBe('(orphan)');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('falls back to TERM=dumb from process.env', () => {
|
|
130
|
+
process.env['TERM'] = 'dumb';
|
|
131
|
+
expect(pickIcon(RelationIcon.GROUPS)).toBe('in');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('returns the emoji when no NO_COLOR signals are present', () => {
|
|
135
|
+
expect(pickIcon(KindIcon.TASK)).toBe('โข');
|
|
136
|
+
expect(pickIcon(StatusIcon.ACTIVE)).toBe('๐ง');
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed icon enums for the human-render contract (ADR-077).
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for the visual language used across all renderers
|
|
5
|
+
* (tree, table, list, badge). Each enum is emoji-first; `ascii()` returns the
|
|
6
|
+
* NO_COLOR-safe fallback for non-UTF-8 terminals or `NO_COLOR=1` environments.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Status icons โ agent-task lifecycle states.
|
|
13
|
+
*
|
|
14
|
+
* Emoji-first; `ascii()` returns a NO_COLOR-safe fallback.
|
|
15
|
+
*/
|
|
16
|
+
export enum StatusIcon {
|
|
17
|
+
/** Task is queued and has not started. */
|
|
18
|
+
PENDING = 'โณ',
|
|
19
|
+
/** Task is in progress. */
|
|
20
|
+
ACTIVE = '๐ง',
|
|
21
|
+
/** Task completed successfully. */
|
|
22
|
+
DONE = 'โ
',
|
|
23
|
+
/** Task is blocked on a dependency or gate. */
|
|
24
|
+
BLOCKED = '๐ช',
|
|
25
|
+
/** Task is archived and no longer actively tracked. */
|
|
26
|
+
ARCHIVED = '๐',
|
|
27
|
+
/** Task was cancelled before completion. */
|
|
28
|
+
CANCELLED = 'โ',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Task-kind icons โ Saga / Epic / Task / Subtask plus ancillary kinds.
|
|
33
|
+
*/
|
|
34
|
+
export enum KindIcon {
|
|
35
|
+
/** Saga โ multi-release theme grouping โฅ 2 Epics. */
|
|
36
|
+
SAGA = '๐ฒ',
|
|
37
|
+
/** Epic โ one releasable slice. */
|
|
38
|
+
EPIC = '๐',
|
|
39
|
+
/** Task โ one atomic PR-sized change. */
|
|
40
|
+
TASK = 'โข',
|
|
41
|
+
/** Subtask โ one commit contributing to a Task's PR. */
|
|
42
|
+
SUBTASK = 'โฆ',
|
|
43
|
+
/** Research task โ investigation or audit. */
|
|
44
|
+
RESEARCH = '๐',
|
|
45
|
+
/** Bug task โ defect tracking. */
|
|
46
|
+
BUG = '๐',
|
|
47
|
+
/** Release task โ shipping work. */
|
|
48
|
+
RELEASE = '๐',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Badge icons โ flags attached to tasks (empty, orphan, nested, etc).
|
|
53
|
+
*/
|
|
54
|
+
export enum BadgeIcon {
|
|
55
|
+
/** Container has no children. */
|
|
56
|
+
EMPTY = '๐ชฆ',
|
|
57
|
+
/**
|
|
58
|
+
* Task has no parent and is unreachable from a Saga or Epic.
|
|
59
|
+
*
|
|
60
|
+
* NOTE: ADR-077 ยง2 originally specified `'๐ช'`, identical to `StatusIcon.BLOCKED`.
|
|
61
|
+
* TypeScript string enums share a single runtime string so `ascii()` cannot
|
|
62
|
+
* disambiguate. Resolved by switching to `'๐ป'` (abandoned/lonely semantics).
|
|
63
|
+
* A follow-up ADR amendment lands in T10137 (B12).
|
|
64
|
+
*/
|
|
65
|
+
ORPHAN = '๐ป',
|
|
66
|
+
/** Task is nested deeper than the depth budget allows. */
|
|
67
|
+
NESTED = '๐',
|
|
68
|
+
/** Caution โ something needs human attention. */
|
|
69
|
+
CAUTION = 'โ ',
|
|
70
|
+
/** Recently added โ surfaced for visibility. */
|
|
71
|
+
NEW = 'โ
',
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Relation icons โ edge semantics in tree views.
|
|
76
|
+
*/
|
|
77
|
+
export enum RelationIcon {
|
|
78
|
+
/** Saga groups Epic via `task_relations.relation_type='groups'`. */
|
|
79
|
+
GROUPS = 'โ',
|
|
80
|
+
/** Parent edge โ direct hierarchical parent. */
|
|
81
|
+
PARENT = 'โคด',
|
|
82
|
+
/** Depends-on edge โ A cannot start until B is done. */
|
|
83
|
+
DEPENDS = 'โจ',
|
|
84
|
+
/** Blocks edge โ A actively blocks B from progressing. */
|
|
85
|
+
BLOCKS = 'โ',
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Union of every icon enum โ useful as the parameter type for helpers that
|
|
90
|
+
* accept any icon without caring which category it came from.
|
|
91
|
+
*/
|
|
92
|
+
export type AnyIcon = StatusIcon | KindIcon | BadgeIcon | RelationIcon;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Maps an emoji icon to its NO_COLOR-safe ASCII equivalent.
|
|
96
|
+
*
|
|
97
|
+
* Used when stdout is not a UTF-8 TTY or `NO_COLOR=1` is set. Every enum
|
|
98
|
+
* member has an explicit ASCII fallback โ the exhaustive switch ensures the
|
|
99
|
+
* TypeScript compiler flags any future icon added without an ASCII mapping.
|
|
100
|
+
*
|
|
101
|
+
* @param icon - Any icon from `StatusIcon`, `KindIcon`, `BadgeIcon`, or `RelationIcon`.
|
|
102
|
+
* @returns The ASCII-only string to render in NO_COLOR contexts.
|
|
103
|
+
*/
|
|
104
|
+
export function ascii(icon: AnyIcon): string {
|
|
105
|
+
switch (icon) {
|
|
106
|
+
// Status
|
|
107
|
+
case StatusIcon.PENDING:
|
|
108
|
+
return '[ ]';
|
|
109
|
+
case StatusIcon.ACTIVE:
|
|
110
|
+
return '[~]';
|
|
111
|
+
case StatusIcon.DONE:
|
|
112
|
+
return '[x]';
|
|
113
|
+
case StatusIcon.BLOCKED:
|
|
114
|
+
return '[!]';
|
|
115
|
+
case StatusIcon.ARCHIVED:
|
|
116
|
+
return '[#]';
|
|
117
|
+
case StatusIcon.CANCELLED:
|
|
118
|
+
return '[-]';
|
|
119
|
+
// Kind
|
|
120
|
+
case KindIcon.SAGA:
|
|
121
|
+
return 'SG';
|
|
122
|
+
case KindIcon.EPIC:
|
|
123
|
+
return 'E';
|
|
124
|
+
case KindIcon.TASK:
|
|
125
|
+
return '-';
|
|
126
|
+
case KindIcon.SUBTASK:
|
|
127
|
+
return '.';
|
|
128
|
+
case KindIcon.RESEARCH:
|
|
129
|
+
return 'R';
|
|
130
|
+
case KindIcon.BUG:
|
|
131
|
+
return 'B';
|
|
132
|
+
case KindIcon.RELEASE:
|
|
133
|
+
return '>';
|
|
134
|
+
// Badge
|
|
135
|
+
case BadgeIcon.EMPTY:
|
|
136
|
+
return '(empty)';
|
|
137
|
+
case BadgeIcon.ORPHAN:
|
|
138
|
+
return '(orphan)';
|
|
139
|
+
case BadgeIcon.NESTED:
|
|
140
|
+
return '(nested)';
|
|
141
|
+
case BadgeIcon.CAUTION:
|
|
142
|
+
return '(!)';
|
|
143
|
+
case BadgeIcon.NEW:
|
|
144
|
+
return '(new)';
|
|
145
|
+
// Relation
|
|
146
|
+
case RelationIcon.GROUPS:
|
|
147
|
+
return 'in';
|
|
148
|
+
case RelationIcon.PARENT:
|
|
149
|
+
return '^';
|
|
150
|
+
case RelationIcon.DEPENDS:
|
|
151
|
+
return '->';
|
|
152
|
+
case RelationIcon.BLOCKS:
|
|
153
|
+
return '!>';
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Options for {@link pickIcon}.
|
|
159
|
+
*/
|
|
160
|
+
export interface PickIconOptions {
|
|
161
|
+
/**
|
|
162
|
+
* Force ASCII fallback. When omitted, falls back to `NO_COLOR=1` or
|
|
163
|
+
* `TERM=dumb` in `process.env`.
|
|
164
|
+
*/
|
|
165
|
+
noColor?: boolean;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Returns the icon to render given the current environment.
|
|
170
|
+
*
|
|
171
|
+
* Honors `NO_COLOR=1` and `TERM=dumb` for ASCII fallback when `opts.noColor`
|
|
172
|
+
* is not provided explicitly.
|
|
173
|
+
*
|
|
174
|
+
* @param icon - The icon to render.
|
|
175
|
+
* @param opts - Optional override for NO_COLOR detection.
|
|
176
|
+
* @returns The emoji icon, or its ASCII equivalent if NO_COLOR mode is active.
|
|
177
|
+
*/
|
|
178
|
+
export function pickIcon<I extends AnyIcon>(icon: I, opts?: PickIconOptions): string {
|
|
179
|
+
const noColor =
|
|
180
|
+
opts?.noColor ?? (process.env['NO_COLOR'] === '1' || process.env['TERM'] === 'dumb');
|
|
181
|
+
return noColor ? ascii(icon) : icon;
|
|
182
|
+
}
|