Skip to content

Commit c6b23dd

Browse files
committed
feat(style)!: Revamp the entire UI to have less borders and distractions
1. Removed a lot of unnecessary borders around elements. 2. Added line numbers for the input in issue conversation 3. Added a `BodyPreview` component that shows a preview of the issue body in the TUI list widget. test(ui): update tests chore: fix lints feat(style): migrate over issue_create fix(style): remove old style borders for new ones in textarea previews fix: fix bugs
1 parent 1039146 commit c6b23dd

22 files changed

Lines changed: 458 additions & 161 deletions

src/ui/components/issue_conversation.rs

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,19 @@ use rat_cursor::HasScreenCursor;
1212
use rat_widget::{
1313
event::{HandleEvent, Outcome, TextOutcome, ct_event},
1414
focus::{FocusBuilder, FocusFlag, HasFocus, Navigation},
15+
line_number::{LineNumberState, LineNumbers},
1516
list::{ListState, selection::RowSelection},
1617
paragraph::{Paragraph, ParagraphState},
1718
textarea::{TextArea, TextAreaState, TextWrap},
1819
};
1920
use ratatui::{
2021
buffer::Buffer,
21-
layout::Rect,
22-
style::{Color, Modifier, Style, Stylize},
22+
layout::{Rect, Spacing},
23+
style::{Color, Modifier, Style},
2324
text::{Line, Span, Text},
24-
widgets::{self, Block, ListItem, StatefulWidget, Widget},
25+
widgets::{self, Block, Borders, ListItem, Padding, StatefulWidget, Widget},
2526
};
26-
use ratatui_macros::{horizontal, line, span, vertical};
27+
use ratatui_macros::{horizontal, line, vertical};
2728
use std::{
2829
collections::{HashMap, HashSet},
2930
sync::{Arc, OnceLock, RwLock},
@@ -170,7 +171,7 @@ pub struct TimelineEventView {
170171
}
171172

172173
impl TimelineEventView {
173-
fn from_api(event: TimelineEvent, fallback_id: u64) -> Option<Self> {
174+
pub(crate) fn from_api(event: TimelineEvent, fallback_id: u64) -> Option<Self> {
174175
if matches!(
175176
event.event,
176177
IssueEvent::Commented | IssueEvent::LineCommented | IssueEvent::CommentDeleted
@@ -210,6 +211,7 @@ impl TimelineEventView {
210211

211212
pub struct IssueConversation {
212213
title: Option<Arc<str>>,
214+
ln_state: LineNumberState,
213215
action_tx: Option<tokio::sync::mpsc::Sender<Action>>,
214216
current: Option<IssueConversationSeed>,
215217
cache_number: Option<u64>,
@@ -264,13 +266,13 @@ enum MessageKey {
264266
}
265267

266268
#[derive(Debug, Clone, Default)]
267-
struct MarkdownRender {
268-
lines: Vec<Line<'static>>,
269-
links: Vec<RenderedLink>,
269+
pub(crate) struct MarkdownRender {
270+
pub(crate) lines: Vec<Line<'static>>,
271+
pub(crate) links: Vec<RenderedLink>,
270272
}
271273

272274
#[derive(Debug, Clone)]
273-
struct RenderedLink {
275+
pub(crate) struct RenderedLink {
274276
line: usize,
275277
col: usize,
276278
label: String,
@@ -314,6 +316,7 @@ impl IssueConversation {
314316
action_tx: None,
315317
current: None,
316318
cache_number: None,
319+
ln_state: LineNumberState::default(),
317320
cache_comments: Vec::new(),
318321
timeline_cache_number: None,
319322
cache_timeline: Vec::new(),
@@ -358,48 +361,40 @@ impl IssueConversation {
358361
return;
359362
}
360363
self.area = area.main_content;
361-
let title = self.title.clone().unwrap_or_default();
362-
let wrapped_title = wrap(&title, area.main_content.width.saturating_sub(2) as usize);
363-
let title_para_height = wrapped_title.len() as u16 + 2;
364-
let last_item = wrapped_title.last();
365-
let last_line = last_item
366-
.as_ref()
367-
.map(|l| {
368-
line![
369-
l.to_string(),
370-
span!(
371-
" #{}",
372-
self.current.as_ref().map(|s| s.number).unwrap_or_default()
373-
)
374-
.dim()
375-
]
376-
})
377-
.unwrap_or_else(|| Line::from(""));
378-
let wrapped_title_len = wrapped_title.len() as u16;
379-
let title_para = Text::from_iter(
380-
wrapped_title
381-
.into_iter()
382-
.take(wrapped_title_len as usize - 1)
383-
.map(Line::from)
384-
.chain(std::iter::once(last_line)),
385-
);
364+
let mut title = self.title.clone().unwrap_or_default().to_string();
365+
title.push_str(&format!(
366+
" #{}",
367+
self.current.as_ref().map(|s| s.number).unwrap_or_default()
368+
));
369+
let title = title.trim();
370+
let wrapped_title = wrap(title, area.main_content.width.saturating_sub(2) as usize);
371+
let title_para_height = wrapped_title.len() as u16 + 1;
372+
let title_para = Text::from_iter(wrapped_title);
386373

387374
let areas = vertical![==title_para_height, *=1, ==5].split(area.main_content);
388375
let title_area = areas[0];
389376
let content_area = areas[1];
390377
let input_area = areas[2];
391-
let content_split = horizontal![*=1, *=1].split(content_area);
378+
let content_split = horizontal![*=1, *=1]
379+
.spacing(Spacing::Overlap(1))
380+
.split(content_area);
392381
let list_area = content_split[0];
393382
let body_area = content_split[1];
394383
let items = self.build_items(list_area, body_area);
395384

396385
let title_widget = widgets::Paragraph::new(title_para)
397-
.block(Block::bordered().border_type(ratatui::widgets::BorderType::Rounded))
386+
.block(
387+
Block::default()
388+
.padding(Padding::horizontal(1))
389+
.borders(Borders::BOTTOM)
390+
.merge_borders(ratatui::symbols::merge::MergeStrategy::Exact),
391+
)
398392
.style(Style::default().add_modifier(Modifier::BOLD));
399393
title_widget.render(title_area, buf);
400394

401-
let mut list_block = Block::bordered()
402-
.border_type(ratatui::widgets::BorderType::Rounded)
395+
let mut list_block = Block::default()
396+
.borders(Borders::RIGHT)
397+
.merge_borders(ratatui::symbols::merge::MergeStrategy::Exact)
403398
.border_style(get_border_style(&self.list_state));
404399

405400
if !self.is_loading_current() {
@@ -449,13 +444,26 @@ impl IssueConversation {
449444

450445
match self.textbox_state {
451446
InputState::Input => {
447+
let [line_numbers, input_area] = horizontal![==self.input_state.len_lines().checked_ilog10().unwrap_or(0) as u16 + 2, *=1].areas(input_area);
448+
let ln_block = Block::default()
449+
.borders(Borders::TOP)
450+
.merge_borders(ratatui::symbols::merge::MergeStrategy::Exact)
451+
.border_style(get_border_style(&self.input_state));
452+
let ln = LineNumbers::new()
453+
.with_textarea(&self.input_state)
454+
.block(ln_block)
455+
.style(Style::default().dim());
456+
ln.render(line_numbers, buf, &mut self.ln_state);
457+
452458
let input_title = if let Some(err) = &self.post_error {
453459
format!("Comment (Ctrl+Enter to send) | {err}")
454460
} else {
455461
"Comment (Ctrl+Enter to send)".to_string()
456462
};
457-
let mut input_block = Block::bordered()
458-
.border_type(ratatui::widgets::BorderType::Rounded)
463+
let mut input_block = Block::default()
464+
.borders(Borders::TOP)
465+
.merge_borders(ratatui::symbols::merge::MergeStrategy::Exact)
466+
.padding(Padding::horizontal(1))
459467
.border_style(get_border_style(&self.input_state));
460468
if !self.posting {
461469
input_block = input_block.title(input_title);
@@ -470,8 +478,10 @@ impl IssueConversation {
470478
render_markdown_lines(&self.input_state.text(), self.markdown_width, 2);
471479
let para = Paragraph::new(rendered)
472480
.block(
473-
Block::bordered()
474-
.border_type(ratatui::widgets::BorderType::Rounded)
481+
Block::default()
482+
.borders(Borders::TOP)
483+
.merge_borders(ratatui::symbols::merge::MergeStrategy::Exact)
484+
.padding(Padding::horizontal(1))
475485
.border_style(get_border_style(&self.paragraph_state))
476486
.title("Preview"),
477487
)
@@ -636,8 +646,9 @@ impl IssueConversation {
636646

637647
let body = Paragraph::new(body_lines)
638648
.block(
639-
Block::bordered()
640-
.border_type(ratatui::widgets::BorderType::Rounded)
649+
Block::default()
650+
.borders(Borders::LEFT)
651+
.merge_borders(ratatui::symbols::merge::MergeStrategy::Exact)
641652
.border_style(get_border_style(&self.body_paragraph_state))
642653
.title(if self.screen == MainScreen::DetailsFullscreen {
643654
"Message Body (PageUp/PageDown/Home/End | f/Esc: exit fullscreen)"
@@ -2441,7 +2452,7 @@ pub(crate) fn render_markdown_lines(text: &str, width: usize, indent: usize) ->
24412452
render_markdown(text, width, indent).lines
24422453
}
24432454

2444-
fn render_markdown(text: &str, width: usize, indent: usize) -> MarkdownRender {
2455+
pub(crate) fn render_markdown(text: &str, width: usize, indent: usize) -> MarkdownRender {
24452456
let mut renderer = MarkdownRenderer::new(width, indent);
24462457
let options = Options::ENABLE_GFM
24472458
| Options::ENABLE_STRIKETHROUGH

0 commit comments

Comments
 (0)