feat: rebrand as Hermit — TUI palette, dual glyphs, binary 0.10.0
ci / tcc-check (push) Failing after 41s
ci / linux (aarch64, 0) (push) Skipped
ci / linux (x86, 1) (push) Skipped
ci / linux (x86_64, 1) (push) Skipped
ci / windows (push) Skipped

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:
WHC
2026-10-29 21:42:45 +08:00
co-authored by Cursor
parent 2602806aea
commit f30b4ede48
32 changed files with 793 additions and 265 deletions
+167
View File
@@ -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 */
}