feat: add Tab key to cycle through thinking levels

- Add onTab callback to CustomEditor
- Implement cycleThinkingLevel() in TuiRenderer
- Only works when model supports reasoning
- Cycles through: off → minimal → low → medium → high → off
- Update footer to show current thinking level with '(tab to cycle)' hint
- Update header instructions to mention tab for thinking
- Show notification when thinking level changes
This commit is contained in:
Tino Ehrich 2025-11-19 10:18:24 +01:00
parent 1f68d6eb40
commit 9e8373b86a
3 changed files with 68 additions and 15 deletions

View file

@ -85,30 +85,41 @@ export class FooterComponent {
const statsLeft = statsParts.join(" ");
// Add model name on the right side
let modelName = this.state.model?.id || "no-model";
// Add model name on the right side, plus thinking level if model supports it
const modelName = this.state.model?.id || "no-model";
// Add thinking level hint if model supports reasoning
let rightSide = modelName;
if (this.state.model?.reasoning) {
const thinkingLevel = this.state.thinkingLevel || "off";
const thinkingHint = chalk.dim(" (tab to cycle)");
rightSide = `${modelName}${thinkingLevel}${thinkingHint}`;
}
const statsLeftWidth = visibleWidth(statsLeft);
const modelWidth = visibleWidth(modelName);
const rightSideWidth = visibleWidth(rightSide);
// Calculate available space for padding (minimum 2 spaces between stats and model)
const minPadding = 2;
const totalNeeded = statsLeftWidth + minPadding + modelWidth;
const totalNeeded = statsLeftWidth + minPadding + rightSideWidth;
let statsLine: string;
if (totalNeeded <= width) {
// Both fit - add padding to right-align model
const padding = " ".repeat(width - statsLeftWidth - modelWidth);
statsLine = statsLeft + padding + modelName;
const padding = " ".repeat(width - statsLeftWidth - rightSideWidth);
statsLine = statsLeft + padding + rightSide;
} else {
// Need to truncate model name
const availableForModel = width - statsLeftWidth - minPadding;
if (availableForModel > 3) {
// Truncate model name to fit
modelName = modelName.substring(0, availableForModel);
const padding = " ".repeat(width - statsLeftWidth - visibleWidth(modelName));
statsLine = statsLeft + padding + modelName;
// Need to truncate right side
const availableForRight = width - statsLeftWidth - minPadding;
if (availableForRight > 3) {
// Truncate to fit (strip ANSI codes for length calculation, then truncate raw string)
const plainRightSide = rightSide.replace(/\x1b\[[0-9;]*m/g, "");
const truncatedPlain = plainRightSide.substring(0, availableForRight);
// For simplicity, just use plain truncated version (loses color, but fits)
const padding = " ".repeat(width - statsLeftWidth - truncatedPlain.length);
statsLine = statsLeft + padding + truncatedPlain;
} else {
// Not enough space for model name at all
// Not enough space for right side at all
statsLine = statsLeft;
}
}