Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,6 @@ jobs:

- name: Detect unused files and dependencies
run: npm run knip

- name: Test
run: npm test
1 change: 1 addition & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"dev": "astro dev --port 4322 --open",
"build": "astro build",
"preview": "astro preview",
"test": "node --test \"tests/**/*.test.js\"",
"astro": "astro",
"typecheck": "astro check",
"format:check": "prettier --check src",
Expand Down
Binary file removed docs/public/features/posters/agentic-demo.jpg
Binary file not shown.
Binary file added docs/public/features/posters/agentic-demo.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/public/features/posters/asc-drag-drop-demo.jpg
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/public/features/posters/onboarding-compare.jpg
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/public/features/posters/quick-actions.jpg
Binary file not shown.
Binary file added docs/public/features/posters/quick-actions.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/public/features/posters/recording-example.jpg
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/public/features/posters/simulator-camera.jpg
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/public/features/posters/slow-animations.jpg
Binary file not shown.
Binary file added docs/public/features/posters/slow-animations.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
11 changes: 8 additions & 3 deletions docs/public/features/reencode-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@
# - encodes H.264 (high profile, yuv420p) at CRF 28
# - drops the audio track (feature videos play muted)
# - moves the moov atom to the front (+faststart) for progressive streaming
# - exports the first frame to ./posters/<name>.jpg as a lightweight poster
# - exports the first frame to ./posters/<name>.webp as a lightweight poster
#
# .mov sources are converted to .mp4 and the original .mov is removed.
# Re-encoding is idempotent enough to re-run; outputs replace the originals.

set -euo pipefail

# Requires ffmpeg (video encode) and cwebp (poster encode: brew install webp).
command -v cwebp >/dev/null || { echo "cwebp not found — brew install webp"; exit 1; }

