termux-dev 1.4.16 → 1.4.17

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/dist/cli/index.js CHANGED
@@ -484,7 +484,7 @@ function drawLogo() {
484
484
  theme.colorFn(' █▀▀▄ █▀▀▀ █ █ █ █'),
485
485
  theme.colorFn(' █ █ █▀▀▀ ▀▄▀ ▀▄▀ '),
486
486
  theme.colorFn(' █▄▄▀ █▄▄▄ ▀ ▀ ▀ '),
487
- ' ' + theme.boldFn('v1.4.16'),
487
+ ' ' + theme.boldFn('v1.4.17'),
488
488
  ''
489
489
  ];
490
490
  for (const line of logo) {
@@ -499,7 +499,7 @@ function drawLogo() {
499
499
  indent + theme.colorFn('▀▀▀█▀▀▀ █▀▀▀ █▀▀█ █▄ ▄█ █ █ ▀▄ ▄▀ █▀▀▄ █▀▀▀ █ █'),
500
500
  indent + theme.colorFn(' █ █▀▀▀ █▄▄▀ █ █ █ █ █ █ ▀▀ █ █ █▀▀▀ █ █'),
501
501
  indent + theme.colorFn(' █ █▄▄▄ █ ▀▄ █ █ ▀▄▄▀ ▄▀ ▀▄ █▄▄▀ █▄▄▄ ▀▄▀ '),
502
- indent + theme.boldFn('v1.4.16'),
502
+ indent + theme.boldFn('v1.4.17'),
503
503
  ''
504
504
  ];
505
505
  for (const line of logo) {
@@ -645,7 +645,7 @@ async function handleSettings(config) {
645
645
  const maxIterLabel = maxIter >= 9999 ? 'Unlimited' : `${maxIter} steps`;
646
646
  const currentTh = getCurrentTheme();
647
647
  const choice = await select({
648
- message: `${pc.bold('⚙️ Settings')} ${pc.dim(`(devx v1.4.16 • theme: ${currentTh.name})`)}`,
648
+ message: `${pc.bold('⚙️ Settings')} ${pc.dim(`(devx v1.4.17 • theme: ${currentTh.name})`)}`,
649
649
  choices: [
650
650
  {
651
651
  name: `🎨 Color Theme: ${currentTh.emoji} ${currentTh.name}`,
@@ -686,7 +686,7 @@ async function handleSettings(config) {
686
686
  description: 'Limit how many tool steps (file edits, terminal commands) agent can do per request'
687
687
  },
688
688
  {
689
- name: `${currentTh.colorFn('✨ About devx')} ${pc.dim('(v1.4.16 by ApvCode)')}`,
689
+ name: `${currentTh.colorFn('✨ About devx')} ${pc.dim('(v1.4.17 by ApvCode)')}`,
690
690
  value: 'about',
691
691
  description: 'Terminal-Native AI Coding Agent created by ApvCode (https://github.com/apvcode/Termux-Dev)'
692
692
  },
@@ -702,7 +702,7 @@ async function handleSettings(config) {
702
702
  continue;
703
703
  }
704
704
  if (choice === 'about') {
705
- p.note(`⚡ devx v1.4.16 — Terminal-Native AI Coding Agent\n` +
705
+ p.note(`⚡ devx v1.4.17 — Terminal-Native AI Coding Agent\n` +
706
706
  `🎨 Theme: ${currentTh.emoji} ${currentTh.name}\n` +
707
707
  `👤 Author: ApvCode (https://github.com/apvcode)\n` +
708
708
  `🌟 Repository: https://github.com/apvcode/Termux-Dev\n` +
@@ -53,7 +53,6 @@ export function askPrompt(opts = {}) {
53
53
  let input = opts.initialValue || '';
54
54
  let cursorPos = input.length;
55
55
  let selectedIndex = 0;
56
- let lastRenderedDropdownLines = 0;
57
56
  let historyIndex = GLOBAL_PROMPT_HISTORY.length;
58
57
  let tempDraft = '';
59
58
  const imageAttachments = [];
@@ -169,78 +168,7 @@ export function askPrompt(opts = {}) {
169
168
  if (disposed)
170
169
  return;
171
170
  const cols = Math.max(20, process.stdout.columns || 40);
172
- const rows = Math.max(8, process.stdout.rows || 20);
173
171
  const items = getDropdownItems();
174
- const dropdownLines = [];
175
- if (items.length > 0) {
176
- if (selectedIndex >= items.length)
177
- selectedIndex = items.length - 1;
178
- if (selectedIndex < 0)
179
- selectedIndex = 0;
180
- // On very small screens (keyboard open on Android), limit dropdown aggressively
181
- const maxDropdownRows = Math.max(2, Math.min(rows - 3, 6));
182
- const pageSize = Math.min(4, Math.max(2, Math.min(maxDropdownRows - 2, Math.floor(rows / 4))));
183
- const innerBoxWidth = Math.max(10, Math.min(cols - 8, 48));
184
- const total = items.length;
185
- let startIndex = 0;
186
- if (total > pageSize) {
187
- if (selectedIndex >= pageSize) {
188
- startIndex = Math.min(selectedIndex - pageSize + 1, total - pageSize);
189
- }
190
- }
191
- const endIndex = Math.min(startIndex + pageSize, total);
192
- const hasMoreUp = startIndex > 0;
193
- const hasMoreDown = endIndex < total;
194
- let topBorderStr = '─'.repeat(innerBoxWidth);
195
- if (hasMoreUp) {
196
- const mid = Math.max(0, Math.floor(innerBoxWidth / 2) - 2);
197
- topBorderStr = '─'.repeat(mid) + ' ▲ ' + '─'.repeat(Math.max(0, innerBoxWidth - mid - 3));
198
- }
199
- let botBorderStr = '─'.repeat(innerBoxWidth);
200
- if (hasMoreDown) {
201
- const mid = Math.max(0, Math.floor(innerBoxWidth / 2) - 2);
202
- botBorderStr = '─'.repeat(mid) + ' ▼ ' + '─'.repeat(Math.max(0, innerBoxWidth - mid - 3));
203
- }
204
- dropdownLines.push(pc.dim('│') + ' ' + pc.dim('╭' + topBorderStr + '╮') + '\x1b[0m');
205
- for (let i = startIndex; i < endIndex; i++) {
206
- const item = items[i];
207
- const isSelected = i === selectedIndex;
208
- const pointer = isSelected ? '› ' : ' ';
209
- const availWidth = innerBoxWidth;
210
- let row = '';
211
- if (availWidth < 20) {
212
- const labelStr = item.label.length > availWidth - 2
213
- ? item.label.slice(0, availWidth - 3) + '…'
214
- : item.label.padEnd(availWidth - 2, ' ');
215
- const plain = (pointer + labelStr).padEnd(availWidth, ' ').slice(0, availWidth);
216
- row = isSelected
217
- ? theme.badgeFn(plain) + '\x1b[0m'
218
- : pointer + theme.boldFn(labelStr) + '\x1b[0m';
219
- }
220
- else {
221
- const labelMax = Math.min(13, Math.max(6, Math.floor(availWidth * 0.35)));
222
- const labelStr = item.label.length > labelMax
223
- ? item.label.slice(0, labelMax - 1) + '…'
224
- : item.label.padEnd(labelMax, ' ');
225
- const descMax = Math.max(4, availWidth - labelMax - pointer.length - 1);
226
- const descStr = item.desc.length > descMax
227
- ? item.desc.slice(0, descMax - 1) + '…'
228
- : item.desc.padEnd(descMax, ' ');
229
- const plain = (pointer + labelStr + ' ' + descStr).padEnd(availWidth, ' ').slice(0, availWidth);
230
- if (isSelected) {
231
- row = theme.badgeFn(plain) + '\x1b[0m';
232
- }
233
- else {
234
- row = `${pointer}${theme.boldFn(labelStr)} ${pc.gray(descStr)}\x1b[0m`;
235
- }
236
- }
237
- dropdownLines.push(pc.dim('│') + ' ' + pc.dim('│') + row + pc.dim('│') + '\x1b[0m');
238
- }
239
- dropdownLines.push(pc.dim('│') + ' ' + pc.dim('╰' + botBorderStr + '╯') + '\x1b[0m');
240
- }
241
- // === SAFE RENDERING: Use \x1b7/\x1b8 save/restore + \x1b[B (down) instead of \n ===
242
- // This prevents hardware scroll when cursor is at the bottom of a small screen.
243
- // 1. Format and write the Prompt line safely (guaranteed <= cols - 1 chars)
244
172
  let inputDisplay = pc.dim('│') + ' ';
245
173
  let renderCursorCol = 3;
246
174
  if (input.length === 0) {
@@ -250,9 +178,28 @@ export function askPrompt(opts = {}) {
250
178
  renderCursorCol = 3;
251
179
  }
252
180
  else {
181
+ if (selectedIndex >= items.length)
182
+ selectedIndex = Math.max(0, items.length - 1);
183
+ if (selectedIndex < 0)
184
+ selectedIndex = 0;
185
+ const selected = items.length > 0 ? items[selectedIndex] : null;
186
+ // Calculate ghost autocomplete text & badge if available
187
+ let ghostText = '';
188
+ let hintBadge = '';
189
+ if (selected) {
190
+ if (input.startsWith('/') && selected.label.toLowerCase().startsWith(input.toLowerCase())) {
191
+ ghostText = selected.label.slice(input.length);
192
+ const remainingWidth = cols - (3 + input.length + ghostText.length + 8);
193
+ if (remainingWidth > 12) {
194
+ const maxDesc = Math.min(remainingWidth, 32);
195
+ const descStr = selected.desc.length > maxDesc ? selected.desc.slice(0, maxDesc - 1) + '…' : selected.desc;
196
+ hintBadge = ' ' + pc.dim('•') + ' ' + pc.gray(descStr);
197
+ }
198
+ }
199
+ }
253
200
  const maxInputLen = Math.max(10, cols - 6);
254
201
  if (input.length <= maxInputLen) {
255
- inputDisplay += formatInputWithBadges(input) + '\x1b[0m';
202
+ inputDisplay += formatInputWithBadges(input) + (ghostText ? pc.dim(ghostText) : '') + hintBadge + '\x1b[0m';
256
203
  renderCursorCol = 3 + cursorPos;
257
204
  }
258
205
  else {
@@ -265,26 +212,8 @@ export function askPrompt(opts = {}) {
265
212
  renderCursorCol = 3 + (prefix ? 1 : 0) + (cursorPos - start);
266
213
  }
267
214
  }
268
- // Write prompt line (no \n — stays on same line)
269
- process.stdout.write(`\x1b[0m\r\x1b[2K${inputDisplay}\x1b[0m`);
270
- // 2. Draw dropdown below using \x1b[1B (cursor down) + \r without scrolling viewport
271
- const maxDropdowns = Math.max(dropdownLines.length, lastRenderedDropdownLines);
272
- if (maxDropdowns > 0) {
273
- // Save cursor position on the prompt line
274
- process.stdout.write('\x1b7');
275
- for (let i = 0; i < maxDropdowns; i++) {
276
- // Move to next line: \x1b[1B moves down 1 without scrolling, \r returns to col 0, \x1b[2K clears line
277
- process.stdout.write(`\x1b[1B\r\x1b[0m\x1b[2K`);
278
- if (i < dropdownLines.length) {
279
- process.stdout.write(dropdownLines[i] + '\x1b[0m');
280
- }
281
- }
282
- // Restore cursor position (back to prompt line)
283
- process.stdout.write('\x1b8');
284
- }
285
- // 3. Place cursor precisely on the prompt line
286
- process.stdout.write(`\r\x1b[${renderCursorCol}C`);
287
- lastRenderedDropdownLines = dropdownLines.length;
215
+ // Write prompt line cleanly (stays strictly on line 0, 0 scrolling, 0 vertical jumps)
216
+ process.stdout.write(`\x1b[0m\r\x1b[2K${inputDisplay}\x1b[0m\r\x1b[${renderCursorCol}C`);
288
217
  }
289
218
  render();
290
219
  function cleanup() {
@@ -300,14 +229,6 @@ export function askPrompt(opts = {}) {
300
229
  process.stdin.pause();
301
230
  }
302
231
  function clearBoxAndExit(finalInput) {
303
- if (lastRenderedDropdownLines > 0) {
304
- process.stdout.write('\x1b7');
305
- for (let i = 0; i < lastRenderedDropdownLines; i++) {
306
- process.stdout.write(`\x1b[1B\r\x1b[0m\x1b[2K`);
307
- }
308
- process.stdout.write('\x1b8');
309
- lastRenderedDropdownLines = 0;
310
- }
311
232
  const fullText = expandPastes(finalInput);
312
233
  if (fullText.trim()) {
313
234
  if (GLOBAL_PROMPT_HISTORY.length === 0 ||
@@ -359,14 +280,6 @@ export function askPrompt(opts = {}) {
359
280
  }
360
281
  // Ctrl+C / SIGINT
361
282
  if (str === '\x03') {
362
- if (lastRenderedDropdownLines > 0) {
363
- process.stdout.write('\x1b7');
364
- for (let i = 0; i < lastRenderedDropdownLines; i++) {
365
- process.stdout.write(`\x1b[1B\r\x1b[0m\x1b[2K`);
366
- }
367
- process.stdout.write('\x1b8');
368
- lastRenderedDropdownLines = 0;
369
- }
370
283
  process.stdout.write(`\x1b[0m\r\x1b[2K\n`);
371
284
  cleanup();
372
285
  resolve('__CANCEL__');
@@ -433,21 +346,23 @@ export function askPrompt(opts = {}) {
433
346
  if (str === '\t' || (str.length === 1 && str.charCodeAt(0) === 9)) {
434
347
  if (items.length > 0 && selectedIndex >= 0 && selectedIndex < items.length) {
435
348
  const selected = items[selectedIndex];
436
- input =
437
- input.slice(0, selected.replaceStart) +
438
- selected.replacement +
439
- input.slice(selected.replaceStart + selected.replaceLen);
440
- cursorPos = selected.replaceStart + selected.replacement.length;
441
- render();
349
+ if (input.startsWith('/')) {
350
+ input = selected.replacement + ' ';
351
+ cursorPos = input.length;
352
+ render();
353
+ return;
354
+ }
355
+ else {
356
+ input =
357
+ input.slice(0, selected.replaceStart) +
358
+ selected.replacement +
359
+ input.slice(selected.replaceStart + selected.replaceLen);
360
+ cursorPos = selected.replaceStart + selected.replacement.length;
361
+ render();
362
+ return;
363
+ }
442
364
  }
443
365
  else {
444
- if (lastRenderedDropdownLines > 0) {
445
- for (let i = 0; i < lastRenderedDropdownLines; i++) {
446
- process.stdout.write(`\n\x1b[0m\x1b[2K`);
447
- }
448
- process.stdout.write(`\x1b[0m\x1b[${lastRenderedDropdownLines}A`);
449
- lastRenderedDropdownLines = 0;
450
- }
451
366
  process.stdout.write(`\x1b[0m\r\x1b[2K`);
452
367
  cleanup();
453
368
  resolve(`__TOGGLE_MODE__:${input}`);
@@ -139,7 +139,7 @@ function renderDirectoryHtml(dirPath, relPath, files, port) {
139
139
  ${parentLink}
140
140
  ${items || '<li style="padding: 20px; text-align: center; color: #6e7681;">No visible files in this directory</li>'}
141
141
  </ul>
142
- <div class="footer">devx v1.4.16 &bull; Terminal-Native AI Assistant</div>
142
+ <div class="footer">devx v1.4.17 &bull; Terminal-Native AI Assistant</div>
143
143
  </div>
144
144
  </body>
145
145
  </html>`;
@@ -63,7 +63,7 @@ export class MCPClient {
63
63
  capabilities: {},
64
64
  clientInfo: {
65
65
  name: 'devx',
66
- version: '1.4.16'
66
+ version: '1.4.17'
67
67
  }
68
68
  });
69
69
  if (!initResult) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termux-dev",
3
- "version": "1.4.16",
3
+ "version": "1.4.17",
4
4
  "description": "Ultra-fast, terminal-native AI coding assistant and agent built for Android Termux, Windows, macOS, and Linux.",
5
5
  "main": "dist/cli/index.js",
6
6
  "type": "module",