thelapyae 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +48 -2
- package/bin/thelapyae.js +16 -14
- package/package.json +5 -2
- package/src/services/conversation-service.js +10 -8
- package/src/tui/components/consultation-panel.js +336 -0
- package/src/tui/components/models-browser.js +192 -0
- package/src/tui/components/session-viewer.js +194 -0
- package/src/tui/index.js +124 -0
- package/src/tui/layouts/main-layout.js +585 -0
- package/src/tui/theme.js +128 -0
- package/tests/unit/search-service.test.js +1 -1
- package/thelapyae-sessions/how-to-improve-life-quality-2026-01-17-01-20-44.md +0 -54
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,38 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.0] - 2026-01-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Full TUI Overhaul**: Implemented a rich Text User Interface (TUI) with a centralized layout and header.
|
|
12
|
+
- **Integrated CLI-to-TUI Consultation**: Running `thelapyae "your question"` now seamlessly launches the TUI and auto-starts the consultation.
|
|
13
|
+
- **Dynamic UI Labels**: Titles and input box labels now update dynamically based on the current mode (e.g., "Consultation", "API Configuration", "Help").
|
|
14
|
+
- **Inline Commands**: Converted popup dialogs for `/help`, `/config`, and `/sessions` into inline views for a more fluid experience.
|
|
15
|
+
- **New Slash Commands**: Added `/new` to start a fresh session and `/exit` to cleanly quit the application.
|
|
16
|
+
- **Branding & Aesthetics**: Added brand color (#f3c12b) and improved text alignment and layout responsiveness.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- **Navigation**: Standardized Esc key to act as a "Back" button to return to the welcome screen from any command view.
|
|
20
|
+
- **Default Behavior**: Removed redundant `q` and `Ctrl+N` keyboard shortcuts in favor of more explicit slash commands and Ctrl+C.
|
|
21
|
+
- **Layout**: Simplified the main layout to prioritize readability with left-aligned examples and centered headers.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- **Input Issues**: Resolved double character input and accidental Vim mode triggers in the TUI input box.
|
|
25
|
+
- **Service Integration**: Fixed errors in `SearchService`, `ConversationService`, and `SessionService` integration.
|
|
26
|
+
- **Terminal Behavior**: Improved Ctrl+C handling for reliable immediate exit.
|
|
27
|
+
- **API Config**: Fixed input blocking and Esc key behavior in the API configuration view.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## [0.4.0] - 2026-01-20
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- Initial implementation of the `blessed`-based TUI framework.
|
|
35
|
+
- Basic support for interactive slash commands.
|
|
36
|
+
- Enhanced search service with Fuse.js integration.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
8
40
|
## [0.3.1] - 2026-01-18
|
|
9
41
|
|
|
10
42
|
### Fixed
|
package/README.md
CHANGED
|
@@ -21,7 +21,16 @@
|
|
|
21
21
|
|
|
22
22
|
It features a **Think-with-AI** mode where it acts as a consultant, analyzing your specific life or work problems using these mental models to provide actionable advice. Even without AI, it provides powerful offline synthesis combining multiple perspectives.
|
|
23
23
|
|
|
24
|
-
## ✨ What's New in v0.
|
|
24
|
+
## ✨ What's New in v0.5.0
|
|
25
|
+
|
|
26
|
+
- 🎨 **Unified TUI Experience**: Entirely redesigned terminal interface with consistent branding (#f3c12b).
|
|
27
|
+
- ⌨️ **Slash Command Power**: New `/new` and `/exit` commands for better session management.
|
|
28
|
+
- 🔍 **Integrated CLI-to-TUI**: Run `thelapyae "question"` to jump directly into a TUI consultation.
|
|
29
|
+
- 📑 **Inline Views**: Help, Configuration, and Sessions now display inline for a smoother workflow.
|
|
30
|
+
- ⌨️ **Esc to Back**: Standardized **Esc** key navigation to return to the welcome screen from anywhere.
|
|
31
|
+
- 🎯 **Fixed Input**: Resolved double-character and Vim mode issues for a fluid typing experience.
|
|
32
|
+
|
|
33
|
+
### Previous Updates (v0.2.0-v0.3.0)
|
|
25
34
|
|
|
26
35
|
- 🔐 **Secure Storage**: API keys now stored in OS keychain (not plain text)
|
|
27
36
|
- 🔍 **Better Search**: Fuzzy matching with typo tolerance
|
|
@@ -47,7 +56,44 @@ npm install -g thelapyae
|
|
|
47
56
|
|
|
48
57
|
## 💡 Usage
|
|
49
58
|
|
|
50
|
-
###
|
|
59
|
+
### TUI Mode (Recommended) 🎨
|
|
60
|
+
|
|
61
|
+
The **new rich terminal interface** provides a beautiful, interactive experience:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
$ thelapyae
|
|
65
|
+
|
|
66
|
+
# You'll see a stunning centralized interface:
|
|
67
|
+
# - Header: Brand logo and version
|
|
68
|
+
# - Content: Consultations, Help, or History
|
|
69
|
+
# - Bottom: Responsive input box
|
|
70
|
+
|
|
71
|
+
# Commands:
|
|
72
|
+
# /help - Show interactive help
|
|
73
|
+
# /new - Start a new session
|
|
74
|
+
# /exit - Cleanly quit
|
|
75
|
+
# /random - Get a random model
|
|
76
|
+
# /list - List all models
|
|
77
|
+
# /sessions - View past sessions
|
|
78
|
+
# /config - Setup API key
|
|
79
|
+
|
|
80
|
+
# Navigation:
|
|
81
|
+
# Enter - Submit question/command
|
|
82
|
+
# Esc - Back to Welcome / Clear input
|
|
83
|
+
# Ctrl+C - Force Quit
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Features:**
|
|
87
|
+
- 🎨 Beautiful interface with brand colors
|
|
88
|
+
- ⌨️ Intuitive slash commands
|
|
89
|
+
- 🔍 Live autocomplete as you type `/`
|
|
90
|
+
- 💬 Interactive AI consultations
|
|
91
|
+
- 📚 Browse session history inline
|
|
92
|
+
- 🎯 Zero mouse required
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### Interactive Mode (Classic)
|
|
51
97
|
|
|
52
98
|
Just run `thelapyae` to enter an immersive interactive session:
|
|
53
99
|
|
package/bin/thelapyae.js
CHANGED
|
@@ -116,19 +116,20 @@ program
|
|
|
116
116
|
|
|
117
117
|
// Default action (when no command is specified)
|
|
118
118
|
program.action(async () => {
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
|
|
119
|
+
const TUI = require('../src/tui');
|
|
120
|
+
const tui = new TUI(models, packageJson);
|
|
121
|
+
tui.start();
|
|
122
122
|
});
|
|
123
123
|
|
|
124
|
-
// Handle unknown commands - treat as consult query
|
|
124
|
+
// Handle unknown commands - treat as consult query in TUI
|
|
125
125
|
program.on('command:*', async function (operands) {
|
|
126
126
|
const query = operands.join(' ');
|
|
127
127
|
|
|
128
|
-
// If it looks like a question or statement,
|
|
128
|
+
// If it looks like a question or statement, launch TUI with query
|
|
129
129
|
if (query && query.length > 0) {
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
const TUI = require('../src/tui');
|
|
131
|
+
const tui = new TUI(models, packageJson);
|
|
132
|
+
tui.start(query);
|
|
132
133
|
} else {
|
|
133
134
|
console.error(chalk.red(`\n❌ Unknown command: ${operands[0]}\n`));
|
|
134
135
|
console.log(chalk.gray('Run'), chalk.cyan('thelapyae --help'), chalk.gray('for available commands\n'));
|
|
@@ -138,18 +139,19 @@ program.on('command:*', async function (operands) {
|
|
|
138
139
|
|
|
139
140
|
// Parse arguments
|
|
140
141
|
if (process.argv.length === 2) {
|
|
141
|
-
// No arguments - start
|
|
142
|
+
// No arguments - start TUI mode directly
|
|
142
143
|
(async () => {
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
|
|
144
|
+
const TUI = require('../src/tui');
|
|
145
|
+
const tui = new TUI(models, packageJson);
|
|
146
|
+
tui.start();
|
|
146
147
|
})();
|
|
147
148
|
} else if (process.argv.length > 2 && !process.argv[2].startsWith('-')) {
|
|
148
|
-
// Direct question provided (not a command or option)
|
|
149
|
+
// Direct question provided (not a command or option) - launch TUI with question
|
|
149
150
|
const query = process.argv.slice(2).join(' ');
|
|
150
151
|
(async () => {
|
|
151
|
-
|
|
152
|
-
|
|
152
|
+
const TUI = require('../src/tui');
|
|
153
|
+
const tui = new TUI(models, packageJson);
|
|
154
|
+
tui.start(query);
|
|
153
155
|
})();
|
|
154
156
|
} else {
|
|
155
157
|
// Parse commands normally
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thelapyae",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Personal CLI of La Pyae – mental models, stoicism, clear thinking",
|
|
5
5
|
"bin": {
|
|
6
|
-
"thelapyae": "
|
|
6
|
+
"thelapyae": "bin/thelapyae.js"
|
|
7
7
|
},
|
|
8
8
|
"author": "La Pyae",
|
|
9
9
|
"license": "MIT",
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@google/generative-ai": "^0.24.1",
|
|
17
|
+
"blessed": "^0.1.81",
|
|
18
|
+
"blessed-contrib": "^4.11.0",
|
|
17
19
|
"boxen": "^8.0.1",
|
|
18
20
|
"chalk": "^4.1.2",
|
|
19
21
|
"cli-table3": "^0.6.3",
|
|
@@ -24,6 +26,7 @@
|
|
|
24
26
|
"gradient-string": "^3.0.0",
|
|
25
27
|
"keytar": "^7.9.0",
|
|
26
28
|
"natural": "^6.10.0",
|
|
29
|
+
"neo-blessed": "^0.2.0",
|
|
27
30
|
"ora": "^5.4.1",
|
|
28
31
|
"terminal-link": "^5.0.0"
|
|
29
32
|
},
|
|
@@ -13,8 +13,9 @@ class ConversationService {
|
|
|
13
13
|
/**
|
|
14
14
|
* Generate reflection questions using AI
|
|
15
15
|
*/
|
|
16
|
-
async generateReflectionQuestions(query, models, count = 5) {
|
|
17
|
-
const
|
|
16
|
+
async generateReflectionQuestions(query, models, count = 5, options = {}) {
|
|
17
|
+
const { showSpinner = true } = options;
|
|
18
|
+
const spinner = showSpinner ? ora('Generating reflection questions...').start() : null;
|
|
18
19
|
|
|
19
20
|
try {
|
|
20
21
|
const prompt = `You are a thoughtful consultant helping someone think through: "${query}"
|
|
@@ -55,11 +56,11 @@ Make them simple, clear, and thought-provoking.`;
|
|
|
55
56
|
// Parse JSON
|
|
56
57
|
const questions = JSON.parse(text);
|
|
57
58
|
|
|
58
|
-
spinner.succeed(`Generated ${questions.length} reflection questions`);
|
|
59
|
+
if (spinner) spinner.succeed(`Generated ${questions.length} reflection questions`);
|
|
59
60
|
return questions;
|
|
60
61
|
|
|
61
62
|
} catch (error) {
|
|
62
|
-
spinner.fail('Failed to generate reflection questions');
|
|
63
|
+
if (spinner) spinner.fail('Failed to generate reflection questions');
|
|
63
64
|
throw new Error(`Could not generate questions: ${error.message}`);
|
|
64
65
|
}
|
|
65
66
|
}
|
|
@@ -136,8 +137,9 @@ Make them simple, clear, and thought-provoking.`;
|
|
|
136
137
|
/**
|
|
137
138
|
* Generate final analysis with mental models
|
|
138
139
|
*/
|
|
139
|
-
async generateFinalAnalysis(query, questionsAndAnswers, models) {
|
|
140
|
-
const
|
|
140
|
+
async generateFinalAnalysis(query, questionsAndAnswers, models, options = {}) {
|
|
141
|
+
const { showSpinner = true } = options;
|
|
142
|
+
const spinner = showSpinner ? ora('Analyzing your responses with mental models...').start() : null;
|
|
141
143
|
|
|
142
144
|
try {
|
|
143
145
|
const conversation = this.formatConversation(query, questionsAndAnswers);
|
|
@@ -180,11 +182,11 @@ CRITICAL FORMATTING RULES FOR TERMINAL DISPLAY:
|
|
|
180
182
|
const response = await result.response;
|
|
181
183
|
const analysis = response.text();
|
|
182
184
|
|
|
183
|
-
spinner.succeed('Analysis complete');
|
|
185
|
+
if (spinner) spinner.succeed('Analysis complete');
|
|
184
186
|
return analysis;
|
|
185
187
|
|
|
186
188
|
} catch (error) {
|
|
187
|
-
spinner.fail('Analysis failed');
|
|
189
|
+
if (spinner) spinner.fail('Analysis failed');
|
|
188
190
|
throw error;
|
|
189
191
|
}
|
|
190
192
|
}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consultation Panel Component
|
|
3
|
+
* Interactive consultation interface with AI
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const blessed = require('blessed');
|
|
7
|
+
const theme = require('../theme');
|
|
8
|
+
const SearchService = require('../../services/search-service');
|
|
9
|
+
const ConversationService = require('../../services/conversation-service');
|
|
10
|
+
const AIService = require('../../services/ai-service');
|
|
11
|
+
const SessionService = require('../../services/session-service');
|
|
12
|
+
const configService = require('../../services/config-service');
|
|
13
|
+
|
|
14
|
+
class ConsultationPanel {
|
|
15
|
+
constructor(parent, models, layout) {
|
|
16
|
+
this.parent = parent;
|
|
17
|
+
this.models = models;
|
|
18
|
+
this.layout = layout;
|
|
19
|
+
|
|
20
|
+
// Create search service instance
|
|
21
|
+
this.searchService = new SearchService(models);
|
|
22
|
+
|
|
23
|
+
// AI and conversation services will be initialized when needed
|
|
24
|
+
this.aiService = null;
|
|
25
|
+
this.conversationService = null;
|
|
26
|
+
|
|
27
|
+
this.currentQuery = '';
|
|
28
|
+
this.currentQuestions = [];
|
|
29
|
+
this.currentAnswers = [];
|
|
30
|
+
this.relevantModels = [];
|
|
31
|
+
|
|
32
|
+
this.welcomeBox = null;
|
|
33
|
+
this.inputBox = null;
|
|
34
|
+
this.outputBox = null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Show the consultation panel
|
|
39
|
+
*/
|
|
40
|
+
show() {
|
|
41
|
+
this.parent.setContent('');
|
|
42
|
+
this.showWelcome();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Show welcome message
|
|
47
|
+
*/
|
|
48
|
+
showWelcome() {
|
|
49
|
+
const welcomeText = `{center}{bold}{#f3c12b-fg}Welcome to thelapyae{/#f3c12b-fg}{/bold}{/center}
|
|
50
|
+
|
|
51
|
+
Ask me anything about thinking, decisions, or life.
|
|
52
|
+
I'll help you explore it using mental models.
|
|
53
|
+
|
|
54
|
+
{bold}Examples:{/bold}
|
|
55
|
+
• "Should I quit my job?"
|
|
56
|
+
• "How do I prioritize my tasks?"
|
|
57
|
+
• "I feel overwhelmed with too many projects"
|
|
58
|
+
|
|
59
|
+
{bold}Slash Commands:{/bold}
|
|
60
|
+
{cyan-fg}/random{/cyan-fg} - Get a random mental model
|
|
61
|
+
{cyan-fg}/list{/cyan-fg} - List all mental models
|
|
62
|
+
{cyan-fg}/sessions{/cyan-fg} - View past consultations
|
|
63
|
+
{cyan-fg}/config{/cyan-fg} - Configure API key
|
|
64
|
+
{cyan-fg}/help{/cyan-fg} - Show help
|
|
65
|
+
{cyan-fg}/new{/cyan-fg} - Start new session
|
|
66
|
+
{cyan-fg}/exit{/cyan-fg} - Exit application
|
|
67
|
+
|
|
68
|
+
Just type your question or a slash command and press Enter!`;
|
|
69
|
+
|
|
70
|
+
this.parent.setContent(welcomeText);
|
|
71
|
+
this.parent.screen.render();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Start a new consultation
|
|
76
|
+
*/
|
|
77
|
+
async startConsultation() {
|
|
78
|
+
// Create input box
|
|
79
|
+
const inputBox = blessed.textarea({
|
|
80
|
+
parent: this.parent.screen,
|
|
81
|
+
top: 'center',
|
|
82
|
+
left: 'center',
|
|
83
|
+
width: '80%',
|
|
84
|
+
height: 7,
|
|
85
|
+
label: ' What\'s on your mind? ',
|
|
86
|
+
border: theme.boxes.input.border,
|
|
87
|
+
style: theme.boxes.input.style,
|
|
88
|
+
inputOnFocus: true,
|
|
89
|
+
keys: true,
|
|
90
|
+
vi: true
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
inputBox.focus();
|
|
94
|
+
this.parent.screen.render();
|
|
95
|
+
|
|
96
|
+
// Get input
|
|
97
|
+
inputBox.readInput(async (err, value) => {
|
|
98
|
+
if (err || !value || !value.trim()) {
|
|
99
|
+
inputBox.destroy();
|
|
100
|
+
this.parent.screen.render();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.currentQuery = value.trim();
|
|
105
|
+
inputBox.destroy();
|
|
106
|
+
|
|
107
|
+
// Start consultation flow
|
|
108
|
+
await this.runConsultation();
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Handle a query from the input box
|
|
114
|
+
*/
|
|
115
|
+
async handleQuery(query) {
|
|
116
|
+
this.currentQuery = query;
|
|
117
|
+
await this.runConsultation();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Run the consultation flow
|
|
122
|
+
*/
|
|
123
|
+
async runConsultation() {
|
|
124
|
+
try {
|
|
125
|
+
// Show loading
|
|
126
|
+
this.showLoading('Analyzing your question...');
|
|
127
|
+
|
|
128
|
+
// Find relevant models using search service
|
|
129
|
+
const searchResults = this.searchService.search(this.currentQuery, 5);
|
|
130
|
+
this.relevantModels = searchResults.map(r => r.model);
|
|
131
|
+
|
|
132
|
+
// Check if API key is configured
|
|
133
|
+
const apiKey = await configService.getApiKey();
|
|
134
|
+
|
|
135
|
+
if (!apiKey) {
|
|
136
|
+
this.showOfflineConsultation();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Initialize AI services if not already done
|
|
141
|
+
if (!this.aiService) {
|
|
142
|
+
this.aiService = new AIService(apiKey);
|
|
143
|
+
}
|
|
144
|
+
if (!this.conversationService) {
|
|
145
|
+
this.conversationService = new ConversationService(this.aiService);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Generate reflection questions
|
|
149
|
+
this.showLoading('Generating reflection questions...');
|
|
150
|
+
this.currentQuestions = await this.conversationService.generateReflectionQuestions(
|
|
151
|
+
this.currentQuery,
|
|
152
|
+
this.relevantModels,
|
|
153
|
+
5,
|
|
154
|
+
{ showSpinner: false }
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// Ask questions interactively
|
|
158
|
+
await this.askQuestions();
|
|
159
|
+
|
|
160
|
+
// Generate final analysis
|
|
161
|
+
this.showLoading('Analyzing your responses...');
|
|
162
|
+
const questionsAndAnswers = this.currentQuestions.map((q, i) => ({
|
|
163
|
+
question: q,
|
|
164
|
+
answer: this.currentAnswers[i]
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
const analysis = await this.conversationService.generateFinalAnalysis(
|
|
168
|
+
this.currentQuery,
|
|
169
|
+
questionsAndAnswers,
|
|
170
|
+
this.relevantModels,
|
|
171
|
+
{ showSpinner: false }
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// Show results
|
|
175
|
+
this.showResults(analysis);
|
|
176
|
+
|
|
177
|
+
// Save session
|
|
178
|
+
const sessionService = new SessionService();
|
|
179
|
+
const modelsUsed = this.relevantModels.map(m => m.title).join(', ');
|
|
180
|
+
sessionService.saveSession(
|
|
181
|
+
this.currentQuery,
|
|
182
|
+
questionsAndAnswers,
|
|
183
|
+
analysis,
|
|
184
|
+
{ modelsUsed }
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
} catch (error) {
|
|
188
|
+
this.showError(error.message);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Ask reflection questions one by one
|
|
194
|
+
*/
|
|
195
|
+
async askQuestions() {
|
|
196
|
+
this.currentAnswers = [];
|
|
197
|
+
|
|
198
|
+
for (let i = 0; i < this.currentQuestions.length; i++) {
|
|
199
|
+
const question = this.currentQuestions[i];
|
|
200
|
+
const answer = await this.askSingleQuestion(question, i + 1, this.currentQuestions.length);
|
|
201
|
+
|
|
202
|
+
if (answer === null) {
|
|
203
|
+
// User cancelled
|
|
204
|
+
throw new Error('Consultation cancelled');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
this.currentAnswers.push(answer);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Ask a single question
|
|
213
|
+
*/
|
|
214
|
+
askSingleQuestion(question, index, total) {
|
|
215
|
+
return new Promise((resolve) => {
|
|
216
|
+
// Show question in content area
|
|
217
|
+
const questionContent = `{bold}{cyan-fg}Question ${index}/${total}{/cyan-fg}{/bold}
|
|
218
|
+
|
|
219
|
+
{bold}${question}{/bold}
|
|
220
|
+
|
|
221
|
+
{gray-fg}Type your answer in the input box below and press Enter.
|
|
222
|
+
Press Esc to skip this question.{/gray-fg}`;
|
|
223
|
+
|
|
224
|
+
this.parent.setContent(questionContent);
|
|
225
|
+
|
|
226
|
+
// Update input box label
|
|
227
|
+
const mainInputBox = this.layout.inputBox;
|
|
228
|
+
mainInputBox.setLabel(` Type your answer here (Question ${index}/${total}) `);
|
|
229
|
+
mainInputBox.style.focus.border.fg = 'cyan';
|
|
230
|
+
|
|
231
|
+
this.parent.screen.render();
|
|
232
|
+
|
|
233
|
+
// Temporarily override Enter handler
|
|
234
|
+
const tempEnterHandler = (ch, key) => {
|
|
235
|
+
if (key.name === 'enter') {
|
|
236
|
+
const answer = mainInputBox.getValue().trim();
|
|
237
|
+
mainInputBox.clearValue();
|
|
238
|
+
|
|
239
|
+
// Restore original state
|
|
240
|
+
mainInputBox.removeListener('keypress', tempEnterHandler);
|
|
241
|
+
mainInputBox.removeListener('keypress', tempEscHandler);
|
|
242
|
+
|
|
243
|
+
// Reset label
|
|
244
|
+
mainInputBox.setLabel(' Ask me anything... (Press Enter to submit, Esc to clear) ');
|
|
245
|
+
mainInputBox.style.focus.border.fg = 'yellow';
|
|
246
|
+
|
|
247
|
+
resolve(answer || '');
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// Temporarily override Esc handler to skip
|
|
252
|
+
const tempEscHandler = (ch, key) => {
|
|
253
|
+
if (key.name === 'escape') {
|
|
254
|
+
mainInputBox.clearValue();
|
|
255
|
+
|
|
256
|
+
// Restore original state
|
|
257
|
+
mainInputBox.removeListener('keypress', tempEnterHandler);
|
|
258
|
+
mainInputBox.removeListener('keypress', tempEscHandler);
|
|
259
|
+
|
|
260
|
+
// Reset label
|
|
261
|
+
mainInputBox.setLabel(' Ask me anything... (Press Enter to submit, Esc to clear) ');
|
|
262
|
+
mainInputBox.style.focus.border.fg = 'yellow';
|
|
263
|
+
|
|
264
|
+
resolve(''); // Empty answer means skipped
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
mainInputBox.on('keypress', tempEnterHandler);
|
|
269
|
+
mainInputBox.on('keypress', tempEscHandler);
|
|
270
|
+
|
|
271
|
+
mainInputBox.focus();
|
|
272
|
+
this.parent.screen.render();
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Show loading message
|
|
278
|
+
*/
|
|
279
|
+
showLoading(message) {
|
|
280
|
+
this.parent.setContent(`{center}\n\n{bold}{cyan-fg}${message}{/cyan-fg}{/bold}\n\n{gray-fg}Please wait...{/gray-fg}{/center}`);
|
|
281
|
+
this.parent.screen.render();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Show offline consultation
|
|
286
|
+
*/
|
|
287
|
+
showOfflineConsultation() {
|
|
288
|
+
const modelsText = this.relevantModels.map((m, i) =>
|
|
289
|
+
`${i + 1}. {bold}${m.title}{/bold}\n ${m.explanation.substring(0, 200)}...`
|
|
290
|
+
).join('\n\n');
|
|
291
|
+
|
|
292
|
+
const content = `{center}{bold}{yellow-fg}Offline Mode{/yellow-fg}{/bold}{/center}
|
|
293
|
+
|
|
294
|
+
{bold}Your Question:{/bold}
|
|
295
|
+
${this.currentQuery}
|
|
296
|
+
|
|
297
|
+
{bold}Relevant Mental Models:{/bold}
|
|
298
|
+
|
|
299
|
+
${modelsText}
|
|
300
|
+
|
|
301
|
+
{center}{gray-fg}Configure your API key with 'thelapyae config' to enable AI consultation.{/gray-fg}{/center}`;
|
|
302
|
+
|
|
303
|
+
this.parent.setContent(content);
|
|
304
|
+
this.parent.screen.render();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Show results
|
|
309
|
+
*/
|
|
310
|
+
showResults(analysis) {
|
|
311
|
+
const content = `{bold}{green-fg}✓ Analysis Complete{/green-fg}{/bold}
|
|
312
|
+
|
|
313
|
+
{bold}Your Question:{/bold}
|
|
314
|
+
${this.currentQuery}
|
|
315
|
+
|
|
316
|
+
{bold}Analysis:{/bold}
|
|
317
|
+
|
|
318
|
+
${analysis}
|
|
319
|
+
|
|
320
|
+
{center}{gray-fg}Session saved. Press Ctrl+N for a new consultation.{/gray-fg}{/center}`;
|
|
321
|
+
|
|
322
|
+
this.parent.setContent(content);
|
|
323
|
+
this.parent.setScrollPerc(0);
|
|
324
|
+
this.parent.screen.render();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Show error
|
|
329
|
+
*/
|
|
330
|
+
showError(message) {
|
|
331
|
+
this.parent.setContent(`{center}\n\n{bold}{red-fg}Error{/red-fg}{/bold}\n\n${message}\n\n{gray-fg}Press Ctrl+N to try again.{/gray-fg}{/center}`);
|
|
332
|
+
this.parent.screen.render();
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
module.exports = ConsultationPanel;
|