Skip to content

Commit 8c06fed

Browse files
program247365claude
andcommitted
refactor: split main.rs into types, hn_api, and ui modules
Extract data types into types.rs, HTTP functions into hn_api.rs, and rendering into ui.rs to match the documented architecture. Add make format and make verify targets. Fix all clippy warnings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 67c1f81 commit 8c06fed

7 files changed

Lines changed: 814 additions & 733 deletions

File tree

CLAUDE.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## What This Is
6+
7+
Hackertuah is a Rust TUI application for browsing Hacker News. It uses ratatui/crossterm for the terminal UI, tokio/reqwest for async HTTP, and integrates with the Claude API for story summarization. The app features vim-style navigation, a command palette, search/filter, and section switching (Top, Ask, Show, Jobs).
8+
9+
## Build & Run
10+
11+
```bash
12+
make build # cargo build
13+
make run # cargo run
14+
make test # cargo test
15+
make lint # cargo clippy -- -D warnings
16+
make format # cargo fmt
17+
make verify # format + lint + build + test (run after any change)
18+
make clean # cargo clean
19+
make install # release build + copy binary to ~/bin
20+
make bump # cog bump --auto (cocogitto versioning)
21+
```
22+
23+
## Architecture
24+
25+
The `src/` directory is split by concern:
26+
27+
- **`types.rs`** — Data types: `Story`, `Section`, `Mode`, `ClaudeRequest`, `Message`. No dependencies on other local modules.
28+
- **`hn_api.rs`** — HTTP layer: `fetch_stories` (HN Firebase API) and `get_claude_summary` (Anthropic API). Depends on `types`.
29+
- **`ui.rs`** — All rendering: `draw_ui`, `draw_menu`, `draw_summary`, `draw_command_palette`, `centered_rect`. Depends on `types` and `app`.
30+
- **`loading_screen.rs`**`MatrixRain` struct for the Matrix-style loading animation.
31+
- **`main.rs`**`App` struct (all application state), `Command`/`CommandPalette`, terminal setup/teardown, and the main event loop. `App` is exposed via `pub mod app` so `ui.rs` can reference it.
32+
33+
The app uses a single-threaded tokio runtime. Story fetching spawns tokio tasks per section that run concurrently. The `App` struct holds all state: stories, UI mode, cached stories per section, command palette state, and search state.
34+
35+
## Key Details
36+
37+
- HN API: Firebase REST API at `hacker-news.firebaseio.com/v0/`. Fetches up to 100 stories per section.
38+
- Claude API: Requires `CLAUDE_API_KEY` env var. Currently hardcoded to `claude-3-opus-20240229` model.
39+
- Versioning: Uses cocogitto (`cog.toml`) with conventional commits. Changelog at `CHANGELOG.md`.
40+
- CI: GitHub Actions runs `cargo build` and `cargo test` on push/PR to main.
41+
- Cargo.toml lists edition 2021; version is `0.1.0` (behind the `0.2.0` tag from cog).

Makefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
.PHONY: help build run test lint clean bump install
1+
.PHONY: help build run test lint format verify clean bump install
22

33
help:
44
@echo "Available commands:"
55
@echo " make build - Build the project (cargo build)"
66
@echo " make run - Run the project (cargo run)"
77
@echo " make test - Run tests (cargo test)"
88
@echo " make lint - Run clippy linter (cargo clippy)"
9+
@echo " make format - Run cargo fmt"
10+
@echo " make verify - Format, lint, build, and test (use after any change)"
911
@echo " make clean - Clean build artifacts (cargo clean)"
1012
@echo " make bump - Bump version with cog (cog bump --auto)"
1113
@echo " make install - Build and install binary to system bin directory"
@@ -22,6 +24,11 @@ test:
2224
lint:
2325
cargo clippy -- -D warnings
2426

27+
format:
28+
cargo fmt
29+
30+
verify: format lint build test
31+
2532
clean:
2633
cargo clean
2734

src/hn_api.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use std::error::Error;
2+
3+
use crate::types::{ClaudeRequest, Message, Section, Story};
4+
5+
pub async fn fetch_stories(section: Section) -> Result<Vec<Story>, Box<dyn Error + Send + Sync>> {
6+
let client = reqwest::Client::new();
7+
8+
let ids: Vec<u32> = client
9+
.get(section.get_api_url())
10+
.send()
11+
.await?
12+
.json()
13+
.await?;
14+
15+
let mut stories = Vec::new();
16+
for id in ids.iter().take(100) {
17+
let story: Story = client
18+
.get(format!(
19+
"https://hacker-news.firebaseio.com/v0/item/{}.json",
20+
id
21+
))
22+
.send()
23+
.await?
24+
.json()
25+
.await?;
26+
stories.push(story);
27+
}
28+
29+
Ok(stories)
30+
}
31+
32+
pub async fn get_claude_summary(text: &str) -> Result<String, Box<dyn Error + Send + Sync>> {
33+
let client = reqwest::Client::new();
34+
35+
let request = ClaudeRequest {
36+
model: "claude-3-opus-20240229".to_string(),
37+
messages: vec![Message {
38+
role: "user".to_string(),
39+
content: format!(
40+
"Please summarize this Hacker News post concisely:\n\n{}",
41+
text
42+
),
43+
}],
44+
max_tokens: 150,
45+
};
46+
47+
let response = client
48+
.post("https://api.anthropic.com/v1/messages")
49+
.header("x-api-key", std::env::var("CLAUDE_API_KEY")?)
50+
.json(&request)
51+
.send()
52+
.await?;
53+
54+
Ok(response.text().await?)
55+
}

src/loading_screen.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ impl MatrixRain {
7171
for x in 0..self.chars.len() {
7272
let pos = self.positions[x] as i32;
7373
let char_index = (y as i32 - pos).rem_euclid(self.chars[x].len() as i32) as usize;
74-
let intensity = ((y as i32 - pos) as f32 * 0.5).min(1.0).max(0.0);
74+
let intensity = ((y as i32 - pos) as f32 * 0.5).clamp(0.0, 1.0);
7575

7676
if intensity <= 0.0 {
7777
line.push(Span::styled(

0 commit comments

Comments
 (0)