@coderyo/ui-shell 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,9 +1,28 @@
1
1
  import * as _coderyo_data from '@coderyo/data';
2
2
  import { Interval, SymbolSearchHit } from '@coderyo/data';
3
+ import { Locale, I18nDictionary } from '@coderyo/i18n';
3
4
  import { IndicatorConfig } from '@coderyo/indicators';
4
5
  import * as _coderyo_drawings from '@coderyo/drawings';
5
6
  import { DrawingRecord } from '@coderyo/drawings';
7
+ import { BridgeLayerPane } from '@coderyo/bridge';
6
8
  import { StreamLanguage } from '@codemirror/language';
9
+ export { isValidLayerBridgePane as isValidBridgeLayerPane } from '@coderyo/core';
10
+
11
+ interface I18nProvider {
12
+ t(key: string, fallback?: string, params?: Record<string, string | number>): string;
13
+ getLocale(): Locale;
14
+ setLocale(locale: Locale): void;
15
+ registerLocale(locale: Locale, dictionary: I18nDictionary): void;
16
+ subscribe(listener: (locale: Locale) => void): () => void;
17
+ }
18
+ declare function createI18nProvider(defaultLocale?: Locale): I18nProvider;
19
+
20
+ interface LogoSlotOptions {
21
+ label?: string;
22
+ href?: string;
23
+ imageSrc?: string;
24
+ }
25
+ declare function mountLogoSlot(parent: HTMLElement, opts?: LogoSlotOptions): HTMLElement;
7
26
 
8
27
  declare const GRID_SETTING_KEY = "tradview:settings:showGrid";
9
28
  declare const RETURN_CURSOR_KEY = "tradview:settings:returnToCursorAfterDraw";
@@ -11,7 +30,9 @@ declare function loadShowGridPreference(): boolean;
11
30
  declare function saveShowGridPreference(show: boolean): void;
12
31
  declare function loadReturnToCursorPreference(): boolean;
13
32
  declare function saveReturnToCursorPreference(v: boolean): void;
33
+ /** @deprecated Prefer `@coderyo/core` `loadIndicatorConfig(storage, …)` when not using ui-shell. */
14
34
  declare function loadIndicatorConfig(symbol: string, interval: string): IndicatorConfig;
35
+ /** @deprecated Prefer `@coderyo/core` `saveIndicatorConfig(storage, …)` when not using ui-shell. */
15
36
  declare function saveIndicatorConfig(symbol: string, interval: string, config: IndicatorConfig): void;
16
37
 
