11use crate :: api:: ws_base;
22use leptos:: * ;
33use wasm_bindgen:: prelude:: * ;
4- use wasm_bindgen:: JsCast ;
5- use web_sys:: { ErrorEvent , MessageEvent , WebSocket } ;
4+ use wasm_bindgen:: JsCast ; // Required for unchecked_into
5+ use web_sys:: { MessageEvent , WebSocket } ; // Removed unused ErrorEvent
66use std:: rc:: Rc ;
77use std:: cell:: Cell ;
88
@@ -31,6 +31,10 @@ extern "C" {
3131 fn on_data ( this : & Terminal , callback : & Closure < dyn FnMut ( String ) > ) ;
3232 #[ wasm_bindgen( method, js_name = loadAddon) ]
3333 fn load_addon ( this : & Terminal , addon : & XtermFitAddon ) ;
34+ #[ wasm_bindgen( method, getter) ]
35+ fn cols ( this : & Terminal ) -> u16 ;
36+ #[ wasm_bindgen( method, getter) ]
37+ fn rows ( this : & Terminal ) -> u16 ;
3438}
3539
3640#[ component]
@@ -41,83 +45,68 @@ pub fn TerminalView(container_id: String) -> impl IntoView {
4145 create_effect ( move |_| {
4246 if let Some ( div) = terminal_div_ref. get ( ) {
4347 let term = Terminal :: new ( ) ;
44-
4548 let fit_addon = XtermFitAddon :: new ( ) ;
4649 term. load_addon ( & fit_addon) ;
4750 term. open ( & div) ;
48-
4951 fit_addon. fit ( ) ;
50- let fit_addon_clone = fit_addon. clone ( ) . unchecked_into :: < XtermFitAddon > ( ) ;
51- let on_resize = Closure :: < dyn FnMut ( ) > :: new ( move || {
52- fit_addon_clone. fit ( ) ;
53- } ) ;
54- window ( ) . set_onresize ( Some ( on_resize. as_ref ( ) . unchecked_ref ( ) ) ) ;
55- on_resize. forget ( ) ;
56- on_cleanup ( move || {
57- window ( ) . set_onresize ( None ) ;
58- } ) ;
52+
5953 term. write ( & format ! ( "Connecting to session {}...\r \n " , id_for_effect) ) ;
6054
61- let term_clone: Terminal = term. clone ( ) . unchecked_into ( ) ;
6255 let ws_url = format ! ( "{}/ws/{}" , ws_base( ) , id_for_effect) ;
56+
57+ // 1. CAST: Ensure clones are treated as Terminal type, not generic JsValue
58+ let term_clone = term. clone ( ) . unchecked_into :: < Terminal > ( ) ;
59+ let term_resize = term. clone ( ) . unchecked_into :: < Terminal > ( ) ;
6360 let first_message = Rc :: new ( Cell :: new ( true ) ) ;
6461
65- // FIX: Removed unwrap() on WebSocket::new
6662 match WebSocket :: new ( & ws_url) {
6763 Ok ( ws) => {
68- let ws_cleanup = ws. clone ( ) ;
64+ let ws_resize = ws. clone ( ) ;
65+ let fit_addon_resize = fit_addon. clone ( ) . unchecked_into :: < XtermFitAddon > ( ) ;
66+
67+ let on_resize = Closure :: < dyn FnMut ( ) > :: new ( move || {
68+ fit_addon_resize. fit ( ) ;
69+ // Now methods like cols() work because term_resize is typed
70+ let cols = term_resize. cols ( ) ;
71+ let rows = term_resize. rows ( ) ;
72+ let _ = ws_resize. send_with_str ( & format ! ( "RESIZE:{}:{}" , cols, rows) ) ;
73+ } ) ;
74+
75+ window ( ) . set_onresize ( Some ( on_resize. as_ref ( ) . unchecked_ref ( ) ) ) ;
76+ on_resize. forget ( ) ;
6977
78+ let ws_cleanup = ws. clone ( ) ;
7079 on_cleanup ( move || {
7180 let _ = ws_cleanup. close ( ) ;
81+ window ( ) . set_onresize ( None ) ;
7282 } ) ;
83+
7384 let onmessage = Closure :: < dyn FnMut ( MessageEvent ) > :: new ( move |e : MessageEvent | {
7485 if let Ok ( txt) = e. data ( ) . dyn_into :: < js_sys:: JsString > ( ) {
7586 let text = String :: from ( txt) ;
76-
77- // If this is the first data packet, clear the screen
7887 if first_message. get ( ) {
79- // \x1b[2J = Clear entire screen
80- // \x1b[H = Move cursor to home (top-left)
8188 term_clone. write ( "\x1b [2J\x1b [H" ) ;
8289 first_message. set ( false ) ;
8390 }
84-
8591 term_clone. write ( & text) ;
8692 }
8793 } ) ;
8894 ws. set_onmessage ( Some ( onmessage. as_ref ( ) . unchecked_ref ( ) ) ) ;
8995 onmessage. forget ( ) ;
9096
91- let ws_clone = ws. clone ( ) ;
92- let on_data_callback =
93- Closure :: < dyn FnMut ( String ) > :: new ( move |data : String | {
94- let _ = ws_clone. send_with_str ( & data) ;
95- } ) ;
97+ let ws_send = ws. clone ( ) ;
98+ let on_data_callback = Closure :: < dyn FnMut ( String ) > :: new ( move |data : String | {
99+ let _ = ws_send. send_with_str ( & data) ;
100+ } ) ;
96101 term. on_data ( & on_data_callback) ;
97102 on_data_callback. forget ( ) ;
98-
99- let term_err = term. clone ( ) . unchecked_into :: < Terminal > ( ) ;
100- let onerror = Closure :: < dyn FnMut ( ErrorEvent ) > :: new ( move |_| {
101- term_err. write ( "\r \n \x1b [31m[!] Connection Error.\x1b [0m\r \n " ) ;
102- } ) ;
103- ws. set_onerror ( Some ( onerror. as_ref ( ) . unchecked_ref ( ) ) ) ;
104- onerror. forget ( ) ;
105-
106- let term_close = term. clone ( ) . unchecked_into :: < Terminal > ( ) ;
107- let onclose = Closure :: < dyn FnMut ( ) > :: new ( move || {
108- term_close. write ( "\r \n \x1b [33m[!] Connection Closed.\x1b [0m\r \n " ) ;
109- } ) ;
110- ws. set_onclose ( Some ( onclose. as_ref ( ) . unchecked_ref ( ) ) ) ;
111- onclose. forget ( ) ;
112103 }
113104 Err ( _) => {
114- term. write (
115- "\r \n \x1b [31m[!] Failed to initialize WebSocket connection.\x1b [0m\r \n " ,
116- ) ;
105+ term. write ( "\r \n \x1b [31m[!] WebSocket Error.\x1b [0m\r \n " ) ;
117106 }
118107 }
119108 }
120109 } ) ;
121110
122111 view ! { <div _ref=terminal_div_ref class="terminal" style="height: 100%; width: 100%; padding: 8px" ></div> }
123- }
112+ }
0 commit comments