diff --git a/web/index.html b/web/index.html index 779b056..1f05ba7 100644 --- a/web/index.html +++ b/web/index.html @@ -294,6 +294,19 @@ border-left: 1px solid var(--background-dim); padding-left: 1px; } + span.json-key { + color: var(--accent); + } + span.json-text { + color: var(--text); + } + .loader { + animation: spin 1s linear infinite; + } + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); + } diff --git a/web/src/page/debug.rs b/web/src/page/debug.rs index 13a1e4f..a949258 100644 --- a/web/src/page/debug.rs +++ b/web/src/page/debug.rs @@ -1,54 +1,73 @@ use std::sync::Arc; use leptos::*; +use leptos_router::*; use crate::prelude::*; #[component] pub fn DebugPage() -> impl IntoView { - let (object, set_object) = create_signal(Arc::new(serde_json::Value::String( - "use this view to fetch remote AP objects and inspect their content".into()) - )); + let query_params = use_query_map(); + let query = move || { + query_params.with(|params| params.get("q").cloned().unwrap_or_default()) + }; let cached_ref: NodeRef = create_node_ref(); let auth = use_context::().expect("missing auth context"); - let (query, set_query) = create_signal("".to_string()); + let (text, set_text) = create_signal("".to_string()); + let navigate = use_navigate(); + + let object = create_local_resource( + query, + move |q| async move { + set_text.set(q.clone()); + if q.is_empty() { return serde_json::Value::Null }; + let cached = cached_ref.get().map(|x| x.checked()).unwrap_or_default(); + if cached { + match CACHE.get(&q) { + Some(x) => (*x).clone(), + None => serde_json::Value::Null, + } + } else { + debug_fetch(&format!("{URL_BASE}/proxy?id={q}"), auth).await + } + } + ); + let loading = object.loading(); + + view! {
config :: devtools
set_object.set(x), - None => set_object.set(Arc::new(serde_json::Value::String("not in cache!".into()))), - } - } else { - let url = format!("{URL_BASE}/proxy?id={fetch_url}"); - spawn_local(async move { set_object.set(Arc::new(debug_fetch(&url, auth).await)) }); - } + navigate(&format!("/web/config/dev?q={}", text.get()), NavigateOptions::default()); } > - + - +
obj " " usr + +
-
-				{move || serde_json::to_string_pretty(object.get().as_ref()).unwrap_or("unserializable".to_string())}
+			
+				{move || object.get().map(|o| view! {  })}
 			
} @@ -67,3 +86,50 @@ async fn debug_fetch(url: &str, token: Auth) -> serde_json::Value { } } } + +#[component] +fn DocumentNode(obj: serde_json::Value, #[prop(optional)] depth: usize) -> impl IntoView { + let prefix = " ".repeat(depth); + match obj { + serde_json::Value::Null => view! { null }.into_view(), + serde_json::Value::Bool(x) => view! { {x} }.into_view(), + serde_json::Value::Number(n) => view! { {n.to_string()} }.into_view(), + serde_json::Value::String(s) => { + if s.starts_with("https://") || s.starts_with("http://") { + view! { + "\""{s}"\"" + }.into_view() + } else { + view! { + "\""{s}"\"" + }.into_view() + } + }, + serde_json::Value::Array(arr) => if arr.is_empty() { + view! { "[]" }.into_view() + } else { + view! { + "[\n" + {arr.into_iter().map(|x| view! { + {prefix.clone()}" "",\n" + }).collect_view()} + {prefix.clone()}"]" + }.into_view() + }, + serde_json::Value::Object(map) => if map.is_empty() { + view! { "{}" }.into_view() + } else { + view! { + "{\n" + { + map.into_iter() + .map(|(k, v)| view! { + {prefix.clone()}" \""{k}"\": "",\n" + }) + .collect_view() + } + {prefix.clone()}"}" + }.into_view() + }, + } +}