# @worksheet-js/core

> **Industrial-grade, high-performance spreadsheet engine for the modern web.**

[![npm version](https://img.shields.io/npm/v/@worksheet-js/core.svg?style=flat-square)](https://www.npmjs.com/package/@worksheet-js/core)
[![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue?style=flat-square)](https://www.typescriptlang.org/)
[![License](https://img.shields.io/badge/license-Proprietary-blue.svg?style=flat-square)](./LICENSE)

`@worksheet-js/core` is the foundational engine behind the Worksheet.js ecosystem. It delivers a virtualized canvas renderer, a robust sheet state model, background formula recalculation, and a comprehensive public API for building professional-grade spreadsheet applications.

---

## Table of Contents

- [Key Features](#key-features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [API Reference](#api-reference)
  - [Static Methods](#static-methods)
  - [Instance Methods — Data](#instance-methods--data)
  - [Instance Methods — Worksheets](#instance-methods--worksheets)
  - [Instance Methods — Navigation & View](#instance-methods--navigation--view)
  - [Instance Methods — Formulas & Computation](#instance-methods--formulas--computation)
  - [Instance Methods — I/O](#instance-methods--io)
  - [Instance Methods — Performance](#instance-methods--performance)
  - [Instance Methods — Lifecycle](#instance-methods--lifecycle)
  - [Event System](#event-system)
- [Configuration Options](#configuration-options)
- [Key Types](#key-types)
- [Architecture Notes](#architecture-notes)
- [License](#license)

---

## Key Features

- **Canvas Tile Renderer** — Virtualized dirty-tile rendering; only changed tiles repaint on scroll.
- **1M+ Cells at 60 FPS** — No DOM cell pool; pure canvas operations with hardware acceleration.
- **Progressive Loading** — First ~300 rows rendered immediately; remaining rows hydrate in the background.
- **Background Formula Engine** — Calculations run in a dedicated Web Worker via `CalcWorkerHost`.
- **300+ Excel Functions** — Powered by `@worksheet-js/formula` with full cross-sheet dependency tracking.
- **High-Fidelity XLSX I/O** — Round-trip import/export preserving styles, merges, conditional formatting, and hyperlinks.
- **Plugin System** — Charts (Chart.js 4.x), Pivot Tables, Data Validation, Conditional Formatting, Comments, Images.
- **Freeze Panes** — Correct per-axis frozen row/column rendering with hit-test support.
- **Find & Replace** — Full-sheet search with match highlighting.
- **Performance Monitoring** — Built-in rolling-window P95/mean/max telemetry across 6 metrics.
- **LRU Display Cache** — Bounded at 100K entries with proactive eviction on scroll.
- **Fully Type-Safe** — Strict TypeScript with exhaustive type definitions for all public surfaces.

---

## Installation

```bash
npm install @worksheet-js/core chart.js
```

> **Note:** `chart.js >= 4.0.0` is a **required peer dependency** for integrated charting. Install it even if you are not using charts.

> **Tip:** Using React or Vue? Install the official wrapper for the best developer experience:
>
> - **React:** `npm install @worksheet-js/core @worksheet-js/react chart.js`
> - **Vue:** `npm install @worksheet-js/core @worksheet-js/vue chart.js`

---

## Quick Start

```typescript
import { Worksheet } from '@worksheet-js/core';

const sheet = await Worksheet.create({
  container: document.getElementById('app')!,
  worksheets: [
    {
      worksheetName: 'Annual Report',
      minDimensions: [26, 1000], // 26 columns (A–Z), 1000 rows
    },
  ],
  toolbar: true,
  formulaBar: true,
  theme: 'light',
});

// Write values
sheet.setValue('A1', 'Product');
sheet.setValue('B1', 'Revenue');
sheet.setValue('B2', '15000');
sheet.setValue('B3', '=SUM(B2:B100)');

// Style cells
sheet.setStyle('A1', { bold: true, color: '#1a1a2e', backgroundColor: '#e8f4f8' });

// Listen for changes
sheet.on('onchange', (ws, cell, x, y, value) => {
  console.log(`[${x},${y}] → ${value}`);
});
```

---

## API Reference

### Static Methods

```typescript
// Create a new spreadsheet instance
Worksheet.create(options: WorksheetOptions): Promise<Worksheet>

// Load from an XLSX binary buffer
Worksheet.loadXlsx(buffer: Uint8Array, options: WorksheetOptions): Promise<Worksheet>

// Load from CSV data
Worksheet.loadCsv(data: string | Uint8Array, options: WorksheetOptions): Promise<Worksheet>

// Load from JSON data
Worksheet.loadJson(data: any, options: WorksheetOptions): Promise<Worksheet>
```

---

### Instance Methods — Data

```typescript
// Get cell value by address or coordinates
getValue(address: string): string
getValue(x: number, y: number): string

// Set cell value by address or coordinates
setValue(address: string, value: string): void
setValue(x: number, y: number, value: string): void

// Get/set cell style
getStyle(address: string): Partial<CellStyle>
setStyle(address: string, style: Partial<CellStyle>): void
setStyle(x: number, y: number, style: Partial<CellStyle>): void
```

---

### Instance Methods — Worksheets

```typescript
// Read-only getter for the currently active sheet
get activeWorksheet(): WorksheetModel | null

// Switch active tab (0-based index)
setActiveWorksheet(index: number): void

// Sheet management
addWorksheet(options?: Partial<SheetMeta>): Promise<WorksheetModel | null>
deleteWorksheet(index: number): void
duplicateWorksheet(index: number): Promise<void>
moveWorksheet(fromIdx: number, toIdx: number): void
renameWorksheet(index: number, name: string): void
hideWorksheet(index: number): void
unhideWorksheet(index: number): void
```

---

### Instance Methods — Navigation & View

```typescript
// Scroll to a specific row/column (0-based)
goto(y: number, x?: number): void

// Directional navigation
left(): void
right(): void
top(): void
down(): void
first(): void   // Jump to A1
last(): void    // Jump to last data cell

// Viewport control
setViewport(width: number | string, height: number | string): void
fullscreen(state: boolean): void

// Zoom (percentage, e.g. 100, 150, 75)
getZoom(): number
setZoom(value: number): void

// Theme
setTheme(theme: 'light' | 'dark'): void
setColors(colors: Partial<ThemeColors>): void
setFonts(fonts: ThemeFonts): void
setDefaultFont(displayName: string, cssFamily?: string): void
```

---

### Instance Methods — Formulas & Computation

```typescript
// Toggle background formula recalculation
calculations(state: boolean): void

// Execute a formula expression against the current sheet (not stored in any cell)
executeFormula(expression: string, x?: number, y?: number, caching?: boolean): unknown

// Volatile cache: store and retrieve arbitrary values keyed by cell name
getCache(cellName: string): unknown
setCache(cellName: string, value: unknown): void
setCache(entries: Record<string, unknown>): void
```

---

### Instance Methods — I/O

```typescript
// Import from a browser File (XLSX or CSV auto-detected)
importFromFile(file: File): Promise<void>

// Export current workbook
exportXlsx(filename?: string): Promise<void>
exportCsv(filename?: string): Promise<void>

// Save (calls the configured onSave callback or triggers download)
save(): Promise<void>

// Dirty state tracking
markDirty(): void
isDirty(): boolean
```

---

### Instance Methods — Performance

The built-in performance monitor tracks 6 rolling-window metrics with P95, mean, and max statistics.

```typescript
// Snapshot of all current performance metrics
getPerfSnapshot(): PerfSnapshot

// Clear all rolling windows (call before starting a benchmark)
resetPerfCounters(): void
```

**`PerfSnapshot` shape:**

```typescript
interface PerfSnapshot {
  renderMs: { p95: number; mean: number; max: number };
  paintMs: { p95: number; mean: number; max: number };
  scrollFps: { p95: number; mean: number; max: number };
  inputLatencyMs: { p95: number; mean: number; max: number };
  formulaMs: { p95: number; mean: number; max: number };
  estimatedCellMemoryKB: { p95: number; mean: number; max: number };
}
```

**Example:**

```typescript
sheet.resetPerfCounters();
// ... run workload ...
const snap = sheet.getPerfSnapshot();
console.log('P95 render:', snap.renderMs.p95, 'ms');
console.log('Scroll FPS:', snap.scrollFps.mean);
```

---

### Instance Methods — Lifecycle

```typescript
// Attach / remove event listeners
on(event: string, callback: (...args: any[]) => void): void
off(event: string, callback: (...args: any[]) => void): void
emit(event: string, ...args: any[]): boolean

// Destroy the instance, unmount from DOM, release Web Worker
destroy(): void
```

---

### Event System

```typescript
// Cell value changed
sheet.on(
  'onchange',
  (worksheet: WorksheetModel, cell: any, x: number, y: number, value: string, oldValue: string) => {
    console.log(`Cell [${x},${y}] changed: ${oldValue} → ${value}`);
  }
);

// Selection changed
sheet.on(
  'onselection',
  (worksheet: WorksheetModel, x1: number, y1: number, x2: number, y2: number) => {
    console.log(`Selection: [${x1},${y1}] → [${x2},${y2}]`);
  }
);

// Cell edit started
sheet.on('oneditionstart', (worksheet: WorksheetModel, x: number, y: number) => {});

// Cell edit committed
sheet.on('oneditionend', (worksheet: WorksheetModel, x: number, y: number, value: string) => {});
```

---

## Configuration Options

```typescript
interface WorksheetOptions {
  /** Required. The DOM element to mount into. */
  container: HTMLElement;

  /** Initial sheet configurations. */
  worksheets?: Partial<SheetMeta>[];

  /** Show the ribbon toolbar. Default: true */
  toolbar?: boolean;

  /** Show the formula bar. Default: true */
  formulaBar?: boolean;

  /** Color theme. Default: 'light' */
  theme?: 'light' | 'dark';

  /** Default column width in pixels. */
  defaultColWidth?: number;

  /** Default row height in pixels. */
  defaultRowHeight?: number;

  /** Number of rows frozen at the top. */
  frozenRows?: number;

  /** Number of columns frozen at the left. */
  frozenCols?: number;

  /** License key for Pro/Enterprise features. */
  licenseKey?: string;

  /** Called when the user triggers Save. */
  onSave?: (data: any) => Promise<void> | void;
}
```

---

## Key Types

```typescript
interface CellAddress {
  x: number; // 0-based column index
  y: number; // 0-based row index
}

interface SelectionRange {
  x1: number;
  y1: number; // Top-left (inclusive)
  x2: number;
  y2: number; // Bottom-right (inclusive)
}

interface CellStyle {
  bold?: boolean;
  italic?: boolean;
  underline?: boolean;
  strikethrough?: boolean;
  fontSize?: number;
  fontFamily?: string;
  color?: string; // CSS hex, e.g. '#FF0000'
  backgroundColor?: string;
  align?: 'left' | 'center' | 'right';
  verticalAlign?: 'top' | 'middle' | 'bottom';
  wrap?: boolean;
  border?: BorderObject;
  numberFormat?: string;
}

interface SheetMeta {
  worksheetName: string;
  minDimensions?: [cols: number, rows: number];
  frozenRows?: number;
  frozenCols?: number;
  hidden?: boolean;
}
```

---

## Architecture Notes

| Component             | File                               | Responsibility                               |
| :-------------------- | :--------------------------------- | :------------------------------------------- |
| `SpreadsheetEngine`   | `engine/SpreadsheetEngine.ts`      | Top-level public API owner                   |
| `WorksheetModel`      | `model/Worksheet.ts`               | Per-sheet state                              |
| `SheetData`           | `model/SheetData.ts`               | Sparse cell storage via `ChunkedCellStore`   |
| `ChunkedCellStore`    | `model/chunks/ChunkedCellStore.ts` | 256×256 block cell store                     |
| `LRUCache`            | `model/chunks/LRUCache.ts`         | Generic O(1) LRU eviction                    |
| `Viewport`            | `render/viewport/Viewport.ts`      | Canvas renderer; dirty-tile partial repaints |
| `DisplayCacheManager` | `model/display-cache/`             | LRU-bounded render cache (max 100K entries)  |
| `CalcWorkerHost`      | `workers/calc/CalcWorkerHost.ts`   | Main-thread proxy to the formula Web Worker  |
| `SparseMetricsIndex`  | `model/metrics/`                   | Fenwick-tree backed row/col offset index     |
| `MergeIndex`          | `model/merges/`                    | Row-bucketed merge lookup                    |
| `PerformanceMonitor`  | `perf/PerformanceMonitor.ts`       | 6-metric rolling-window telemetry            |

**Invariants enforced by the architecture:**

1. No O(totalRows) or O(totalCols) work inside scroll or render handlers.
2. Formula evaluation never happens on the paint thread.
3. `_onViewChange(x, y)` triggers a partial tile repaint; `_onViewChange(-1, -1)` triggers a full repaint.
4. Display cache evicted proactively on scroll to keep memory footprint bounded.

---

## License

Copyright &copy; 2024-present Worksheet Systems. All rights reserved.
Proprietary software. Usage is subject to the [EULA](LICENSE) terms.

For technical support: [support@worksheetjs.com](mailto:support@worksheetjs.com)
