jvcs 1.6.2 → 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.
@@ -1,143 +1,81 @@
1
1
  const blessed = require("blessed");
2
2
  const aiAnalyzer = require("./aiAnalyzer");
3
- const { marked } = require("marked");
4
- const { TerminalRenderer } = require("marked-terminal");
5
-
6
- marked.setOptions({
7
- renderer: TerminalRenderer
8
- });
9
3
 
10
4
  function startUI(state) {
11
5
  const screen = blessed.screen({
12
6
  smartCSR: true,
13
7
  title: "JVCS Diff Viewer"
14
- })
8
+ });
15
9
 
16
10
  // ---------------------------------------
17
- // HEADER
11
+ // UI COMPONENTS
18
12
  // ---------------------------------------
19
13
  const header = blessed.box({
20
- top: 0,
21
- height: 1,
22
- width: "100%",
14
+ top: 0, height: 1, width: "100%",
23
15
  content: ` JVCS DIFF VIEW (${state.mode}) | ↑↓ Navigate | Enter Open | Esc Back | Tab Switch | Q Quit `,
24
- style: {
25
- fg: "white",
26
- bg: "blue",
27
- bold: true,
28
- underline: true
29
- },
16
+ style: { fg: "white", bg: "blue", bold: true, underline: true },
30
17
  tags: true
31
- })
32
- screen.append(header)
18
+ });
19
+ screen.append(header);
33
20
 
34
- // ---------------------------------------
35
- // FILE LIST VIEW
36
- // ---------------------------------------
37
21
  const fileList = blessed.list({
38
- top: 1,
39
- left: 0,
40
- width: "100%",
41
- height: "100%-1",
42
- keys: true,
43
- vi: true,
44
- border: "line",
22
+ top: 1, left: 0, width: "100%", height: "100%-1",
23
+ keys: true, vi: true, border: "line",
45
24
  label: " Changed Files ",
46
- style: {
47
- selected: { bg: "blue" },
48
- item: { fg: "white" }
49
- },
25
+ style: { selected: { bg: "blue" }, item: { fg: "white" } },
50
26
  tags: true
51
- })
52
- screen.append(fileList)
27
+ });
28
+ screen.append(fileList);
53
29
 
54
- function renderFileList() {
55
- const items = state.files.map(file => {
56
- let statusColored
57
- 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()
62
- }
63
- return `${statusColored} ${file.path}`
64
- })
65
- fileList.setItems(items)
66
- fileList.select(state.selectedIndex)
67
- fileList.focus()
68
- screen.render()
69
- }
30
+ const panelOptions = {
31
+ top: 1, height: "100%-1", border: "line",
32
+ scrollable: true, alwaysScroll: true, keys: true, vi: true,
33
+ scrollbar: { ch: "│", track: { bg: "black" }, style: { bg: "yellow" } },
34
+ padding: { left: 1, right: 1 }, tags: true
35
+ };
36
+
37
+ const leftBox = blessed.box({ ...panelOptions, left: 0, width: "33%", label: " LEFT " });
38
+ const rightBox = blessed.box({ ...panelOptions, left: "33%", width: "34%", label: " RIGHT " });
39
+ const aiBox = blessed.box({ ...panelOptions, left: "67%", width: "33%", label: " AI ANALYSIS " });
70
40
 
71
41
  // ---------------------------------------
72
- // FILE VIEW (3 PANELS)
42
+ // HELPERS
73
43
  // ---------------------------------------
74
- const leftBox = blessed.box({
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({
91
- top: 1,
92
- left: "33%",
93
- width: "34%",
94
- height: "100%-1",
95
- border: "line",
96
- label: " RIGHT ",
97
- scrollable: true,
98
- alwaysScroll: true,
99
- keys: true,
100
- vi: true,
101
- scrollbar: { ch: "│", track: { bg: "black" }, style: { bg: "yellow" } },
102
- padding: { left: 1, right: 1 },
103
- tags: true
104
- })
105
-
106
- const aiBox = blessed.box({
107
- top: 1,
108
- left: "67%",
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
- })
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
+ }
121
54
 
122
55
  function colorDiffContent(content, type) {
56
+ if (!content) return "";
123
57
  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")
58
+ if(type === "added") return `{green-fg}+ ${line}{/green-fg}`;
59
+ if(type === "removed") return `{red-fg}- ${line}{/red-fg}`;
60
+ return ` ${line}`;
61
+ }).join("\n");
128
62
  }
