Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 41 additions & 7 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,32 @@ usage(const char *prog)
" Set CLM_API_KEY in the environment to send a bearer token.\n");
}

static int use_color = 1;

static const char *
esc_warn(void)
{
return use_color ? "\033[33m" : "\033[1m";
}

static const char *
esc_err(void)
{
return use_color ? "\033[31m" : "\033[1;7m";
}

static const char *
esc_dim(void)
{
return "\033[2m";
}

static const char *
esc_reset(void)
{
return "\033[0m";
}

struct cli_state {
uv_loop_t *loop;
struct clm_host *host;
Expand Down Expand Up @@ -114,7 +140,7 @@ static void
cb_reasoning(const char *text, void *user)
{
(void)user;
printf("\033[2m%s\033[0m", text); /* dim "thinking" channel */
printf("%s%s%s", esc_dim(), text, esc_reset());
fflush(stdout);
}

Expand All @@ -123,20 +149,23 @@ cb_finish_reason(enum clm_finish_reason reason, void *user)
{
(void)user;
if (reason == CLM_FINISH_LENGTH)
printf("\n\033[33m[truncated: hit token limit]\033[0m\n");
printf("\n%s[truncated: hit token limit]%s\n", esc_warn(),
esc_reset());
else if (reason == CLM_FINISH_CONTENT_FILTER)
printf("\n\033[31m[stopped: content filter]\033[0m\n");
printf("\n%s[stopped: content filter]%s\n", esc_err(),
esc_reset());
fflush(stdout);
}

static void
cb_usage(const struct clm_usage *usage, void *user)
{
(void)user;
printf("\033[2m[%d+%d tok", usage->prompt_tokens, usage->completion_tokens);
printf("%s[%d+%d tok", esc_dim(), usage->prompt_tokens,
usage->completion_tokens);
if (usage->tokens_per_sec > 0)
printf(", %.1f tok/s", usage->tokens_per_sec);
printf("]\033[0m\n");
printf("]%s\n", esc_reset());
fflush(stdout);
}

Expand All @@ -157,10 +186,12 @@ cb_tool_result(const char *name, const char *content, enum clm_tool_outcome outc
printf("[result: %s]\n%s\n", name, content ? content : "");
break;
case CLM_TOOL_FAILED:
printf("\033[31m[x %s]\033[0m %s\n", name, content ? content : "");
printf("%s[x %s]%s %s\n", esc_err(), name, esc_reset(),
content ? content : "");
break;
case CLM_TOOL_TIMEDOUT:
printf("\033[33m[timeout %s]\033[0m %s\n", name, content ? content : "");
printf("%s[timeout %s]%s %s\n", esc_warn(), name, esc_reset(),
content ? content : "");
break;
}
fflush(stdout);
Expand Down Expand Up @@ -463,6 +494,9 @@ main(int argc, char *argv[])
int opt, r;
struct clm_lua_cfg *lcfg = NULL;

if (getenv("NO_COLOR") != NULL)
use_color = 0;

if (argc >= 2 && strcmp(argv[1], "setup") == 0)
return run_setup();

Expand Down
108 changes: 72 additions & 36 deletions src/tui.c
Original file line number Diff line number Diff line change
Expand Up @@ -789,31 +789,55 @@ static const struct clm_callbacks tui_callbacks = {
/* ---- rendering (all drawing happens here, from the repaint timer) ---- */

static int
seg_attr(enum ui_style style)
{
seg_attr(struct ui *u, enum ui_style style)
{
if (u->color) {
switch (style) {
case ST_USER:
return COLOR_PAIR(1);
case ST_LABEL:
return COLOR_PAIR(8);
case ST_PERM:
return COLOR_PAIR(9);
case ST_REASON:
return COLOR_PAIR(6);
case ST_TOOL:
return COLOR_PAIR(2);
case ST_ERROR:
return COLOR_PAIR(3);
case ST_TIMEOUT:
return COLOR_PAIR(4);
default:
break;
}
} else {
switch (style) {
case ST_USER:
return A_BOLD;
case ST_LABEL:
return A_BOLD;
case ST_PERM:
return A_BOLD | A_UNDERLINE;
case ST_REASON:
return A_DIM;
case ST_TOOL:
return A_UNDERLINE;
case ST_ERROR:
return A_BOLD | A_REVERSE;
case ST_TIMEOUT:
return A_REVERSE;
default:
break;
}
}
switch (style) {
case ST_USER:
return COLOR_PAIR(1);
case ST_ASSIST:
return A_NORMAL;
case ST_LABEL:
return COLOR_PAIR(8);
case ST_PERM:
return COLOR_PAIR(9);
case ST_REASON:
return COLOR_PAIR(6);
case ST_TOOL:
return COLOR_PAIR(2);
case ST_TOOL_OUT:
return A_DIM;
case ST_ERROR:
return COLOR_PAIR(3);
case ST_TIMEOUT:
return COLOR_PAIR(4);
case ST_META:
case ST_BATCH:
return A_DIM;
case ST_NORMAL:
default:
break;
}
return A_NORMAL;
Expand Down Expand Up @@ -880,8 +904,13 @@ draw_status(struct ui *u)
char sp = u->busy ? spin[u->spinner & 3] : ' ';

werase(u->stat);
wbkgd(u->stat, COLOR_PAIR(5));
wattron(u->stat, COLOR_PAIR(5));
if (u->color) {
wbkgd(u->stat, COLOR_PAIR(5));
wattron(u->stat, COLOR_PAIR(5));
} else {
wbkgd(u->stat, A_REVERSE);
wattron(u->stat, A_REVERSE);
}

/* Live status sits on the left, next to the model, so the eye doesn't
* have to travel across the width of the terminal to read it. */
Expand Down Expand Up @@ -975,7 +1004,7 @@ draw_status(struct ui *u)
mvwprintw(u->stat, 0, w - hw, "%s", hints);
}

wattroff(u->stat, COLOR_PAIR(5));
wattroff(u->stat, u->color ? COLOR_PAIR(5) : A_REVERSE);
wnoutrefresh(u->stat);
}

Expand Down Expand Up @@ -1200,8 +1229,13 @@ md_emit(const struct md_run *run, void *userdata)
unsigned attr = s->base_attr | md_style_to_attr(run->style);

/* Code carries its own colour, overriding the stream's default pair. */
if (run->style & MD_ST_CODE)
attr = (attr & ~(unsigned)A_COLOR) | (unsigned)COLOR_PAIR(7);
if (run->style & MD_ST_CODE) {
if (s->u->color)
attr = (attr & ~(unsigned)A_COLOR) |
(unsigned)COLOR_PAIR(7);
else
attr = (attr & ~(unsigned)A_COLOR) | (unsigned)A_BOLD;
}

if (s->no_bold)
attr &= ~(unsigned)A_BOLD;
Expand Down Expand Up @@ -1262,13 +1296,13 @@ push_tool_output(struct ui *u, const char *text, bool is_latest)
char hint[64];

if (u->expand_output || (is_latest && total <= PREVIEW)) {
rseg_push(u, seg_attr(ST_TOOL_OUT), text, len);
rseg_push(u, seg_attr(u, ST_TOOL_OUT), text, len);
return;
}
if (!is_latest) {
snprintf(hint, sizeof(hint), " ... (%d lines, ^O to expand)\n",
total);
rseg_push(u, seg_attr(ST_TOOL_OUT), hint, strlen(hint));
rseg_push(u, seg_attr(u, ST_TOOL_OUT), hint, strlen(hint));
return;
}
cut = 0;
Expand All @@ -1278,10 +1312,10 @@ push_tool_output(struct ui *u, const char *text, bool is_latest)
seen++;
cut++;
}
rseg_push(u, seg_attr(ST_TOOL_OUT), text, cut);
rseg_push(u, seg_attr(u, ST_TOOL_OUT), text, cut);
snprintf(hint, sizeof(hint), " ... (+%d lines, ^O to expand)\n",
total - PREVIEW);
rseg_push(u, seg_attr(ST_TOOL_OUT), hint, strlen(hint));
rseg_push(u, seg_attr(u, ST_TOOL_OUT), hint, strlen(hint));
}

