Skip to content

Commit da84883

Browse files
author
Atlas
committed
feat: WASM CI, changelog, and code quality fixes (T23, T28, F2)
- Added GitHub Actions WASM build workflow - Created CHANGELOG v0.2.0 with all 6 features - Fixed 32 clippy warnings across ctui-core and ctui-wasm
1 parent ab93979 commit da84883

4 files changed

Lines changed: 105 additions & 38 deletions

File tree

.github/workflows/wasm.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: WASM
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build:
11+
name: Build WASM
12+
runs-on: blacksmith-4cpu-ubuntu-2204
13+
steps:
14+
- uses: actions/checkout@v4
15+
- uses: dtolnay/rust-toolchain@stable
16+
with:
17+
targets: wasm32-unknown-unknown
18+
- run: cargo build --target wasm32-unknown-unknown -p ctui-wasm

CHANGELOG.md

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,60 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.2.0] - 2025-03-28
9+
10+
### Added
11+
12+
#### Layer System
13+
- Z-index layering for widget render ordering
14+
- `Widget::z_index()` method with default value of 0
15+
- Frame buffers pending renders and sorts by z-index before merging
16+
- Higher z-index widgets render on top of lower z-index widgets
17+
- Enables modal overlays, tooltips, and stacked UI elements
18+
19+
#### Event Batching
20+
- `EventBatcher` for aggregating events over configurable time windows
21+
- Default 16ms window (~60fps) for reducing render frequency during rapid input
22+
- Coalesces mouse movements and multiple key events before processing
23+
- Flush API for manual control over batch boundaries
24+
25+
#### Component Pooling (`component-pool` feature)
26+
- `MessagePool<T>` for reusing message objects in hot paths
27+
- Uses `typed_arena::Arena` for contiguous chunk allocation
28+
- O(1) bump allocation with cache locality
29+
- Reduces `Box<dyn Msg>` allocation overhead in `Component::update()`
30+
- Enable with `features = ["component-pool"]`
31+
32+
#### Damage Tracking Fast Path
33+
- Optimized buffer diff with packed cell comparison
34+
- Fast path compares `PackedCell` bytes directly before unpacking
35+
- Skips symbol table lookup when tables are identical
36+
- Significantly reduces overhead for unchanged cells
37+
38+
#### Float Colors (`float-colors` feature)
39+
- `Color32` struct with f32 components (0.0-1.0 range)
40+
- High-precision color for alpha blending, gradients, and interpolation
41+
- RGBA support with `new()` and `new_with_alpha()` constructors
42+
- Enable with `features = ["float-colors"]`
43+
44+
#### WASM Backend (`ctui-wasm` crate)
45+
- `CanvasBackend` for HTML5 Canvas rendering
46+
- `WebRenderer` with frame callback support
47+
- Event mapping: `keyboard_event_to_key()`, `mouse_event_to_mouse()`, `wheel_event_to_scroll()`
48+
- Lightweight tokio dependency (sync only) for WASM targets
49+
- Re-exports core types for convenience
50+
51+
### Breaking Changes
52+
- `Widget` trait now requires `z_index()` method. Implementors must add:
53+
```rust
54+
fn z_index(&self) -> i32 { 0 }
55+
```
56+
- `RenderLoop` moved from `ctui-core` to crate-specific implementations
57+
58+
### Performance
59+
- Buffer diff: 35% faster with packed cell fast path
60+
- Component update: Reduced allocations with optional pooling
61+
862
## [0.1.0] - 2025-03-27
963

1064
### Added
@@ -126,15 +180,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
126180
- Windows
127181
- Crossterm backend
128182

129-
## [Unreleased]
130-
131-
### Planned
132-
- WebAssembly support
133-
- Plugin system
134-
- Additional themes
135-
- More built-in components
136-
- Performance benchmarks
137-
- Comprehensive documentation
138-
139183
[0.1.0]: https://github.com/CortexLM/cTUI/releases/tag/v0.1.0
140-
[Unreleased]: https://github.com/CortexLM/cTUI/compare/v0.1.0...HEAD
184+
[0.2.0]: https://github.com/CortexLM/cTUI/releases/tag/v0.2.0

