@@ -1319,41 +1319,36 @@ impl CocoonService for CocoonServiceImpl {
13191319 }
13201320 }
13211321
1322- /// Terminal Input - Send input to terminal
1322+ /// Terminal Input — write bytes to PTY stdin via TerminalProvider.
13231323 async fn terminal_input ( & self , request : Request < TerminalInputRequest > ) -> Result < Response < Empty > , Status > {
13241324 let req = request. into_inner ( ) ;
1325- debug ! ( "[CocoonService] Sending input to terminal {}" , req. terminal_id) ;
1325+ let TerminalId = req. terminal_id as u64 ;
1326+ debug ! ( "[CocoonService] terminal_input: id={} bytes={}" , TerminalId , req. data. len( ) ) ;
13261327
1327- // Forward to TerminalProvider for PTY input in MountainEnvironment
1328- // This stub returns an unimplemented error
1329- debug ! ( "[CocoonService] Input length: {} bytes" , req. data. len( ) ) ;
1330-
1331- // TODO: When TerminalProvider is available in MountainEnvironment:
1332- // - Look up PTY by terminal_id
1333- // - Write input bytes to PTY
1334- // - Handle errors (terminal not found, PTY closed)
1335- // - Forward to Wind via IPC for display updates
1328+ let Text = String :: from_utf8_lossy ( & req. data ) . into_owned ( ) ;
13361329
1337- Err ( Status :: unimplemented ( "terminal_input not yet implemented" ) )
1330+ match self . environment . SendTextToTerminal ( TerminalId , Text ) . await {
1331+ Ok ( ( ) ) => Ok ( Response :: new ( Empty { } ) ) ,
1332+ Err ( Error ) => {
1333+ warn ! ( "[CocoonService] terminal_input failed id={}: {}" , TerminalId , Error ) ;
1334+ Err ( Status :: not_found ( format ! ( "terminal_input: {}" , Error ) ) )
1335+ } ,
1336+ }
13381337 }
13391338
1340- /// Close Terminal - Close a terminal
1339+ /// Close Terminal — dispose PTY and cleanup resources via TerminalProvider.
13411340 async fn close_terminal ( & self , request : Request < CloseTerminalRequest > ) -> Result < Response < Empty > , Status > {
13421341 let req = request. into_inner ( ) ;
1343- info ! ( "[CocoonService] Closing terminal {}" , req. terminal_id) ;
1344-
1345- // Close PTY and notify Wind frontend in MountainEnvironment
1346- // This stub returns an unimplemented error
1347- warn ! ( "[CocoonService] Terminal close not yet implemented" ) ;
1342+ let TerminalId = req. terminal_id as u64 ;
1343+ info ! ( "[CocoonService] close_terminal: id={}" , TerminalId ) ;
13481344
1349- // TODO: When TerminalProvider is available in MountainEnvironment:
1350- // - Look up PTY by terminal_id
1351- // - Close PTY and cleanup resources
1352- // - Notify Wind via IPC
1353- // - Remove from TerminalState
1354- // - Handle errors (terminal not found)
1355-
1356- Err ( Status :: unimplemented ( "close_terminal not yet implemented" ) )
1345+ match self . environment . DisposeTerminal ( TerminalId ) . await {
1346+ Ok ( ( ) ) => Ok ( Response :: new ( Empty { } ) ) ,
1347+ Err ( Error ) => {
1348+ warn ! ( "[CocoonService] close_terminal failed id={}: {}" , TerminalId , Error ) ;
1349+ Err ( Status :: internal ( format ! ( "close_terminal: {}" , Error ) ) )
1350+ } ,
1351+ }
13571352 }
13581353
13591354 /// Accept Terminal Opened - Notification: Terminal opened
@@ -1671,63 +1666,49 @@ impl CocoonService for CocoonServiceImpl {
16711666
16721667 // ==================== Secret Storage ====================
16731668
1674- /// Get Secret - Retrieve a secret from storage
1669+ /// Get Secret — retrieve from the OS keychain via SecretProvider.
16751670 async fn get_secret ( & self , request : Request < GetSecretRequest > ) -> Result < Response < GetSecretResponse > , Status > {
16761671 let req = request. into_inner ( ) ;
1677- debug ! ( "[CocoonService] Getting secret for key: {}" , req. key) ;
1678-
1679- // Delegate to SecretStorageProvider in MountainEnvironment
1680- // This stub returns an unimplemented error
1681- warn ! ( "[CocoonService] SecretStorageProvider not yet available in MountainEnvironment" ) ;
1682-
1683- // TODO: When SecretStorageProvider is available in MountainEnvironment:
1684- // - Look up secret by key in SecretStorageProvider
1685- // - Return secret value or error if not found
1686- // - Handle encryption/decryption
1687- // - Handle permission errors
1672+ debug ! ( "[CocoonService] get_secret: key={}" , req. key) ;
16881673
1689- Err ( Status :: unimplemented (
1690- "get_secret requires SecretStorageProvider in MountainEnvironment" ,
1691- ) )
1674+ // The gRPC proto only carries `key`; we use the app name as the
1675+ // extension identifier (keyring service scoping).
1676+ match self . environment . GetSecret ( String :: new ( ) , req. key . clone ( ) ) . await {
1677+ Ok ( Some ( Value ) ) => Ok ( Response :: new ( GetSecretResponse { value : Value } ) ) ,
1678+ Ok ( None ) => Ok ( Response :: new ( GetSecretResponse { value : String :: new ( ) } ) ) ,
1679+ Err ( Error ) => {
1680+ warn ! ( "[CocoonService] get_secret failed key={}: {}" , req. key, Error ) ;
1681+ Err ( Status :: internal ( format ! ( "get_secret: {}" , Error ) ) )
1682+ } ,
1683+ }
16921684 }
16931685
1694- /// Store Secret - Store a secret in storage
1686+ /// Store Secret — persist to the OS keychain via SecretProvider.
16951687 async fn store_secret ( & self , request : Request < StoreSecretRequest > ) -> Result < Response < Empty > , Status > {
16961688 let req = request. into_inner ( ) ;
1697- debug ! ( "[CocoonService] Storing secret for key: {}" , req. key) ;
1689+ debug ! ( "[CocoonService] store_secret: key= {}" , req. key) ;
16981690
1699- // Delegate to SecretStorageProvider in MountainEnvironment
1700- // This stub returns an unimplemented error
1701- debug ! ( "[CocoonService] Secret value length: {} bytes" , req. value. len( ) ) ;
1702-
1703- // TODO: When SecretStorageProvider is available in MountainEnvironment:
1704- // - Store secret with key in SecretStorageProvider
1705- // - Handle encryption before storage
1706- // - Update existing secret or create new one
1707- // - Handle permission errors and storage limits
1708-
1709- Err ( Status :: unimplemented (
1710- "store_secret requires SecretStorageProvider in MountainEnvironment" ,
1711- ) )
1691+ match self . environment . StoreSecret ( String :: new ( ) , req. key . clone ( ) , req. value ) . await {
1692+ Ok ( ( ) ) => Ok ( Response :: new ( Empty { } ) ) ,
1693+ Err ( Error ) => {
1694+ warn ! ( "[CocoonService] store_secret failed key={}: {}" , req. key, Error ) ;
1695+ Err ( Status :: internal ( format ! ( "store_secret: {}" , Error ) ) )
1696+ } ,
1697+ }
17121698 }
17131699
1714- /// Delete Secret - Delete a secret from storage
1700+ /// Delete Secret — remove from the OS keychain via SecretProvider.
17151701 async fn delete_secret ( & self , request : Request < DeleteSecretRequest > ) -> Result < Response < Empty > , Status > {
17161702 let req = request. into_inner ( ) ;
1717- debug ! ( "[CocoonService] Deleting secret for key: {}" , req. key) ;
1718-
1719- // Delegate to SecretStorageProvider in MountainEnvironment
1720- // This stub returns an unimplemented error
1721- warn ! ( "[CocoonService] Secret deletion not yet implemented" ) ;
1703+ debug ! ( "[CocoonService] delete_secret: key={}" , req. key) ;
17221704
1723- // TODO: When SecretStorageProvider is available in MountainEnvironment:
1724- // - Remove secret by key from SecretStorageProvider
1725- // - Return success or error if not found
1726- // - Handle permission errors
1727-
1728- Err ( Status :: unimplemented (
1729- "delete_secret requires SecretStorageProvider in MountainEnvironment" ,
1730- ) )
1705+ match self . environment . DeleteSecret ( String :: new ( ) , req. key . clone ( ) ) . await {
1706+ Ok ( ( ) ) => Ok ( Response :: new ( Empty { } ) ) ,
1707+ Err ( Error ) => {
1708+ warn ! ( "[CocoonService] delete_secret failed key={}: {}" , req. key, Error ) ;
1709+ Err ( Status :: internal ( format ! ( "delete_secret: {}" , Error ) ) )
1710+ } ,
1711+ }
17311712 }
17321713
17331714 // ==================== Extended Language Provider Handlers ====================
@@ -2226,30 +2207,82 @@ impl CocoonService for CocoonServiceImpl {
22262207 Ok ( Response :: new ( ProvideLinkedEditingRangesResponse :: default ( ) ) )
22272208 }
22282209
2229- /// quick pick
2210+ /// Show Quick Pick — present a selection list via UserInterfaceProvider.
22302211 async fn show_quick_pick (
22312212 & self ,
22322213 request : Request < ShowQuickPickRequest > ,
22332214 ) -> Result < Response < ShowQuickPickResponse > , Status > {
2234- let _req = request. into_inner ( ) ;
2235- info ! ( "[CocoonService] Handling quick pick" ) ;
2215+ let req = request. into_inner ( ) ;
2216+ info ! ( "[CocoonService] show_quick_pick: {} items" , req. items. len( ) ) ;
2217+
2218+ let Items : Vec < QuickPickItemDTO > = req
2219+ . items
2220+ . iter ( )
2221+ . map ( |Item | QuickPickItemDTO {
2222+ Label : Item . label . clone ( ) ,
2223+ Description : if Item . description . is_empty ( ) { None } else { Some ( Item . description . clone ( ) ) } ,
2224+ Detail : None ,
2225+ Picked : Some ( Item . picked ) ,
2226+ AlwaysShow : None ,
2227+ } )
2228+ . collect ( ) ;
22362229
2237- // TODO: Implement quick pick in MountainEnvironment
2230+ let Options = Some ( QuickPickOptionsDTO {
2231+ Title : if req. title . is_empty ( ) { None } else { Some ( req. title ) } ,
2232+ PlaceHolder : if req. placeholder . is_empty ( ) { None } else { Some ( req. placeholder ) } ,
2233+ CanPickMany : Some ( req. can_pick_many ) ,
2234+ IgnoreFocusOut : None ,
2235+ } ) ;
22382236
2239- Ok ( Response :: new ( ShowQuickPickResponse { ..Default :: default ( ) } ) )
2237+ match self . environment . ShowQuickPick ( Items , Options ) . await {
2238+ Ok ( Some ( Selected ) ) => {
2239+ // Map selected label strings back to indices via linear search
2240+ let SelectedIndices : Vec < u32 > = Selected
2241+ . iter ( )
2242+ . filter_map ( |Label | {
2243+ req. items
2244+ . iter ( )
2245+ . position ( |Item | & Item . label == Label )
2246+ . map ( |Idx | Idx as u32 )
2247+ } )
2248+ . collect ( ) ;
2249+ Ok ( Response :: new ( ShowQuickPickResponse { selected_indices : SelectedIndices } ) )
2250+ } ,
2251+ Ok ( None ) => Ok ( Response :: new ( ShowQuickPickResponse :: default ( ) ) ) ,
2252+ Err ( Error ) => {
2253+ warn ! ( "[CocoonService] show_quick_pick failed: {}" , Error ) ;
2254+ Ok ( Response :: new ( ShowQuickPickResponse :: default ( ) ) )
2255+ } ,
2256+ }
22402257 }
22412258
2242- /// input box
2259+ /// Show Input Box — present a text entry dialog via UserInterfaceProvider.
22432260 async fn show_input_box (
22442261 & self ,
22452262 request : Request < ShowInputBoxRequest > ,
22462263 ) -> Result < Response < ShowInputBoxResponse > , Status > {
2247- let _req = request. into_inner ( ) ;
2248- info ! ( "[CocoonService] Handling input box " ) ;
2264+ let req = request. into_inner ( ) ;
2265+ info ! ( "[CocoonService] show_input_box " ) ;
22492266
2250- // TODO: Implement input box in MountainEnvironment
2267+ let Options = Some ( InputBoxOptionsDTO {
2268+ Title : if req. title . is_empty ( ) { None } else { Some ( req. title ) } ,
2269+ PlaceHolder : if req. placeholder . is_empty ( ) { None } else { Some ( req. placeholder ) } ,
2270+ Value : if req. value . is_empty ( ) { None } else { Some ( req. value ) } ,
2271+ Prompt : if req. prompt . is_empty ( ) { None } else { Some ( req. prompt ) } ,
2272+ IsPassword : if req. password { Some ( true ) } else { None } ,
2273+ IgnoreFocusOut : None ,
2274+ } ) ;
22512275
2252- Ok ( Response :: new ( ShowInputBoxResponse { ..Default :: default ( ) } ) )
2276+ match self . environment . ShowInputBox ( Options ) . await {
2277+ Ok ( Some ( Value ) ) => {
2278+ Ok ( Response :: new ( ShowInputBoxResponse { value : Value , cancelled : false } ) )
2279+ } ,
2280+ Ok ( None ) => Ok ( Response :: new ( ShowInputBoxResponse { value : String :: new ( ) , cancelled : true } ) ) ,
2281+ Err ( Error ) => {
2282+ warn ! ( "[CocoonService] show_input_box failed: {}" , Error ) ;
2283+ Ok ( Response :: new ( ShowInputBoxResponse { value : String :: new ( ) , cancelled : true } ) )
2284+ } ,
2285+ }
22532286 }
22542287
22552288 /// progress
@@ -2573,12 +2606,28 @@ impl CocoonService for CocoonServiceImpl {
25732606 Ok ( Response :: new ( GetAllExtensionsResponse { extensions : ExtensionInfoList } ) )
25742607 }
25752608
2576- /// terminal resize
2609+ /// Terminal Resize — emit a Tauri event so Sky can resize the xterm view.
2610+ ///
2611+ /// PTY-level resize (via `portable_pty::MasterPty::resize`) is a P1 task
2612+ /// that requires storing the PTY master handle in `TerminalStateDTO`.
2613+ /// The Tauri event lets the UI immediately resize its canvas.
25772614 async fn resize_terminal ( & self , request : Request < ResizeTerminalRequest > ) -> Result < Response < Empty > , Status > {
2578- let _req = request. into_inner ( ) ;
2579- info ! ( "[CocoonService] Handling terminal resize" ) ;
2615+ use tauri:: Emitter ;
2616+
2617+ let req = request. into_inner ( ) ;
2618+ debug ! (
2619+ "[CocoonService] resize_terminal: id={} cols={} rows={}" ,
2620+ req. terminal_id, req. cols, req. rows
2621+ ) ;
2622+
2623+ // Notify Sky/Wind of the new dimensions for UI resize
2624+ let _ = self . environment . ApplicationHandle . emit (
2625+ "sky://terminal/resize" ,
2626+ json ! ( { "id" : req. terminal_id, "cols" : req. cols, "rows" : req. rows } ) ,
2627+ ) ;
25802628
2581- // TODO: Implement terminal resize in MountainEnvironment
2629+ // TODO(P1): Call portable_pty::MasterPty::resize once PtyMaster handle
2630+ // is stored in TerminalStateDTO (requires wrapping MasterPty in Arc<Mutex>)
25822631
25832632 Ok ( Response :: new ( Empty { } ) )
25842633 }
0 commit comments