1#![allow(non_snake_case, unused_variables, dead_code, unused_imports)]
2
3pub mod Commands;
7
8pub mod Configuration;
9
10pub mod Extension;
11
12pub mod Extensions;
13
14pub mod FileSystem;
15
16pub mod Git;
17
18pub mod Model;
19
20pub mod NativeDialog;
21
22pub mod NativeHost;
23
24pub mod Navigation;
25
26pub mod Output;
27
28pub mod Search;
29
30pub mod Storage;
31
32pub mod Terminal;
33
34pub mod UI;
35
36pub mod Utilities;
37
38use std::{collections::HashMap, path::PathBuf, sync::Arc};
44
45use Commands::*;
46use Configuration::*;
47use Extensions::{
48 ExtensionsGet::ExtensionsGet,
49 ExtensionsGetAll::ExtensionsGetAll,
50 ExtensionsGetInstalled::ExtensionsGetInstalled,
51 ExtensionsIsActive::ExtensionsIsActive,
52};
53use FileSystem::{
54 Managed::{
55 FileCopy::*,
56 FileDelete::*,
57 FileExists::*,
58 FileMkdir::*,
59 FileMove::*,
60 FileRead::*,
61 FileReadBinary::*,
62 FileReaddir::*,
63 FileStat::*,
64 FileWrite::*,
65 FileWriteBinary::*,
66 },
67 Native::{
68 FileCloneNative::*,
69 FileDeleteNative::*,
70 FileExistsNative::*,
71 FileMkdirNative::*,
72 FileReadNative::*,
73 FileReaddirNative::*,
74 FileRealpath::*,
75 FileRenameNative::*,
76 FileStatNative::*,
77 FileWriteNative::*,
78 },
79};
80use Model::{
81 ModelClose::ModelClose,
82 ModelGet::ModelGet,
83 ModelGetAll::ModelGetAll,
84 ModelOpen::ModelOpen,
85 ModelUpdateContent::ModelUpdateContent,
86 TextfileRead::TextfileRead,
87 TextfileSave::TextfileSave,
88 TextfileWrite::TextfileWrite,
89};
90use NativeHost::{
91 FindFreePort::*,
92 GetColorScheme::*,
93 IsFullscreen::*,
94 IsMaximized::*,
95 OSProperties::*,
96 OSStatistics::*,
97 OpenExternal::*,
98 PickFolder::*,
99 ShowItemInFolder::*,
100 ShowOpenDialog::*,
101};
102use Navigation::{
103 HistoryCanGoBack::HistoryCanGoBack,
104 HistoryCanGoForward::HistoryCanGoForward,
105 HistoryClear::HistoryClear,
106 HistoryGetStack::HistoryGetStack,
107 HistoryGoBack::HistoryGoBack,
108 HistoryGoForward::HistoryGoForward,
109 HistoryPush::HistoryPush,
110 LabelGetBase::LabelGetBase,
111 LabelGetURI::LabelGetURI,
112 LabelGetWorkspace::LabelGetWorkspace,
113};
114use Output::{
115 OutputAppend::OutputAppend,
116 OutputAppendLine::OutputAppendLine,
117 OutputClear::OutputClear,
118 OutputCreate::OutputCreate,
119 OutputShow::OutputShow,
120};
121use Search::*;
122use Storage::{
123 StorageDelete::StorageDelete,
124 StorageGet::StorageGet,
125 StorageGetItems::StorageGetItems,
126 StorageKeys::StorageKeys,
127 StorageSet::StorageSet,
128 StorageUpdateItems::StorageUpdateItems,
129};
130use Terminal::{
131 LocalPTYGetDefaultShell::LocalPTYGetDefaultShell,
132 LocalPTYGetEnvironment::LocalPTYGetEnvironment,
133 LocalPTYGetProfiles::LocalPTYGetProfiles,
134 TerminalCreate::TerminalCreate,
135 TerminalDispose::TerminalDispose,
136 TerminalHide::TerminalHide,
137 TerminalSendText::TerminalSendText,
138 TerminalShow::TerminalShow,
139};
140use UI::{
141 Decoration::*,
142 Keybinding::*,
143 Lifecycle::*,
144 Notification::*,
145 Progress::*,
146 QuickInput::*,
147 Theme::*,
148 WorkingCopy::*,
149 Workspace::*,
150};
151use Utilities::{
152 ApplicationRoot::*,
153 ChannelPriority::*,
154 JsonValueHelpers::*,
155 MetadataEncoding::*,
156 PathExtraction::*,
157 RecentlyOpened::*,
158 UserdataDir::*,
159};
160use Echo::Task::Priority::Priority as EchoPriority;
161use serde_json::{Value, json};
162use tauri::{AppHandle, Manager};
163use CommonLibrary::Configuration::DTO::{
165 ConfigurationOverridesDTO as ConfigurationOverridesDTOModule,
166 ConfigurationTarget as ConfigurationTargetModule,
167};
168
169use crate::dev_log;
170
171type ConfigurationOverridesDTO = ConfigurationOverridesDTOModule::ConfigurationOverridesDTO;
172
173type ConfigurationTarget = ConfigurationTargetModule::ConfigurationTarget;
174
175use CommonLibrary::{
176 Command::CommandExecutor::CommandExecutor,
177 Configuration::ConfigurationProvider::ConfigurationProvider,
178 Environment::Requires::Requires,
179 Error::CommonError::CommonError,
180 ExtensionManagement::ExtensionManagementService::ExtensionManagementService,
181 FileSystem::{FileSystemReader::FileSystemReader, FileSystemWriter::FileSystemWriter},
182 IPC::SkyEvent::SkyEvent,
183 Storage::StorageProvider::StorageProvider,
184};
185
186use crate::{
187 ApplicationState::{
188 DTO::WorkspaceFolderStateDTO::WorkspaceFolderStateDTO,
189 State::{
190 ApplicationState::ApplicationState,
191 WorkspaceState::WorkspaceDelta::UpdateWorkspaceFoldersAndBroadcast,
192 },
193 },
194 RunTime::ApplicationRunTime::ApplicationRunTime,
195};
196
197pub async fn mountain_ipc_invoke(
214 ApplicationHandle:AppHandle,
215
216 command:String,
217
218 Arguments:Vec<Value>,
219) -> Result<Value, String> {
220 let IsHighFrequencyCommand = matches!(
223 command.as_str(),
224 "logger:log"
225 | "logger:registerLogger"
226 | "logger:createLogger"
227 | "log:registerLogger"
228 | "log:createLogger"
229 | "file:stat"
230 | "file:readFile"
231 | "file:readdir"
232 | "file:writeFile"
233 | "file:delete"
234 | "file:rename"
235 | "file:realpath"
236 | "file:read"
237 | "file:write"
238 | "storage:getItems"
239 | "storage:updateItems"
240 | "configuration:lookup"
241 | "configuration:inspect"
242 | "themes:getColorTheme"
243 | "output:append"
244 | "progress:report"
245 );
246
247 let OTLPStart = if IsHighFrequencyCommand { 0 } else { crate::IPC::DevLog::NowNano::Fn() };
248
249 if !IsHighFrequencyCommand {
257 dev_log!("ipc", "invoke: {} args_count={}", command, Arguments.len());
258 }
259
260 ensure_userdata_dirs();
262
263 let RunTime:Arc<ApplicationRunTime> = ApplicationHandle.state::<Arc<ApplicationRunTime>>().inner().clone();
267
268 if IsHighFrequencyCommand {
274 match command.as_str() {
275 "logger:log" | "logger:warn" | "logger:error" | "logger:info"
277 | "logger:debug" | "logger:trace" | "logger:critical"
278 | "logger:flush" | "logger:setLevel" | "logger:getLevel"
279 | "logger:createLogger" | "logger:registerLogger"
280 | "logger:deregisterLogger" | "logger:getRegisteredLoggers"
281 | "logger:setVisibility"
282 | "file:watch" | "file:unwatch"
284 | "storage:onDidChangeItems" | "storage:logStorage"
286 | "commands:registerCommand" | "commands:unregisterCommand"
288 | "commands:onDidRegisterCommand" | "commands:onDidExecuteCommand"
289 | "configuration:onDidChange"
291 | "storage:optimize" | "storage:isUsed" | "storage:close" => {
293 let Elapsed = crate::IPC::DevLog::NowNano::Fn().saturating_sub(OTLPStart);
294 dev_log!("ipc", "done: {} ok=true t_ns={}", command, Elapsed);
295 return Ok(Value::Null);
296 },
297 _ => {}, }
299 }
300
301 let CommandPriority = ResolveChannelPriority(&command);
321
322 let Scheduler = RunTime.Scheduler.clone();
323
324 let (ResultSender, ResultReceiver) = tokio::sync::oneshot::channel::<Result<Value, String>>();
325
326 let DispatchAppHandle = ApplicationHandle.clone();
327
328 let DispatchRuntime = RunTime.clone();
329
330 let DispatchCommand = command.clone();
331
332 let DispatchArgs = Arguments;
333
334 Scheduler.Submit(
335 async move {
336 let ApplicationHandle = DispatchAppHandle;
337 let RunTime = DispatchRuntime;
338 let command = DispatchCommand;
339 let Arguments = DispatchArgs;
340
341 let MatchResult = match command.as_str() {
342 "configuration:get" | "configuration:getValue" => {
348 dev_log!("config", "{}", command);
349 ConfigurationGet(RunTime.clone(), Arguments).await
350 },
351 "configuration:update" | "configuration:updateValue" => {
352 dev_log!("config", "{}", command);
353 ConfigurationUpdate(RunTime.clone(), Arguments).await
354 },
355 "configuration:onDidChange" => Ok(Value::Null),
361
362 "logger:log"
364 | "logger:warn"
365 | "logger:error"
366 | "logger:info"
367 | "logger:debug"
368 | "logger:trace"
369 | "logger:critical"
370 | "logger:flush"
371 | "logger:setLevel"
372 | "logger:getLevel"
373 | "logger:createLogger"
374 | "logger:registerLogger"
375 | "logger:deregisterLogger"
376 | "logger:getRegisteredLoggers"
377 | "logger:setVisibility" => Ok(Value::Null),
378
379 "file:read" | "file:readFile" => FileReadNative(Arguments).await,
391 "file:write" | "file:writeFile" => FileWriteNative(Arguments).await,
392 "file:stat" => FileStatNative(Arguments).await,
393 "file:exists" => FileExistsNative(Arguments).await,
394 "file:delete" => FileDeleteNative(Arguments).await,
395 "file:copy" => FileCloneNative(Arguments).await,
396 "file:move" | "file:rename" => FileRenameNative(Arguments).await,
397 "file:mkdir" => FileMkdirNative(Arguments).await,
398 "file:readdir" => FileReaddirNative(Arguments).await,
399 "file:readBinary" => FileReadBinary(RunTime.clone(), Arguments).await,
400 "file:writeBinary" => FileWriteBinary(RunTime.clone(), Arguments).await,
401 "file:watch" | "file:unwatch" => {
408 dev_log!("fs-route", "{} (stub-ack)", command);
409 Ok(Value::Null)
410 },
411
412 "storage:get" => StorageGet(RunTime.clone(), Arguments).await,
419 "storage:set" => StorageSet(RunTime.clone(), Arguments).await,
420 "storage:getItems" => {
421 dev_log!("storage-verbose", "storage:getItems");
425 StorageGetItems(RunTime.clone(), Arguments).await
426 },
427 "storage:updateItems" => {
428 dev_log!("storage-verbose", "storage:updateItems");
429 StorageUpdateItems(RunTime.clone(), Arguments).await
430 },
431 "storage:optimize" => {
432 dev_log!("storage", "storage:optimize");
433 Ok(Value::Null)
434 },
435 "storage:isUsed" => {
436 dev_log!("storage", "storage:isUsed");
437 Ok(Value::Null)
438 },
439 "storage:close" => {
440 dev_log!("storage", "storage:close");
441 Ok(Value::Null)
442 },
443 "storage:onDidChangeItems" | "storage:logStorage" => {
447 dev_log!("storage-verbose", "{} (stub-ack)", command);
448 Ok(Value::Null)
449 },
450
451 "environment:get" => {
453 dev_log!("config", "environment:get");
454 EnvironmentGet(RunTime.clone(), Arguments).await
455 },
456
457 "native:showItemInFolder" => ShowItemInFolder(RunTime.clone(), Arguments).await,
459 "native:openExternal" => OpenExternal(RunTime.clone(), Arguments).await,
460
461 "workbench:getConfiguration" => WorkbenchConfiguration(RunTime.clone(), Arguments).await,
463
464 "diagnostic:log" => {
470 let Tag = Arguments.first().and_then(|V| V.as_str()).unwrap_or("webview").to_string();
471 let Message = Arguments.get(1).and_then(|V| V.as_str()).unwrap_or("").to_string();
472 let Extras = if Arguments.len() > 2 {
473 let Tail:Vec<String> = Arguments
474 .iter()
475 .skip(2)
476 .map(|V| {
477 let S = serde_json::to_string(V).unwrap_or_default();
478 if S.len() > 240 {
484 let CutAt = S
485 .char_indices()
486 .map(|(Index, _)| Index)
487 .take_while(|Index| *Index <= 240)
488 .last()
489 .unwrap_or(0);
490 format!("{}…", &S[..CutAt])
491 } else {
492 S
493 }
494 })
495 .collect();
496 format!(" {}", Tail.join(" "))
497 } else {
498 String::new()
499 };
500 dev_log!("diagnostic", "[{}] {}{}", Tag, Message, Extras);
501 Ok(Value::Null)
502 },
503
504 "commands:execute" | "commands:executeCommand" => CommandsExecute(RunTime.clone(), Arguments).await,
509 "commands:getAll" | "commands:getCommands" => {
510 dev_log!("commands", "{}", command);
511 CommandsGetAll(RunTime.clone()).await
512 },
513 "commands:registerCommand"
518 | "commands:unregisterCommand"
519 | "commands:onDidRegisterCommand"
520 | "commands:onDidExecuteCommand" => Ok(Value::Null),
521
522 "extensions:getAll" => {
524 dev_log!("extensions", "extensions:getAll");
525 ExtensionsGetAll(RunTime.clone()).await
526 },
527 "extensions:get" => {
528 dev_log!("extensions", "extensions:get");
529 ExtensionsGet(RunTime.clone(), Arguments).await
530 },
531 "extensions:isActive" => {
532 dev_log!("extensions", "extensions:isActive");
533 ExtensionsIsActive(RunTime.clone(), Arguments).await
534 },
535
536 "extensions:getInstalled" | "extensions:scanSystemExtensions" => {
549 let ArgsSummary = Arguments
555 .iter()
556 .enumerate()
557 .map(|(Idx, V)| {
558 let Preview = serde_json::to_string(V).unwrap_or_default();
559 let Trimmed = if Preview.len() > 180 {
562 let CutAt = Preview
563 .char_indices()
564 .map(|(Index, _)| Index)
565 .take_while(|Index| *Index <= 180)
566 .last()
567 .unwrap_or(0);
568 format!("{}…", &Preview[..CutAt])
569 } else {
570 Preview
571 };
572 format!("[{}]={}", Idx, Trimmed)
573 })
574 .collect::<Vec<_>>()
575 .join(" ");
576 dev_log!("extensions", "{} Arguments={}", command, ArgsSummary);
577 let EffectiveArgs = if command == "extensions:scanSystemExtensions" {
586 let mut Overridden = Arguments.clone();
587 if Overridden.is_empty() {
588 Overridden.push(Value::Null);
589 }
590 Overridden[0] = json!(0);
591 Overridden
592 } else {
593 Arguments.clone()
594 };
595 ExtensionsGetInstalled(RunTime.clone(), EffectiveArgs).await
596 },
597 "extensions:scanUserExtensions" => {
598 dev_log!("extensions", "{} (forwarded to getInstalled with type=User)", command);
607 let mut UserArgs = Arguments.clone();
608 if UserArgs.is_empty() {
609 UserArgs.push(Value::Null);
610 }
611 UserArgs[0] = json!(1);
612 ExtensionsGetInstalled(RunTime.clone(), UserArgs).await
613 },
614 "extensions:getUninstalled" => {
615 dev_log!("extensions", "{} (returning [])", command);
619 Ok(Value::Array(Vec::new()))
620 },
621 "extensions:query" | "extensions:getExtensions" | "extensions:getRecommendations" => {
625 dev_log!("extensions", "{} (offline gallery - returning [])", command);
626 Ok(Value::Array(Vec::new()))
627 },
628 "extensions:getExtensionsControlManifest" => {
634 dev_log!("extensions", "{} (offline gallery - empty manifest)", command);
635 Ok(json!({
636 "malicious": [],
637 "deprecated": {},
638 "search": [],
639 "autoUpdate": {},
640 }))
641 },
642 "extensions:resetPinnedStateForAllUserExtensions" => {
649 dev_log!("extensions", "{} (no-op, pin state is UI-local)", command);
650 Ok(Value::Null)
651 },
652 "extensions:install" => {
660 Extension::ExtensionInstall::ExtensionInstall(ApplicationHandle.clone(), RunTime.clone(), Arguments)
661 .await
662 },
663 "extensions:uninstall" => {
664 Extension::ExtensionUninstall::ExtensionUninstall(
665 ApplicationHandle.clone(),
666 RunTime.clone(),
667 Arguments,
668 )
669 .await
670 },
671
672 "extensions:getManifest" => {
681 let VsixPath = match Arguments.first() {
682 Some(serde_json::Value::String(Path)) => Path.clone(),
683 Some(Obj) => {
684 Obj.get("fsPath")
685 .and_then(|V| V.as_str())
686 .map(str::to_owned)
687 .or_else(|| Obj.get("path").and_then(|V| V.as_str()).map(str::to_owned))
688 .unwrap_or_default()
689 },
690 None => String::new(),
691 };
692 dev_log!("extensions", "extensions:getManifest vsix={}", VsixPath);
693 if VsixPath.is_empty() {
694 Err("extensions:getManifest: missing VSIX path argument".to_string())
695 } else {
696 let Path = std::path::PathBuf::from(&VsixPath);
697 match crate::ExtensionManagement::VsixInstaller::ReadFullManifest(&Path) {
698 Ok(Manifest) => Ok(Manifest),
699 Err(Error) => {
700 dev_log!(
701 "extensions",
702 "warn: [WindServiceHandlers] extensions:getManifest failed for '{}': {}",
703 VsixPath,
704 Error
705 );
706 Err(format!("extensions:getManifest failed: {}", Error))
707 },
708 }
709 }
710 },
711 "extensions:reinstall" | "extensions:updateMetadata" => {
716 dev_log!("extensions", "{} (no-op: no gallery backend)", command);
717 Ok(Value::Null)
718 },
719
720 "terminal:create" => {
722 dev_log!("terminal", "terminal:create");
723 TerminalCreate(RunTime.clone(), Arguments).await
724 },
725 "terminal:sendText" => {
726 dev_log!("terminal", "terminal:sendText");
727 TerminalSendText(RunTime.clone(), Arguments).await
728 },
729 "terminal:dispose" => {
730 dev_log!("terminal", "terminal:dispose");
731 TerminalDispose(RunTime.clone(), Arguments).await
732 },
733 "terminal:show" => {
734 dev_log!("terminal", "terminal:show");
735 TerminalShow(RunTime.clone(), Arguments).await
736 },
737 "terminal:hide" => {
738 dev_log!("terminal", "terminal:hide");
739 TerminalHide(RunTime.clone(), Arguments).await
740 },
741
742 "output:create" => OutputCreate(ApplicationHandle.clone(), Arguments).await,
744 "output:append" => {
745 dev_log!("output", "output:append");
746 OutputAppend(ApplicationHandle.clone(), Arguments).await
747 },
748 "output:appendLine" => {
749 dev_log!("output", "output:appendLine");
750 OutputAppendLine(ApplicationHandle.clone(), Arguments).await
751 },
752 "output:clear" => {
753 dev_log!("output", "output:clear");
754 OutputClear(ApplicationHandle.clone(), Arguments).await
755 },
756 "output:show" => {
757 dev_log!("output", "output:show");
758 OutputShow(ApplicationHandle.clone(), Arguments).await
759 },
760
761 "textFile:read" => {
763 dev_log!("textfile", "textFile:read");
764 TextfileRead(RunTime.clone(), Arguments).await
765 },
766 "textFile:write" => {
767 dev_log!("textfile", "textFile:write");
768 TextfileWrite(RunTime.clone(), Arguments).await
769 },
770 "textFile:save" => TextfileSave(RunTime.clone(), Arguments).await,
771
772 "storage:delete" => {
774 dev_log!("storage", "storage:delete");
775 StorageDelete(RunTime.clone(), Arguments).await
776 },
777 "storage:keys" => {
778 dev_log!("storage", "storage:keys");
779 StorageKeys(RunTime.clone()).await
780 },
781
782 "notification:show" => {
784 dev_log!("notification", "notification:show");
785 NotificationShow(ApplicationHandle.clone(), Arguments).await
786 },
787 "notification:showProgress" => {
788 dev_log!("notification", "notification:showProgress");
789 NotificationShowProgress(ApplicationHandle.clone(), Arguments).await
790 },
791 "notification:updateProgress" => {
792 dev_log!("notification", "notification:updateProgress");
793 NotificationUpdateProgress(ApplicationHandle.clone(), Arguments).await
794 },
795 "notification:endProgress" => {
796 dev_log!("notification", "notification:endProgress");
797 NotificationEndProgress(ApplicationHandle.clone(), Arguments).await
798 },
799
800 "progress:begin" => {
802 dev_log!("progress", "progress:begin");
803 ProgressBegin(ApplicationHandle.clone(), Arguments).await
804 },
805 "progress:report" => {
806 dev_log!("progress", "progress:report");
807 ProgressReport(ApplicationHandle.clone(), Arguments).await
808 },
809 "progress:end" => {
810 dev_log!("progress", "progress:end");
811 ProgressEnd(ApplicationHandle.clone(), Arguments).await
812 },
813
814 "quickInput:showQuickPick" => {
816 dev_log!("quickinput", "quickInput:showQuickPick");
817 QuickInputShowQuickPick(RunTime.clone(), Arguments).await
818 },
819 "quickInput:showInputBox" => {
820 dev_log!("quickinput", "quickInput:showInputBox");
821 QuickInputShowInputBox(RunTime.clone(), Arguments).await
822 },
823
824 "workspaces:getFolders" | "workspaces:getWorkspaceFolders" | "workspaces:getWorkspace" => {
829 dev_log!("workspaces", "{}", command);
830 WorkspacesGetFolders(RunTime.clone()).await
831 },
832 "workspaces:addFolder" | "workspaces:addWorkspaceFolders" => {
833 dev_log!("workspaces", "{}", command);
834 WorkspacesAddFolder(RunTime.clone(), Arguments).await
835 },
836 "workspaces:removeFolder" | "workspaces:removeWorkspaceFolders" => {
837 dev_log!("workspaces", "{}", command);
838 WorkspacesRemoveFolder(RunTime.clone(), Arguments).await
839 },
840 "workspaces:getName" => {
841 dev_log!("workspaces", "{}", command);
842 WorkspacesGetName(RunTime.clone()).await
843 },
844 "workspaces:onDidChangeWorkspaceFolders" | "workspaces:onDidChangeWorkspaceName" => {
848 dev_log!("workspaces", "{} (stub-ack)", command);
849 Ok(Value::Null)
850 },
851
852 "themes:getActive" => {
854 dev_log!("themes", "themes:getActive");
855 ThemesGetActive(RunTime.clone()).await
856 },
857 "themes:list" => {
858 dev_log!("themes", "themes:list");
859 ThemesList(RunTime.clone()).await
860 },
861 "themes:set" => {
862 dev_log!("themes", "themes:set");
863 ThemesSet(RunTime.clone(), Arguments).await
864 },
865
866 "search:findInFiles" | "search:textSearch" | "search:searchText" => {
870 dev_log!("search", "{}", command);
871 SearchFindInFiles(RunTime.clone(), Arguments).await
872 },
873 "search:findFiles" | "search:fileSearch" | "search:searchFile" => {
874 dev_log!("search", "{}", command);
875 SearchFindFiles(RunTime.clone(), Arguments).await
876 },
877 "search:cancel" | "search:clearCache" | "search:onDidChangeResult" => {
882 dev_log!("search", "{} (stub-ack)", command);
883 Ok(Value::Null)
884 },
885
886 "decorations:get" => {
888 dev_log!("decorations", "decorations:get");
889 DecorationsGet(RunTime.clone(), Arguments).await
890 },
891 "decorations:getMany" => {
892 dev_log!("decorations", "decorations:getMany");
893 DecorationsGetMany(RunTime.clone(), Arguments).await
894 },
895 "decorations:set" => {
896 dev_log!("decorations", "decorations:set");
897 DecorationsSet(RunTime.clone(), Arguments).await
898 },
899 "decorations:clear" => {
900 dev_log!("decorations", "decorations:clear");
901 DecorationsClear(RunTime.clone(), Arguments).await
902 },
903
904 "workingCopy:isDirty" => {
906 dev_log!("workingcopy", "workingCopy:isDirty");
907 WorkingCopyIsDirty(RunTime.clone(), Arguments).await
908 },
909 "workingCopy:setDirty" => {
910 dev_log!("workingcopy", "workingCopy:setDirty");
911 WorkingCopySetDirty(RunTime.clone(), Arguments).await
912 },
913 "workingCopy:getAllDirty" => {
914 dev_log!("workingcopy", "workingCopy:getAllDirty");
915 WorkingCopyGetAllDirty(RunTime.clone()).await
916 },
917 "workingCopy:getDirtyCount" => {
918 dev_log!("workingcopy", "workingCopy:getDirtyCount");
919 WorkingCopyGetDirtyCount(RunTime.clone()).await
920 },
921
922 "keybinding:add" => {
924 dev_log!("keybinding", "keybinding:add");
925 KeybindingAdd(RunTime.clone(), Arguments).await
926 },
927 "keybinding:remove" => {
928 dev_log!("keybinding", "keybinding:remove");
929 KeybindingRemove(RunTime.clone(), Arguments).await
930 },
931 "keybinding:lookup" => {
932 dev_log!("keybinding", "keybinding:lookup");
933 KeybindingLookup(RunTime.clone(), Arguments).await
934 },
935 "keybinding:getAll" => {
936 dev_log!("keybinding", "keybinding:getAll");
937 KeybindingGetAll(RunTime.clone()).await
938 },
939
940 "lifecycle:getPhase" => {
942 dev_log!("lifecycle", "lifecycle:getPhase");
943 LifecycleGetPhase(RunTime.clone()).await
944 },
945 "lifecycle:whenPhase" => {
946 dev_log!("lifecycle", "lifecycle:whenPhase");
947 LifecycleWhenPhase(RunTime.clone(), Arguments).await
948 },
949 "lifecycle:requestShutdown" => {
950 dev_log!("lifecycle", "lifecycle:requestShutdown");
951 LifecycleRequestShutdown(ApplicationHandle.clone()).await
952 },
953 "lifecycle:advancePhase" | "lifecycle:setPhase" => {
954 dev_log!("lifecycle", "{}", command);
955 let NewPhase = Arguments.first().and_then(|V| V.as_u64()).unwrap_or(1) as u8;
960 RunTime
961 .Environment
962 .ApplicationState
963 .Feature
964 .Lifecycle
965 .AdvanceAndBroadcast(NewPhase, &ApplicationHandle);
966
967 if NewPhase >= 3 {
981 if let Some(MainWindow) = ApplicationHandle.get_webview_window("main") {
982 if let Ok(false) = MainWindow.is_visible() {
983 if let Err(Error) = MainWindow.show() {
984 dev_log!(
985 "lifecycle",
986 "warn: [Lifecycle] main window show() failed on phase {}: {}",
987 NewPhase,
988 Error
989 );
990 } else {
991 dev_log!(
992 "lifecycle",
993 "[Lifecycle] main window revealed on phase {} (hidden-until-ready)",
994 NewPhase
995 );
996 let _ = MainWindow.set_focus();
997 }
998 }
999 }
1000 }
1001
1002 Ok(json!(RunTime.Environment.ApplicationState.Feature.Lifecycle.GetPhase()))
1003 },
1004
1005 "label:getUri" => {
1007 dev_log!("label", "label:getUri");
1008 LabelGetURI(RunTime.clone(), Arguments).await
1009 },
1010 "label:getWorkspace" => {
1011 dev_log!("label", "label:getWorkspace");
1012 LabelGetWorkspace(RunTime.clone()).await
1013 },
1014 "label:getBase" => {
1015 dev_log!("label", "label:getBase");
1016 LabelGetBase(Arguments).await
1017 },
1018
1019 "model:open" => {
1021 dev_log!("model", "model:open");
1022 ModelOpen(RunTime.clone(), Arguments).await
1023 },
1024 "model:close" => {
1025 dev_log!("model", "model:close");
1026 ModelClose(RunTime.clone(), Arguments).await
1027 },
1028 "model:get" => {
1029 dev_log!("model", "model:get");
1030 ModelGet(RunTime.clone(), Arguments).await
1031 },
1032 "model:getAll" => {
1033 dev_log!("model", "model:getAll");
1034 ModelGetAll(RunTime.clone()).await
1035 },
1036 "model:updateContent" => {
1037 dev_log!("model", "model:updateContent");
1038 ModelUpdateContent(RunTime.clone(), Arguments).await
1039 },
1040
1041 "history:goBack" => {
1043 dev_log!("history", "history:goBack");
1044 HistoryGoBack(RunTime.clone()).await
1045 },
1046 "history:goForward" => {
1047 dev_log!("history", "history:goForward");
1048 HistoryGoForward(RunTime.clone()).await
1049 },
1050 "history:canGoBack" => {
1051 dev_log!("history", "history:canGoBack");
1052 HistoryCanGoBack(RunTime.clone()).await
1053 },
1054 "history:canGoForward" => {
1055 dev_log!("history", "history:canGoForward");
1056 HistoryCanGoForward(RunTime.clone()).await
1057 },
1058 "history:push" => {
1059 dev_log!("history", "history:push");
1060 HistoryPush(RunTime.clone(), Arguments).await
1061 },
1062 "history:clear" => {
1063 dev_log!("history", "history:clear");
1064 HistoryClear(RunTime.clone()).await
1065 },
1066 "history:getStack" => {
1067 dev_log!("history", "history:getStack");
1068 HistoryGetStack(RunTime.clone()).await
1069 },
1070
1071 "mountain_get_status" => {
1073 let status = json!({
1074 "connected": true,
1075 "version": "1.0.0"
1076 });
1077 Ok(status)
1078 },
1079 "mountain_get_configuration" => {
1080 let config = json!({
1081 "editor": { "theme": "dark" },
1082 "extensions": { "installed": [] }
1083 });
1084 Ok(config)
1085 },
1086 "mountain_get_services_status" => {
1087 let services = json!({
1088 "editor": { "status": "running" },
1089 "extensionHost": { "status": "running" }
1090 });
1091 Ok(services)
1092 },
1093 "mountain_get_state" => {
1094 let state = json!({
1095 "ui": {},
1096 "editor": {},
1097 "workspace": {}
1098 });
1099 Ok(state)
1100 },
1101
1102 "file:realpath" => FileRealpath(Arguments).await,
1108 "file:open" => {
1109 dev_log!("vfs", "file:open stub - no fd support yet");
1110 Ok(json!(0))
1111 },
1112 "file:close" => {
1113 dev_log!("vfs", "file:close stub");
1114 Ok(Value::Null)
1115 },
1116 "file:cloneFile" => FileCloneNative(Arguments).await,
1117
1118 "nativeHost:pickFolderAndOpen" => NativePickFolder(ApplicationHandle.clone(), Arguments).await,
1124 "nativeHost:pickFileAndOpen" => NativePickFolder(ApplicationHandle.clone(), Arguments).await,
1125 "nativeHost:pickFileFolderAndOpen" => NativePickFolder(ApplicationHandle.clone(), Arguments).await,
1126 "nativeHost:pickWorkspaceAndOpen" => NativePickFolder(ApplicationHandle.clone(), Arguments).await,
1127 "nativeHost:showOpenDialog" => NativeShowOpenDialog(ApplicationHandle.clone(), Arguments).await,
1128 "nativeHost:showSaveDialog" => {
1129 use tauri_plugin_dialog::DialogExt;
1130 let Options = Arguments.first().cloned().unwrap_or(Value::Null);
1131 let Title = Options.get("title").and_then(Value::as_str).unwrap_or("Save").to_string();
1132 let DefaultPath = Options.get("defaultPath").and_then(Value::as_str).map(str::to_string);
1133 let Handle = ApplicationHandle.clone();
1134 let Joined = tokio::task::spawn_blocking(move || -> Option<String> {
1135 let mut Builder = Handle.dialog().file().set_title(&Title);
1136 if let Some(Path) = DefaultPath.as_deref() {
1137 Builder = Builder.set_directory(Path);
1138 }
1139 Builder.blocking_save_file().map(|P| P.to_string())
1140 })
1141 .await;
1142 match Joined {
1143 Ok(Some(Path)) => Ok(json!({ "canceled": false, "filePath": Path })),
1144 Ok(None) => Ok(json!({ "canceled": true })),
1145 Err(Error) => Err(format!("showSaveDialog join error: {}", Error)),
1146 }
1147 },
1148 "nativeHost:showMessageBox" => {
1149 use tauri_plugin_dialog::{DialogExt, MessageDialogKind};
1150 let Options = Arguments.first().cloned().unwrap_or(Value::Null);
1151 let Message = Options.get("message").and_then(Value::as_str).unwrap_or("").to_string();
1152 let Detail = Options.get("detail").and_then(Value::as_str).map(str::to_string);
1153 let DialogType = Options
1154 .get("type")
1155 .and_then(Value::as_str)
1156 .map(|S| S.to_lowercase())
1157 .unwrap_or_default();
1158 let Title = Options.get("title").and_then(Value::as_str).unwrap_or("").to_string();
1159 let Kind = match DialogType.as_str() {
1160 "warning" | "warn" => MessageDialogKind::Warning,
1161 "error" => MessageDialogKind::Error,
1162 _ => MessageDialogKind::Info,
1163 };
1164 let Handle = ApplicationHandle.clone();
1165 let Joined = tokio::task::spawn_blocking(move || -> bool {
1166 let mut Builder = Handle.dialog().message(&Message).kind(Kind);
1167 if !Title.is_empty() {
1168 Builder = Builder.title(&Title);
1169 }
1170 if let Some(DetailText) = Detail.as_deref() {
1171 Builder = Builder.title(DetailText);
1172 }
1173 Builder.blocking_show()
1174 })
1175 .await;
1176 match Joined {
1177 Ok(Answered) => Ok(json!({ "response": if Answered { 0 } else { 1 } })),
1178 Err(Error) => Err(format!("showMessageBox join error: {}", Error)),
1179 }
1180 },
1181
1182 "nativeHost:getEnvironmentPaths" => {
1186 let PathResolver = ApplicationHandle.path();
1187 let AppDataDir = PathResolver.app_data_dir().unwrap_or_default();
1188 let HomeDir = PathResolver.home_dir().unwrap_or_default();
1189 let TmpDir = std::env::temp_dir();
1190
1191 let SessionLogRoot = AppDataDir.join("logs").join(crate::IPC::DevLog::SessionTimestamp::Fn());
1199 let SessionLogWindowDir = SessionLogRoot.join("window1");
1200 let _ = std::fs::create_dir_all(&SessionLogWindowDir);
1201
1202 dev_log!(
1203 "config",
1204 "getEnvironmentPaths: userDataDir={} logsPath={} homeDir={}",
1205 AppDataDir.display(),
1206 SessionLogRoot.display(),
1207 HomeDir.display()
1208 );
1209 let DevLogEnv = std::env::var("Trace").unwrap_or_default();
1210 Ok(json!({
1211 "userDataDir": AppDataDir.to_string_lossy(),
1212 "logsPath": SessionLogRoot.to_string_lossy(),
1213 "homeDir": HomeDir.to_string_lossy(),
1214 "tmpDir": TmpDir.to_string_lossy(),
1215 "devLog": if DevLogEnv.is_empty() { Value::Null } else { json!(DevLogEnv) },
1216 }))
1217 },
1218
1219 "nativeHost:getOSColorScheme" => {
1221 dev_log!("nativehost", "nativeHost:getOSColorScheme");
1222 NativeGetColorScheme().await
1223 },
1224 "nativeHost:getOSProperties" => {
1225 dev_log!("nativehost", "nativeHost:getOSProperties");
1226 NativeOSProperties().await
1227 },
1228 "nativeHost:getOSStatistics" => {
1229 dev_log!("nativehost", "nativeHost:getOSStatistics");
1230 NativeOSStatistics().await
1231 },
1232 "nativeHost:getOSVirtualMachineHint" => {
1233 dev_log!("nativehost", "nativeHost:getOSVirtualMachineHint");
1234 Ok(json!(0))
1235 },
1236
1237 "nativeHost:isWindowAlwaysOnTop" => {
1239 dev_log!("window", "nativeHost:isWindowAlwaysOnTop");
1240 Ok(json!(false))
1241 },
1242 "nativeHost:isFullScreen" => {
1243 dev_log!("window", "nativeHost:isFullScreen");
1244 NativeIsFullscreen(ApplicationHandle.clone()).await
1245 },
1246 "nativeHost:isMaximized" => {
1247 dev_log!("window", "nativeHost:isMaximized");
1248 NativeIsMaximized(ApplicationHandle.clone()).await
1249 },
1250 "nativeHost:getActiveWindowId" => {
1251 dev_log!("window", "nativeHost:getActiveWindowId");
1252 Ok(json!(1))
1253 },
1254 "nativeHost:getCursorScreenPoint" => {
1269 dev_log!("window", "nativeHost:getCursorScreenPoint");
1270 Ok(json!({ "x": 0, "y": 0 }))
1271 },
1272 "nativeHost:getWindows" => Ok(json!([{ "id": 1, "title": "Land", "filename": "" }])),
1273 "nativeHost:getWindowCount" => Ok(json!(1)),
1274
1275 "nativeHost:openAgentsWindow" | "nativeHost:openDevToolsWindow" | "nativeHost:openAuxiliaryWindow" => {
1285 dev_log!("window", "{} (acknowledged, no-op - aux window unsupported)", command);
1286 Ok(Value::Null)
1287 },
1288
1289 "nativeHost:focusWindow" => {
1293 dev_log!("window", "{}", command);
1294 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1295 let _ = Window.set_focus();
1296 }
1297 Ok(Value::Null)
1298 },
1299 "nativeHost:maximizeWindow" => {
1300 dev_log!("window", "{}", command);
1301 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1302 let _ = Window.maximize();
1303 }
1304 Ok(Value::Null)
1305 },
1306 "nativeHost:unmaximizeWindow" => {
1307 dev_log!("window", "{}", command);
1308 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1309 let _ = Window.unmaximize();
1310 }
1311 Ok(Value::Null)
1312 },
1313 "nativeHost:minimizeWindow" => {
1314 dev_log!("window", "{}", command);
1315 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1316 let _ = Window.minimize();
1317 }
1318 Ok(Value::Null)
1319 },
1320 "nativeHost:toggleFullScreen" => {
1321 dev_log!("window", "{}", command);
1322 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1323 let IsFullscreen = Window.is_fullscreen().unwrap_or(false);
1324 let _ = Window.set_fullscreen(!IsFullscreen);
1325 }
1326 Ok(Value::Null)
1327 },
1328 "nativeHost:closeWindow" => {
1329 dev_log!("window", "{}", command);
1330 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1336 let _ = Window.destroy();
1337 }
1338 Ok(Value::Null)
1339 },
1340 "nativeHost:setWindowAlwaysOnTop" => {
1341 dev_log!("window", "{}", command);
1342 let OnTop = Arguments.first().and_then(|V| V.as_bool()).unwrap_or(false);
1343 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1344 let _ = Window.set_always_on_top(OnTop);
1345 }
1346 Ok(Value::Null)
1347 },
1348 "nativeHost:toggleWindowAlwaysOnTop" => {
1349 dev_log!("window", "{}", command);
1350 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1356 let _ = Window.set_always_on_top(true);
1357 }
1358 Ok(Value::Null)
1359 },
1360 "nativeHost:setRepresentedFilename" => {
1361 dev_log!("window", "{}", command);
1362 #[cfg(target_os = "macos")]
1363 {
1364 let Path = Arguments.first().and_then(|V| V.as_str()).unwrap_or("").to_string();
1365 if !Path.is_empty() {
1366 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1367 let _ = Window.set_title(&Path);
1368 }
1369 }
1370 }
1371 let _ = (&Arguments, &ApplicationHandle);
1372 Ok(Value::Null)
1373 },
1374
1375 "nativeHost:updateWindowControls"
1380 | "nativeHost:setMinimumSize"
1381 | "nativeHost:notifyReady"
1382 | "nativeHost:saveWindowSplash"
1383 | "nativeHost:updateTouchBar"
1384 | "nativeHost:moveWindowTop"
1385 | "nativeHost:positionWindow"
1386 | "nativeHost:setDocumentEdited"
1387 | "nativeHost:setBackgroundThrottling"
1388 | "nativeHost:updateWindowAccentColor" => {
1389 dev_log!("window", "{}", command);
1390 Ok(Value::Null)
1391 },
1392
1393 "nativeHost:isAdmin" => Ok(json!(false)),
1395 "nativeHost:isRunningUnderARM64Translation" => {
1396 #[cfg(target_os = "macos")]
1397 {
1398 let Output = std::process::Command::new("sysctl")
1400 .args(["-n", "sysctl.proc_translated"])
1401 .output();
1402 let IsTranslated = Output
1403 .ok()
1404 .map(|O| String::from_utf8_lossy(&O.stdout).trim() == "1")
1405 .unwrap_or(false);
1406 Ok(json!(IsTranslated))
1407 }
1408 #[cfg(not(target_os = "macos"))]
1409 {
1410 Ok(json!(false))
1411 }
1412 },
1413 "nativeHost:hasWSLFeatureInstalled" => {
1414 #[cfg(target_os = "windows")]
1415 {
1416 Ok(json!(std::path::Path::new("C:\\Windows\\System32\\wsl.exe").exists()))
1417 }
1418 #[cfg(not(target_os = "windows"))]
1419 {
1420 Ok(json!(false))
1421 }
1422 },
1423 "nativeHost:showItemInFolder" => ShowItemInFolder(RunTime.clone(), Arguments).await,
1424 "nativeHost:openExternal" => OpenExternal(RunTime.clone(), Arguments).await,
1425 "nativeHost:moveItemToTrash" => {
1431 let Path = Arguments.first().and_then(|V| V.as_str()).unwrap_or("").to_string();
1432 if Path.is_empty() {
1433 Ok(json!(false))
1434 } else {
1435 dev_log!("nativehost", "nativeHost:moveItemToTrash path={}", Path);
1436 let Moved = {
1437 #[cfg(target_os = "macos")]
1438 {
1439 tokio::process::Command::new("osascript")
1440 .args([
1441 "-e",
1442 &format!(
1443 "tell application \"Finder\" to delete POSIX file \"{}\"",
1444 Path.replace('"', "\\\"")
1445 ),
1446 ])
1447 .status()
1448 .await
1449 .map(|S| S.success())
1450 .unwrap_or(false)
1451 }
1452 #[cfg(target_os = "linux")]
1453 {
1454 let Gio = tokio::process::Command::new("gio")
1455 .args(["trash", &Path])
1456 .status()
1457 .await
1458 .map(|S| S.success())
1459 .unwrap_or(false);
1460 if Gio {
1461 true
1462 } else {
1463 tokio::process::Command::new("trash")
1464 .arg(&Path)
1465 .status()
1466 .await
1467 .map(|S| S.success())
1468 .unwrap_or(false)
1469 }
1470 }
1471 #[cfg(target_os = "windows")]
1472 {
1473 let Script = format!(
1474 "(new-object -comobject Shell.Application).NameSpace(0xA).MoveHere('{}')",
1475 Path.replace('\'', "''")
1476 );
1477 tokio::process::Command::new("powershell.exe")
1478 .args(["-NoProfile", "-Command", &Script])
1479 .status()
1480 .await
1481 .map(|S| S.success())
1482 .unwrap_or(false)
1483 }
1484 #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
1485 {
1486 false
1487 }
1488 };
1489 Ok(json!(Moved))
1490 }
1491 },
1492
1493 "nativeHost:readClipboardText" => {
1498 dev_log!("clipboard", "readClipboardText");
1499 match arboard::Clipboard::new() {
1500 Ok(mut Cb) => Ok(json!(Cb.get_text().unwrap_or_default())),
1501 Err(_) => Ok(json!("")),
1502 }
1503 },
1504 "nativeHost:writeClipboardText" => {
1505 dev_log!("clipboard", "writeClipboardText");
1506 let Text = Arguments.first().and_then(|V| V.as_str()).unwrap_or("").to_string();
1507 if let Ok(mut Cb) = arboard::Clipboard::new() {
1508 let _ = Cb.set_text(Text);
1509 }
1510 Ok(Value::Null)
1511 },
1512 "nativeHost:readClipboardFindText" => {
1513 dev_log!("clipboard", "readClipboardFindText");
1514 match arboard::Clipboard::new() {
1517 Ok(mut Cb) => Ok(json!(Cb.get_text().unwrap_or_default())),
1518 Err(_) => Ok(json!("")),
1519 }
1520 },
1521 "nativeHost:writeClipboardFindText" => {
1522 dev_log!("clipboard", "writeClipboardFindText");
1523 let Text = Arguments.first().and_then(|V| V.as_str()).unwrap_or("").to_string();
1524 if let Ok(mut Cb) = arboard::Clipboard::new() {
1525 let _ = Cb.set_text(Text);
1526 }
1527 Ok(Value::Null)
1528 },
1529 "nativeHost:readClipboardBuffer" => {
1530 dev_log!("clipboard", "readClipboardBuffer");
1531 Ok(json!([]))
1532 },
1533 "nativeHost:writeClipboardBuffer" => {
1534 dev_log!("clipboard", "writeClipboardBuffer");
1535 Ok(Value::Null)
1536 },
1537 "nativeHost:hasClipboard" => {
1538 dev_log!("clipboard", "hasClipboard");
1539 Ok(json!(false))
1540 },
1541 "nativeHost:readImage" => {
1542 dev_log!("clipboard", "readImage");
1543 Ok(json!([]))
1544 },
1545 "nativeHost:triggerPaste" => {
1546 dev_log!("clipboard", "triggerPaste");
1547 Ok(Value::Null)
1548 },
1549
1550 "nativeHost:getProcessId" => Ok(json!(std::process::id())),
1552 "nativeHost:killProcess" => Ok(Value::Null),
1553
1554 "nativeHost:findFreePort" => NativeFindFreePort(Arguments).await,
1556 "nativeHost:isPortFree" => Ok(json!(true)),
1557 "nativeHost:resolveProxy" => Ok(Value::Null),
1558 "nativeHost:lookupAuthorization" => Ok(Value::Null),
1559 "nativeHost:lookupKerberosAuthorization" => Ok(Value::Null),
1560 "nativeHost:loadCertificates" => Ok(json!([])),
1561
1562 "nativeHost:relaunch" => Ok(Value::Null),
1564 "nativeHost:reload" => Ok(Value::Null),
1565 "nativeHost:quit" => Ok(Value::Null),
1566 "nativeHost:exit" => Ok(Value::Null),
1567
1568 "nativeHost:openDevTools" => Ok(Value::Null),
1570 "nativeHost:toggleDevTools" => Ok(Value::Null),
1571
1572 "nativeHost:getSystemIdleState" => Ok(json!("active")),
1574 "nativeHost:getSystemIdleTime" => Ok(json!(0)),
1575 "nativeHost:getCurrentThermalState" => Ok(json!("nominal")),
1576 "nativeHost:isOnBatteryPower" => Ok(json!(false)),
1577 "nativeHost:startPowerSaveBlocker" => Ok(json!(0)),
1578 "nativeHost:stopPowerSaveBlocker" => Ok(json!(false)),
1579 "nativeHost:isPowerSaveBlockerStarted" => Ok(json!(false)),
1580
1581 "nativeHost:newWindowTab" => Ok(Value::Null),
1583 "nativeHost:showPreviousWindowTab" => Ok(Value::Null),
1584 "nativeHost:showNextWindowTab" => Ok(Value::Null),
1585 "nativeHost:moveWindowTabToNewWindow" => Ok(Value::Null),
1586 "nativeHost:mergeAllWindowTabs" => Ok(Value::Null),
1587 "nativeHost:toggleWindowTabsBar" => Ok(Value::Null),
1588 "nativeHost:installShellCommand" => Ok(Value::Null),
1589 "nativeHost:uninstallShellCommand" => Ok(Value::Null),
1590
1591 "localPty:getProfiles" => {
1595 dev_log!("terminal", "localPty:getProfiles");
1596 LocalPTYGetProfiles().await
1597 },
1598 "localPty:getDefaultSystemShell" => {
1599 dev_log!("terminal", "localPty:getDefaultSystemShell");
1600 LocalPTYGetDefaultShell().await
1601 },
1602 "localPty:getTerminalLayoutInfo" => {
1603 dev_log!("terminal", "localPty:getTerminalLayoutInfo");
1604 Ok(Value::Null)
1605 },
1606 "localPty:setTerminalLayoutInfo" => {
1607 dev_log!("terminal", "localPty:setTerminalLayoutInfo");
1608 Ok(Value::Null)
1609 },
1610 "localPty:getPerformanceMarks" => {
1611 dev_log!("terminal", "localPty:getPerformanceMarks");
1612 Ok(json!([]))
1613 },
1614 "localPty:reduceConnectionGraceTime" => {
1615 dev_log!("terminal", "localPty:reduceConnectionGraceTime");
1616 Ok(Value::Null)
1617 },
1618 "localPty:listProcesses" => {
1619 dev_log!("terminal", "localPty:listProcesses");
1620 Ok(json!([]))
1621 },
1622 "localPty:getEnvironment" => {
1623 dev_log!("terminal", "localPty:getEnvironment");
1624 LocalPTYGetEnvironment().await
1625 },
1626 "localPty:getLatency" => {
1638 dev_log!("terminal", "localPty:getLatency");
1639 Ok(json!([]))
1640 },
1641
1642 "cocoon:request" => {
1653 dev_log!("ipc", "cocoon:request method={:?}", Arguments.first());
1654 let MethodOpt = Arguments.first().and_then(|V| V.as_str()).map(|S| S.to_string());
1655 match MethodOpt {
1656 None => Err("cocoon:request requires method string in slot 0".to_string()),
1657 Some(Method) => {
1658 let Payload = Arguments.get(1).cloned().unwrap_or(Value::Null);
1659 let _ = crate::Vine::Client::WaitForClientConnection::Fn("cocoon-main", 5000).await;
1673 crate::Vine::Client::SendRequest::Fn("cocoon-main", Method.clone(), Payload, 30_000)
1674 .await
1675 .map_err(|Error| format!("cocoon:request {} failed: {:?}", Method, Error))
1676 },
1677 }
1678 },
1679
1680 "cocoon:notify" => {
1690 dev_log!("ipc", "cocoon:notify method={:?}", Arguments.first());
1691 let MethodOpt = Arguments.first().and_then(|V| V.as_str()).map(|S| S.to_string());
1692 match MethodOpt {
1693 None => Err("cocoon:notify requires method string in slot 0".to_string()),
1694 Some(Method) => {
1695 let Payload = Arguments.get(1).cloned().unwrap_or(Value::Null);
1696 if let Err(Error) = crate::Vine::Client::SendNotification::Fn(
1697 "cocoon-main".to_string(),
1698 Method.clone(),
1699 Payload,
1700 )
1701 .await
1702 {
1703 dev_log!("ipc", "warn: [cocoon:notify] {} failed: {:?}", Method, Error);
1704 }
1705 Ok(Value::Null)
1706 },
1707 }
1708 },
1709
1710 "localPty:spawn" => {
1727 dev_log!("terminal", "{}", command);
1732 TerminalCreate(RunTime.clone(), Arguments).await
1733 },
1734 "localPty:createProcess" => {
1735 dev_log!("terminal", "{}", command);
1736 match TerminalCreate(RunTime.clone(), Arguments).await {
1737 Ok(Response) => {
1738 let TerminalIdOption = Response.get("id").and_then(serde_json::Value::as_u64);
1742 match TerminalIdOption {
1743 Some(TerminalId) if TerminalId > 0 => Ok(serde_json::json!(TerminalId)),
1744 Some(_) | None => {
1745 dev_log!(
1756 "terminal",
1757 "error: [localPty:createProcess] CreateTerminal returned no usable id; \
1758 response={:?}",
1759 Response
1760 );
1761 Err(format!(
1762 "localPty:createProcess: CreateTerminal returned no terminal id (response={})",
1763 Response
1764 ))
1765 },
1766 }
1767 },
1768 Err(Error) => Err(Error),
1769 }
1770 },
1771 "localPty:start" => {
1772 dev_log!("terminal", "{} no-op (eager-spawn)", command);
1785 Ok(Value::Null)
1786 },
1787 "localPty:input" | "localPty:write" => {
1788 dev_log!("terminal", "{}", command);
1789 TerminalSendText(RunTime.clone(), Arguments).await
1790 },
1791 "localPty:shutdown" | "localPty:dispose" => {
1792 dev_log!("terminal", "{}", command);
1793 TerminalDispose(RunTime.clone(), Arguments).await
1794 },
1795 "localPty:resize" => {
1796 dev_log!("terminal", "localPty:resize");
1797 let (TerminalId, Columns, Rows) = {
1809 let First = Arguments.first().cloned().unwrap_or(Value::Null);
1810 if First.is_object() {
1811 let Id = First.get("id").and_then(|V| V.as_u64()).unwrap_or(0);
1812 let C = First.get("cols").and_then(|V| V.as_u64()).unwrap_or(80) as u16;
1813 let R = First.get("rows").and_then(|V| V.as_u64()).unwrap_or(24) as u16;
1814 (Id, C, R)
1815 } else {
1816 let Id = Arguments.get(0).and_then(|V| V.as_u64()).unwrap_or(0);
1817 let C = Arguments.get(1).and_then(|V| V.as_u64()).unwrap_or(80) as u16;
1818 let R = Arguments.get(2).and_then(|V| V.as_u64()).unwrap_or(24) as u16;
1819 (Id, C, R)
1820 }
1821 };
1822 if TerminalId == 0 {
1823 Ok(Value::Null)
1824 } else {
1825 let Columns = if Columns == 0 { 1 } else { Columns };
1826 let Rows = if Rows == 0 { 1 } else { Rows };
1827 use CommonLibrary::{
1828 Environment::Requires::Requires,
1829 Terminal::TerminalProvider::TerminalProvider,
1830 };
1831 let Provider:Arc<dyn TerminalProvider> = RunTime.Environment.Require();
1832 match Provider.ResizeTerminal(TerminalId, Columns, Rows).await {
1833 Ok(_) => Ok(Value::Null),
1834 Err(Error) => {
1835 dev_log!(
1845 "terminal",
1846 "warn: localPty:resize id={} cols={} rows={} failed: {}",
1847 TerminalId,
1848 Columns,
1849 Rows,
1850 Error
1851 );
1852 Ok(Value::Null)
1853 },
1854 }
1855 }
1856 },
1857 "localPty:acknowledgeDataEvent" => {
1858 Ok(Value::Null)
1860 },
1861 "localPty:processBinary"
1866 | "localPty:attachToProcess"
1867 | "localPty:detachFromProcess"
1868 | "localPty:orphanQuestionReply"
1869 | "localPty:updateTitle"
1870 | "localPty:updateIcon"
1871 | "localPty:refreshProperty"
1872 | "localPty:updateProperty"
1873 | "localPty:getRevivedPtyNewId"
1874 | "localPty:freePortKillProcess"
1875 | "localPty:reviveTerminalProcesses"
1876 | "localPty:getBackendOS"
1877 | "localPty:installAutoReply"
1878 | "localPty:uninstallAllAutoReplies"
1879 | "localPty:serializeTerminalState" => Ok(Value::Null),
1880
1881 "update:_getInitialState" => {
1885 dev_log!("update", "update:_getInitialState");
1886 Ok(json!({ "type": "idle", "updateType": 0 }))
1887 },
1888 "update:isLatestVersion" => {
1889 dev_log!("update", "update:isLatestVersion");
1890 Ok(json!(true))
1891 },
1892 "update:checkForUpdates" => {
1893 dev_log!("update", "update:checkForUpdates");
1894 Ok(Value::Null)
1895 },
1896 "update:downloadUpdate" => {
1897 dev_log!("update", "update:downloadUpdate");
1898 Ok(Value::Null)
1899 },
1900 "update:applyUpdate" => {
1901 dev_log!("update", "update:applyUpdate");
1902 Ok(Value::Null)
1903 },
1904 "update:quitAndInstall" => {
1905 dev_log!("update", "update:quitAndInstall");
1906 Ok(Value::Null)
1907 },
1908
1909 "menubar:updateMenubar" => {
1921 use std::{
1922 sync::{Arc, Mutex as StandardMutex, OnceLock},
1923 time::Duration,
1924 };
1925
1926 use tokio::task::JoinHandle;
1927 type MenubarCell = StandardMutex<(Option<JoinHandle<()>>, u64)>;
1928 static MENUBAR_DEBOUNCE:OnceLock<Arc<MenubarCell>> = OnceLock::new();
1929 let Cell = MENUBAR_DEBOUNCE.get_or_init(|| Arc::new(StandardMutex::new((None, 0)))).clone();
1930
1931 if let Ok(mut Guard) = Cell.lock() {
1932 if let Some(Pending) = Guard.0.take() {
1933 Pending.abort();
1934 }
1935 Guard.1 = Guard.1.saturating_add(1);
1936 let CellForTask = Cell.clone();
1937 Guard.0 = Some(tokio::spawn(async move {
1938 tokio::time::sleep(Duration::from_millis(50)).await;
1939 let Coalesced = if let Ok(mut Post) = CellForTask.lock() {
1940 let N = Post.1;
1941 Post.1 = 0;
1942 Post.0 = None;
1943 N
1944 } else {
1945 0
1946 };
1947 dev_log!("menubar", "menubar:updateMenubar (applied, coalesced {} pending)", Coalesced);
1948 }));
1949 } else {
1950 dev_log!("menubar", "menubar:updateMenubar (debouncer lock poisoned)");
1951 }
1952 Ok(Value::Null)
1953 },
1954
1955 "url:registerExternalUriOpener" => {
1959 dev_log!("url", "url:registerExternalUriOpener");
1960 Ok(Value::Null)
1961 },
1962
1963 "encryption:encrypt" => {
1967 dev_log!("encryption", "encryption:encrypt");
1968 Ok(json!(""))
1969 },
1970 "encryption:decrypt" => {
1971 dev_log!("encryption", "encryption:decrypt");
1972 Ok(json!(""))
1973 },
1974
1975 "extensionHostStarter:createExtensionHost" => {
1979 dev_log!("exthost", "extensionHostStarter:createExtensionHost");
1980 Ok(json!({ "id": "1" }))
1981 },
1982 "extensionHostStarter:start" => {
1983 let Pid =
1990 crate::ProcessManagement::CocoonManagement::GetCocoonPid().unwrap_or_else(std::process::id);
1991 dev_log!("exthost", "extensionHostStarter:start pid={}", Pid);
1992 Ok(json!({ "pid": Pid }))
1993 },
1994 "extensionHostStarter:kill" => {
1995 dev_log!("exthost", "extensionHostStarter:kill");
1996 Ok(Value::Null)
1997 },
1998 "extensionHostStarter:getExitInfo" => {
1999 dev_log!("exthost", "extensionHostStarter:getExitInfo");
2000 Ok(json!({ "code": null, "signal": null }))
2001 },
2002
2003 "cocoon:extensionHostMessage" => {
2007 let ByteCount = Arguments
2008 .first()
2009 .map(|P| P.get("data").and_then(|D| D.as_array()).map(|A| A.len()).unwrap_or(0))
2010 .unwrap_or(0);
2011 dev_log!("exthost", "cocoon:extensionHostMessage bytes={}", ByteCount);
2012
2013 let Payload = Arguments.first().cloned().unwrap_or(Value::Null);
2016 tokio::spawn(async move {
2017 if let Err(Error) = crate::Vine::Client::SendNotification::Fn(
2018 "cocoon-main".to_string(),
2019 "extensionHostMessage".to_string(),
2020 Payload,
2021 )
2022 .await
2023 {
2024 dev_log!("exthost", "cocoon:extensionHostMessage forward failed: {}", Error);
2025 }
2026 });
2027 Ok(Value::Null)
2028 },
2029
2030 "extensionhostdebugservice:reload" => {
2034 dev_log!("exthost", "extensionhostdebugservice:reload");
2035 use tauri::Emitter;
2040 if let Err(Error) = ApplicationHandle.emit(SkyEvent::ExtHostDebugReload.AsStr(), json!({})) {
2041 dev_log!("exthost", "warn: extensionhostdebugservice:reload emit failed: {}", Error);
2042 }
2043 Ok(Value::Null)
2044 },
2045 "extensionhostdebugservice:close" => {
2046 dev_log!("exthost", "extensionhostdebugservice:close");
2047 use tauri::Emitter;
2048 if let Err(Error) = ApplicationHandle.emit("sky://exthost/debug-close", json!({})) {
2049 dev_log!("exthost", "warn: extensionhostdebugservice:close emit failed: {}", Error);
2050 }
2051 Ok(Value::Null)
2052 },
2053 "extensionhostdebugservice:attachSession" | "extensionhostdebugservice:terminateSession" => {
2054 dev_log!("exthost", "{}", command);
2055 Ok(Value::Null)
2056 },
2057
2058 "workspaces:getRecentlyOpened" => {
2062 dev_log!("workspaces", "workspaces:getRecentlyOpened");
2063 ReadRecentlyOpened()
2064 },
2065 "workspaces:removeRecentlyOpened" => {
2066 dev_log!("workspaces", "workspaces:removeRecentlyOpened");
2067 let Uri = Arguments.first().and_then(|V| V.as_str()).unwrap_or("").to_string();
2068 if !Uri.is_empty() {
2069 MutateRecentlyOpened(|List| {
2070 if let Some(Workspaces) = List.get_mut("workspaces").and_then(|V| V.as_array_mut()) {
2071 Workspaces
2072 .retain(|Entry| Entry.get("uri").and_then(|V| V.as_str()).unwrap_or("") != Uri);
2073 }
2074 if let Some(Files) = List.get_mut("files").and_then(|V| V.as_array_mut()) {
2075 Files.retain(|Entry| Entry.get("uri").and_then(|V| V.as_str()).unwrap_or("") != Uri);
2076 }
2077 });
2078 }
2079 Ok(Value::Null)
2080 },
2081 "workspaces:addRecentlyOpened" => {
2082 dev_log!("workspaces", "workspaces:addRecentlyOpened");
2083 let Entries:Vec<Value> = Arguments.first().and_then(|V| V.as_array()).cloned().unwrap_or_default();
2085 if !Entries.is_empty() {
2086 MutateRecentlyOpened(|List| {
2087 let Workspaces = List
2088 .get_mut("workspaces")
2089 .and_then(|V| V.as_array_mut())
2090 .map(|V| std::mem::take(V))
2091 .unwrap_or_default();
2092 let Files = List
2093 .get_mut("files")
2094 .and_then(|V| V.as_array_mut())
2095 .map(|V| std::mem::take(V))
2096 .unwrap_or_default();
2097 let mut MergedWorkspaces = Workspaces;
2098 let mut MergedFiles = Files;
2099 for Entry in Entries {
2100 let Folder = Entry
2101 .get("folderUri")
2102 .cloned()
2103 .or_else(|| Entry.get("workspace").and_then(|W| W.get("configPath").cloned()));
2104 let File = Entry.get("fileUri").cloned();
2105 if let Some(FolderUri) = Folder.and_then(|V| v_str(&V)) {
2106 MergedWorkspaces
2107 .retain(|E| E.get("uri").and_then(|V| V.as_str()).unwrap_or("") != FolderUri);
2108 let mut Item = serde_json::Map::new();
2109 Item.insert("uri".into(), json!(FolderUri));
2110 if let Some(Label) = Entry.get("label").and_then(|V| V.as_str()) {
2111 Item.insert("label".into(), json!(Label));
2112 }
2113 MergedWorkspaces.insert(0, Value::Object(Item));
2114 }
2115 if let Some(FileUri) = File.and_then(|V| v_str(&V)) {
2116 MergedFiles
2117 .retain(|E| E.get("uri").and_then(|V| V.as_str()).unwrap_or("") != FileUri);
2118 let mut Item = serde_json::Map::new();
2119 Item.insert("uri".into(), json!(FileUri));
2120 MergedFiles.insert(0, Value::Object(Item));
2121 }
2122 }
2123 MergedWorkspaces.truncate(50);
2126 MergedFiles.truncate(50);
2127 List.insert("workspaces".into(), Value::Array(MergedWorkspaces));
2128 List.insert("files".into(), Value::Array(MergedFiles));
2129 });
2130 }
2131 Ok(Value::Null)
2132 },
2133 "workspaces:clearRecentlyOpened" => {
2134 dev_log!("workspaces", "workspaces:clearRecentlyOpened");
2135 MutateRecentlyOpened(|List| {
2136 List.insert("workspaces".into(), json!([]));
2137 List.insert("files".into(), json!([]));
2138 });
2139 Ok(Value::Null)
2140 },
2141 "workspaces:enterWorkspace" => {
2142 dev_log!("workspaces", "workspaces:enterWorkspace");
2143 Ok(Value::Null)
2144 },
2145 "workspaces:createUntitledWorkspace" => {
2146 dev_log!("workspaces", "workspaces:createUntitledWorkspace");
2147 Ok(Value::Null)
2148 },
2149 "workspaces:deleteUntitledWorkspace" => {
2150 dev_log!("workspaces", "workspaces:deleteUntitledWorkspace");
2151 Ok(Value::Null)
2152 },
2153 "workspaces:getWorkspaceIdentifier" => {
2154 let Workspace = &RunTime.Environment.ApplicationState.Workspace;
2161 let Folders = Workspace.GetWorkspaceFolders();
2162 if let Some(First) = Folders.first() {
2163 use std::{
2164 collections::hash_map::DefaultHasher,
2165 hash::{Hash, Hasher},
2166 };
2167 let mut Hasher = DefaultHasher::new();
2168 First.URI.as_str().hash(&mut Hasher);
2169 let Id = format!("{:016x}", Hasher.finish());
2170 Ok(json!({
2171 "id": Id,
2172 "configPath": Value::Null,
2173 "uri": First.URI.to_string(),
2174 }))
2175 } else {
2176 Ok(Value::Null)
2177 }
2178 },
2179 "workspaces:getDirtyWorkspaces" => Ok(json!([])),
2180
2181 "git:exec" => {
2186 dev_log!("git", "git:exec");
2187 Git::HandleExec::HandleExec(Arguments).await
2188 },
2189 "git:clone" => {
2190 dev_log!("git", "git:clone");
2191 Git::HandleClone::HandleClone(Arguments).await
2192 },
2193 "git:pull" => {
2194 dev_log!("git", "git:pull");
2195 Git::HandlePull::HandlePull(Arguments).await
2196 },
2197 "git:checkout" => {
2198 dev_log!("git", "git:checkout");
2199 Git::HandleCheckout::HandleCheckout(Arguments).await
2200 },
2201 "git:revParse" => {
2202 dev_log!("git", "git:revParse");
2203 Git::HandleRevParse::HandleRevParse(Arguments).await
2204 },
2205 "git:fetch" => {
2206 dev_log!("git", "git:fetch");
2207 Git::HandleFetch::HandleFetch(Arguments).await
2208 },
2209 "git:revListCount" => {
2210 dev_log!("git", "git:revListCount");
2211 Git::HandleRevListCount::HandleRevListCount(Arguments).await
2212 },
2213 "git:cancel" => {
2214 dev_log!("git", "git:cancel");
2215 Git::HandleCancel::HandleCancel(Arguments).await
2216 },
2217 "git:isAvailable" => {
2218 dev_log!("git", "git:isAvailable");
2219 Git::HandleIsAvailable::HandleIsAvailable(Arguments).await
2220 },
2221
2222 "tree:getChildren" => {
2229 let ViewId = Arguments
2230 .first()
2231 .and_then(|V| V.get("viewId").or_else(|| V.get(0)))
2232 .and_then(Value::as_str)
2233 .unwrap_or("")
2234 .to_string();
2235 let ItemHandle = Arguments
2236 .first()
2237 .and_then(|V| V.get("treeItemHandle").or_else(|| V.get(1)))
2238 .and_then(Value::as_str)
2239 .unwrap_or("")
2240 .to_string();
2241 dev_log!(
2242 "tree-view",
2243 "[TreeView] invoke:getChildren view={} parent={}",
2244 ViewId,
2245 ItemHandle
2246 );
2247 if ViewId.is_empty() {
2248 Err("tree:getChildren requires viewId".to_string())
2249 } else {
2250 let Parameters = json!({
2251 "viewId": ViewId,
2252 "treeItemHandle": ItemHandle,
2253 });
2254 let _ = crate::Vine::Client::WaitForClientConnection::Fn("cocoon-main", 5000).await;
2273 match crate::Vine::Client::SendRequest::Fn(
2284 "cocoon-main",
2285 "$provideTreeChildren".to_string(),
2286 Parameters,
2287 5000,
2293 )
2294 .await
2295 {
2296 Ok(Value_) => {
2307 match &Value_ {
2308 Value::Object(_) | Value::Array(_) => Ok(Value_),
2309 _ => Ok(json!({ "items": [] })),
2310 }
2311 },
2312 Err(Error) => {
2313 crate::IPC::DevLog::DebugOnce::Fn(
2328 "tree-view",
2329 &format!("get-children-error:{}", ViewId),
2330 &format!(
2331 "[TreeView] invoke:getChildren error view={} err={:?} (further occurrences \
2332 silenced)",
2333 ViewId, Error
2334 ),
2335 );
2336 Ok(json!({ "items": [] }))
2337 },
2338 }
2339 }
2340 },
2341
2342 "sky:replay-events" => {
2354 use tauri::Emitter;
2355 let mut TreeViewCount:usize = 0;
2356 let mut ScmCount:usize = 0;
2357 let mut CommandCount:usize = 0;
2358 let mut TerminalCount:usize = 0;
2359 let mut TerminalDataBytes:usize = 0;
2360 if let Ok(TreeViews) = RunTime.Environment.ApplicationState.Feature.TreeViews.ActiveTreeViews.lock()
2361 {
2362 for (ViewId, Dto) in TreeViews.iter() {
2363 let Payload = serde_json::json!({
2364 "viewId": ViewId,
2365 "options": {
2366 "canSelectMany": Dto.CanSelectMany,
2367 "showCollapseAll": Dto.HasHandleDrag,
2368 "title": Dto.Title.clone().unwrap_or_default(),
2369 },
2370 });
2371 if ApplicationHandle.emit("sky://tree-view/create", Payload).is_ok() {
2372 TreeViewCount += 1;
2373 }
2374 }
2375 }
2376 if let Ok(ScmProviders) = RunTime
2386 .Environment
2387 .ApplicationState
2388 .Feature
2389 .Markers
2390 .SourceControlManagementProviders
2391 .lock()
2392 {
2393 for (Handle, Dto) in ScmProviders.iter() {
2394 let RootUriStr = Dto
2395 .RootURI
2396 .as_ref()
2397 .and_then(|V| V.get("external").or_else(|| V.get("path")))
2398 .and_then(serde_json::Value::as_str)
2399 .unwrap_or("")
2400 .to_string();
2401 let ScmId = if Dto.Identifier.is_empty() {
2402 "git".to_string()
2403 } else {
2404 Dto.Identifier.clone()
2405 };
2406 let Payload = serde_json::json!({
2407 "scmId": ScmId,
2408 "label": Dto.Label,
2409 "rootUri": RootUriStr,
2410 "extensionId": "",
2411 "handle": *Handle,
2412 });
2413 if ApplicationHandle.emit("sky://scm/register", Payload).is_ok() {
2414 ScmCount += 1;
2415 }
2416 }
2417 }
2418 if let Ok(Commands) = RunTime.Environment.ApplicationState.Extension.Registry.CommandRegistry.lock()
2434 {
2435 let mut Batch:Vec<serde_json::Value> = Vec::new();
2436 for (CommandId, Handler) in Commands.iter() {
2437 use crate::Environment::CommandProvider::CommandHandler;
2438 let Kind = match Handler {
2439 CommandHandler::Native(_) => continue,
2440 CommandHandler::Proxied { .. } => "extension",
2441 };
2442 Batch.push(serde_json::json!({
2443 "id": CommandId,
2444 "commandId": CommandId,
2445 "kind": Kind,
2446 }));
2447 }
2448 if !Batch.is_empty() {
2449 let Count = Batch.len();
2450 if ApplicationHandle
2451 .emit("sky://command/register", serde_json::json!({ "commands": Batch }))
2452 .is_ok()
2453 {
2454 CommandCount = Count;
2455 }
2456 }
2457 }
2458 if let Ok(Terminals) = RunTime.Environment.ApplicationState.Feature.Terminals.ActiveTerminals.lock()
2466 {
2467 for (TerminalId, Arc) in Terminals.iter() {
2468 let (Name, Pid) = if let Ok(State) = Arc.lock() {
2469 (State.Name.clone(), State.OSProcessIdentifier.unwrap_or(0))
2470 } else {
2471 (String::new(), 0)
2472 };
2473 let CreatePayload = serde_json::json!({
2474 "id": *TerminalId,
2475 "name": Name,
2476 "pid": Pid,
2477 });
2478 if ApplicationHandle.emit("sky://terminal/create", CreatePayload).is_ok() {
2479 TerminalCount += 1;
2480 }
2481 }
2482 }
2483 for (TerminalId, Bytes) in crate::Environment::TerminalProvider::DrainTerminalOutputBuffer() {
2484 let DataString = String::from_utf8_lossy(&Bytes).to_string();
2485 TerminalDataBytes += Bytes.len();
2486 let _ = ApplicationHandle.emit(
2487 "sky://terminal/data",
2488 serde_json::json!({ "id": TerminalId, "data": DataString }),
2489 );
2490 }
2491 dev_log!(
2492 "sky-emit",
2493 "[SkyEmit] replay-events tree-views={} scm={} commands={} terminals={} terminal-bytes={}",
2494 TreeViewCount,
2495 ScmCount,
2496 CommandCount,
2497 TerminalCount,
2498 TerminalDataBytes
2499 );
2500 Ok(serde_json::json!({
2501 "treeViews": TreeViewCount,
2502 "scmProviders": ScmCount,
2503 "commands": CommandCount,
2504 "terminals": TerminalCount,
2505 "terminalDataBytes": TerminalDataBytes,
2506 }))
2507 },
2508
2509 _ => {
2518 use std::str::FromStr;
2519 match CommonLibrary::IPC::Channel::Channel::from_str(&command) {
2520 Ok(KnownChannel) => {
2521 dev_log!(
2522 "ipc",
2523 "error: [WindServiceHandlers] Channel {:?} is registered but has no dispatch arm",
2524 KnownChannel
2525 );
2526 Err(format!("IPC channel registered but unimplemented: {}", command))
2527 },
2528 Err(_) => {
2529 dev_log!("ipc", "error: [WindServiceHandlers] Unknown IPC command: {}", command);
2530 Err(format!("Unknown IPC command: {}", command))
2531 },
2532 }
2533 },
2534 };
2535
2536 if ResultSender.send(MatchResult).is_err() {
2537 dev_log!(
2538 "ipc",
2539 "warn: [WindServiceHandlers] IPC result receiver dropped before dispatch completed"
2540 );
2541 }
2542 },
2543 CommandPriority,
2544 );
2545
2546 let Result = match ResultReceiver.await {
2547 Ok(Dispatched) => Dispatched,
2548 Err(_) => {
2549 dev_log!(
2550 "ipc",
2551 "error: [WindServiceHandlers] IPC task cancelled before producing a result"
2552 );
2553 Err("IPC task cancelled before result was produced".to_string())
2554 },
2555 };
2556
2557 if !IsHighFrequencyCommand {
2561 let IsErr = Result.is_err();
2562 let SpanName = if IsErr {
2563 format!("land:mountain:ipc:{}:error", command)
2564 } else {
2565 format!("land:mountain:ipc:{}", command)
2566 };
2567 crate::otel_span!(&SpanName, OTLPStart, &[("ipc.command", command.as_str())]);
2568
2569 let HandlerElapsedNanos = crate::IPC::DevLog::NowNano::Fn().saturating_sub(OTLPStart);
2573 let HandlerDurationMs = HandlerElapsedNanos / 1_000_000;
2574 crate::Binary::Build::PostHogPlugin::CaptureHandler::Fn(&command, HandlerDurationMs, !IsErr);
2575 }
2576
2577 if !IsHighFrequencyCommand {
2586 let ElapsedNanos = crate::IPC::DevLog::NowNano::Fn().saturating_sub(OTLPStart);
2587 dev_log!("ipc", "done: {} ok={} t_ns={}", command, !Result.is_err(), ElapsedNanos);
2588 }
2589
2590 Result
2591}
2592
2593pub fn register_wind_ipc_handlers(ApplicationHandle:&tauri::AppHandle) -> Result<(), String> {
2594 dev_log!("lifecycle", "registering IPC handlers");
2595
2596 Ok(())
2600}