Skip to content

baguette v0.1.92

Choose a tag to compare

@github-actions github-actions released this 18 Aug 09:23
· 47 commits to main since this release
a8f64f9

Added

  • Motion — baguette motion start|set|stop, and a walk that drives it.
    Makes a simulator's apps read CMMotionActivity (walking, running,
    cycling, automotive), CMPedometer counters, and CMMotionManager
    samples. All three report unavailable in a stock simulator —
    CoreMotion and locationd both gate on a hardware-capability bit derived
    from the device's HW type, and a simulated device is an "Unsupported HW
    type", so locationd refuses a motion-activity subscription outright. The
    runtime even ships a simulation hook
    (simulateMotionState:withState:withHint:) that locationd accepts and
    that changes nothing, because the availability gate sits upstream of it.
    So this works the way the virtual camera does:
    by injecting a dylib into the app under test. Only apps launched after
    motion start see anything
    — dyld inserts at exec time.
    Turn it on in the browser's Location card and the walk joystick and
    route speeds it already posts classify the activity, so the preset you
    picked (Walk 1.4 · Cycle 6 · Drive 13.4) is what your app observes;
    pinning a point parks it as stationary, which is exactly when locationd
    drops course to -1. Motion stays opt-in — moving the device never
    arms a simulator-wide DYLD_INSERT_LIBRARIES on your behalf.
    The ABI notes are the part worth keeping: those {fff} structs pass by
    value
    (a pointer reads zeros and displaces the timestamp),
    CMGyroData's initialiser takes degrees while its property returns
    radians, CMDeviceMotion's quaternion is stored w,x,y,z against a
    public x,y,z,w, its gravity is derived from attitude rather than set,
    it ignores its own timestamp: argument, and CMMotionActivity built by
    poking ivars reads back fine and then crashes in -description. A
    load-time self-check verifies each surface and skips hooking any that
    fails
    , so a future iOS leaves apps seeing the platform's honest
    "unavailable" rather than fabricated garbage. Floor counting and the
    magnetometer are refused on purpose. See
    docs/features/motion.md.

  • One output size for every capture — --size appstore-6.9, and the
    same words in the UI.
    Screenshots and recordings, 2D and 3D, browser
    and CLI, now share one vocabulary: the App Store submission sizes
    (appstore-6.9, appstore-6.5, appstore-ipad-13), the common ratios
    (square, 16:9, 9:16, 4:3, 4:5), plus 1920x1080 and any bare
    W:H. Pick "App Store 6.9″" from the toolbar chip once and reproduce
    exactly those pixels in CI with --size appstore-6.9.

    A ratio grows rather than crops: square on a 1290 × 2796 phone
    gives a 2796 × 2796 canvas with the whole phone centred, not a
    1290 × 1290 cut through the middle of the screen. Cropping the device
    out of a marketing shot is the one thing nobody asking for a square
    wanted. --fit (contain / cover / stretch) and --background
    say what fills the rest. Unknown sizes are rejected — baguette never
    substitutes a nearby one.

    See docs/features/capture-size.md.

  • baguette record — video straight from the CLI. A booted simulator,
    an --output, and either --duration or Ctrl-C; the file is flushed
    and playable either way. Takes --size / --fit / --fps /
    --bitrate, writes .mp4 or .mov (the extension picks the
    container).

    docs/features/recording.md has argued for a while that server-side
    recording was tried and rejected, and that argument still stands for
    the live stream
    — a recorder attaching mid-stream never sees the
    SPS/PPS the encoder emitted on its first IDR, and an N+1th
    VideoToolbox session stutters every farm tile. Neither applies to a
    standalone CLI run: it owns the encode from frame one and has no
    competing viewer. So the design that was wrong as a passenger is the
    right one on its own, and it is wired into nothing — no route, no WS
    verb.

  • PNG screenshots, and a bezelled one. baguette screenshot --format png (inferred from a .png --output, so -o shot.png can
    never quietly hold JPEG bytes), plus GET …/screenshot.png and
    GET …/screenshot-bezel.png — the frame composited inside its
    DeviceKit chrome, which previously meant opening a browser and
    cropping. All three routes take ?size=&fit=&background=;
    screenshot.jpg with no new parameters returns byte-identical output
    to before.

  • A size chip on every capture surface, and a Record button where
    there wasn't one. The focus-mode view had only ever had Screenshot; it
    now records too, in both 2D and 3D — dropping the bezel in 3D, since
    the rendered frame already contains a device. The legacy stream
    sidebar and the device-farm focus pane get the same chip, each
    remembering its own selection, and it drives Capture and Record alike:
    a screenshot and a clip taken a second apart come out at the same
    dimensions. Saved files are named for what they are —
    …-appstore-6.9-1290x2796.png.

  • The browser's 3D export is now lossless. It used to save
    toDataURL() of the decoded video canvas — a ~960 px H.264 or MJPEG
    frame. It now asks the server to re-render the same pose at the picked
    size, so appstore-6.9 really is 1290 × 2796 instead of an upscale of
    a video still. render-3d --size and the route's "size" field take
    preset names as well as literal pixels, and the live 3D stream accepts
    size=.

  • Deep links — baguette openurl <url> and baguette schemes. Opens a link
    on a booted simulator, and lists the URL schemes its apps registered (ranked:
    an app's own scheme before its reverse-DNS and exp+ aliases). Unlike
    simctl openurl / idb open / Maestro, openurl warns that https:// lands
    in Safari rather than your app — the simulator doesn't resolve associated
    domains, so dispatch succeeds and your app never comes up. Schemes need two
    reads:
    simctl listapps gives the roster and each app's Path but not
    CFBundleURLTypes, so those come from <Path>/Info.plist. Over HTTP as
    POST /simulators/:udid/openurl and GET /simulators/:udid/schemes.json,
    reachable by a plugin holding the new open-url capability — which is apart
    from apps, since this only launches software that is already installed.
    See docs/features/deep-links.md.

  • A deep-link panel, as an installable official plugin. Not in the toolbar —
    baguette ships the toolbar, this is a thing you choose:

    baguette bakery add tddworks/baguette
    baguette plugin install deeplink

    This makes baguette's own repo a bakery. Only a11y still ships inside the
    binary; everything else maintained alongside baguette is official and
    installed on purpose.

  • Plugin panels can be operated, not just read. A panel was a report: the
    host ran a command and drew the rows. It can now carry a text field
    (body.prompt) and tickable rows (body.control — switches, checkboxes,
    radios, grouped so one panel can ask several questions). Both invoke the
    panel's own source command with args, which is the path rowAction: "run"
    already took, so this adds widgets rather than an execution model — still no
    plugin code in the page.

    The field completes as you type (Tab / to accept), remembers the last 25
    submissions on / , filters the rows already on screen, and takes a row's
    text on click via rowAction: "fill" — so a list of suggestions behaves like a
    URL bar instead of a launcher. History is browser-side: a plugin sees what you
    submit, never what you typed before.

    Ticks are local and batched — no subprocess per tick, and rows the device
    hasn't confirmed are drawn pending until the submit returns. Every answer
    rebuilds them from what the plugin reported, so a refused setting snaps back.
    a11y's display panel uses this (1.2.0) and no longer writes "● Light" /
    "○ Dark" into row titles. All of it is additive — apiVersion stays 1.

Changed

  • The plugins rail follows the focus-mode design system. The rail, its
    flyout, the panel and the bakery modal were styled by a stylesheet
    sim-plugins.js injected into document.head — and a stylesheet written
    beside its module can't see the --nv-* theme tokens, which are defined on
    #simNativeView. Every colour in it therefore carried a guessed fallback,
    and the guesses were light-theme values: light-slate shadows with no dark
    variant, a white var(--panel) text field inside a dark glass modal, and an
    accent pulled from sim.html's light-only --accent rather than
    --nv-accent. It also invented its own geometry — 34×34 rail buttons at
    radius 9 against the toolbar's 30×28 at radius 8, a 600 10px/0.06em accent
    heading against the app's 700 9.5px/0.10em faint one, a fourth primary
    button, and four 2px accent seams no other surface has.

    All of it now lives in sim-native.html under #simNativeView, beside the
    panels it sits next to, with no fallbacks — the same arrangement the logs,
    status-bar, location and a11y panels already use. The companion-screens rail
    moved with it and the two now share every rule they had duplicated. The rails
    being separate is the trust signal (see docs/features/plugins.md); the
    plugin rail keeps its accent emblem to say which is which, and drops the
    second colour system that was layered on top.

Added

  • --nv-danger, --nv-warn and --nv-scrim theme tokens, in both light and
    dark. A panel reporting an error previously picked its own red, and the one
    it picked was a light-theme #b91c1c on a near-black page. Plugin row
    severity is now a data-severity attribute the stylesheet colours, rather
    than an inline hex the module carries.

What's Changed

  • fix(web): style the plugins rail from the focus-mode design system by @hanrw in #48
  • feat: add deep links and scheme completion for plugins by @crockalet in #50
  • feat: size-aware stills in CaptureGallery by @hanrw in #51
  • feat: size-aware recording and a bezel-less 3D mode by @hanrw in #52
  • feat: lossless sized 3D capture in Sim3DPanel by @hanrw in #53
  • feat: capture size picker on the legacy stream sidebar (Capture + Record) by @hanrw in #54
  • feat: render-3d and the live 3D stream accept capture-size presets by @hanrw in #55
  • feat: capture output size on the device farm focus pane by @hanrw in #56
  • feat: --size / --fit / --background / --format for baguette screenshot by @hanrw in #57
  • docs: document the shared capture-size vocabulary by @hanrw in #58
  • Sized screenshots and recording in the native view by @hanrw in #59
  • feat: sized screenshots, PNG output, and a server-side bezel composite by @hanrw in #60
  • feat(motion): drive CMMotionActivity, CMPedometer and CMMotionManager from a walk by @crockalet in #61
  • fix: composite bezel captures at framebuffer resolution, and grow ratios on render-3d by @hanrw in #62
  • feat: baguette record — H.264 video from the CLI, at the shared capture sizes by @hanrw in #63
  • feat: supersample the 3D stream when a capture size is picked by @hanrw in #64
  • feat: fold the focus toolbar into context clusters by @hanrw in #65

Full Changelog: v0.1.91...v0.1.92