This commit is contained in:
2026-08-30 18:50:48 -04:00
parent 0c46d08e1d
commit ebad6e6097
8 changed files with 2631 additions and 103 deletions
+48 -103
View File
@@ -22,7 +22,7 @@ use ldp::reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use ldp::{RdfSource, RdfSourceUpdateResponse, ResourceRequestBuilder, SerializationOptions};
use oxigraph::io::RdfFormat;
use oxigraph::model::{BaseDirection, Graph, NamedNode, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, Term, TermRef, Triple, TripleRef};
use tracing::{error, trace};
use tracing::{debug, error, trace};
use gl_graph::class::Class;
use gl_graph::ontology::{ConnectedOntology, OntologyBuilder, ReadOnlyEntity, ResourceDescription};
use gl_search::tantivy::schema::Value;
@@ -74,10 +74,9 @@ pub(crate) enum Message {
NavigateBack,
NavigateForward,
ResetState,
SetShowReadOnly(bool),
SetShowInferredTriples(bool),
RunInference,
SetInferredTriples(Graph),
ShowInferencesWindow,
Event(Event),
}
@@ -110,13 +109,12 @@ pub(crate) struct Publisher {
abbreviated_datatypes: Vec<String>,
resource_descriptions: HashMap<NamedNode, ResourceDescription>,
window_id: window::Id,
inference_window_id: Option<window::Id>,
url_input: String,
url_input_valid: bool,
navigator: Navigator<Url>,
document: RdfSource<KeyedDataset<RowState>>,
inferred_triples: Graph,
show_read_only: bool,
show_inferred_triples: bool,
hovered_row: Option<QuadKey>,
search_state: Option<SearchState>,
index: SearchIndex,
@@ -168,13 +166,12 @@ impl Publisher {
abbreviated_datatypes: Vec::new(),
resource_descriptions: HashMap::new(),
window_id: id,
inference_window_id: None,
url_input: starting_url.to_string(),
url_input_valid: true,
navigator,
document,
inferred_triples: Graph::new(),
show_read_only: false,
show_inferred_triples: false,
hovered_row: None,
search_state: None,
index,
@@ -574,11 +571,17 @@ impl Publisher {
}
}
Message::NavigateToObject(key) => {
if let Some(quad) = self.document.dataset_mut().quads.get_mut(key)
&& let Term::NamedNode(node) = &quad.object {
let url = Url::parse(node.as_str()).expect("Invalid URL");
if let Some(quad) = self.document.dataset_mut().quads.get_mut(key) {
let url = if let Term::NamedNode(node) = &quad.object {
Url::parse(node.as_str()).ok()
} else if let Term::Literal(literal) = &quad.object {
Url::parse(literal.value()).ok()
} else { None };
if let Some(url) = url {
task = Task::done(Message::URLInputChanged(url.to_string()))
.chain(Task::done(Message::URLInputSubmitted));
}
}
}
Message::NavigateBack => {
@@ -591,12 +594,6 @@ impl Publisher {
task = Task::done(Message::URLInputChanged(url.to_string()))
.chain(Task::done(Message::NavigateTo(url)));
}
Message::SetShowReadOnly(value) => {
self.show_read_only = value;
}
Message::SetShowInferredTriples(value) => {
self.show_inferred_triples = value;
}
Message::RunInference => {
if let Some(ontology) = &mut self.ontology {
let graph = self.document
@@ -627,6 +624,15 @@ impl Publisher {
self.inferred_triples = graph;
}
Message::ShowInferencesWindow => {
if let Some(id) = self.inference_window_id {
task = window::gain_focus(id);
} else {
let (id, window_task) = window::open(Settings::default());
self.inference_window_id = Some(id);
task = window_task.map(|_| Message::None);
}
}
Message::Event(Event::KeyPressed {
key: keyboard::Key::Named(key::Named::Tab),
modifiers,
@@ -643,8 +649,14 @@ impl Publisher {
task
}
pub(crate) fn title(&self, _window: window::Id) -> String {
"Graph of Liberty Publisher".to_string()
pub(crate) fn title(&self, window: window::Id) -> String {
if let Some(inference_window_id) = self.inference_window_id && window == inference_window_id {
"Graph of Liberty Publisher - Inferences".to_string()
} else if let Some(search_state) = &self.search_state && window == search_state.window_id {
"Graph of Liberty Publisher - Search".to_string()
} else {
"Graph of Liberty Publisher".to_string()
}
}
pub(crate) fn subscription(&self) -> Subscription<Message> {
@@ -727,17 +739,10 @@ impl Publisher {
})
.unwrap_or(Horizontal::Left);
// If the datatype is something, then it's not a named node, and we should use a plain
// text_input as opposed to an iri_input.
let object_input: Element<Message> = if term.datatype().is_some() {
let base = text_input("Object", value.clone());
if state.read_only {
base.into()
} else {
base.on_input(move |value| Message::ObjectUpdated(key, value))
.into()
}
} else {
/* If the object is not a valid URL (either as a literal string or as a named node), then
* use a normal text input instead of an iri_input.
*/
let object_input: Element<Message> = if term.is_named_node() || Url::parse(term.value()).is_ok() {
let base = iri_input(&self.curie_helper, "Object", Some(base), value.as_str())
.on_shift_click(Message::NavigateToObject(key));
if state.read_only {
@@ -750,6 +755,14 @@ impl Publisher {
)))
.into()
}
} else {
let base = text_input("Object", value.clone());
if state.read_only {
base.into()
} else {
base.on_input(move |value| Message::ObjectUpdated(key, value))
.into()
}
};
let selected_datatype = term
@@ -888,6 +901,11 @@ impl Publisher {
return self.view_search_window(search_state);
}
if let Some(inference_window_id) = self.inference_window_id && window == inference_window_id {
return crate::window::inferences::view_inference_window(&self.resource_descriptions, self.document.origin(), &self.curie_helper, &self.inferred_triples);
}
let show_inferences_button = button("\u{1f9e0}").on_press(Message::ShowInferencesWindow);
let back_button = button("\u{1f870}").on_press(Message::NavigateBack);
let forward_button = button("\u{1f872}").on_press(Message::NavigateForward);
let new_document_button = button("New Document").on_press(Message::ShowNewDocumentButtons);
@@ -905,7 +923,6 @@ impl Publisher {
rows = self.document
.dataset()
.iter_both()
.filter(|(_, _, state)| self.show_read_only || !state.read_only)
.map(|(key, quad, state)| self.view_row(key, quad, state))
.collect();
@@ -928,63 +945,6 @@ impl Publisher {
column(rows).into()
};
let inference_table = if self.show_inferred_triples {
let subject_column = table::column("Subject", |triple: TripleRef| {
let curie = if let NamedOrBlankNodeRef::NamedNode(subject) = triple.subject {
Some(self.curie_helper
.abbreviate(Some(self.document.origin()), subject.as_str())
.unwrap_or_else(|| subject.as_str().to_string()))
} else {
None
};
curie.map(text)
});
let predicate_column = table::column("Predicate", |triple: TripleRef| {
let curie = self.curie_helper
.abbreviate(None, triple.predicate.as_str())
.unwrap_or_else(|| triple.predicate.as_str().to_string());
let label = self.resource_descriptions
.get(&triple.predicate.into_owned())
.and_then(|description| description.label.clone());
if let Some(label) = label {
text(format!("{curie} ({label})"))
} else {
text(curie)
}
});
let object_column = table::column("Object", |triple: TripleRef| {
match triple.object {
TermRef::NamedNode(node) => {
let curie = self.curie_helper
.abbreviate(Some(self.document.origin()), node.as_str())
.unwrap_or_else(|| node.as_str().to_string());
let label = self.resource_descriptions
.get(&node.into_owned())
.and_then(|description| description.label.clone());
if let Some(label) = label {
text(format!("{curie} ({label})"))
} else {
text(curie)
}
}
_ => text(triple.object.to_string()),
}
});
Some(table(
[subject_column, predicate_column, object_column],
self.inferred_triples.iter(),
))
} else {
None
};
let save_button_base = button(text("\u{1f4be}"));
let save_button = if self.modified {
save_button_base.on_press(Message::SaveGraph(false))
@@ -992,22 +952,9 @@ impl Publisher {
save_button_base
};
let inferred_triples_toggle =
toggler(self.show_inferred_triples).on_toggle(Message::SetShowInferredTriples);
let read_only_toggle = toggler(self.show_read_only).on_toggle(Message::SetShowReadOnly);
let footer = row![
space::horizontal(),
text("Show Inferred Triples"),
inferred_triples_toggle,
space::horizontal(),
text("Show Read Only Triples"),
read_only_toggle
];
let content = navigation_area(column![
row![
show_inferences_button,
new_document_button,
add_row_button,
back_button,
@@ -1015,9 +962,7 @@ impl Publisher {
address_input,
save_button,
],
footer,
scrollable(body),
scrollable(inference_table),
])
.on_back_click(Message::NavigateBack)
.on_forward_click(Message::NavigateForward);