omnibiofex 2.6.0 → 2.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/package.json +1 -1
- package/src/utils/display.js +276 -21
package/package.json
CHANGED
package/src/utils/display.js
CHANGED
|
@@ -316,39 +316,292 @@ async function displayMissionDashboard(mission) {
|
|
|
316
316
|
console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════\n'));
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
// ====================
|
|
319
|
+
// ==================== HELPERS FOR TABLE RENDERING ====================
|
|
320
|
+
function formatTable(rows) {
|
|
321
|
+
if (!rows || rows.length === 0) return '';
|
|
322
|
+
|
|
323
|
+
// Calculate column widths
|
|
324
|
+
const colWidths = [];
|
|
325
|
+
rows.forEach(row => {
|
|
326
|
+
row.forEach((cell, i) => {
|
|
327
|
+
colWidths[i] = Math.max(colWidths[i] || 0, cell.length);
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// Format each row
|
|
332
|
+
return rows.map((row, rowIndex) => {
|
|
333
|
+
const isHeader = rowIndex === 0;
|
|
334
|
+
const formatted = row.map((cell, i) => {
|
|
335
|
+
const padded = cell.padEnd(colWidths[i]);
|
|
336
|
+
return isHeader ? chalk.white.bold(padded) : chalk.gray(padded);
|
|
337
|
+
}).join(' │ ');
|
|
338
|
+
|
|
339
|
+
return chalk.gray('│ ') + formatted + chalk.gray(' │');
|
|
340
|
+
}).join('\n');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function addVisualBreak() {
|
|
344
|
+
console.log(chalk.gray('\n' + '·'.repeat(60) + '\n'));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ==================== INLINE MARKDOWN PARSER ====================
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Parse a line and return array of segments with formatting
|
|
351
|
+
* Handles: **bold**, *italic*, `code`, and combinations
|
|
352
|
+
*/
|
|
353
|
+
function parseInlineMarkdown(text) {
|
|
354
|
+
const segments = [];
|
|
355
|
+
let remaining = text;
|
|
356
|
+
|
|
357
|
+
while (remaining.length > 0) {
|
|
358
|
+
// Find the next markdown pattern
|
|
359
|
+
const boldMatch = remaining.match(/\*\*(.+?)\*\*/);
|
|
360
|
+
const italicMatch = remaining.match(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/);
|
|
361
|
+
const codeMatch = remaining.match(/`([^`]+)`/);
|
|
362
|
+
|
|
363
|
+
// Find earliest match
|
|
364
|
+
let earliest = null;
|
|
365
|
+
let earliestIndex = remaining.length;
|
|
366
|
+
|
|
367
|
+
if (boldMatch && boldMatch.index < earliestIndex) {
|
|
368
|
+
earliest = { type: 'bold', match: boldMatch, index: boldMatch.index };
|
|
369
|
+
earliestIndex = boldMatch.index;
|
|
370
|
+
}
|
|
371
|
+
if (codeMatch && codeMatch.index < earliestIndex) {
|
|
372
|
+
earliest = { type: 'code', match: codeMatch, index: codeMatch.index };
|
|
373
|
+
earliestIndex = codeMatch.index;
|
|
374
|
+
}
|
|
375
|
+
if (italicMatch && italicMatch.index < earliestIndex) {
|
|
376
|
+
earliest = { type: 'italic', match: italicMatch, index: italicMatch.index };
|
|
377
|
+
earliestIndex = italicMatch.index;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (!earliest) {
|
|
381
|
+
// No more markdown, add remaining as plain text
|
|
382
|
+
if (remaining.length > 0) {
|
|
383
|
+
segments.push({ text: remaining, style: 'plain' });
|
|
384
|
+
}
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Add text before the match
|
|
389
|
+
if (earliestIndex > 0) {
|
|
390
|
+
segments.push({
|
|
391
|
+
text: remaining.substring(0, earliestIndex),
|
|
392
|
+
style: 'plain'
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Add the formatted segment
|
|
397
|
+
segments.push({
|
|
398
|
+
text: earliest.match[1],
|
|
399
|
+
style: earliest.type
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// Continue with remaining text
|
|
403
|
+
remaining = remaining.substring(earliestIndex + earliest.match[0].length);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return segments;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Type a line with inline markdown formatting (bold, italic, code)
|
|
411
|
+
*/
|
|
412
|
+
async function typeFormattedLine(text, speed = 8) {
|
|
413
|
+
const segments = parseInlineMarkdown(text);
|
|
414
|
+
|
|
415
|
+
for (const segment of segments) {
|
|
416
|
+
let colorFn = chalk.white;
|
|
417
|
+
|
|
418
|
+
switch (segment.style) {
|
|
419
|
+
case 'bold':
|
|
420
|
+
colorFn = chalk.white.bold;
|
|
421
|
+
break;
|
|
422
|
+
case 'italic':
|
|
423
|
+
colorFn = chalk.white.italic;
|
|
424
|
+
break;
|
|
425
|
+
case 'code':
|
|
426
|
+
colorFn = chalk.bgBlack.cyan;
|
|
427
|
+
break;
|
|
428
|
+
case 'plain':
|
|
429
|
+
default:
|
|
430
|
+
colorFn = chalk.white;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
await typeText(segment.text, speed, colorFn);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
process.stdout.write('\n');
|
|
437
|
+
}
|
|
438
|
+
|
|
320
439
|
/**
|
|
321
|
-
*
|
|
440
|
+
* Type a line with inline markdown, but use gray color for plain text
|
|
322
441
|
*/
|
|
442
|
+
async function typeFormattedLineGray(text, speed = 6) {
|
|
443
|
+
const segments = parseInlineMarkdown(text);
|
|
444
|
+
|
|
445
|
+
for (const segment of segments) {
|
|
446
|
+
let colorFn = chalk.gray;
|
|
447
|
+
|
|
448
|
+
switch (segment.style) {
|
|
449
|
+
case 'bold':
|
|
450
|
+
colorFn = chalk.white.bold;
|
|
451
|
+
break;
|
|
452
|
+
case 'italic':
|
|
453
|
+
colorFn = chalk.white.italic;
|
|
454
|
+
break;
|
|
455
|
+
case 'code':
|
|
456
|
+
colorFn = chalk.bgBlack.cyan;
|
|
457
|
+
break;
|
|
458
|
+
case 'plain':
|
|
459
|
+
default:
|
|
460
|
+
colorFn = chalk.gray;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
await typeText(segment.text, speed, colorFn);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
process.stdout.write('\n');
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// ==================== UPDATED AI RESPONSE RENDERER ====================
|
|
470
|
+
|
|
323
471
|
async function typeAIResponse(response) {
|
|
324
472
|
console.log(chalk.hex('#F24E1E').bold('\n📄 Research Report\n'));
|
|
325
|
-
console.log(chalk.gray('
|
|
473
|
+
console.log(chalk.gray('═'.repeat(60)) + '\n');
|
|
326
474
|
|
|
327
|
-
// Stream in chunks for natural feel (faster than char-by-char for long reports)
|
|
328
475
|
const lines = response.split('\n');
|
|
476
|
+
let inTable = false;
|
|
329
477
|
|
|
330
|
-
for (
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
}
|
|
478
|
+
for (let i = 0; i < lines.length; i++) {
|
|
479
|
+
const line = lines[i];
|
|
480
|
+
const trimmed = line.trim();
|
|
481
|
+
|
|
482
|
+
// Skip empty lines but add spacing
|
|
483
|
+
if (trimmed === '') {
|
|
484
|
+
if (!inTable) {
|
|
485
|
+
console.log('');
|
|
486
|
+
await sleep(80);
|
|
487
|
+
}
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Detect table rows (lines with |)
|
|
492
|
+
if (trimmed.includes('|') && trimmed.startsWith('|')) {
|
|
493
|
+
// Skip separator rows like |---|---| or | ---------- |
|
|
494
|
+
if (trimmed.match(/^\|[\s\-:]+\|$/)) {
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (!inTable) {
|
|
499
|
+
inTable = true;
|
|
500
|
+
console.log(chalk.gray('┌' + '─'.repeat(58) + '┐'));
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Parse table cells
|
|
504
|
+
const cells = trimmed.split('|').filter(c => c.trim() !== '').map(c => c.trim());
|
|
505
|
+
|
|
506
|
+
// Format table row with inline markdown support
|
|
507
|
+
if (cells.length > 0) {
|
|
508
|
+
const isHeader = i === 0 || (i > 0 && lines[i-1].trim() === '');
|
|
509
|
+
|
|
510
|
+
process.stdout.write(chalk.gray('│ '));
|
|
511
|
+
|
|
512
|
+
for (let j = 0; j < cells.length; j++) {
|
|
513
|
+
const cell = cells[j];
|
|
514
|
+
const padded = cell.padEnd(15);
|
|
515
|
+
|
|
516
|
+
if (isHeader) {
|
|
517
|
+
// Header cells - parse markdown and make bold
|
|
518
|
+
const segments = parseInlineMarkdown(padded);
|
|
519
|
+
for (const seg of segments) {
|
|
520
|
+
process.stdout.write(chalk.white.bold(seg.text));
|
|
521
|
+
}
|
|
522
|
+
} else {
|
|
523
|
+
// Regular cells - parse markdown
|
|
524
|
+
const segments = parseInlineMarkdown(padded);
|
|
525
|
+
for (const seg of segments) {
|
|
526
|
+
let colorFn = chalk.gray;
|
|
527
|
+
if (seg.style === 'bold') colorFn = chalk.white.bold;
|
|
528
|
+
else if (seg.style === 'italic') colorFn = chalk.white.italic;
|
|
529
|
+
else if (seg.style === 'code') colorFn = chalk.bgBlack.cyan;
|
|
530
|
+
process.stdout.write(colorFn(seg.text));
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (j < cells.length - 1) {
|
|
535
|
+
process.stdout.write(chalk.gray(' │ '));
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
console.log(chalk.gray(' │'));
|
|
540
|
+
await sleep(50);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
continue;
|
|
544
|
+
} else if (inTable) {
|
|
545
|
+
// End of table
|
|
546
|
+
console.log(chalk.gray('└' + '─'.repeat(58) + '┘'));
|
|
342
547
|
console.log('');
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
548
|
+
inTable = false;
|
|
549
|
+
await sleep(100);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Headers (## or #)
|
|
553
|
+
if (trimmed.startsWith('## ')) {
|
|
554
|
+
const header = trimmed.substring(3);
|
|
555
|
+
console.log('');
|
|
556
|
+
process.stdout.write(chalk.hex('#F24E1E').bold('▸ '));
|
|
557
|
+
await typeFormattedLine(header, 15);
|
|
558
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
559
|
+
await sleep(150);
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (trimmed.startsWith('# ')) {
|
|
564
|
+
const header = trimmed.substring(2);
|
|
347
565
|
console.log('');
|
|
566
|
+
process.stdout.write(chalk.hex('#F24E1E').bold.underline(' '));
|
|
567
|
+
await typeFormattedLine(header, 20);
|
|
568
|
+
console.log('');
|
|
569
|
+
await sleep(200);
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Bullet points
|
|
574
|
+
if (trimmed.startsWith('• ') || trimmed.startsWith('- ') || trimmed.startsWith('* ')) {
|
|
575
|
+
const bullet = trimmed.substring(2);
|
|
576
|
+
process.stdout.write(chalk.hex('#F24E1E')(' ▸ '));
|
|
577
|
+
await typeFormattedLine(bullet, 8);
|
|
578
|
+
await sleep(60);
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Numbered lists
|
|
583
|
+
const numberedMatch = trimmed.match(/^(\d+)\.\s+(.+)/);
|
|
584
|
+
if (numberedMatch) {
|
|
585
|
+
const num = numberedMatch[1];
|
|
586
|
+
const text = numberedMatch[2];
|
|
587
|
+
process.stdout.write(chalk.hex('#F24E1E')(` ${num}. `));
|
|
588
|
+
await typeFormattedLine(text, 8);
|
|
589
|
+
await sleep(60);
|
|
590
|
+
continue;
|
|
348
591
|
}
|
|
592
|
+
|
|
593
|
+
// Regular paragraph (with inline markdown support)
|
|
594
|
+
process.stdout.write(' ');
|
|
595
|
+
await typeFormattedLineGray(trimmed, 6);
|
|
596
|
+
await sleep(40);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Close any open table
|
|
600
|
+
if (inTable) {
|
|
601
|
+
console.log(chalk.gray('└' + '─'.repeat(58) + '┘'));
|
|
349
602
|
}
|
|
350
603
|
|
|
351
|
-
console.log(chalk.gray('
|
|
604
|
+
console.log('\n' + chalk.gray('═'.repeat(60)) + '\n');
|
|
352
605
|
}
|
|
353
606
|
|
|
354
607
|
// ==================== RENDER MARKDOWN (existing) ====================
|
|
@@ -405,4 +658,6 @@ module.exports = {
|
|
|
405
658
|
renderMarkdown,
|
|
406
659
|
displayReport,
|
|
407
660
|
showThinking,
|
|
661
|
+
formatTable,
|
|
662
|
+
addVisualBreak,
|
|
408
663
|
};
|