4141
4242#include < algorithm>
4343#include < chrono>
44+ #include < cmath>
4445#include < ctime>
4546#include < filesystem>
4647#include < fstream>
@@ -469,7 +470,7 @@ static void load_settings(NitroConfig &cfg) {
469470 std::string json = oss.str ();
470471
471472 cfg.thinking = true ;
472-
473+
473474 // String fields
474475 settings_get_str (json, " model_path" , cfg.model_path );
475476 settings_get_str (json, " embed_path" , cfg.embed_path );
@@ -1170,23 +1171,30 @@ void TuiState::redraw_input() const {
11701171 ncplane_erase (inputpl);
11711172
11721173 if (thinking) {
1173- static constexpr const char *ROBOT_RIGHT = " 🤖➡" ;
1174- static constexpr const char *ROBOT_LEFT = " ⬅🤖" ;
1175- // 15 steps each way = 20 frame cycle
1176- static constexpr int STEPS = 15 ;
1177- int cycle = spinner_frame % (STEPS * 2 );
1178- bool going_right = (cycle < STEPS);
1179- int pos = going_right ? cycle : (STEPS * 2 - 1 - cycle);
1180-
1181- std::string sep (term_cols, ' -' );
1182- // blank 4 cols to fit robot + arrow (each emoji is 2 cols wide)
1183- for (int i = pos; i < std::min (pos + 4 , term_cols); ++i) sep[i] = ' ' ;
1184- ncplane_set_channels (inputpl, inp_ch (80 , 120 , 160 ));
1185- ncplane_putstr_yx (inputpl, 0 , 0 , sep.c_str ());
1186-
1187- ncplane_set_channels (inputpl, NCCHANNELS_INITIALIZER (255 , 220 , 80 , BG_INP_R, BG_INP_G, BG_INP_B));
1188- ncplane_putstr_yx (inputpl, 0 , pos, going_right ? ROBOT_RIGHT : ROBOT_LEFT);
1189-
1174+ static constexpr const char *BLOCKS[] = { " -" , " ~" , " ≈" , " ~" , " -" };
1175+ static constexpr int N_BLOCKS = 5 ;
1176+ static constexpr double FREQ = 0.25 ; // gentler wave
1177+ static constexpr double SPEED = 0.15 ; // slower scroll
1178+ static constexpr int DELAY = 12 ; // frames before animation starts
1179+
1180+ if (spinner_frame < DELAY) {
1181+ // still just a plain separator during the pause
1182+ ncplane_set_channels (inputpl, inp_ch (80 , 120 , 160 ));
1183+ std::string sep (term_cols, ' -' );
1184+ ncplane_putstr_yx (inputpl, 0 , 0 , sep.c_str ());
1185+ } else {
1186+ int frame = spinner_frame - DELAY; // animation frame relative to start
1187+ for (int col = 0 ; col < term_cols; ++col) {
1188+ double phase = (col * FREQ) - (frame * SPEED);
1189+ int idx = (int )((std::sin (phase) + 1.0 ) * 0.5 * (N_BLOCKS - 1 ));
1190+ idx = std::max (0 , std::min (idx, N_BLOCKS - 1 ));
1191+ // subtle brightness shift — blue-grey, not full glow
1192+ int brightness = 80 + idx * 20 ;
1193+ ncplane_set_channels (inputpl, NCCHANNELS_INITIALIZER (brightness, brightness + 20 , brightness + 40 ,
1194+ BG_INP_R, BG_INP_G, BG_INP_B));
1195+ ncplane_putstr_yx (inputpl, 0 , col, BLOCKS[idx]);
1196+ }
1197+ }
11901198 ncplane_set_channels (inputpl, inp_ch (140 , 140 , 180 ));
11911199 ncplane_putstr_yx (inputpl, 1 , 2 , " thinking…" );
11921200 } else {
@@ -1787,7 +1795,7 @@ bool AgentState::setup_model(const NitroConfig &cfg, TuiState &tui) {
17871795 tui.vram_total = mem.vram_total ;
17881796
17891797 tui.append_line (std::string (" [sys] Thinking mode: " ) + (cfg.thinking ? " enabled" : " disabled" ));
1790- tui.redraw_all ();
1798+ tui.redraw_all ();
17911799 return true ;
17921800}
17931801
@@ -2070,6 +2078,7 @@ bool AgentState::run_turn(const std::string &user_message, const NitroConfig &cf
20702078 // in_think starts false — models that don't use <think> blocks emit
20712079 // visible text immediately. The spinner activates only while thinking.
20722080 enum {t_init, t_think, t_thunk} think_mode = (cfg.thinking ? t_init : t_thunk);
2081+
20732082 tui.set_thinking (false );
20742083 std::string buffer;
20752084
@@ -2089,7 +2098,7 @@ bool AgentState::run_turn(const std::string &user_message, const NitroConfig &cf
20892098 tool = buffer;
20902099 }
20912100
2092- log_write (" tool request: [% s]" , tool.c_str ());
2101+ log_write (" tool request: mode:[%d] [% s]" , think_mode , tool.c_str ());
20932102 std::string result = process_tool (tool, cfg, tui);
20942103 std::string content = TOOL_RESULT + std::vformat (template_str, std::make_format_args (result));
20952104 log_write (" tool: [%s] result: [%s]" , tool.c_str (), result.c_str ());
@@ -2137,7 +2146,23 @@ bool AgentState::run_turn(const std::string &user_message, const NitroConfig &cf
21372146 return false ;
21382147 }
21392148 std::string tok = llama->next (*iter);
2140- buffer += tok;
2149+ if (tok == " <" ) {
2150+ // fetch the complete tag
2151+ std::string tag = tok;
2152+ while (iter->_has_next && tag.find (" >" ) == std::string::npos) {
2153+ tag += llama->next (*iter);
2154+ }
2155+ if (tag == " <|think|>" ) {
2156+ think_mode = t_think;
2157+ tui.set_thinking (true );
2158+ continue ;
2159+ } else {
2160+ buffer += tag;
2161+ }
2162+ } else {
2163+ buffer += tok;
2164+ }
2165+
21412166 if (think_mode == t_init) {
21422167 start_think (" <think>" );
21432168 start_think (" <|think|>" );
0 commit comments