Skip to main content

DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/IPC/WindServiceHandlers/Sky/
ReplayEvents.rs

1#![allow(non_snake_case, unused_variables, dead_code, unused_imports)]
2
3//! Wire method: `sky:replay-events`.
4//! Called by SkyBridge after every `sky://*` Tauri listener is installed.
5//! Mountain → Sky `app.emit()` events are NOT buffered: any emit fired before
6//! the listener was registered is silently dropped. In the bundled-electron
7//! profile, extension activation starts ~580 log lines before the Sky bundle
8//! finishes booting (~1995 lines). Without replay, all tree-view + SCM
9//! register events are lost and the Activity Bar comes up empty.
10//!
11//! Replays: tree-views, SCM providers, extension commands, active terminals
12//! (including buffered stdout from before SkyBridge's listeners were up).
13
14use std::sync::Arc;
15
16use serde_json::Value;
17use tauri::{AppHandle, Emitter};
18
19use crate::RunTime::ApplicationRunTime::ApplicationRunTime;
20
21pub async fn SkyReplayEvents(ApplicationHandle:AppHandle, RunTime:Arc<ApplicationRunTime>) -> Result<Value, String> {
22	let mut TreeViewCount:usize = 0;
23	let mut ScmCount:usize = 0;
24	let mut CommandCount:usize = 0;
25	let mut TerminalCount:usize = 0;
26	let mut TerminalDataBytes:usize = 0;
27
28	// ── Tree views ────────────────────────────────────────────────────────
29	if let Ok(TreeViews) = RunTime.Environment.ApplicationState.Feature.TreeViews.ActiveTreeViews.lock() {
30		for (ViewId, Dto) in TreeViews.iter() {
31			let Payload = serde_json::json!({
32				"viewId": ViewId,
33				"options": {
34					"canSelectMany": Dto.CanSelectMany,
35					"showCollapseAll": Dto.HasHandleDrag,
36					"title": Dto.Title.clone().unwrap_or_default(),
37				},
38			});
39			if ApplicationHandle.emit("sky://tree-view/create", Payload).is_ok() {
40				TreeViewCount += 1;
41			}
42		}
43	}
44
45	// ── SCM providers ─────────────────────────────────────────────────────
46	// Pre-DTO-Identifier-field DTOs default `Identifier` to "" (serde
47	// default); fall back to "git" - the only SCM provider in production
48	// today is `vscode.git` and a stale state file with empty id is the
49	// realistic upgrade-path mismatch.
50	if let Ok(ScmProviders) = RunTime
51		.Environment
52		.ApplicationState
53		.Feature
54		.Markers
55		.SourceControlManagementProviders
56		.lock()
57	{
58		for (Handle, Dto) in ScmProviders.iter() {
59			let RootUriStr = Dto
60				.RootURI
61				.as_ref()
62				.and_then(|V| V.get("external").or_else(|| V.get("path")))
63				.and_then(serde_json::Value::as_str)
64				.unwrap_or("")
65				.to_string();
66			let ScmId = if Dto.Identifier.is_empty() {
67				"git".to_string()
68			} else {
69				Dto.Identifier.clone()
70			};
71			let Payload = serde_json::json!({
72				"scmId": ScmId,
73				"label": Dto.Label,
74				"rootUri": RootUriStr,
75				"extensionId": "",
76				"handle": *Handle,
77			});
78			if ApplicationHandle.emit("sky://scm/register", Payload).is_ok() {
79				ScmCount += 1;
80			}
81		}
82	}
83
84	// ── Extension commands ────────────────────────────────────────────────
85	// Emit ONE batched event with the whole array. Per-command emits
86	// (one per registered command, ~1000+ during extension boot) saturate
87	// Tauri's shared WKWebView IPC channel and starve keystroke delivery.
88	// SkyBridge accepts `{ commands: [...] }` or `{ id, commandId, kind }`.
89	if let Ok(Commands) = RunTime.Environment.ApplicationState.Extension.Registry.CommandRegistry.lock() {
90		let mut Batch:Vec<serde_json::Value> = Vec::new();
91		for (CommandId, Handler) in Commands.iter() {
92			use crate::Environment::CommandProvider::CommandHandler;
93			let Kind = match Handler {
94				CommandHandler::Native(_) => continue,
95				CommandHandler::Proxied { .. } => "extension",
96			};
97			Batch.push(serde_json::json!({
98				"id": CommandId,
99				"commandId": CommandId,
100				"kind": Kind,
101			}));
102		}
103		if !Batch.is_empty() {
104			let Count = Batch.len();
105			if ApplicationHandle
106				.emit("sky://command/register", serde_json::json!({ "commands": Batch }))
107				.is_ok()
108			{
109				CommandCount = Count;
110			}
111		}
112	}
113
114	// ── Terminals + buffered stdout ───────────────────────────────────────
115	// Each active terminal needs its `create` event AND any buffered stdout
116	// the PTY reader produced before SkyBridge was up. Without this, the
117	// shell's first prompt is silently dropped and the user sees an empty
118	// terminal pane until they type.
119	if let Ok(Terminals) = RunTime.Environment.ApplicationState.Feature.Terminals.ActiveTerminals.lock() {
120		for (TerminalId, Arc) in Terminals.iter() {
121			let (Name, Pid) = if let Ok(State) = Arc.lock() {
122				(State.Name.clone(), State.OSProcessIdentifier.unwrap_or(0))
123			} else {
124				(String::new(), 0)
125			};
126			let CreatePayload = serde_json::json!({
127				"id": *TerminalId,
128				"name": Name,
129				"pid": Pid,
130			});
131			if ApplicationHandle.emit("sky://terminal/create", CreatePayload).is_ok() {
132				TerminalCount += 1;
133			}
134		}
135	}
136	for (TerminalId, Bytes) in crate::Environment::TerminalProvider::DrainTerminalOutputBuffer() {
137		let DataString = String::from_utf8_lossy(&Bytes).to_string();
138		TerminalDataBytes += Bytes.len();
139		let _ = ApplicationHandle.emit(
140			"sky://terminal/data",
141			serde_json::json!({ "id": TerminalId, "data": DataString }),
142		);
143	}
144
145	crate::dev_log!(
146		"sky-emit",
147		"[SkyEmit] replay-events tree-views={} scm={} commands={} terminals={} terminal-bytes={}",
148		TreeViewCount,
149		ScmCount,
150		CommandCount,
151		TerminalCount,
152		TerminalDataBytes
153	);
154	Ok(serde_json::json!({
155		"treeViews": TreeViewCount,
156		"scmProviders": ScmCount,
157		"commands": CommandCount,
158		"terminals": TerminalCount,
159		"terminalDataBytes": TerminalDataBytes,
160	}))
161}