ai_chat/
main.rs

1#![cfg_attr(
2    all(not(debug_assertions), target_os = "windows"),
3    windows_subsystem = "windows"
4)]
5
6use freya::prelude::*;
7use rig::{
8    client::{
9        CompletionClient,
10        ProviderClient,
11    },
12    completion::Prompt,
13    providers::openai,
14};
15use tokio::runtime::Builder;
16
17#[derive(Clone, Debug, PartialEq)]
18enum Role {
19    AI,
20    User,
21}
22
23#[derive(Clone, Debug)]
24struct Message {
25    role: Role,
26    content: String,
27}
28
29fn main() {
30    let rt = Builder::new_multi_thread().enable_all().build().unwrap();
31    let _rt = rt.enter();
32    launch(LaunchConfig::new().with_window(WindowConfig::new(app).with_size(800., 600.)))
33}
34
35fn app() -> impl IntoElement {
36    let mut messages = use_state(|| {
37        vec![Message {
38            role: Role::AI,
39            content: "Hello! I'm a AI chat. Type a message and press Send to see a response."
40                .to_string(),
41        }]
42    });
43    let mut input_value = use_state(|| String::new());
44
45    let send_message = move |_| {
46        let user_message = input_value.read().clone();
47        if user_message.trim().is_empty() {
48            return;
49        }
50
51        // Add user message
52        messages.write().push(Message {
53            role: Role::User,
54            content: user_message.clone(),
55        });
56
57        // Clear input
58        *input_value.write() = String::new();
59
60        // Add AI response using rig-core
61        let user_msg = user_message.clone();
62        spawn(async move {
63            let client = openai::Client::from_env();
64            let agent = client.agent("gpt-5.2").build();
65            match agent.prompt(&user_msg).await {
66                Ok(response) => {
67                    messages.write().push(Message {
68                        role: Role::AI,
69                        content: response,
70                    });
71                }
72                Err(e) => {
73                    messages.write().push(Message {
74                        role: Role::AI,
75                        content: format!("Error: {}", e),
76                    });
77                }
78            }
79        });
80    };
81
82    let chat_area = rect().width(Size::fill()).height(Size::flex(1.)).child(
83        ScrollView::new().child(rect().width(Size::fill()).padding(16.).children(
84            messages.read().iter().map(|msg| {
85                let is_user = msg.role == Role::User;
86                let bg_color = if is_user {
87                    (59, 130, 246)
88                } else {
89                    (55, 65, 81)
90                };
91                let align = if is_user {
92                    Alignment::End
93                } else {
94                    Alignment::Start
95                };
96                let text_align = if is_user {
97                    TextAlign::End
98                } else {
99                    TextAlign::Start
100                };
101
102                rect()
103                    .width(Size::fill())
104                    .margin(8.)
105                    .cross_align(align)
106                    .child(
107                        rect()
108                            .padding(12.)
109                            .background(bg_color)
110                            .corner_radius(16.)
111                            .color((255, 255, 255))
112                            .text_align(text_align)
113                            .child(if is_user {
114                                SelectableText::new(msg.content.clone()).into_element()
115                            } else {
116                                MarkdownViewer::new(msg.content.clone()).into_element()
117                            }),
118                    )
119                    .into()
120            }),
121        )),
122    );
123
124    let input_area = rect()
125        .width(Size::fill())
126        .height(Size::px(60.))
127        .padding(12.)
128        .child(
129            rect()
130                .horizontal()
131                .expanded()
132                .cross_align(Alignment::Center)
133                .spacing(8.)
134                .content(Content::Flex)
135                .child(
136                    Input::new()
137                        .value(input_value)
138                        .on_change(move |value| {
139                            *input_value.write() = value;
140                        })
141                        .background((65, 65, 65))
142                        .hover_background((75, 75, 75))
143                        .border_fill(Color::TRANSPARENT)
144                        .color((200, 200, 200))
145                        .placeholder("Type your message...")
146                        .width(Size::flex(1.)),
147                )
148                .child(
149                    Button::new()
150                        .background((65, 65, 65))
151                        .hover_background((75, 75, 75))
152                        .border_fill(Color::TRANSPARENT)
153                        .color((200, 200, 200))
154                        .on_press(send_message)
155                        .child("Send"),
156                ),
157        );
158
159    rect()
160        .expanded()
161        .content(Content::Flex)
162        .background((30, 30, 30))
163        .child(chat_area)
164        .child(input_area)
165}