Skip to content

Releases: Studnicky/yamete

v2.5.1

Choose a tag to compare

@github-actions github-actions released this 19 May 00:14
Immutable release. Only release title and notes can be modified.
8b5c8a2

Yamete v2.5.1

Added

  • Docs site SEO infrastructure. The VitePress site at
    https://studnicky.github.io/yamete now ships a full SEO surface area:
    per-page canonical / og: / twitter: meta via transformPageData,
    JSON-LD blocks for SoftwareApplication + WebSite + Organization
    • per-page BreadcrumbList, search-console verification slots
      (suppressed at build time until populated in package.json
      yamete.seo), hreflang declarations, modern robots hints
      (max-snippet:-1, max-image-preview:large), and a complete favicon
      stack with manifest.webmanifest for PWA-style install.
  • Sitemap + RSS feed. sitemap.xml is generated from every rendered
    page; feed.xml is composed from docs/CHANGELOG.md at build end
    and linked from the head via <link rel="alternate" type="application/rss+xml"> so release watchers, Sparkle-style
    updaters, and GitHub-action notifiers can subscribe to version drops
    without polling.
  • llms.txt index. A semantic table of contents at /llms.txt
    surfaces the docs structure to retrieval-augmented language models in
    the format proposed at https://llmstxt.org.
  • Release banner image. New docs/public/og-image.{svg,png}
    (1200×630 social card, pink theme, wincing face with impact rays)
    drives the unfurl preview on Discord, Slack, iMessage, Twitter/X, and
    LinkedIn. New docs/public/yamete-banner.svg.template is stamped per
    release by scripts/stamp-version.mjs (reads MARKETING_VERSION from
    project.yml) into yamete-banner.svg with the version pill visible;
    the release workflow now prepends this banner at the top of every
    GitHub release body via a raw.githubusercontent.com URL.
  • GitHub repository social preview. New
    docs/public/github-social.{svg,png} (1280×640, GitHub's recommended
    Repository → Settings → General → Social preview dimensions). Upload
    the PNG via the repo settings UI to control the unfurl card that
    shows when github.com/Studnicky/yamete itself is shared on Discord
    / Twitter / Slack / iMessage. GitHub provides no API for this
    surface; the asset lives in docs/public/ for convenient access.
  • Docs-build CI gate. ci.yml gains a docs-build job that runs
    npm ci, stamp-version:check, docs:build, and asserts the SEO
    outputs (sitemap, feed, robots, manifest, OG image, stamped banner)
    exist in docs/.vitepress/dist. Docs regressions are now caught at
    PR time instead of after merge to master.

Fixed

  • GitHub Pages deploy was failing since release 2.3.0 due to bare
    *<display>* placeholders in docs/using.md and docs/CHANGELOG.md
    which Vue's SFC parser read as unclosed HTML start tags during
    VitePress's vite-plugin-vue pass. Replaced with &lt;display&gt;
    entity escapes so the markdown italicises the placeholder without
    triggering the HTML tokeniser. The new docs-build CI gate (above)
    prevents this class of regression from reaching master again.
  • VitePress build no longer chokes on local-only design notes. The
    gitignored docs/art/ design-exploration markdown is now declared in
    srcExclude so VitePress's srcDir glob skips it. Previously the
    files were silently picked up by local builds and could throw
    interpolation parser errors on {{ }} brace pairs unrelated to the
    shipped site.