17
38
  interface SettingsPanelOptions {
@@ -21,10 +42,25 @@ interface SettingsPanelOptions {
21
42
  onReturnToCursorChange?: (v: boolean) => void;
22
43
  indicatorConfig?: IndicatorConfig;
23
44
  onIndicatorConfigChange?: (config: IndicatorConfig) => void;
45
+ onClearAllIndicators?: () => void;
46
+ onClearAllDrawings?: () => void;
24
47
  }
25
48
  declare function mountSettingsPanel(parent: HTMLElement, opts?: SettingsPanelOptions): HTMLElement;
26
49
 
27
- type SymbolInputMode = 'manual' | 'search' | 'none';
50
+ type ThemeId = 'dark' | 'light';
51
+ declare const THEME_STORAGE_KEY = "tradview:theme";
52
+ declare function loadTheme(): ThemeId;
53
+ declare function saveTheme(theme: ThemeId): void;
54
+ declare function applyThemeToDocument(theme: ThemeId, root?: HTMLElement): void;
55
+ interface ThemeProvider {
56
+ getTheme(): ThemeId;
57
+ setTheme(theme: ThemeId): void;
58
+ toggle(): ThemeId;
59
+ subscribe(listener: (theme: ThemeId) => void): () => void;
60
+ }
61
+ declare function createThemeProvider(initial?: ThemeId): ThemeProvider;
62
+
63
+ type SymbolInputMode = 'manual' | 'search' | 'dialog' | 'none';
28
64
  interface TopBarOptions {
29
65
  intervals?: Interval[];
30
66
  /** Highlighted interval button (defaults to first in `intervals`). */
@@ -34,6 +70,10 @@ interface TopBarOptions {
34
70
  onSymbolSelect?: (symbol: string) => void;
35
71
  onIntervalChange?: (interval: Interval) => void;
36
72
  onThemeToggle?: () => void;
73
+ onThemeChange?: (theme: 'dark' | 'light') => void;
74
+ themeProvider?: ThemeProvider;
75
+ i18n?: I18nProvider;
76
+ logo?: LogoSlotOptions | false;
37
77
  onFullscreen?: () => void;
38
78
  onScreenshot?: () => void;
39
79
  settings?: SettingsPanelOptions;
@@ -45,6 +85,142 @@ declare function mountTopBar(parent: HTMLElement, opts?: TopBarOptions): {
45
85
  setActiveInterval: (interval: Interval) => void;
46
86
  };
47
87
 
88
+ interface ThemeToggleOptions {
89
+ themeProvider: ThemeProvider;
90
+ i18n?: I18nProvider;
91
+ onThemeChange?: (theme: ThemeId) => void;
92
+ }
93
+ declare function mountThemeToggle(parent: HTMLElement, opts: ThemeToggleOptions): HTMLButtonElement;
94
+
95
+ interface SymbolSearchDialogOptions {
96
+ onSearch: (query: string) => Promise<SymbolSearchHit[]>;
97
+ onSelect: (symbol: string) => void;
98
+ initialSymbol?: string;
99
+ i18n?: I18nProvider;
100
+ }
101
+ interface SymbolSearchDialogHandle {
102
+ open(): void;
103
+ close(): void;
104
+ destroy(): void;
105
+ }
106
+ declare function createSymbolSearchDialog(opts: SymbolSearchDialogOptions): SymbolSearchDialogHandle;
107
+ /** TopBar trigger: shows current symbol and opens the dialog. */
108
+ declare function mountSymbolSearchDialogTrigger(parent: HTMLElement, dialog: SymbolSearchDialogHandle, opts: {
109
+ initialSymbol?: string;
110
+ i18n?: I18nProvider;
111
+ }): HTMLElement;
112
+
113
+ /** LayoutPreset v2 — free-floating layer compositor (replaces layout-schema v1). */
114
+ declare const LAYER_PRESET_VERSION: 2;
115
+ type LayerPresetAuthor = 'integrator' | 'user';
116
+ type LayerType = 'shell.topBar' | 'shell.leftToolbar' | 'shell.bottomToolbar' | 'shell.statusBar' | 'shell.propertiesPanel' | 'chart.host' | 'chart.main' | 'chart.volume' | 'chart.indicator' | 'chart.indicatorHost' | 'overlay.crosshairLegend' | 'overlay.drawing' | 'group';
117
+ /** Normalized frame relative to page root (0..1). */
118
+ interface LayerFrame {
119
+ x: number;
120
+ y: number;
121
+ w: number;
122
+ h: number;
123
+ }
124
+ interface LayoutPage {
125
+ id: string;
126
+ title: string;
127
+ }
128
+ interface LayerNode {
129
+ id: string;
130
+ pageId: string;
131
+ type: LayerType;
132
+ /** Maps to legacy widget id or chart role for mount. */
133
+ widgetKey: string;
134
+ frame: LayerFrame;
135
+ zIndex: number;
136
+ visible: boolean;
137
+ locked: boolean;
138
+ groupId?: string;
139
+ /**
140
+ * @public Time-scale sync group for chart panes. Same non-empty id → pan/zoom together;
141
+ * omit or empty → pane is independent (own TimeScaleBus).
142
+ */
143
+ syncTimeScaleGroupId?: string;
144
+ }
145
+ interface BindGroup {
146
+ id: string;
147
+ name?: string;
148
+ layerIds: string[];
149
+ }
150
+ interface LayoutPreset {
151
+ version: typeof LAYER_PRESET_VERSION;
152
+ /**
153
+ * Monotonic revision for remote `host.layer.setPreset` merge/replace (integer ≥ 1).
154
+ * @public Bridge schema 2 — stale host revision is rejected with `STALE_PRESET_REVISION`.
155
+ */
156
+ revision?: number;
157
+ id: string;
158
+ name: string;
159
+ author: LayerPresetAuthor;
160
+ readonly?: boolean;
161
+ forkedFrom?: string;
162
+ pages: LayoutPage[];
163
+ layers: LayerNode[];
164
+ groups: BindGroup[];
165
+ }
166
+ type LayerWidgetKey = 'topBar' | 'leftToolbar' | 'bottomToolbar' | 'chartHost' | 'chartMain' | 'chartVolume' | 'chartIndicator' | 'indicatorHost' | 'statusBar' | 'propertiesPanel' | 'crosshairLegend' | 'drawingOverlay';
167
+
168
+ /** @public Mutable controller for LayoutPreset v2 (layers, groups, pane focus). */
169
+ declare class LayerController {
170
+ private preset;
171
+ private readonly listeners;
172
+ private readonly focusListeners;
173
+ private interactionDepth;
174
+ private pageIdSeq;
175
+ activePageId: string;
176
+ /** P2: active chart pane layer (click-to-focus). */
177
+ focusedPaneLayerId: string | null;
178
+ /** Monotonic preset revision (Bridge `host.layer.setPreset`). */
179
+ presetRevision: number;
180
+ constructor(initial: LayoutPreset);
181
+ getPreset(): LayoutPreset;
182
+ /** True while pointer drag/resize/marquee is active — blocks setPreset. */
183
+ isInteracting(): boolean;
184
+ beginInteraction(): void;
185
+ endInteraction(): void;
186
+ setPreset(next: LayoutPreset): boolean;
187
+ getLayersForActivePage(): LayoutPreset['layers'];
188
+ getFocusedPaneLayerId(): string | null;
189
+ getFocusedPaneId(): 'main' | 'volume' | 'indicator' | null;
190
+ /** Raise z-index among chart panes only; does not rebuild compositor DOM. */
191
+ focusChartPane(layerId: string): void;
192
+ subscribeFocus(listener: () => void): () => void;
193
+ private notifyFocus;
194
+ private afterChartMainFrameChange;
195
+ setActivePage(pageId: string): boolean;
196
+ addPage(title?: string): string;
197
+ renamePage(pageId: string, title: string): boolean;
198
+ removePage(pageId: string): boolean;
199
+ getLayer(layerId: string): LayerNode | undefined;
200
+ /**
201
+ * @public Set time-scale sync group for a chart pane layer (`''` clears → independent).
202
+ */
203
+ setLayerSyncGroup(layerId: string, groupId: string | null | undefined): boolean;
204
+ getGroup(groupId: string): BindGroup | undefined;
205
+ /** Layer ids that move/resize together (group members or singleton). */
206
+ getTransformLayerIds(layerId: string): string[];
207
+ getGroupMemberLayers(groupId: string): LayoutPreset['layers'];
208
+ setLayerFrame(layerId: string, frame: LayerFrame): void;
209
+ private expandTransformIds;
210
+ moveLayers(layerIds: string[], dx: number, dy: number): void;
211
+ resizeLayersFromBbox(layerIds: string[], newBbox: LayerFrame): void;
212
+ setLayerVisible(layerId: string, visible: boolean): void;
213
+ setLayerLocked(layerId: string, locked: boolean): void;
214
+ private syncGroupLayerIds;
215
+ bumpZIndex(layerId: string, delta: 1 | -1): void;
216
+ reorderLayers(orderedIds: string[]): void;
217
+ createBindGroup(layerIds: string[], name?: string): string | null;
218
+ dissolveGroup(groupId: string): void;
219
+ removeLayerFromGroup(layerId: string): void;
220
+ subscribe(listener: () => void): () => void;
221
+ private emit;
222
+ }
223
+
48
224
  interface ContextMenuAction {
49
225
  id: string;
50
226
  label: string;
@@ -79,7 +255,7 @@ interface CrosshairLegendOptions {
79
255
  symbol?: string;
80
256
  interval?: string;
81
257
  }
82
- declare function mountCrosshairLegend(chartHost: HTMLElement, opts?: CrosshairLegendOptions): {
258
+ declare function mountCrosshairLegend(chartHost?: HTMLElement, opts?: CrosshairLegendOptions): {
83
259
  el: HTMLElement;
84
260
  update: (payload: {
85
261
  time?: number;
@@ -112,7 +288,7 @@ interface LayoutFeatures {
112
288
  showSettings?: boolean;
113
289
  showShortcuts?: boolean;
114
290
  /** When TopBar is on and no search API: manual symbol input (default). */
115
- symbolInput?: 'manual' | 'search' | 'none';
291
+ symbolInput?: 'manual' | 'search' | 'dialog' | 'none';
116
292
  }
117
293
  interface ResolvedLayoutFeatures {
118
294
  showTopBar: boolean;
@@ -124,7 +300,7 @@ interface ResolvedLayoutFeatures {
124
300
  showContextMenu: boolean;
125
301
  showSettings: boolean;
126
302
  showShortcuts: boolean;
127
- symbolInput: 'manual' | 'search' | 'none';
303
+ symbolInput: 'manual' | 'search' | 'dialog' | 'none';
128
304
  }
129
305
  declare const DEFAULT_LAYOUT_FEATURES: ResolvedLayoutFeatures;
130
306
  declare function resolveLayoutFeatures(opts?: ChartLayoutOptions): ResolvedLayoutFeatures;
@@ -132,8 +308,52 @@ declare function mergeLayoutFeatures(current: ResolvedLayoutFeatures, patch: Lay
132
308
  /** Playground: enable full TV shell. */
133
309
  declare function createDemoLayoutOptions(partial?: ChartLayoutOptions): ChartLayoutOptions;
134
310
 
311
+ /** Visual layout schema (grid-based composable shell). */
312
+ type LayoutWidgetId = 'topBar' | 'leftToolbar' | 'bottomToolbar' | 'chartHost' | 'indicatorHost' | 'statusBar' | 'propertiesPanel';
313
+ interface LayoutWidgetPlacement {
314
+ id: LayoutWidgetId;
315
+ col: number;
316
+ row: number;
317
+ colSpan: number;
318
+ rowSpan: number;
319
+ }
320
+ interface LayoutSchema {
321
+ version: 1;
322
+ columns: number;
323
+ rows: number;
324
+ widgets: LayoutWidgetPlacement[];
325
+ }
326
+ declare const LAYOUT_SCHEMA_VERSION: 1;
327
+ /** TV-style default when integrator omits `layout` (matches legacy mountChartLayout proportions). */
328
+ declare const DEFAULT_LAYOUT_SCHEMA: LayoutSchema;
329
+ declare function layoutStorageKey(layoutId: string): string;
330
+ declare function resolveLayoutSchema(partial?: LayoutSchema | null): LayoutSchema;
331
+ declare function cloneLayoutSchema(schema: LayoutSchema): LayoutSchema;
332
+ declare function normalizeLayoutSchema(input: LayoutSchema): LayoutSchema;
333
+ declare function loadLayoutSchema(layoutId: string): LayoutSchema | null;
334
+ declare function saveLayoutSchema(layoutId: string, schema: LayoutSchema): void;
335
+ declare function getWidgetPlacement(schema: LayoutSchema, id: LayoutWidgetId): LayoutWidgetPlacement | undefined;
336
+
135
337
  type DrawingToolId = 'cursor' | 'trendline' | 'hline' | 'vline' | 'rectangle' | 'fibonacci' | 'text';
136
338
  interface ChartLayoutOptions extends TopBarOptions {
339
+ themeProvider?: ThemeProvider;
340
+ i18n?: I18nProvider;
341
+ /** When false, do not auto-create theme provider / localStorage (default true). */
342
+ autoThemeProvider?: boolean;
343
+ /** When false, do not auto-create i18n provider (default true). */
344
+ autoI18n?: boolean;
345
+ /** Grid layout schema; omit to use {@link DEFAULT_LAYOUT_SCHEMA}. */
346
+ layout?: LayoutSchema | null;
347
+ /** Key for layout persistence (`layoutPersist`). Default `default`. */
348
+ layoutId?: string;
349
+ /** Persist layout schema to localStorage on change / saveLayout(). */
350
+ layoutPersist?: boolean;
351
+ /**
352
+ * Enable drag/resize layout editor on mount.
353
+ * @deprecated Ignored when `layerCompositorManaged` — use `mountLayerCompositor` + `enableLayerEditor`.
354
+ */
355
+ layoutEditor?: boolean;
356
+ onLayoutChange?: (schema: LayoutSchema) => void;
137
357
  showTopBar?: boolean;
138
358
  showLeftToolbar?: boolean;
139
359
  showBottomToolbar?: boolean;
@@ -148,16 +368,28 @@ interface ChartLayoutOptions extends TopBarOptions {
148
368
  showContextMenu?: boolean;
149
369
  showSettings?: boolean;
150
370
  showShortcuts?: boolean;
151
- symbolInput?: 'manual' | 'search' | 'none';
371
+ symbolInput?: 'manual' | 'search' | 'dialog' | 'none';
152
372
  onDrawingStyleChange?: (patch: {
153
373
  color?: string;
154
374
  lineWidth?: number;
155
375
  text?: string;
156
376
  }) => void;
157
377
  onDrawingSelectionBind?: (bind: (drawing: _coderyo_drawings.DrawingRecord | null) => void) => void;
378
+ /**
379
+ * When true, crosshair legend is not mounted under chartMain and visibility is driven by
380
+ * LayerCompositor (not grid CSS / el.style.display here).
381
+ */
382
+ layerCompositorManaged?: boolean;
158
383
  }
159
384
  declare function mountChartLayout(root: HTMLElement, opts?: ChartLayoutOptions): {
385
+ layoutRoot: HTMLElement;
386
+ layoutGrid: HTMLElement;
387
+ /** @deprecated Alias of chartMain — use chartMain for LWC mount. */
160
388
  chartHost: HTMLElement;
389
+ chartMain: HTMLElement;
390
+ chartVolume: HTMLElement;
391
+ /** Drawing canvas host (same element as chartMain when compositor-managed). */
392
+ drawingOverlay: HTMLElement;
161
393
  indicatorHost: HTMLElement;
162
394
  topBar: HTMLElement;
163
395
  setActiveInterval: (interval: _coderyo_data.Interval) => void;
@@ -166,10 +398,236 @@ declare function mountChartLayout(root: HTMLElement, opts?: ChartLayoutOptions):
166
398
  detachContextMenu: () => void;
167
399
  setActiveDrawingTool: (tool: DrawingToolId) => void;
168
400
  propertiesPanel: ReturnType<typeof mountDrawingPropertiesPanel>;
401
+ /** Bind drawing + apply properties panel visibility (compositor-safe). */
402
+ handleDrawingSelection: (drawing: _coderyo_drawings.DrawingRecord | null) => void;
403
+ /** Sync showCrosshairLegend (etc.) to compositor layer.visible when layerCompositorManaged. */
404
+ syncCompositorShellVisibility?: (controller: LayerController) => void;
405
+ /**
406
+ * Bind compositor controller so `setLayoutFeatures` keeps shell/legend layer.visible in sync.
407
+ * Call once after `mountLayerCompositor` (Playground pattern).
408
+ */
409
+ bindLayerCompositorController?: (controller: LayerController) => void;
169
410
  setLayoutFeatures: (patch: LayoutFeatures) => void;
170
411
  getLayoutFeatures: () => ResolvedLayoutFeatures;
412
+ getLayoutSchema: () => LayoutSchema;
413
+ setLayoutSchema: (schema: LayoutSchema) => void;
414
+ enableLayoutEditor: (enabled: boolean) => void;
415
+ saveLayout: () => void;
171
416
  };
172
417
 
418
+ type LayoutWidgetElements = Partial<Record<LayoutWidgetId, HTMLElement>>;
419
+ interface LayoutGridHandle {
420
+ root: HTMLElement;
421
+ grid: HTMLElement;
422
+ cells: Map<LayoutWidgetId, HTMLElement>;
423
+ applySchema: (schema: LayoutSchema, features: ResolvedLayoutFeatures) => void;
424
+ enableEditor: (enabled: boolean) => void;
425
+ getSchema: () => LayoutSchema;
426
+ setSchema: (schema: LayoutSchema) => void;
427
+ destroyEditor: () => void;
428
+ }
429
+ interface CreateLayoutGridOptions {
430
+ schema: LayoutSchema;
431
+ widgets: LayoutWidgetElements;
432
+ features: ResolvedLayoutFeatures;
433
+ editor?: boolean;
434
+ onSchemaChange?: (schema: LayoutSchema) => void;
435
+ }
436
+ declare function createLayoutGrid(opts: CreateLayoutGridOptions): LayoutGridHandle;
437
+
438
+ declare function clampFrame(frame: LayerFrame): LayerFrame;
439
+ declare function normalizeLayoutPreset(input: LayoutPreset): LayoutPreset;
440
+ declare function cloneLayoutPreset(preset: LayoutPreset): LayoutPreset;
441
+
442
+ /** Convert legacy 12×12 grid schema to normalized layer preset (single page). */
443
+ declare function layoutSchemaToPreset(schema?: LayoutSchema, opts?: {
444
+ id?: string;
445
+ name?: string;
446
+ }): LayoutPreset;
447
+
448
+ /** Built-in integrator preset — matches legacy DEFAULT_LAYOUT_SCHEMA proportions. */
449
+ declare const VENDOR_DEFAULT_PRESET: LayoutPreset;
450
+ /** Compact: chart-focused, smaller chrome. */
451
+ declare const VENDOR_COMPACT_PRESET: LayoutPreset;
452
+ /** Demo: main+volume share `prices`; indicator pane uses separate `osc` group. */
453
+ declare const VENDOR_DUAL_SYNC_PRESET: LayoutPreset;
454
+ declare const BUILTIN_PRESETS: LayoutPreset[];
455
+ declare function getBuiltinPreset(id: string): LayoutPreset | undefined;
456
+
457
+ declare function presetStorageKey(id: string): string;
458
+ interface PresetListEntry {
459
+ id: string;
460
+ name: string;
461
+ author: LayoutPreset['author'];
462
+ readonly?: boolean;
463
+ builtin?: boolean;
464
+ }
465
+ declare function listPresets(): PresetListEntry[];
466
+ declare function loadPreset(id: string): LayoutPreset | null;
467
+ declare function savePreset(preset: LayoutPreset): void;
468
+ declare function deleteUserPreset(id: string): boolean;
469
+ declare function forkPreset(sourceId: string, newId: string, newName: string): LayoutPreset | null;
470
+ declare function resolvePreset(id: string | LayoutPreset | null | undefined): LayoutPreset;
471
+
472
+ /** Union bounding box of layer frames (normalized 0..1). */
473
+ declare function getLayersBoundingBox(layers: Pick<LayerNode, 'frame'>[]): LayerFrame | null;
474
+ /** Move all frames by normalized delta; each frame is clamped independently. */
475
+ declare function moveLayerFrames(frames: LayerFrame[], dx: number, dy: number): LayerFrame[];
476
+ /**
477
+ * Resize a group by mapping each member's relative position inside oldBbox into newBbox.
478
+ */
479
+ declare function resizeGroupFrames(members: Pick<LayerNode, 'frame'>[], oldBbox: LayerFrame, newBbox: LayerFrame): LayerFrame[];
480
+ declare function clampBBox(frame: LayerFrame): LayerFrame;
481
+
482
+ type LayerWidgetElements = Partial<Record<LayerWidgetKey, HTMLElement>>;
483
+ interface LayerCompositorOptions {
484
+ preset: LayoutPreset;
485
+ widgets: LayerWidgetElements;
486
+ /** Hide legacy grid container when compositor positions widgets. */
487
+ hideLegacyGrid?: HTMLElement;
488
+ /** Fired after user drag/resize commits a preset change. */
489
+ onPresetChange?: (preset: LayoutPreset) => void;
490
+ /** Shift+drag marquee on compositor root (edit mode). */
491
+ onMarqueeSelect?: (layerIds: string[]) => void;
492
+ /** P2: chart pane focus changed (main / volume / indicator). */
493
+ onChartPaneFocus?: (pane: 'main' | 'volume' | 'indicator' | null) => void;
494
+ }
495
+ interface LayerCompositorHandle {
496
+ root: HTMLElement;
497
+ controller: LayerController;
498
+ apply(): void;
499
+ enableLayerEditor(enabled: boolean): void;
500
+ isLayerEditorEnabled(): boolean;
501
+ destroy(): void;
502
+ }
503
+ /** @public Mount floating layer compositor (LayoutPreset v2) over shell widgets. */
504
+ declare function mountLayerCompositor(parent: HTMLElement, opts: LayerCompositorOptions): LayerCompositorHandle;
505
+
506
+ interface LayerEditorOptions {
507
+ controller: LayerController;
508
+ /** Called after drag/resize commits preset changes. */
509
+ onPresetChange?: () => void;
510
+ /** Shift+drag marquee selection on compositor root. */
511
+ onMarqueeSelect?: (layerIds: string[]) => void;
512
+ }
513
+ interface LayerEditorHandle {
514
+ setEnabled(enabled: boolean): void;
515
+ isEnabled(): boolean;
516
+ rebind(): void;
517
+ destroy(): void;
518
+ }
519
+ declare function attachLayerEditor(compositorRoot: HTMLElement, opts: LayerEditorOptions): LayerEditorHandle;
520
+
521
+ interface LayerPanelOptions {
522
+ onSaveAsPreset?: (preset: ReturnType<LayerController['getPreset']>) => void;
523
+ }
524
+ interface LayerPanelHandle {
525
+ el: HTMLElement;
526
+ destroy: () => void;
527
+ toggle: () => boolean;
528
+ /** Replace panel checkbox selection (e.g. compositor shift+marquee). */
529
+ selectLayers(layerIds: string[]): void;
530
+ }
531
+ /** @public Layer list UI bound to a LayerController. */
532
+ declare function mountLayerPanel(parent: HTMLElement, controller: LayerController, opts?: LayerPanelOptions): LayerPanelHandle;
533
+
534
+ declare const CHART_PANE_LAYER_TYPES: readonly LayerType[];
535
+ declare const DEFAULT_SYNC_TIME_SCALE_GROUP = "chart-timescale";
536
+ declare function isChartPaneLayerType(type: string): type is LayerType;
537
+ declare function widgetKeyForChartPaneType(type: LayerType): string | null;
538
+ declare function paneIdFromWidgetKey(widgetKey: string): 'main' | 'volume' | 'indicator' | null;
539
+ interface HostSplitResult {
540
+ layers: LayerNode[];
541
+ /** Maps removed `chart.host` layer id → [mainId, volumeId]. */
542
+ idRemap: Map<string, string[]>;
543
+ }
544
+ /** Split legacy `chart.host` into main + volume layers (vertical tile inside host frame). */
545
+ declare function expandLegacyChartHostLayers(layers: LayerNode[]): LayerNode[];
546
+ declare function splitLegacyChartHost(layers: LayerNode[]): HostSplitResult;
547
+ /** @deprecated Use syncOverlayLayersToMain / syncAllOverlayLayersToMain */
548
+ declare function syncCrosshairLegendToMain(layers: LayerNode[]): void;
549
+ declare function upgradeIndicatorHostType(layers: LayerNode[]): LayerNode[];
550
+
551
+ declare const OVERLAY_LAYER_TYPES: readonly LayerType[];
552
+ declare function isOverlayLayerType(type: string): type is LayerType;
553
+ /** Anchor crosshair legend + drawing overlay frames to chart.main on one page (P3). */
554
+ declare function syncOverlayLayersToMain(layers: LayerNode[], pageId: string): void;
555
+ /** Sync overlays for every page (normalize / bulk migrate). */
556
+ declare function syncAllOverlayLayersToMain(layers: LayerNode[]): void;
557
+ /** Active-page drawing overlay visibility (wire to `IChart.setFeatures({ drawings: { layer } })`). */
558
+ declare function getDrawingOverlayVisible(controller: {
559
+ getLayersForActivePage(): LayerNode[];
560
+ }): boolean;
561
+ /** Insert default overlay layers when chart.main exists (normalize). */
562
+ declare function ensureOverlayLayers(layers: LayerNode[], pageId: string): LayerNode[];
563
+
564
+ type CompositorShellWidgetId = LayoutWidgetId;
565
+ interface CompositorShellHandle {
566
+ root: HTMLElement;
567
+ /** Hidden anchor grid (v2 compositor positions widgets; no 12×12 layout). */
568
+ grid: HTMLElement;
569
+ cells: Map<CompositorShellWidgetId, HTMLElement>;
570
+ }
571
+ interface CreateCompositorShellOptions {
572
+ widgets: Partial<Record<CompositorShellWidgetId, HTMLElement>>;
573
+ }
574
+ /** Minimal DOM shell for LayoutPreset v2 — replaces legacy createLayoutGrid when compositor-managed. */
575
+ declare function createCompositorShell(opts: CreateCompositorShellOptions): CompositorShellHandle;
576
+ /** No-op schema apply for compositor shell (features still drive widget mount in chart-layout). */
577
+ declare function applyCompositorShellFeatures(_cells: Map<CompositorShellWidgetId, HTMLElement>, _features: ResolvedLayoutFeatures): void;
578
+
579
+ /** Map `LayoutFeatures` toggles to compositor layer.visible on the active page. */
580
+ declare function syncCompositorShellVisibilityFromFeatures(controller: LayerController, features: ResolvedLayoutFeatures): void;
581
+
582
+ interface PageNavigatorOptions {
583
+ /** Show tab bar even on wide viewports (default: narrow only). */
584
+ alwaysVisible?: boolean;
585
+ narrowMq?: string;
586
+ onPageChange?: (pageId: string) => void;
587
+ }
588
+ interface PageNavigatorHandle {
589
+ el: HTMLElement;
590
+ destroy: () => void;
591
+ refresh: () => void;
592
+ }
593
+ /** @public Mobile-style page tabs for LayoutPreset v2 (P4). */
594
+ declare function mountPageNavigator(parent: HTMLElement, controller: LayerController, opts?: PageNavigatorOptions): PageNavigatorHandle;
595
+
596
+ /** Minimal chart surface for layer time-scale sync (matches `IChart.applyTimeScaleSyncFromLayers`). */
597
+ interface LayerTimeScaleSyncChart {
598
+ applyTimeScaleSyncFromLayers(layers: Array<{
599
+ type: string;
600
+ pageId?: string;
601
+ syncTimeScaleGroupId?: string;
602
+ }>, pageId?: string): unknown;
603
+ }
604
+ interface BindLayerTimeScaleSyncOptions {
605
+ /** Called after each sync apply (e.g. `requestAnimationFrame(chart.resize)`). */
606
+ onSync?: () => void;
607
+ }
608
+ /**
609
+ * @public Wire `IChart.applyTimeScaleSyncFromLayers` to `LayerController` preset mutations.
610
+ * Applies immediately and on `controller.subscribe` (page switch, sync group, layer edits).
611
+ * Returns unsubscribe — call after `mountLayerCompositor` + `createChart`.
612
+ */
613
+ declare function bindLayerTimeScaleSync(chart: LayerTimeScaleSyncChart, controller: LayerController, options?: BindLayerTimeScaleSyncOptions): () => void;
614
+
615
+ /**
616
+ * @public Merge partial LayoutPreset v2 into `current` (pages/layers/groups upsert by id).
617
+ * Unmentioned entries are preserved. Result is normalized.
618
+ */
619
+ declare function mergeLayoutPreset(current: LayoutPreset, partial: LayoutPreset): LayoutPreset;
620
+
621
+ declare function layerTypeForBridgePane(pane: BridgeLayerPane): LayerType;
622
+
623
+ /**
624
+ * @public Resolve chart pane → layer ids for preset scope (supports `allPages`).
625
+ */
626
+ declare function resolvePaneLayerIds(preset: Pick<LayoutPreset, 'layers' | 'pages'>, pane: BridgeLayerPane, opts?: {
627
+ allPages?: boolean;
628
+ activePageId?: string;
629
+ }): string[];
630
+
173
631
  interface SymbolSearchOptions {
174
632
  onSearch: (query: string) => Promise<SymbolSearchHit[]>;
175
633
  onSelect: (symbol: string) => void;
@@ -210,4 +668,4 @@ declare function mountPineEditorPanel(parent: HTMLElement, opts?: PineEditorPane
210
668
 
211
669
  declare const pineLanguage: StreamLanguage<{}>;
212
670
 
213
- export { type ChartLayoutOptions, type ContextMenuAction, type ContextMenuOptions, type CrosshairLegendOptions, DEFAULT_LAYOUT_FEATURES, type DrawingContextMenuHandlers, type DrawingPropertiesPanelOptions, type DrawingToolId, GRID_SETTING_KEY, type LayoutFeatures, type OhlcvSnapshot, PINE_SCRIPT_STORAGE_KEY, type PineEditorPanelOptions, RETURN_CURSOR_KEY, type ResolvedLayoutFeatures, type SettingsPanelOptions as SettingsMenuOptions, type SettingsPanelOptions, type StatusBarOptions, type SymbolInputMode, type SymbolSearchOptions, type TopBarOptions, attachChartContextMenu, bindShortcutsModal, createDemoLayoutOptions, loadIndicatorConfig, loadPineScriptPreference, loadReturnToCursorPreference, loadShowGridPreference, mergeLayoutFeatures, mountChartLayout, mountCodeSnippetPanel, mountCrosshairLegend, mountDrawingPropertiesPanel, mountPineEditorPanel, mountSettingsPanel as mountSettingsMenu, mountSettingsPanel, mountStatusBar, mountSymbolSearch, mountTopBar, openDrawingContextMenu, openShortcutsModal, pineLanguage, resolveLayoutFeatures, saveIndicatorConfig, savePineScriptPreference, saveReturnToCursorPreference, saveShowGridPreference };
671
+ export { BUILTIN_PRESETS, type BindGroup, type BindLayerTimeScaleSyncOptions, CHART_PANE_LAYER_TYPES, type ChartLayoutOptions, type CompositorShellHandle, type CompositorShellWidgetId, type ContextMenuAction, type ContextMenuOptions, type CreateCompositorShellOptions, type CreateLayoutGridOptions, type CrosshairLegendOptions, DEFAULT_LAYOUT_FEATURES, DEFAULT_LAYOUT_SCHEMA, DEFAULT_SYNC_TIME_SCALE_GROUP, type DrawingContextMenuHandlers, type DrawingPropertiesPanelOptions, type DrawingToolId, GRID_SETTING_KEY, type HostSplitResult, type I18nProvider, LAYER_PRESET_VERSION, LAYOUT_SCHEMA_VERSION, type LayerCompositorHandle, type LayerCompositorOptions, LayerController, type LayerEditorHandle, type LayerEditorOptions, type LayerFrame, type LayerNode, type LayerPanelHandle, type LayerPanelOptions, type LayerPresetAuthor, type LayerTimeScaleSyncChart, type LayerType, type LayerWidgetElements, type LayerWidgetKey, type LayoutFeatures, type LayoutGridHandle, type LayoutPage, type LayoutPreset, type LayoutSchema, type LayoutWidgetElements, type LayoutWidgetId, type LayoutWidgetPlacement, type LogoSlotOptions, OVERLAY_LAYER_TYPES, type OhlcvSnapshot, PINE_SCRIPT_STORAGE_KEY, type PageNavigatorHandle, type PageNavigatorOptions, type PineEditorPanelOptions, type PresetListEntry, RETURN_CURSOR_KEY, type ResolvedLayoutFeatures, type SettingsPanelOptions as SettingsMenuOptions, type SettingsPanelOptions, type StatusBarOptions, type SymbolInputMode, type SymbolSearchDialogHandle, type SymbolSearchDialogOptions, type SymbolSearchOptions, THEME_STORAGE_KEY, type ThemeId, type ThemeProvider, type ThemeToggleOptions, type TopBarOptions, VENDOR_COMPACT_PRESET, VENDOR_DEFAULT_PRESET, VENDOR_DUAL_SYNC_PRESET, applyCompositorShellFeatures, applyThemeToDocument, attachChartContextMenu, attachLayerEditor, bindLayerTimeScaleSync, bindShortcutsModal, clampBBox, clampFrame, cloneLayoutPreset, cloneLayoutSchema, createCompositorShell, createDemoLayoutOptions, createI18nProvider, createLayoutGrid, createSymbolSearchDialog, createThemeProvider, deleteUserPreset, ensureOverlayLayers, expandLegacyChartHostLayers, forkPreset, getBuiltinPreset, getDrawingOverlayVisible, getLayersBoundingBox, getWidgetPlacement, isChartPaneLayerType, isOverlayLayerType, layerTypeForBridgePane, layoutSchemaToPreset, layoutStorageKey, listPresets, loadIndicatorConfig, loadLayoutSchema, loadPineScriptPreference, loadPreset, loadReturnToCursorPreference, loadShowGridPreference, loadTheme, mergeLayoutFeatures, mergeLayoutPreset, mountChartLayout, mountCodeSnippetPanel, mountCrosshairLegend, mountDrawingPropertiesPanel, mountLayerCompositor, mountLayerPanel, mountLogoSlot, mountPageNavigator, mountPineEditorPanel, mountSettingsPanel as mountSettingsMenu, mountSettingsPanel, mountStatusBar, mountSymbolSearch, mountSymbolSearchDialogTrigger, mountThemeToggle, mountTopBar, moveLayerFrames, normalizeLayoutPreset, normalizeLayoutSchema, openDrawingContextMenu, openShortcutsModal, paneIdFromWidgetKey, pineLanguage, presetStorageKey, resizeGroupFrames, resolveLayoutFeatures, resolveLayoutSchema, resolvePaneLayerIds, resolvePreset, saveIndicatorConfig, saveLayoutSchema, savePineScriptPreference, savePreset, saveReturnToCursorPreference, saveShowGridPreference, saveTheme, splitLegacyChartHost, syncAllOverlayLayersToMain, syncCompositorShellVisibilityFromFeatures, syncCrosshairLegendToMain, syncOverlayLayersToMain, upgradeIndicatorHostType, widgetKeyForChartPaneType };