Releases: Studnicky/yamete
Releases · Studnicky/yamete
Release list
v2.5.1
Immutable
release. Only release title and notes can be modified.
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 viatransformPageData,
JSON-LD blocks forSoftwareApplication+WebSite+Organization- per-page
BreadcrumbList, search-console verification slots
(suppressed at build time until populated inpackage.json→
yamete.seo),hreflangdeclarations, modernrobotshints
(max-snippet:-1,max-image-preview:large), and a complete favicon
stack withmanifest.webmanifestfor PWA-style install.
- per-page
- Sitemap + RSS feed.
sitemap.xmlis generated from every rendered
page;feed.xmlis composed fromdocs/CHANGELOG.mdat 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.txtindex. 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. Newdocs/public/yamete-banner.svg.templateis stamped per
release byscripts/stamp-version.mjs(readsMARKETING_VERSIONfrom
project.yml) intoyamete-banner.svgwith the version pill visible;
the release workflow now prepends this banner at the top of every
GitHub release body via araw.githubusercontent.comURL. - 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 whengithub.com/Studnicky/yameteitself is shared on Discord
/ Twitter / Slack / iMessage. GitHub provides no API for this
surface; the asset lives indocs/public/for convenient access. - Docs-build CI gate.
ci.ymlgains adocs-buildjob that runs
npm ci,stamp-version:check,docs:build, and asserts the SEO
outputs (sitemap, feed, robots, manifest, OG image, stamped banner)
exist indocs/.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 indocs/using.mdanddocs/CHANGELOG.md
which Vue's SFC parser read as unclosed HTML start tags during
VitePress'svite-plugin-vuepass. Replaced with<display>
entity escapes so the markdown italicises the placeholder without
triggering the HTML tokeniser. The newdocs-buildCI gate (above)
prevents this class of regression from reaching master again. - VitePress build no longer chokes on local-only design notes. The
gitignoreddocs/art/design-exploration markdown is now declared in
srcExcludeso 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.mjsscript readsMARKETING_VERSIONfrom
project.yml(the canonical macOS bundle version source of truth)
and stamps everydocs/public/*.svg.templateinto a sibling.svg
with__VERSION__replaced byv<version>. Hooked into
predocs:build.--checkmode exits non-zero if any output is
stale so CI flags pre-tag drift.render-og.mjsscript rasterisesog-image.svgand
yamete-banner.svgto PNG viasharp(devDep). Hooked into
predocs:buildafter stamp.release.ymlbanner verification step greps the stamped
yamete-banner.svgfor the version string and fails the workflow
if it doesn't match the tag — surfaces missednpm run stamp-version
invocations before the release notes go out.
SHA256: 5d6c46c290249aff27231a01cff155482376b3626e1a315bd33f0faa27efface
v2.5.0
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 ownLEDOutputConfigflag insideLEDFlash.actionso 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, andHeadphoneMotionAdapter.swiftnow
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. Vendor0x05AC, Product0x8104, UsagePage
0x0020, Usage0x008A, Feature Report 1 polled at 30 Hz,
decoded asUInt16 LEwhole degrees from bytes [1..2]. The previous
SPUusagePage 0xFF00 / usage 8subscription 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 7tousage 5. The M4 Max
ioreg dump and AsahiLinux's reverse-engineering both confirm ALS
rides onusagePage 0xFF00 / usage 5. The7was a guess that
worked only because the SPU broker fans every report regardless of
usage tuple —usage 7does not exist on the SPU bus. - Gyroscope and ALS gain App Store sandbox parity with the
accelerometer.isAvailablenow combines device presence with a
runtime activity probe via_last_event_timestampon the
dispatchGyroanddispatchAlsservices 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-kickstartnow activates gyro and ALS alongside
accel. The helper previously short-circuited the
AppleSPUHIDDriveriterator ondispatchAccel, 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, ordispatchAlsand writes the
activation triplet to each. Existing helper installs need a one-time
sudo ./install.shto pick up the change.AccelHardware.isSensorActivelyReportingis now parameterised
overdispatchKeyso gyro and ALS can mirror the accel probe.
The probe also no longer bails on the first stale sibling — it
scans the entireAppleSPUHIDDriveriterator and returns true if
any matching service is fresh.
Fixed
- Lid sensor no longer fires nonstop
LidSlammedreactions. Field
audit logs from this release showed 3 500+lidSlammedevents 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. keyboardBrightnessEnabledis now wired into pipeline rebuilds.
Previously the impact pipeline'sshouldRunpredicate did not list
keyboardBrightnessEnabled, so flipping it on alone (with every
other output off) would not start sensors. The setting is now
observed byYamete.startSettingsObservationand included in
rebuildSensorPipeline.shouldRun.- 40 lprojs synchronised with the new keyboard/Caps Lock string
keys.setting_led_enabled,setting_keyboard_leds,
help_keyboard_leds, andlegend_ledare 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
Added
- Yamete+ rebrand for the Direct download. The notarised
Developer-ID-signed download is now named Yamete+ instead of
"Yamete Direct". The bundle isYamete+.app, the executable is
yamete-plus, the bundle identifier is
com.studnicky.yamete.plus, the DMG asset isYamete+.dmg. The
App Store variant keeps its existingYamete/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 legacyYamete.Direct.dmg
asset name, which the 2.4.0 release publishes as a transitional
alias alongside the canonicalYamete+.dmg. After the update
installs, a one-shot bundle-relocation step at app launch detects
the legacy/Applications/Yamete Direct.apppath, copies the
bundle to/Applications/Yamete+.app, removes the legacy bundle,
and relaunches from the canonical path. UserDefaults migrate from
com.studnicky.yamete.directtocom.studnicky.yamete.pluson
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.shruns only for pushes to
release/*andhotfix/*branches; it wipes the sandbox-mirror
at~/Library/Containers/com.studnicky.yamete/Data/tmp/yamete-snapshots/HostApp/,
invokesmake test-host-apptwice (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
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
IOHIDCheckAccessat launch and on app foreground. When permission
is missing (most commonly becausemake 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. InputMonitoringAccesshelper (Sources/YameteCore/)
consolidates the three previously-inline TCC checks
(MouseActivitySource,KeyboardActivitySource,
RealLEDBrightnessDriver) onto a singlestatus()/
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:laptopcomputerfor built-in
speakers,tvfor DisplayPort/HDMI,cable.connectorfor USB,
headphones.bluetoothfor wireless,headphonesfor analog jack,
bolt.horizontalfor Thunderbolt,hifispeakerotherwise. 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
AppleSPUDevicelifecycle race fix. The multi-subscriber broker
for the SPU HID device serialised concurrent open/close transitions
through a newtransitionLock. 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_openDeviceCalledExactlyOncepins 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
MainActormentions in commit messages and prose. All future
mentions in markdown / prose contexts useMainActorwithout the
@prefix to avoid GitHub's @-mention parser indexing an unrelated
user. Source code (*.swift) keeps the@MainActorattribute as
required by the Swift compiler.
SHA256: 29c4d0e6a87a8f5ca23fc2a71166de6e7cbf48996a63be55d00f7fddc2a81ee2
v2.2.0
Added
- Thermal sensitivity ratchet. New
thermalRatchettunable 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 indocs/. 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
Fixed
- Release workflow now reads
docs/CHANGELOG.mdinstead of the
pre-reorg rootCHANGELOG.mdpath. 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