Excel-like data grid development with TypeScript, formula engine, plugin system, and multi-company support. Includes dashboard widgets, IndexedDB persistence, and comprehensive testing.
Comprehensive guidance for working with DJ DataForge v6 - an Excel-like data grid application with formula engine, plugin system, and multi-company support. Built with TypeScript, Vite, and IndexedDB for client-side persistence.
**Core Development:**
```bash
npm run dev # Start dev server (http://localhost:5173)
npm run build # TypeScript compile + production build
npm run preview # Preview production build (http://localhost:4173)
npm run type-check # TypeScript type checking (no emit)
```
**Code Quality:**
```bash
npm run lint # ESLint (src/**/*.ts)
npm run format # Prettier formatting
```
**Testing:**
```bash
npm run test # Run all tests (vitest)
npm run test:unit # Unit tests only
npm run test:e2e # Playwright E2E tests
npm run test:watch # Watch mode
npm run coverage # Coverage report
npm run bench # Performance benchmarks
```
**Plugin Builds:**
```bash
npm run build:plugin # Build plugin using vite.config.plugin.ts
npm run build:standalone # Build standalone version (pre + post processing)
```
1. **NEVER** open `dist/index.html` directly in browser (CORS blocks `file://` protocol)
2. **ALWAYS** use `npm run preview` or HTTP server for testing builds
3. Production deployments must serve files from `dist/` via HTTP server
4. Requires Node.js >= 18.0.0
The codebase follows a layered architecture:
```
UI Layer (ui-manager.ts)
↓ Grid View | Dashboard Mode | Selection | Toolbar
Plugin Layer (plugins/)
↓ FX-Finance | Charts | ProLease | Custom Plugins
API Layer (PluginContext + EventBus)
↓ Events | UI Integration | Storage API | Formulas
Core Services (Kernel)
↓ Workbook | CalcEngine | Dashboard | Table | Grid
Persistence Layer (storage-utils)
↓ IndexedDB | Companies | Workbooks | Plugin Data
```
The Kernel singleton orchestrates all components:
```typescript
import { kernel } from '@core/kernel';
// or
import { DJDataForgeKernel } from '@core/kernel';
const kernel = DJDataForgeKernel.getInstance();
// Available managers:
kernel.workbookManager // Workbooks and sheets
kernel.calcEngine // Formula parser and evaluator
kernel.storageManager // IndexedDB persistence
kernel.companyManager // Multi-company contexts
kernel.pluginHost // Plugin loading and management
kernel.eventBus // Pub/sub event system
kernel.sessionManager // Session tracking
kernel.dashboardManager // Dashboard creation and management
kernel.tableManager // Table operations
```
**Core Layer** (`src/@core/`):
**UI Layer:**
**Plugin Layer** (`src/plugins/`):
**Path Aliases** (use in all imports):
```typescript
@core/* → src/@core/*
@ui/* → src/@ui/*
@plugins/* → src/@plugins/*
```
**Core Functions (20 formulas):**
**FX-Finance Plugin Functions:**
**ProLease Plugin Functions:**
```typescript
const registry = kernel.calcEngine.getRegistry();
registry.register('MY_FORMULA', (arg1: number, arg2: number) => {
return arg1 + arg2;
}, {
argCount: 2,
description: 'Adds two numbers'
});
```
1. User enters: `=SOMA(A1:A10)`
2. FormulaParser tokenizes → AST
3. CalcEngine evaluates AST recursively
4. Registry lookup for SOMA
5. Arguments evaluated (A1:A10 → array)
6. Function called, result stored in cell
```typescript
import type { Plugin, PluginContext, PluginManifest } from '@core/types';
export class MyPlugin implements Plugin {
manifest: PluginManifest = {
id: 'my-plugin',
name: 'My Plugin',
version: '1.0.0',
author: 'Your Name',
description: 'Does something cool',
permissions: ['read:workbook', 'write:workbook', 'ui:toolbar'],
entryPoint: 'my-plugin.js',
};
async init(context: PluginContext): Promise<void> {
// Register custom formulas
const registry = context.kernel.calcEngine.getRegistry();
registry.register('MY_FUNC', (arg: number) => arg * 2, {
argCount: 1,
description: 'Doubles a number'
});
// Add UI elements
context.ui.addToolbarButton({
id: 'my-button',
label: 'My Action',
icon: '🚀',
onClick: () => this.handleClick(context)
});
// Access plugin storage
await context.storage.set('config', { enabled: true });
// Listen to events
context.events.on('cell:changed', (data) => {
// React to cell changes
});
context.ui.showToast('My Plugin loaded!', 'success');
}
private handleClick(context: PluginContext): void {
// Plugin logic here
}
async dispose(): Promise<void> {
// Cleanup resources
}
}
```
**In `src/plugins/index.ts`:**
```typescript
import { MyPlugin } from './my-plugin';
export const myPlugin = new MyPlugin();
```
**In `src/app.ts`:**
```typescript
await kernel.pluginHost.loadPlugin(myPlugin);
```
```typescript
// Create workbook
const wb = kernel.workbookManager.createWorkbook('My Workbook');
// Add sheet
const sheet = wb.addSheet('Sheet1');
// Set cell values
sheet.setCell(0, 0, 'Name');
sheet.setCell(1, 0, 'John', { type: 'text' });
// Set formula
sheet.setCell(1, 1, '=A2&" Smith"', {
formula: '=A2&" Smith"',
type: 'formula'
});
// Recalculate
await kernel.recalculate(sheet.id);
// Save
await kernel.saveWorkbook(wb.id);
```
```typescript
const wb = kernel.workbookManager.getActiveWorkbook();
if (!wb) return;
// Add sheet with data
const sheet = wb.addSheet('Data');
const headers = ['ID', 'Name', 'Amount'];
headers.forEach((h, col) => sheet.setCell(0, col, h));
// Add data rows
const data = [[1, 'Alice', 100], [2, 'Bob', 200]];
data.forEach((row, rowIdx) => {
row.forEach((val, colIdx) => {
sheet.setCell(rowIdx + 1, colIdx, val);
});
});
await kernel.recalculate(sheet.id);
await kernel.saveWorkbook(wb.id);
```
```typescript
// Create company context
const company = await kernel.companyManager.createCompany('Acme Corp');
// Set as active
kernel.companyManager.setActiveCompany(company.id);
// Workbooks now inherit company settings
const wb = kernel.workbookManager.createWorkbook('Q4 Sales');
// wb.companyId === company.id
```
Company contexts include:
**Kernel:** `kernel:ready`, `kernel:shutdown`, `kernel:recalc-done`, `kernel:autosave-done`
**Workbook:** `workbook:created`, `workbook:deleted`, `workbook:saved`
**Sheet:** `sheet:created`, `sheet:deleted`, `sheet:renamed`, `sheet:activated`
**Plugin:** `plugin:loaded`, `plugin:unloaded`
**Cell:** `cell:changed`, `cell:edited`
**Dashboard:** `dashboard:created`, `widget:added`, `widget:updated`
```typescript
// Listen to events
kernel.eventBus.on('workbook:saved', (data) => {
console.log('Workbook saved:', data.workbookId);
});
// Emit custom events
kernel.eventBus.emit('custom:event', { data: 'payload' });
```
```typescript
// Create dashboard
const dashboard = kernel.dashboardManager.createDashboard('Sales Dashboard');
// Add KPI widget
dashboard.addWidget({
type: 'kpi-card',
config: {
title: 'Total Revenue',
dataSource: '=SOMA(Sales!D:D)',
format: 'currency'
}
});
// Render to sheet
await dashboard.renderToSheet('Dashboard Sheet');
```
```typescript
const uiManager = window.DJUIManager;
// Switch to dashboard mode
uiManager.setMode('dashboard');
// Get current selection
const selection = uiManager.getSelection();
// Register keyboard shortcut
uiManager.registerShortcut('Ctrl+Shift+D', () => {
// Custom action
});
```
**Database:** `DJ_DataForge_v6`
**Object Stores:**
This project uses **strict TypeScript** with aggressive linting:
Always ensure full type safety and handle all edge cases explicitly.
1. **Always use path aliases** (`@core/*`, `@ui/*`, `@plugins/*`)
2. **Access kernel via singleton** (`kernel` import or `DJDataForgeKernel.getInstance()`)
3. **Use EventBus for decoupled communication** (avoid direct dependencies)
4. **Test builds with HTTP server** (never `file://` protocol)
5. **Leverage plugin storage for persistence** (scoped to plugin ID)
6. **Follow formula naming conventions** (Portuguese for core, namespaced for plugins)
7. **Register widgets in WidgetRegistry** (for dashboard reusability)
8. **Handle async operations properly** (await recalculations and saves)
9. **Use UI Manager for mode switching** (grid vs dashboard)
10. **Respect TypeScript strict mode** (no implicit any, handle nulls)
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/dj-dataforge-v6-development-guide/raw