Iocraft
GitHub概要
ccbrown/iocraft
A Rust crate for beautiful, artisanally crafted CLIs, TUIs, and text-based IO.
スター679
ウォッチ4
フォーク15
作成日:2024年9月2日
言語:Rust
ライセンス:Apache License 2.0
トピックス
cliiocraftlogsrustterminaltui
スター履歴
データ取得日時: 2025/7/25 11:09
Iocraft
Iocraftは、ReactのコンセプトをRustのTUI開発に持ち込んだ革新的なライブラリです。JSX風の構文、コンポーネントベースの設計、フックの使用など、Reactの良さをターミナルアプリケーション開発に活かすことができます。
特徴
ReactライクなAPI
- JSX風構文: マクロを使ったJSXライクな構文
- コンポーネント: 関数コンポーネントのサポート
- フック: useState、useEffectなどのフックAPI
- Props: コンポーネント間のデータ受け渡し
- コンテキスト: React Contextのような状態管理
宣言的UI
- 仮想DOM: 効率的な差分レンダリング
- 自動再レンダリング: 状態変更時の自動UI更新
- イベントハンドリング: イベントの宣言的な処理
- レイアウト: Flexboxライクなレイアウトシステム
開発者体験
- ホットリロード: 開発中の自動リロード
- デバッグツール: React DevTools風のデバッグ機能
- 型安全: Rustの型システムによる安全性
- エラーハンドリング: エラーバウンダリのサポート
モダンな機能
- 非同期対応: async/awaitのサポート
- サスペンス: 非同期コンポーネントのサポート
- ポータル: コンポーネントの動的な配置
- カスタムフック: 独自フックの作成
基本的な使用方法
インストール
[dependencies]
iocraft = "0.5"
tokio = { version = "1", features = ["full"] }
Hello World
use iocraft::prelude::*;
#[component]
fn App() -> impl Into<AnyElement> {
element! {
Box(padding: 2, border_style: BorderStyle::Round) {
Text(content: "Hello, Iocraft!")
}
}
}
#[tokio::main]
async fn main() -> Result<()> {
let mut terminal = Terminal::new()?;
terminal.run(App).await?;
Ok(())
}
コンポーネントの作成
use iocraft::prelude::*;
#[derive(Props, Default)]
struct ButtonProps {
label: String,
#[prop(default = || Box::new(|_| {}))]
on_click: Handler<'static, ()>,
}
#[component]
fn Button(props: &ButtonProps) -> impl Into<AnyElement> {
let ButtonProps { label, on_click } = props;
let (is_hovered, set_is_hovered) = use_state(|| false);
let style = if is_hovered {
Style::default().fg(Color::Yellow).bold()
} else {
Style::default()
};
element! {
Box(
border_style: BorderStyle::Round,
padding: 1,
on_mouse_enter: move |_| set_is_hovered(true),
on_mouse_leave: move |_| set_is_hovered(false),
on_click: on_click.clone()
) {
Text(content: label.clone(), style)
}
}
}
#[component]
fn App() -> impl Into<AnyElement> {
let (count, set_count) = use_state(|| 0);
element! {
Flex(direction: FlexDirection::Column, gap: 1) {
Text(content: format!("Count: {}", count))
Button(
label: "Increment".to_string(),
on_click: move |_| set_count(count + 1)
)
Button(
label: "Decrement".to_string(),
on_click: move |_| set_count(count.saturating_sub(1))
)
}
}
}
フックの使用
use iocraft::prelude::*;
use std::time::Duration;
#[component]
fn Timer() -> impl Into<AnyElement> {
let (seconds, set_seconds) = use_state(|| 0);
// useEffect相当
use_effect(move || {
let handle = tokio::spawn(async move {
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
set_seconds.update(|s| s + 1);
}
});
// Cleanup function
move || {
handle.abort();
}
});
element! {
Box(border_style: BorderStyle::Single) {
Text(content: format!("Timer: {} seconds", seconds))
}
}
}
フォーム入力
use iocraft::prelude::*;
#[component]
fn LoginForm() -> impl Into<AnyElement> {
let (username, set_username) = use_state(String::new);
let (password, set_password) = use_state(String::new);
let (message, set_message) = use_state(String::new);
let handle_submit = move |_| {
if username.is_empty() || password.is_empty() {
set_message("Please fill in all fields".to_string());
} else {
set_message(format!("Welcome, {}!", username));
}
};
element! {
Box(padding: 2, border_style: BorderStyle::Double) {
Flex(direction: FlexDirection::Column, gap: 1) {
Text(content: "Login Form", style: Style::default().bold())
Box {
Text(content: "Username: ")
TextInput(
value: username.clone(),
on_change: move |new_value| set_username(new_value),
width: 20
)
}
Box {
Text(content: "Password: ")
TextInput(
value: password.clone(),
on_change: move |new_value| set_password(new_value),
password: true,
width: 20
)
}
Button(
label: "Login".to_string(),
on_click: handle_submit
)
if !message.is_empty() {
Text(content: message.clone(), style: Style::default().fg(Color::Green))
}
}
}
}
}
高度な機能
コンテキストAPI
use iocraft::prelude::*;
#[derive(Clone)]
struct ThemeContext {
primary_color: Color,
secondary_color: Color,
}
impl ThemeContext {
fn dark() -> Self {
Self {
primary_color: Color::Blue,
secondary_color: Color::DarkGray,
}
}
fn light() -> Self {
Self {
primary_color: Color::Cyan,
secondary_color: Color::Gray,
}
}
}
#[component]
fn ThemedButton(props: &ButtonProps) -> impl Into<AnyElement> {
let theme = use_context::<ThemeContext>();
element! {
Box(
border_style: BorderStyle::Single,
border_color: theme.primary_color,
padding: 1
) {
Text(
content: props.label.clone(),
style: Style::default().fg(theme.primary_color)
)
}
}
}
#[component]
fn App() -> impl Into<AnyElement> {
let (is_dark, set_is_dark) = use_state(|| true);
let theme = if is_dark { ThemeContext::dark() } else { ThemeContext::light() };
element! {
ContextProvider(value: theme) {
Flex(direction: FlexDirection::Column, gap: 1) {
ThemedButton(label: "Themed Button".to_string())
Button(
label: "Toggle Theme".to_string(),
on_click: move |_| set_is_dark(!is_dark)
)
}
}
}
}
カスタムフック
use iocraft::prelude::*;
use std::collections::HashMap;
fn use_local_storage<T: serde::Serialize + serde::de::DeserializeOwned + Clone + 'static>(
key: &str,
initial_value: T,
) -> (T, impl Fn(T)) {
let (value, set_value) = use_state(move || {
// ローカルストレージから読み込み
if let Ok(stored) = std::fs::read_to_string(format!(".iocraft_{}", key)) {
serde_json::from_str(&stored).unwrap_or(initial_value)
} else {
initial_value
}
});
let key = key.to_string();
let set_with_storage = move |new_value: T| {
// ローカルストレージに保存
if let Ok(serialized) = serde_json::to_string(&new_value) {
let _ = std::fs::write(format!(".iocraft_{}", key), serialized);
}
set_value(new_value);
};
(value, set_with_storage)
}
リストの仮想化
#[component]
fn VirtualList(props: &VirtualListProps) -> impl Into<AnyElement> {
let (scroll_offset, set_scroll_offset) = use_state(|| 0);
let visible_items = 10;
let start_index = scroll_offset;
let end_index = (start_index + visible_items).min(props.items.len());
element! {
Box(
height: visible_items,
on_scroll: move |event| {
set_scroll_offset(event.offset);
}
) {
for i in start_index..end_index {
ListItem(item: props.items[i].clone(), index: i)
}
}
}
}
エコシステム
関連プロジェクト
- iocraft-cli: CLIアプリケーションテンプレート
- iocraft-components: コミュニティ製コンポーネント
- iocraft-devtools: 開発ツール
学習リソース
- 公式ドキュメント: 詳細なAPIドキュメント
- チュートリアル: ステップバイステップの学習ガイド
- サンプル集: 実用的な例
利点
- React経験を活用: React開発者がすぐに使える
- 宣言的: UIの構造が分かりやすい
- コンポーネント再利用: 効率的な開発
- 現代的: 最新のUI開発パターン
- 型安全: Rustの強力な型システム
制約事項
- 新しい: コミュニティが成長中
- 学習コスト: ReactとRust両方の知識が必要
- パフォーマンス: 仮想DOMのオーバーヘッド
- エコシステム: サードパーティ製コンポーネントが少ない
- 安定性: APIが変更される可能性
他のライブラリとの比較
項目 | Iocraft | Ratatui | TUI Realm |
---|---|---|---|
パラダイム | Reactライク | イミディエート | MVU |
学習コスト | 中(React経験者は低) | 中 | 高 |
柔軟性 | 高 | 非常に高 | 高 |
コミュニティ | 成長中 | 大きい | 中程度 |
特徴 | JSX風構文 | パフォーマンス | 構造化 |
まとめ
Iocraftは、Reactの開発体験をRustのTUI開発に持ち込む革新的なライブラリです。JSX風の構文、コンポーネントベースの設計、フックAPIなど、Reactの優れた概念を活用できます。特に、Reactの経験がある開発者や、モダンなUI開発パターンをターミナルアプリケーションに適用したい開発者にとって理想的な選択です。