mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 15:02:32 +00:00
Release v0.7.21
This commit is contained in:
parent
5112fc6ba9
commit
1b28780155
16 changed files with 346 additions and 341 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { Chalk } from "chalk";
|
||||
import { marked, type Token } from "marked";
|
||||
import type { Component } from "../tui.js";
|
||||
import { visibleWidth, wrapTextWithAnsi } from "../utils.js";
|
||||
import { applyBackgroundToLine, visibleWidth, wrapTextWithAnsi } from "../utils.js";
|
||||
|
||||
// Use a chalk instance with color level 3 for consistent ANSI output
|
||||
const colorChalk = new Chalk({ level: 3 });
|
||||
|
|
@ -86,51 +86,41 @@ export class Markdown implements Component {
|
|||
renderedLines.push(...tokenLines);
|
||||
}
|
||||
|
||||
// Wrap lines to fit content width
|
||||
// Wrap lines (NO padding, NO background yet)
|
||||
const wrappedLines: string[] = [];
|
||||
for (const line of renderedLines) {
|
||||
wrappedLines.push(...wrapTextWithAnsi(line, contentWidth));
|
||||
}
|
||||
|
||||
// Add padding and apply background color if specified
|
||||
const leftPad = " ".repeat(this.paddingX);
|
||||
const paddedLines: string[] = [];
|
||||
// Add margins and background to each wrapped line
|
||||
const leftMargin = " ".repeat(this.paddingX);
|
||||
const rightMargin = " ".repeat(this.paddingX);
|
||||
const bgRgb = this.defaultTextStyle?.bgColor ? this.parseBgColor() : undefined;
|
||||
const contentLines: string[] = [];
|
||||
|
||||
for (const line of wrappedLines) {
|
||||
// Calculate visible length
|
||||
const visibleLength = visibleWidth(line);
|
||||
// Right padding to fill to width (accounting for left padding and content)
|
||||
const rightPadLength = Math.max(0, width - this.paddingX - visibleLength);
|
||||
const rightPad = " ".repeat(rightPadLength);
|
||||
const lineWithMargins = leftMargin + line + rightMargin;
|
||||
|
||||
// Add left padding, content, and right padding
|
||||
let paddedLine = leftPad + line + rightPad;
|
||||
|
||||
// Apply background color to entire line if specified
|
||||
if (this.defaultTextStyle?.bgColor) {
|
||||
paddedLine = this.applyBgColor(paddedLine);
|
||||
if (bgRgb) {
|
||||
contentLines.push(applyBackgroundToLine(lineWithMargins, width, bgRgb));
|
||||
} else {
|
||||
// No background - just pad to width
|
||||
const visibleLen = visibleWidth(lineWithMargins);
|
||||
const paddingNeeded = Math.max(0, width - visibleLen);
|
||||
contentLines.push(lineWithMargins + " ".repeat(paddingNeeded));
|
||||
}
|
||||
|
||||
paddedLines.push(paddedLine);
|
||||
}
|
||||
|
||||
// Add top padding (empty lines)
|
||||
// Add top/bottom padding (empty lines)
|
||||
const emptyLine = " ".repeat(width);
|
||||
const topPadding: string[] = [];
|
||||
const emptyLines: string[] = [];
|
||||
for (let i = 0; i < this.paddingY; i++) {
|
||||
const paddedEmptyLine = this.defaultTextStyle?.bgColor ? this.applyBgColor(emptyLine) : emptyLine;
|
||||
topPadding.push(paddedEmptyLine);
|
||||
}
|
||||
|
||||
// Add bottom padding (empty lines)
|
||||
const bottomPadding: string[] = [];
|
||||
for (let i = 0; i < this.paddingY; i++) {
|
||||
const paddedEmptyLine = this.defaultTextStyle?.bgColor ? this.applyBgColor(emptyLine) : emptyLine;
|
||||
bottomPadding.push(paddedEmptyLine);
|
||||
const line = bgRgb ? applyBackgroundToLine(emptyLine, width, bgRgb) : emptyLine;
|
||||
emptyLines.push(line);
|
||||
}
|
||||
|
||||
// Combine top padding, content, and bottom padding
|
||||
const result = [...topPadding, ...paddedLines, ...bottomPadding];
|
||||
const result = [...emptyLines, ...contentLines, ...emptyLines];
|
||||
|
||||
// Update cache
|
||||
this.cachedText = this.text;
|
||||
|
|
@ -141,29 +131,43 @@ export class Markdown implements Component {
|
|||
}
|
||||
|
||||
/**
|
||||
* Apply only background color from default style.
|
||||
* Used for padding lines that don't have text content.
|
||||
* Parse background color from defaultTextStyle to RGB values
|
||||
*/
|
||||
private applyBgColor(text: string): string {
|
||||
private parseBgColor(): { r: number; g: number; b: number } | undefined {
|
||||
if (!this.defaultTextStyle?.bgColor) {
|
||||
return text;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (this.defaultTextStyle.bgColor.startsWith("#")) {
|
||||
// Hex color
|
||||
const hex = this.defaultTextStyle.bgColor.substring(1);
|
||||
const r = Number.parseInt(hex.substring(0, 2), 16);
|
||||
const g = Number.parseInt(hex.substring(2, 4), 16);
|
||||
const b = Number.parseInt(hex.substring(4, 6), 16);
|
||||
return colorChalk.bgRgb(r, g, b)(text);
|
||||
return {
|
||||
r: Number.parseInt(hex.substring(0, 2), 16),
|
||||
g: Number.parseInt(hex.substring(2, 4), 16),
|
||||
b: Number.parseInt(hex.substring(4, 6), 16),
|
||||
};
|
||||
}
|
||||
// Named background color (bgRed, bgBlue, etc.)
|
||||
return (colorChalk as any)[this.defaultTextStyle.bgColor](text);
|
||||
|
||||
// Named colors - map to RGB (common terminal colors)
|
||||
const colorMap: Record<string, { r: number; g: number; b: number }> = {
|
||||
bgBlack: { r: 0, g: 0, b: 0 },
|
||||
bgRed: { r: 255, g: 0, b: 0 },
|
||||
bgGreen: { r: 0, g: 255, b: 0 },
|
||||
bgYellow: { r: 255, g: 255, b: 0 },
|
||||
bgBlue: { r: 0, g: 0, b: 255 },
|
||||
bgMagenta: { r: 255, g: 0, b: 255 },
|
||||
bgCyan: { r: 0, g: 255, b: 255 },
|
||||
bgWhite: { r: 255, g: 255, b: 255 },
|
||||
};
|
||||
|
||||
return colorMap[this.defaultTextStyle.bgColor];
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply default text style to a string.
|
||||
* This is the base styling applied to all text content.
|
||||
* NOTE: Background color is NOT applied here - it's applied at the padding stage
|
||||
* to ensure it extends to the full line width.
|
||||
*/
|
||||
private applyDefaultStyle(text: string): string {
|
||||
if (!this.defaultTextStyle) {
|
||||
|
|
@ -172,7 +176,7 @@ export class Markdown implements Component {
|
|||
|
||||
let styled = text;
|
||||
|
||||
// Apply color
|
||||
// Apply foreground color (NOT background - that's applied at padding stage)
|
||||
if (this.defaultTextStyle.color) {
|
||||
if (this.defaultTextStyle.color.startsWith("#")) {
|
||||
// Hex color
|
||||
|
|
@ -187,21 +191,6 @@ export class Markdown implements Component {
|
|||
}
|
||||
}
|
||||
|
||||
// Apply background color
|
||||
if (this.defaultTextStyle.bgColor) {
|
||||
if (this.defaultTextStyle.bgColor.startsWith("#")) {
|
||||
// Hex color
|
||||
const hex = this.defaultTextStyle.bgColor.substring(1);
|
||||
const r = Number.parseInt(hex.substring(0, 2), 16);
|
||||
const g = Number.parseInt(hex.substring(2, 4), 16);
|
||||
const b = Number.parseInt(hex.substring(4, 6), 16);
|
||||
styled = colorChalk.bgRgb(r, g, b)(styled);
|
||||
} else {
|
||||
// Named background color (bgRed, bgBlue, etc.)
|
||||
styled = (colorChalk as any)[this.defaultTextStyle.bgColor](styled);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply text decorations
|
||||
if (this.defaultTextStyle.bold) {
|
||||
styled = colorChalk.bold(styled);
|
||||
|
|
@ -338,12 +327,8 @@ export class Markdown implements Component {
|
|||
}
|
||||
|
||||
case "codespan":
|
||||
// Apply code styling, then reapply default style after
|
||||
result +=
|
||||
colorChalk.gray("`") +
|
||||
colorChalk.cyan(token.text) +
|
||||
colorChalk.gray("`") +
|
||||
this.applyDefaultStyle("");
|
||||
// Apply code styling without backticks
|
||||
result += colorChalk.cyan(token.text) + this.applyDefaultStyle("");
|
||||
break;
|
||||
|
||||
case "link": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import chalk from "chalk";
|
||||
import { Chalk } from "chalk";
|
||||
import type { Component } from "../tui.js";
|
||||
import { visibleWidth, wrapTextWithAnsi } from "../utils.js";
|
||||
import { applyBackgroundToLine, visibleWidth, wrapTextWithAnsi } from "../utils.js";
|
||||
|
||||
const colorChalk = new Chalk({ level: 3 });
|
||||
|
||||
/**
|
||||
* Text component - displays multi-line text with word wrapping
|
||||
|
|
@ -30,7 +32,6 @@ export class Text implements Component {
|
|||
|
||||
setText(text: string): void {
|
||||
this.text = text;
|
||||
// Invalidate cache when text changes
|
||||
this.cachedText = undefined;
|
||||
this.cachedWidth = undefined;
|
||||
this.cachedLines = undefined;
|
||||
|
|
@ -38,7 +39,6 @@ export class Text implements Component {
|
|||
|
||||
setCustomBgRgb(customBgRgb?: { r: number; g: number; b: number }): void {
|
||||
this.customBgRgb = customBgRgb;
|
||||
// Invalidate cache when color changes
|
||||
this.cachedText = undefined;
|
||||
this.cachedWidth = undefined;
|
||||
this.cachedLines = undefined;
|
||||
|
|
@ -50,68 +50,53 @@ export class Text implements Component {
|
|||
return this.cachedLines;
|
||||
}
|
||||
|
||||
// Calculate available width for content (subtract horizontal padding)
|
||||
const contentWidth = Math.max(1, width - this.paddingX * 2);
|
||||
|
||||
// Don't render anything if there's no actual text
|
||||
if (!this.text || this.text.trim() === "") {
|
||||
const result: string[] = [];
|
||||
// Update cache
|
||||
this.cachedText = this.text;
|
||||
this.cachedWidth = width;
|
||||
this.cachedLines = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Replace tabs with 3 spaces for consistent rendering
|
||||
// Replace tabs with 3 spaces
|
||||
const normalizedText = this.text.replace(/\t/g, " ");
|
||||
|
||||
// Use shared ANSI-aware word wrapping
|
||||
const lines = wrapTextWithAnsi(normalizedText, contentWidth);
|
||||
// Calculate content width (subtract left/right margins)
|
||||
const contentWidth = Math.max(1, width - this.paddingX * 2);
|
||||
|
||||
// Add padding to each line
|
||||
const leftPad = " ".repeat(this.paddingX);
|
||||
const paddedLines: string[] = [];
|
||||
// Wrap text (this preserves ANSI codes but does NOT pad)
|
||||
const wrappedLines = wrapTextWithAnsi(normalizedText, contentWidth);
|
||||
|
||||
for (const line of lines) {
|
||||
// Calculate visible length (strip ANSI codes)
|
||||
const visibleLength = visibleWidth(line);
|
||||
// Right padding to fill to width (accounting for left padding and content)
|
||||
const rightPadLength = Math.max(0, width - this.paddingX - visibleLength);
|
||||
const rightPad = " ".repeat(rightPadLength);
|
||||
let paddedLine = leftPad + line + rightPad;
|
||||
// Add margins and background to each line
|
||||
const leftMargin = " ".repeat(this.paddingX);
|
||||
const rightMargin = " ".repeat(this.paddingX);
|
||||
const contentLines: string[] = [];
|
||||
|
||||
// Apply background color if specified
|
||||
for (const line of wrappedLines) {
|
||||
// Add margins
|
||||
const lineWithMargins = leftMargin + line + rightMargin;
|
||||
|
||||
// Apply background if specified (this also pads to full width)
|
||||
if (this.customBgRgb) {
|
||||
paddedLine = chalk.bgRgb(this.customBgRgb.r, this.customBgRgb.g, this.customBgRgb.b)(paddedLine);
|
||||
contentLines.push(applyBackgroundToLine(lineWithMargins, width, this.customBgRgb));
|
||||
} else {
|
||||
// No background - just pad to width with spaces
|
||||
const visibleLen = visibleWidth(lineWithMargins);
|
||||
const paddingNeeded = Math.max(0, width - visibleLen);
|
||||
contentLines.push(lineWithMargins + " ".repeat(paddingNeeded));
|
||||
}
|
||||
|
||||
paddedLines.push(paddedLine);
|
||||
}
|
||||
|
||||
// Add top padding (empty lines)
|
||||
// Add top/bottom padding (empty lines)
|
||||
const emptyLine = " ".repeat(width);
|
||||
const topPadding: string[] = [];
|
||||
const emptyLines: string[] = [];
|
||||
for (let i = 0; i < this.paddingY; i++) {
|
||||
let emptyPaddedLine = emptyLine;
|
||||
if (this.customBgRgb) {
|
||||
emptyPaddedLine = chalk.bgRgb(this.customBgRgb.r, this.customBgRgb.g, this.customBgRgb.b)(emptyPaddedLine);
|
||||
}
|
||||
topPadding.push(emptyPaddedLine);
|
||||
const line = this.customBgRgb ? applyBackgroundToLine(emptyLine, width, this.customBgRgb) : emptyLine;
|
||||
emptyLines.push(line);
|
||||
}
|
||||
|
||||
// Add bottom padding (empty lines)
|
||||
const bottomPadding: string[] = [];
|
||||
for (let i = 0; i < this.paddingY; i++) {
|
||||
let emptyPaddedLine = emptyLine;
|
||||
if (this.customBgRgb) {
|
||||
emptyPaddedLine = chalk.bgRgb(this.customBgRgb.r, this.customBgRgb.g, this.customBgRgb.b)(emptyPaddedLine);
|
||||
}
|
||||
bottomPadding.push(emptyPaddedLine);
|
||||
}
|
||||
|
||||
// Combine top padding, content, and bottom padding
|
||||
const result = [...topPadding, ...paddedLines, ...bottomPadding];
|
||||
const result = [...emptyLines, ...contentLines, ...emptyLines];
|
||||
|
||||
// Update cache
|
||||
this.cachedText = this.text;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue