# Windows

zero-native supports multiple windows. The main window is created automatically; secondary windows can be created from Zig or JavaScript.

## Creating windows from Zig

```zig
const info = try runtime.createWindow(.{
    .label = "tools",
    .title = "Tools",
    .default_frame = zero_native.geometry.RectF.init(80, 80, 420, 320),
});
try runtime.focusWindow(info.id);
```

## Creating windows from JavaScript

`js_window_api` exposes the `window.zero.windows.*` helper in JavaScript. Window commands still require an allowed origin and the `window` permission when runtime permissions are configured:

```zig
const app_permissions = [_][]const u8{zero_native.security.permission_window};

.security = .{
    .permissions = &app_permissions,
    .navigation = .{ .allowed_origins = &.{ "zero://app" } },
},
.js_window_api = true,
```

Then JavaScript can call the helper from an allowed origin:

```javascript
const win = await window.zero.windows.create({
  label: "tools",
  title: "Tools",
  width: 420,
  height: 320,
});

const all = await window.zero.windows.list();
await window.zero.windows.focus(win.id);
await window.zero.windows.close(win.id);
```

## Window types

<table>
  <thead>
    <tr>
      <th>Type</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code>WindowId</code></td>
      <td>Opaque identifier (<code>u64</code>)</td>
    </tr>
    <tr>
      <td><code>WindowCreateOptions</code></td>
      <td>Options for <code>runtime.createWindow()</code>: label, title, frame</td>
    </tr>
    <tr>
      <td><code>WindowInfo</code></td>
      <td>Returned after creation: id, label, title, frame</td>
    </tr>
    <tr>
      <td><code>WindowState</code></td>
      <td>Persisted state: id, label, title, frame, open, focused, maximized, fullscreen, scale</td>
    </tr>
    <tr>
      <td><code>WindowRestorePolicy</code></td>
      <td>How restored frames are placed, such as clamping to the visible screen or centering on the primary display</td>
    </tr>
  </tbody>
</table>

## Platform limits

<table>
  <thead>
    <tr>
      <th>Constant</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code>max_windows</code></td>
      <td>16</td>
    </tr>
    <tr>
      <td><code>max_window_label_bytes</code></td>
      <td>64</td>
    </tr>
    <tr>
      <td><code>max_window_title_bytes</code></td>
      <td>128</td>
    </tr>
  </tbody>
</table>

## Window state persistence

The `window_state.Store` persists geometry to `windows.zon` in the app's state directory. Startup restore uses the window `label` from the manifest and applies the saved frame; titles continue to come from `app.zon` or runtime window creation options.

<table>
  <thead>
    <tr>
      <th>Field</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code>id</code></td>
      <td>Window ID</td>
    </tr>
    <tr>
      <td><code>label</code></td>
      <td>Window label (used for merge matching)</td>
    </tr>
    <tr>
      <td><code>frame</code></td>
      <td>Position and size (x, y, width, height)</td>
    </tr>
    <tr>
      <td><code>maximized</code></td>
      <td>Whether the window was maximized</td>
    </tr>
    <tr>
      <td><code>fullscreen</code></td>
      <td>Whether the window was fullscreen</td>
    </tr>
    <tr>
      <td><code>scale</code></td>
      <td>Display scale factor</td>
    </tr>
  </tbody>
</table>

When `saveWindow` is called, the store merges by matching on `label` or `id`, so secondary windows are preserved alongside the main window. Records with missing, malformed, or empty labels are ignored on load and omitted the next time the file is rewritten.

## Declaring windows in app.zon

```zig
.windows = .{
    .{ .label = "main", .title = "zero-native", .width = 720, .height = 480, .restore_state = true },
},
```
