You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Multiple sessions. A tab strip above the main pane holds one session per tab, each with its own connection, terminal settings, macros, filters, rules, logging and graphing — RTT from one board in one tab and serial from another in the next, in a single window. The "+" button opens a session with default settings; drag a tab to reorder, right-click it to rename, duplicate or close; Ctrl+Tab / Ctrl+Shift+Tab switch. Sessions persist as appData.sessions (app data v25 wraps the old single config as "Session 1") and the settings pane names the session it is editing.
MCP tools are session-aware.list_sessions is new; get_terminal_output, send_data and get_connection_status take an optional session (id or name) and default to the active one; each session has an RX stream at ninjaterm://sessions/{id}/rxstream, with ninjaterm://terminal/rxstream still following the active session. send_data now goes through the session, so it works for sockets, RTT and Bluetooth and is echoed and logged like typed data.
Find supports regular expressions. A .* toggle in the Find bar switches the query to a regex (the case toggle still applies); an invalid pattern shows Invalid, with the compile error on hover. Filters and Highlight Rules already took regex.
Fixed
The MCP rxstream buffer is capped. A session that subscribed but never read the resource held the entire serial stream in main-process memory for the life of the app; RxStreamBuffer keeps the last 1,000,000 characters, drops the oldest, and prefixes the next read with a one-line notice of how much was dropped.
Closing the window now stops the CPU-usage monitor and the flow-control poll.stopCpuMonitoring was an empty method and ConnController.cleanup never cleared the 1s getFlowControlSignals interval; both are now cancelled, and the poll logs rather than throws on a bad IPC response.
Toggling ANSI parsing from a fake port now sticks.FakePortsController wrote rxSettings.ansiEscapeCodeParsingEnabled directly, which only ever reached the runtime copy of the setting; the persisted value kept the old state and a preset or restart silently reverted it. Seven such writes now go through the setter — and the settings classes no longer have a runtime copy to bypass.
Applying a preset that names a serial port now connects to that port.setSelectedPort wrote a serialPortInfo field nothing read, so the open used whichever port was selected in the UI — or failed outright — while the snackbar reported the preset's port. The serial reconnection poller went through the same call and was equally ineffective.
Packaged builds no longer download the React DevTools extension on every launch.installExtension ran unconditionally, so a shipped build fetched an extension from Google's CDN at startup and installed it with file access; now guarded by app.isPackaged.
NinjaTerm no longer contacts Google Fonts or Ko-fi's CDN at runtime. The kofi-button package injected a remote stylesheet and two CDN images every time the terminal pane mounted; replaced by a local KofiDonateButton drawn with an MUI icon, which opens the Ko-fi page in your browser via shell.openExternal.
External links open in your browser instead of inside NinjaTerm. The two target="_blank" links (Moment.js docs, Ko-fi button) opened an Electron window that inherits the preload bridge; setWindowOpenHandler now denies in-app windows and hands http(s) URLs to the OS.
Log files no longer lose data, or duplicate it. Anything that arrived while a write was in flight was discarded by the buffer clear that ran after the write (and counted as written anyway), and two overlapping writes each wrote the same bytes; Logging now takes the buffer before awaiting and serialises writes.
Logging no longer crashes on a large chunk.bufferedData.push(...rxData) spread the chunk into function arguments, throwing RangeError past ~65k bytes — reachable in one RTT or socket read. The buffer holds Uint8Array chunks instead, which also drops a MobX notification per received byte.
Changed
App is split into App and Session. Everything about one connection moved to Session; every session-owned class takes its session rather than the app, and App exposes the active session's members through delegating getters so the views are unchanged. Config-reload notifications moved from AppDataManager onto the session.
Each setting now has exactly one copy. The five settings classes plus Graphing, Logging, RightDrawer and Terminals kept a runtime field per setting and synced it with the persisted *Data object by hand (_loadConfig/_saveConfig/_isLoading); they are now thin typed façades over the persisted object via SettingsBranch, so there is nothing to load, save back, or guard. Root cause was the config tree being observable only on the second launch, not the first — fixed in AppDataManager. The baud-rate, socket-timeout, RTT-speed and RTT-channel inputs now validate as you type like every other field.
Graphing's numeric settings are stored as numbers. Buffer size, data-point limit and the four axis bounds were the only settings persisted as strings; app data v25 converts them, and drops the right drawer's dead per-branch version field.
Log settings follow an applied preset even while logging is running. They used to be ignored until logging stopped; the directory, file name and existing-file behaviour are only read when logging starts, and the raw TX/RX toggles now take effect immediately.
Connection handling is now one state machine over a Transport interface, not five copies of one.ConnController.openConnection was a ~370-line if/else per connection type, with close, unexpected-close and reconnection each repeating the same fan-out; serial, socket, RTT, Bluetooth and fake ports now implement a common interface and the controller is 1082 lines down to 653.
Recording TX/RX throughput samples is no longer O(n) per chunk. The rate-tracking arrays trimmed a single entry off the front on every push once at their cap, and were MobX-observable with no reactive observer; they now trim in batches and are plain arrays.
Transmitted data no longer crosses the process boundary as a boxed value per byte. The serial, socket and rtt write channels take a Uint8Array instead of a number[], so ConnController.writeData hands its bytes straight to the transport; Bluetooth already did this.
The terminal handles incoming data ~1.5x faster and repaints 2-12x faster.TerminalRow stores a flat chars array plus run-length styleRuns instead of an object per column, row contents moved off MobX onto a per-chunk SingleTerminal.renderVersion signal, and the SingleTerminalView row renderer was hoisted to module scope. Numbers and method in performance-profiles/THROUGHPUT_BASELINES.md.