jvcs 1.6.2 → 1.6.3
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/controllers/diff-engine/ui.js +80 -116
- package/package.json +2 -1
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
const blessed = require("blessed");
|
|
2
2
|
const aiAnalyzer = require("./aiAnalyzer");
|
|
3
3
|
const { marked } = require("marked");
|
|
4
|
-
const {
|
|
5
|
-
|
|
6
|
-
marked.setOptions({
|
|
7
|
-
renderer: TerminalRenderer
|
|
8
|
-
});
|
|
4
|
+
const { PlaintextRenderer } = require("marked-plaintext");
|
|
9
5
|
|
|
10
6
|
function startUI(state) {
|
|
11
7
|
const screen = blessed.screen({
|
|
12
8
|
smartCSR: true,
|
|
13
9
|
title: "JVCS Diff Viewer"
|
|
14
|
-
})
|
|
10
|
+
});
|
|
15
11
|
|
|
16
12
|
// ---------------------------------------
|
|
17
13
|
// HEADER
|
|
@@ -28,8 +24,8 @@ function startUI(state) {
|
|
|
28
24
|
underline: true
|
|
29
25
|
},
|
|
30
26
|
tags: true
|
|
31
|
-
})
|
|
32
|
-
screen.append(header)
|
|
27
|
+
});
|
|
28
|
+
screen.append(header);
|
|
33
29
|
|
|
34
30
|
// ---------------------------------------
|
|
35
31
|
// FILE LIST VIEW
|
|
@@ -48,52 +44,33 @@ function startUI(state) {
|
|
|
48
44
|
item: { fg: "white" }
|
|
49
45
|
},
|
|
50
46
|
tags: true
|
|
51
|
-
})
|
|
52
|
-
screen.append(fileList)
|
|
47
|
+
});
|
|
48
|
+
screen.append(fileList);
|
|
53
49
|
|
|
54
50
|
function renderFileList() {
|
|
55
51
|
const items = state.files.map(file => {
|
|
56
|
-
let statusColored
|
|
52
|
+
let statusColored;
|
|
57
53
|
switch(file.status) {
|
|
58
|
-
case "added": statusColored = `{green-fg}${file.status.toUpperCase()}{/green-fg}`; break
|
|
59
|
-
case "deleted": statusColored = `{red-fg}${file.status.toUpperCase()}{/red-fg}`; break
|
|
60
|
-
case "modified": statusColored = `{yellow-fg}${file.status.toUpperCase()}{/yellow-fg}`; break
|
|
61
|
-
default: statusColored = file.status.toUpperCase()
|
|
54
|
+
case "added": statusColored = `{green-fg}${file.status.toUpperCase()}{/green-fg}`; break;
|
|
55
|
+
case "deleted": statusColored = `{red-fg}${file.status.toUpperCase()}{/red-fg}`; break;
|
|
56
|
+
case "modified": statusColored = `{yellow-fg}${file.status.toUpperCase()}{/yellow-fg}`; break;
|
|
57
|
+
default: statusColored = file.status.toUpperCase();
|
|
62
58
|
}
|
|
63
|
-
return `${statusColored} ${file.path}
|
|
64
|
-
})
|
|
65
|
-
fileList.setItems(items)
|
|
66
|
-
fileList.select(state.selectedIndex)
|
|
67
|
-
fileList.focus()
|
|
68
|
-
screen.render()
|
|
59
|
+
return `${statusColored} ${file.path}`;
|
|
60
|
+
});
|
|
61
|
+
fileList.setItems(items);
|
|
62
|
+
fileList.select(state.selectedIndex);
|
|
63
|
+
fileList.focus();
|
|
64
|
+
screen.render();
|
|
69
65
|
}
|
|
70
66
|
|
|
71
67
|
// ---------------------------------------
|
|
72
|
-
// FILE VIEW
|
|
68
|
+
// FILE VIEW PANELS
|
|
73
69
|
// ---------------------------------------
|
|
74
|
-
const
|
|
75
|
-
top: 1,
|
|
76
|
-
left: 0,
|
|
77
|
-
width: "33%",
|
|
78
|
-
height: "100%-1",
|
|
79
|
-
border: "line",
|
|
80
|
-
label: " LEFT ",
|
|
81
|
-
scrollable: true,
|
|
82
|
-
alwaysScroll: true,
|
|
83
|
-
keys: true,
|
|
84
|
-
vi: true,
|
|
85
|
-
scrollbar: { ch: "│", track: { bg: "black" }, style: { bg: "yellow" } },
|
|
86
|
-
padding: { left: 1, right: 1 },
|
|
87
|
-
tags: true
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
const rightBox = blessed.box({
|
|
70
|
+
const panelOptions = {
|
|
91
71
|
top: 1,
|
|
92
|
-
left: "33%",
|
|
93
|
-
width: "34%",
|
|
94
72
|
height: "100%-1",
|
|
95
73
|
border: "line",
|
|
96
|
-
label: " RIGHT ",
|
|
97
74
|
scrollable: true,
|
|
98
75
|
alwaysScroll: true,
|
|
99
76
|
keys: true,
|
|
@@ -101,43 +78,33 @@ function startUI(state) {
|
|
|
101
78
|
scrollbar: { ch: "│", track: { bg: "black" }, style: { bg: "yellow" } },
|
|
102
79
|
padding: { left: 1, right: 1 },
|
|
103
80
|
tags: true
|
|
104
|
-
}
|
|
81
|
+
};
|
|
105
82
|
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
width: "33%",
|
|
110
|
-
height: "100%-1",
|
|
111
|
-
border: "line",
|
|
112
|
-
label: " AI ANALYSIS ",
|
|
113
|
-
scrollable: true,
|
|
114
|
-
alwaysScroll: true,
|
|
115
|
-
keys: true,
|
|
116
|
-
vi: true,
|
|
117
|
-
scrollbar: { ch: "│", track: { bg: "black" }, style: { bg: "yellow" } },
|
|
118
|
-
padding: { left: 1, right: 1 },
|
|
119
|
-
tags: true
|
|
120
|
-
})
|
|
83
|
+
const leftBox = blessed.box({ ...panelOptions, left: 0, width: "33%", label: " LEFT " });
|
|
84
|
+
const rightBox = blessed.box({ ...panelOptions, left: "33%", width: "34%", label: " RIGHT " });
|
|
85
|
+
const aiBox = blessed.box({ ...panelOptions, left: "67%", width: "33%", label: " AI ANALYSIS " });
|
|
121
86
|
|
|
122
87
|
function colorDiffContent(content, type) {
|
|
88
|
+
if (!content) return "";
|
|
123
89
|
return content.split("\n").map(line => {
|
|
124
|
-
if(type === "added") return `{green-fg}+ ${line}{/green-fg}
|
|
125
|
-
if(type === "removed") return `{red-fg}- ${line}{/red-fg}
|
|
126
|
-
return ` ${line}
|
|
127
|
-
}).join("\n")
|
|
90
|
+
if(type === "added") return `{green-fg}+ ${line}{/green-fg}`;
|
|
91
|
+
if(type === "removed") return `{red-fg}- ${line}{/red-fg}`;
|
|
92
|
+
return ` ${line}`;
|
|
93
|
+
}).join("\n");
|
|
128
94
|
}
|
|
129
95
|
|
|
130
96
|
async function renderFileView() {
|
|
131
|
-
const file = state.getCurrentFile()
|
|
132
|
-
if (!file) return
|
|
133
|
-
|
|
134
|
-
leftBox.setContent(colorDiffContent(file.leftContent, "removed"))
|
|
135
|
-
rightBox.setContent(colorDiffContent(file.rightContent, "added"))
|
|
97
|
+
const file = state.getCurrentFile();
|
|
98
|
+
if (!file) return;
|
|
136
99
|
|
|
100
|
+
leftBox.setContent(colorDiffContent(file.leftContent, "removed"));
|
|
101
|
+
rightBox.setContent(colorDiffContent(file.rightContent, "added"));
|
|
137
102
|
aiBox.setContent("AI Analysis: Loading...");
|
|
138
|
-
|
|
139
|
-
screen.append(
|
|
140
|
-
screen.append(
|
|
103
|
+
|
|
104
|
+
screen.append(leftBox);
|
|
105
|
+
screen.append(rightBox);
|
|
106
|
+
screen.append(aiBox);
|
|
107
|
+
|
|
141
108
|
updateActiveTabHighlight();
|
|
142
109
|
screen.render();
|
|
143
110
|
|
|
@@ -147,85 +114,82 @@ function startUI(state) {
|
|
|
147
114
|
leftContent: file.leftContent,
|
|
148
115
|
rightContent: file.rightContent,
|
|
149
116
|
mode: state.mode
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// USE PLAINTEXT RENDERER HERE
|
|
120
|
+
const renderer = new PlaintextRenderer();
|
|
121
|
+
const cleanText = marked.parse(aiSummary, { renderer: renderer });
|
|
122
|
+
|
|
123
|
+
aiBox.setContent(cleanText);
|
|
124
|
+
screen.render();
|
|
155
125
|
}
|
|
156
126
|
catch(error) {
|
|
157
|
-
aiBox.setContent(`AI Analysis failed: ${error.message || error}`);
|
|
158
|
-
screen.render()
|
|
127
|
+
aiBox.setContent(`{red-fg}AI Analysis failed: ${error.message || error}{/red-fg}`);
|
|
128
|
+
screen.render();
|
|
159
129
|
}
|
|
160
130
|
}
|
|
161
131
|
|
|
162
132
|
function destroyFileView() {
|
|
163
|
-
leftBox.detach()
|
|
164
|
-
rightBox.detach()
|
|
165
|
-
aiBox.detach()
|
|
166
|
-
screen.render()
|
|
133
|
+
leftBox.detach();
|
|
134
|
+
rightBox.detach();
|
|
135
|
+
aiBox.detach();
|
|
136
|
+
screen.render();
|
|
167
137
|
}
|
|
168
138
|
|
|
169
139
|
function updateActiveTabHighlight() {
|
|
170
|
-
leftBox.style.border.fg = state.activeTab === 0 ? "yellow" : "grey"
|
|
171
|
-
rightBox.style.border.fg = state.activeTab === 1 ? "yellow" : "grey"
|
|
172
|
-
aiBox.style.border.fg = state.activeTab === 2 ? "yellow" : "grey"
|
|
140
|
+
leftBox.style.border.fg = state.activeTab === 0 ? "yellow" : "grey";
|
|
141
|
+
rightBox.style.border.fg = state.activeTab === 1 ? "yellow" : "grey";
|
|
142
|
+
aiBox.style.border.fg = state.activeTab === 2 ? "yellow" : "grey";
|
|
173
143
|
|
|
174
|
-
if(state.activeTab === 0) leftBox.focus()
|
|
175
|
-
else if(state.activeTab === 1) rightBox.focus()
|
|
176
|
-
else aiBox.focus()
|
|
144
|
+
if(state.activeTab === 0) leftBox.focus();
|
|
145
|
+
else if(state.activeTab === 1) rightBox.focus();
|
|
146
|
+
else aiBox.focus();
|
|
177
147
|
|
|
178
|
-
screen.render()
|
|
148
|
+
screen.render();
|
|
179
149
|
}
|
|
180
150
|
|
|
181
151
|
// ---------------------------------------
|
|
182
152
|
// KEYBOARD CONTROLS
|
|
183
153
|
// ---------------------------------------
|
|
184
|
-
screen.key(["q", "C-c"], () => process.exit(0))
|
|
154
|
+
screen.key(["q", "C-c"], () => process.exit(0));
|
|
185
155
|
|
|
186
|
-
// Navigate list
|
|
187
156
|
fileList.key(["up"], () => {
|
|
188
157
|
if(state.selectedIndex > 0) {
|
|
189
|
-
state.selectFile(state.selectedIndex - 1)
|
|
190
|
-
renderFileList()
|
|
158
|
+
state.selectFile(state.selectedIndex - 1);
|
|
159
|
+
renderFileList();
|
|
191
160
|
}
|
|
192
|
-
})
|
|
161
|
+
});
|
|
162
|
+
|
|
193
163
|
fileList.key(["down"], () => {
|
|
194
164
|
if(state.selectedIndex < state.files.length - 1) {
|
|
195
|
-
state.selectFile(state.selectedIndex + 1)
|
|
196
|
-
renderFileList()
|
|
165
|
+
state.selectFile(state.selectedIndex + 1);
|
|
166
|
+
renderFileList();
|
|
197
167
|
}
|
|
198
|
-
})
|
|
168
|
+
});
|
|
199
169
|
|
|
200
|
-
// Enter → Open file view
|
|
201
170
|
fileList.key(["enter"], async () => {
|
|
202
|
-
state.goToFileView()
|
|
203
|
-
fileList.detach()
|
|
204
|
-
await renderFileView()
|
|
205
|
-
})
|
|
171
|
+
state.goToFileView();
|
|
172
|
+
fileList.detach();
|
|
173
|
+
await renderFileView();
|
|
174
|
+
});
|
|
206
175
|
|
|
207
|
-
// ESC → Back to list
|
|
208
176
|
screen.key(["escape"], () => {
|
|
209
177
|
if(state.screen === "FILE_VIEW") {
|
|
210
|
-
state.goToListView()
|
|
211
|
-
destroyFileView()
|
|
212
|
-
screen.append(fileList)
|
|
213
|
-
renderFileList()
|
|
178
|
+
state.goToListView();
|
|
179
|
+
destroyFileView();
|
|
180
|
+
screen.append(fileList);
|
|
181
|
+
renderFileList();
|
|
214
182
|
}
|
|
215
|
-
})
|
|
183
|
+
});
|
|
216
184
|
|
|
217
|
-
// TAB → switch active panel
|
|
218
185
|
screen.key(["tab"], () => {
|
|
219
186
|
if(state.screen === "FILE_VIEW") {
|
|
220
|
-
state.switchTab((state.activeTab + 1) % 3)
|
|
221
|
-
updateActiveTabHighlight()
|
|
187
|
+
state.switchTab((state.activeTab + 1) % 3);
|
|
188
|
+
updateActiveTabHighlight();
|
|
222
189
|
}
|
|
223
|
-
})
|
|
190
|
+
});
|
|
224
191
|
|
|
225
|
-
|
|
226
|
-
// INITIAL RENDER
|
|
227
|
-
// ---------------------------------------
|
|
228
|
-
renderFileList()
|
|
192
|
+
renderFileList();
|
|
229
193
|
}
|
|
230
194
|
|
|
231
|
-
module.exports = startUI
|
|
195
|
+
module.exports = startUI;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jvcs",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"bin": {
|
|
5
5
|
"jvcs": "index.js"
|
|
6
6
|
},
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"googleapis": "^164.1.0",
|
|
17
17
|
"inquirer": "^8.2.7",
|
|
18
18
|
"marked": "^15.0.12",
|
|
19
|
+
"marked-plaintext": "^0.0.2",
|
|
19
20
|
"marked-terminal": "^7.3.0",
|
|
20
21
|
"minimatch": "^10.2.4",
|
|
21
22
|
"uuid": "^13.0.0",
|