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 1 => args.into_iter().next().unwrap(),
327 _ => Value::Array(args),
328 }
329}
330
331macro_rules! forward_to_cocoon {
332 ($tag:literal, $command:ident, $Arguments:ident) => {{
333 dev_log!("ipc", "{}: {} (→ Cocoon)", $tag, $command);
334 let Payload = cocoon_payload($Arguments);
335 let _ = crate::Vine::Client::WaitForClientConnection::Fn("cocoon-main", 3000).await;
336 Ok(
337 crate::Vine::Client::SendRequest::Fn("cocoon-main", $command.clone(), Payload, 10_000)
338 .await
339 .unwrap_or(Value::Null),
340 )
341 }};
342}
343
344const TIER_TERMINAL:&str = env!("TierTerminal", "Mountain");
367const TIER_SCM:&str = env!("TierSCM", "Mountain");
368const TIER_DEBUG:&str = env!("TierDebug", "Mountain");
369const TIER_LANGUAGE_FEATURES:&str = env!("TierLanguageFeatures", "Mountain");
370const TIER_SEARCH:&str = env!("TierSearch", "Mountain");
371const TIER_OUTPUT_CHANNEL:&str = env!("TierOutputChannel", "Mountain");
372const TIER_NATIVE_HOST:&str = env!("TierNativeHost", "Mountain");
373const TIER_TREE_VIEW:&str = env!("TierTreeView", "Mountain");
374const TIER_STORAGE:&str = env!("TierStorage", "Mountain");
375const TIER_MODEL:&str = env!("TierModel", "Mountain");
376const TIER_TASKS:&str = env!("TierTasks", "Node");
377const TIER_AUTH:&str = env!("TierAuth", "Node");
378const TIER_ENCRYPTION:&str = env!("TierEncryption", "Mountain");
379const TIER_WEBSOCKET:&str = env!("TierWebSocket", "Disabled");
380
381#[inline]
382fn tier_routes_to_node(BakedConst:&'static str, EnvKey:&str) -> bool {
383 let Resolved = std::env::var(EnvKey).unwrap_or_else(|_| BakedConst.to_string());
384
385 Resolved == "Node"
386}
387
388pub async fn mountain_ipc_invoke(
389 ApplicationHandle:AppHandle,
390
391 command:String,
392
393 Arguments:Vec<Value>,
394) -> Result<Value, String> {
395 let IsHighFrequencyCommand = matches!(
398 command.as_str(),
399 "logger:log"
400 | "logger:info"
401 | "logger:debug"
402 | "logger:trace"
403 | "logger:warn"
404 | "logger:error"
405 | "logger:critical"
406 | "logger:flush"
407 | "logger:setLevel"
408 | "logger:getLevel"
409 | "logger:registerLogger"
410 | "logger:createLogger"
411 | "logger:deregisterLogger"
412 | "logger:getRegisteredLoggers"
413 | "logger:setVisibility"
414 | "log:registerLogger"
415 | "log:createLogger"
416 | "file:stat"
418 | "file:readFile"
419 | "file:readdir"
420 | "file:writeFile"
421 | "file:delete"
422 | "file:rename"
423 | "file:realpath"
424 | "file:read"
425 | "file:write"
426 | "file:open"
428 | "file:close"
429 | "textFile:save"
431 | "storage:getItems"
433 | "storage:updateItems"
434 | "configuration:lookup"
436 | "configuration:inspect"
437 | "themes:getColorTheme"
439 | "output:append"
441 | "progress:report"
442 | "menubar:updateMenubar"
444 | "storage:onDidChangeItems"
446 | "storage:logStorage"
447 | "configuration:onDidChange"
448 | "workspaces:onDidChangeWorkspaceFolders"
449 | "workspaces:onDidChangeWorkspaceName"
450 | "commands:registerCommand"
452 | "commands:unregisterCommand"
453 | "commands:onDidRegisterCommand"
454 | "commands:onDidExecuteCommand"
455 );
456
457 let OTLPStart = if IsHighFrequencyCommand { 0 } else { crate::IPC::DevLog::NowNano::Fn() };
458
459 if !IsHighFrequencyCommand {
467 dev_log!("ipc", "invoke: {} args_count={}", command, Arguments.len());
468 }
469
470 ensure_userdata_dirs();
472
473 let RunTime:Arc<ApplicationRunTime> = ApplicationHandle.state::<Arc<ApplicationRunTime>>().inner().clone();
477
478 if IsHighFrequencyCommand {
484 match command.as_str() {
485
486 "logger:error" | "logger:critical" => {
491
492 let Msg = Arguments.get(1).and_then(|V| V.as_str()).unwrap_or(
493 Arguments.first().and_then(|V| V.as_str()).unwrap_or(""),
494 );
495
496 if !Msg.is_empty() {
497
498 dev_log!("vscode-log", "[ERROR] {}", Msg);
499 }
500
501 return Ok(Value::Null);
502 },
503
504 "logger:warn" => {
505
506 let Msg = Arguments.get(1).and_then(|V| V.as_str()).unwrap_or(
507 Arguments.first().and_then(|V| V.as_str()).unwrap_or(""),
508 );
509
510 if !Msg.is_empty() {
511
512 dev_log!("vscode-log", "[WARN] {}", Msg);
513 }
514
515 return Ok(Value::Null);
516 },
517
518 "logger:log" | "logger:info" | "logger:debug" | "logger:trace"
519 | "logger:flush" | "logger:setLevel" | "logger:getLevel"
520 | "logger:createLogger" | "logger:registerLogger"
521 | "logger:deregisterLogger" | "logger:getRegisteredLoggers"
522 | "logger:setVisibility"
523 | "log:registerLogger" | "log:createLogger"
529 | "storage:onDidChangeItems" | "storage:logStorage"
531 | "commands:registerCommand" | "commands:unregisterCommand"
533 | "commands:onDidRegisterCommand" | "commands:onDidExecuteCommand"
534 | "configuration:onDidChange"
536 | "storage:optimize" | "storage:isUsed" | "storage:close"
538 | "workspaces:onDidChangeWorkspaceFolders"
540 | "workspaces:onDidChangeWorkspaceName" => {
541
542 return Ok(Value::Null);
543 },
544
545 "menubar:updateMenubar" => {
548
549 use std::sync::atomic::{AtomicU64, Ordering as AO};
550
551 static MENUBAR_CALLS_FAST:AtomicU64 = AtomicU64::new(0);
552
553 let N = MENUBAR_CALLS_FAST.fetch_add(1, AO::Relaxed) + 1;
554
555 if N == 1 || N % 100 == 0 {
556
557 dev_log!("menubar", "menubar:updateMenubar (fast-path call #{})", N);
558 }
559
560 return Ok(Value::Null);
561 },
562
563 _ => {}, }
565 }
566
567 let CommandPriority = ResolveChannelPriority(&command);
587
588 let Scheduler = RunTime.Scheduler.clone();
589
590 let (ResultSender, ResultReceiver) = tokio::sync::oneshot::channel::<Result<Value, String>>();
591
592 let DispatchAppHandle = ApplicationHandle.clone();
593
594 let DispatchRuntime = RunTime.clone();
595
596 let DispatchCommand = command.clone();
597
598 let DispatchArgs = Arguments;
599
600 Scheduler.Submit(
601 async move {
602 let ApplicationHandle = DispatchAppHandle;
603 let RunTime = DispatchRuntime;
604 let command = DispatchCommand;
605 let Arguments = DispatchArgs;
606
607 macro_rules! call {
610 (rt, $tag:literal, $Fn:path, $Arguments:ident) => {{
611 dev_log!($tag, "{}", command);
612 $Fn(RunTime.clone(), $Arguments).await
613 }};
614 (rt, $tag:literal, $Fn:path) => {{
615 dev_log!($tag, "{}", command);
616 $Fn(RunTime.clone()).await
617 }};
618 (rt, $tag:literal, $msg:literal, $Fn:path, $Arguments:ident) => {{
619 dev_log!($tag, $msg);
620 $Fn(RunTime.clone(), $Arguments).await
621 }};
622 (rt, $tag:literal, $msg:literal, $Fn:path) => {{
623 dev_log!($tag, $msg);
624 $Fn(RunTime.clone()).await
625 }};
626 (app, $tag:literal, $Fn:path, $Arguments:ident) => {{
627 dev_log!($tag, "{}", command);
628 $Fn(ApplicationHandle.clone(), $Arguments).await
629 }};
630 (app, $tag:literal, $msg:literal, $Fn:path, $Arguments:ident) => {{
631 dev_log!($tag, $msg);
632 $Fn(ApplicationHandle.clone(), $Arguments).await
633 }};
634 (app, $tag:literal, $msg:literal, $Fn:path) => {{
635 dev_log!($tag, $msg);
636 $Fn(ApplicationHandle.clone()).await
637 }};
638 }
639
640 let MatchResult = match command.as_str() {
641 "configuration:get" | "configuration:getValue" => call!(rt, "config", ConfigurationGet, Arguments),
647 "configuration:update" | "configuration:updateValue" => {
648 call!(rt, "config", ConfigurationUpdate, Arguments)
649 },
650 "configuration:onDidChange" => Ok(Value::Null),
656
657 "configuration:lookup" => {
663 call!(rt, "config", "configuration:lookup (→ get)", ConfigurationGet, Arguments)
664 },
665
666 "configuration:inspect" => {
676 dev_log!("config", "configuration:inspect");
677 let CurrentValue = ConfigurationGet(RunTime.clone(), Arguments).await.unwrap_or(Value::Null);
678 Ok(json!({
679 "value": CurrentValue,
680 "default": CurrentValue,
681 "user": Value::Null,
682 "workspace": Value::Null,
683 "workspaceFolder": Value::Null,
684 "memory": Value::Null,
685 }))
686 },
687
688 "logger:log" | "logger:warn" | "logger:error" | "logger:info" | "logger:debug" | "logger:trace" => {
693 let Level = command.trim_start_matches("logger:");
694 let Msg = if Arguments.len() >= 2 {
695 let Tail:Vec<String> = Arguments
696 .iter()
697 .skip(1)
698 .filter_map(|V| V.as_str().map(str::to_owned).or_else(|| serde_json::to_string(V).ok()))
699 .collect();
700 Tail.join(" ")
701 } else {
702 Arguments
703 .first()
704 .and_then(|V| V.as_str().map(str::to_owned))
705 .unwrap_or_default()
706 };
707 if !Msg.is_empty() {
708 match Level {
709 "error" | "critical" => dev_log!("vscode-log", "[ERROR] {}", Msg),
710 "warn" => dev_log!("vscode-log", "[WARN] {}", Msg),
711 _ => dev_log!("vscode-log", "{}", Msg),
712 }
713 }
714 Ok(Value::Null)
715 },
716 "logger:flush"
717 | "logger:setLevel"
718 | "logger:getLevel"
719 | "logger:createLogger"
720 | "logger:registerLogger"
721 | "logger:deregisterLogger"
722 | "logger:getRegisteredLoggers"
723 | "logger:setVisibility" => Ok(Value::Null),
724
725 "file:read" | "file:readFile" => FileReadNative(Arguments).await,
737 "file:write" | "file:writeFile" => FileWriteNative(Arguments).await,
738 "file:stat" => FileStatNative(Arguments).await,
739 "file:exists" => FileExistsNative(Arguments).await,
740 "file:delete" => FileDeleteNative(Arguments).await,
741 "file:copy" => FileCloneNative(Arguments).await,
742 "file:move" | "file:rename" => FileRenameNative(Arguments).await,
743 "file:mkdir" => FileMkdirNative(Arguments).await,
744 "file:readdir" => FileReaddirNative(Arguments).await,
745 "file:readBinary" => FileReadBinary(RunTime.clone(), Arguments).await,
746 "file:writeBinary" => FileWriteBinary(RunTime.clone(), Arguments).await,
747 "file:watch" => FileWatch(RunTime.clone(), Arguments).await,
750 "file:unwatch" => FileUnwatch(RunTime.clone(), Arguments).await,
751
752 "storage:get"
766 | "storage:set"
767 | "storage:getItems"
768 | "storage:updateItems"
769 | "storage:optimize"
770 | "storage:isUsed"
771 | "storage:close"
772 | "storage:delete"
773 | "storage:keys"
774 | "storage:onDidChangeItems"
775 | "storage:logStorage"
776 if tier_routes_to_node(TIER_STORAGE, "TierStorage") =>
777 {
778 forward_to_cocoon!("storage", command, Arguments)
779 },
780 "storage:get" => StorageGet(RunTime.clone(), Arguments).await,
781 "storage:set" => StorageSet(RunTime.clone(), Arguments).await,
782 "storage:getItems" => call!(rt, "storage-verbose", "storage:getItems", StorageGetItems, Arguments),
786 "storage:updateItems" => {
787 call!(rt, "storage-verbose", "storage:updateItems", StorageUpdateItems, Arguments)
788 },
789 "storage:optimize" => {
790 dev_log!("storage", "storage:optimize");
791 Ok(Value::Null)
792 },
793 "storage:isUsed" => {
794 dev_log!("storage", "storage:isUsed");
795 Ok(Value::Null)
796 },
797 "storage:close" => {
798 dev_log!("storage", "storage:close");
799 Ok(Value::Null)
800 },
801 "storage:onDidChangeItems" | "storage:logStorage" => {
805 dev_log!("storage-verbose", "{} (stub-ack)", command);
806 Ok(Value::Null)
807 },
808
809 "environment:get" => call!(rt, "config", "environment:get", EnvironmentGet, Arguments),
811
812 "native:showItemInFolder" => ShowItemInFolder(RunTime.clone(), Arguments).await,
814 "native:openExternal" => OpenExternal(RunTime.clone(), Arguments).await,
815
816 "workbench:getConfiguration" => WorkbenchConfiguration(RunTime.clone(), Arguments).await,
818
819 "diagnostic:log" => {
825 let Tag = arg_string_or(&Arguments, 0, "webview");
826 let Message = arg_string(&Arguments, 1);
827 let Extras = if Arguments.len() > 2 {
828 let Tail:Vec<String> = Arguments
829 .iter()
830 .skip(2)
831 .map(|V| {
832 let S = serde_json::to_string(V).unwrap_or_default();
833 if S.len() > 240 {
839 let CutAt = S
840 .char_indices()
841 .map(|(Index, _)| Index)
842 .take_while(|Index| *Index <= 240)
843 .last()
844 .unwrap_or(0);
845 format!("{}…", &S[..CutAt])
846 } else {
847 S
848 }
849 })
850 .collect();
851 format!(" {}", Tail.join(" "))
852 } else {
853 String::new()
854 };
855 dev_log!("diagnostic", "[{}] {}{}", Tag, Message, Extras);
856 Ok(Value::Null)
857 },
858
859 "commands:execute" | "commands:executeCommand" => CommandsExecute(RunTime.clone(), Arguments).await,
864 "commands:getAll" | "commands:getCommands" => call!(rt, "commands", CommandsGetAll),
865 "commands:registerCommand"
870 | "commands:unregisterCommand"
871 | "commands:onDidRegisterCommand"
872 | "commands:onDidExecuteCommand" => Ok(Value::Null),
873
874 "extensions:getAll" => call!(rt, "extensions", "extensions:getAll", ExtensionsGetAll),
876 "extensions:get" => call!(rt, "extensions", "extensions:get", ExtensionsGet, Arguments),
877 "extensions:isActive" => call!(rt, "extensions", "extensions:isActive", ExtensionsIsActive, Arguments),
878 "extensions:activate" => {
884 let ExtensionId = arg_string(&Arguments, 0);
885 dev_log!("extensions", "extensions:activate id={}", ExtensionId);
886 if ExtensionId.is_empty() {
887 Ok(Value::Null)
888 } else {
889 let Notification = json!({
890 "event": format!("onCustom:{}", ExtensionId),
891 "extensionId": ExtensionId,
892 });
893 let _ = crate::Vine::Client::SendNotification::Fn(
894 "cocoon-main".to_string(),
895 "$activateByEvent".to_string(),
896 Notification,
897 )
898 .await;
899 Ok(Value::Null)
900 }
901 },
902
903 "extensions:getInstalled" | "extensions:scanSystemExtensions" => {
916 let ArgsSummary = Arguments
922 .iter()
923 .enumerate()
924 .map(|(Idx, V)| {
925 let Preview = serde_json::to_string(V).unwrap_or_default();
926 let Trimmed = if Preview.len() > 180 {
929 let CutAt = Preview
930 .char_indices()
931 .map(|(Index, _)| Index)
932 .take_while(|Index| *Index <= 180)
933 .last()
934 .unwrap_or(0);
935 format!("{}…", &Preview[..CutAt])
936 } else {
937 Preview
938 };
939 format!("[{}]={}", Idx, Trimmed)
940 })
941 .collect::<Vec<_>>()
942 .join(" ");
943 dev_log!("extensions", "{} Arguments={}", command, ArgsSummary);
944 let EffectiveArgs = if command == "extensions:scanSystemExtensions" {
953 let mut Overridden = Arguments.clone();
954 if Overridden.is_empty() {
955 Overridden.push(Value::Null);
956 }
957 Overridden[0] = json!(0);
958 Overridden
959 } else {
960 Arguments.clone()
961 };
962 ExtensionsGetInstalled(RunTime.clone(), EffectiveArgs).await
963 },
964 "extensions:scanUserExtensions" => {
965 dev_log!("extensions", "{} (forwarded to getInstalled with type=User)", command);
974 let mut UserArgs = Arguments.clone();
975 if UserArgs.is_empty() {
976 UserArgs.push(Value::Null);
977 }
978 UserArgs[0] = json!(1);
979 ExtensionsGetInstalled(RunTime.clone(), UserArgs).await
980 },
981 "extensions:getUninstalled" => {
982 dev_log!("extensions", "{} (returning [])", command);
986 Ok(Value::Array(Vec::new()))
987 },
988 "extensions:query" | "extensions:getExtensions" | "extensions:getRecommendations" => {
992 dev_log!("extensions", "{} (offline gallery - returning [])", command);
993 Ok(Value::Array(Vec::new()))
994 },
995 "extensions:getExtensionsControlManifest" => {
1001 dev_log!("extensions", "{} (offline gallery - empty manifest)", command);
1002 Ok(json!({
1003 "malicious": [],
1004 "deprecated": {},
1005 "search": [],
1006 "autoUpdate": {},
1007 }))
1008 },
1009 "extensions:resetPinnedStateForAllUserExtensions" => {
1016 dev_log!("extensions", "{} (no-op, pin state is UI-local)", command);
1017 Ok(Value::Null)
1018 },
1019 "extensions:install" => {
1027 Extension::ExtensionInstall::Fn(ApplicationHandle.clone(), RunTime.clone(), Arguments).await
1028 },
1029 "extensions:uninstall" => {
1030 Extension::ExtensionUninstall::Fn(ApplicationHandle.clone(), RunTime.clone(), Arguments).await
1031 },
1032
1033 "extensions:getManifest" => {
1042 let VsixPath = match Arguments.first() {
1043 Some(serde_json::Value::String(Path)) => Path.clone(),
1044 Some(Obj) => {
1045 Obj.get("fsPath")
1046 .and_then(|V| V.as_str())
1047 .map(str::to_owned)
1048 .or_else(|| Obj.get("path").and_then(|V| V.as_str()).map(str::to_owned))
1049 .unwrap_or_default()
1050 },
1051 None => String::new(),
1052 };
1053 dev_log!("extensions", "extensions:getManifest vsix={}", VsixPath);
1054 if VsixPath.is_empty() {
1055 Err("extensions:getManifest: missing VSIX path argument".to_string())
1056 } else {
1057 let Path = std::path::PathBuf::from(&VsixPath);
1058 match crate::ExtensionManagement::VsixInstaller::ReadFullManifest(&Path) {
1059 Ok(Manifest) => Ok(Manifest),
1060 Err(Error) => {
1061 dev_log!(
1062 "extensions",
1063 "warn: [WindServiceHandlers] extensions:getManifest failed for '{}': {}",
1064 VsixPath,
1065 Error
1066 );
1067 Err(format!("extensions:getManifest failed: {}", Error))
1068 },
1069 }
1070 }
1071 },
1072 "extensions:reinstall" | "extensions:updateMetadata" => {
1077 dev_log!("extensions", "{} (no-op: no gallery backend)", command);
1078 Ok(Value::Null)
1079 },
1080
1081 "terminal:create" => call!(rt, "terminal", "terminal:create", TerminalCreate, Arguments),
1083 "terminal:sendText" => call!(rt, "terminal", "terminal:sendText", TerminalSendText, Arguments),
1084 "terminal:dispose" => call!(rt, "terminal", "terminal:dispose", TerminalDispose, Arguments),
1085 "terminal:show" => call!(rt, "terminal", "terminal:show", TerminalShow, Arguments),
1086 "terminal:hide" => call!(rt, "terminal", "terminal:hide", TerminalHide, Arguments),
1087
1088 "output:create" => OutputCreate(ApplicationHandle.clone(), Arguments).await,
1090 "output:append" => call!(app, "output", "output:append", OutputAppend, Arguments),
1091 "output:appendLine" => call!(app, "output", "output:appendLine", OutputAppendLine, Arguments),
1092 "output:clear" => call!(app, "output", "output:clear", OutputClear, Arguments),
1093 "output:show" => call!(app, "output", "output:show", OutputShow, Arguments),
1094
1095 "textFile:read" => call!(rt, "textfile", "textFile:read", TextfileRead, Arguments),
1097 "textFile:write" => call!(rt, "textfile", "textFile:write", TextfileWrite, Arguments),
1098 "textFile:save" => TextfileSave(RunTime.clone(), Arguments).await,
1099
1100 "storage:delete" => call!(rt, "storage", "storage:delete", StorageDelete, Arguments),
1102 "storage:keys" => call!(rt, "storage", "storage:keys", StorageKeys),
1103
1104 "notification:show" => call!(app, "notification", "notification:show", NotificationShow, Arguments),
1106 "notification:showProgress" => {
1107 call!(
1108 app,
1109 "notification",
1110 "notification:showProgress",
1111 NotificationShowProgress,
1112 Arguments
1113 )
1114 },
1115 "notification:updateProgress" => {
1116 call!(
1117 app,
1118 "notification",
1119 "notification:updateProgress",
1120 NotificationUpdateProgress,
1121 Arguments
1122 )
1123 },
1124 "notification:endProgress" => {
1125 call!(
1126 app,
1127 "notification",
1128 "notification:endProgress",
1129 NotificationEndProgress,
1130 Arguments
1131 )
1132 },
1133
1134 "progress:begin" => call!(app, "progress", "progress:begin", ProgressBegin, Arguments),
1136 "progress:report" => call!(app, "progress", "progress:report", ProgressReport, Arguments),
1137 "progress:end" => call!(app, "progress", "progress:end", ProgressEnd, Arguments),
1138
1139 "quickInput:showQuickPick" => {
1141 call!(rt, "quickinput", "quickInput:showQuickPick", QuickInputShowQuickPick, Arguments)
1142 },
1143 "quickInput:showInputBox" => {
1144 call!(rt, "quickinput", "quickInput:showInputBox", QuickInputShowInputBox, Arguments)
1145 },
1146
1147 "workspaces:getFolders" | "workspaces:getWorkspaceFolders" | "workspaces:getWorkspace" => {
1152 call!(rt, "workspaces", WorkspacesGetFolders)
1153 },
1154 "workspaces:addFolder" | "workspaces:addWorkspaceFolders" => {
1155 call!(rt, "workspaces", WorkspacesAddFolder, Arguments)
1156 },
1157 "workspaces:removeFolder" | "workspaces:removeWorkspaceFolders" => {
1158 call!(rt, "workspaces", WorkspacesRemoveFolder, Arguments)
1159 },
1160 "workspaces:getName" => call!(rt, "workspaces", WorkspacesGetName),
1161 "themes:getActive" => call!(rt, "themes", "themes:getActive", ThemesGetActive),
1163 "themes:list" => call!(rt, "themes", "themes:list", ThemesList),
1164 "themes:set" => call!(rt, "themes", "themes:set", ThemesSet, Arguments),
1165 "themes:getColorTheme" => call!(rt, "themes", "themes:getColorTheme (→ getActive)", ThemesGetActive),
1170
1171 "search:findInFiles" | "search:textSearch" | "search:searchText" => {
1175 call!(rt, "search", SearchFindInFiles, Arguments)
1176 },
1177 "search:findFiles" | "search:fileSearch" | "search:searchFile" => {
1178 call!(rt, "search", SearchFindFiles, Arguments)
1179 },
1180 "search:cancel" | "search:clearCache" | "search:onDidChangeResult" => {
1185 dev_log!("search", "{} (stub-ack)", command);
1186 Ok(Value::Null)
1187 },
1188
1189 "decorations:get" => call!(rt, "decorations", "decorations:get", DecorationsGet, Arguments),
1191 "decorations:getMany" => call!(rt, "decorations", "decorations:getMany", DecorationsGetMany, Arguments),
1192 "decorations:set" => call!(rt, "decorations", "decorations:set", DecorationsSet, Arguments),
1193 "decorations:clear" => call!(rt, "decorations", "decorations:clear", DecorationsClear, Arguments),
1194
1195 "workingCopy:isDirty" => call!(rt, "workingcopy", "workingCopy:isDirty", WorkingCopyIsDirty, Arguments),
1197 "workingCopy:setDirty" => {
1198 call!(rt, "workingcopy", "workingCopy:setDirty", WorkingCopySetDirty, Arguments)
1199 },
1200 "workingCopy:getAllDirty" => {
1201 call!(rt, "workingcopy", "workingCopy:getAllDirty", WorkingCopyGetAllDirty)
1202 },
1203 "workingCopy:getDirtyCount" => {
1204 call!(rt, "workingcopy", "workingCopy:getDirtyCount", WorkingCopyGetDirtyCount)
1205 },
1206
1207 "keybinding:add" => call!(rt, "keybinding", "keybinding:add", KeybindingAdd, Arguments),
1209 "keybinding:remove" => call!(rt, "keybinding", "keybinding:remove", KeybindingRemove, Arguments),
1210 "keybinding:lookup" => call!(rt, "keybinding", "keybinding:lookup", KeybindingLookup, Arguments),
1211 "keybinding:getAll" => call!(rt, "keybinding", "keybinding:getAll", KeybindingGetAll),
1212
1213 "lifecycle:getPhase" => call!(rt, "lifecycle", "lifecycle:getPhase", LifecycleGetPhase),
1215 "lifecycle:whenPhase" => call!(rt, "lifecycle", "lifecycle:whenPhase", LifecycleWhenPhase, Arguments),
1216 "lifecycle:requestShutdown" => {
1217 call!(app, "lifecycle", "lifecycle:requestShutdown", LifecycleRequestShutdown)
1218 },
1219 "lifecycle:advancePhase" | "lifecycle:setPhase" => {
1220 dev_log!("lifecycle", "{}", command);
1221 let NewPhase = arg_u64_or(&Arguments, 0, 1) as u8;
1226 RunTime
1227 .Environment
1228 .ApplicationState
1229 .Feature
1230 .Lifecycle
1231 .AdvanceAndBroadcast(NewPhase, &ApplicationHandle);
1232
1233 if NewPhase >= 3 {
1247 if let Some(MainWindow) = ApplicationHandle.get_webview_window("main") {
1248 if let Ok(false) = MainWindow.is_visible() {
1249 if let Err(Error) = MainWindow.show() {
1250 dev_log!(
1251 "lifecycle",
1252 "warn: [Lifecycle] main window show() failed on phase {}: {}",
1253 NewPhase,
1254 Error
1255 );
1256 } else {
1257 dev_log!(
1258 "lifecycle",
1259 "[Lifecycle] main window revealed on phase {} (hidden-until-ready)",
1260 NewPhase
1261 );
1262 let _ = MainWindow.set_focus();
1263 }
1264 }
1265 }
1266 }
1267
1268 Ok(json!(RunTime.Environment.ApplicationState.Feature.Lifecycle.GetPhase()))
1269 },
1270
1271 "label:getUri" => {
1273 dev_log!("label", "label:getUri");
1274 LabelGetURI(RunTime.clone(), Arguments).await
1275 },
1276 "label:getWorkspace" => {
1277 dev_log!("label", "label:getWorkspace");
1278 LabelGetWorkspace(RunTime.clone()).await
1279 },
1280 "label:getBase" => {
1281 dev_log!("label", "label:getBase");
1282 LabelGetBase(Arguments).await
1283 },
1284
1285 "model:open" => {
1287 dev_log!("model", "model:open");
1288 ModelOpen(RunTime.clone(), Arguments).await
1289 },
1290 "model:close" => {
1291 dev_log!("model", "model:close");
1292 ModelClose(RunTime.clone(), Arguments).await
1293 },
1294 "model:get" => {
1295 dev_log!("model", "model:get");
1296 ModelGet(RunTime.clone(), Arguments).await
1297 },
1298 "model:getAll" => {
1299 dev_log!("model", "model:getAll");
1300 ModelGetAll(RunTime.clone()).await
1301 },
1302 "model:updateContent" => {
1303 dev_log!("model", "model:updateContent");
1304 ModelUpdateContent(RunTime.clone(), Arguments).await
1305 },
1306
1307 "history:goBack" => {
1309 dev_log!("history", "history:goBack");
1310 HistoryGoBack(RunTime.clone()).await
1311 },
1312 "history:goForward" => {
1313 dev_log!("history", "history:goForward");
1314 HistoryGoForward(RunTime.clone()).await
1315 },
1316 "history:canGoBack" => {
1317 dev_log!("history", "history:canGoBack");
1318 HistoryCanGoBack(RunTime.clone()).await
1319 },
1320 "history:canGoForward" => {
1321 dev_log!("history", "history:canGoForward");
1322 HistoryCanGoForward(RunTime.clone()).await
1323 },
1324 "history:push" => {
1325 dev_log!("history", "history:push");
1326 HistoryPush(RunTime.clone(), Arguments).await
1327 },
1328 "history:clear" => {
1329 dev_log!("history", "history:clear");
1330 HistoryClear(RunTime.clone()).await
1331 },
1332 "history:getStack" => {
1333 dev_log!("history", "history:getStack");
1334 HistoryGetStack(RunTime.clone()).await
1335 },
1336
1337 "mountain_get_status" => {
1339 let status = json!({
1340 "connected": true,
1341 "version": "1.0.0"
1342 });
1343 Ok(status)
1344 },
1345 "mountain_get_configuration" => {
1346 let Config = RunTime.Environment.ApplicationState.Configuration.GetGlobalConfiguration();
1348 Ok(Config)
1349 },
1350 "mountain_get_services_status" => {
1351 let CocoonConnected = crate::Vine::Client::IsClientConnected::Fn("cocoon-main");
1352 Ok(json!({
1353 "cocoon": { "connected": CocoonConnected },
1354 "vine": { "running": true }
1355 }))
1356 },
1357 "mountain_get_state" => {
1358 let FolderCount = RunTime
1359 .Environment
1360 .ApplicationState
1361 .Workspace
1362 .WorkspaceFolders
1363 .lock()
1364 .map(|G| G.len())
1365 .unwrap_or(0);
1366 Ok(json!({
1367 "workspace": { "folderCount": FolderCount },
1368 "activeDocument": RunTime.Environment.ApplicationState.Workspace.GetActiveDocumentURI()
1369 }))
1370 },
1371
1372 "file:realpath" => FileRealpath(Arguments).await,
1378 "file:open" => FileOpenFd(Arguments).await,
1379 "file:close" => FileCloseFd(Arguments).await,
1380 "file:cloneFile" => FileCloneNative(Arguments).await,
1381
1382 "nativeHost:pickFolderAndOpen" => NativePickFolder(ApplicationHandle.clone(), Arguments).await,
1388 "nativeHost:pickFileAndOpen" => NativePickFolder(ApplicationHandle.clone(), Arguments).await,
1389 "nativeHost:pickFileFolderAndOpen" => NativePickFolder(ApplicationHandle.clone(), Arguments).await,
1390 "nativeHost:pickWorkspaceAndOpen" => NativePickFolder(ApplicationHandle.clone(), Arguments).await,
1391 "nativeHost:showOpenDialog" => NativeShowOpenDialog(ApplicationHandle.clone(), Arguments).await,
1392
1393 "UserInterface.ShowOpenDialog" => {
1399 match NativeShowOpenDialog(ApplicationHandle.clone(), Arguments).await {
1400 Ok(Response) => {
1401 let Paths = Response
1402 .get("filePaths")
1403 .and_then(|V| V.as_array())
1404 .cloned()
1405 .unwrap_or_default();
1406 Ok(Value::Array(Paths))
1407 },
1408 Err(Error) => Err(Error),
1409 }
1410 },
1411 "nativeHost:showSaveDialog" => NativeShowSaveDialog(ApplicationHandle.clone(), Arguments).await,
1412 "UserInterface.ShowSaveDialog" => {
1415 UserInterfaceShowSaveDialog(ApplicationHandle.clone(), Arguments).await
1416 },
1417 "nativeHost:showMessageBox" => NativeShowMessageBox(ApplicationHandle.clone(), Arguments).await,
1418
1419 "nativeHost:getEnvironmentPaths" => NativeGetEnvironmentPaths(ApplicationHandle.clone()).await,
1421
1422 "nativeHost:getOSColorScheme" => {
1424 dev_log!("nativehost", "nativeHost:getOSColorScheme");
1425 NativeGetColorScheme().await
1426 },
1427 "nativeHost:getOSProperties" => {
1428 dev_log!("nativehost", "nativeHost:getOSProperties");
1429 NativeOSProperties().await
1430 },
1431 "nativeHost:getOSStatistics" => {
1432 dev_log!("nativehost", "nativeHost:getOSStatistics");
1433 NativeOSStatistics().await
1434 },
1435 "nativeHost:getOSVirtualMachineHint" => {
1436 dev_log!("nativehost", "nativeHost:getOSVirtualMachineHint");
1437 Ok(json!(0))
1438 },
1439
1440 "nativeHost:isWindowAlwaysOnTop" => {
1442 dev_log!("window", "nativeHost:isWindowAlwaysOnTop");
1443 Ok(json!(false))
1444 },
1445 "nativeHost:isFullScreen" => {
1446 dev_log!("window", "nativeHost:isFullScreen");
1447 NativeIsFullscreen(ApplicationHandle.clone()).await
1448 },
1449 "nativeHost:isMaximized" => {
1450 dev_log!("window", "nativeHost:isMaximized");
1451 NativeIsMaximized(ApplicationHandle.clone()).await
1452 },
1453 "nativeHost:getActiveWindowId" => {
1454 dev_log!("window", "nativeHost:getActiveWindowId");
1455 Ok(json!(1))
1456 },
1457 "nativeHost:getCursorScreenPoint" => {
1472 dev_log!("window", "nativeHost:getCursorScreenPoint");
1473 Ok(json!({ "x": 0, "y": 0 }))
1478 },
1479 "nativeHost:getWindows" => {
1480 let Title = std::env::var("ProductNameShort").unwrap_or_else(|_| "Land".into());
1481 let ActiveDoc = RunTime
1482 .Environment
1483 .ApplicationState
1484 .Workspace
1485 .GetActiveDocumentURI()
1486 .unwrap_or_default();
1487 Ok(json!([{ "id": 1, "title": Title, "filename": ActiveDoc }]))
1488 },
1489 "nativeHost:getWindowCount" => Ok(json!(1)),
1490
1491 "nativeHost:openAgentsWindow" | "nativeHost:openDevToolsWindow" | "nativeHost:openAuxiliaryWindow" => {
1501 dev_log!("window", "{} (acknowledged, no-op - aux window unsupported)", command);
1502 Ok(Value::Null)
1503 },
1504
1505 "nativeHost:focusWindow" => {
1509 dev_log!("window", "{}", command);
1510 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1511 let _ = Window.set_focus();
1512 }
1513 Ok(Value::Null)
1514 },
1515 "nativeHost:maximizeWindow" => {
1516 dev_log!("window", "{}", command);
1517 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1518 let _ = Window.maximize();
1519 }
1520 Ok(Value::Null)
1521 },
1522 "nativeHost:unmaximizeWindow" => {
1523 dev_log!("window", "{}", command);
1524 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1525 let _ = Window.unmaximize();
1526 }
1527 Ok(Value::Null)
1528 },
1529 "nativeHost:minimizeWindow" => {
1530 dev_log!("window", "{}", command);
1531 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1532 let _ = Window.minimize();
1533 }
1534 Ok(Value::Null)
1535 },
1536 "nativeHost:toggleFullScreen" => {
1537 dev_log!("window", "{}", command);
1538 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1539 let IsFullscreen = Window.is_fullscreen().unwrap_or(false);
1540 let _ = Window.set_fullscreen(!IsFullscreen);
1541 }
1542 Ok(Value::Null)
1543 },
1544 "nativeHost:closeWindow" => {
1545 dev_log!("window", "{}", command);
1546 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1552 let _ = Window.destroy();
1553 }
1554 Ok(Value::Null)
1555 },
1556 "nativeHost:setWindowAlwaysOnTop" => {
1557 dev_log!("window", "{}", command);
1558 let OnTop = arg_bool(&Arguments, 0);
1559 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1560 let _ = Window.set_always_on_top(OnTop);
1561 }
1562 Ok(Value::Null)
1563 },
1564 "nativeHost:toggleWindowAlwaysOnTop" => {
1565 dev_log!("window", "{}", command);
1566 static ALWAYS_ON_TOP:std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
1567 let Next = !ALWAYS_ON_TOP.fetch_xor(true, std::sync::atomic::Ordering::Relaxed);
1568 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1569 let _ = Window.set_always_on_top(Next);
1570 }
1571 Ok(Value::Null)
1572 },
1573 "nativeHost:setRepresentedFilename" => {
1577 dev_log!("window", "{}", command);
1578 let Path = arg_string(&Arguments, 0);
1579 if !Path.is_empty() {
1580 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1581 let Filename = std::path::Path::new(&Path)
1584 .file_name()
1585 .and_then(|N| N.to_str())
1586 .unwrap_or(&Path);
1587 let _ = Window.set_title(Filename);
1588 }
1589 }
1590 Ok(Value::Null)
1591 },
1592
1593 "nativeHost:setDocumentEdited" => {
1597 let _ = Arguments;
1598 Ok(Value::Null)
1599 },
1600
1601 "nativeHost:setMinimumSize" => {
1604 let Width = arg_u64_or(&Arguments, 0, 400) as u32;
1605 let Height = arg_u64_or(&Arguments, 1, 300) as u32;
1606 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1607 let _ = Window.set_min_size(Some(tauri::Size::Physical(tauri::PhysicalSize {
1608 width:Width,
1609 height:Height,
1610 })));
1611 }
1612 Ok(Value::Null)
1613 },
1614
1615 "nativeHost:positionWindow" => {
1618 if let Some(Rect) = Arguments.first() {
1619 let X = Rect.get("x").and_then(|V| V.as_i64()).unwrap_or(0) as i32;
1620 let Y = Rect.get("y").and_then(|V| V.as_i64()).unwrap_or(0) as i32;
1621 let W = Rect.get("width").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1622 let H = Rect.get("height").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1623 if let Some(Window) = ApplicationHandle.get_webview_window("main") {
1624 let _ =
1625 Window.set_position(tauri::Position::Physical(tauri::PhysicalPosition { x:X, y:Y }));
1626 if W > 0 && H > 0 {
1627 let _ =
1628 Window.set_size(tauri::Size::Physical(tauri::PhysicalSize { width:W, height:H }));
1629 }
1630 }
1631 }
1632 Ok(Value::Null)
1633 },
1634
1635 "nativeHost:updateWindowControls"
1637 | "nativeHost:notifyReady"
1638 | "nativeHost:saveWindowSplash"
1639 | "nativeHost:updateTouchBar"
1640 | "nativeHost:moveWindowTop"
1641 | "nativeHost:setBackgroundThrottling"
1642 | "nativeHost:updateWindowAccentColor" => {
1643 dev_log!("window", "{}", command);
1644 Ok(Value::Null)
1645 },
1646
1647 "nativeHost:isAdmin" => Ok(json!(false)),
1649 "nativeHost:isRunningUnderARM64Translation" => NativeIsRunningUnderARM64Translation().await,
1650 "nativeHost:hasWSLFeatureInstalled" => {
1651 #[cfg(target_os = "windows")]
1652 {
1653 Ok(json!(std::path::Path::new("C:\\Windows\\System32\\wsl.exe").exists()))
1654 }
1655 #[cfg(not(target_os = "windows"))]
1656 {
1657 Ok(json!(false))
1658 }
1659 },
1660 "nativeHost:showItemInFolder" => ShowItemInFolder(RunTime.clone(), Arguments).await,
1661 "nativeHost:openExternal" => OpenExternal(RunTime.clone(), Arguments).await,
1662 "nativeHost:moveItemToTrash" => {
1664 dev_log!("nativehost", "nativeHost:moveItemToTrash");
1665 NativeMoveItemToTrash(Arguments).await
1666 },
1667
1668 "nativeHost:readClipboardText" => {
1670 dev_log!("clipboard", "readClipboardText");
1671 NativeReadClipboardText(Arguments).await
1672 },
1673 "nativeHost:writeClipboardText" => {
1674 dev_log!("clipboard", "writeClipboardText");
1675 NativeWriteClipboardText(Arguments).await
1676 },
1677 "nativeHost:readClipboardFindText" => {
1678 dev_log!("clipboard", "readClipboardFindText");
1679 NativeReadClipboardFindText(Arguments).await
1680 },
1681 "nativeHost:writeClipboardFindText" => {
1682 dev_log!("clipboard", "writeClipboardFindText");
1683 NativeWriteClipboardFindText(Arguments).await
1684 },
1685 "nativeHost:readClipboardBuffer" => {
1686 dev_log!("clipboard", "readClipboardBuffer");
1687 NativeReadClipboardBuffer(Arguments).await
1688 },
1689 "nativeHost:writeClipboardBuffer" => {
1690 dev_log!("clipboard", "writeClipboardBuffer");
1691 NativeWriteClipboardBuffer(Arguments).await
1692 },
1693 "nativeHost:hasClipboard" => {
1694 dev_log!("clipboard", "hasClipboard");
1695 NativeHasClipboard(Arguments).await
1696 },
1697 "nativeHost:readImage" => {
1698 dev_log!("clipboard", "readImage");
1699 NativeReadImage(Arguments).await
1700 },
1701 "nativeHost:triggerPaste" => {
1702 dev_log!("clipboard", "triggerPaste");
1703 NativeTriggerPaste(Arguments).await
1704 },
1705
1706 "nativeHost:getProcessId" => Ok(json!(std::process::id())),
1708 "nativeHost:killProcess" => KillProcess(Arguments).await,
1709
1710 "nativeHost:findFreePort" => NativeFindFreePort(Arguments).await,
1712 "nativeHost:isPortFree" => {
1713 let Port = arg_u64(&Arguments, 0) as u16;
1714 if Port == 0 {
1715 Ok(json!(false))
1716 } else {
1717 let Free = tokio::net::TcpListener::bind(std::net::SocketAddr::from(([127, 0, 0, 1], Port)))
1718 .await
1719 .is_ok();
1720 Ok(json!(Free))
1721 }
1722 },
1723 "nativeHost:resolveProxy" => {
1728 let Url = arg_str(&Arguments, 0);
1729 let Scheme = if Url.starts_with("https") { "HTTPS" } else { "HTTP" };
1730 let ProxyEnv = std::env::var(format!("{}_PROXY", Scheme))
1731 .or_else(|_| std::env::var(format!("{}_proxy", Scheme.to_lowercase())))
1732 .or_else(|_| std::env::var("ALL_PROXY"))
1733 .or_else(|_| std::env::var("all_proxy"));
1734 match ProxyEnv {
1735 Ok(P) if !P.is_empty() => {
1736 let Lower = P.to_lowercase();
1740 let (Keyword, Host) = if Lower.starts_with("socks") {
1741 let H = P
1742 .trim_start_matches("socks5://")
1743 .trim_start_matches("socks4://")
1744 .trim_start_matches("socks://");
1745
1746 ("SOCKS", H)
1747 } else {
1748 let H = P.trim_start_matches("http://").trim_start_matches("https://");
1749
1750 ("PROXY", H)
1751 };
1752
1753 Ok(json!(format!("{} {}", Keyword, Host)))
1754 },
1755 _ => Ok(json!("DIRECT")),
1756 }
1757 },
1758 "nativeHost:lookupAuthorization" => Ok(Value::Null),
1759 "nativeHost:lookupKerberosAuthorization" => Ok(Value::Null),
1760 "nativeHost:loadCertificates" => Ok(json!([])),
1761
1762 "nativeHost:relaunch" => Relaunch(ApplicationHandle.clone(), Arguments).await,
1764 "nativeHost:reload" => Reload(ApplicationHandle.clone(), Arguments).await,
1765 "nativeHost:quit" => Quit(ApplicationHandle.clone(), Arguments).await,
1766 "nativeHost:exit" => Exit(ApplicationHandle.clone(), Arguments).await,
1767
1768 "nativeHost:openDevTools" => OpenDevTools(ApplicationHandle.clone(), Arguments).await,
1770 "nativeHost:toggleDevTools" => ToggleDevTools(ApplicationHandle.clone(), Arguments).await,
1771
1772 "nativeHost:getSystemIdleState" => Ok(json!("active")),
1774 "nativeHost:getSystemIdleTime" => Ok(json!(0)),
1775 "nativeHost:getCurrentThermalState" => Ok(json!("nominal")),
1776 "nativeHost:isOnBatteryPower" => Ok(json!(false)),
1777 "nativeHost:startPowerSaveBlocker" => Ok(json!(0)),
1778 "nativeHost:stopPowerSaveBlocker" => Ok(json!(false)),
1779 "nativeHost:isPowerSaveBlockerStarted" => Ok(json!(false)),
1780
1781 "browserView:updateKeybindings"
1786 | "browserView:updateTheme"
1787 | "browserView:updateConfiguration"
1788 | "browserView:openDevTools"
1789 | "browserView:closeDevTools" => Ok(Value::Null),
1790 "browserView:getBrowserViews" => Ok(serde_json::json!([])),
1791
1792 "nativeHost:newWindowTab" => Ok(Value::Null),
1794 "nativeHost:showPreviousWindowTab" => Ok(Value::Null),
1795 "nativeHost:showNextWindowTab" => Ok(Value::Null),
1796 "nativeHost:moveWindowTabToNewWindow" => Ok(Value::Null),
1797 "nativeHost:mergeAllWindowTabs" => Ok(Value::Null),
1798 "nativeHost:toggleWindowTabsBar" => Ok(Value::Null),
1799 "nativeHost:installShellCommand" => InstallShellCommand(Arguments).await,
1800 "nativeHost:uninstallShellCommand" => UninstallShellCommand(Arguments).await,
1801
1802 "localPty:getProfiles" => {
1806 dev_log!("terminal", "localPty:getProfiles");
1807 LocalPTYGetProfiles().await
1808 },
1809 "localPty:getDefaultSystemShell" => {
1810 dev_log!("terminal", "localPty:getDefaultSystemShell");
1811 LocalPTYGetDefaultShell().await
1812 },
1813 "localPty:getTerminalLayoutInfo" => {
1821 dev_log!("terminal", "localPty:getTerminalLayoutInfo");
1822 use CommonLibrary::{Environment::Requires::Requires, Storage::StorageProvider::StorageProvider};
1823 let StorageProvider:Arc<dyn StorageProvider> = RunTime.Environment.Require();
1824 match StorageProvider.GetStorageValue(true, "terminal:layoutInfo").await {
1825 Ok(Some(Stored)) => Ok(Stored),
1826 Ok(None) => Ok(Value::Null),
1827 Err(Error) => {
1828 dev_log!("terminal", "warn: [getTerminalLayoutInfo] storage read failed: {}", Error);
1829 Ok(Value::Null)
1830 },
1831 }
1832 },
1833 "localPty:setTerminalLayoutInfo" => {
1836 dev_log!("terminal", "localPty:setTerminalLayoutInfo");
1837 use CommonLibrary::{Environment::Requires::Requires, Storage::StorageProvider::StorageProvider};
1838 let StorageProvider:Arc<dyn StorageProvider> = RunTime.Environment.Require();
1839 let Payload = arg_val(&Arguments, 0);
1840 let _ = StorageProvider
1841 .UpdateStorageValue(true, "terminal:layoutInfo".to_string(), Some(Payload))
1842 .await;
1843 Ok(Value::Null)
1844 },
1845 "localPty:getPerformanceMarks" => {
1846 dev_log!("terminal", "localPty:getPerformanceMarks");
1847 Ok(json!([]))
1848 },
1849 "localPty:reduceConnectionGraceTime" => {
1850 dev_log!("terminal", "localPty:reduceConnectionGraceTime");
1851 Ok(Value::Null)
1852 },
1853 "localPty:listProcesses" => {
1854 dev_log!("terminal", "localPty:listProcesses");
1855 Ok(json!([]))
1856 },
1857 "localPty:getEnvironment" => {
1858 dev_log!("terminal", "localPty:getEnvironment");
1859 LocalPTYGetEnvironment().await
1860 },
1861 "localPty:getLatency" => {
1873 dev_log!("terminal", "localPty:getLatency");
1874 Ok(json!([]))
1875 },
1876
1877 "cocoon:request" => {
1888 dev_log!("ipc", "cocoon:request method={:?}", Arguments.first());
1889 CocoonRequest(Arguments).await
1890 },
1891 "cocoon:notify" => {
1892 dev_log!("ipc", "cocoon:notify method={:?}", Arguments.first());
1893 CocoonNotify(Arguments).await
1894 },
1895
1896 "localPty:spawn" => call!(rt, "terminal", TerminalCreate, Arguments),
1916 "localPty:createProcess" => call!(rt, "terminal", LocalPTYCreateProcess, Arguments),
1917 "localPty:start" => {
1918 dev_log!("terminal", "{} no-op (eager-spawn)", command);
1931 Ok(Value::Null)
1932 },
1933 "localPty:input" | "localPty:write" => call!(rt, "terminal", TerminalSendText, Arguments),
1934 "localPty:shutdown" | "localPty:dispose" => call!(rt, "terminal", TerminalDispose, Arguments),
1935 "localPty:resize" => call!(rt, "terminal", "localPty:resize", LocalPTYResize, Arguments),
1936 "localPty:acknowledgeDataEvent" => {
1937 Ok(Value::Null)
1939 },
1940 "localPty:getBackendOS" => {
1945 #[cfg(target_os = "macos")]
1946 {
1947 Ok(json!(1))
1948 }
1949 #[cfg(target_os = "linux")]
1950 {
1951 Ok(json!(2))
1952 }
1953 #[cfg(target_os = "windows")]
1954 {
1955 Ok(json!(3))
1956 }
1957 #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
1958 {
1959 Ok(json!(2))
1960 }
1961 },
1962
1963 "localPty:refreshProperty" => {
1973 use CommonLibrary::{
1974 Environment::Requires::Requires,
1975 Terminal::TerminalProvider::TerminalProvider,
1976 };
1977 let TerminalId = arg_u64(&Arguments, 0);
1978 let PropId = arg_u64(&Arguments, 1);
1979 if TerminalId == 0 {
1980 Ok(Value::Null)
1981 } else if PropId == 0 {
1982 let Cwd = RunTime
1984 .Environment
1985 .ApplicationState
1986 .Feature
1987 .Terminals
1988 .ActiveTerminals
1989 .lock()
1990 .ok()
1991 .and_then(|G| G.get(&TerminalId).cloned())
1992 .and_then(|S| S.lock().ok().and_then(|S| S.CurrentWorkingDirectory.clone()))
1993 .map(|P| P.to_string_lossy().into_owned());
1994 Ok(Cwd.map(|C| json!(C)).unwrap_or(Value::Null))
1995 } else if PropId == 1 {
1996 let Provider:Arc<dyn TerminalProvider> = RunTime.Environment.Require();
1998 match Provider.GetTerminalProcessId(TerminalId).await {
1999 Ok(Some(Pid)) => Ok(json!(Pid)),
2000 _ => Ok(Value::Null),
2001 }
2002 } else {
2003 Ok(Value::Null)
2004 }
2005 },
2006
2007 "localPty:updateProperty" => Ok(Value::Null),
2010
2011 "localPty:freePortKillProcess" => {
2014 dev_log!("terminal", "localPty:freePortKillProcess");
2015 LocalPTYFreePortKillProcess(Arguments).await
2016 },
2017
2018 "localPty:serializeTerminalState" => {
2023 dev_log!("terminal", "localPty:serializeTerminalState");
2024 SerializeTerminalState(RunTime.clone()).await
2025 },
2026
2027 "localPty:reviveTerminalProcesses" => {
2031 dev_log!(
2032 "terminal",
2033 "localPty:reviveTerminalProcesses count={}",
2034 Arguments.first().and_then(|V| V.as_array()).map(|A| A.len()).unwrap_or(0)
2035 );
2036 ReviveTerminalProcesses(RunTime.clone(), Arguments).await
2037 },
2038
2039 "localPty:getRevivedPtyNewId" => {
2045 let NewId = RunTime.Environment.ApplicationState.GetNextTerminalIdentifier();
2046 dev_log!("terminal", "localPty:getRevivedPtyNewId id={}", NewId);
2047 Ok(json!(NewId))
2048 },
2049
2050 "localPty:attachToProcess" => {
2056 dev_log!("terminal", "localPty:attachToProcess");
2057 AttachToProcess(RunTime.clone(), Arguments).await
2058 },
2059 "localPty:detachFromProcess" => {
2060 dev_log!("terminal", "localPty:detachFromProcess");
2061 DetachFromProcess(RunTime.clone(), Arguments).await
2062 },
2063
2064 "localPty:setActive" => {
2068 let TermId = Arguments.first().and_then(Value::as_i64);
2069 let Payload = match TermId {
2070 Some(Id) => serde_json::json!({ "id": Id }),
2071 None => serde_json::json!({ "id": null }),
2072 };
2073 let _ = crate::Vine::Client::SendNotification::Fn(
2074 "cocoon-main".to_string(),
2075 "$acceptActiveTerminalChanged".to_string(),
2076 Payload,
2077 )
2078 .await;
2079 Ok(Value::Null)
2080 },
2081
2082 "localPty:setShellIntegrationActive" => {
2087 let TermId = Arguments.first().and_then(Value::as_i64).unwrap_or(0) as u64;
2088 let _ = crate::Vine::Client::SendNotification::Fn(
2089 "cocoon-main".to_string(),
2090 "$acceptTerminalShellIntegrationActivated".to_string(),
2091 serde_json::json!({ "id": TermId }),
2092 )
2093 .await;
2094 Ok(Value::Null)
2095 },
2096
2097 "localPty:setInteracted" => {
2104 let Payload = arg_val(&Arguments, 0);
2105 let _ = crate::Vine::Client::SendNotification::Fn(
2106 "cocoon-main".to_string(),
2107 "$acceptTerminalStateChanged".to_string(),
2108 Payload,
2109 )
2110 .await;
2111 Ok(Value::Null)
2112 },
2113
2114 "localPty:setCwd" => {
2119 let TermId = Arguments.first().and_then(Value::as_i64).unwrap_or(0) as u64;
2120 let Cwd = Arguments.get(1).and_then(Value::as_str).unwrap_or("").to_string();
2121 if !Cwd.is_empty() {
2122 if let Ok(Guard) = RunTime.Environment.ApplicationState.Feature.Terminals.ActiveTerminals.lock()
2125 {
2126 if let Some(StateEntry) = Guard.get(&TermId) {
2127 if let Ok(mut State) = StateEntry.lock() {
2128 State.CurrentWorkingDirectory = Some(std::path::PathBuf::from(&Cwd));
2129 }
2130 }
2131 }
2132 let _ = crate::Vine::Client::SendNotification::Fn(
2133 "cocoon-main".to_string(),
2134 "$acceptTerminalCwdChange".to_string(),
2135 serde_json::json!({ "id": TermId, "cwd": Cwd }),
2136 )
2137 .await;
2138 }
2139 Ok(Value::Null)
2140 },
2141
2142 "localPty:processBinary"
2146 | "localPty:orphanQuestionReply"
2147 | "localPty:updateTitle"
2148 | "localPty:updateIcon"
2149 | "localPty:installAutoReply"
2150 | "localPty:uninstallAllAutoReplies" => Ok(Value::Null),
2151
2152 "localPty:shellExecutionStart" => {
2160 let Payload = arg_val(&Arguments, 0);
2161 let _ = crate::Vine::Client::SendNotification::Fn(
2162 "cocoon-main".to_string(),
2163 "$acceptTerminalShellExecutionStart".to_string(),
2164 Payload,
2165 )
2166 .await;
2167 Ok(Value::Null)
2168 },
2169
2170 "localPty:shellExecutionEnd" => {
2181 let Payload = arg_val(&Arguments, 0);
2182 let _ = crate::Vine::Client::SendNotification::Fn(
2183 "cocoon-main".to_string(),
2184 "$acceptTerminalShellExecutionEnd".to_string(),
2185 Payload.clone(),
2186 )
2187 .await;
2188 let _ = crate::Vine::Client::SendNotification::Fn(
2189 "cocoon-main".to_string(),
2190 "$acceptExecutedTerminalCommand".to_string(),
2191 Payload,
2192 )
2193 .await;
2194 Ok(Value::Null)
2195 },
2196
2197 "update:_getInitialState" => UpdateGetInitialState().await,
2201 "update:isLatestVersion" => UpdateIsLatestVersion().await,
2202 "update:checkForUpdates" => UpdateCheckForUpdates().await,
2203 "update:downloadUpdate" => UpdateDownloadUpdate().await,
2204 "update:applyUpdate" => UpdateApplyUpdate().await,
2205 "update:quitAndInstall" => UpdateQuitAndInstall().await,
2206
2207 "url:registerExternalUriOpener" => {
2219 dev_log!("url", "url:registerExternalUriOpener");
2220 Ok(Value::Null)
2221 },
2222
2223 "encryption:encrypt" => Encrypt(Arguments).await,
2227 "encryption:decrypt" => Decrypt(Arguments).await,
2228
2229 "process:getPlatform" => {
2236 Ok(json!(match std::env::consts::OS {
2237 "windows" => "win32",
2238 "macos" => "darwin",
2239 _ => "linux",
2240 }))
2241 },
2242
2243 "process:getArch" => {
2244 Ok(json!(match std::env::consts::ARCH {
2245 "x86_64" => "x64",
2246 "aarch64" => "arm64",
2247 "x86" => "ia32",
2248 _ => "x64",
2249 }))
2250 },
2251
2252 "process:getPid" => Ok(json!(std::process::id())),
2253
2254 "process:getExecPath" => {
2255 Ok(json!(
2256 std::env::current_exe().unwrap_or_default().to_string_lossy().into_owned()
2257 ))
2258 },
2259
2260 "process:getMemoryInfo" => {
2261 Ok(json!({
2265 "workingSetSize": 0u64,
2266 "peakWorkingSetSize": 0u64,
2267 "privateBytes": 0u64,
2268 "sharedBytes": 0u64,
2269 }))
2270 },
2271
2272 "process:getCpuInfo" => {
2273 Ok(json!([{
2275 "model": format!("{} ({})", std::env::consts::ARCH, std::env::consts::OS),
2276 "speed": 0u32,
2277 "times": { "user": 0u64, "nice": 0u64, "sys": 0u64, "idle": 0u64, "irq": 0u64 },
2278 }]))
2279 },
2280
2281 "extensionHostStarter:createExtensionHost" => {
2285 dev_log!("exthost", "extensionHostStarter:createExtensionHost");
2286 ExtensionHostStarterCreate(Arguments).await
2287 },
2288 "extensionHostStarter:start" => {
2289 dev_log!("exthost", "extensionHostStarter:start");
2290 ExtensionHostStarterStart(Arguments).await
2291 },
2292 "extensionHostStarter:kill" => {
2293 dev_log!("exthost", "extensionHostStarter:kill");
2294 ExtensionHostStarterKill(Arguments).await
2295 },
2296 "extensionHostStarter:getExitInfo" => {
2297 dev_log!("exthost", "extensionHostStarter:getExitInfo");
2298 ExtensionHostStarterGetExitInfo(Arguments).await
2299 },
2300 "extensionHostStarter:waitForExit" => {
2301 dev_log!("exthost", "extensionHostStarter:waitForExit");
2302 ExtensionHostStarterWaitForExit(Arguments).await
2303 },
2304
2305 "cocoon:extensionHostMessage" => {
2309 dev_log!("exthost", "cocoon:extensionHostMessage");
2310 CocoonExtensionHostMessage(ApplicationHandle.clone(), Arguments).await
2311 },
2312
2313 "extensionhostdebugservice:reload" => {
2317 dev_log!("exthost", "extensionhostdebugservice:reload");
2318 ExtensionHostDebugReload(ApplicationHandle.clone()).await
2319 },
2320 "extensionhostdebugservice:close" => {
2321 dev_log!("exthost", "extensionhostdebugservice:close");
2322 ExtensionHostDebugClose(ApplicationHandle.clone()).await
2323 },
2324 "extensionhostdebugservice:attachSession" | "extensionhostdebugservice:terminateSession" => {
2325 dev_log!("exthost", "{}", command);
2326 Ok(Value::Null)
2327 },
2328
2329 "workspaces:getRecentlyOpened" => {
2333 dev_log!("workspaces", "workspaces:getRecentlyOpened");
2334 ReadRecentlyOpened()
2335 },
2336 "workspaces:removeRecentlyOpened" => {
2337 dev_log!("workspaces", "workspaces:removeRecentlyOpened");
2338 let Uri = arg_string(&Arguments, 0);
2339 if !Uri.is_empty() {
2340 MutateRecentlyOpened(|List| {
2341 if let Some(Workspaces) = List.get_mut("workspaces").and_then(|V| V.as_array_mut()) {
2342 Workspaces
2343 .retain(|Entry| Entry.get("uri").and_then(|V| V.as_str()).unwrap_or("") != Uri);
2344 }
2345 if let Some(Files) = List.get_mut("files").and_then(|V| V.as_array_mut()) {
2346 Files.retain(|Entry| Entry.get("uri").and_then(|V| V.as_str()).unwrap_or("") != Uri);
2347 }
2348 });
2349 }
2350 Ok(Value::Null)
2351 },
2352 "workspaces:addRecentlyOpened" => {
2353 dev_log!("workspaces", "workspaces:addRecentlyOpened");
2354 let Entries:Vec<Value> = Arguments.first().and_then(|V| V.as_array()).cloned().unwrap_or_default();
2356 if !Entries.is_empty() {
2357 MutateRecentlyOpened(|List| {
2358 let Workspaces = List
2359 .get_mut("workspaces")
2360 .and_then(|V| V.as_array_mut())
2361 .map(|V| std::mem::take(V))
2362 .unwrap_or_default();
2363 let Files = List
2364 .get_mut("files")
2365 .and_then(|V| V.as_array_mut())
2366 .map(|V| std::mem::take(V))
2367 .unwrap_or_default();
2368 let mut MergedWorkspaces = Workspaces;
2369 let mut MergedFiles = Files;
2370 for Entry in Entries {
2371 let Folder = Entry
2372 .get("folderUri")
2373 .cloned()
2374 .or_else(|| Entry.get("workspace").and_then(|W| W.get("configPath").cloned()));
2375 let File = Entry.get("fileUri").cloned();
2376 if let Some(FolderUri) = Folder.and_then(|V| v_str(&V)) {
2377 MergedWorkspaces
2378 .retain(|E| E.get("uri").and_then(|V| V.as_str()).unwrap_or("") != FolderUri);
2379 let mut Item = serde_json::Map::new();
2380 Item.insert("uri".into(), json!(FolderUri));
2381 if let Some(Label) = Entry.get("label").and_then(|V| V.as_str()) {
2382 Item.insert("label".into(), json!(Label));
2383 }
2384 MergedWorkspaces.insert(0, Value::Object(Item));
2385 }
2386 if let Some(FileUri) = File.and_then(|V| v_str(&V)) {
2387 MergedFiles
2388 .retain(|E| E.get("uri").and_then(|V| V.as_str()).unwrap_or("") != FileUri);
2389 let mut Item = serde_json::Map::new();
2390 Item.insert("uri".into(), json!(FileUri));
2391 MergedFiles.insert(0, Value::Object(Item));
2392 }
2393 }
2394 MergedWorkspaces.truncate(50);
2397 MergedFiles.truncate(50);
2398 List.insert("workspaces".into(), Value::Array(MergedWorkspaces));
2399 List.insert("files".into(), Value::Array(MergedFiles));
2400 });
2401 }
2402 Ok(Value::Null)
2403 },
2404 "workspaces:clearRecentlyOpened" => {
2405 dev_log!("workspaces", "workspaces:clearRecentlyOpened");
2406 MutateRecentlyOpened(|List| {
2407 List.insert("workspaces".into(), json!([]));
2408 List.insert("files".into(), json!([]));
2409 });
2410 Ok(Value::Null)
2411 },
2412 "workspaces:enterWorkspace" => {
2413 dev_log!("workspaces", "workspaces:enterWorkspace");
2414 Ok(Value::Null)
2415 },
2416 "workspaces:createUntitledWorkspace" => {
2417 dev_log!("workspaces", "workspaces:createUntitledWorkspace");
2418 Ok(Value::Null)
2419 },
2420 "workspaces:deleteUntitledWorkspace" => {
2421 dev_log!("workspaces", "workspaces:deleteUntitledWorkspace");
2422 Ok(Value::Null)
2423 },
2424 "workspaces:getWorkspaceIdentifier" => {
2425 let Workspace = &RunTime.Environment.ApplicationState.Workspace;
2432 let Folders = Workspace.GetWorkspaceFolders();
2433 if let Some(First) = Folders.first() {
2434 use std::{
2435 collections::hash_map::DefaultHasher,
2436 hash::{Hash, Hasher},
2437 };
2438 let mut Hasher = DefaultHasher::new();
2439 First.URI.as_str().hash(&mut Hasher);
2440 let Id = format!("{:016x}", Hasher.finish());
2441 Ok(json!({
2442 "id": Id,
2443 "configPath": Value::Null,
2444 "uri": First.URI.to_string(),
2445 }))
2446 } else {
2447 Ok(Value::Null)
2448 }
2449 },
2450 "workspaces:getDirtyWorkspaces" => Ok(json!([])),
2451
2452 "git:exec" | "git:clone" | "git:pull" | "git:checkout" | "git:revParse" | "git:fetch"
2464 | "git:revListCount" | "git:cancel" | "git:isAvailable"
2465 if tier_routes_to_node(TIER_SCM, "TierSCM") =>
2466 {
2467 forward_to_cocoon!("scm", command, Arguments)
2468 },
2469 "git:exec" => {
2470 dev_log!("git", "git:exec");
2471 Git::HandleExec::Fn(Arguments).await
2472 },
2473 "git:clone" => {
2474 dev_log!("git", "git:clone");
2475 Git::HandleClone::Fn(Arguments).await
2476 },
2477 "git:pull" => {
2478 dev_log!("git", "git:pull");
2479 Git::HandlePull::Fn(Arguments).await
2480 },
2481 "git:checkout" => {
2482 dev_log!("git", "git:checkout");
2483 Git::HandleCheckout::Fn(Arguments).await
2484 },
2485 "git:revParse" => {
2486 dev_log!("git", "git:revParse");
2487 Git::HandleRevParse::Fn(Arguments).await
2488 },
2489 "git:fetch" => {
2490 dev_log!("git", "git:fetch");
2491 Git::HandleFetch::Fn(Arguments).await
2492 },
2493 "git:revListCount" => {
2494 dev_log!("git", "git:revListCount");
2495 Git::HandleRevListCount::Fn(Arguments).await
2496 },
2497 "git:cancel" => {
2498 dev_log!("git", "git:cancel");
2499 Git::HandleCancel::Fn(Arguments).await
2500 },
2501 "git:isAvailable" => {
2502 dev_log!("git", "git:isAvailable");
2503 Git::HandleIsAvailable::Fn(Arguments).await
2504 },
2505
2506 "tree:getChildren" => TreeGetChildren(ApplicationHandle.clone(), RunTime.clone(), Arguments).await,
2513
2514 "tree.reveal" | "tree:reveal" => {
2517 use tauri::Emitter;
2518 let ViewId = arg_string(&Arguments, 0);
2519 let Handle = arg_string(&Arguments, 1);
2520 let Options = arg_val(&Arguments, 2);
2521 dev_log!("ipc", "tree.reveal viewId={} handle={}", ViewId, Handle);
2522 let _ = ApplicationHandle.emit(
2523 "sky://tree-view/reveal",
2524 json!({
2525 "viewId": ViewId,
2526 "handle": Handle,
2527 "options": Options,
2528 }),
2529 );
2530 Ok(Value::Null)
2531 },
2532
2533 "tree:selectionChanged" | "tree:collapseElement" | "tree:expandElement" | "tree:visibilityChanged" => {
2537 let Payload = arg_val(&Arguments, 0);
2538 let Method = match command.as_str() {
2539 "tree:selectionChanged" => "$treeView:selectionChanged",
2540 "tree:collapseElement" => "$treeView:collapseElement",
2541 "tree:expandElement" => "$treeView:expandElement",
2542 _ => "$treeView:visibilityChanged",
2543 };
2544 tokio::spawn(async move {
2545 if let Err(E) = crate::Vine::Client::SendNotification::Fn(
2546 "cocoon-main".to_string(),
2547 Method.to_string(),
2548 Payload,
2549 )
2550 .await
2551 {
2552 dev_log!("ipc", "warn: [tree] Cocoon notify {} failed: {:?}", Method, E);
2553 }
2554 });
2555 Ok(Value::Null)
2556 },
2557
2558 "sky:replay-events" => SkyReplayEvents(ApplicationHandle.clone(), RunTime.clone()).await,
2560
2561 "editor:revealRange" | "window:revealRange" => {
2565 use tauri::Emitter;
2566 let Payload = arg_val(&Arguments, 0);
2567 let _ = ApplicationHandle.emit("sky://editor/revealRange", &Payload);
2568 Ok(Value::Null)
2569 },
2570
2571 "sky:editor:selectionChanged" => {
2579 let Uri = Arguments
2580 .first()
2581 .and_then(|V| V.get("uri"))
2582 .and_then(|V| V.as_str())
2583 .unwrap_or("")
2584 .to_string();
2585 let Selections = Arguments
2586 .first()
2587 .and_then(|V| V.get("selections"))
2588 .cloned()
2589 .unwrap_or(Value::Array(Vec::new()));
2590 dev_log!("model", "[SelectionChanged] uri={}", Uri);
2591 if !Uri.is_empty() {
2593 RunTime
2594 .Environment
2595 .ApplicationState
2596 .Workspace
2597 .SetActiveDocumentURI(Some(Uri.clone()));
2598 }
2599 let ViewColumn = Arguments
2600 .first()
2601 .and_then(|V| V.get("viewColumn"))
2602 .and_then(|V| V.as_u64())
2603 .unwrap_or(1);
2604 let Payload = json!({ "uri": Uri, "selections": Selections, "viewColumn": ViewColumn });
2608 let _ = crate::Vine::Client::SendNotification::Fn(
2609 "cocoon-main".to_string(),
2610 "window.didChangeTextEditorSelection".to_string(),
2611 Payload,
2612 )
2613 .await;
2614 Ok(Value::Null)
2615 },
2616
2617 "sky:model:contentChanged" => {
2623 let Payload = arg_val(&Arguments, 0);
2624 let Uri = Payload.get("uri").and_then(Value::as_str).unwrap_or("").to_string();
2625 if !Uri.is_empty() {
2626 let Content = Payload.get("content").and_then(Value::as_str).unwrap_or("").to_string();
2627 let Version = Payload.get("version").and_then(Value::as_i64).unwrap_or(1);
2628 if let Some(mut Doc) = RunTime.Environment.ApplicationState.Feature.Documents.Get(&Uri) {
2630 Doc.Version = Version;
2631 Doc.Lines = Content.lines().map(|L| L.to_owned()).collect();
2632 Doc.IsDirty = true;
2633 RunTime
2634 .Environment
2635 .ApplicationState
2636 .Feature
2637 .Documents
2638 .AddOrUpdate(Uri.clone(), Doc);
2639 }
2640 let Payload2 = json!([
2642 { "external": Uri.clone(), "$mid": 1 },
2643
2644 { "content": Content, "versionId": Version, "isDirty": true, "changes": [] }
2645 ]);
2646 tokio::spawn(async move {
2647 let _ = crate::Vine::Client::SendNotification::Fn(
2648 "cocoon-main".to_string(),
2649 "$acceptModelChanged".to_string(),
2650 Payload2,
2651 )
2652 .await;
2653 });
2654 }
2655 Ok(Value::Null)
2656 },
2657
2658 "sky:editor:activeChanged" => {
2659 let Payload = arg_val(&Arguments, 0);
2660 let Uri = Payload.get("uri").and_then(Value::as_str).unwrap_or("").to_string();
2661 dev_log!("model", "[ActiveEditorChanged] uri={}", Uri);
2662 if !Uri.is_empty() {
2663 RunTime
2664 .Environment
2665 .ApplicationState
2666 .Workspace
2667 .SetActiveDocumentURI(Some(Uri.clone()));
2668 }
2669 let _ = crate::Vine::Client::SendNotification::Fn(
2670 "cocoon-main".to_string(),
2671 "window.didChangeActiveTextEditor".to_string(),
2672 Payload,
2673 )
2674 .await;
2675 Ok(Value::Null)
2676 },
2677
2678 "sky:editor:visibleChanged" => {
2689 let Payload = arg_val(&Arguments, 0);
2690 let _ = crate::Vine::Client::SendNotification::Fn(
2691 "cocoon-main".to_string(),
2692 "$acceptVisibleEditorsChanged".to_string(),
2693 Payload,
2694 )
2695 .await;
2696 Ok(Value::Null)
2697 },
2698
2699 "sky:editor:tabsChanged" => {
2709 let Payload = arg_val(&Arguments, 0);
2710 let _ = crate::Vine::Client::SendNotification::Fn(
2711 "cocoon-main".to_string(),
2712 "$acceptTabsChanged".to_string(),
2713 Payload,
2714 )
2715 .await;
2716 Ok(Value::Null)
2717 },
2718
2719 "sky:editor:visibleRangesChanged" => {
2728 let Payload = arg_val(&Arguments, 0);
2729 let _ = crate::Vine::Client::SendNotification::Fn(
2730 "cocoon-main".to_string(),
2731 "$acceptVisibleRangesChanged".to_string(),
2732 Payload,
2733 )
2734 .await;
2735 Ok(Value::Null)
2736 },
2737
2738 "sky:editor:optionsChanged" => {
2747 let Payload = arg_val(&Arguments, 0);
2748 let _ = crate::Vine::Client::SendNotification::Fn(
2749 "cocoon-main".to_string(),
2750 "$acceptTextEditorOptionsChanged".to_string(),
2751 Payload,
2752 )
2753 .await;
2754 Ok(Value::Null)
2755 },
2756
2757 "sky:editor:diffInformationChanged" => {
2765 let Payload = arg_val(&Arguments, 0);
2766 let _ = crate::Vine::Client::SendNotification::Fn(
2767 "cocoon-main".to_string(),
2768 "$acceptTextEditorDiffInformationChanged".to_string(),
2769 Payload,
2770 )
2771 .await;
2772 Ok(Value::Null)
2773 },
2774
2775 "sky:editor:viewColumnChanged" => {
2783 let Payload = arg_val(&Arguments, 0);
2784 let _ = crate::Vine::Client::SendNotification::Fn(
2785 "cocoon-main".to_string(),
2786 "$acceptTextEditorViewColumnChanged".to_string(),
2787 Payload,
2788 )
2789 .await;
2790 Ok(Value::Null)
2791 },
2792
2793 "language:provideInlineCompletions" => {
2804 let Payload = arg_val(&Arguments, 0);
2805 let UriStr = Payload.get("uri").and_then(Value::as_str).unwrap_or("").to_string();
2806
2807 if UriStr.is_empty() {
2808 Ok(json!({ "items": [] }))
2809 } else {
2810 let Line = Payload
2811 .get("position")
2812 .and_then(|P| P.get("line"))
2813 .and_then(Value::as_u64)
2814 .unwrap_or(0) as i64 + 1;
2815 let Character = Payload
2816 .get("position")
2817 .and_then(|P| P.get("character"))
2818 .and_then(Value::as_u64)
2819 .unwrap_or(0) as i64 + 1;
2820 let Context = Payload.get("context").cloned().unwrap_or_else(|| json!({ "triggerKind": 0 }));
2821
2822 match url::Url::parse(&UriStr) {
2823 Ok(Uri) => {
2824 let Position = PositionDTO { LineNumber:Line as u32, Column:Character as u32 };
2825 match RunTime.Environment.ProvideInlineCompletionItems(Uri, Position, Context).await {
2826 Ok(Some(Result)) => {
2827 let Items = Result
2828 .get("items")
2829 .cloned()
2830 .unwrap_or_else(|| if Result.is_array() { Result } else { json!([]) });
2831 Ok(json!({ "items": Items }))
2832 },
2833 Ok(None) => Ok(json!({ "items": [] })),
2834 Err(Error) => {
2835 dev_log!("ipc", "warn: language:provideInlineCompletions error: {}", Error);
2836 Ok(json!({ "items": [] }))
2837 },
2838 }
2839 },
2840 Err(_) => Ok(json!({ "items": [] })),
2841 }
2842 }
2843 },
2844
2845 "languages:getAll" | "languages:getEncodedLanguageId" => {
2846 dev_log!("extensions", "languages: {} (→ Cocoon)", command);
2847 let Payload = Arguments.into_iter().next().unwrap_or(Value::Null);
2848 if !crate::Vine::Client::IsClientConnected::Fn("cocoon-main") {
2858 Ok(Value::Array(Vec::new()))
2859 } else {
2860 Ok(
2861 crate::Vine::Client::SendRequest::Fn("cocoon-main", command.clone(), Payload, 5_000)
2862 .await
2863 .unwrap_or(Value::Array(Vec::new())),
2864 )
2865 }
2866 },
2867
2868 "language:prepareCallHierarchy"
2878 | "language:provideCallHierarchyIncomingCalls"
2879 | "language:provideCallHierarchyOutgoingCalls" => {
2880 forward_to_cocoon!("language", command, Arguments)
2881 },
2882
2883 "language:prepareTypeHierarchy"
2887 | "language:provideTypeHierarchySupertypes"
2888 | "language:provideTypeHierarchySubtypes" => {
2889 forward_to_cocoon!("language", command, Arguments)
2890 },
2891
2892 "language:provideLinkedEditingRanges" => {
2896 forward_to_cocoon!("language", command, Arguments)
2897 },
2898
2899 "scm:createSourceControl" | "scm:getSourceControls" | "scm:setActiveProvider" => {
2903 forward_to_cocoon!("scm", command, Arguments)
2904 },
2905
2906 "debug:startDebugging"
2917 | "debug:stopDebugging"
2918 | "debug:getSessions"
2919 | "debug:getBreakpoints"
2920 | "debug:addBreakpoints"
2921 | "debug:removeBreakpoints" => {
2922 let _ = TIER_DEBUG;
2923 forward_to_cocoon!("debug", command, Arguments)
2924 },
2925
2926 "tasks:executeTask" | "tasks:getTasks" | "tasks:getTaskExecution" => {
2930 forward_to_cocoon!("tasks", command, Arguments)
2931 },
2932
2933 "auth:getSessions" | "auth:createSession" | "auth:removeSession" => {
2937 forward_to_cocoon!("auth", command, Arguments)
2938 },
2939
2940 _ => {
2953 use std::str::FromStr;
2954
2955 let TierIPC = std::env::var("TierIPC").unwrap_or_else(|_| "Mountain".into());
2960 let ShouldDefer = TierIPC == "NodeDeferred" || TierIPC == "Node";
2961
2962 if ShouldDefer {
2963 let Payload = cocoon_payload(Arguments);
2967 dev_log!("ipc", "deferred → Cocoon: {}", command);
2968 let _ = crate::Vine::Client::WaitForClientConnection::Fn("cocoon-main", 3000).await;
2969 match crate::Vine::Client::SendRequest::Fn("cocoon-main", command.clone(), Payload, 15_000)
2970 .await
2971 {
2972 Ok(Response) => Ok(Response),
2973 Err(CocoonError) => {
2974 dev_log!(
2975 "ipc",
2976 "warn: [NodeDeferred] {} deferred but Cocoon rejected: {:?}",
2977 command,
2978 CocoonError
2979 );
2980 Ok(Value::Null)
2981 },
2982 }
2983 } else {
2984 match CommonLibrary::IPC::Channel::Channel::from_str(&command) {
2985 Ok(KnownChannel) => {
2986 dev_log!(
2987 "ipc",
2988 "error: [WindServiceHandlers] Channel {:?} is registered but has no dispatch arm",
2989 KnownChannel
2990 );
2991 Err(format!("IPC channel registered but unimplemented: {}", command))
2992 },
2993 Err(_) => {
2994 dev_log!("ipc", "error: [WindServiceHandlers] Unknown IPC command: {}", command);
2995 Err(format!("Unknown IPC command: {}", command))
2996 },
2997 }
2998 }
2999 },
3000 };
3001
3002 if ResultSender.send(MatchResult).is_err() {
3003 dev_log!(
3004 "ipc",
3005 "warn: [WindServiceHandlers] IPC result receiver dropped before dispatch completed"
3006 );
3007 }
3008 },
3009 CommandPriority,
3010 );
3011
3012 let Result = match ResultReceiver.await {
3013 Ok(Dispatched) => Dispatched,
3014
3015 Err(_) => {
3016 dev_log!(
3017 "ipc",
3018 "error: [WindServiceHandlers] IPC task cancelled before producing a result"
3019 );
3020
3021 Err("IPC task cancelled before result was produced".to_string())
3022 },
3023 };
3024
3025 if !IsHighFrequencyCommand {
3029 let IsErr = Result.is_err();
3030
3031 let SpanName = if IsErr {
3032 format!("land:mountain:ipc:{}:error", command)
3033 } else {
3034 format!("land:mountain:ipc:{}", command)
3035 };
3036
3037 crate::otel_span!(&SpanName, OTLPStart, &[("ipc.command", command.as_str())]);
3038
3039 let HandlerElapsedNanos = crate::IPC::DevLog::NowNano::Fn().saturating_sub(OTLPStart);
3043
3044 let HandlerDurationMs = HandlerElapsedNanos / 1_000_000;
3045
3046 crate::Binary::Build::PostHogPlugin::CaptureHandler::Fn(&command, HandlerDurationMs, !IsErr);
3047 }
3048
3049 if !IsHighFrequencyCommand {
3058 let ElapsedNanos = crate::IPC::DevLog::NowNano::Fn().saturating_sub(OTLPStart);
3059
3060 dev_log!("ipc", "done: {} ok={} t_ns={}", command, !Result.is_err(), ElapsedNanos);
3061 }
3062
3063 Result
3064}
3065
3066pub fn register_wind_ipc_handlers(ApplicationHandle:&tauri::AppHandle) -> Result<(), String> {
3067 dev_log!("lifecycle", "registering IPC handlers");
3068
3069 Ok(())
3073}