Skip to content

Commit 90543d4

Browse files
adameskaHettinger, David
authored andcommitted
ColorStuff
Signed-off-by: Hettinger, David <[email protected]>
1 parent 53141be commit 90543d4

File tree

5 files changed

+839
-7
lines changed

5 files changed

+839
-7
lines changed

packages/webview/src/component/pods/PodLogs.svelte

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
<script lang="ts">
2-
import type { V1Pod } from '@kubernetes/client-node';
3-
import { getContext, onDestroy, onMount, tick } from 'svelte';
4-
import { Streams } from '/@/stream/streams';
52
import type { IDisposable } from '@kubernetes-dashboard/channels';
3+
import type { V1Pod } from '@kubernetes/client-node';
64
import { EmptyScreen } from '@podman-desktop/ui-svelte';
7-
import NoLogIcon from '/@/component/icons/NoLogIcon.svelte';
85
import type { Terminal } from '@xterm/xterm';
9-
import TerminalWindow from '/@/component/terminal/TerminalWindow.svelte';
6+
import { getContext, onDestroy, onMount, tick } from 'svelte';
107
import { SvelteMap } from 'svelte/reactivity';
11-
import { ansi256Colours, colourizedANSIContainerName } from '/@/component/terminal/terminal-colors';
8+
import NoLogIcon from '/@/component/icons/NoLogIcon.svelte';
9+
import {
10+
ansi256Colours,
11+
colorizeJSON,
12+
colorizeLogLevel,
13+
colourizedANSIContainerName,
14+
} from '/@/component/terminal/terminal-colors';
15+
import TerminalWindow from '/@/component/terminal/TerminalWindow.svelte';
16+
import { Streams } from '/@/stream/streams';
1217
1318
interface Props {
1419
object: V1Pod;
@@ -54,13 +59,19 @@ onMount(async () => {
5459
// All lines are prefixed, except the last one if it's empty.
5560
const lines = data
5661
.split('\n')
62+
.map(line => colorizeJSON(line))
63+
.map(line => colorizeLogLevel(line))
5764
.map((line, index, arr) =>
5865
index < arr.length - 1 || line.length > 0 ? `${padding}${colouredName}|${line}` : line,
5966
);
6067
callback(lines.join('\n'));
6168
}
6269
: (_name: string, data: string, callback: (data: string) => void): void => {
63-
callback(data);
70+
const lines = data
71+
.split('\n')
72+
.map(line => colorizeJSON(line))
73+
.map(line => colorizeLogLevel(line));
74+
callback(lines.join('\n'));
6475
};
6576
6677
for (const containerName of object.spec?.containers.map(c => c.name) ?? []) {
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
/**********************************************************************
2+
* Copyright (C) 2025 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
***********************************************************************/
18+
19+
import { describe, expect, test } from 'vitest';
20+
21+
import type { JsonColorScheme } from './json-colorizer.js';
22+
import { JsonColorizer } from './json-colorizer.js';
23+
24+
describe('JsonColorizer', () => {
25+
const colorScheme: JsonColorScheme = {
26+
braceColor: '\u001b[33m', // yellow
27+
numberColor: '\u001b[32m', // green
28+
booleanColor: '\u001b[35m', // magenta
29+
nullColor: '\u001b[35m', // magenta
30+
reset: '\u001b[0m',
31+
};
32+
33+
const colorizer = new JsonColorizer(colorScheme);
34+
35+
test('should colorize braces in yellow', () => {
36+
const jsonLine = '{"name": "test"}';
37+
const result = colorizer.colorize(jsonLine);
38+
39+
// Braces should be yellow
40+
expect(result).toContain('\u001b[33m{\u001b[0m');
41+
expect(result).toContain('\u001b[33m}\u001b[0m');
42+
43+
// Strings should not be colorized
44+
expect(result).toContain('"name"');
45+
expect(result).toContain('"test"');
46+
});
47+
48+
test('should colorize brackets in yellow', () => {
49+
const jsonLine = '["item1", "item2"]';
50+
const result = colorizer.colorize(jsonLine);
51+
52+
// Brackets should be yellow
53+
expect(result).toContain('\u001b[33m[\u001b[0m');
54+
expect(result).toContain('\u001b[33m]\u001b[0m');
55+
});
56+
57+
test('should colorize JSON with number values', () => {
58+
const jsonLine = '{"count": 42, "price": 19.99}';
59+
const result = colorizer.colorize(jsonLine);
60+
61+
// Braces should be yellow
62+
expect(result).toContain('\u001b[33m{\u001b[0m');
63+
expect(result).toContain('\u001b[33m}\u001b[0m');
64+
65+
// Numbers should be green
66+
expect(result).toContain('\u001b[32m42\u001b[0m');
67+
expect(result).toContain('\u001b[32m19.99\u001b[0m');
68+
69+
// Keys should not be colorized
70+
expect(result).toContain('"count"');
71+
expect(result).toContain('"price"');
72+
});
73+
74+
test('should colorize JSON with boolean values', () => {
75+
const jsonLine = '{"enabled": true, "active": false}';
76+
const result = colorizer.colorize(jsonLine);
77+
78+
// Braces should be yellow
79+
expect(result).toContain('\u001b[33m{\u001b[0m');
80+
expect(result).toContain('\u001b[33m}\u001b[0m');
81+
82+
// Booleans should be magenta
83+
expect(result).toContain('\u001b[35mtrue\u001b[0m');
84+
expect(result).toContain('\u001b[35mfalse\u001b[0m');
85+
});
86+
87+
test('should colorize JSON with null values', () => {
88+
const jsonLine = '{"value": null}';
89+
const result = colorizer.colorize(jsonLine);
90+
91+
// Braces should be yellow
92+
expect(result).toContain('\u001b[33m{\u001b[0m');
93+
expect(result).toContain('\u001b[33m}\u001b[0m');
94+
95+
// null should be magenta
96+
expect(result).toContain('\u001b[35mnull\u001b[0m');
97+
});
98+
99+
test('should colorize JSON array with strings', () => {
100+
const jsonLine = '["item1", "item2", "item3"]';
101+
const result = colorizer.colorize(jsonLine);
102+
103+
// Brackets should be yellow
104+
expect(result).toContain('\u001b[33m[\u001b[0m');
105+
expect(result).toContain('\u001b[33m]\u001b[0m');
106+
107+
// Strings should not be colorized
108+
expect(result).toContain('"item1"');
109+
expect(result).toContain('"item2"');
110+
expect(result).toContain('"item3"');
111+
});
112+
113+
test('should colorize JSON array with numbers', () => {
114+
const jsonLine = '[1, 2, 3, 4.5]';
115+
const result = colorizer.colorize(jsonLine);
116+
117+
// Brackets should be yellow
118+
expect(result).toContain('\u001b[33m[\u001b[0m');
119+
expect(result).toContain('\u001b[33m]\u001b[0m');
120+
121+
// Numbers in array should be green
122+
expect(result).toContain('\u001b[32m1\u001b[0m');
123+
expect(result).toContain('\u001b[32m2\u001b[0m');
124+
expect(result).toContain('\u001b[32m3\u001b[0m');
125+
expect(result).toContain('\u001b[32m4.5\u001b[0m');
126+
});
127+
128+
test('should colorize JSON array with booleans', () => {
129+
const jsonLine = '[true, false, true]';
130+
const result = colorizer.colorize(jsonLine);
131+
132+
// Brackets should be yellow
133+
expect(result).toContain('\u001b[33m[\u001b[0m');
134+
expect(result).toContain('\u001b[33m]\u001b[0m');
135+
136+
// Booleans in array should be magenta
137+
expect(result).toContain('\u001b[35mtrue\u001b[0m');
138+
expect(result).toContain('\u001b[35mfalse\u001b[0m');
139+
});
140+
141+
test('should colorize complex nested JSON', () => {
142+
const jsonLine = '{"user": {"name": "John", "age": 30, "active": true}}';
143+
const result = colorizer.colorize(jsonLine);
144+
145+
// Braces should be yellow (4 total: outer { }, inner { })
146+
expect(result).toContain('\u001b[33m{\u001b[0m');
147+
expect(result).toContain('\u001b[33m}\u001b[0m');
148+
149+
// Values should be appropriately colored
150+
expect(result).toContain('\u001b[32m30\u001b[0m');
151+
expect(result).toContain('\u001b[35mtrue\u001b[0m');
152+
153+
// Strings should not be colorized
154+
expect(result).toContain('"user"');
155+
expect(result).toContain('"name"');
156+
expect(result).toContain('"age"');
157+
expect(result).toContain('"active"');
158+
expect(result).toContain('"John"');
159+
});
160+
161+
test('should colorize JSON with negative numbers', () => {
162+
const jsonLine = '{"temperature": -15, "balance": -99.99}';
163+
const result = colorizer.colorize(jsonLine);
164+
165+
// Braces should be yellow
166+
expect(result).toContain('\u001b[33m{\u001b[0m');
167+
expect(result).toContain('\u001b[33m}\u001b[0m');
168+
169+
// Negative numbers should be green
170+
expect(result).toContain('\u001b[32m-15\u001b[0m');
171+
expect(result).toContain('\u001b[32m-99.99\u001b[0m');
172+
});
173+
174+
test('should colorize JSON log level format', () => {
175+
const jsonLine = '{"timestamp":"123","level":"information","message":"test"}';
176+
const result = colorizer.colorize(jsonLine);
177+
178+
// Braces should be yellow
179+
expect(result).toContain('\u001b[33m{\u001b[0m');
180+
expect(result).toContain('\u001b[33m}\u001b[0m');
181+
182+
// Strings should not be colorized
183+
expect(result).toContain('"timestamp"');
184+
expect(result).toContain('"level"');
185+
expect(result).toContain('"message"');
186+
expect(result).toContain('"123"');
187+
expect(result).toContain('"information"');
188+
expect(result).toContain('"test"');
189+
});
190+
191+
test('should handle empty JSON object', () => {
192+
const jsonLine = '{}';
193+
const result = colorizer.colorize(jsonLine);
194+
195+
// Braces should be yellow
196+
expect(result).toContain('\u001b[33m{\u001b[0m');
197+
expect(result).toContain('\u001b[33m}\u001b[0m');
198+
});
199+
200+
test('should handle empty JSON array', () => {
201+
const jsonLine = '[]';
202+
const result = colorizer.colorize(jsonLine);
203+
204+
// Brackets should be yellow
205+
expect(result).toContain('\u001b[33m[\u001b[0m');
206+
expect(result).toContain('\u001b[33m]\u001b[0m');
207+
});
208+
209+
test('should colorize JSON with mixed types in array', () => {
210+
const jsonLine = '["text", 123, true, null]';
211+
const result = colorizer.colorize(jsonLine);
212+
213+
// Brackets should be yellow
214+
expect(result).toContain('\u001b[33m[\u001b[0m');
215+
expect(result).toContain('\u001b[33m]\u001b[0m');
216+
217+
// Each type should be colorized appropriately
218+
expect(result).toContain('"text"'); // strings not colorized
219+
expect(result).toContain('\u001b[32m123\u001b[0m');
220+
expect(result).toContain('\u001b[35mtrue\u001b[0m');
221+
expect(result).toContain('\u001b[35mnull\u001b[0m');
222+
});
223+
224+
test('should handle JSON with escaped quotes', () => {
225+
const jsonLine = '{"message": "He said \\"hello\\""}';
226+
const result = colorizer.colorize(jsonLine);
227+
228+
// Braces should be yellow
229+
expect(result).toContain('\u001b[33m{\u001b[0m');
230+
expect(result).toContain('\u001b[33m}\u001b[0m');
231+
232+
// Should handle escaped quotes properly - strings not colorized
233+
expect(result).toContain('"message"');
234+
expect(result).toContain('"He said \\"hello\\""');
235+
});
236+
237+
test('should work with custom color scheme', () => {
238+
const customScheme: JsonColorScheme = {
239+
braceColor: '\u001b[31m', // red
240+
numberColor: '\u001b[33m', // yellow
241+
booleanColor: '\u001b[34m', // blue
242+
nullColor: '\u001b[35m', // magenta
243+
reset: '\u001b[0m',
244+
};
245+
246+
const customColorizer = new JsonColorizer(customScheme);
247+
const jsonLine = '{"key": "value", "num": 42, "flag": true, "empty": null}';
248+
const result = customColorizer.colorize(jsonLine);
249+
250+
// Should use custom colors
251+
expect(result).toContain('\u001b[31m{\u001b[0m'); // red braces
252+
expect(result).toContain('\u001b[31m}\u001b[0m'); // red braces
253+
expect(result).toContain('\u001b[33m42\u001b[0m'); // yellow number
254+
expect(result).toContain('\u001b[34mtrue\u001b[0m'); // blue boolean
255+
expect(result).toContain('\u001b[35mnull\u001b[0m'); // magenta null
256+
257+
// Strings should not be colorized
258+
expect(result).toContain('"key"');
259+
expect(result).toContain('"value"');
260+
});
261+
262+
test('should handle zero as a number', () => {
263+
const jsonLine = '{"value": 0}';
264+
const result = colorizer.colorize(jsonLine);
265+
266+
// Braces should be yellow
267+
expect(result).toContain('\u001b[33m{\u001b[0m');
268+
expect(result).toContain('\u001b[33m}\u001b[0m');
269+
270+
// Zero should be green
271+
expect(result).toContain('\u001b[32m0\u001b[0m');
272+
});
273+
274+
test('should handle decimal numbers starting with dot', () => {
275+
const jsonLine = '{"value": 0.5}';
276+
const result = colorizer.colorize(jsonLine);
277+
278+
// Braces should be yellow
279+
expect(result).toContain('\u001b[33m{\u001b[0m');
280+
expect(result).toContain('\u001b[33m}\u001b[0m');
281+
282+
// Decimal should be green
283+
expect(result).toContain('\u001b[32m0.5\u001b[0m');
284+
});
285+
});

0 commit comments

Comments
 (0)