mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 02:29:33 +01:00
Merge fix/desktop-keep-awake-window-close: closing the window no longer ends Keep This Mac Awake
# Conflicts: # packages/desktop/README.md
This commit is contained in:
commit
9518f4ecec
3 files changed
+54
-7
No files matched your search
@@ -41,7 +41,7 @@ cannot do.
|
||||
| **Downloads** | Codeman's Download links, and downloads Codeman opens in a new window, save the file to `~/Downloads` whatever its type, and a toast in Codeman names the file. Files the web view cannot display are saved the same way. |
|
||||
| **A page that stops** | When the web content process of a window ends (a WebKit crash, or macOS taking the process back under memory pressure), the window loads its page again instead of staying blank. A second time within 30 seconds, the main window goes back to the connect screen and a pop-out closes, so a page that cannot stay up never becomes a reload loop. |
|
||||
| **Apple logo** | On a MacBook Pro with a PROLED XDR display, the lit Apple logo on the lid says what the agents are doing. **View > Blink Apple Logo** is the one switch, off until you turn it on. The more the logo moves, the more it needs you: it breathes (a slow fade out and back in) while agents work, blinks every two seconds for a yellow tab and flashes three times, then rests, for a red one. Each state can take another style under **View > Apple Logo Blink Styles**: Off, Breathe, Calm, Slow, Wink, Heartbeat, Fast, or Alarm. Picking a style turns the switch on, and turning the switch on with every state off brings back the recommended three (Breathe, Calm, Alarm). When several states hold, the most urgent one with a style shows: red, then yellow, then work. Styles chosen in 1.50.0 are kept, with the switch on if any of them was. The menu only exists on a Mac with that logo. No password is needed. Shell tabs do not count. The app asks the server every two seconds, and a change has to hold for three seconds before the logo follows it, so an agent between two turns does not flicker it. It keeps working while the window is hidden, and with any Codeman server. The logo moves on its own hardware fades, the ones [LogoLight](https://github.com/Ark0N/LogoLight) uses, so a blink writes no setting, and a logo you switched off yourself stays off. While LogoLight runs, Codeman leaves the logo to it. While the app is closed nothing blinks, and while the display sleeps or the lid is closed the blink pauses and writes nothing. |
|
||||
| **Keep awake** | **Codeman > Keep This Mac Awake** keeps the Mac from going to sleep while Codeman's agents work on it: **While Agents Work** (an agent at work, and for 5 minutes after the last one stopped, so the gap between two turns or a reply from your phone does not let it drop off), or **Always** (while the app is open). Off until you choose. Only for a Codeman on this Mac (`127.0.0.1`, `localhost`, or one of this Mac's own addresses); for a server on another machine the menu says so. The display still sleeps. No password is needed for this part: the app holds a power assertion of its own, which macOS shows in Activity Monitor and in `pmset -g assertions` as *Codeman keeps this Mac awake while its agents work*, and which ends with the app, a crash or a Force Quit included. On a MacBook, **Only When Plugged In** (on by default) pauses it on battery, and **With the Lid Closed…** keeps a closed MacBook awake as well: nothing an app holds can do that, so ticking it asks for your administrator password and installs a small helper (see Decisions), and unticking it asks again and removes the helper. The lid is never kept awake on battery, whatever the settings, and while the lid is covered *Sleep* in the Apple menu does nothing. A line at the bottom of the menu says what it does right now. It lives in the app, so it ends when the app quits: Codeman keeps running as a LaunchAgent, but nothing keeps the Mac awake then. |
|
||||
| **Keep awake** | **Codeman > Keep This Mac Awake** keeps the Mac from going to sleep while Codeman's agents work on it: **While Agents Work** (an agent at work, and for 5 minutes after the last one stopped, so the gap between two turns or a reply from your phone does not let it drop off), or **Always** (while the app is open). Off until you choose. Only for a Codeman on this Mac (`127.0.0.1`, `localhost`, or one of this Mac's own addresses); for a server on another machine the menu says so. The display still sleeps. No password is needed for this part: the app holds a power assertion of its own, which macOS shows in Activity Monitor and in `pmset -g assertions` as *Codeman keeps this Mac awake while its agents work*, and which ends with the app, a crash or a Force Quit included. On a MacBook, **Only When Plugged In** (on by default) pauses it on battery, and **With the Lid Closed…** keeps a closed MacBook awake as well: nothing an app holds can do that, so ticking it asks for your administrator password and installs a small helper (see Decisions), and unticking it asks again and removes the helper. The lid is never kept awake on battery, whatever the settings, and while the lid is covered *Sleep* in the Apple menu does nothing. A line at the bottom of the menu says what it does right now. It lives in the app, so it ends when the app quits: Codeman keeps running as a LaunchAgent, but nothing keeps the Mac awake then. That is why, while it is on, closing the Codeman window hides the app instead of quitting it, as Cmd+H does: a click on its Dock icon brings it back, and only Cmd+Q (or Quit from the Dock) ends it. |
|
||||
| **Window** | Size, position and full-screen state are remembered. A saved window that hangs off the screen it opens on (it was last on a larger display that is gone, say) is pulled back on and shrunk to fit; one whose title bar is on no screen at all opens at the default size instead. Clicking the Dock icon brings back a minimized window. |
|
||||
|
||||
## Build (on a Mac)
|
||||
|
||||
@@ -33,7 +33,9 @@
|
||||
//! another, so no separate "remove" item and no helper left behind unasked.
|
||||
//!
|
||||
//! It lives in the app, so it ends with the app: Codeman's LaunchAgent keeps the server
|
||||
//! running after the app quits, but nothing keeps the Mac awake then.
|
||||
//! running after the app quits, but nothing keeps the Mac awake then. So while a mode is
|
||||
//! on, closing the main window hides the app instead of quitting it
|
||||
//! (`Mode::close_hides_app`): only Cmd+Q (or Quit from the Dock) ends it.
|
||||
//!
|
||||
//! The agents are asked natively, like the logo blink does (`agents`): WebKit stalls the
|
||||
//! event stream of a hidden page. A worker thread does the asking and the holding, and
|
||||
@@ -123,6 +125,14 @@ impl Mode {
|
||||
pub fn from_menu_id(id: &str) -> Option<Self> {
|
||||
Self::ALL.into_iter().find(|mode| mode.menu_id() == id)
|
||||
}
|
||||
|
||||
/// Whether closing the main window hides the app instead of quitting it.
|
||||
/// Keep-awake ends with the app, so while a mode is on, the red button (or
|
||||
/// Cmd+W) must not end it: the app hides as Cmd+H does, a click on its Dock
|
||||
/// icon brings it back, and Cmd+Q still quits.
|
||||
pub fn close_hides_app(self) -> bool {
|
||||
available() && self != Mode::Off
|
||||
}
|
||||
}
|
||||
|
||||
/// The `keepAwake` object in `desktop.json`.
|
||||
@@ -1221,6 +1231,16 @@ mod tests {
|
||||
assert!(Status::Recently.text().contains("5 minutes"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closing_the_window_hides_the_app_only_while_a_mode_is_on() {
|
||||
// Off quits as it always did; a mode that keeps the Mac awake must not
|
||||
// end with the red button, since keep-awake ends with the app.
|
||||
assert!(!Mode::Off.close_hides_app());
|
||||
for mode in [Mode::WhileWorking, Mode::Always] {
|
||||
assert_eq!(mode.close_hides_app(), available(), "{mode:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_menu_id_names_one_mode() {
|
||||
for mode in Mode::ALL {
|
||||
|
||||
@@ -727,6 +727,18 @@ fn build_webview(
|
||||
Ok(webview)
|
||||
}
|
||||
|
||||
/// Hides the whole app, as Cmd+H does. A click on the Dock icon (or Cmd+Tab)
|
||||
/// shows it again with every window as it was, a full-screen one included,
|
||||
/// which hiding the main window alone would not do.
|
||||
#[cfg(target_os = "macos")]
|
||||
fn hide_app(target: &EventLoopWindowTarget<UserEvent>) {
|
||||
use tao::platform::macos::EventLoopWindowTargetExtMacOS;
|
||||
target.hide_application();
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn hide_app(_target: &EventLoopWindowTarget<UserEvent>) {}
|
||||
|
||||
impl App {
|
||||
fn handle(
|
||||
&mut self,
|
||||
@@ -743,7 +755,7 @@ impl App {
|
||||
self.menu.help_menu.set_as_help_menu_for_nsapp();
|
||||
}
|
||||
}
|
||||
Event::WindowEvent { window_id, event, .. } => self.on_window_event(window_id, event, control_flow),
|
||||
Event::WindowEvent { window_id, event, .. } => self.on_window_event(window_id, event, target, control_flow),
|
||||
Event::UserEvent(event) => self.on_user_event(event, target),
|
||||
// A click on the Dock icon while no window is visible, which is
|
||||
// the main window minimized: tao only reports it, so bring the
|
||||
@@ -815,18 +827,33 @@ impl App {
|
||||
self.fullscreen_retry = Some((now + FULLSCREEN_RETRY_EVERY, attempts + 1));
|
||||
}
|
||||
|
||||
fn on_window_event(&mut self, id: WindowId, event: WindowEvent, control_flow: &mut ControlFlow) {
|
||||
fn on_window_event(
|
||||
&mut self,
|
||||
id: WindowId,
|
||||
event: WindowEvent,
|
||||
target: &EventLoopWindowTarget<UserEvent>,
|
||||
control_flow: &mut ControlFlow,
|
||||
) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
if id == self.main_id {
|
||||
if id != self.main_id {
|
||||
self.remove_window(id);
|
||||
} else if self.config.keep_awake.mode.close_hides_app() {
|
||||
// Keep This Mac Awake ends with the app, so closing the
|
||||
// window while it is on hides Codeman instead, as Cmd+H
|
||||
// does (full screen and pop-outs included): the Mac stays
|
||||
// awake, a click on the Dock icon brings it back, and
|
||||
// Cmd+Q still quits.
|
||||
self.record_main_frame();
|
||||
self.save_config();
|
||||
hide_app(target);
|
||||
} else {
|
||||
// Closing the main window quits; the server and its
|
||||
// sessions keep running, so nothing is lost.
|
||||
self.keep_awake.shutdown();
|
||||
self.record_main_frame();
|
||||
self.save_config();
|
||||
*control_flow = ControlFlow::Exit;
|
||||
} else {
|
||||
self.remove_window(id);
|
||||
}
|
||||
}
|
||||
WindowEvent::Moved(_) | WindowEvent::Resized(_) if id == self.main_id => self.record_main_frame(),
|
||||
|
||||
Reference in new issue
Block a user