feat: rebrand as Hermit — TUI palette, dual glyphs, binary 0.10.0
Ship hermit binary (cst-agent symlink), ~/.config/hermit with legacy fallback, Hermit color theme and mini crab frames, plus SVG→PUA font build pipeline. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "cst_hermit_glyph.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
const char *hermit_getenv(const char *suffix) {
|
||||
char hk[96], ck[96];
|
||||
if (!suffix || !suffix[0])
|
||||
return "";
|
||||
snprintf(hk, sizeof(hk), "HERMIT_%s", suffix);
|
||||
const char *e = getenv(hk);
|
||||
if (e && e[0])
|
||||
return e;
|
||||
snprintf(ck, sizeof(ck), "CST_%s", suffix);
|
||||
e = getenv(ck);
|
||||
return e ? e : "";
|
||||
}
|
||||
|
||||
hermit_icon_mode_t hermit_icon_mode(void) {
|
||||
const char *e = hermit_getenv("ICONS");
|
||||
if (!e[0] || !strcmp(e, "auto"))
|
||||
return HERMIT_ICON_AUTO;
|
||||
if (!strcmp(e, "font") || !strcmp(e, "ttf"))
|
||||
return HERMIT_ICON_FONT;
|
||||
return HERMIT_ICON_MINI;
|
||||
}
|
||||
|
||||
hermit_icon_mode_t hermit_icon_mode_eff(int ascii) {
|
||||
if (ascii)
|
||||
return HERMIT_ICON_MINI;
|
||||
hermit_icon_mode_t m = hermit_icon_mode();
|
||||
if (m != HERMIT_ICON_AUTO)
|
||||
return m;
|
||||
/* AUTO: font only when forced path exists or HERMIT_ICON_FONT=1. */
|
||||
if (hermit_getenv("ICON_FONT")[0] == '1')
|
||||
return HERMIT_ICON_FONT;
|
||||
return HERMIT_ICON_MINI;
|
||||
}
|
||||
|
||||
hermit_state_t hermit_state_from_ui(int busy, int ask, int error) {
|
||||
if (error)
|
||||
return HERMIT_ST_ERROR;
|
||||
if (ask)
|
||||
return HERMIT_ST_QUESTION;
|
||||
if (busy)
|
||||
return HERMIT_ST_CODING;
|
||||
return HERMIT_ST_IDLE;
|
||||
}
|
||||
|
||||
wchar_t hermit_font_codepoint(hermit_state_t st, int frame, int with_shell) {
|
||||
if (st < 0 || st >= HERMIT_ST_N)
|
||||
st = HERMIT_ST_IDLE;
|
||||
if (frame < 0)
|
||||
frame = 0;
|
||||
frame %= HERMIT_FONT_FRAMES;
|
||||
int shell = with_shell ? 1 : 0;
|
||||
return (wchar_t)(HERMIT_FONT_PUA_BASE +
|
||||
(shell * HERMIT_ST_N + (int)st) * HERMIT_FONT_FRAMES +
|
||||
frame);
|
||||
}
|
||||
|
||||
/* --- minimal badge ------------------------------------------------------- */
|
||||
|
||||
const char *hermit_mini_badge(hermit_state_t st, int frame, int ascii) {
|
||||
int f = frame < 0 ? 0 : frame;
|
||||
if (ascii) {
|
||||
switch (st) {
|
||||
case HERMIT_ST_QUESTION:
|
||||
return "||?";
|
||||
case HERMIT_ST_ERROR:
|
||||
return "\\/!";
|
||||
case HERMIT_ST_SUCCESS:
|
||||
return "||*";
|
||||
case HERMIT_ST_THINKING:
|
||||
return (f % 2) ? "~|" : "|~";
|
||||
case HERMIT_ST_CODING:
|
||||
case HERMIT_ST_IDLE:
|
||||
default: {
|
||||
static const char *const spin[] = {"||", ">_", "||", "-"};
|
||||
return spin[f % 4];
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (st) {
|
||||
case HERMIT_ST_QUESTION:
|
||||
return "║║?";
|
||||
case HERMIT_ST_ERROR:
|
||||
return "╲╱!";
|
||||
case HERMIT_ST_SUCCESS:
|
||||
return "║║✦";
|
||||
case HERMIT_ST_THINKING:
|
||||
return (f % 2) ? "║~" : "~║";
|
||||
case HERMIT_ST_CODING:
|
||||
case HERMIT_ST_IDLE:
|
||||
default: {
|
||||
static const char *const spin[] = {"║║", ">_", "║║", "─"};
|
||||
return spin[f % 4];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* --- home shell art (fixed grid) ----------------------------------------- */
|
||||
/* 6 rows x 14 cols. Roles: 0 body 1 shell 2 eye 3 leg 4 prompt 5 empty/mark */
|
||||
|
||||
#define ART_H 6
|
||||
#define ART_W 14
|
||||
|
||||
static const char *const ART_SHELL[ART_H] = {
|
||||
" ######## ", /* shell top */
|
||||
" ## >_ ## ", /* shell + prompt */
|
||||
" ########## ", /* shell bottom */
|
||||
" ###### ", /* body */
|
||||
" #::## ", /* eyes :: on body */
|
||||
" | | ", /* legs */
|
||||
};
|
||||
|
||||
static const char *const ART_NOSHELL[ART_H] = {
|
||||
" ",
|
||||
" ",
|
||||
" ###### ",
|
||||
" #::## ",
|
||||
" ###### ",
|
||||
" | | ",
|
||||
};
|
||||
|
||||
static const char *const ART_SHELL_ASC[ART_H] = {
|
||||
" ######## ",
|
||||
" ## >_ ## ",
|
||||
" ########## ",
|
||||
" ###### ",
|
||||
" #::## ",
|
||||
" | | ",
|
||||
};
|
||||
|
||||
int hermit_shell_art(hermit_state_t st, int with_shell, int ascii,
|
||||
const char **lines, int max_lines) {
|
||||
(void)st;
|
||||
if (!lines || max_lines < 1)
|
||||
return 0;
|
||||
const char *const *src =
|
||||
with_shell ? (ascii ? ART_SHELL_ASC : ART_SHELL) : ART_NOSHELL;
|
||||
int n = ART_H < max_lines ? ART_H : max_lines;
|
||||
for (int i = 0; i < n; i++)
|
||||
lines[i] = src[i];
|
||||
return n;
|
||||
}
|
||||
|
||||
int hermit_shell_cell_role(hermit_state_t st, int with_shell, int row, int col) {
|
||||
(void)st;
|
||||
if (row < 0 || row >= ART_H || col < 0 || col >= ART_W)
|
||||
return 5;
|
||||
const char *const *src = with_shell ? ART_SHELL : ART_NOSHELL;
|
||||
char c = src[row][col];
|
||||
if (c == ' ' || !c)
|
||||
return 5;
|
||||
if (c == '>' || c == '_')
|
||||
return 4;
|
||||
if (c == ':')
|
||||
return 2;
|
||||
if (c == '|')
|
||||
return 3;
|
||||
if (with_shell && row <= 2)
|
||||
return 1; /* shell */
|
||||
return 0; /* body */
|
||||
}
|
||||
+1
-1
@@ -671,7 +671,7 @@ int main(int argc, char **argv) {
|
||||
do_migrate = 1;
|
||||
break;
|
||||
case 'V':
|
||||
printf("cst-agent %s\n", CST_VERSION);
|
||||
printf("hermit %s\n", CST_VERSION);
|
||||
cst_curl_cleanup();
|
||||
return 0;
|
||||
default:
|
||||
|
||||
+41
-13
@@ -230,6 +230,7 @@ int os_rename_replace(const char *from, const char *to) {
|
||||
int os_config_dir(char *out, size_t out_sz) {
|
||||
if (!out || out_sz < 8)
|
||||
return -1;
|
||||
char neu[OS_PATH_MAX], old[OS_PATH_MAX];
|
||||
#ifdef _WIN32
|
||||
const char *base = getenv("XDG_CONFIG_HOME");
|
||||
if (!base || !os_path_is_abs(base))
|
||||
@@ -238,20 +239,42 @@ int os_config_dir(char *out, size_t out_sz) {
|
||||
base = getenv("LOCALAPPDATA");
|
||||
if (!base || !base[0])
|
||||
return -1;
|
||||
int n = snprintf(out, out_sz, "%s/cst-agent", base);
|
||||
os_path_norm_slashes(out);
|
||||
int n = snprintf(neu, sizeof(neu), "%s/hermit", base);
|
||||
int o = snprintf(old, sizeof(old), "%s/cst-agent", base);
|
||||
if (n < 0 || o < 0 || (size_t)n >= sizeof(neu) || (size_t)o >= sizeof(old))
|
||||
return -1;
|
||||
os_path_norm_slashes(neu);
|
||||
os_path_norm_slashes(old);
|
||||
#else
|
||||
const char *xdg = getenv("XDG_CONFIG_HOME");
|
||||
const char *home = getenv("HOME");
|
||||
int n;
|
||||
if (xdg && xdg[0] == '/')
|
||||
n = snprintf(out, out_sz, "%s/cst-agent", xdg);
|
||||
else if (home && home[0])
|
||||
n = snprintf(out, out_sz, "%s/.config/cst-agent", home);
|
||||
else
|
||||
int n, o;
|
||||
if (xdg && xdg[0] == '/') {
|
||||
n = snprintf(neu, sizeof(neu), "%s/hermit", xdg);
|
||||
o = snprintf(old, sizeof(old), "%s/cst-agent", xdg);
|
||||
} else if (home && home[0]) {
|
||||
n = snprintf(neu, sizeof(neu), "%s/.config/hermit", home);
|
||||
o = snprintf(old, sizeof(old), "%s/.config/cst-agent", home);
|
||||
} else
|
||||
return -1;
|
||||
if (n < 0 || o < 0 || (size_t)n >= sizeof(neu) || (size_t)o >= sizeof(old))
|
||||
return -1;
|
||||
#endif
|
||||
if (n < 0 || (size_t)n >= out_sz)
|
||||
/* Prefer hermit/; fall back to legacy cst-agent/ when hermit is absent. */
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(neu, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
if (snprintf(out, out_sz, "%s", neu) < 0 || strlen(neu) >= out_sz)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
if (stat(old, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
if (snprintf(out, out_sz, "%s", old) < 0 || strlen(old) >= out_sz)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (snprintf(out, out_sz, "%s", neu) < 0 || strlen(neu) >= out_sz)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
@@ -298,10 +321,15 @@ static void tmp_pick(void) {
|
||||
snprintf(cand[nc++], OS_PATH_MAX, "%s/tmp", prefix);
|
||||
snprintf(cand[nc++], OS_PATH_MAX, "/tmp");
|
||||
snprintf(cand[nc++], OS_PATH_MAX, "/var/tmp");
|
||||
if (cache && cache[0] == '/')
|
||||
snprintf(cand[nc++], OS_PATH_MAX, "%s/cst-agent/tmp", cache);
|
||||
else if (home && home[0] == '/')
|
||||
snprintf(cand[nc++], OS_PATH_MAX, "%s/.cache/cst-agent/tmp", home);
|
||||
if (cache && cache[0] == '/') {
|
||||
snprintf(cand[nc++], OS_PATH_MAX, "%s/hermit/tmp", cache);
|
||||
if (nc < 5)
|
||||
snprintf(cand[nc++], OS_PATH_MAX, "%s/cst-agent/tmp", cache);
|
||||
} else if (home && home[0] == '/') {
|
||||
snprintf(cand[nc++], OS_PATH_MAX, "%s/.cache/hermit/tmp", home);
|
||||
if (nc < 5)
|
||||
snprintf(cand[nc++], OS_PATH_MAX, "%s/.cache/cst-agent/tmp", home);
|
||||
}
|
||||
for (int i = 0; i < nc; i++) {
|
||||
tmp_trim(cand[i]);
|
||||
if (i == nc - 1)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "cst_tools.h"
|
||||
#include "cst_util.h"
|
||||
#include "cst_version.h"
|
||||
#include "cst_hermit_glyph.h"
|
||||
#include "os_compat.h"
|
||||
#include "sashidown.h"
|
||||
|
||||
@@ -496,18 +497,18 @@ static char *xstrndup(const char *s, size_t n) {
|
||||
|
||||
/* --- theme ---------------------------------------------------------------- */
|
||||
|
||||
/* Dark panel chrome — cool steel accents (not peach OpenCode clone). */
|
||||
/* Hermit palette: brand teal, crab orange, eye purple (design mock). */
|
||||
static void theme_init(void) {
|
||||
const char *nc = getenv("NO_COLOR");
|
||||
g_nocolor = nc && nc[0];
|
||||
const char *asc = getenv("CST_ASCII");
|
||||
const char *asc = hermit_getenv("ASCII");
|
||||
#if defined(__ANDROID__)
|
||||
g_utf8 = 1; /* bionic is UTF-8 only; nl_langinfo needs API 26 */
|
||||
#elif !defined(_WIN32)
|
||||
const char *cs = nl_langinfo(CODESET);
|
||||
g_utf8 = cs && (strcasecmp(cs, "UTF-8") == 0 || strcasecmp(cs, "utf8") == 0);
|
||||
#endif
|
||||
g_ascii = asc && asc[0] ? asc[0] != '0' : !g_utf8;
|
||||
g_ascii = asc[0] ? asc[0] != '0' : !g_utf8;
|
||||
if (!HAS_COLOR())
|
||||
return;
|
||||
start_color();
|
||||
@@ -515,54 +516,62 @@ static void theme_init(void) {
|
||||
|
||||
short primary = COLOR_CYAN;
|
||||
short panel = COLOR_BLACK;
|
||||
short muted = COLOR_CYAN;
|
||||
short muted = COLOR_WHITE;
|
||||
short success = COLOR_GREEN;
|
||||
short err = COLOR_RED;
|
||||
short assist = COLOR_CYAN;
|
||||
short assist = COLOR_MAGENTA;
|
||||
short text = COLOR_WHITE;
|
||||
short box = COLOR_BLACK, side = COLOR_BLACK;
|
||||
short accent = COLOR_BLUE, bright = COLOR_WHITE, logo_dim = COLOR_CYAN;
|
||||
short accent = COLOR_CYAN, bright = COLOR_WHITE, logo_dim = COLOR_CYAN;
|
||||
short tool = COLOR_YELLOW;
|
||||
short prompt_c = COLOR_CYAN;
|
||||
|
||||
if (COLORS >= 256) {
|
||||
box = 235;
|
||||
side = 234;
|
||||
accent = 75; /* #5c9cf5 steel blue */
|
||||
accent = 75; /* #277F94 brand teal */
|
||||
bright = 255;
|
||||
logo_dim = 242;
|
||||
primary = 75; /* brand/user accent: blue, not peach */
|
||||
panel = 233; /* #121212 */
|
||||
primary = 75;
|
||||
panel = 233;
|
||||
muted = 244;
|
||||
success = 114;
|
||||
err = 167;
|
||||
assist = 74; /* soft cyan assistant */
|
||||
text = 252; /* brighter body for readability */
|
||||
assist = 177; /* #9957AD eye purple */
|
||||
tool = 216; /* #F68042 crab orange */
|
||||
prompt_c = 152; /* #A9D9D7 prompt cyan */
|
||||
text = 252;
|
||||
if (can_change_color()) {
|
||||
init_color(75, 430, 660, 996); /* #6ea8fe brand */
|
||||
init_color(233, 55, 58, 70); /* #0e0f12 deeper panel */
|
||||
init_color(244, 502, 520, 560); /* cool muted */
|
||||
init_color(252, 820, 830, 850); /* body */
|
||||
init_color(75, 153, 498, 580); /* #277F94 brand */
|
||||
init_color(152, 663, 851, 843); /* #A9D9D7 prompt */
|
||||
init_color(216, 965, 502, 259); /* #F68042 crab */
|
||||
init_color(177, 600, 341, 678); /* #9957AD eye */
|
||||
init_color(167, 850, 320, 320); /* error red */
|
||||
init_color(168, 643, 341, 310); /* #A4574F brick legs */
|
||||
init_color(233, 55, 58, 70); /* #0e0f12 panel */
|
||||
init_color(244, 502, 520, 560); /* muted */
|
||||
init_color(252, 820, 830, 850); /* body text */
|
||||
init_color(114, 400, 750, 450);
|
||||
init_color(167, 850, 320, 320);
|
||||
init_color(74, 380, 720, 780);
|
||||
init_color(235, 100, 105, 120); /* raised box */
|
||||
init_color(234, 78, 82, 96); /* sidebar */
|
||||
init_color(216, 980, 720, 420); /* warm tip only */
|
||||
init_color(235, 100, 105, 120);
|
||||
init_color(234, 78, 82, 96);
|
||||
}
|
||||
} else if (can_change_color() && COLORS >= 8) {
|
||||
init_color(COLOR_BLACK, 55, 58, 70);
|
||||
init_color(COLOR_CYAN, 430, 660, 996);
|
||||
init_color(COLOR_CYAN, 153, 498, 580);
|
||||
init_color(COLOR_WHITE, 820, 830, 850);
|
||||
init_color(COLOR_GREEN, 400, 750, 450);
|
||||
init_color(COLOR_RED, 850, 320, 320);
|
||||
init_color(COLOR_BLUE, 380, 720, 780);
|
||||
init_color(COLOR_YELLOW, 980, 720, 420);
|
||||
init_color(COLOR_MAGENTA, 600, 341, 678);
|
||||
init_color(COLOR_YELLOW, 965, 502, 259);
|
||||
panel = COLOR_BLACK;
|
||||
primary = COLOR_CYAN;
|
||||
muted = COLOR_WHITE;
|
||||
text = COLOR_WHITE;
|
||||
success = COLOR_GREEN;
|
||||
err = COLOR_RED;
|
||||
assist = COLOR_BLUE;
|
||||
assist = COLOR_MAGENTA;
|
||||
tool = COLOR_YELLOW;
|
||||
prompt_c = COLOR_CYAN;
|
||||
}
|
||||
|
||||
/* Header is dark too — primary only as text accent (not a bright bar). */
|
||||
@@ -574,11 +583,11 @@ static void theme_init(void) {
|
||||
init_pair(CP_MUTED, muted, panel);
|
||||
init_pair(CP_SUCCESS, success, panel);
|
||||
init_pair(CP_ERROR, err, panel);
|
||||
init_pair(CP_TOOL, primary, panel);
|
||||
init_pair(CP_TOOL, tool, panel);
|
||||
init_pair(CP_PANEL, text, panel);
|
||||
init_pair(CP_SGR_GREEN, success, panel);
|
||||
init_pair(CP_SGR_YELLOW, primary, panel);
|
||||
init_pair(CP_SGR_CYAN, assist, panel);
|
||||
init_pair(CP_SGR_YELLOW, tool, panel);
|
||||
init_pair(CP_SGR_CYAN, prompt_c, panel);
|
||||
if (COLORS >= 256)
|
||||
init_pair(CP_SGR_MAGENTA, 176, panel);
|
||||
else
|
||||
@@ -2687,20 +2696,87 @@ static void draw_todo(tui_t *t) {
|
||||
|
||||
/* --- start screen ---------------------------------------------------------- */
|
||||
|
||||
/* 3-row block font for "cstagent"; each glyph is 4 columns wide. */
|
||||
static const char *const LOGO[8][3] = {
|
||||
{"█▀▀▀", "█ ", "▀▀▀▀"}, /* c */
|
||||
{"█▀▀▀", "▀▀▀█", "▀▀▀▀"}, /* s */
|
||||
{"▀█▀▀", " █ ", " ▀▀▀"}, /* t */
|
||||
{"█▀▀█", "█▀▀█", "▀ ▀"}, /* a */
|
||||
{"█▀▀▀", "█ ▀█", "▀▀▀▀"}, /* g */
|
||||
{"█▀▀▀", "█▀▀ ", "▀▀▀▀"}, /* e */
|
||||
{"█▀▀▄", "█ █", "▀ ▀"}, /* n */
|
||||
{"▀█▀▀", " █ ", " ▀▀▀"}, /* t */
|
||||
/* 3-row block font for "HERMIT"; each glyph is 4 columns wide. */
|
||||
static const char *const LOGO[6][3] = {
|
||||
{"█ █", "█▀▀█", "█ █"}, /* H */
|
||||
{"█▀▀▀", "█▀▀ ", "▀▀▀▀"}, /* E */
|
||||
{"█▀▀█", "█▀▀█", "▀ ▀"}, /* R */
|
||||
{"█▀▄█", "█ █", "▀ ▀"}, /* M */
|
||||
{"▀█▀▀", " █ ", " ▀▀▀"}, /* I */
|
||||
{"▀█▀▀", " █ ", " ▀▀▀"}, /* T */
|
||||
};
|
||||
#define LOGO_W (8 * 5 - 1)
|
||||
#define LOGO_N 6
|
||||
#define LOGO_W (LOGO_N * 5 - 1)
|
||||
#define LOGO_H 3
|
||||
|
||||
static int hermit_role_pair(int role) {
|
||||
switch (role) {
|
||||
case 0:
|
||||
return CP_TOOL; /* body */
|
||||
case 1:
|
||||
return CP_ACCENT; /* shell */
|
||||
case 2:
|
||||
return CP_ASSIST; /* eye */
|
||||
case 3:
|
||||
return COLORS >= 256 ? CP_MUTED : CP_ERROR; /* brick legs */
|
||||
case 4:
|
||||
return CP_SGR_CYAN; /* >_ */
|
||||
default:
|
||||
return CP_MUTED;
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_hermit_icon(WINDOW *win, int y0, int x0, hermit_state_t st,
|
||||
int with_shell, int frame) {
|
||||
hermit_icon_mode_t mode = hermit_icon_mode_eff(g_ascii);
|
||||
if (mode == HERMIT_ICON_FONT) {
|
||||
wchar_t wc = hermit_font_codepoint(st, frame, with_shell);
|
||||
cchar_t cc;
|
||||
wchar_t wstr[2] = {wc, 0};
|
||||
if (setcchar(&cc, wstr, 0, 0, NULL) == OK) {
|
||||
if (HAS_COLOR())
|
||||
wattron(win, COLOR_PAIR(CP_TOOL));
|
||||
mvwadd_wch(win, y0, x0, &cc);
|
||||
if (HAS_COLOR())
|
||||
wattroff(win, COLOR_PAIR(CP_TOOL));
|
||||
return;
|
||||
}
|
||||
}
|
||||
const char *lines[8];
|
||||
int n = hermit_shell_art(st, with_shell, g_ascii, lines, 8);
|
||||
for (int r = 0; r < n; r++) {
|
||||
const char *row = lines[r];
|
||||
for (int c = 0; row[c]; c++) {
|
||||
if (row[c] == ' ')
|
||||
continue;
|
||||
char ch[2] = {row[c], 0};
|
||||
int pair = hermit_role_pair(hermit_shell_cell_role(st, with_shell, r, c));
|
||||
draw_ansi_at(win, y0 + r, x0 + c, 1, ch, pair, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_hermit_badge(WINDOW *win, int y, int x, const tui_t *t) {
|
||||
hermit_state_t st = hermit_state_from_ui(
|
||||
t->busy, t->ask_pending || t->awaiting_perm, 0);
|
||||
hermit_icon_mode_t mode = hermit_icon_mode_eff(g_ascii);
|
||||
if (mode == HERMIT_ICON_FONT) {
|
||||
wchar_t wc = hermit_font_codepoint(st, t->spin, 0);
|
||||
cchar_t cc;
|
||||
wchar_t wstr[2] = {wc, 0};
|
||||
if (setcchar(&cc, wstr, 0, 0, NULL) == OK) {
|
||||
if (HAS_COLOR())
|
||||
wattron(win, COLOR_PAIR(CP_TOOL));
|
||||
mvwadd_wch(win, y, x, &cc);
|
||||
if (HAS_COLOR())
|
||||
wattroff(win, COLOR_PAIR(CP_TOOL));
|
||||
return;
|
||||
}
|
||||
}
|
||||
const char *b = hermit_mini_badge(st, t->spin, g_ascii);
|
||||
draw_ansi_at(win, y, x, (int)strlen(b), b, CP_TOOL, 0, 0);
|
||||
}
|
||||
|
||||
static const char *const TIPS[] = {
|
||||
"Tab switches between Build and Plan (read-only) mode",
|
||||
"Shift+Tab cycles approval: ask, auto, yolo",
|
||||
@@ -2720,40 +2796,51 @@ static void draw_home(tui_t *t) {
|
||||
getmaxyx(t->logw, h, w);
|
||||
int box_top = t->in_y - t->log_y;
|
||||
int box_bot = box_top + t->in_h;
|
||||
/* Tight home stack: wordmark (default) or CST_LOGO=block, tagline, chip. */
|
||||
int brand_y = box_top - 2;
|
||||
if (brand_y < 0)
|
||||
brand_y = 0;
|
||||
int want_block = 0;
|
||||
{
|
||||
const char *lb = getenv("CST_LOGO");
|
||||
want_block = lb && !strcmp(lb, "block");
|
||||
const char *lb = hermit_getenv("LOGO");
|
||||
want_block = lb[0] && !strcmp(lb, "block");
|
||||
}
|
||||
/* Crab on the right of the brand stack when there is room. */
|
||||
int crab_w = 14, crab_h = 6;
|
||||
int crab_x = w - crab_w - 2;
|
||||
int show_crab = crab_x > LOGO_W + 4 && box_top >= crab_h + 1;
|
||||
if (want_block && !(g_ascii || !g_utf8 || g_logo_text) &&
|
||||
box_top - LOGO_H - 2 >= 0 && w >= LOGO_W) {
|
||||
int x0 = (w - LOGO_W) / 2, y0 = box_top - LOGO_H - 2;
|
||||
for (int g = 0; g < 8; g++)
|
||||
int x0 = show_crab ? (crab_x - LOGO_W) / 2 : (w - LOGO_W) / 2;
|
||||
if (x0 < 0)
|
||||
x0 = 0;
|
||||
int y0 = box_top - LOGO_H - 2;
|
||||
for (int g = 0; g < LOGO_N; g++)
|
||||
for (int r = 0; r < LOGO_H; r++)
|
||||
draw_ansi_at(t->logw, y0 + r, x0 + g * 5, 4, LOGO[g][r],
|
||||
g < 3 ? CP_ACCENT : CP_BRIGHT, 0, 0);
|
||||
draw_ansi_at(t->logw, y0 + r, x0 + g * 5, 4, LOGO[g][r], CP_ACCENT, 0,
|
||||
0);
|
||||
brand_y = y0 + LOGO_H;
|
||||
} else {
|
||||
const char *mark = "CST-agent";
|
||||
const char *mark = "HERMIT";
|
||||
int mw = (int)strlen(mark);
|
||||
int x = (w - mw) / 2;
|
||||
int x = show_crab ? (crab_x - mw) / 2 : (w - mw) / 2;
|
||||
if (x < 0)
|
||||
x = 0;
|
||||
if (brand_y >= 1)
|
||||
brand_y -= 1;
|
||||
draw_ansi_at(t->logw, brand_y, x, 3, "CST", CP_ACCENT, 1, 0);
|
||||
draw_ansi_at(t->logw, brand_y, x + 3, mw - 3, "-agent", CP_BRIGHT, 1, 0);
|
||||
draw_ansi_at(t->logw, brand_y, x, mw, mark, CP_ACCENT, 1, 0);
|
||||
}
|
||||
if (show_crab) {
|
||||
int cy = box_top - crab_h;
|
||||
if (cy < 0)
|
||||
cy = 0;
|
||||
draw_hermit_icon(t->logw, cy, crab_x, HERMIT_ST_IDLE, 1, t->spin);
|
||||
}
|
||||
int ty = brand_y + 1;
|
||||
if (ty < box_top) {
|
||||
const char *tag =
|
||||
g_ascii ? "code . plan . parliament" : "code · plan · parliament";
|
||||
int tw = (int)strlen(tag);
|
||||
int x = (w - tw) / 2;
|
||||
int x = show_crab ? (crab_x - tw) / 2 : (w - tw) / 2;
|
||||
if (x < 0)
|
||||
x = 0;
|
||||
draw_ansi_at(t->logw, ty, x, tw, tag, CP_MUTED, 0, 0);
|
||||
@@ -2772,9 +2859,10 @@ static void draw_home(tui_t *t) {
|
||||
}
|
||||
int right = t->in_x - t->log_x + t->in_w;
|
||||
if (box_bot < h) {
|
||||
char hint[96];
|
||||
char hint[128];
|
||||
snprintf(hint, sizeof(hint),
|
||||
"\033[97mtab\033[39m modes \033[97mctrl+p\033[39m commands");
|
||||
"\033[97mtab\033[39m modes \033[97mctrl+p\033[39m commands "
|
||||
"\033[97mshift+tab\033[39m approval");
|
||||
int hw = vis_cols(hint);
|
||||
if (right - hw >= 0)
|
||||
draw_ansi_at(t->logw, box_bot, right - hw, hw, hint, CP_MUTED, 0, 0);
|
||||
@@ -2785,7 +2873,7 @@ static void draw_home(tui_t *t) {
|
||||
int tw = vis_cols(tip);
|
||||
if (tw > w - 4)
|
||||
tw = w - 4;
|
||||
draw_ansi_at(t->logw, box_bot + 2, (w - tw) / 2, tw, tip, CP_MUTED, 0, 0);
|
||||
draw_ansi_at(t->logw, box_bot + 2, (w - tw) / 2, tw, tip, CP_TOOL, 0, 0);
|
||||
}
|
||||
const char *ext = cst_extensions_report();
|
||||
if (ext[0] && box_bot + 3 < h) {
|
||||
@@ -3018,7 +3106,7 @@ static void draw_side(tui_t *t) {
|
||||
|
||||
if (h >= 4) {
|
||||
draw_ansi_at(s, h - 3, x, mw, t->where, CP_SIDE_MUTED, 0, 0);
|
||||
snprintf(line, sizeof(line), "CST %s", CST_VERSION);
|
||||
snprintf(line, sizeof(line), "Hermit %s", CST_VERSION);
|
||||
draw_ansi_at(s, h - 2, x, mw, line, CP_SIDE_MUTED, 0, 0);
|
||||
}
|
||||
wrefresh(s);
|
||||
@@ -3183,9 +3271,11 @@ static void draw_topbar(tui_t *t) {
|
||||
if (hexish)
|
||||
title = "New session";
|
||||
}
|
||||
/* CST mark + title + model. Role lives only in the Seat Deck. */
|
||||
/* Hermit badge + title + model. Role lives only in the Seat Deck. */
|
||||
int x = 1;
|
||||
x += draw_ansi_at(w, 0, x, 3, "CST", CP_TOPBAR_ACCENT, 1, 0);
|
||||
draw_hermit_badge(w, 0, x, t);
|
||||
x += 4;
|
||||
x += draw_ansi_at(w, 0, x, 6, "Hermit", CP_TOPBAR_ACCENT, 1, 0);
|
||||
x += draw_ansi_at(w, 0, x, 1, " ", CP_TOPBAR_MUTED, 0, 0);
|
||||
int model_room = 0;
|
||||
char mid[160] = "";
|
||||
|
||||
Reference in New Issue
Block a user