mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 18:39:33 +01:00
The Mac app showed one server at a time, and moving to another machine meant Switch Server and a fresh connect. Now every server gets a tab in the title bar, and every tab stays live. - Tab strip: a small bundled page (assets/tabs.html) in its own web view across the top of the window, with the traffic lights inset into it. Each tab has a colour and a status badge: a red count when sessions need you, an amber dot when they are waiting, a blue ring while they work, a dashed circle while the server does not answer. - The status comes from the app, not from the page: one thread per tab reads GET /api/sessions every 2 seconds (macOS stalls hidden web views), backs off for 60 s on a 429 and stops on 401/403, so a wrong password is never retried in a loop. - + or ⌘T opens the connect screen in a new tab. A server that is already open just brings its tab forward instead of opening twice, also when two connects race (checked again when the probe ends). - Double-click or the right-click menu renames a tab; the same menu sets the colour, reloads, reconnects, closes, or closes the others. Tabs can be dragged. A new Server menu carries ⌘T, ⌘W, ⇧⌘T (reopen closed tab, last 10), ⌘0, ⇧⌘[ / ⇧⌘] and ⌘1 to ⌘8 (⌘9 is the last tab once there are nine). Close Window moves to ⇧⌘W. Ctrl+Tab and Alt+1..9 stay with Codeman's own session switching. - ⌘0 shows every server at once (assets/overview.html): the ones that need you first, up to four sessions each with what they ask, and a click on a session opens its tab and selects it. - Tabs are saved in desktop.json (tabs, activeTab) and come back at launch, connecting by themselves when auto-connect is on. - Logins: each tab keeps its own Basic credentials, and a challenge is answered only with the login of the tab whose web view asked (macos.rs binds each WKWebView to its tab). - Cookies: on macOS 14 and later every server gets its own WebKit data store (WKWebsiteDataStore dataStoreForIdentifier, with a stable id hashed from the server URL), so a server on port 3000 and a beta on 5000 of the same host cannot log each other out. Before macOS 14 a second tab on a host that is already open is refused with a message. The server used last before this change keeps the default store, so its per-device settings (font size, skin) survive; every other server starts those fresh once. - The lid logo blink watches all open servers, and Keep This Mac Awake follows the tab that runs on this Mac. New pure model in src/tabs.rs (names, colours, order, shortcuts, strip and overview state) with unit tests; agents.rs reads the per-session detail for the badges and the overview; policy.rs parses the strip and overview IPC and refuses it from any other frame. Browser tests drive both bundled pages (test/desktop-tabs-pages.browser.test.ts) and the connect page's restored-tab path. Not yet run in a real window: the Mac mini has no screen session. The owner checks the traffic-light placement, clicks under the transparent title bar, window drag and zoom from the strip, keyboard focus after strip clicks, and two logged-in servers side by side. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
101 lines
2.9 KiB
TOML
101 lines
2.9 KiB
TOML
[package]
|
|
name = "codeman-desktop"
|
|
version = "0.1.0"
|
|
edition = "2021"
|
|
rust-version = "1.90"
|
|
description = "Codeman Desktop: a small native window for a Codeman server"
|
|
license = "MIT"
|
|
publish = false
|
|
|
|
[[bin]]
|
|
name = "codeman-desktop"
|
|
path = "src/main.rs"
|
|
|
|
[dependencies]
|
|
# Window + event loop (tao), the system web view (wry: WKWebView on macOS,
|
|
# WebView2 on Windows, WebKitGTK on Linux) and native menus (muda). Tauri is
|
|
# built on these three; using them directly keeps the app small and gives us
|
|
# `load_url_with_headers`, which Tauri's navigate() does not have (see README).
|
|
tao = "0.37"
|
|
wry = { version = "0.57", features = ["devtools"] }
|
|
muda = "0.21"
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
url = "2"
|
|
# The versions ureq and wry already pull in, so neither crate is built twice
|
|
# (ureq builds base64 without its default SIMD engines, and so do we).
|
|
base64 = { version = "0.23", default-features = false, features = ["std"] }
|
|
dirs = "7"
|
|
# The reachability probe. platform-verifier = the OS trust store, the same one
|
|
# WKWebView uses, so the probe and the window agree on which certificates pass.
|
|
ureq = { version = "3", features = ["platform-verifier"] }
|
|
|
|
[target.'cfg(target_os = "macos")'.dependencies]
|
|
security-framework = "3"
|
|
# Ignoring signals in the logo blinker (src/logo.rs). Already in the tree
|
|
# through wry and tao, so no new crate is added.
|
|
libc = "0.2"
|
|
# JavaScript dialogs, HTTP Basic challenges, clearing one server's cookies and
|
|
# the window's content size (src/macos.rs). The versions wry already pulls in, so no new
|
|
# crate is added.
|
|
objc2 = { version = "0.6.5", default-features = false, features = ["std"] }
|
|
block2 = { version = "0.6.2", default-features = false, features = ["std"] }
|
|
objc2-foundation = { version = "0.3.2", default-features = false, features = [
|
|
"std",
|
|
"objc2-core-foundation",
|
|
"block2",
|
|
"NSArray",
|
|
"NSBundle",
|
|
"NSDate",
|
|
"NSDictionary",
|
|
"NSGeometry",
|
|
"NSHTTPCookie",
|
|
"NSObject",
|
|
"NSOperation",
|
|
"NSProcessInfo",
|
|
"NSRange",
|
|
"NSSet",
|
|
"NSString",
|
|
"NSURL",
|
|
"NSURLAuthenticationChallenge",
|
|
"NSURLCredential",
|
|
"NSURLProtectionSpace",
|
|
"NSURLRequest",
|
|
"NSURLResponse",
|
|
"NSURLSession",
|
|
"NSUUID",
|
|
"NSValue",
|
|
] }
|
|
objc2-app-kit = { version = "0.3.2", default-features = false, features = [
|
|
"std",
|
|
"objc2-core-foundation",
|
|
"NSAlert",
|
|
"NSApplication",
|
|
"NSButton",
|
|
"NSControl",
|
|
"NSResponder",
|
|
"NSTextField",
|
|
"NSView",
|
|
"NSWindow",
|
|
] }
|
|
objc2-web-kit = { version = "0.3.2", default-features = false, features = [
|
|
"std",
|
|
"block2",
|
|
"objc2-app-kit",
|
|
"WKFrameInfo",
|
|
"WKHTTPCookieStore",
|
|
"WKNavigationDelegate",
|
|
"WKSecurityOrigin",
|
|
"WKUIDelegate",
|
|
"WKWebView",
|
|
"WKWebsiteDataRecord",
|
|
"WKWebsiteDataStore",
|
|
] }
|
|
|
|
[profile.release]
|
|
opt-level = "s"
|
|
lto = true
|
|
codegen-units = 1
|
|
strip = true
|
|
panic = "abort"
|