Build & release pipeline

  • stamp-version.mjs script reads MARKETING_VERSION from
    project.yml (the canonical macOS bundle version source of truth)
    and stamps every docs/public/*.svg.template into a sibling .svg
    with __VERSION__ replaced by v<version>. Hooked into
    predocs:build. --check mode exits non-zero if any output is
    stale so CI flags pre-tag drift.
  • render-og.mjs script rasterises og-image.svg and
    yamete-banner.svg to PNG via sharp (devDep). Hooked into
    predocs:build after stamp.
  • release.yml banner verification step greps the stamped
    yamete-banner.svg for the version string and fails the workflow
    if it doesn't match the tag — surfaces missed npm run stamp-version
    invocations before the release notes go out.

SHA256: 5d6c46c290249aff27231a01cff155482376b3626e1a315bd33f0faa27efface

v2.5.0

Choose a tag to compare

@github-actions github-actions released this 08 May 02:59
f0fb369

Added

  • Independent Caps Lock LED and Keyboard Brightness toggles. The
    Reactions group renders two separate accordion cards instead of one
    bundled "Keyboard" card, and the per-event matrix in Stimuli has a
    dedicated column per output (KbBright + Caps). Each output is gated
    by its own LEDOutputConfig flag inside LEDFlash.action so a user
    can enable the keyboard backlight without firing the Caps Lock LED,
    or vice versa. The Keyboard Brightness card hides on hosts where the
    backlight isn't available (Mac mini, Mac Pro, etc.).
  • Per-model coverage tables in every sensor source header.
    LidAngleSource.swift, GyroscopeSource.swift,
    AmbientLightSource.swift, and HeadphoneMotionAdapter.swift now
    document expected reach across M1 → M4 silicon and headphone models.
    The runtime probe is the source of truth; the tables are the
    documentation.

Changed

  • Lid angle now reads the dedicated Apple lid HID device, not the
    SPU IMU stream.
    Vendor 0x05AC, Product 0x8104, UsagePage
    0x0020, Usage 0x008A, Feature Report 1 polled at 30 Hz,
    decoded as UInt16 LE whole degrees from bytes [1..2]. The previous
    SPU usagePage 0xFF00 / usage 8 subscription was decoding
    uninitialized tail bytes — the IMU report is exhausted by accel/gyro
    at offsets 6/10/14, with no lid channel anywhere in the buffer. M2
    Pro/Max, M3 family, and M4 family laptops have the dedicated device;
    M1 (any), M2 base MBA / 13" MBP, and every desktop have either no
    device or no IMU silicon at all, and the toggle hides accordingly.
  • Ambient light corrected from usage 7 to usage 5. The M4 Max
    ioreg dump and AsahiLinux's reverse-engineering both confirm ALS
    rides on usagePage 0xFF00 / usage 5. The 7 was a guess that
    worked only because the SPU broker fans every report regardless of
    usage tuple — usage 7 does not exist on the SPU bus.
  • Gyroscope and ALS gain App Store sandbox parity with the
    accelerometer.
    isAvailable now combines device presence with a
    runtime activity probe via _last_event_timestamp on the
    dispatchGyro and dispatchAls services respectively. On Direct
    builds the probe is bypassed (the unsandboxed process can write the
    activation properties directly); on App Store builds the probe must
    report fresh-streaming for the toggle to surface. A one-shot
    activelyReporting= log fires at every source start so silent
    failures are visible in the audit log.
  • yamete-sensor-kickstart now activates gyro and ALS alongside
    accel.
    The helper previously short-circuited the
    AppleSPUHIDDriver iterator on dispatchAccel, leaving the gyro
    and ALS kernel-driver instances silent on App Store builds. The
    iterator now walks every service flagged with any of
    dispatchAccel, dispatchGyro, or dispatchAls and writes the
    activation triplet to each. Existing helper installs need a one-time
    sudo ./install.sh to pick up the change.
  • AccelHardware.isSensorActivelyReporting is now parameterised
    over dispatchKey
    so gyro and ALS can mirror the accel probe.
    The probe also no longer bails on the first stale sibling — it
    scans the entire AppleSPUHIDDriver iterator and returns true if
    any matching service is fresh.

Fixed

  • Lid sensor no longer fires nonstop LidSlammed reactions. Field
    audit logs from this release showed 3 500+ lidSlammed events per
    hour with physically impossible angles (-322°, -244°, etc.) — the
    consequence of the offset-18 garbage decode driving the slam-rate
    state machine into a loop. The dedicated-device rewrite produces
    real angles in the [0°, 180°] range, and a defensive sanity gate
    rejects anything outside that envelope.
  • keyboardBrightnessEnabled is now wired into pipeline rebuilds.
    Previously the impact pipeline's shouldRun predicate did not list
    keyboardBrightnessEnabled, so flipping it on alone (with every
    other output off) would not start sensors. The setting is now
    observed by Yamete.startSettingsObservation and included in
    rebuildSensorPipeline.shouldRun.
  • 40 lprojs synchronised with the new keyboard/Caps Lock string
    keys.
    setting_led_enabled, setting_keyboard_leds,
    help_keyboard_leds, and legend_led are dropped (replaced by
    setting_keyboard_brightness, setting_caps_lock_led,
    help_keyboard_brightness, help_caps_lock_led,
    desc_caps_lock_led, legend_keyboard_brightness,
    legend_caps_lock). Non-en locales receive English copy as a
    fallback for translator follow-up.

SHA256: 8354fa8ae04c8c4bfc707694487ad64fe4a323964220580dd975c339332ec2e9

v2.4.0

Choose a tag to compare

@github-actions github-actions released this 07 May 06:05
d0e9796

Added

  • Yamete+ rebrand for the Direct download. The notarised
    Developer-ID-signed download is now named Yamete+ instead of
    "Yamete Direct". The bundle is Yamete+.app, the executable is
    yamete-plus, the bundle identifier is
    com.studnicky.yamete.plus, the DMG asset is Yamete+.dmg. The
    App Store variant keeps its existing Yamete / com.studnicky.yamete
    identity. The menu's header title now reads "Yamete+" with a pink
    + glyph for Direct builds; App Store renders an unmarked
    "Yamete". The previous footer build-variant pill is gone — the
    header carries the indicator.
  • Footer reorganisation. Footer cells render as a single
    priority-ordered list flowing column-major into a 2-column grid:
    Debug Logging (Direct only) → Launch at Login → Version → Reset
    Settings → Info Links → Quit. With Debug Logging absent (App
    Store), Launch at Login slides into the top-left slot. The
    central inter-column divider that read as a table border is gone.

Changed

  • Auto-update path from "Yamete Direct" to "Yamete+". Existing
    pre-2.4.0 installs auto-update through their legacy Yamete.Direct.dmg
    asset name, which the 2.4.0 release publishes as a transitional
    alias alongside the canonical Yamete+.dmg. After the update
    installs, a one-shot bundle-relocation step at app launch detects
    the legacy /Applications/Yamete Direct.app path, copies the
    bundle to /Applications/Yamete+.app, removes the legacy bundle,
    and relaunches from the canonical path. UserDefaults migrate from
    com.studnicky.yamete.direct to com.studnicky.yamete.plus on
    first launch under the new identifier — every existing setting
    carries over. Input Monitoring TCC grants do not carry over
    (macOS scopes them per-bundle-identifier); the diagnostics row
    surfaces the missing-permission state with an "Open System
    Settings…" deep-link the moment a feature that needs it is used.
  • List containers drop their wrapping background. The faint
    grey rounded-rect around DeviceToggleList, SelectionList, and
    NotificationLocalePicker is gone; per-row dividers carry the
    "this is a list" affordance. Diagnostics pills lose their stroke
    for the same reason — the tinted fill is enough.
  • Pre-push hook auto-refreshes host-app snapshot baselines. New
    scripts/refresh-host-app-snapshots.sh runs only for pushes to
    release/* and hotfix/* branches; it wipes the sandbox-mirror
    at ~/Library/Containers/com.studnicky.yamete/Data/tmp/yamete-snapshots/HostApp/,
    invokes make test-host-app twice (recording iteration tolerates
    the standard ".missing"-mode failures, verification iteration
    must pass clean), and syncs freshly-recorded baselines back to
    Tests/__Snapshots__/HostApp/SnapshotUI_Tests/. If the source
    tree moved, the push is refused with a clear "stage and commit
    these and re-push" message. Feature branches and CI self-skip.

SHA256: 44d7525ce1a6a50fa2e6c59ee1f08a862c2c53138ce12f591a3d6d7a1faeb8db

v2.3.0

Choose a tag to compare

@github-actions github-actions released this 06 May 19:36
edb9127

Added

  • Diagnostics row. New surface in the menu directly below the header
    that aggregates active warnings/errors (paused state, missing
    Input Monitoring permission, sensor errors). Hidden when no
    diagnostics are active. The "paused" indicator that previously sat
    inside the top header row moved here so the header is back to a clean
    2x2 grid (title | impacts; subtext | last-impact).
  • Input Monitoring permission sentinel. Yamete now consults
    IOHIDCheckAccess at launch and on app foreground. When permission
    is missing (most commonly because make install's ad-hoc resign
    changed the bundle's cdhash and macOS silently revoked the prior
    grant), the diagnostics row surfaces "Input Monitoring required" with
    an "Open System Settings…" button that deep-links to the Privacy &
    Security pane. The previous failure mode was silent — no mouse clicks,
    no keyboard typing, no Caps Lock LED, with only a single warn-level
    log line as evidence.
  • InputMonitoringAccess helper (Sources/YameteCore/)
    consolidates the three previously-inline TCC checks
    (MouseActivitySource, KeyboardActivitySource,
    RealLEDBrightnessDriver) onto a single status() /
    requestIfUnknown() API. Sources never prompt; only
    Yamete.bootstrap() does, matching macOS's "one prompt per process
    lifetime" semantics.
  • Active output only toggle for audio. Mirrors the existing
    active-display-only toggle: when on, impact sounds route to the
    system default audio device only, ignoring the per-device selection.
    Hidden when ≤1 audio device is selected (same gating rule applied
    to the active-display-only toggle for consistency). Useful when the
    user wants the sound to follow "wherever I'm listening right now"
    instead of fanning out across configured outputs.
  • Audio transport class tagging. Each audio output row in the
    menu now renders an icon derived from
    kAudioDevicePropertyTransportType: laptopcomputer for built-in
    speakers, tv for DisplayPort/HDMI, cable.connector for USB,
    headphones.bluetooth for wireless, headphones for analog jack,
    bolt.horizontal for Thunderbolt, hifispeaker otherwise. Lets the
    user tell at a glance which outputs are monitor speakers vs USB
    headsets vs wireless devices.
  • EDID-based monitor↔speaker pairing. Audio devices that ride on a
    display's video cable (DisplayPort, HDMI, MacBook built-in) now get
    paired to the display they belong to. The pairing reads the standard
    EDID identifier triple (vendor, product, serial) from the audio
    device's IORegistry ancestors and matches against
    CGDisplayVendorNumber/ModelNumber/SerialNumber. Built-in
    speakers pair structurally to whatever display reports
    CGDisplayIsBuiltin == 1. Audio rows render an "attached to "
    footnote when pairing succeeds. See
    docs/architecture/display-audio-pairing
    for failure modes (USB-C docks, KVMs, adapter chains, monitors with
    zero-EDID-serial).

Changed

  • AppleSPUDevice lifecycle race fix. The multi-subscriber broker
    for the SPU HID device serialised concurrent open/close transitions
    through a new transitionLock. Previous behaviour: when the four
    initial subscribers (accel, gyro, ALS, lid) raced into the
    refcount-zero open path, the loser's cleanup called
    SensorActivation.deactivate() on the winner's still-live session,
    killing the report stream after one report and tripping the 5-second
    watchdog. The fix eliminates the race-loser teardown path entirely;
    there is now exactly one IOKit lifecycle operation in flight at a
    time across the broker. New regression test
    testConcurrentSubscribes_openDeviceCalledExactlyOnce pins the
    invariant.
  • Active-display-only toggle visibility. The toggle now only
    renders when ≥2 displays are selected. With 0 or 1 displays
    selected the toggle adds no signal and was UI noise. The audio
    side gets the same treatment.
  • Header layout simplification. The top bar's centre column
    (which held the conditional "Paused" pill) is gone. Header is now a
    clean 2x2: title and impacts counter on the first row, rotating
    subtext and last-impact tier on the second. The paused indicator
    surfaces in the diagnostics row instead.

Fixed

  • MainActor mentions in commit messages and prose. All future
    mentions in markdown / prose contexts use MainActor without the
    @ prefix to avoid GitHub's @-mention parser indexing an unrelated
    user. Source code (*.swift) keeps the @MainActor attribute as
    required by the Swift compiler.

SHA256: 29c4d0e6a87a8f5ca23fc2a71166de6e7cbf48996a63be55d00f7fddc2a81ee2

v2.2.0

Choose a tag to compare

@github-actions github-actions released this 06 May 02:52
e395007

Added

  • Thermal sensitivity ratchet. New thermalRatchet tunable gates
    the sensitivity of thermal state transitions to prevent hair-triggering
    during power/load transients. Configurable per-state thresholds (nominal,
    fair, serious, critical) with separate warmup and cooldown windows.

Changed

  • Documentation reorganization. ARCHITECTURE and CONFIGURATION guides
    split into dedicated pages in docs/. README streamlined to link to
    full documentation.
  • Diagram enhancements. Architecture diagrams now support zoom and
    improved visual clarity for large sensor topology graphs.

Fixed

  • Lint configuration alignment. ESLint and TypeScript strict checks
    unified across the build system.

SHA256: ef9ed31af120903c057e1406d4d7e6a32c77a295d12ef6d7beb62d9d149ff2de

v2.1.1

Choose a tag to compare

@github-actions github-actions released this 05 May 13:13
b8775e2

Fixed

  • Release workflow now reads docs/CHANGELOG.md instead of the
    pre-reorg root CHANGELOG.md path. The v2.1.0 tag's release run
    failed at the changelog-extract step (awk: can't open file CHANGELOG.md) and never published the GitHub Release with the DMG
    attached. CI-only fix; no app behaviour changes between v2.1.0 and
    v2.1.1. v2.1.1 is the version that actually ships a published release.

SHA256: 7af158fcaeaa25bc9d7a8f470c1ebf059336f59d913ed86ec97f7598d942ec39