mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eef02ab00a | ||
|
|
5cd8fa049a | ||
|
|
979275af1f | ||
|
|
ede99b309c | ||
|
|
c307d63d3b | ||
|
|
4c53d424a7 | ||
|
|
39f2e847d8 |
No files matched your search
@@ -40,7 +40,7 @@ cannot do.
|
||||
| **Links** | On Codeman's own pages (the dashboard and session windows), a link to another site opens in your default browser, also on `⌘`-click or a middle click. Pages can send at most 3 links to the browser at once, then 1 per second. Inside a rendered HTML preview or a web tab, a plain link to another site does nothing, while one that asks for a new window (`target="_blank"`, `window.open`) opens in your default browser within the same budget (see Known limits). |
|
||||
| **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. |
|
||||
| **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. LogoLight (1.3 or later) steps aside while Codeman shows its agents on the logo, and takes the logo back afterwards, with whatever it showed before; while an older LogoLight runs, Codeman leaves the logo to it. While the app is closed nothing blinks. With the lid closed it just goes on while the Mac stays awake on its power adapter (**Keep This Mac Awake > With the Lid Closed…**, or an external display): macOS keeps the logo dark under a closed lid, so the app tells the logo the lid is open and the keyboard that it is closed, which stays dark, as LogoLight's *Lit with Lid Closed* does. The light sensor is covered then, so the logo glows as it would in a dark room. The line under **Apple Logo Blink Styles** says what the closed lid did last (blinking, or why not). On battery, and while the display only sleeps with the lid open, the blink pauses and writes nothing. |
|
||||
| **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)
|
||||
@@ -249,6 +249,56 @@ changing on screen. So the short dark beats (Alarm's, Heartbeat's, Wink's, Fast'
|
||||
while their fall is still under way and never quite reach dark, and a long one (Calm's,
|
||||
Slow's, the breath's rest) times its light from when the rise starts.
|
||||
|
||||
With the lid closed the daemon tells every module so (`ClamshellUpdate` 1), and the logo's
|
||||
target is then 0 whatever `DisplayLogicalState` says. A client's `ClamshellUpdate` reaches the
|
||||
same modules, and LogoLight measured on the real MacBook what follows from that, first with an
|
||||
external display and, for its 1.3.1, on a Mac kept awake with none:
|
||||
- Kept awake with the lid closed and no other display, the built-in display stays online and
|
||||
`CGDisplayIsAsleep` says 0. So the lid sensor (`AppleClamshellState` on `IOPMrootDomain`)
|
||||
decides that the logo is out of sight, not the display list.
|
||||
- Once the daemon has handled the lid (`CBClamshellState` reads 1, a moment after the sensor),
|
||||
`ClamshellUpdate` 0 relights the logo and fades work as with the lid open. The child then
|
||||
writes `ClamshellUpdate` 1 to each keyboard alone, which stays dark, and to each display in
|
||||
`CBDisplayList` alone (`setProperty:withKey:andDisplay:`): told the lid is open, True Tone
|
||||
adapts an external display to the covered light sensor and tints it warm and dim.
|
||||
- The daemon sends the real lid state again when a display or a keyboard comes up. Every beat
|
||||
ends lit, so a logo found dark at the start of the next one is told again.
|
||||
- `DisplayPowerStateEvent` is never written. A client's DidSleep keeps the logo dark, with the
|
||||
lid open too, until someone writes DidWake.
|
||||
|
||||
The child does this only on the power adapter (on battery a closed MacBook sleeps, and a dark
|
||||
wake must not light the logo in a bag) and only with every keyboard reachable. The first rise
|
||||
has to move the logo within 1.5 s. If it does not, the logo is told again, three times in a
|
||||
row and then every half minute, and the menu line says it stayed dark. Ending a blink with the
|
||||
lid closed hands the logo back to normal (the automatic duration, then Unblank, which a held
|
||||
level needs) and then writes `ClamshellUpdate` 1, since normal for a closed lid is dark; the
|
||||
marker covers a child killed in between. When the lid opens, the daemon tells every module
|
||||
itself and nothing is written. What the closed lid did last (blinking, stayed dark, nothing to
|
||||
show with no agent at work, not tried and why, or that the Mac went to sleep under it, which
|
||||
the child sees as wall-clock time that passed while its monotonic clock stood still) goes to a
|
||||
file next to `desktop.json` (`logo-lid`, with the readings behind it on its second line), and
|
||||
the app shows its first line as **Lid Closed: …** under View > Apple Logo Blink Styles, or
|
||||
says nothing watches the lid while no child runs.
|
||||
|
||||
Whether the Mac stays awake under the lid is not the logo's business: Keep This Mac Awake's
|
||||
lid helper does that (`pmset disablesleep`, which holds the Mac at full wake), or LogoLight's
|
||||
own Lit with Lid Closed, or an external display. An assertion against system sleep alone would
|
||||
not do: LogoLight saw the Mac enter a dark wake with it, where the logo stays dark.
|
||||
|
||||
LogoLight drives the logo the same way, so the two would fight over every fade. The marker the
|
||||
child keeps next to the app's settings while it moves the logo (`logo-fade`, which also lets the
|
||||
next launch put back a logo a killed child left dark) holds the child's process ID, goes down
|
||||
right before the first write and comes off once the logo is back to normal. A LogoLight that says
|
||||
`LogoLightStepsAsideForCodeman` in its Info.plist (read from the running app's bundle, by its
|
||||
process ID) looks at that file every second and pauses while it names a running
|
||||
`codeman-desktop`, so the child waits two seconds after writing it before its first fade.
|
||||
LogoLight takes the logo back only once the file has been gone for a second and a half, so a
|
||||
blink that starts within a second of the last one's end (work, then a permission) does not
|
||||
wait again. An older LogoLight is left the logo, as before. LogoLight ships its side since
|
||||
1.3.0. With the lid closed, the two tell the logo the same thing (the lid is open), so a
|
||||
LogoLight that lights the logo under the lid and a blink do not undo each other; when a blink
|
||||
ends there the logo goes dark, and LogoLight lights it again as it takes it back.
|
||||
|
||||
The app must not end with the logo dark, and the release profile has `panic = "abort"`, so no
|
||||
cleanup code of the app can be relied on. So all of it runs as this same binary with
|
||||
`--logo-blink`, for as long as the switch is on and a server is open, tied to the app by
|
||||
@@ -329,8 +379,18 @@ crosses, so the login is never on a command line.
|
||||
outright (SIGKILL) leaves the logo where it was, perhaps dark, until the
|
||||
display next sleeps and wakes or the app next starts. Another app that drives
|
||||
the logo the same way and is not LogoLight is not recognised, and the two
|
||||
fight. `api/sessions` does not say which turns are Codeman's own, so with
|
||||
fight. The handover with a LogoLight that steps aside was checked against a
|
||||
stand-in process carrying LogoLight 1.3.1's Info.plist and against LogoLight's
|
||||
own tests, and ran in that release candidate next to LogoLight 1.3.1; its
|
||||
timing on a real logo was not measured. `api/sessions` does not say which turns are Codeman's own, so with
|
||||
prompt-cache keep-warm on, a keep-alive turn blinks too.
|
||||
- **Blinking with the lid closed was seen on one MacBook Pro so far** (macOS
|
||||
27.2, on power, a release candidate of 1.50.1 on 2026-11-10; the method is
|
||||
LogoLight's, which its author measured for LogoLight 1.3.1). Nothing was
|
||||
measured in that run, it was watched. Not checked, by LogoLight or here: the
|
||||
built-in display's brightness when the lid opens again, and an Apple display
|
||||
with its own light sensor. If the logo does not move under a closed lid, the
|
||||
menu line says "stayed dark".
|
||||
- **Self-signed HTTPS is refused.** A server started with `codeman web --https`
|
||||
uses a self-signed certificate, and WKWebView rejects it (wry has no trust
|
||||
override). Use `http://127.0.0.1:3000` for a server on this Mac. For another
|
||||
|
||||
+1423
-49
File diff suppressed because it is too large.
Load diff
@@ -110,8 +110,7 @@ enter it again (a single-user Codeman accepts the right password at once), or wa
|
||||
const KEYCHAIN_UNREADABLE: &str = "The saved password could not be read from the Keychain. Enter it again.";
|
||||
const PAGE_KEEPS_STOPPING: &str = "Codeman's page stopped twice in a row, so it was closed. Connect again.";
|
||||
const LOGO_SWITCHED_OFF: &str = "The Apple logo is switched off on this Mac, so it cannot blink.";
|
||||
const LOGO_DRIVEN_ELSEWHERE: &str =
|
||||
"LogoLight is driving the Apple logo, so Codeman leaves it alone. Quit LogoLight to see your agents on it.";
|
||||
const LOGO_DRIVEN_ELSEWHERE: &str = "An older LogoLight is driving the Apple logo, so Codeman leaves it alone. Update LogoLight to 1.3 or later (or quit it) to see your agents on it.";
|
||||
/// What a download's toast calls a file whose name is not known.
|
||||
const UNNAMED_FILE: &str = "the file";
|
||||
const START_REFUSED: &str = "Codeman can only be started here for an http://127.0.0.1 or http://localhost address.";
|
||||
@@ -184,6 +183,8 @@ enum UserEvent {
|
||||
/// install opened in Terminal, the install command copied): the message
|
||||
/// goes to the page if it is still the one choosing a server.
|
||||
ToConnectPage(serde_json::Value),
|
||||
/// What the closed lid did with the logo last (logo::watch_lid_status).
|
||||
LogoLidStatus(String),
|
||||
}
|
||||
|
||||
struct ProbeDone {
|
||||
@@ -401,6 +402,13 @@ fn main() {
|
||||
logo_watch: logo::Watcher::default(),
|
||||
};
|
||||
|
||||
if app.menu.logo_lid.is_some() {
|
||||
let lid_proxy = app.proxy.clone();
|
||||
logo::watch_lid_status(move |line| {
|
||||
let _ = lid_proxy.send_event(UserEvent::LogoLidStatus(line));
|
||||
});
|
||||
}
|
||||
|
||||
event_loop.run(move |event, target, control_flow| app.handle(event, target, control_flow));
|
||||
}
|
||||
|
||||
@@ -823,6 +831,7 @@ impl App {
|
||||
self.to_connect_page(msg);
|
||||
}
|
||||
}
|
||||
UserEvent::LogoLidStatus(line) => self.show_lid_status(line),
|
||||
UserEvent::ServerStartDone {
|
||||
generation,
|
||||
url,
|
||||
@@ -1040,6 +1049,20 @@ impl App {
|
||||
let server = phase.loaded().filter(|_| styles.active() && logo::available());
|
||||
self.logo_watch
|
||||
.sync(server.map(|url| (url.as_str(), self.auth.as_deref(), styles)));
|
||||
drop(phase);
|
||||
self.show_lid_status(logo::lid_status_line());
|
||||
}
|
||||
|
||||
/// The line under the logo's styles: what the closed lid did last, or that
|
||||
/// nothing watches it now.
|
||||
fn show_lid_status(&self, line: String) {
|
||||
if let Some(item) = &self.menu.logo_lid {
|
||||
if self.logo_watch.watching() {
|
||||
item.set_text(line);
|
||||
} else {
|
||||
item.set_text(logo::LID_NOT_WATCHED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn choose_logo_blink(&mut self, signal: Signal, style: BlinkStyle) {
|
||||
|
||||
@@ -32,6 +32,9 @@ pub struct AppMenu {
|
||||
pub logo_switch: Option<CheckMenuItem>,
|
||||
/// One item per state and style, empty without the switch.
|
||||
pub logo_styles: Vec<(Signal, BlinkStyle, CheckMenuItem)>,
|
||||
/// What the closed lid did with the logo last, a line that cannot be
|
||||
/// clicked. None without the switch.
|
||||
pub logo_lid: Option<MenuItem>,
|
||||
}
|
||||
|
||||
pub fn build_menu(config: &Config) -> muda::Result<AppMenu> {
|
||||
@@ -117,6 +120,7 @@ pub fn build_menu(config: &Config) -> muda::Result<AppMenu> {
|
||||
// style for each state behind it.
|
||||
let mut logo_styles = Vec::new();
|
||||
let mut logo_switch = None;
|
||||
let mut logo_lid = None;
|
||||
if logo::available() {
|
||||
let switch = CheckMenuItem::with_id(
|
||||
menu_id::LOGO_BLINK,
|
||||
@@ -140,9 +144,12 @@ pub fn build_menu(config: &Config) -> muda::Result<AppMenu> {
|
||||
submenu.append(&group)?;
|
||||
}
|
||||
// Right under "Always Open in Full Screen", before the separator.
|
||||
let lid = MenuItem::new(logo::lid_status_line(), false, None);
|
||||
view.insert(&switch, 4)?;
|
||||
view.insert(&submenu, 5)?;
|
||||
view.insert(&lid, 6)?;
|
||||
logo_switch = Some(switch);
|
||||
logo_lid = Some(lid);
|
||||
}
|
||||
let window_menu = Submenu::with_items(
|
||||
"Window",
|
||||
@@ -172,5 +179,6 @@ pub fn build_menu(config: &Config) -> muda::Result<AppMenu> {
|
||||
start_fullscreen,
|
||||
logo_switch,
|
||||
logo_styles,
|
||||
logo_lid,
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user