Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions benches/todo_widget_filtering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use multi_launcher::plugins::todo::TodoEntry;
fn build_entries(count: usize) -> Vec<TodoEntry> {
(0..count)
.map(|i| TodoEntry {
id: String::new(),
text: format!("Todo item {i:05} with mixed CASE"),
done: i % 3 == 0,
priority: (i % 10) as u8,
Expand All @@ -12,6 +13,7 @@ fn build_entries(count: usize) -> Vec<TodoEntry> {
format!("Feature{}", i % 16),
"Urgent".into(),
],
entity_refs: Vec::new(),
})
.collect()
}
Expand Down
9 changes: 7 additions & 2 deletions src/actions/todo.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
pub fn add(text: &str, priority: u8, tags: &[String]) -> anyhow::Result<()> {
crate::plugins::todo::append_todo(crate::plugins::todo::TODO_FILE, text, priority, tags)?;
pub fn add(
text: &str,
priority: u8,
tags: &[String],
refs: &[crate::common::entity_ref::EntityRef],
) -> anyhow::Result<()> {
crate::plugins::todo::append_todo(crate::plugins::todo::TODO_FILE, text, priority, tags, refs)?;
Ok(())
}

Expand Down
39 changes: 39 additions & 0 deletions src/common/entity_ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct EntityRef {
pub kind: EntityKind,
pub id: String,
#[serde(default)]
pub title: Option<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum EntityKind {
Note,
Todo,
Event,
}

impl EntityRef {
pub fn new(kind: EntityKind, id: impl Into<String>, title: Option<String>) -> Self {
Self {
kind,
id: id.into(),
title,
}
}

pub fn display(&self) -> String {
let kind = match self.kind {
EntityKind::Note => "note",
EntityKind::Todo => "todo",
EntityKind::Event => "event",
};
match &self.title {
Some(title) if !title.is_empty() => format!("{kind}:{} ({title})", self.id),
_ => format!("{kind}:{}", self.id),
}
}
}
1 change: 1 addition & 0 deletions src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub fn strip_prefix_ci<'a>(s: &'a str, prefix: &str) -> Option<&'a str> {

pub mod command;
pub mod config_files;
pub mod entity_ref;
pub mod json_watch;
pub mod lru;
pub mod query;
Expand Down
70 changes: 70 additions & 0 deletions src/dashboard/widgets/context_links.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use super::{Widget, WidgetAction};
use crate::actions::Action;
use crate::dashboard::dashboard::{DashboardContext, WidgetActivation};
use std::collections::BTreeMap;

#[derive(Default)]
pub struct ContextLinksWidget;

impl ContextLinksWidget {
pub fn new(_: ()) -> Self {
Self
}
}

impl Widget for ContextLinksWidget {
fn render(
&mut self,
ui: &mut eframe::egui::Ui,
ctx: &DashboardContext<'_>,
_activation: WidgetActivation,
) -> Option<WidgetAction> {
let snapshot = ctx.data_cache.snapshot();
let mut bundles: BTreeMap<String, (usize, usize)> = BTreeMap::new();
for note in snapshot.notes.iter() {
for tag in &note.tags {
let e = bundles.entry(tag.to_lowercase()).or_default();
e.0 += 1;
}
}
for todo in snapshot.todos.iter() {
for tag in &todo.tags {
let e = bundles.entry(tag.to_lowercase()).or_default();
e.1 += 1;
}
}
if bundles.is_empty() {
ui.label("No context links yet.");
return None;
}
let mut out = None;
for (tag, (notes, todos)) in bundles.into_iter().take(10) {
ui.horizontal(|ui| {
ui.label(format!("#{tag}"));
if ui.link(format!("notes: {notes}")).clicked() {
out = Some(WidgetAction {
action: Action {
label: format!("notes #{tag}"),
desc: "Note".into(),
action: format!("query:note list #{tag}"),
args: None,
},
query_override: Some(format!("note list #{tag}")),
});
}
if ui.link(format!("todos: {todos}")).clicked() {
out = Some(WidgetAction {
action: Action {
label: format!("todos #{tag}"),
desc: "Todo".into(),
action: format!("query:todo list #{tag}"),
args: None,
},
query_override: Some(format!("todo list #{tag}")),
});
}
});
}
out
}
}
3 changes: 3 additions & 0 deletions src/dashboard/widgets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod calendar;
mod clipboard_recent;
mod clipboard_snippets;
mod command_history;
mod context_links;
mod diagnostics;
mod frequent_commands;
mod gesture_cheat_sheet;
Expand Down Expand Up @@ -54,6 +55,7 @@ pub use calendar::CalendarWidget;
pub use clipboard_recent::ClipboardRecentWidget;
pub use clipboard_snippets::ClipboardSnippetsWidget;
pub use command_history::CommandHistoryWidget;
pub use context_links::ContextLinksWidget;
pub use diagnostics::DiagnosticsWidget;
pub use frequent_commands::FrequentCommandsWidget;
pub use gesture_cheat_sheet::GestureCheatSheetWidget;
Expand Down Expand Up @@ -239,6 +241,7 @@ impl WidgetRegistry {
.with_settings_ui(CommandHistoryWidget::settings_ui),
);
reg.register("diagnostics", WidgetFactory::new(DiagnosticsWidget::new));
reg.register("context_links", WidgetFactory::new(ContextLinksWidget::new));
reg.register(
"recent_commands",
WidgetFactory::new(RecentCommandsWidget::new)
Expand Down
6 changes: 6 additions & 0 deletions src/dashboard/widgets/todo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,22 +497,28 @@ mod tests {
fn sample_entries() -> Vec<TodoEntry> {
vec![
TodoEntry {
id: String::new(),
text: "alpha".into(),
done: false,
priority: 1,
tags: vec!["work".into()],
entity_refs: Vec::new(),
},
TodoEntry {
id: String::new(),
text: "beta".into(),
done: true,
priority: 4,
tags: vec!["home".into(), "urgent".into()],
entity_refs: Vec::new(),
},
TodoEntry {
id: String::new(),
text: "gamma".into(),
done: false,
priority: 5,
tags: vec!["urgent".into()],
entity_refs: Vec::new(),
},
]
}
Expand Down
6 changes: 6 additions & 0 deletions src/dashboard/widgets/todo_focus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,22 +239,28 @@ mod tests {
});
let entries = vec![
TodoEntry {
id: String::new(),
text: "low".into(),
done: false,
priority: 1,
tags: vec!["urgent".into()],
entity_refs: Vec::new(),
},
TodoEntry {
id: String::new(),
text: "done".into(),
done: true,
priority: 5,
tags: vec!["urgent".into()],
entity_refs: Vec::new(),
},
TodoEntry {
id: String::new(),
text: "focus".into(),
done: false,
priority: 4,
tags: vec!["urgent".into()],
entity_refs: Vec::new(),
},
];

Expand Down
1 change: 1 addition & 0 deletions src/gui/calendar_event_details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ impl CalendarEventDetails {
category: event.category.clone(),
created_at: Local::now().naive_local(),
updated_at: None,
entity_refs: event.entity_refs.clone(),
};
events.push(new_event);
if let Err(err) = save_events(CALENDAR_EVENTS_FILE, &events) {
Expand Down
1 change: 1 addition & 0 deletions src/gui/calendar_event_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ impl CalendarEventEditor {
category: None,
created_at: existing.as_ref().map(|e| e.created_at).unwrap_or(now),
updated_at: Some(now),
entity_refs: existing.map(|e| e.entity_refs).unwrap_or_default(),
};

if let Some(scope) = self.split_from.take() {
Expand Down
9 changes: 9 additions & 0 deletions src/gui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3679,6 +3679,13 @@ impl eframe::App for LauncherApp {
if ui.button("Command List").clicked() {
self.help_window.open = true;
}
if ui.button("Linking Guide (todo/note/cal)").clicked() {
self.help_window.open = true;
self.help_window.filter = "todo note cal @note: @todo:".into();
}
if ui.button("Quick Help Overlay").clicked() {
self.help_window.overlay_open = true;
}
if ui.button("Open Toast Log").clicked() {
if std::fs::OpenOptions::new()
.create(true)
Expand Down Expand Up @@ -4638,6 +4645,7 @@ impl LauncherApp {
links: Vec::new(),
slug: String::new(),
alias,
entity_refs: Vec::new(),
}
});
if let Some(existing_idx) = self
Expand Down Expand Up @@ -5187,6 +5195,7 @@ mod tests {
links: Vec::new(),
slug: "alpha".into(),
alias: None,
entity_refs: Vec::new(),
}])
},
|p| {
Expand Down
Loading