jvcs 1.6.3 → 1.6.4
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 +40 -105
- package/package.json +1 -1
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
const blessed = require("blessed");
|
|
2
2
|
const aiAnalyzer = require("./aiAnalyzer");
|
|
3
|
-
const { marked } = require("marked");
|
|
4
|
-
const { PlaintextRenderer } = require("marked-plaintext");
|
|
5
3
|
|
|
6
4
|
function startUI(state) {
|
|
7
5
|
const screen = blessed.screen({
|
|
@@ -10,80 +8,50 @@ function startUI(state) {
|
|
|
10
8
|
});
|
|
11
9
|
|
|
12
10
|
// ---------------------------------------
|
|
13
|
-
//
|
|
11
|
+
// UI COMPONENTS
|
|
14
12
|
// ---------------------------------------
|
|
15
13
|
const header = blessed.box({
|
|
16
|
-
top: 0,
|
|
17
|
-
height: 1,
|
|
18
|
-
width: "100%",
|
|
14
|
+
top: 0, height: 1, width: "100%",
|
|
19
15
|
content: ` JVCS DIFF VIEW (${state.mode}) | ↑↓ Navigate | Enter Open | Esc Back | Tab Switch | Q Quit `,
|
|
20
|
-
style: {
|
|
21
|
-
fg: "white",
|
|
22
|
-
bg: "blue",
|
|
23
|
-
bold: true,
|
|
24
|
-
underline: true
|
|
25
|
-
},
|
|
16
|
+
style: { fg: "white", bg: "blue", bold: true, underline: true },
|
|
26
17
|
tags: true
|
|
27
18
|
});
|
|
28
19
|
screen.append(header);
|
|
29
20
|
|
|
30
|
-
// ---------------------------------------
|
|
31
|
-
// FILE LIST VIEW
|
|
32
|
-
// ---------------------------------------
|
|
33
21
|
const fileList = blessed.list({
|
|
34
|
-
top: 1,
|
|
35
|
-
|
|
36
|
-
width: "100%",
|
|
37
|
-
height: "100%-1",
|
|
38
|
-
keys: true,
|
|
39
|
-
vi: true,
|
|
40
|
-
border: "line",
|
|
22
|
+
top: 1, left: 0, width: "100%", height: "100%-1",
|
|
23
|
+
keys: true, vi: true, border: "line",
|
|
41
24
|
label: " Changed Files ",
|
|
42
|
-
style: {
|
|
43
|
-
selected: { bg: "blue" },
|
|
44
|
-
item: { fg: "white" }
|
|
45
|
-
},
|
|
25
|
+
style: { selected: { bg: "blue" }, item: { fg: "white" } },
|
|
46
26
|
tags: true
|
|
47
27
|
});
|
|
48
28
|
screen.append(fileList);
|
|
49
29
|
|
|
50
|
-
function renderFileList() {
|
|
51
|
-
const items = state.files.map(file => {
|
|
52
|
-
let statusColored;
|
|
53
|
-
switch(file.status) {
|
|
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();
|
|
58
|
-
}
|
|
59
|
-
return `${statusColored} ${file.path}`;
|
|
60
|
-
});
|
|
61
|
-
fileList.setItems(items);
|
|
62
|
-
fileList.select(state.selectedIndex);
|
|
63
|
-
fileList.focus();
|
|
64
|
-
screen.render();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// ---------------------------------------
|
|
68
|
-
// FILE VIEW PANELS
|
|
69
|
-
// ---------------------------------------
|
|
70
30
|
const panelOptions = {
|
|
71
|
-
top: 1,
|
|
72
|
-
|
|
73
|
-
border: "line",
|
|
74
|
-
scrollable: true,
|
|
75
|
-
alwaysScroll: true,
|
|
76
|
-
keys: true,
|
|
77
|
-
vi: true,
|
|
31
|
+
top: 1, height: "100%-1", border: "line",
|
|
32
|
+
scrollable: true, alwaysScroll: true, keys: true, vi: true,
|
|
78
33
|
scrollbar: { ch: "│", track: { bg: "black" }, style: { bg: "yellow" } },
|
|
79
|
-
padding: { left: 1, right: 1 },
|
|
80
|
-
tags: true
|
|
34
|
+
padding: { left: 1, right: 1 }, tags: true
|
|
81
35
|
};
|
|
82
36
|
|
|
83
37
|
const leftBox = blessed.box({ ...panelOptions, left: 0, width: "33%", label: " LEFT " });
|
|
84
38
|
const rightBox = blessed.box({ ...panelOptions, left: "33%", width: "34%", label: " RIGHT " });
|
|
85
39
|
const aiBox = blessed.box({ ...panelOptions, left: "67%", width: "33%", label: " AI ANALYSIS " });
|
|
86
40
|
|
|
41
|
+
// ---------------------------------------
|
|
42
|
+
// HELPERS
|
|
43
|
+
// ---------------------------------------
|
|
44
|
+
function stripMarkdown(text) {
|
|
45
|
+
return text
|
|
46
|
+
.replace(/#{1,6}\s/g, "") // Remove headers
|
|
47
|
+
.replace(/\*\*(.*?)\*\*/g, "$1") // Bold to text
|
|
48
|
+
.replace(/\*(.*?)\*/g, "$1") // Italics to text
|
|
49
|
+
.replace(/`(.*?)`/g, "$1") // Code ticks to text
|
|
50
|
+
.replace(/\[(.*?)\]\(.*?\)/g, "$1") // Links to text
|
|
51
|
+
.replace(/^>\s/gm, "") // Blockquotes
|
|
52
|
+
.replace(/^- /gm, "• "); // List markers to bullets
|
|
53
|
+
}
|
|
54
|
+
|
|
87
55
|
function colorDiffContent(content, type) {
|
|
88
56
|
if (!content) return "";
|
|
89
57
|
return content.split("\n").map(line => {
|
|
@@ -93,6 +61,9 @@ function startUI(state) {
|
|
|
93
61
|
}).join("\n");
|
|
94
62
|
}
|
|
95
63
|
|
|
64
|
+
// ---------------------------------------
|
|
65
|
+
// RENDER LOGIC
|
|
66
|
+
// ---------------------------------------
|
|
96
67
|
async function renderFileView() {
|
|
97
68
|
const file = state.getCurrentFile();
|
|
98
69
|
if (!file) return;
|
|
@@ -116,11 +87,8 @@ function startUI(state) {
|
|
|
116
87
|
mode: state.mode
|
|
117
88
|
});
|
|
118
89
|
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
const cleanText = marked.parse(aiSummary, { renderer: renderer });
|
|
122
|
-
|
|
123
|
-
aiBox.setContent(cleanText);
|
|
90
|
+
// Convert to clean text for terminal
|
|
91
|
+
aiBox.setContent(stripMarkdown(aiSummary));
|
|
124
92
|
screen.render();
|
|
125
93
|
}
|
|
126
94
|
catch(error) {
|
|
@@ -129,65 +97,32 @@ function startUI(state) {
|
|
|
129
97
|
}
|
|
130
98
|
}
|
|
131
99
|
|
|
132
|
-
function destroyFileView() {
|
|
133
|
-
leftBox.detach();
|
|
134
|
-
rightBox.detach();
|
|
135
|
-
aiBox.detach();
|
|
136
|
-
screen.render();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
100
|
function updateActiveTabHighlight() {
|
|
140
101
|
leftBox.style.border.fg = state.activeTab === 0 ? "yellow" : "grey";
|
|
141
102
|
rightBox.style.border.fg = state.activeTab === 1 ? "yellow" : "grey";
|
|
142
103
|
aiBox.style.border.fg = state.activeTab === 2 ? "yellow" : "grey";
|
|
143
|
-
|
|
144
104
|
if(state.activeTab === 0) leftBox.focus();
|
|
145
105
|
else if(state.activeTab === 1) rightBox.focus();
|
|
146
106
|
else aiBox.focus();
|
|
147
|
-
|
|
148
107
|
screen.render();
|
|
149
108
|
}
|
|
150
109
|
|
|
151
110
|
// ---------------------------------------
|
|
152
|
-
//
|
|
111
|
+
// INPUT HANDLING
|
|
153
112
|
// ---------------------------------------
|
|
154
113
|
screen.key(["q", "C-c"], () => process.exit(0));
|
|
114
|
+
fileList.key(["up"], () => { if(state.selectedIndex > 0) { state.selectFile(state.selectedIndex - 1); renderFileList(); }});
|
|
115
|
+
fileList.key(["down"], () => { if(state.selectedIndex < state.files.length - 1) { state.selectFile(state.selectedIndex + 1); renderFileList(); }});
|
|
116
|
+
fileList.key(["enter"], async () => { state.goToFileView(); fileList.detach(); await renderFileView(); });
|
|
117
|
+
screen.key(["escape"], () => { if(state.screen === "FILE_VIEW") { state.goToListView(); leftBox.detach(); rightBox.detach(); aiBox.detach(); screen.append(fileList); renderFileList(); }});
|
|
118
|
+
screen.key(["tab"], () => { if(state.screen === "FILE_VIEW") { state.switchTab((state.activeTab + 1) % 3); updateActiveTabHighlight(); }});
|
|
155
119
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
fileList.key(["down"], () => {
|
|
164
|
-
if(state.selectedIndex < state.files.length - 1) {
|
|
165
|
-
state.selectFile(state.selectedIndex + 1);
|
|
166
|
-
renderFileList();
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
fileList.key(["enter"], async () => {
|
|
171
|
-
state.goToFileView();
|
|
172
|
-
fileList.detach();
|
|
173
|
-
await renderFileView();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
screen.key(["escape"], () => {
|
|
177
|
-
if(state.screen === "FILE_VIEW") {
|
|
178
|
-
state.goToListView();
|
|
179
|
-
destroyFileView();
|
|
180
|
-
screen.append(fileList);
|
|
181
|
-
renderFileList();
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
screen.key(["tab"], () => {
|
|
186
|
-
if(state.screen === "FILE_VIEW") {
|
|
187
|
-
state.switchTab((state.activeTab + 1) % 3);
|
|
188
|
-
updateActiveTabHighlight();
|
|
189
|
-
}
|
|
190
|
-
});
|
|
120
|
+
function renderFileList() {
|
|
121
|
+
fileList.setItems(state.files.map(f => `${f.status === "added" ? "{green-fg}" : f.status === "deleted" ? "{red-fg}" : "{yellow-fg}"}${f.status.toUpperCase()}{/} ${f.path}`));
|
|
122
|
+
fileList.select(state.selectedIndex);
|
|
123
|
+
fileList.focus();
|
|
124
|
+
screen.render();
|
|
125
|
+
}
|
|
191
126
|
|
|
192
127
|
renderFileList();
|
|
193
128
|
}
|