diffstalker 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/.dependency-cruiser.cjs +67 -0
  2. package/.githooks/pre-commit +2 -0
  3. package/.githooks/pre-push +15 -0
  4. package/.github/workflows/release.yml +8 -0
  5. package/README.md +43 -35
  6. package/bun.lock +82 -3
  7. package/dist/App.js +555 -552
  8. package/dist/FollowMode.js +85 -0
  9. package/dist/KeyBindings.js +228 -0
  10. package/dist/MouseHandlers.js +192 -0
  11. package/dist/core/ExplorerStateManager.js +423 -78
  12. package/dist/core/GitStateManager.js +260 -119
  13. package/dist/git/diff.js +102 -17
  14. package/dist/git/status.js +16 -54
  15. package/dist/git/test-helpers.js +67 -0
  16. package/dist/index.js +60 -53
  17. package/dist/ipc/CommandClient.js +6 -7
  18. package/dist/state/UIState.js +39 -4
  19. package/dist/ui/PaneRenderers.js +76 -0
  20. package/dist/ui/modals/FileFinder.js +193 -0
  21. package/dist/ui/modals/HotkeysModal.js +12 -3
  22. package/dist/ui/modals/ThemePicker.js +1 -2
  23. package/dist/ui/widgets/CommitPanel.js +1 -1
  24. package/dist/ui/widgets/CompareListView.js +123 -80
  25. package/dist/ui/widgets/DiffView.js +228 -180
  26. package/dist/ui/widgets/ExplorerContent.js +15 -28
  27. package/dist/ui/widgets/ExplorerView.js +148 -43
  28. package/dist/ui/widgets/FileList.js +62 -95
  29. package/dist/ui/widgets/FlatFileList.js +65 -0
  30. package/dist/ui/widgets/Footer.js +25 -11
  31. package/dist/ui/widgets/Header.js +17 -52
  32. package/dist/ui/widgets/fileRowFormatters.js +73 -0
  33. package/dist/utils/ansiTruncate.js +0 -1
  34. package/dist/utils/displayRows.js +101 -21
  35. package/dist/utils/fileCategories.js +37 -0
  36. package/dist/utils/fileTree.js +148 -0
  37. package/dist/utils/flatFileList.js +67 -0
  38. package/dist/utils/layoutCalculations.js +5 -3
  39. package/eslint.metrics.js +15 -0
  40. package/metrics/.gitkeep +0 -0
  41. package/metrics/v0.2.1.json +268 -0
  42. package/metrics/v0.2.2.json +229 -0
  43. package/package.json +9 -2
  44. package/dist/utils/ansiToBlessed.js +0 -125
  45. package/dist/utils/mouseCoordinates.js +0 -165
  46. package/dist/utils/rowCalculations.js +0 -246
