cxstatusline 0.8.0 → 0.9.1
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/README.md +72 -12
- package/dist/cxstatusline.js +955 -882
- package/package.json +3 -2
- package/patches/codex-0.156.1.patch +273 -53
- package/patches/ink@6.2.0.patch +119 -0
- package/patches/manifest.json +10 -8
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cxstatusline",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Customizable statusline for OpenAI Codex CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/adrijshikhar/cxstatusline.git"
|
|
9
9
|
},
|
|
10
|
-
"homepage": "https://
|
|
10
|
+
"homepage": "https://cxstatusline.adrijshikhar.dev",
|
|
11
11
|
"bugs": "https://github.com/adrijshikhar/cxstatusline/issues",
|
|
12
12
|
"type": "module",
|
|
13
13
|
"bin": {
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"test:website:e2e": "cd website && bun run test:e2e",
|
|
33
33
|
"seo:drift:baseline": "bun run scripts/seo-drift.ts baseline",
|
|
34
34
|
"seo:drift:compare": "bun run scripts/seo-drift.ts compare",
|
|
35
|
+
"seo:indexnow": "bun run scripts/indexnow.ts",
|
|
35
36
|
"check:package": "bun run scripts/check-package.ts",
|
|
36
37
|
"upstream:watch": "bun run scripts/upstream-watch.ts",
|
|
37
38
|
"probe:stdio": "bun run scripts/probe-detached-stdio.ts",
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
diff --git a/codex-rs/chatgpt/src/lib.rs b/codex-rs/chatgpt/src/lib.rs
|
|
2
|
+
index 057478d..5589621 100644
|
|
3
|
+
--- a/codex-rs/chatgpt/src/lib.rs
|
|
4
|
+
+++ b/codex-rs/chatgpt/src/lib.rs
|
|
5
|
+
@@ -1,3 +1,5 @@
|
|
6
|
+
+#![recursion_limit = "256"]
|
|
7
|
+
+
|
|
8
|
+
pub mod apply_command;
|
|
9
|
+
mod chatgpt_client;
|
|
10
|
+
pub mod connectors;
|
|
1
11
|
diff --git a/codex-rs/tui/src/app/background_requests.rs b/codex-rs/tui/src/app/background_requests.rs
|
|
2
12
|
index 4cdde21..23e1b62 100644
|
|
3
13
|
--- a/codex-rs/tui/src/app/background_requests.rs
|
|
@@ -267,7 +277,7 @@ index 1129bbe..dfd794e 100644
|
|
|
267
277
|
|
|
268
278
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
269
279
|
diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs
|
|
270
|
-
index 42937b1..
|
|
280
|
+
index 42937b1..bcbbe5b 100644
|
|
271
281
|
--- a/codex-rs/tui/src/bottom_pane/chat_composer.rs
|
|
272
282
|
+++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs
|
|
273
283
|
@@ -319,6 +319,7 @@ use super::footer::inset_footer_hint_area;
|
|
@@ -309,46 +319,93 @@ index 42937b1..7d745cd 100644
|
|
|
309
319
|
pub(crate) fn set_side_conversation_context_label(&mut self, label: Option<String>) -> bool {
|
|
310
320
|
if self.footer.side_conversation_context_label == label {
|
|
311
321
|
return false;
|
|
312
|
-
@@ -
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
+
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
+
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
+
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
+
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
322
|
+
@@ -4597,6 +4608,7 @@ impl ChatComposer {
|
|
323
|
+
remote_images: remote_images_rect,
|
|
324
|
+
textarea: textarea_rect,
|
|
325
|
+
popup: popup_rect,
|
|
326
|
+
+ cxstatusline,
|
|
327
|
+
footer: footer_rect,
|
|
328
|
+
} = self.layout_with_options(area, options);
|
|
329
|
+
self.render_status_surface(status, buf);
|
|
330
|
+
@@ -4640,6 +4652,7 @@ impl ChatComposer {
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
+ render_cxstatusline_block(cxstatusline, buf, &self.footer.cxstatusline_block);
|
|
335
|
+
let transcript_hint_area = inset_footer_hint_area(footer_rect);
|
|
336
|
+
let warning_notice = self.warning_notice_layout(transcript_hint_area, options);
|
|
337
|
+
let warning_area = warning_notice.as_ref().map(|(area, _)| *area);
|
|
338
|
+
diff --git a/codex-rs/tui/src/bottom_pane/chat_composer/composer_layout.rs b/codex-rs/tui/src/bottom_pane/chat_composer/composer_layout.rs
|
|
339
|
+
index b79c28b..c62c4a6 100644
|
|
340
|
+
--- a/codex-rs/tui/src/bottom_pane/chat_composer/composer_layout.rs
|
|
341
|
+
+++ b/codex-rs/tui/src/bottom_pane/chat_composer/composer_layout.rs
|
|
342
|
+
@@ -102,6 +102,12 @@ impl ChatComposer {
|
|
343
|
+
(composer, popup, popup)
|
|
344
|
+
};
|
|
345
|
+
footer_rect.y = footer_rect.y.saturating_add(status.height);
|
|
346
|
+
+ let cxstatusline = Rect {
|
|
347
|
+
+ height: (self.footer.cxstatusline_block.len() as u16).min(footer_rect.height),
|
|
348
|
+
+ ..footer_rect
|
|
349
|
+
+ };
|
|
350
|
+
+ footer_rect.y = footer_rect.y.saturating_add(cxstatusline.height);
|
|
351
|
+
+ footer_rect.height = footer_rect.height.saturating_sub(cxstatusline.height);
|
|
352
|
+
// Keep the draft visible when clipped.
|
|
353
|
+
let voice_rows = voice_rows * u16::from(composer_rect.height >= voice_rows + 3);
|
|
354
|
+
let mut textarea_rect = composer_rect.inset(Insets::tlbr(
|
|
355
|
+
@@ -133,6 +139,7 @@ impl ChatComposer {
|
|
356
|
+
remote_images: remote_images_rect,
|
|
357
|
+
textarea: textarea_rect,
|
|
358
|
+
popup: popup_rect,
|
|
359
|
+
+ cxstatusline,
|
|
360
|
+
footer: footer_rect,
|
|
361
|
+
}
|
|
362
|
+
}
|
|
339
363
|
diff --git a/codex-rs/tui/src/bottom_pane/chat_composer/footer_state.rs b/codex-rs/tui/src/bottom_pane/chat_composer/footer_state.rs
|
|
340
|
-
index f13aaa7..
|
|
364
|
+
index f13aaa7..2ace588 100644
|
|
341
365
|
--- a/codex-rs/tui/src/bottom_pane/chat_composer/footer_state.rs
|
|
342
366
|
+++ b/codex-rs/tui/src/bottom_pane/chat_composer/footer_state.rs
|
|
343
|
-
@@ -
|
|
367
|
+
@@ -1,6 +1,7 @@
|
|
368
|
+
//! Footer and status-row presentation state for the chat composer.
|
|
369
|
+
//! Owners schedule flash expiry redraws; replacing a draft clears its flash.
|
|
370
|
+
//! One resolved layout supplies the status, input, popup, and hint rectangles to each consumer.
|
|
371
|
+
+//! Renderer-owned status rows reserve their own rectangle above every native footer mode.
|
|
372
|
+
//! Slash-command and unified mention suggestions preserve passive footer content while keeping input.
|
|
373
|
+
//! Interactive transcript footers keep focus over nonempty composer hints. Their owner projects
|
|
374
|
+
//! actual pending chord hints; unrelated hints resume when the transcript interaction closes.
|
|
375
|
+
@@ -39,6 +40,7 @@ pub(super) struct ComposerLayout {
|
|
376
|
+
pub(super) remote_images: Rect,
|
|
377
|
+
pub(super) textarea: Rect,
|
|
378
|
+
pub(super) popup: Rect,
|
|
379
|
+
+ pub(super) cxstatusline: Rect,
|
|
380
|
+
pub(super) footer: Rect,
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
@@ -129,16 +131,22 @@ impl super::ChatComposer {
|
|
384
|
+
|
|
385
|
+
pub(super) fn footer_hint_height(&self, width: u16, options: ComposerRenderOptions<'_>) -> u16 {
|
|
386
|
+
if self.show_warning_notice(options) || self.shortcuts_above_composer(options) {
|
|
387
|
+
- return 1;
|
|
388
|
+
+ return (self.footer.cxstatusline_block.len() as u16).saturating_add(1);
|
|
389
|
+
}
|
|
390
|
+
options
|
|
391
|
+
.footer
|
|
344
392
|
.map_or_else(
|
|
345
393
|
|| {
|
|
346
394
|
self.custom_footer_height()
|
|
347
|
-
+ .map(|height|
|
|
395
|
+
+ .map(|height| {
|
|
396
|
+
+ height.saturating_add(self.footer.cxstatusline_block.len() as u16)
|
|
397
|
+
+ })
|
|
348
398
|
.unwrap_or_else(|| footer_height(&self.hint_footer_props(options), width))
|
|
349
399
|
},
|
|
350
|
-
|
|
351
|
-
|
|
400
|
+
- |footer| footer.text.height().try_into().unwrap_or(u16::MAX),
|
|
401
|
+
+ |footer| {
|
|
402
|
+
+ let height: u16 = footer.text.height().try_into().unwrap_or(u16::MAX);
|
|
403
|
+
+ height.saturating_add(self.footer.cxstatusline_block.len() as u16)
|
|
404
|
+
+ },
|
|
405
|
+
)
|
|
406
|
+
.max(u16::from(options.separate_status_line))
|
|
407
|
+
}
|
|
408
|
+
@@ -213,6 +221,7 @@ pub(super) struct FooterState {
|
|
352
409
|
pub(super) status_line_value: Option<Line<'static>>,
|
|
353
410
|
pub(super) status_line_hyperlink_url: Option<String>,
|
|
354
411
|
pub(super) status_line_enabled: bool,
|
|
@@ -356,27 +413,190 @@ index f13aaa7..089eb36 100644
|
|
|
356
413
|
pub(super) side_conversation_context_label: Option<String>,
|
|
357
414
|
pub(super) active_agent_label: Option<String>,
|
|
358
415
|
pub(super) external_editor_key: Option<ShortcutHint>,
|
|
359
|
-
diff --git a/codex-rs/tui/src/bottom_pane/chat_composer/
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
+
|
|
368
|
-
|
|
369
|
-
+
|
|
370
|
-
|
|
371
|
-
+
|
|
372
|
-
+
|
|
373
|
-
+
|
|
374
|
-
+
|
|
375
|
-
+
|
|
376
|
-
+
|
|
377
|
-
|
|
378
|
-
|
|
416
|
+
diff --git a/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__warning_notice__tests__cxstatusline_three_rows_with_warnings.snap b/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__warning_notice__tests__cxstatusline_three_rows_with_warnings.snap
|
|
417
|
+
new file mode 100644
|
|
418
|
+
index 0000000..64a85de
|
|
419
|
+
--- /dev/null
|
|
420
|
+
+++ b/codex-rs/tui/src/bottom_pane/chat_composer/snapshots/codex_tui__bottom_pane__chat_composer__warning_notice__tests__cxstatusline_three_rows_with_warnings.snap
|
|
421
|
+
@@ -0,0 +1,12 @@
|
|
422
|
+
+---
|
|
423
|
+
+source: tui/src/bottom_pane/chat_composer/warning_notice_tests.rs
|
|
424
|
+
+expression: "text(&render(&composer, 100, 3))"
|
|
425
|
+
+---
|
|
426
|
+
+
|
|
427
|
+
+› Ask Codex
|
|
428
|
+
+
|
|
429
|
+
+ MODEL · ~/project
|
|
430
|
+
+ CX row one
|
|
431
|
+
+ CX row two
|
|
432
|
+
+ CX row three
|
|
433
|
+
+ ? for shortcuts ⚠ 3 warnings · f2 to view
|
|
434
|
+
diff --git a/codex-rs/tui/src/bottom_pane/chat_composer/warning_notice_tests.rs b/codex-rs/tui/src/bottom_pane/chat_composer/warning_notice_tests.rs
|
|
435
|
+
index 2e11e17..6fe9fab 100644
|
|
436
|
+
--- a/codex-rs/tui/src/bottom_pane/chat_composer/warning_notice_tests.rs
|
|
437
|
+
+++ b/codex-rs/tui/src/bottom_pane/chat_composer/warning_notice_tests.rs
|
|
438
|
+
@@ -163,3 +163,161 @@ fn warnings_yield_to_queue_controls_and_complete_navigation_hints() {
|
|
379
439
|
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
+
|
|
443
|
+
+#[test]
|
|
444
|
+
+fn cxstatusline_rows_survive_warning_and_menu_transitions() {
|
|
445
|
+
+ let mut composer = composer();
|
|
446
|
+
+ composer.set_cxstatusline_block(vec![
|
|
447
|
+
+ "CX row one".into(),
|
|
448
|
+
+ "CX row two".into(),
|
|
449
|
+
+ "CX row three".into(),
|
|
450
|
+
+ ]);
|
|
451
|
+
+ for draft in ["", "/", "", "hello", ""] {
|
|
452
|
+
+ composer.set_text_content(draft.into(), Vec::new(), Vec::new());
|
|
453
|
+
+ for count in [0, 3, 0] {
|
|
454
|
+
+ let screen = text(&render(&composer, /*width*/ 100, count));
|
|
455
|
+
+ for row in ["CX row one", "CX row two", "CX row three"] {
|
|
456
|
+
+ assert!(screen.contains(row), "missing {row}: {screen}");
|
|
457
|
+
+ }
|
|
458
|
+
+ }
|
|
459
|
+
+ }
|
|
460
|
+
+ insta::assert_snapshot!(
|
|
461
|
+
+ "cxstatusline_three_rows_with_warnings",
|
|
462
|
+
+ text(&render(&composer, /*width*/ 100, /*count*/ 3))
|
|
463
|
+
+ );
|
|
464
|
+
+}
|
|
465
|
+
+
|
|
466
|
+
+#[test]
|
|
467
|
+
+fn cxstatusline_rows_preserve_transcript_cursor_and_shortcut_help() {
|
|
468
|
+
+ let mut composer = composer();
|
|
469
|
+
+ composer.set_cxstatusline_block(vec![
|
|
470
|
+
+ "CX row one".into(),
|
|
471
|
+
+ "CX row two".into(),
|
|
472
|
+
+ "CX row three".into(),
|
|
473
|
+
+ ]);
|
|
474
|
+
+ let footer = TranscriptFooter {
|
|
475
|
+
+ text: "transcript input".into(),
|
|
476
|
+
+ cursor_column: Some(3),
|
|
477
|
+
+ is_interactive: true,
|
|
478
|
+
+ };
|
|
479
|
+
+ for mode in [FooterMode::ComposerEmpty, FooterMode::ShortcutOverlay] {
|
|
480
|
+
+ composer.footer.mode = mode;
|
|
481
|
+
+ for transcript in [None, Some(&footer)] {
|
|
482
|
+
+ let options = composer.resolve_render_options(ComposerRenderOptions {
|
|
483
|
+
+ footer: transcript,
|
|
484
|
+
+ ..Default::default()
|
|
485
|
+
+ });
|
|
486
|
+
+ let area = Rect::new(
|
|
487
|
+
+ 0,
|
|
488
|
+
+ 0,
|
|
489
|
+
+ 100,
|
|
490
|
+
+ composer.desired_height_with_options(100, options),
|
|
491
|
+
+ );
|
|
492
|
+
+ let mut buffer = Buffer::empty(area);
|
|
493
|
+
+ composer.render_with_options(area, &mut buffer, None, options);
|
|
494
|
+
+ let screen = text(&buffer);
|
|
495
|
+
+ for row in ["CX row one", "CX row two", "CX row three"] {
|
|
496
|
+
+ assert!(screen.contains(row), "missing {row}: {screen}");
|
|
497
|
+
+ }
|
|
498
|
+
+ if transcript.is_some() {
|
|
499
|
+
+ let row = screen
|
|
500
|
+
+ .lines()
|
|
501
|
+
+ .position(|line| line.contains("transcript input"))
|
|
502
|
+
+ .unwrap();
|
|
503
|
+
+ assert_eq!(
|
|
504
|
+
+ composer.cursor_pos_with_options(area, options),
|
|
505
|
+
+ Some((5, row as u16))
|
|
506
|
+
+ );
|
|
507
|
+
+ }
|
|
508
|
+
+ }
|
|
509
|
+
+ }
|
|
510
|
+
+}
|
|
511
|
+
+
|
|
512
|
+
+#[test]
|
|
513
|
+
+fn cxstatusline_rows_survive_real_slash_popup_input_and_resize() {
|
|
514
|
+
+ let mut composer = composer();
|
|
515
|
+
+ composer.set_collaboration_modes_enabled(true);
|
|
516
|
+
+ composer.set_cxstatusline_block(vec![
|
|
517
|
+
+ "CX row one".into(),
|
|
518
|
+
+ "CX row two".into(),
|
|
519
|
+
+ "CX row three".into(),
|
|
520
|
+
+ ]);
|
|
521
|
+
+ for ch in ['/', 'm'] {
|
|
522
|
+
+ composer.handle_key_event(KeyEvent::from(KeyCode::Char(ch)));
|
|
523
|
+
+ }
|
|
524
|
+
+ composer.handle_key_event(KeyEvent::new(KeyCode::Down, KeyModifiers::NONE));
|
|
525
|
+
+ assert!(matches!(composer.popups.active, ActivePopup::Command(_)));
|
|
526
|
+
+
|
|
527
|
+
+ for width in [80, 32] {
|
|
528
|
+
+ let options = composer.resolve_render_options(ComposerRenderOptions {
|
|
529
|
+
+ separate_status_line: true,
|
|
530
|
+
+ command_popup_placement: CommandPopupPlacement::AboveComposer,
|
|
531
|
+
+ ..Default::default()
|
|
532
|
+
+ });
|
|
533
|
+
+ let area = Rect::new(
|
|
534
|
+
+ 0,
|
|
535
|
+
+ 0,
|
|
536
|
+
+ width,
|
|
537
|
+
+ composer.desired_height_with_options(width, options),
|
|
538
|
+
+ );
|
|
539
|
+
+ let mut buffer = Buffer::empty(area);
|
|
540
|
+
+ composer.render_with_options(area, &mut buffer, None, options);
|
|
541
|
+
+ let layout = composer.layout_with_options(area, options);
|
|
542
|
+
+ assert!(
|
|
543
|
+
+ layout.popup.height > 0,
|
|
544
|
+
+ "slash popup should remain open at width {width}: {}",
|
|
545
|
+
+ text(&buffer)
|
|
546
|
+
+ );
|
|
547
|
+
+ assert_eq!(layout.cxstatusline.height, 3);
|
|
548
|
+
+ assert_eq!(layout.cxstatusline.bottom(), layout.footer.y);
|
|
549
|
+
+ for row in ["CX row one", "CX row two", "CX row three"] {
|
|
550
|
+
+ assert!(
|
|
551
|
+
+ text(&buffer).contains(row),
|
|
552
|
+
+ "missing {row} at width {width}"
|
|
553
|
+
+ );
|
|
554
|
+
+ }
|
|
555
|
+
+ let (x, y) = composer
|
|
556
|
+
+ .cursor_pos_with_options(area, options)
|
|
557
|
+
+ .expect("input cursor");
|
|
558
|
+
+ assert!(
|
|
559
|
+
+ x >= area.x && x < area.right(),
|
|
560
|
+
+ "cursor x outside width {width}: {x}"
|
|
561
|
+
+ );
|
|
562
|
+
+ assert!(
|
|
563
|
+
+ y >= area.y && y < area.bottom(),
|
|
564
|
+
+ "cursor y outside screen: {y}"
|
|
565
|
+
+ );
|
|
566
|
+
+ }
|
|
567
|
+
+
|
|
568
|
+
+ composer.handle_key_event(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE));
|
|
569
|
+
+ assert!(matches!(composer.popups.active, ActivePopup::None));
|
|
570
|
+
+ let options = composer.resolve_render_options(ComposerRenderOptions {
|
|
571
|
+
+ separate_status_line: true,
|
|
572
|
+
+ command_popup_placement: CommandPopupPlacement::AboveComposer,
|
|
573
|
+
+ ..Default::default()
|
|
574
|
+
+ });
|
|
575
|
+
+ let area = Rect::new(0, 0, 80, composer.desired_height_with_options(80, options));
|
|
576
|
+
+ let layout = composer.layout_with_options(area, options);
|
|
577
|
+
+ assert_eq!(layout.cxstatusline.height, 3);
|
|
578
|
+
+
|
|
579
|
+
+ for running in [true, false] {
|
|
580
|
+
+ composer.set_task_running(running);
|
|
581
|
+
+ let buffer = render(&composer, 80, 0);
|
|
582
|
+
+ for row in ["CX row one", "CX row two", "CX row three"] {
|
|
583
|
+
+ assert!(
|
|
584
|
+
+ text(&buffer).contains(row),
|
|
585
|
+
+ "missing {row}, running={running}"
|
|
586
|
+
+ );
|
|
587
|
+
+ }
|
|
588
|
+
+ let layout = composer.layout_with_options(buffer.area, options);
|
|
589
|
+
+ let (_, y) = composer
|
|
590
|
+
+ .cursor_pos_with_options(buffer.area, options)
|
|
591
|
+
+ .expect("input cursor");
|
|
592
|
+
+ assert!(
|
|
593
|
+
+ y < layout.cxstatusline.y,
|
|
594
|
+
+ "cursor overlaps footer, running={running}"
|
|
595
|
+
+ );
|
|
596
|
+
+ }
|
|
597
|
+
+ composer.handle_key_event(KeyEvent::from(KeyCode::Char('x')));
|
|
598
|
+
+ assert!(text(&render(&composer, 80, 0)).contains("/mx"));
|
|
599
|
+
+}
|
|
380
600
|
diff --git a/codex-rs/tui/src/bottom_pane/footer.rs b/codex-rs/tui/src/bottom_pane/footer.rs
|
|
381
601
|
index 76a13b7..8479e1e 100644
|
|
382
602
|
--- a/codex-rs/tui/src/bottom_pane/footer.rs
|
package/patches/ink@6.2.0.patch
CHANGED
|
@@ -1,3 +1,107 @@
|
|
|
1
|
+
diff --git a/build/ink.d.ts b/build/ink.d.ts
|
|
2
|
+
index 9dce0fe328a8eeb18d459b974ab0c3cf5fb38dda..f90bb0fe8c71beae7bf7db4e4d9d5f62812d33a3 100644
|
|
3
|
+
--- a/build/ink.d.ts
|
|
4
|
+
+++ b/build/ink.d.ts
|
|
5
|
+
@@ -4,6 +4,8 @@ export type Options = {
|
|
6
|
+
stdin: NodeJS.ReadStream;
|
|
7
|
+
stderr: NodeJS.WriteStream;
|
|
8
|
+
debug: boolean;
|
|
9
|
+
+ preserveScrollback?: boolean;
|
|
10
|
+
+ isCI?: boolean;
|
|
11
|
+
exitOnCtrlC: boolean;
|
|
12
|
+
patchConsole: boolean;
|
|
13
|
+
isScreenReaderEnabled?: boolean;
|
|
14
|
+
@@ -14,6 +16,7 @@ export default class Ink {
|
|
15
|
+
private readonly log;
|
|
16
|
+
private readonly throttledLog;
|
|
17
|
+
private readonly isScreenReaderEnabled;
|
|
18
|
+
+ private readonly isInCi;
|
|
19
|
+
private isUnmounted;
|
|
20
|
+
private lastOutput;
|
|
21
|
+
private lastOutputHeight;
|
|
22
|
+
diff --git a/build/ink.js b/build/ink.js
|
|
23
|
+
index 5cad56e135f1074474d7d498ffd87c5699b9df78..46acd25d0df2d4382f2bb5c2586f4d802bbb4af2 100644
|
|
24
|
+
--- a/build/ink.js
|
|
25
|
+
+++ b/build/ink.js
|
|
26
|
+
@@ -21,6 +21,7 @@ export default class Ink {
|
|
27
|
+
log;
|
|
28
|
+
throttledLog;
|
|
29
|
+
isScreenReaderEnabled;
|
|
30
|
+
+ isInCi;
|
|
31
|
+
// Ignore last render after unmounting a tree to prevent empty output before exit
|
|
32
|
+
isUnmounted;
|
|
33
|
+
lastOutput;
|
|
34
|
+
@@ -36,6 +37,7 @@ export default class Ink {
|
|
35
|
+
constructor(options) {
|
|
36
|
+
autoBind(this);
|
|
37
|
+
this.options = options;
|
|
38
|
+
+ this.isInCi = options.isCI ?? isInCi;
|
|
39
|
+
this.rootNode = dom.createNode('ink-root');
|
|
40
|
+
this.rootNode.onComputeLayout = this.calculateLayout;
|
|
41
|
+
this.isScreenReaderEnabled =
|
|
42
|
+
@@ -83,7 +85,7 @@ export default class Ink {
|
|
43
|
+
if (options.patchConsole) {
|
|
44
|
+
this.patchConsole();
|
|
45
|
+
}
|
|
46
|
+
- if (!isInCi) {
|
|
47
|
+
+ if (!this.isInCi) {
|
|
48
|
+
options.stdout.on('resize', this.resized);
|
|
49
|
+
this.unsubscribeResize = () => {
|
|
50
|
+
options.stdout.off('resize', this.resized);
|
|
51
|
+
@@ -118,7 +120,7 @@ export default class Ink {
|
|
52
|
+
this.options.stdout.write(this.fullStaticOutput + output);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
- if (isInCi) {
|
|
56
|
+
+ if (this.isInCi) {
|
|
57
|
+
if (hasStaticOutput) {
|
|
58
|
+
this.options.stdout.write(staticOutput);
|
|
59
|
+
}
|
|
60
|
+
@@ -129,7 +131,7 @@ export default class Ink {
|
|
61
|
+
if (hasStaticOutput) {
|
|
62
|
+
this.fullStaticOutput += staticOutput;
|
|
63
|
+
}
|
|
64
|
+
- if (this.lastOutputHeight >= this.options.stdout.rows) {
|
|
65
|
+
+ if (!this.options.preserveScrollback && this.lastOutputHeight >= this.options.stdout.rows) {
|
|
66
|
+
this.options.stdout.write(ansiEscapes.clearTerminal + this.fullStaticOutput + output + '\n');
|
|
67
|
+
this.lastOutput = output;
|
|
68
|
+
this.lastOutputHeight = outputHeight;
|
|
69
|
+
@@ -166,7 +168,7 @@ export default class Ink {
|
|
70
|
+
this.options.stdout.write(data + this.fullStaticOutput + this.lastOutput);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
- if (isInCi) {
|
|
74
|
+
+ if (this.isInCi) {
|
|
75
|
+
this.options.stdout.write(data);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
@@ -183,7 +185,7 @@ export default class Ink {
|
|
79
|
+
this.options.stdout.write(this.fullStaticOutput + this.lastOutput);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
- if (isInCi) {
|
|
83
|
+
+ if (this.isInCi) {
|
|
84
|
+
this.options.stderr.write(data);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
@@ -207,7 +209,7 @@ export default class Ink {
|
|
88
|
+
}
|
|
89
|
+
// CIs don't handle erasing ansi escapes well, so it's better to
|
|
90
|
+
// only render last frame of non-static output
|
|
91
|
+
- if (isInCi) {
|
|
92
|
+
+ if (this.isInCi) {
|
|
93
|
+
this.options.stdout.write(this.lastOutput + '\n');
|
|
94
|
+
}
|
|
95
|
+
else if (!this.options.debug) {
|
|
96
|
+
@@ -236,7 +238,7 @@ export default class Ink {
|
|
97
|
+
return this.exitPromise;
|
|
98
|
+
}
|
|
99
|
+
clear() {
|
|
100
|
+
- if (!isInCi && !this.options.debug) {
|
|
101
|
+
+ if (!this.isInCi && !this.options.debug) {
|
|
102
|
+
this.log.clear();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
1
105
|
diff --git a/build/parse-keypress.js b/build/parse-keypress.js
|
|
2
106
|
index 1e13e819f59d259a3476510db822695576a9d93c..e100611d8661187c4ca191e89621cd5e480fcacc 100644
|
|
3
107
|
--- a/build/parse-keypress.js
|
|
@@ -15,3 +119,18 @@ index 1e13e819f59d259a3476510db822695576a9d93c..e100611d8661187c4ca191e89621cd5e
|
|
|
15
119
|
key.meta = s.charAt(0) === '\x1b';
|
|
16
120
|
}
|
|
17
121
|
else if (s === '\x1b' || s === '\x1b\x1b') {
|
|
122
|
+
diff --git a/build/render.d.ts b/build/render.d.ts
|
|
123
|
+
index ffcfbaca277cbd95b2e6a2c857230dd69e41b7e0..5fd94d71aa136f48447c162dd7453617f1824cc5 100644
|
|
124
|
+
--- a/build/render.d.ts
|
|
125
|
+
+++ b/build/render.d.ts
|
|
126
|
+
@@ -24,6 +24,10 @@ export type RenderOptions = {
|
|
127
|
+
* @default false
|
|
128
|
+
*/
|
|
129
|
+
debug?: boolean;
|
|
130
|
+
+ /** Preserve prior terminal output when an Ink view exceeds terminal height. */
|
|
131
|
+
+ preserveScrollback?: boolean;
|
|
132
|
+
+ /** Override CI detection when the caller has verified an interactive terminal. */
|
|
133
|
+
+ isCI?: boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Configure whether Ink should listen to Ctrl+C keyboard input and exit the app. This is needed in case `process.stdin` is in raw mode, because then Ctrl+C is ignored by default and process is expected to handle it manually.
|
|
136
|
+
*
|
package/patches/manifest.json
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version":
|
|
2
|
+
"version": 2,
|
|
3
3
|
"tag_prefix": "rust-v",
|
|
4
4
|
"candidate": "0.156.1",
|
|
5
5
|
"patches": [
|
|
6
|
-
{ "min": "0.152.1", "max": "0.152.1", "file": "codex-0.152.1.patch" },
|
|
7
|
-
{ "min": "0.153.0", "max": "0.153.0", "file": "codex-0.153.0.patch" },
|
|
8
|
-
{ "min": "0.153.
|
|
9
|
-
{ "min": "0.
|
|
10
|
-
{ "min": "0.
|
|
11
|
-
{ "min": "0.155.
|
|
12
|
-
{ "min": "0.
|
|
6
|
+
{ "patchVersion": 1, "min": "0.152.1", "max": "0.152.1", "file": "codex-0.152.1.patch" },
|
|
7
|
+
{ "patchVersion": 1, "min": "0.153.0", "max": "0.153.0", "file": "codex-0.153.0.patch" },
|
|
8
|
+
{ "patchVersion": 1, "min": "0.153.1", "max": "0.153.1", "file": "codex-0.153.4.patch" },
|
|
9
|
+
{ "patchVersion": 1, "min": "0.153.4", "max": "0.153.4", "file": "codex-0.153.4.patch" },
|
|
10
|
+
{ "patchVersion": 1, "min": "0.154.0", "max": "0.154.0", "file": "codex-0.154.0.patch" },
|
|
11
|
+
{ "patchVersion": 1, "min": "0.155.0", "max": "0.155.0", "file": "codex-0.155.0.patch" },
|
|
12
|
+
{ "patchVersion": 1, "min": "0.155.1", "max": "0.155.1", "file": "codex-0.155.1.patch" },
|
|
13
|
+
{ "patchVersion": 2, "min": "0.156.0", "max": "0.156.0", "file": "codex-0.156.1.patch" },
|
|
14
|
+
{ "patchVersion": 2, "min": "0.156.1", "max": "0.156.1", "file": "codex-0.156.1.patch" }
|
|
13
15
|
]
|
|
14
16
|
}
|