feat(dashboard): extract inline script to standalone dashboard.ts/js, add UI build/check recipes, and add route unit tests

This commit is contained in:
Riz Ashraf committed 2026-10-05 12:48:36 +01:00
1 parent 4b307b55b9
commit 19f9c7ace7
24 files changed
+2343 -1819

No files matched your search

+14 -19
View File
@@ -2028,10 +2028,7 @@ mod tests {
#[tokio::test]
async fn test_all_meta_handlers_comprehensive() {
use crate::handlers::tasks::{
AddMilestoneHandler, ListMilestonesHandler, SetAcceptanceCriteriaHandler,
UpdateMilestoneHandler, VerifyAcceptanceCriteriaHandler,
};
use crate::handlers::tasks::{MilestonesHandler, TasksHandler};
use crate::handlers::graph::SweepGraphHealthHandler;
use crate::handlers::git::QueryGitDiffsHandler;
@@ -2116,10 +2113,11 @@ mod tests {
assert!(q_hyp_res.contains("Caching improves response speed"));
// AddMilestone & UpdateMilestone & ListMilestones
let add_ms = AddMilestoneHandler;
let ms_res = add_ms
let handler_ms = MilestonesHandler;
let ms_res = handler_ms
.execute(
serde_json::json!({
"action": "add",
"title": "v1.0 Release",
"description": "First major release"
}),
@@ -2130,10 +2128,10 @@ mod tests {
assert!(ms_res.contains("Milestone added"));
let ms_id = state.project.milestones.read_with(|ms| ms[0].id.clone());
let upd_ms = UpdateMilestoneHandler;
let upd_ms_res = upd_ms
let upd_ms_res = handler_ms
.execute(
serde_json::json!({
"action": "update",
"id": ms_id,
"status": "completed"
}),
@@ -2142,10 +2140,7 @@ mod tests {
.await;
assert!(upd_ms_res.is_ok());
let list_ms = ListMilestonesHandler;
let list_ms_res = list_ms.execute(serde_json::json!({}), state.clone()).await.unwrap();
let list_ms_res = handler_ms.execute(serde_json::json!({"action": "list"}), state.clone()).await.unwrap();
assert!(list_ms_res.contains("v1.0 Release"));
// SetAcceptanceCriteria & VerifyAcceptanceCriteria
@@ -2164,11 +2159,12 @@ mod tests {
};
state.project.tasks.modify(|t| t.push(task));
let set_ac = SetAcceptanceCriteriaHandler;
let set_ac_res = set_ac
let handler_tasks = TasksHandler;
let set_ac_res = handler_tasks
.execute(
serde_json::json!({
"task_title": "AC Task",
"action": "set_criteria",
"id": "t_ac_1",
"criteria": ["Code compiles cleanly", "Tests pass"]
}),
state.clone(),
@@ -2177,12 +2173,11 @@ mod tests {
.unwrap();
assert!(set_ac_res.contains("Acceptance criteria set successfully."));
let ver_ac = VerifyAcceptanceCriteriaHandler;
let ver_ac_res = ver_ac
let ver_ac_res = handler_tasks
.execute(
serde_json::json!({
"task_id": "t_ac_1",
"criteria": "Code compiles cleanly",
"action": "verify",
"id": "t_ac_1",
"proof": "cargo test passed"
}),
state.clone(),
+9 -133
View File
@@ -91,130 +91,7 @@ impl McpTool for StickyNotesHandler {
}
}
pub struct AddStickyNoteHandler;
#[async_trait]
impl McpTool for AddStickyNoteHandler {
fn name(&self) -> &'static str {
"add_sticky_note"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<AddStickyNoteTool>("add_sticky_note", "Execute add_sticky_note")
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: AddStickyNoteTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let now = crate::handlers::utils::now_secs();
let expires_at = if let Some(ttl) = req.ttl_seconds {
Some(now + ttl)
} else if req.session_only.unwrap_or(false) {
Some(now + 14400) // Default 4-hour session TTL
} else {
None
};
state.code.sticky.modify(|notes| {
notes.push(StickyNote {
timestamp: now,
content: req.content,
expires_at,
});
});
Ok("Sticky note added.".to_string())
}
}
pub struct ReadStickyNotesHandler;
#[async_trait]
impl McpTool for ReadStickyNotesHandler {
fn name(&self) -> &'static str {
"read_sticky_notes"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<ReadStickyNotesTool>(
"read_sticky_notes",
"Execute read_sticky_notes",
)
}
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let now = crate::handlers::utils::now_secs();
let mut active_notes = Vec::new();
state.code.sticky.modify(|notes| {
notes.retain(|n| {
if let Some(exp) = n.expires_at {
exp > now
} else {
true
}
});
active_notes = notes.clone();
});
Ok(serde_json::to_string(&active_notes)?)
}
}
pub struct DeleteStickyNoteHandler;
#[async_trait]
impl McpTool for DeleteStickyNoteHandler {
fn name(&self) -> &'static str {
"delete_sticky_note"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<DeleteStickyNoteTool>(
"delete_sticky_note",
"Execute delete_sticky_note",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: DeleteStickyNoteTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut success = false;
state.code.sticky.modify(|notes| {
if req.index > 0 && req.index <= notes.len() {
notes.remove(req.index - 1);
success = true;
}
});
if success {
Ok("Sticky note deleted.".to_string())
} else {
Err(crate::error::AppError::Internal(
"Invalid sticky note index.".to_string(),
))
}
}
}
pub struct ClearStickyNotesHandler;
#[async_trait]
impl McpTool for ClearStickyNotesHandler {
fn name(&self) -> &'static str {
"clear_sticky_notes"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<ClearStickyNotesTool>(
"clear_sticky_notes",
"Execute clear_sticky_notes",
)
}
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
state.code.sticky.modify(|notes| {
notes.clear();
});
Ok("All sticky notes cleared.".to_string())
}
}
pub struct HandoffMemosHandler;
@@ -414,37 +291,36 @@ mod tests {
let dir = tempdir().unwrap();
let state = Arc::new(MemoryState::new(dir.path().to_str().unwrap()));
let add_handler = AddStickyNoteHandler;
let handler = StickyNotesHandler;
let args = json!({
"action": "add",
"content": "Buy milk",
});
let res = add_handler
let res = handler
.execute(args, state.clone())
.await
.map_err(|e| crate::error::AppError::Internal(e.to_string()))
.unwrap();
assert!(res.contains("Sticky note added"));
let read_handler = ReadStickyNotesHandler;
let res2 = read_handler
.execute(json!({}), state.clone())
let res2 = handler
.execute(json!({"action": "read"}), state.clone())
.await
.map_err(|e| crate::error::AppError::Internal(e.to_string()))
.unwrap();
assert!(res2.contains("Buy milk"));
let delete_handler = DeleteStickyNoteHandler;
let args2 = json!({"index": 1});
let res3 = delete_handler
let args2 = json!({"action": "delete", "index": 1});
let res3 = handler
.execute(args2, state.clone())
.await
.map_err(|e| crate::error::AppError::Internal(e.to_string()))
.unwrap();
assert_eq!(res3, "Sticky note deleted.");
let res4 = read_handler
.execute(json!({}), state.clone())
let res4 = handler
.execute(json!({"action": "read"}), state.clone())
.await
.map_err(|e| crate::error::AppError::Internal(e.to_string()))
.unwrap();
File diff suppressed because it is too large. Load diff
+2
View File
@@ -206,6 +206,8 @@ impl McpTool for ToggleClipboardWatchModeHandler {
let mut watch_mode = state.clipboard_watch_mode.write().await;
*watch_mode = tool_args.enable;
drop(watch_mode);
state.clipboard_notify.notify_waiters();
let status_msg = if tool_args.enable {
"Clipboard watch mode enabled. Changes will be ingested as StickyNotes."