/* Render one combined "-- ran N commands, read N files, ..." line summarizing
Expand All @@ -1294,7 +1328,7 @@ push_collapsed_summary(struct ui *u, const int cnt[4])
char line[160];

if (format_tool_tally(line, sizeof(line), cnt, " (^O to expand)\n"))
rseg_push(u, seg_attr(ST_META), line, strlen(line));
rseg_push(u, seg_attr(u, ST_META), line, strlen(line));
}

/* Resolve the source span list into the rendered run cache for width w. */
Expand Down Expand Up @@ -1378,11 +1412,12 @@ rebuild_render(struct ui *u, int w)
if (g->style == ST_ASSIST && w >= 4)
render_markdown(u, g->text, w, A_NORMAL, false);
else if (g->style == ST_REASON && w >= 4)
render_markdown(u, g->text, w, COLOR_PAIR(6), true);
render_markdown(u, g->text, w,
u->color ? COLOR_PAIR(6) : A_DIM, true);
else if (g->style == ST_TOOL_OUT)
push_tool_output(u, g->text, i == last_tool_out);
else
rseg_push(u, seg_attr(g->style), g->text,
rseg_push(u, seg_attr(u, g->style), g->text,
strlen(g->text));
}
if (aggregating)
Expand Down Expand Up @@ -1577,11 +1612,11 @@ draw_transcript(struct ui *u)
for (size_t i = 0; i < u->steering_nqueue; i++) {
char line[600];

rseg_push(u, seg_attr(ST_USER), "\nyou> ", 6);
rseg_push(u, seg_attr(ST_USER), u->steering_queue[i],
rseg_push(u, seg_attr(u, ST_USER), "\nyou> ", 6);
rseg_push(u, seg_attr(u, ST_USER), u->steering_queue[i],
strlen(u->steering_queue[i]));
(void)snprintf(line, sizeof(line), " (queued)\n");
rseg_push(u, seg_attr(ST_META), line, strlen(line));
rseg_push(u, seg_attr(u, ST_META), line, strlen(line));
}

total = wrap_walk(u, w, h, 0, false, base);
Expand Down Expand Up @@ -2969,12 +3004,13 @@ on_winch(uv_signal_t *s, int signum)
* only contributes attributes, never pair colours. Keep it this way.
*/
static void
init_colors(void)
init_colors(struct ui *u)
{
if (!has_colors())
if (getenv("NO_COLOR") != NULL || !has_colors())
return;
start_color();
use_default_colors();
u->color = true;
/*
* Colours are chosen to read well on a stock Solarized 16-colour palette
* where A_BOLD promotes 0-7 accents to the grey 8-15 base tones. So we
Expand Down Expand Up @@ -3145,7 +3181,7 @@ tui_run(const struct clm_cfg *cfg, const char *plugin_dir,
nonl();
set_escdelay(25); /* make a lone Escape (cancel) responsive */
mousemask(BUTTON4_PRESSED | BUTTON5_PRESSED, NULL);
init_colors();
init_colors(u);
make_windows(u);

/*
Expand Down
1 change: 1 addition & 0 deletions src/tui_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ struct ui {
size_t hist_pos;
char hist_saved[1024];

bool color; /* print text in color, otherwise just attributes */
bool dirty; /* repaint requested */
bool full_redraw; /* force an artifact-free repaint (resize / ^L) */
bool quit;
Expand Down