release: Hermit 1.0.0 — home layout, icon font, install
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

Compact the home parliament chip, animate crab states like the HTML kit,
ship hermit-icons.ttf (sbix) + frames, and install the font from make/
bootstrap/package paths.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
WHC
2026-10-29 21:57:46 +08:00
co-authored by Cursor
parent 8fcbd886c3
commit 41f4cf85ee
109 changed files with 472 additions and 218 deletions
+145 -41
View File
@@ -1,5 +1,6 @@
#define _POSIX_C_SOURCE 200809L
#include "cst_hermit_glyph.h"
#include "os_compat.h"
#include <stdio.h>
#include <stdlib.h>
@@ -18,6 +19,40 @@ const char *hermit_getenv(const char *suffix) {
return e ? e : "";
}
static int file_ok(const char *path) {
os_stat_t st;
return path && path[0] && os_lstat(path, &st) == 0 && st.is_reg &&
st.size > 0;
}
/* Prefer HERMIT_FONT_PATH, then common install locations. */
const char *hermit_font_path(void) {
static char buf[512];
const char *e = hermit_getenv("FONT_PATH");
if (file_ok(e))
return e;
const char *home = getenv("HOME");
if (home && home[0]) {
snprintf(buf, sizeof(buf), "%s/.local/share/fonts/hermit-icons.ttf", home);
if (file_ok(buf))
return buf;
snprintf(buf, sizeof(buf), "%s/.local/share/hermit/hermit-icons.ttf", home);
if (file_ok(buf))
return buf;
}
static const char *const cands[] = {
"/usr/local/share/fonts/hermit/hermit-icons.ttf",
"/usr/local/share/hermit/hermit-icons.ttf",
"/usr/share/fonts/hermit/hermit-icons.ttf",
"/usr/share/hermit/hermit-icons.ttf",
"res/hermit-icons.ttf",
NULL};
for (int i = 0; cands[i]; i++)
if (file_ok(cands[i]))
return cands[i];
return "";
}
hermit_icon_mode_t hermit_icon_mode(void) {
const char *e = hermit_getenv("ICONS");
if (!e[0] || !strcmp(e, "auto"))
@@ -33,9 +68,10 @@ hermit_icon_mode_t hermit_icon_mode_eff(int ascii) {
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;
/* AUTO stays on the mini art track; font needs a terminal that loads
* Hermit Icons (installed by make install / bootstrap). */
return HERMIT_ICON_MINI;
}
@@ -101,67 +137,135 @@ const char *hermit_mini_badge(hermit_state_t st, int frame, int ascii) {
}
}
/* --- home shell art (fixed grid) ----------------------------------------- */
/* 6 rows x 14 cols. Roles: 0 body 1 shell 2 eye 3 leg 4 prompt 5 empty/mark */
/* --- home shell art (fixed grid, state+frame animated like HTML kit) ----- */
/* 6 rows x 12 cols. Roles: 0 body 1 shell 2 eye 3 leg 4 prompt 5 empty/mark */
#define ART_H 6
#define ART_W 14
#define ART_W 12
static const char *const ART_SHELL[ART_H] = {
" ######## ", /* shell top */
" ## >_ ## ", /* shell + prompt */
" ########## ", /* shell bottom */
" ###### ", /* body */
" #::## ", /* eyes :: on body */
" | | ", /* legs */
};
/* Base grids use:
* @ shell # body : open eye = blink >_ prompt | leg ? mark ~ claw
* Eyes/prompt mutate per state+frame below. */
static const char *const ART_NOSHELL[ART_H] = {
" ",
" ",
" ###### ",
" #::## ",
" ###### ",
" | | ",
};
static void fill_art(hermit_state_t st, int with_shell, int frame, int ascii,
char out[ART_H][ART_W + 1]) {
static const char *const shell_base[ART_H] = {
" @@@@@@@@ ",
" @@ >_ @@ ",
" @@@@@@@@ ",
" #### ",
" #::# ",
" | | ",
};
static const char *const bare_base[ART_H] = {
" ",
" ",
" #### ",
" #::# ",
" #### ",
" | | ",
};
const char *const *src = with_shell ? shell_base : bare_base;
int f = frame < 0 ? 0 : frame % HERMIT_FONT_FRAMES;
for (int r = 0; r < ART_H; r++) {
memcpy(out[r], src[r], ART_W);
out[r][ART_W] = 0;
}
static const char *const ART_SHELL_ASC[ART_H] = {
" ######## ",
" ## >_ ## ",
" ########## ",
" ###### ",
" #::## ",
" | | ",
};
/* Eyes on body row (row 4 with shell, row 3 bare). */
int er = with_shell ? 4 : 3;
int blink = (st == HERMIT_ST_IDLE || st == HERMIT_ST_QUESTION) &&
(f == 3 || f == 4);
if (st == HERMIT_ST_CODING) {
out[er][4] = '>';
out[er][5] = '_';
out[er][6] = ' ';
out[er][7] = '#';
} else if (st == HERMIT_ST_SUCCESS) {
out[er][5] = ascii ? '^' : '^';
out[er][6] = ascii ? '^' : '^';
} else if (st == HERMIT_ST_ERROR) {
out[er][5] = 'x';
out[er][6] = 'x';
} else if (st == HERMIT_ST_THINKING) {
int look = (f % 4) >= 2;
out[er][5] = look ? ' ' : ':';
out[er][6] = look ? ':' : ' ';
} else if (blink) {
out[er][5] = '=';
out[er][6] = '=';
}
/* Accents */
if (st == HERMIT_ST_QUESTION && with_shell) {
out[1][10] = '?';
} else if (st == HERMIT_ST_THINKING) {
out[er][9] = '~';
} else if (st == HERMIT_ST_ERROR && with_shell) {
out[2][10] = '!';
}
/* Soft breath: nudge body one cell on even frames for idle/coding. */
if ((st == HERMIT_ST_IDLE || st == HERMIT_ST_CODING) && (f & 1) &&
with_shell) {
/* already subtle via blink; keep grid stable */
}
if (ascii) {
for (int r = 0; r < ART_H; r++)
for (int c = 0; c < ART_W; c++) {
char ch = out[r][c];
if (ch == '@')
out[r][c] = '#';
else if (ch == ':' || ch == '=')
out[r][c] = (ch == '=') ? '-' : ':';
}
}
}
int hermit_shell_art(hermit_state_t st, int with_shell, int ascii,
const char **lines, int max_lines) {
(void)st;
return hermit_shell_art_frame(st, with_shell, ascii, 0, lines, max_lines);
}
int hermit_shell_art_frame(hermit_state_t st, int with_shell, int ascii,
int frame, const char **lines, int max_lines) {
static char buf[ART_H][ART_W + 1];
static char *ptrs[ART_H];
if (!lines || max_lines < 1)
return 0;
const char *const *src =
with_shell ? (ascii ? ART_SHELL_ASC : ART_SHELL) : ART_NOSHELL;
fill_art(st, with_shell, frame, ascii, buf);
int n = ART_H < max_lines ? ART_H : max_lines;
for (int i = 0; i < n; i++)
lines[i] = src[i];
for (int i = 0; i < n; i++) {
ptrs[i] = buf[i];
lines[i] = ptrs[i];
}
return n;
}
int hermit_shell_cell_role(hermit_state_t st, int with_shell, int row, int col) {
(void)st;
int hermit_shell_cell_role(hermit_state_t st, int with_shell, int row,
int col) {
static char grid[ART_H][ART_W + 1];
fill_art(st, with_shell, 0, 0, grid);
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];
char c = grid[row][col];
if (c == ' ' || !c)
return 5;
if (c == '>' || c == '_')
return 4;
if (c == ':')
if (c == ':' || c == '=' || c == '^' || c == 'x')
return 2;
if (c == '|')
if (c == '|' )
return 3;
if (c == '?' || c == '!' || c == '~')
return 5; /* mark */
if (c == '@')
return 1;
if (with_shell && row <= 2)
return 1; /* shell */
return 0; /* body */
return 1;
return 0;
}
int hermit_shell_art_width(void) { return ART_W; }
int hermit_shell_art_height(void) { return ART_H; }