@@ -0,0 +1,268 @@
1
+ {
2
+ "timestamp": "2026-01-30T15:45:56.951Z",
3
+ "gitRef": "v0.2.1",
4
+ "gitSha": "3d0506b",
5
+ "summary": {
6
+ "files": 62,
7
+ "lines": 12327,
8
+ "functions": 754,
9
+ "avgCyclomaticComplexity": 5.6,
10
+ "maxCyclomaticComplexity": {
11
+ "value": 64,
12
+ "function": "formatDisplayRow",
13
+ "file": "src/ui/widgets/DiffView.ts:68"
14
+ },
15
+ "avgCognitiveComplexity": 7.6,
16
+ "maxCognitiveComplexity": {
17
+ "value": 96,
18
+ "function": "formatDisplayRow",
19
+ "file": "src/ui/widgets/DiffView.ts:68"
20
+ },
21
+ "smells": 44
22
+ },
23
+ "hotspots": [
24
+ {
25
+ "file": "src/App.ts",
26
+ "lines": 1131,
27
+ "cyclomaticMax": 19,
28
+ "cognitiveMax": 16,
29
+ "smells": 7
30
+ },
31
+ {
32
+ "file": "src/core/ExplorerStateManager.ts",
33
+ "lines": 769,
34
+ "cyclomaticMax": 18,
35
+ "cognitiveMax": 34,
36
+ "smells": 7
37
+ },
38
+ {
39
+ "file": "src/ui/widgets/Header.ts",
40
+ "lines": 89,
41
+ "cyclomaticMax": 14,
42
+ "cognitiveMax": 22,
43
+ "smells": 3
44
+ },
45
+ {
46
+ "file": "src/state/UIState.ts",
47
+ "lines": 280,
48
+ "cyclomaticMax": 10,
49
+ "cognitiveMax": 12,
50
+ "smells": 3
51
+ },
52
+ {
53
+ "file": "src/ipc/CommandClient.ts",
54
+ "lines": 202,
55
+ "cyclomaticMax": 7,
56
+ "cognitiveMax": 6,
57
+ "smells": 3
58
+ },
59
+ {
60
+ "file": "src/utils/displayRows.ts",
61
+ "lines": 459,
62
+ "cyclomaticMax": 42,
63
+ "cognitiveMax": 68,
64
+ "smells": 2
65
+ },
66
+ {
67
+ "file": "src/index.ts",
68
+ "lines": 178,
69
+ "cyclomaticMax": 16,
70
+ "cognitiveMax": 17,
71
+ "smells": 2
72
+ },
73
+ {
74
+ "file": "src/utils/ansiTruncate.ts",
75
+ "lines": 124,
76
+ "cyclomaticMax": 16,
77
+ "cognitiveMax": 24,
78
+ "smells": 2
79
+ },
80
+ {
81
+ "file": "src/utils/languageDetection.ts",
82
+ "lines": 258,
83
+ "cyclomaticMax": 10,
84
+ "cognitiveMax": 8,
85
+ "smells": 2
86
+ },
87
+ {
88
+ "file": "src/ui/modals/HotkeysModal.ts",
89
+ "lines": 242,
90
+ "cyclomaticMax": 7,
91
+ "cognitiveMax": 9,
92
+ "smells": 2
93
+ },
94
+ {
95
+ "file": "src/ui/modals/ThemePicker.ts",
96
+ "lines": 133,
97
+ "cyclomaticMax": 5,
98
+ "cognitiveMax": 7,
99
+ "smells": 2
100
+ },
101
+ {
102
+ "file": "src/ui/widgets/DiffView.ts",
103
+ "lines": 366,
104
+ "cyclomaticMax": 64,
105
+ "cognitiveMax": 96,
106
+ "smells": 1
107
+ },
108
+ {
109
+ "file": "src/git/diff.ts",
110
+ "lines": 567,
111
+ "cyclomaticMax": 27,
112
+ "cognitiveMax": 43,
113
+ "smells": 1
114
+ },
115
+ {
116
+ "file": "src/ui/widgets/ExplorerView.ts",
117
+ "lines": 243,
118
+ "cyclomaticMax": 24,
119
+ "cognitiveMax": 37,
120
+ "smells": 1
121
+ },
122
+ {
123
+ "file": "src/ui/widgets/ExplorerContent.ts",
124
+ "lines": 131,
125
+ "cyclomaticMax": 20,
126
+ "cognitiveMax": 24,
127
+ "smells": 1
128
+ },
129
+ {
130
+ "file": "src/ui/PaneRenderers.ts",
131
+ "lines": 170,
132
+ "cyclomaticMax": 18,
133
+ "cognitiveMax": 6,
134
+ "smells": 1
135
+ },
136
+ {
137
+ "file": "src/ui/widgets/FileList.ts",
138
+ "lines": 225,
139
+ "cyclomaticMax": 16,
140
+ "cognitiveMax": 26,
141
+ "smells": 1
142
+ },
143
+ {
144
+ "file": "src/ui/widgets/CommitPanel.ts",
145
+ "lines": 83,
146
+ "cyclomaticMax": 14,
147
+ "cognitiveMax": 12,
148
+ "smells": 1
149
+ },
150
+ {
151
+ "file": "src/ui/widgets/Footer.ts",
152
+ "lines": 67,
153
+ "cyclomaticMax": 7,
154
+ "cognitiveMax": 7,
155
+ "smells": 1
156
+ },
157
+ {
158
+ "file": "src/core/GitOperationQueue.test.ts",
159
+ "lines": 276,
160
+ "cyclomaticMax": 0,
161
+ "cognitiveMax": 0,
162
+ "smells": 1
163
+ },
164
+ {
165
+ "file": "src/git/status.ts",
166
+ "lines": 310,
167
+ "cyclomaticMax": 28,
168
+ "cognitiveMax": 32,
169
+ "smells": 0
170
+ },
171
+ {
172
+ "file": "src/ui/widgets/CompareListView.ts",
173
+ "lines": 350,
174
+ "cyclomaticMax": 23,
175
+ "cognitiveMax": 21,
176
+ "smells": 0
177
+ },
178
+ {
179
+ "file": "src/MouseHandlers.ts",
180
+ "lines": 212,
181
+ "cyclomaticMax": 17,
182
+ "cognitiveMax": 15,
183
+ "smells": 0
184
+ },
185
+ {
186
+ "file": "src/ipc/CommandServer.ts",
187
+ "lines": 266,
188
+ "cyclomaticMax": 17,
189
+ "cognitiveMax": 6,
190
+ "smells": 0
191
+ },
192
+ {
193
+ "file": "src/utils/diffRowCalculations.ts",
194
+ "lines": 136,
195
+ "cyclomaticMax": 13,
196
+ "cognitiveMax": 24,
197
+ "smells": 0
198
+ },
199
+ {
200
+ "file": "src/utils/lineBreaking.ts",
201
+ "lines": 114,
202
+ "cyclomaticMax": 12,
203
+ "cognitiveMax": 17,
204
+ "smells": 0
205
+ },
206
+ {
207
+ "file": "src/config.ts",
208
+ "lines": 107,
209
+ "cyclomaticMax": 10,
210
+ "cognitiveMax": 13,
211
+ "smells": 0
212
+ },
213
+ {
214
+ "file": "src/core/GitStateManager.ts",
215
+ "lines": 720,
216
+ "cyclomaticMax": 9,
217
+ "cognitiveMax": 13,
218
+ "smells": 0
219
+ },
220
+ {
221
+ "file": "src/ui/modals/FileFinder.ts",
222
+ "lines": 280,
223
+ "cyclomaticMax": 9,
224
+ "cognitiveMax": 13,
225
+ "smells": 0
226
+ },
227
+ {
228
+ "file": "src/ui/widgets/HistoryView.ts",
229
+ "lines": 97,
230
+ "cyclomaticMax": 9,
231
+ "cognitiveMax": 12,
232
+ "smells": 0
233
+ },
234
+ {
235
+ "file": "src/utils/formatPath.ts",
236
+ "lines": 72,
237
+ "cyclomaticMax": 9,
238
+ "cognitiveMax": 11,
239
+ "smells": 0
240
+ },
241
+ {
242
+ "file": "src/utils/explorerDisplayRows.ts",
243
+ "lines": 206,
244
+ "cyclomaticMax": 8,
245
+ "cognitiveMax": 15,
246
+ "smells": 0
247
+ },
248
+ {
249
+ "file": "src/ui/modals/BaseBranchPicker.ts",
250
+ "lines": 134,
251
+ "cyclomaticMax": 6,
252
+ "cognitiveMax": 13,
253
+ "smells": 0
254
+ }
255
+ ],
256
+ "smellsByRule": {
257
+ "@typescript-eslint/no-unused-vars": 22,
258
+ "@typescript-eslint/no-explicit-any": 1,
259
+ "sonarjs/no-ignored-exceptions": 4,
260
+ "sonarjs/slow-regex": 4,
261
+ "sonarjs/updated-loop-counter": 2,
262
+ "prefer-const": 1,
263
+ "sonarjs/no-dead-store": 2,
264
+ "sonarjs/no-all-duplicated-branches": 1,
265
+ "sonarjs/no-nested-conditional": 3,
266
+ "no-control-regex": 4
267
+ }
268
+ }
@@ -0,0 +1,229 @@
1
+ {
2
+ "timestamp": "2026-02-01T21:09:51.898Z",
3
+ "gitRef": "v0.2.2",
4
+ "gitSha": "b349fdc",
5
+ "summary": {
6
+ "files": 77,
7
+ "lines": 14894,
8
+ "functions": 1046,
9
+ "avgCyclomaticComplexity": 5.3,
10
+ "maxCyclomaticComplexity": {
11
+ "value": 42,
12
+ "function": "buildDiffDisplayRows",
13
+ "file": "src/utils/displayRows.ts:112"
14
+ },
15
+ "avgCognitiveComplexity": 6.4,
16
+ "maxCognitiveComplexity": {
17
+ "value": 68,
18
+ "function": "buildDiffDisplayRows",
19
+ "file": "src/utils/displayRows.ts:112"
20
+ },
21
+ "smells": 0
22
+ },
23
+ "hotspots": [
24
+ {
25
+ "file": "src/utils/displayRows.ts",
26
+ "lines": 567,
27
+ "cyclomaticMax": 42,
28
+ "cognitiveMax": 68,
29
+ "smells": 0
30
+ },
31
+ {
32
+ "file": "src/utils/flatFileList.ts",
33
+ "lines": 100,
34
+ "cyclomaticMax": 29,
35
+ "cognitiveMax": 14,
36
+ "smells": 0
37
+ },
38
+ {
39
+ "file": "src/git/diff.ts",
40
+ "lines": 656,
41
+ "cyclomaticMax": 26,
42
+ "cognitiveMax": 41,
43
+ "smells": 0
44
+ },
45
+ {
46
+ "file": "src/App.ts",
47
+ "lines": 1391,
48
+ "cyclomaticMax": 23,
49
+ "cognitiveMax": 13,
50
+ "smells": 0
51
+ },
52
+ {
53
+ "file": "src/git/status.ts",
54
+ "lines": 268,
55
+ "cyclomaticMax": 22,
56
+ "cognitiveMax": 25,
57
+ "smells": 0
58
+ },
59
+ {
60
+ "file": "src/ui/PaneRenderers.ts",
61
+ "lines": 224,
62
+ "cyclomaticMax": 20,
63
+ "cognitiveMax": 8,
64
+ "smells": 0
65
+ },
66
+ {
67
+ "file": "src/ui/widgets/ExplorerContent.ts",
68
+ "lines": 130,
69
+ "cyclomaticMax": 20,
70
+ "cognitiveMax": 24,
71
+ "smells": 0
72
+ },
73
+ {
74
+ "file": "src/MouseHandlers.ts",
75
+ "lines": 257,
76
+ "cyclomaticMax": 17,
77
+ "cognitiveMax": 15,
78
+ "smells": 0
79
+ },
80
+ {
81
+ "file": "src/ipc/CommandServer.ts",
82
+ "lines": 266,
83
+ "cyclomaticMax": 17,
84
+ "cognitiveMax": 6,
85
+ "smells": 0
86
+ },
87
+ {
88
+ "file": "src/index.ts",
89
+ "lines": 178,
90
+ "cyclomaticMax": 16,
91
+ "cognitiveMax": 17,
92
+ "smells": 0
93
+ },
94
+ {
95
+ "file": "src/ui/widgets/ExplorerView.ts",
96
+ "lines": 245,
97
+ "cyclomaticMax": 16,
98
+ "cognitiveMax": 21,
99
+ "smells": 0
100
+ },
101
+ {
102
+ "file": "src/utils/ansiTruncate.ts",
103
+ "lines": 123,
104
+ "cyclomaticMax": 16,
105
+ "cognitiveMax": 24,
106
+ "smells": 0
107
+ },
108
+ {
109
+ "file": "src/ui/widgets/DiffView.ts",
110
+ "lines": 511,
111
+ "cyclomaticMax": 15,
112
+ "cognitiveMax": 12,
113
+ "smells": 0
114
+ },
115
+ {
116
+ "file": "src/ui/widgets/CompareListView.ts",
117
+ "lines": 383,
118
+ "cyclomaticMax": 14,
119
+ "cognitiveMax": 16,
120
+ "smells": 0
121
+ },
122
+ {
123
+ "file": "src/ui/widgets/CommitPanel.ts",
124
+ "lines": 83,
125
+ "cyclomaticMax": 13,
126
+ "cognitiveMax": 11,
127
+ "smells": 0
128
+ },
129
+ {
130
+ "file": "src/utils/diffRowCalculations.ts",
131
+ "lines": 136,
132
+ "cyclomaticMax": 13,
133
+ "cognitiveMax": 24,
134
+ "smells": 0
135
+ },
136
+ {
137
+ "file": "src/core/ExplorerStateManager.ts",
138
+ "lines": 750,
139
+ "cyclomaticMax": 12,
140
+ "cognitiveMax": 15,
141
+ "smells": 0
142
+ },
143
+ {
144
+ "file": "src/ui/widgets/FileList.ts",
145
+ "lines": 215,
146
+ "cyclomaticMax": 12,
147
+ "cognitiveMax": 12,
148
+ "smells": 0
149
+ },
150
+ {
151
+ "file": "src/utils/lineBreaking.ts",
152
+ "lines": 114,
153
+ "cyclomaticMax": 12,
154
+ "cognitiveMax": 17,
155
+ "smells": 0
156
+ },
157
+ {
158
+ "file": "src/ui/widgets/Header.ts",
159
+ "lines": 98,
160
+ "cyclomaticMax": 11,
161
+ "cognitiveMax": 13,
162
+ "smells": 0
163
+ },
164
+ {
165
+ "file": "src/config.ts",
166
+ "lines": 107,
167
+ "cyclomaticMax": 10,
168
+ "cognitiveMax": 13,
169
+ "smells": 0
170
+ },
171
+ {
172
+ "file": "src/state/UIState.ts",
173
+ "lines": 307,
174
+ "cyclomaticMax": 10,
175
+ "cognitiveMax": 12,
176
+ "smells": 0
177
+ },
178
+ {
179
+ "file": "src/KeyBindings.ts",
180
+ "lines": 289,
181
+ "cyclomaticMax": 9,
182
+ "cognitiveMax": 15,
183
+ "smells": 0
184
+ },
185
+ {
186
+ "file": "src/core/GitStateManager.ts",
187
+ "lines": 815,
188
+ "cyclomaticMax": 9,
189
+ "cognitiveMax": 12,
190
+ "smells": 0
191
+ },
192
+ {
193
+ "file": "src/ui/widgets/HistoryView.ts",
194
+ "lines": 97,
195
+ "cyclomaticMax": 9,
196
+ "cognitiveMax": 12,
197
+ "smells": 0
198
+ },
199
+ {
200
+ "file": "src/utils/formatPath.ts",
201
+ "lines": 72,
202
+ "cyclomaticMax": 9,
203
+ "cognitiveMax": 11,
204
+ "smells": 0
205
+ },
206
+ {
207
+ "file": "src/utils/explorerDisplayRows.ts",
208
+ "lines": 206,
209
+ "cyclomaticMax": 8,
210
+ "cognitiveMax": 15,
211
+ "smells": 0
212
+ },
213
+ {
214
+ "file": "src/ui/modals/FileFinder.ts",
215
+ "lines": 226,
216
+ "cyclomaticMax": 7,
217
+ "cognitiveMax": 13,
218
+ "smells": 0
219
+ },
220
+ {
221
+ "file": "src/ui/modals/BaseBranchPicker.ts",
222
+ "lines": 134,
223
+ "cyclomaticMax": 6,
224
+ "cognitiveMax": 13,
225
+ "smells": 0
226
+ }
227
+ ],
228
+ "smellsByRule": {}
229
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diffstalker",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Terminal application that displays git diff/status for directories",
5
5
  "author": "yogh-io",
6
6
  "license": "MIT",
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "scripts": {
21
21
  "postinstall": "node scripts/patch-neo-blessed.cjs",
22
+ "prepare": "git config core.hooksPath .githooks",
22
23
  "dev": "bun --watch src/index.ts",
23
24
  "build": "tsc",
24
25
  "build:prod": "tsc && bun build dist/index.js --outfile dist/index.js --minify --target node --packages external",
@@ -27,10 +28,13 @@
27
28
  "start:bundle": "bun dist/bundle/index.js",
28
29
  "test": "bun test src/*.test.ts src/**/*.test.ts",
29
30
  "test:watch": "bun test --watch src/*.test.ts src/**/*.test.ts",
30
- "lint": "eslint src/",
31
+ "lint": "eslint src/ && depcruise src/ --config .dependency-cruiser.cjs",
31
32
  "lint:fix": "eslint src/ --fix",
32
33
  "format": "prettier --write src/",
33
34
  "format:check": "prettier --check src/",
35
+ "deps": "depcruise src/ --config .dependency-cruiser.cjs",
36
+ "metrics": "bun scripts/collect-metrics.ts",
37
+ "metrics:snapshot": "bun scripts/collect-metrics.ts --save",
34
38
  "prepublishOnly": "bun run build:prod"
35
39
  },
36
40
  "keywords": [
@@ -47,6 +51,7 @@
47
51
  "chokidar": "^4.0.3",
48
52
  "emphasize": "^7.0.0",
49
53
  "fast-diff": "^1.3.0",
54
+ "fzf": "^0.5.2",
50
55
  "ignore": "^7.0.5",
51
56
  "neo-blessed": "^0.2.0",
52
57
  "simple-git": "^3.27.0",
@@ -56,8 +61,10 @@
56
61
  "@eslint/js": "^9.39.2",
57
62
  "@types/blessed": "^0.1.27",
58
63
  "@types/node": "^22.10.7",
64
+ "dependency-cruiser": "^17.3.7",
59
65
  "eslint": "^9.39.2",
60
66
  "eslint-config-prettier": "^10.1.8",
67
+ "eslint-plugin-sonarjs": "^3.0.6",
61
68
  "patch-package": "^8.0.1",
62
69
  "prettier": "^3.8.0",
63
70
  "typescript": "^5.7.3",
@@ -1,125 +0,0 @@
1
- /**
2
- * Convert ANSI escape codes to blessed tags.
3
- * Supports basic foreground colors and styles.
4
- */
5
- // ANSI color code to blessed color name mapping
6
- const ANSI_FG_COLORS = {
7
- 30: 'black',
8
- 31: 'red',
9
- 32: 'green',
10
- 33: 'yellow',
11
- 34: 'blue',
12
- 35: 'magenta',
13
- 36: 'cyan',
14
- 37: 'white',
15
- 90: 'gray',
16
- 91: 'red',
17
- 92: 'green',
18
- 93: 'yellow',
19
- 94: 'blue',
20
- 95: 'magenta',
21
- 96: 'cyan',
22
- 97: 'white',
23
- };
24
- /**
25
- * Escape blessed tags in plain text.
26
- */
27
- function escapeBlessed(text) {
28
- return text.replace(/\{/g, '{{').replace(/\}/g, '}}');
29
- }
30
- /**
31
- * Convert ANSI escape sequences to blessed tags.
32
- *
33
- * @param input - String containing ANSI escape codes
34
- * @returns String with blessed tags
35
- */
36
- export function ansiToBlessed(input) {
37
- if (!input)
38
- return '';
39
- // Track current styles
40
- const activeStyles = [];
41
- let result = '';
42
- let i = 0;
43
- while (i < input.length) {
44
- // Check for ANSI escape sequence
45
- if (input[i] === '\x1b' && input[i + 1] === '[') {
46
- // Find the end of the sequence (look for 'm')
47
- let j = i + 2;
48
- while (j < input.length && input[j] !== 'm') {
49
- j++;
50
- }
51
- if (input[j] === 'm') {
52
- // Parse the codes
53
- const codes = input
54
- .slice(i + 2, j)
55
- .split(';')
56
- .map(Number);
57
- for (const code of codes) {
58
- if (code === 0) {
59
- // Reset - close all active styles
60
- while (activeStyles.length > 0) {
61
- const style = activeStyles.pop();
62
- if (style) {
63
- result += `{/${style}}`;
64
- }
65
- }
66
- }
67
- else if (code === 1) {
68
- // Bold
69
- activeStyles.push('bold');
70
- result += '{bold}';
71
- }
72
- else if (code === 2) {
73
- // Dim/faint - blessed doesn't have direct support, use gray
74
- activeStyles.push('gray-fg');
75
- result += '{gray-fg}';
76
- }
77
- else if (code === 3) {
78
- // Italic - not well supported in terminals, skip
79
- }
80
- else if (code === 4) {
81
- // Underline
82
- activeStyles.push('underline');
83
- result += '{underline}';
84
- }
85
- else if (code >= 30 && code <= 37) {
86
- // Standard foreground colors
87
- const color = ANSI_FG_COLORS[code];
88
- if (color) {
89
- activeStyles.push(`${color}-fg`);
90
- result += `{${color}-fg}`;
91
- }
92
- }
93
- else if (code >= 90 && code <= 97) {
94
- // Bright foreground colors
95
- const color = ANSI_FG_COLORS[code];
96
- if (color) {
97
- activeStyles.push(`${color}-fg`);
98
- result += `{${color}-fg}`;
99
- }
100
- }
101
- // Note: We ignore background colors (40-47, 100-107) for simplicity
102
- }
103
- i = j + 1;
104
- continue;
105
- }
106
- }
107
- // Regular character - escape if needed and append
108
- const char = input[i];
109
- if (char === '{' || char === '}') {
110
- result += char + char;
111
- }
112
- else {
113
- result += char;
114
- }
115
- i++;
116
- }
117
- // Close any remaining active styles
118
- while (activeStyles.length > 0) {
119
- const style = activeStyles.pop();
120
- if (style) {
121
- result += `{/${style}}`;
122
- }
123
- }
124
- return result;
125
- }