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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Add `show_avatar` config field.

## [0.9.8]

### Fixed
Expand Down
3 changes: 3 additions & 0 deletions README-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ cargo_integr = true

# 是否用 fronted id 创建代码目录
dir_with_frontend_id = true

# 是否展示头像,在kitty上工作的很好,在其他终端有可能出现问题
show_avatar = false
```

## 用户信息
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,25 +254,29 @@ page_size = 25
# - helix
#
# You can add additional parameters at the end.
# like `editor = ["vim", "--noplugin"]`
# Like `editor = ["vim", "--noplugin"]`
editor = ["vim"]

# Set your selected programming language.
lang = "rust"

# Set the location for storing code and test cases.
# You can also starting with `~`
# like `code_dir = "~/.local/share/lcode"`
# Like `code_dir = "~/.local/share/lcode"`.
code_dir = "/home/user/.local/share/lcode"

# Checkout the [Cookies (Important)](#cookies-important) section above.
browser = ""

# For better rust coding. It will add a `Cargo.toml` file
# For better rust coding. It will add a `Cargo.toml` file.
cargo_integr = true

# use frontend id create code dir or not
# Use frontend id create code dir or not.
dir_with_frontend_id = true

# Show you lc avatar or not.
# Works fine in kitty, may have problems in other terminals.
show_avatar = false
```

You can checkout the info/tab3 in tui for ensure cookies is valid.
Expand Down
10 changes: 7 additions & 3 deletions crates/lcode-config/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,13 @@ pub struct Config {
pub code_dir: PathBuf,
#[serde(default)]
pub browser: String,
#[serde(default = "cargo_default")]
#[serde(default = "default_true")]
pub cargo_integr: bool,
#[serde(default = "default_ser_bool")]
pub dir_with_frontend_id: bool, // create qs dir use frontend id
#[serde(default)]
/// create qs dir use frontend id
pub dir_with_frontend_id: bool,
#[serde(default)]
pub show_avatar: bool,
}

impl Config {
Expand Down Expand Up @@ -86,6 +89,7 @@ impl Default for Config {
browser: String::new(),
cargo_integr: true,
dir_with_frontend_id: false,
show_avatar: false,
}
}
}
Expand Down
6 changes: 1 addition & 5 deletions crates/lcode-config/src/config/user_serializes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,9 @@ where
serializer.serialize_str(res)
}

pub(super) const fn cargo_default() -> bool {
pub(super) const fn default_true() -> bool {
true
}
/// return false
pub(super) const fn default_ser_bool() -> bool {
false
}
pub(super) fn lang_default() -> String {
"rust".to_owned()
}
Expand Down
102 changes: 62 additions & 40 deletions crates/lcode/src/app/impl_app/get_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use leetcode_api::{
glob_leetcode,
leetcode::resps::{checkin::TotalPoints, pass_qs::PassData, user_data::UserStatus},
};
use miette::IntoDiagnostic;
use notify_rust::Notification;
use ratatui::prelude::*;
use ratatui_image::{Resize, picker::Picker, protocol::StatefulProtocol, thread::ThreadProtocol};
Expand Down Expand Up @@ -104,57 +103,80 @@ impl App<'_> {
});
}

