diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 8de679798..ecb8a9d1b 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -432,6 +432,7 @@ dependencies = [ "flate2", "futures-util", "getrandom 0.2.17", + "github-copilot-shell-tasks", "http", "indexmap", "libloading", @@ -457,6 +458,14 @@ dependencies = [ "zip", ] +[[package]] +name = "github-copilot-shell-tasks" +version = "0.0.0-dev" +dependencies = [ + "windows", + "windows-core", +] + [[package]] name = "h2" version = "0.4.15" @@ -2042,12 +2051,107 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -2106,6 +2210,15 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0f18a9b15..4816e4cb7 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -27,12 +27,25 @@ include = [ [lib] name = "github_copilot_sdk" +# This manifest is both the root package and the workspace root. The standalone +# `github-copilot-shell-tasks` crate is a member so it builds/tests with the +# workspace and can be depended on by path above. +[workspace] +members = ["crates/github-copilot-shell-tasks"] +resolver = "3" + [features] default = ["bundled-cli"] bundled-cli = ["dep:tar", "dep:flate2", "dep:zip"] bundled-in-process = ["bundled-cli", "dep:libloading"] derive = ["dep:schemars"] test-support = [] +# Shared `Windows.UI.Shell.Tasks` ("Forerunner" / taskbar presence) integration, +# re-exported as the `shell_tasks` module. Off by default to keep the base SDK +# lean. Enabling it pulls the standalone, build.rs-free +# `github-copilot-shell-tasks` crate (its `windows` / `windows-core` deps only +# activate on Windows), mirroring how `bundled-cli` gates its optional `zip`. +shell-tasks = ["dep:github-copilot-shell-tasks"] # Build docs.rs documentation with all features so feature-gated APIs # (e.g. `define_tool`, `schema_for`) appear and intra-doc links resolve. @@ -59,6 +72,10 @@ getrandom = "0.2" uuid = { version = "1", default-features = false, features = ["v4"] } flate2 = { version = "1", optional = true } tar = { version = "0.4", optional = true } +# Shared Windows.UI.Shell.Tasks integration, re-exported as `shell_tasks` behind +# the `shell-tasks` feature. Standalone + build.rs-free so enabling it does not +# perturb the base SDK; its Windows-only WinRT deps activate only on Windows. +github-copilot-shell-tasks = { version = "0.0.0-dev", path = "crates/github-copilot-shell-tasks", optional = true } # LLM inference callback transport: idiomatic HTTP/WebSocket forwarding for the # `CopilotRequestHandler`, plus base64/byte/stream plumbing for the chunk protocol. base64 = "0.22" diff --git a/rust/crates/github-copilot-shell-tasks/Cargo.toml b/rust/crates/github-copilot-shell-tasks/Cargo.toml new file mode 100644 index 000000000..3ef915d42 --- /dev/null +++ b/rust/crates/github-copilot-shell-tasks/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "github-copilot-shell-tasks" +version = "0.0.0-dev" +edition = "2024" +rust-version = "1.94.0" +description = "Shared Windows.UI.Shell.Tasks (Forerunner / taskbar presence) integration for GitHub Copilot, consumable by the desktop app and CLI without the full SDK client." +keywords = ["copilot", "github", "windows", "taskbar", "forerunner"] +categories = ["os::windows-apis", "api-bindings"] +repository = "https://github.com/github/copilot-sdk" +homepage = "https://github.com/github/copilot-sdk" +license = "MIT" + +[lib] +name = "github_copilot_shell_tasks" + +# Intentionally lightweight: no build.rs, and the only dependencies are the +# Windows-only WinRT crates. This lets a napi addon (CLI) or Tauri backend (app) +# depend on the shared integration without dragging in the `github-copilot-sdk` +# client crate (JSON-RPC, tokio, and its network-touching build.rs). +[dependencies] + +# `Foundation` covers the projection's `DateTime`/`IReference`/`Uri`; +# `ApplicationModel` + `Management_Deployment` + `Win32_System_Com` back the +# sparse-identity registrar (`sparse`); `windows-core` provides the WinRT +# runtime. Declared under `cfg(windows)` so non-Windows builds pull neither +# crate and the whole crate compiles as a pure-`std` no-op. +[target.'cfg(windows)'.dependencies] +windows = { version = "0.62", features = [ + "Foundation", + "ApplicationModel", + "Management_Deployment", + "Win32_System_Com", +] } +windows-core = "0.62" diff --git a/rust/crates/github-copilot-shell-tasks/src/bindings.rs b/rust/crates/github-copilot-shell-tasks/src/bindings.rs new file mode 100644 index 000000000..1df353add --- /dev/null +++ b/rust/crates/github-copilot-shell-tasks/src/bindings.rs @@ -0,0 +1,839 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// VENDORED WinRT PROJECTIONS — DO NOT EDIT BY HAND. +// +// Generated by `windows-bindgen` 0.61 from the Windows.UI.Shell.Tasks.AppTaskContract +// winmd shipped in the installed Windows SDK. To regenerate (Windows, with the 10.0.26100.0 +// SDK installed), run a throwaway crate depending on `windows-bindgen = "0.61"` with: +// +// windows_bindgen::bindgen([ +// "--in", +// r"C:\Program Files (x86)\Windows Kits\10\References\10.0.26100.0\Windows.UI.Shell.Tasks.AppTaskContract\2.0.0.0\Windows.UI.Shell.Tasks.AppTaskContract.winmd", +// "default", +// "--out", "apptask_bindings.rs", +// "--filter", "Windows.UI.Shell.Tasks", +// "--reference", "windows,skip-root,Windows", +// ]); +// +// The API is [experimental] and absent from the public `windows` crate metadata, so we +// project it locally from the SDK winmd. `Windows.Foundation.*` types resolve to the +// `windows` crate via the `--reference` mapping above. +// Bindings generated by `windows-bindgen` 0.61.1 + +#![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all +)] + +pub mod Windows { + pub mod UI { + pub mod Shell { + pub mod Tasks { + #[repr(transparent)] + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct AppTaskContent(windows_core::IUnknown); + windows_core::imp::interface_hierarchy!( + AppTaskContent, + windows_core::IUnknown, + windows_core::IInspectable + ); + impl AppTaskContent { + pub fn AddButton( + &self, + text: &windows_core::HSTRING, + actionuri: P1, + ) -> windows_core::Result<()> + where + P1: windows_core::Param, + { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).AddButton)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(text), + actionuri.param().abi(), + ) + .ok() + } + } + + pub fn SetTextInput( + &self, + placeholdertext: &windows_core::HSTRING, + actionuritemplate: &windows_core::HSTRING, + ) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).SetTextInput)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(placeholdertext), + core::mem::transmute_copy(actionuritemplate), + ) + .ok() + } + } + + pub fn SetQuestion( + &self, + question: &windows_core::HSTRING, + ) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).SetQuestion)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(question), + ) + .ok() + } + } + + pub fn CreateSequenceOfSteps( + completedsteps: &[windows_core::HSTRING], + executingstep: &windows_core::HSTRING, + ) -> windows_core::Result { + Self::IAppTaskContentStatics(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateSequenceOfSteps)( + windows_core::Interface::as_raw(this), + completedsteps.len().try_into().unwrap(), + core::mem::transmute(completedsteps.as_ptr()), + core::mem::transmute_copy(executingstep), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + + pub fn CreatePreviewThumbnail( + imageuri: P0, + executingstep: &windows_core::HSTRING, + ) -> windows_core::Result + where + P0: windows_core::Param, + { + Self::IAppTaskContentStatics(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreatePreviewThumbnail)( + windows_core::Interface::as_raw(this), + imageuri.param().abi(), + core::mem::transmute_copy(executingstep), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + + pub fn CreateTextSummaryResult( + text: &windows_core::HSTRING, + ) -> windows_core::Result { + Self::IAppTaskContentStatics(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateTextSummaryResult)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(text), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + + pub fn CreateGeneratedAssetsResult( + assets: &[Option], + ) -> windows_core::Result { + Self::IAppTaskContentStatics(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateGeneratedAssetsResult)( + windows_core::Interface::as_raw(this), + assets.len().try_into().unwrap(), + core::mem::transmute(assets.as_ptr()), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + + pub fn MaxButtons() -> windows_core::Result { + Self::IAppTaskContentStatics(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).MaxButtons)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + }) + } + + fn IAppTaskContentStatics< + R, + F: FnOnce(&IAppTaskContentStatics) -> windows_core::Result, + >( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache< + AppTaskContent, + IAppTaskContentStatics, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } + } + impl windows_core::RuntimeType for AppTaskContent { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); + } + unsafe impl windows_core::Interface for AppTaskContent { + type Vtable = ::Vtable; + + const IID: windows_core::GUID = + ::IID; + } + impl windows_core::RuntimeName for AppTaskContent { + const NAME: &'static str = "Windows.UI.Shell.Tasks.AppTaskContent"; + } + unsafe impl Send for AppTaskContent {} + unsafe impl Sync for AppTaskContent {} + #[repr(transparent)] + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct AppTaskInfo(windows_core::IUnknown); + windows_core::imp::interface_hierarchy!( + AppTaskInfo, + windows_core::IUnknown, + windows_core::IInspectable + ); + impl AppTaskInfo { + pub fn Remove(&self) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).Remove)( + windows_core::Interface::as_raw(this), + ) + .ok() + } + } + + pub fn Update( + &self, + state: AppTaskState, + content: P1, + ) -> windows_core::Result<()> + where + P1: windows_core::Param, + { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).Update)( + windows_core::Interface::as_raw(this), + state, + content.param().abi(), + ) + .ok() + } + } + + pub fn UpdateState(&self, state: AppTaskState) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).UpdateState)( + windows_core::Interface::as_raw(this), + state, + ) + .ok() + } + } + + pub fn UpdateTitles( + &self, + title: &windows_core::HSTRING, + subtitle: &windows_core::HSTRING, + ) -> windows_core::Result<()> { + let this = self; + unsafe { + (windows_core::Interface::vtable(this).UpdateTitles)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(title), + core::mem::transmute_copy(subtitle), + ) + .ok() + } + } + + pub fn GetCompletedSteps( + &self, + ) -> windows_core::Result> + { + let this = self; + unsafe { + let mut result__ = core::mem::MaybeUninit::zeroed(); + (windows_core::Interface::vtable(this).GetCompletedSteps)( + windows_core::Interface::as_raw(this), + windows_core::Array::::set_abi_len( + core::mem::transmute(&mut result__), + ), + result__.as_mut_ptr() as *mut _ as _, + ) + .map(|| result__.assume_init()) + } + } + + pub fn GetExecutingStep(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).GetExecutingStep)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| core::mem::transmute(result__)) + } + } + + pub fn Title(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Title)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| core::mem::transmute(result__)) + } + } + + pub fn Subtitle(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Subtitle)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| core::mem::transmute(result__)) + } + } + + pub fn DeepLink(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).DeepLink)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + + pub fn IconUri(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).IconUri)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + + pub fn State(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).State)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + + pub fn StartTime(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).StartTime)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + + pub fn EndTime( + &self, + ) -> windows_core::Result< + windows::Foundation::IReference, + > { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).EndTime)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + + pub fn Id(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Id)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| core::mem::transmute(result__)) + } + } + + pub fn HiddenByUser(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).HiddenByUser)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + + pub fn UpdateDeepLink(&self, deeplink: P0) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + (windows_core::Interface::vtable(this).UpdateDeepLink)( + windows_core::Interface::as_raw(this), + deeplink.param().abi(), + ) + .ok() + } + } + + pub fn IsSupported() -> windows_core::Result { + Self::IAppTaskInfoStatics(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).IsSupported)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + }) + } + + pub fn FindAll() -> windows_core::Result> { + Self::IAppTaskInfoStatics(|this| unsafe { + let mut result__ = core::mem::MaybeUninit::zeroed(); + (windows_core::Interface::vtable(this).FindAll)( + windows_core::Interface::as_raw(this), + windows_core::Array::::set_abi_len( + core::mem::transmute(&mut result__), + ), + result__.as_mut_ptr() as *mut _ as _, + ) + .map(|| result__.assume_init()) + }) + } + + pub fn Create( + title: &windows_core::HSTRING, + subtitle: &windows_core::HSTRING, + deeplink: P2, + iconuri: P3, + content: P4, + ) -> windows_core::Result + where + P2: windows_core::Param, + P3: windows_core::Param, + P4: windows_core::Param, + { + Self::IAppTaskInfoStatics(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Create)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(title), + core::mem::transmute_copy(subtitle), + deeplink.param().abi(), + iconuri.param().abi(), + content.param().abi(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + + fn IAppTaskInfoStatics< + R, + F: FnOnce(&IAppTaskInfoStatics) -> windows_core::Result, + >( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache< + AppTaskInfo, + IAppTaskInfoStatics, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } + } + impl windows_core::RuntimeType for AppTaskInfo { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); + } + unsafe impl windows_core::Interface for AppTaskInfo { + type Vtable = ::Vtable; + + const IID: windows_core::GUID = ::IID; + } + impl windows_core::RuntimeName for AppTaskInfo { + const NAME: &'static str = "Windows.UI.Shell.Tasks.AppTaskInfo"; + } + unsafe impl Send for AppTaskInfo {} + unsafe impl Sync for AppTaskInfo {} + #[repr(transparent)] + #[derive(Clone, Debug, Eq, PartialEq)] + pub struct AppTaskResultAsset(windows_core::IUnknown); + windows_core::imp::interface_hierarchy!( + AppTaskResultAsset, + windows_core::IUnknown, + windows_core::IInspectable + ); + impl AppTaskResultAsset { + pub fn CreateInstance( + name: &windows_core::HSTRING, + context: &windows_core::HSTRING, + iconuri: P2, + asseturi: P3, + ) -> windows_core::Result + where + P2: windows_core::Param, + P3: windows_core::Param, + { + Self::IAppTaskResultAssetFactory(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateInstance)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(name), + core::mem::transmute_copy(context), + iconuri.param().abi(), + asseturi.param().abi(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + + fn IAppTaskResultAssetFactory< + R, + F: FnOnce(&IAppTaskResultAssetFactory) -> windows_core::Result, + >( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache< + AppTaskResultAsset, + IAppTaskResultAssetFactory, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } + } + impl windows_core::RuntimeType for AppTaskResultAsset { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); + } + unsafe impl windows_core::Interface for AppTaskResultAsset { + type Vtable = ::Vtable; + + const IID: windows_core::GUID = + ::IID; + } + impl windows_core::RuntimeName for AppTaskResultAsset { + const NAME: &'static str = "Windows.UI.Shell.Tasks.AppTaskResultAsset"; + } + unsafe impl Send for AppTaskResultAsset {} + unsafe impl Sync for AppTaskResultAsset {} + #[repr(transparent)] + #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] + pub struct AppTaskState(pub i32); + impl AppTaskState { + pub const Completed: Self = Self(1i32); + pub const Error: Self = Self(4i32); + pub const NeedsAttention: Self = Self(2i32); + pub const Paused: Self = Self(3i32); + pub const Running: Self = Self(0i32); + } + impl windows_core::TypeKind for AppTaskState { + type TypeKind = windows_core::CopyType; + } + impl windows_core::RuntimeType for AppTaskState { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::from_slice( + b"enum(Windows.UI.Shell.Tasks.AppTaskState;i4)", + ); + } + windows_core::imp::define_interface!( + IAppTaskContent, + IAppTaskContent_Vtbl, + 0x2411bf59_1b2d_5b63_8181_03d6c2248a68 + ); + impl windows_core::RuntimeType for IAppTaskContent { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); + } + #[repr(C)] + #[doc(hidden)] + pub struct IAppTaskContent_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub AddButton: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub SetTextInput: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub SetQuestion: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + } + windows_core::imp::define_interface!( + IAppTaskContentStatics, + IAppTaskContentStatics_Vtbl, + 0xaabd19f6_7afc_5b1b_94f6_5dc9dc9af9e7 + ); + impl windows_core::RuntimeType for IAppTaskContentStatics { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); + } + #[repr(C)] + #[doc(hidden)] + pub struct IAppTaskContentStatics_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub CreateSequenceOfSteps: unsafe extern "system" fn( + *mut core::ffi::c_void, + u32, + *const windows_core::HSTRING, + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub CreatePreviewThumbnail: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub CreateTextSummaryResult: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub CreateGeneratedAssetsResult: + unsafe extern "system" fn( + *mut core::ffi::c_void, + u32, + *const AppTaskResultAsset, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub MaxButtons: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut u32, + ) + -> windows_core::HRESULT, + } + windows_core::imp::define_interface!( + IAppTaskInfo, + IAppTaskInfo_Vtbl, + 0x6720eed6_435b_5db9_be66_9343b70654f7 + ); + impl windows_core::RuntimeType for IAppTaskInfo { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); + } + #[repr(C)] + #[doc(hidden)] + pub struct IAppTaskInfo_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Remove: + unsafe extern "system" fn(*mut core::ffi::c_void) -> windows_core::HRESULT, + pub Update: unsafe extern "system" fn( + *mut core::ffi::c_void, + AppTaskState, + *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub UpdateState: unsafe extern "system" fn( + *mut core::ffi::c_void, + AppTaskState, + ) + -> windows_core::HRESULT, + pub UpdateTitles: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub GetCompletedSteps: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut u32, + *mut *mut *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub GetExecutingStep: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub Title: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub Subtitle: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub DeepLink: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub IconUri: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub State: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut AppTaskState, + ) + -> windows_core::HRESULT, + pub StartTime: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut windows::Foundation::DateTime, + ) + -> windows_core::HRESULT, + pub EndTime: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + } + windows_core::imp::define_interface!( + IAppTaskInfo2, + IAppTaskInfo2_Vtbl, + 0xad724d71_f137_51c0_8111_3552436bf447 + ); + impl windows_core::RuntimeType for IAppTaskInfo2 { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); + } + #[repr(C)] + #[doc(hidden)] + pub struct IAppTaskInfo2_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Id: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub HiddenByUser: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut bool, + ) + -> windows_core::HRESULT, + pub UpdateDeepLink: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + } + windows_core::imp::define_interface!( + IAppTaskInfoStatics, + IAppTaskInfoStatics_Vtbl, + 0xa0b0434f_c640_5800_88c9_d7691ac2f48f + ); + impl windows_core::RuntimeType for IAppTaskInfoStatics { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); + } + #[repr(C)] + #[doc(hidden)] + pub struct IAppTaskInfoStatics_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub IsSupported: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut bool, + ) + -> windows_core::HRESULT, + pub FindAll: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut u32, + *mut *mut *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + pub Create: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + } + windows_core::imp::define_interface!( + IAppTaskResultAsset, + IAppTaskResultAsset_Vtbl, + 0x75d0c2b3_8a31_5f8f_bda4_bdca96e95532 + ); + impl windows_core::RuntimeType for IAppTaskResultAsset { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); + } + #[repr(C)] + #[doc(hidden)] + pub struct IAppTaskResultAsset_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + } + windows_core::imp::define_interface!( + IAppTaskResultAssetFactory, + IAppTaskResultAssetFactory_Vtbl, + 0x0334d9df_0e06_5999_ba41_85d72e980085 + ); + impl windows_core::RuntimeType for IAppTaskResultAssetFactory { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); + } + #[repr(C)] + #[doc(hidden)] + pub struct IAppTaskResultAssetFactory_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub CreateInstance: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) + -> windows_core::HRESULT, + } + } + } + } +} diff --git a/rust/crates/github-copilot-shell-tasks/src/content.rs b/rust/crates/github-copilot-shell-tasks/src/content.rs new file mode 100644 index 000000000..336bb2900 --- /dev/null +++ b/rust/crates/github-copilot-shell-tasks/src/content.rs @@ -0,0 +1,301 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +//! Data-driven builder for `Windows.UI.Shell.Tasks.AppTaskContent` — the one +//! piece of the Forerunner engine that the Copilot desktop app and the CLI +//! implement *identically*. +//! +//! Both products translate their own domain state into the same fixed sequence +//! of WinRT calls: create a base content shape (a step sequence, a preview +//! thumbnail, a text summary, or a generated-assets grid), then optionally +//! attach a question, action buttons, and a text input. Only the *source* of +//! that data differs — the CLI feeds a flat napi options struct dispatched by a +//! template string; the app feeds domain `AttentionRequest` / activity-timeline +//! state and builds its own deep links. This module captures the common tail as +//! a neutral [`ContentSpec`] plus a `build_content` function so neither +//! product hand-rolls the WinRT call sequence. +//! +//! The spec types are pure data (cross-platform, unit-testable). The functions +//! that actually call WinRT are `#[cfg(windows)]` and operate on the vendored +//! projection in `super::bindings`; each product keeps its own worker thread, +//! object registry, deep-link grammar, and telemetry as a thin adapter that +//! maps its state into a [`ContentSpec`] and calls `build_content`. +//! +//! # Faithfulness +//! +//! `build_content` applies the question, then every button in order, then the +//! text input — the exact order both products use. Because each product only +//! populates the fields it uses (the app never sets both buttons and a text +//! input; the CLI passes through whatever its caller supplied), the union +//! builder reproduces each product's current WinRT calls byte-for-byte. Product +//! specific preparation that is *not* shared — the CLI's template-string +//! dispatch, the app's 12-asset cap, `file://` URI resolution, and per-asset +//! icon choice — stays in each adapter, which hands this module an already +//! resolved [`ContentSpec`]. + +/// A single generated asset shown in the [`ContentBody::Assets`] result grid. +/// Maps to a `Windows.UI.Shell.Tasks.AppTaskResultAsset`. All URIs are already +/// resolved to forms the shell can open (`ms-appx:///…`, `file:///…`, or an +/// absolute path); resolution and filtering are the caller's responsibility. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ResultAsset { + /// Display name of the generated asset. + pub name: String, + /// Contextual description shown alongside the asset. + pub context: String, + /// Icon URI representing the asset (an image asset typically uses itself). + pub icon_uri: String, + /// URI launched when the asset is activated. + pub asset_uri: String, +} + +/// The base content shape of a task's hover card, before any interactive +/// elements are attached. Each variant maps 1:1 to an `AppTaskContent::Create*` +/// factory. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ContentBody { + /// A sequence of completed step labels plus the currently executing step + /// (`AppTaskContent::CreateSequenceOfSteps`). The common running-progress + /// shape. + Steps { + /// Completed step labels, in order. + completed_steps: Vec, + /// The step currently executing (empty string for none). + current_step: String, + }, + /// A preview image captioned with the current step + /// (`AppTaskContent::CreatePreviewThumbnail`). + Preview { + /// Preview image URI. + image_uri: String, + /// Caption shown under the preview (typically the current step). + caption: String, + }, + /// A plain result summary (`AppTaskContent::CreateTextSummaryResult`). + Summary { + /// Result summary text. + text: String, + }, + /// A grid of generated assets (`AppTaskContent::CreateGeneratedAssetsResult`). + Assets { + /// The assets to show, already resolved and capped by the caller. + assets: Vec, + }, +} + +/// A clickable button attached to a task's hover card. The shell caps the count +/// (currently 2); this builder applies every button supplied, matching both +/// products, and leaves the cap to the caller/shell. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Button { + /// Button label. + pub text: String, + /// URI launched when the button is clicked. + pub action_uri: String, +} + +/// A single-line text input attached to a task's hover card. Submitted text is +/// substituted into `action_uri_template` by the shell and the result launched. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TextInput { + /// Placeholder text shown in the empty input. + pub placeholder: String, + /// URI template the submitted text is substituted into. + pub action_uri_template: String, +} + +/// Everything needed to build one `AppTaskContent`: a base [`ContentBody`] plus +/// the optional interactive elements attached on top. Interactive fields apply +/// to any body and are what the `NeedsAttention` state uses (it requires a +/// `question`). +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ContentSpec { + /// The base content shape. + pub body: ContentBody, + /// A question shown to the user (required for the `NeedsAttention` state). + pub question: Option, + /// Action buttons, applied in order. + pub buttons: Vec