Mountain/IPC/WindServiceHandlers/TreeView/GetChildren.rs
1//! Wire method: `tree:getChildren`.
2//! Renderer-side tree-view child lookup. Mirrors the Cocoon→Mountain
3//! `GetTreeChildren` gRPC path but is invoked directly by the Wind/Sky
4//! tree-view bridge so the UI can request children without waiting for
5//! Cocoon to ask first.
6//!
7//! Waits up to 5 s for the Cocoon gRPC connection (boot-race guard) then
8//! dispatches `$provideTreeChildren` with a 5 s timeout. On timeout or
9//! extension rejection the workbench receives `{ items: [] }` and schedules
10//! its own retry when the view scrolls back into view.
11
12use std::sync::Arc;
13
14use serde_json::{Value, json};
15use tauri::AppHandle;
16
17use crate::RunTime::ApplicationRunTime::ApplicationRunTime;
18
19pub async fn Fn(
20 ApplicationHandle:AppHandle,
21
22 RunTime:Arc<ApplicationRunTime>,
23
24 Arguments:Vec<Value>,
25) -> Result<Value, String> {
26 let ViewId = Arguments
27 .first()
28 .and_then(|V| V.get("viewId").or_else(|| V.get(0)))
29 .and_then(Value::as_str)
30 .unwrap_or("")
31 .to_string();
32
33 let ItemHandle = Arguments
34 .first()
35 .and_then(|V| V.get("treeItemHandle").or_else(|| V.get(1)))
36 .and_then(Value::as_str)
37 .unwrap_or("")
38 .to_string();
39
40 crate::dev_log!(
41 "tree-view",
42 "[TreeView] invoke:getChildren view={} parent={}",
43 ViewId,
44 ItemHandle
45 );
46
47 if ViewId.is_empty() {
48 return Err("tree:getChildren requires viewId".to_string());
49 }
50
51 let Parameters = json!({
52 "viewId": ViewId,
53 "treeItemHandle": ItemHandle,
54 });
55
56 // Boot-race: the workbench's Explorer view fires `tree:getChildren` ~700
57 // log lines before Cocoon's gRPC client finishes handshaking. Without
58 // this wait the first call returns `ClientNotConnected`, the workbench
59 // caches an empty list, and the Explorer never recovers without a manual
60 // refresh. 5000 ms chosen from boot-trace observation.
61 let _ = ::Vine::Client::WaitForClientConnection::Fn("cocoon-main", 5000).await;
62
63 match ::Vine::Client::SendRequest::Fn(
64 "cocoon-main",
65 "$provideTreeChildren".to_string(),
66 Parameters,
67 // 15000 ms: warm tree calls take 1-50 ms but cold scans can
68 // blow well past 5 s on first activation (`npm` walks every
69 // `package.json` in the workspace; `vscode.git` enumerates
70 // every nested submodule). 5 s repeatedly tripped on multi-
71 // repo workspaces like Land itself, so the panel rendered
72 // empty until the user manually refreshed. Bumping to 15 s
73 // keeps the upper bound bounded (renderer still receives
74 // `{items:[]}` on real hang) while letting genuine cold
75 // scans complete.
76 15000,
77 )
78 .await
79 {
80 Ok(Value_) => {
81 match &Value_ {
82 Value::Object(_) | Value::Array(_) => Ok(Value_),
83
84 // Non-conforming shape: force to {items:[]} so the renderer
85 // always has iterable data and avoids TypeError crashes.
86 _ => Ok(json!({ "items": [] })),
87 }
88 },
89
90 Err(Error) => {
91 // Log first failure per view; silence repeats so the dev log
92 // doesn't fill with identical lines while the user browses
93 // nodes from a misbehaving extension.
94 crate::IPC::DevLog::DebugOnce::Fn(
95 "tree-view",
96 &format!("get-children-error:{}", ViewId),
97 &format!(
98 "[TreeView] invoke:getChildren error view={} err={:?} (further occurrences silenced)",
99 ViewId, Error
100 ),
101 );
102
103 Ok(json!({ "items": [] }))
104 },
105 }
106}