Skip to content

Commit ffb79fd

Browse files
authored
feat: proper terminal rendering - even renders btop! (#27)
1 parent 490d54d commit ffb79fd

File tree

5 files changed

+378
-187
lines changed

5 files changed

+378
-187
lines changed

lib/colors.ts

Lines changed: 93 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,9 @@ export function mixColors(
144144
c2: number,
145145
alpha?: number | null
146146
): number {
147-
// if (c1 === 0x1ff) return c1;
148-
// if (c2 === 0x1ff) return c1;
149-
if (c1 === 0x1ff) c1 = 0;
150-
if (c2 === 0x1ff) c2 = 0;
147+
// Handle new default color marker
148+
if (c1 === 0xffffff) c1 = 0;
149+
if (c2 === 0xffffff) c2 = 0;
151150
if (alpha === null) alpha = 0.5;
152151

153152
const c1Color = vcolors[c1];
@@ -174,81 +173,90 @@ export function blend(
174173
): number {
175174
var name, i, c, nc;
176175

177-
var bg = attr & 0x1ff;
176+
// Extract colors from new format
177+
var bg = attr & 0xffffff;
178+
var fg = Math.floor(attr / 0x1000000) & 0xffffff;
179+
var flags = Math.floor(attr / 0x1000000000000);
180+
178181
if (attr2 !== null) {
179-
var bg2 = attr2 & 0x1ff;
180-
if (bg === 0x1ff) bg = 0;
181-
if (bg2 === 0x1ff) bg2 = 0;
182+
var bg2 = attr2 & 0xffffff;
183+
var fg2 = Math.floor(attr2 / 0x1000000) & 0xffffff;
184+
185+
// Handle default colors
186+
if (bg === 0xffffff) bg = 0;
187+
if (bg2 === 0xffffff) bg2 = 0;
188+
if (fg === 0xffffff) fg = 7;
189+
if (fg2 === 0xffffff) fg2 = 7;
190+
191+
// For RGB colors (> 255), convert to palette for blending
192+
if (bg > 255) bg = match([(bg >> 16) & 0xff, (bg >> 8) & 0xff, bg & 0xff]);
193+
if (bg2 > 255)
194+
bg2 = match([(bg2 >> 16) & 0xff, (bg2 >> 8) & 0xff, bg2 & 0xff]);
195+
if (fg > 255) fg = match([(fg >> 16) & 0xff, (fg >> 8) & 0xff, fg & 0xff]);
196+
if (fg2 > 255)
197+
fg2 = match([(fg2 >> 16) & 0xff, (fg2 >> 8) & 0xff, fg2 & 0xff]);
198+
182199
bg = mixColors(bg, bg2, alpha);
200+
fg = mixColors(fg, fg2, alpha);
183201
} else {
184-
if ((blend as any)._cache[bg] !== null) {
185-
bg = (blend as any)._cache[bg];
186-
// } else if (bg < 8) {
187-
// bg += 8;
188-
} else if (bg >= 8 && bg <= 15) {
189-
bg -= 8;
190-
} else {
191-
name = ncolors[bg];
192-
if (name) {
193-
for (i = 0; i < ncolors.length; i++) {
194-
if (name === ncolors[i] && i !== bg) {
195-
c = vcolors[bg];
196-
nc = vcolors[i];
197-
if (nc[0] + nc[1] + nc[2] < c[0] + c[1] + c[2]) {
198-
(blend as any)._cache[bg] = i;
199-
bg = i;
200-
break;
202+
// Handle single color processing
203+
if (bg === 0xffffff) bg = 0;
204+
if (fg === 0xffffff) fg = 7;
205+
206+
// For RGB colors, convert to palette
207+
if (bg > 255) bg = match([(bg >> 16) & 0xff, (bg >> 8) & 0xff, bg & 0xff]);
208+
if (fg > 255) fg = match([(fg >> 16) & 0xff, (fg >> 8) & 0xff, fg & 0xff]);
209+
210+
// Apply cache and brightness adjustments for palette colors
211+
if (bg <= 255) {
212+
if ((blend as any)._cache[bg] !== null) {
213+
bg = (blend as any)._cache[bg];
214+
} else if (bg >= 8 && bg <= 15) {
215+
bg -= 8;
216+
} else {
217+
name = ncolors[bg];
218+
if (name) {
219+
for (i = 0; i < ncolors.length; i++) {
220+
if (name === ncolors[i] && i !== bg) {
221+
c = vcolors[bg];
222+
nc = vcolors[i];
223+
if (nc[0] + nc[1] + nc[2] < c[0] + c[1] + c[2]) {
224+
(blend as any)._cache[bg] = i;
225+
bg = i;
226+
break;
227+
}
201228
}
202229
}
203230
}
204231
}
205232
}
206-
}
207233

208-
attr &= ~0x1ff;
209-
attr |= bg;
210-
211-
var fg = (attr >> 9) & 0x1ff;
212-
if (attr2 !== null) {
213-
var fg2 = (attr2 >> 9) & 0x1ff;
214-
// 0, 7, 188, 231, 251
215-
if (fg === 0x1ff) {
216-
// XXX workaround
217-
fg = 248;
218-
} else {
219-
if (fg === 0x1ff) fg = 7;
220-
if (fg2 === 0x1ff) fg2 = 7;
221-
fg = mixColors(fg, fg2, alpha);
222-
}
223-
} else {
224-
if ((blend as any)._cache[fg] !== null) {
225-
fg = (blend as any)._cache[fg];
226-
// } else if (fg < 8) {
227-
// fg += 8;
228-
} else if (fg >= 8 && fg <= 15) {
229-
fg -= 8;
230-
} else {
231-
name = ncolors[fg];
232-
if (name) {
233-
for (i = 0; i < ncolors.length; i++) {
234-
if (name === ncolors[i] && i !== fg) {
235-
c = vcolors[fg];
236-
nc = vcolors[i];
237-
if (nc[0] + nc[1] + nc[2] < c[0] + c[1] + c[2]) {
238-
(blend as any)._cache[fg] = i;
239-
fg = i;
240-
break;
234+
if (fg <= 255) {
235+
if ((blend as any)._cache[fg] !== null) {
236+
fg = (blend as any)._cache[fg];
237+
} else if (fg >= 8 && fg <= 15) {
238+
fg -= 8;
239+
} else {
240+
name = ncolors[fg];
241+
if (name) {
242+
for (i = 0; i < ncolors.length; i++) {
243+
if (name === ncolors[i] && i !== fg) {
244+
c = vcolors[fg];
245+
nc = vcolors[i];
246+
if (nc[0] + nc[1] + nc[2] < c[0] + c[1] + c[2]) {
247+
(blend as any)._cache[fg] = i;
248+
fg = i;
249+
break;
250+
}
241251
}
242252
}
243253
}
244254
}
245255
}
246256
}
247257

248-
attr &= ~(0x1ff << 9);
249-
attr |= fg << 9;
250-
251-
return attr;
258+
// Pack back into new format
259+
return bg + fg * 0x1000000 + flags * 0x1000000000000;
252260
}
253261

254262
blend._cache = {};
@@ -389,6 +397,25 @@ export function convert(color: ColorInput): number {
389397
let name = color.replace(/[\- ]/g, '');
390398
if (colorNames[name] !== null) {
391399
result = colorNames[name];
400+
} else if (color.startsWith('#')) {
401+
// Handle hex colors like #ff0000
402+
const hex = color.slice(1);
403+
if (hex.length === 6) {
404+
const r = parseInt(hex.slice(0, 2), 16);
405+
const g = parseInt(hex.slice(2, 4), 16);
406+
const b = parseInt(hex.slice(4, 6), 16);
407+
result = (r << 16) | (g << 8) | b; // Return full RGB value
408+
} else {
409+
result = match(color);
410+
}
411+
} else {
412+
result = match(color);
413+
}
414+
} else if (Array.isArray(color) && color.length === 3) {
415+
// Handle RGB arrays like [255, 0, 0]
416+
const [r, g, b] = color;
417+
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
418+
result = (r << 16) | (g << 8) | b; // Return full RGB value
392419
} else {
393420
result = match(color);
394421
}
@@ -397,7 +424,9 @@ export function convert(color: ColorInput): number {
397424
} else {
398425
result = -1;
399426
}
400-
return result !== -1 ? result : 0x1ff;
427+
428+
// Return default color marker for undefined/null/-1, otherwise return the color
429+
return result !== -1 ? result : 0xffffff;
401430
}
402431

403432
// Map higher colors to the first 8 colors.

lib/widgets/element.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -376,17 +376,20 @@ Element.prototype.sattr = function (
376376
if (typeof fg === 'function') fg = fg(this);
377377
if (typeof bg === 'function') bg = bg(this);
378378

379-
// return (this.uid << 24)
380-
// | ((this.dockBorders ? 32 : 0) << 18)
381-
return (
382-
((invisible ? 16 : 0) << 18) |
383-
((inverse ? 8 : 0) << 18) |
384-
((blink ? 4 : 0) << 18) |
385-
((underline ? 2 : 0) << 18) |
386-
((bold ? 1 : 0) << 18) |
387-
(colors.convert(fg) << 9) |
388-
colors.convert(bg)
389-
);
379+
// Updated for 24-bit color support using new format:
380+
// bg + (fg * 2^24) + (flags * 2^48)
381+
var flags = 0;
382+
if (invisible) flags |= 16;
383+
if (inverse) flags |= 8;
384+
if (blink) flags |= 4;
385+
if (underline) flags |= 2;
386+
if (bold) flags |= 1;
387+
388+
var fgColor = colors.convert(fg);
389+
var bgColor = colors.convert(bg);
390+
391+
// Pack into new format using Math operations for large numbers
392+
return bgColor + fgColor * 0x1000000 + flags * 0x1000000000000;
390393
};
391394

392395
Element.prototype.onScreenEvent = function (
@@ -2205,7 +2208,12 @@ Element.prototype.render = function () {
22052208
this.parent.items[this.parent.selected] === this &&
22062209
this.parent.options.invertSelected !== false
22072210
) {
2208-
attr = (attr & ~(0x1ff << 9)) | (dattr & (0x1ff << 9));
2211+
// Copy foreground color from dattr in new format
2212+
var dFg = Math.floor(dattr / 0x1000000) & 0xffffff;
2213+
attr =
2214+
(attr & 0xffffff) +
2215+
dFg * 0x1000000 +
2216+
Math.floor(attr / 0x1000000000000) * 0x1000000000000;
22092217
}
22102218
ch = content[ci] || bch;
22112219
ci++;

0 commit comments

Comments
 (0)