From 41f4cf85ee3a70fec5a4a4c08e08c65b76e5f441 Mon Sep 17 00:00:00 2001 From: WHC Date: Thu, 29 Oct 2026 21:57:46 +0800 Subject: [PATCH] =?UTF-8?q?release:=20Hermit=201.0.0=20=E2=80=94=20home=20?= =?UTF-8?q?layout,=20icon=20font,=20install?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- CMakeLists.txt | 13 + MIGRATION.md | 2 +- Makefile | 27 +- README.md | 10 +- icons/agent-motion/README.md | 2 +- include/cst_hermit_glyph.h | 10 +- include/cst_version.h | 6 +- res/hermit-frames/bare/coding/f00.png | Bin 0 -> 280 bytes res/hermit-frames/bare/coding/f01.png | Bin 0 -> 280 bytes res/hermit-frames/bare/coding/f02.png | Bin 0 -> 280 bytes res/hermit-frames/bare/coding/f03.png | Bin 0 -> 280 bytes res/hermit-frames/bare/coding/f04.png | Bin 0 -> 280 bytes res/hermit-frames/bare/coding/f05.png | Bin 0 -> 280 bytes res/hermit-frames/bare/coding/f06.png | Bin 0 -> 280 bytes res/hermit-frames/bare/coding/f07.png | Bin 0 -> 280 bytes res/hermit-frames/bare/error/f00.png | Bin 0 -> 314 bytes res/hermit-frames/bare/error/f01.png | Bin 0 -> 314 bytes res/hermit-frames/bare/error/f02.png | Bin 0 -> 314 bytes res/hermit-frames/bare/error/f03.png | Bin 0 -> 314 bytes res/hermit-frames/bare/error/f04.png | Bin 0 -> 314 bytes res/hermit-frames/bare/error/f05.png | Bin 0 -> 314 bytes res/hermit-frames/bare/error/f06.png | Bin 0 -> 314 bytes res/hermit-frames/bare/error/f07.png | Bin 0 -> 314 bytes res/hermit-frames/bare/idle/f00.png | Bin 0 -> 239 bytes res/hermit-frames/bare/idle/f01.png | Bin 0 -> 239 bytes res/hermit-frames/bare/idle/f02.png | Bin 0 -> 239 bytes res/hermit-frames/bare/idle/f03.png | Bin 0 -> 238 bytes res/hermit-frames/bare/idle/f04.png | Bin 0 -> 238 bytes res/hermit-frames/bare/idle/f05.png | Bin 0 -> 239 bytes res/hermit-frames/bare/idle/f06.png | Bin 0 -> 239 bytes res/hermit-frames/bare/idle/f07.png | Bin 0 -> 239 bytes res/hermit-frames/bare/question/f00.png | Bin 0 -> 320 bytes res/hermit-frames/bare/question/f01.png | Bin 0 -> 320 bytes res/hermit-frames/bare/question/f02.png | Bin 0 -> 321 bytes res/hermit-frames/bare/question/f03.png | Bin 0 -> 320 bytes res/hermit-frames/bare/question/f04.png | Bin 0 -> 320 bytes res/hermit-frames/bare/question/f05.png | Bin 0 -> 321 bytes res/hermit-frames/bare/question/f06.png | Bin 0 -> 321 bytes res/hermit-frames/bare/question/f07.png | Bin 0 -> 321 bytes res/hermit-frames/bare/success/f00.png | Bin 0 -> 263 bytes res/hermit-frames/bare/success/f01.png | Bin 0 -> 263 bytes res/hermit-frames/bare/success/f02.png | Bin 0 -> 263 bytes res/hermit-frames/bare/success/f03.png | Bin 0 -> 263 bytes res/hermit-frames/bare/success/f04.png | Bin 0 -> 263 bytes res/hermit-frames/bare/success/f05.png | Bin 0 -> 263 bytes res/hermit-frames/bare/success/f06.png | Bin 0 -> 263 bytes res/hermit-frames/bare/success/f07.png | Bin 0 -> 263 bytes res/hermit-frames/bare/thinking/f00.png | Bin 0 -> 299 bytes res/hermit-frames/bare/thinking/f01.png | Bin 0 -> 299 bytes res/hermit-frames/bare/thinking/f02.png | Bin 0 -> 299 bytes res/hermit-frames/bare/thinking/f03.png | Bin 0 -> 299 bytes res/hermit-frames/bare/thinking/f04.png | Bin 0 -> 299 bytes res/hermit-frames/bare/thinking/f05.png | Bin 0 -> 299 bytes res/hermit-frames/bare/thinking/f06.png | Bin 0 -> 295 bytes res/hermit-frames/bare/thinking/f07.png | Bin 0 -> 299 bytes res/hermit-frames/shell/coding/f00.png | Bin 0 -> 439 bytes res/hermit-frames/shell/coding/f01.png | Bin 0 -> 439 bytes res/hermit-frames/shell/coding/f02.png | Bin 0 -> 443 bytes res/hermit-frames/shell/coding/f03.png | Bin 0 -> 439 bytes res/hermit-frames/shell/coding/f04.png | Bin 0 -> 439 bytes res/hermit-frames/shell/coding/f05.png | Bin 0 -> 441 bytes res/hermit-frames/shell/coding/f06.png | Bin 0 -> 421 bytes res/hermit-frames/shell/coding/f07.png | Bin 0 -> 441 bytes res/hermit-frames/shell/error/f00.png | Bin 0 -> 457 bytes res/hermit-frames/shell/error/f01.png | Bin 0 -> 457 bytes res/hermit-frames/shell/error/f02.png | Bin 0 -> 466 bytes res/hermit-frames/shell/error/f03.png | Bin 0 -> 457 bytes res/hermit-frames/shell/error/f04.png | Bin 0 -> 457 bytes res/hermit-frames/shell/error/f05.png | Bin 0 -> 458 bytes res/hermit-frames/shell/error/f06.png | Bin 0 -> 443 bytes res/hermit-frames/shell/error/f07.png | Bin 0 -> 458 bytes res/hermit-frames/shell/idle/f00.png | Bin 0 -> 406 bytes res/hermit-frames/shell/idle/f01.png | Bin 0 -> 406 bytes res/hermit-frames/shell/idle/f02.png | Bin 0 -> 410 bytes res/hermit-frames/shell/idle/f03.png | Bin 0 -> 404 bytes res/hermit-frames/shell/idle/f04.png | Bin 0 -> 404 bytes res/hermit-frames/shell/idle/f05.png | Bin 0 -> 410 bytes res/hermit-frames/shell/idle/f06.png | Bin 0 -> 386 bytes res/hermit-frames/shell/idle/f07.png | Bin 0 -> 410 bytes res/hermit-frames/shell/question/f00.png | Bin 0 -> 486 bytes res/hermit-frames/shell/question/f01.png | Bin 0 -> 486 bytes res/hermit-frames/shell/question/f02.png | Bin 0 -> 476 bytes res/hermit-frames/shell/question/f03.png | Bin 0 -> 484 bytes res/hermit-frames/shell/question/f04.png | Bin 0 -> 484 bytes res/hermit-frames/shell/question/f05.png | Bin 0 -> 480 bytes res/hermit-frames/shell/question/f06.png | Bin 0 -> 467 bytes res/hermit-frames/shell/question/f07.png | Bin 0 -> 483 bytes res/hermit-frames/shell/success/f00.png | Bin 0 -> 433 bytes res/hermit-frames/shell/success/f01.png | Bin 0 -> 434 bytes res/hermit-frames/shell/success/f02.png | Bin 0 -> 434 bytes res/hermit-frames/shell/success/f03.png | Bin 0 -> 434 bytes res/hermit-frames/shell/success/f04.png | Bin 0 -> 433 bytes res/hermit-frames/shell/success/f05.png | Bin 0 -> 416 bytes res/hermit-frames/shell/success/f06.png | Bin 0 -> 431 bytes res/hermit-frames/shell/success/f07.png | Bin 0 -> 416 bytes res/hermit-frames/shell/thinking/f00.png | Bin 0 -> 453 bytes res/hermit-frames/shell/thinking/f01.png | Bin 0 -> 453 bytes res/hermit-frames/shell/thinking/f02.png | Bin 0 -> 461 bytes res/hermit-frames/shell/thinking/f03.png | Bin 0 -> 454 bytes res/hermit-frames/shell/thinking/f04.png | Bin 0 -> 453 bytes res/hermit-frames/shell/thinking/f05.png | Bin 0 -> 470 bytes res/hermit-frames/shell/thinking/f06.png | Bin 0 -> 449 bytes res/hermit-frames/shell/thinking/f07.png | Bin 0 -> 471 bytes res/hermit-icons.ttf | Bin 0 -> 38216 bytes scripts/bootstrap_linux.sh | 42 +++- scripts/build_hermit_font.py | 304 +++++++++++++---------- scripts/package_linux.sh | 14 ++ src/hermit_glyph.c | 186 +++++++++++--- src/tui.c | 74 ++++-- 109 files changed, 472 insertions(+), 218 deletions(-) create mode 100644 res/hermit-frames/bare/coding/f00.png create mode 100644 res/hermit-frames/bare/coding/f01.png create mode 100644 res/hermit-frames/bare/coding/f02.png create mode 100644 res/hermit-frames/bare/coding/f03.png create mode 100644 res/hermit-frames/bare/coding/f04.png create mode 100644 res/hermit-frames/bare/coding/f05.png create mode 100644 res/hermit-frames/bare/coding/f06.png create mode 100644 res/hermit-frames/bare/coding/f07.png create mode 100644 res/hermit-frames/bare/error/f00.png create mode 100644 res/hermit-frames/bare/error/f01.png create mode 100644 res/hermit-frames/bare/error/f02.png create mode 100644 res/hermit-frames/bare/error/f03.png create mode 100644 res/hermit-frames/bare/error/f04.png create mode 100644 res/hermit-frames/bare/error/f05.png create mode 100644 res/hermit-frames/bare/error/f06.png create mode 100644 res/hermit-frames/bare/error/f07.png create mode 100644 res/hermit-frames/bare/idle/f00.png create mode 100644 res/hermit-frames/bare/idle/f01.png create mode 100644 res/hermit-frames/bare/idle/f02.png create mode 100644 res/hermit-frames/bare/idle/f03.png create mode 100644 res/hermit-frames/bare/idle/f04.png create mode 100644 res/hermit-frames/bare/idle/f05.png create mode 100644 res/hermit-frames/bare/idle/f06.png create mode 100644 res/hermit-frames/bare/idle/f07.png create mode 100644 res/hermit-frames/bare/question/f00.png create mode 100644 res/hermit-frames/bare/question/f01.png create mode 100644 res/hermit-frames/bare/question/f02.png create mode 100644 res/hermit-frames/bare/question/f03.png create mode 100644 res/hermit-frames/bare/question/f04.png create mode 100644 res/hermit-frames/bare/question/f05.png create mode 100644 res/hermit-frames/bare/question/f06.png create mode 100644 res/hermit-frames/bare/question/f07.png create mode 100644 res/hermit-frames/bare/success/f00.png create mode 100644 res/hermit-frames/bare/success/f01.png create mode 100644 res/hermit-frames/bare/success/f02.png create mode 100644 res/hermit-frames/bare/success/f03.png create mode 100644 res/hermit-frames/bare/success/f04.png create mode 100644 res/hermit-frames/bare/success/f05.png create mode 100644 res/hermit-frames/bare/success/f06.png create mode 100644 res/hermit-frames/bare/success/f07.png create mode 100644 res/hermit-frames/bare/thinking/f00.png create mode 100644 res/hermit-frames/bare/thinking/f01.png create mode 100644 res/hermit-frames/bare/thinking/f02.png create mode 100644 res/hermit-frames/bare/thinking/f03.png create mode 100644 res/hermit-frames/bare/thinking/f04.png create mode 100644 res/hermit-frames/bare/thinking/f05.png create mode 100644 res/hermit-frames/bare/thinking/f06.png create mode 100644 res/hermit-frames/bare/thinking/f07.png create mode 100644 res/hermit-frames/shell/coding/f00.png create mode 100644 res/hermit-frames/shell/coding/f01.png create mode 100644 res/hermit-frames/shell/coding/f02.png create mode 100644 res/hermit-frames/shell/coding/f03.png create mode 100644 res/hermit-frames/shell/coding/f04.png create mode 100644 res/hermit-frames/shell/coding/f05.png create mode 100644 res/hermit-frames/shell/coding/f06.png create mode 100644 res/hermit-frames/shell/coding/f07.png create mode 100644 res/hermit-frames/shell/error/f00.png create mode 100644 res/hermit-frames/shell/error/f01.png create mode 100644 res/hermit-frames/shell/error/f02.png create mode 100644 res/hermit-frames/shell/error/f03.png create mode 100644 res/hermit-frames/shell/error/f04.png create mode 100644 res/hermit-frames/shell/error/f05.png create mode 100644 res/hermit-frames/shell/error/f06.png create mode 100644 res/hermit-frames/shell/error/f07.png create mode 100644 res/hermit-frames/shell/idle/f00.png create mode 100644 res/hermit-frames/shell/idle/f01.png create mode 100644 res/hermit-frames/shell/idle/f02.png create mode 100644 res/hermit-frames/shell/idle/f03.png create mode 100644 res/hermit-frames/shell/idle/f04.png create mode 100644 res/hermit-frames/shell/idle/f05.png create mode 100644 res/hermit-frames/shell/idle/f06.png create mode 100644 res/hermit-frames/shell/idle/f07.png create mode 100644 res/hermit-frames/shell/question/f00.png create mode 100644 res/hermit-frames/shell/question/f01.png create mode 100644 res/hermit-frames/shell/question/f02.png create mode 100644 res/hermit-frames/shell/question/f03.png create mode 100644 res/hermit-frames/shell/question/f04.png create mode 100644 res/hermit-frames/shell/question/f05.png create mode 100644 res/hermit-frames/shell/question/f06.png create mode 100644 res/hermit-frames/shell/question/f07.png create mode 100644 res/hermit-frames/shell/success/f00.png create mode 100644 res/hermit-frames/shell/success/f01.png create mode 100644 res/hermit-frames/shell/success/f02.png create mode 100644 res/hermit-frames/shell/success/f03.png create mode 100644 res/hermit-frames/shell/success/f04.png create mode 100644 res/hermit-frames/shell/success/f05.png create mode 100644 res/hermit-frames/shell/success/f06.png create mode 100644 res/hermit-frames/shell/success/f07.png create mode 100644 res/hermit-frames/shell/thinking/f00.png create mode 100644 res/hermit-frames/shell/thinking/f01.png create mode 100644 res/hermit-frames/shell/thinking/f02.png create mode 100644 res/hermit-frames/shell/thinking/f03.png create mode 100644 res/hermit-frames/shell/thinking/f04.png create mode 100644 res/hermit-frames/shell/thinking/f05.png create mode 100644 res/hermit-frames/shell/thinking/f06.png create mode 100644 res/hermit-frames/shell/thinking/f07.png create mode 100644 res/hermit-icons.ttf diff --git a/CMakeLists.txt b/CMakeLists.txt index 890701b..27b2b49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -428,9 +428,22 @@ endif() if(WIN32) install(TARGETS cst-agent RUNTIME DESTINATION .) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/res/hermit-icons.ttf") + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/res/hermit-icons.ttf" DESTINATION .) + endif() else() include(GNUInstallDirs) install(TARGETS cst-agent RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/res/hermit-icons.ttf") + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/res/hermit-icons.ttf" + DESTINATION ${CMAKE_INSTALL_DATADIR}/hermit) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/res/hermit-icons.ttf" + DESTINATION ${CMAKE_INSTALL_DATADIR}/fonts/hermit) + endif() + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/res/hermit-frames") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/res/hermit-frames/" + DESTINATION ${CMAKE_INSTALL_DATADIR}/hermit/frames) + endif() endif() message(STATUS "Compiler: ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER}") diff --git a/MIGRATION.md b/MIGRATION.md index 18d34e5..153bc1a 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -47,7 +47,7 @@ git clone https://git.whcself.xyz/WHC/CST-agent.git # 兼容;请改 origin | Release `cst-agent-VERSION-linux-*.tar.gz` | `hermit-VERSION-linux-*.tar.gz`(过渡期可能双发) | ```bash -hermit --version # hermit 0.10.x +hermit --version # hermit 1.0.0 cst-agent --version # 同二进制(软链) ``` diff --git a/Makefile b/Makefile index a00b32b..784a863 100644 --- a/Makefile +++ b/Makefile @@ -219,8 +219,8 @@ help: @echo " all / hermit incremental build (ccache auto if installed)" @echo " check build + run unit tests" @echo " asan AddressSanitizer rebuild + check" - @echo " hermit-font optional SVG→color font (needs rsvg + fonttools)" - @echo " install PREFIX=$(PREFIX); hermit + cst-agent symlink" + @echo " hermit-font bake PNG frames + hermit-icons.ttf (Pillow+fonttools)" + @echo " install PREFIX=$(PREFIX); hermit + font + cst-agent symlink" @echo " install-service systemd --user parliament unit" @echo "Env: USE_CCACHE=0|1|auto STATIC=1 CST_WEBUI=0 CC=tcc" @echo " HERMIT_ICONS=auto|mini|font HERMIT_*/CST_* env" @@ -250,7 +250,7 @@ hermit: $(OBJ) sashidown-lib hermit-font: @python3 scripts/build_hermit_font.py || \ - echo "hermit-font: skipped (install rsvg-convert + fonttools to build)" + echo "hermit-font: skipped (need Pillow+fonttools; try: nix-shell -p python3Packages.pillow python3Packages.fonttools)" # Built-in model data as a C byte list (included by model_registry.c). .gen/models_builtin.inc: src/models_builtin.json @@ -328,9 +328,24 @@ install: hermit install -m 0755 hermit "$(DESTDIR)$(PREFIX)/bin/hermit" ln -sfr "$(DESTDIR)$(PREFIX)/bin/hermit" "$(DESTDIR)$(PREFIX)/bin/cst-agent" 2>/dev/null || \ ln -sf hermit "$(DESTDIR)$(PREFIX)/bin/cst-agent" + @if [ ! -f res/hermit-icons.ttf ]; then $(MAKE) hermit-font || true; fi @if [ -f res/hermit-icons.ttf ]; then \ install -d "$(DESTDIR)$(PREFIX)/share/hermit"; \ - install -m 0644 res/hermit-icons.ttf "$(DESTDIR)$(PREFIX)/share/hermit/"; \ + install -d "$(DESTDIR)$(PREFIX)/share/fonts/hermit"; \ + install -m 0644 res/hermit-icons.ttf "$(DESTDIR)$(PREFIX)/share/hermit/hermit-icons.ttf"; \ + install -m 0644 res/hermit-icons.ttf "$(DESTDIR)$(PREFIX)/share/fonts/hermit/hermit-icons.ttf"; \ + if [ -n "$(HOME)" ] && [ "$(DESTDIR)" = "" ]; then \ + install -d "$(HOME)/.local/share/fonts"; \ + install -m 0644 res/hermit-icons.ttf "$(HOME)/.local/share/fonts/hermit-icons.ttf"; \ + command -v fc-cache >/dev/null 2>&1 && fc-cache -f "$(HOME)/.local/share/fonts" >/dev/null 2>&1 || true; \ + fi; \ + echo "installed Hermit Icons → $(PREFIX)/share/fonts/hermit/hermit-icons.ttf"; \ + else \ + echo "warning: res/hermit-icons.ttf missing (run: make hermit-font)" >&2; \ + fi + @if [ -d res/hermit-frames ]; then \ + install -d "$(DESTDIR)$(PREFIX)/share/hermit/frames"; \ + cp -a res/hermit-frames/. "$(DESTDIR)$(PREFIX)/share/hermit/frames/"; \ fi # Optional systemd --user unit for the parliament seat service. @@ -346,6 +361,10 @@ install-service: install uninstall: rm -f "$(DESTDIR)$(PREFIX)/bin/hermit" "$(DESTDIR)$(PREFIX)/bin/cst-agent" + rm -f "$(DESTDIR)$(PREFIX)/share/hermit/hermit-icons.ttf" + rm -f "$(DESTDIR)$(PREFIX)/share/fonts/hermit/hermit-icons.ttf" + rm -rf "$(DESTDIR)$(PREFIX)/share/hermit/frames" + rm -f "$(HOME)/.local/share/fonts/hermit-icons.ttf" rm -f "$(HOME)/.config/systemd/user/cst-agent-parliament.service" -include $(DEPFILES) diff --git a/README.md b/README.md index c9a72f0..fe39895 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ curl -fsSL https://git.whcself.xyz/WHC/Hermit/raw/branch/main/scripts/bootstrap_ ```bash ./scripts/bootstrap_linux.sh . -hermit --version # hermit 0.10.x +hermit --version # hermit 1.0.0 cst-agent-update # 升级助手仍可用 ./scripts/migrate_remote_to_hermit.sh # 已有 CST-agent 检出时改 origin ``` @@ -54,11 +54,11 @@ cd ~/my-project && hermit ```bash make -j$(nproc) # 产出 hermit(并链 cst-agent) make -j$(nproc) check -make hermit-font # 可选:SVG→PUA 字体(需 rsvg/fonttools) -make install PREFIX=$HOME/.local +make hermit-font # 烘焙 PNG 帧 + hermit-icons.ttf(Pillow + fonttools) +make install PREFIX=$HOME/.local # 同时安装字体到 share/fonts/hermit 与 ~/.local/share/fonts ``` -TUI 图标:`HERMIT_ICONS=auto|mini|font`(默认极简字符帧;`font` 需安装 `share/hermit/hermit-icons.ttf`)。 +TUI 图标:`HERMIT_ICONS=auto|mini|font`(默认色块动画帧,状态与 `icons/agent-motion` HTML 套件一致;`font` 输出 PUA `U+E700+`,需终端加载已安装的 Hermit Icons)。 --- @@ -89,4 +89,4 @@ scripts/prompt_smoke.sh scripts/e2e_webui.sh ``` -`icons/agent-motion/` 为 SVG 资产;TUI 默认用字体序列极简轨,`make hermit-font` 生成 PUA 字体轨。 +`icons/agent-motion/` 为 SVG 资产;`make hermit-font` 生成 `res/hermit-icons.ttf`(sbix 彩色位图,96 帧)与 `res/hermit-frames/`。安装脚本会把字体装进系统字体目录。 diff --git a/icons/agent-motion/README.md b/icons/agent-motion/README.md index 75ecfef..6d9f5b9 100644 --- a/icons/agent-motion/README.md +++ b/icons/agent-motion/README.md @@ -18,6 +18,6 @@ icons/{状态}-shell.svg 是大的带壳图标;icons/{状态}.svg 是小的无 普通字符终端不直接渲染 SVG;图标适用于支持图形的终端界面、Webview 或 CLI 伴随应用。 -**TUI 双轨**:默认极简字符/色块帧见 `src/hermit_glyph.c`;`make hermit-font`(`scripts/build_hermit_font.py`)把本目录 SVG 批量打成 `res/hermit-icons.ttf`(PUA `U+E700+`),`HERMIT_ICONS=font` 时直接输出码点序列。 +**TUI 双轨**:默认极简字符/色块帧见 `src/hermit_glyph.c`(idle 眨眼、coding `>_`、ask/thinking/success/error,与本目录 HTML 动画状态一致);`make hermit-font`(Pillow + fonttools,无需 rsvg)烘焙 `res/hermit-frames/` 与 `res/hermit-icons.ttf`(sbix,PUA `U+E700+`)。`make install` / bootstrap 会安装该字体;`HERMIT_ICONS=font` 时输出码点序列(终端需选用 Hermit Icons)。 更新:橘杏色身体、莓紫眼睛、低饱和蓝绿终端壳。预览主角色以 620ms 缓入缓出插值连续改变眼睛路径与身体姿态,支持中途切换;不重建主角色。状态符号淡入淡出。独立 SVG 保留各状态循环动效;跨状态变形逻辑位于 index.html。 diff --git a/include/cst_hermit_glyph.h b/include/cst_hermit_glyph.h index 2e6a18b..fe78c49 100644 --- a/include/cst_hermit_glyph.h +++ b/include/cst_hermit_glyph.h @@ -45,8 +45,13 @@ const char *hermit_mini_badge(hermit_state_t st, int frame, int ascii); /* Home shell crab: up to *nlines strings (not owned). Returns line count. */ int hermit_shell_art(hermit_state_t st, int with_shell, int ascii, const char **lines, int max_lines); +/* Same as hermit_shell_art, but advances HTML-kit animation by frame. */ +int hermit_shell_art_frame(hermit_state_t st, int with_shell, int ascii, + int frame, const char **lines, int max_lines); +int hermit_shell_art_width(void); +int hermit_shell_art_height(void); -/* Color roles for art cells: 0=body(crab) 1=shell 2=eye 3=leg 4=prompt 5=mark */ +/* Color roles for art cells: 0=body 1=shell 2=eye 3=leg 4=prompt 5=mark */ int hermit_shell_cell_role(hermit_state_t st, int with_shell, int row, int col); /* PUA base U+E700; layout: shell*HERMIT_ST_N*HERMIT_FONT_FRAMES + st*FRAMES + frame */ @@ -54,6 +59,9 @@ int hermit_shell_cell_role(hermit_state_t st, int with_shell, int row, int col); #define HERMIT_FONT_FRAMES 8 wchar_t hermit_font_codepoint(hermit_state_t st, int frame, int with_shell); +/* Non-empty path when hermit-icons.ttf is installed (may be ""). */ +const char *hermit_font_path(void); + /* Map agent UI signals → icon state. */ hermit_state_t hermit_state_from_ui(int busy, int ask, int error); diff --git a/include/cst_version.h b/include/cst_version.h index a81d868..bf1d5f4 100644 --- a/include/cst_version.h +++ b/include/cst_version.h @@ -1,9 +1,9 @@ #ifndef CST_VERSION_H #define CST_VERSION_H -#define CST_VERSION_MAJOR 0 -#define CST_VERSION_MINOR 10 -#define CST_VERSION_PATCH 1 +#define CST_VERSION_MAJOR 1 +#define CST_VERSION_MINOR 0 +#define CST_VERSION_PATCH 0 #define CST_VERSION_STR_(x) #x #define CST_VERSION_STR(x) CST_VERSION_STR_(x) diff --git a/res/hermit-frames/bare/coding/f00.png b/res/hermit-frames/bare/coding/f00.png new file mode 100644 index 0000000000000000000000000000000000000000..899b1e324479b03e92e410126781d514634d0fa8 GIT binary patch literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=_dQ)4Ln`LHy?&6l$w8p?;l+wg z!nZas_Qx$@nzeva>p-`=^aft5kE@oe9y`HkoX>Xne=JAinREMc60Y+AwSz#z)f*b! z_O}jSUVUxfUhk^AT3xe#`o#sVpTM#pD=aX48`D2l#uW1jy$p-kUbU-bt%!7D(wO|d zJ+08Og(V<-?$uE9d-|F!u}2i<3)k^KzWsUScacM34m;g^viMH^i`pt|^4VVagLH^U z=-fLZrK#Enc1MP;VAzngN&ofJ5*c&n=da&BFWmFWCsBFIT?aF!6L|{KKp-`=^aft5kE@oe9y`HkoX>Xne=JAinREMc60Y+AwSz#z)f*b! z_O}jSUVUxfUhk^AT3xe#`o#sVpTM#pD=aX48`D2l#uW1jy$p-kUbU-bt%!7D(wO|d zJ+08Og(V<-?$uE9d-|F!u}2i<3)k^KzWsUScacM34m;g^viMH^i`pt|^4VVagLH^U z=-fLZrK#Enc1MP;VAzngN&ofJ5*c&n=da&BFWmFWCsBFIT?aF!6L|{KKp-`=^aft5kE@oe9y`HkoX>Xne=JAinREMc60Y+AwS&Q$qi&IZ zF3Qb|+xqQo?4{RXfmhCyf9k4gRbdF-+_h_y=m&ich24!gECQwh_9lVOXN80uX4cr7 zbaN}IH0*k{X5Frjb)JfEl-PdG`A{u!f1dx|iFHHXCjQe;OJu$?J%9c7dEuT{K8f-Z?mCz;oyb#=W=t^$JIvz) X(-J*Xy?dfb_d$G5S3j3^P6p-`=^aft5kE@oe9y`HkoX>Xne=JAinREMc60Y+AwSz#z)f*b! z_O}jSUVUxfUhk^AT3xe#`o#sVpTM#pD=aX48`D2l#uW1jy$p-kUbU-bt%!7D(wO|d zJ+08Og(V<-?$uE9d-|F!u}2i<3)k^KzWsUScacM34m;g^viMH^i`pt|^4VVagLH^U z=-fLZrK#Enc1MP;VAzngN&ofJ5*c&n=da&BFWmFWCsBFIT?aF!6L|{KKp-`=^aft5kE@oe9y`HkoX>Xne=JAinREMc60Y+AwSz#z)f*b! z_O}jSUVUxfUhk^AT3xe#`o#sVpTM#pD=aX48`D2l#uW1jy$p-kUbU-bt%!7D(wO|d zJ+08Og(V<-?$uE9d-|F!u}2i<3)k^KzWsUScacM34m;g^viMH^i`pt|^4VVagLH^U z=-fLZrK#Enc1MP;VAzngN&ofJ5*c&n=da&BFWmFWCsBFIT?aF!6L|{KKp-`=^aft5kE@oe9y`HkoX>Xne=JAinREMc60Y+AwS&M0IqB4o z?$s4lS8r`E_go+C8fbj~r1mS(Nem%pwO*YHJFs7&;SJj+L5C|2e=oT3ntFRMq<;E) zK}WA+0>i6Sn|HlB_FJ`eL$}0};zREfYpU(O&U3oKrE$H(^Q_{{_|;`iY5&rh>YBS2 zt(rW0(YhSg3iH)mq6}fa-%|I^>3f{^qGs=3o4)Y5nmH|-q))gp+~iGB23iMpS;u## WNDouJ@|j=qKzvVEKbLh*2~7YPHgo9! literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/coding/f06.png b/res/hermit-frames/bare/coding/f06.png new file mode 100644 index 0000000000000000000000000000000000000000..bcf3b932c2baf994245e0cb1b4a8f54a58650719 GIT binary patch literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=_dQ)4Ln`LHy}pt6kbyw!L*~GZ zp4So>y`+pBr=1X*sbDfi=YZ7B7BMY%j)_Vy?!^ZDTi?0J<@x8ys<+dC+CgB0l62}v z_v(tOtGBk7d#;am4K%)gQu~$YB!-Z)TCYxp9oVnX@P=)Zpu-i1zZYD1O}#xBQa}B@ zprh9@f#KDv&AVP5`>xu$pc&1*%Lvd1q!_*wP4Qf9qXumxwD`Gj7EP0}aa7;f@{T_*6J XbtSi{-T^_oEg-(9tDnm{r-UW|n4xd& literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/coding/f07.png b/res/hermit-frames/bare/coding/f07.png new file mode 100644 index 0000000000000000000000000000000000000000..1056702a22cbcd8fd1985ed4d56e46a07150ed3b GIT binary patch literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=_dQ)4Ln`LHy?&6l$w8p?;l+wg z!nZas_Qx$@nzeva>p-`=^aft5kE@oe9y`HkoX>Xne=JAinREMc60Y+AwS&M0IqB4o z?$s4lS8r`E_go+C8fbj~r1mS(Nem%pwO*YHJFs7&;SJj+L5C|2e=oT3ntFRMq<;E) zK}WA+0>i6Sn|HlB_FJ`eL$}0};zREfYpU(O&U3oKrE$H(^Q_{{_|;`iY5&rh>YBS2 zt(rW0(YhSg3iH)mq6}fa-%|I^>3f{^qGs=3o4)Y5nmH|-q))gp+~iGB23iMpS;u## WNDouJ@|j=qKzvVEKbLh*2~7YPHgo9! literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/error/f00.png b/res/hermit-frames/bare/error/f00.png new file mode 100644 index 0000000000000000000000000000000000000000..32f139caf0dd1add4e2f3eafb2ab56bd1fde3a5a GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU}W@kaSW-L^Y*%-*g*$@V;}pM z7YSc4U~E<5-Fiqh#%ba#HCHb20$%frZlXaP56`~2@%(@G;TeB(%!E0Ch5*6-^*XPX z>AX6&=-Q1%9_Kg|b~R?O2;6np(wYA8bzWXkkoTpMa=FN>uZlFaZtCrOD%f;d_rb=B z^}Fip*-|7Mu8L@+N*!1hHES+wBwK=}Cqw&bvzOoP9$K-NZ0(Lb|E<}6cGGE@3*UFV z(t8sr@P3{5x6KZlWEE`1zP^%|+vKdHdY4gbO*w;q;|sP$J6RrleKzgv=?N+K*MzTN z_;CG>-?ik8JN%nhzxwj^eZAX6&=-Q1%9_Kg|b~R?O2;6np(wYA8bzWXkkoTpMa=FN>uZlFaZtCrOD%f;d_rb=B z^}Fip*-|7Mu8L@+N*!1hHES+wBwK=}Cqw&bvzOoP9$K-NZ0(Lb|E<}6cGGE@3*UFV z(t8sr@P3{5x6KZlWEE`1zP^%|+vKdHdY4gbO*w;q;|sP$J6RrleKzgv=?N+K*MzTN z_;CG>-?ik8JN%nhzxwj^eZaz>GFNc9m%JtwYB<#*a%C)N-5 z@9<4a-nfI`dG)I=U*AVueR)_$;I4xSQ%9bH6r+nd*!3I*%qR2g&gbi}-UsnLUHx3v IIVCg!02zILC;$Ke literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/error/f03.png b/res/hermit-frames/bare/error/f03.png new file mode 100644 index 0000000000000000000000000000000000000000..32f139caf0dd1add4e2f3eafb2ab56bd1fde3a5a GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU}W@kaSW-L^Y*%-*g*$@V;}pM z7YSc4U~E<5-Fiqh#%ba#HCHb20$%frZlXaP56`~2@%(@G;TeB(%!E0Ch5*6-^*XPX z>AX6&=-Q1%9_Kg|b~R?O2;6np(wYA8bzWXkkoTpMa=FN>uZlFaZtCrOD%f;d_rb=B z^}Fip*-|7Mu8L@+N*!1hHES+wBwK=}Cqw&bvzOoP9$K-NZ0(Lb|E<}6cGGE@3*UFV z(t8sr@P3{5x6KZlWEE`1zP^%|+vKdHdY4gbO*w;q;|sP$J6RrleKzgv=?N+K*MzTN z_;CG>-?ik8JN%nhzxwj^eZAX6&=-Q1%9_Kg|b~R?O2;6np(wYA8bzWXkkoTpMa=FN>uZlFaZtCrOD%f;d_rb=B z^}Fip*-|7Mu8L@+N*!1hHES+wBwK=}Cqw&bvzOoP9$K-NZ0(Lb|E<}6cGGE@3*UFV z(t8sr@P3{5x6KZlWEE`1zP^%|+vKdHdY4gbO*w;q;|sP$J6RrleKzgv=?N+K*MzTN z_;CG>-?ik8JN%nhzxwj^eZbXBhZ5$C916P{GmOta+Fq1d6y$wr&mNyOSzmXGOwAF$|B>V1Q?U;b zd&2MTw`bnOb0CXriV<%@`1-Twnsk^qOi^igTsr?t_5Fgm%)fRW%@J?EIN$eRDxXAE z%vbSmS{?q^Rewh++~k`uue<6yfB#LzX_MYDa9^`-SPrr_hUr69@#$x&9-H#7X@@ZU z$j(!Ly)wdf*+Jjhe?QCDyy8g|=*m-&VstSV=w(PDo`43tliKlVNvNZkbq$WW!PwMw#`$m(2g&_FCq`b=|bx7Cm+OTzbU^E|gx4 zzp+}uZm-_W^Gqkp8otC_*&SbThHvAP684A<_ZaUm0j(|OJn;IY?(BBOllvmq2QV`H zKl3)rQgroki#;pP+t>Gp&QbP0l+XkK D&U=I- literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/error/f07.png b/res/hermit-frames/bare/error/f07.png new file mode 100644 index 0000000000000000000000000000000000000000..9b6a7b86b613486cb5fc866814826be4347a6fe3 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU}W@kaSW-L^Y*%-*g*$@V;}pM z7YSc4U~E<5-Fis1h|@J|mWXD|0nbXBhZ5$C916P{GmOta+Fq1d6y$wr&mNyOSzmXGOwAF$|B>V1Q?U;b zd&2MTw`bnOb0CXriV<%@`1-Twnsk^qOi^igTsr?t_5Fgm%)fRW%@J?EIN$eRDxXAE z%vbSmS{?q^Rewh++~k`uue<6yfB#LzX_MYDa9^`-SPrr_hUr69@#$x&9-H#7X@@ZU z$j(!Ly)wdf*+Jjhe?QCDyy8g|=*m-&VstSV=w(O4{#KQ4y0?}QsE^@5+M7v-r+@tM#`Lv; zol@lY&V_5g*8V)QXS)aE$_<@6H}d`v=UTxR;LEt*CTCW=+1bOdo^zTluyQbE4Pjp) zwljWtUensI|J85&VRPZ~_`6f}*)g*PV!{9b literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/idle/f01.png b/res/hermit-frames/bare/idle/f01.png new file mode 100644 index 0000000000000000000000000000000000000000..8010d0363fef608182d771b8ae5682c4203a007d GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=+dN$yLn`LHy}pt6kb?lrf%XZT zG8!hd@ZD*tHsn;~z0hV|$R(Jfa!p_T$;>O4{#KQ4y0?}QsE^@5+M7v-r+@tM#`Lv; zol@lY&V_5g*8V)QXS)aE$_<@6H}d`v=UTxR;LEt*CTCW=+1bOdo^zTluyQbE4Pjp) zwljWtUensI|J85&VRPZ~_`6f}*)g*PV!{9b literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/idle/f02.png b/res/hermit-frames/bare/idle/f02.png new file mode 100644 index 0000000000000000000000000000000000000000..1f2f1cc255301f7335b7a1efecc210ba2b547843 GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=+dN$yLn`LHy}pt6kb?lrf%XZT zG8!hd@ZD*tHsn;~z0hV|$R(Jfa!p_T$;>O4{#KQ4y0?}QsE>gmdZRaA{ISQIudg}$ zqa)?6dfT>Z-=7M9zNf^x{XOmI?pr`RX?sUNKx136Nb7 z^`vg@F3xS&{?AYNEG?j^@b&q$8Pc~K?j;5zA`6zyJbxBU8?{5#C6 nm_vdcN*l7cR`3CBc4!oDZ&W{!^=SKKkV8CO{an^LB{Ts5i~3|` literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/idle/f03.png b/res/hermit-frames/bare/idle/f03.png new file mode 100644 index 0000000000000000000000000000000000000000..41abe3a6d74c16fe9b3815e3e738ba577b2f3075 GIT binary patch literal 238 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=TRmMILn`LHy}pt6kb?lrf%XZT zG8!hd@ZD*tHsn;~z0hW@*verf^yM_?U!Sa(b$hEKpI-xNV_-;-nEm9#?>+TjW^J9F zKXJ|NBNwjy+EFv6&uS_fb^{evG2Xlo@ zam&OOUi-EF$l?18tSW8G{@oEOPBvdqej{WhgL8Ln+TjW^J9F zKXJ|NBNwjy+EFv6&uS_fb^{evG2Xlo@ zam&OOUi-EF$l?18tSW8G{@oEOPBvdqej{WhgL8Ln6Usg-*y7R94{7cj4;^GTm nFI1*-xoD{$V0**-0pt=-S3j3^P6OU>4IVW}wvve%%w_qkiDX+({=vF7b5rb6Mw<&;$TLk6`)$ literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/idle/f07.png b/res/hermit-frames/bare/idle/f07.png new file mode 100644 index 0000000000000000000000000000000000000000..3e5ca7334126376581e9cdadf081d03ddc5d3a81 GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=+dN$yLn`LHy}pt6kb?lrf%XZT zG8!hd@ZD*tHsn;~z0hW@*verf^!mE`lbKg8{jDn9bZ;#qP#?nv+1*b*Jg=zvI;&Jd ze&U+nM=o6ZwXY`Wp8Jx9kQ+zt++h2|%^JeKVp)SJ|LkWAeLrvbQQ5@1f-k_A@hamg z*LT-f&Q8ew`=8TnKjTEEp!&Sdof~;y$eM*}9awgB-6Usg-*y7R94{7cj4;^GTm nFI1*-xoD{$V0**-0pt=-S3j3^P6dZGF)?+dRE zb(=A0vpk4x*zU``;+9QF?!F(ztX2nhGvtE=%}>^>oyFq3pbP0l+XkK0s(;l literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/question/f01.png b/res/hermit-frames/bare/question/f01.png new file mode 100644 index 0000000000000000000000000000000000000000..e39f30cb11ef11f24fde8ec89d72fb4c7288b5f0 GIT binary patch literal 320 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU}X1naSW-L^Y;2i!6pZR)`yob zY|1%spy9}!2DOZ431KEC=?%PAhDSNt3tJp-96wk2UwoeN^LxdX(*=NLFeo@$zh|lo zP4oS}vuy48?(-Yt!_RHoCtH_Q+m~ULP;ug+zTGyva1mL9(i0mECIzSYZdYm0uyW{R z*u%YH!?TdZGF)?+dRE zb(=A0vpk4x*zU``;+9QF?!F(ztX2nhGvtE=%}>^>oyFq3pbP0l+XkK0s(;l literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/question/f02.png b/res/hermit-frames/bare/question/f02.png new file mode 100644 index 0000000000000000000000000000000000000000..92a8b6e480c1065245b5dc9c3b758ede5c4678b7 GIT binary patch literal 321 zcmV-H0lxl;P)9X8_g$0eh1)E?S)Ywo^xB<7pPHeH#VHoid@8M3&|CMnjo*Y9AApigX z008o{@(vKAF1lelc5L@iYgzvuXIak{|7!f5q;|k~k>g){Zu}F;?0{E`oGT+cbD3dg&-_I5q3JS#ib|d zL(t;DOS%9H6yP1e+u{W@eHog*jQ-pjJHR`DHwy3$;7#@}pt=c$VEayx{SJ^7pm=jr z%q!gi)?rnp0?3!_P&pO5$35WCo}<6VYMwdV1H1!xqX6#!-YCF3000000KmZwt@=(r T;$+iV00000NkvXXu0mjf#*B-5 literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/question/f03.png b/res/hermit-frames/bare/question/f03.png new file mode 100644 index 0000000000000000000000000000000000000000..9f3e6252975ad757efa6e27f3f4b8deadea932cf GIT binary patch literal 320 zcmV-G0l)r9M?kg$0eh1()C&)Ywo^cmc1$O>D8!VHoiz?#E8d`;>7eZa$W{h5!Hn z000?Uc^43)F1lg5c5L@iXIcMV&a$2z{?+9M?kg$0eh1()C&)Ywo^cmc1$O>D8!VHoiz?#E8d`;>7eZa$W{h5!Hn z000?Uc^43)F1lg5c5L@iXIcMV&a$2z{?+9X8_g$0eh1)E?S)Ywo^xB<7pPHeH#VHoid@8M3&|CMnjo*Y9AApigX z0LanGJ3x%O=!WUovE4_lW&L}cWj$N`tMPY|+5zK5j(_pF@lPbP170n1u8i!|MK^p~ zKr%Z(g#x?-Sc!f1|E7Su*!w`XjZJfOi0Iix<%JWoY^``g3dS0Pg_aD8M^_H`%*@>LwV1?K?sCJ3v-|;>}Gl zuXG1khgFpd0NIkAs;6T2xCkBEbM*IE%`=CKkaqxY6yP1e8wGd=000000AP7HxFSwI T<55pq00000NkvXXu0mjfO%97- literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/question/f06.png b/res/hermit-frames/bare/question/f06.png new file mode 100644 index 0000000000000000000000000000000000000000..90f5b087fc569c9097d2982e3a97ec56aadf72f1 GIT binary patch literal 321 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEVC3+0aSW-L^Y*&6P?Ld3>%)wf zo9;BUvi9y}ot+?kV{c<*5N|g90`2L~ang3G@)#k}CwFm&sXwX@^hxy2g z?z|a&vbSrmL|xl|Jo4J+qx+@m)mO?I#69`Zb-uS=X{PLltck^`Da-YCJ-p2{=_aF= z$OG8}UT^mPH)p+bbRPRA)%@2{xo6|NXTM6C7hc63p%-Tu&vA%JBysuYD4D8BbsSMP zA13J?U}CLc|FErs(Ko(rnf~JX?Z@~&@IB~d+QVEixt4o{Uz6gEI^mT4%qgrI`|gN* z_9|~EjR;-I&=7MnPJ4OMcMDFrTg6}gN1QcopS+-)As^%hX`qQfZ!j<r*$cZ9nY4_Pn4kI}g9*pQFdGJ?~n?yo}FM%&iEhZW@b#E4zkZ zLph_^HJjh{f)4p-Z6@esCY^tj@ojsFhTNp6)c%I24@G}}RbgDwCHpSh^ovV1XH;y( zMYe=ihCSRLwq-Je&Te0IR=j?l1AB$|gIi47f8*ioaG*2jJyvK21H$y(lgIJI+7#MC>GhT4?vzv79 QurrA3>FVdQ&MBb@0E#Y!s{jB1 literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/success/f00.png b/res/hermit-frames/bare/success/f00.png new file mode 100644 index 0000000000000000000000000000000000000000..aef7942cfba70e014eba00d2bc4039e1911c90be GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC==R92;Ln`LHy}pt6kbyw!!)Bw| zDZF9}mM99Rbm~mxbecSgH9~pzl2$GkMyE~hesVtjALaf0^W@aI%0SIvaHD7LrXN-^ z?;^L(dZ_lYbiIbu#Oeyyz3QF}tI|YEQ_Ua53pDIzG-q0|^X8T>hPt&McR6ikxX1tP z<%Ip$LRZ~uSSPzU`}m_$){`YmXE7^%Qg@PbEN9MRS)j4%*G|n>r}7-iHm?d`Ot_q) z|N3c3P4D67Kcn{eht18GXt~3?LlA6qfjQVE1{DmGPZ=NBKIy_N5Z}|)&t;ucLK6T@ CL1^3n literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/success/f01.png b/res/hermit-frames/bare/success/f01.png new file mode 100644 index 0000000000000000000000000000000000000000..f71efef8493c4d0e098e216486fc5ce80e6363ff GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC==R92;Ln`LHy}pt6kbyw!!)Bw| zDZF9}mM99Rbm~mxbecSgH9~pzl2$GkMyE~hesVtjALaf0^W@aI%0SIvaC>90SbY0o z+tp#lcU1Pi6LnQC;$Oewy9%en)isOOyUGweD*ZEWlQzr!dHkL_AmSQoZ|)j-~IjMs?WU56LvG2LyUe0@(=^Viua72Q;iSQTGw6xaXnrAT-G@yGywo6 Cwq|kw literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/success/f02.png b/res/hermit-frames/bare/success/f02.png new file mode 100644 index 0000000000000000000000000000000000000000..f71efef8493c4d0e098e216486fc5ce80e6363ff GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC==R92;Ln`LHy}pt6kbyw!!)Bw| zDZF9}mM99Rbm~mxbecSgH9~pzl2$GkMyE~hesVtjALaf0^W@aI%0SIvaC>90SbY0o z+tp#lcU1Pi6LnQC;$Oewy9%en)isOOyUGweD*ZEWlQzr!dHkL_AmSQoZ|)j-~IjMs?WU56LvG2LyUe0@(=^Viua72Q;iSQTGw6xaXnrAT-G@yGywo6 Cwq|kw literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/success/f03.png b/res/hermit-frames/bare/success/f03.png new file mode 100644 index 0000000000000000000000000000000000000000..f71efef8493c4d0e098e216486fc5ce80e6363ff GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC==R92;Ln`LHy}pt6kbyw!!)Bw| zDZF9}mM99Rbm~mxbecSgH9~pzl2$GkMyE~hesVtjALaf0^W@aI%0SIvaC>90SbY0o z+tp#lcU1Pi6LnQC;$Oewy9%en)isOOyUGweD*ZEWlQzr!dHkL_AmSQoZ|)j-~IjMs?WU56LvG2LyUe0@(=^Viua72Q;iSQTGw6xaXnrAT-G@yGywo6 Cwq|kw literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/success/f04.png b/res/hermit-frames/bare/success/f04.png new file mode 100644 index 0000000000000000000000000000000000000000..aef7942cfba70e014eba00d2bc4039e1911c90be GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC==R92;Ln`LHy}pt6kbyw!!)Bw| zDZF9}mM99Rbm~mxbecSgH9~pzl2$GkMyE~hesVtjALaf0^W@aI%0SIvaHD7LrXN-^ z?;^L(dZ_lYbiIbu#Oeyyz3QF}tI|YEQ_Ua53pDIzG-q0|^X8T>hPt&McR6ikxX1tP z<%Ip$LRZ~uSSPzU`}m_$){`YmXE7^%Qg@PbEN9MRS)j4%*G|n>r}7-iHm?d`Ot_q) z|N3c3P4D67Kcn{eht18GXt~3?LlA6qfjQVE1{DmGPZ=NBKIy_N5Z}|)&t;ucLK6T@ CL1^3n literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/success/f05.png b/res/hermit-frames/bare/success/f05.png new file mode 100644 index 0000000000000000000000000000000000000000..cb92a83853327ac2dc7db212314d82c83790bc00 GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC==R92;Ln`LHy}pt6kbyw!!)Bw| zDZF9}mM99Rbm~mxbecSgH9~pzl2$GkMyE~hesVtjALaf0^W>>>l!2N-Am^lCWQ{D} z_cc*x3nqWr6)w_y!gkN1dy`cdLN|Bq+9dr!pX0#Y25H7CcXFbB&6u{YB6eXE!+ZAM zB`4xvYlY@JT<^QMHTmPNrj%VlXBh>53MJVGTSZlZIeMet&O}ahxm7KuDdgUkTFZdp~F?KCnHGOu{x;HE-?V(eFKG^16 zwsXgvzV+e{EBA@jt7o4x-@|%`@s7p;*#o^ycNoELS@E7xE8Y0OLY?-vAik%opUXO@ GgeCw2M`&sQ literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/success/f07.png b/res/hermit-frames/bare/success/f07.png new file mode 100644 index 0000000000000000000000000000000000000000..cb92a83853327ac2dc7db212314d82c83790bc00 GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC==R92;Ln`LHy}pt6kbyw!!)Bw| zDZF9}mM99Rbm~mxbecSgH9~pzl2$GkMyE~hesVtjALaf0^W>>>l!2N-Am^lCWQ{D} z_cc*x3nqWr6)w_y!gkN1dy`cdLN|Bq+9dr!pX0#Y25H7CcXFbB&6u{YB6eXE!+ZAM zB`4xvYlY@JT<^QMHTmPNrj%VlXBh>53M7_Y{V5PG=9@+oSZ}{Srgy#;%x~EJvEyD#Rc3vXxYBTy$oQ#^(L! zwG9~Wu~sYw5tD1;SMOsvd+5J#k5ikbjrikN6=n78=cns1UY$Q%DSR9Iidh0t@4IVa zu3fD&il6&LEo^ACD==vmi0|p@=d#Wzp$PyaM|g1n literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/thinking/f01.png b/res/hermit-frames/bare/thinking/f01.png new file mode 100644 index 0000000000000000000000000000000000000000..6a209e7e947635c458a79b48aae34f54eca144a7 GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=pFCY0Ln`LHy>7_Y{V5PG=9@+oSZ}{Srgy#;%x~EJvEyD#Rc3vXxYBTy$oQ#^(L! zwG9~Wu~sYw5tD1;SMOsvd+5J#k5ikbjrikN6=n78=cns1UY$Q%DSR9Iidh0t@4IVa zu3fD&il6&LEo^ACD==vmi0|p@=d#Wzp$PyaM|g1n literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/thinking/f02.png b/res/hermit-frames/bare/thinking/f02.png new file mode 100644 index 0000000000000000000000000000000000000000..d07394553577d3fc656e3bf6662647afa9190b46 GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=pFCY0Ln`LHy>7^L$U&g>VW^19 zhHrY)#G;~ZRW!#aiEr%cUAmNOTAX6~2C+*K8L1wLs-KN^zxf~9c;Ix(jJr8bKr7_Y6d>Sw@nS}j z&zig4OIxQ+Gke$IyFpMpM@RSSkxRN8*s_JCx!0`$=poxrZZ`;OiVhO3w>x#K4w;-!&`!(^O zJJxjnP1$Evp3byu(RbdAtI-Q=-uWgng&b^uyR2f??#C;?JuAKI|H>^S>#ER-TTf5- ohP?PIw1DvmdKI;Vst0DZB4g8%>k literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/thinking/f04.png b/res/hermit-frames/bare/thinking/f04.png new file mode 100644 index 0000000000000000000000000000000000000000..6a209e7e947635c458a79b48aae34f54eca144a7 GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=pFCY0Ln`LHy>7_Y{V5PG=9@+oSZ}{Srgy#;%x~EJvEyD#Rc3vXxYBTy$oQ#^(L! zwG9~Wu~sYw5tD1;SMOsvd+5J#k5ikbjrikN6=n78=cns1UY$Q%DSR9Iidh0t@4IVa zu3fD&il6&LEo^ACD==vmi0|p@=d#Wzp$PyaM|g1n literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/thinking/f05.png b/res/hermit-frames/bare/thinking/f05.png new file mode 100644 index 0000000000000000000000000000000000000000..a06da2ddda310e3c3c7a26647afa11429238828e GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=pFCY0Ln`LHy>7_Y#Ot~H~;w0vUo-!*ZHFsGUwmDfAMpY>VjXz!B0+oV+a*a%C4Q* zf7t=`Ln`LHy{^c2$U&g>VPk`$ zW3jjjlZwz`-m1=fOC?l|>g%^tv#y4pT$Aj&V&?n)@F%sNTmihRPfX2q(9=+;YRC||@SSz`woS{{TGhYr z$*2Q0aG93esI30 zKf7d}&b8+?W(wj*#SK>MKJb3m_oY90Pb_0N%9x?Hhg+uX_Lg(MAFupex~J&h$u#B= kh@Aqq4qF~^gPl3$1+!bOVcW9ih3i3lPgg&ebxsLQ08r9)-~a#s literal 0 HcmV?d00001 diff --git a/res/hermit-frames/bare/thinking/f07.png b/res/hermit-frames/bare/thinking/f07.png new file mode 100644 index 0000000000000000000000000000000000000000..aa519bd350d4de6764b4398e11303ca0f8014b06 GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=pFCY0Ln`LHy>7_Y6d>Sw@nS}j z&zig4OIxQ+Gke$IyFpMpM@RSSkxRN8*s_JCx!0_ z9CwiX=QUk^{hG|G$ycmGT4w#cw@2x{`z40Zja@M}SxQ)WJMjYY6SZ$MhxmTD_De~A zdg;dbXTqPq^Kl6OvX60X759|mdo_I;w0>;3E8<^VZgchCo^`)H?{;{tjbaMjU9)o{ n*WP$06NOz389)N!&RO4CH(oY03fO*qJxIXQ)z4*}Q$iB}m^64= literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/coding/f00.png b/res/hermit-frames/shell/coding/f00.png new file mode 100644 index 0000000000000000000000000000000000000000..decd0ce70ff82f63409f286dac00ff02ee4a322d GIT binary patch literal 439 zcmV;o0Z9IdP)T6h&_kD;hyr(gGBK#0CkG3PKGKsDV@vgv17k0zwNcBN|~FR%2N(mOO7JzVo?f zlaJ%^yT>mxvoIip5JHGnnGGE{eR$qaSx5@LAMXb93~}W9vc&L!t?qvUN)7J_t>4=L zEg=Ey0@z3Zy8t#4z%GD|Nx;+1Md~_e1PgHCYmpEv;Qn>ny{|!n=`T=|1oI?Viv+WP ze(1i8(uiIKN)8W1tO7v!;em)%poZ`OaBMkgO!zSqV-6CV-wK;LU<^6j-7%;Gm>2(= zy(b&%Y=31skJbSx!^ezA5s*53%(%_Z0TRG2fQVz`dpj6@vGXl0!cvaj2aj0GzBogC3Ldc7Wws zn^K_#{aKwS@3PRl4-n@7OR-aqCxj49K$|&Vt}nY~@b>M)Tu1CUS!{)wNC3M4HWI)t hfKBrPgb<=>egV;AV0Qs7c+CI+002ovPDHLkV1ly{w4(q3 literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/coding/f01.png b/res/hermit-frames/shell/coding/f01.png new file mode 100644 index 0000000000000000000000000000000000000000..decd0ce70ff82f63409f286dac00ff02ee4a322d GIT binary patch literal 439 zcmV;o0Z9IdP)T6h&_kD;hyr(gGBK#0CkG3PKGKsDV@vgv17k0zwNcBN|~FR%2N(mOO7JzVo?f zlaJ%^yT>mxvoIip5JHGnnGGE{eR$qaSx5@LAMXb93~}W9vc&L!t?qvUN)7J_t>4=L zEg=Ey0@z3Zy8t#4z%GD|Nx;+1Md~_e1PgHCYmpEv;Qn>ny{|!n=`T=|1oI?Viv+WP ze(1i8(uiIKN)8W1tO7v!;em)%poZ`OaBMkgO!zSqV-6CV-wK;LU<^6j-7%;Gm>2(= zy(b&%Y=31skJbSx!^ezA5s*53%(%_Z0TRG2fQVz`dpj6@vGXl0!cvaj2aj0GzBogC3Ldc7Wws zn^K_#{aKwS@3PRl4-n@7OR-aqCxj49K$|&Vt}nY~@b>M)Tu1CUS!{)wNC3M4HWI)t hfKBrPgb<=>egV;AV0Qs7c+CI+002ovPDHLkV1ly{w4(q3 literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/coding/f02.png b/res/hermit-frames/shell/coding/f02.png new file mode 100644 index 0000000000000000000000000000000000000000..d26e171bf01c7a62541d89db498ca462919b1269 GIT binary patch literal 443 zcmV;s0Yv_ZP)RECl*0HvH}c%#03e_3Cavmn1N1Egv14j0m=#-D;D8ePL85Bk?h}gUShxR z)HbdA-dD#?r2s+*A%v)vS>J*4r`P?EK~nJh{LtH{kCUz!r&u1a7U0U4A|Y779&li5xomcxjYcD3jotE4@B$&C6ot%Q_F{Z%KI#gV~|+=rLbuR_%McF*N5t7K=lH$ zodQ1m%s!Kib+NxOeU3H*QkIVyk0Ky-`IvF5Ujrn79e|Alumdc`7Cb768hcm%KXnx8)LbT6h&_kD;hyr(gGBK#0CkG3PKGKsDV@vgv17k0zwNcBN|~FR%2N(mOO7JzVo?f zlaJ%^yT>mxvoIip5JHGnnGGE{eR$qaSx5@LAMXb93~}W9vc&L!t?qvUN)7J_t>4=L zEg=Ey0@z3Zy8t#4z%GD|Nx;+1Md~_e1PgHCYmpEv;Qn>ny{|!n=`T=|1oI?Viv+WP ze(1i8(uiIKN)8W1tO7v!;em)%poZ`OaBMkgO!zSqV-6CV-wK;LU<^6j-7%;Gm>2(= zy(b&%Y=31skJbSx!^ezA5s*53%(%_Z0TRG2fQVz`dpj6@vGXl0!cvaj2aj0GzBogC3Ldc7Wws zn^K_#{aKwS@3PRl4-n@7OR-aqCxj49K$|&Vt}nY~@b>M)Tu1CUS!{)wNC3M4HWI)t hfKBrPgb<=>egV;AV0Qs7c+CI+002ovPDHLkV1ly{w4(q3 literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/coding/f04.png b/res/hermit-frames/shell/coding/f04.png new file mode 100644 index 0000000000000000000000000000000000000000..decd0ce70ff82f63409f286dac00ff02ee4a322d GIT binary patch literal 439 zcmV;o0Z9IdP)T6h&_kD;hyr(gGBK#0CkG3PKGKsDV@vgv17k0zwNcBN|~FR%2N(mOO7JzVo?f zlaJ%^yT>mxvoIip5JHGnnGGE{eR$qaSx5@LAMXb93~}W9vc&L!t?qvUN)7J_t>4=L zEg=Ey0@z3Zy8t#4z%GD|Nx;+1Md~_e1PgHCYmpEv;Qn>ny{|!n=`T=|1oI?Viv+WP ze(1i8(uiIKN)8W1tO7v!;em)%poZ`OaBMkgO!zSqV-6CV-wK;LU<^6j-7%;Gm>2(= zy(b&%Y=31skJbSx!^ezA5s*53%(%_Z0TRG2fQVz`dpj6@vGXl0!cvaj2aj0GzBogC3Ldc7Wws zn^K_#{aKwS@3PRl4-n@7OR-aqCxj49K$|&Vt}nY~@b>M)Tu1CUS!{)wNC3M4HWI)t hfKBrPgb<=>egV;AV0Qs7c+CI+002ovPDHLkV1ly{w4(q3 literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/coding/f05.png b/res/hermit-frames/shell/coding/f05.png new file mode 100644 index 0000000000000000000000000000000000000000..4052e469564e0a32a1d9bf7dc7895fb5d4916a5f GIT binary patch literal 441 zcmV;q0Y?6bP)Wx>IJ!1;CcuW{ zU>|I((RrrY4|WQKEblWOL_p~BKI0a@2vPt#02&HF2QchT3&{6IA;6U1off@|h(f@8 zxRv>ETfOd60`yMMa9Ox6qiK8F=QKn!1I%NHQZ7U>14RBlnq5k{ngN=>nZ$%^{>D?F zYYEz4ZvSz&`f|<T7=$N?UZNbSfdY`YAR$sgr~w2uBo!n=;(|m0)W9)HHQ}_DMeJzz-;HM<^Ih_A zyq@{I@j6lfA%qY@w90I3!~Au9oH9rX4sXw+ea5&Ndb7mxfGzilfKtmlLh}#KfR>N| zb^taKzz)Dh0@wlAm;}5(-lon$BUpedUyFob0WTjv-SZkGn7#rvNibgoYms0U&@bJW zQ5w;!K*{BSh*bb6zdR7J3e->@0In@N`IPs$Fy4|m-k8lOJD%{gMn(VZYW02>Kl2Vf%s>;OUtAq0;Wx>IJ!1;CcuW{ zU>|I((RrrY4|WQKEblWOL_p~BKI0a@2vPt#02&HF2QchT3&{6IA;6U1off@|h(f@8 zxRv>ETfOd60`yMMa9Ox6qiK8F=QKn!1I%NHQZ7U>14RBlnq5k{ngN=>nZ$%^{>D?F zYYEz4ZvSz&`f|<6&N4+?*x(e7h<}h|$arDq^I>LBJR6|+MX?IAU&W+ZOZ%!Cz{||RPdCU6$ z(QNCkPN0zt0os%I@$S1pf5Re7m@F!OlvyUQMBMw6 z{Aty->sxGvw;BsCkhJ?Et`f8U+|B>|Gi;b{J#7CH{Z}Sn3Bx*~>wj+i6*#B)=k9ZgD`h;o&fb(~U6&N4+?*x(e7h<}h|$arDq^I>LBJR6|+MX?IAU&W+ZOZ%!Cz{||RPdCU6$ z(QNCkPN0zt0os%I@$S1pf5Re7m@F!OlvyUQMBMw6 z{Aty->sxGvw;BsCkhJ?Et`f8U+|B>|Gi;b{J#7CH{Z}Sn3Bx*~>wj+i6*#B)=k9ZgD`h;o&fb(~UIC^L{9btUHHAPT-VXT(>v>UA-Z=CCS`+t?j&C+N4 zD~|hZase92kTLb-4es{x{=a(Mo(Hf0J)1Gl@LStF9YdRmYu?2(dGWsISX*DfwuOZy zLwo@*oaps9aQJEFE2}FUVh;{oS5k~P@b>iYZ-u-LBX(x2YAic0oId-;U$CyAPlHd5#iB;=zwG3$gLyN5 zt98amD=IB&{^@=p*QnVyplH4C$`ves=gm)h7kmuBW#&X>)JB6(+F+q$BrHijw z%-g;BZIDIm-pmdKI;Vst E00PawhyVZp literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/error/f03.png b/res/hermit-frames/shell/error/f03.png new file mode 100644 index 0000000000000000000000000000000000000000..53157aae6e969caa7803288bd1b3c40d577745e2 GIT binary patch literal 457 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV4UFT;uumf=j|**KW0M#SM`pr zZiQ1ZjC>6&N4+?*x(e7h<}h|$arDq^I>LBJR6|+MX?IAU&W+ZOZ%!Cz{||RPdCU6$ z(QNCkPN0zt0os%I@$S1pf5Re7m@F!OlvyUQMBMw6 z{Aty->sxGvw;BsCkhJ?Et`f8U+|B>|Gi;b{J#7CH{Z}Sn3Bx*~>wj+i6*#B)=k9ZgD`h;o&fb(~U6&N4+?*x(e7h<}h|$arDq^I>LBJR6|+MX?IAU&W+ZOZ%!Cz{||RPdCU6$ z(QNCkPN0zt0os%I@$S1pf5Re7m@F!OlvyUQMBMw6 z{Aty->sxGvw;BsCkhJ?Et`f8U+|B>|Gi;b{J#7CH{Z}Sn3Bx*~>wj+i6*#B)=k9ZgD`h;o&fb(~Ul*AzkVg{D{Cw38;^{CLFp-rx0ILe-}K zD~)$=^I-v+*$^bT^>4wS4>8{(JJ{~`m#?k7Ws&i#sN-R0N@+f)MAi?7tiQd+3se*i z>|)495H3vf)}7n(+CVwdfn$DmOA6!n{MBcl%cykxW3u!VFh3Zh(!qLUVX~z2;?G(S z4F9Z&y0$s%U#ni&4fVCYG;S_`t*L zFYC;%?bs(UH(kxaxJE+XV@GVvCi(u^Ul`tgh&`sek26iPVK=wRsgQ=>at}DgnV!A& zT>t#&jCagQPBUgt7XP+UYKMS;)A{R@>s^l=v|MBX^?ISoLq>)L6=$rTXP54aWLzgM r|Hx`rU-R4rrHt1c8lVIy1ZDTzx8@~sYMvAa@)$f_{an^LB{Ts5=fAmZ literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/error/f06.png b/res/hermit-frames/shell/error/f06.png new file mode 100644 index 0000000000000000000000000000000000000000..9b4aee1637b4c71ef8b5f46a12e179b325077695 GIT binary patch literal 443 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU~KYqaSW-L^LCbF-XQ}4*Y;KA zj~!y#?#jW-&kGY5PNWFxsqbUfj_^$|FUG|TokRK%K6CCK$J6yu`VONjn(YvzB?k- z*PH`ihaQOf5pZNRL*W0z!doL)gE;(MzG!3~;XFT6Lf7L=owkk?qm0G=cZDB>-&UP} z8gb>_eU>}QX%*M!PyVoR_J;d>tM4#vJiPs-`EN@FPsTg33W1B5&YWkQW}ui*J*({2 zIrm)2HIEMMv0r=h`LP2n4B}PQv+jvJWPjfAD8Z@Wuz2D7IT15vFt0Lc?U9{#Vnu^q zW5X-PllOg`Cfe;R{KdO=e;Lb{z$O(N28M<^fzf9Bx~9L5p1u99V~qRlx(&it^c*^>bP0l+XkKJIcFp literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/error/f07.png b/res/hermit-frames/shell/error/f07.png new file mode 100644 index 0000000000000000000000000000000000000000..36346aa2ac227c997ba38066c28b6343937b1458 GIT binary patch literal 458 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV4Udb;uumf=j|**zrzj!uIe3K z-3q5-82K7lT2)z0T`ibd&M~;YQdlD5_=w>l*AzkVg{D{Cw38;^{CLFp-rx0ILe-}K zD~)$=^I-v+*$^bT^>4wS4>8{(JJ{~`m#?k7Ws&i#sN-R0N@+f)MAi?7tiQd+3se*i z>|)495H3vf)}7n(+CVwdfn$DmOA6!n{MBcl%cykxW3u!VFh3Zh(!qLUVX~z2;?G(S z4F9Z&y0$s%U#ni&4fVCYG;S_`t*L zFYC;%?bs(UH(kxaxJE+XV@GVvCi(u^Ul`tgh&`sek26iPVK=wRsgQ=>at}DgnV!A& zT>t#&jCagQPBUgt7XP+UYKMS;)A{R@>s^l=v|MBX^?ISoLq>)L6=$rTXP54aWLzgM r|Hx`rU-R4rrHt1c8lVIy1ZDTzx8@~sYMvAa@)$f_{an^LB{Ts5=fAmZ literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/idle/f00.png b/res/hermit-frames/shell/idle/f00.png new file mode 100644 index 0000000000000000000000000000000000000000..b9fb24bccef199d3e05e1f94c8459220f3d4351c GIT binary patch literal 406 zcmV;H0crk;P)IF@_;S(m4IB!SA^zoodKSZ z0CoU262K0?MgrIY*q8*oKRu+*K_gf|RlXDn!2&j)KlSGlB$)mJB}p(Zf~80>3uu;Z z%E*oAT_ET3K*TNp0Cs>T`{Nq>|gHNIuI#pFTib11w?hIi3(gxPZ2DempMg zZSeW)V9pUcj{8?(31A0cBLVCHY}^YFLI~Gf0LR!<389&7+yDRo07*qoM6N<$f~+N| AtpET3 literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/idle/f01.png b/res/hermit-frames/shell/idle/f01.png new file mode 100644 index 0000000000000000000000000000000000000000..b9fb24bccef199d3e05e1f94c8459220f3d4351c GIT binary patch literal 406 zcmV;H0crk;P)IF@_;S(m4IB!SA^zoodKSZ z0CoU262K0?MgrIY*q8*oKRu+*K_gf|RlXDn!2&j)KlSGlB$)mJB}p(Zf~80>3uu;Z z%E*oAT_ET3K*TNp0Cs>T`{Nq>|gHNIuI#pFTib11w?hIi3(gxPZ2DempMg zZSeW)V9pUcj{8?(31A0cBLVCHY}^YFLI~Gf0LR!<389&7+yDRo07*qoM6N<$f~+N| AtpET3 literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/idle/f02.png b/res/hermit-frames/shell/idle/f02.png new file mode 100644 index 0000000000000000000000000000000000000000..02a2c6f201fe4cab537421f6382e2add92488102 GIT binary patch literal 410 zcmV;L0cHM)P)Nu7Dxe+a)}ht0o0I?)Q}E9NV!C!0BC_DqLEzTw5ye1C3|e=ukHU% zh}Dk%y!DQKSU?CNgb=kd>bo%c_`dvSkQkglm%aV;F~0X_j?DwM+&2PpZN9-b|J@Z( z6B57}Vf z>8_022;K#9ZXO8O1%UjU2Lg715}F5q2g^+^&ASZ7AxNzLC~R5*E)3!6x={TJs9r#_ zSHOki=r>xe>E*?AA8iH1Y(AtOML_K4L+VyP10;YQfQLdsNkE=r*0V(-g&63IW6+oE*z9CcAr47N@5>iPOLX-_{ zaBNKD_Kzvmvg^yA)nlgx!m zEdov)om1T|HlE9x9wK>!b^X~z8~g>&?$NK+(O-1>ulj-OpEQ^KEKYau31F~dkm7P+ zcW`EW!H^Ncv~6ZC`hTn^g~oqS_CmxkA$%pSY#XG5_+lJERZnR&%u3wgH+H850Z|2e&8_japw5A)eF z;*3`c16{1ocwcn}gZ8hu?{oKeU5>kdcaKUa>#Pl-eeN8Jfeg`>{f{4(OV63N{*}zO s$1eYM96+AZV31|-{ldhdsFG!;Cl!3*&s&2Bz|dy!boFyt=akR{0F%(9NB{r; literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/idle/f04.png b/res/hermit-frames/shell/idle/f04.png new file mode 100644 index 0000000000000000000000000000000000000000..a5aeecc5d66fc32c9358320764644c3fc6df116e GIT binary patch literal 404 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV2t*3aSW-L^LEyG&O-(gN9~VJ z?(T38zre_%-}HjTs`-D~1x}HJ-47UcHMtiGuKyaMdCTdu>KD_Kzvmvg^yA)nlgx!m zEdov)om1T|HlE9x9wK>!b^X~z8~g>&?$NK+(O-1>ulj-OpEQ^KEKYau31F~dkm7P+ zcW`EW!H^Ncv~6ZC`hTn^g~oqS_CmxkA$%pSY#XG5_+lJERZnR&%u3wgH+H850Z|2e&8_japw5A)eF z;*3`c16{1ocwcn}gZ8hu?{oKeU5>kdcaKUa>#Pl-eeN8Jfeg`>{f{4(OV63N{*}zO s$1eYM96+AZV31|-{ldhdsFG!;Cl!3*&s&2Bz|dy!boFyt=akR{0F%(9NB{r; literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/idle/f05.png b/res/hermit-frames/shell/idle/f05.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1893c68a4a611763a36b97ea87e55a1c8dbff7 GIT binary patch literal 410 zcmV;L0cHM)P)!CzRdjZ zTZtjVdtW9q2}FbtLI@#N>9rjgynkNq86*bZAFr)_+UPg^nPPbnUG6IZsg^JC&EGf! zEI|S20B9%x9RLjlpaY;$30O{FV&{P2Eubu)iv({0Z`)(_I|m7>TOcP1>P0XY32FiL z(sdcB5xfheTwWw#7Z6Fmyhy+bh_<0WN&? z_R+?Ax{Oul!ES+=d13CSAfef8#B1YYEynxBqxtoV;^GulCR4Iojtqn(~AYVjkGa zxtWZr9bmRQsB?slWByT?0?+}_Pyjjr8uJ1OA%s}w7kS522S_@a82|tP07*qoM6N<$ Ef*NI|5&!@I literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/idle/f06.png b/res/hermit-frames/shell/idle/f06.png new file mode 100644 index 0000000000000000000000000000000000000000..9ab7107c33fe843d6843b7edfa8b1a1a13672735 GIT binary patch literal 386 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEVD$8KaSW-L^LExj&O-(gt^d1r zcetk)FdXbwuxdQF;02GBqw5DHiw5Hgk>x9wPjia)_~#NodCz;5wqxJ-9b4AgCE&!N zs1mwqUE{@nA%BcJ`0d(r)9354<;J%~#JNo0t*CJ2lj^pg(cunW0Ss0QQd|z~4$h1( z7&1bb`cAKWFJrKVb;{G^i3z$1<+Wk+lP4_r#eGD@u~x9hwJFMBZ%M3MhQ)g(#p&XO zH&|T`>FYn*z^C#^E ziVmM=b?V+yH=kUY`+57i1_9NKQ^HOhp0YQ#6-J&}B`y`dz1^z)wap%37pOmgW;X!C c;NVAAXWmebrEI6&fnm$w>FVdQ&MBb@0OPTknE(I) literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/idle/f07.png b/res/hermit-frames/shell/idle/f07.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1893c68a4a611763a36b97ea87e55a1c8dbff7 GIT binary patch literal 410 zcmV;L0cHM)P)!CzRdjZ zTZtjVdtW9q2}FbtLI@#N>9rjgynkNq86*bZAFr)_+UPg^nPPbnUG6IZsg^JC&EGf! zEI|S20B9%x9RLjlpaY;$30O{FV&{P2Eubu)iv({0Z`)(_I|m7>TOcP1>P0XY32FiL z(sdcB5xfheTwWw#7Z6Fmyhy+bh_<0WN&? z_R+?Ax{Oul!ES+=d13CSAfef8#B1YYEynxBqxtoV;^GulCR4Iojtqn(~AYVjkGa zxtWZr9bmRQsB?slWByT?0?+}_Pyjjr8uJ1OA%s}w7kS522S_@a82|tP07*qoM6N<$ Ef*NI|5&!@I literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/question/f00.png b/res/hermit-frames/shell/question/f00.png new file mode 100644 index 0000000000000000000000000000000000000000..bac3855b4ce207de7326ce5f425af0fd5b1fcc6f GIT binary patch literal 486 zcmV@P)0c)9Dg~kr? zAE(cW-zQb764iO{`LF#)vIrrB5JFNVbKN+&d01^zNC>`eFP(l|?AR_2(Y%OGcP)US znm73Bw^l$(FaWdwGz&4sp`xru~y*#vS zC9CGEp`f|?+^=0L%G~*qq+SGLtOT2R5s}BqvvX3_0mN1TRr5ueXXazBxkBi?3&dvr z{_y+Hz5hbk^?g`u=0)nJHz~?I3swQfbS&n(;#~nE?aa4X7-I-AiB`C27f@9|8)BH< zO>F%@Zvn(aQzHZo;3InpHrCPVO!Yh18W7Tam+>G0gf?#*w`t+4GTL3B>3?5kv;nk* z0iXq-i7J5kaw_xXRDEAVQ~@Ah0B8Ydk`KVzEcAUyqOVR+L}YLF?sZJYS^=7~HbVtw z^k;S)yvlrMUj(=YXvA)FJVFRb0@%v=G&x;wgBRtiT1VG$l5d3>09pVV27nfTCiwse cAtY&j0Y6rHH;qhUi~s-t07*qoM6N<$g6VM0_5c6? literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/question/f01.png b/res/hermit-frames/shell/question/f01.png new file mode 100644 index 0000000000000000000000000000000000000000..bac3855b4ce207de7326ce5f425af0fd5b1fcc6f GIT binary patch literal 486 zcmV@P)0c)9Dg~kr? zAE(cW-zQb764iO{`LF#)vIrrB5JFNVbKN+&d01^zNC>`eFP(l|?AR_2(Y%OGcP)US znm73Bw^l$(FaWdwGz&4sp`xru~y*#vS zC9CGEp`f|?+^=0L%G~*qq+SGLtOT2R5s}BqvvX3_0mN1TRr5ueXXazBxkBi?3&dvr z{_y+Hz5hbk^?g`u=0)nJHz~?I3swQfbS&n(;#~nE?aa4X7-I-AiB`C27f@9|8)BH< zO>F%@Zvn(aQzHZo;3InpHrCPVO!Yh18W7Tam+>G0gf?#*w`t+4GTL3B>3?5kv;nk* z0iXq-i7J5kaw_xXRDEAVQ~@Ah0B8Ydk`KVzEcAUyqOVR+L}YLF?sZJYS^=7~HbVtw z^k;S)yvlrMUj(=YXvA)FJVFRb0@%v=G&x;wgBRtiT1VG$l5d3>09pVV27nfTCiwse cAtY&j0Y6rHH;qhUi~s-t07*qoM6N<$g6VM0_5c6? literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/question/f02.png b/res/hermit-frames/shell/question/f02.png new file mode 100644 index 0000000000000000000000000000000000000000..bc2bea4599151c27cc265b78a7d4de14fd3bfbe2 GIT binary patch literal 476 zcmV<20VDp2P)H**ijD-_$1J*JnvWSiB zSG&LC{GU{*N>ulMpOa@tiU=Wu5JHAZ- zT6{xje(M!5Bq#tK01XA81E8S*bO1Ce0rwZDv2(x(7SI%5iiBVRH_Ny7dkGR$w?Ih} zI!{j_dug+HQwo~v&%@5KX+5?+DXLe2J{2z_@-%s|PVCmJK(574>v7e5nqxKz>Qz7_ z@8ZpK|4C4<0yz~gBJwc$Za+3$Jz!t+XFkRItPVLy9Q;;Tbp`m4!|a+wT?P8g-(HaH z3doJ9O)w@O)oZk|jyLDQzk%KYF^kuZ2NMvxc-y$H#%EG=6Lj78CPfSIfdbG0&}1iI zv7X6dJyV|#B|8DTh62z5&~yZ7UNh-4uy=$~?F21x z%5kAhFt&tLQVAjD0&5!2le2aiysTI19NUh=tuQ(O8VW!MKtloO0E7@ifcygUNPR9& Sm7p;I00002qA=|O6Hn4xOrH0DI^Bpx0g;oE_Q5#eKao;rn?qE zU(Hwe>bF)vN-zMl05l8$EdUJzKnp+<6u{l&B(@G1-T_qRhZ2H!0N2m&>+eGdq4x05 z^eAbXH$&0D)z^M)tSEEqgHmu4)LIEP^CBYi@r(7s1_z+A0!;HonP=6f8ncDadKb`Y z-kjSO!mjThX*4e)azFanI$xA|7Nr6z)0)h;!Mg%Pnwf9X7&U~L%u(323j|d_6Es|1 z8=DRC-vZEv>mURV;4^!Tw${<=%=uP)^gWd&P_r1xW184#R zKnp;_0MG)^s0(1RoXTQ34W9Q!T>xDJKMHSharoG3-j`YhguG_bXRNnCN+Cdv{?3l0 zzmBsv`|$DvZGygHyIzkFLIw}4Yd(!n*IVFu`4+6B?K;Un3orn*05l8$EdWjO0T4n+ a()4B18i00002qA=|O6Hn4xOrH0DI^Bpx0g;oE_Q5#eKao;rn?qE zU(Hwe>bF)vN-zMl05l8$EdUJzKnp+<6u{l&B(@G1-T_qRhZ2H!0N2m&>+eGdq4x05 z^eAbXH$&0D)z^M)tSEEqgHmu4)LIEP^CBYi@r(7s1_z+A0!;HonP=6f8ncDadKb`Y z-kjSO!mjThX*4e)azFanI$xA|7Nr6z)0)h;!Mg%Pnwf9X7&U~L%u(323j|d_6Es|1 z8=DRC-vZEv>mURV;4^!Tw${<=%=uP)^gWd&P_r1xW184#R zKnp;_0MG)^s0(1RoXTQ34W9Q!T>xDJKMHSharoG3-j`YhguG_bXRNnCN+Cdv{?3l0 zzmBsv`|$DvZGygHyIzkFLIw}4Yd(!n*IVFu`4+6B?K;Un3orn*05l8$EdWjO0T4n+ a()4B18i0000NWIN?OV$|g(anHCHpdLy?*IRfzh}8CGJ(urC$g`xd3L++d?%G3`}ThM^>O3lT^25%U2N{IWoXN~C$Red zPNpIz#tiNYY;dAY^uXOWE>Z1L7a1i!Cvz^`TF!UV^X9Yl^Y6E+mOg)#y}xwrD(~l( zjiF52US0`nI&C9(vFO*MJsaPs%}CuHu`bIvvx` zE-v3E&tLmEG*(PWTfy~nYzNhGAIP#@ zezoeHT)*m0hF@UssQqTRa-PXok5AsZ_xPnK#yQu0+Ilv#{NiXVF5*ti|0uXL_HW*Q zy}qOK?2pW1XJ~j6__mjHBs`K8nVt zb(`#60jB8~E5T-7MC4^Ovp!hWD-df4runKai{{f@vxT5;g0Y3LYTg`M7s9UghOza6 z$HSks<5gW2!73m&^L_9RU`XagdaMGenRl%MsvgkAe3!dC^ZpL>Fci|fc0O1Dq0QUobu~VdK_7=*@4d;O1Ly(+Knp+z8CSSE|H zdfb=j0@yYT04)Gb_5nn97`i-q?lta9_7%_yK=U^f4Vsf%7xwNxy!2ZuAk`3j%@aZh z$sO3TF&&-${a$xbzo~WforgO?v;Z^=04)Fw13(KPgb+e-`~pQoc)t<*qnH2y002ov JPDHLkV1je{%%}hW literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/question/f07.png b/res/hermit-frames/shell/question/f07.png new file mode 100644 index 0000000000000000000000000000000000000000..5a4a7471d0cd00fe7708b317fc5a30daf41e2dfd GIT binary patch literal 483 zcmV<90UZ8`P)+<>)AL8h_W z_>bG?#P5@`*sAm1i+{(CDnbY$gpgFpTpfGYck@jO3BlLRh0~9VEn8z3&5P)C*8=FO z`3hhC))|lz3;-zj#ttfNn89`M7rsqg`f@~rZO}Sr|7J;#%GUUBtE@P;G=_ z$LHgyssuvuk-Y>P>u^3${SHs@BnVoaDE{3;-Gef3dcc%KM zzLnU_v#NZ)Zma;F0XS4!SoZzDFpKn%q0YK z09Dh~FjB*N7f9JW;IRt;>6-^Uc7Yts1Hj1gP#5!E24f5%7Jn2rbwC$l_;$OX4q$%x z&tMa**3I$3^c<}NLYnufM+qRbd7rw)&j1C$7QjXUumx0E?dDkR=H_(^(F9O6)@1Up zbt};X006V`60`Bryl$a(p`eey$N%db0Gv*QE*B-D>Hr3{04&!`V!~B_V+W)jf(taG zhsAC1S#!1c4s%JW4=iH)tS5vJML?UJACLE!ZE(8YnsxZr(NUN!fQwUjCwxa|PLI@#5tqi&vhEK2CQwE8_&-1jio-WSXPUcuVV9R|VAlKq|_~vgN z0W~23>;P;efE|F11h50JF$s9NzmA=QhPQxS@uf)c7Vx-S?T<^4V0r{fl3<<$OOap} z&@8$sBR7Iqft-s60#*Sa|KfpwRiK380pQ$nkxTI|6JrVztKSNnMt}<`++7!X6X3%4 z;3HaJm)l#@b+ktyX7M5YQ3S*;KBV93CqW5d2Vf%s>;O#`>j@U?iMicEG6I@~wH0}+ z-AXb70AM_tV?3Ih+btY?Pd~-i;lB0(z~7G0=Rbj@MgRi|_0CoT& cgb;$qFEg-Y&e^0e5&!@I07*qoM6N<$f}U))iU0rr literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/success/f02.png b/res/hermit-frames/shell/success/f02.png new file mode 100644 index 0000000000000000000000000000000000000000..ba322707b217c6226e9e7fbe90ef29d805009e92 GIT binary patch literal 434 zcmV;j0ZsmiP)wUjCwxa|PLI@#5tqi&vhEK2CQwE8_&-1jio-WSXPUcuVV9R|VAlKq|_~vgN z0W~23>;P;efE|F11h50JF$s9NzmA=QhPQxS@uf)c7Vx-S?T<^4V0r{fl3<<$OOap} z&@8$sBR7Iqft-s60#*Sa|KfpwRiK380pQ$nkxTI|6JrVztKSNnMt}<`++7!X6X3%4 z;3HaJm)l#@b+ktyX7M5YQ3S*;KBV93CqW5d2Vf%s>;O#`>j@U?iMicEG6I@~wH0}+ z-AXb70AM_tV?3Ih+btY?Pd~-i;lB0(z~7G0=Rbj@MgRi|_0CoT& cgb;$qFEg-Y&e^0e5&!@I07*qoM6N<$f}U))iU0rr literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/success/f03.png b/res/hermit-frames/shell/success/f03.png new file mode 100644 index 0000000000000000000000000000000000000000..ba322707b217c6226e9e7fbe90ef29d805009e92 GIT binary patch literal 434 zcmV;j0ZsmiP)wUjCwxa|PLI@#5tqi&vhEK2CQwE8_&-1jio-WSXPUcuVV9R|VAlKq|_~vgN z0W~23>;P;efE|F11h50JF$s9NzmA=QhPQxS@uf)c7Vx-S?T<^4V0r{fl3<<$OOap} z&@8$sBR7Iqft-s60#*Sa|KfpwRiK380pQ$nkxTI|6JrVztKSNnMt}<`++7!X6X3%4 z;3HaJm)l#@b+ktyX7M5YQ3S*;KBV93CqW5d2Vf%s>;O#`>j@U?iMicEG6I@~wH0}+ z-AXb70AM_tV?3Ih+btY?Pd~-i;lB0(z~7G0=Rbj@MgRi|_0CoT& cgb;$qFEg-Y&e^0e5&!@I07*qoM6N<$f}U))iU0rr literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/success/f04.png b/res/hermit-frames/shell/success/f04.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6211591bb3819845cc68f07e201e2e93e2a1ac GIT binary patch literal 433 zcmV;i0Z#sjP)Gef3dcc%KM zzLnU_v#NZ)Zma;F0XS4!SoZzDFpKn%q0YK z09Dh~FjB*N7f9JW;IRt;>6-^Uc7Yts1Hj1gP#5!E24f5%7Jn2rbwC$l_;$OX4q$%x z&tMa**3I$3^c<}NLYnufM+qRbd7rw)&j1C$7QjXUumx0E?dDkR=H_(^(F9O6)@1Up zbt};X006V`60`Bryl$a(p`eey$N%db0Gv*QE*B-D>Hr3{04&!`V!~B_V+W)jf(taG zhsAC1S#!1c4s%JW4=iH)tS5vJML?UJACLE!ZE(8YnsxZr(NUN!fQf2-0V z!~d^oCSd{?V~jCos?3H4uAW}^=M0j9-{;4{K0{peeOY38fXm$xP-^*x(EPn;z?856 zbpSOMpbntM0@MN2Py$}=7pZf|2o}(kuSG(zfc5+L;d2cVsJ}o>66lLyEfQz}?b2-- zr4hXflw2N&SOtLc%L5UsKn>*q;F3A$l=7!s7;}(F2}s4y>@(S1*ZW)QxX}b0`*l;6 zZ{}kpNFIh#myem(PlM_JYAiq~NzR>)We@Dm| zW6YG1EgNrlH-}~R;p2h1CJnSb#c!8VgVdFvb{Tbo>Ep=x44xNzpyBMgZc`MJTAEd#wC$evvy6G)|fWuilNR=P%m3clocA+YUL;Vs8lD zy5v`&=!Mq*t6D^FA6pgW7T(0~475+CA*x~45~heX>Je*ZhqrGlYfE8TA_l_dmiM> z{H)x0Bi-mASHitpOB~ki>NPyJVuG&pd;ap@dyiedZoYQ$dkZ^mn0GgTe~DWM4fV6nNA literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/success/f07.png b/res/hermit-frames/shell/success/f07.png new file mode 100644 index 0000000000000000000000000000000000000000..688a095a11a988f7fdbe9497162a6017451048ad GIT binary patch literal 416 zcmV;R0bl-!P)f2-0V z!~d^oCSd{?V~jCos?3H4uAW}^=M0j9-{;4{K0{peeOY38fXm$xP-^*x(EPn;z?856 zbpSOMpbntM0@MN2Py$}=7pZf|2o}(kuSG(zfc5+L;d2cVsJ}o>66lLyEfQz}?b2-- zr4hXflw2N&SOtLc%L5UsKn>*q;F3A$l=7!s7;}(F2}s4y>@(S1*ZW)QxX}b0`*l;6 zZ{}kpNFIh#myem(PlM_JYAiq~NzR>)We@Dm| zW6YG1EgNrlH-}~R;p2h1CJnSb#c!8VgVdFvb{Tbo>Ep=x4R%LA*eYU~Oxuprv3Vh&PC5(C8U#ts;VrU>QZPU>Q7uwO|{oECbHM&Oftx zJ2UTtK*%z)@4fx!pCt?+gb+fAS{b!n*uB16u2V<~zHTmC{j{;+`!PrJfKB(m0CF|I zB2>S(0%}45um!MD0Biwl6aZTQ8&d$c=ZC3v&IqIRMwR8%AzK?*ciS2O@R>Ab;~f#4b>Rc>vh74C-ROOJf{Dh}FLen<}6SLwLGf zPz5k={yBP1HrL+r)HIG(0V&PL%tr|zwfUHNtM35{fGvQH0$>YpnJp%mEhgsc76ugn z0|meqz^3{DyhlpI0zuB^HWX?x|1(ux|WtcG5h}i>H_9X8d-)Z5o%= v67Y3-H%JTBF98L>7QjXUumuPqgdp+yY5>o00000NkvXXu0mjfR;;;n literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/thinking/f01.png b/res/hermit-frames/shell/thinking/f01.png new file mode 100644 index 0000000000000000000000000000000000000000..8bc21b15161dd69fa2c93dbea303256949d13d4c GIT binary patch literal 453 zcmV;$0XqJPP)R%LA*eYU~Oxuprv3Vh&PC5(C8U#ts;VrU>QZPU>Q7uwO|{oECbHM&Oftx zJ2UTtK*%z)@4fx!pCt?+gb+fAS{b!n*uB16u2V<~zHTmC{j{;+`!PrJfKB(m0CF|I zB2>S(0%}45um!MD0Biwl6aZTQ8&d$c=ZC3v&IqIRMwR8%AzK?*ciS2O@R>Ab;~f#4b>Rc>vh74C-ROOJf{Dh}FLen<}6SLwLGf zPz5k={yBP1HrL+r)HIG(0V&PL%tr|zwfUHNtM35{fGvQH0$>YpnJp%mEhgsc76ugn z0|meqz^3{DyhlpI0zuB^HWX?x|1(ux|WtcG5h}i>H_9X8d-)Z5o%= v67Y3-H%JTBF98L>7QjXUumuPqgdp+yY5>o00000NkvXXu0mjfR;;;n literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/thinking/f02.png b/res/hermit-frames/shell/thinking/f02.png new file mode 100644 index 0000000000000000000000000000000000000000..9aec75c1a52c6dd9c5160b68ff08d54e01ba4367 GIT binary patch literal 461 zcmV;;0W$uHP)b323@`!g0BlSEJHUU&(;>#w zq58a(RPzfE|EM^#TmTFskMcff-=Zb0zdv00000NkvXXu0mjf DtRKh< literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/thinking/f03.png b/res/hermit-frames/shell/thinking/f03.png new file mode 100644 index 0000000000000000000000000000000000000000..562994a0cddca7d78e3aaf9ee01a14f7aadde465 GIT binary patch literal 454 zcmV;%0XhDOP)R%LA*eYU~Oxuprw#T5N{CApwTnfT15gjf~AUH!7_LRYr!^FSq9yOoquNc z&Fs7n0wK%HzW4T@f0i(S5JCtcN@dh^VfX5G)u)gcd|qEP`e|aL?#CR>12*0N0?5_; z8ejdj6;KiifGvQH0$>YZqX5_f*q8#iIXjH4gNAniYx6@1!8?G<#as1#2qBo3z>q>P zH^HHVU=F}F?S_#X!Mi}t=7E4+0Lb4w5U>jj!8`zLS_ZW--=;C95MuGK!lnvnLkdr~ z4XOa<%|Awu(dOD)O-$ox6%f;W$b6IlVw(?{xA-2Q0N4W9C;+yAzs#3Y%$HO1bzhPT zpl=iaTL7El1E@Vx()A&UuHFOzU}yI3bxfwV0xV~3SbXnqRUZp9qd&8QC{^YfA0Iu( zIV9BwzNOj~Nr(vH>*ctrq^v3+6^ajl`j?n5Zn?)o6~Maj{pm^dG|wNNf}8Q{-L`35 wR!g9+%ez5ZD1Hej0JZ=&3VR%LA*eYU~Oxuprv3Vh&PC5(C8U#ts;VrU>QZPU>Q7uwO|{oECbHM&Oftx zJ2UTtK*%z)@4fx!pCt?+gb+fAS{b!n*uB16u2V<~zHTmC{j{;+`!PrJfKB(m0CF|I zB2>S(0%}45um!MD0Biwl6aZTQ8&d$c=ZC3v&IqIRMwR8%AzK?*ciS2O@R>Ab;~f#4b>Rc>vh74C-ROOJf{Dh}FLen<}6SLwLGf zPz5k={yBP1HrL+r)HIG(0V&PL%tr|zwfUHNtM35{fGvQH0$>YpnJp%mEhgsc76ugn z0|meqz^3{DyhlpI0zuB^HWX?x|1(ux|WtcG5h}i>H_9X8d-)Z5o%= v67Y3-H%JTBF98L>7QjXUumuPqgdp+yY5>o00000NkvXXu0mjfR;;;n literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/thinking/f05.png b/res/hermit-frames/shell/thinking/f05.png new file mode 100644 index 0000000000000000000000000000000000000000..128dfbbe0ce624bf4619668c782c80520f6867e5 GIT binary patch literal 470 zcmV;{0V)28P)_$7RM0YDBZxjhd1rg?)yRBaOXmda8wx-N zK;vFOYwrS9?oOYh&THwM04+b~{p$GW^H+WS)-7Q9%tVXz%`IeHZGPxCLMdK~>K$-= zu~1K5>$BZ9-|v^zL|Fv5-v!(Y$Z8j;w|gqwcR(=$EDN9ra2I&FxvJWEIGdYu#Ew%; r2Uwv1bO1CIfDVAhy#PW8A)fgIuH0XQ^CmqF00000NkvXXu0mjfIp)1F literal 0 HcmV?d00001 diff --git a/res/hermit-frames/shell/thinking/f07.png b/res/hermit-frames/shell/thinking/f07.png new file mode 100644 index 0000000000000000000000000000000000000000..c8cfbf177ce2845ea73de7019a60737fabdd5e2a GIT binary patch literal 471 zcmV;|0Vw{7P)_$7RM0YDBZxjhdE$yld==B$km z+Ap{N@C`3A=a5trd=0mQN18bT)^{J$5)gxr$Gu9TMnLhqK=A@vw++t29PY8;2tfVu z%|X9H$;JQx literal 0 HcmV?d00001 diff --git a/res/hermit-icons.ttf b/res/hermit-icons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8ca6ee97d824f5e5f72e61a4c36ffbf84ffb9a6d GIT binary patch literal 38216 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 literal 0 HcmV?d00001 diff --git a/scripts/bootstrap_linux.sh b/scripts/bootstrap_linux.sh index 70c5a20..be51e81 100755 --- a/scripts/bootstrap_linux.sh +++ b/scripts/bootstrap_linux.sh @@ -49,6 +49,35 @@ install_bin() { ln -sf hermit "$prefix/bin/cst-agent" } +install_font() { + local prefix="$1" dir="$2" + local ttf="" + if [[ -f "$dir/res/hermit-icons.ttf" ]]; then + ttf="$dir/res/hermit-icons.ttf" + elif [[ -f "$dir/share/hermit/hermit-icons.ttf" ]]; then + ttf="$dir/share/hermit/hermit-icons.ttf" + elif [[ -f "$dir/hermit-icons.ttf" ]]; then + ttf="$dir/hermit-icons.ttf" + fi + [[ -n "$ttf" ]] || { + echo "warning: hermit-icons.ttf not found; run 'make hermit-font' in the tree" >&2 + return 0 + } + mkdir -p "$prefix/share/hermit" "$prefix/share/fonts/hermit" \ + "${HOME:-/tmp}/.local/share/fonts" + install -m 0644 "$ttf" "$prefix/share/hermit/hermit-icons.ttf" + install -m 0644 "$ttf" "$prefix/share/fonts/hermit/hermit-icons.ttf" + install -m 0644 "$ttf" "${HOME}/.local/share/fonts/hermit-icons.ttf" + if [[ -d "$dir/res/hermit-frames" ]]; then + mkdir -p "$prefix/share/hermit/frames" + cp -a "$dir/res/hermit-frames/." "$prefix/share/hermit/frames/" + fi + if command -v fc-cache >/dev/null 2>&1; then + fc-cache -f "${HOME}/.local/share/fonts" >/dev/null 2>&1 || true + fi + echo "==> installed Hermit Icons font" +} + install_helpers() { local prefix="$1" dir="$2" mkdir -p "$prefix/bin" @@ -123,8 +152,12 @@ install_from_release() { bin="$(find "$tmp" -type f \( -name hermit -o -name cst-agent \) | head -1)" [[ -x "$bin" ]] || { echo "tarball missing hermit/cst-agent" >&2; rm -rf "$tmp"; return 1; } install_bin "$prefix" "$bin" + # Font may live next to the binary inside the tarball. + local rel_dir + rel_dir="$(dirname "$bin")" + install_font "$prefix" "$rel_dir" rm -rf "$tmp" - echo "==> installed release $ver ($arch) -> $prefix/bin/cst-agent" + echo "==> installed release $ver ($arch) -> $prefix/bin/hermit" } main() { @@ -211,8 +244,11 @@ main() { run_make -j"$jobs" check >/dev/null 2>&1 || echo "warning: some tests failed (run 'make check')" fi - echo "==> install to $prefix/bin" - install_bin "$prefix" "$dir/cst-agent" + echo "==> bake Hermit Icons font (if tooling present)" + run_make hermit-font >/dev/null 2>&1 || true + echo "==> install to $prefix/bin (+ font)" + install_bin "$prefix" "$dir/hermit" + install_font "$prefix" "$dir" install_helpers "$prefix" "$dir" echo diff --git a/scripts/build_hermit_font.py b/scripts/build_hermit_font.py index 82483e1..aee437b 100755 --- a/scripts/build_hermit_font.py +++ b/scripts/build_hermit_font.py @@ -1,38 +1,121 @@ #!/usr/bin/env python3 -"""Batch-convert icons/agent-motion SVG states into a color emoji-style font. +"""Bake Hermit animation frames (HTML-kit states) into PNG + sbix color TTF. + +Geometry/colors follow icons/agent-motion. Animation mirrors the HTML kit: +idle breath/blink, ask tilt+?, thinking look+claw, coding >_, success smile, +error mark — as 8 discrete frames per state × bare/shell. Outputs: - res/hermit-icons.ttf (CBDT/CBLC bitmap glyphs when fonttools works) - .gen/hermit_font_map.inc (PUA codepoint table for the TUI) + res/hermit-frames/{bare,shell}/{state}/fNN.png + res/hermit-icons.ttf + .gen/hermit_font_map.inc -PUA layout matches cst_hermit_glyph.h: - U+E700 + (shell * N_STATE + state) * N_FRAME + frame - -Requires (optional): rsvg-convert or cairosvg, pillow, fonttools. -Without them, still writes hermit_font_map.inc so FONT mode can emit codepoints. + nix-shell -p python3Packages.pillow python3Packages.fonttools --run \\ + 'python3 scripts/build_hermit_font.py' """ from __future__ import annotations -import struct -import subprocess +import math import sys -import tempfile from pathlib import Path ROOT = Path(__file__).resolve().parents[1] -ICONS = ROOT / "icons" / "agent-motion" / "icons" +OUT_FRAMES = ROOT / "res" / "hermit-frames" OUT_TTF = ROOT / "res" / "hermit-icons.ttf" OUT_INC = ROOT / ".gen" / "hermit_font_map.inc" STATES = ["idle", "question", "thinking", "coding", "success", "error"] N_FRAME = 8 PUA_BASE = 0xE700 +SIZE = 64 + +SHELL = (0x27, 0x7F, 0x94, 255) +PROMPT = (0xA9, 0xD9, 0xD7, 255) +BODY = (0xF6, 0x80, 0x42, 255) +EYE = (0x99, 0x57, 0xAD, 255) +LEG = (0xA4, 0x57, 0x4F, 255) +MARK = (0xE9, 0xBA, 0x47, 255) +TRANS = (0, 0, 0, 0) def codepoint(shell: int, state: int, frame: int) -> int: return PUA_BASE + (shell * len(STATES) + state) * N_FRAME + frame +def draw_icon(state: str, frame: int, with_shell: bool): + from PIL import Image, ImageDraw + + im = Image.new("RGBA", (SIZE, SIZE), TRANS) + d = ImageDraw.Draw(im) + phase = frame * (math.pi * 2 / N_FRAME) + t = frame / max(N_FRAME - 1, 1) + + cx, cy = (40, 38) if with_shell else (32, 30) + breath = math.sin(phase) * 1.2 + rot = 0.0 + if state == "question": + rot = 6 * math.sin(phase * 0.5) + elif state == "coding": + rot = 1.4 * math.sin(phase * 2) + elif state == "error": + rot = 3 * math.sin(phase * 3) if 0.7 < t < 0.95 else 0 + elif state == "success": + breath -= abs(math.sin(phase)) * 2.0 + elif state == "thinking": + rot = -2 + + if with_shell: + d.rounded_rectangle([4, 8, 52, 44], radius=3, fill=SHELL) + d.line([(12, 18), (16, 22), (12, 26)], fill=PROMPT, width=2) + d.line([(22, 27), (30, 27)], fill=PROMPT, width=2) + + ly = cy + 14 + breath * 0.3 + d.line([(cx - 9, ly), (cx - 9, ly + 6)], fill=LEG, width=3) + d.line([(cx + 9, ly), (cx + 9, ly + 6)], fill=LEG, width=3) + + bw, bh = 22, 15 + bx0 = cx - bw // 2 + by0 = int(cy - bh // 2 + breath) + shift = int(rot * 0.3) + d.rounded_rectangle( + [bx0 + shift, by0, bx0 + bw + shift, by0 + bh], radius=3, fill=BODY + ) + + blink = state in ("idle", "question") and 3 <= frame <= 4 + ey = by0 + (7 if not blink else 9) + eh = 1 if blink else 4 + bx0s = bx0 + shift + + if state == "coding": + d.line([(bx0s + 4, ey), (bx0s + 7, ey + 2), (bx0s + 4, ey + 4)], fill=EYE, width=2) + d.line([(bx0s + 12, ey + 3), (bx0s + 17, ey + 3)], fill=EYE, width=2) + elif state == "success": + d.arc([bx0s + 4, ey, bx0s + 9, ey + 5], 200, 340, fill=EYE, width=2) + d.arc([bx0s + 12, ey, bx0s + 17, ey + 5], 200, 340, fill=EYE, width=2) + elif state == "error": + d.line([(bx0s + 4, ey), (bx0s + 8, ey + 2)], fill=EYE, width=2) + d.line([(bx0s + 14, ey + 2), (bx0s + 18, ey)], fill=EYE, width=2) + elif state == "thinking": + look = 1 if frame % 4 >= 2 else 0 + d.line([(bx0s + 6 + look, ey), (bx0s + 6 + look, ey + eh)], fill=EYE, width=2) + d.line([(bx0s + 14 + look, ey), (bx0s + 14 + look, ey + eh)], fill=EYE, width=2) + else: + d.line([(bx0s + 6, ey), (bx0s + 6, ey + eh)], fill=EYE, width=2) + d.line([(bx0s + 14, ey), (bx0s + 14, ey + eh)], fill=EYE, width=2) + + if state == "question": + d.arc([bx0s + bw - 2, by0 - 10, bx0s + bw + 8, by0], 200, 40, fill=EYE, width=2) + d.point((bx0s + bw + 3, by0 + 2), fill=EYE) + elif state == "thinking": + d.line([(bx0s + bw - 2, by0 + bh - 2), (bx0s + bw - 2, by0 + bh + 5)], fill=MARK, width=3) + d.line([(bx0s + bw - 2, by0 + bh + 5), (bx0s + bw - 8, by0 + bh + 5)], fill=MARK, width=3) + elif state == "error": + d.line([(bx0s + bw + 2, by0 - 6), (bx0s + bw + 2, by0)], fill=EYE, width=2) + d.point((bx0s + bw + 2, by0 + 4), fill=EYE) + + return im + + def write_map_inc() -> None: OUT_INC.parent.mkdir(parents=True, exist_ok=True) lines = [ @@ -40,162 +123,109 @@ def write_map_inc() -> None: f"#define HERMIT_FONT_PUA_BASE_GEN 0x{PUA_BASE:X}", f"#define HERMIT_FONT_FRAMES_GEN {N_FRAME}", f"#define HERMIT_FONT_STATES_GEN {len(STATES)}", - "/* state names: " + ", ".join(STATES) + " */", - "", + "static const char *const HERMIT_FONT_STATE_NAMES[] = {", ] + for s in STATES: + lines.append(f' "{s}",') + lines.append("};") OUT_INC.write_text("\n".join(lines) + "\n", encoding="utf-8") print(f"wrote {OUT_INC}") -def render_svg(svg: Path, png: Path, size: int) -> bool: - png.parent.mkdir(parents=True, exist_ok=True) +def build_sbix_font(images: dict[int, object]) -> bool: try: - if subprocess.call( - ["rsvg-convert", "-w", str(size), "-h", str(size), "-o", str(png), str(svg)], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) == 0: - return png.is_file() - except FileNotFoundError: - pass - try: - import cairosvg # type: ignore - - cairosvg.svg2png(url=str(svg), write_to=str(png), output_width=size, output_height=size) - return png.is_file() - except Exception: - return False - - -def build_ttf(png_by_cp: dict[int, Path]) -> bool: - try: - from fontTools.fontBuilder import FontBuilder # type: ignore - from fontTools.ttLib import TTFont # type: ignore - from fontTools.ttLib.tables.E_B_D_T_ import table_E_B_D_T_ # noqa: F401 - from fontTools.ttLib.tables.C_B_D_T_ import table_C_B_D_T_ # noqa: F401 - from fontTools.ttLib.tables.C_B_L_C_ import table_C_B_L_C_ # noqa: F401 - from fontTools.ttLib.tables._g_l_y_f import Glyph # type: ignore - from fontTools.pens.ttGlyphPen import TTGlyphPen # type: ignore + from fontTools.fontBuilder import FontBuilder + from fontTools.pens.ttGlyphPen import TTGlyphPen + from fontTools.ttLib import newTable + from fontTools.ttLib.tables.sbixGlyph import Glyph as SbixGlyph + from fontTools.ttLib.tables.sbixStrike import Strike except Exception as e: - print(f"fonttools CBDT path unavailable: {e}", file=sys.stderr) + print(f"fontTools missing: {e}", file=sys.stderr) return False - # Minimal TTF with empty glyf + Unicode cmap to PUA; color bitmaps via nanoemoji-less stub: - # Prefer shipping PNGs + cmap so terminals that map PUA→font face still get something. - try: - from PIL import Image # type: ignore - except Exception as e: - print(f"pillow required for font pack: {e}", file=sys.stderr) - return False + cps = sorted(images) + glyph_order = [".notdef"] + [f"u{cp:04X}" for cp in cps] + cmap = {cp: f"u{cp:04X}" for cp in cps} - # Simplified: write a placeholder TTF that only has cmap entries; bitmaps - # need full CBDT authoring. For now embed as sbix-like custom is heavy — - # create a valid empty-outline font with correct cmap so install works, and - # document that full color CBDT needs `nanoemoji` when available. - glyph_order = [".notdef"] - cmap = {} - for cp, png in sorted(png_by_cp.items()): - name = f"u{cp:04X}" - glyph_order.append(name) - cmap[cp] = name - - fb = FontBuilder(1024, isTTF=True) + fb = FontBuilder(SIZE, isTTF=True) fb.setupGlyphOrder(glyph_order) fb.setupCharacterMap(cmap) pen = TTGlyphPen(None) empty = pen.glyph() - glyphs = {".notdef": empty} - for name in glyph_order[1:]: - glyphs[name] = empty - fb.setupGlyf(glyphs) - fb.setupHorizontalMetrics({n: (1024, 0) for n in glyph_order}) - fb.setupHorizontalHeader(ascent=1024, descent=0) + fb.setupGlyf({n: empty for n in glyph_order}) + fb.setupHorizontalMetrics({n: (SIZE, 0) for n in glyph_order}) + fb.setupHorizontalHeader(ascent=SIZE, descent=0) fb.setupNameTable( { "familyName": "Hermit Icons", "styleName": "Regular", - "uniqueFontIdentifier": "Hermit Icons", + "uniqueFontIdentifier": "Hermit Icons 1.0", "fullName": "Hermit Icons", "psName": "HermitIcons", - "version": "0.10", + "version": "Version 1.000", } ) - fb.setupOS2() + fb.setupOS2(sTypoAscender=SIZE, sTypoDescender=0, usWinAscent=SIZE, usWinDescent=0) fb.setupPost() + font = fb.font + + import io + + strike = Strike(ppem=SIZE, resolution=72) + strike.glyphs = {} + for name in glyph_order: + if name == ".notdef": + continue + cp = int(name[1:], 16) + im = images[cp] + bio = io.BytesIO() + im.save(bio, format="PNG") + g = SbixGlyph() + g.glyphName = name + g.originOffsetX = 0 + g.originOffsetY = 0 + g.graphicType = "png " + g.imageData = bio.getvalue() + strike.glyphs[name] = g + + sbix = newTable("sbix") + sbix.version = 1 + sbix.flags = 1 + sbix.strikes = {SIZE: strike} + font["sbix"] = sbix + OUT_TTF.parent.mkdir(parents=True, exist_ok=True) - fb.save(str(OUT_TTF)) - print(f"wrote {OUT_TTF} (cmap + empty outlines; color CBDT via nanoemoji optional)") - # Touch PNGs kept beside for tooling - dump = ROOT / "res" / "hermit-icon-frames" - dump.mkdir(parents=True, exist_ok=True) - for cp, png in png_by_cp.items(): - target = dump / f"u{cp:04X}.png" - target.write_bytes(png.read_bytes()) - print(f"wrote {len(png_by_cp)} frames under {dump}") + font.save(str(OUT_TTF)) + print(f"wrote {OUT_TTF} (sbix, {len(images)} glyphs)") return True -def try_nanoemoji(svgs: list[Path]) -> bool: - """If nanoemoji is installed, build a proper color font from SVGs.""" - if not svgs: - return False - try: - with tempfile.TemporaryDirectory() as td: - td = Path(td) - for i, s in enumerate(svgs): - (td / f"{i:03d}.svg").write_bytes(s.read_bytes()) - out = td / "HermitIcons.ttf" - rc = subprocess.call( - ["nanoemoji", f"--output_file={out}", *sorted(str(p) for p in td.glob("*.svg"))], - cwd=str(td), - ) - if rc == 0 and out.is_file(): - OUT_TTF.parent.mkdir(parents=True, exist_ok=True) - OUT_TTF.write_bytes(out.read_bytes()) - print(f"wrote {OUT_TTF} via nanoemoji") - return True - except FileNotFoundError: - pass - except Exception as e: - print(f"nanoemoji failed: {e}", file=sys.stderr) - return False - - def main() -> int: - write_map_inc() - if not ICONS.is_dir(): - print(f"missing {ICONS}", file=sys.stderr) - return 1 - - svg_list: list[Path] = [] - png_by_cp: dict[int, Path] = {} - with tempfile.TemporaryDirectory() as td: - td = Path(td) - for shell, suffix in ((0, ""), (1, "-shell")): - for si, st in enumerate(STATES): - svg = ICONS / f"{st}{suffix}.svg" - if not svg.is_file(): - print(f"skip missing {svg.name}", file=sys.stderr) - continue - svg_list.append(svg) - # One master frame per state for now; duplicate across N_FRAME. - png = td / f"{shell}_{st}.png" - if not render_svg(svg, png, 64): - print(f"render failed: {svg.name}", file=sys.stderr) - continue - for fr in range(N_FRAME): - png_by_cp[codepoint(shell, si, fr)] = png - - if try_nanoemoji(svg_list): - return 0 - if png_by_cp and build_ttf(png_by_cp): - return 0 + try: + from PIL import Image # noqa: F401 + except ImportError: print( - "font binary not built (install rsvg-convert/cairosvg + pillow + fonttools " - "or nanoemoji). Map .inc is ready for PUA emission.", + "need Pillow: nix-shell -p python3Packages.pillow python3Packages.fonttools", file=sys.stderr, ) - return 0 # map is enough for compile + return 1 + + write_map_inc() + images: dict[int, object] = {} + for shell in (0, 1): + for si, st in enumerate(STATES): + d = OUT_FRAMES / ("shell" if shell else "bare") / st + d.mkdir(parents=True, exist_ok=True) + for fr in range(N_FRAME): + im = draw_icon(st, fr, with_shell=bool(shell)) + im.save(d / f"f{fr:02d}.png") + images[codepoint(shell, si, fr)] = im + print(f"wrote {len(images)} PNGs under {OUT_FRAMES}") + + if not build_sbix_font(images): + print("TTF not written — PNGs ready; install fonttools for the font", file=sys.stderr) + return 1 + return 0 if __name__ == "__main__": diff --git a/scripts/package_linux.sh b/scripts/package_linux.sh index 71975bc..947faab 100755 --- a/scripts/package_linux.sh +++ b/scripts/package_linux.sh @@ -59,6 +59,20 @@ ln -sfr "$rel/hermit" "$rel/cst-agent" 2>/dev/null || ln -sf hermit "$rel/cst-ag "${T}strip" "$rel/hermit" cp -f README.md "$rel/README.md" echo "$ver" >"$rel/VERSION" +# Ship icon font + animation frames with the release tarball. +if [[ ! -f res/hermit-icons.ttf ]]; then + python3 scripts/build_hermit_font.py >/dev/null 2>&1 || true +fi +if [[ -f res/hermit-icons.ttf ]]; then + mkdir -p "$rel/share/hermit" "$rel/share/fonts/hermit" + cp -f res/hermit-icons.ttf "$rel/hermit-icons.ttf" + cp -f res/hermit-icons.ttf "$rel/share/hermit/hermit-icons.ttf" + cp -f res/hermit-icons.ttf "$rel/share/fonts/hermit/hermit-icons.ttf" +fi +if [[ -d res/hermit-frames ]]; then + mkdir -p "$rel/share/hermit/frames" + cp -a res/hermit-frames/. "$rel/share/hermit/frames/" +fi tgz="$(cst_pack_tgz dist "$name")" # Compat alias package name for one release cycle cp -f "$tgz" "dist/cst-agent-$ver-linux-$ARCH.tar.gz" diff --git a/src/hermit_glyph.c b/src/hermit_glyph.c index 9c7df97..14274ad 100644 --- a/src/hermit_glyph.c +++ b/src/hermit_glyph.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include "cst_hermit_glyph.h" +#include "os_compat.h" #include #include @@ -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; } diff --git a/src/tui.c b/src/tui.c index 32afa40..7331c7b 100644 --- a/src/tui.c +++ b/src/tui.c @@ -2721,6 +2721,8 @@ static int hermit_role_pair(int role) { return COLORS >= 256 ? CP_MUTED : CP_ERROR; /* brick legs */ case 4: return CP_SGR_CYAN; /* >_ */ + case 5: + return CP_ASSIST; /* ? ! claw mark */ default: return CP_MUTED; } @@ -2743,15 +2745,31 @@ static void draw_hermit_icon(WINDOW *win, int y0, int x0, hermit_state_t st, } } const char *lines[8]; - int n = hermit_shell_art(st, with_shell, g_ascii, lines, 8); + int n = hermit_shell_art_frame(st, with_shell, g_ascii, frame, lines, 8); for (int r = 0; r < n; r++) { const char *row = lines[r]; for (int c = 0; row[c]; c++) { - if (row[c] == ' ') + char raw = row[c]; + if (raw == ' ') 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); + char draw = (raw == '@') ? '#' : raw; + int role = 0; + if (raw == '@') + role = 1; + else if (raw == '#' ) + role = (with_shell && r <= 2) ? 1 : 0; + else if (raw == '>' || raw == '_') + role = 4; + else if (raw == ':' || raw == '=' || raw == '^' || raw == 'x') + role = 2; + else if (raw == '|') + role = 3; + else if (raw == '?' || raw == '!' || raw == '~') + role = 5; + else + role = hermit_shell_cell_role(st, with_shell, r, c); + char ch[2] = {draw, 0}; + draw_ansi_at(win, y0 + r, x0 + c, 1, ch, hermit_role_pair(role), 0, 0); } } } @@ -2804,10 +2822,12 @@ static void draw_home(tui_t *t) { 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; + hermit_state_t crab_st = hermit_state_from_ui( + t->busy, t->ask_pending || t->awaiting_perm, 0); + int crab_w = hermit_shell_art_width(); + int crab_h = hermit_shell_art_height(); int crab_x = w - crab_w - 2; - int show_crab = crab_x > LOGO_W + 4 && box_top >= crab_h + 1; + int show_crab = crab_x > 20 && box_top >= crab_h + 1 && w >= 48; if (want_block && !(g_ascii || !g_utf8 || g_logo_text) && box_top - LOGO_H - 2 >= 0 && w >= LOGO_W) { int x0 = show_crab ? (crab_x - LOGO_W) / 2 : (w - LOGO_W) / 2; @@ -2822,7 +2842,7 @@ static void draw_home(tui_t *t) { } else { const char *mark = "HERMIT"; int mw = (int)strlen(mark); - int x = show_crab ? (crab_x - mw) / 2 : (w - mw) / 2; + int x = (w - mw) / 2; if (x < 0) x = 0; if (brand_y >= 1) @@ -2830,32 +2850,42 @@ static void draw_home(tui_t *t) { draw_ansi_at(t->logw, brand_y, x, mw, mark, CP_ACCENT, 1, 0); } if (show_crab) { - int cy = box_top - crab_h; + int cy = brand_y; + if (cy + crab_h > box_top) + cy = box_top - crab_h; if (cy < 0) cy = 0; - draw_hermit_icon(t->logw, cy, crab_x, HERMIT_ST_IDLE, 1, t->spin); + draw_hermit_icon(t->logw, cy, crab_x, crab_st, 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 = show_crab ? (crab_x - tw) / 2 : (w - tw) / 2; + int x = (w - tw) / 2; if (x < 0) x = 0; draw_ansi_at(t->logw, ty, x, tw, tag, CP_MUTED, 0, 0); } + /* Compact parliament chip — never dump full status_text on the home. */ if (cst_parliament_is_active() && ty + 1 < box_top) { - char *st = cst_parliament_status_text(); - if (st && st[0]) { - char chip[160]; - snprintf(chip, sizeof(chip), "%s %s", g_ascii ? "*" : "◆", st); - int cw = vis_cols(chip); - if (cw > w - 2) - cw = w - 2; - draw_ansi_at(t->logw, ty + 1, (w - cw) / 2, cw, chip, CP_ACCENT, 0, 0); - } - free(st); + cst_parliament_meta_t m; + memset(&m, 0, sizeof(m)); + cst_parliament_load(&m); + char sid[64] = {0}; + int sess = cst_parliament_session_ok(sid, sizeof(sid)); + int chair = sess && cst_parliament_is_chair(); + char chip[96]; + snprintf(chip, sizeof(chip), "%s parliament · rev=%lu · %s", + g_ascii ? "*" : "◆", (unsigned long)m.revision, + !sess ? "login" : chair ? "chair" : "member"); + int cw = vis_cols(chip); + if (cw > w - 4) + cw = w - 4; + int cx = (w - cw) / 2; + if (cx < 0) + cx = 0; + draw_ansi_at(t->logw, ty + 1, cx, cw, chip, CP_ACCENT, 0, 0); } int right = t->in_x - t->log_x + t->in_w; if (box_bot < h) {