# Lumen Charts documentation

Public integration documentation for a proprietary library. Runtime access is separately authorized. Exported API signatures do not imply that all legacy capabilities are supported; preserve compatibility notes when using these documents.

<!-- Public documentation export; not library implementation source. -->
# Saving and loading charts

What you save depends on what you want users to get back. Layouts, indicator templates, drawing templates and chart settings templates each cover a different part of the chart.

| What's saved | What it contains | Usually handled by |
| --- | --- | --- |
| Chart layout | How the chart is set up, plus saved analysis | A complete layout adapter |
| Study template | A set of indicator settings | A study template adapter |
| Drawing template | A reusable drawing style | A drawing template adapter |
| Chart settings template | Appearance and settings | A chart template adapter |
| Separate drawing state | Drawings and groups, stored on their own | A separate drawing adapter |

## Quick snapshots

```js
widget.save(state => {
  localStorage.setItem('my-chart:layout:v1', JSON.stringify(state));
});
const saved = localStorage.getItem('my-chart:layout:v1');
if (saved) widget.load(JSON.parse(saved));
```

Be ready for JSON parse errors, a full storage quota and saved data the runtime rejects. Putting a version in your own storage key doesn't change the runtime's own schema version.

## Built-in Save and Load controls

Provide a complete adapter and the chart shows its Save and Load controls. If the layout adapter is missing or incomplete, those controls stay off. The chart never guesses a storage URL. The sample stores layouts in the browser and supports list, load, save and delete.

## Save/load adapter

A layout adapter needs all four operations: list, save, load and delete. Handle sign-in in your backend requests. Don't put credentials in the chart options.

| Method | Returns |
| --- | --- |
| `getAllCharts()` | A list of layout details |
| `saveChart(chartData)` | The saved layout's ID |
| `getChartContent(id)` | The content string saved earlier |
| `removeChart(id)` | Completes once the layout is deleted |

### Example: saving in the browser

```ts
// Import these public types from the declarations in your authorized distribution.
export const storageKey = 'lumencharts-docs:layouts:v1'
type RecordEntry = ChartData & { id: string; timestamp: number }
type StoragePort = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>
export function createDemoStorage(storage: StoragePort) {
  function read(): RecordEntry[] {
    const raw = storage.getItem(storageKey)
    if (!raw) return []
    let parsed: unknown
    try { parsed = JSON.parse(raw) } catch { throw new Error('Saved sample layouts are invalid. Reset saved layouts to start again.') }
    if (!Array.isArray(parsed) || parsed.length > 30 || !parsed.every(item => item && typeof item === 'object' && typeof item.id === 'string' && /^\d+$/.test(item.id) && typeof item.name === 'string' && typeof item.content === 'string' && typeof item.symbol === 'string' && typeof item.resolution === 'string' && Number.isFinite(item.timestamp))) throw new Error('Saved sample layouts are invalid. Reset saved layouts to start again.')
    return parsed as RecordEntry[]
  }
  function write(records: RecordEntry[]) {
    const value = JSON.stringify(records)
    if (value.length > 2_000_000) throw new Error('Sample storage is full. Delete a saved layout first.')
    storage.setItem(storageKey, value)
  }
  const adapter: Pick<IExternalSaveLoadAdapter, 'getAllCharts' | 'saveChart' | 'getChartContent' | 'removeChart'> = {
    async getAllCharts() { return read().map(({ id, name, symbol, resolution, timestamp }) => ({ id: Number(id), name, symbol, resolution: resolution as ResolutionString, timestamp })) },
    async saveChart(chart) {
      const records = read()
      const id = String(chart.id ?? Math.max(Date.now(), ...records.map(item => Number(item.id) + 1)))
      const next = records.filter(item => item.id !== id)
      if (next.length >= 30) throw new Error('Save up to 30 sample layouts. Delete one first.')
      next.push({ ...chart, id, timestamp: Math.floor(Date.now() / 1000) })
      write(next)
      return id
    },
    async getChartContent(id) { const item = read().find(item => item.id === String(id)); if (!item) throw new Error('Saved layout was not found'); return item.content },
    async removeChart(id) { write(read().filter(item => item.id !== String(id))) },
  }
  return { adapter, reset: () => storage.removeItem(storageKey) }
}
```