pub fn get_status_done(
&mut self,
info: (UserStatus, TotalPoints, PassData, Option<PathBuf>),
) -> miette::Result<()> {
#[expect(clippy::cognitive_complexity, reason = "todo refactor")]
pub fn get_status_done(&mut self, info: (UserStatus, TotalPoints, PassData, Option<PathBuf>)) {
(
self.info.user_status,
self.info.points,
self.info.pass_data,
self.info.avatar_path,
) = info;

if self.img_state.is_none() && self.info.avatar_path.is_some() {
let mut picker =
Picker::from_query_stdio().or(Err(miette::miette!("Image Picker error")))?;

picker.set_background_color([255, 0, 255, 0]);
let dyn_img = image::ImageReader::open(
self.info
.avatar_path
.as_ref()
.expect("No avatar file"),
)
.into_diagnostic()?
.with_guessed_format()
.into_diagnostic()?
.decode()
.into_diagnostic()?
.resize_to_fill(150, 150, ratatui_image::FilterType::Triangle);

// Send a [ResizeProtocol] to resize and encode it in a separate thread.
let (tx_worker, rec_worker) = mpsc::channel::<(StatefulProtocol, Resize, Rect)>();

// Resize and encode in background thread.
let tx_main_render = self.events.tx.clone();
thread::spawn(move || {
loop {
if let Ok((mut protocol, resize, area)) = rec_worker.recv() {
protocol.resize_encode(&resize, Rgba([0; 4]), area);
if let Err(e) = tx_main_render.send(UserEvent::RedrawImg(protocol)) {
tracing::error!("{e}");
'o: {
if self.img_state.is_none()
&& self.info.avatar_path.is_some()
&& G_USER_CONFIG.config.show_avatar
{
let mut picker = match Picker::from_query_stdio() {
Ok(p) => p,
Err(e) => {
tracing::error!("Image error: {}", e);
break 'o;
},
};

picker.set_background_color([255, 0, 255, 0]);

let dyn_img = match image::ImageReader::open(unsafe {
self.info
.avatar_path
.as_ref()
.unwrap_unchecked()
}) {
Ok(img) => img,
Err(e) => {
tracing::error!("Image error: {}", e);
break 'o;
},
};
let dyn_img = match dyn_img.with_guessed_format() {
Ok(i) => i,
Err(e) => {
tracing::error!("Image error: {}", e);
break 'o;
},
};
let dyn_img = match dyn_img.decode() {
Ok(i) => i,
Err(e) => {
tracing::error!("Image error: {}", e);
break 'o;
},
};
let dyn_img = dyn_img.resize_to_fill(150, 150, ratatui_image::FilterType::Triangle);

// Send a [ResizeProtocol] to resize and encode it in a separate thread.
let (tx_worker, rec_worker) = mpsc::channel::<(StatefulProtocol, Resize, Rect)>();

// Resize and encode in background thread.
let tx_main_render = self.events.tx.clone();
thread::spawn(move || {
loop {
if let Ok((mut protocol, resize, area)) = rec_worker.recv() {
protocol.resize_encode(&resize, Rgba([0; 4]), area);
if let Err(e) = tx_main_render.send(UserEvent::RedrawImg(protocol)) {
tracing::error!("{e}");
}
}
}
}
});
});

let async_state = ThreadProtocol::new(tx_worker, picker.new_resize_protocol(dyn_img));
self.img_state = Some(async_state);
let async_state =
ThreadProtocol::new(tx_worker, picker.new_resize_protocol(dyn_img));
self.img_state = Some(async_state);
}
}

self.render();

Ok(())
}
}
2 changes: 1 addition & 1 deletion crates/lcode/src/mytui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub async fn run() -> Result<()> {
Term::stop()?;
break;
},
UserEvent::UserInfo(info) => app.get_status_done(*info)?,
UserEvent::UserInfo(info) => app.get_status_done(*info),
UserEvent::SubmitDone(s_res) => {
// update info
if s_res.total_correct == s_res.total_testcases {
Expand Down
14 changes: 8 additions & 6 deletions crates/lcode/src/mytui/ui/info.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use lcode_config::global::G_THEME;
use lcode_config::global::{G_THEME, G_USER_CONFIG};
use ratatui::{prelude::*, widgets::*};
use ratatui_image::{Resize, thread::ThreadImage};

Expand Down Expand Up @@ -77,11 +77,13 @@ pub fn draw_info(f: &mut Frame, app: &mut App, area: Rect) {
.split(chunks[0]);
assert!(chunks1.len() >= 2);
f.render_widget(user_info_list, chunks1[0]);
draw_avatar(
f,
app,
helper::top_right_rect(14, 9, chunks1[0].inner(Margin::new(1, 1))),
);
if G_USER_CONFIG.config.show_avatar {
draw_avatar(
f,
app,
helper::top_right_rect(14, 9, chunks1[0].inner(Margin::new(1, 1))),
);
}
f.render_widget(pass_info_list, chunks1[1]);
f.render_stateful_widget(keymap_list, chunks[1], &mut app.info.keymap.list_state);
}
Expand Down
Loading