Skip to content

Latest commit

 

History

History
78 lines (61 loc) · 4.87 KB

File metadata and controls

78 lines (61 loc) · 4.87 KB

Live Console — Development Guide

Prerequisites

  • Node.js ≥ 24.14.1
  • Ableton Live 12 beta with the Extension Host (…/Contents/Helpers/ExtensionHost/ExtensionHostNodeModule.node on macOS)
  • The SDK/CLI tarballs are vendored in vendor/npm install needs no registry access for them.

Scripts

Script What it does
npm start typecheck + dev build, then extensions-cli run (loads into Live; needs .env + Developer Mode in Live's Extensions preferences)
npm test engine tests via Node's built-in runner (node --import tsx --test) — no Live required
npm run typecheck tsc --noEmit over src, tests, scripts
npm run build production bundles (minified, no sourcemaps)
npm run package production build + extensions-cli packageLive-Console-<version>.ablx
npx tsx scripts/dev-preview.ts render the console dialog with demo state to dist/preview/index.html for browser-only UI work

Extension logs (console.log/... from the extension process) land in Live's ExtensionHost.txt (macOS: ~/Library/Preferences/Ableton/Live x.x.x/). extensions-cli run --inspect attaches a debugger.

Architecture

src/
  extension.ts        entrypoint: activate(), context-menu registration, console session loop
  core/               PURE TypeScript — no SDK imports; runs in Node AND the dialog browser
    types.ts          snapshot, ParsedCommand, PlannedOp, Resolution, dialog protocol
    tokenizer.ts      quoted-string tokenizer
    parser.ts         deterministic grammar v0 → ParsedCommand | ParseError
    resolve.ts        ParsedCommand + SetSnapshot → concrete plan (ops, preview, warnings)
    grammar.ts        help entries (single source for help panel + docs)
    capabilities.ts   static capability facts for SDK 1.0.0 (audited, not runtime-detected)
    colors.ts         color names → 0xRRGGBB
    audit.ts          audit set report
    cleanNames.ts     clean-name suggestions (preview-only)
  live/               the ONLY layer that touches @ableton-extensions/sdk
    adapter.ts        LiveAdapter interface + SdkLiveAdapter (snapshot building, guarded mutations)
    selection.ts      context-menu invocation → selection (tracks/clips/scene)
    executor.ts       plan → adapter calls inside withinTransaction, per-target outcomes
  ui/
    console.html      dialog template (dark Live-style CSS, STATE/SCRIPT placeholders)
    main.ts           dialog app: live preview, confirm-gate, history ↑/↓, help panel
  state/history.ts    persisted command history (storageDirectory, best-effort JSON)
  util/inlineHtml.ts  safe inlining of state JSON + UI bundle into the dialog HTML
tests/                node:test suites for everything in core/, util/, and the executor

Core rule: the parser, resolver, and executor logic are testable without Ableton Live. tests/executor.test.ts runs the full execution path against an in-memory LiveAdapter.

Why the console reopens after each command

SDK 1.0.0-beta.0 offers exactly one extension UI surface: ui.showModalDialog(url, w, h) which resolves once with a single string posted by the dialog (close_and_send). There are no persistent panels and no host→dialog messaging. So the session loop in extension.ts is:

build snapshot → open dialog (state embedded in the data: URL)
  → user types; dialog previews locally against the snapshot (zero round-trips)
  → Execute posts {action:"execute", raw} and the dialog closes
→ extension re-parses + re-resolves raw against the same snapshot, applies in one
  undo step, then reopens the dialog with results + refreshed snapshot

Read-only commands (audit, clean names, help) execute inside the dialog with no round-trip; their raw text is persisted to history on the next close/execute via historyAppend.

Safety model

  • Preview-first: nothing mutates until Execute; >5 targets require a confirm click.
  • Every op carries expectName; the adapter re-reads the object's name and skips (with a per-target message) if it changed since the preview.
  • All SDK property writes are individually try/caught — one bad target never aborts the rest; failures are reported per target.
  • Mutations run inside withinTransaction → one Live undo step per command.
  • Set names are rendered with textContent only, and state JSON / UI JS are escaped against </script> breakout (src/util/inlineHtml.ts, tested).

Two-pass build (build.ts)

  1. src/ui/main.ts → IIFE browser bundle → src/generated/ui.bundle.js.txt (gitignored)
  2. src/extension.ts → CJS Node bundle → dist/extension.js (the manifest entry), inlining console.html and the UI bundle as text

The repo intentionally diverges from the SDK template in two places: moduleResolution: "bundler" (extensionless imports; esbuild and tsx both resolve them) and tests via Node's built-in runner (zero extra dependencies).