Skip to content

Commit 914f248

Browse files
unhappychoiceclaude
andcommitted
feat: add repository information to result and session screens
- Show repository name in Stage Results section with [owner/repo] format - Thread repository info through stage_manager and result_screen methods - Update typing_screen to pass repository info to countdown screens - Ensure consistent repository display across all game screens 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent c796d22 commit 914f248

File tree

3 files changed

+105
-56
lines changed

3 files changed

+105
-56
lines changed

src/game/screens/result_screen.rs

Lines changed: 82 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::game::ascii_digits::get_digit_patterns;
22
use crate::game::ascii_rank_titles_generated::get_rank_title_display;
33
use crate::scoring::{RankingTitle, ScoringEngine, TypingMetrics};
44
use crate::sharing::{SharingPlatform, SharingService};
5-
use crate::Result;
5+
use crate::{extractor::GitRepositoryInfo, Result};
66
use crossterm::{
77
cursor::MoveTo,
88
event::{self, Event, KeyCode, KeyModifiers},
@@ -279,13 +279,14 @@ impl ResultScreen {
279279
AnimationScreen::show_session_animation(total_stages, completed_stages, stage_engines)?;
280280

281281
// Then show the original result screen
282-
Self::show_session_summary_original(total_stages, completed_stages, stage_engines)
282+
Self::show_session_summary_original(total_stages, completed_stages, stage_engines, &None)
283283
}
284284

285285
pub fn show_session_summary_original(
286286
_total_stages: usize,
287287
_completed_stages: usize,
288288
stage_engines: &[(String, ScoringEngine)],
289+
repo_info: &Option<GitRepositoryInfo>,
289290
) -> Result<()> {
290291
let mut stdout = stdout();
291292

@@ -335,22 +336,15 @@ impl ResultScreen {
335336

336337
// Display session complete title at the top
337338
let session_title = "=== SESSION COMPLETE ===";
338-
let lines: Vec<&str> = session_title.split('\n').collect();
339-
340-
for (i, line) in lines.iter().enumerate() {
341-
let title_col = center_col.saturating_sub(line.len() as u16 / 2);
342-
execute!(
343-
stdout,
344-
MoveTo(title_col, rank_start_row.saturating_sub(4) + i as u16)
345-
)?;
346-
execute!(
347-
stdout,
348-
SetAttribute(Attribute::Bold),
349-
SetForegroundColor(Color::Cyan)
350-
)?;
351-
execute!(stdout, Print(line))?;
352-
execute!(stdout, ResetColor)?;
353-
}
339+
let title_col = center_col.saturating_sub(session_title.len() as u16 / 2);
340+
execute!(stdout, MoveTo(title_col, rank_start_row.saturating_sub(4)))?;
341+
execute!(
342+
stdout,
343+
SetAttribute(Attribute::Bold),
344+
SetForegroundColor(Color::Cyan)
345+
)?;
346+
execute!(stdout, Print(session_title))?;
347+
execute!(stdout, ResetColor)?;
354348

355349
// Display "you're:" label before rank title (1 line gap from rank title)
356350
let youre_label = "YOU'RE:";
@@ -469,11 +463,18 @@ impl ResultScreen {
469463
if !stage_engines.is_empty() {
470464
let stage_results_start_row = summary_start_row + summary_lines.len() as u16 + 2;
471465

472-
let stage_label = "Stage Results:";
466+
let stage_label = if let Some(repo) = repo_info {
467+
format!(
468+
"Stage Results: [{}/{}]",
469+
repo.user_name, repo.repository_name
470+
)
471+
} else {
472+
"Stage Results:".to_string()
473+
};
473474
let stage_label_col = center_col.saturating_sub(stage_label.len() as u16 / 2);
474475
execute!(stdout, MoveTo(stage_label_col, stage_results_start_row))?;
475476
execute!(stdout, SetForegroundColor(Color::Cyan))?;
476-
execute!(stdout, Print(stage_label))?;
477+
execute!(stdout, Print(&stage_label))?;
477478
execute!(stdout, ResetColor)?;
478479

479480
for (i, (stage_name, engine)) in stage_engines.iter().enumerate().take(5) {
@@ -530,11 +531,13 @@ impl ResultScreen {
530531
total_stages: usize,
531532
completed_stages: usize,
532533
stage_engines: &[(String, ScoringEngine)],
534+
repo_info: &Option<GitRepositoryInfo>,
533535
) -> Result<ResultAction> {
534536
Self::show_session_summary_with_input_internal(
535537
total_stages,
536538
completed_stages,
537539
stage_engines,
540+
repo_info,
538541
true,
539542
)
540543
}
@@ -543,11 +546,13 @@ impl ResultScreen {
543546
total_stages: usize,
544547
completed_stages: usize,
545548
stage_engines: &[(String, ScoringEngine)],
549+
repo_info: &Option<GitRepositoryInfo>,
546550
) -> Result<ResultAction> {
547551
Self::show_session_summary_with_input_internal(
548552
total_stages,
549553
completed_stages,
550554
stage_engines,
555+
repo_info,
551556
false,
552557
)
553558
}
@@ -556,6 +561,7 @@ impl ResultScreen {
556561
total_stages: usize,
557562
completed_stages: usize,
558563
stage_engines: &[(String, ScoringEngine)],
564+
repo_info: &Option<GitRepositoryInfo>,
559565
show_animation: bool,
560566
) -> Result<ResultAction> {
561567
use crate::game::screens::AnimationScreen;
@@ -566,7 +572,12 @@ impl ResultScreen {
566572
}
567573

568574
// Show the result screen
569-
Self::show_session_summary_original(total_stages, completed_stages, stage_engines)?;
575+
Self::show_session_summary_original(
576+
total_stages,
577+
completed_stages,
578+
stage_engines,
579+
repo_info,
580+
)?;
570581

571582
// Wait for user input and return action
572583
loop {
@@ -602,6 +613,7 @@ impl ResultScreen {
602613
total_stages: usize,
603614
completed_stages: usize,
604615
stage_engines: &[(String, ScoringEngine)],
616+
repo_info: &Option<GitRepositoryInfo>,
605617
) -> Result<()> {
606618
let mut stdout = stdout();
607619

@@ -619,22 +631,15 @@ impl ResultScreen {
619631

620632
// Header - show FAILED status (centered)
621633
let header_text = "=== SESSION FAILED ===";
622-
let lines: Vec<&str> = header_text.split('\n').collect();
623-
624-
for (i, line) in lines.iter().enumerate() {
625-
let header_x = (terminal_width - line.len() as u16) / 2;
626-
execute!(
627-
stdout,
628-
MoveTo(header_x, center_y.saturating_sub(6) + i as u16)
629-
)?;
630-
execute!(
631-
stdout,
632-
SetForegroundColor(Color::Red),
633-
SetAttribute(Attribute::Bold)
634-
)?;
635-
execute!(stdout, Print(line))?;
636-
execute!(stdout, ResetColor)?;
637-
}
634+
let header_x = (terminal_width - header_text.len() as u16) / 2;
635+
execute!(stdout, MoveTo(header_x, center_y.saturating_sub(6)))?;
636+
execute!(
637+
stdout,
638+
SetForegroundColor(Color::Red),
639+
SetAttribute(Attribute::Bold)
640+
)?;
641+
execute!(stdout, Print(header_text))?;
642+
execute!(stdout, ResetColor)?;
638643

639644
// Show stage progress (centered, cyan)
640645
let stage_text = format!("Stages: {}/{}", completed_stages, total_stages);
@@ -684,7 +689,10 @@ impl ResultScreen {
684689
Ok(())
685690
}
686691

687-
pub fn show_sharing_menu(metrics: &TypingMetrics) -> Result<()> {
692+
pub fn show_sharing_menu(
693+
metrics: &TypingMetrics,
694+
repo_info: &Option<GitRepositoryInfo>,
695+
) -> Result<()> {
688696
let mut stdout = stdout();
689697

690698
// Comprehensive screen reset
@@ -720,10 +728,22 @@ impl ResultScreen {
720728
}
721729

722730
// Show preview of what will be shared
723-
let preview_text = format!(
724-
"\"{}\" - Score: {:.0}, CPM: {:.0}, Mistakes: {}",
725-
metrics.ranking_title, metrics.challenge_score, metrics.cpm, metrics.mistakes
726-
);
731+
let preview_text = if let Some(repo) = repo_info {
732+
format!(
733+
"\"{}\" with {:.0}pts on [{}/{}] - CPM: {:.0}, Mistakes: {}",
734+
metrics.ranking_title,
735+
metrics.challenge_score,
736+
repo.user_name,
737+
repo.repository_name,
738+
metrics.cpm,
739+
metrics.mistakes
740+
)
741+
} else {
742+
format!(
743+
"\"{}\" with {:.0}pts - CPM: {:.0}, Mistakes: {}",
744+
metrics.ranking_title, metrics.challenge_score, metrics.cpm, metrics.mistakes
745+
)
746+
};
727747
let preview_col = center_col.saturating_sub(preview_text.len() as u16 / 2);
728748
execute!(stdout, MoveTo(preview_col, center_row.saturating_sub(5)))?;
729749
execute!(stdout, SetForegroundColor(Color::Cyan))?;
@@ -766,21 +786,35 @@ impl ResultScreen {
766786
if let Event::Key(key_event) = event::read()? {
767787
match key_event.code {
768788
KeyCode::Char('1') => {
769-
let _ = SharingService::share_result(metrics, SharingPlatform::X);
789+
let _ = SharingService::share_result(
790+
metrics,
791+
SharingPlatform::X,
792+
repo_info,
793+
);
770794
break;
771795
}
772796
KeyCode::Char('2') => {
773-
let _ = SharingService::share_result(metrics, SharingPlatform::Reddit);
797+
let _ = SharingService::share_result(
798+
metrics,
799+
SharingPlatform::Reddit,
800+
repo_info,
801+
);
774802
break;
775803
}
776804
KeyCode::Char('3') => {
777-
let _ =
778-
SharingService::share_result(metrics, SharingPlatform::LinkedIn);
805+
let _ = SharingService::share_result(
806+
metrics,
807+
SharingPlatform::LinkedIn,
808+
repo_info,
809+
);
779810
break;
780811
}
781812
KeyCode::Char('4') => {
782-
let _ =
783-
SharingService::share_result(metrics, SharingPlatform::Facebook);
813+
let _ = SharingService::share_result(
814+
metrics,
815+
SharingPlatform::Facebook,
816+
repo_info,
817+
);
784818
break;
785819
}
786820
KeyCode::Esc => break,

src/game/screens/typing_screen.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use super::{
55
CountdownScreen,
66
};
77
use crate::scoring::{engine::ScoringEngine, TypingMetrics};
8-
use crate::Result;
8+
use crate::{extractor::GitRepositoryInfo, Result};
99
use crossterm::{
1010
event::{self, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
1111
terminal,
@@ -28,6 +28,7 @@ pub struct TypingScreen {
2828
#[allow(dead_code)]
2929
last_esc_time: Option<std::time::Instant>,
3030
dialog_shown: bool,
31+
repo_info: Option<GitRepositoryInfo>,
3132
}
3233

3334
pub enum GameState {
@@ -40,7 +41,7 @@ pub enum GameState {
4041
}
4142

4243
impl TypingScreen {
43-
pub fn new(challenge_text: String) -> Result<Self> {
44+
pub fn new(challenge_text: String, repo_info: Option<GitRepositoryInfo>) -> Result<Self> {
4445
let processed_text = TextProcessor::process_challenge_text(&challenge_text);
4546
let challenge_chars: Vec<char> = processed_text.chars().collect();
4647
let line_starts = TextProcessor::calculate_line_starts(&processed_text);
@@ -70,10 +71,14 @@ impl TypingScreen {
7071
skips_remaining: 3,
7172
last_esc_time: None,
7273
dialog_shown: false,
74+
repo_info,
7375
})
7476
}
7577

76-
pub fn new_with_challenge(challenge: &Challenge) -> Result<Self> {
78+
pub fn new_with_challenge(
79+
challenge: &Challenge,
80+
repo_info: Option<GitRepositoryInfo>,
81+
) -> Result<Self> {
7782
// Apply basic text processing (remove empty lines, etc.)
7883
// Indentation normalization is already done in extractor
7984
let (processed_text, mapped_comment_ranges) =
@@ -109,6 +114,7 @@ impl TypingScreen {
109114
skips_remaining: 3,
110115
last_esc_time: None,
111116
dialog_shown: false,
117+
repo_info,
112118
})
113119
}
114120

@@ -124,7 +130,7 @@ impl TypingScreen {
124130
}
125131

126132
// Show countdown with challenge info if available
127-
CountdownScreen::show_with_challenge(self.challenge.as_ref())?;
133+
CountdownScreen::show_with_challenge_and_repo(self.challenge.as_ref(), &self.repo_info)?;
128134

129135
// Reset start time after countdown
130136
self.start_time = std::time::Instant::now();
@@ -141,6 +147,7 @@ impl TypingScreen {
141147
self.skips_remaining,
142148
self.dialog_shown,
143149
&self.scoring_engine,
150+
&self.repo_info,
144151
)?;
145152

146153
loop {
@@ -196,6 +203,7 @@ impl TypingScreen {
196203
self.skips_remaining,
197204
self.dialog_shown,
198205
&self.scoring_engine,
206+
&self.repo_info,
199207
)?;
200208

201209
loop {
@@ -248,6 +256,7 @@ impl TypingScreen {
248256
self.skips_remaining,
249257
self.dialog_shown,
250258
&self.scoring_engine,
259+
&self.repo_info,
251260
)?;
252261

253262
let final_state = loop {
@@ -424,6 +433,7 @@ impl TypingScreen {
424433
self.skips_remaining,
425434
self.dialog_shown,
426435
&self.scoring_engine,
436+
&self.repo_info,
427437
)
428438
}
429439

src/game/stage_manager.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,17 +171,18 @@ impl StageManager {
171171
// Show countdown before each stage
172172
if self.current_stage == 0 {
173173
// First stage - show initial countdown with challenge info
174-
CountdownScreen::show_with_challenge(Some(challenge))?;
174+
CountdownScreen::show_with_challenge_and_repo(Some(challenge), &self.git_info)?;
175175
} else {
176176
// Subsequent stages - show stage transition countdown with challenge info
177-
CountdownScreen::show_stage_transition_with_challenge(
177+
CountdownScreen::show_stage_transition_with_challenge_and_repo(
178178
self.current_stage + 1,
179179
self.current_challenges.len(),
180180
Some(challenge),
181+
&self.git_info,
181182
)?;
182183
}
183184

184-
let mut screen = TypingScreen::new_with_challenge(challenge)?;
185+
let mut screen = TypingScreen::new_with_challenge(challenge, self.git_info.clone())?;
185186
screen.set_skips_remaining(self.skips_remaining);
186187
let (metrics, final_state) = screen.show_with_state()?;
187188
self.skips_remaining = screen.get_skips_remaining();
@@ -320,7 +321,8 @@ impl StageManager {
320321
.unwrap();
321322

322323
if let Ok(session_metrics) = combined_engine.calculate_metrics() {
323-
let _ = ResultScreen::show_sharing_menu(&session_metrics);
324+
let _ =
325+
ResultScreen::show_sharing_menu(&session_metrics, &self.git_info);
324326
}
325327
}
326328
// Continue showing the summary screen after sharing (without animation)
@@ -382,12 +384,14 @@ impl StageManager {
382384
self.current_challenges.len(),
383385
self.stage_engines.len(),
384386
&self.stage_engines,
387+
&self.git_info,
385388
)
386389
} else {
387390
ResultScreen::show_session_summary_with_input_no_animation(
388391
self.current_challenges.len(),
389392
self.stage_engines.len(),
390393
&self.stage_engines,
394+
&self.git_info,
391395
)
392396
}
393397
}
@@ -408,6 +412,7 @@ impl StageManager {
408412
self.current_challenges.len(),
409413
self.stage_engines.len(),
410414
&self.stage_engines,
415+
&self.git_info,
411416
)?;
412417

413418
// Wait for navigation input

0 commit comments

Comments
 (0)