12345module view67import fmt891011pub struct ManualLine {12pub:13 text string14 section string15}161718pub fn page_manual(lines []ManualLine) {19 enter_screen()20 defer {21 leave_screen()22 }23 mut page := 024 for {25 rows, cols := terminal_size()26 size := if rows > 2 { rows - 1 } else { 1 }27 last := last_page(lines.len, size)28 if page > last {29 page = last30 }31 paint(render(manual_frame(lines, page, size, cols)))32 key := read_key()33 match key.kind {34 .interrupt, .eof, .escape, .enter {35 return36 }37 .down, .page_down {38 page = if page < last { page + 1 } else { page }39 }40 .up, .page_up {41 page = if page > 0 { page - 1 } else { 0 }42 }43 .home {44 page = 045 }46 .end {47 page = last48 }49 .tab {50 page = section_page(lines, page, size, true)51 }52 .backtab {53 page = section_page(lines, page, size, false)54 }55 .rune {56 match key.text {57 'q' { return }58 'j', ' ' { page = if page < last { page + 1 } else { page } }59 'k' { page = if page > 0 { page - 1 } else { 0 } }60 'g' { page = 0 }61 'G' { page = last }62 else {}63 }64 }65 else {}66 }67 }68}697071pub fn page_text(lines []string, title string) {72 enter_screen()73 defer { leave_screen() }74 mut page := 075 for {76 rows, cols := terminal_size()77 size := if rows > 2 { rows - 1 } else { 1 }78 last := last_page(lines.len, size)79 if page > last {80 page = last81 }82 start := page * size83 end := if start + size < lines.len { start + size } else { lines.len }84 mut frame := lines[start..end].map(fmt.fit(it, cols))85 for frame.len < size {86 frame << ''87 }88 frame << fmt.fit('${fmt.blue(title)} · page ${page + 1} of ${last + 1} · ↑↓ j/k · q quit', cols)89 paint(render(frame))90 key := read_key()91 match key.kind {92 .interrupt, .eof, .escape, .enter { return }93 .down, .page_down { page = if page < last { page + 1 } else { page } }94 .up, .page_up { page = if page > 0 { page - 1 } else { 0 } }95 .rune {96 if key.text == 'q' {97 return98 }99 if key.text == 'j' || key.text == ' ' {100 page = if page < last { page + 1 } else { page }101 }102 if key.text == 'k' {103 page = if page > 0 { page - 1 } else { 0 }104 }105 }106 else {}107 }108 }109}110111fn last_page(count int, size int) int {112 return if count == 0 { 0 } else { (count - 1) / size }113}114115116pub fn manual_frame(lines []ManualLine, page int, size int, cols int) []string {117 start := page * size118 end := if start + size < lines.len { start + size } else { lines.len }119 mut frame := []string{}120 for line in lines[start..end] {121 frame << fmt.fit(line.text, cols)122 }123 for frame.len < size {124 frame << ''125 }126 bar := '${fmt.blue(section_at(lines, start))} · ' +127 fmt.muted('page ${page + 1} of ${last_page(lines.len, size) + 1} · tab: next section · ↑↓ j/k · q quit')128 frame << fmt.fit(bar, cols)129 return frame130}131132133134fn section_at(lines []ManualLine, index int) string {135 for i := index; i >= 0; i-- {136 if i < lines.len && lines[i].section != '' {137 return lines[i].section138 }139 }140 return 'KOH'141}142143144145pub fn section_page(lines []ManualLine, page int, size int, forward bool) int {146 if forward {147 for i := (page + 1) * size; i < lines.len; i++ {148 if lines[i].section != '' {149 return i / size150 }151 }152 return page153 }154 for i := page * size - 1; i >= 0; i-- {155 if lines[i].section != '' {156 return i / size157 }158 }159 return page160}