Skip to main content

Chart workspace layouts

A chart layout is a named snapshot of the complete chart workspace: every chart, the active chart, symbols, intervals, chart types, indicators, comparisons, display settings, multi-chart grid metadata, and sync settings. Drawings are included when their persistence mode is embedded or both.

Use ChartLayout when users should reopen one specific chart workspace as they left it. The host must mount the expected chart controllers with the same stable chart ids before restore; applying a chart layout does not create a missing chart. Use a template when the same configuration should be applied elsewhere, user settings when preferences should survive switching chart layouts, and WidgetLayoutState for the surrounding widget panel arrangement.

What a chart layout owns

Included in the chart layoutStored elsewhere
Every chart's symbol, interval, chart type, indicators, comparisons, display settings, and optionally drawingsPer-user values in User settings
The active chart, multi-chart grid metadata, and cross-chart sync settingsSeparate drawing buckets when drawing persistence is separate
Chart layout id, name, schema version, SDK-owned multi-chart grid metadata, and host metadataWidget panel arrangements in WidgetLayoutState

A public ChartLayout is versioned JSON with id, optional name, activeChartId, charts, optional sync, and optional metadata. The SDK reserves metadata.multiChart for the multi-chart split tree, selection, visibility, and maximize state; preserve it when adding host metadata. Each chart entry carries its exact symbol, interval, state, drawings, indicators, comparisons, and display settings.

Use the chart workspace controller

widget.chart().saveLayout() saves one chart only. It cannot preserve sibling charts, the active chart, chart grid metadata, or cross-chart sync. Use that low-level API only for a deliberately single-chart surface with host-owned UI.

Save and restore one chart workspace

Use widget.chartLayouts() as the canonical chart-layout authority for both single-chart and multi-chart widgets:

chartLayoutRoundTrip.ts
import type { ChartWidgetApi } from '@tradescript/pro/sdk/core';

declare const widget: ChartWidgetApi;

const chartLayouts = widget.chartLayouts();

await chartLayouts.saveAs('opening-drive', 'Opening drive');

const summaries = await chartLayouts.list();
const summary = summaries.find((item) => item.id === 'opening-drive');
if (!summary) throw new Error('Saved chart layout was not listed');

const restored = await chartLayouts.load('opening-drive');
if (!restored) throw new Error('Saved chart layout was not found');

await chartLayouts.save('opening-drive');
await chartLayouts.rename('opening-drive', 'Opening drive — revised');
await chartLayouts.delete('opening-drive');

Verify the user-visible result at each step:

  • saveAs assigns the chart layout's stable logical id and display name.
  • list() returns the saved chart layout summary.
  • After changing a chart or the chart grid, load() restores saved content and grid state for the mounted charts whose ids match the saved chart entries.
  • save() updates the same saved chart layout.
  • rename() changes the display name without changing chart layout identity.
  • delete() removes only that saved chart layout.
  • load() resolves null when the chart layout does not exist and leaves the live chart workspace unchanged.

Define chart layout identity and lifecycle

FactRule
ChartLayout.idStable logical identity for one named chart workspace
ChartLayout.nameHuman-readable label; it may change without creating a new identity
ChartLayout.versionSaved-state schema version; currently 1
ChartLayout.chartsComplete per-chart entries restored together as one chart workspace
ChartLayout.metadataSDK-owned multiChart grid state plus optional host data; preserve SDK-owned keys and never place unrelated persisted objects here

saveAs(layoutId, name) re-identifies the current chart workspace under the supplied chart layout id and optional name. save(layoutId) captures later changes under that identity. Do not derive chart layout identity from the display name.

Deleting a chart layout must not cascade to separately stored drawings, templates, user settings, replay state, or widget panel arrangements. If the product offers cascading cleanup, make it a separate user-confirmed workflow.

When drawing persistence is embedded, the drawings share the chart layout's lifecycle. When it is separate, the chart layout identifies the drawing context but does not own the drawing records. See Drawing storage before choosing the drawing lifecycle.

Restore matches ChartLayout.charts[].id to controllers already attached to the workspace. A saved entry with no mounted match is skipped; an attached chart with no saved entry keeps its current content. Treat chart ids as part of the persisted-state contract and test them across application releases.

Preserve backend record identity across sessions

When a backend assigns a storageId different from ChartLayout.id, the chart layout controller keeps that mapping in memory so updates target the existing record. Persist and restore the mapping when host code drives chart layouts directly:

chartLayoutIds.ts
import type { ChartWorkspaceLayoutControllerApi } from '@tradescript/pro/sdk';

declare const chartLayouts: ChartWorkspaceLayoutControllerApi;
declare function saveChartLayoutIds(ids: Record<string, string>): Promise<void>;
declare function loadChartLayoutIds(): Promise<Record<string, string> | undefined>;

chartLayouts.restoreStorageIds(await loadChartLayoutIds());

await chartLayouts.saveAs('opening-drive', 'Opening drive');
await saveChartLayoutIds(chartLayouts.getStorageIds());

Restore the mapping before a direct load(logicalId), save(logicalId), rename(logicalId, ...), or delete(logicalId) in a later session. When features.workspaceTabs is enabled, its user-settings record persists this mapping together with the open tab order and active tab.

Chart layout tabs

Enable features.workspaceTabs when the product needs multiple named, open chart workspaces. The chart layout tab strip owns create, save, duplicate, rename, reorder, close, delete, and actions for opening saved chart layouts so the product has one chart-layout UI authority.

When chart layout tabs are disabled, the chart storage menu exposes the chart layout save and load actions and still routes them through widget.chartLayouts().

loadLastChart is also widget-owned. It runs once after all chart controllers exist, chooses the configured or newest saved chart layout, and applies matching chart entries by chart id.

Missing and failed chart-layout operations

OutcomeController behavior
Saved chart layout is missingload(chartLayoutId) resolves null; the live chart workspace stays unchanged
Apply fails while creating or saving asThe controller restores the previous live chart workspace before rethrowing
A separate drawing load failsThe chart layout and drawing failure remain separate storage outcomes

The chart-layout controller reports the storage failure to its caller. Product UI should preserve the user's unsaved chart workspace and expose a retry or recovery path instead of treating a failed request as a missing chart layout.

Low-level single-chart state

Use the chart-level API only when one chart is the entire persistence boundary and the host owns its save and load UI:

  • chart.getLayout(chartLayoutId) captures one chart as ChartLayout.
  • chart.applyLayout(chartLayout) applies a supplied single-chart snapshot.
  • chart.loadLayout({ layoutId, apply: false }) reads a stored chart layout without applying it.
  • widget.chart().saveLayout() saves only the active chart, not the complete multi-chart workspace.

Do not use these methods as a substitute for widget.chartLayouts() when the surface contains sibling charts or chart grid state.

Next steps