Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,018 changes: 992 additions & 26 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ anyhow = "1"
steamworks = { version = "0.12", features = ["raw-bindings"] }
libloading = "0.8"
indicatif = "0.17"
image = "0.25"
icy_sixel = "0.5"
base64 = "0.22.1"
libc = "0.2.182"
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,18 @@ export STEAM_ID="your_steam_id_here"
# Display your Steam stats
steamfetch

# Show profile avatar image instead of ASCII logo
steamfetch --image

# Specify image protocol (auto, kitty, iterm, sixel)
steamfetch --image --image-protocol sixel

# Demo mode (no API key required)
steamfetch --demo

# Demo mode with image
steamfetch --demo --image

# Show config file path
steamfetch --config-path

Expand All @@ -110,8 +119,23 @@ steamfetch --help
- Recently played games (last 2 weeks)
- Rarest achievement display
- Beautiful SteamOS ASCII art with gradient colors
- **Image display**: Show your Steam avatar with `--image` flag
- Demo mode for testing without API setup

### Image Display

Use `--image` to show your Steam profile avatar instead of the ASCII logo.

Supported protocols:
- **Sixel** - Windows Terminal, WezTerm, foot, mlterm, xterm
- **Kitty** - Kitty terminal
- **iTerm2** - iTerm2
- **Block characters** - Fallback for unsupported terminals

Protocol is auto-detected by default. Use `--image-protocol` to override.

Images are cached locally at `~/.cache/steamfetch/images/`.

## How It Works

### With Steam Client Running
Expand Down
66 changes: 64 additions & 2 deletions src/display.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,76 @@
use colored::Colorize;
use std::io::{self, Write};

use crate::image_display;
use crate::steam::SteamStats;
use crate::ImageProtocol;

pub fn render(stats: &SteamStats) {
const IMAGE_COLS: u32 = 34;
const IMAGE_ROWS: u32 = 18;

pub struct ImageConfig {
pub enabled: bool,
pub protocol: ImageProtocol,
}

pub async fn render(stats: &SteamStats, image_config: &ImageConfig) {
let info_lines = build_info_lines(stats);

if image_config.enabled {
render_with_image(stats, &info_lines, image_config).await;
} else {
render_with_ascii(&info_lines);
}
}

async fn render_with_image(stats: &SteamStats, info_lines: &[String], config: &ImageConfig) {
let avatar = match &stats.avatar_url {
Some(url) => {
let cache_key = format!("avatar_{}.png", stats.username);
image_display::load_cached_or_download(url, &cache_key).await
}
None => None,
};

let Some(img) = avatar else {
return render_with_ascii(info_lines);
};

println!();

// Print image and rewind cursor to top-left of image area
let image_rows =
image_display::print_image_and_rewind(&img, &config.protocol, IMAGE_COLS, IMAGE_ROWS);

let Some(image_rows) = image_rows else {
return render_with_ascii(info_lines);
};

let col_offset = IMAGE_COLS + 3; // image width + gap
let mut stdout = io::stdout().lock();

// Print info lines to the right of the image
for (i, line) in info_lines.iter().enumerate() {
if i > 0 {
writeln!(stdout).unwrap();
}
image_display::cursor_right(col_offset);
write!(stdout, "{}", line).unwrap();
}

// Ensure we end up below the image area
let extra = (image_rows as usize).saturating_sub(info_lines.len());
for _ in 0..=extra {
writeln!(stdout).unwrap();
}
stdout.flush().unwrap();
}

fn render_with_ascii(info_lines: &[String]) {
let logo_lines = build_logo();

println!();
for (i, logo_line) in logo_lines.iter().enumerate() {
// Offset info by 1 line to align vertically
let info = if i == 0 {
""
} else {
Expand Down
Loading