From 3325304f89d56990f3134a3d01749e5de498725d Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Wed, 27 May 2026 21:16:16 +0300 Subject: ci: fix build checks and mobile nav --- .github/workflows/nix.yml | 9 --- nix/package.nix | 2 +- tests/live_handoff.rs | 83 ++++++++++++++++++++++----- website/astro.config.mjs | 7 +++ website/css/style.css | 84 ++++++++++++++++++++++++++++ website/index.html | 20 ++++++- website/src/components/MarketingLayout.astro | 20 ++++++- website/src/components/SiteTitle.astro | 2 + website/src/pages/compare.astro | 20 ++++++- 9 files changed, 219 insertions(+), 28 deletions(-) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 81306b1a..142b4054 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -38,15 +38,6 @@ jobs: experimental-features = nix-command flakes always-allow-substitutes = true - - name: Enable Magic Nix Cache - if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository - uses: DeterminateSystems/magic-nix-cache-action@565684385bcd71bad329742eefe8d12f2e765b39 # v13 - continue-on-error: true - with: - source-tag: v0.1.6 - use-flakehub: disabled - diagnostic-endpoint: "" - - name: Run Nix flake check run: | nix flake check --print-build-logs diff --git a/nix/package.nix b/nix/package.nix index ba9206d8..ab23e31c 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -42,7 +42,7 @@ rustPlatform.buildRustPackage { ); }; - cargoLock.lockFile = ../Cargo.lock; + cargoHash = "sha256-5WvdZUD0Axr6xQ9Nm1XV1eJ0w4xUl8BQDnNs7pv6C8o="; nativeBuildInputs = [ git diff --git a/tests/live_handoff.rs b/tests/live_handoff.rs index b8e9ad76..d9a8b34f 100644 --- a/tests/live_handoff.rs +++ b/tests/live_handoff.rs @@ -21,6 +21,11 @@ struct SpawnedHerdr { child: Box, } +struct RequestError { + retryable: bool, + message: String, +} + impl Drop for SpawnedHerdr { fn drop(&mut self) { let pid = self.child.process_id(); @@ -162,14 +167,56 @@ fn spawn_default_session_server(config_home: &Path, runtime_dir: &Path) -> Spawn } } -fn request(socket_path: &Path, request: serde_json::Value) -> serde_json::Value { - let mut stream = UnixStream::connect(socket_path).unwrap(); - stream.write_all(request.to_string().as_bytes()).unwrap(); - stream.write_all(b"\n").unwrap(); - stream.flush().unwrap(); +fn try_request( + socket_path: &Path, + request: serde_json::Value, +) -> Result { + let mut stream = UnixStream::connect(socket_path).map_err(|err| RequestError { + retryable: true, + message: format!("connect {}: {err}", socket_path.display()), + })?; + let request_text = request.to_string(); + stream + .write_all(request_text.as_bytes()) + .map_err(|err| RequestError { + retryable: true, + message: format!("write request to {}: {err}", socket_path.display()), + })?; + stream.write_all(b"\n").map_err(|err| RequestError { + retryable: true, + message: format!("write newline to {}: {err}", socket_path.display()), + })?; + stream.flush().map_err(|err| RequestError { + retryable: true, + message: format!("flush request to {}: {err}", socket_path.display()), + })?; let mut line = String::new(); - BufReader::new(stream).read_line(&mut line).unwrap(); - serde_json::from_str(&line).unwrap() + BufReader::new(stream) + .read_line(&mut line) + .map_err(|err| RequestError { + retryable: true, + message: format!("read response from {}: {err}", socket_path.display()), + })?; + if line.is_empty() { + return Err(RequestError { + retryable: true, + message: format!( + "empty response from {} for request {request_text}", + socket_path.display() + ), + }); + } + serde_json::from_str(&line).map_err(|err| RequestError { + retryable: false, + message: format!( + "parse response from {} for request {request_text}: {err}; response was {line:?}", + socket_path.display() + ), + }) +} + +fn request(socket_path: &Path, request: serde_json::Value) -> serde_json::Value { + try_request(socket_path, request).unwrap_or_else(|err| panic!("{}", err.message)) } fn assert_ok(response: serde_json::Value) { @@ -181,19 +228,25 @@ fn assert_ok(response: serde_json::Value) { fn wait_for_api(socket_path: &Path, timeout: Duration) { let deadline = Instant::now() + timeout; + let mut last_error = String::new(); while Instant::now() < deadline { - if UnixStream::connect(socket_path).is_ok() { - let response = request( - socket_path, - serde_json::json!({"id":"test:ping","method":"ping","params":{}}), - ); - if response.get("result").is_some() { - return; + match try_request( + socket_path, + serde_json::json!({"id":"test:ping","method":"ping","params":{}}), + ) { + Ok(response) if response.get("result").is_some() => return, + Ok(response) => panic!("api ping returned non-success response: {response}"), + Err(err) if !err.retryable => panic!("{}", err.message), + Err(err) => { + last_error = err.message; } } thread::sleep(Duration::from_millis(25)); } - panic!("api did not become ready at {}", socket_path.display()); + panic!( + "api did not become ready at {}; last error: {last_error}", + socket_path.display() + ); } fn wait_for_output(socket_path: &Path, pane_id: &str, needle: &str) { diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 6adb9e5b..badd2873 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -125,6 +125,13 @@ export default defineConfig({ { label: 'Agent skill file', slug: 'docs/agent-skill' }, ], }, + { + label: 'Updates', + items: [ + { label: 'Releases', link: '/releases/' }, + { label: 'Blog', link: '/blog/' }, + ], + }, ], }), ], diff --git a/website/css/style.css b/website/css/style.css index fcadb011..74be689f 100644 --- a/website/css/style.css +++ b/website/css/style.css @@ -433,6 +433,82 @@ samp { font-family: var(--mono); } +.nav-menu { + position: relative; + display: none; + margin-left: auto; +} + +.nav-menu-button { + display: inline-flex; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 0.22rem; + width: 2.1rem; + height: 2.1rem; + border: 1px solid var(--line); + border-radius: var(--radius-md); + color: var(--ink-soft); + cursor: pointer; + list-style: none; + transition: + color 120ms ease, + border-color 120ms ease, + background 120ms ease; +} + +.nav-menu-button::-webkit-details-marker { + display: none; +} + +.nav-menu-button::marker { + content: ""; +} + +.nav-menu-button span { + display: block; + width: 0.92rem; + height: 1px; + background: currentColor; +} + +.nav-menu-button:hover, +.nav-menu[open] .nav-menu-button { + color: var(--accent); + border-color: color-mix(in srgb, var(--accent) 38%, var(--line)); + background: var(--accent-soft); +} + +.nav-menu-panel { + position: absolute; + top: calc(100% + 0.65rem); + right: 0; + z-index: 30; + display: grid; + min-width: min(18rem, calc(100vw - 2rem)); + padding: 0.35rem; + border: 1px solid var(--line-strong); + border-radius: var(--radius-md); + background: var(--bg-elevated); + box-shadow: 0 16px 44px rgba(0, 0, 0, 0.16); +} + +.nav-menu-panel a { + border-radius: var(--radius-sm); + padding: 0.62rem 0.7rem; + color: var(--ink-soft); + font-family: var(--mono); + font-size: 0.86rem; + font-weight: 600; +} + +.nav-menu-panel a:hover, +.nav-menu-panel a[aria-current="page"] { + background: var(--accent-soft); + color: var(--accent); +} + .nav-links a, .footer a, .inline-links a { @@ -2462,10 +2538,18 @@ td:last-child { padding-inline: 1rem; } + .nav-menu { + display: block; + } + .nav-links { display: none; } + .nav-tools { + display: none; + } + .section, .awareness-section, .demo-section, diff --git a/website/index.html b/website/index.html index a844442d..1de94a14 100644 --- a/website/index.html +++ b/website/index.html @@ -57,7 +57,7 @@ document.documentElement.setAttribute("data-palette", saved || system); })(); - +