Per Pete: staff Macs restart Stream Deck/the plugin process often enough
that the one-shot 10s-post-launch check is sufficient; the periodic
recheck was unwanted complexity.
The auto-updater used a one-shot setTimeout, so a version pushed after the
initial 10s post-launch check would sit unapplied until Stream Deck restarted
or someone hit the manual Check for Updates button.
Spawns the Nextcloud.app binary directly (detached, no Terminal window)
since it doesn't register properly with launch services on some setups.
New action requires a one-time plugin reinstall (manifest changes don't
ship through the OTA updater, which only touches plugin.js + assets).
v1.0.49's panel-polling approach and v1.0.50's diagnostic logging did
not produce a working auto-refresh in testing -- the panel still hung
after the restarting message until a different button was clicked.
Rather than keep guessing at Stream Deck's PI-reconnect lifecycle,
removed the polling/debug scaffolding entirely and changed the final
status message to explicitly instruct the user: 'Updated to vX. Click
any other button above to confirm version update has been successful.'
No functional change. Adds streamDeck.logger.info() calls at
onPropertyInspectorDidAppear, sendProjectsToPI(), and onSendToPlugin
entry, plus a small on-panel debug trail (property-inspector.html)
that timestamps $PI.onConnected firings, button clicks, poll sends,
and received payloads. v1.0.49's panel-polling fix didn't visibly
work in testing and produced no status text at all on the second
attempt -- need to see the actual event sequence rather than guess
again. Remove once the refresh flow is confirmed working.
The v1.0.47/48 attempt pushed sendProjectsToPI() from the plugin right
after streamDeck.connect() on startup, assuming that would reach an
already-open panel post-update-restart. It doesn't: per the SDK,
sendToPropertyInspector only delivers 'when the property inspector is
visible for an action provided by this plugin', and that visibility
state is tracked per-process via live didAppear/onSendToPlugin events
-- a freshly restarted process has never received one, so the push was
silently dropped. Confirmed in testing: the panel stayed frozen on
'Updating to vX...' indefinitely.
Removed the ineffective push. Instead, the property inspector's
'Check for Updates' handler now schedules a few refreshProjects
requests (1.5s/3.5s/6s/9s after the click) -- the same proven
PI-initiated round trip already used when entering an API token, which
reliably re-populates the panel including the version text once the
new process comes back up.
Also hardened checkForUpdates(): the final 'Updated to vX --
restarting...' status is now awaited with a short delay before
process.exit(0), best-effort only, since the panel no longer depends
on it arriving.
The version number shown in the PI only updates via the 'projects' push,
which only fires from onPropertyInspectorDidAppear (i.e. when a
different button is clicked, forcing the PI to re-appear). After
'Check for Updates' completes and the plugin process restarts, an
already-open PI kept showing the pre-update version indefinitely.
Two changes: send a final 'Updated to vX — restarting…' status right
before process.exit(0) so the update message doesn't hang on
'Updating…', and push sendProjectsToPI() once on every plugin startup
(after streamDeck.connect()) so a PI that survived the restart gets
the new version immediately instead of waiting for an unrelated button
click.
The onKeyDown stop/switch paths only cleared memRunningActionId/EntryId
after a successful stopTimer call. If the tracked Notion entry was
already gone (archived/deleted/edited outside the plugin), stopTimer
threw and the stale ID was never cleared -- every subsequent keypress
retried the same doomed stop call, showed a warning triangle, and the
deck got permanently stuck (unable to unselect the stuck button or
start any new timer). checkElapsed already handled this correctly by
clearing state unconditionally; onKeyDown now does the same: stopTimer
failures are logged but non-fatal, and local state clears regardless.
setSettings always resets the button visual state — no workaround
exists. Removed activeEntryId from per-button settings entirely.
Running state now tracked via runningActionId in global settings
(alongside runningEntryId). onWillAppear restores state from
memRunningActionId. onKeyDown only calls setState — clean, instant.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously setSettings was awaited first (causing blue flash), then
setState was called. Now both are sent in the same Promise.all so
Stream Deck processes setState in the same batch, overriding the
visual reset from setSettings with no visible blue transition.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
setState was being called after setRunningEntry, which makes two
async global settings calls and takes ~1 second. During that time
the button sat in the blue reset state from setSettings. Now
setState is called immediately after setSettings, before
setRunningEntry, so the blue flash is imperceptibly brief.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
setSettings() resets the visual state in both directions. The start
path already re-asserted setState(1) after setSettings. The stop path
was missing the equivalent setState(0), leaving the button green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When pressing B while A was running, B went green immediately but A
stayed green until getGlobal()+getRunningEntryId() resolved. Now uses
settingsCache to turn off all running buttons in the same synchronous
pass as turning B on — no async gap where both appear active.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Visual state change now happens before any async work (getGlobal,
getRunningEntryId, API calls). Previously the button waited for
getGlobal() to resolve before going green, causing a 1-2s delay.
Also reverts optimistic state on API error.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
setSettings() causes Stream Deck to reset the button visual back to
its default state (state 0). The optimistic setState(1) was correct
but got overridden. Now setState(1) is called again after setSettings
completes to ensure the button stays green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Selecting a name called saveCredentials, which sent refreshProjects,
which re-fetched users and called populateUsers(users, globalUserId)
with globalUserId still empty — immediately clearing the selection.
Fix: saveCredentials now updates globalUserId immediately, and
refreshProjects is only triggered by token changes (scheduleCredSave),
not by name changes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previous versions of checkForUpdates only wrote plugin.js, leaving
property-inspector.html stale on all machines that used Check for
Updates. PI fixes (name dropdown, refreshProjects) never reached them.
Now downloads and writes all UI and image assets before restarting.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
onSendToPropertyInspector (users list) arrives before
onDidReceiveGlobalSettings (saved userId) in most cases, leaving
globalUserId empty when populateUsers runs. Now cachedUsers stores
the list, and onDidReceiveGlobalSettings re-populates if users
already arrived — handles both orderings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
onDidReceiveGlobalSettings fired before the users list was populated,
so the saved userId couldn't be selected. Now stored in globalUserId
variable and applied when populateUsers runs, regardless of order.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously staff had to close and reopen the property inspector
after entering their token to trigger the fetch. Now saving the
token immediately sends a refreshProjects event to the plugin,
which fetches and returns the project list and name dropdown.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GITEA_BASE was pointing at master (v1.0.22), so Check for Updates
always said "already up to date". Now points at stable-rebuild so
updates are served from the correct branch. Also bumps version.json
to 1.0.26.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both buttons now flip to their final visual state immediately on key
press, before any API calls. Previously the old button stayed green
throughout the stopTimer network round-trip (~0.5–2s), causing a
window where both buttons appeared green simultaneously.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Stream Deck plugin settings panel was showing "version 1" because
manifest.json had a hardcoded "Version": "1.0.0".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Known-good state: blue icons, cross-folder timer fix, name dropdown.
Versioned above master (1.0.22) so auto-updater does not overwrite.
Install script points at stable-rebuild branch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Stop previously running timer globally using persisted runningEntryId,
works across nested folders and plugin restarts
- In-memory cache prevents onWillAppear flash of stale running state
- Replace SVG button icons with Aurora timer PNG icons (on/off)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove onWillDisappear cache pruning so buttons in other folders remain
in settingsCache — previously navigating away deleted their entries,
causing the stop-other-timers loop to miss them on next key press.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes chicken-and-egg setup flow where new users couldn't populate
the name dropdown because isConfigured required userId to already exist.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>