129
63
 
64
+ // ---------------------------------------
65
+ // RENDER LOGIC
66
+ // ---------------------------------------
130
67
  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"))
68
+ const file = state.getCurrentFile();
69
+ if (!file) return;
136
70
 
71
+ leftBox.setContent(colorDiffContent(file.leftContent, "removed"));
72
+ rightBox.setContent(colorDiffContent(file.rightContent, "added"));
137
73
  aiBox.setContent("AI Analysis: Loading...");
138
- screen.append(leftBox)
139
- screen.append(rightBox)
140
- screen.append(aiBox)
74
+
75
+ screen.append(leftBox);
76
+ screen.append(rightBox);
77
+ screen.append(aiBox);
78
+
141
79
  updateActiveTabHighlight();
142
80
  screen.render();
143
81
 
@@ -147,85 +85,46 @@ function startUI(state) {
147
85
  leftContent: file.leftContent,
148
86
  rightContent: file.rightContent,
149
87
  mode: state.mode
150
- })
88
+ });
151
89
 
152
- const parsed = marked(aiSummary); // Markdown terminal-friendly text
153
- aiBox.setContent(parsed);
154
- screen.render()
90
+ // Convert to clean text for terminal
91
+ aiBox.setContent(stripMarkdown(aiSummary));
92
+ screen.render();
155
93
  }
156
94
  catch(error) {
157
- aiBox.setContent(`AI Analysis failed: ${error.message || error}`);
158
- screen.render()
95
+ aiBox.setContent(`{red-fg}AI Analysis failed: ${error.message || error}{/red-fg}`);
96
+ screen.render();
159
97
  }
160
98
  }
161
99
 
162
- function destroyFileView() {
163
- leftBox.detach()
164
- rightBox.detach()
165
- aiBox.detach()
166
- screen.render()
167
- }
168
-
169
100
  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"
173
-
174
- if(state.activeTab === 0) leftBox.focus()
175
- else if(state.activeTab === 1) rightBox.focus()
176
- else aiBox.focus()
177
-
178
- screen.render()
101
+ leftBox.style.border.fg = state.activeTab === 0 ? "yellow" : "grey";
102
+ rightBox.style.border.fg = state.activeTab === 1 ? "yellow" : "grey";
103
+ aiBox.style.border.fg = state.activeTab === 2 ? "yellow" : "grey";
104
+ if(state.activeTab === 0) leftBox.focus();
105
+ else if(state.activeTab === 1) rightBox.focus();
106
+ else aiBox.focus();
107
+ screen.render();
179
108
  }
180
109
 
181
110
  // ---------------------------------------
182
- // KEYBOARD CONTROLS
111
+ // INPUT HANDLING
183
112
  // ---------------------------------------
184
- screen.key(["q", "C-c"], () => process.exit(0))
185
-
186
- // Navigate list
187
- fileList.key(["up"], () => {
188
- if(state.selectedIndex > 0) {
189
- state.selectFile(state.selectedIndex - 1)
190
- renderFileList()
191
- }
192
- })
193
- fileList.key(["down"], () => {
194
- if(state.selectedIndex < state.files.length - 1) {
195
- state.selectFile(state.selectedIndex + 1)
196
- renderFileList()
197
- }
198
- })
199
-
200
- // Enter → Open file view
201
- fileList.key(["enter"], async () => {
202
- state.goToFileView()
203
- fileList.detach()
204
- await renderFileView()
205
- })
206
-
207
- // ESC → Back to list
208
- screen.key(["escape"], () => {
209
- if(state.screen === "FILE_VIEW") {
210
- state.goToListView()
211
- destroyFileView()
212
- screen.append(fileList)
213
- renderFileList()
214
- }
215
- })
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(); }});
216
119
 
217
- // TAB → switch active panel
218
- screen.key(["tab"], () => {
219
- if(state.screen === "FILE_VIEW") {
220
- state.switchTab((state.activeTab + 1) % 3)
221
- updateActiveTabHighlight()
222
- }
223
- })
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
+ }
224
126
 
225
- // ---------------------------------------
226
- // INITIAL RENDER
227
- // ---------------------------------------
228
- renderFileList()
127
+ renderFileList();
229
128
  }
230
129
 
231
- module.exports = startUI
130
+ module.exports = startUI;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jvcs",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
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",