INPUT_DIR="$(cd "$(dirname "$0")" && pwd)"
POSTER_DIR="${INPUT_DIR}/posters"
MAX_LONG_EDGE=1440
Expand All @@ -34,7 +37,7 @@ for file in "$INPUT_DIR"/*.mp4 "$INPUT_DIR"/*.mov; do
name="${filename%.*}"
tmp="${INPUT_DIR}/${name}.tmp.mp4"
output="${INPUT_DIR}/${name}.mp4"
poster="${POSTER_DIR}/${name}.jpg"
poster="${POSTER_DIR}/${name}.webp"

echo "Optimizing $filename → $(basename "$output")"

Expand All @@ -54,7 +57,9 @@ for file in "$INPUT_DIR"/*.mp4 "$INPUT_DIR"/*.mov; do

echo " Poster → posters/$(basename "$poster")"
ffmpeg -y -i "$output" -vf "$SCALE_FILTER" \
-frames:v 1 -update 1 -q:v 4 "$poster"
-frames:v 1 -update 1 -f image2 -c:v png "${POSTER_DIR}/${name}.tmp.png"
cwebp -quiet -q 78 "${POSTER_DIR}/${name}.tmp.png" -o "$poster"
rm -f "${POSTER_DIR}/${name}.tmp.png"
done

echo "Done. Remember to update any .md asset paths if a .mov became .mp4."
2 changes: 1 addition & 1 deletion docs/src/content/blog/ai-agents-ios-simulator/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ I do not want an agent to guess whether those changes worked. I want it to open
<video
controls
playsinline
poster="/features/posters/agentic-demo.jpg"
poster="/features/posters/agentic-demo.webp"
class="w-full rounded-2xl"
>
<source src="/features/agentic-demo.mp4" type="video/mp4" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ RocketSim takes a different route. With [RocketSim Connect](/docs/getting-starte
<video
controls
playsinline
poster="/features/posters/network-monitoring-demo.jpg"
poster="/features/posters/network-monitoring-demo.webp"
class="w-full rounded-2xl"
>
<source src="/features/network-monitoring-demo.mp4" type="video/mp4" />
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/blog/ios-simulator-camera/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ I have made this mistake myself while building camera-related flows: I postponed
<video
controls
playsinline
poster="/features/posters/simulator-camera.jpg"
poster="/features/posters/simulator-camera.webp"
class="w-full rounded-2xl"
>
<source src="/features/simulator-camera.mp4" type="video/mp4" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
---
title: "Network Speed Control & Simulator Airplane Mode"
description: "Throttle network speed on the iOS Simulator to test your app under 3G, Edge, or airplane mode conditions. Your Mac stays online while the Simulator is throttled."
title: "Simulator Airplane Mode & Network Speed"
description: "Turn on airplane mode in the iOS Simulator and throttle to 3G, Edge, or 100% packet loss. Your Mac stays online while only the Simulator loses connectivity."
sidebar:
order: 1
label: "Network Speed Control"
---

It's essential to test your app in bad networking conditions during app development. Historically, you've had to test this on an actual device using Airplane mode or turn off your Mac's WiFi. Both are not ideal and often slow you down during development.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ RocketSim brings its fastest testing workflows to a connected physical iPhone or
muted
playsinline
preload="metadata"
poster="/features/posters/physical-device-testing.jpg"
poster="/features/posters/physical-device-testing.webp"
aria-label="RocketSim simulating a location and opening a deep link on a connected physical iOS device"
>
<source src="/features/physical-device-testing.mp4" type="video/mp4" />
Expand Down
4 changes: 2 additions & 2 deletions docs/src/layouts/components/Feature.astro
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ const parsedCaption = asset.caption
? marked.parseInline(asset.caption)
: undefined;

// Posters live alongside the videos in `/features/posters/<name>.jpg` and are
// Posters live alongside the videos in `/features/posters/<name>.webp` and are
// generated by `public/features/reencode-all.sh`. They give the video area
// instant visual content while the actual video bytes stay deferred.
const posterPath =
asset.type === "video"
? asset.path.replace(/\/([^/]+)\.[^/.]+$/, "/posters/$1.jpg")
? asset.path.replace(/\/([^/]+)\.[^/.]+$/, "/posters/$1.webp")
: undefined;

const blogPath = blogSlug
Expand Down
68 changes: 68 additions & 0 deletions docs/tests/build-output.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { readFileSync, readdirSync, statSync } from "node:fs";
import { join } from "node:path";

const DIST = new URL("../dist/", import.meta.url).pathname;

export function readDist(relativePath) {
return readFileSync(join(DIST, relativePath), "utf8");
}

test("feature posters ship as webp, not jpeg", () => {
const posters = readdirSync(join(DIST, "features/posters"));
const jpegs = posters.filter((f) => f.endsWith(".jpg"));
assert.deepEqual(jpegs, [], `expected no .jpg posters, found: ${jpegs}`);
});

test("poster payload shrank against the jpeg baseline", () => {
const dir = join(DIST, "features/posters");
const total = readdirSync(dir).reduce(
(sum, f) => sum + statSync(join(dir, f)).size,
0,
);
// Byte-sum of the 13 jpg posters, measured 2026-07-25. Threshold is 0.75,
// not 0.55: the converted set saves ~51%, but the directory also keeps the
// pre-existing physical-device-location-system-behaviors.webp (105 KB,
// no jpg twin), which this change does not touch.
const BASELINE_BYTES = 856528;
assert.ok(
total <= BASELINE_BYTES * 0.75,
`poster payload ${Math.round(total / 1024)}KB exceeds 75% of baseline`,
);
});

test("no rendered page references a .jpg poster", () => {
const html = readDist("index.html");
assert.equal(
/posters\/[^"']*\.jpg/.test(html),
false,
"homepage still references a .jpg poster",
);
});

test("homepage videos stay deferred", () => {
const html = readDist("index.html");
const videoCount = (html.match(/<video/g) || []).length;
const preloadNone = (html.match(/preload="none"/g) || []).length;
assert.equal(
preloadNone,
videoCount,
"every homepage video must keep preload=none",
);
});

test("airplane-mode doc leads its title with the searched phrase", () => {
const html = readDist(
"docs/features/networking/network-speed-control/index.html",
);
const titleMatch = html.match(/<title>([^<]*)<\/title>/);
assert.ok(titleMatch, "no <title> tag found in the built page");
const title = titleMatch[1];
assert.match(
title,
/^Simulator Airplane Mode/,
`title should lead with the query phrase, got: ${title}`,
);
assert.ok(title.length <= 60, `title is ${title.length} chars, max 60`);
});