The legacy interface types list extra methods for other features. The runtime checks for the complete layout methods on their own. Optional template and separate drawing storage each need their own complete set of methods. Don't add empty methods that just report success to satisfy a broad type.

### On your server

Check that the signed-in user is allowed to perform every list, read, write and delete. The `client_id` and `user_id` strings narrow down whose data you're working with, but they don't prove who the user is. Limit list sizes in the database, and enforce a size limit on content before you store it.

Return saved content exactly as you received it; don't rewrite what's inside. Give a clear error for missing records, denied access, invalid content and temporary failures. If a response arrives after another operation has started, or after the chart was removed, ignore it.

[Interactive chart example](https://docs.lumencharts.com/charting-library/persistence/) — runs in a browser with synthetic data.

---
Documentation page: https://docs.lumencharts.com/charting-library/persistence/


---

<!-- Public documentation export; not library implementation source. -->
# Templates, settings and drawing storage

Templates save part of a chart's state so it can be reused. Store them separately from full layouts.

## Chart settings templates

These save appearance and settings only. They don't include the symbol, interval, panes, drawings or indicators. Before applying one, the runtime checks it against the exact current template schema. Storing chart templates needs the complete list, load, save and remove methods.

## Drawing templates

Save templates by tool and name. Only allowed style properties belong in a template. Details about a specific drawing, such as its ID, a custom title or its lifecycle state, shouldn't end up in reusable defaults. The saved template format is stricter than the options you can pass when creating a drawing.

## Study templates

Use the public API to create and apply study templates, along with the matching adapter methods. Decide up front whether a template should include the symbol and interval. A study template isn't a complete user workspace.

## User settings

For preferences that should survive the chart being recreated, use the supported settings adapter. Keep server-saved settings tied to the signed-in user, so one user's preferences never show up in someone else's session. If you don't configure an adapter, document any browser-only behavior your app depends on.

## Separate drawing storage

Storing drawings separately is optional. To use it, turn on the matching feature and provide the complete save/load contract.

### Use the official format

`getLineToolsState()` returns drawings and groups in the supported format. `applyLineToolsState(state)` checks the whole input before changing anything. Keep source IDs, groups, symbol ownership and version fields intact. Don't flatten the state into a plain list of points without a version.

The drawing format uses exact property names and a strict, current schema. Saved state is rejected if it contains nested creation `overrides`, unknown keys or alternative names. If even one item is invalid, the whole drawing payload is rejected. The chart won't quietly apply only the valid items.

### Keeping layouts in sync

Whether drawings can be shared depends on symbol ownership and layout sharing details. Group IDs stay local to one chart. Restoring can sync copies of a drawing, but don't build global IDs from list positions.

### Recovering from failures

Keep the last good saved copy until a new save succeeds. If saved data fails validation, leave the user's current chart as it is and offer a clear way to recover. Don't drop the broken items quietly and report the restore as a success.

[Drawing API](https://docs.lumencharts.com/charting-library/drawings) · [Migration policy](https://docs.lumencharts.com/charting-library/persistence/migration)

---
Documentation page: https://docs.lumencharts.com/charting-library/persistence/templates


---

<!-- Public documentation export; not library implementation source. -->
# Schemas and migration

Saved state has a version, and the version is what decides compatibility. A similar-looking UI or similar method names don't mean two formats are compatible.

## Checked before anything changes

The layout and drawing decoders read and check the whole saved payload before applying any of it. Layout migration happens in its own named decoder. For now, while drawing and template formats are pre-release, only their current versions are accepted. Older payloads aren't migrated automatically.

## Keep providers separate

Don't assume a layout saved by another chart provider will work with this runtime. Store each provider's data separately, and don't load another provider's saved payload into this chart.

## Upgrade checklist

1. Back up a sample of real saved layouts and templates.
2. Load both valid and deliberately broken samples with the new release.
3. Make sure a rejected load leaves the current chart unchanged.
4. Check that new saves reload correctly on the same release.
5. Deploy the docs, wrapper, iframe and optional worker together as one package.

Don't hand-edit schema fields based on screenshots. If you need a migration, write it down and test it as a versioned transformation.

[Upgrade guide](https://docs.lumencharts.com/charting-library/reference/releases#upgrade-safely)

---
Documentation page: https://docs.lumencharts.com/charting-library/persistence/migration
