use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
use ratzilla::CanvasBackend;
use ratzilla::WebRenderer;
use ratzilla::ratatui::Terminal;
use vortex::array::MaskFuture;
use vortex::array::serde::SerializedArray;
use vortex::error::VortexExpect;
use vortex::expr::root;
use vortex::layout::layouts::flat::Flat;
use vortex::layout::segments::SegmentSource;
use vortex::session::VortexSession;
use wasm_bindgen::prelude::*;
use crate::browse::app::AppState;
use crate::browse::app::KeyMode;
use crate::browse::handle_normal_mode;
use crate::browse::handle_search_mode;
use crate::browse::input::InputEvent;
use crate::browse::ui::render_app;
#[wasm_bindgen(start)]
pub fn init() {
console_error_panic_hook::set_once();
}
fn maybe_load_flat_data(app: &Rc<RefCell<AppState>>) {
let borrowed = app.borrow();
if !borrowed.cursor.layout().is::<Flat>() {
return;
}
if borrowed.cached_flat_array.is_some() {
return;
}
let layout = borrowed.cursor.layout().clone();
let segment_source = borrowed.vxf.segment_source();
let session = borrowed.session.clone();
let row_count = layout.row_count();
drop(borrowed);
let app = app.clone();
wasm_bindgen_futures::spawn_local(async move {
let array = load_flat_array(&layout, &segment_source, &session, row_count).await;
let fb_size = load_flatbuffer_size(&layout, &segment_source).await;
let mut app = app.borrow_mut();
app.cached_flat_array = Some(array);
app.cached_flatbuffer_size = Some(fb_size);
});
}
async fn load_flat_array(
layout: &vortex::layout::LayoutRef,
segment_source: &Arc<dyn SegmentSource>,
session: &VortexSession,
row_count: u64,
) -> vortex::array::ArrayRef {
let reader = layout
.new_reader(
"".into(),
segment_source.clone(),
session,
&Default::default(),
)
.vortex_expect("Failed to create reader");
reader
.projection_evaluation(
&(0..row_count),
&root(),
MaskFuture::new_true(
usize::try_from(row_count).vortex_expect("row_count overflowed usize"),
),
)
.vortex_expect("Failed to construct projection")
.await
.vortex_expect("Failed to read flat array")
}
async fn load_flatbuffer_size(
layout: &vortex::layout::LayoutRef,
segment_source: &Arc<dyn SegmentSource>,
) -> usize {
let segment_id = layout.as_::<Flat>().segment_id();
let segment = segment_source
.request(segment_id)
.await
.vortex_expect("Failed to read segment");
SerializedArray::try_from(segment)
.vortex_expect("Failed to parse segment")
.metadata()
.len()
}
#[wasm_bindgen]
pub fn open_vortex_file(data: &[u8]) -> Result<(), JsValue> {
use vortex::VortexSessionDefault;
use vortex::buffer::ByteBuffer;
use vortex::io::runtime::wasm::WasmRuntime;
use vortex::io::session::RuntimeSessionExt;
let session = VortexSession::default().with_handle(WasmRuntime::handle());
let buffer = ByteBuffer::from(data.to_vec());
let app = Rc::new(RefCell::new(
AppState::from_buffer(session, buffer).map_err(|e| JsValue::from_str(&e.to_string()))?,
));
let window = web_sys::window().ok_or_else(|| JsValue::from_str("no window"))?;
let vw = window
.inner_width()
.ok()
.and_then(|v| v.as_f64())
.unwrap_or(1200.0);
let vh = window
.inner_height()
.ok()
.and_then(|v| v.as_f64())
.unwrap_or(800.0);
let backend = CanvasBackend::new_with_size(vw as u32, vh as u32)
.map_err(|e| JsValue::from_str(&e.to_string()))?;
let terminal = Terminal::new(backend).map_err(|e| JsValue::from_str(&e.to_string()))?;
{
let app_for_keys = app.clone();
let closure = Closure::<dyn FnMut(_)>::new(move |event: web_sys::KeyboardEvent| {
event.prevent_default();
{
let mut app_mut = app_for_keys.borrow_mut();
let input = InputEvent::from(event);
match app_mut.key_mode {
KeyMode::Normal => {
handle_normal_mode(&mut app_mut, input);
}
KeyMode::Search => {
handle_search_mode(&mut app_mut, input);
}
}
}
maybe_load_flat_data(&app_for_keys);
});
let document = window
.document()
.ok_or_else(|| JsValue::from_str("no document"))?;
document
.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())
.map_err(|e| JsValue::from_str(&format!("addEventListener failed: {e:?}")))?;
closure.forget();
}
terminal.draw_web(move |frame| {
let mut app = app.borrow_mut();
render_app(&mut app, frame);
});
Ok(())
}