Skip to content

jsaddle-terminal: build the runner on Windows; drop the jsaddle-warp fallback - #168

Merged
hamishmack merged 3 commits into
masterfrom
hkm/windows-frontend
Aug 2, 2026
Merged

jsaddle-terminal: build the runner on Windows; drop the jsaddle-warp fallback#168
hamishmack merged 3 commits into
masterfrom
hkm/windows-frontend

Conversation

@hamishmack

Copy link
Copy Markdown
Member

Follow-up to #167. Makes the jsaddle-terminal runner (Language.Javascript.JSaddle.Terminal) build on every platform, so an IDE like leksah can host jsaddle apps in a terminal window on Windows too — not just link the pure Protocol/Bootstrap modules there.

The runner was POSIX-only for two reasons; both are addressed:

Raw terminal mode → cross-platform

The runner puts its own controlling terminal into raw mode to read keystrokes byte-by-byte (no echo/line-buffering, ISIG off so Ctrl-C arrives as raw 0x03). That was termios (unix). It's now behind withRawMode, with a Windows implementation using the console API — SetConsoleMode clearing ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT and setting ENABLE_VIRTUAL_TERMINAL_INPUT (keys arrive as VT escape sequences, matching the existing reader), plus ENABLE_VIRTUAL_TERMINAL_PROCESSING on output. Both modes are saved and restored. getRunPid likewise wraps getProcessID / getCurrentProcessId. All from the Win32 package — no C shim.

Drop the jsaddle-warp fallback

When no IDE answered the handshake the runner used to serve the app over jsaddle-warp on a free port. That was the only user of jsaddle-warp / warp / websockets, and it forced aeson < 2.3. It's removed: without an IDE there's now nothing to tunnel to, so the runner reports that and stops. Dropping it frees the aeson bound and is what lets the runner build on Windows without dragging that dependency tree onto the mingw cross.

cabal

The runner module and aeson / stm move to the unconditional set; the platform split is just unix (POSIX) vs Win32 >=2.13 (Windows). Demo/test stanzas are unchanged (still off by default / non-Windows).

Testing

  • Builds unchanged on POSIX (macOS, GHC 9.14).
  • Cross-compiles for x86_64-w64-mingw32 via haskell.nix — jsaddle-terminal library (runner included) and, as a consumer, leksah's leksah-webview2.exe link cleanly.
  • Runtime testing on real Windows still pending (the one unknown is GHC reading binary stdin from the console in VT-input mode).

The runner was POSIX-only for two reasons: it puts its own terminal into raw
mode (termios), and when no IDE answered the handshake it fell back to serving
the app over jsaddle-warp.  Both are now handled:

- Raw mode is abstracted behind withRawMode, with a Windows implementation
  using the console API (SetConsoleMode: drop line/echo/processed-input, add
  ENABLE_VIRTUAL_TERMINAL_INPUT; enable ENABLE_VIRTUAL_TERMINAL_PROCESSING on
  output) — the direct analog of the termios bracket.  getRunPid likewise wraps
  getProcessID / getCurrentProcessId.
- The jsaddle-warp fallback is removed.  It was the only user of jsaddle-warp /
  warp / websockets (and forced aeson<2.3); without an IDE there is now nothing
  to tunnel to, so the runner just reports that and stops.

So the whole library (runner included) builds on every platform, letting an
IDE like leksah host jsaddle apps in a terminal window on Windows too.  Deps:
aeson/stm move to the unconditional set; unix on POSIX, Win32 on Windows.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes the jsaddle-terminal runner (Language.Javascript.JSaddle.Terminal) build cross-platform by introducing a Windows console raw-mode implementation (via Win32) alongside the existing POSIX termios path, and removes the previous jsaddle-warp fallback behavior/dependency chain.

Changes:

  • Add CPP-gated raw terminal mode handling: POSIX (unix/termios) vs Windows (Win32 console mode + VT input/output).
  • Remove the “serve via jsaddle-warp on a free port” fallback and instead report “no IDE answered” and exit.
  • Update jsaddle-terminal.cabal to expose the runner unconditionally and adjust dependencies (aeson/stm unconditional; unix vs Win32 platform split).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
jsaddle-terminal/src/Language/Javascript/JSaddle/Terminal.hs Adds cross-platform raw-mode support and removes the jsaddle-warp fallback path.
jsaddle-terminal/jsaddle-terminal.cabal Exposes the runner module on all platforms and updates dependency conditionals (unix vs Win32).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +129 to +133
rawOut = outSaved .|. eNABLE_VIRTUAL_TERMINAL_PROCESSING
bracket_
(setConsoleMode hIn rawIn >> setConsoleMode hOut rawOut)
(setConsoleMode hIn inSaved >> setConsoleMode hOut outSaved)
act

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 71c7dc2 — one bracket_ per handle, nested, so the input mode is restored even if setting the output mode throws.

(atomically, newTChanIO, readTChan, writeTChan, TChan)
import Control.Exception (bracket_, catch, SomeException, try)
import Control.Monad (forever, void, when)
import Control.Exception (bracket_, catch, SomeException)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed: the nested-bracket_ fix in 71c7dc2 gets the same guarantee without onException, so the import list is unchanged.

Comment on lines +42 to +50
-- The runner puts its own terminal into raw mode. On POSIX that is termios
-- (unix); on Windows it is the console API (SetConsoleMode with VT flags),
-- from Win32. The IDE-tunnel transport itself opens no sockets, so the
-- runner builds on every platform now (it used to fall back to jsaddle-warp
-- when no IDE answered — dropped, so the aeson<2.3 cap it carried is gone).
if os(windows)
build-depends: Win32 >=2.13.0.0 && <2.15
else
build-depends: unix

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — the description: text now says the runner reports on stderr and stops, and notes the jsaddle-warp fallback was removed (71c7dc2).

…ription

Two review points from the PR:

* The Windows raw-mode bracket set both console modes in one acquire action.
  If the second call (output mode) threw, bracket_ would not run the release
  action at all, leaving the user's console in raw input mode.  Use one
  bracket per handle, nested, so the input mode is always restored.

* The package description still advertised the jsaddle-warp fallback this
  branch removes.
@hamishmack

Copy link
Copy Markdown
Member Author

Addressed the review in 71c7dc2:

  • Windows raw mode: one bracket_ per handle, nested, so a failure setting the output mode can no longer skip the release action and leave the console in raw input mode. (No onException needed, so the import list is unchanged.)
  • description: no longer advertises the jsaddle-warp fallback this PR removes.

Verification: jsaddle-terminal builds clean on macOS/GHC 9.14, and the mingw32_HOST_OS branch type-checks with -Wall under -optP-Dmingw32_HOST_OS=1 against a stub Win32 carrying the real signatures (Haskell-CI is Linux-only, so it does not compile that branch).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (1)

jsaddle-terminal/src/Language/Javascript/JSaddle/Terminal.hs:172

  • This comment still refers specifically to a "raw-termios bracket", but the raw-mode wrapper is now cross-platform (termios on POSIX, console mode on Windows). Updating this wording avoids misleading readers about what tunnel assumes.
-- | The tunnel session, inside the raw-termios bracket.

@hamishmack

Copy link
Copy Markdown
Member Author

Also took the suppressed nit from the second pass: tunnel's haddock said "inside the raw-termios bracket", which stopped being accurate once withRawMode grew a Windows implementation (9eca56c).

@hamishmack
hamishmack merged commit 5c88692 into master Aug 2, 2026
11 checks passed
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.

2 participants