Skip to content

feat: auto-updater (Node for Max) — check + notify + one-click update#5

Closed
c0remusic wants to merge 52 commits into
mainfrom
feat/auto-updater
Closed

feat: auto-updater (Node for Max) — check + notify + one-click update#5
c0remusic wants to merge 52 commits into
mainfrom
feat/auto-updater

Conversation

@c0remusic

Copy link
Copy Markdown
Owner

Summary

  • Adds device/tuple_updater.js (Node for Max): checks GitHub Releases API for a newer version, downloads the zip, backs up the current install, extracts and overwrites in-place, restores on failure
  • UI banner in device/ui/tuple_ui.html + site/demo.html: update badge, progress indicator, done/error state
  • Engine (tuple_chord_engine.js): reloadui() re-sends the jweb URL after update so the new UI loads without manual reload
  • 7 unit tests (semver, installDir, unzipCmd, verifyZip) — all green

Test plan

  • Load device in Max → no update banner if already on latest
  • Trigger checkUpdate manually → banner appears if newer version on GitHub Releases
  • Trigger doUpdate → zip downloaded, extracted, device reloads UI
  • Validate on Windows + macOS paths

c0remusic added 15 commits June 18, 2026 15:59
…rve original formatting

- Reverts all compact-JSON .amxd edits (which broke Max)
- Starts from main's 55464-byte formatted .amxd
- Inserts 5 updater boxes (obj-UPD/URT/URM/RMSG/LVER) + 11 patchlines
- Removes old obj-8->obj-CE(order:1) — now routed via obj-URT
- Result: 92 boxes / 116 lines / 58232 bytes — parses clean
…web/send

- Base: main's 55464-byte formatted .amxd (clean)
- 4 new boxes: obj-UPD (node.script), obj-URT (route), obj-URM (route), obj-RMSG (msg)
- 7 new lines: obj-8[1]->URT->CE for jweb passthrough; UPD->URM->RMSG->CE for reload
- NO loadbang->node.script (was causing 'not ready' error; setTimeout in JS handles init)
- NO URM[1] connections (update_available was corrupting jweb URL via s tuple_ui)
- Result: 91 boxes / 112 lines — device unmodified except updater intercept
@c0remusic c0remusic force-pushed the feat/auto-updater branch from 336aac8 to f29c7fc Compare June 18, 2026 14:03
c0remusic and others added 23 commits June 19, 2026 01:24
…ish custom text

- WizardStyle=modern + WizardImageFile=wizard_panel.png (164×314px dark panel,
  gold "Tuple", separator, "Max for Live", version — auto-regenerated each build)
- Custom [Messages]: guided English text on Welcome, SelectDir, and Finish pages
- OutputBaseFilename=Tuple-Installer (no version — device calls it automatically)
- build_installer.py: _regen_panel(ver) regenerates PNG before each ISCC run

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…installer silently

Windows: Install button → sends installupdate to engine → node.script downloads
Tuple-Installer.exe to temp → runs /VERYSILENT (AppId remembers install path)

macOS: Download button → node.script downloads tuple.zip → extracts Tuple/ →
copies to ~/.tuple-install-path (saved by Install Tuple.command on first install)
→ xattr -dr (removes quarantine). Falls back to browser if no saved path.

Changes:
- device/tuple_dl.js: new node.script — download helper with redirect following
- device/tuple.amxd: surgical insert of [node.script tuple_dl.js] (varname=tuple_dl)
- device/tuple_chord_engine.js: installupdate() routes to node.script via getnamed()
- device/ui/tuple_ui.html: OS detection (_isWin), platform-aware asset lookup
  (Tuple-Installer.exe on Win / tuple.zip on Mac), Install vs Download label
- installer/Install Tuple.command: saves $DEST to ~/.tuple-install-path post-install
- installer/tuple.iss + build_zip.py: include tuple_dl.js in both distributions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s AV/SmartScreen flags

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
auto-updater:
- checkForUpdates() now fetches version.json from release assets; reads
  requires_reinstall to choose update path
- requires_reinstall=false: both platforms extract zip in-place (new
  win-inplace route); .amxd EBUSY/EPERM skipped silently on Windows
- requires_reinstall=true: Windows → installer (.exe), Mac → in-place
- tuple_dl.js: dlWinInPlace() + pure Node.js extractZip() (no unzip CLI
  dependency); dlMac() now accepts amxdPath, prefers it over saved file
