Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T01:53:45.408Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

destination_list.rs

208 lines · 7.0 KB · rust
1use std::{path::PathBuf, sync::Arc};
2
3use itertools::Itertools;
4use smallvec::SmallVec;
5use windows::{
6    Win32::{
7        Foundation::PROPERTYKEY,
8        Globalization::u_strlen,
9        System::Com::{CLSCTX_INPROC_SERVER, CoCreateInstance, StructuredStorage::PROPVARIANT},
10        UI::{
11            Controls::INFOTIPSIZE,
12            Shell::{
13                Common::{IObjectArray, IObjectCollection},
14                DestinationList, EnumerableObjectCollection, ICustomDestinationList, IShellLinkW,
15                PropertiesSystem::IPropertyStore,
16                ShellLink,
17            },
18        },
19    },
20    core::{GUID, HSTRING, Interface},
21};
22
23use gpui::{Action, MenuItem, SharedString};
24
25pub(crate) struct JumpList {
26    pub(crate) dock_menus: Vec<DockMenuItem>,
27    pub(crate) recent_workspaces: Arc<[SmallVec<[PathBuf; 2]>]>,
28}
29
30impl JumpList {
31    pub(crate) fn new() -> Self {
32        Self {
33            dock_menus: Vec::default(),
34            recent_workspaces: Arc::default(),
35        }
36    }
37}
38
39pub(crate) struct DockMenuItem {
40    pub(crate) name: SharedString,
41    pub(crate) description: SharedString,
42    pub(crate) action: Box<dyn Action>,
43}
44
45impl DockMenuItem {
46    pub(crate) fn new(item: MenuItem) -> anyhow::Result<Self> {
47        match item {
48            MenuItem::Action { name, action, .. } => Ok(Self {
49                name: name.clone(),
50                description: if name == "New Window" {
51                    "Opens a new window".into()
52                } else {
53                    name
54                },
55                action,
56            }),
57            _ => anyhow::bail!("Only `MenuItem::Action` is supported for dock menu on Windows."),
58        }
59    }
60}
61
62// This code is based on the example from Microsoft:
63// https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/winui/shell/appshellintegration/RecipePropertyHandler/RecipePropertyHandler.cpp
64pub(crate) fn update_jump_list(
65    recent_workspaces: &[SmallVec<[PathBuf; 2]>],
66    dock_menus: &[(SharedString, SharedString)],
67) -> anyhow::Result<Vec<SmallVec<[PathBuf; 2]>>> {
68    let (list, removed) = create_destination_list()?;
69    add_recent_folders(&list, recent_workspaces, removed.as_ref())?;
70    add_dock_menu(&list, dock_menus)?;
71    unsafe { list.CommitList() }?;
72    Ok(removed)
73}
74
75// Copied from:
76// https://github.com/microsoft/windows-rs/blob/0fc3c2e5a13d4316d242bdeb0a52af611eba8bd4/crates/libs/windows/src/Windows/Win32/Storage/EnhancedStorage/mod.rs#L1881
77const PKEY_TITLE: PROPERTYKEY = PROPERTYKEY {
78    fmtid: GUID::from_u128(0xf29f85e0_4ff9_1068_ab91_08002b27b3d9),
79    pid: 2,
80};
81
82fn create_destination_list() -> anyhow::Result<(ICustomDestinationList, Vec<SmallVec<[PathBuf; 2]>>)>
83{
84    let list: ICustomDestinationList =
85        unsafe { CoCreateInstance(&DestinationList, None, CLSCTX_INPROC_SERVER) }?;
86
87    let mut slots = 0;
88    let user_removed: IObjectArray = unsafe { list.BeginList(&mut slots) }?;
89
90    let count = unsafe { user_removed.GetCount() }?;
91    if count == 0 {
92        return Ok((list, Vec::new()));
93    }
94
95    let mut removed = Vec::with_capacity(count as usize);
96    for i in 0..count {
97        let shell_link: IShellLinkW = unsafe { user_removed.GetAt(i)? };
98        let description = {
99            // INFOTIPSIZE is the maximum size of the buffer
100            // see https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishelllinkw-getdescription
101            let mut buffer = [0u16; INFOTIPSIZE as usize];
102            unsafe { shell_link.GetDescription(&mut buffer)? };
103            let len = unsafe { u_strlen(buffer.as_ptr()) };
104            String::from_utf16_lossy(&buffer[..len as usize])
105        };
106        let args = description.split('\n').map(PathBuf::from).collect();
107
108        removed.push(args);
109    }
110
111    Ok((list, removed))
112}
113
114fn add_dock_menu(
115    list: &ICustomDestinationList,
116    dock_menus: &[(SharedString, SharedString)],
117) -> anyhow::Result<()> {
118    unsafe {
119        let tasks: IObjectCollection =
120            CoCreateInstance(&EnumerableObjectCollection, None, CLSCTX_INPROC_SERVER)?;
121        for (idx, (name, description)) in dock_menus.iter().enumerate() {
122            let argument = HSTRING::from(format!("--dock-action {}", idx));
123            let description = HSTRING::from(description.as_str());
124            let display = name.as_str();
125            let task = create_shell_link(argument, description, None, display)?;
126            tasks.AddObject(&task)?;
127        }
128        list.AddUserTasks(&tasks)?;
129        Ok(())
130    }
131}
132
133fn add_recent_folders(
134    list: &ICustomDestinationList,
135    entries: &[SmallVec<[PathBuf; 2]>],
136    removed: &Vec<SmallVec<[PathBuf; 2]>>,
137) -> anyhow::Result<()> {
138    unsafe {
139        let tasks: IObjectCollection =
140            CoCreateInstance(&EnumerableObjectCollection, None, CLSCTX_INPROC_SERVER)?;
141
142        for folder_path in entries.iter().filter(|path| !removed.contains(path)) {
143            let argument = HSTRING::from(
144                folder_path
145                    .iter()
146                    .map(|path| format!("\"{}\"", path.display()))
147                    .join(" "),
148            );
149
150            let description = HSTRING::from(
151                folder_path
152                    .iter()
153                    .map(|path| path.to_string_lossy())
154                    .collect::<Vec<_>>()
155                    .join("\n"),
156            );
157            // simulate folder icon
158            // https://github.com/microsoft/vscode/blob/7a5dc239516a8953105da34f84bae152421a8886/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts#L380
159            let icon = HSTRING::from("explorer.exe");
160
161            let display = folder_path
162                .iter()
163                .map(|p| {
164                    p.file_name()
165                        .map(|name| name.to_string_lossy())
166                        .unwrap_or_else(|| p.to_string_lossy())
167                })
168                .join(", ");
169
170            tasks.AddObject(&create_shell_link(
171                argument,
172                description,
173                Some(icon),
174                &display,
175            )?)?;
176        }
177
178        if tasks.GetCount().unwrap_or(0) > 0 {
179            list.AppendCategory(&HSTRING::from("Recent Folders"), &tasks)?;
180        }
181        Ok(())
182    }
183}
184
185fn create_shell_link(
186    argument: HSTRING,
187    description: HSTRING,
188    icon: Option<HSTRING>,
189    display: &str,
190) -> anyhow::Result<IShellLinkW> {
191    unsafe {
192        let link: IShellLinkW = CoCreateInstance(&ShellLink, None, CLSCTX_INPROC_SERVER)?;
193        let exe_path = HSTRING::from(std::env::current_exe()?.as_os_str());
194        link.SetPath(&exe_path)?;
195        link.SetArguments(&argument)?;
196        link.SetDescription(&description)?;
197        if let Some(icon) = icon {
198            link.SetIconLocation(&icon, 0)?;
199        }
200        let store: IPropertyStore = link.cast()?;
201        let title = PROPVARIANT::from(display);
202        store.SetValue(&PKEY_TITLE, &title)?;
203        store.Commit()?;
204
205        Ok(link)
206    }
207}
208
Served at tenant.openagents/omega Member data and write actions are omitted.