1pub mod Cocoon;
5
6#[path = "Commands/mod.rs"]
7pub mod Commands;
8
9#[path = "Configuration/mod.rs"]
10pub mod Configuration;
11
12pub mod Encryption;
13
14pub mod Extension;
15
16pub mod ExtensionHost;
17
18pub mod Extensions;
19
20pub mod FileSystem;
21
22pub mod Git;
23
24pub mod Model;
25
26pub mod NativeDialog;
27
28pub mod NativeHost;
29
30pub mod Navigation;
31
32pub mod Output;
33
34#[path = "Search/mod.rs"]
35pub mod Search;
36
37pub mod Sky;
38
39pub mod Storage;
40
41pub mod Terminal;
42
43pub mod UI;
44
45pub mod TreeView;
46
47pub mod Update;
48
49pub mod Utilities;
50
51use std::{collections::HashMap, path::PathBuf, sync::Arc};
57
58use Cocoon::{
59 ExtensionHostMessage::Fn as CocoonExtensionHostMessage,
60 Notify::Fn as CocoonNotify,
61 Request::Fn as CocoonRequest,
62};
63use ExtensionHost::{
64 DebugServiceClose::Fn as ExtensionHostDebugClose,
65 DebugServiceReload::Fn as ExtensionHostDebugReload,
66 StarterCreate::Fn as ExtensionHostStarterCreate,
67 StarterGetExitInfo::Fn as ExtensionHostStarterGetExitInfo,
68 StarterKill::Fn as ExtensionHostStarterKill,
69 StarterStart::Fn as ExtensionHostStarterStart,
70 StarterWaitForExit::Fn as ExtensionHostStarterWaitForExit,
71};
72use Sky::ReplayEvents::Fn as SkyReplayEvents;
73use TreeView::GetChildren::Fn as TreeGetChildren;
74use Update::{
75 ApplyUpdate::Fn as UpdateApplyUpdate,
76 CheckForUpdates::Fn as UpdateCheckForUpdates,
77 DownloadUpdate::Fn as UpdateDownloadUpdate,
78 GetInitialState::Fn as UpdateGetInitialState,
79 IsLatestVersion::Fn as UpdateIsLatestVersion,
80 QuitAndInstall::Fn as UpdateQuitAndInstall,
81};
82use Commands::{Execute::Fn as CommandsExecute, GetAll::Fn as CommandsGetAll};
83use Configuration::{
84 EnvironmentGet::Fn as EnvironmentGet,
85 Get::Fn as ConfigurationGet,
86 Update::Fn as ConfigurationUpdate,
87 Workbench::Fn as WorkbenchConfiguration,
88};
89use Encryption::{Decrypt::Fn as Decrypt, Encrypt::Fn as Encrypt};
90use Extensions::{
91 ExtensionsGet::Fn as ExtensionsGet,
92 ExtensionsGetAll::Fn as ExtensionsGetAll,
93 ExtensionsGetInstalled::Fn as ExtensionsGetInstalled,
94 ExtensionsIsActive::Fn as ExtensionsIsActive,
95};
96use FileSystem::{
97 Managed::{
98 FileCopy::Fn as FileCopy,
99 FileDelete::Fn as FileDelete,
100 FileExists::Fn as FileExists,
101 FileMkdir::Fn as FileMkdir,
102 FileMove::Fn as FileMove,
103 FileRead::Fn as FileRead,
104 FileReadBinary::Fn as FileReadBinary,
105 FileReaddir::Fn as FileReaddir,
106 FileStat::Fn as FileStat,
107 FileWrite::Fn as FileWrite,
108 FileWriteBinary::Fn as FileWriteBinary,
109 },
110 Native::{
111 FileCloneNative::Fn as FileCloneNative,
112 FileCloseFd::Fn as FileCloseFd,
113 FileDeleteNative::Fn as FileDeleteNative,
114 FileExistsNative::Fn as FileExistsNative,
115 FileMkdirNative::Fn as FileMkdirNative,
116 FileOpenFd::Fn as FileOpenFd,
117 FileReadNative::Fn as FileReadNative,
118 FileReaddirNative::Fn as FileReaddirNative,
119 FileRealpath::Fn as FileRealpath,
120 FileRenameNative::Fn as FileRenameNative,
121 FileStatNative::Fn as FileStatNative,
122 FileUnwatch::Fn as FileUnwatch,
123 FileWatch::Fn as FileWatch,
124 FileWriteNative::Fn as FileWriteNative,
125 },
126};
127use Model::{
128 ModelClose::Fn as ModelClose,
129 ModelGet::Fn as ModelGet,
130 ModelGetAll::Fn as ModelGetAll,
131 ModelOpen::Fn as ModelOpen,
132 ModelUpdateContent::Fn as ModelUpdateContent,
133 TextfileRead::Fn as TextfileRead,
134 TextfileSave::Fn as TextfileSave,
135 TextfileWrite::Fn as TextfileWrite,
136};
137use NativeHost::{
138 ClipboardHas::Fn as NativeHasClipboard,
139 ClipboardReadBuffer::Fn as NativeReadClipboardBuffer,
140 ClipboardReadFindText::Fn as NativeReadClipboardFindText,
141 ClipboardReadImage::Fn as NativeReadImage,
142 ClipboardReadText::Fn as NativeReadClipboardText,
143 ClipboardTriggerPaste::Fn as NativeTriggerPaste,
144 ClipboardWriteBuffer::Fn as NativeWriteClipboardBuffer,
145 ClipboardWriteFindText::Fn as NativeWriteClipboardFindText,
146 ClipboardWriteText::Fn as NativeWriteClipboardText,
147 Exit::Fn as Exit,
148 FindFreePort::Fn as NativeFindFreePort,
149 GetColorScheme::Fn as NativeGetColorScheme,
150 GetEnvironmentPaths::Fn as NativeGetEnvironmentPaths,
151 InstallShellCommand::Fn as InstallShellCommand,
152 IsFullscreen::Fn as NativeIsFullscreen,
153 IsMaximized::Fn as NativeIsMaximized,
154 IsRunningUnderARM64Translation::Fn as NativeIsRunningUnderARM64Translation,
155 KillProcess::Fn as KillProcess,
156 MoveItemToTrash::Fn as NativeMoveItemToTrash,
157 OSProperties::Fn as NativeOSProperties,
158 OSStatistics::Fn as NativeOSStatistics,
159 OpenDevTools::Fn as OpenDevTools,
160 OpenExternal::Fn as OpenExternal,
161 PickFolder::Fn as NativePickFolder,
162 Quit::Fn as Quit,
163 Relaunch::Fn as Relaunch,
164 Reload::Fn as Reload,
165 ShowItemInFolder::Fn as ShowItemInFolder,
166 ShowMessageBox::Fn as NativeShowMessageBox,
167 ShowOpenDialog::Fn as NativeShowOpenDialog,
168 ShowSaveDialog::Fn as NativeShowSaveDialog,
169 ShowSaveDialogUI::Fn as UserInterfaceShowSaveDialog,
170 ToggleDevTools::Fn as ToggleDevTools,
171 UninstallShellCommand::Fn as UninstallShellCommand,
172};
173use Navigation::{
174 HistoryCanGoBack::Fn as HistoryCanGoBack,
175 HistoryCanGoForward::Fn as HistoryCanGoForward,
176 HistoryClear::Fn as HistoryClear,
177 HistoryGetStack::Fn as HistoryGetStack,
178 HistoryGoBack::Fn as HistoryGoBack,
179 HistoryGoForward::Fn as HistoryGoForward,
180 HistoryPush::Fn as HistoryPush,
181 LabelGetBase::Fn as LabelGetBase,
182 LabelGetURI::Fn as LabelGetURI,
183 LabelGetWorkspace::Fn as LabelGetWorkspace,
184};
185use Output::{
186 OutputAppend::Fn as OutputAppend,
187 OutputAppendLine::Fn as OutputAppendLine,
188 OutputClear::Fn as OutputClear,
189 OutputCreate::Fn as OutputCreate,
190 OutputShow::Fn as OutputShow,
191};
192use Search::{FindFiles::Fn as SearchFindFiles, FindInFiles::Fn as SearchFindInFiles};
193use Storage::{
194 StorageDelete::Fn as StorageDelete,
195 StorageGet::Fn as StorageGet,
196 StorageGetItems::Fn as StorageGetItems,
197 StorageKeys::Fn as StorageKeys,
198 StorageSet::Fn as StorageSet,
199 StorageUpdateItems::Fn as StorageUpdateItems,
200};
201use Terminal::{
202 AttachToProcess::Fn as AttachToProcess,
203 DetachFromProcess::Fn as DetachFromProcess,
204 LocalPTYCreateProcess::Fn as LocalPTYCreateProcess,
205 LocalPTYFreePortKillProcess::Fn as LocalPTYFreePortKillProcess,
206 LocalPTYGetDefaultShell::Fn as LocalPTYGetDefaultShell,
207 LocalPTYGetEnvironment::Fn as LocalPTYGetEnvironment,
208 LocalPTYGetProfiles::Fn as LocalPTYGetProfiles,
209 LocalPTYResize::Fn as LocalPTYResize,
210 ReviveTerminalProcesses::Fn as ReviveTerminalProcesses,
211 SerializeTerminalState::Fn as SerializeTerminalState,
212 TerminalCreate::Fn as TerminalCreate,
213 TerminalDispose::Fn as TerminalDispose,
214 TerminalHide::Fn as TerminalHide,
215 TerminalSendText::Fn as TerminalSendText,
216 TerminalShow::Fn as TerminalShow,
217};
218use UI::{
219 DecorationsClear::Fn as DecorationsClear,
220 DecorationsGet::Fn as DecorationsGet,
221 DecorationsGetMany::Fn as DecorationsGetMany,
222 DecorationsSet::Fn as DecorationsSet,
223 KeybindingAdd::Fn as KeybindingAdd,
224 KeybindingGetAll::Fn as KeybindingGetAll,
225 KeybindingLookup::Fn as KeybindingLookup,
226 KeybindingRemove::Fn as KeybindingRemove,
227 LifecycleGetPhase::Fn as LifecycleGetPhase,
228 LifecycleRequestShutdown::Fn as LifecycleRequestShutdown,
229 LifecycleWhenPhase::Fn as LifecycleWhenPhase,
230 NotificationEndProgress::Fn as NotificationEndProgress,
231 NotificationShow::Fn as NotificationShow,
232 NotificationShowProgress::Fn as NotificationShowProgress,
233 NotificationUpdateProgress::Fn as NotificationUpdateProgress,
234 ProgressBegin::Fn as ProgressBegin,
235 ProgressEnd::Fn as ProgressEnd,
236 ProgressReport::Fn as ProgressReport,
237 QuickInputShowInputBox::Fn as QuickInputShowInputBox,
238 QuickInputShowQuickPick::Fn as QuickInputShowQuickPick,
239 ThemesGetActive::Fn as ThemesGetActive,
240 ThemesList::Fn as ThemesList,
241 ThemesSet::Fn as ThemesSet,
242 WorkingCopyGetAllDirty::Fn as WorkingCopyGetAllDirty,
243 WorkingCopyGetDirtyCount::Fn as WorkingCopyGetDirtyCount,
244 WorkingCopyIsDirty::Fn as WorkingCopyIsDirty,
245 WorkingCopySetDirty::Fn as WorkingCopySetDirty,
246 WorkspacesAddFolder::Fn as WorkspacesAddFolder,
247 WorkspacesGetFolders::Fn as WorkspacesGetFolders,
248 WorkspacesGetName::Fn as WorkspacesGetName,
249 WorkspacesRemoveFolder::Fn as WorkspacesRemoveFolder,
250};
251use Utilities::{
252 ApplicationRoot::{Get::Fn as get_static_application_root, Set::Fn as set_static_application_root},
253 ChannelPriority::Fn as ResolveChannelPriority,
254 FiddeeRoot::Fn as FiddeeRoot,
255 JsonValueHelpers::{
256 Fn as v_str,
257 arg_bool,
258 arg_bool_true,
259 arg_f64,
260 arg_i64,
261 arg_str,
262 arg_string,
263 arg_string_or,
264 arg_u64,
265 arg_u64_or,
266 arg_val,
267 req_string,
268 },
269 MetadataEncoding::Fn as metadata_to_istat,
270 PathExtraction::{Fn as extract_path_from_arg, percent_decode},
271 RecentlyOpened::{
272 Mutate::Fn as MutateRecentlyOpened,
273 Path::Fn as RecentlyOpenedPath,
274 Read::Fn as ReadRecentlyOpened,
275 },
276 UserdataDir::{
277 Ensure::Fn as ensure_userdata_dirs,
278 Get::Fn as get_userdata_base_dir,
279 Set::Fn as set_userdata_base_dir,
280 },
281};
282use Echo::Task::Priority::Priority as EchoPriority;
283use serde_json::{Value, json};
284use tauri::{AppHandle, Manager};
285use CommonLibrary::Configuration::DTO::{
287 ConfigurationOverridesDTO as ConfigurationOverridesDTOModule,
288 ConfigurationTarget as ConfigurationTargetModule,
289};
290
291use crate::dev_log;
292
293type ConfigurationOverridesDTO = ConfigurationOverridesDTOModule::ConfigurationOverridesDTO;
294
295type ConfigurationTarget = ConfigurationTargetModule::ConfigurationTarget;
296
297use CommonLibrary::{
298 Command::CommandExecutor::CommandExecutor,
299 Configuration::ConfigurationProvider::ConfigurationProvider,
300 Environment::Requires::Requires,
301 Error::CommonError::CommonError,
302 ExtensionManagement::ExtensionManagementService::ExtensionManagementService,
303 FileSystem::{FileSystemReader::FileSystemReader, FileSystemWriter::FileSystemWriter},
304 IPC::SkyEvent::SkyEvent,
305 LanguageFeature::{
306 DTO::PositionDTO::PositionDTO,
307 LanguageFeatureProviderRegistry::LanguageFeatureProviderRegistry,
308 },
309 Storage::StorageProvider::StorageProvider,
310};
311
312use crate::{
313 ApplicationState::{
314 DTO::WorkspaceFolderStateDTO::WorkspaceFolderStateDTO,
315 State::{
316 ApplicationState::ApplicationState,
317 WorkspaceState::WorkspaceDelta::UpdateWorkspaceFoldersAndBroadcast,
318 },
319 },
320 RunTime::ApplicationRunTime::ApplicationRunTime,
321};
322
323fn cocoon_payload(args:Vec<Value>) -> Value {
324 match args.len() {
325 0 => Value::Null,
326
327 1 => args.into_iter().next().unwrap(),
328
329 _ => Value::Array(args),
330 }
331}
332
333macro_rules! forward_to_cocoon {
334 ($tag:literal, $command:ident, $Arguments:ident) => {{
335 dev_log!("ipc", "{}: {} (→ Cocoon)", $tag, $command);
336
337 let Payload = cocoon_payload($Arguments);
338
339 let _ = ::Vine::Client::WaitForClientConnection::Fn("cocoon-main", 3000).await;
340
341 Ok(
342 ::Vine::Client::SendRequest::Fn("cocoon-main", $command.clone(), Payload, 10_000)
343 .await
344 .unwrap_or(Value::Null),
345 )
346 }};
347}
348
349const TIER_TERMINAL:&str = env!("TierTerminal", "Mountain");
372
373const TIER_SCM:&str = env!("TierSCM", "Mountain");
374
375const TIER_DEBUG:&str = env!("TierDebug", "Mountain");
376
377const TIER_LANGUAGE_FEATURES:&str = env!("TierLanguageFeatures", "Mountain");
378
379const TIER_SEARCH:&str = env!("TierSearch", "Mountain");
380
381const TIER_OUTPUT_CHANNEL:&str = env!("TierOutputChannel", "Mountain");
382
383const TIER_NATIVE_HOST:&str = env!("TierNativeHost", "Mountain");
384
385const TIER_TREE_VIEW:&str = env!("TierTreeView", "Mountain");
386
387const TIER_STORAGE:&str = env!("TierStorage", "Mountain");
388
389const TIER_MODEL:&str = env!("TierModel", "Mountain");
390
391const TIER_TASKS:&str = env!("TierTasks", "Node");
392
393const TIER_AUTH:&str = env!("TierAuth", "Node");
394
395const TIER_ENCRYPTION:&str = env!("TierEncryption", "Mountain");
396
397const TIER_WEBSOCKET:&str = env!("TierWebSocket", "Disabled");
398
399#[inline]
400fn tier_routes_to_node(BakedConst:&'static str, EnvKey:&str) -> bool {
401 let Resolved = std::env::var(EnvKey).unwrap_or_else(|_| BakedConst.to_string());
402
403 Resolved == "Node"
404}
405
406pub async fn mountain_ipc_invoke(
407 ApplicationHandle:AppHandle,
408
409 command:String,
410
411 Arguments:Vec<Value>,
412) -> Result<Value, String> {
413 let IsHighFrequencyCommand = matches!(
416 command.as_str(),
417 "logger:log"
418 | "logger:info"
419 | "logger:debug"
420 | "logger:trace"
421 | "logger:warn"
422 | "logger:error"
423 | "logger:critical"
424 | "logger:flush"
425 | "logger:setLevel"
426 | "logger:getLevel"
427 | "logger:registerLogger"
428 | "logger:createLogger"
429 | "logger:deregisterLogger"
430 | "logger:getRegisteredLoggers"
431 | "logger:setVisibility"
432 | "log:registerLogger"
433 | "log:createLogger"
434 | "file:stat"
436 | "file:readFile"
437 | "file:readdir"
438 | "file:writeFile"
439 | "file:delete"
440 | "file:rename"
441 | "file:realpath"
442 | "file:read"
443 | "file:write"
444 | "file:open"
446 | "file:close"
447 | "textFile:save"
449 | "storage:getItems"
451 | "storage:updateItems"
452 | "configuration:lookup"
454 | "configuration:inspect"
455 | "themes:getColorTheme"
457 | "output:append"
459 | "progress:report"
460 | "menubar:updateMenubar"
462 | "storage:onDidChangeItems"
464 | "storage:logStorage"
465 | "configuration:onDidChange"
466 | "workspaces:onDidChangeWorkspaceFolders"
467 | "workspaces:onDidChangeWorkspaceName"
468 | "commands:registerCommand"
470 | "commands:unregisterCommand"
471 | "commands:onDidRegisterCommand"
472 | "commands:onDidExecuteCommand"
473 );
474
475 let OTLPStart = if IsHighFrequencyCommand { 0 } else { crate::IPC::DevLog::NowNano::Fn() };
476
477 if !IsHighFrequencyCommand {
485 dev_log!("ipc", "invoke: {} args_count={}", command, Arguments.len());
486 }
487
488 ensure_userdata_dirs();
490
491 let RunTime:Arc<ApplicationRunTime> = ApplicationHandle.state::<Arc<ApplicationRunTime>>().inner().clone();
495
496 if IsHighFrequencyCommand {
502 match command.as_str() {
503
504 "logger:error" | "logger:critical" => {
509
510 let Msg = Arguments.get(1).and_then(|V| V.as_str()).unwrap_or(
511 Arguments.first().and_then(|V| V.as_str()).unwrap_or(""),
512 );
513
514 if !Msg.is_empty() {
515
516 dev_log!("vscode-log", "[ERROR] {}", Msg);
517 }
518
519 return Ok(Value::Null);
520 },
521
522 "logger:warn" => {
523
524 let Msg = Arguments.get(1).and_then(|V| V.as_str()).unwrap_or(
525 Arguments.first().and_then(|V| V.as_str()).unwrap_or(""),
526 );
527
528 if !Msg.is_empty() {
529
530 dev_log!("vscode-log", "[WARN] {}", Msg);
531 }
532
533 return Ok(Value::Null);
534 },
535
536 "logger:log" | "logger:info" | "logger:debug" | "logger:trace"
537 | "logger:flush" | "logger:setLevel" | "logger:getLevel"
538 | "logger:createLogger" | "logger:registerLogger"
539 | "logger:deregisterLogger" | "logger:getRegisteredLoggers"
540 | "logger:setVisibility"
541 | "log:registerLogger" | "log:createLogger"
547 | "storage:onDidChangeItems" | "storage:logStorage"
549 | "commands:registerCommand" | "commands:unregisterCommand"
551 | "commands:onDidRegisterCommand" | "commands:onDidExecuteCommand"
552 | "configuration:onDidChange"
554 | "storage:optimize" | "storage:isUsed" | "storage:close"
556 | "workspaces:onDidChangeWorkspaceFolders"
558 | "workspaces:onDidChangeWorkspaceName" => {
559
560 return Ok(Value::Null);
561 },
562
563 "menubar:updateMenubar" => {
566
567 use std::sync::atomic::{AtomicU64, Ordering as AO};
568
569 static MENUBAR_CALLS_FAST:AtomicU64 = AtomicU64::new(0);
570
571 let N = MENUBAR_CALLS_FAST.fetch_add(1, AO::Relaxed) + 1;
572
573 if N == 1 || N % 100 == 0 {
574
575 dev_log!("menubar", "menubar:updateMenubar (fast-path call #{})", N);
576 }
577
578 return Ok(Value::Null);
579 },
580
581 _ => {}, }
583 }
584
585 let CommandPriority = ResolveChannelPriority(&command);
605
606 let Scheduler = RunTime.Scheduler.clone();
607
608 let (ResultSender, ResultReceiver) = tokio::sync::oneshot::channel::<Result<Value, String>>();
609
610 let DispatchAppHandle = ApplicationHandle.clone();
611
612 let DispatchRuntime = RunTime.clone();
613
614 let DispatchCommand = command.clone();
615
616 let DispatchArgs = Arguments;
617
618 Scheduler.Submit(
619 async move {
620 let ApplicationHandle = DispatchAppHandle;
621
622 let RunTime = DispatchRuntime;
623
624 let command = DispatchCommand;
625
626 let Arguments = DispatchArgs;
627
628 macro_rules! call {
631 (rt, $tag:literal, $Fn:path, $Arguments:ident) => {{
632 dev_log!($tag, "{}", command);
633
634 $Fn(RunTime.clone(), $Arguments).await
635 }};
636
637 (rt, $tag:literal, $Fn:path) => {{
638 dev_log!($tag, "{}", command);
639
640 $Fn(RunTime.clone()).await
641 }};
642
643 (rt, $tag:literal, $msg:literal, $Fn:path, $Arguments:ident) => {{
644 dev_log!($tag, $msg);
645
646 $Fn(RunTime.clone(), $Arguments).await
647 }};
648
649 (rt, $tag:literal, $msg:literal, $Fn:path) => {{
650 dev_log!($tag, $msg);
651
652 $Fn(RunTime.clone()).await
653 }};
654
655 (app, $tag:literal, $Fn:path, $Arguments:ident) => {{
656 dev_log!($tag, "{}", command);
657
658 $Fn(ApplicationHandle.clone(), $Arguments).await
659 }};
660
661 (app, $tag:literal, $msg:literal, $Fn:path, $Arguments:ident) => {{
662 dev_log!($tag, $msg);
663
664 $Fn(ApplicationHandle.clone(), $Arguments).await
665 }};
666
667 (app, $tag:literal, $msg:literal, $Fn:path) => {{
668 dev_log!($tag, $msg);
669
670 $Fn(ApplicationHandle.clone()).await
671 }};
672 }
673
674 let MatchResult = match command.as_str() {
675 "configuration:get" | "configuration:getValue" => call!(rt, "config", ConfigurationGet, Arguments),
681 "configuration:update" | "configuration:updateValue" => {
682 call!(rt, "config", ConfigurationUpdate, Arguments)
683 },
684 "configuration:onDidChange" => Ok(Value::Null),
690
691 "configuration:lookup" => {
697 call!(rt, "config", "configuration:lookup (→ get)", ConfigurationGet, Arguments)
698 },
699
700 "configuration:inspect" => {
710 dev_log!("config", "configuration:inspect");
711
712 let CurrentValue = ConfigurationGet(RunTime.clone(), Arguments).await.unwrap_or(Value::Null);
713
714 Ok(json!({
715 "value": CurrentValue,
716 "default": CurrentValue,
717 "user": Value::Null,
718 "workspace": Value::Null,
719 "workspaceFolder": Value::Null,
720 "memory": Value::Null,
721 }))
722 },
723
724 "logger:log" | "logger:warn" | "logger:error" | "logger:info" | "logger:debug" | "logger:trace" => {
729 let Level = command.trim_start_matches("logger:");
730
731 let Msg = if Arguments.len() >= 2 {
732 let Tail:Vec<String> = Arguments
733 .iter()
734 .skip(1)
735 .filter_map(|V| V.as_str().map(str::to_owned).or_else(|| serde_json::to_string(V).ok()))
736 .collect();
737
738 Tail.join(" ")
739 } else {
740 Arguments
741 .first()
742 .and_then(|V| V.as_str().map(str::to_owned))
743 .unwrap_or_default()
744 };
745
746 if !Msg.is_empty() {
747 match Level {
748 "error" | "critical" => dev_log!("vscode-log", "[ERROR] {}", Msg),
749 "warn" => dev_log!("vscode-log", "[WARN] {}", Msg),
750 _ => dev_log!("vscode-log", "{}", Msg),
751 }
752 }
753
754 Ok(Value::Null)
755 },
756 "logger:flush"
757 | "logger:setLevel"
758 | "logger:getLevel"
759 | "logger:createLogger"
760 | "logger:registerLogger"
761 | "logger:deregisterLogger"
762 | "logger:getRegisteredLoggers"
763 | "logger:setVisibility" => Ok(Value::Null),
764
765 "file:read" | "file:readFile" => FileReadNative(Arguments).await,
777 "file:write" | "file:writeFile" => FileWriteNative(Arguments).await,
778 "file:stat" => FileStatNative(Arguments).await,
779 "file:exists" => FileExistsNative(Arguments).await,
780 "file:delete" => FileDeleteNative(Arguments).await,
781 "file:copy" => FileCloneNative(Arguments).await,
782 "file:move" | "file:rename" => FileRenameNative(Arguments).await,
783 "file:mkdir" => FileMkdirNative(Arguments).await,
784 "file:readdir" => FileReaddirNative(Arguments).await,
785 "file:readBinary" => FileReadBinary(RunTime.clone(), Arguments).await,
786 "file:writeBinary" => FileWriteBinary(RunTime.clone(), Arguments).await,
787 "file:watch" => FileWatch(RunTime.clone(), Arguments).await,
790 "file:unwatch" => FileUnwatch(RunTime.clone(), Arguments).await,
791
792 "storage:get"
806 | "storage:set"
807 | "storage:getItems"
808 | "storage:updateItems"
809 | "storage:optimize"
810 | "storage:isUsed"
811 | "storage:close"
812 | "storage:delete"
813 | "storage:keys"
814 | "storage:onDidChangeItems"
815 | "storage:logStorage"
816 if tier_routes_to_node(TIER_STORAGE, "TierStorage") =>
817 {
818 forward_to_cocoon!("storage", command, Arguments)
819 },
820 "storage:get" => StorageGet(RunTime.clone(), Arguments).await,
821 "storage:set" => StorageSet(RunTime.clone(), Arguments).await,
822 "storage:getItems" => call!(rt, "storage-verbose", "storage:getItems", StorageGetItems, Arguments),
826 "storage:updateItems" => {
827 call!(rt, "storage-verbose", "storage:updateItems", StorageUpdateItems, Arguments)
828 },
829 "storage:optimize" => {
830 dev_log!("storage", "storage:optimize");
831
832 Ok(Value::Null)
833 },
834 "storage:isUsed" => {
835 dev_log!("storage", "storage:isUsed");
836
837 Ok(Value::Null)
838 },
839 "storage:close" => {
840 dev_log!("storage", "storage:close");
841
842 Ok(Value::Null)
843 },
844 "storage:onDidChangeItems" | "storage:logStorage" => {
848 dev_log!("storage-verbose", "{} (stub-ack)", command);
849
850 Ok(Value::Null)
851 },
852
853 "environment:get" => call!(rt, "config", "environment:get", EnvironmentGet, Arguments),
855
856 "native:showItemInFolder" => ShowItemInFolder(RunTime.clone(), Arguments).await,
858 "native:openExternal" => OpenExternal(RunTime.clone(), Arguments).await,
859
860 "workbench:getConfiguration" => WorkbenchConfiguration(RunTime.clone(), Arguments).await,
862
863 "diagnostic:log" => {
869 let Tag = arg_string_or(&Arguments, 0, "webview");
870
871 let Message = arg_string(&Arguments, 1);
872
873 let Extras = if Arguments.len() > 2 {
874 let Tail:Vec<String> = Arguments
875 .iter()
876 .skip(2)
877 .map(|V| {
878 let S = serde_json::to_string(V).unwrap_or_default();
879
880 if S.len() > 240 {
886 let CutAt = S
887 .char_indices()
888 .map(|(Index, _)| Index)
889 .take_while(|Index| *Index <= 240)
890 .last()
891 .unwrap_or(0);
892
893 format!("{}…", &S[..CutAt])
894 } else {
895 S
896 }
897 })
898 .collect();
899
900 format!(" {}", Tail.join(" "))
901 } else {
902 String::new()
903 };
904
905 dev_log!("diagnostic", "[{}] {}{}", Tag, Message, Extras);
906
907 Ok(Value::Null)
908 },
909
910 "commands:execute" | "commands:executeCommand" => CommandsExecute(RunTime.clone(), Arguments).await,
915 "commands:getAll" | "commands:getCommands" => call!(rt, "commands", CommandsGetAll),
916 "commands:registerCommand"
921 | "commands:unregisterCommand"
922 | "commands:onDidRegisterCommand"
923 | "commands:onDidExecuteCommand" => Ok(Value::Null),
924
925 "extensions:getAll" => call!(rt, "extensions", "extensions:getAll", ExtensionsGetAll),
927 "extensions:get" => call!(rt, "extensions", "extensions:get", ExtensionsGet, Arguments),
928 "extensions:isActive" => call!(rt, "extensions", "extensions:isActive", ExtensionsIsActive, Arguments),
929 "extensions:activate" => {
935 let ExtensionId = arg_string(&Arguments, 0);
936
937 dev_log!("extensions", "extensions:activate id={}", ExtensionId);
938
939 if ExtensionId.is_empty() {
940 Ok(Value::Null)
941 } else {
942 let Notification = json!({
943 "event": format!("onCustom:{}", ExtensionId),
944 "extensionId": ExtensionId,
945 });
946
947 let _ = ::Vine::Client::SendNotification::Fn(
948 "cocoon-main".to_string(),
949 "$activateByEvent".to_string(),
950 Notification,
951 )
952 .await;
953
954 Ok(Value::Null)
955 }
956 },
957
958 "extensions:getInstalled" | "extensions:scanSystemExtensions" => {
971 let ArgsSummary = Arguments
977 .iter()
978 .enumerate()
979 .map(|(Idx, V)| {
980 let Preview = serde_json::to_string(V).unwrap_or_default();
981
982 let Trimmed = if Preview.len() > 180 {
985 let CutAt = Preview
986 .char_indices()
987 .map(|(Index, _)| Index)
988 .take_while(|Index| *Index <= 180)
989 .last()
990 .unwrap_or(0);
991
992 format!("{}…", &Preview[..CutAt])
993 } else {
994 Preview
995 };
996
997 format!("[{}]={}", Idx, Trimmed)
998 })
999 .collect::<Vec<_>>()
1000 .join(" ");
1001
1002 dev_log!("extensions", "{} Arguments={}", command, ArgsSummary);
1003
1004 let EffectiveArgs = if command == "extensions:scanSystemExtensions" {
1013 let mut Overridden = Arguments.clone();
1014
1015 if Overridden.is_empty() {
1016 Overridden.push(Value::Null);
1017 }
1018
1019 Overridden[0] = json!(0);
1020
1021 Overridden
1022 } else {
1023 Arguments.clone()
1024 };
1025
1026 ExtensionsGetInstalled(RunTime.clone(), EffectiveArgs).await
1027 },
1028 "extensions:scanUserExtensions" => {
1029 dev_log!("extensions", "{} (forwarded to getInstalled with type=User)", command);
1038
1039 let mut UserArgs = Arguments.clone();
1040
1041 if UserArgs.is_empty() {
1042 UserArgs.push(Value::Null);
1043 }
1044
1045 UserArgs[0] = json!(1);
1046
1047 ExtensionsGetInstalled(RunTime.clone(), UserArgs).await
1048 },
1049 "extensions:getUninstalled" => {
1050 dev_log!("extensions", "{} (returning [])", command);
1054
1055 Ok(Value::Array(Vec::new()))
1056 },
1057 "extensions:query" | "extensions:getExtensions" | "extensions:getRecommendations" => {
1061 dev_log!("extensions", "{} (offline gallery - returning [])", command);
1062
1063 Ok(Value::Array(Vec::new()))
1064 },
1065 "extensions:getExtensionsControlManifest" => {
1071 dev_log!("extensions", "{} (offline gallery - empty manifest)", command);
1072
1073 Ok(json!({
1074 "malicious": [],
1075 "deprecated": {},
1076 "search": [],
1077 "autoUpdate": {},
1078 }))
1079 },
1080 "extensions:resetPinnedStateForAllUserExtensions" => {
1087 dev_log!("extensions", "{} (no-op, pin state is UI-local)", command);
1088
1089 Ok(Value::Null)
1090 },
1091 "extensions:install" => {
1099 Extension::ExtensionInstall::Fn(ApplicationHandle.clone(), RunTime.clone(), Arguments).await
1100 },
1101 "extensions:uninstall" => {
1102 Extension::ExtensionUninstall::Fn(ApplicationHandle.clone(), RunTime.clone(), Arguments).await
1103 },
1104
1105 "extensions:getManifest" => {
1114 let VsixPath = match Arguments.first() {
1115 Some(serde_json::Value::String(Path)) => Path.clone(),
1116 Some(Obj) => {
1117 Obj.get("fsPath")
1118 .and_then(|V| V.as_str())
1119 .map(str::to_owned)
1120 .or_else(|| Obj.get("path").and_then(|V| V.as_str()).map(str::to_owned))
1121 .unwrap_or_default()
1122 },
1123 None => String::new(),
1124 };
1125
1126 dev_log!("extensions", "extensions:getManifest vsix={}", VsixPath);
1127
1128 if VsixPath.is_empty() {
1129 Err("extensions:getManifest: missing VSIX path argument".to_string())
1130 } else {
1131 let Path = std::path::PathBuf::from(&VsixPath);
1132
1133 match crate::ExtensionManagement::VsixInstaller::ReadFullManifest(&Path) {
1134 Ok(Manifest) => Ok(Manifest),
1135 Err(Error) => {
1136 dev_log!(
1137 "extensions",
1138 "warn: [WindServiceHandlers] extensions:getManifest failed for '{}': {}",
1139 VsixPath,
1140 Error
1141 );
1142
1143 Err(format!("extensions:getManifest failed: {}", Error))
1144 },
1145 }
1146 }
1147 },
1148 "extensions:reinstall" | "extensions:updateMetadata" => {
1153 dev_log!("extensions", "{} (no-op: no gallery backend)", command);
1154
1155 Ok(Value::Null)
1156 },
1157
1158 "terminal:create" => call!(rt, "terminal", "terminal:create", TerminalCreate, Arguments),
1160 "terminal:sendText" => call!(rt, "terminal", "terminal:sendText", TerminalSendText, Arguments),
1161 "terminal:dispose" => call!(rt, "terminal", "terminal:dispose", TerminalDispose, Arguments),
1162 "terminal:show" => call!(rt, "terminal", "terminal:show", TerminalShow, Arguments),
1163 "terminal:hide" => call!(rt, "terminal", "terminal:hide", TerminalHide, Arguments),
1164
1165 "output:create" => OutputCreate(ApplicationHandle.clone(), Arguments).await,
1167 "output:append" => call!(app, "output", "output:append", OutputAppend, Arguments),
1168 "output:appendLine" => call!(app, "output", "output:appendLine", OutputAppendLine, Arguments),
1169 "output:clear" => call!(app, "output", "output:clear", OutputClear, Arguments),
1170 "output:show" => call!(app, "output", "output:show", OutputShow, Arguments),
1171
1172 "textFile:read" => call!(rt, "textfile", "textFile:read", TextfileRead, Arguments),
1174 "textFile:write" => call!(rt, "textfile", "textFile:write", TextfileWrite, Arguments),
1175 "textFile:save" => TextfileSave(RunTime.clone(), Arguments).await,
1176
1177 "storage:delete" => call!(rt, "storage", "storage:delete", StorageDelete, Arguments),
1179 "storage:keys" => call!(rt, "storage", "storage:keys", StorageKeys),
1180
1181 "notification:show" => call!(app, "notification", "notification:show", NotificationShow, Arguments),
1183 "notification:showProgress" => {
1184 call!(
1185 app,
1186 "notification",
1187 "notification:showProgress",
1188 NotificationShowProgress,
1189 Arguments
1190 )
1191 },
1192 "notification:updateProgress" => {
1193 call!(
1194 app,
1195 "notification",
1196 "notification:updateProgress",
1197 NotificationUpdateProgress,
1198 Arguments
1199 )
1200 },
1201 "notification:endProgress" => {
1202 call!(
1203 app,
1204 "notification",
1205 "notification:endProgress",
1206 NotificationEndProgress,
1207 Arguments
1208 )
1209 },
1210
1211 "progress:begin" => call!(app, "progress", "progress:begin", ProgressBegin, Arguments),
1213 "progress:report" => call!(app, "progress", "progress:report", ProgressReport, Arguments),
1214 "progress:end" => call!(app, "progress", "progress:end", ProgressEnd, Arguments),
1215
1216 "quickInput:showQuickPick" => {
1218 call!(rt, "quickinput", "quickInput:showQuickPick", QuickInputShowQuickPick, Arguments)
1219 },
1220 "quickInput:showInputBox" => {
1221 call!(rt, "quickinput", "quickInput:showInputBox", QuickInputShowInputBox, Arguments)
1222 },
1223
1224 "workspaces:getFolders" | "workspaces:getWorkspaceFolders" | "workspaces:getWorkspace" => {
1229 call!(rt, "workspaces", WorkspacesGetFolders)
1230 },
1231 "workspaces:addFolder" | "workspaces:addWorkspaceFolders" => {
1232 call!(rt, "workspaces", WorkspacesAddFolder, Arguments)
1233 },
1234 "workspaces:removeFolder" | "workspaces:removeWorkspaceFolders" => {
1235 call!(rt, "workspaces", WorkspacesRemoveFolder, Arguments)
1236 },
1237 "workspaces:getName" => call!(rt, "workspaces", WorkspacesGetName),
1238 "themes:getActive" => call!(rt, "themes", "themes:getActive", ThemesGetActive),
1240 "themes:list" => call!(rt, "themes", "themes:list", ThemesList),
1241 "themes:set" => call!(rt, "themes", "themes:set", ThemesSet, Arguments),
1242 "themes:getColorTheme" => call!(rt, "themes", "themes:getColorTheme (→ getActive)", ThemesGetActive),
1247
1248 "search:findInFiles" | "search:textSearch" | "search:searchText" => {
1252 call!(rt, "search", SearchFindInFiles, Arguments)
1253 },
1254 "search:findFiles" | "search:fileSearch" | "search:searchFile" => {
1255 call!(rt, "search", SearchFindFiles, Arguments)
1256 },
1257 "search:cancel" | "search:clearCache" | "search:onDidChangeResult" => {
1262 dev_log!("search", "{} (stub-ack)", command);
1263
1264 Ok(Value::Null)
1265 },
1266
1267 "decorations:get" => call!(rt, "decorations", "decorations:get", DecorationsGet, Arguments),
1269 "decorations:getMany" => call!(rt, "decorations", "decorations:getMany", DecorationsGetMany, Arguments),
1270 "decorations:set" => call!(rt, "decorations", "decorations:set", DecorationsSet, Arguments),
1271 "decorations:clear" => call!(rt, "decorations", "decorations:clear", DecorationsClear, Arguments),
1272
1273 "workingCopy:isDirty" => call!(rt, "workingcopy", "workingCopy:isDirty", WorkingCopyIsDirty, Arguments),
1275 "workingCopy:setDirty" => {
1276 call!(rt, "workingcopy", "workingCopy:setDirty", WorkingCopySetDirty, Arguments)
1277 },
1278 "workingCopy:getAllDirty" => {
1279 call!(rt, "workingcopy", "workingCopy:getAllDirty", WorkingCopyGetAllDirty)
1280 },
1281 "workingCopy:getDirtyCount" => {
1282 call!(rt, "workingcopy", "workingCopy:getDirtyCount", WorkingCopyGetDirtyCount)
1283 },
1284
1285 "keybinding:add" => call!(rt, "keybinding", "keybinding:add", KeybindingAdd, Arguments),
1287 "keybinding:remove" => call!(rt, "keybinding", "keybinding:remove", KeybindingRemove, Arguments),
1288 "keybinding:lookup" => call!(rt, "keybinding", "keybinding:lookup", KeybindingLookup, Arguments),
1289 "keybinding:getAll" => call!(rt, "keybinding", "keybinding:getAll", KeybindingGetAll),
1290
1291 "lifecycle:getPhase" => call!(rt, "lifecycle", "lifecycle:getPhase", LifecycleGetPhase),
1293 "lifecycle:whenPhase" => call!(rt, "lifecycle", "lifecycle:whenPhase", LifecycleWhenPhase, Arguments),
1294 "lifecycle:requestShutdown" => {
1295 call!(app, "lifecycle", "lifecycle:requestShutdown", LifecycleRequestShutdown)
1296 },
1297 "lifecycle:advancePhase" | "lifecycle:setPhase" => {
1298 dev_log!("lifecycle", "{}", command);
1299
1300 let NewPhase = arg_u64_or(&Arguments, 0, 1) as u8;
1305
1306 RunTime
1307 .Environment
1308 .ApplicationState
1309 .Feature
1310 .Lifecycle
1311 .AdvanceAndBroadcast(NewPhase, &ApplicationHandle);
1312
1313 if NewPhase >= 3 {
1327 if let Some(MainWindow) = ApplicationHandle.get_webview_window("main") {
1328 if let Ok(false) = MainWindow.is_visible() {
1329 if let Err(Error) = MainWindow.show() {
1330 dev_log!(
1331 "lifecycle",
1332 "warn: [Lifecycle] main window show() failed on phase {}: {}",
1333 NewPhase,
1334 Error
1335 );
1336 } else {
1337 dev_log!(
1338 "lifecycle",
1339 "[Lifecycle] main window revealed on phase {} (hidden-until-ready)",
1340 NewPhase
1341 );
1342
1343 let _ = MainWindow.set_focus();
1344 }
1345 }
1346 }
1347 }
1348
1349 Ok(json!(RunTime.Environment.ApplicationState.Feature.Lifecycle.GetPhase()))
1350 },
1351
1352 "label:getUri" => {
1354 dev_log!("label", "label:getUri");
1355
1356 LabelGetURI(RunTime.clone(), Arguments).await
1357 },
1358 "label:getWorkspace" => {
1359 dev_log!("label", "label:getWorkspace");
1360
1361 LabelGetWorkspace(RunTime.clone()).await
1362 },
1363 "label:getBase" => {
1364 dev_log!("label", "label:getBase");
1365
1366 LabelGetBase(Arguments).await
1367 },
1368
1369 "model:open" => {
1371 dev_log!("model", "model:open");
1372
1373 ModelOpen(RunTime.clone(), Arguments).await
1374 },
1375 "model:close" => {
1376 dev_log!("model", "model:close");
1377
1378 ModelClose(RunTime.clone(), Arguments).await
1379 },
1380 "model:get" => {
1381 dev_log!("model", "model:get");
1382
1383 ModelGet(RunTime.clone(), Arguments).await
1384 },
1385 "model:getAll" => {
1386 dev_log!("model", "model:getAll");
1387
1388 ModelGetAll(RunTime.clone()).await
1389 },
1390 "model:updateContent" => {
1391 dev_log!("model", "model:updateContent");
1392
1393 ModelUpdateContent(RunTime.clone(), Arguments).await
1394 },
1395
1396 "history:goBack" => {
1398 dev_log!("history", "history:goBack");
1399
1400 HistoryGoBack(RunTime.clone()).await
1401 },
1402 "history:goForward" => {
1403 dev_log!("history", "history:goForward");
1404
1405 HistoryGoForward(RunTime.clone()).await
1406 },
1407 "history:canGoBack" => {
1408 dev_log!("history", "history:canGoBack");
1409
1410 HistoryCanGoBack(RunTime.clone()).await
1411 },
1412 "history:canGoForward" => {
1413 dev_log!("history", "history:canGoForward");
1414
1415 HistoryCanGoForward(RunTime.clone()).await
1416 },
1417 "history:push" => {
1418 dev_log!("history", "history:push");
1419
1420 HistoryPush(RunTime.clone(), Arguments).await
1421 },
1422 "history:clear" => {
1423 dev_log!("history", "history:clear");
1424
1425 HistoryClear(RunTime.clone()).await
1426 },
1427 "history:getStack" => {
1428 dev_log!("history", "history:getStack");
1429
1430 HistoryGetStack(RunTime.clone()).await
1431 },
1432
1433 "mountain_get_status" => {
1435 let status = json!({
1436 "connected": true,
1437 "version": "1.0.0"
1438 });
1439
1440 Ok(status)
1441 },
1442 "mountain_get_configuration" => {
1443 let Config = RunTime.Environment.ApplicationState.Configuration.GetGlobalConfiguration();
1445
1446 Ok(Config)
1447 },
1448 "mountain_get_services_status" => {
1449 let CocoonConnected = ::Vine::Client::IsClientConnected::Fn("cocoon-main");
1450
1451 Ok(json!({
1452 "cocoon": { "connected": CocoonConnected },
1453 "vine": { "running": true }
1454 }))
1455 },
1456 "mountain_get_state" => {
1457 let FolderCount = RunTime
1458 .Environment
1459 .ApplicationState
1460 .Workspace
1461 .WorkspaceFolders
1462 .lock()
1463 .map(|G| G.len())
1464 .unwrap_or(0);
1465
1466 Ok(json!({
1467 "workspace": { "folderCount": FolderCount },
1468 "activeDocument": RunTime.Environment.ApplicationState.Workspace.GetActiveDocumentURI()
1469 }))
1470 },
1471
1472 "file:realpath" => FileRealpath(Arguments).await,
1478 "file:open" => FileOpenFd(Arguments).await,
1479 "file:close" => FileCloseFd(Arguments).await,
1480 "file:cloneFile" => FileCloneNative(Arguments).await,
1481
1482 "nativeHost:pickFolderAndOpen" => NativePickFolder(ApplicationHandle.clone(), Arguments).await,
1488 "nativeHost:pickFileAndOpen" => NativePickFolder(ApplicationHandle.clone(), Arguments).await,
1489 "nativeHost:pickFileFolderAndOpen" => NativePickFolder(ApplicationHandle.clone(), Arguments).await,
1490 "nativeHost:pickWorkspaceAndOpen" => NativePickFolder(ApplicationHandle.clone(), Arguments).await,
1491 "nativeHost:showOpenDialog" => NativeShowOpenDialog(ApplicationHandle.clone(), Arguments).await,
1492
1493 "UserInterface.ShowOpenDialog" => {
1499 match NativeShowOpenDialog(ApplicationHandle.clone(), Arguments).await {
1500 Ok(Response) => {
1501 let Paths = Response
1502 .get("filePaths")
1503 .and_then(|V| V.as_array())
1504 .cloned()
1505 .unwrap_or_default();
1506
1507 Ok(Value::Array(Paths))
1508 },
1509 Err(Error) => Err(Error),
1510 }
1511 },
1512 "nativeHost:showSaveDialog" => NativeShowSaveDialog(ApplicationHandle.clone(), Arguments).await,
1513 "UserInterface.ShowSaveDialog" => {
1516 UserInterfaceShowSaveDialog(ApplicationHandle.clone(), Arguments).await
1517 },
1518 "nativeHost:showMessageBox" => NativeShowMessageBox(ApplicationHandle.clone(), Arguments).await,
1519
1520 "nativeHost:getEnvironmentPaths" => NativeGetEnvironmentPaths(ApplicationHandle.clone()).await,
1522
1523 "nativeHost:getOSColorScheme" => {
1525 dev_log!("nativehost", "nativeHost:getOSColorScheme");
1526
1527 NativeGetColorScheme().await
1528 },
1529 "nativeHost:getOSProperties" => {
1530 dev_log!("nativehost", "nativeHost:getOSProperties");
1531
1532 NativeOSProperties().await
1533 },
1534 "nativeHost:getOSStatistics" => {
1535 dev_log!("nativehost", "nativeHost:getOSStatistics");
1536
1537 NativeOSStatistics().await
1538 },
1539 "nativeHost:getOSVirtualMachineHint" => {
1540 dev_log!("nativehost", "nativeHost:getOSVirtualMachineHint");
1541
1542 Ok(json!(0))
1543 },
1544
1545 "nativeHost:isWindowAlwaysOnTop" => {
1547 dev_log!("window", "nativeHost:isWindowAlwaysOnTop");
1548
1549 Ok(json!(false))
1550 },
1551 "nativeHost:isFullScreen" => {
1552 dev_log!("window", "nativeHost:isFullScreen");
1553
1554 NativeIsFullscreen(ApplicationHandle.clone()).await
1555 },
1556 "nativeHost:isMaximized" => {
1557 dev_log!("window", "nativeHost:isMaximized");
1558
1559 NativeIsMaximized(ApplicationHandle.clone()).await
1560 },
1561 "nativeHost:getActiveWindowId" => {
1562 dev_log!("window", "nativeHost:getActiveWindowId");
1563
1564 Ok(json!(1))
1565 },
1566 "nativeHost:getCursorScreenPoint" => {
1581 dev_log!("window", "nativeHost:getCursorScreenPoint");
1582
1583 Ok(json!({ "x": 0, "y": 0 }))
1588 },
1589 "nativeHost:getWindows" => {
1590 let Title = std::env::var("ProductNameShort").unwrap_or_else(|_| "Land".into());
1591
1592 let ActiveDoc = RunTime
1593 .Environment
1594 .ApplicationState
1595 .Workspace
1596 .GetActiveDocumentURI()
1597 .unwrap_or_default();
1598
1599 Ok(json!([{ "id": 1, "title": Title, "filename": ActiveDoc }]))
1600 },
1601 "nativeHost:getWindowCount" => Ok(json!(1)),
1602
1603 "nativeHost:openAgentsWindow" | "nativeHost:openDevToolsWindow" | "nativeHost:openAuxiliaryWindow" => {
1613 dev_log!("window", "{} (acknowledged, no-op - aux window unsupported)", command);
1614
1615 Ok(Value::Null)
1616 },
1617
1618 "nativeHost:focusWindow" => {
1622 dev_log!("window", "{}", command);
1623
1624 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1625 let _ = Window.set_focus();
1626 }
1627
1628 Ok(Value::Null)
1629 },
1630 "nativeHost:maximizeWindow" => {
1631 dev_log!("window", "{}", command);
1632
1633 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1634 let _ = Window.maximize();
1635 }
1636
1637 Ok(Value::Null)
1638 },
1639 "nativeHost:unmaximizeWindow" => {
1640 dev_log!("window", "{}", command);
1641
1642 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1643 let _ = Window.unmaximize();
1644 }
1645
1646 Ok(Value::Null)
1647 },
1648 "nativeHost:minimizeWindow" => {
1649 dev_log!("window", "{}", command);
1650
1651 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1652 let _ = Window.minimize();
1653 }
1654
1655 Ok(Value::Null)
1656 },
1657 "nativeHost:toggleFullScreen" => {
1658 dev_log!("window", "{}", command);
1659
1660 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1661 let IsFullscreen = Window.is_fullscreen().unwrap_or(false);
1662
1663 let _ = Window.set_fullscreen(!IsFullscreen);
1664 }
1665
1666 Ok(Value::Null)
1667 },
1668 "nativeHost:closeWindow" => {
1669 dev_log!("window", "{}", command);
1670
1671 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1677 let _ = Window.destroy();
1678 }
1679
1680 Ok(Value::Null)
1681 },
1682 "nativeHost:setWindowAlwaysOnTop" => {
1683 dev_log!("window", "{}", command);
1684
1685 let OnTop = arg_bool(&Arguments, 0);
1686
1687 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1688 let _ = Window.set_always_on_top(OnTop);
1689 }
1690
1691 Ok(Value::Null)
1692 },
1693 "nativeHost:toggleWindowAlwaysOnTop" => {
1694 dev_log!("window", "{}", command);
1695
1696 static ALWAYS_ON_TOP:std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
1697
1698 let Next = !ALWAYS_ON_TOP.fetch_xor(true, std::sync::atomic::Ordering::Relaxed);
1699
1700 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1701 let _ = Window.set_always_on_top(Next);
1702 }
1703
1704 Ok(Value::Null)
1705 },
1706 "nativeHost:setRepresentedFilename" => {
1710 dev_log!("window", "{}", command);
1711
1712 let Path = arg_string(&Arguments, 0);
1713
1714 if !Path.is_empty() {
1715 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1716 let Filename = std::path::Path::new(&Path)
1719 .file_name()
1720 .and_then(|N| N.to_str())
1721 .unwrap_or(&Path);
1722
1723 let _ = Window.set_title(Filename);
1724 }
1725 }
1726
1727 Ok(Value::Null)
1728 },
1729
1730 "nativeHost:setDocumentEdited" => {
1734 let _ = Arguments;
1735
1736 Ok(Value::Null)
1737 },
1738
1739 "nativeHost:setMinimumSize" => {
1742 let Width = arg_u64_or(&Arguments, 0, 400) as u32;
1743
1744 let Height = arg_u64_or(&Arguments, 1, 300) as u32;
1745
1746 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1747 let _ = Window.set_min_size(Some(tauri::Size::Physical(tauri::PhysicalSize {
1748 width:Width,
1749 height:Height,
1750 })));
1751 }
1752
1753 Ok(Value::Null)
1754 },
1755
1756 "nativeHost:positionWindow" => {
1759 if let Some(Rect) = Arguments.first() {
1760 let X = Rect.get("x").and_then(|V| V.as_i64()).unwrap_or(0) as i32;
1761
1762 let Y = Rect.get("y").and_then(|V| V.as_i64()).unwrap_or(0) as i32;
1763
1764 let W = Rect.get("width").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1765
1766 let H = Rect.get("height").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1767
1768 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1769 let _ =
1770 Window.set_position(tauri::Position::Physical(tauri::PhysicalPosition { x:X, y:Y }));
1771
1772 if W > 0 && H > 0 {
1773 let _ =
1774 Window.set_size(tauri::Size::Physical(tauri::PhysicalSize { width:W, height:H }));
1775 }
1776 }
1777 }
1778
1779 Ok(Value::Null)
1780 },
1781
1782 "nativeHost:updateWindowControls"
1784 | "nativeHost:notifyReady"
1785 | "nativeHost:saveWindowSplash"
1786 | "nativeHost:updateTouchBar"
1787 | "nativeHost:moveWindowTop"
1788 | "nativeHost:setBackgroundThrottling"
1789 | "nativeHost:updateWindowAccentColor" => {
1790 dev_log!("window", "{}", command);
1791
1792 Ok(Value::Null)
1793 },
1794
1795 "nativeHost:isAdmin" => Ok(json!(false)),
1797 "nativeHost:isRunningUnderARM64Translation" => NativeIsRunningUnderARM64Translation().await,
1798 "nativeHost:hasWSLFeatureInstalled" => {
1799 #[cfg(target_os = "windows")]
1800 {
1801 Ok(json!(std::path::Path::new("C:\\Windows\\System32\\wsl.exe").exists()))
1802 }
1803
1804 #[cfg(not(target_os = "windows"))]
1805 {
1806 Ok(json!(false))
1807 }
1808 },
1809 "nativeHost:showItemInFolder" => ShowItemInFolder(RunTime.clone(), Arguments).await,
1810 "nativeHost:openExternal" => OpenExternal(RunTime.clone(), Arguments).await,
1811 "nativeHost:moveItemToTrash" => {
1813 dev_log!("nativehost", "nativeHost:moveItemToTrash");
1814
1815 NativeMoveItemToTrash(Arguments).await
1816 },
1817
1818 "nativeHost:readClipboardText" => {
1820 dev_log!("clipboard", "readClipboardText");
1821
1822 NativeReadClipboardText(Arguments).await
1823 },
1824 "nativeHost:writeClipboardText" => {
1825 dev_log!("clipboard", "writeClipboardText");
1826
1827 NativeWriteClipboardText(Arguments).await
1828 },
1829 "nativeHost:readClipboardFindText" => {
1830 dev_log!("clipboard", "readClipboardFindText");
1831
1832 NativeReadClipboardFindText(Arguments).await
1833 },
1834 "nativeHost:writeClipboardFindText" => {
1835 dev_log!("clipboard", "writeClipboardFindText");
1836
1837 NativeWriteClipboardFindText(Arguments).await
1838 },
1839 "nativeHost:readClipboardBuffer" => {
1840 dev_log!("clipboard", "readClipboardBuffer");
1841
1842 NativeReadClipboardBuffer(Arguments).await
1843 },
1844 "nativeHost:writeClipboardBuffer" => {
1845 dev_log!("clipboard", "writeClipboardBuffer");
1846
1847 NativeWriteClipboardBuffer(Arguments).await
1848 },
1849 "nativeHost:hasClipboard" => {
1850 dev_log!("clipboard", "hasClipboard");
1851
1852 NativeHasClipboard(Arguments).await
1853 },
1854 "nativeHost:readImage" => {
1855 dev_log!("clipboard", "readImage");
1856
1857 NativeReadImage(Arguments).await
1858 },
1859 "nativeHost:triggerPaste" => {
1860 dev_log!("clipboard", "triggerPaste");
1861
1862 NativeTriggerPaste(Arguments).await
1863 },
1864
1865 "nativeHost:getProcessId" => Ok(json!(std::process::id())),
1867 "nativeHost:killProcess" => KillProcess(Arguments).await,
1868
1869 "nativeHost:findFreePort" => NativeFindFreePort(Arguments).await,
1871 "nativeHost:isPortFree" => {
1872 let Port = arg_u64(&Arguments, 0) as u16;
1873
1874 if Port == 0 {
1875 Ok(json!(false))
1876 } else {
1877 let Free = tokio::net::TcpListener::bind(std::net::SocketAddr::from(([127, 0, 0, 1], Port)))
1878 .await
1879 .is_ok();
1880
1881 Ok(json!(Free))
1882 }
1883 },
1884 "nativeHost:resolveProxy" => {
1889 let Url = arg_str(&Arguments, 0);
1890
1891 let Scheme = if Url.starts_with("https") { "HTTPS" } else { "HTTP" };
1892
1893 let ProxyEnv = std::env::var(format!("{}_PROXY", Scheme))
1894 .or_else(|_| std::env::var(format!("{}_proxy", Scheme.to_lowercase())))
1895 .or_else(|_| std::env::var("ALL_PROXY"))
1896 .or_else(|_| std::env::var("all_proxy"));
1897
1898 match ProxyEnv {
1899 Ok(P) if !P.is_empty() => {
1900 let Lower = P.to_lowercase();
1904
1905 let (Keyword, Host) = if Lower.starts_with("socks") {
1906 let H = P
1907 .trim_start_matches("socks5://")
1908 .trim_start_matches("socks4://")
1909 .trim_start_matches("socks://");
1910
1911 ("SOCKS", H)
1912 } else {
1913 let H = P.trim_start_matches("http://").trim_start_matches("https://");
1914
1915 ("PROXY", H)
1916 };
1917
1918 Ok(json!(format!("{} {}", Keyword, Host)))
1919 },
1920 _ => Ok(json!("DIRECT")),
1921 }
1922 },
1923 "nativeHost:lookupAuthorization" => Ok(Value::Null),
1924 "nativeHost:lookupKerberosAuthorization" => Ok(Value::Null),
1925 "nativeHost:loadCertificates" => Ok(json!([])),
1926
1927 "nativeHost:relaunch" => Relaunch(ApplicationHandle.clone(), Arguments).await,
1929 "nativeHost:reload" => Reload(ApplicationHandle.clone(), Arguments).await,
1930 "nativeHost:quit" => Quit(ApplicationHandle.clone(), Arguments).await,
1931 "nativeHost:exit" => Exit(ApplicationHandle.clone(), Arguments).await,
1932
1933 "nativeHost:openDevTools" => OpenDevTools(ApplicationHandle.clone(), Arguments).await,
1935 "nativeHost:toggleDevTools" => ToggleDevTools(ApplicationHandle.clone(), Arguments).await,
1936
1937 "nativeHost:getSystemIdleState" => Ok(json!("active")),
1939 "nativeHost:getSystemIdleTime" => Ok(json!(0)),
1940 "nativeHost:getCurrentThermalState" => Ok(json!("nominal")),
1941 "nativeHost:isOnBatteryPower" => Ok(json!(false)),
1942 "nativeHost:startPowerSaveBlocker" => Ok(json!(0)),
1943 "nativeHost:stopPowerSaveBlocker" => Ok(json!(false)),
1944 "nativeHost:isPowerSaveBlockerStarted" => Ok(json!(false)),
1945
1946 "browserView:updateKeybindings"
1951 | "browserView:updateTheme"
1952 | "browserView:updateConfiguration"
1953 | "browserView:openDevTools"
1954 | "browserView:closeDevTools" => Ok(Value::Null),
1955 "browserView:getBrowserViews" => Ok(serde_json::json!([])),
1956
1957 "nativeHost:newWindowTab" => Ok(Value::Null),
1959 "nativeHost:showPreviousWindowTab" => Ok(Value::Null),
1960 "nativeHost:showNextWindowTab" => Ok(Value::Null),
1961 "nativeHost:moveWindowTabToNewWindow" => Ok(Value::Null),
1962 "nativeHost:mergeAllWindowTabs" => Ok(Value::Null),
1963 "nativeHost:toggleWindowTabsBar" => Ok(Value::Null),
1964 "nativeHost:installShellCommand" => InstallShellCommand(Arguments).await,
1965 "nativeHost:uninstallShellCommand" => UninstallShellCommand(Arguments).await,
1966
1967 "localPty:getProfiles" => {
1971 dev_log!("terminal", "localPty:getProfiles");
1972
1973 LocalPTYGetProfiles().await
1974 },
1975 "localPty:getDefaultSystemShell" => {
1976 dev_log!("terminal", "localPty:getDefaultSystemShell");
1977
1978 LocalPTYGetDefaultShell().await
1979 },
1980 "localPty:getTerminalLayoutInfo" => {
1988 dev_log!("terminal", "localPty:getTerminalLayoutInfo");
1989
1990 use CommonLibrary::{Environment::Requires::Requires, Storage::StorageProvider::StorageProvider};
1991
1992 let StorageProvider:Arc<dyn StorageProvider> = RunTime.Environment.Require();
1993
1994 match StorageProvider.GetStorageValue(true, "terminal:layoutInfo").await {
1995 Ok(Some(Stored)) => Ok(Stored),
1996 Ok(None) => Ok(Value::Null),
1997 Err(Error) => {
1998 dev_log!("terminal", "warn: [getTerminalLayoutInfo] storage read failed: {}", Error);
1999
2000 Ok(Value::Null)
2001 },
2002 }
2003 },
2004 "localPty:setTerminalLayoutInfo" => {
2007 dev_log!("terminal", "localPty:setTerminalLayoutInfo");
2008
2009 use CommonLibrary::{Environment::Requires::Requires, Storage::StorageProvider::StorageProvider};
2010
2011 let StorageProvider:Arc<dyn StorageProvider> = RunTime.Environment.Require();
2012
2013 let Payload = arg_val(&Arguments, 0);
2014
2015 let _ = StorageProvider
2016 .UpdateStorageValue(true, "terminal:layoutInfo".to_string(), Some(Payload))
2017 .await;
2018
2019 Ok(Value::Null)
2020 },
2021 "localPty:getPerformanceMarks" => {
2022 dev_log!("terminal", "localPty:getPerformanceMarks");
2023
2024 Ok(json!([]))
2025 },
2026 "localPty:reduceConnectionGraceTime" => {
2027 dev_log!("terminal", "localPty:reduceConnectionGraceTime");
2028
2029 Ok(Value::Null)
2030 },
2031 "localPty:listProcesses" => {
2032 dev_log!("terminal", "localPty:listProcesses");
2033
2034 Ok(json!([]))
2035 },
2036 "localPty:getEnvironment" => {
2037 dev_log!("terminal", "localPty:getEnvironment");
2038
2039 LocalPTYGetEnvironment().await
2040 },
2041 "localPty:getLatency" => {
2053 dev_log!("terminal", "localPty:getLatency");
2054
2055 Ok(json!([]))
2056 },
2057
2058 "cocoon:request" => {
2069 dev_log!("ipc", "cocoon:request method={:?}", Arguments.first());
2070
2071 CocoonRequest(Arguments).await
2072 },
2073 "cocoon:notify" => {
2074 dev_log!("ipc", "cocoon:notify method={:?}", Arguments.first());
2075
2076 CocoonNotify(Arguments).await
2077 },
2078
2079 "localPty:spawn" => call!(rt, "terminal", TerminalCreate, Arguments),
2099 "localPty:createProcess" => call!(rt, "terminal", LocalPTYCreateProcess, Arguments),
2100 "localPty:start" => {
2101 dev_log!("terminal", "{} no-op (eager-spawn)", command);
2114
2115 Ok(Value::Null)
2116 },
2117 "localPty:input" | "localPty:write" => call!(rt, "terminal", TerminalSendText, Arguments),
2118 "localPty:shutdown" | "localPty:dispose" => call!(rt, "terminal", TerminalDispose, Arguments),
2119 "localPty:resize" => call!(rt, "terminal", "localPty:resize", LocalPTYResize, Arguments),
2120 "localPty:acknowledgeDataEvent" => {
2121 Ok(Value::Null)
2123 },
2124 "localPty:getBackendOS" => {
2129 #[cfg(target_os = "macos")]
2130 {
2131 Ok(json!(1))
2132 }
2133
2134 #[cfg(target_os = "linux")]
2135 {
2136 Ok(json!(2))
2137 }
2138
2139 #[cfg(target_os = "windows")]
2140 {
2141 Ok(json!(3))
2142 }
2143
2144 #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
2145 {
2146 Ok(json!(2))
2147 }
2148 },
2149
2150 "localPty:refreshProperty" => {
2160 use CommonLibrary::{
2161 Environment::Requires::Requires,
2162 Terminal::TerminalProvider::TerminalProvider,
2163 };
2164
2165 let TerminalId = arg_u64(&Arguments, 0);
2166
2167 let PropId = arg_u64(&Arguments, 1);
2168
2169 if TerminalId == 0 {
2170 Ok(Value::Null)
2171 } else if PropId == 0 {
2172 let Cwd = RunTime
2174 .Environment
2175 .ApplicationState
2176 .Feature
2177 .Terminals
2178 .ActiveTerminals
2179 .lock()
2180 .ok()
2181 .and_then(|G| G.get(&TerminalId).cloned())
2182 .and_then(|S| S.lock().ok().and_then(|S| S.CurrentWorkingDirectory.clone()))
2183 .map(|P| P.to_string_lossy().into_owned());
2184
2185 Ok(Cwd.map(|C| json!(C)).unwrap_or(Value::Null))
2186 } else if PropId == 1 {
2187 let Provider:Arc<dyn TerminalProvider> = RunTime.Environment.Require();
2189
2190 match Provider.GetTerminalProcessId(TerminalId).await {
2191 Ok(Some(Pid)) => Ok(json!(Pid)),
2192 _ => Ok(Value::Null),
2193 }
2194 } else {
2195 Ok(Value::Null)
2196 }
2197 },
2198
2199 "localPty:updateProperty" => Ok(Value::Null),
2202
2203 "localPty:freePortKillProcess" => {
2206 dev_log!("terminal", "localPty:freePortKillProcess");
2207
2208 LocalPTYFreePortKillProcess(Arguments).await
2209 },
2210
2211 "localPty:serializeTerminalState" => {
2216 dev_log!("terminal", "localPty:serializeTerminalState");
2217
2218 SerializeTerminalState(RunTime.clone()).await
2219 },
2220
2221 "localPty:reviveTerminalProcesses" => {
2225 dev_log!(
2226 "terminal",
2227 "localPty:reviveTerminalProcesses count={}",
2228 Arguments.first().and_then(|V| V.as_array()).map(|A| A.len()).unwrap_or(0)
2229 );
2230
2231 ReviveTerminalProcesses(RunTime.clone(), Arguments).await
2232 },
2233
2234 "localPty:getRevivedPtyNewId" => {
2240 let NewId = RunTime.Environment.ApplicationState.GetNextTerminalIdentifier();
2241
2242 dev_log!("terminal", "localPty:getRevivedPtyNewId id={}", NewId);
2243
2244 Ok(json!(NewId))
2245 },
2246
2247 "localPty:attachToProcess" => {
2253 dev_log!("terminal", "localPty:attachToProcess");
2254
2255 AttachToProcess(RunTime.clone(), Arguments).await
2256 },
2257 "localPty:detachFromProcess" => {
2258 dev_log!("terminal", "localPty:detachFromProcess");
2259
2260 DetachFromProcess(RunTime.clone(), Arguments).await
2261 },
2262
2263 "localPty:setActive" => {
2267 let TermId = Arguments.first().and_then(Value::as_i64);
2268
2269 let Payload = match TermId {
2270 Some(Id) => serde_json::json!({ "id": Id }),
2271 None => serde_json::json!({ "id": null }),
2272 };
2273
2274 let _ = ::Vine::Client::SendNotification::Fn(
2275 "cocoon-main".to_string(),
2276 "$acceptActiveTerminalChanged".to_string(),
2277 Payload,
2278 )
2279 .await;
2280
2281 Ok(Value::Null)
2282 },
2283
2284 "localPty:setShellIntegrationActive" => {
2289 let TermId = Arguments.first().and_then(Value::as_i64).unwrap_or(0) as u64;
2290
2291 let _ = ::Vine::Client::SendNotification::Fn(
2292 "cocoon-main".to_string(),
2293 "$acceptTerminalShellIntegrationActivated".to_string(),
2294 serde_json::json!({ "id": TermId }),
2295 )
2296 .await;
2297
2298 Ok(Value::Null)
2299 },
2300
2301 "localPty:setInteracted" => {
2308 let Payload = arg_val(&Arguments, 0);
2309
2310 let _ = ::Vine::Client::SendNotification::Fn(
2311 "cocoon-main".to_string(),
2312 "$acceptTerminalStateChanged".to_string(),
2313 Payload,
2314 )
2315 .await;
2316
2317 Ok(Value::Null)
2318 },
2319
2320 "localPty:setCwd" => {
2325 let TermId = Arguments.first().and_then(Value::as_i64).unwrap_or(0) as u64;
2326
2327 let Cwd = Arguments.get(1).and_then(Value::as_str).unwrap_or("").to_string();
2328
2329 if !Cwd.is_empty() {
2330 if let Ok(Guard) = RunTime.Environment.ApplicationState.Feature.Terminals.ActiveTerminals.lock()
2333 {
2334 if let Some(StateEntry) = Guard.get(&TermId) {
2335 if let Ok(mut State) = StateEntry.lock() {
2336 State.CurrentWorkingDirectory = Some(std::path::PathBuf::from(&Cwd));
2337 }
2338 }
2339 }
2340
2341 let _ = ::Vine::Client::SendNotification::Fn(
2342 "cocoon-main".to_string(),
2343 "$acceptTerminalCwdChange".to_string(),
2344 serde_json::json!({ "id": TermId, "cwd": Cwd }),
2345 )
2346 .await;
2347 }
2348
2349 Ok(Value::Null)
2350 },
2351
2352 "localPty:processBinary" => {
2360 call!(rt, "terminal", TerminalSendText, Arguments)
2361 },
2362 "localPty:orphanQuestionReply"
2366 | "localPty:updateTitle"
2367 | "localPty:updateIcon"
2368 | "localPty:installAutoReply"
2369 | "localPty:uninstallAllAutoReplies" => Ok(Value::Null),
2370
2371 "localPty:shellExecutionStart" => {
2379 let Payload = arg_val(&Arguments, 0);
2380
2381 let _ = ::Vine::Client::SendNotification::Fn(
2382 "cocoon-main".to_string(),
2383 "$acceptTerminalShellExecutionStart".to_string(),
2384 Payload,
2385 )
2386 .await;
2387
2388 Ok(Value::Null)
2389 },
2390
2391 "localPty:shellExecutionEnd" => {
2402 let Payload = arg_val(&Arguments, 0);
2403
2404 let _ = ::Vine::Client::SendNotification::Fn(
2405 "cocoon-main".to_string(),
2406 "$acceptTerminalShellExecutionEnd".to_string(),
2407 Payload.clone(),
2408 )
2409 .await;
2410
2411 let _ = ::Vine::Client::SendNotification::Fn(
2412 "cocoon-main".to_string(),
2413 "$acceptExecutedTerminalCommand".to_string(),
2414 Payload,
2415 )
2416 .await;
2417
2418 Ok(Value::Null)
2419 },
2420
2421 "update:_getInitialState" => UpdateGetInitialState().await,
2425 "update:isLatestVersion" => UpdateIsLatestVersion().await,
2426 "update:checkForUpdates" => UpdateCheckForUpdates().await,
2427 "update:downloadUpdate" => UpdateDownloadUpdate().await,
2428 "update:applyUpdate" => UpdateApplyUpdate().await,
2429 "update:quitAndInstall" => UpdateQuitAndInstall().await,
2430
2431 "url:registerExternalUriOpener" => {
2443 dev_log!("url", "url:registerExternalUriOpener");
2444
2445 Ok(Value::Null)
2446 },
2447
2448 "encryption:encrypt" => Encrypt(Arguments).await,
2452 "encryption:decrypt" => Decrypt(Arguments).await,
2453
2454 "process:getPlatform" => {
2461 Ok(json!(match std::env::consts::OS {
2462 "windows" => "win32",
2463 "macos" => "darwin",
2464 _ => "linux",
2465 }))
2466 },
2467
2468 "process:getArch" => {
2469 Ok(json!(match std::env::consts::ARCH {
2470 "x86_64" => "x64",
2471 "aarch64" => "arm64",
2472 "x86" => "ia32",
2473 _ => "x64",
2474 }))
2475 },
2476
2477 "process:getPid" => Ok(json!(std::process::id())),
2478
2479 "process:getExecPath" => {
2480 Ok(json!(
2481 std::env::current_exe().unwrap_or_default().to_string_lossy().into_owned()
2482 ))
2483 },
2484
2485 "process:getMemoryInfo" => {
2486 Ok(json!({
2490 "workingSetSize": 0u64,
2491 "peakWorkingSetSize": 0u64,
2492 "privateBytes": 0u64,
2493 "sharedBytes": 0u64,
2494 }))
2495 },
2496
2497 "process:getCpuInfo" => {
2498 Ok(json!([{
2500 "model": format!("{} ({})", std::env::consts::ARCH, std::env::consts::OS),
2501 "speed": 0u32,
2502 "times": { "user": 0u64, "nice": 0u64, "sys": 0u64, "idle": 0u64, "irq": 0u64 },
2503 }]))
2504 },
2505
2506 "extensionHostStarter:createExtensionHost" => {
2510 dev_log!("exthost", "extensionHostStarter:createExtensionHost");
2511
2512 ExtensionHostStarterCreate(Arguments).await
2513 },
2514 "extensionHostStarter:start" => {
2515 dev_log!("exthost", "extensionHostStarter:start");
2516
2517 ExtensionHostStarterStart(Arguments).await
2518 },
2519 "extensionHostStarter:kill" => {
2520 dev_log!("exthost", "extensionHostStarter:kill");
2521
2522 ExtensionHostStarterKill(Arguments).await
2523 },
2524 "extensionHostStarter:getExitInfo" => {
2525 dev_log!("exthost", "extensionHostStarter:getExitInfo");
2526
2527 ExtensionHostStarterGetExitInfo(Arguments).await
2528 },
2529 "extensionHostStarter:waitForExit" => {
2530 dev_log!("exthost", "extensionHostStarter:waitForExit");
2531
2532 ExtensionHostStarterWaitForExit(Arguments).await
2533 },
2534
2535 "cocoon:extensionHostMessage" => {
2539 dev_log!("exthost", "cocoon:extensionHostMessage");
2540
2541 CocoonExtensionHostMessage(ApplicationHandle.clone(), Arguments).await
2542 },
2543
2544 "extensionhostdebugservice:reload" => {
2548 dev_log!("exthost", "extensionhostdebugservice:reload");
2549
2550 ExtensionHostDebugReload(ApplicationHandle.clone()).await
2551 },
2552 "extensionhostdebugservice:close" => {
2553 dev_log!("exthost", "extensionhostdebugservice:close");
2554
2555 ExtensionHostDebugClose(ApplicationHandle.clone()).await
2556 },
2557 "extensionhostdebugservice:attachSession" | "extensionhostdebugservice:terminateSession" => {
2558 dev_log!("exthost", "{}", command);
2559
2560 Ok(Value::Null)
2561 },
2562
2563 "workspaces:getRecentlyOpened" => {
2567 dev_log!("workspaces", "workspaces:getRecentlyOpened");
2568
2569 ReadRecentlyOpened()
2570 },
2571 "workspaces:removeRecentlyOpened" => {
2572 dev_log!("workspaces", "workspaces:removeRecentlyOpened");
2573
2574 let Uri = arg_string(&Arguments, 0);
2575
2576 if !Uri.is_empty() {
2577 MutateRecentlyOpened(|List| {
2578 if let Some(Workspaces) = List.get_mut("workspaces").and_then(|V| V.as_array_mut()) {
2579 Workspaces
2580 .retain(|Entry| Entry.get("uri").and_then(|V| V.as_str()).unwrap_or("") != Uri);
2581 }
2582
2583 if let Some(Files) = List.get_mut("files").and_then(|V| V.as_array_mut()) {
2584 Files.retain(|Entry| Entry.get("uri").and_then(|V| V.as_str()).unwrap_or("") != Uri);
2585 }
2586 });
2587 }
2588
2589 Ok(Value::Null)
2590 },
2591 "workspaces:addRecentlyOpened" => {
2592 dev_log!("workspaces", "workspaces:addRecentlyOpened");
2593
2594 let Entries:Vec<Value> = Arguments.first().and_then(|V| V.as_array()).cloned().unwrap_or_default();
2596
2597 if !Entries.is_empty() {
2598 MutateRecentlyOpened(|List| {
2599 let Workspaces = List
2600 .get_mut("workspaces")
2601 .and_then(|V| V.as_array_mut())
2602 .map(|V| std::mem::take(V))
2603 .unwrap_or_default();
2604
2605 let Files = List
2606 .get_mut("files")
2607 .and_then(|V| V.as_array_mut())
2608 .map(|V| std::mem::take(V))
2609 .unwrap_or_default();
2610
2611 let mut MergedWorkspaces = Workspaces;
2612
2613 let mut MergedFiles = Files;
2614
2615 for Entry in Entries {
2616 let Folder = Entry
2617 .get("folderUri")
2618 .cloned()
2619 .or_else(|| Entry.get("workspace").and_then(|W| W.get("configPath").cloned()));
2620
2621 let File = Entry.get("fileUri").cloned();
2622
2623 if let Some(FolderUri) = Folder.and_then(|V| v_str(&V)) {
2624 MergedWorkspaces
2625 .retain(|E| E.get("uri").and_then(|V| V.as_str()).unwrap_or("") != FolderUri);
2626
2627 let mut Item = serde_json::Map::new();
2628
2629 Item.insert("uri".into(), json!(FolderUri));
2630
2631 if let Some(Label) = Entry.get("label").and_then(|V| V.as_str()) {
2632 Item.insert("label".into(), json!(Label));
2633 }
2634
2635 MergedWorkspaces.insert(0, Value::Object(Item));
2636 }
2637
2638 if let Some(FileUri) = File.and_then(|V| v_str(&V)) {
2639 MergedFiles
2640 .retain(|E| E.get("uri").and_then(|V| V.as_str()).unwrap_or("") != FileUri);
2641
2642 let mut Item = serde_json::Map::new();
2643
2644 Item.insert("uri".into(), json!(FileUri));
2645
2646 MergedFiles.insert(0, Value::Object(Item));
2647 }
2648 }
2649
2650 MergedWorkspaces.truncate(50);
2653
2654 MergedFiles.truncate(50);
2655
2656 List.insert("workspaces".into(), Value::Array(MergedWorkspaces));
2657
2658 List.insert("files".into(), Value::Array(MergedFiles));
2659 });
2660 }
2661
2662 Ok(Value::Null)
2663 },
2664 "workspaces:clearRecentlyOpened" => {
2665 dev_log!("workspaces", "workspaces:clearRecentlyOpened");
2666
2667 MutateRecentlyOpened(|List| {
2668 List.insert("workspaces".into(), json!([]));
2669
2670 List.insert("files".into(), json!([]));
2671 });
2672
2673 Ok(Value::Null)
2674 },
2675 "workspaces:enterWorkspace" => {
2676 dev_log!("workspaces", "workspaces:enterWorkspace");
2677
2678 Ok(Value::Null)
2679 },
2680 "workspaces:createUntitledWorkspace" => {
2681 dev_log!("workspaces", "workspaces:createUntitledWorkspace");
2682
2683 Ok(Value::Null)
2684 },
2685 "workspaces:deleteUntitledWorkspace" => {
2686 dev_log!("workspaces", "workspaces:deleteUntitledWorkspace");
2687
2688 Ok(Value::Null)
2689 },
2690 "workspaces:getWorkspaceIdentifier" => {
2691 let Workspace = &RunTime.Environment.ApplicationState.Workspace;
2698
2699 let Folders = Workspace.GetWorkspaceFolders();
2700
2701 if let Some(First) = Folders.first() {
2702 use std::{
2703 collections::hash_map::DefaultHasher,
2704 hash::{Hash, Hasher},
2705 };
2706
2707 let mut Hasher = DefaultHasher::new();
2708
2709 First.URI.as_str().hash(&mut Hasher);
2710
2711 let Id = format!("{:016x}", Hasher.finish());
2712
2713 Ok(json!({
2714 "id": Id,
2715 "configPath": Value::Null,
2716 "uri": First.URI.to_string(),
2717 }))
2718 } else {
2719 Ok(Value::Null)
2720 }
2721 },
2722 "workspaces:getDirtyWorkspaces" => Ok(json!([])),
2723
2724 "git:exec" | "git:clone" | "git:pull" | "git:checkout" | "git:revParse" | "git:fetch"
2736 | "git:revListCount" | "git:cancel" | "git:isAvailable"
2737 if tier_routes_to_node(TIER_SCM, "TierSCM") =>
2738 {
2739 forward_to_cocoon!("scm", command, Arguments)
2740 },
2741 "git:exec" => {
2742 dev_log!("git", "git:exec");
2743
2744 Git::HandleExec::Fn(Arguments).await
2745 },
2746 "git:clone" => {
2747 dev_log!("git", "git:clone");
2748
2749 Git::HandleClone::Fn(Arguments).await
2750 },
2751 "git:pull" => {
2752 dev_log!("git", "git:pull");
2753
2754 Git::HandlePull::Fn(Arguments).await
2755 },
2756 "git:checkout" => {
2757 dev_log!("git", "git:checkout");
2758
2759 Git::HandleCheckout::Fn(Arguments).await
2760 },
2761 "git:revParse" => {
2762 dev_log!("git", "git:revParse");
2763
2764 Git::HandleRevParse::Fn(Arguments).await
2765 },
2766 "git:fetch" => {
2767 dev_log!("git", "git:fetch");
2768
2769 Git::HandleFetch::Fn(Arguments).await
2770 },
2771 "git:revListCount" => {
2772 dev_log!("git", "git:revListCount");
2773
2774 Git::HandleRevListCount::Fn(Arguments).await
2775 },
2776 "git:cancel" => {
2777 dev_log!("git", "git:cancel");
2778
2779 Git::HandleCancel::Fn(Arguments).await
2780 },
2781 "git:isAvailable" => {
2782 dev_log!("git", "git:isAvailable");
2783
2784 Git::HandleIsAvailable::Fn(Arguments).await
2785 },
2786
2787 "tree:getChildren" => TreeGetChildren(ApplicationHandle.clone(), RunTime.clone(), Arguments).await,
2794
2795 "tree.reveal" | "tree:reveal" => {
2798 use tauri::Emitter;
2799
2800 let ViewId = arg_string(&Arguments, 0);
2801
2802 let Handle = arg_string(&Arguments, 1);
2803
2804 let Options = arg_val(&Arguments, 2);
2805
2806 dev_log!("ipc", "tree.reveal viewId={} handle={}", ViewId, Handle);
2807
2808 let _ = ApplicationHandle.emit(
2809 "sky://tree-view/reveal",
2810 json!({
2811 "viewId": ViewId,
2812 "handle": Handle,
2813 "options": Options,
2814 }),
2815 );
2816
2817 Ok(Value::Null)
2818 },
2819
2820 "tree:selectionChanged" | "tree:collapseElement" | "tree:expandElement" | "tree:visibilityChanged" => {
2824 let Payload = arg_val(&Arguments, 0);
2825
2826 let Method = match command.as_str() {
2827 "tree:selectionChanged" => "$treeView:selectionChanged",
2828 "tree:collapseElement" => "$treeView:collapseElement",
2829 "tree:expandElement" => "$treeView:expandElement",
2830 _ => "$treeView:visibilityChanged",
2831 };
2832
2833 tokio::spawn(async move {
2834 if let Err(E) =
2835 ::Vine::Client::SendNotification::Fn("cocoon-main".to_string(), Method.to_string(), Payload)
2836 .await
2837 {
2838 dev_log!("ipc", "warn: [tree] Cocoon notify {} failed: {:?}", Method, E);
2839 }
2840 });
2841
2842 Ok(Value::Null)
2843 },
2844
2845 "sky:replay-events" => SkyReplayEvents(ApplicationHandle.clone(), RunTime.clone()).await,
2847
2848 "editor:revealRange" | "window:revealRange" => {
2852 use tauri::Emitter;
2853
2854 let Payload = arg_val(&Arguments, 0);
2855
2856 let _ = ApplicationHandle.emit("sky://editor/revealRange", &Payload);
2857
2858 Ok(Value::Null)
2859 },
2860
2861 "sky:editor:selectionChanged" => {
2869 let Uri = Arguments
2870 .first()
2871 .and_then(|V| V.get("uri"))
2872 .and_then(|V| V.as_str())
2873 .unwrap_or("")
2874 .to_string();
2875
2876 let Selections = Arguments
2877 .first()
2878 .and_then(|V| V.get("selections"))
2879 .cloned()
2880 .unwrap_or(Value::Array(Vec::new()));
2881
2882 dev_log!("model", "[SelectionChanged] uri={}", Uri);
2883
2884 if !Uri.is_empty() {
2886 RunTime
2887 .Environment
2888 .ApplicationState
2889 .Workspace
2890 .SetActiveDocumentURI(Some(Uri.clone()));
2891 }
2892
2893 let ViewColumn = Arguments
2894 .first()
2895 .and_then(|V| V.get("viewColumn"))
2896 .and_then(|V| V.as_u64())
2897 .unwrap_or(1);
2898
2899 let Payload = json!({ "uri": Uri, "selections": Selections, "viewColumn": ViewColumn });
2903
2904 let _ = ::Vine::Client::SendNotification::Fn(
2905 "cocoon-main".to_string(),
2906 "window.didChangeTextEditorSelection".to_string(),
2907 Payload,
2908 )
2909 .await;
2910
2911 Ok(Value::Null)
2912 },
2913
2914 "sky:model:contentChanged" => {
2920 let Payload = arg_val(&Arguments, 0);
2921
2922 let Uri = Payload.get("uri").and_then(Value::as_str).unwrap_or("").to_string();
2923
2924 if !Uri.is_empty() {
2925 let Content = Payload.get("content").and_then(Value::as_str).unwrap_or("").to_string();
2926
2927 let Version = Payload.get("version").and_then(Value::as_i64).unwrap_or(1);
2928
2929 if let Some(mut Doc) = RunTime.Environment.ApplicationState.Feature.Documents.Get(&Uri) {
2931 Doc.Version = Version;
2932
2933 Doc.Lines = Content.lines().map(|L| L.to_owned()).collect();
2934
2935 Doc.IsDirty = true;
2936
2937 RunTime
2938 .Environment
2939 .ApplicationState
2940 .Feature
2941 .Documents
2942 .AddOrUpdate(Uri.clone(), Doc);
2943 }
2944
2945 let Payload2 = json!([
2947 { "external": Uri.clone(), "$mid": 1 },
2948
2949 { "content": Content, "versionId": Version, "isDirty": true, "changes": [] }
2950 ]);
2951
2952 tokio::spawn(async move {
2953 let _ = ::Vine::Client::SendNotification::Fn(
2954 "cocoon-main".to_string(),
2955 "$acceptModelChanged".to_string(),
2956 Payload2,
2957 )
2958 .await;
2959 });
2960 }
2961
2962 Ok(Value::Null)
2963 },
2964
2965 "sky:editor:activeChanged" => {
2966 let Payload = arg_val(&Arguments, 0);
2967
2968 let Uri = Payload.get("uri").and_then(Value::as_str).unwrap_or("").to_string();
2969
2970 dev_log!("model", "[ActiveEditorChanged] uri={}", Uri);
2971
2972 if !Uri.is_empty() {
2973 RunTime
2974 .Environment
2975 .ApplicationState
2976 .Workspace
2977 .SetActiveDocumentURI(Some(Uri.clone()));
2978 }
2979
2980 let _ = ::Vine::Client::SendNotification::Fn(
2981 "cocoon-main".to_string(),
2982 "window.didChangeActiveTextEditor".to_string(),
2983 Payload,
2984 )
2985 .await;
2986
2987 Ok(Value::Null)
2988 },
2989
2990 "sky:editor:visibleChanged" => {
3001 let Payload = arg_val(&Arguments, 0);
3002
3003 let _ = ::Vine::Client::SendNotification::Fn(
3004 "cocoon-main".to_string(),
3005 "$acceptVisibleEditorsChanged".to_string(),
3006 Payload,
3007 )
3008 .await;
3009
3010 Ok(Value::Null)
3011 },
3012
3013 "sky:editor:tabsChanged" => {
3023 let Payload = arg_val(&Arguments, 0);
3024
3025 let _ = ::Vine::Client::SendNotification::Fn(
3026 "cocoon-main".to_string(),
3027 "$acceptTabsChanged".to_string(),
3028 Payload,
3029 )
3030 .await;
3031
3032 Ok(Value::Null)
3033 },
3034
3035 "sky:editor:visibleRangesChanged" => {
3044 let Payload = arg_val(&Arguments, 0);
3045
3046 let _ = ::Vine::Client::SendNotification::Fn(
3047 "cocoon-main".to_string(),
3048 "$acceptVisibleRangesChanged".to_string(),
3049 Payload,
3050 )
3051 .await;
3052
3053 Ok(Value::Null)
3054 },
3055
3056 "sky:editor:optionsChanged" => {
3065 let Payload = arg_val(&Arguments, 0);
3066
3067 let _ = ::Vine::Client::SendNotification::Fn(
3068 "cocoon-main".to_string(),
3069 "$acceptTextEditorOptionsChanged".to_string(),
3070 Payload,
3071 )
3072 .await;
3073
3074 Ok(Value::Null)
3075 },
3076
3077 "sky:editor:diffInformationChanged" => {
3085 let Payload = arg_val(&Arguments, 0);
3086
3087 let _ = ::Vine::Client::SendNotification::Fn(
3088 "cocoon-main".to_string(),
3089 "$acceptTextEditorDiffInformationChanged".to_string(),
3090 Payload,
3091 )
3092 .await;
3093
3094 Ok(Value::Null)
3095 },
3096
3097 "sky:editor:viewColumnChanged" => {
3105 let Payload = arg_val(&Arguments, 0);
3106
3107 let _ = ::Vine::Client::SendNotification::Fn(
3108 "cocoon-main".to_string(),
3109 "$acceptTextEditorViewColumnChanged".to_string(),
3110 Payload,
3111 )
3112 .await;
3113
3114 Ok(Value::Null)
3115 },
3116
3117 "language:provideInlineCompletions" => {
3128 let Payload = arg_val(&Arguments, 0);
3129
3130 let UriStr = Payload.get("uri").and_then(Value::as_str).unwrap_or("").to_string();
3131
3132 if UriStr.is_empty() {
3133 Ok(json!({ "items": [] }))
3134 } else {
3135 let Line = Payload
3136 .get("position")
3137 .and_then(|P| P.get("line"))
3138 .and_then(Value::as_u64)
3139 .unwrap_or(0) as i64 + 1;
3140
3141 let Character = Payload
3142 .get("position")
3143 .and_then(|P| P.get("character"))
3144 .and_then(Value::as_u64)
3145 .unwrap_or(0) as i64 + 1;
3146
3147 let Context = Payload.get("context").cloned().unwrap_or_else(|| json!({ "triggerKind": 0 }));
3148
3149 match url::Url::parse(&UriStr) {
3150 Ok(Uri) => {
3151 let Position = PositionDTO { LineNumber:Line as u32, Column:Character as u32 };
3152
3153 match RunTime.Environment.ProvideInlineCompletionItems(Uri, Position, Context).await {
3154 Ok(Some(Result)) => {
3155 let Items = Result
3156 .get("items")
3157 .cloned()
3158 .unwrap_or_else(|| if Result.is_array() { Result } else { json!([]) });
3159
3160 Ok(json!({ "items": Items }))
3161 },
3162 Ok(None) => Ok(json!({ "items": [] })),
3163 Err(Error) => {
3164 dev_log!("ipc", "warn: language:provideInlineCompletions error: {}", Error);
3165
3166 Ok(json!({ "items": [] }))
3167 },
3168 }
3169 },
3170 Err(_) => Ok(json!({ "items": [] })),
3171 }
3172 }
3173 },
3174
3175 "languages:getAll" | "languages:getEncodedLanguageId" => {
3176 dev_log!("extensions", "languages: {} (→ Cocoon)", command);
3177
3178 let Payload = Arguments.into_iter().next().unwrap_or(Value::Null);
3179
3180 if !::Vine::Client::IsClientConnected::Fn("cocoon-main") {
3190 Ok(Value::Array(Vec::new()))
3191 } else {
3192 Ok(::Vine::Client::SendRequest::Fn("cocoon-main", command.clone(), Payload, 5_000)
3193 .await
3194 .unwrap_or(Value::Array(Vec::new())))
3195 }
3196 },
3197
3198 "language:prepareCallHierarchy"
3208 | "language:provideCallHierarchyIncomingCalls"
3209 | "language:provideCallHierarchyOutgoingCalls" => {
3210 forward_to_cocoon!("language", command, Arguments)
3211 },
3212
3213 "language:prepareTypeHierarchy"
3217 | "language:provideTypeHierarchySupertypes"
3218 | "language:provideTypeHierarchySubtypes" => {
3219 forward_to_cocoon!("language", command, Arguments)
3220 },
3221
3222 "language:provideLinkedEditingRanges" => {
3226 forward_to_cocoon!("language", command, Arguments)
3227 },
3228
3229 "scm:createSourceControl" | "scm:getSourceControls" | "scm:setActiveProvider" => {
3233 forward_to_cocoon!("scm", command, Arguments)
3234 },
3235
3236 "debug:startDebugging"
3247 | "debug:stopDebugging"
3248 | "debug:getSessions"
3249 | "debug:getBreakpoints"
3250 | "debug:addBreakpoints"
3251 | "debug:removeBreakpoints" => {
3252 let _ = TIER_DEBUG;
3253
3254 forward_to_cocoon!("debug", command, Arguments)
3255 },
3256
3257 "tasks:executeTask" | "tasks:getTasks" | "tasks:getTaskExecution" => {
3261 forward_to_cocoon!("tasks", command, Arguments)
3262 },
3263
3264 "auth:getSessions" | "auth:createSession" | "auth:removeSession" => {
3268 forward_to_cocoon!("auth", command, Arguments)
3269 },
3270
3271 _ => {
3284 use std::str::FromStr;
3285
3286 let TierIPC = std::env::var("TierIPC").unwrap_or_else(|_| "Mountain".into());
3291
3292 let ShouldDefer = TierIPC == "NodeDeferred" || TierIPC == "Node";
3293
3294 if ShouldDefer {
3295 let Payload = cocoon_payload(Arguments);
3299
3300 dev_log!("ipc", "deferred → Cocoon: {}", command);
3301
3302 let _ = ::Vine::Client::WaitForClientConnection::Fn("cocoon-main", 3000).await;
3303
3304 match ::Vine::Client::SendRequest::Fn("cocoon-main", command.clone(), Payload, 15_000).await {
3305 Ok(Response) => Ok(Response),
3306 Err(CocoonError) => {
3307 dev_log!(
3308 "ipc",
3309 "warn: [NodeDeferred] {} deferred but Cocoon rejected: {:?}",
3310 command,
3311 CocoonError
3312 );
3313
3314 Ok(Value::Null)
3315 },
3316 }
3317 } else {
3318 match CommonLibrary::IPC::Channel::Channel::from_str(&command) {
3319 Ok(KnownChannel) => {
3320 dev_log!(
3321 "ipc",
3322 "error: [WindServiceHandlers] Channel {:?} is registered but has no dispatch arm",
3323 KnownChannel
3324 );
3325
3326 Err(format!("IPC channel registered but unimplemented: {}", command))
3327 },
3328 Err(_) => {
3329 dev_log!("ipc", "error: [WindServiceHandlers] Unknown IPC command: {}", command);
3330
3331 Err(format!("Unknown IPC command: {}", command))
3332 },
3333 }
3334 }
3335 },
3336 };
3337
3338 if ResultSender.send(MatchResult).is_err() {
3339 dev_log!(
3340 "ipc",
3341 "warn: [WindServiceHandlers] IPC result receiver dropped before dispatch completed"
3342 );
3343 }
3344 },
3345 CommandPriority,
3346 );
3347
3348 let Result = match ResultReceiver.await {
3349 Ok(Dispatched) => Dispatched,
3350
3351 Err(_) => {
3352 dev_log!(
3353 "ipc",
3354 "error: [WindServiceHandlers] IPC task cancelled before producing a result"
3355 );
3356
3357 Err("IPC task cancelled before result was produced".to_string())
3358 },
3359 };
3360
3361 if !IsHighFrequencyCommand {
3365 let IsErr = Result.is_err();
3366
3367 let SpanName = if IsErr {
3368 format!("land:mountain:ipc:{}:error", command)
3369 } else {
3370 format!("land:mountain:ipc:{}", command)
3371 };
3372
3373 crate::otel_span!(&SpanName, OTLPStart, &[("ipc.command", command.as_str())]);
3374
3375 let HandlerElapsedNanos = crate::IPC::DevLog::NowNano::Fn().saturating_sub(OTLPStart);
3379
3380 let HandlerDurationMs = HandlerElapsedNanos / 1_000_000;
3381
3382 crate::Binary::Build::PostHogPlugin::CaptureHandler::Fn(&command, HandlerDurationMs, !IsErr);
3383 }
3384
3385 if !IsHighFrequencyCommand {
3394 let ElapsedNanos = crate::IPC::DevLog::NowNano::Fn().saturating_sub(OTLPStart);
3395
3396 dev_log!("ipc", "done: {} ok={} t_ns={}", command, !Result.is_err(), ElapsedNanos);
3397 }
3398
3399 Result
3400}
3401
3402pub fn register_wind_ipc_handlers(ApplicationHandle:&tauri::AppHandle) -> Result<(), String> {
3403 dev_log!("lifecycle", "registering IPC handlers");
3404
3405 Ok(())
3409}