- tuple_chord_engine.js: installupdate() passes patcher.filepath as 3rd
  arg so tuple_dl can derive installDir on both platforms

UI fixes (tuple_ui.html):
- body width: 100vw → 100% (eliminates right grey band in strip)
- html: added overflow:hidden
- body.strip .vsep { display:none } (bare .vsep line 701 caused 1px left
  line in strip mode)
- logo font-size 10px→12px, margin-bottom -6px→-8px
- CAPTURE button: styled with cell border/bg + hover + .on gold state
- pg-cursor: 3px wide, min-height 22px, border-radius 2px, pulse animation
- drag lastTarget: retains last valid hover so drop outside dock commits
  the last position instead of discarding
- Update UI: Install button always labelled 'Install' (was 'Download' on
  Mac); _updRequiresReinstall controls platform sent to engine
- Tour: added Expression step, added Progression step (now 8 steps)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rebuild of the update UI that the cleanup commit 1c1416b stripped:
- About overlay switches to a dedicated Download view when an update is
  available (logo badge → click → Download view, not the About card)
- Release notes rendered from the GitHub release body (minimal md → html)
- "Ignore this version" persists the skipped tag in localStorage
- Install / Close, current-vs-new version line, status line
- Feedback bindInlets restored (updprogress / updone / upderr), ready for
  the tuple_dl → jweb wiring (the one piece still missing)
- About scroll fix in strip (hide Creator row)
- Debug: ?upd=preview opens the Download view for run-tuple screenshots

WIP checkpoint — LOCAL_VERSION forced to 1.2.1 for live testing (restore
to 1.2.2 before release). Committed now so the work can't be lost again.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The cleanup commit 1c1416b removed the relay that fed install progress
back to the UI, leaving tuple_dl's outlet dangling. Restore the feedback
on the clean (tuple_dl, no relay) architecture:

- tuple.amxd: 2 new patchlines — tuple_dl (obj-UPD-dl) outlet → strip jweb
  (obj-6) + s tuple_ui (obj-SUI), mirroring engine outlet 7's path to the
  jweb. Edited via Node + UInt32LE size @28 rewrite; JSON re-verified.
- tuple_dl.js: emit single-token jweb selectors (no spaces — multi-word
  breaks the Max atom, cf. bf686b1): updprogress <downloading|extracting|
  installer>, updone, upderr <download|extract|nopath>.
- tuple_ui.html: map those tokens to friendly status text in the Download
  view; Done ✓ disables the button, error → Retry.

Now: Install → Downloading… → Installing… → Done ✓ (reload), end to end.
Backup at device/_backups/tuple.amxd.pre-updwire.bak.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The colour-logic legend ("pastilles") had disappeared from the device —
only the demo (site/demo.html) still had it. Device/demo had diverged.
Restore it in device/ui/tuple_ui.html, mirroring the demo:
- .smart-legend always visible (gridwrap hides it in strip) + i.bar style
- _lgDot/_lgBar/_lgChip/buildLayoutLegend/buildLegend (per colour scheme:
  Spectrum bar, Function dots, Tension/Fifths bars, Quality dots + Borrowed)
- buildLegend() wired into applyScheme, smart toggle, griddone, loadDemo

Verified: legend renders "Scale degree (I → VII) · Borrowed" at grid bottom.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…istory

Reconstructed from the session transcript everything that worked before the
"tu as cassé le device" break. The thing that made the install actually work:
the node.script boots lazily ("Node script not ready"), so the engine must
KICK it and RETRY the dl message until Node answers.

engine (tuple_chord_engine.js):
- installupdate() kicks tuple_dl (message 'script','start') + schedules _doDl
- _doDl() retries 'dl' up to 6× (1.5s apart); handleprogress('done') clears the
  pending state which stops the loop
- progress()/handleprogress(): tuple_dl outlet → obj-CE → outlet(7,'updatedone')
- auto-sync: setautosync + _initAutoSync LiveAPI observers (root_note/scale_name),
  init on onloadend + global task; synclive now outlet(7,'sync',1) to flash UI
- LIST_DISPATCH += autosync, progress

tuple_dl.js:
- _busy lock so the engine's retried 'dl' triggers a single download
- report via outlet 'progress done' / 'progress error' (→ obj-CE → handleprogress)

