Implement HTML view type.

This commit is contained in:
Condorra 2024-11-22 23:52:49 +11:00
parent 3623acbd21
commit de00a4de66
7 changed files with 146 additions and 2 deletions

View File

@ -8,6 +8,7 @@ use yew::prelude::*;
use crate::{
command_handler::command_handler,
editor_view::CodeEditorView,
html_view::HtmlView,
lineengine::line::{Readline, ReadlineEvent},
term_split::TermSplit,
timer_host::TimerHost,
@ -76,6 +77,7 @@ pub struct FrameId(pub u64);
pub enum FrameViewType {
Terminal,
Editor,
Html { inner: String },
}
#[derive(Properties)]
@ -239,6 +241,9 @@ pub fn term_view(props: &TermViewProps) -> Html {
global_memo={props.global_memo.clone()}
global_layout={props.global_layout.clone()}/>
},
FrameViewType::Html { inner } => html! {
<HtmlView inner={inner.clone()}/>
},
}
}

12
src/html_view.rs Normal file
View File

@ -0,0 +1,12 @@
use yew::prelude::*;
use yew::{function_component, Properties};
#[derive(Properties, PartialEq)]
pub struct HtmlViewProps {
pub inner: String,
}
#[function_component(HtmlView)]
pub fn html_view(props: &HtmlViewProps) -> Html {
html! { <iframe class="htmlview" srcdoc={props.inner.clone()} sandbox=""/> }
}

View File

@ -221,6 +221,7 @@ pub fn install_lua_globals(
register_command!(echo_frame_raw);
register_command!(editor);
register_stateless_command!(cmd_help, "help");
register_command!(html);
register_command!(hsplit);
register_stateless_command!(cmd_include, "include");
register_command!(cmd_list_logs, "listlogs");

View File

@ -12,6 +12,8 @@ use piccolo::{
SequenceReturn, StashedTable, StashedUserData, StashedValue, Table, UserData, Value, Variadic,
};
use std::{rc::Rc, str};
use wasm_bindgen::JsValue;
use web_sys::console;
use yew::UseStateSetter;
use super::call_checking_metatable;
@ -774,7 +776,57 @@ pub(super) fn editor<'gc>(
new_gl
.frame_views
.insert(cur_frame_id, FrameViewType::Editor);
global_layout.set(new_gl.into());
let new_gl: Rc<GlobalLayoutState> = new_gl.into();
global_layout.set(new_gl.clone());
(*global_memo.layout.borrow_mut()) = new_gl;
}
};
Ok(piccolo::CallbackReturn::Return)
})
}
pub fn html<'gc>(
ctx: Context<'gc>,
global_memo: &GlobalMemoCell,
global_layout: &UseStateSetter<GlobalLayoutCell>,
) -> Callback<'gc> {
let global_memo = global_memo.clone();
let global_layout = global_layout.clone();
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
let (frame, inner): (u64, String) = stack.consume(ctx)?;
let target_frame_id: FrameId = FrameId(frame);
let will_have_remaining_terminal = {
let layout = global_memo.layout.borrow();
layout.term_splits.iter().any(|f| {
(*f != target_frame_id)
&& matches!(
layout
.frame_views
.get(f)
.unwrap_or(&FrameViewType::Terminal),
FrameViewType::Terminal
)
})
};
if !will_have_remaining_terminal {
Err(anyhow::Error::msg(
"html command that would leave user with no remaining terminal is invalid",
))?
}
match global_memo
.frame_registry
.borrow_mut()
.get_mut(&target_frame_id)
{
None => Err(anyhow::Error::msg("Frame no longer exists"))?,
Some(_frame_dat) => {
let mut new_gl: GlobalLayoutState = (*global_memo.layout.borrow().as_ref()).clone();
new_gl
.frame_views
.insert(target_frame_id, FrameViewType::Html { inner });
let new_gl: Rc<GlobalLayoutState> = new_gl.into();
global_layout.set(new_gl.clone());
(*global_memo.layout.borrow_mut()) = new_gl;
}
};
Ok(piccolo::CallbackReturn::Return)

View File

@ -11,6 +11,7 @@ use yew::prelude::*;
pub mod command_handler;
pub mod editor_view;
pub mod frame_view;
pub mod html_view;
pub mod id_intern;
pub mod lineengine;
pub mod logging;

View File

@ -1,5 +1,8 @@
use itertools::Itertools;
use std::{collections::BTreeMap, rc::Rc};
use std::{
collections::{BTreeMap, VecDeque},
rc::Rc,
};
use crate::FrameId;
@ -19,6 +22,12 @@ pub enum TermSplit {
}
impl TermSplit {
pub fn iter<'t>(&'t self) -> AccessibleSplitIter<'t> {
AccessibleSplitIter {
queue: vec![self].into(),
}
}
fn collect_term_frames(&self, into: &mut BTreeMap<FrameId, usize>) {
match self {
TermSplit::Term { frame } => {
@ -136,6 +145,31 @@ impl TermSplit {
}
}
pub struct AccessibleSplitIter<'t> {
queue: VecDeque<&'t TermSplit>,
}
impl<'t> Iterator for AccessibleSplitIter<'t> {
type Item = &'t FrameId;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.queue.pop_back() {
Some(TermSplit::Horizontal { left, right }) => {
self.queue.push_front(left);
self.queue.push_front(right);
}
Some(TermSplit::Vertical { top, bottom }) => {
self.queue.push_front(top);
self.queue.push_front(bottom);
}
Some(TermSplit::Term { frame }) => break Some(frame),
None => break None,
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -237,4 +271,36 @@ mod tests {
})
);
}
#[test]
fn iterates_termframes() {
use TermSplit::*;
let t = Vertical {
top: Horizontal {
left: Horizontal {
left: Term { frame: FrameId(42) }.into(),
right: Term { frame: FrameId(64) }.into(),
}
.into(),
right: Term { frame: FrameId(42) }.into(),
}
.into(),
bottom: Vertical {
top: Term { frame: FrameId(43) }.into(),
bottom: Term { frame: FrameId(44) }.into(),
}
.into(),
};
let frames: Vec<_> = t.iter().collect();
assert_eq!(
frames,
vec![
&FrameId(42),
&FrameId(43),
&FrameId(44),
&FrameId(42),
&FrameId(64),
]
);
}
}

View File

@ -85,3 +85,10 @@ body {
min-width: 1px;
min-height: 1px;
}
.htmlview {
margin: 10px;
min-width: 1px;
min-height: 1px;
width: 100%;
height: 100%;
}