From b7db49d88c2c6cf170a45e9bda459f3ca48ae678 Mon Sep 17 00:00:00 2001 From: p11 Date: Tue, 8 Apr 2025 01:17:30 +0200 Subject: [PATCH] Made stories interative by default --- src/main.rs | 111 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index c34ef9f..475d9b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -519,11 +519,11 @@ fn handle_relative_connection( .rsplit_once('/') .map_or(relative_path, |(_, title)| title); - fn entry_handler( + fn entry_handler( path: &Path, relative_path: &str, censored: bool, - ) -> impl Fn(&str, &mut TcpStream, usize) { + ) -> impl Fn(&str, &mut W, usize) { move |mut entry, output, level| { let level = level + 1; let mut pki_path = path.to_path_buf(); @@ -573,14 +573,105 @@ fn handle_relative_connection( if !title.is_empty() { let _ = writeln!(stream, "

{title}

"); } - convert_extended( - lines.map(Result::unwrap_or_default), - &mut stream, - Settings::default() - .with_handler(entry_handler(path, relative_path, censored)) - .with_start_level(start_level) - .with_use_textboxes(true), - ); + + let check_path: &Path = relative_path.as_ref(); + let interactive = check_path.parent().is_some_and(|parent| { + std::fs::metadata(parent.with_extension("vng")).is_ok_and(|file| file.is_file()) + }); + + if interactive { + let mut sections = Vec::new(); + let mut current_section = Vec::new(); + + let mut add_section = |current_section: Vec| { + if current_section.is_empty() { + return; + } + + let mut section_html = Vec::new(); + convert_extended( + current_section, + &mut section_html, + Settings::default() + .with_handler(entry_handler(path, relative_path, censored)) + .with_start_level(start_level) + .with_use_textboxes(true), + ); + sections.push(String::from_utf8_lossy(§ion_html).into_owned()); + }; + + for line in lines { + let line = line.unwrap_or_default(); + if !line.is_empty() { + current_section.push(line); + continue; + } + + add_section(std::mem::take(&mut current_section)); + } + + add_section(current_section); + + let html = html! { + div id="story-container" { + div { + span id="section-counter" { "1/" (sections.len()) } + } + + div { + button onclick="prev()" { "Prev" } + button onclick="next()" { "Next" } + } + + @for (index, section) in sections.iter().enumerate() { + div class="story-section" data-section-index=(index) + style=(format!("display: {};", if index == 0 { "block" } else { "none" })) { + (maud::PreEscaped(section)) + } + } + + script { + (maud::PreEscaped(r" + let currentSection = 0; + const totalSections = ".to_owned() + §ions.len().to_string() + r"; + + function updateSection() { + document.querySelectorAll('.story-section').forEach((el, index) => { + el.style.display = index === currentSection ? 'block' : 'none'; + }); + document.getElementById('section-counter').textContent = + `${currentSection + 1}/${totalSections}`; + } + + function prev() { + if (currentSection > 0) { + --currentSection; + updateSection(); + } + } + + function next() { + if (currentSection < totalSections - 1) { + ++currentSection; + updateSection(); + } + } + ")) + } + } + }; + + let _ = write!(stream, "{}", html.into_string()); + } else { + convert_extended( + lines.map(Result::unwrap_or_default), + &mut stream, + Settings::default() + .with_handler(entry_handler(path, relative_path, censored)) + .with_start_level(start_level) + .with_use_textboxes(true), + ); + } section(&mut stream);