tuple.amxd:
- rewired obj-UPD-dl (tuple_dl) outlet → obj-CE (engine), replacing the earlier
  obj-6/obj-SUI direct-to-jweb wiring (Node edit + size@28; backups kept)

ui/tuple_ui.html — validated Download window + Tupline-style sync:
- About overlay → Download view (ib-about.hidden / ib-update-view.active)
- logo text badge "↑ vX.Y.Z" (more visible than the dot), cur→new version line,
  changelog from the release body, Install/Ignore vX/Later, reload note,
  "Show changelog →" (strip), bind 'updatedone'
- SYNC = auto-follow toggle: .sync.auto, locks KEY/SCALE (.csel.locked),
  send autosync 1/0, bind autosync

Verified: engine/tuple_dl node --check, amxd JSON parses, download window
renders with live v1.2.2 release notes (driver screenshot). NOT yet tested
live in Max. LOCAL_VERSION still forced to 1.2.1 for testing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…zip-patchable) after live test

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… path

Low-risk hardening found during the pre-release review of the restored
updater/sync code:
- engine pushUIState() echoes 'autosync' → SYNC button + KEY/SCALE lock stay
  in sync with the engine after a jweb reload (was hidden-state desync)
- tuple_dl: dispatch wrapped in try/catch → a synchronous error can't leave
  _busy stuck (which would block all future updates until reload)
- tuple_dl dlWin: installer path no longer emits 'done' — the external
  installer + Ableton restart complete it, so the jweb keeps "Installer
  launched — restart Ableton" instead of flipping to "reopen the device";
  _busy stays set so retried 'dl' can't relaunch the installer
- removed duplicate download/extract log posts

No behaviour change to the working in-place flow. node --check OK both files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- VERSION → 1.3.0 (synced to TUPLE_VERSION, LOCAL_VERSION, About panel, .iss)
- build_zip.py: regenerate version.json (site/ + in-zip), requires_reinstall via
  TUPLE_REQUIRES_REINSTALL env (True for this release — .amxd changed)
- fix: LOCAL_VERSION sync was patching the decoy comment, not the code line —
  reworded the comment so the regex hits the real var (1.3.0 now applied)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Strip controls 20px → 18px; space-evenly layout; no vertical padding
  → 6.1px uniform gaps (was 4.8px non-uniform). Sep hidden unconditionally.
- amxd: openrect width pinned to 404px (= jweb width) — eliminates right
  grey band that was caused by Ableton auto-sizing past the jweb boundary.
  bgcolor set to #232323 for the pre-load frame.
- Fix ♭/# toggle: useflats was missing from LIST_DISPATCH in engine;
  send('useflats', 1) arrived as a 2-arg list and was silently dropped.
- build_zip.py: makedirs(site/) before writing version.json — fixes CI
  FileNotFoundError (site/ is local-only, absent on device-repo checkout).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…alStorage

- Strip brand: 12px -> 14px. Small superscript "?" in strip mode signals
  the logo is clickable (About panel). Hidden in full mode (already clear).
- Grid: .col.bor gets border-left + margin-left for a subtle separator
  between the I-VII degree columns and the Borrowed column (mirrors demo).
- Changelog from strip: clicking "Show changelog ->" stores the pending
  update data (tag, body, url, reinstall) in localStorage so the full
  window can consume it immediately on boot (150ms) instead of waiting
  2s for checkForUpdates to re-fire. Both jwebs share the same file://
  origin -> same localStorage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- brand-hint '?' now visible in body.full (9px superscript, gold .38 alpha).
  Was only shown in strip mode — base rule is display:none, full override missing.
- .col.bor separator: replace border-left (invisible against grid bg) with
  ::before gradient line matching the Progression separator style (vertical
  gradient, transparent fadeout at top/bottom, rgba .08).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@c0remusic

Copy link
Copy Markdown
Owner Author

Fermée : approche obsolète. L'auto-updater a été refait autrement (device/tuple_dl.js, updater in-place) et est en prod depuis v1.3.0. Le device/tuple_updater.js proposé ici n'existe plus sur main, et cette branche est ~103 commits derrière. Décision : tuple_dl + UI/feedback reconstruits (pas le relais Node.script).

@c0remusic c0remusic closed this Jun 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant