# SheetNext JSON Format

> Manual supplemental document. This file is not generated by `scripts/doc-scanner.js`.

## Purpose

`SN.IO.getData()` exports the current workbook into a JSON object.

`SN.IO.setData(data)` restores a workbook from the same structure.

This document describes the transport format used by those two APIs.

## Versioning

- Current version: `2.0`
- `setData` requires:
  - `data.version === "2.0"`
  - `data.sheets` to be a non-empty array

If either check fails, `setData` returns `false`.

## Top-Level Shape

```json
{
  "version": "2.0",
  "workbookName": "Book1",
  "activeSheet": "Sheet1",
  "calcMode": "auto",
  "readOnly": false,
  "properties": {},
  "definedNames": {},
  "styleTable": {
    "fonts": [],
    "fills": [],
    "borders": [],
    "aligns": [],
    "styles": []
  },
  "sizeTable": {
    "rowHeights": [],
    "colWidths": []
  },
  "sheets": []
}
```

## Top-Level Fields

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `version` | string | Yes | Must currently be `"2.0"` |
| `workbookName` | string | No | Workbook display name |
| `activeSheet` | string \| null | No | Active sheet name to restore |
| `calcMode` | `"auto"` \| `"manual"` | No | Defaults to `auto` on import |
| `readOnly` | boolean | No | Workbook read-only state |
| `properties` | object | No | Workbook custom properties |
| `definedNames` | object | No | Workbook-level defined names |
| `styleTable` | object | Yes | Deduplicated shared style pools |
| `sizeTable` | object | Yes | Deduplicated row/column size pools |
| `sheets` | array | Yes | Workbook sheet list |
| `pivotCaches` | array | No | Pivot cache payloads |
| `slicerCaches` | array | No | Slicer cache payloads |
| `_snTrace` | any | No | Internal/export trace marker |

## Shared Tables

`styleTable` and `sizeTable` are shared lookup tables.

Instead of storing the same style or size object repeatedly on every cell/row/column, SheetNext stores the object once in a table and references it by index.

### `styleTable`

| Field | Type | Used By |
| --- | --- | --- |
| `fonts` | array | cell `fo` |
| `fills` | array | cell `fi` |
| `borders` | array | cell `b` |
| `aligns` | array | cell `a` |
| `styles` | array | row `s`, column `s`, cell `s` |

### `sizeTable`

| Field | Type | Used By |
| --- | --- | --- |
| `rowHeights` | array<number> | row `h` |
| `colWidths` | array<number> | column `w` |

## Sheet Shape

Each item in `sheets` looks like this:

```json
{
  "name": "Sheet1",
  "hidden": false,
  "showGridLines": true,
  "showRowColHeaders": true,
  "showPageBreaks": false,
  "outlinePr": {},
  "frozenCols": 0,
  "frozenRows": 0,
  "freezeStartRow": 0,
  "freezeStartCol": 0,
  "defaultColWidth": 72,
  "defaultRowHeight": 21,
  "zoom": 1,
  "activeCell": { "r": 0, "c": 0 },
  "viewStart": { "r": 0, "c": 0 },
  "printSettings": null,
  "rowCount": 200,
  "colCount": 32,
  "cols": [],
  "rows": [],
  "merges": [],
  "drawings": []
}
```

### Common Sheet Fields

| Field | Type | Notes |
| --- | --- | --- |
| `name` | string | Sheet name |
| `hidden` | boolean | Hidden sheet state |
| `showGridLines` | boolean | Gridline visibility |
| `showRowColHeaders` | boolean | Row/column header visibility |
| `showPageBreaks` | boolean | Page break visibility |
| `outlinePr` | object | Outline options |
| `frozenCols` | number | Frozen column count |
| `frozenRows` | number | Frozen row count |
| `freezeStartRow` | number | Freeze split start row |
| `freezeStartCol` | number | Freeze split start column |
| `defaultColWidth` | number | Default width in px |
| `defaultRowHeight` | number | Default height in px |
| `zoom` | number | Import is clamped to `0.1` to `4` |
| `activeCell` | `{r,c}` | Active cell |
| `viewStart` | `{r,c}` | View origin |
| `printSettings` | object \| null | Print config |
| `rowCount` | number | Sheet size hint |
| `colCount` | number | Sheet size hint |
| `sheetProtection` | object | Optional sheet protection config |

### Optional Sheet Sections

These sections are included only when data exists:

- `cols`
- `rows`
- `merges`
- `drawings`
- `sparklines`
- `comments`
- `tables`
- `autoFilters`
- `cfRules`
- `pivotTables`
- `slicers`

## Column Records

```json
{
  "cIndex": 0,
  "w": 3,
  "hidden": false,
  "ol": 1,
  "cl": false,
  "s": 2
}
```

| Field | Type | Notes |
| --- | --- | --- |
| `cIndex` | number | Zero-based column index |
| `w` | number | Index into `sizeTable.colWidths` |
| `hidden` | boolean | Hidden column |
| `ol` | number | Outline level |
| `cl` | boolean | Collapsed state |
| `s` | number | Index into `styleTable.styles` |

## Row Records

```json
{
  "rIndex": 0,
  "h": 4,
  "hidden": false,
  "ol": 1,
  "cl": false,
  "s": 5,
  "cells": []
}
```

| Field | Type | Notes |
| --- | --- | --- |
| `rIndex` | number | Zero-based row index |
| `h` | number | Index into `sizeTable.rowHeights` |
| `hidden` | boolean | Hidden row |
| `ol` | number | Outline level |
| `cl` | boolean | Collapsed state |
| `s` | number | Index into `styleTable.styles` |
| `cells` | array | Cell payload list |

## Cell Records

Cell payloads are stored inside `row.cells`.

```json
{
  "c": 0,
  "v": "Hello",
  "vt": "string",
  "s": 1
}
```

### Cell Position Fields

| Field | Type | Notes |
| --- | --- | --- |
| `c` | number | Start column index |
| `e` | number | Optional end column index for repeated identical cells |

If `e` is present, the same decoded cell template is applied from `c` through `e` inclusive.

### Cell Value Fields

| Field | Type | Notes |
| --- | --- | --- |
| `v` | any | Raw cell value |
| `vt` | string \| null | Value type marker |
| `f` | string | Formula string; importer ensures a leading `=` |
| `cv` | any | Cached formula result |
| `cvt` | string \| null | Cached result type marker |

Supported type markers:

- `date`
- `time`
- `dateTime`
- `nan`
- `inf`
- `-inf`

### Cell Style Fields

| Field | Type | Notes |
| --- | --- | --- |
| `s` | number | Index into `styleTable.styles` |
| `fo` | number | Index into `styleTable.fonts` |
| `fi` | number | Index into `styleTable.fills` |
| `b` | number | Index into `styleTable.borders` |
| `a` | number | Index into `styleTable.aligns` |
| `fmt` | string | Number format |
| `p` | object | Protection overrides |

### Cell Feature Fields

| Field | Type | Notes |
| --- | --- | --- |
| `link` | object | Hyperlink payload |
| `dv` | object | Data validation payload |
| `rt` | array | Rich text runs |

Hyperlink shape:

```json
{
  "t": "https://example.com",
  "l": null,
  "tt": "Open link"
}
```

## Merge Records

Each merge item is a normalized range object:

```json
{
  "s": { "r": 0, "c": 0 },
  "e": { "r": 1, "c": 2 }
}
```

## Drawing Records

Basic drawing shape:

```json
{
  "type": "image",
  "startCell": { "r": 1, "c": 1 },
  "offsetX": 0,
  "offsetY": 0,
  "width": 240,
  "height": 120,
  "anchorType": "twoCell",
  "rotation": 0
}
```

Additional drawing fields depend on `type`:

- `chart`: `chartOption`
- `image`: `imageBase64`
- `shape` / `connector`: `shapeType`, `shapeStyle`, `shapeText`, `isTextBox`

## Other Optional Sections

Sheet-level optional payloads:

- `sparklines`
- `comments`
- `tables`
- `autoFilters`
- `cfRules`
- `pivotTables`
- `slicers`

Workbook-level optional payloads:

- `pivotCaches`
- `slicerCaches`

These sections are round-tripped by `getData` / `setData`, but many of them are larger feature-specific payloads and are best produced by exporting an existing workbook first.

## Dynamic Value Wrapper

Some advanced cache payloads use a typed wrapper so special values survive JSON serialization:

```json
{
  "__sn_json_type": "date",
  "__sn_json_value": "2026-03-17T00:00:00.000Z"
}
```

Possible wrapper types:

- `date`
- `nan`
- `inf`
- `-inf`

## Minimal Example

```json
{
  "version": "2.0",
  "workbookName": "Demo",
  "activeSheet": "Sheet1",
  "calcMode": "auto",
  "readOnly": false,
  "properties": {},
  "definedNames": {},
  "styleTable": {
    "fonts": [],
    "fills": [],
    "borders": [],
    "aligns": [],
    "styles": []
  },
  "sizeTable": {
    "rowHeights": [],
    "colWidths": []
  },
  "sheets": [
    {
      "name": "Sheet1",
      "hidden": false,
      "showGridLines": true,
      "showRowColHeaders": true,
      "showPageBreaks": false,
      "outlinePr": {},
      "frozenCols": 0,
      "frozenRows": 0,
      "freezeStartRow": 0,
      "freezeStartCol": 0,
      "defaultColWidth": 72,
      "defaultRowHeight": 21,
      "zoom": 1,
      "activeCell": { "r": 0, "c": 0 },
      "viewStart": { "r": 0, "c": 0 },
      "printSettings": null,
      "rowCount": 10,
      "colCount": 10,
      "cols": [],
      "rows": [
        {
          "rIndex": 0,
          "h": 0,
          "cells": [
            { "c": 0, "v": "Name" },
            { "c": 1, "v": "Score" }
          ]
        },
        {
          "rIndex": 1,
          "h": 0,
          "cells": [
            { "c": 0, "v": "Alice" },
            { "c": 1, "v": 95 }
          ]
        }
      ],
      "merges": [],
      "drawings": []
    }
  ]
}
```

## Recommended Workflow

If you need to generate JSON externally:

1. create a workbook in SheetNext
2. export it with `SN.IO.getData()`
3. treat that JSON as the baseline contract
4. modify only the sections you need
5. import it back with `SN.IO.setData(data)`

This is the safest way to stay aligned with future feature additions.