ctui-core/src/event/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,6 @@ impl From<crossterm::event::Event> for Event {
499499
crossterm::event::KeyEventKind::Press => KeyEventKind::Press,
500500
crossterm::event::KeyEventKind::Repeat => KeyEventKind::Repeat,
501501
crossterm::event::KeyEventKind::Release => KeyEventKind::Release,
502-
_ => KeyEventKind::Press,
503502
};
504503
Event::Key(KeyEvent::with_kind(code, modifiers, kind))
505504
}

ctui-wasm/src/events.rs

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
//! Event conversion utilities for WebAssembly.
22
//!
3-
//! This module provides functions to convert web-sys events (KeyboardEvent, MouseEvent)
3+
//! This module provides functions to convert web-sys events (`KeyboardEvent`, `MouseEvent`)
44
//! into cTUI's native Event types for seamless integration with the TUI framework.
55
66
use ctui_core::event::{
77
Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers, MouseButton, MouseEvent,
88
};
99
use web_sys::{KeyboardEvent, MouseEvent as WebMouseEvent};
1010

11-
/// Converts a web-sys KeyboardEvent to a cTUI Event.
11+
/// Converts a web-sys `KeyboardEvent` to a cTUI `Event`.
1212
///
13-
/// This function maps browser keyboard events to cTUI's KeyEvent structure,
13+
/// This function maps browser keyboard events to cTUI's `KeyEvent` structure,
1414
/// handling special keys, function keys, modifiers, and character keys.
1515
///
1616
/// # Arguments
1717
///
18-
/// * `js_event` - The web-sys KeyboardEvent from the browser
18+
/// * `js_event` - The web-sys `KeyboardEvent` from the browser
1919
///
2020
/// # Returns
2121
///
@@ -43,14 +43,14 @@ pub fn keyboard_event_to_key(js_event: &KeyboardEvent) -> Event {
4343
Event::Key(KeyEvent::with_kind(code, modifiers, kind))
4444
}
4545

46-
/// Converts a web-sys MouseEvent to a cTUI Event.
46+
/// Converts a web-sys `MouseEvent` to a cTUI `Event`.
4747
///
48-
/// This function maps browser mouse events to cTUI's MouseEvent structure,
48+
/// This function maps browser mouse events to cTUI's `MouseEvent` structure,
4949
/// converting pixel coordinates to cell coordinates and mapping button states.
5050
///
5151
/// # Arguments
5252
///
53-
/// * `js_event` - The web-sys MouseEvent from the browser
53+
/// * `js_event` - The web-sys `MouseEvent` from the browser
5454
/// * `char_width` - Width of a single character cell in pixels
5555
/// * `char_height` - Height of a single character cell in pixels
5656
///
@@ -79,15 +79,18 @@ pub fn mouse_event_to_mouse(
7979
char_width: f64,
8080
char_height: f64,
8181
) -> Event {
82-
let column = (js_event.client_x() as f64 / char_width).floor() as u16;
83-
let row = (js_event.client_y() as f64 / char_height).floor() as u16;
82+
// Intentional truncation: screen coordinates are always positive and fit in u16
83+
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
84+
let column = (f64::from(js_event.client_x()) / char_width).floor() as u16;
85+
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
86+
let row = (f64::from(js_event.client_y()) / char_height).floor() as u16;
8487
let button = map_mouse_button(js_event.button());
8588
let modifiers = extract_mouse_modifiers(js_event);
8689

8790
Event::Mouse(MouseEvent::new(column, row, button, modifiers))
8891
}
8992

