# Node <-> Native

Because the native window renderer and Node.js run in completely separate processes, they cannot talk to each other directly. Positron solves this by establishing a localized WebSocket server that handles two distinct types of communication: **Outbound Commands** and **Inbound Events**.

### Communication Flow

```mermaid
graph TD
    App["<b>JavaScript App / Node.js</b>"]
    
    WS["<b>WebSocket</b>"]
    
    Runtime["<b>Native Runtime:</b><br>Swift (Mac) or C# (Windows)"]
    
    Viewport["<b>Native Window Viewport</b>"]

    %% Flow / Edge Labels
    App --> |"(1) .sendCommand() Encodes JSON Frame"| WS
    WS --> |"(2) Dispatches over TCP"| Runtime
    Runtime --> |"(3) Parses Command -> Executes Native OS Call"| Viewport

    %% Styling
    classDef default fill:#333,stroke:#333,stroke-width:1px,text-align:center;
    class App,WS,Runtime,Viewport default;
```

### Outbound Commands (Node.js ➔ Native Runtime)

When you call a method on a `Window` instance in Node.js (like `setTitle` or `loadURL`), the framework converts that action into a standardized JSON string called a **Command Frame**.

#### The Lifecycle of a Command:

1. **Invocation:** You call `mainWindow.setTitle('My App')`.
2. **Normalization:** The `Window` class captures this, forces all arguments into string format, and attaches the target `windowId`.
3. **Queueing (If needed):** If the native window hasn't fully launched or connected to the WebSocket yet, the command is pushed into a `commandQueue` so nothing gets lost.
4. **Transmission:** Once connected, the frame is sent over the WebSocket socket.

#### The Command Frame Format:

```json
{ 
  "windowId": 1, 
  "command": "setTitle", 
  "args": ["My App"] 
}
```

### Inbound Events & IPC (Native Runtime ➔ Node.js)

When things happen inside the native window (like the user clicking a button in the web frontend or closing the window), the Native Runtime sends an **Event Frame** back to the Node.js process.

This is managed by the `IpcRouter` (`ipc.js`).

#### The Event Frame Format:

```json
{
  "event": "ipcMessage",
  "windowId": 1,
  "data": {
    "channel": "submit-form",
    "payload": "{\"username\":\"alice\"}"
  }
}
```

#### How Node.js Processes It:

1. The WebSocket server receives the raw message and checks the `msg.event` type.
2. If it is an `"ipcMessage"`, it hands it over to `ipc.dispatch()`.
3. The `IpcRouter` looks up the channel name (e.g., `"submit-form"`) in its internal registration map.
4. It executes your JavaScript callback function, providing utility helpers like `reply()` and `emit()` to easily send data right back to the frontend.

### Extensible Commands

Positron's command system is uniquely extensible. Developers can map their own custom commands to native desktop code (Swift or C#) written outside the core framework. Learn more here: [EXTENSIONS](/v1/extensions/setup.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://positronjs.gitbook.io/v1/ipc/native.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