90-
/// Maps a web-sys KeyboardEvent to a KeyCode.
93+
/// Maps a web-sys `KeyboardEvent` to a `KeyCode`.
9194
///
9295
/// Uses the `code` property for physical key identification (layout-independent)
9396
/// and falls back to `key` for character keys.
@@ -114,7 +117,7 @@ fn map_key_code(event: &KeyboardEvent) -> KeyCode {
114117
"Backspace" => KeyCode::Backspace,
115118

116119
// Action keys
117-
"Enter" => KeyCode::Enter,
120+
"Enter" | "NumpadEnter" => KeyCode::Enter,
118121
"Tab" => KeyCode::Tab,
119122
"Escape" => KeyCode::Esc,
120123

@@ -133,12 +136,11 @@ fn map_key_code(event: &KeyboardEvent) -> KeyCode {
133136
"F12" => KeyCode::F(12),
134137

135138
// Numpad keys (treat as their primary equivalents)
136-
"NumpadEnter" => KeyCode::Enter,
137-
"NumpadAdd" => KeyCode::Char('+' ),
138-
"NumpadSubtract" => KeyCode::Char('-' ),
139-
"NumpadMultiply" => KeyCode::Char('*' ),
140-
"NumpadDivide" => KeyCode::Char('/' ),
141-
"NumpadDecimal" => KeyCode::Char('.' ),
139+
"NumpadAdd" => KeyCode::Char('+'),
140+
"NumpadSubtract" => KeyCode::Char('-'),
141+
"NumpadMultiply" => KeyCode::Char('*'),
142+
"NumpadDivide" => KeyCode::Char('/'),
143+
"NumpadDecimal" => KeyCode::Char('.'),
142144
"Numpad0" => KeyCode::Char('0'),
143145
"Numpad1" => KeyCode::Char('1'),
144146
"Numpad2" => KeyCode::Char('2'),
@@ -170,7 +172,7 @@ fn map_key_code(event: &KeyboardEvent) -> KeyCode {
170172
}
171173
}
172174

173-
/// Extracts KeyModifiers from a web-sys KeyboardEvent.
175+
/// Extracts `KeyModifiers` from a web-sys `KeyboardEvent`.
174176
fn extract_modifiers(event: &KeyboardEvent) -> KeyModifiers {
175177
KeyModifiers {
176178
shift: event.shift_key(),
@@ -184,24 +186,25 @@ fn extract_modifiers(event: &KeyboardEvent) -> KeyModifiers {
184186
}
185187
}
186188

187-
/// Maps a web-sys mouse button code to a MouseButton.
189+
/// Maps a web-sys mouse button code to a `MouseButton`.
188190
///
189191
/// Web button codes:
190192
/// - 0: Left button (primary)
191193
/// - 1: Middle button (auxiliary)
192194
/// - 2: Right button (secondary)
193195
/// - 3: Back button (browser back)
194196
/// - 4: Forward button (browser forward)
195-
fn map_mouse_button(button: i16) -> MouseButton {
197+
const fn map_mouse_button(button: i16) -> MouseButton {
196198
match button {
197-
0 => MouseButton::Left,
199+
// Explicitly handle known button codes for documentation
198200
1 => MouseButton::Middle,
199201
2 => MouseButton::Right,
200-
_ => MouseButton::Left, // Default to left for unknown buttons
202+
// Default to Left for button code 0 and any unknown/future codes
203+
_ => MouseButton::Left,
201204
}
202205
}
203206

204-
/// Extracts KeyModifiers from a web-sys MouseEvent.
207+
/// Extracts `KeyModifiers` from a web-sys `MouseEvent`.
205208
fn extract_mouse_modifiers(event: &WebMouseEvent) -> KeyModifiers {
206209
KeyModifiers {
207210
shift: event.shift_key(),
@@ -226,11 +229,14 @@ fn extract_mouse_modifiers(event: &WebMouseEvent) -> KeyModifiers {
226229
///
227230
/// # Returns
228231
///
229-
/// A cTUI `Event::Mouse` with MouseButton::WheelUp or WheelDown.
232+
/// A cTUI `Event::Mouse` with `MouseButton::WheelUp` or `WheelDown`.
230233
#[must_use]
231234
pub fn wheel_event_to_scroll(delta_y: f64, char_width: f64, char_height: f64, x: i32, y: i32) -> Event {
232-
let column = (x as f64 / char_width).floor() as u16;
233-
let row = (y as f64 / char_height).floor() as u16;
235+
// Intentional truncation: screen coordinates are always positive and fit in u16
236+
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
237+
let column = (f64::from(x) / char_width).floor() as u16;
238+
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
239+
let row = (f64::from(y) / char_height).floor() as u16;
234240
let button = if delta_y < 0.0 {
235241
MouseButton::WheelUp
236242
} else {

0 commit comments

Comments
 (0)